Capture encode and play stereo audio in Android live streams
This commit is contained in:
1 parent
22b62699ac
commit
e06e9b0e22
3 files changed
+42
-31
No files matched your search
@@ -345,11 +345,12 @@ class StreamCaptureEngine(
|
||||
}
|
||||
@SuppressLint("MissingPermission")
|
||||
private fun startAudio() {
|
||||
val min = AudioRecord.getMinBufferSize(48000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT)
|
||||
val min = AudioRecord.getMinBufferSize(48000, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT)
|
||||
check(min > 0)
|
||||
val rec = AudioRecord(MediaRecorder.AudioSource.CAMCORDER, 48000, AudioFormat.CHANNEL_IN_MONO,
|
||||
AudioFormat.ENCODING_PCM_16BIT, (min * 2).coerceAtLeast(960 * 8))
|
||||
val rec = AudioRecord(MediaRecorder.AudioSource.CAMCORDER, 48000, AudioFormat.CHANNEL_IN_STEREO,
|
||||
AudioFormat.ENCODING_PCM_16BIT, (min * 2).coerceAtLeast(1920 * 8))
|
||||
record = rec; check(rec.state == AudioRecord.STATE_INITIALIZED)
|
||||
check(rec.channelCount == 2) { "CAMCORDER stereo capture unavailable: channels=${rec.channelCount}" }
|
||||
rec.startRecording(); check(rec.recordingState == AudioRecord.RECORDSTATE_RECORDING) { "CAMCORDER capture did not start" }
|
||||
audioRunning = true
|
||||
LogManager.addLog("INFO", "stream", "audio capture started id=$id source=CAMCORDER(${rec.audioSource}) " +
|
||||
@@ -358,7 +359,7 @@ class StreamCaptureEngine(
|
||||
var opus = 0L
|
||||
try {
|
||||
opus = NativeLib.streamOpusCreate(true); check(opus != 0L)
|
||||
val pcm = ShortArray(960); val stamp = AudioTimestamp()
|
||||
val pcm = ShortArray(1920); val stamp = AudioTimestamp()
|
||||
var total = 0L; var waitingStamp = true; var last = -1L
|
||||
while (audioRunning) {
|
||||
var filled = 0
|
||||
@@ -368,7 +369,7 @@ class StreamCaptureEngine(
|
||||
if (n > 0) filled += n
|
||||
}
|
||||
if (!audioRunning || filled != pcm.size) break
|
||||
val first = total; total += filled
|
||||
val first = total; total += filled / 2 // AudioTimestamp считает кадры на канал, а read — interleaved сэмплы.
|
||||
if (rec.getTimestamp(stamp, AudioTimestamp.TIMEBASE_MONOTONIC) != AudioRecord.SUCCESS) {
|
||||
if (waitingStamp) LogManager.addLog("DEBUG", "stream", "audio timestamp pending id=$id samples=$total")
|
||||
continue
|
||||
|
||||
@@ -117,7 +117,10 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv
|
||||
while (running && offset < pcm.size) {
|
||||
val count = t.write(pcm, offset, pcm.size - offset, AudioTrack.WRITE_NON_BLOCKING)
|
||||
check(count >= 0) { "AudioTrack write=$count" }
|
||||
if (count > 0) { offset += count; written += count }
|
||||
if (count > 0) {
|
||||
check(count % 2 == 0) { "AudioTrack returned an incomplete stereo frame: samples=$count" }
|
||||
offset += count; written += count / 2
|
||||
}
|
||||
else {
|
||||
check(System.nanoTime() < deadline) { "AudioTrack stalled for 100ms" }
|
||||
Thread.sleep(2)
|
||||
@@ -207,13 +210,13 @@ class StreamPlayerEngine(private val group: Long, private val source: Long, priv
|
||||
opus = NativeLib.streamOpusCreate(false); check(opus != 0L)
|
||||
track = AudioTrack.Builder().setAudioAttributes(AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA)
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_MOVIE).build()).setAudioFormat(AudioFormat.Builder()
|
||||
.setSampleRate(48000).setChannelMask(AudioFormat.CHANNEL_OUT_MONO).setEncoding(AudioFormat.ENCODING_PCM_16BIT).build())
|
||||
.setBufferSizeInBytes(AudioTrack.getMinBufferSize(48000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT)
|
||||
.coerceAtLeast(9600)).setTransferMode(AudioTrack.MODE_STREAM).build()
|
||||
.setSampleRate(48000).setChannelMask(AudioFormat.CHANNEL_OUT_STEREO).setEncoding(AudioFormat.ENCODING_PCM_16BIT).build())
|
||||
.setBufferSizeInBytes(AudioTrack.getMinBufferSize(48000, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT)
|
||||
.coerceAtLeast(19200)).setTransferMode(AudioTrack.MODE_STREAM).build()
|
||||
check(track!!.state == AudioTrack.STATE_INITIALIZED)
|
||||
audioBase = audios.first.pts
|
||||
track!!.play()
|
||||
LogManager.addLog("INFO", "stream", "audio playback starts id=$id PTS=$audioBase")
|
||||
LogManager.addLog("INFO", "stream", "audio playback starts id=$id PTS=$audioBase rate=48000 channels=${track!!.channelCount}")
|
||||
}
|
||||
val t = track
|
||||
if (t != null && audios.isNotEmpty()) {
|
||||
|
||||
Reference in new issue
Block a user