diff --git a/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/ts/TSMuxer.kt b/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/ts/TSMuxer.kt index 7768270b6..40b686998 100644 --- a/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/ts/TSMuxer.kt +++ b/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/ts/TSMuxer.kt @@ -20,7 +20,6 @@ import android.media.MediaFormat import io.github.thibaultbee.streampack.data.AudioConfig import io.github.thibaultbee.streampack.data.Config import io.github.thibaultbee.streampack.internal.data.Frame -import io.github.thibaultbee.streampack.internal.orientation.ISourceOrientationProvider import io.github.thibaultbee.streampack.internal.muxers.IMuxer import io.github.thibaultbee.streampack.internal.muxers.IMuxerListener import io.github.thibaultbee.streampack.internal.muxers.ts.data.Service @@ -32,9 +31,10 @@ import io.github.thibaultbee.streampack.internal.muxers.ts.packets.Pmt import io.github.thibaultbee.streampack.internal.muxers.ts.packets.Sdt import io.github.thibaultbee.streampack.internal.muxers.ts.utils.MuxerConst import io.github.thibaultbee.streampack.internal.muxers.ts.utils.TSConst +import io.github.thibaultbee.streampack.internal.muxers.ts.utils.av.OpusControlHeader +import io.github.thibaultbee.streampack.internal.orientation.ISourceOrientationProvider import io.github.thibaultbee.streampack.internal.utils.av.audio.aac.ADTSFrameWriter import io.github.thibaultbee.streampack.internal.utils.av.audio.aac.LATMFrameWriter -import io.github.thibaultbee.streampack.internal.utils.av.audio.opus.OpusFrameWriter import java.nio.ByteBuffer import java.util.MissingFormatArgumentException import kotlin.random.Random @@ -151,8 +151,17 @@ class TSMuxer( } MediaFormat.MIMETYPE_AUDIO_OPUS -> { - frame.copy(rawBuffer = OpusFrameWriter.fromPayload(frame.rawBuffer).toByteBuffer()) + val payloadSize = frame.buffer.remaining() + val controlHeader = OpusControlHeader( + payloadSize = payloadSize + ) + val buffer = ByteBuffer.allocate(controlHeader.size + payloadSize) + controlHeader.write(buffer) + buffer.put(frame.buffer) + buffer.rewind() + frame.copy(rawBuffer = buffer) } + else -> throw IllegalArgumentException("Unsupported mimeType ${frame.mimeType}") } diff --git a/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/ts/utils/av/OpusControlHeader.kt b/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/ts/utils/av/OpusControlHeader.kt new file mode 100644 index 000000000..3a10d80ca --- /dev/null +++ b/core/src/main/java/io/github/thibaultbee/streampack/internal/muxers/ts/utils/av/OpusControlHeader.kt @@ -0,0 +1,44 @@ +package io.github.thibaultbee.streampack.internal.muxers.ts.utils.av + +import io.github.thibaultbee.streampack.internal.utils.av.buffer.ByteBufferWriter +import io.github.thibaultbee.streampack.internal.utils.extensions.put +import io.github.thibaultbee.streampack.internal.utils.extensions.shl +import java.nio.ByteBuffer +import kotlin.experimental.and +import kotlin.math.min + +data class OpusControlHeader( + private val prefix: Short = 0x3FF, + private val payloadSize: Int, + private val startTrim: Short? = null, + private val endTrim: Short? = null, +) : ByteBufferWriter() { + override val size = + 3 + payloadSize / UByte.MAX_VALUE.toInt() + (startTrim?.let { 2 } + ?: 0) + (endTrim?.let { 2 } ?: 0) + + override fun write(output: ByteBuffer) { + // control_header_prefix 11 bits (0x3FF or 01111111111) + // start_trim_flag 1 bit (0) + // end_trim_flag 1 bit (0) + // control_extension_flag 1 bit (0) + // reserved 2 bits (0) + output.putShort(((prefix shl 5) + or ((startTrim?.let { 1 } ?: 0) shl 4) + or ((endTrim?.let { 1 } ?: 0) shl 3)).toShort()) + + var n = payloadSize + while (n >= 0) { + output.put(min(n, UByte.MAX_VALUE.toInt())) + n -= UByte.MAX_VALUE.toInt() + } + + if (startTrim != null) { + output.putShort(startTrim and 0x7FF) + } + + if (endTrim != null) { + output.putShort(endTrim and 0x7FF) + } + } +} \ No newline at end of file diff --git a/core/src/main/java/io/github/thibaultbee/streampack/internal/utils/av/audio/opus/OpusFrameWriter.kt b/core/src/main/java/io/github/thibaultbee/streampack/internal/utils/av/audio/opus/OpusFrameWriter.kt deleted file mode 100644 index 747ff6c65..000000000 --- a/core/src/main/java/io/github/thibaultbee/streampack/internal/utils/av/audio/opus/OpusFrameWriter.kt +++ /dev/null @@ -1,42 +0,0 @@ -package io.github.thibaultbee.streampack.internal.utils.av.audio.opus - -import io.github.thibaultbee.streampack.internal.utils.av.buffer.ByteBufferWriter -import java.nio.ByteBuffer - -class OpusFrameWriter(private val frameBuffer: ByteBuffer) : ByteBufferWriter() { - private val payloadSize: Int - get() = frameBuffer.remaining() - private val payloadSizeFullBytesCount: Int - get() = payloadSize / 255 - private val payloadSizeRemainderByte: Int - get() = payloadSize % 255 - private val controlHeaderSize: Int - get() = 2 + payloadSizeFullBytesCount + payloadSizeRemainderByte.coerceAtMost(1) - - override val size = controlHeaderSize + payloadSize - - override fun write(output: ByteBuffer) { - // control_header_prefix 11 bits (0x3FF or 01111111111) - // start_trim_flag 1 bit (0) - // end_trim_flag 1 bit (0) - // control_extension_flag 1 bit (0) - // reserved 2 bits (0) - output.put(0x7F.toByte()) - output.put(0xE0.toByte()) - - repeat(payloadSizeFullBytesCount) { - output.put(0xFF.toByte()) - } - if (payloadSizeRemainderByte > 0) { - output.put(payloadSizeRemainderByte.toByte()) - } - - output.put(frameBuffer) - } - - companion object { - fun fromPayload(frameBuffer: ByteBuffer): OpusFrameWriter { - return OpusFrameWriter(frameBuffer) - } - } -} \ No newline at end of file diff --git a/core/src/main/java/io/github/thibaultbee/streampack/internal/utils/extensions/BitOperationExtensions.kt b/core/src/main/java/io/github/thibaultbee/streampack/internal/utils/extensions/BitOperationExtensions.kt index c36fa2646..6d4388cfe 100644 --- a/core/src/main/java/io/github/thibaultbee/streampack/internal/utils/extensions/BitOperationExtensions.kt +++ b/core/src/main/java/io/github/thibaultbee/streampack/internal/utils/extensions/BitOperationExtensions.kt @@ -55,10 +55,11 @@ infix fun Byte.shr(i: Int) = infix fun Byte.or(b: Byte) = this.toInt() or b.toInt() - infix fun Byte.or(other: Int) = this.toInt() or other +infix fun Short.shl(i: Int) = + this.toInt() shl i fun Int.toBitList(): List { val list = mutableListOf() diff --git a/core/src/test/java/io/github/thibaultbee/streampack/internal/muxers/ts/utils/av/OpusControlHeaderTest.kt b/core/src/test/java/io/github/thibaultbee/streampack/internal/muxers/ts/utils/av/OpusControlHeaderTest.kt new file mode 100644 index 000000000..239458971 --- /dev/null +++ b/core/src/test/java/io/github/thibaultbee/streampack/internal/muxers/ts/utils/av/OpusControlHeaderTest.kt @@ -0,0 +1,19 @@ +package io.github.thibaultbee.streampack.internal.muxers.ts.utils.av + +import io.github.thibaultbee.streampack.internal.utils.extensions.toByteArray +import org.junit.Assert.assertArrayEquals +import org.junit.Test + +class OpusControlHeaderTest { + @Test + fun `test write`() { + val opusControlHeader = OpusControlHeader(payloadSize = 515) + val output = opusControlHeader.toByteBuffer() + assertArrayEquals( + byteArrayOf( + 0x7F, 0xE0.toByte(), 0xFF.toByte(), 0xFF.toByte(), 0x5 + ), + output.toByteArray() + ) + } +} \ No newline at end of file