Skip to content

Commit a5e3e6e

Browse files
committed
Merge branch 'dev/4.5.0' into dev/4.5.0-windows
2 parents 78ca6b0 + 0a5deb4 commit a5e3e6e

36 files changed

Lines changed: 782 additions & 646 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
}

iOS/APIExample-SwiftUI/APIExample-SwiftUI/ContentView.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ struct ContentView: View {
105105
}
106106
}
107107
}
108+
109+
Text("SDK Version: arsenal_46627_FULL_20241022_0154_394587")
108110
}
109111
.listStyle(GroupedListStyle())
110112
.navigationTitle("Agora API Example")

iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/ARKit/ARKit.swift

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,20 @@ struct ARKit: View {
9696

9797
@State private var statsInfo: String = ""
9898
@ObservedObject private var agoraKit = ARKitRTC()
99-
@GestureState private var tappedLocation: CGPoint = .zero
100-
99+
@State var tappedLocation: CGPoint = .zero
100+
@State var dragLocation: CGPoint = .zero
101+
101102
let sceneView = ARViewWrapper()
102103

103104
var body: some View {
104105
VStack {
106+
let tap = TapGesture().onEnded {
107+
tappedLocation = dragLocation
108+
agoraKit.doSceneViewTapped(tappedLocation)
109+
}
110+
let drag = DragGesture(minimumDistance: 0).onChanged { value in
111+
dragLocation = value.location
112+
}.sequenced(before: tap)
105113
sceneView
106114
.adaptiveBackground().alert(isPresented: $agoraKit.isSupportedAR) {
107115
let message = "This app requires world tracking, which is available only on iOS devices with the A9 processor or later.".localized
@@ -115,13 +123,8 @@ struct ARKit: View {
115123
}).onReceive(agoraKit.$isHiddenStatsLabel, perform: { _ in
116124
sceneView.statsLabel.isHidden = agoraKit.isHiddenStatsLabel
117125
sceneView.statsLabel.text = agoraKit.stats
118-
}).onTapGesture {
119-
agoraKit.doSceneViewTapped(tappedLocation)
120-
}.gesture(DragGesture(minimumDistance: 0)
121-
.updating($tappedLocation) { (value, state, _) in
122-
state = value.startLocation
123-
}
124-
)
126+
})
127+
.gesture(drag)
125128

126129
}.onAppear(perform: {
127130
agoraKit.setupRTC(configs: configs,

iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/KtvCopyrightMusic/KtvCopyrightMusic.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ struct KtvCopyrightMusic: View {
1111
var body: some View {
1212
ZStack {
1313
Button("Ktv copyright music".localized) {
14-
let urlString = "https://doc.shengwang.cn/doc/online-ktv/ios/implementation/ktv-scenario/integrate-ktvapi"
14+
let urlString = "https://doc.shengwang.cn/doc/online-ktv/ios/ktv-scenario/get-started/integrate-ktvapi"
15+
1516
if let url = URL(string: urlString) {
1617
UIApplication.shared.open(url)
1718
}

iOS/APIExample-SwiftUI/APIExample-SwiftUI/Examples/Advanced/LiveStreaming/LiveStreaming.swift

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -194,22 +194,16 @@ struct LiveStreaming: View {
194194
ZStack {
195195
backgroundView
196196
VStack {
197-
if liveStreamRTCKit.remoteUid != nil {
198-
HStack {
199-
Spacer()
200-
foregroundView
201-
.frame(width: 136, height: 182)
202-
.offset(x: -20)
203-
.onTapGesture {
204-
liveStreamRTCKit.onTapForegroundVideo()
205-
}
206-
}
207-
} else {
208-
Rectangle()
197+
HStack {
198+
Spacer()
199+
foregroundView
209200
.frame(width: 136, height: 182)
210-
.adaptiveForegroundStyle(.clear)
201+
.offset(x: -20)
202+
.onTapGesture {
203+
liveStreamRTCKit.onTapForegroundVideo()
204+
}
211205
}
212-
206+
213207
//防抖
214208
HStack {
215209
Text("anti shake".localized)

0 commit comments

Comments
 (0)