Skip to content

Commit 6908cdb

Browse files
CYGCYG
authored andcommitted
[Android]add giant chorus select route logic
1 parent 777ee09 commit 6908cdb

File tree

5 files changed

+177
-9
lines changed

5 files changed

+177
-9
lines changed

KTVAPI/Android/app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
1010
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
1111
<uses-permission android:name="android.permission.BLUETOOTH" />
12+
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
1213

1314
<application
1415
android:name=".MyApplication"

KTVAPI/Android/app/src/main/java/io/agora/ktvdemo/ui/LivingFragment.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,8 +302,7 @@ class LivingFragment : BaseFragment<FragmentLivingBinding>() {
302302
musicStreamUid = 2023,
303303
musicStreamToken = RtcEngineController.musicStreamToken,
304304
maxCacheSize = 10,
305-
musicType = if (KeyCenter.isMcc) KTVMusicType.SONG_CODE else KTVMusicType.SONG_URL,
306-
topN = 6
305+
musicType = if (KeyCenter.isMcc) KTVMusicType.SONG_CODE else KTVMusicType.SONG_URL
307306
)
308307
)
309308
}

KTVAPI/Android/lib_ktvapi/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ dependencies {
7676
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
7777
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
7878

79-
api "io.agora.rtc:agora-special-full:4.1.1.26"
79+
api "io.agora.rtc:agora-special-full:4.1.1.27"
8080
implementation 'com.google.protobuf:protobuf-java:3.19.3'
8181
implementation 'com.google.protobuf:protobuf-java-util:3.19.3'
8282
}

KTVAPI/Android/lib_ktvapi/src/main/java/io/agora/ktvapi/KTVApi.kt

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,30 @@ enum class AudioTrackMode(val value: Int) {
114114
DAO_CHANG(2),
115115
}
116116

117+
/**
118+
* 大合唱中演唱者互相收听对方音频流的选路策略
119+
* @param RANDOM 随机选取几条流
120+
* @param BY_DELAY 根据延迟选择最低的几条流
121+
* @param TOP_N 根据音强选流
122+
* @param BY_DELAY_AND_TOP_N 同时开始延迟选路和音强选流
123+
*/
124+
enum class GiantChorusRouteSelectionType(val value: Int) {
125+
RANDOM(0),
126+
BY_DELAY(1),
127+
TOP_N(2),
128+
BY_DELAY_AND_TOP_N(3)
129+
}
130+
131+
/**
132+
* 大合唱中演唱者互相收听对方音频流的选路配置
133+
* @param type 选路策略
134+
* @param streamNum 最大选取的流个数(推荐6)
135+
*/
136+
data class GiantChorusRouteSelectionConfig constructor(
137+
val type: GiantChorusRouteSelectionType,
138+
val streamNum: Int
139+
)
140+
117141
/**
118142
* 歌词组件接口,您setLrcView传入的歌词组件需要继承此接口类,并实现以下几个方法
119143
*/
@@ -277,7 +301,7 @@ data class KTVApiConfig constructor(
277301
* @param musicStreamToken 音乐流token
278302
* @param maxCacheSize 最大缓存歌曲数
279303
* @param musicType 音乐类型
280-
* @param topN 演唱之间互相能听到的最大数量
304+
* @param routeSelectionConfig 选路配置
281305
*/
282306
data class KTVGiantChorusApiConfig constructor(
283307
val appId: String,
@@ -291,8 +315,7 @@ data class KTVGiantChorusApiConfig constructor(
291315
val musicStreamUid: Int,
292316
val musicStreamToken: String,
293317
val maxCacheSize: Int = 10,
294-
val musicType: KTVMusicType = KTVMusicType.SONG_CODE,
295-
val topN: Int = 0
318+
val musicType: KTVMusicType = KTVMusicType.SONG_CODE
296319
)
297320

298321
/**
@@ -336,6 +359,8 @@ interface KTVApi {
336359
var debugMode = false
337360
// 内部测试使用,无需关注
338361
var mccDomain = ""
362+
// 大合唱的选路策略
363+
var routeSelectionConfig = GiantChorusRouteSelectionConfig(GiantChorusRouteSelectionType.BY_DELAY, 6)
339364
}
340365

341366
/**

KTVAPI/Android/lib_ktvapi/src/main/java/io/agora/ktvapi/KTVGiantChorusApiImpl.kt

Lines changed: 146 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ class KTVGiantChorusApiImpl(
240240
// Android Only
241241
mRtcEngine.setParameters("{\"che.audio.enable_estimated_device_delay\":false}")
242242

243-
// TopN
243+
// TopN + SendAudioMetadata
244244
mRtcEngine.setParameters("{\"rtc.use_audio4\": true}")
245245

246246
// mutipath
@@ -753,6 +753,9 @@ class KTVGiantChorusApiImpl(
753753
mRtcEngine.setParameters("{\"che.audio.custom_bitrate\": 48000}")
754754
}
755755

756+
private val subScribeSingerMap = mutableMapOf<Int, Int>() // <uid, ntpE2eDelay>
757+
private val singerList = mutableListOf<Int>() // <uid>
758+
private var mainSingerDelay = 0
756759
private fun joinChorus(newRole: KTVSingRole) {
757760
ktvApiLog("joinChorus: $newRole")
758761
val singChannelMediaOptions = ChannelMediaOptions()
@@ -763,10 +766,11 @@ class KTVGiantChorusApiImpl(
763766
if (newRole == KTVSingRole.LeadSinger) {
764767
// 主唱不参加TopN
765768
singChannelMediaOptions.isAudioFilterable = false
766-
mRtcEngine.setParameters("{\"che.audio.filter_streams\":${giantChorusApiConfig.topN}}")
769+
mRtcEngine.setParameters("{\"che.audio.filter_streams\":${KTVApi.routeSelectionConfig.streamNum}}")
767770
} else {
768-
mRtcEngine.setParameters("{\"che.audio.filter_streams\":${giantChorusApiConfig.topN - 1}}")
771+
mRtcEngine.setParameters("{\"che.audio.filter_streams\":${KTVApi.routeSelectionConfig.streamNum - 1}}")
769772
}
773+
770774
// 加入演唱频道
771775
mRtcEngine.joinChannelEx(giantChorusApiConfig.chorusChannelToken, singChannelRtcConnection, singChannelMediaOptions, object :
772776
IRtcEngineEventHandler() {
@@ -825,8 +829,65 @@ class KTVGiantChorusApiImpl(
825829
isPublishAudio = false
826830
}
827831
}
832+
833+
// 延迟选路策略
834+
override fun onUserJoined(uid: Int, elapsed: Int) {
835+
super.onUserJoined(uid, elapsed)
836+
if (uid != giantChorusApiConfig.musicStreamUid && subScribeSingerMap.size < 8) {
837+
mRtcEngine.muteRemoteAudioStreamEx(uid, false, singChannelRtcConnection)
838+
if (uid != mainSingerUid) {
839+
subScribeSingerMap[uid] = 0
840+
}
841+
} else if (uid != giantChorusApiConfig.musicStreamUid && subScribeSingerMap.size == 8) {
842+
mRtcEngine.muteRemoteAudioStreamEx(uid, true, singChannelRtcConnection)
843+
}
844+
if (uid != giantChorusApiConfig.musicStreamUid && uid != mainSingerUid) {
845+
singerList.add(uid)
846+
}
847+
}
848+
849+
override fun onUserOffline(uid: Int, reason: Int) {
850+
super.onUserOffline(uid, reason)
851+
subScribeSingerMap.remove(uid)
852+
singerList.remove(uid)
853+
}
854+
855+
override fun onLeaveChannel(stats: RtcStats?) {
856+
super.onLeaveChannel(stats)
857+
subScribeSingerMap.clear()
858+
singerList.clear()
859+
}
860+
861+
override fun onRemoteAudioStats(stats: RemoteAudioStats?) {
862+
super.onRemoteAudioStats(stats)
863+
stats ?: return
864+
if (KTVApi.routeSelectionConfig.type == GiantChorusRouteSelectionType.RANDOM || KTVApi.routeSelectionConfig.type == GiantChorusRouteSelectionType.TOP_N) return
865+
val uid = stats.uid
866+
if (uid == mainSingerUid) {
867+
mainSingerDelay = stats.e2eDelay
868+
}
869+
// if (uid == mainSingerUid && stats.e2eDelay > 300) {
870+
// //ToastUtils.showToast("主唱 $mainSingerUid 延迟超过300ms,目前延迟:${stats.ntpE2eDelay}")
871+
// }
872+
// if (subScribeSingerMap.any { it.key == uid } && stats.e2eDelay > 300) {
873+
// //ToastUtils.showToast("当前订阅用户 $uid 延迟超过300ms,目前延迟:${stats.ntpE2eDelay}")
874+
// }
875+
if (uid != mainSingerUid && uid != giantChorusApiConfig.musicStreamUid && subScribeSingerMap.containsKey(uid)) {
876+
subScribeSingerMap[uid] = stats.e2eDelay
877+
}
878+
}
828879
})
829880
mRtcEngine.setParameters("{\"rtc.use_audio4\": true}")
881+
// 选路策略处理
882+
if (KTVApi.routeSelectionConfig.type == GiantChorusRouteSelectionType.TOP_N || KTVApi.routeSelectionConfig.type == GiantChorusRouteSelectionType.BY_DELAY_AND_TOP_N) {
883+
if (newRole == KTVSingRole.LeadSinger) {
884+
mRtcEngine.setParameters("{\"che.audio.filter_streams\":${KTVApi.routeSelectionConfig.streamNum}}")
885+
} else {
886+
mRtcEngine.setParameters("{\"che.audio.filter_streams\":${KTVApi.routeSelectionConfig.streamNum - 1}}")
887+
}
888+
} else {
889+
mRtcEngine.setParameters("{\"che.audio.filter_streams\": 0}")
890+
}
830891
mRtcEngine.enableAudioVolumeIndicationEx(50, 10, true, singChannelRtcConnection)
831892

832893
when (newRole) {
@@ -1063,6 +1124,86 @@ class KTVGiantChorusApiImpl(
10631124
}
10641125
}
10651126

1127+
// ------------------ 延迟选路 ------------------
1128+
private var mStopProcessDelay = true
1129+
1130+
private val mProcessDelayTask = Runnable {
1131+
if (!mStopProcessDelay && singerRole != KTVSingRole.Audience) {
1132+
val n = if (singerRole == KTVSingRole.LeadSinger) KTVApi.routeSelectionConfig.streamNum else KTVApi.routeSelectionConfig.streamNum -1
1133+
val sortedEntries = subScribeSingerMap.entries.sortedBy { it.value }
1134+
val other = sortedEntries.drop(3)
1135+
val drop = mutableListOf<Int>()
1136+
if (n > 3) {
1137+
other.drop(n - 3).forEach { (uid, _) ->
1138+
drop.add(uid)
1139+
mRtcEngine.muteRemoteAudioStreamEx(uid, true, singChannelRtcConnection)
1140+
subScribeSingerMap.remove(uid)
1141+
}
1142+
}
1143+
ktvApiLog("选路重新订阅, drop:$drop")
1144+
1145+
val filteredList = singerList.filter { !subScribeSingerMap.containsKey(it) }
1146+
val filteredList2 = filteredList.filter { !drop.contains(it) }
1147+
val shuffledList = filteredList2.shuffled()
1148+
if (subScribeSingerMap.size < 8) {
1149+
val randomSingers = shuffledList.take(8 - subScribeSingerMap.size)
1150+
ktvApiLog("选路重新订阅, newSingers:$randomSingers")
1151+
for (singer in randomSingers) {
1152+
subScribeSingerMap[singer] = 0
1153+
mRtcEngine.muteRemoteAudioStreamEx(singer, false, singChannelRtcConnection)
1154+
}
1155+
}
1156+
ktvApiLog("选路重新订阅, newSubScribeSingerMap:$subScribeSingerMap")
1157+
}
1158+
}
1159+
1160+
private val mProcessSubscribeTask = Runnable {
1161+
if (!mStopProcessDelay && singerRole != KTVSingRole.Audience) {
1162+
val n = if (singerRole == KTVSingRole.LeadSinger) KTVApi.routeSelectionConfig.streamNum else KTVApi.routeSelectionConfig.streamNum -1
1163+
val sortedEntries = subScribeSingerMap.entries.sortedBy { it.value }
1164+
val mustToHave = sortedEntries.take(3)
1165+
mustToHave.forEach { (uid, _) ->
1166+
mRtcEngine.adjustUserPlaybackSignalVolumeEx(uid, 100, singChannelRtcConnection)
1167+
}
1168+
val other = sortedEntries.drop(3)
1169+
if (n > 3) {
1170+
other.take(n - 3).forEach { (uid, delay) ->
1171+
if (delay > 300) {
1172+
mRtcEngine.adjustUserPlaybackSignalVolumeEx(uid, 0, singChannelRtcConnection)
1173+
} else {
1174+
mRtcEngine.adjustUserPlaybackSignalVolumeEx(uid, 100, singChannelRtcConnection)
1175+
}
1176+
}
1177+
other.drop(n - 3).forEach { (uid, _) ->
1178+
mRtcEngine.adjustUserPlaybackSignalVolumeEx(uid, 0, singChannelRtcConnection)
1179+
}
1180+
}
1181+
1182+
ktvApiLog("选路排序+调整播放音量, mustToHave:$mustToHave, other:$other")
1183+
}
1184+
}
1185+
1186+
private var mProcessDelayFuture :ScheduledFuture<*>? = null
1187+
private var mProcessSubscribeFuture :ScheduledFuture<*>? = null
1188+
private fun startProcessDelay() {
1189+
if (KTVApi.routeSelectionConfig.type == GiantChorusRouteSelectionType.TOP_N || KTVApi.routeSelectionConfig.type == GiantChorusRouteSelectionType.RANDOM) return
1190+
mStopProcessDelay = false
1191+
mProcessDelayFuture = scheduledThreadPool.scheduleAtFixedRate(mProcessDelayTask, 10000, 20000, TimeUnit.MILLISECONDS)
1192+
mProcessSubscribeFuture = scheduledThreadPool.scheduleAtFixedRate(mProcessSubscribeTask,15000,20000, TimeUnit.MILLISECONDS)
1193+
}
1194+
1195+
private fun stopProcessDelay() {
1196+
mStopProcessDelay = true
1197+
1198+
mProcessDelayFuture?.cancel(true)
1199+
mProcessSubscribeFuture?.cancel(true)
1200+
mProcessDelayFuture = null
1201+
if (scheduledThreadPool is ScheduledThreadPoolExecutor) {
1202+
scheduledThreadPool.remove(mProcessDelayTask)
1203+
scheduledThreadPool.remove(mProcessSubscribeTask)
1204+
}
1205+
}
1206+
10661207
private fun loadLyric(songNo: Long, onLoadLyricCallback: (songNo: Long, lyricUrl: String?) -> Unit) {
10671208
ktvApiLog("loadLyric: $songNo")
10681209
val requestId = mMusicCenter.getLyric(songNo, 0)
@@ -1346,6 +1487,7 @@ class KTVGiantChorusApiImpl(
13461487
) {
13471488
mPlayer.play()
13481489
}
1490+
startProcessDelay()
13491491
}
13501492
MediaPlayerState.PLAYER_STATE_PLAYING -> {
13511493
mRtcEngine.adjustPlaybackSignalVolume(KTVApi.remoteVolume)
@@ -1356,6 +1498,7 @@ class KTVGiantChorusApiImpl(
13561498
MediaPlayerState.PLAYER_STATE_STOPPED -> {
13571499
mRtcEngine.adjustPlaybackSignalVolume(100)
13581500
duration = 0
1501+
stopProcessDelay()
13591502
}
13601503
else -> {}
13611504
}

0 commit comments

Comments
 (0)