Toggle navigation
Toggle navigation
此项目
正在载入...
Sign in
xuning
/
livekitAndroidXuningTest
转到一个项目
Toggle navigation
项目
群组
代码片段
帮助
Toggle navigation pinning
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
Antoine Baché
2024-02-20 03:28:35 +0100
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Committed by
GitHub
2024-02-20 11:28:35 +0900
Commit
c1be1855950e56dbe40d8ba581ee195c845da578
c1be1855
1 parent
72131d40
CommunicationWorkaround crash fix and global references leak (#379)
显示空白字符变更
内嵌
并排对比
正在显示
1 个修改的文件
包含
49 行增加
和
31 行删除
livekit-android-sdk/src/main/java/io/livekit/android/audio/CommunicationWorkaround.kt
livekit-android-sdk/src/main/java/io/livekit/android/audio/CommunicationWorkaround.kt
查看文件 @
c1be185
...
...
@@ -27,10 +27,12 @@ import io.livekit.android.dagger.InjectionNames
import io.livekit.android.util.CloseableCoroutineScope
import kotlinx.coroutines.MainCoroutineDispatcher
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
import java.nio.ByteBuffer
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
import javax.inject.Named
import javax.inject.Singleton
...
...
@@ -87,11 +89,13 @@ constructor(
private var audioTrack: AudioTrack? = null
private val isAudioTrackStarted = AtomicBoolean(false)
init {
coroutineScope.launch {
started.combine(playoutStopped) { a, b -> a to b }
.distinctUntilChanged()
.collect { (started, playoutStopped) ->
.collect
Latest
{ (started, playoutStopped) ->
onStateChanged(started, playoutStopped)
}
}
...
...
@@ -113,32 +117,32 @@ constructor(
playoutStopped.value = true
}
@SuppressLint("NewApi")
override fun dispose() {
coroutineScope.close()
stop()
audioTrack?.stop()
audioTrack?.release()
}
private fun onStateChanged(started: Boolean, playoutStopped: Boolean) {
if (started && playoutStopped) {
start
AudioTrackIfNeeded()
play
AudioTrackIfNeeded()
} else {
stop
AudioTrackIfNeeded()
pause
AudioTrackIfNeeded()
}
}
@SuppressLint("Range")
private fun startAudioTrackIfNeeded() {
if (audioTrack != null) {
return
}
private fun buildAudioTrack(): AudioTrack {
val audioSample = ByteBuffer.allocateDirect(getBytesPerSample(AUDIO_FORMAT) * AUDIO_FRAME_PER_BUFFER)
val sampleRate = 16000
val audioFormat = AudioFormat.ENCODING_PCM_16BIT
val bytesPerFrame = 1 * getBytesPerSample(audioFormat)
val framesPerBuffer: Int = sampleRate / 100 // 10 ms
val byteBuffer = ByteBuffer.allocateDirect(bytesPerFrame * framesPerBuffer)
audioTrack = AudioTrack.Builder()
return AudioTrack.Builder()
.setAudioFormat(
AudioFormat.Builder()
.setEncoding(audioFormat)
.setSampleRate(sampleRate)
.setEncoding(AUDIO_FORMAT)
.setSampleRate(SAMPLE_RATE)
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
.build(),
)
...
...
@@ -148,15 +152,35 @@ constructor(
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build(),
)
.setBufferSizeInBytes(
byteBuffer
.capacity())
.setBufferSizeInBytes(
audioSample
.capacity())
.setTransferMode(AudioTrack.MODE_STATIC)
.setSessionId(AudioManager.AUDIO_SESSION_ID_GENERATE)
.build()
.apply {
write(audioSample, audioSample.remaining(), AudioTrack.WRITE_BLOCKING)
setLoopPoints(0, AUDIO_FRAME_PER_BUFFER - 1, -1)
}
}
audioTrack?.write(byteBuffer, byteBuffer.remaining(), AudioTrack.WRITE_BLOCKING)
audioTrack?.setLoopPoints(0, framesPerBuffer - 1, -1)
private fun playAudioTrackIfNeeded() {
val swapped = isAudioTrackStarted.compareAndSet(false, true)
if (!swapped) {
// Already playing, nothing to do
return
}
val audioTrack = audioTrack ?: buildAudioTrack().also { audioTrack = it }
audioTrack.play()
}
private fun pauseAudioTrackIfNeeded() {
val swapped = isAudioTrackStarted.compareAndSet(true, false)
if (!swapped) {
// Already stopped, nothing to do
return
}
audioTrack?.p
lay
()
audioTrack?.p
ause
()
}
// Reference from Android code, AudioFormat.getBytesPerSample. BitPerSample / 8
...
...
@@ -172,15 +196,9 @@ constructor(
}
}
private fun stopAudioTrackIfNeeded() {
audioTrack?.stop()
audioTrack?.release()
audioTrack = null
}
override fun dispose() {
coroutineScope.close()
stop()
stopAudioTrackIfNeeded()
companion object {
private const val SAMPLE_RATE = 16000
private const val AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT
private const val AUDIO_FRAME_PER_BUFFER = SAMPLE_RATE / 100 // 10 ms
}
}
...
...
请
注册
或
登录
后发表评论