diff --git a/common/src/main/java/com/pedro/common/Extensions.kt b/common/src/main/java/com/pedro/common/Extensions.kt index 1113b1d86..2fafc8368 100644 --- a/common/src/main/java/com/pedro/common/Extensions.kt +++ b/common/src/main/java/com/pedro/common/Extensions.kt @@ -286,13 +286,15 @@ fun Long.compare(l: Long): Int { } fun SurfaceTexture.tryClear() { - val surface = Surface(this) + //Surface constructor fail if the SurfaceTexture is already released + var surface: Surface? = null try { + surface = Surface(this) val canvas = surface.lockCanvas(null) canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR) surface.unlockCanvasAndPost(canvas) } catch (_: Exception) {} finally { - surface.release() + surface?.release() } } diff --git a/library/src/main/java/com/pedro/library/base/StreamBase.kt b/library/src/main/java/com/pedro/library/base/StreamBase.kt index a49b79ac0..5f0aabb3b 100644 --- a/library/src/main/java/com/pedro/library/base/StreamBase.kt +++ b/library/src/main/java/com/pedro/library/base/StreamBase.kt @@ -411,7 +411,7 @@ abstract class StreamBase( } videoSource.stop() videoSource.release() - glInterface.surfaceTexture.tryClear() + if (glInterface.isRunning) glInterface.surfaceTexture.tryClear() if (wasRunning) source.start(glInterface.surfaceTexture) glInterface.setOrientationConfig(source.getOrientationConfig()) videoSource = source @@ -544,7 +544,7 @@ abstract class StreamBase( stopSources() videoSource.release() audioSource.release() - glInterface.surfaceTexture.tryClear() + if (glInterface.isRunning) glInterface.surfaceTexture.tryClear() } /** diff --git a/library/src/main/java/com/pedro/library/util/FlvMuxerRecordController.kt b/library/src/main/java/com/pedro/library/util/FlvMuxerRecordController.kt index 3a66ab4e8..3cb01ff1b 100644 --- a/library/src/main/java/com/pedro/library/util/FlvMuxerRecordController.kt +++ b/library/src/main/java/com/pedro/library/util/FlvMuxerRecordController.kt @@ -71,6 +71,7 @@ class FlvMuxerRecordController: AsyncBaseRecordController() { VideoCodec.VP8 -> Vp8Packet() VideoCodec.VP9 -> Vp9Packet() } + sendInfo = false outputStream?.let { try { it.write(createFlvFileHeader()) @@ -217,6 +218,11 @@ class FlvMuxerRecordController: AsyncBaseRecordController() { } } is Vp8Packet -> sendInfo = true + //vp9 video info is not in the MediaFormat, only extracted from keyframes + is Vp9Packet -> {} + else -> { + Log.e(TAG, "Unsupported codec: ${videoPacket?.javaClass?.name ?: "null"}") + } } if (sendInfo && recordStatus == RecordController.Status.STARTED) { myRequestKeyFrame = null diff --git a/library/src/main/java/com/pedro/library/util/Mpeg2TsMuxerRecordController.kt b/library/src/main/java/com/pedro/library/util/Mpeg2TsMuxerRecordController.kt index 57b08f4e7..a2cd414c7 100644 --- a/library/src/main/java/com/pedro/library/util/Mpeg2TsMuxerRecordController.kt +++ b/library/src/main/java/com/pedro/library/util/Mpeg2TsMuxerRecordController.kt @@ -15,10 +15,10 @@ import com.pedro.library.base.recording.RecordController.RecordTracks import com.pedro.srt.mpeg2ts.MpegTsPacket import com.pedro.srt.mpeg2ts.MpegTsPacketizer import com.pedro.srt.mpeg2ts.MpegType -import com.pedro.srt.mpeg2ts.Pid import com.pedro.srt.mpeg2ts.packets.AacPacket import com.pedro.srt.mpeg2ts.packets.BasePacket -import com.pedro.srt.mpeg2ts.packets.H26XPacket +import com.pedro.srt.mpeg2ts.packets.H264Packet +import com.pedro.srt.mpeg2ts.packets.H265Packet import com.pedro.srt.mpeg2ts.packets.OpusPacket import com.pedro.srt.mpeg2ts.psi.Psi import com.pedro.srt.mpeg2ts.psi.PsiManager @@ -51,7 +51,7 @@ class Mpeg2TsMuxerRecordController : AsyncBaseRecordController() { private val mpegTsPacketizer = MpegTsPacketizer(psiManager) private var audioPacket: BasePacket = AacPacket(limitSize, psiManager) - private val videoPacket = H26XPacket(limitSize, psiManager) + private var videoPacket: BasePacket = H264Packet(limitSize, psiManager) private val chunkSize = limitSize / MpegTsPacketizer.packetSize private var sampleRate = 0 private var isStereo = true @@ -86,11 +86,14 @@ class Mpeg2TsMuxerRecordController : AsyncBaseRecordController() { throw IOException("Unsupported AudioCodec: " + getAudioCodec().name) } } - if (getVideoCodec() == VideoCodec.AV1 || getVideoCodec() == VideoCodec.VP8 || getVideoCodec() == VideoCodec.VP9) { - throw IOException("Unsupported VideoCodec: " + getVideoCodec().name) + videoPacket = when (getVideoCodec()) { + VideoCodec.H264 -> H264Packet(limitSize, psiManager) + VideoCodec.H265 -> H265Packet(limitSize, psiManager) + else -> { + throw IOException("Unsupported VideoCodec: " + getVideoCodec().name) + } } - audioPacket.setLimitSize(limitSize) - videoPacket.setLimitSize(limitSize) + sendInfo = false outputStream?.let { val videoEnabled = tracks == RecordTracks.VIDEO || tracks == RecordTracks.ALL val audioEnabled = tracks == RecordTracks.AUDIO || tracks == RecordTracks.ALL @@ -182,29 +185,29 @@ class Mpeg2TsMuxerRecordController : AsyncBaseRecordController() { private fun getVideoInfo(buffer: ByteBuffer, info: MediaCodec.BufferInfo) { if (info.isKeyframe() || isKeyFrame(buffer)) { if (!sendInfo) { - when (getVideoCodec()) { - VideoCodec.H264 -> { + when (videoPacket) { + is H264Packet -> { val buffers = VideoEncoderHelper.decodeSpsPpsFromBuffer(buffer.duplicate(), info.size) if (buffers != null) { Log.i(TAG, "manual sps/pps extraction success") val oldSps = buffers.first val oldPps = buffers.second - videoPacket.sendVideoInfo(oldSps, oldPps, null) + (videoPacket as H264Packet).sendVideoInfo(oldSps, oldPps) sendInfo = true } else { Log.e(TAG, "manual sps/pps extraction failed") } } - VideoCodec.H265 -> { + is H265Packet -> { val byteBufferList = VideoEncoderHelper.extractVpsSpsPpsFromH265(buffer.duplicate()) if (byteBufferList.size == 3) { Log.i(TAG, "manual vps/sps/pps extraction success") val oldSps = byteBufferList[1] val oldPps = byteBufferList[2] val oldVps = byteBufferList[0] - videoPacket.sendVideoInfo(oldSps, oldPps, oldVps) + (videoPacket as H265Packet).sendVideoInfo(oldSps, oldPps, oldVps) sendInfo = true } else { Log.e(TAG, "manual vps/sps/pps extraction failed") @@ -212,7 +215,7 @@ class Mpeg2TsMuxerRecordController : AsyncBaseRecordController() { } else -> { - Log.e(TAG, "Unsupported codec: ${getVideoCodec()}") + Log.e(TAG, "Unsupported codec: ${videoPacket.javaClass.name}") } } } @@ -228,17 +231,17 @@ class Mpeg2TsMuxerRecordController : AsyncBaseRecordController() { } override fun setVideoFormat(videoFormat: MediaFormat) { - when (getVideoCodec()) { - VideoCodec.H264 -> { + when (videoPacket) { + is H264Packet -> { val sps = videoFormat.getByteBuffer("csd-0") val pps = videoFormat.getByteBuffer("csd-1") if (sps != null && pps != null) { - videoPacket.sendVideoInfo(sps.duplicate(), pps.duplicate(), null) + (videoPacket as H264Packet).sendVideoInfo(sps.duplicate(), pps.duplicate()) sendInfo = true } } - VideoCodec.H265 -> { + is H265Packet -> { val bufferInfo = videoFormat.getByteBuffer("csd-0") if (bufferInfo != null) { val byteBufferList = VideoEncoderHelper.extractVpsSpsPpsFromH265(bufferInfo.duplicate()) @@ -246,13 +249,15 @@ class Mpeg2TsMuxerRecordController : AsyncBaseRecordController() { val sps = byteBufferList[1] val pps = byteBufferList[2] val vps = byteBufferList[0] - videoPacket.sendVideoInfo(sps, pps, vps) + (videoPacket as H265Packet).sendVideoInfo(sps, pps, vps) sendInfo = true } } } - else -> {} + else -> { + Log.e(TAG, "Unsupported codec: ${videoPacket.javaClass.name}") + } } if (sendInfo && recordStatus == RecordController.Status.STARTED) { myRequestKeyFrame = null diff --git a/srt/src/main/java/com/pedro/srt/mpeg2ts/packets/H264Packet.kt b/srt/src/main/java/com/pedro/srt/mpeg2ts/packets/H264Packet.kt new file mode 100644 index 000000000..28705e0c0 --- /dev/null +++ b/srt/src/main/java/com/pedro/srt/mpeg2ts/packets/H264Packet.kt @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2024 pedroSG94. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.pedro.srt.mpeg2ts.packets + +import android.util.Log +import com.pedro.common.VideoCodec +import com.pedro.common.frame.MediaFrame +import com.pedro.common.getData +import com.pedro.common.nal.NalReader +import com.pedro.common.removeInfo +import com.pedro.srt.mpeg2ts.MpegTsPacket +import com.pedro.srt.mpeg2ts.MpegType +import com.pedro.srt.mpeg2ts.Pes +import com.pedro.srt.mpeg2ts.PesType +import com.pedro.srt.mpeg2ts.psi.PsiManager +import com.pedro.srt.srt.packets.data.PacketPosition +import com.pedro.srt.utils.chunkPackets +import com.pedro.srt.utils.getPayload +import java.nio.ByteBuffer + +/** + * Created by pedro on 20/8/23. + * + * Used for H264/H265 + */ +class H264Packet( + limitSize: Int, + psiManager: PsiManager, +): BasePacket(psiManager, limitSize) { + + private val TAG = "H264Packet" + + private var sps: ByteBuffer? = null + private var pps: ByteBuffer? = null + + override suspend fun createAndSendPacket( + mediaFrame: MediaFrame, + callback: suspend (List) -> Unit + ) { + val fixedBuffer = mediaFrame.data.removeInfo(mediaFrame.info) + val isKeyFrame = mediaFrame.info.isKeyFrame + val nals = NalReader.extractNals(fixedBuffer, VideoCodec.H264, false) + if (nals.isEmpty()) return + + val sps = this.sps + val pps = this.pps + if (sps == null || pps == null) { + Log.e(TAG, "waiting for a valid video info") + return + } + + if (isKeyFrame) { + if (!nals.contains(pps)) nals.add(0, pps.duplicate()) + if (!nals.contains(sps)) nals.add(0, sps.duplicate()) + } + + val payload = nals.getPayload { + ByteBuffer.allocate(6).apply { + putInt(0x00000001) //annex-b header + put(0x09.toByte()) + put(0xf0.toByte()) + flip() + } + } + val pes = Pes(psiManager.getVideoPid().toInt(), isKeyFrame, PesType.VIDEO, mediaFrame.info.timestamp, payload) + val mpeg2tsPackets = mpegTsPacketizer.write(listOf(pes)).chunkPackets(chunkSize).map { buffer -> + MpegTsPacket(buffer, MpegType.VIDEO, PacketPosition.SINGLE, isKeyFrame) + } + if (mpeg2tsPackets.isNotEmpty()) callback(mpeg2tsPackets) + } + + override fun resetPacket(resetInfo: Boolean) { + if (resetInfo) { + sps = null + pps = null + } + } + + fun sendVideoInfo(sps: ByteBuffer, pps: ByteBuffer) { + this.sps = ByteBuffer.wrap(sps.getData()) + this.pps = ByteBuffer.wrap(pps.getData()) + } +} \ No newline at end of file diff --git a/srt/src/main/java/com/pedro/srt/mpeg2ts/packets/H26XPacket.kt b/srt/src/main/java/com/pedro/srt/mpeg2ts/packets/H265Packet.kt similarity index 60% rename from srt/src/main/java/com/pedro/srt/mpeg2ts/packets/H26XPacket.kt rename to srt/src/main/java/com/pedro/srt/mpeg2ts/packets/H265Packet.kt index 5cf2f4e3d..a6f1eef8f 100644 --- a/srt/src/main/java/com/pedro/srt/mpeg2ts/packets/H26XPacket.kt +++ b/srt/src/main/java/com/pedro/srt/mpeg2ts/packets/H265Packet.kt @@ -29,6 +29,7 @@ import com.pedro.srt.mpeg2ts.PesType import com.pedro.srt.mpeg2ts.psi.PsiManager import com.pedro.srt.srt.packets.data.PacketPosition import com.pedro.srt.utils.chunkPackets +import com.pedro.srt.utils.getPayload import java.nio.ByteBuffer /** @@ -36,17 +37,16 @@ import java.nio.ByteBuffer * * Used for H264/H265 */ -class H26XPacket( +class H265Packet( limitSize: Int, psiManager: PsiManager, ): BasePacket(psiManager, limitSize) { - private val TAG = "H26XPacket" + private val TAG = "H265Packet" private var sps: ByteBuffer? = null private var pps: ByteBuffer? = null private var vps: ByteBuffer? = null - private var codec = VideoCodec.H264 override suspend fun createAndSendPacket( mediaFrame: MediaFrame, @@ -54,13 +54,13 @@ class H26XPacket( ) { val fixedBuffer = mediaFrame.data.removeInfo(mediaFrame.info) val isKeyFrame = mediaFrame.info.isKeyFrame - val nals = NalReader.extractNals(fixedBuffer, codec, false) + val nals = NalReader.extractNals(fixedBuffer, VideoCodec.H265, false) if (nals.isEmpty()) return val sps = this.sps val pps = this.pps val vps = this.vps - if (sps == null || pps == null || (codec == VideoCodec.H265 && vps == null)) { + if (sps == null || pps == null || vps == null) { Log.e(TAG, "waiting for a valid video info") return } @@ -68,10 +68,18 @@ class H26XPacket( if (isKeyFrame) { if (!nals.contains(pps)) nals.add(0, pps.duplicate()) if (!nals.contains(sps)) nals.add(0, sps.duplicate()) - if (vps != null) if (!nals.contains(vps)) nals.add(0, vps.duplicate()) + if (!nals.contains(vps)) nals.add(0, vps.duplicate()) } - val payload = getPayload(nals, isKeyFrame) + val payload = nals.getPayload { + ByteBuffer.allocate(7).apply { + putInt(0x00000001) //annex-b header + put(0x46.toByte()) + put(0x01.toByte()) + put(0x50.toByte()) + flip() + } + } val pes = Pes(psiManager.getVideoPid().toInt(), isKeyFrame, PesType.VIDEO, mediaFrame.info.timestamp, payload) val mpeg2tsPackets = mpegTsPacketizer.write(listOf(pes)).chunkPackets(chunkSize).map { buffer -> MpegTsPacket(buffer, MpegType.VIDEO, PacketPosition.SINGLE, isKeyFrame) @@ -87,50 +95,9 @@ class H26XPacket( } } - fun setVideoCodec(codec: VideoCodec) { - if (codec != VideoCodec.H265 && codec != VideoCodec.H264) { - throw IllegalArgumentException("This packet only support H264 and H265") - } - this.codec = codec - } - - fun sendVideoInfo(sps: ByteBuffer, pps: ByteBuffer?, vps: ByteBuffer?) { + fun sendVideoInfo(sps: ByteBuffer, pps: ByteBuffer, vps: ByteBuffer) { this.sps = ByteBuffer.wrap(sps.getData()) - this.pps = pps?.let { ByteBuffer.wrap(pps.getData()) } - this.vps = vps?.let { ByteBuffer.wrap(vps.getData()) } - } - - private fun getPayload(nals: List, isKeyFrame: Boolean): ByteBuffer { - val nalsSize = nals.sumOf { it.remaining() } - val bufferSize = if (isKeyFrame) { - val audSize = when (codec) { - VideoCodec.H265 -> 7 - else -> 6 - } - audSize + nalsSize + (nals.size * 4) - } else nalsSize + (nals.size * 4) - - val payload = ByteBuffer.allocate(bufferSize) - //add AUD nal - if (isKeyFrame) { - payload.putInt(0x00000001) //annex-b header - when (codec) { - VideoCodec.H265 -> { - payload.put(0x46.toByte()) - payload.put(0x01.toByte()) - payload.put(0x50.toByte()) - } - else -> { - payload.put(0x09.toByte()) - payload.put(0xf0.toByte()) - } - } - } - nals.forEach { - payload.putInt(0x00000001) //annex-b header - payload.put(it) - } - payload.flip() - return payload + this.pps = ByteBuffer.wrap(pps.getData()) + this.vps = ByteBuffer.wrap(vps.getData()) } } \ No newline at end of file diff --git a/srt/src/main/java/com/pedro/srt/srt/SrtSender.kt b/srt/src/main/java/com/pedro/srt/srt/SrtSender.kt index 0383228f0..a430e83b9 100644 --- a/srt/src/main/java/com/pedro/srt/srt/SrtSender.kt +++ b/srt/src/main/java/com/pedro/srt/srt/SrtSender.kt @@ -19,6 +19,7 @@ package com.pedro.srt.srt import android.util.Log import com.pedro.common.AudioCodec import com.pedro.common.ConnectChecker +import com.pedro.common.VideoCodec import com.pedro.common.base.BaseSender import com.pedro.common.frame.MediaFrame import com.pedro.common.onMainThread @@ -28,7 +29,8 @@ import com.pedro.srt.mpeg2ts.MpegTsPacketizer import com.pedro.srt.mpeg2ts.MpegType import com.pedro.srt.mpeg2ts.packets.AacPacket import com.pedro.srt.mpeg2ts.packets.BasePacket -import com.pedro.srt.mpeg2ts.packets.H26XPacket +import com.pedro.srt.mpeg2ts.packets.H264Packet +import com.pedro.srt.mpeg2ts.packets.H265Packet import com.pedro.srt.mpeg2ts.packets.OpusPacket import com.pedro.srt.mpeg2ts.psi.Psi import com.pedro.srt.mpeg2ts.psi.PsiManager @@ -62,7 +64,7 @@ class SrtSender( private val mpegTsPacketizer = MpegTsPacketizer(psiManager) private var audioPacket: BasePacket = AacPacket(limitSize, psiManager) - private val videoPacket = H26XPacket(limitSize, psiManager) + private var videoPacket: BasePacket = H264Packet(limitSize, psiManager) var socket: SrtSocket? = null private fun setTrackConfig(videoEnabled: Boolean, audioEnabled: Boolean) { @@ -74,16 +76,36 @@ class SrtSender( } override fun setVideoInfo(sps: ByteBuffer, pps: ByteBuffer?, vps: ByteBuffer?) { - videoPacket.setVideoCodec(commandsManager.videoCodec) - videoPacket.sendVideoInfo(sps, pps, vps) + videoPacket = when (commandsManager.videoCodec) { + VideoCodec.H264 -> { + if (pps == null) throw IllegalArgumentException("pps can't be null with h264") + (videoPacket as? H264Packet ?: H264Packet(limitSize, psiManager)).apply { + setLimitSize(limitSize) + sendVideoInfo(sps, pps) + } + } + VideoCodec.H265 -> { + if (vps == null || pps == null) throw IllegalArgumentException("pps or vps can't be null with h265") + (videoPacket as? H265Packet ?: H265Packet(limitSize, psiManager)).apply { + setLimitSize(limitSize) + sendVideoInfo(sps, pps, vps) + } + } + else -> { + throw IllegalArgumentException("Unsupported codec: ${commandsManager.videoCodec.name}") + } + } } override fun setAudioInfo(sampleRate: Int, isStereo: Boolean) { audioPacket = when (commandsManager.audioCodec) { - AudioCodec.AAC, AudioCodec.HE_AAC -> AacPacket(limitSize, psiManager).apply { + AudioCodec.AAC, AudioCodec.HE_AAC -> (audioPacket as? AacPacket ?: AacPacket(limitSize, psiManager)).apply { + setLimitSize(limitSize) sendAudioInfo(sampleRate, isStereo, commandsManager.audioCodec) } - AudioCodec.OPUS -> OpusPacket(limitSize, psiManager) + AudioCodec.OPUS -> (audioPacket as? OpusPacket ?: OpusPacket(limitSize, psiManager)).apply { + setLimitSize(limitSize) + } AudioCodec.G711 -> { throw IllegalArgumentException("Unsupported codec: ${commandsManager.audioCodec.name}") } diff --git a/srt/src/main/java/com/pedro/srt/utils/Extensions.kt b/srt/src/main/java/com/pedro/srt/utils/Extensions.kt index 3c385a3f8..f4177138e 100644 --- a/srt/src/main/java/com/pedro/srt/utils/Extensions.kt +++ b/srt/src/main/java/com/pedro/srt/utils/Extensions.kt @@ -64,4 +64,23 @@ fun List.chunkPackets(size: Int): List { chunks.forEach { buffer.put(it) } buffer.toByteArray() } +} + +fun List.getPayload(onAud: () -> ByteBuffer? = { null }): ByteBuffer { + val nalsSize = this.sumOf { it.remaining() } + val bufferSize = nalsSize + (this.size * 4) + + val payload = onAud()?.let { aud -> + //add AUD nal + ByteBuffer.allocate(aud.remaining() + bufferSize).apply { + put(aud) + } + } ?: ByteBuffer.allocate(bufferSize) + + this.forEach { + payload.putInt(0x00000001) //annex-b header + payload.put(it) + } + payload.flip() + return payload } \ No newline at end of file diff --git a/udp/src/main/java/com/pedro/udp/UdpSender.kt b/udp/src/main/java/com/pedro/udp/UdpSender.kt index 1e614706c..d333389c2 100644 --- a/udp/src/main/java/com/pedro/udp/UdpSender.kt +++ b/udp/src/main/java/com/pedro/udp/UdpSender.kt @@ -19,6 +19,7 @@ package com.pedro.udp import android.util.Log import com.pedro.common.AudioCodec import com.pedro.common.ConnectChecker +import com.pedro.common.VideoCodec import com.pedro.common.base.BaseSender import com.pedro.common.frame.MediaFrame import com.pedro.common.onMainThread @@ -26,10 +27,10 @@ import com.pedro.common.validMessage import com.pedro.srt.mpeg2ts.MpegTsPacket import com.pedro.srt.mpeg2ts.MpegTsPacketizer import com.pedro.srt.mpeg2ts.MpegType -import com.pedro.srt.mpeg2ts.Pid import com.pedro.srt.mpeg2ts.packets.AacPacket import com.pedro.srt.mpeg2ts.packets.BasePacket -import com.pedro.srt.mpeg2ts.packets.H26XPacket +import com.pedro.srt.mpeg2ts.packets.H264Packet +import com.pedro.srt.mpeg2ts.packets.H265Packet import com.pedro.srt.mpeg2ts.packets.OpusPacket import com.pedro.srt.mpeg2ts.psi.Psi import com.pedro.srt.mpeg2ts.psi.PsiManager @@ -59,7 +60,7 @@ class UdpSender( private val limitSize = Constants.MTU private val mpegTsPacketizer = MpegTsPacketizer(psiManager) private var audioPacket: BasePacket = AacPacket(limitSize, psiManager) - private val videoPacket = H26XPacket(limitSize, psiManager) + private var videoPacket: BasePacket = H264Packet(limitSize, psiManager) var socket: UdpSocket? = null private fun setTrackConfig(videoEnabled: Boolean, audioEnabled: Boolean) { @@ -71,14 +72,36 @@ class UdpSender( } override fun setVideoInfo(sps: ByteBuffer, pps: ByteBuffer?, vps: ByteBuffer?) { - videoPacket.setVideoCodec(commandManager.videoCodec) - videoPacket.sendVideoInfo(sps, pps, vps) + videoPacket = when (commandManager.videoCodec) { + VideoCodec.H264 -> { + if (pps == null) throw IllegalArgumentException("pps can't be null with h264") + (videoPacket as? H264Packet ?: H264Packet(limitSize, psiManager)).apply { + setLimitSize(limitSize) + sendVideoInfo(sps, pps) + } + } + VideoCodec.H265 -> { + if (vps == null || pps == null) throw IllegalArgumentException("pps or vps can't be null with h265") + (videoPacket as? H265Packet ?: H265Packet(limitSize, psiManager)).apply { + setLimitSize(limitSize) + sendVideoInfo(sps, pps, vps) + } + } + else -> { + throw IllegalArgumentException("Unsupported codec: ${commandManager.videoCodec.name}") + } + } } override fun setAudioInfo(sampleRate: Int, isStereo: Boolean) { audioPacket = when (commandManager.audioCodec) { - AudioCodec.AAC, AudioCodec.HE_AAC -> AacPacket(limitSize, psiManager).apply { sendAudioInfo(sampleRate, isStereo, commandManager.audioCodec) } - AudioCodec.OPUS -> OpusPacket(limitSize, psiManager) + AudioCodec.AAC, AudioCodec.HE_AAC -> (audioPacket as? AacPacket ?: AacPacket(limitSize, psiManager)).apply { + setLimitSize(limitSize) + sendAudioInfo(sampleRate, isStereo, commandManager.audioCodec) + } + AudioCodec.OPUS -> (audioPacket as? OpusPacket ?: OpusPacket(limitSize, psiManager)).apply { + setLimitSize(limitSize) + } AudioCodec.G711 -> { throw IllegalArgumentException("Unsupported codec: ${commandManager.audioCodec.name}") }