I am trying to convert some bitmaps to video and encode them with this library https://github.com/israel-fl/bitmap2video
In the FrameBuilder.kt, it encodes a given into AVC/H264.
However, dequeueOutputBuffer, at the end of the stream, will return -1/INFO_TRY_AGAIN_LATER, on my Google Pixel Pixel 6 Pro, but it works fine on Samsung S21/S22. Why is that?
Output for Pixel 6 Pro
12:35:03.435 D drainCodec(false), encoderStatus=-1
12:35:03.457 D drainCodec(false), encoderStatus=-1
12:35:03.480 D drainCodec(false), encoderStatus=-1
12:35:03.493 D drainCodec(false), encoderStatus=-1
12:35:03.506 D drainCodec(false), encoderStatus=-1
12:35:03.516 D releasing encoder objects
12:35:03.516 D drainCodec(true), encoderStatus=-1
12:35:03.516 D sending EOS to encoder
12:35:03.527 D no output available, spinning to await EOS
12:35:03.537 D no output available, spinning to await EOS
12:35:03.549 D no output available, spinning to await EOS
12:35:03.560 D no output available, spinning to await EOS
12:35:03.571 D no output available, spinning to await EOS
12:35:03.583 D no output available, spinning to await EOS
~ continues indefinitely
Samsung S21 logs:
12:49:05.493 I qqq uri=content://media/external/video/media/1000026049
12:49:14.362 D drainCodec(false)
12:49:14.396 D drainCodec(false)
12:49:14.442 D drainCodec(false)
12:49:14.492 D drainCodec(false)
12:49:14.494 D encoder output format changed: {max-bitrate=1500000, crop-right=2159, level=32768, mime=video/avc, profile=8, bitrate=1500000, priority=0, intra-refresh-period=0, color-standard=1, csd-1=java.nio.HeapByteBuffer[pos=0 lim=8 cap=8], color-transfer=3, crop-bottom=3839, prepend-sps-pps-to-idr-frames=0, video-qp-average=0, crop-left=0, width=2160, bitrate-mode=1, color-range=2, crop-top=0, frame-rate=30, height=3840, csd-0=java.nio.HeapByteBuffer[pos=0 lim=22 cap=22]}
12:49:14.498 D {max-bitrate=1500000, crop-right=2159, level=32768, mime=video/avc, profile=8, bitrate=1500000, priority=0, intra-refresh-period=0, color-standard=1, csd-1=java.nio.HeapByteBuffer[pos=0 lim=8 cap=8], color-transfer=3, crop-bottom=3839, prepend-sps-pps-to-idr-frames=0, video-qp-average=0, crop-left=0, width=2160, bitrate-mode=1, color-range=2, crop-top=0, frame-rate=30, height=3840, csd-0=java.nio.HeapByteBuffer[pos=0 lim=22 cap=22]}
12:49:14.507 D ignoring BUFFER_FLAG_CODEC_CONFIG
12:49:14.509 D sent 6605 bytes to muxer
12:49:14.510 D sent 1255 bytes to muxer
12:49:14.517 D sent 2901 bytes to muxer
12:49:14.548 D drainCodec(false)
12:49:14.578 D drainCodec(false)
12:49:14.579 D sent 7208 bytes to muxer
12:49:14.610 D drainCodec(false)
12:49:14.611 D sent 10116 bytes to muxer
12:49:14.646 D drainCodec(false)
12:49:14.648 D sent 18212 bytes to muxer
12:49:14.678 D drainCodec(false)
12:49:14.679 D sent 14077 bytes to muxer
12:49:14.711 D drainCodec(false)
12:49:14.712 D sent 13230 bytes to muxer
12:49:14.745 D drainCodec(false)
12:49:14.746 D sent 13348 bytes to muxer
12:49:14.778 D drainCodec(false)
12:49:14.778 D sent 14504 bytes to muxer
12:49:14.796 D drainCodec(false)
12:49:14.804 D sent 14219 bytes to muxer
12:49:14.814 D releasing encoder objects
12:49:14.814 D drainCodec(true)
12:49:14.814 D sending EOS to encoder
12:49:14.825 D no output available, spinning to await EOS
12:49:14.828 D sent 12867 bytes to muxer
12:49:14.838 D no output available, spinning to await EOS
12:49:14.843 D end of stream reached
drainCoded function
private fun drainCodec(endOfStream: Boolean) {
if (VERBOSE) Log.d(TAG, "drainCodec($endOfStream)")
if (endOfStream) {
if (VERBOSE) Log.d(TAG, "sending EOS to encoder")
mediaCodec.signalEndOfInputStream()
}
while (true) {
val encoderStatus: Int = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC.toLong())
if (VERBOSE) Log.d(TAG, "encoderStatus=$encoderStatus")
if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
// no output available yet, stuck here on Google Pixel
if (!endOfStream) {
break // out of while
} else {
if (VERBOSE) Log.d(TAG, "no output available, spinning to await EOS")
}
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// should happen before receiving buffers, and should only happen once
if (frameMuxer.isStarted()) throw RuntimeException("format changed twice")
val newFormat: MediaFormat = mediaCodec.outputFormat
Log.d(TAG, "encoder output format changed: $newFormat")
// now that we have the Magic Goodies, start the muxer
frameMuxer.start(newFormat, audioExtractor)
} else {
val encodedData = mediaCodec.getOutputBuffer(encoderStatus) ?: throw RuntimeException("encoderOutputBuffer $encoderStatus was null")
if (bufferInfo.flags and MediaCodec.BUFFER_FLAG_CODEC_CONFIG != 0) {
// The codec config data was pulled out and fed to the muxer when we got
// the INFO_OUTPUT_FORMAT_CHANGED status. Ignore it.
if (VERBOSE) Log.d(TAG, "ignoring BUFFER_FLAG_CODEC_CONFIG")
bufferInfo.size = 0
} else {
if (VERBOSE) Log.d(TAG, "Flags not configured")
}
if (bufferInfo.size != 0) {
if (!frameMuxer.isStarted()) throw RuntimeException("muxer hasn't started")
frameMuxer.muxVideoFrame(encodedData, bufferInfo)
if (VERBOSE) Log.d(TAG, "sent " + bufferInfo.size + " bytes to muxer")
} else {
if (VERBOSE) Log.d(TAG, "No bytes sent")
}
mediaCodec.releaseOutputBuffer(encoderStatus, false)
if (bufferInfo.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM != 0) {
if (!endOfStream) {
Log.w(TAG, "reached end of stream unexpectedly")
} else {
if (VERBOSE) Log.d(TAG, "end of stream reached")
}
break // out of while
}
}
}
}