Skip to content

Commit 0a5deb4

Browse files
committed
Merge branch 'dev/4.5.0' of https://github.com/AgoraIO/API-Examples into dev/4.5.0
2 parents 5b49ec4 + 4853233 commit 0a5deb4

5 files changed

Lines changed: 89 additions & 76 deletions

File tree

Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/CustomAudioSource.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ fun CustomAudioSource() {
143143
Toast.makeText(context, "Permission Granted", Toast.LENGTH_LONG).show()
144144
option.channelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING
145145
option.clientRoleType = Constants.CLIENT_ROLE_BROADCASTER
146+
option.publishMicrophoneTrack = false
146147
TokenUtils.gen(channelName, 0){
147148
rtcEngine.joinChannel(it, channelName, 0, option)
148149
}

Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/MediaRecorder.kt

Lines changed: 76 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import android.view.View
66
import android.widget.Toast
77
import androidx.activity.compose.rememberLauncherForActivityResult
88
import androidx.activity.result.contract.ActivityResultContracts
9+
import androidx.compose.foundation.layout.BoxScope
910
import androidx.compose.foundation.layout.Column
1011
import androidx.compose.foundation.layout.padding
1112
import androidx.compose.material3.AlertDialog
@@ -19,6 +20,7 @@ import androidx.compose.runtime.mutableIntStateOf
1920
import androidx.compose.runtime.mutableStateMapOf
2021
import androidx.compose.runtime.mutableStateOf
2122
import androidx.compose.runtime.remember
23+
import androidx.compose.runtime.rememberCoroutineScope
2224
import androidx.compose.runtime.saveable.rememberSaveable
2325
import androidx.compose.runtime.setValue
2426
import androidx.compose.ui.Alignment
@@ -29,6 +31,7 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController
2931
import androidx.compose.ui.res.stringResource
3032
import androidx.compose.ui.tooling.preview.Preview
3133
import androidx.compose.ui.unit.dp
34+
import androidx.core.content.ContentProviderCompat.requireContext
3235
import io.agora.api.example.compose.BuildConfig
3336
import io.agora.api.example.compose.R
3437
import io.agora.api.example.compose.data.SettingPreferences
@@ -47,6 +50,7 @@ import io.agora.rtc2.RtcEngine
4750
import io.agora.rtc2.RtcEngineConfig
4851
import io.agora.rtc2.video.VideoCanvas
4952
import io.agora.rtc2.video.VideoEncoderConfiguration
53+
import kotlinx.coroutines.launch
5054
import java.io.File
5155

5256
@Composable
@@ -173,7 +177,7 @@ fun MediaRecorder() {
173177
Toast.makeText(context, "Permission Denied", Toast.LENGTH_LONG).show()
174178
}
175179
}
176-
180+
val coroutineScope = rememberCoroutineScope()
177181
MediaRecorderView(
178182
channelName = channelName,
179183
isJoined = isJoined,
@@ -200,58 +204,81 @@ fun MediaRecorder() {
200204
rtcEngine.setupRemoteVideo(VideoCanvas(view, Constants.RENDER_MODE_HIDDEN, id))
201205
}
202206
},
203-
onRecorderClick = { id, isRecording ->
204-
if (isRecording) {
205-
val storagePath: String =
206-
context.externalCacheDir?.absolutePath + File.separator + "media_recorder_" + channelName + "_" + id + ".mp4"
207-
val recorder = rtcEngine.createMediaRecorder(RecorderStreamInfo(channelName, id,0))
208-
recorder.setMediaRecorderObserver(object : IMediaRecorderCallback {
209-
override fun onRecorderStateChanged(
210-
channelId: String?,
211-
uid: Int,
212-
state: Int,
213-
reason: Int
214-
) {
215-
Log.d(
216-
"MediaRecorder",
217-
"LocalMediaRecorder -- onRecorderStateChanged channelId=$channelId, uid=$uid, state=$state, reason=$reason"
218-
)
219-
if (state == AgoraMediaRecorder.RECORDER_STATE_STOP) {
220-
recorders.remove(uid)
221-
recoderResult = storagePath
222-
}
223-
}
207+
overlay = { _, id ->
208+
var isRecording by remember { mutableStateOf(false) }
209+
Button(
210+
modifier = Modifier
211+
.padding(8.dp)
212+
.align(Alignment.BottomEnd),
213+
onClick = {
214+
isRecording = !isRecording
215+
if (isRecording) {
216+
val storagePath: String =
217+
context.externalCacheDir?.absolutePath + File.separator + "media_recorder_" + channelName + "_" + id + ".mp4"
218+
val recorder =
219+
rtcEngine.createMediaRecorder(RecorderStreamInfo(channelName, id, 0))
220+
recorder.setMediaRecorderObserver(object : IMediaRecorderCallback {
221+
override fun onRecorderStateChanged(
222+
channelId: String?,
223+
uid: Int,
224+
state: Int,
225+
reason: Int
226+
) {
227+
Log.d(
228+
"MediaRecorder",
229+
"LocalMediaRecorder -- onRecorderStateChanged channelId=$channelId, uid=$uid, state=$state, reason=$reason"
230+
)
231+
if (state == AgoraMediaRecorder.RECORDER_STATE_STOP) {
232+
recorders.remove(uid)
233+
recoderResult = storagePath
234+
} else if (state == AgoraMediaRecorder.RECORDER_STATE_ERROR && reason == AgoraMediaRecorder.RECORDER_REASON_CONFIG_CHANGED) {
235+
coroutineScope.launch {
236+
isRecording = false
237+
recorders[id]?.let {
238+
it.stopRecording()
239+
it.release()
240+
}
241+
}
242+
}
243+
}
224244

225-
override fun onRecorderInfoUpdated(
226-
channelId: String?,
227-
uid: Int,
228-
info: RecorderInfo?
229-
) {
230-
info ?: return
231-
Log.d(
232-
"MediaRecorder",
233-
"LocalMediaRecorder -- onRecorderInfoUpdated channelId="
234-
+ channelId + ", uid=" + uid + ", fileName=" + info.fileName
235-
+ ", durationMs=" + info.durationMs + ", fileSize=" + info.fileSize
245+
override fun onRecorderInfoUpdated(
246+
channelId: String?,
247+
uid: Int,
248+
info: RecorderInfo?
249+
) {
250+
info ?: return
251+
Log.d(
252+
"MediaRecorder",
253+
"LocalMediaRecorder -- onRecorderInfoUpdated channelId="
254+
+ channelId + ", uid=" + uid + ", fileName=" + info.fileName
255+
+ ", durationMs=" + info.durationMs + ", fileSize=" + info.fileSize
256+
)
257+
}
258+
})
259+
recorder.startRecording(
260+
AgoraMediaRecorder.MediaRecorderConfiguration(
261+
storagePath,
262+
AgoraMediaRecorder.CONTAINER_MP4,
263+
AgoraMediaRecorder.STREAM_TYPE_BOTH,
264+
120000,
265+
0
266+
)
236267
)
268+
recorders[id] = recorder
269+
} else {
270+
recorders[id]?.let {
271+
it.stopRecording()
272+
it.release()
273+
}
237274
}
238-
239275
})
240-
recorder.startRecording(
241-
AgoraMediaRecorder.MediaRecorderConfiguration(
242-
storagePath,
243-
AgoraMediaRecorder.CONTAINER_MP4,
244-
AgoraMediaRecorder.STREAM_TYPE_BOTH,
245-
120000,
246-
0
276+
{
277+
Text(
278+
text = if (!isRecording) stringResource(id = R.string.start_recording) else stringResource(
279+
id = R.string.stop_recording
247280
)
248281
)
249-
recorders[id] = recorder
250-
} else {
251-
recorders[id]?.let {
252-
it.stopRecording()
253-
it.release()
254-
}
255282
}
256283
},
257284
onCameraSwitchClick = {
@@ -284,30 +311,15 @@ private fun MediaRecorderView(
284311
videoIdList: List<Int>,
285312
setupVideo: (View, Int, Boolean) -> Unit,
286313
statsMap: Map<Int, VideoStatsInfo> = emptyMap(),
287-
onRecorderClick: (id: Int, isRecording: Boolean) -> Unit = { _, _ -> },
314+
overlay: @Composable BoxScope.(index: Int, id: Int) -> Unit? = { _, _ -> },
288315
onCameraSwitchClick: () -> Unit = { }
289316
) {
290317
Column {
291318
VideoGrid(
292319
modifier = Modifier.weight(1.0f),
293320
videoIdList = videoIdList,
294321
setupVideo = setupVideo,
295-
overlay = { _, id ->
296-
var isRecording by rememberSaveable { mutableStateOf(false) }
297-
Button(
298-
modifier = Modifier
299-
.padding(8.dp)
300-
.align(Alignment.BottomEnd),
301-
onClick = {
302-
isRecording = !isRecording
303-
onRecorderClick(id, isRecording)
304-
})
305-
{
306-
Text(text = if (!isRecording) stringResource(id = R.string.start_recording) else stringResource(
307-
id = R.string.stop_recording
308-
))
309-
}
310-
}
322+
overlay = overlay
311323
)
312324
Button(
313325
modifier = Modifier

Android/APIExample-Compose/app/src/main/java/io/agora/api/example/compose/samples/PlayAudioFiles.kt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ private fun PlayAudioFilesView(
201201
modifier = Modifier
202202
.weight(1.0f)
203203
.padding(16.dp, 8.dp)
204-
.clickable {
204+
.clickable(isJoined) {
205205
rtcEngine?.startAudioMixing(
206206
"/assets/music_1.m4a",
207207
false,
@@ -218,7 +218,7 @@ private fun PlayAudioFilesView(
218218
modifier = Modifier
219219
.weight(1.0f)
220220
.padding(16.dp, 8.dp)
221-
.clickable {
221+
.clickable(isJoined) {
222222
rtcEngine?.resumeAudioMixing()
223223
}
224224
)
@@ -230,7 +230,7 @@ private fun PlayAudioFilesView(
230230
modifier = Modifier
231231
.weight(1.0f)
232232
.padding(16.dp, 8.dp)
233-
.clickable {
233+
.clickable(isJoined) {
234234
rtcEngine?.pauseAudioMixing()
235235
}
236236
)
@@ -242,7 +242,7 @@ private fun PlayAudioFilesView(
242242
modifier = Modifier
243243
.weight(1.0f)
244244
.padding(16.dp, 8.dp)
245-
.clickable {
245+
.clickable(isJoined) {
246246
rtcEngine?.stopAudioMixing()
247247
}
248248
)
@@ -279,7 +279,7 @@ private fun PlayAudioFilesView(
279279
modifier = Modifier
280280
.weight(1.0f)
281281
.padding(16.dp, 8.dp)
282-
.clickable {
282+
.clickable(isJoined) {
283283
rtcEngine?.playEffect(
284284
EFFECT_SOUND_ID, // The sound ID of the audio effect file to be played.
285285
EFFECT_FILE_PATH, // The file path of the audio effect file.
@@ -297,7 +297,7 @@ private fun PlayAudioFilesView(
297297
modifier = Modifier
298298
.weight(1.0f)
299299
.padding(16.dp, 8.dp)
300-
.clickable {
300+
.clickable(isJoined) {
301301
rtcEngine?.resumeEffect(EFFECT_SOUND_ID)
302302
})
303303
Text(text = stringResource(id = R.string.pause),
@@ -307,7 +307,7 @@ private fun PlayAudioFilesView(
307307
modifier = Modifier
308308
.weight(1.0f)
309309
.padding(16.dp, 8.dp)
310-
.clickable {
310+
.clickable (isJoined) {
311311
rtcEngine?.pauseEffect(EFFECT_SOUND_ID)
312312
})
313313
Text(text = stringResource(id = R.string.stop),
@@ -317,7 +317,7 @@ private fun PlayAudioFilesView(
317317
modifier = Modifier
318318
.weight(1.0f)
319319
.padding(16.dp, 8.dp)
320-
.clickable {
320+
.clickable(isJoined) {
321321
rtcEngine?.stopEffect(EFFECT_SOUND_ID)
322322
})
323323
}

Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/LiveStreaming.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ private void joinChannel(String channelId) {
556556

557557
ChannelMediaOptions option = new ChannelMediaOptions();
558558
option.channelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING;
559-
option.clientRoleType = Constants.CLIENT_ROLE_BROADCASTER;
559+
option.clientRoleType = CLIENT_ROLE_AUDIENCE;
560560
option.autoSubscribeAudio = true;
561561
option.autoSubscribeVideo = true;
562562
int res;

Android/APIExample/app/src/main/java/io/agora/api/example/examples/basic/JoinChannelAudio.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -492,16 +492,16 @@ public void onTokenGen(String token) {
492492
} else {
493493
LocalAudioMixerConfiguration config = new LocalAudioMixerConfiguration();
494494
config.syncWithLocalMic = false;
495-
config.mixedAudioStreams.clear();
495+
config.audioInputStreams.clear();
496496
LocalAudioMixerConfiguration.MixedAudioStream remoteStream = new LocalAudioMixerConfiguration.MixedAudioStream();
497497
remoteStream.sourceType = AUDIO_SOURCE_REMOTE_CHANNEL;
498498
remoteStream.channelId = channelId;
499-
config.mixedAudioStreams.add(remoteStream);
499+
config.audioInputStreams.add(remoteStream);
500500

501501
LocalAudioMixerConfiguration.MixedAudioStream remoteStream2 = new LocalAudioMixerConfiguration.MixedAudioStream();
502502
remoteStream2.sourceType = AUDIO_SOURCE_MICROPHONE;
503503
remoteStream2.channelId = channelId;
504-
config.mixedAudioStreams.add(remoteStream2);
504+
config.audioInputStreams.add(remoteStream2);
505505
engine.startLocalAudioMixer(config);
506506
}
507507
}

0 commit comments

Comments
 (0)