Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ fun CustomAudioSource() {
Toast.makeText(context, "Permission Granted", Toast.LENGTH_LONG).show()
option.channelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING
option.clientRoleType = Constants.CLIENT_ROLE_BROADCASTER
option.publishMicrophoneTrack = false
TokenUtils.gen(channelName, 0){
rtcEngine.joinChannel(it, channelName, 0, option)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import android.view.View
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.AlertDialog
Expand All @@ -19,6 +20,7 @@ import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
Expand All @@ -29,6 +31,7 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.content.ContentProviderCompat.requireContext
import io.agora.api.example.compose.BuildConfig
import io.agora.api.example.compose.R
import io.agora.api.example.compose.data.SettingPreferences
Expand All @@ -47,6 +50,7 @@ import io.agora.rtc2.RtcEngine
import io.agora.rtc2.RtcEngineConfig
import io.agora.rtc2.video.VideoCanvas
import io.agora.rtc2.video.VideoEncoderConfiguration
import kotlinx.coroutines.launch
import java.io.File

@Composable
Expand Down Expand Up @@ -173,7 +177,7 @@ fun MediaRecorder() {
Toast.makeText(context, "Permission Denied", Toast.LENGTH_LONG).show()
}
}

val coroutineScope = rememberCoroutineScope()
MediaRecorderView(
channelName = channelName,
isJoined = isJoined,
Expand All @@ -200,58 +204,81 @@ fun MediaRecorder() {
rtcEngine.setupRemoteVideo(VideoCanvas(view, Constants.RENDER_MODE_HIDDEN, id))
}
},
onRecorderClick = { id, isRecording ->
if (isRecording) {
val storagePath: String =
context.externalCacheDir?.absolutePath + File.separator + "media_recorder_" + channelName + "_" + id + ".mp4"
val recorder = rtcEngine.createMediaRecorder(RecorderStreamInfo(channelName, id,0))
recorder.setMediaRecorderObserver(object : IMediaRecorderCallback {
override fun onRecorderStateChanged(
channelId: String?,
uid: Int,
state: Int,
reason: Int
) {
Log.d(
"MediaRecorder",
"LocalMediaRecorder -- onRecorderStateChanged channelId=$channelId, uid=$uid, state=$state, reason=$reason"
)
if (state == AgoraMediaRecorder.RECORDER_STATE_STOP) {
recorders.remove(uid)
recoderResult = storagePath
}
}
overlay = { _, id ->
var isRecording by remember { mutableStateOf(false) }
Button(
modifier = Modifier
.padding(8.dp)
.align(Alignment.BottomEnd),
onClick = {
isRecording = !isRecording
if (isRecording) {
val storagePath: String =
context.externalCacheDir?.absolutePath + File.separator + "media_recorder_" + channelName + "_" + id + ".mp4"
val recorder =
rtcEngine.createMediaRecorder(RecorderStreamInfo(channelName, id, 0))
recorder.setMediaRecorderObserver(object : IMediaRecorderCallback {
override fun onRecorderStateChanged(
channelId: String?,
uid: Int,
state: Int,
reason: Int
) {
Log.d(
"MediaRecorder",
"LocalMediaRecorder -- onRecorderStateChanged channelId=$channelId, uid=$uid, state=$state, reason=$reason"
)
if (state == AgoraMediaRecorder.RECORDER_STATE_STOP) {
recorders.remove(uid)
recoderResult = storagePath
} else if (state == AgoraMediaRecorder.RECORDER_STATE_ERROR && reason == AgoraMediaRecorder.RECORDER_REASON_CONFIG_CHANGED) {
coroutineScope.launch {
isRecording = false
recorders[id]?.let {
it.stopRecording()
it.release()
}
}
}
}

override fun onRecorderInfoUpdated(
channelId: String?,
uid: Int,
info: RecorderInfo?
) {
info ?: return
Log.d(
"MediaRecorder",
"LocalMediaRecorder -- onRecorderInfoUpdated channelId="
+ channelId + ", uid=" + uid + ", fileName=" + info.fileName
+ ", durationMs=" + info.durationMs + ", fileSize=" + info.fileSize
override fun onRecorderInfoUpdated(
channelId: String?,
uid: Int,
info: RecorderInfo?
) {
info ?: return
Log.d(
"MediaRecorder",
"LocalMediaRecorder -- onRecorderInfoUpdated channelId="
+ channelId + ", uid=" + uid + ", fileName=" + info.fileName
+ ", durationMs=" + info.durationMs + ", fileSize=" + info.fileSize
)
}
})
recorder.startRecording(
AgoraMediaRecorder.MediaRecorderConfiguration(
storagePath,
AgoraMediaRecorder.CONTAINER_MP4,
AgoraMediaRecorder.STREAM_TYPE_BOTH,
120000,
0
)
)
recorders[id] = recorder
} else {
recorders[id]?.let {
it.stopRecording()
it.release()
}
}

})
recorder.startRecording(
AgoraMediaRecorder.MediaRecorderConfiguration(
storagePath,
AgoraMediaRecorder.CONTAINER_MP4,
AgoraMediaRecorder.STREAM_TYPE_BOTH,
120000,
0
{
Text(
text = if (!isRecording) stringResource(id = R.string.start_recording) else stringResource(
id = R.string.stop_recording
)
)
recorders[id] = recorder
} else {
recorders[id]?.let {
it.stopRecording()
it.release()
}
}
},
onCameraSwitchClick = {
Expand Down Expand Up @@ -284,30 +311,15 @@ private fun MediaRecorderView(
videoIdList: List<Int>,
setupVideo: (View, Int, Boolean) -> Unit,
statsMap: Map<Int, VideoStatsInfo> = emptyMap(),
onRecorderClick: (id: Int, isRecording: Boolean) -> Unit = { _, _ -> },
overlay: @Composable BoxScope.(index: Int, id: Int) -> Unit? = { _, _ -> },
onCameraSwitchClick: () -> Unit = { }
) {
Column {
VideoGrid(
modifier = Modifier.weight(1.0f),
videoIdList = videoIdList,
setupVideo = setupVideo,
overlay = { _, id ->
var isRecording by rememberSaveable { mutableStateOf(false) }
Button(
modifier = Modifier
.padding(8.dp)
.align(Alignment.BottomEnd),
onClick = {
isRecording = !isRecording
onRecorderClick(id, isRecording)
})
{
Text(text = if (!isRecording) stringResource(id = R.string.start_recording) else stringResource(
id = R.string.stop_recording
))
}
}
overlay = overlay
)
Button(
modifier = Modifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ private fun PlayAudioFilesView(
modifier = Modifier
.weight(1.0f)
.padding(16.dp, 8.dp)
.clickable {
.clickable(isJoined) {
rtcEngine?.startAudioMixing(
"/assets/music_1.m4a",
false,
Expand All @@ -218,7 +218,7 @@ private fun PlayAudioFilesView(
modifier = Modifier
.weight(1.0f)
.padding(16.dp, 8.dp)
.clickable {
.clickable(isJoined) {
rtcEngine?.resumeAudioMixing()
}
)
Expand All @@ -230,7 +230,7 @@ private fun PlayAudioFilesView(
modifier = Modifier
.weight(1.0f)
.padding(16.dp, 8.dp)
.clickable {
.clickable(isJoined) {
rtcEngine?.pauseAudioMixing()
}
)
Expand All @@ -242,7 +242,7 @@ private fun PlayAudioFilesView(
modifier = Modifier
.weight(1.0f)
.padding(16.dp, 8.dp)
.clickable {
.clickable(isJoined) {
rtcEngine?.stopAudioMixing()
}
)
Expand Down Expand Up @@ -279,7 +279,7 @@ private fun PlayAudioFilesView(
modifier = Modifier
.weight(1.0f)
.padding(16.dp, 8.dp)
.clickable {
.clickable(isJoined) {
rtcEngine?.playEffect(
EFFECT_SOUND_ID, // The sound ID of the audio effect file to be played.
EFFECT_FILE_PATH, // The file path of the audio effect file.
Expand All @@ -297,7 +297,7 @@ private fun PlayAudioFilesView(
modifier = Modifier
.weight(1.0f)
.padding(16.dp, 8.dp)
.clickable {
.clickable(isJoined) {
rtcEngine?.resumeEffect(EFFECT_SOUND_ID)
})
Text(text = stringResource(id = R.string.pause),
Expand All @@ -307,7 +307,7 @@ private fun PlayAudioFilesView(
modifier = Modifier
.weight(1.0f)
.padding(16.dp, 8.dp)
.clickable {
.clickable (isJoined) {
rtcEngine?.pauseEffect(EFFECT_SOUND_ID)
})
Text(text = stringResource(id = R.string.stop),
Expand All @@ -317,7 +317,7 @@ private fun PlayAudioFilesView(
modifier = Modifier
.weight(1.0f)
.padding(16.dp, 8.dp)
.clickable {
.clickable(isJoined) {
rtcEngine?.stopEffect(EFFECT_SOUND_ID)
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ private void joinChannel(String channelId) {

ChannelMediaOptions option = new ChannelMediaOptions();
option.channelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING;
option.clientRoleType = Constants.CLIENT_ROLE_BROADCASTER;
option.clientRoleType = CLIENT_ROLE_AUDIENCE;
option.autoSubscribeAudio = true;
option.autoSubscribeVideo = true;
int res;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -492,16 +492,16 @@ public void onTokenGen(String token) {
} else {
LocalAudioMixerConfiguration config = new LocalAudioMixerConfiguration();
config.syncWithLocalMic = false;
config.mixedAudioStreams.clear();
config.audioInputStreams.clear();
LocalAudioMixerConfiguration.MixedAudioStream remoteStream = new LocalAudioMixerConfiguration.MixedAudioStream();
remoteStream.sourceType = AUDIO_SOURCE_REMOTE_CHANNEL;
remoteStream.channelId = channelId;
config.mixedAudioStreams.add(remoteStream);
config.audioInputStreams.add(remoteStream);

LocalAudioMixerConfiguration.MixedAudioStream remoteStream2 = new LocalAudioMixerConfiguration.MixedAudioStream();
remoteStream2.sourceType = AUDIO_SOURCE_MICROPHONE;
remoteStream2.channelId = channelId;
config.mixedAudioStreams.add(remoteStream2);
config.audioInputStreams.add(remoteStream2);
engine.startLocalAudioMixer(config);
}
}
Expand Down