#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).
Спасибо!