Browse Source

Merge pull request #90 from syanbo/chore/2.9.0-sdk-integrate

upgrade to 2.9.0 sdk
matrixbirds 5 years ago
parent
commit
b013b6d64e

+ 15
- 0
CHANGELOG View File

@@ -2,6 +2,21 @@
2 2
 
3 3
 #### 2.9.0-alpha.1
4 4
   - fix typo: rename `methodisSpeakerphoneEnabled` to `isSpeakerphoneEnabled`
5
+  - events deprecated & instead:
6
+    * `microphoneEnabled` instead `localAudioStateChanged`
7
+    * `audioTransportStatsOfUid` instead `remoteAudioStats`
8
+    * `videoTransportStatsOfUid` instead `remoteVideoStats`
9
+    * `userMuteVideo`, `userEnableVideo`, `userEnableLocalVideo` & `firstRemoteVideoDecoded` instead `remoteVideoStateChanged`
10
+  - events enhancement:
11
+    * `rtcStats`, `leaveChannel` add channel stats `txAudioBytes`, `txVideoBytes`, `rxAudioBytes`, `rxVideoBytes`, `txPacketLossRate`, `rxPacketLossRate`
12
+  - new events:
13
+    * `remoteAudioStateChanged` subscribe for remote audio state
14
+    * `localAudioStateChanged` subscribe for local audio state
15
+    * `localAudioStats` subscribe for local audio stats
16
+    * `mediaRelayStateChanged`, `receivedChannelMediaRelay`
17
+  - new apis:
18
+    * `switchChannel` switch to specified channel
19
+    * `startChannelMediaRelay`, `updateChannelMediaRelay`, `stopChannelMediaRelay`, `removeChannelMediaRelay` relay media streams operation for across channels.
5 20
 
6 21
 #### 2.8.0-alpha.1
7 22
   - add `string uid` api support

+ 44
- 0
android/CMakeLists.txt View File

@@ -0,0 +1,44 @@
1
+# For more information about using CMake with Android Studio, read the
2
+# documentation: https://d.android.com/studio/projects/add-native-code.html
3
+
4
+# Sets the minimum version of CMake required to build the native library.
5
+
6
+cmake_minimum_required(VERSION 3.4.1)
7
+
8
+# Creates and names a library, sets it as either STATIC
9
+# or SHARED, and provides the relative paths to its source code.
10
+# You can define multiple libraries, and CMake builds them for you.
11
+# Gradle automatically packages shared libraries with your APK.
12
+
13
+add_library( # Sets the name of the library.
14
+             apm-plugin-raw-data-api-java
15
+
16
+             # Sets the library as a shared library.
17
+             SHARED
18
+
19
+             # Provides a relative path to your source file(s).
20
+             src/main/cpp/src/agora_media_pre_processing.cpp )
21
+
22
+# Searches for a specified prebuilt library and stores the path as a
23
+# variable. Because CMake includes system libraries in the search path by
24
+# default, you only need to specify the name of the public NDK library
25
+# you want to add. CMake verifies that the library exists before
26
+# completing its build.
27
+
28
+find_library( # Sets the name of the path variable.
29
+              log-lib
30
+
31
+              # Specifies the name of the NDK library that
32
+              # you want CMake to locate.
33
+              log )
34
+
35
+# Specifies libraries CMake should link to your target library. You
36
+# can link multiple libraries, such as libraries you define in this
37
+# build script, prebuilt third-party libraries, or system libraries.
38
+
39
+target_link_libraries( # Specifies the target library.
40
+                       apm-plugin-raw-data-api-java
41
+
42
+                       # Links the target library to the log library
43
+                       # included in the NDK.
44
+                       ${log-lib} )

+ 1
- 1
android/build.gradle View File

@@ -61,7 +61,7 @@ dependencies {
61 61
     def androidSupportVersion = rootProject.hasProperty("androidSupportVersion")  ? rootProject.androidSupportVersion : DEFAULT_ANDROID_SUPPORT_VERSION
62 62
     // from internet
63 63
     implementation "com.android.support:appcompat-v7:$androidSupportVersion"
64
-    implementation "io.agora.rtc:full-sdk:2.8.2"
64
+    implementation "io.agora.rtc:full-sdk:2.9.0"
65 65
     // from node_modules
66 66
     implementation "com.facebook.react:react-native:+"
67 67
 }

+ 781
- 0
android/src/main/cpp/include/AgoraBase.h View File

@@ -0,0 +1,781 @@
1
+//  Agora Engine SDK
2
+//
3
+//  Copyright (c) 2019 Agora.io. All rights reserved.
4
+//
5
+
6
+#ifndef AGORA_BASE_H
7
+#define AGORA_BASE_H
8
+
9
+#include <stddef.h>
10
+#include <stdio.h>
11
+#include <stdarg.h>
12
+
13
+#if defined(_WIN32)
14
+#define WIN32_LEAN_AND_MEAN
15
+#include <windows.h>
16
+#define AGORA_CALL __cdecl
17
+#if defined(AGORARTC_EXPORT)
18
+#define AGORA_API extern "C" __declspec(dllexport)
19
+#else
20
+#define AGORA_API extern "C" __declspec(dllimport)
21
+#endif
22
+#elif defined(__APPLE__)
23
+#include <TargetConditionals.h>
24
+#define AGORA_API __attribute__((visibility("default"))) extern "C"
25
+#define AGORA_CALL
26
+#elif defined(__ANDROID__) || defined(__linux__)
27
+#define AGORA_API extern "C" __attribute__((visibility("default")))
28
+#define AGORA_CALL
29
+#else
30
+#define AGORA_API extern "C"
31
+#define AGORA_CALL
32
+#endif
33
+
34
+namespace agora {
35
+namespace util {
36
+
37
+template<class T>
38
+class AutoPtr {
39
+    typedef T value_type;
40
+    typedef T* pointer_type;
41
+public:
42
+    AutoPtr(pointer_type p=0)
43
+        :ptr_(p)
44
+    {}
45
+    ~AutoPtr() {
46
+        if (ptr_)
47
+            ptr_->release();
48
+    }
49
+    operator bool() const { return ptr_ != (pointer_type)0; }
50
+    value_type& operator*() const {
51
+        return *get();
52
+    }
53
+
54
+    pointer_type operator->() const {
55
+        return get();
56
+    }
57
+
58
+    pointer_type get() const {
59
+        return ptr_;
60
+    }
61
+
62
+    pointer_type release() {
63
+        pointer_type tmp = ptr_;
64
+        ptr_ = 0;
65
+        return tmp;
66
+    }
67
+
68
+    void reset(pointer_type ptr = 0) {
69
+        if (ptr != ptr_ && ptr_)
70
+            ptr_->release();
71
+        ptr_ = ptr;
72
+    }
73
+    template<class C1, class C2>
74
+    bool queryInterface(C1* c, C2 iid) {
75
+        pointer_type p = NULL;
76
+        if (c && !c->queryInterface(iid, (void**)&p))
77
+        {
78
+            reset(p);
79
+        }
80
+        return p != NULL;
81
+	}
82
+private:
83
+    AutoPtr(const AutoPtr&);
84
+    AutoPtr& operator=(const AutoPtr&);
85
+private:
86
+    pointer_type ptr_;
87
+};
88
+class IString {
89
+protected:
90
+    virtual ~IString(){}
91
+public:
92
+    virtual bool empty() const = 0;
93
+    virtual const char* c_str() = 0;
94
+    virtual const char* data() = 0;
95
+    virtual size_t length() = 0;
96
+    virtual void release() = 0;
97
+};
98
+typedef AutoPtr<IString> AString;
99
+
100
+}//namespace util
101
+
102
+enum INTERFACE_ID_TYPE
103
+{
104
+    AGORA_IID_AUDIO_DEVICE_MANAGER = 1,
105
+    AGORA_IID_VIDEO_DEVICE_MANAGER = 2,
106
+    AGORA_IID_RTC_ENGINE_PARAMETER = 3,
107
+    AGORA_IID_MEDIA_ENGINE = 4,
108
+    AGORA_IID_SIGNALING_ENGINE = 8,
109
+};
110
+
111
+    /** Warning code.
112
+     */
113
+enum WARN_CODE_TYPE
114
+{
115
+  /** 8: The specified view is invalid. Specify a view when using the video call function.
116
+  */
117
+    WARN_INVALID_VIEW = 8,
118
+    /** 16: Failed to initialize the video function, possibly caused by a lack of resources. The users cannot see the video while the voice communication is not affected.
119
+    */
120
+    WARN_INIT_VIDEO = 16,
121
+    /** 20: The request is pending, usually due to some module not being ready, and the SDK postponed processing the request.
122
+    */
123
+    WARN_PENDING = 20,
124
+    /** 103: No channel resources are available. Maybe because the server cannot allocate any channel resource.
125
+    */
126
+    WARN_NO_AVAILABLE_CHANNEL = 103,
127
+    /** 104: A timeout occurs when looking up the channel. When joining a channel, the SDK looks up the specified channel. This warning usually occurs when the network condition is too poor for the SDK to connect to the server.
128
+    */
129
+    WARN_LOOKUP_CHANNEL_TIMEOUT = 104,
130
+    /** **DEPRECATED** 105: The server rejects the request to look up the channel. The server cannot process this request or the request is illegal.
131
+     
132
+     Deprecated as of v2.4.1. Use CONNECTION_CHANGED_REJECTED_BY_SERVER(10) in the \ref agora::rtc::IRtcEngineEventHandler::onConnectionStateChanged "onConnectionStateChanged" callback instead.
133
+    */
134
+    WARN_LOOKUP_CHANNEL_REJECTED = 105,
135
+    /** 106: A timeout occurs when opening the channel. Once the specific channel is found, the SDK opens the channel. This warning usually occurs when the network condition is too poor for the SDK to connect to the server.
136
+    */
137
+    WARN_OPEN_CHANNEL_TIMEOUT = 106,
138
+    /** 107: The server rejects the request to open the channel. The server cannot process this request or the request is illegal.
139
+    */
140
+    WARN_OPEN_CHANNEL_REJECTED = 107,
141
+
142
+    // sdk: 100~1000
143
+    /** 111: A timeout occurs when switching to the live video.
144
+    */
145
+    WARN_SWITCH_LIVE_VIDEO_TIMEOUT = 111,
146
+    /** 118: A timeout occurs when setting the client role in the live broadcast profile.
147
+    */
148
+    WARN_SET_CLIENT_ROLE_TIMEOUT = 118,
149
+    /** 121: The ticket to open the channel is invalid.
150
+    */
151
+    WARN_OPEN_CHANNEL_INVALID_TICKET = 121,
152
+    /** 122: Try connecting to another server.
153
+    */
154
+    WARN_OPEN_CHANNEL_TRY_NEXT_VOS = 122,
155
+    WARN_CHANNEL_CONNECTION_UNRECOVERABLE = 131,
156
+    WARN_CHANNEL_CONNECTION_IP_CHANGED = 132,
157
+    WARN_CHANNEL_CONNECTION_PORT_CHANGED = 133,
158
+    /** 701: An error occurs in opening the audio mixing file.
159
+    */
160
+    WARN_AUDIO_MIXING_OPEN_ERROR = 701,
161
+    /** 1014: Audio Device Module: a warning occurs in the playback device.
162
+    */
163
+    WARN_ADM_RUNTIME_PLAYOUT_WARNING = 1014,
164
+    /** 1016: Audio Device Module: a warning occurs in the recording device.
165
+    */
166
+    WARN_ADM_RUNTIME_RECORDING_WARNING = 1016,
167
+    /** 1019: Audio Device Module: no valid audio data is collected.
168
+    */
169
+    WARN_ADM_RECORD_AUDIO_SILENCE = 1019,
170
+    /** 1020: Audio Device Module: the playback device fails.
171
+    */
172
+    WARN_ADM_PLAYOUT_MALFUNCTION = 1020,
173
+    /** 1021: Audio Device Module: the recording device fails.
174
+    */
175
+    WARN_ADM_RECORD_MALFUNCTION = 1021,
176
+    /** 1029: During a call, the audio session category should be set to 
177
+     * AVAudioSessionCategoryPlayAndRecord, and RtcEngine monitors this value. 
178
+     * If the audio session category is set to other values, this warning code 
179
+     * is triggered and RtcEngine will forcefully set it back to 
180
+     * AVAudioSessionCategoryPlayAndRecord.
181
+    */
182
+    WARN_ADM_IOS_CATEGORY_NOT_PLAYANDRECORD = 1029,
183
+    /** 
184
+     */
185
+    WARN_ADM_IOS_SAMPLERATE_CHANGE = 1030,
186
+    /** 1031: Audio Device Module: the recorded audio voice is too low.
187
+    */
188
+    WARN_ADM_RECORD_AUDIO_LOWLEVEL = 1031,
189
+    /** 1032: Audio Device Module: the playback audio voice is too low.
190
+    */
191
+    WARN_ADM_PLAYOUT_AUDIO_LOWLEVEL = 1032,
192
+    /** 1040: Audio device module: An exception occurs with the audio drive. 
193
+     * Solutions: 
194
+     * - Disable or re-enable the audio device.
195
+     * - Re-enable your device.
196
+     * - Update the sound card drive.
197
+     */
198
+    WARN_ADM_WINDOWS_NO_DATA_READY_EVENT = 1040,
199
+    /** 1051: Audio Device Module: howling is detected.
200
+    */
201
+    WARN_APM_HOWLING = 1051,
202
+    /** 1052: Audio Device Module: the device is in the glitch state.
203
+    */
204
+    WARN_ADM_GLITCH_STATE = 1052,
205
+    /** 1053: Audio Device Module: the underlying audio settings have changed.
206
+    */
207
+    WARN_ADM_IMPROPER_SETTINGS = 1053,
208
+    /**
209
+        */
210
+    WARN_ADM_WIN_CORE_NO_RECORDING_DEVICE = 1322,
211
+    /** 1323: Audio device module: No available playback device. 
212
+     * Solution: Plug in the audio device.
213
+    */
214
+    WARN_ADM_WIN_CORE_NO_PLAYOUT_DEVICE = 1323,
215
+    /** Audio device module: The capture device is released improperly. 
216
+     * Solutions: 
217
+     * - Disable or re-enable the audio device.
218
+     * - Re-enable your device.
219
+     * - Update the sound card drive.
220
+     */
221
+    WARN_ADM_WIN_CORE_IMPROPER_CAPTURE_RELEASE = 1324,
222
+    /** 1610: Super-resolution warning: the original video dimensions of the remote user exceed 640 &times; 480.
223
+    */
224
+    WARN_SUPER_RESOLUTION_STREAM_OVER_LIMITATION = 1610,
225
+    /** 1611: Super-resolution warning: another user is using super resolution.
226
+    */
227
+    WARN_SUPER_RESOLUTION_USER_COUNT_OVER_LIMITATION = 1611,
228
+    /** 1612: The device is not supported.
229
+    */
230
+    WARN_SUPER_RESOLUTION_DEVICE_NOT_SUPPORTED = 1612,
231
+
232
+    WARN_RTM_LOGIN_TIMEOUT = 2005,
233
+    WARN_RTM_KEEP_ALIVE_TIMEOUT = 2009
234
+};
235
+
236
+/** Error code.
237
+*/
238
+enum ERROR_CODE_TYPE
239
+{
240
+  /** 0: No error occurs.
241
+  */
242
+    ERR_OK = 0,
243
+    //1~1000
244
+    /** 1: A general error occurs (no specified reason).
245
+    */
246
+    ERR_FAILED = 1,
247
+    /** 2: An invalid parameter is used. For example, the specific channel name includes illegal characters.
248
+    */
249
+    ERR_INVALID_ARGUMENT = 2,
250
+    /** 3: The SDK module is not ready. Possible solutions:
251
+
252
+     - Check the audio device.
253
+     - Check the completeness of the application.
254
+     - Re-initialize the RTC engine.
255
+     */
256
+    ERR_NOT_READY = 3,
257
+    /** 4: The SDK does not support this function.
258
+     */
259
+    ERR_NOT_SUPPORTED = 4,
260
+    /** 5: The request is rejected. This is for internal SDK use only, and it does not return to the application through any method or callback.
261
+     */
262
+    ERR_REFUSED = 5,
263
+    /** 6: The buffer size is not big enough to store the returned data.
264
+     */
265
+    ERR_BUFFER_TOO_SMALL = 6,
266
+    /** 7: The SDK is not initialized before calling this method.
267
+     */
268
+    ERR_NOT_INITIALIZED = 7,
269
+    /** 9: No permission exists. This is for internal SDK use only, and it does not return to the application through any method or callback.
270
+     */
271
+    ERR_NO_PERMISSION = 9,
272
+    /** 10: An API method timeout occurs. Some API methods require the SDK to return the execution result, and this error occurs if the request takes too long (more than 10 seconds) for the SDK to process.
273
+     */
274
+    ERR_TIMEDOUT = 10,
275
+    /** 11: The request is canceled. This is for internal SDK use only, and it does not return to the application through any method or callback.
276
+     */
277
+    ERR_CANCELED = 11,
278
+    /** 12: The method is called too often. This is for internal SDK use only, and it does not return to the application through any method or callback.
279
+     */
280
+    ERR_TOO_OFTEN = 12,
281
+    /** 13: The SDK fails to bind to the network socket. This is for internal SDK use only, and it does not return to the application through any method or callback.
282
+     */
283
+    ERR_BIND_SOCKET = 13,
284
+    /** 14: The network is unavailable. This is for internal SDK use only, and it does not return to the application through any method or callback.
285
+     */
286
+    ERR_NET_DOWN = 14,
287
+    /** 15: No network buffers are available. This is for internal SDK internal use only, and it does not return to the application through any method or callback.
288
+     */
289
+    ERR_NET_NOBUFS = 15,
290
+    /** 17: The request to join the channel is rejected. This error usually occurs when the user is already in the channel, and still calls the method to join the channel, for example, \ref agora::rtc::IRtcEngine::joinChannel "joinChannel".
291
+     */
292
+    ERR_JOIN_CHANNEL_REJECTED = 17,
293
+    /** 18: The request to leave the channel is rejected.
294
+
295
+     This error usually occurs:
296
+
297
+     - When the user has left the channel and still calls \ref agora::rtc::IRtcEngine::leaveChannel "leaveChannel" to leave the channel. In this case, stop calling \ref agora::rtc::IRtcEngine::leaveChannel "leaveChannel".
298
+     - When the user has not joined the channel and still calls \ref agora::rtc::IRtcEngine::leaveChannel "leaveChannel" to leave the channel. In this case, no extra operation is needed.
299
+     */
300
+    ERR_LEAVE_CHANNEL_REJECTED = 18,
301
+    /** 19: Resources are occupied and cannot be reused.
302
+     */
303
+    ERR_ALREADY_IN_USE = 19,
304
+    /** 20: The SDK gives up the request due to too many requests.
305
+     */
306
+    ERR_ABORTED = 20,
307
+    /** 21: In Windows, specific firewall settings can cause the SDK to fail to initialize and crash.
308
+     */
309
+    ERR_INIT_NET_ENGINE = 21,
310
+    /** 22: The application uses too much of the system resources and the SDK fails to allocate the resources.
311
+     */
312
+    ERR_RESOURCE_LIMITED = 22,
313
+    /** 101: The specified App ID is invalid. Please try to rejoin the channel with a valid App ID.
314
+     */
315
+    ERR_INVALID_APP_ID = 101,
316
+    /** 102: The specified channel name is invalid. Please try to rejoin the channel with a valid channel name.
317
+     */
318
+    ERR_INVALID_CHANNEL_NAME = 102,
319
+    /** **DEPRECATED** 109: Deprecated as of v2.4.1. Use CONNECTION_CHANGED_TOKEN_EXPIRED(9) in the \ref agora::rtc::IRtcEngineEventHandler::onConnectionStateChanged "onConnectionStateChanged" callback instead.
320
+     
321
+     The token expired due to one of the following reasons:
322
+     
323
+     - Authorized Timestamp expired: The timestamp is represented by the number of seconds elapsed since 1/1/1970. The user can use the Token to access the Agora service within five minutes after the Token is generated. If the user does not access the Agora service after five minutes, this Token is no longer valid.
324
+     - Call Expiration Timestamp expired: The timestamp is the exact time when a user can no longer use the Agora service (for example, when a user is forced to leave an ongoing call). When a value is set for the Call Expiration Timestamp, it does not mean that the token will expire, but that the user will be banned from the channel.
325
+     */
326
+    ERR_TOKEN_EXPIRED = 109,
327
+    /** **DEPRECATED** 110: Deprecated as of v2.4.1. Use CONNECTION_CHANGED_INVALID_TOKEN(8) in the \ref agora::rtc::IRtcEngineEventHandler::onConnectionStateChanged "onConnectionStateChanged" callback instead.
328
+     
329
+     The token is invalid due to one of the following reasons:
330
+     
331
+     - The App Certificate for the project is enabled in Dashboard, but the user is still using the App ID. Once the App Certificate is enabled, the user must use a token.
332
+     - The uid is mandatory, and users must set the same uid as the one set in the \ref agora::rtc::IRtcEngine::joinChannel "joinChannel" method.
333
+     */
334
+    ERR_INVALID_TOKEN = 110,
335
+    /** 111: The internet connection is interrupted. This applies to the Agora Web SDK only.
336
+     */
337
+    ERR_CONNECTION_INTERRUPTED = 111, // only used in web sdk
338
+    /** 112: The internet connection is lost. This applies to the Agora Web SDK only.
339
+     */
340
+    ERR_CONNECTION_LOST = 112, // only used in web sdk
341
+    /** 113: The user is not in the channel when calling the \ref agora::rtc::IRtcEngine::sendStreamMessage "sendStreamMessage" or \ref agora::rtc::IRtcEngine::getUserInfoByUserAccount "getUserInfoByUserAccount" method.
342
+     */
343
+    ERR_NOT_IN_CHANNEL = 113,
344
+    /** 114: The size of the sent data is over 1024 bytes when the user calls the \ref agora::rtc::IRtcEngine::sendStreamMessage "sendStreamMessage" method.
345
+     */
346
+    ERR_SIZE_TOO_LARGE = 114,
347
+    /** 115: The bitrate of the sent data exceeds the limit of 6 Kbps when the user calls the \ref agora::rtc::IRtcEngine::sendStreamMessage "sendStreamMessage" method.
348
+     */
349
+    ERR_BITRATE_LIMIT = 115,
350
+    /** 116: Too many data streams (over 5 streams) are created when the user calls the \ref agora::rtc::IRtcEngine::createDataStream "createDataStream" method.
351
+     */
352
+    ERR_TOO_MANY_DATA_STREAMS = 116,
353
+    /** 117: The data stream transmission timed out.
354
+     */
355
+    ERR_STREAM_MESSAGE_TIMEOUT = 117,
356
+    /** 119: Switching roles fail. Please try to rejoin the channel.
357
+     */
358
+    ERR_SET_CLIENT_ROLE_NOT_AUTHORIZED = 119,
359
+    /** 120: Decryption fails. The user may have used a different encryption password to join the channel. Check your settings or try rejoining the channel.
360
+     */
361
+    ERR_DECRYPTION_FAILED = 120,
362
+    /** 123: The client is banned by the server.
363
+     */
364
+    ERR_CLIENT_IS_BANNED_BY_SERVER = 123,
365
+    /** 124: Incorrect watermark file parameter.
366
+     */
367
+    ERR_WATERMARK_PARAM = 124,
368
+    /** 125: Incorrect watermark file path.
369
+     */
370
+    ERR_WATERMARK_PATH = 125,
371
+    /** 126: Incorrect watermark file format.
372
+     */
373
+    ERR_WATERMARK_PNG = 126,
374
+    /** 127: Incorrect watermark file information.
375
+     */
376
+    ERR_WATERMARKR_INFO = 127,
377
+    /** 128: Incorrect watermark file data format.
378
+     */
379
+    ERR_WATERMARK_ARGB = 128,
380
+    /** 129: An error occurs in reading the watermark file.
381
+     */
382
+    ERR_WATERMARK_READ = 129,
383
+    /** 130: Encryption is enabled when the user calls the \ref agora::rtc::IRtcEngine::addPublishStreamUrl "addPublishStreamUrl" method (CDN live streaming does not support encrypted streams).
384
+     */
385
+    ERR_ENCRYPTED_STREAM_NOT_ALLOWED_PUBLISH = 130,
386
+    /** 134: The user account is invalid. */
387
+    ERR_INVALID_USER_ACCOUNT = 134,
388
+
389
+    /** 151: CDN related errors. Remove the original URL address and add a new one by calling the \ref agora::rtc::IRtcEngine::removePublishStreamUrl "removePublishStreamUrl" and \ref agora::rtc::IRtcEngine::addPublishStreamUrl "addPublishStreamUrl" methods.
390
+     */
391
+    ERR_PUBLISH_STREAM_CDN_ERROR = 151,
392
+    /** 152: The host publishes more than 10 URLs. Delete the unnecessary URLs before adding new ones.
393
+     */
394
+    ERR_PUBLISH_STREAM_NUM_REACH_LIMIT = 152,
395
+    /** 153: The host manipulates other hosts' URLs. Check your app logic.
396
+     */
397
+    ERR_PUBLISH_STREAM_NOT_AUTHORIZED = 153,
398
+    /** 154: An error occurs in Agora's streaming server. Call the addPublishStreamUrl method to publish the streaming again.
399
+     */
400
+    ERR_PUBLISH_STREAM_INTERNAL_SERVER_ERROR = 154,
401
+    /** 155: The server fails to find the stream.
402
+     */
403
+    ERR_PUBLISH_STREAM_NOT_FOUND = 155,
404
+    /** 156: The format of the RTMP stream URL is not supported. Check whether the URL format is correct.
405
+     */
406
+    ERR_PUBLISH_STREAM_FORMAT_NOT_SUPPORTED = 156,
407
+
408
+    //signaling: 400~600
409
+    /**
410
+    */
411
+    ERR_LOGOUT_OTHER = 400,  //
412
+    /** 401: The user logged out.
413
+     */
414
+    ERR_LOGOUT_USER = 401,  // logout by user
415
+    /** 402: Network failure.
416
+     */
417
+    ERR_LOGOUT_NET = 402,  // network failure
418
+    /** 403: Logged in another device.
419
+     */
420
+    ERR_LOGOUT_KICKED = 403,  // login in other device
421
+    /**
422
+     */
423
+    ERR_LOGOUT_PACKET = 404,  //
424
+    /** 405: The token expired.
425
+     */
426
+    ERR_LOGOUT_TOKEN_EXPIRED = 405,  // token expired
427
+    /**
428
+     */
429
+    ERR_LOGOUT_OLDVERSION = 406,  //
430
+    /**
431
+     */
432
+    ERR_LOGOUT_TOKEN_WRONG = 407,
433
+    /**
434
+    */
435
+    ERR_LOGOUT_ALREADY_LOGOUT = 408,
436
+    /**
437
+     */
438
+    ERR_LOGIN_OTHER = 420,
439
+    /**
440
+    */
441
+    ERR_LOGIN_NET = 421,
442
+    /**
443
+     */
444
+    ERR_LOGIN_FAILED = 422,
445
+    /**
446
+     */
447
+    ERR_LOGIN_CANCELED = 423,
448
+    /**
449
+     */
450
+    ERR_LOGIN_TOKEN_EXPIRED = 424,
451
+    /**
452
+     */
453
+    ERR_LOGIN_OLD_VERSION = 425,
454
+    /**
455
+     */
456
+    ERR_LOGIN_TOKEN_WRONG = 426,
457
+    /**
458
+     */
459
+    ERR_LOGIN_TOKEN_KICKED = 427,
460
+    /**
461
+     */
462
+    ERR_LOGIN_ALREADY_LOGIN = 428,
463
+    /**
464
+    */
465
+    ERR_JOIN_CHANNEL_OTHER = 440,
466
+    /**
467
+     */
468
+    ERR_SEND_MESSAGE_OTHER = 440,
469
+    /**
470
+     */
471
+    ERR_SEND_MESSAGE_TIMEOUT = 441,
472
+    /**
473
+     */
474
+    ERR_QUERY_USERNUM_OTHER = 450,
475
+    /**
476
+     */
477
+    ERR_QUERY_USERNUM_TIMEOUT = 451,
478
+    /**
479
+     */
480
+    ERR_QUERY_USERNUM_BYUSER = 452,
481
+    /**
482
+     */
483
+    ERR_LEAVE_CHANNEL_OTHER = 460,
484
+    /**
485
+     */
486
+    ERR_LEAVE_CHANNEL_KICKED = 461,
487
+    /**
488
+     */
489
+    ERR_LEAVE_CHANNEL_BYUSER = 462,
490
+    /**
491
+     */
492
+    ERR_LEAVE_CHANNEL_LOGOUT = 463,
493
+    /**
494
+     */
495
+    ERR_LEAVE_CHANNEL_DISCONNECTED = 464,
496
+    /**
497
+     */
498
+    ERR_INVITE_OTHER = 470,
499
+    /**
500
+     */
501
+    ERR_INVITE_REINVITE = 471,
502
+    /**
503
+     */
504
+    ERR_INVITE_NET = 472,
505
+    /**
506
+     */
507
+    ERR_INVITE_PEER_OFFLINE = 473,
508
+    ERR_INVITE_TIMEOUT = 474,
509
+    ERR_INVITE_CANT_RECV = 475,
510
+
511
+
512
+    //1001~2000
513
+    /** 1001: Fails to load the media engine.
514
+     */
515
+    ERR_LOAD_MEDIA_ENGINE = 1001,
516
+    /** 1002: Fails to start the call after enabling the media engine.
517
+     */
518
+    ERR_START_CALL = 1002,
519
+    /** **DEPRECATED** 1003: Fails to start the camera.
520
+     
521
+    Deprecated as of v2.4.1. Use LOCAL_VIDEO_STREAM_ERROR_CAPTURE_FAILURE(4) in the \ref agora::rtc::IRtcEngineEventHandler::onConnectionStateChanged "onConnectionStateChanged" callback instead.
522
+     */
523
+    ERR_START_CAMERA = 1003,
524
+    /** 1004: Fails to start the video rendering module.
525
+     */
526
+    ERR_START_VIDEO_RENDER = 1004,
527
+    /** 1005: A general error occurs in the Audio Device Module (no specified reason). Check if the audio device is used by another application, or try rejoining the channel.
528
+     */
529
+    ERR_ADM_GENERAL_ERROR = 1005,
530
+    /** 1006: Audio Device Module: An error occurs in using the Java resources.
531
+     */
532
+    ERR_ADM_JAVA_RESOURCE = 1006,
533
+    /** 1007: Audio Device Module: An error occurs in setting the sampling frequency.
534
+     */
535
+    ERR_ADM_SAMPLE_RATE = 1007,
536
+    /** 1008: Audio Device Module: An error occurs in initializing the playback device.
537
+     */
538
+    ERR_ADM_INIT_PLAYOUT = 1008,
539
+    /** 1009: Audio Device Module: An error occurs in starting the playback device.
540
+     */
541
+    ERR_ADM_START_PLAYOUT = 1009,
542
+    /** 1010: Audio Device Module: An error occurs in stopping the playback device.
543
+     */
544
+    ERR_ADM_STOP_PLAYOUT = 1010,
545
+    /** 1011: Audio Device Module: An error occurs in initializing the recording device.
546
+     */
547
+    ERR_ADM_INIT_RECORDING = 1011,
548
+    /** 1012: Audio Device Module: An error occurs in starting the recording device.
549
+     */
550
+    ERR_ADM_START_RECORDING = 1012,
551
+    /** 1013: Audio Device Module: An error occurs in stopping the recording device.
552
+     */
553
+    ERR_ADM_STOP_RECORDING = 1013,
554
+    /** 1015: Audio Device Module: A playback error occurs. Check your playback device and try rejoining the channel.
555
+     */
556
+    ERR_ADM_RUNTIME_PLAYOUT_ERROR = 1015,
557
+    /** 1017: Audio Device Module: A recording error occurs.
558
+     */
559
+    ERR_ADM_RUNTIME_RECORDING_ERROR = 1017,
560
+    /** 1018: Audio Device Module: Fails to record.
561
+     */
562
+    ERR_ADM_RECORD_AUDIO_FAILED = 1018,
563
+    /** 1022: Audio Device Module: An error occurs in initializing the 
564
+     * loopback device.
565
+     */
566
+    ERR_ADM_INIT_LOOPBACK = 1022,
567
+    /** 1023: Audio Device Module: An error occurs in starting the loopback 
568
+     * device.
569
+     */
570
+    ERR_ADM_START_LOOPBACK = 1023,
571
+    /** 1027: Audio Device Module: No recording permission exists. Check if the
572
+     *  recording permission is granted.
573
+     */
574
+    ERR_ADM_NO_PERMISSION = 1027,
575
+    /** 1033: Audio device module: The device is occupied. 
576
+    */
577
+    ERR_ADM_RECORD_AUDIO_IS_ACTIVE = 1033,
578
+    /** 1101: Audio device module: A fatal exception occurs.
579
+    */
580
+    ERR_ADM_ANDROID_JNI_JAVA_RESOURCE = 1101,
581
+    /** 1108: Audio device module: The recording frequency is lower than 50. 
582
+     * 0 indicates that the recording is not yet started. We recommend 
583
+     * checking your recording permission.
584
+    */
585
+    ERR_ADM_ANDROID_JNI_NO_RECORD_FREQUENCY = 1108,
586
+    /** 1109: The playback frequency is lower than 50. 0 indicates that the 
587
+     * playback is not yet started. We recommend checking if you have created 
588
+     * too many AudioTrack instances. 
589
+    */
590
+    ERR_ADM_ANDROID_JNI_NO_PLAYBACK_FREQUENCY = 1109,
591
+    /** 1111: Audio device module: AudioRecord fails to start up. A ROM system 
592
+     * error occurs. We recommend the following options to debug: 
593
+     * - Restart your App.
594
+     * - Restart your cellphone. 
595
+     * - Check your recording permission.
596
+     */
597
+    ERR_ADM_ANDROID_JNI_JAVA_START_RECORD = 1111,
598
+    /** 1112: Audio device module: AudioTrack fails to start up. A ROM system 
599
+     * error occurs. We recommend the following options to debug: 
600
+     * - Restart your App.
601
+     * - Restart your cellphone. 
602
+     * - Check your playback permission.
603
+    */
604
+    ERR_ADM_ANDROID_JNI_JAVA_START_PLAYBACK = 1112,
605
+    /** 1115: Audio device module: AudioRecord returns error. The SDK will 
606
+     * automatically restart AudioRecord. */
607
+    ERR_ADM_ANDROID_JNI_JAVA_RECORD_ERROR = 1115,
608
+    /** **DEPRECATED** */
609
+    ERR_ADM_ANDROID_OPENSL_CREATE_ENGINE = 1151,
610
+    /** **DEPRECATED** */
611
+    ERR_ADM_ANDROID_OPENSL_CREATE_AUDIO_RECORDER = 1153,
612
+    /** **DEPRECATED** */
613
+    ERR_ADM_ANDROID_OPENSL_START_RECORDER_THREAD = 1156,
614
+    /** **DEPRECATED** */
615
+    ERR_ADM_ANDROID_OPENSL_CREATE_AUDIO_PLAYER = 1157,
616
+    /** **DEPRECATED** */
617
+    ERR_ADM_ANDROID_OPENSL_START_PLAYER_THREAD = 1160,
618
+    /** 1201: Audio device module: The current device does not support audio 
619
+     * input, possibly because you have mistakenly configured the audio session
620
+     *  category, or because some other app is occupying the input device. We 
621
+     * recommend terminating all background apps and re-joining the channel. */
622
+    ERR_ADM_IOS_INPUT_NOT_AVAILABLE = 1201,
623
+    /** 1206: Audio device module: Cannot activate the Audio Session.*/
624
+    ERR_ADM_IOS_ACTIVATE_SESSION_FAIL = 1206,
625
+    /** 1210: Audio device module: Fails to initialize the audio device, 
626
+     * normally because the audio device parameters are wrongly set.*/
627
+    ERR_ADM_IOS_VPIO_INIT_FAIL = 1210,
628
+    /** 1213: Audio device module: Fails to re-initialize the audio device, 
629
+     * normally because the audio device parameters are wrongly set.*/
630
+    ERR_ADM_IOS_VPIO_REINIT_FAIL = 1213,
631
+    /** 1214: Fails to re-start up the Audio Unit, possibly because the audio 
632
+     * session category is not compatible with the settings of the Audio Unit.
633
+    */
634
+    ERR_ADM_IOS_VPIO_RESTART_FAIL = 1214,
635
+    ERR_ADM_IOS_SET_RENDER_CALLBACK_FAIL = 1219,
636
+    /** **DEPRECATED** */
637
+    ERR_ADM_IOS_SESSION_SAMPLERATR_ZERO = 1221,
638
+    /** 1301: Audio device module: An audio driver abnomality or a 
639
+     * compatibility issue occurs. Solutions: Disable and restart the audio 
640
+     * device, or reboot the system.*/
641
+    ERR_ADM_WIN_CORE_INIT = 1301,
642
+    /** 1303: Audio device module: A recording driver abnomality or a 
643
+     * compatibility issue occurs. Solutions: Disable and restart the audio 
644
+     * device, or reboot the system. */
645
+    ERR_ADM_WIN_CORE_INIT_RECORDING = 1303,
646
+    /** 1306: Audio device module: A playout driver abnomality or a 
647
+     * compatibility issue occurs. Solutions: Disable and restart the audio 
648
+     * device, or reboot the system. */
649
+    ERR_ADM_WIN_CORE_INIT_PLAYOUT = 1306,
650
+    /** 1307: Audio device module: No audio device is available. Solutions: 
651
+     * Plug in a proper audio device. */
652
+    ERR_ADM_WIN_CORE_INIT_PLAYOUT_NULL = 1307,
653
+    /** 1309: Audio device module: An audio driver abnomality or a 
654
+     * compatibility issue occurs. Solutions: Disable and restart the audio 
655
+     * device, or reboot the system. */
656
+    ERR_ADM_WIN_CORE_START_RECORDING = 1309,
657
+    /** 1311: Audio device module: Insufficient system memory or poor device 
658
+     * performance. Solutions: Reboot the system or replace the device.
659
+    */
660
+    ERR_ADM_WIN_CORE_CREATE_REC_THREAD = 1311,
661
+    /** 1314: Audio device module: An audio driver abnormality occurs. 
662
+     * Solutions:
663
+     * - Disable and then re-enable the audio device.
664
+     * - Reboot the system.
665
+     * - Upgrade your audio card driver.*/
666
+    ERR_ADM_WIN_CORE_CAPTURE_NOT_STARTUP = 1314,
667
+    /** 1319: Audio device module: Insufficient system memory or poor device 
668
+     * performance. Solutions: Reboot the system or replace the device. */
669
+    ERR_ADM_WIN_CORE_CREATE_RENDER_THREAD = 1319,
670
+    /** 1320: Audio device module: An audio driver abnormality occurs. 
671
+     * Solutions:
672
+     * - Disable and then re-enable the audio device.
673
+     * - Reboot the system.
674
+     * - Replace the device. */
675
+    ERR_ADM_WIN_CORE_RENDER_NOT_STARTUP = 1320,
676
+    /** 1322: Audio device module: No audio sampling device is available. 
677
+     * Solutions: Plug in a proper recording device. */
678
+    ERR_ADM_WIN_CORE_NO_RECORDING_DEVICE = 1322,
679
+    /** 1323: Audio device module: No audio playout device is available. 
680
+     * Solutions: Plug in a proper playback device.*/
681
+    ERR_ADM_WIN_CORE_NO_PLAYOUT_DEVICE = 1323,
682
+    /** 1351: Audio device module: An audio driver abnormality or a 
683
+     * compatibility issue occurs. Solutions:
684
+     * - Disable and then re-enable the audio device.
685
+     * - Reboot the system.
686
+     * - Upgrade your audio card driver. */
687
+    ERR_ADM_WIN_WAVE_INIT = 1351,
688
+    /** 1353: Audio device module: An audio driver abnormality occurs. 
689
+     * Solutions:
690
+     * - Disable and then re-enable the audio device.
691
+     * - Reboot the system.
692
+     * - Upgrade your audio card driver. */
693
+    ERR_ADM_WIN_WAVE_INIT_RECORDING = 1353,
694
+    /** 1354: Audio device module: An audio driver abnormality occurs. 
695
+     * Solutions:
696
+     * - Disable and then re-enable the audio device.
697
+     * - Reboot the system.
698
+     * - Upgrade your audio card driver. */
699
+    ERR_ADM_WIN_WAVE_INIT_MICROPHONE = 1354,
700
+    /** 1355: Audio device module: An audio driver abnormality occurs. 
701
+     * Solutions:
702
+     * - Disable and then re-enable the audio device.
703
+     * - Reboot the system.
704
+     * - Upgrade your audio card driver. */
705
+    ERR_ADM_WIN_WAVE_INIT_PLAYOUT = 1355,
706
+    /** 1356: Audio device module: An audio driver abnormality occurs. 
707
+     * Solutions:
708
+     * - Disable and then re-enable the audio device.
709
+     * - Reboot the system.
710
+     * - Upgrade your audio card driver. */
711
+    ERR_ADM_WIN_WAVE_INIT_SPEAKER = 1356,
712
+    /** 1357: Audio device module: An audio driver abnormality occurs. 
713
+     * Solutions:
714
+     * - Disable and then re-enable the audio device.
715
+     * - Reboot the system.
716
+     * - Upgrade your audio card driver. */
717
+    ERR_ADM_WIN_WAVE_START_RECORDING = 1357,
718
+    /** 1358: Audio device module: An audio driver abnormality occurs. 
719
+     * Solutions:
720
+     * - Disable and then re-enable the audio device.
721
+     * - Reboot the system.
722
+     * - Upgrade your audio card driver.*/
723
+    ERR_ADM_WIN_WAVE_START_PLAYOUT = 1358,
724
+    /** 1359: Audio Device Module: No recording device exists.
725
+     */
726
+    ERR_ADM_NO_RECORDING_DEVICE = 1359,
727
+    /** 1360: Audio Device Module: No playback device exists.
728
+     */
729
+    ERR_ADM_NO_PLAYOUT_DEVICE = 1360,
730
+
731
+    // VDM error code starts from 1500
732
+    /** 1501: Video Device Module: The camera is unauthorized.
733
+     */
734
+    ERR_VDM_CAMERA_NOT_AUTHORIZED = 1501,
735
+
736
+	// VDM error code starts from 1500
737
+	/** **DEPRECATED** 1502: Video Device Module: The camera in use.
738
+     
739
+     Deprecated as of v2.4.1. Use LOCAL_VIDEO_STREAM_ERROR_DEVICE_BUSY(3) in the \ref agora::rtc::IRtcEngineEventHandler::onConnectionStateChanged "onConnectionStateChanged" callback instead.
740
+	 */
741
+	ERR_VDM_WIN_DEVICE_IN_USE = 1502,
742
+
743
+    // VCM error code starts from 1600
744
+    /** 1600: Video Device Module: An unknown error occurs.
745
+     */
746
+    ERR_VCM_UNKNOWN_ERROR = 1600,
747
+    /** 1601: Video Device Module: An error occurs in initializing the video encoder.
748
+    */
749
+    ERR_VCM_ENCODER_INIT_ERROR = 1601,
750
+    /** 1602: Video Device Module: An error occurs in encoding.
751
+     */
752
+    ERR_VCM_ENCODER_ENCODE_ERROR = 1602,
753
+    /** 1603: Video Device Module: An error occurs in setting the video encoder.
754
+     */
755
+    ERR_VCM_ENCODER_SET_ERROR = 1603,
756
+};
757
+
758
+    /** Output log filter level. */
759
+enum LOG_FILTER_TYPE
760
+{
761
+/** 0: Do not output any log information. */
762
+    LOG_FILTER_OFF = 0,
763
+     /** 0x080f: Output all log information.
764
+      Set your log filter as debug if you want to get the most complete log file.      */
765
+    LOG_FILTER_DEBUG = 0x080f,
766
+     /** 0x000f: Output CRITICAL, ERROR, WARNING, and INFO level log information.
767
+      We recommend setting your log filter as this level.
768
+      */
769
+    LOG_FILTER_INFO = 0x000f,
770
+     /** 0x000e: Outputs CRITICAL, ERROR, and WARNING level log information.
771
+      */
772
+    LOG_FILTER_WARN = 0x000e,
773
+     /** 0x000c: Outputs CRITICAL and ERROR level log information. */
774
+    LOG_FILTER_ERROR = 0x000c,
775
+     /** 0x0008: Outputs CRITICAL level log information. */
776
+    LOG_FILTER_CRITICAL = 0x0008,
777
+    LOG_FILTER_MASK = 0x80f,
778
+};
779
+} // namespace agora
780
+
781
+#endif

+ 215
- 0
android/src/main/cpp/include/IAgoraMediaEngine.h View File

@@ -0,0 +1,215 @@
1
+#ifndef AGORA_MEDIA_ENGINE_H
2
+#define AGORA_MEDIA_ENGINE_H
3
+#if defined _WIN32 || defined __CYGWIN__
4
+typedef __int64 int64_t;
5
+typedef unsigned __int64 uint64_t;
6
+#else
7
+#include <stdint.h>
8
+#endif
9
+
10
+namespace agora
11
+{
12
+namespace media
13
+{
14
+
15
+enum MEDIA_SOURCE_TYPE {
16
+    AUDIO_PLAYOUT_SOURCE = 0,
17
+    AUDIO_RECORDING_SOURCE = 1,
18
+};
19
+
20
+class IAudioFrameObserver
21
+{
22
+public:
23
+  enum AUDIO_FRAME_TYPE {
24
+    FRAME_TYPE_PCM16 = 0,  //PCM 16bit little endian
25
+  };
26
+  struct AudioFrame {
27
+    AUDIO_FRAME_TYPE type;
28
+    int samples;  //number of samples in this frame
29
+    int bytesPerSample;  //number of bytes per sample: 2 for PCM16
30
+    int channels;  //number of channels (data are interleaved if stereo)
31
+    int samplesPerSec;  //sampling rate
32
+    void* buffer;  //data buffer
33
+    int64_t renderTimeMs;
34
+    int avsync_type;
35
+  };
36
+public:
37
+  virtual bool onRecordAudioFrame(AudioFrame& audioFrame) = 0;
38
+  virtual bool onPlaybackAudioFrame(AudioFrame& audioFrame) = 0;
39
+  virtual bool onMixedAudioFrame(AudioFrame& audioFrame) = 0;
40
+  virtual bool onPlaybackAudioFrameBeforeMixing(unsigned int uid, AudioFrame& audioFrame) = 0;
41
+};
42
+
43
+class IVideoFrameObserver
44
+{
45
+public:
46
+  enum VIDEO_FRAME_TYPE {
47
+    FRAME_TYPE_YUV420 = 0,  //YUV 420 format
48
+  };
49
+  struct VideoFrame {
50
+    VIDEO_FRAME_TYPE type;
51
+    int width;  //width of video frame
52
+    int height;  //height of video frame
53
+    int yStride;  //stride of Y data buffer
54
+    int uStride;  //stride of U data buffer
55
+    int vStride;  //stride of V data buffer
56
+    void* yBuffer;  //Y data buffer
57
+    void* uBuffer;  //U data buffer
58
+    void* vBuffer;  //V data buffer
59
+    int rotation; // rotation of this frame (0, 90, 180, 270)
60
+    int64_t renderTimeMs;
61
+    int avsync_type;
62
+  };
63
+public:
64
+  virtual bool onCaptureVideoFrame(VideoFrame& videoFrame) = 0;
65
+  virtual bool onRenderVideoFrame(unsigned int uid, VideoFrame& videoFrame) = 0;
66
+};
67
+
68
+class IVideoFrame
69
+{
70
+public:
71
+  enum PLANE_TYPE {
72
+    Y_PLANE = 0,
73
+    U_PLANE = 1,
74
+    V_PLANE = 2,
75
+    NUM_OF_PLANES = 3
76
+  };
77
+  enum VIDEO_TYPE {
78
+    VIDEO_TYPE_UNKNOWN = 0,
79
+    VIDEO_TYPE_I420 = 1,
80
+    VIDEO_TYPE_IYUV = 2,
81
+    VIDEO_TYPE_RGB24 = 3,
82
+    VIDEO_TYPE_ABGR = 4,
83
+    VIDEO_TYPE_ARGB = 5,
84
+    VIDEO_TYPE_ARGB4444 = 6,
85
+    VIDEO_TYPE_RGB565 = 7,
86
+    VIDEO_TYPE_ARGB1555 = 8,
87
+    VIDEO_TYPE_YUY2 = 9,
88
+    VIDEO_TYPE_YV12 = 10,
89
+    VIDEO_TYPE_UYVY = 11,
90
+    VIDEO_TYPE_MJPG = 12,
91
+    VIDEO_TYPE_NV21 = 13,
92
+    VIDEO_TYPE_NV12 = 14,
93
+    VIDEO_TYPE_BGRA = 15,
94
+    VIDEO_TYPE_RGBA = 16,
95
+  };
96
+  virtual void release() = 0;
97
+  virtual const unsigned char* buffer(PLANE_TYPE type) const = 0;
98
+
99
+  // Copy frame: If required size is bigger than allocated one, new buffers of
100
+  // adequate size will be allocated.
101
+  // Return value: 0 on success ,-1 on error.
102
+  virtual int copyFrame(IVideoFrame** dest_frame) const = 0;
103
+
104
+  // Convert frame
105
+  // Input:
106
+  //   - src_frame        : Reference to a source frame.
107
+  //   - dst_video_type   : Type of output video.
108
+  //   - dst_sample_size  : Required only for the parsing of MJPG.
109
+  //   - dst_frame        : Pointer to a destination frame.
110
+  // Return value: 0 if OK, < 0 otherwise.
111
+  // It is assumed that source and destination have equal height.
112
+  virtual int convertFrame(VIDEO_TYPE dst_video_type, int dst_sample_size, unsigned char* dst_frame) const = 0;
113
+
114
+  // Get allocated size per plane.
115
+  virtual int allocated_size(PLANE_TYPE type) const = 0;
116
+
117
+  // Get allocated stride per plane.
118
+  virtual int stride(PLANE_TYPE type) const = 0;
119
+
120
+  // Get frame width.
121
+  virtual int width() const = 0;
122
+
123
+  // Get frame height.
124
+  virtual int height() const = 0;
125
+
126
+  // Get frame timestamp (90kHz).
127
+  virtual unsigned int timestamp() const = 0;
128
+
129
+  // Get render time in milliseconds.
130
+  virtual int64_t render_time_ms() const = 0;
131
+
132
+  // Return true if underlying plane buffers are of zero size, false if not.
133
+  virtual bool IsZeroSize() const = 0;
134
+};
135
+
136
+class IExternalVideoRenderCallback
137
+{
138
+public:
139
+  virtual void onViewSizeChanged(int width, int height) = 0;
140
+  virtual void onViewDestroyed() = 0;
141
+};
142
+
143
+struct ExternalVideoRenerContext
144
+{
145
+  IExternalVideoRenderCallback* renderCallback;
146
+  void* view;
147
+  int renderMode;
148
+  int zOrder;
149
+  float left;
150
+  float top;
151
+  float right;
152
+  float bottom;
153
+};
154
+
155
+class IExternalVideoRender
156
+{
157
+public:
158
+  virtual void release() = 0;
159
+  virtual int initialize() = 0;
160
+  virtual int deliverFrame(const IVideoFrame& videoFrame, int rotation, bool mirrored) = 0;
161
+};
162
+
163
+class IExternalVideoRenderFactory
164
+{
165
+public:
166
+  virtual IExternalVideoRender* createRenderInstance(const ExternalVideoRenerContext& context) = 0;
167
+};
168
+
169
+struct ExternalVideoFrame
170
+{
171
+  enum VIDEO_BUFFER_TYPE
172
+  {
173
+    VIDEO_BUFFER_RAW_DATA = 1,
174
+  };
175
+
176
+  enum VIDEO_PIXEL_FORMAT
177
+  {
178
+    VIDEO_PIXEL_UNKNOWN = 0,
179
+    VIDEO_PIXEL_I420 = 1,
180
+    VIDEO_PIXEL_BGRA = 2,
181
+    VIDEO_PIXEL_NV12 = 8,
182
+  };
183
+
184
+  VIDEO_BUFFER_TYPE type;
185
+  VIDEO_PIXEL_FORMAT format;
186
+  void* buffer;
187
+  int stride;
188
+  int height;
189
+  int cropLeft;
190
+  int cropTop;
191
+  int cropRight;
192
+  int cropBottom;
193
+  int rotation;
194
+  long long timestamp;
195
+};
196
+
197
+class IMediaEngine {
198
+public:
199
+  virtual void release() = 0;
200
+  virtual int registerAudioFrameObserver(IAudioFrameObserver* observer) = 0;
201
+  virtual int registerVideoFrameObserver(IVideoFrameObserver* observer) = 0;
202
+  virtual int registerVideoRenderFactory(IExternalVideoRenderFactory* factory) = 0;
203
+  virtual int pushAudioFrame(MEDIA_SOURCE_TYPE type, IAudioFrameObserver::AudioFrame *frame, bool wrap) = 0;
204
+  virtual int pushAudioFrame(IAudioFrameObserver::AudioFrame *frame) = 0;
205
+  virtual int pullAudioFrame(IAudioFrameObserver::AudioFrame *frame) = 0;
206
+
207
+  virtual int setExternalVideoSource(bool enable, bool useTexture) = 0;
208
+  virtual int pushVideoFrame(ExternalVideoFrame *frame) = 0;
209
+};
210
+
211
+} //media
212
+
213
+} //agora
214
+
215
+#endif //AGORA_MEDIA_ENGINE_H

+ 7473
- 0
android/src/main/cpp/include/IAgoraRtcEngine.h
File diff suppressed because it is too large
View File


+ 76
- 0
android/src/main/cpp/include/IAgoraService.h View File

@@ -0,0 +1,76 @@
1
+//  Agora SDK
2
+//
3
+//  Copyright (c) 2019 Agora.io. All rights reserved.
4
+//
5
+
6
+#ifndef AGORA_SERVICE_H
7
+#define AGORA_SERVICE_H
8
+#include "AgoraBase.h"
9
+
10
+namespace agora {
11
+    namespace rtc {
12
+        class IRtcEngine;
13
+    }
14
+    namespace rtm {
15
+        class IRtmService;
16
+    }
17
+namespace base {
18
+
19
+struct AgoraServiceContext
20
+{
21
+};
22
+
23
+
24
+class IAgoraService
25
+{
26
+protected:
27
+    virtual ~IAgoraService(){}
28
+public:
29
+    virtual void release() = 0;
30
+
31
+	/** Initializes the engine.
32
+     
33
+    @param context RtcEngine context.
34
+    @return
35
+     - 0: Success.
36
+     - < 0: Failure.
37
+    */
38
+    virtual int initialize(const AgoraServiceContext& context) = 0;
39
+
40
+    /** Retrieves the SDK version number.
41
+    * @param build Build number.
42
+    * @return The current SDK version in the string format. For example, 2.4.0
43
+    */
44
+    virtual const char* getVersion(int* build) = 0;
45
+
46
+    virtual rtm::IRtmService* createRtmService() = 0;
47
+};
48
+
49
+} //namespace base
50
+} // namespace agora
51
+
52
+/** Gets the SDK version number.
53
+ 
54
+ @param build Build number of the Agora SDK.
55
+ @return
56
+ - 0: Success.
57
+ - < 0: Failure.
58
+*/
59
+AGORA_API const char* AGORA_CALL getAgoraSdkVersion(int* build);
60
+
61
+/**
62
+* Creates the RtcEngine object and returns the pointer.
63
+* @param err Error code
64
+* @return returns Description of the error code
65
+*/
66
+AGORA_API const char* AGORA_CALL getAgoraSdkErrorDescription(int err);
67
+
68
+/**
69
+* Creates the Agora Service object and returns the pointer.
70
+* @return returns Pointer of the Agora Service object
71
+*/
72
+AGORA_API agora::base::IAgoraService* AGORA_CALL createAgoraService();
73
+
74
+AGORA_API int AGORA_CALL setAgoraSdkExternalSymbolLoader(void* (*func)(const char* symname));
75
+
76
+#endif

+ 53
- 0
android/src/main/cpp/include/VMUtil.h View File

@@ -0,0 +1,53 @@
1
+#ifndef __VM_UTIL_H__
2
+#define __VM_UTIL_H__
3
+
4
+#include <jni.h>
5
+#include <stddef.h>
6
+
7
+#include <assert.h>
8
+
9
+#include <pthread.h>
10
+#include <android/log.h>
11
+
12
+#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "AG_EX_AV", __VA_ARGS__)
13
+#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "AG_EX_AV", __VA_ARGS__)
14
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "AG_EX_AV", __VA_ARGS__)
15
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "AG_EX_AV", __VA_ARGS__)
16
+
17
+#define TRUE true
18
+#define FALSE false
19
+
20
+#define CHECK_POINTER(pValue, rValue, ...) 	if (NULL == pValue) {  \
21
+					                           LOGE(__VA_ARGS__); \
22
+					                           return rValue; }
23
+
24
+class AttachThreadScoped
25
+{
26
+public:
27
+    explicit AttachThreadScoped(JavaVM* jvm)
28
+        : attached_(false), jvm_(jvm), env_(nullptr) {
29
+        jint ret_val = jvm->GetEnv(reinterpret_cast<void**>(&env_),
30
+            JNI_VERSION_1_6);
31
+        if (ret_val == JNI_EDETACHED) {
32
+            // Attach the thread to the Java VM.
33
+            ret_val = jvm_->AttachCurrentThread(&env_, nullptr);
34
+            attached_ = ret_val >= 0;
35
+            assert(attached_);
36
+        }
37
+	}
38
+
39
+    ~AttachThreadScoped() {
40
+        if (attached_ && (jvm_->DetachCurrentThread() < 0)) {
41
+            assert(false);
42
+        }
43
+    }
44
+
45
+    JNIEnv* env() { return env_; }
46
+
47
+private:
48
+    bool attached_;
49
+    JavaVM* jvm_;
50
+    JNIEnv* env_;
51
+};
52
+
53
+#endif  // __VM_UTIL_H__

+ 327
- 0
android/src/main/cpp/src/agora_media_pre_processing.cpp View File

@@ -0,0 +1,327 @@
1
+//
2
+// Created by LY on 2019-08-21.
3
+//
4
+
5
+#include <jni.h>
6
+#include <android/log.h>
7
+#include <cstring>
8
+#include "../include/IAgoraMediaEngine.h"
9
+
10
+#include "../include/IAgoraRtcEngine.h"
11
+#include <string.h>
12
+#include "agora_media_pre_processing.h"
13
+#include "../include/VMUtil.h"
14
+
15
+#include <map>
16
+
17
+using namespace std;
18
+
19
+jobject gCallBack = nullptr;
20
+jclass gCallbackClass = nullptr;
21
+jmethodID recordAudioMethodId = nullptr;
22
+jmethodID playbackAudioMethodId = nullptr;
23
+jmethodID playBeforeMixAudioMethodId = nullptr;
24
+jmethodID mixAudioMethodId = nullptr;
25
+jmethodID captureVideoMethodId = nullptr;
26
+jmethodID renderVideoMethodId = nullptr;
27
+void *_javaDirectPlayBufferCapture = nullptr;
28
+void *_javaDirectPlayBufferRecordAudio = nullptr;
29
+void *_javaDirectPlayBufferPlayAudio = nullptr;
30
+void *_javaDirectPlayBufferBeforeMixAudio = nullptr;
31
+void *_javaDirectPlayBufferMixAudio = nullptr;
32
+map<int, void *> decodeBufferMap;
33
+
34
+static JavaVM *gJVM = nullptr;
35
+
36
+class AgoraVideoFrameObserver : public agora::media::IVideoFrameObserver {
37
+
38
+public:
39
+    AgoraVideoFrameObserver() {
40
+
41
+    }
42
+
43
+    ~AgoraVideoFrameObserver() {
44
+
45
+    }
46
+
47
+    void
48
+    getVideoFrame(VideoFrame &videoFrame, _jmethodID *jmethodID, void *_byteBufferObject, unsigned int uid) {
49
+
50
+        if (_byteBufferObject) {
51
+            int width = videoFrame.width;
52
+            int height = videoFrame.height;
53
+            size_t widthAndHeight = (size_t) videoFrame.yStride * height;
54
+            size_t length = widthAndHeight * 3 / 2;
55
+
56
+            AttachThreadScoped ats(gJVM);
57
+            JNIEnv *env = ats.env();
58
+
59
+            memcpy(_byteBufferObject, videoFrame.yBuffer, widthAndHeight);
60
+            memcpy((uint8_t *) _byteBufferObject + widthAndHeight, videoFrame.uBuffer,
61
+                   widthAndHeight / 4);
62
+            memcpy((uint8_t *) _byteBufferObject + widthAndHeight * 5 / 4, videoFrame.vBuffer,
63
+                   widthAndHeight / 4);
64
+
65
+            if (uid == 0) {
66
+                env->CallVoidMethod(gCallBack, jmethodID, videoFrame.type, width, height, length,
67
+                                    videoFrame.yStride, videoFrame.uStride,
68
+                                    videoFrame.vStride, videoFrame.rotation,
69
+                                    videoFrame.renderTimeMs);
70
+            } else {
71
+                env->CallVoidMethod(gCallBack, jmethodID, uid, videoFrame.type, width, height,
72
+                                    length,
73
+                                    videoFrame.yStride, videoFrame.uStride,
74
+                                    videoFrame.vStride, videoFrame.rotation,
75
+                                    videoFrame.renderTimeMs);
76
+            }
77
+        }
78
+
79
+    }
80
+
81
+    void writebackVideoFrame(VideoFrame &videoFrame, void *byteBuffer) {
82
+        if (byteBuffer == nullptr) {
83
+            return;
84
+        }
85
+
86
+        int width = videoFrame.width;
87
+        int height = videoFrame.height;
88
+        size_t widthAndHeight = (size_t) videoFrame.yStride * height;
89
+
90
+        memcpy(videoFrame.yBuffer, byteBuffer, widthAndHeight);
91
+        memcpy(videoFrame.uBuffer, (uint8_t *) byteBuffer + widthAndHeight, widthAndHeight / 4);
92
+        memcpy(videoFrame.vBuffer, (uint8_t *) byteBuffer + widthAndHeight * 5 / 4,
93
+               widthAndHeight / 4);
94
+    }
95
+
96
+public:
97
+    virtual bool onCaptureVideoFrame(VideoFrame &videoFrame) override {
98
+        getVideoFrame(videoFrame, captureVideoMethodId, _javaDirectPlayBufferCapture, 0);
99
+        writebackVideoFrame(videoFrame, _javaDirectPlayBufferCapture);
100
+        return true;
101
+    }
102
+
103
+    virtual bool onRenderVideoFrame(unsigned int uid, VideoFrame &videoFrame) override {
104
+        map<int, void *>::iterator it_find;
105
+        it_find = decodeBufferMap.find(uid);
106
+
107
+        if (it_find != decodeBufferMap.end()) {
108
+            if (it_find->second != nullptr) {
109
+                getVideoFrame(videoFrame, renderVideoMethodId, it_find->second, uid);
110
+                writebackVideoFrame(videoFrame, it_find->second);
111
+            }
112
+        }
113
+
114
+        return true;
115
+    }
116
+
117
+};
118
+
119
+
120
+class AgoraAudioFrameObserver : public agora::media::IAudioFrameObserver {
121
+
122
+public:
123
+    AgoraAudioFrameObserver() {
124
+        gCallBack = nullptr;
125
+    }
126
+
127
+    ~AgoraAudioFrameObserver() {
128
+    }
129
+
130
+    void getAudioFrame(AudioFrame &audioFrame, _jmethodID *jmethodID, void *_byteBufferObject, unsigned int uid) {
131
+        if (_byteBufferObject) {
132
+            AttachThreadScoped ats(gJVM);
133
+            JNIEnv *env = ats.env();
134
+            if (env == nullptr) {
135
+                return;
136
+            }
137
+            int len = audioFrame.samples * audioFrame.bytesPerSample;
138
+            memcpy(_byteBufferObject, audioFrame.buffer, (size_t) len); // * sizeof(int16_t)
139
+
140
+            if (uid == 0) {
141
+                env->CallVoidMethod(gCallBack, jmethodID, audioFrame.type, audioFrame.samples,
142
+                                    audioFrame.bytesPerSample,
143
+                                    audioFrame.channels, audioFrame.samplesPerSec,
144
+                                    audioFrame.renderTimeMs, len);
145
+            } else {
146
+                env->CallVoidMethod(gCallBack, jmethodID, uid, audioFrame.type, audioFrame.samples,
147
+                                    audioFrame.bytesPerSample,
148
+                                    audioFrame.channels, audioFrame.samplesPerSec,
149
+                                    audioFrame.renderTimeMs, len);
150
+            }
151
+        }
152
+
153
+    }
154
+
155
+    void writebackAudioFrame(AudioFrame &audioFrame, void *byteBuffer) {
156
+        if (byteBuffer == nullptr) {
157
+            return;
158
+        }
159
+
160
+        int len = audioFrame.samples * audioFrame.bytesPerSample;
161
+        memcpy(audioFrame.buffer, byteBuffer, (size_t) len);
162
+    }
163
+
164
+public:
165
+    virtual bool onRecordAudioFrame(AudioFrame &audioFrame) override {
166
+        getAudioFrame(audioFrame, recordAudioMethodId, _javaDirectPlayBufferRecordAudio, 0);
167
+        writebackAudioFrame(audioFrame, _javaDirectPlayBufferRecordAudio);
168
+        return true;
169
+    }
170
+
171
+    virtual bool onPlaybackAudioFrame(AudioFrame &audioFrame) override {
172
+        getAudioFrame(audioFrame, playbackAudioMethodId, _javaDirectPlayBufferPlayAudio, 0);
173
+        writebackAudioFrame(audioFrame, _javaDirectPlayBufferPlayAudio);
174
+        return true;
175
+    }
176
+
177
+    virtual bool
178
+    onPlaybackAudioFrameBeforeMixing(unsigned int uid, AudioFrame &audioFrame) override {
179
+        getAudioFrame(audioFrame, playBeforeMixAudioMethodId, _javaDirectPlayBufferBeforeMixAudio, uid);
180
+        writebackAudioFrame(audioFrame, _javaDirectPlayBufferBeforeMixAudio);
181
+        return true;
182
+    }
183
+
184
+    virtual bool onMixedAudioFrame(AudioFrame &audioFrame) override {
185
+        getAudioFrame(audioFrame, mixAudioMethodId, _javaDirectPlayBufferMixAudio, 0);
186
+        writebackAudioFrame(audioFrame, _javaDirectPlayBufferMixAudio);
187
+        return true;
188
+    }
189
+};
190
+
191
+
192
+static AgoraAudioFrameObserver s_audioFrameObserver;
193
+
194
+static AgoraVideoFrameObserver s_videoFrameObserver;
195
+static agora::rtc::IRtcEngine *rtcEngine = nullptr;
196
+
197
+#ifdef __cplusplus
198
+extern "C" {
199
+#endif
200
+
201
+int __attribute__((visibility("default")))
202
+loadAgoraRtcEnginePlugin(agora::rtc::IRtcEngine *engine) {
203
+    __android_log_print(ANDROID_LOG_DEBUG, "agora-raw-data-plugin", "loadAgoraRtcEnginePlugin");
204
+    rtcEngine = engine;
205
+    return 0;
206
+}
207
+
208
+void __attribute__((visibility("default")))
209
+unloadAgoraRtcEnginePlugin(agora::rtc::IRtcEngine *engine) {
210
+    __android_log_print(ANDROID_LOG_DEBUG, "agora-raw-data-plugin", "unloadAgoraRtcEnginePlugin");
211
+
212
+    rtcEngine = nullptr;
213
+}
214
+
215
+JNIEXPORT void JNICALL
216
+Java_com_syan_agora_media_MediaPreProcessing_setCallback(JNIEnv *env, jclass obj,
217
+                                                                jobject callback) {
218
+    if (!rtcEngine) return;
219
+
220
+    env->GetJavaVM(&gJVM);
221
+
222
+    agora::util::AutoPtr<agora::media::IMediaEngine> mediaEngine;
223
+    mediaEngine.queryInterface(rtcEngine, agora::INTERFACE_ID_TYPE::AGORA_IID_MEDIA_ENGINE);
224
+    if (mediaEngine) {
225
+        mediaEngine->registerVideoFrameObserver(&s_videoFrameObserver);
226
+        mediaEngine->registerAudioFrameObserver(&s_audioFrameObserver);
227
+    }
228
+
229
+    if (gCallBack == nullptr) {
230
+        gCallBack = env->NewGlobalRef(callback);
231
+        gCallbackClass = env->GetObjectClass(gCallBack);
232
+
233
+        recordAudioMethodId = env->GetMethodID(gCallbackClass, "onRecordAudioFrame", "(IIIIIJI)V");
234
+        playbackAudioMethodId = env->GetMethodID(gCallbackClass, "onPlaybackAudioFrame",
235
+                                                 "(IIIIIJI)V");
236
+        playBeforeMixAudioMethodId = env->GetMethodID(gCallbackClass,
237
+                                                      "onPlaybackAudioFrameBeforeMixing",
238
+                                                      "(IIIIIIJI)V");
239
+        mixAudioMethodId = env->GetMethodID(gCallbackClass, "onMixedAudioFrame", "(IIIIIJI)V");
240
+
241
+        captureVideoMethodId = env->GetMethodID(gCallbackClass, "onCaptureVideoFrame",
242
+                                                "(IIIIIIIIJ)V");
243
+        renderVideoMethodId = env->GetMethodID(gCallbackClass, "onRenderVideoFrame",
244
+                                               "(IIIIIIIIIJ)V");
245
+
246
+        __android_log_print(ANDROID_LOG_DEBUG, "setCallback", "setCallback done successfully");
247
+    }
248
+}
249
+
250
+JNIEXPORT void JNICALL Java_com_syan_agora_media_MediaPreProcessing_setVideoCaptureByteBuffer
251
+        (JNIEnv *env, jclass obj, jobject bytebuffer) {
252
+    _javaDirectPlayBufferCapture = env->GetDirectBufferAddress(bytebuffer);
253
+}
254
+
255
+JNIEXPORT void JNICALL Java_com_syan_agora_media_MediaPreProcessing_setAudioRecordByteBuffer
256
+        (JNIEnv *env, jclass obj, jobject bytebuffer) {
257
+    _javaDirectPlayBufferRecordAudio = env->GetDirectBufferAddress(bytebuffer);
258
+}
259
+
260
+JNIEXPORT void JNICALL Java_com_syan_agora_media_MediaPreProcessing_setAudioPlayByteBuffer
261
+        (JNIEnv *env, jclass obj, jobject bytebuffer) {
262
+    _javaDirectPlayBufferPlayAudio = env->GetDirectBufferAddress(bytebuffer);
263
+}
264
+
265
+JNIEXPORT void JNICALL
266
+Java_com_syan_agora_media_MediaPreProcessing_setBeforeAudioMixByteBuffer
267
+        (JNIEnv *env, jclass obj, jobject bytebuffer) {
268
+    _javaDirectPlayBufferBeforeMixAudio = env->GetDirectBufferAddress(bytebuffer);
269
+}
270
+
271
+JNIEXPORT void JNICALL Java_com_syan_agora_media_MediaPreProcessing_setAudioMixByteBuffer
272
+        (JNIEnv *env, jclass obj, jobject bytebuffer) {
273
+    _javaDirectPlayBufferMixAudio = env->GetDirectBufferAddress(bytebuffer);
274
+}
275
+
276
+JNIEXPORT void JNICALL
277
+Java_com_syan_agora_media_MediaPreProcessing_setVideoDecodeByteBuffer(JNIEnv *env,
278
+                                                                             jclass type,
279
+                                                                             jint uid,
280
+                                                                             jobject byteBuffer) {
281
+    if (byteBuffer == nullptr) {
282
+        decodeBufferMap.erase(uid);
283
+    } else {
284
+        void *_javaDirectDecodeBuffer = env->GetDirectBufferAddress(byteBuffer);
285
+        decodeBufferMap.insert(make_pair(uid, _javaDirectDecodeBuffer));
286
+        __android_log_print(ANDROID_LOG_DEBUG, "agora-raw-data-plugin",
287
+                            "setVideoDecodeByteBuffer uid: %u, _javaDirectDecodeBuffer: %p",
288
+                            uid, _javaDirectDecodeBuffer);
289
+    }
290
+}
291
+
292
+JNIEXPORT void JNICALL
293
+Java_com_syan_agora_media_MediaPreProcessing_releasePoint(JNIEnv *env, jclass type) {
294
+    agora::util::AutoPtr<agora::media::IMediaEngine> mediaEngine;
295
+    mediaEngine.queryInterface(rtcEngine, agora::INTERFACE_ID_TYPE::AGORA_IID_MEDIA_ENGINE);
296
+
297
+    if (mediaEngine) {
298
+        mediaEngine->registerVideoFrameObserver(NULL);
299
+        mediaEngine->registerAudioFrameObserver(NULL);
300
+    }
301
+
302
+    if (gCallBack != nullptr) {
303
+        env->DeleteGlobalRef(gCallBack);
304
+        gCallBack = nullptr;
305
+    }
306
+    gCallbackClass = nullptr;
307
+
308
+    recordAudioMethodId = nullptr;
309
+    playbackAudioMethodId = nullptr;
310
+    playBeforeMixAudioMethodId = nullptr;
311
+    mixAudioMethodId = nullptr;
312
+    captureVideoMethodId = nullptr;
313
+    renderVideoMethodId = nullptr;
314
+
315
+    _javaDirectPlayBufferCapture = nullptr;
316
+    _javaDirectPlayBufferRecordAudio = nullptr;
317
+    _javaDirectPlayBufferPlayAudio = nullptr;
318
+    _javaDirectPlayBufferBeforeMixAudio = nullptr;
319
+    _javaDirectPlayBufferMixAudio = nullptr;
320
+
321
+    decodeBufferMap.clear();
322
+}
323
+
324
+
325
+#ifdef __cplusplus
326
+}
327
+#endif

+ 77
- 0
android/src/main/cpp/src/agora_media_pre_processing.h View File

@@ -0,0 +1,77 @@
1
+/* DO NOT EDIT THIS FILE - it is machine generated */
2
+#include <jni.h>
3
+/* Header for class com_syan_agora_media_MediaPreProcessing */
4
+
5
+#ifndef _Included_com_syan_agora_media_MediaPreProcessing
6
+#define _Included_com_syan_agora_media_MediaPreProcessing
7
+#ifdef __cplusplus
8
+extern "C" {
9
+#endif
10
+/*
11
+ * Class:     com_syan_agora_media_MediaPreProcessing
12
+ * Method:    setCallback
13
+ * Signature: (Lcom/syan/agora/media/MediaPreProcessing/ProgressCallback;)V
14
+ */
15
+JNIEXPORT void JNICALL Java_com_syan_agora_media_MediaPreProcessing_setCallback
16
+  (JNIEnv *, jclass, jobject);
17
+
18
+/*
19
+ * Class:     com_syan_agora_media_MediaPreProcessing
20
+ * Method:    setVideoCaptureByteBuffer
21
+ * Signature: (Ljava/nio/ByteBuffer;)V
22
+ */
23
+JNIEXPORT void JNICALL Java_com_syan_agora_media_MediaPreProcessing_setVideoCaptureByteBuffer
24
+  (JNIEnv *, jclass, jobject);
25
+
26
+/*
27
+ * Class:     com_syan_agora_media_MediaPreProcessing
28
+ * Method:    setAudioRecordByteBuffer
29
+ * Signature: (Ljava/nio/ByteBuffer;)V
30
+ */
31
+JNIEXPORT void JNICALL Java_com_syan_agora_media_MediaPreProcessing_setAudioRecordByteBuffer
32
+  (JNIEnv *, jclass, jobject);
33
+
34
+/*
35
+ * Class:     com_syan_agora_media_MediaPreProcessing
36
+ * Method:    setAudioPlayByteBuffer
37
+ * Signature: (Ljava/nio/ByteBuffer;)V
38
+ */
39
+JNIEXPORT void JNICALL Java_com_syan_agora_media_MediaPreProcessing_setAudioPlayByteBuffer
40
+  (JNIEnv *, jclass, jobject);
41
+
42
+/*
43
+ * Class:     com_syan_agora_media_MediaPreProcessing
44
+ * Method:    setBeforeAudioMixByteBuffer
45
+ * Signature: (Ljava/nio/ByteBuffer;)V
46
+ */
47
+JNIEXPORT void JNICALL Java_com_syan_agora_media_MediaPreProcessing_setBeforeAudioMixByteBuffer
48
+  (JNIEnv *, jclass, jobject);
49
+
50
+/*
51
+ * Class:     com_syan_agora_media_MediaPreProcessing
52
+ * Method:    setAudioMixByteBuffer
53
+ * Signature: (Ljava/nio/ByteBuffer;)V
54
+ */
55
+JNIEXPORT void JNICALL Java_com_syan_agora_media_MediaPreProcessing_setAudioMixByteBuffer
56
+  (JNIEnv *, jclass, jobject);
57
+
58
+/*
59
+ * Class:     com_syan_agora_media_MediaPreProcessing
60
+ * Method:    setVideoDecodeByteBuffer
61
+ * Signature: (ILjava/nio/ByteBuffer;)V
62
+ */
63
+JNIEXPORT void JNICALL Java_com_syan_agora_media_MediaPreProcessing_setVideoDecodeByteBuffer
64
+  (JNIEnv *, jclass, jint, jobject);
65
+
66
+/*
67
+ * Class:     com_syan_agora_media_MediaPreProcessing
68
+ * Method:    releasePoint
69
+ * Signature: ()V
70
+ */
71
+JNIEXPORT void JNICALL Java_com_syan_agora_media_MediaPreProcessing_releasePoint
72
+  (JNIEnv *, jclass);
73
+
74
+#ifdef __cplusplus
75
+}
76
+#endif
77
+#endif

+ 5
- 7
android/src/main/java/com/syan/agora/AgoraConst.java View File

@@ -19,18 +19,13 @@ public class AgoraConst {
19 19
     public final static String AGTokenPrivilegeWillExpire = "tokenPrivilegeWillExpire";
20 20
     public final static String AGRequestToken = "requestToken";
21 21
 
22
-    public final static String AGMicrophoneEnabled = "microphoneEnabled";
23 22
     public final static String AGAudioVolumeIndication = "audioVolumeIndication";
24 23
     public final static String AGActiveSpeaker = "activeSpeaker";
25 24
     public final static String AGFirstLocalAudioFrame = "firstLocalAudioFrame";
26 25
     public final static String AGFirstRemoteAudioFrame = "firstRemoteAudioFrame";
27 26
     public final static String AGFirstLocalVideoFrame = "firstLocalVideoFrame";
28
-    public final static String AGFirstRemoteVideoDecoded = "firstRemoteVideoDecoded";
29 27
     public final static String AGFirstRemoteVideoFrame = "firstRemoteVideoFrame";
30 28
     public final static String AGUserMuteAudio = "userMuteAudio";
31
-    public final static String AGUserMuteVideo = "userMuteVideo";
32
-    public final static String AGUserEnableVideo = "userEnableVideo";
33
-    public final static String AGUserEnableLocalVideo = "userEnableLocalVideo";
34 29
     public final static String AGVideoSizeChanged = "videoSizeChanged";
35 30
     public final static String AGRtmpStreamingStateChanged = "rtmpStreamingStateChanged";
36 31
     public final static String AGNetworkTypeChanged = "networkTypeChanged";
@@ -44,6 +39,11 @@ public class AgoraConst {
44 39
     public final static String AGAudioRouteChanged = "audioRouteChanged";
45 40
     public final static String AGCameraFocusAreaChanged = "cameraFocusAreaChanged";
46 41
     public final static String AGCameraExposureAreaChanged = "cameraExposureAreaChanged";
42
+    public final static String AGRemoteAudioStateChanged = "remoteAudioStateChanged";
43
+    public final static String AGLocalAudioStateChanged = "localAudioStateChanged";
44
+    public final static String AGLocalAudioStats = "localAudioStats";
45
+    public final static String AGMediaRelayStateChanged = "mediaRelayStateChanged";
46
+    public final static String AGReceivedChannelMediaRelay = "receivedChannelMediaRelay";
47 47
 
48 48
     public final static String AGRtcStats = "rtcStats";
49 49
     public final static String AGLastmileQuality = "lastmileQuality";
@@ -51,8 +51,6 @@ public class AgoraConst {
51 51
     public final static String AGLocalVideoStats = "localVideoStats";
52 52
     public final static String AGRemoteVideoStats = "remoteVideoStats";
53 53
     public final static String AGRemoteAudioStats = "remoteAudioStats";
54
-    public final static String AGAudioTransportStatsOfUid = "audioTransportStatsOfUid";
55
-    public final static String AGVideoTransportStatsOfUid = "videoTransportStatsOfUid";
56 54
 
57 55
     public final static String AGRemoteAudioMixingStart = "remoteAudioMixingStart";
58 56
     public final static String AGRemoteAudioMixingFinish = "remoteAudioMixingFinish";

+ 329
- 172
android/src/main/java/com/syan/agora/AgoraModule.java View File

@@ -2,6 +2,7 @@ package com.syan.agora;
2 2
 
3 3
 import android.graphics.Rect;
4 4
 import android.support.annotation.Nullable;
5
+import android.view.SurfaceView;
5 6
 
6 7
 import com.facebook.react.bridge.Arguments;
7 8
 import com.facebook.react.bridge.Callback;
@@ -34,6 +35,8 @@ import io.agora.rtc.models.UserInfo;
34 35
 import io.agora.rtc.video.AgoraImage;
35 36
 import io.agora.rtc.video.BeautyOptions;
36 37
 import io.agora.rtc.video.CameraCapturerConfiguration;
38
+import io.agora.rtc.video.ChannelMediaInfo;
39
+import io.agora.rtc.video.ChannelMediaRelayConfiguration;
37 40
 import io.agora.rtc.video.VideoEncoderConfiguration;
38 41
 
39 42
 import static com.facebook.react.bridge.UiThreadUtil.runOnUiThread;
@@ -276,7 +279,7 @@ public class AgoraModule extends ReactContextBaseJavaModule {
276 279
                 public void run() {
277 280
                     WritableMap map = Arguments.createMap();
278 281
                     map.putString("message", "AgoraWarning");
279
-                    map.putInt("code", code);
282
+                    map.putInt("errorCode", code);
280 283
                     sendEvent(getReactApplicationContext(), AGWarning, map);
281 284
                 }
282 285
             });
@@ -289,7 +292,7 @@ public class AgoraModule extends ReactContextBaseJavaModule {
289 292
                 public void run() {
290 293
                     WritableMap map = Arguments.createMap();
291 294
                     map.putString("message", "AgoraError");
292
-                    map.putInt("code", code);
295
+                    map.putInt("errorCode", code);
293 296
                     sendEvent(getReactApplicationContext(), AGError, map);
294 297
                 }
295 298
             });
@@ -301,7 +304,7 @@ public class AgoraModule extends ReactContextBaseJavaModule {
301 304
                 @Override
302 305
                 public void run() {
303 306
                     WritableMap map = Arguments.createMap();
304
-                    map.putInt("error", code);
307
+                    map.putInt("errorCode", code);
305 308
                     map.putString("api", api);
306 309
                     map.putString("result", result);
307 310
                     if (code != 0) {
@@ -350,8 +353,12 @@ public class AgoraModule extends ReactContextBaseJavaModule {
350 353
                     statsMap.putInt("duration", stats.totalDuration);
351 354
                     statsMap.putInt("txBytes", stats.txBytes);
352 355
                     statsMap.putInt("rxBytes", stats.rxBytes);
353
-                    // statsMap.putInt("txKBitRate", stats.txKBitRate);
354
-                    // statsMap.putInt("rxKBitRate", stats.rxKBitRate);
356
+                    statsMap.putInt("txAudioBytes", stats.txAudioBytes);
357
+                    statsMap.putInt("txVideoBytes", stats.txVideoBytes);
358
+                    statsMap.putInt("rxAudioBytes", stats.rxAudioBytes);
359
+                    statsMap.putInt("rxVideoBytes", stats.rxVideoBytes);
360
+                    statsMap.putInt("txKBitRate", stats.txKBitRate);
361
+                    statsMap.putInt("rxKBitRate", stats.rxKBitRate);
355 362
                     statsMap.putInt("txAudioKBitRate", stats.txAudioKBitRate);
356 363
                     statsMap.putInt("rxAudioKBitRate", stats.rxAudioKBitRate);
357 364
                     statsMap.putInt("txVideoKBitRate", stats.txVideoKBitRate);
@@ -488,19 +495,6 @@ public class AgoraModule extends ReactContextBaseJavaModule {
488 495
             });
489 496
         }
490 497
 
491
-        @Override
492
-        public void onMicrophoneEnabled(final boolean enabled) {
493
-            runOnUiThread(new Runnable() {
494
-                @Override
495
-                public void run() {
496
-                    WritableMap map = Arguments.createMap();
497
-                    map.putBoolean("enabled", enabled);
498
-                    sendEvent(getReactApplicationContext(), AGMicrophoneEnabled, map);
499
-                }
500
-
501
-            });
502
-        }
503
-
504 498
         @Override
505 499
         public void onAudioVolumeIndication(final AudioVolumeInfo [] speakers, final int totalVolume) {
506 500
             runOnUiThread(new Runnable() {
@@ -574,24 +568,6 @@ public class AgoraModule extends ReactContextBaseJavaModule {
574 568
             });
575 569
         }
576 570
 
577
-        /**
578
-         * onFirstRemoteVideoDecoded
579
-         */
580
-        @Override
581
-        public void onFirstRemoteVideoDecoded(final int uid, final int width, final int height, final int elapsed) {
582
-            runOnUiThread(new Runnable() {
583
-                @Override
584
-                public void run() {
585
-                    WritableMap map = Arguments.createMap();
586
-                    map.putInt("uid", uid);
587
-                    map.putInt("width", width);
588
-                    map.putInt("height", height);
589
-                    map.putInt("elapsed", elapsed);
590
-                    sendEvent(getReactApplicationContext(), AGFirstRemoteVideoDecoded, map);
591
-                }
592
-            });
593
-        }
594
-
595 571
         @Override
596 572
         public void onFirstRemoteVideoFrame(final int uid, final int width, final int height, final int elapsed) {
597 573
             runOnUiThread(new Runnable() {
@@ -621,81 +597,73 @@ public class AgoraModule extends ReactContextBaseJavaModule {
621 597
         }
622 598
 
623 599
         @Override
624
-        public void onUserMuteVideo(final int uid, final boolean muted) {
600
+        public void onVideoSizeChanged(final int uid, final int width, final int height, final int rotation) {
625 601
             runOnUiThread(new Runnable() {
626 602
                 @Override
627 603
                 public void run() {
628 604
                     WritableMap map = Arguments.createMap();
629
-                    map.putBoolean("muted", muted);
630 605
                     map.putInt("uid", uid);
631
-                    sendEvent(getReactApplicationContext(), AGUserMuteVideo, map);
606
+                    map.putInt("width", width);
607
+                    map.putInt("height", height);
608
+                    map.putInt("rotation", rotation);
609
+                    sendEvent(getReactApplicationContext(), AGVideoSizeChanged, map);
632 610
                 }
633 611
             });
634 612
         }
635 613
 
636 614
         @Override
637
-        public void onUserEnableVideo(final int uid, final boolean enabled) {
615
+        public void onRtmpStreamingStateChanged(final String url, final int state, final int errCode) {
638 616
             runOnUiThread(new Runnable() {
639 617
                 @Override
640 618
                 public void run() {
641 619
                     WritableMap map = Arguments.createMap();
642
-                    map.putBoolean("enabled", enabled);
643
-                    map.putInt("uid", uid);
644
-                    sendEvent(getReactApplicationContext(), AGUserEnableVideo, map);
620
+                    map.putString("url", url);
621
+                    map.putInt("state", state);
622
+                    map.putInt("errorCode", errCode);
623
+                    sendEvent(getReactApplicationContext(), AGRtmpStreamingStateChanged, map);
645 624
                 }
646 625
             });
647 626
         }
648 627
 
649 628
         @Override
650
-        public void onUserEnableLocalVideo(final int uid, final boolean enabled) {
629
+        public void onNetworkTypeChanged(final int type) {
651 630
             runOnUiThread(new Runnable() {
652 631
                 @Override
653 632
                 public void run() {
654 633
                     WritableMap map = Arguments.createMap();
655
-                    map.putBoolean("enabled", enabled);
656
-                    map.putInt("uid", uid);
657
-                    sendEvent(getReactApplicationContext(), AGUserEnableLocalVideo, map);
634
+                    map.putInt("type", type);
635
+                    sendEvent(getReactApplicationContext(), AGNetworkTypeChanged, map);
658 636
                 }
659 637
             });
660 638
         }
661 639
 
662
-        @Override
663
-        public void onVideoSizeChanged(final int uid, final int width, final int height, final int rotation) {
664
-            runOnUiThread(new Runnable() {
665
-                @Override
666
-                public void run() {
667
-                    WritableMap map = Arguments.createMap();
668
-                    map.putInt("uid", uid);
669
-                    map.putInt("width", width);
670
-                    map.putInt("height", height);
671
-                    map.putInt("rotation", rotation);
672
-                    sendEvent(getReactApplicationContext(), AGVideoSizeChanged, map);
673
-                }
674
-            });
675
-        }
676 640
 
677 641
         @Override
678
-        public void onRtmpStreamingStateChanged(final String url, final int state, final int errCode) {
642
+        public void onLocalAudioStateChanged(final int state, final int errCode) {
679 643
             runOnUiThread(new Runnable() {
680 644
                 @Override
681 645
                 public void run() {
682 646
                     WritableMap map = Arguments.createMap();
683
-                    map.putString("url", url);
684 647
                     map.putInt("state", state);
685 648
                     map.putInt("errorCode", errCode);
686
-                    sendEvent(getReactApplicationContext(), AGRtmpStreamingStateChanged, map);
649
+                    sendEvent(getReactApplicationContext(), AGLocalAudioStateChanged, map);
687 650
                 }
688 651
             });
689 652
         }
690
-
691 653
         @Override
692
-        public void onNetworkTypeChanged(final int type) {
654
+        public void onRemoteAudioStateChanged(final int uid,
655
+                                              final int state,
656
+                                              final int reason,
657
+                                              final int elapsed) {
693 658
             runOnUiThread(new Runnable() {
694 659
                 @Override
695 660
                 public void run() {
696 661
                     WritableMap map = Arguments.createMap();
697
-                    map.putInt("type", type);
698
-                    sendEvent(getReactApplicationContext(), AGNetworkTypeChanged, map);
662
+                    map.putInt("uid", uid);
663
+                    map.putInt("state", state);
664
+                    map.putInt("uid", reason);
665
+                    map.putInt("elapsed", elapsed);
666
+                    sendEvent(getReactApplicationContext(), AGRemoteAudioStateChanged, map);
699 667
                 }
700 668
             });
701 669
         }
@@ -714,13 +682,18 @@ public class AgoraModule extends ReactContextBaseJavaModule {
714 682
         }
715 683
 
716 684
         @Override
717
-        public void onRemoteVideoStateChanged(final int uid, final int state) {
685
+        public void onRemoteVideoStateChanged(final int uid,
686
+                                              final int state,
687
+                                              final int reason,
688
+                                              final int elapsed)  {
718 689
             runOnUiThread(new Runnable() {
719 690
                 @Override
720 691
                 public void run() {
721 692
                     WritableMap map = Arguments.createMap();
722 693
                     map.putInt("uid", uid);
723 694
                     map.putInt("state", state);
695
+                    map.putInt("reason", reason);
696
+                    map.putInt("elapsed", elapsed);
724 697
                     sendEvent(getReactApplicationContext(), AGRemoteVideoStateChanged, map);
725 698
                 }
726 699
             });
@@ -829,7 +802,13 @@ public class AgoraModule extends ReactContextBaseJavaModule {
829 802
                     statsMap.putInt("duration", stats.totalDuration);
830 803
                     statsMap.putInt("txBytes", stats.txBytes);
831 804
                     statsMap.putInt("rxBytes", stats.rxBytes);
832
-                    statsMap.putInt("txAudioKBitRate", stats.txAudioKBitRate);
805
+                    statsMap.putInt("txAudioBytes", stats.txAudioBytes);
806
+                    statsMap.putInt("txVideoBytes", stats.txVideoBytes);
807
+                    statsMap.putInt("rxAudioBytes", stats.rxAudioBytes);
808
+                    statsMap.putInt("rxVideoBytes", stats.rxVideoBytes);
809
+                    statsMap.putInt("txKBitRate", stats.txKBitRate);
810
+                    statsMap.putInt("rxKBitRate", stats.rxKBitRate);
811
+                    statsMap.putInt("rxVideoKBitRate", stats.rxVideoKBitRate);
833 812
                     statsMap.putInt("rxAudioKBitRate", stats.rxAudioKBitRate);
834 813
                     statsMap.putInt("txVideoKBitRate", stats.txVideoKBitRate);
835 814
                     statsMap.putInt("rxVideoKBitRate", stats.rxVideoKBitRate);
@@ -884,6 +863,14 @@ public class AgoraModule extends ReactContextBaseJavaModule {
884 863
                     statsMap.putInt("sentFrameRate", stats.sentFrameRate);
885 864
                     statsMap.putInt("encoderOutputFrameRate", stats.encoderOutputFrameRate);
886 865
                     statsMap.putInt("rendererOutputFrameRate", stats.rendererOutputFrameRate);
866
+                    statsMap.putInt("targetBitrate", stats.targetBitrate);
867
+                    statsMap.putInt("targetFrameRate", stats.targetFrameRate);
868
+                    statsMap.putInt("qualityAdaptIndication", stats.qualityAdaptIndication);
869
+                    statsMap.putInt("encodedBitrate", stats.encodedBitrate);
870
+                    statsMap.putInt("encodedFrameWidth", stats.encodedFrameWidth);
871
+                    statsMap.putInt("encodedFrameHeight", stats.encodedFrameHeight);
872
+                    statsMap.putInt("encodedFrameCount", stats.encodedFrameCount);
873
+                    statsMap.putInt("codecType", stats.codecType);
887 874
                     WritableMap map = Arguments.createMap();
888 875
                     map.putMap("stats", statsMap);
889 876
                     sendEvent(getReactApplicationContext(), AGLocalVideoStats, map);
@@ -901,7 +888,9 @@ public class AgoraModule extends ReactContextBaseJavaModule {
901 888
                     statsMap.putInt("width", stats.width);
902 889
                     statsMap.putInt("height", stats.height);
903 890
                     statsMap.putInt("receivedBitrate", stats.receivedBitrate);
891
+                    statsMap.putInt("decoderOutputFrameRate", stats.decoderOutputFrameRate);
904 892
                     statsMap.putInt("rendererOutputFrameRate", stats.rendererOutputFrameRate);
893
+                    statsMap.putInt("packetLossRate", stats.packetLossRate);
905 894
                     statsMap.putInt("rxStreamType", stats.rxStreamType);
906 895
                     statsMap.putInt("totalFrozenTime", stats.totalFrozenTime);
907 896
                     statsMap.putInt("frozenRate", stats.frozenRate);
@@ -912,46 +901,6 @@ public class AgoraModule extends ReactContextBaseJavaModule {
912 901
             });
913 902
         }
914 903
 
915
-        @Override
916
-        public void onRemoteAudioTransportStats(final int uid,
917
-                                                final int delay,
918
-                                                final int lost,
919
-                                                final int rxKBitRate) {
920
-            runOnUiThread(new Runnable() {
921
-                @Override
922
-                public void run() {
923
-                    WritableMap statsMap = Arguments.createMap();
924
-                    statsMap.putInt("uid", uid);
925
-                    statsMap.putInt("delay", delay);
926
-                    statsMap.putInt("lost", lost);
927
-                    statsMap.putInt("rxKBitRate", rxKBitRate);
928
-                    WritableMap map = Arguments.createMap();
929
-                    map.putMap("stats", statsMap);
930
-                    sendEvent(getReactApplicationContext(), AGAudioTransportStatsOfUid, map);
931
-                }
932
-            });
933
-        }
934
-
935
-        @Override
936
-        public void onRemoteVideoTransportStats(final int uid,
937
-                                                final int delay,
938
-                                                final int lost,
939
-                                                final int rxKBitRate) {
940
-            runOnUiThread(new Runnable() {
941
-                @Override
942
-                public void run() {
943
-                    WritableMap statsMap = Arguments.createMap();
944
-                    statsMap.putInt("uid", uid);
945
-                    statsMap.putInt("delay", delay);
946
-                    statsMap.putInt("lost", lost);
947
-                    statsMap.putInt("rxKBitRate", rxKBitRate);
948
-                    WritableMap map = Arguments.createMap();
949
-                    map.putMap("stats", statsMap);
950
-                    sendEvent(getReactApplicationContext(), AGVideoTransportStatsOfUid, map);
951
-                }
952
-            });
953
-        }
954
-
955 904
         @Override
956 905
         public void onAudioMixingStateChanged(final int state, final int errorCode) {
957 906
             runOnUiThread(new Runnable() {
@@ -984,7 +933,7 @@ public class AgoraModule extends ReactContextBaseJavaModule {
984 933
                 public void run() {
985 934
                     WritableMap map = Arguments.createMap();
986 935
                     map.putString("url", url);
987
-                    map.putInt("code", errorCode);
936
+                    map.putInt("errorCode", errorCode);
988 937
                     sendEvent(getReactApplicationContext(), AGStreamPublished, map);
989 938
                 }
990 939
             });
@@ -1054,7 +1003,7 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1054 1003
                     WritableMap map = Arguments.createMap();
1055 1004
                     map.putInt("uid", uid);
1056 1005
                     map.putInt("streamId", streamId);
1057
-                    map.putInt("error", error);
1006
+                    map.putInt("errorCode", error);
1058 1007
                     map.putInt("missed", missed);
1059 1008
                     map.putInt("cached", cached);
1060 1009
                     sendEvent(getReactApplicationContext(), AGOccurStreamMessageError, map);
@@ -1112,6 +1061,48 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1112 1061
                 }
1113 1062
             });
1114 1063
         }
1064
+
1065
+        @Override
1066
+        public void onChannelMediaRelayEvent(final int code) {
1067
+            super.onChannelMediaRelayEvent(code);
1068
+            runOnUiThread(new Runnable() {
1069
+                @Override
1070
+                public void run() {
1071
+                    WritableMap map = Arguments.createMap();
1072
+                    map.putInt("errorCode", code);
1073
+                    sendEvent(getReactApplicationContext(), AGReceivedChannelMediaRelay, map);
1074
+                }
1075
+            });
1076
+        }
1077
+
1078
+        @Override
1079
+        public void onChannelMediaRelayStateChanged(final int state, final int code) {
1080
+            super.onChannelMediaRelayStateChanged(state, code);
1081
+            runOnUiThread(new Runnable() {
1082
+                @Override
1083
+                public void run() {
1084
+                    WritableMap map = Arguments.createMap();
1085
+                    map.putInt("state", state);
1086
+                    map.putInt("errorCode", code);
1087
+                    sendEvent(getReactApplicationContext(), AGMediaRelayStateChanged, map);
1088
+                }
1089
+            });
1090
+        }
1091
+
1092
+        @Override
1093
+        public void onLocalAudioStats(final LocalAudioStats rtcStats) {
1094
+            super.onLocalAudioStats(rtcStats);
1095
+            runOnUiThread(new Runnable() {
1096
+                @Override
1097
+                public void run() {
1098
+                    WritableMap map = Arguments.createMap();
1099
+                    map.putInt("numChannels", rtcStats.numChannels);
1100
+                    map.putInt("sentSampleRate", rtcStats.sentSampleRate);
1101
+                    map.putInt("sentBitrate", rtcStats.sentBitrate);
1102
+                    sendEvent(getReactApplicationContext(), AGLocalAudioStats, map);
1103
+                }
1104
+            });
1105
+        }
1115 1106
     };
1116 1107
 
1117 1108
     @ReactMethod
@@ -1162,10 +1153,14 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1162 1153
         }
1163 1154
     }
1164 1155
 
1156
+    private String channelName = null;
1157
+
1165 1158
     @ReactMethod
1166 1159
     public void joinChannel(ReadableMap options, Promise promise) {
1167 1160
         Integer res = AgoraManager.getInstance().joinChannel(options);
1168 1161
         if (res == 0) {
1162
+            String channelName = options.getString("channelName");
1163
+            this.channelName = channelName;
1169 1164
             promise.resolve(null);
1170 1165
         } else {
1171 1166
             promise.reject("-1", res.toString());
@@ -1188,8 +1183,10 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1188 1183
         if (options.hasKey("token")) {
1189 1184
             token = options.getString("token");
1190 1185
         }
1186
+        String channelName = options.getString("channelName");
1191 1187
         Integer res = rtcEngine.joinChannelWithUserAccount(token, options.getString("channelName"), options.getString("userAccount"));
1192 1188
         if (res == 0) {
1189
+            this.channelName = channelName;
1193 1190
             promise.resolve(null);
1194 1191
         } else {
1195 1192
             promise.reject("-1", res.toString());
@@ -1224,6 +1221,24 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1224 1221
         }
1225 1222
     }
1226 1223
 
1224
+    @ReactMethod
1225
+    public void switchChannel(ReadableMap options, Promise promise) {
1226
+        String token = null;
1227
+        String channel = null;
1228
+        if (options.hasKey("token")) {
1229
+            token = options.getString("token");
1230
+        }
1231
+        if (options.hasKey("channelName")) {
1232
+            channel = options.getString("channelName");
1233
+        }
1234
+        Integer res = AgoraManager.getInstance().mRtcEngine.switchChannel(token, channel);
1235
+        if (res == 0) {
1236
+            promise.resolve(null);
1237
+        } else {
1238
+            promise.reject("-1", res.toString());
1239
+        }
1240
+    }
1241
+
1227 1242
     @ReactMethod
1228 1243
     public void leaveChannel(Promise promise) {
1229 1244
         Integer res = AgoraManager.getInstance().leaveChannel();
@@ -1239,6 +1254,121 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1239 1254
         RtcEngine.destroy();
1240 1255
     }
1241 1256
 
1257
+    @ReactMethod
1258
+    public void startChannelMediaRelay(ReadableMap options, Promise promise) {
1259
+        ChannelMediaRelayConfiguration config = new ChannelMediaRelayConfiguration();
1260
+        ChannelMediaInfo src = config.getSrcChannelMediaInfo();
1261
+        if (options.hasKey("src")) {
1262
+            ReadableMap srcOption = options.getMap("src");
1263
+            if (srcOption.hasKey("token")) {
1264
+                src.token = srcOption.getString("token");
1265
+            }
1266
+            if (srcOption.hasKey("channelName")) {
1267
+                src.channelName = srcOption.getString("channelName");
1268
+            }
1269
+        }
1270
+        ReadableArray dstMediaInfo = options.getArray("channels");
1271
+        for (int i = 0; i < dstMediaInfo.size(); i++) {
1272
+            ReadableMap dst = dstMediaInfo.getMap(i);
1273
+            String channelName = null;
1274
+            String token = null;
1275
+            Integer uid = 0;
1276
+            if (dst.hasKey("token")) {
1277
+                token = token;
1278
+            }
1279
+            if (dst.hasKey("channelName")) {
1280
+                channelName = dst.getString("channelName");
1281
+            }
1282
+            if (dst.hasKey("uid")) {
1283
+                uid = dst.getInt("uid");
1284
+            }
1285
+            config.setDestChannelInfo(channelName, new ChannelMediaInfo(channelName, token, uid));
1286
+        }
1287
+        Integer res = AgoraManager.getInstance().mRtcEngine.startChannelMediaRelay(config);
1288
+        if (res == 0) {
1289
+            promise.resolve(null);
1290
+        } else {
1291
+            promise.reject("-1", res.toString());
1292
+        }
1293
+    }
1294
+
1295
+    @ReactMethod
1296
+    public void removeChannelMediaRelay(ReadableMap options, Promise promise) {
1297
+        ChannelMediaRelayConfiguration config = new ChannelMediaRelayConfiguration();
1298
+        ChannelMediaInfo src = config.getSrcChannelMediaInfo();
1299
+        if (options.hasKey("src")) {
1300
+            ReadableMap srcOption = options.getMap("src");
1301
+            if (srcOption.hasKey("token")) {
1302
+                src.token = srcOption.getString("token");
1303
+            }
1304
+            if (srcOption.hasKey("channelName")) {
1305
+                src.channelName = srcOption.getString("channelName");
1306
+            }
1307
+        }
1308
+        ReadableArray dstMediaInfo = options.getArray("channels");
1309
+        for (int i = 0; i < dstMediaInfo.size(); i++) {
1310
+            ReadableMap dst = dstMediaInfo.getMap(i);
1311
+            if (dst.hasKey("channelName")) {
1312
+                channelName = dst.getString("channelName");
1313
+                config.removeDestChannelInfo(channelName);
1314
+            }
1315
+        }
1316
+        Integer res = AgoraManager.getInstance().mRtcEngine.updateChannelMediaRelay(config);
1317
+        if (res == 0) {
1318
+            promise.resolve(null);
1319
+        } else {
1320
+            promise.reject("-1", res.toString());
1321
+        }
1322
+    }
1323
+
1324
+    @ReactMethod
1325
+    public void updateChannelMediaRelay(ReadableMap options, Promise promise) {
1326
+        ChannelMediaRelayConfiguration config = new ChannelMediaRelayConfiguration();
1327
+        ChannelMediaInfo src = config.getSrcChannelMediaInfo();
1328
+        if (options.hasKey("src")) {
1329
+            ReadableMap srcOption = options.getMap("src");
1330
+            if (srcOption.hasKey("token")) {
1331
+                src.token = srcOption.getString("token");
1332
+            }
1333
+            if (srcOption.hasKey("channelName")) {
1334
+                src.channelName = srcOption.getString("channelName");
1335
+            }
1336
+        }
1337
+        ReadableArray dstMediaInfo = options.getArray("channels");
1338
+        for (int i = 0; i < dstMediaInfo.size(); i++) {
1339
+            ReadableMap dst = dstMediaInfo.getMap(i);
1340
+            String channelName = null;
1341
+            String token = null;
1342
+            Integer uid = 0;
1343
+            if (dst.hasKey("token")) {
1344
+                token = token;
1345
+            }
1346
+            if (dst.hasKey("channelName")) {
1347
+                channelName = dst.getString("channelName");
1348
+            }
1349
+            if (dst.hasKey("uid")) {
1350
+                uid = dst.getInt("uid");
1351
+            }
1352
+            config.setDestChannelInfo(src.channelName, new ChannelMediaInfo(channelName, token, uid));
1353
+        }
1354
+        Integer res = AgoraManager.getInstance().mRtcEngine.updateChannelMediaRelay(config);
1355
+        if (res == 0) {
1356
+            promise.resolve(null);
1357
+        } else {
1358
+            promise.reject("-1", res.toString());
1359
+        }
1360
+    }
1361
+
1362
+    @ReactMethod
1363
+    public void stopChannelMediaRelay(Promise promise) {
1364
+        Integer res = AgoraManager.getInstance().mRtcEngine.stopChannelMediaRelay();
1365
+        if (res == 0) {
1366
+            promise.resolve(null);
1367
+        } else {
1368
+            promise.reject("-1", res.toString());
1369
+        }
1370
+    }
1371
+
1242 1372
     @ReactMethod
1243 1373
     public void startPreview(Promise promise) {
1244 1374
         Integer res = AgoraManager.getInstance().startPreview();
@@ -1385,9 +1515,9 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1385 1515
     @ReactMethod
1386 1516
     public void setCameraFocusPositionInPreview(ReadableMap options, Promise promise) {
1387 1517
         Integer res = AgoraManager.getInstance().mRtcEngine.setCameraFocusPositionInPreview(
1388
-                    (float)options.getDouble("x"),
1389
-                    (float)options.getDouble("y")
1390
-            );
1518
+                (float)options.getDouble("x"),
1519
+                (float)options.getDouble("y")
1520
+        );
1391 1521
         if (res == 0) {
1392 1522
             promise.resolve(null);
1393 1523
         } else {
@@ -1560,10 +1690,10 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1560 1690
     @ReactMethod
1561 1691
     public void createDataStream(ReadableMap options, Promise promise) {
1562 1692
         Integer res = AgoraManager.getInstance().mRtcEngine
1563
-                    .createDataStream(
1564
-                            options.getBoolean("ordered"),
1565
-                            options.getBoolean("reliable")
1566
-                            );
1693
+                .createDataStream(
1694
+                        options.getBoolean("ordered"),
1695
+                        options.getBoolean("reliable")
1696
+                );
1567 1697
         if (res == 0) {
1568 1698
             promise.resolve(null);
1569 1699
         } else {
@@ -1631,10 +1761,10 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1631 1761
     @ReactMethod
1632 1762
     public void startAudioMixing(ReadableMap options, Promise promise) {
1633 1763
         Integer res = AgoraManager.getInstance().mRtcEngine.startAudioMixing(
1634
-                    options.getString("filepath"),
1635
-                    options.getBoolean("loopback"),
1636
-                    options.getBoolean("replace"),
1637
-                    options.getInt("cycle")
1764
+                options.getString("filepath"),
1765
+                options.getBoolean("loopback"),
1766
+                options.getBoolean("replace"),
1767
+                options.getInt("cycle")
1638 1768
         );
1639 1769
         if (res == 0) {
1640 1770
             promise.resolve(null);
@@ -1756,10 +1886,10 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1756 1886
     @ReactMethod
1757 1887
     public void startAudioRecording(ReadableMap options, Promise promise) {
1758 1888
         Integer res = AgoraManager.getInstance().mRtcEngine
1759
-                    .startAudioRecording(
1760
-                            options.getString("filepath"),
1761
-                            options.getInt("quality")
1762
-        );
1889
+                .startAudioRecording(
1890
+                        options.getString("filepath"),
1891
+                        options.getInt("quality")
1892
+                );
1763 1893
         if (res == 0) {
1764 1894
             promise.resolve(null);
1765 1895
         } else {
@@ -1770,7 +1900,7 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1770 1900
     @ReactMethod
1771 1901
     public void stopAudioRecording(Promise promise) {
1772 1902
         Integer res = AgoraManager.getInstance().mRtcEngine
1773
-                    .stopAudioRecording();
1903
+                .stopAudioRecording();
1774 1904
         if (res == 0) {
1775 1905
             promise.resolve(null);
1776 1906
         } else {
@@ -1781,7 +1911,7 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1781 1911
     @ReactMethod
1782 1912
     public void stopEchoTest(Promise promise) {
1783 1913
         Integer res = AgoraManager.getInstance().mRtcEngine
1784
-                    .stopEchoTest();
1914
+                .stopEchoTest();
1785 1915
         if (res == 0) {
1786 1916
             promise.resolve(null);
1787 1917
         } else {
@@ -1792,7 +1922,7 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1792 1922
     @ReactMethod
1793 1923
     public void enableLastmileTest(Promise promise) {
1794 1924
         Integer res = AgoraManager.getInstance().mRtcEngine
1795
-                    .enableLastmileTest();
1925
+                .enableLastmileTest();
1796 1926
         if (res == 0) {
1797 1927
             promise.resolve(null);
1798 1928
         } else {
@@ -1803,7 +1933,7 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1803 1933
     @ReactMethod
1804 1934
     public void disableLastmileTest(Promise promise) {
1805 1935
         Integer res = AgoraManager.getInstance().mRtcEngine
1806
-                    .disableLastmileTest();
1936
+                .disableLastmileTest();
1807 1937
         if (res == 0) {
1808 1938
             promise.resolve(null);
1809 1939
         } else {
@@ -1814,12 +1944,12 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1814 1944
     @ReactMethod
1815 1945
     public void setRecordingAudioFrameParameters(ReadableMap options, Promise promise) {
1816 1946
         Integer res = AgoraManager.getInstance().mRtcEngine
1817
-                    .setRecordingAudioFrameParameters(
1818
-                            options.getInt("sampleRate"),
1819
-                            options.getInt("channel"),
1820
-                            options.getInt("mode"),
1821
-                            options.getInt("samplesPerCall")
1822
-        );
1947
+                .setRecordingAudioFrameParameters(
1948
+                        options.getInt("sampleRate"),
1949
+                        options.getInt("channel"),
1950
+                        options.getInt("mode"),
1951
+                        options.getInt("samplesPerCall")
1952
+                );
1823 1953
         if (res == 0) {
1824 1954
             promise.resolve(null);
1825 1955
         } else {
@@ -1830,12 +1960,12 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1830 1960
     @ReactMethod
1831 1961
     public void setPlaybackAudioFrameParameters(ReadableMap options, Promise promise) {
1832 1962
         Integer res = AgoraManager.getInstance().mRtcEngine
1833
-                    .setPlaybackAudioFrameParameters(
1834
-                            options.getInt("sampleRate"),
1835
-                            options.getInt("channel"),
1836
-                            options.getInt("mode"),
1837
-                            options.getInt("samplesPerCall")
1838
-        );
1963
+                .setPlaybackAudioFrameParameters(
1964
+                        options.getInt("sampleRate"),
1965
+                        options.getInt("channel"),
1966
+                        options.getInt("mode"),
1967
+                        options.getInt("samplesPerCall")
1968
+                );
1839 1969
         if (res == 0) {
1840 1970
             promise.resolve(null);
1841 1971
         } else {
@@ -1846,10 +1976,10 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1846 1976
     @ReactMethod
1847 1977
     public void setMixedAudioFrameParameters(WritableMap options, Promise promise) {
1848 1978
         Integer res = AgoraManager.getInstance().mRtcEngine
1849
-                    .setMixedAudioFrameParameters(
1850
-                            options.getInt("sampleRate"),
1851
-                            options.getInt("samplesPerCall")
1852
-                    );
1979
+                .setMixedAudioFrameParameters(
1980
+                        options.getInt("sampleRate"),
1981
+                        options.getInt("samplesPerCall")
1982
+                );
1853 1983
         if (res == 0) {
1854 1984
             promise.resolve(null);
1855 1985
         } else {
@@ -1870,7 +2000,7 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1870 2000
     @ReactMethod
1871 2001
     public void addVideoWatermark(ReadableMap options, Promise promise) {
1872 2002
         Integer res = AgoraManager.getInstance().mRtcEngine
1873
-                    .addVideoWatermark(createAgoraImage(options));
2003
+                .addVideoWatermark(createAgoraImage(options));
1874 2004
         if (res == 0) {
1875 2005
             promise.resolve(null);
1876 2006
         } else {
@@ -1881,7 +2011,7 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1881 2011
     @ReactMethod
1882 2012
     public void clearVideoWatermarks(Promise promise) {
1883 2013
         Integer res = AgoraManager.getInstance().mRtcEngine
1884
-                    .clearVideoWatermarks();
2014
+                .clearVideoWatermarks();
1885 2015
         if (res == 0) {
1886 2016
             promise.resolve(null);
1887 2017
         } else {
@@ -1892,7 +2022,7 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1892 2022
     @ReactMethod
1893 2023
     public void setLocalPublishFallbackOption(int option, Promise promise) {
1894 2024
         Integer res = AgoraManager.getInstance().mRtcEngine
1895
-                    .setLocalPublishFallbackOption(option);
2025
+                .setLocalPublishFallbackOption(option);
1896 2026
         if (res == 0) {
1897 2027
             promise.resolve(null);
1898 2028
         } else {
@@ -1903,7 +2033,7 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1903 2033
     @ReactMethod
1904 2034
     public void setRemoteSubscribeFallbackOption(int option, Promise promise) {
1905 2035
         Integer res = AgoraManager.getInstance().mRtcEngine
1906
-                    .setRemoteSubscribeFallbackOption(option);
2036
+                .setRemoteSubscribeFallbackOption(option);
1907 2037
         if (res == 0) {
1908 2038
             promise.resolve(null);
1909 2039
         } else {
@@ -1914,7 +2044,7 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1914 2044
     @ReactMethod
1915 2045
     public void enableDualStreamMode(boolean enabled, Promise promise) {
1916 2046
         Integer res = AgoraManager.getInstance().mRtcEngine
1917
-                    .enableDualStreamMode(enabled);
2047
+                .enableDualStreamMode(enabled);
1918 2048
         if (res == 0) {
1919 2049
             promise.resolve(null);
1920 2050
         } else {
@@ -1926,10 +2056,10 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1926 2056
     @ReactMethod
1927 2057
     public void setRemoteVideoStreamType(ReadableMap options, Promise promise) {
1928 2058
         Integer res = AgoraManager.getInstance().mRtcEngine
1929
-                    .setRemoteVideoStreamType(
1930
-                            options.getInt("uid"),
1931
-                            options.getInt("streamType")
1932
-                        );
2059
+                .setRemoteVideoStreamType(
2060
+                        options.getInt("uid"),
2061
+                        options.getInt("streamType")
2062
+                );
1933 2063
         if (res == 0) {
1934 2064
             promise.resolve(null);
1935 2065
         } else {
@@ -1940,9 +2070,9 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1940 2070
     @ReactMethod
1941 2071
     public void setRemoteDefaultVideoStreamType(ReadableMap options, Promise promise) {
1942 2072
         Integer res = AgoraManager.getInstance().mRtcEngine
1943
-                    .setRemoteDefaultVideoStreamType(
1944
-                            options.getInt("streamType")
1945
-                    );
2073
+                .setRemoteDefaultVideoStreamType(
2074
+                        options.getInt("streamType")
2075
+                );
1946 2076
         if (res == 0) {
1947 2077
             promise.resolve(null);
1948 2078
         } else {
@@ -1972,6 +2102,33 @@ public class AgoraModule extends ReactContextBaseJavaModule {
1972 2102
         }
1973 2103
     }
1974 2104
 
2105
+    private static boolean recording = false;
2106
+
2107
+    // TODO: need implementation
2108
+    @ReactMethod
2109
+    public void startAVRecording(final ReadableMap option, final Promise promise) {
2110
+        String path = option.getString("path");
2111
+        Integer uid = option.getInt("uid");
2112
+        String format = option.getString("format");
2113
+        if (true == recording) {
2114
+            promise.reject("-1", "recording already started");
2115
+        }
2116
+        SurfaceView view = AgoraManager.getInstance().getSurfaceView(uid);
2117
+        if (null == view) {
2118
+            promise.reject("-1", "recording already started");
2119
+        }
2120
+    }
2121
+
2122
+    // TODO: need implementation
2123
+    @ReactMethod
2124
+    public void stopAVRecording(final Promise promise) {
2125
+        if (false == recording) {
2126
+            promise.reject("-1", "recording didn't start");
2127
+        } else {
2128
+            promise.resolve(null);
2129
+        }
2130
+    }
2131
+
1975 2132
     public LiveInjectStreamConfig.AudioSampleRateType getAudioSampleRateEnum (int val) {
1976 2133
         LiveInjectStreamConfig.AudioSampleRateType type = LiveInjectStreamConfig.AudioSampleRateType.TYPE_32000;
1977 2134
         switch (Integer.valueOf(val)) {
@@ -2065,7 +2222,7 @@ public class AgoraModule extends ReactContextBaseJavaModule {
2065 2222
     @ReactMethod
2066 2223
     public void removeInjectStreamUrl(ReadableMap options, Promise promise) {
2067 2224
         Integer res = AgoraManager.getInstance().mRtcEngine
2068
-                    .removeInjectStreamUrl(options.getString("url"));
2225
+                .removeInjectStreamUrl(options.getString("url"));
2069 2226
         if (res == 0) {
2070 2227
             promise.resolve(null);
2071 2228
         } else {
@@ -2076,10 +2233,10 @@ public class AgoraModule extends ReactContextBaseJavaModule {
2076 2233
     @ReactMethod
2077 2234
     public void addPublishStreamUrl(ReadableMap options, Promise promise) {
2078 2235
         Integer res = AgoraManager.getInstance().mRtcEngine
2079
-                    .addPublishStreamUrl(
2080
-                            options.getString("url"),
2081
-                            options.getBoolean("enable")
2082
-                    );
2236
+                .addPublishStreamUrl(
2237
+                        options.getString("url"),
2238
+                        options.getBoolean("enable")
2239
+                );
2083 2240
         if (res == 0) {
2084 2241
             promise.resolve(null);
2085 2242
         } else {
@@ -2090,7 +2247,7 @@ public class AgoraModule extends ReactContextBaseJavaModule {
2090 2247
     @ReactMethod
2091 2248
     public void removePublishStreamUrl(ReadableMap options, Promise promise) {
2092 2249
         Integer res = AgoraManager.getInstance().mRtcEngine
2093
-                    .removePublishStreamUrl(options.getString("url"));
2250
+                .removePublishStreamUrl(options.getString("url"));
2094 2251
         if (res == 0) {
2095 2252
             promise.resolve(null);
2096 2253
         } else {
@@ -2222,13 +2379,13 @@ public class AgoraModule extends ReactContextBaseJavaModule {
2222 2379
     public void playEffect(ReadableMap options, Promise promise) {
2223 2380
         IAudioEffectManager manager = AgoraManager.getInstance().mRtcEngine.getAudioEffectManager();
2224 2381
         Integer res = manager.playEffect(
2225
-            options.getInt("soundid"),
2226
-            options.getString("filepath"),
2227
-            options.getInt("loopcount"),
2228
-            options.getDouble("pitch"),
2229
-            options.getDouble("pan"),
2230
-            options.getDouble("gain"),
2231
-            options.getBoolean("publish")
2382
+                options.getInt("soundid"),
2383
+                options.getString("filepath"),
2384
+                options.getInt("loopcount"),
2385
+                options.getDouble("pitch"),
2386
+                options.getDouble("pan"),
2387
+                options.getDouble("gain"),
2388
+                options.getBoolean("publish")
2232 2389
         );
2233 2390
         if (res == 0) {
2234 2391
             promise.resolve(null);

+ 53
- 5
android/src/main/java/com/syan/agora/AgoraVideoView.java View File

@@ -1,18 +1,38 @@
1 1
 package com.syan.agora;
2 2
 
3 3
 import android.content.Context;
4
+import android.media.MediaCodec;
5
+import android.media.MediaCodecInfo;
6
+import android.media.MediaCodecList;
7
+import android.media.MediaFormat;
8
+import android.media.MediaMuxer;
9
+import android.os.Build;
10
+import android.support.annotation.RequiresApi;
4 11
 import android.util.AttributeSet;
5
-import android.view.SurfaceView;
12
+import android.util.Log;
6 13
 import android.view.View;
7 14
 import android.widget.LinearLayout;
8 15
 
9
-import io.agora.rtc.mediaio.AgoraSurfaceView;
16
+import com.syan.agora.media.MediaDataAudioObserver;
17
+import com.syan.agora.media.MediaDataVideoObserver;
18
+
19
+import java.io.IOException;
20
+import java.util.concurrent.ExecutorService;
21
+import java.util.concurrent.Executors;
22
+import java.util.concurrent.Future;
23
+
24
+import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar;
25
+import static android.media.MediaFormat.KEY_BIT_RATE;
26
+import static android.media.MediaFormat.KEY_COLOR_FORMAT;
27
+import static android.media.MediaFormat.KEY_FRAME_RATE;
28
+import static android.media.MediaFormat.KEY_I_FRAME_INTERVAL;
10 29
 
11 30
 /**
12 31
  * Created by DB on 2017/6/27.
13 32
  */
14 33
 
15
-public class AgoraVideoView extends LinearLayout {
34
+public class AgoraVideoView extends LinearLayout implements MediaDataAudioObserver, MediaDataVideoObserver {
35
+
16 36
     public boolean isShowLocalVideo() {
17 37
         return showLocalVideo;
18 38
     }
@@ -27,7 +47,6 @@ public class AgoraVideoView extends LinearLayout {
27 47
 
28 48
     public void setRenderMode(Integer renderMode) {
29 49
         this.renderMode = renderMode;
30
-
31 50
     }
32 51
 
33 52
     public Integer getRemoteUid() {
@@ -50,7 +69,6 @@ public class AgoraVideoView extends LinearLayout {
50 69
     private Integer renderMode = 1;
51 70
     private Integer remoteUid;
52 71
     private boolean zOrderMediaOverlay;
53
-    private SurfaceView surfaceView;
54 72
 
55 73
     public AgoraVideoView(Context context) {
56 74
         super(context);
@@ -75,4 +93,34 @@ public class AgoraVideoView extends LinearLayout {
75 93
             }
76 94
         }
77 95
     }
96
+
97
+    @Override
98
+    public void onRecordAudioFrame(byte[] data, int audioFrameType, int samples, int bytesPerSample, int channels, int samplesPerSec, long renderTimeMs, int bufferLength) {
99
+
100
+    }
101
+
102
+    @Override
103
+    public void onPlaybackAudioFrame(byte[] data, int audioFrameType, int samples, int bytesPerSample, int channels, int samplesPerSec, long renderTimeMs, int bufferLength) {
104
+
105
+    }
106
+
107
+    @Override
108
+    public void onPlaybackAudioFrameBeforeMixing(int uid, byte[] data, int audioFrameType, int samples, int bytesPerSample, int channels, int samplesPerSec, long renderTimeMs, int bufferLength) {
109
+
110
+    }
111
+
112
+    @Override
113
+    public void onMixedAudioFrame(byte[] data, int audioFrameType, int samples, int bytesPerSample, int channels, int samplesPerSec, long renderTimeMs, int bufferLength) {
114
+
115
+    }
116
+
117
+    @Override
118
+    public void onCaptureVideoFrame(byte[] data, int frameType, int width, int height, int bufferLength, int yStride, int uStride, int vStride, int rotation, long renderTimeMs) {
119
+
120
+    }
121
+
122
+    @Override
123
+    public void onRenderVideoFrame(int uid, byte[] data, int frameType, int width, int height, int bufferLength, int yStride, int uStride, int vStride, int rotation, long renderTimeMs) {
124
+
125
+    }
78 126
 }

+ 29
- 0
android/src/main/java/com/syan/agora/media/DecodeDataBuffer.java View File

@@ -0,0 +1,29 @@
1
+package com.syan.agora.media;
2
+
3
+import java.nio.ByteBuffer;
4
+
5
+public class DecodeDataBuffer {
6
+    private int uid;
7
+    private ByteBuffer byteBuffer;
8
+
9
+    public DecodeDataBuffer(int uid, ByteBuffer byteBuffer) {
10
+        this.uid = uid;
11
+        this.byteBuffer = byteBuffer;
12
+    }
13
+
14
+    public int getUid() {
15
+        return uid;
16
+    }
17
+
18
+    public void setUid(int uid) {
19
+        this.uid = uid;
20
+    }
21
+
22
+    public ByteBuffer getByteBuffer() {
23
+        return byteBuffer;
24
+    }
25
+
26
+    public void setByteBuffer(ByteBuffer byteBuffer) {
27
+        this.byteBuffer = byteBuffer;
28
+    }
29
+}

+ 13
- 0
android/src/main/java/com/syan/agora/media/MediaDataAudioObserver.java View File

@@ -0,0 +1,13 @@
1
+
2
+package com.syan.agora.media;
3
+
4
+public interface MediaDataAudioObserver {
5
+
6
+    void onRecordAudioFrame(byte[] data, int audioFrameType, int samples, int bytesPerSample, int channels, int samplesPerSec, long renderTimeMs, int bufferLength);
7
+
8
+    void onPlaybackAudioFrame(byte[] data, int audioFrameType, int samples, int bytesPerSample, int channels, int samplesPerSec, long renderTimeMs, int bufferLength);
9
+
10
+    void onPlaybackAudioFrameBeforeMixing(int uid, byte[] data,int audioFrameType, int samples, int bytesPerSample, int channels, int samplesPerSec, long renderTimeMs, int bufferLength);
11
+
12
+    void onMixedAudioFrame(byte[] data,int audioFrameType, int samples, int bytesPerSample, int channels, int samplesPerSec, long renderTimeMs, int bufferLength);
13
+}

+ 297
- 0
android/src/main/java/com/syan/agora/media/MediaDataObserverPlugin.java View File

@@ -0,0 +1,297 @@
1
+package com.syan.agora.media;
2
+
3
+import android.graphics.Bitmap;
4
+import android.graphics.BitmapFactory;
5
+import android.graphics.ImageFormat;
6
+import android.graphics.Matrix;
7
+import android.graphics.Rect;
8
+import android.graphics.YuvImage;
9
+
10
+import java.io.ByteArrayOutputStream;
11
+import java.io.File;
12
+import java.io.FileNotFoundException;
13
+import java.io.FileOutputStream;
14
+import java.io.IOException;
15
+import java.nio.ByteBuffer;
16
+import java.util.ArrayList;
17
+import java.util.Iterator;
18
+import java.util.concurrent.CopyOnWriteArrayList;
19
+
20
+public class MediaDataObserverPlugin implements MediaPreProcessing.ProgressCallback {
21
+
22
+    private final CopyOnWriteArrayList<MediaDataVideoObserver> videoObserverList = new CopyOnWriteArrayList<>();
23
+    private final CopyOnWriteArrayList<MediaDataAudioObserver> audioObserverList = new CopyOnWriteArrayList<>();
24
+
25
+    private static final int VIDEO_DEFAULT_BUFFER_SIZE = 3240 * 1080; // default maximum video size Full HD+
26
+    private static final int AUDIO_DEFAULT_BUFFER_SIZE = 2048;
27
+
28
+    public ByteBuffer byteBufferCapture = ByteBuffer.allocateDirect(VIDEO_DEFAULT_BUFFER_SIZE);
29
+    public ByteBuffer byteBufferRender = ByteBuffer.allocateDirect(VIDEO_DEFAULT_BUFFER_SIZE);
30
+    public ByteBuffer byteBufferAudioRecord = ByteBuffer.allocateDirect(AUDIO_DEFAULT_BUFFER_SIZE);
31
+    public ByteBuffer byteBufferAudioPlay = ByteBuffer.allocateDirect(AUDIO_DEFAULT_BUFFER_SIZE);
32
+    public ByteBuffer byteBufferBeforeAudioMix = ByteBuffer.allocateDirect(AUDIO_DEFAULT_BUFFER_SIZE);
33
+    public ByteBuffer byteBufferAudioMix = ByteBuffer.allocateDirect(AUDIO_DEFAULT_BUFFER_SIZE);
34
+
35
+    private final ArrayList<DecodeDataBuffer> decodeBufferList = new ArrayList<>();
36
+
37
+    private static MediaDataObserverPlugin myAgent = null;
38
+
39
+    private boolean beCaptureVideoShot = false;
40
+    private boolean beRenderVideoShot = false;
41
+    private String captureFilePath = null;
42
+    private String renderFilePath = null;
43
+    private int renderVideoShotUid;
44
+
45
+    public static MediaDataObserverPlugin the() {
46
+        if (myAgent == null) {
47
+            synchronized (MediaDataObserverPlugin.class) {
48
+                if (myAgent == null)
49
+                    myAgent = new MediaDataObserverPlugin();
50
+            }
51
+        }
52
+        return myAgent;
53
+    }
54
+
55
+    public void addVideoObserver(MediaDataVideoObserver observer) {
56
+        videoObserverList.add(observer);
57
+    }
58
+
59
+    public void removeVideoObserver(MediaDataVideoObserver observer) {
60
+        videoObserverList.remove(observer);
61
+    }
62
+
63
+    public void addAudioObserver(MediaDataAudioObserver observer) {
64
+        audioObserverList.add(observer);
65
+    }
66
+
67
+    public void removeAudioObserver(MediaDataAudioObserver observer) {
68
+        audioObserverList.remove(observer);
69
+    }
70
+
71
+    public void saveCaptureVideoSnapshot(String filePath) {
72
+        beCaptureVideoShot = true;
73
+        captureFilePath = filePath;
74
+    }
75
+
76
+    public void saveRenderVideoSnapshot(String filePath, int uid) {
77
+        beRenderVideoShot = true;
78
+        renderFilePath = filePath;
79
+        renderVideoShotUid = uid;
80
+    }
81
+
82
+    public void addDecodeBuffer(int uid) {
83
+        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(VIDEO_DEFAULT_BUFFER_SIZE);
84
+        decodeBufferList.add(new DecodeDataBuffer(uid, byteBuffer));
85
+        MediaPreProcessing.setVideoDecodeByteBuffer(uid, byteBuffer);
86
+    }
87
+
88
+    public void removeDecodeBuffer(int uid) {
89
+        Iterator<DecodeDataBuffer> it = decodeBufferList.iterator();
90
+        while (it.hasNext()) {
91
+            DecodeDataBuffer buffer = it.next();
92
+            if (buffer.getUid() == uid) {
93
+                it.remove();
94
+            }
95
+        }
96
+
97
+        MediaPreProcessing.setVideoDecodeByteBuffer(uid, null);
98
+    }
99
+
100
+    public void removeAllBuffer() {
101
+        decodeBufferList.removeAll(decodeBufferList);
102
+        releaseBuffer();
103
+    }
104
+
105
+    @Override
106
+    public void onCaptureVideoFrame(int videoFrameType, int width, int height, int bufferLength, int yStride, int uStride, int vStride, int rotation, long renderTimeMs) {
107
+
108
+        byte[] buf = new byte[bufferLength];
109
+        byteBufferCapture.limit(bufferLength);
110
+        byteBufferCapture.get(buf);
111
+        byteBufferCapture.flip();
112
+
113
+        for (MediaDataVideoObserver observer : videoObserverList) {
114
+            observer.onCaptureVideoFrame(buf, videoFrameType, width, height, bufferLength, yStride, uStride, vStride, rotation, renderTimeMs);
115
+        }
116
+
117
+        byteBufferCapture.put(buf);
118
+        byteBufferCapture.flip();
119
+
120
+        if (beCaptureVideoShot) {
121
+            beCaptureVideoShot = false;
122
+
123
+            getVideoSnapshot(width, height, rotation, bufferLength, buf, captureFilePath, yStride, uStride, vStride);
124
+        }
125
+    }
126
+
127
+    @Override
128
+    public void onRenderVideoFrame(int uid, int videoFrameType, int width, int height, int bufferLength, int yStride, int uStride, int vStride, int rotation, long renderTimeMs) {
129
+
130
+        for (MediaDataVideoObserver observer : videoObserverList) {
131
+            Iterator<DecodeDataBuffer> it = decodeBufferList.iterator();
132
+            while (it.hasNext()) {
133
+                DecodeDataBuffer tmp = it.next();
134
+                if (tmp.getUid() == uid) {
135
+                    byte[] buf = new byte[bufferLength];
136
+                    tmp.getByteBuffer().limit(bufferLength);
137
+                    tmp.getByteBuffer().get(buf);
138
+                    tmp.getByteBuffer().flip();
139
+
140
+                    observer.onRenderVideoFrame(uid, buf, videoFrameType, width, height, bufferLength, yStride, uStride, vStride, rotation, renderTimeMs);
141
+
142
+                    tmp.getByteBuffer().put(buf);
143
+                    tmp.getByteBuffer().flip();
144
+
145
+                    if (beRenderVideoShot) {
146
+                        if (uid == renderVideoShotUid) {
147
+                            beRenderVideoShot = false;
148
+
149
+                            getVideoSnapshot(width, height, rotation, bufferLength, buf, renderFilePath, yStride, uStride, vStride);
150
+                        }
151
+                    }
152
+                }
153
+            }
154
+        }
155
+    }
156
+
157
+    @Override
158
+    public void onRecordAudioFrame(int audioFrameType, int samples, int bytesPerSample, int channels, int samplesPerSec, long renderTimeMs, int bufferLength) {
159
+        byte[] buf = new byte[bufferLength];
160
+        byteBufferAudioRecord.limit(bufferLength);
161
+        byteBufferAudioRecord.get(buf);
162
+        byteBufferAudioRecord.flip();
163
+
164
+        for (MediaDataAudioObserver observer : audioObserverList) {
165
+            observer.onRecordAudioFrame(buf, audioFrameType, samples, bytesPerSample, channels, samplesPerSec, renderTimeMs, bufferLength);
166
+        }
167
+
168
+        byteBufferAudioRecord.put(buf);
169
+        byteBufferAudioRecord.flip();
170
+    }
171
+
172
+    @Override
173
+    public void onPlaybackAudioFrame(int audioFrameType, int samples, int bytesPerSample, int channels, int samplesPerSec, long renderTimeMs, int bufferLength) {
174
+        byte[] buf = new byte[bufferLength];
175
+        byteBufferAudioPlay.limit(bufferLength);
176
+        byteBufferAudioPlay.get(buf);
177
+        byteBufferAudioPlay.flip();
178
+
179
+        for (MediaDataAudioObserver observer : audioObserverList) {
180
+            observer.onPlaybackAudioFrame(buf, audioFrameType, samples, bytesPerSample, channels, samplesPerSec, renderTimeMs, bufferLength);
181
+        }
182
+
183
+        byteBufferAudioPlay.put(buf);
184
+        byteBufferAudioPlay.flip();
185
+    }
186
+
187
+    @Override
188
+    public void onPlaybackAudioFrameBeforeMixing(int uid, int audioFrameType, int samples, int bytesPerSample, int channels, int samplesPerSec, long renderTimeMs, int bufferLength) {
189
+        byte[] buf = new byte[bufferLength];
190
+        byteBufferBeforeAudioMix.limit(bufferLength);
191
+        byteBufferBeforeAudioMix.get(buf);
192
+        byteBufferBeforeAudioMix.flip();
193
+
194
+        for (MediaDataAudioObserver observer : audioObserverList) {
195
+            observer.onPlaybackAudioFrameBeforeMixing(uid, buf, audioFrameType, samples, bytesPerSample, channels, samplesPerSec, renderTimeMs, bufferLength);
196
+        }
197
+
198
+        byteBufferBeforeAudioMix.put(buf);
199
+        byteBufferBeforeAudioMix.flip();
200
+    }
201
+
202
+    @Override
203
+    public void onMixedAudioFrame(int audioFrameType, int samples, int bytesPerSample, int channels, int samplesPerSec, long renderTimeMs, int bufferLength) {
204
+        byte[] buf = new byte[bufferLength];
205
+        byteBufferAudioMix.limit(bufferLength);
206
+        byteBufferAudioMix.get(buf);
207
+        byteBufferAudioMix.flip();
208
+
209
+        for (MediaDataAudioObserver observer : audioObserverList) {
210
+            observer.onMixedAudioFrame(buf, audioFrameType, samples, bytesPerSample, channels, samplesPerSec, renderTimeMs, bufferLength);
211
+        }
212
+
213
+        byteBufferAudioMix.put(buf);
214
+        byteBufferAudioMix.flip();
215
+    }
216
+
217
+    private void getVideoSnapshot(int width, int height, int rotation, int bufferLength, byte[] buffer, String filePath, int yStride, int uStride, int vStride) {
218
+        File file = new File(filePath);
219
+
220
+        byte[] NV21 = new byte[bufferLength];
221
+        swapYU12toYUV420SemiPlanar(buffer, NV21, width, height, yStride, uStride, vStride);
222
+
223
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
224
+
225
+        int[] strides = {yStride, yStride};
226
+        YuvImage image = new YuvImage(NV21, ImageFormat.NV21, width, height, strides);
227
+
228
+        image.compressToJpeg(
229
+                new Rect(0, 0, image.getWidth(), image.getHeight()),
230
+                100, baos);
231
+
232
+        // rotate picture when saving to file
233
+        Matrix matrix = new Matrix();
234
+        matrix.postRotate(rotation);
235
+        byte[] bytes = baos.toByteArray();
236
+        try {
237
+            baos.close();
238
+        } catch (IOException e) {
239
+            e.printStackTrace();
240
+        }
241
+        Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
242
+        Bitmap target = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
243
+
244
+        File fileParent = file.getParentFile();
245
+        if (!fileParent.exists()) {
246
+            fileParent.mkdirs();
247
+        }
248
+        if (file.exists()) {
249
+            file.delete();
250
+        }
251
+
252
+        try {
253
+            file.createNewFile();
254
+        } catch (IOException e) {
255
+            e.printStackTrace();
256
+        }
257
+
258
+        FileOutputStream fos = null;
259
+        try {
260
+            fos = new FileOutputStream(file);
261
+        } catch (FileNotFoundException e) {
262
+            e.printStackTrace();
263
+        }
264
+
265
+        target.compress(Bitmap.CompressFormat.JPEG, 100, fos);
266
+
267
+        target.recycle();
268
+        bitmap.recycle();
269
+
270
+        try {
271
+            fos.close();
272
+        } catch (IOException e) {
273
+            e.printStackTrace();
274
+        }
275
+    }
276
+
277
+    private void swapYU12toYUV420SemiPlanar(byte[] yu12bytes, byte[] i420bytes, int width, int height, int yStride, int uStride, int vStride) {
278
+        System.arraycopy(yu12bytes, 0, i420bytes, 0, yStride * height);
279
+        int startPos = yStride * height;
280
+        int yv_start_pos_u = startPos;
281
+        int yv_start_pos_v = startPos + startPos / 4;
282
+        for (int i = 0; i < startPos / 4; i++) {
283
+            i420bytes[startPos + 2 * i + 0] = yu12bytes[yv_start_pos_v + i];
284
+            i420bytes[startPos + 2 * i + 1] = yu12bytes[yv_start_pos_u + i];
285
+        }
286
+    }
287
+
288
+    public void releaseBuffer() {
289
+        byteBufferCapture.clear();
290
+        byteBufferRender.clear();
291
+        byteBufferAudioRecord.clear();
292
+        byteBufferAudioPlay.clear();
293
+        byteBufferBeforeAudioMix.clear();
294
+        byteBufferAudioMix.clear();
295
+    }
296
+
297
+}

+ 8
- 0
android/src/main/java/com/syan/agora/media/MediaDataVideoObserver.java View File

@@ -0,0 +1,8 @@
1
+package com.syan.agora.media;
2
+
3
+public interface MediaDataVideoObserver {
4
+
5
+    void onCaptureVideoFrame(byte[] data, int frameType, int width, int height, int bufferLength, int yStride, int uStride, int vStride, int rotation, long renderTimeMs);
6
+
7
+    void onRenderVideoFrame(int uid, byte[] data, int frameType, int width, int height, int bufferLength, int yStride, int uStride, int vStride, int rotation, long renderTimeMs);
8
+}

+ 39
- 0
android/src/main/java/com/syan/agora/media/MediaPreProcessing.java View File

@@ -0,0 +1,39 @@
1
+package com.syan.agora.media;
2
+
3
+import java.nio.ByteBuffer;
4
+
5
+public class MediaPreProcessing {
6
+    static {
7
+        System.loadLibrary("apm-plugin-raw-data-api-java");
8
+    }
9
+
10
+    public interface ProgressCallback {
11
+        void onCaptureVideoFrame(int videoFrameType, int width, int height, int bufferLength, int yStride, int uStride, int vStride, int rotation, long renderTimeMs);
12
+
13
+        void onRenderVideoFrame(int uid, int videoFrameType, int width, int height, int bufferLength, int yStride, int uStride, int vStride, int rotation, long renderTimeMs);
14
+
15
+        void onRecordAudioFrame(int audioFrameType, int samples, int bytesPerSample, int channels, int samplesPerSec, long renderTimeMs, int bufferLength);
16
+
17
+        void onPlaybackAudioFrame(int audioFrameType, int samples, int bytesPerSample, int channels, int samplesPerSec, long renderTimeMs, int bufferLength);
18
+
19
+        void onPlaybackAudioFrameBeforeMixing(int uid, int audioFrameType, int samples, int bytesPerSample, int channels, int samplesPerSec, long renderTimeMs, int bufferLength);
20
+
21
+        void onMixedAudioFrame(int audioFrameType, int samples, int bytesPerSample, int channels, int samplesPerSec, long renderTimeMs, int bufferLength);
22
+    }
23
+
24
+    public static native void setCallback(ProgressCallback callback);
25
+
26
+    public static native void setVideoCaptureByteBuffer(ByteBuffer byteBuffer);
27
+
28
+    public static native void setAudioRecordByteBuffer(ByteBuffer byteBuffer);
29
+
30
+    public static native void setAudioPlayByteBuffer(ByteBuffer byteBuffer);
31
+
32
+    public static native void setBeforeAudioMixByteBuffer(ByteBuffer byteBuffer);
33
+
34
+    public static native void setAudioMixByteBuffer(ByteBuffer byteBuffer);
35
+
36
+    public static native void setVideoDecodeByteBuffer(int uid, ByteBuffer byteBuffer);
37
+
38
+    public static native void releasePoint();
39
+}

+ 6
- 7
ios/RCTAgora/AgoraConst.h View File

@@ -28,19 +28,17 @@ static NSString *AGConnectionLost = @"connectionLost";
28 28
 static NSString *AGTokenPrivilegeWillExpire = @"tokenPrivilegeWillExpire";
29 29
 static NSString *AGRequestToken = @"requestToken";
30 30
 
31
-static NSString *AGMicrophoneEnabled = @"microphoneEnabled";
31
+static NSString *AGLocalAudioStateChanged = @"localAudioStateChanged";
32
+static NSString *AGRemoteAudioStateChanged = @"remoteAudioStateChanged";
33
+static NSString *AGLocalAudioStats = @"localAudioStats";
32 34
 static NSString *AGAudioVolumeIndication = @"audioVolumeIndication";
33 35
 static NSString *AGActiveSpeaker = @"activeSpeaker";
34 36
 static NSString *AGFirstLocalAudioFrame = @"firstLocalAudioFrame";
35 37
 static NSString *AGFirstRemoteAudioFrame = @"firstRemoteAudioFrame";
36 38
 static NSString *AGFirstRemoteAudioDecoded = @"firstRemoteAudioDecoded";
37 39
 static NSString *AGFirstLocalVideoFrame = @"firstLocalVideoFrame";
38
-static NSString *AGFirstRemoteVideoDecoded = @"firstRemoteVideoDecoded";
39 40
 static NSString *AGFirstRemoteVideoFrame = @"firstRemoteVideoFrame";
40 41
 static NSString *AGUserMuteAudio = @"userMuteAudio";
41
-static NSString *AGUserMuteVideo = @"userMuteVideo";
42
-static NSString *AGUserEnableVideo = @"userEnableVideo";
43
-static NSString *AGUserEnableLocalVideo = @"userEnableLocalVideo";
44 42
 static NSString *AGVideoSizeChanged = @"videoSizeChanged";
45 43
 static NSString *AGRemoteVideoStateChanged = @"remoteVideoStateChanged";
46 44
 static NSString *AGLocalPublishFallbackToAudioOnly = @"localPublishFallbackToAudioOnly";
@@ -56,8 +54,6 @@ static NSString *AGNetworkQuality = @"networkQuality";
56 54
 static NSString *AGLocalVideoStats = @"localVideoStats";
57 55
 static NSString *AGRemoteVideoStats = @"remoteVideoStats";
58 56
 static NSString *AGRemoteAudioStats = @"remoteAudioStats";
59
-static NSString *AGAudioTransportStatsOfUid = @"audioTransportStatsOfUid";
60
-static NSString *AGVideoTransportStatsOfUid = @"videoTransportStatsOfUid";
61 57
 
62 58
 static NSString *AGRemoteAudioMixingStart = @"remoteAudioMixingStart";
63 59
 static NSString *AGRemoteAudioMixingFinish = @"remoteAudioMixingFinish";
@@ -72,6 +68,9 @@ static NSString *AGStreamInjectedStatus = @"streamInjectedStatus";
72 68
 static NSString *AGReceiveStreamMessage = @"receiveStreamMessage";
73 69
 static NSString *AGOccurStreamMessageError = @"occurStreamMessageError";
74 70
 
71
+static NSString *AGReceivedChannelMediaRelay = @"receivedChannelMediaRelay";
72
+static NSString *AGMediaRelayStateChanged = @"mediaRelayStateChanged";
73
+
75 74
 static NSString *AGMediaEngineLoaded = @"mediaEngineLoaded";
76 75
 static NSString *AGMediaEngineStartCall = @"mediaEngineStartCall";
77 76
 

+ 5
- 7
ios/RCTAgora/AgoraConst.m View File

@@ -44,19 +44,14 @@ static AgoraConst *_person;
44 44
                                   AGTokenPrivilegeWillExpire,
45 45
                                   AGRequestToken,
46 46
                                   
47
-                                  AGMicrophoneEnabled,
48 47
                                   AGAudioVolumeIndication,
49 48
                                   AGActiveSpeaker,
50 49
                                   AGFirstLocalAudioFrame,
51 50
                                   AGFirstRemoteAudioFrame,
52 51
                                   AGFirstRemoteAudioDecoded,
53 52
                                   AGFirstLocalVideoFrame,
54
-                                  AGFirstRemoteVideoDecoded,
55 53
                                   AGFirstRemoteVideoFrame,
56 54
                                   AGUserMuteAudio,
57
-                                  AGUserMuteVideo,
58
-                                  AGUserEnableVideo,
59
-                                  AGUserEnableLocalVideo,
60 55
                                   AGVideoSizeChanged,
61 56
                                   AGRemoteVideoStateChanged,
62 57
                                   AGLocalPublishFallbackToAudioOnly,
@@ -72,8 +67,11 @@ static AgoraConst *_person;
72 67
                                   AGLocalVideoStats,
73 68
                                   AGRemoteVideoStats,
74 69
                                   AGRemoteAudioStats,
75
-                                  AGAudioTransportStatsOfUid,
76
-                                  AGVideoTransportStatsOfUid,
70
+                                  AGLocalAudioStateChanged,
71
+                                  AGRemoteAudioStateChanged,
72
+                                  AGLocalAudioStats,
73
+                                  AGMediaRelayStateChanged,
74
+                                  AGReceivedChannelMediaRelay,
77 75
                                   
78 76
                                   AGAudioMixingStateChanged,
79 77
                                   AGRemoteAudioMixingStart,

+ 189
- 83
ios/RCTAgora/RCTAgora.m View File

@@ -48,7 +48,7 @@
48 48
   return toSend;
49 49
 }
50 50
 
51
-- (void)receiveMetadata:(NSData *_Nonnull)data fromUser:(NSUInteger)uid atTimestamp:(NSTimeInterval)timestamp {
51
+- (void)receiveMetadata:(NSData *_Nonnull)data fromUser:(NSInteger)uid atTimestamp:(NSTimeInterval)timestamp {
52 52
   NSString *dataStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
53 53
   [self sendEvent:AGMediaMetaDataReceived params:@{
54 54
                                                    @"uid": @(uid),
@@ -301,6 +301,111 @@ RCT_EXPORT_METHOD(joinChannel:(NSDictionary *)options
301 301
   }
302 302
 }
303 303
 
304
+// switch channel
305
+RCT_EXPORT_METHOD(switchChannel:(NSDictionary *)options
306
+                  resolve:(RCTPromiseResolveBlock)resolve
307
+                  reject:(RCTPromiseRejectBlock)reject) {
308
+  NSInteger res = [self.rtcEngine switchChannelByToken:options[@"token"] channelId:options[@"channelName"] joinSuccess:nil];
309
+  if (res == 0) {
310
+    resolve(nil);
311
+  } else {
312
+    reject(@(-1).stringValue, @(res).stringValue, nil);
313
+  }
314
+}
315
+
316
+// startChannelMediaRelay
317
+RCT_EXPORT_METHOD(startChannelMediaRelay:(NSDictionary *)options
318
+                  resolve:(RCTPromiseResolveBlock)resolve
319
+                  reject:(RCTPromiseRejectBlock)reject) {
320
+  AgoraChannelMediaRelayConfiguration *config = [[AgoraChannelMediaRelayConfiguration alloc] init];
321
+  AgoraChannelMediaRelayInfo *src = [config sourceInfo];
322
+  NSDictionary *srcOption = options[@"src"];
323
+  if (srcOption != nil) {
324
+    src.channelName = srcOption[@"channelName"];
325
+    src.uid = [srcOption[@"uid"] integerValue];
326
+    src.token = srcOption[@"token"];
327
+  }
328
+  NSArray *channels = options[@"channels"];
329
+  for (NSDictionary *channel in channels) {
330
+    AgoraChannelMediaRelayInfo *dst = [[AgoraChannelMediaRelayInfo alloc] init];
331
+    dst.channelName = channel[@"channelName"];
332
+    dst.uid = [channel[@"uid"] integerValue];
333
+    dst.token = channel[@"token"];
334
+    [config setDestinationInfo:dst forChannelName:dst.channelName];
335
+  }
336
+  NSInteger res = [self.rtcEngine startChannelMediaRelay:config];
337
+  if (res == 0) {
338
+    resolve(nil);
339
+  } else {
340
+    reject(@(-1).stringValue, @(res).stringValue, nil);
341
+  }
342
+}
343
+
344
+// updateChannelMediaRelay
345
+RCT_EXPORT_METHOD(updateChannelMediaRelay:(NSDictionary *)options
346
+                  resolve:(RCTPromiseResolveBlock)resolve
347
+                  reject:(RCTPromiseRejectBlock)reject) {
348
+  AgoraChannelMediaRelayConfiguration *config = [[AgoraChannelMediaRelayConfiguration alloc] init];
349
+  AgoraChannelMediaRelayInfo *src = [config sourceInfo];
350
+  NSDictionary *srcOption = options[@"src"];
351
+  if (srcOption != nil) {
352
+    src.channelName = srcOption[@"channelName"];
353
+    src.uid = [srcOption[@"uid"] integerValue];
354
+    src.token = srcOption[@"token"];
355
+  }
356
+  NSArray *channels = options[@"channels"];
357
+  for (NSDictionary *channel in channels) {
358
+    AgoraChannelMediaRelayInfo *dst = [[AgoraChannelMediaRelayInfo alloc] init];
359
+    dst.channelName = channel[@"channelName"];
360
+    dst.uid = [channel[@"uid"] integerValue];
361
+    dst.token = channel[@"token"];
362
+    [config setDestinationInfo:dst forChannelName:dst.channelName];
363
+  }
364
+  NSInteger res = [self.rtcEngine updateChannelMediaRelay:config];
365
+  if (res == 0) {
366
+    resolve(nil);
367
+  } else {
368
+    reject(@(-1).stringValue, @(res).stringValue, nil);
369
+  }
370
+}
371
+
372
+// removeChannelMediaRelay
373
+RCT_EXPORT_METHOD(removeChannelMediaRelay:(NSDictionary *)options
374
+                  resolve:(RCTPromiseResolveBlock)resolve
375
+                  reject:(RCTPromiseRejectBlock)reject) {
376
+  AgoraChannelMediaRelayConfiguration *config = [[AgoraChannelMediaRelayConfiguration alloc] init];
377
+  AgoraChannelMediaRelayInfo *src = [config sourceInfo];
378
+  NSDictionary *srcOption = options[@"src"];
379
+  if (srcOption != nil) {
380
+    src.channelName = srcOption[@"channelName"];
381
+    src.uid = [srcOption[@"uid"] integerValue];
382
+    src.token = srcOption[@"token"];
383
+  }
384
+  NSArray *channels = options[@"channels"];
385
+  for (NSDictionary *channel in channels) {
386
+    if (channel[@"channelName"] != nil) {
387
+      [config removeDestinationInfoForChannelName:channel[@"channelName"]];
388
+    }
389
+  }
390
+  NSInteger res = [self.rtcEngine updateChannelMediaRelay:config];
391
+  if (res == 0) {
392
+    resolve(nil);
393
+  } else {
394
+    reject(@(-1).stringValue, @(res).stringValue, nil);
395
+  }
396
+}
397
+
398
+// stopChannelMediaRelay
399
+RCT_EXPORT_METHOD(stopChannelMediaRelay:(RCTPromiseResolveBlock)resolve
400
+                  reject:(RCTPromiseRejectBlock)reject) {
401
+  NSInteger res = [self.rtcEngine stopChannelMediaRelay];
402
+  if (res == 0) {
403
+    resolve(nil);
404
+  } else {
405
+    reject(@(-1).stringValue, @(res).stringValue, nil);
406
+  }
407
+}
408
+
304 409
 // register user account
305 410
 RCT_EXPORT_METHOD(registerLocalUserAccount:(NSDictionary *)options
306 411
                   resolve:(RCTPromiseResolveBlock)resolve
@@ -334,9 +439,9 @@ RCT_EXPORT_METHOD(getUserInfoByUid:(NSUInteger)uid
334 439
   AgoraUserInfo *info = [self.rtcEngine getUserInfoByUid:uid withError:&code];
335 440
   if ((int)code == 0) {
336 441
     resolve(@{
337
-        @"uid": @(info.uid),
338
-        @"userAccount": info.userAccount
339
-    });
442
+              @"uid": @(info.uid),
443
+              @"userAccount": info.userAccount
444
+              });
340 445
   } else {
341 446
     reject(@(-1).stringValue, @((int)code).stringValue, nil);
342 447
   }
@@ -362,20 +467,26 @@ RCT_EXPORT_METHOD(getUserInfoByUserAccount:(NSString *)userAccount
362 467
 RCT_EXPORT_METHOD(leaveChannel
363 468
                   :(RCTPromiseResolveBlock) resolve
364 469
                   reject:(RCTPromiseRejectBlock) reject) {
365
-  NSInteger res = [self.rtcEngine leaveChannel:^(AgoraChannelStats * _Nonnull stat) {
470
+  NSInteger res = [self.rtcEngine leaveChannel:^(AgoraChannelStats * _Nonnull stats) {
366 471
     [self sendEvent:AGLeaveChannel params:@{
367 472
                                             @"message": @"leaveChannel",
368
-                                            @"duration": @(stat.duration),
369
-                                            @"txBytes": @(stat.txBytes),
370
-                                            @"rxBytes": @(stat.rxBytes),
371
-                                            @"txAudioKBitrate": @(stat.txAudioKBitrate),
372
-                                            @"rxAudioKBitrate": @(stat.rxAudioKBitrate),
373
-                                            @"txVideoKBitrate": @(stat.txVideoKBitrate),
374
-                                            @"rxVideoKBitrate": @(stat.rxVideoKBitrate),
375
-                                            @"lastmileDelay": @(stat.lastmileDelay),
376
-                                            @"userCount": @(stat.userCount),
377
-                                            @"cpuAppUsage": @(stat.cpuAppUsage),
378
-                                            @"cpuTotalUsage": @(stat.cpuTotalUsage)
473
+                                            @"duration": @(stats.duration),
474
+                                            @"txBytes": @(stats.txBytes),
475
+                                            @"rxBytes": @(stats.rxBytes),
476
+                                            @"txAudioBytes": @(stats.txAudioBytes),
477
+                                            @"txVideoBytes": @(stats.txVideoBytes),
478
+                                            @"rxAudioBytes": @(stats.rxAudioBytes),
479
+                                            @"rxVideoBytes": @(stats.rxVideoBytes),
480
+                                            @"txPacketLossRate": @(stats.txPacketLossRate),
481
+                                            @"rxPacketLossRate": @(stats.rxPacketLossRate),
482
+                                            @"txAudioKBitrate": @(stats.txAudioKBitrate),
483
+                                            @"rxAudioKBitrate": @(stats.rxAudioKBitrate),
484
+                                            @"txVideoKBitrate": @(stats.txVideoKBitrate),
485
+                                            @"rxVideoKBitrate": @(stats.rxVideoKBitrate),
486
+                                            @"lastmileDelay": @(stats.lastmileDelay),
487
+                                            @"userCount": @(stats.userCount),
488
+                                            @"cpuAppUsage": @(stats.cpuAppUsage),
489
+                                            @"cpuTotalUsage": @(stats.cpuTotalUsage)
379 490
                                             }];
380 491
   }];
381 492
   if (res == 0) {
@@ -909,7 +1020,7 @@ RCT_EXPORT_METHOD(setEffectsVolume
909 1020
 
910 1021
 // set volume of effect
911 1022
 RCT_EXPORT_METHOD(setVolumeOfEffect
912
-                  :(NSInteger) soundId
1023
+                  :(int) soundId
913 1024
                   volume:(double)volume
914 1025
                   resolve:(RCTPromiseResolveBlock)resolve
915 1026
                   reject:(RCTPromiseRejectBlock)reject) {
@@ -1699,11 +1810,11 @@ RCT_EXPORT_METHOD(registerMediaMetadataObserver
1699 1810
 #pragma mark - <AgoraRtcEngineDelegate>
1700 1811
 // EVENT CALLBACKS
1701 1812
 - (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine didOccurWarning:(AgoraWarningCode)warningCode {
1702
-  [self sendEvent:AGWarning params:@{@"message": @"AgoraWarning", @"code": @(warningCode)}];
1813
+  [self sendEvent:AGWarning params:@{@"message": @"AgoraWarning", @"errorCode": @(warningCode)}];
1703 1814
 }
1704 1815
 
1705 1816
 - (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine didOccurError:(AgoraErrorCode)errorCode {
1706
-  [self sendEvent:AGError params:@{@"message": @"AgoraError", @"code": @(errorCode)}];
1817
+  [self sendEvent:AGError params:@{@"message": @"AgoraError", @"errorCode": @(errorCode)}];
1707 1818
 }
1708 1819
 
1709 1820
 - (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine didApiCallExecute:(NSInteger)error api:(NSString *_Nonnull)api result:(NSString *_Nonnull)result {
@@ -1711,13 +1822,13 @@ RCT_EXPORT_METHOD(registerMediaMetadataObserver
1711 1822
     [self sendEvent:AGError  params:@{
1712 1823
                                       @"api": api,
1713 1824
                                       @"result": result,
1714
-                                      @"error": @(error)
1825
+                                      @"errorCode": @(error)
1715 1826
                                       }];
1716 1827
   } else {
1717 1828
     [self sendEvent:AGApiCallExecute  params:@{
1718 1829
                                                @"api": api,
1719 1830
                                                @"result": result,
1720
-                                               @"error": @(error)
1831
+                                               @"errorCode": @(error)
1721 1832
                                                }];
1722 1833
   }
1723 1834
 }
@@ -1744,6 +1855,12 @@ RCT_EXPORT_METHOD(registerMediaMetadataObserver
1744 1855
                                               @"duration": @(stats.duration),
1745 1856
                                               @"txBytes": @(stats.txBytes),
1746 1857
                                               @"rxBytes": @(stats.rxBytes),
1858
+                                              @"txAudioBytes": @(stats.txAudioBytes),
1859
+                                              @"txVideoBytes": @(stats.txVideoBytes),
1860
+                                              @"rxAudioBytes": @(stats.rxAudioBytes),
1861
+                                              @"rxVideoBytes": @(stats.rxVideoBytes),
1862
+                                              @"txPacketLossRate": @(stats.txPacketLossRate),
1863
+                                              @"rxPacketLossRate": @(stats.rxPacketLossRate),
1747 1864
                                               @"txAudioKBitrate": @(stats.txAudioKBitrate),
1748 1865
                                               @"rxAudioKBitrate": @(stats.rxVideoKBitrate),
1749 1866
                                               @"txVideoKBitrate": @(stats.txVideoKBitrate),
@@ -1779,10 +1896,10 @@ RCT_EXPORT_METHOD(registerMediaMetadataObserver
1779 1896
 
1780 1897
 - (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine didUpdatedUserInfo:(AgoraUserInfo *_Nonnull)userInfo withUid:(NSUInteger)uid {
1781 1898
   [self sendEvent:AGUserInfoUpdated params:@{
1782
-                                                 @"uid": @(uid),
1783
-                                                 @"peer": @{
1784
-                                                     @"uid": @(userInfo.uid),
1785
-                                                     @"userAccount": userInfo.userAccount
1899
+                                             @"uid": @(uid),
1900
+                                             @"peer": @{
1901
+                                                 @"uid": @(userInfo.uid),
1902
+                                                 @"userAccount": userInfo.userAccount
1786 1903
                                                  }}];
1787 1904
 }
1788 1905
 
@@ -1825,10 +1942,28 @@ RCT_EXPORT_METHOD(registerMediaMetadataObserver
1825 1942
                                           }];
1826 1943
 }
1827 1944
 
1828
-- (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine didMicrophoneEnabled:(BOOL)enabled {
1829
-  [self sendEvent:AGMicrophoneEnabled params:@{
1830
-                                               @"enabled": @(enabled)
1831
-                                               }];
1945
+- (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine localAudioStateChange:(AgoraAudioLocalState)state error:(AgoraAudioLocalError)error {
1946
+  [self sendEvent:AGLocalAudioStateChanged params:@{
1947
+                                                    @"state": @(state),
1948
+                                                    @"errorCode": @(error)
1949
+                                                    }];
1950
+}
1951
+
1952
+- (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine remoteAudioStateChangedOfUid:(NSUInteger)uid state:(AgoraAudioRemoteState)state reason:(AgoraAudioRemoteStateReason)reason elapsed:(NSInteger)elapsed {
1953
+  [self sendEvent:AGRemoteAudioStateChanged params:@{
1954
+                                                     @"uid": @(uid),
1955
+                                                     @"state": @(state),
1956
+                                                     @"reason": @(reason),
1957
+                                                     @"elapsed": @(elapsed)
1958
+                                                     }];
1959
+}
1960
+
1961
+- (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine localAudioStats:(AgoraRtcLocalAudioStats *_Nonnull)stats {
1962
+  [self sendEvent:AGLocalAudioStats params:@{
1963
+                                             @"numChannels": @(stats.numChannels),
1964
+                                             @"sentSampleRate": @(stats.sentSampleRate),
1965
+                                             @"sentBitrate": @(stats.sentBitrate),
1966
+                                             }];
1832 1967
 }
1833 1968
 
1834 1969
 - (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine reportAudioVolumeIndicationOfSpeakers:(NSArray<AgoraRtcAudioVolumeInfo*> *_Nonnull)speakers totalVolume:(NSInteger)totalVolume {
@@ -1879,15 +2014,6 @@ RCT_EXPORT_METHOD(registerMediaMetadataObserver
1879 2014
                                                   }];
1880 2015
 }
1881 2016
 
1882
-- (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine firstRemoteVideoDecodedOfUid:(NSUInteger)uid size:(CGSize)size elapsed:(NSInteger)elapsed {
1883
-  [self sendEvent:AGFirstRemoteVideoDecoded params:@{
1884
-                                                     @"uid": @(uid),
1885
-                                                     @"width": @(size.width),
1886
-                                                     @"height": @(size.height),
1887
-                                                     @"elapsed": @(elapsed)
1888
-                                                     }];
1889
-}
1890
-
1891 2017
 - (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine firstRemoteVideoFrameOfUid:(NSUInteger)uid size:(CGSize)size elapsed:(NSInteger)elapsed {
1892 2018
   [self sendEvent:AGFirstRemoteVideoFrame params:@{
1893 2019
                                                    @"uid": @(uid),
@@ -1903,27 +2029,6 @@ RCT_EXPORT_METHOD(registerMediaMetadataObserver
1903 2029
                                            }];
1904 2030
 }
1905 2031
 
1906
-- (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine didVideoMuted:(BOOL)muted byUid:(NSUInteger)uid {
1907
-  [self sendEvent:AGUserMuteVideo params:@{
1908
-                                           @"muted": @(muted),
1909
-                                           @"uid": @(uid)
1910
-                                           }];
1911
-}
1912
-
1913
-- (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine didVideoEnabled:(BOOL)enabled byUid:(NSUInteger)uid {
1914
-  [self sendEvent:AGUserEnableVideo params:@{
1915
-                                             @"enabled": @(enabled),
1916
-                                             @"uid": @(uid)
1917
-                                             }];
1918
-}
1919
-
1920
-- (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine didLocalVideoEnabled:(BOOL)enabled byUid:(NSUInteger)uid {
1921
-  [self sendEvent:AGUserEnableLocalVideo params:@{
1922
-                                                  @"enabled": @(enabled),
1923
-                                                  @"uid": @(uid)
1924
-                                                  }];
1925
-}
1926
-
1927 2032
 - (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine videoSizeChangedOfUid:(NSUInteger)uid size:(CGSize)size rotation:(NSInteger)rotation {
1928 2033
   [self sendEvent:AGVideoSizeChanged params:@{
1929 2034
                                               @"uid": @(uid),
@@ -1933,10 +2038,12 @@ RCT_EXPORT_METHOD(registerMediaMetadataObserver
1933 2038
                                               }];
1934 2039
 }
1935 2040
 
1936
-- (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine remoteVideoStateChangedOfUid:(NSUInteger)uid state:(AgoraVideoRemoteState)state {
2041
+- (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine remoteVideoStateChangedOfUid:(NSUInteger)uid state:(AgoraVideoRemoteState)state reason:(AgoraVideoRemoteStateReason)reason elapsed:(NSInteger)elapsed {
1937 2042
   [self sendEvent:AGRemoteVideoStateChanged params:@{
1938 2043
                                                      @"uid": @(uid),
1939
-                                                     @"state": @(state)
2044
+                                                     @"state": @(state),
2045
+                                                     @"reason": @(reason),
2046
+                                                     @"elapsed": @(elapsed)
1940 2047
                                                      }];
1941 2048
 }
1942 2049
 
@@ -1992,10 +2099,14 @@ RCT_EXPORT_METHOD(registerMediaMetadataObserver
1992 2099
   [self sendEvent:AGRtcStats params:@{
1993 2100
                                       @"stats": @{
1994 2101
                                           @"duration": @(stats.duration),
1995
-                                          @"txPacketLossRate": @(stats.txPacketLossRate),
1996
-                                          @"rxPacketLossRate": @(stats.rxPacketLossRate),
1997 2102
                                           @"txBytes": @(stats.txBytes),
1998 2103
                                           @"rxBytes": @(stats.rxBytes),
2104
+                                          @"txAudioBytes": @(stats.txAudioBytes),
2105
+                                          @"txVideoBytes": @(stats.txVideoBytes),
2106
+                                          @"rxAudioBytes": @(stats.rxAudioBytes),
2107
+                                          @"rxVideoBytes": @(stats.rxVideoBytes),
2108
+                                          @"txPacketLossRate": @(stats.txPacketLossRate),
2109
+                                          @"rxPacketLossRate": @(stats.rxPacketLossRate),
1999 2110
                                           @"txAudioKBitrate": @(stats.txAudioKBitrate),
2000 2111
                                           @"rxAudioKBitrate": @(stats.rxAudioKBitrate),
2001 2112
                                           @"txVideoKBitrate": @(stats.txVideoKBitrate),
@@ -2050,24 +2161,6 @@ RCT_EXPORT_METHOD(registerMediaMetadataObserver
2050 2161
                                               }];
2051 2162
 }
2052 2163
 
2053
-- (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine audioTransportStatsOfUid:(NSUInteger)uid delay:(NSUInteger)delay lost:(NSUInteger)lost rxKBitRate:(NSUInteger)rxKBitRate {
2054
-  [self sendEvent:AGAudioTransportStatsOfUid params:@{
2055
-                                                      @"uid": @(uid),
2056
-                                                      @"delay": @(delay),
2057
-                                                      @"lost": @(lost),
2058
-                                                      @"rxKBitrate": @(rxKBitRate)
2059
-                                                      }];
2060
-}
2061
-
2062
-- (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine videoTransportStatsOfUid:(NSUInteger)uid delay:(NSUInteger)delay lost:(NSUInteger)lost rxKBitRate:(NSUInteger)rxKBitRate {
2063
-  [self sendEvent:AGVideoTransportStatsOfUid params:@{
2064
-                                                      @"uid": @(uid),
2065
-                                                      @"delay": @(delay),
2066
-                                                      @"lost": @(lost),
2067
-                                                      @"rxKBitrate": @(rxKBitRate)
2068
-                                                      }];
2069
-}
2070
-
2071 2164
 - (void)rtcEngineRemoteAudioMixingDidStart:(AgoraRtcEngineKit *_Nonnull)engine {
2072 2165
   [self sendEvent:AGRemoteAudioMixingStart params:@{
2073 2166
                                                     @"message": @"RemoteAudioMixingStarted"
@@ -2089,7 +2182,7 @@ RCT_EXPORT_METHOD(registerMediaMetadataObserver
2089 2182
 - (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine streamPublishedWithUrl:(NSString *_Nonnull)url errorCode:(AgoraErrorCode)errorCode {
2090 2183
   [self sendEvent:AGStreamPublished params:@{
2091 2184
                                              @"url": url,
2092
-                                             @"code": @(errorCode)
2185
+                                             @"errorCode": @(errorCode)
2093 2186
                                              }];
2094 2187
 }
2095 2188
 
@@ -2128,6 +2221,19 @@ RCT_EXPORT_METHOD(registerMediaMetadataObserver
2128 2221
                                                   }];
2129 2222
 }
2130 2223
 
2224
+- (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine channelMediaRelayStateDidChange:(AgoraChannelMediaRelayState)state error:(AgoraChannelMediaRelayError)error {
2225
+  [self sendEvent:AGMediaRelayStateChanged params:@{
2226
+                                                    @"state": @(state),
2227
+                                                    @"errorCode": @(error),
2228
+                                                    }];
2229
+}
2230
+
2231
+- (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine didReceiveChannelMediaRelayEvent:(AgoraChannelMediaRelayEvent)event {
2232
+  [self sendEvent:AGReceivedChannelMediaRelay params:@{
2233
+                                                       @"event": @(event),
2234
+                                                       }];
2235
+}
2236
+
2131 2237
 - (void)rtcEngine:(AgoraRtcEngineKit *_Nonnull)engine receiveStreamMessageFromUid:(NSUInteger)uid streamId:(NSInteger)streamId data:(NSData *_Nonnull)data {
2132 2238
   NSString *_data = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
2133 2239
   [self sendEvent:AGReceiveStreamMessage params:@{
@@ -2140,7 +2246,7 @@ RCT_EXPORT_METHOD(registerMediaMetadataObserver
2140 2246
   [self sendEvent:AGOccurStreamMessageError params:@{
2141 2247
                                                      @"uid": @(uid),
2142 2248
                                                      @"streamId": @(streamId),
2143
-                                                     @"error": @(error),
2249
+                                                     @"errorCode": @(error),
2144 2250
                                                      @"missed": @(missed),
2145 2251
                                                      @"cached": @(cached)
2146 2252
                                                      }];

+ 54
- 8
lib/RtcEngine.native.d.ts View File

@@ -1,4 +1,4 @@
1
-import { Option, Callback, AgoraUserInfo, AudioMixingOption, PlayEffectOption, AudioRecordingOption, AudioFrameOption, MixedAudioFrameOption, ImageOption, VideoStreamOption, DefaultVideoStreamOption, InjectStreamOption, RemoveInjectStreamOption, PublishStreamOption, RemovePublishStreamOption, LiveTranscodingOption, PositionOption, BeautyOption, LastmileProbeConfig, CameraCapturerConfiguration } from "./types";
1
+import { Option, Callback, AgoraUserInfo, AudioMixingOption, PlayEffectOption, AudioRecordingOption, AudioFrameOption, MixedAudioFrameOption, ImageOption, VideoStreamOption, DefaultVideoStreamOption, InjectStreamOption, RemoveInjectStreamOption, PublishStreamOption, RemovePublishStreamOption, LiveTranscodingOption, PositionOption, BeautyOption, LastmileProbeConfig, CameraCapturerConfiguration, ChannelMediaConfiguration } from "./types";
2 2
 /**
3 3
  * RtcEngine is the javascript object for control agora native sdk through react native bridge.
4 4
  *
@@ -34,6 +34,54 @@ declare class RtcEngine {
34 34
      * @param info
35 35
      */
36 36
     static joinChannel(channelName: string, uid?: number, token?: string, info?: Object): Promise<any>;
37
+    /**
38
+     * switch to specified channel
39
+     *
40
+     * This method will switch channel smoothly than you invoke leaveChannel & joinChannel.
41
+     * Otherwise, it will invoke error by the event
42
+     * It will occurs two events:
43
+     * Occurs leaveChannel when achieve leaving stage
44
+     * Occurs joinChannelSuccess when achieve joining stage
45
+     * @param channelName
46
+     * @param token
47
+     */
48
+    static switchChannel(channelName: string, token?: string): Promise<any>;
49
+    /**
50
+     * Starts to relay media streams across channels.
51
+     *
52
+     * This method will start relay media stream across specified channels. (maximum support 4 channels)
53
+     * It will occurs event:
54
+     *  Occurs onChannelMediaRelayStateChanged
55
+     * @param config
56
+     */
57
+    static startChannelMediaRelay(config: ChannelMediaConfiguration): Promise<any>;
58
+    /**
59
+     * Remove to relay media streams across channels.
60
+     *
61
+     * This method will remove & update relay media stream across specified channels. (maximum support relay 4 channels)
62
+     * It will occurs event:
63
+     *  Occurs onChannelMediaRelayStateChanged
64
+     * @param config
65
+     */
66
+    static removeChannelMediaRelay(config: ChannelMediaConfiguration): Promise<any>;
67
+    /**
68
+     * Updates to relay media streams across channels.
69
+     *
70
+     * This method will update relay media stream across specified channels. (maximum support 4 channels)
71
+     * It will occurs event:
72
+     *  Occurs onChannelMediaRelayStateChanged
73
+     * @param config
74
+     */
75
+    static updateChannelMediaRelay(config: ChannelMediaConfiguration): Promise<any>;
76
+    /**
77
+     * Stop to relay media streams across channels.
78
+     *
79
+     * This method will stop relay media stream across specified channels.
80
+     * It will occurs event:
81
+     *  Occurs onChannelMediaRelayStateChanged
82
+     * @param config
83
+     */
84
+    static stopChannelMediaRelay(): Promise<any>;
37 85
     /**
38 86
      * Registers a user account.
39 87
      *
@@ -117,21 +165,19 @@ declare class RtcEngine {
117 165
      * connectionLost | occurs when sdk connection lost | on("connectionLost", evt) |
118 166
      * tokenPrivilegeWillExpire | occurs when token will expire | on("tokenPrivilegeWillExpire", evt) |
119 167
      * requestToken | occurs when token expired | on("requestToken") |
120
-     * microphoneEnabled | occurs when microphone enable state changed | on("microphoneEnabled", evt) |
168
+     * localAudioStateChanged | occurs when local audio device state changed | on("localAudioStateChanged", (state, errorCode) => {}) |
121 169
      * audioVolumeIndication | occurs when audio volume indication changed | on("audioVolumeIndication", evt) |
122 170
      * activeSpeaker | occurs when detect active speaker | on("activeSpeaker", evt) |
123 171
      * firstLocalAudioFrame | occurs when sent first audio frame on local | on("firstLocalAudioFrame", evt) |
124 172
      * firstRemoteAudioFrame | occurs when received first audio frame from remote side | on("firstRemoteAudioFrame", evt) |
125 173
      * firstRemoteAudioDecoded | occurs when first remote audio decoded | on("firstRemoteAudioDecoded", evt) |
126 174
      * firstLocalVideoFrame | occurs when sent first video frame on local | on("firstLocalVideoFrame", evt) |
127
-     * firstRemoteVideoDecoded | occurs when received first video frame from remote side decoded | on("firstRemoteVideoDecoded", evt) |
128 175
      * firstRemoteVideoFrame | occurs when received first video frame from remote side | on("firstRemoteVideoFrame", evt) |
129 176
      * userMuteAudio | occurs when user mute audio | on("userMuteAudio", evt) |
130
-     * userMuteVideo | occurs when user mute video | on("userMuteVideo", evt) |
131
-     * userEnableVideo | occurs when remote side's user change video enable state | on("userEnableVideo", evt) |
132
-     * userEnableLocalVideo | occurs when user change video enable state on local | on("userEnableLocalVideo", evt) |
133 177
      * videoSizeChanged | occurs when change local or remote side video size or rotation | on("videoSizeChanged", evt) |
134 178
      * remoteVideoStateChanged | occurs when remote video state has any changed | on("remoteVideoStateChanged", evt) |
179
+     * remoteAudioStateChanged | occurs when remote audio state has any changed | on("remoteAudioStateChanged", evt) |
180
+     * localAudioStats | occurs when engine start to report local audio stats | on("localAudioStats", evt) |
135 181
      * localPublishFallbackToAudioOnly | occurs when published stream from local side fallback to audio stream | on("localPublishFallbackToAudioOnly", evt) |
136 182
      * remoteSubscribeFallbackToAudioOnly | occurs when subscribed side's stream fallback to audio stream | on("remoteSubscribeFallbackToAudioOnly", evt) |
137 183
      * audioRouteChanged | occurs when local audio route changed | on("audioRouteChanged", evt) |
@@ -143,8 +189,6 @@ declare class RtcEngine {
143 189
      * localVideoStats | occurs when reports local video statistics | on("localVideoStats", evt) |
144 190
      * remoteVideoStats | occurs when reports remote video statistics| on("remoteVideoStats", evt) |
145 191
      * remoteAudioStats | occurs when reports remote audio statistics| on("remoteAudioStats", evt) |
146
-     * audioTransportStatsOfUid | occurs when reports  transport-layer statistics of each remote audio stream. | on("audioTransportStatsOfUid", evt) |
147
-     * videoTransportStatsOfUid | occurs when reports  transport-layer statistics of each remote video stream.| on("videoTransportStatsOfUid", evt) |
148 192
      * audioEffectFinish | occurs when the local audio effect playback finishes. | on("audioEffectFinish", evt) |
149 193
      * streamPublished | occurs when addPublishStreamUrl success| on("streamPublished", evt) |
150 194
      * streamUnpublish | occurs when removePublishStreamUrl success| on("streamUnpublish", evt) |
@@ -161,6 +205,8 @@ declare class RtcEngine {
161 205
      * mediaMetaDataReceived | occurs when you received media meta data from the remote side through sendMediaData | on("mediaMetaDataReceived", evt) |
162 206
      * localUserRegistered | occurs when you register user account success | on("localUserRegistered", evt) |
163 207
      * userInfoUpdated | occurs when you peer side using user account join channel | on("userInfoUpdated", evt) |
208
+     * receivedChannelMediaRelay | occurs when you received channel media relay | on('receivedChannelMediaRelay", evt)|
209
+     * mediaRelayStateChanged | occurs when you received remote media relay state changed | on('mediaRelayStateChanged", evt)|
164 210
      * ---
165 211
      *
166 212
      * @param eventType

+ 64
- 14
lib/RtcEngine.native.js View File

@@ -35,6 +35,64 @@ class RtcEngine {
35 35
     static joinChannel(channelName, uid, token, info) {
36 36
         return Agora.joinChannel({ channelName, uid, token, info });
37 37
     }
38
+    /**
39
+     * switch to specified channel
40
+     *
41
+     * This method will switch channel smoothly than you invoke leaveChannel & joinChannel.
42
+     * Otherwise, it will invoke error by the event
43
+     * It will occurs two events:
44
+     * Occurs leaveChannel when achieve leaving stage
45
+     * Occurs joinChannelSuccess when achieve joining stage
46
+     * @param channelName
47
+     * @param token
48
+     */
49
+    static switchChannel(channelName, token) {
50
+        return Agora.switchChannel({ channelName, token });
51
+    }
52
+    /**
53
+     * Starts to relay media streams across channels.
54
+     *
55
+     * This method will start relay media stream across specified channels. (maximum support 4 channels)
56
+     * It will occurs event:
57
+     *  Occurs onChannelMediaRelayStateChanged
58
+     * @param config
59
+     */
60
+    static startChannelMediaRelay(config) {
61
+        return Agora.startChannelMediaRelay(config);
62
+    }
63
+    /**
64
+     * Remove to relay media streams across channels.
65
+     *
66
+     * This method will remove & update relay media stream across specified channels. (maximum support relay 4 channels)
67
+     * It will occurs event:
68
+     *  Occurs onChannelMediaRelayStateChanged
69
+     * @param config
70
+     */
71
+    static removeChannelMediaRelay(config) {
72
+        return Agora.removeChannelMediaRelay(config);
73
+    }
74
+    /**
75
+     * Updates to relay media streams across channels.
76
+     *
77
+     * This method will update relay media stream across specified channels. (maximum support 4 channels)
78
+     * It will occurs event:
79
+     *  Occurs onChannelMediaRelayStateChanged
80
+     * @param config
81
+     */
82
+    static updateChannelMediaRelay(config) {
83
+        return Agora.updateChannelMediaRelay(config);
84
+    }
85
+    /**
86
+     * Stop to relay media streams across channels.
87
+     *
88
+     * This method will stop relay media stream across specified channels.
89
+     * It will occurs event:
90
+     *  Occurs onChannelMediaRelayStateChanged
91
+     * @param config
92
+     */
93
+    static stopChannelMediaRelay() {
94
+        return Agora.stopChannelMediaRelay();
95
+    }
38 96
     /**
39 97
      * Registers a user account.
40 98
      *
@@ -141,21 +199,19 @@ class RtcEngine {
141 199
      * connectionLost | occurs when sdk connection lost | on("connectionLost", evt) |
142 200
      * tokenPrivilegeWillExpire | occurs when token will expire | on("tokenPrivilegeWillExpire", evt) |
143 201
      * requestToken | occurs when token expired | on("requestToken") |
144
-     * microphoneEnabled | occurs when microphone enable state changed | on("microphoneEnabled", evt) |
202
+     * localAudioStateChanged | occurs when local audio device state changed | on("localAudioStateChanged", (state, errorCode) => {}) |
145 203
      * audioVolumeIndication | occurs when audio volume indication changed | on("audioVolumeIndication", evt) |
146 204
      * activeSpeaker | occurs when detect active speaker | on("activeSpeaker", evt) |
147 205
      * firstLocalAudioFrame | occurs when sent first audio frame on local | on("firstLocalAudioFrame", evt) |
148 206
      * firstRemoteAudioFrame | occurs when received first audio frame from remote side | on("firstRemoteAudioFrame", evt) |
149 207
      * firstRemoteAudioDecoded | occurs when first remote audio decoded | on("firstRemoteAudioDecoded", evt) |
150 208
      * firstLocalVideoFrame | occurs when sent first video frame on local | on("firstLocalVideoFrame", evt) |
151
-     * firstRemoteVideoDecoded | occurs when received first video frame from remote side decoded | on("firstRemoteVideoDecoded", evt) |
152 209
      * firstRemoteVideoFrame | occurs when received first video frame from remote side | on("firstRemoteVideoFrame", evt) |
153 210
      * userMuteAudio | occurs when user mute audio | on("userMuteAudio", evt) |
154
-     * userMuteVideo | occurs when user mute video | on("userMuteVideo", evt) |
155
-     * userEnableVideo | occurs when remote side's user change video enable state | on("userEnableVideo", evt) |
156
-     * userEnableLocalVideo | occurs when user change video enable state on local | on("userEnableLocalVideo", evt) |
157 211
      * videoSizeChanged | occurs when change local or remote side video size or rotation | on("videoSizeChanged", evt) |
158 212
      * remoteVideoStateChanged | occurs when remote video state has any changed | on("remoteVideoStateChanged", evt) |
213
+     * remoteAudioStateChanged | occurs when remote audio state has any changed | on("remoteAudioStateChanged", evt) |
214
+     * localAudioStats | occurs when engine start to report local audio stats | on("localAudioStats", evt) |
159 215
      * localPublishFallbackToAudioOnly | occurs when published stream from local side fallback to audio stream | on("localPublishFallbackToAudioOnly", evt) |
160 216
      * remoteSubscribeFallbackToAudioOnly | occurs when subscribed side's stream fallback to audio stream | on("remoteSubscribeFallbackToAudioOnly", evt) |
161 217
      * audioRouteChanged | occurs when local audio route changed | on("audioRouteChanged", evt) |
@@ -167,8 +223,6 @@ class RtcEngine {
167 223
      * localVideoStats | occurs when reports local video statistics | on("localVideoStats", evt) |
168 224
      * remoteVideoStats | occurs when reports remote video statistics| on("remoteVideoStats", evt) |
169 225
      * remoteAudioStats | occurs when reports remote audio statistics| on("remoteAudioStats", evt) |
170
-     * audioTransportStatsOfUid | occurs when reports  transport-layer statistics of each remote audio stream. | on("audioTransportStatsOfUid", evt) |
171
-     * videoTransportStatsOfUid | occurs when reports  transport-layer statistics of each remote video stream.| on("videoTransportStatsOfUid", evt) |
172 226
      * audioEffectFinish | occurs when the local audio effect playback finishes. | on("audioEffectFinish", evt) |
173 227
      * streamPublished | occurs when addPublishStreamUrl success| on("streamPublished", evt) |
174 228
      * streamUnpublish | occurs when removePublishStreamUrl success| on("streamUnpublish", evt) |
@@ -185,6 +239,8 @@ class RtcEngine {
185 239
      * mediaMetaDataReceived | occurs when you received media meta data from the remote side through sendMediaData | on("mediaMetaDataReceived", evt) |
186 240
      * localUserRegistered | occurs when you register user account success | on("localUserRegistered", evt) |
187 241
      * userInfoUpdated | occurs when you peer side using user account join channel | on("userInfoUpdated", evt) |
242
+     * receivedChannelMediaRelay | occurs when you received channel media relay | on('receivedChannelMediaRelay", evt)|
243
+     * mediaRelayStateChanged | occurs when you received remote media relay state changed | on('mediaRelayStateChanged", evt)|
188 244
      * ---
189 245
      *
190 246
      * @param eventType
@@ -203,15 +259,11 @@ class RtcEngine {
203 259
             'receiveStreamMessage',
204 260
             'activeSpeaker',
205 261
             'firstRemoteAudioFrame',
206
-            'firstRemoteVideoDecoded',
207 262
             'firstRemoteVideoFrame',
208 263
             'userMuteAudio',
209
-            'userMuteVideo',
210
-            'userEnableVideo',
211
-            'userEnableLocalVideo',
212 264
             'videoSizeChanged',
213
-            'firstRemoteAudioDecoded',
214 265
             'remoteVideoStateChanged',
266
+            'remoteAudioStateChanged',
215 267
             'remoteSubscribeFallbackToAudioOnly',
216 268
             'networkQuality',
217 269
             'streamInjectedStatus',
@@ -248,8 +300,6 @@ class RtcEngine {
248 300
         if ([
249 301
             'remoteAudioStats',
250 302
             'remoteVideoStats',
251
-            'audioTransportStatsOfUid',
252
-            'videoTransportStatsOfUid',
253 303
         ].indexOf(eventType) != -1) {
254 304
             AgoraEventEmitter.addListener(`${RtcEngine.AG_PREFIX}${eventType}`, (args) => {
255 305
                 args.stats.uid = this.Int32ToUint32(args.stats.uid);

+ 1
- 1
lib/RtcEngine.native.js.map
File diff suppressed because it is too large
View File


+ 28
- 0
lib/types.d.ts View File

@@ -1,4 +1,32 @@
1 1
 import { ViewProps } from 'react-native';
2
+/**
3
+ * ChannelMediaInfo
4
+ * @property channelName: string
5
+ * @property token: string
6
+ * @property uid: number
7
+ */
8
+export interface ChannelMediaInfo {
9
+    channelName: string;
10
+    token?: string;
11
+    uid?: number;
12
+}
13
+/**
14
+ * ChannelMediaConfiguration
15
+ * @property src: {
16
+ *    @member channelName,
17
+ *    @member token,
18
+ *    @member uid,
19
+ * }
20
+ * @property channels: {@link Array<ChannelMediaInfo>}
21
+ */
22
+export interface ChannelMediaConfiguration {
23
+    src?: {
24
+        channelName: string;
25
+        token?: string;
26
+        uid?: number;
27
+    };
28
+    channels: Array<ChannelMediaInfo>;
29
+}
2 30
 /**
3 31
  * AgoraViewMode
4 32
  * @mode hidden Uniformly scale the video until it fills the visible boundaries (cropped). One dimension of the video may have clipped contents.

+ 1
- 1
lib/types.js.map View File

@@ -1 +1 @@
1
-{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;AAEA;;;;GAIG;AACH,IAAY,aAGX;AAHD,WAAY,aAAa;IACvB,qDAAU,CAAA;IACV,+CAAO,CAAA;AACT,CAAC,EAHW,aAAa,GAAb,qBAAa,KAAb,qBAAa,QAGxB;AAUA,CAAC;AAuGF,IAAY,iBAIX;AAJD,WAAY,iBAAiB;IAC3B,kEAAa,CAAA;IACb,0DAAS,CAAA;IACT,2DAAU,CAAA;AACZ,CAAC,EAJW,iBAAiB,GAAjB,yBAAiB,KAAjB,yBAAiB,QAI5B;AAED,IAAY,iBAGX;AAHD,WAAY,iBAAiB;IAC3B,6DAAU,CAAA;IACV,6DAAU,CAAA;AACZ,CAAC,EAHW,iBAAiB,GAAjB,yBAAiB,KAAjB,yBAAiB,QAG5B;AAED,IAAY,eAIX;AAJD,WAAY,eAAe;IACzB,qEAAkB,CAAA;IAClB,qEAAkB,CAAA;IAClB,qEAAkB,CAAA;AACpB,CAAC,EAJW,eAAe,GAAf,uBAAe,KAAf,uBAAe,QAI1B;AAED;;;GAGG;AACH,IAAY,kBAMX;AAND,WAAY,kBAAkB;IAC5B,yDAAO,CAAA;IACP,yDAAO,CAAA;IACP,6DAAS,CAAA;IACT,2DAAQ,CAAA;IACR,2DAAQ,CAAA;AACV,CAAC,EANW,kBAAkB,GAAlB,0BAAkB,KAAlB,0BAAkB,QAM7B"}
1
+{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;AA+BA;;;;GAIG;AACH,IAAY,aAGX;AAHD,WAAY,aAAa;IACvB,qDAAU,CAAA;IACV,+CAAO,CAAA;AACT,CAAC,EAHW,aAAa,GAAb,qBAAa,KAAb,qBAAa,QAGxB;AAUA,CAAC;AAuGF,IAAY,iBAIX;AAJD,WAAY,iBAAiB;IAC3B,kEAAa,CAAA;IACb,0DAAS,CAAA;IACT,2DAAU,CAAA;AACZ,CAAC,EAJW,iBAAiB,GAAjB,yBAAiB,KAAjB,yBAAiB,QAI5B;AAED,IAAY,iBAGX;AAHD,WAAY,iBAAiB;IAC3B,6DAAU,CAAA;IACV,6DAAU,CAAA;AACZ,CAAC,EAHW,iBAAiB,GAAjB,yBAAiB,KAAjB,yBAAiB,QAG5B;AAED,IAAY,eAIX;AAJD,WAAY,eAAe;IACzB,qEAAkB,CAAA;IAClB,qEAAkB,CAAA;IAClB,qEAAkB,CAAA;AACpB,CAAC,EAJW,eAAe,GAAf,uBAAe,KAAf,uBAAe,QAI1B;AAED;;;GAGG;AACH,IAAY,kBAMX;AAND,WAAY,kBAAkB;IAC5B,yDAAO,CAAA;IACP,yDAAO,CAAA;IACP,6DAAS,CAAA;IACT,2DAAQ,CAAA;IACR,2DAAQ,CAAA;AACV,CAAC,EANW,kBAAkB,GAAlB,0BAAkB,KAAlB,0BAAkB,QAM7B"}

+ 1
- 1
package.json View File

@@ -1,6 +1,6 @@
1 1
 {
2 2
   "name": "react-native-agora",
3
-  "version": "2.8.0-alpha.2",
3
+  "version": "2.9.0-alpha.1",
4 4
   "description": "React Native around the Agora RTC SDKs for Android and iOS agora",
5 5
   "summary": "agora native sdk for react-native",
6 6
   "main": "lib/index.js",

+ 1
- 1
react-native-agora.podspec View File

@@ -20,5 +20,5 @@ Pod::Spec.new do |s|
20 20
     end
21 21
 
22 22
     s.dependency 'React'
23
-    s.dependency "AgoraRtcEngine_iOS", "2.8"
23
+    s.dependency "AgoraRtcEngine_iOS", "2.9"
24 24
 end

+ 72
- 16
src/RtcEngine.native.ts View File

@@ -25,7 +25,8 @@ import {
25 25
     PositionOption,
26 26
     BeautyOption,
27 27
     LastmileProbeConfig,
28
-    CameraCapturerConfiguration
28
+    CameraCapturerConfiguration,
29
+    ChannelMediaConfiguration
29 30
 } from "./types";
30 31
 
31 32
 
@@ -76,6 +77,69 @@ class RtcEngine {
76 77
         return Agora.joinChannel({channelName, uid, token, info});
77 78
     }
78 79
 
80
+    /**
81
+     * switch to specified channel
82
+     *
83
+     * This method will switch channel smoothly than you invoke leaveChannel & joinChannel.
84
+     * Otherwise, it will invoke error by the event
85
+     * It will occurs two events:
86
+     * Occurs leaveChannel when achieve leaving stage
87
+     * Occurs joinChannelSuccess when achieve joining stage
88
+     * @param channelName
89
+     * @param token
90
+     */
91
+    public static switchChannel(channelName: string, token?: string): Promise<any> {
92
+        return Agora.switchChannel({channelName, token});
93
+    }
94
+    
95
+    /**
96
+     * Starts to relay media streams across channels.
97
+     * 
98
+     * This method will start relay media stream across specified channels. (maximum support 4 channels)
99
+     * It will occurs event:
100
+     *  Occurs onChannelMediaRelayStateChanged
101
+     * @param config
102
+     */
103
+    public static startChannelMediaRelay(config: ChannelMediaConfiguration): Promise<any> {
104
+        return Agora.startChannelMediaRelay(config);
105
+    }
106
+
107
+    /**
108
+     * Remove to relay media streams across channels.
109
+     * 
110
+     * This method will remove & update relay media stream across specified channels. (maximum support relay 4 channels)
111
+     * It will occurs event:
112
+     *  Occurs onChannelMediaRelayStateChanged
113
+     * @param config
114
+     */
115
+    public static removeChannelMediaRelay(config: ChannelMediaConfiguration): Promise<any> {
116
+        return Agora.removeChannelMediaRelay(config);
117
+    }
118
+
119
+    /**
120
+     * Updates to relay media streams across channels.
121
+     * 
122
+     * This method will update relay media stream across specified channels. (maximum support 4 channels)
123
+     * It will occurs event:
124
+     *  Occurs onChannelMediaRelayStateChanged
125
+     * @param config
126
+     */
127
+    public static updateChannelMediaRelay(config: ChannelMediaConfiguration): Promise<any> {
128
+        return Agora.updateChannelMediaRelay(config);
129
+    }
130
+
131
+    /**
132
+     * Stop to relay media streams across channels.
133
+     * 
134
+     * This method will stop relay media stream across specified channels.
135
+     * It will occurs event:
136
+     *  Occurs onChannelMediaRelayStateChanged
137
+     * @param config
138
+     */
139
+    public static stopChannelMediaRelay(): Promise<any> {
140
+        return Agora.stopChannelMediaRelay();
141
+    }
142
+
79 143
     /**
80 144
      * Registers a user account.
81 145
      * 
@@ -182,21 +246,19 @@ class RtcEngine {
182 246
      * connectionLost | occurs when sdk connection lost | on("connectionLost", evt) |
183 247
      * tokenPrivilegeWillExpire | occurs when token will expire | on("tokenPrivilegeWillExpire", evt) |
184 248
      * requestToken | occurs when token expired | on("requestToken") |
185
-     * microphoneEnabled | occurs when microphone enable state changed | on("microphoneEnabled", evt) |
249
+     * localAudioStateChanged | occurs when local audio device state changed | on("localAudioStateChanged", (state, errorCode) => {}) |
186 250
      * audioVolumeIndication | occurs when audio volume indication changed | on("audioVolumeIndication", evt) |
187 251
      * activeSpeaker | occurs when detect active speaker | on("activeSpeaker", evt) |
188 252
      * firstLocalAudioFrame | occurs when sent first audio frame on local | on("firstLocalAudioFrame", evt) |
189 253
      * firstRemoteAudioFrame | occurs when received first audio frame from remote side | on("firstRemoteAudioFrame", evt) |
190 254
      * firstRemoteAudioDecoded | occurs when first remote audio decoded | on("firstRemoteAudioDecoded", evt) |
191 255
      * firstLocalVideoFrame | occurs when sent first video frame on local | on("firstLocalVideoFrame", evt) |
192
-     * firstRemoteVideoDecoded | occurs when received first video frame from remote side decoded | on("firstRemoteVideoDecoded", evt) |
193 256
      * firstRemoteVideoFrame | occurs when received first video frame from remote side | on("firstRemoteVideoFrame", evt) |
194 257
      * userMuteAudio | occurs when user mute audio | on("userMuteAudio", evt) |
195
-     * userMuteVideo | occurs when user mute video | on("userMuteVideo", evt) |
196
-     * userEnableVideo | occurs when remote side's user change video enable state | on("userEnableVideo", evt) |
197
-     * userEnableLocalVideo | occurs when user change video enable state on local | on("userEnableLocalVideo", evt) |
198 258
      * videoSizeChanged | occurs when change local or remote side video size or rotation | on("videoSizeChanged", evt) |
199 259
      * remoteVideoStateChanged | occurs when remote video state has any changed | on("remoteVideoStateChanged", evt) |
260
+     * remoteAudioStateChanged | occurs when remote audio state has any changed | on("remoteAudioStateChanged", evt) |
261
+     * localAudioStats | occurs when engine start to report local audio stats | on("localAudioStats", evt) |
200 262
      * localPublishFallbackToAudioOnly | occurs when published stream from local side fallback to audio stream | on("localPublishFallbackToAudioOnly", evt) |
201 263
      * remoteSubscribeFallbackToAudioOnly | occurs when subscribed side's stream fallback to audio stream | on("remoteSubscribeFallbackToAudioOnly", evt) |
202 264
      * audioRouteChanged | occurs when local audio route changed | on("audioRouteChanged", evt) |
@@ -208,8 +270,6 @@ class RtcEngine {
208 270
      * localVideoStats | occurs when reports local video statistics | on("localVideoStats", evt) | 
209 271
      * remoteVideoStats | occurs when reports remote video statistics| on("remoteVideoStats", evt) | 
210 272
      * remoteAudioStats | occurs when reports remote audio statistics| on("remoteAudioStats", evt) | 
211
-     * audioTransportStatsOfUid | occurs when reports  transport-layer statistics of each remote audio stream. | on("audioTransportStatsOfUid", evt) | 
212
-     * videoTransportStatsOfUid | occurs when reports  transport-layer statistics of each remote video stream.| on("videoTransportStatsOfUid", evt) | 
213 273
      * audioEffectFinish | occurs when the local audio effect playback finishes. | on("audioEffectFinish", evt) | 
214 274
      * streamPublished | occurs when addPublishStreamUrl success| on("streamPublished", evt) | 
215 275
      * streamUnpublish | occurs when removePublishStreamUrl success| on("streamUnpublish", evt) | 
@@ -226,6 +286,8 @@ class RtcEngine {
226 286
      * mediaMetaDataReceived | occurs when you received media meta data from the remote side through sendMediaData | on("mediaMetaDataReceived", evt) | 
227 287
      * localUserRegistered | occurs when you register user account success | on("localUserRegistered", evt) |
228 288
      * userInfoUpdated | occurs when you peer side using user account join channel | on("userInfoUpdated", evt) |
289
+     * receivedChannelMediaRelay | occurs when you received channel media relay | on('receivedChannelMediaRelay", evt)|
290
+     * mediaRelayStateChanged | occurs when you received remote media relay state changed | on('mediaRelayStateChanged", evt)|
229 291
      * ---
230 292
      * 
231 293
      * @param eventType
@@ -244,15 +306,11 @@ class RtcEngine {
244 306
             'receiveStreamMessage',
245 307
             'activeSpeaker',
246 308
             'firstRemoteAudioFrame',
247
-            'firstRemoteVideoDecoded',
248 309
             'firstRemoteVideoFrame',
249 310
             'userMuteAudio',
250
-            'userMuteVideo',
251
-            'userEnableVideo',
252
-            'userEnableLocalVideo',
253 311
             'videoSizeChanged',
254
-            'firstRemoteAudioDecoded',
255 312
             'remoteVideoStateChanged',
313
+            'remoteAudioStateChanged',
256 314
             'remoteSubscribeFallbackToAudioOnly',
257 315
             'networkQuality',
258 316
             'streamInjectedStatus',
@@ -295,8 +353,6 @@ class RtcEngine {
295 353
         if ([
296 354
             'remoteAudioStats',
297 355
             'remoteVideoStats',
298
-            'audioTransportStatsOfUid',
299
-            'videoTransportStatsOfUid',   
300 356
         ].indexOf(eventType) != -1) {
301 357
             AgoraEventEmitter.addListener(`${RtcEngine.AG_PREFIX}${eventType}`, (args) => {
302 358
                 args.stats.uid = this.Int32ToUint32(args.stats.uid);
@@ -389,7 +445,7 @@ class RtcEngine {
389 445
             for (let eventType of this._eventTypes) {
390 446
                 AgoraEventEmitter.removeAllListeners(eventType);
391 447
             }
392
-            this._eventTypes.clear()
448
+            this._eventTypes.clear();
393 449
         }
394 450
         return Agora.destroy();
395 451
     }

+ 29
- 0
src/types.ts View File

@@ -1,5 +1,34 @@
1 1
 import { ViewProps } from 'react-native';
2 2
 
3
+/**
4
+ * ChannelMediaInfo
5
+ * @property channelName: string
6
+ * @property token: string
7
+ * @property uid: number
8
+ */
9
+export interface ChannelMediaInfo {
10
+  channelName: string
11
+  token?: string
12
+  uid?: number
13
+}
14
+/**
15
+ * ChannelMediaConfiguration
16
+ * @property src: {
17
+ *    @member channelName,
18
+ *    @member token,
19
+ *    @member uid,
20
+ * }
21
+ * @property channels: {@link Array<ChannelMediaInfo>}
22
+ */
23
+export interface ChannelMediaConfiguration {
24
+  src?: {
25
+    channelName: string
26
+    token?: string
27
+    uid?: number
28
+  }
29
+  channels: Array<ChannelMediaInfo>
30
+}
31
+
3 32
 /**
4 33
  * AgoraViewMode
5 34
  * @mode hidden Uniformly scale the video until it fills the visible boundaries (cropped). One dimension of the video may have clipped contents.