Как передать уже закодированный кадр в webrtc stream android kotlin

#java #android #kotlin #webrtc #h.264

Вопрос:

Я пишу и создаю приложение для Android в Котлине для одновременной записи и прямой трансляции видео с USB-камеры, как в формате кодека H264. Я использую Google с открытым исходным кодом для потоковой передачи WebRTC и пакет android.media для записи.

Проблема в том, что мне приходится кодировать каждый захваченный кадр дважды, так как я не смог найти способ передать кодированный кадр H264 из процесса записи в WebRTC. Это в значительной степени снижает производительность моего приложения до частоты кадров всего 7-8 кадров в секунду.

Вот обратный вызов для кодирования вновь поступившего кадра в потоке записи.

     private val mediaEncoderCallback = object : MediaCodec.Callback() {
        override fun onInputBufferAvailable(p0: MediaCodec, p1: Int) {}

        override fun onError(p0: MediaCodec, p1: MediaCodec.CodecException) {}

        private var encoderLock = Object()
        override fun onOutputBufferAvailable(
            p0: MediaCodec,
            index: Int,
            info: MediaCodec.BufferInfo
        ) = synchronized(encoderLock) {
            if (isStopped.get()) return
            val encodedData = try {
                requireNotNull(mediaEncoder?.getOutputBuffer(index))
            } catch (exception: Exception) {
                Timber.e(exception)
                return
            }


            if (info.flags and MediaCodec.BUFFER_FLAG_CODEC_CONFIG != 0) {
                info.size = 0
            }
            if (info.size != 0) {
                if (muxerStarted.get()) {
                    encodedData.position(info.offset)
                    encodedData.limit(info.offset   info.size)
                    val trackId = trackIndex.get()
                    muxer?.writeSampleData(trackId, encodedData, info)
                }
            }
            mediaEncoder?.releaseOutputBuffer(index, false)
            Unit
        }

        override fun onOutputFormatChanged(p0: MediaCodec, p1: MediaFormat) {
            if (isStopped.get()) return
            timer.schedule(object : TimerTask() {
                override fun run() {
                    forceStopRecording()
                }

            }, captureTimeInMillis)
            val currentMuxer = muxer ?: return
            if (trackIndex.get() == -1) {
                currentMuxer.addTrack(mediaEncoder?.outputFormat!!).also {
                    trackIndex.set(it)
                }
            }
            if (!muxerStarted.get() amp;amp; trackIndex.get() >= 0) {
                muxerStarted.set(true)
                currentMuxer.start()
            }
        }
    }

    override fun startRecording(captureFile: File, captureTimeInMillis: Long) {
        this.captureTimeInMillis = captureTimeInMillis

        if (!CodecProvider.checkIfCodecSupported(VIDEO_MIME_TYPE)) return
        muxer = MediaMuxer(captureFile.path, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
        val codec = MediaCodec.createEncoderByType(VIDEO_MIME_TYPE).also {
            mediaEncoder = it
        }
        codec.configure(getVideoFormat(), null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
        inputSurface = codec.createInputSurface()

        codec.setCallback(mediaEncoderCallback)
        codec.start()
        codecStarted.set(true)
        inputSurface?.let { onSurfaceUpdated(it) }
    }
 

Вот обратный вызов для потоковой передачи нового необработанного кадра в потоке webrtc.

 override fun handleNewBuffer(cameraKey: String, byteBuffer: ByteBuffer) {
        if (!isStreamingStarted) return
        val imageArray = ByteArray(byteBuffer.remaining())
        byteBuffer.get(imageArray)
        byteBuffer.rewind()
        val buffer = NV21Buffer(
            imageArray,
            CameraConfig.WIDTH_SIZE,
            CameraConfig.HEIGHT_SIZE,
            null
        )
        val timestamp = TimeUnit.MILLISECONDS.toNanos(SystemClock.elapsedRealtime())
        val videoFrame = VideoFrame(buffer, 0, timestamp)
        localVideoSource.capturerObserver.onFrameCaptured(videoFrame)
        videoFrame.release()
    }
 

Существует ли какой-либо обходной путь для однократного кодирования как для потоковой передачи, так и для записи (или, более конкретно, для передачи *кодированных данных из потока записи в поток webrtc вместо необработанного буфера ByteBuffer).

Спасибо!