Mar*_*ark 5 android encode decode mediacodec mediaextractor
我正在编写一个可以通过摄像头输入编码视频并通过解码编辑编码步骤处理视频的APP.对于相机,我使用Camera类而不是Intent来配置相机的细节设置.然后我将摄像机帧提供给编码器(API 16中的MediaCodec)和复用器(我使用ffmpeg muxer,因为我想在4.1设备上工作).
我按系统纳米时间测量相机帧的时间码,并选择帧的子集以适合所需的FPS(当前为15).时间值中存在一些小的"噪声",例如(以ms为单位):0,60718,135246,201049,...而不是0,66000,133000,200000,.......
在尝试正确配置多路复用器之后(正如这个问题),我可以制作一个视频(带有AVC编解码器),可以由设备上的视频播放器播放.播放速度是正确的,所以我认为视频应该有正确的帧时间信息.
但是,当我尝试解码视频以执行视频编辑过程时,我遇到了问题.我使用标准视频提取/解码步骤作为这些示例,如下所示:
int decode_input_index = decoder.dequeueInputBuffer(TIMEOUT_USEC);
if (decode_input_index >= 0)
{
ByteBuffer decoder_input_buffer = decode_input_buffers[decode_input_index];
int sample_size = extractor.readSampleData(decoder_input_buffer, 0);
if (sample_size < 0)
{
decoder.queueInputBuffer(decode_input_index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
is_decode_input_done = true;
}
else
{
long sample_time = extractor.getSampleTime();
decoder.queueInputBuffer(decode_input_index, 0, sample_size, sample_time, 0);
extractor.advance();
}
}
else
{
Log.v(TAG, "Decoder dequeueInputBuffer timed out! Try again later");
}
Run Code Online (Sandbox Code Playgroud)
来自getSampleTime()的采样时间在我对视频进行编码时具有正确的值.(例如,它们正好是0,60718,135246,201049,......在我们身上).它也是decoder.queueInputBuffer()输入中的显示时间.当解码器继续解码这个帧时,我得到帧时间:
int decode_output_index = decoder.dequeueOutputBuffer(decode_buffer_info, TIMEOUT_USEC);
switch (decode_output_index)
{
....
(some negative-value flags in MediaCodec)
....
default:
{
ByteBuffer decode_output_buffer = decode_output_buffers[decode_output_index];
long ptime_us = decode_buffer_info.presentationTimeUs;
boolean is_decode_EOS = ((decode_buffer_info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0);
....
}
}
Run Code Online (Sandbox Code Playgroud)
我希望设置与解码器输入中的时间序列相同的时间序列,但是我从解码器输出的BufferInfo中获得了很多0.解码的帧内容似乎是正确的,但是大多数呈现时间值都是0.只有最后几帧具有正确的呈现时间.
我在使用Android 4.3的设备上测试整个相同的过程(即使使用相同的ffmpeg muxer而不是API 18中的MediaMuxer),一切看起来都很好.在4.1/4.2设备上,如果我通过设备上的内置摄像头APP捕获视频然后解码视频,那么演示时间也是正确的,尽管时间值也会因摄像头延迟而产生噪音.
视频或解码过程出现什么问题,视频可以正常播放和解码,但是采样时间和演示时间不正确?我可能必须使用变通方法来通过采样时间来测量演示时间(通过使用队列很容易),但我想弄清楚我的工作中是否有任何缺失的部分.
在 Android 4.3 之前,无法保证MediaCodec正确处理演示时间戳。这是因为直到那时才添加确认 PTS 行为的 CTS 测试。
我确实记得某些供应商的 AVC 编解码器中的时间戳处理存在问题。我暂时不记得详细信息,但如果您在各种 4.1/4.2 设备上运行EncodeDecodeTest的缓冲区到缓冲区和缓冲区到表面测试,您会发现一些失败。(当然,您需要去掉面对面的测试。)
您的时间戳处理代码看起来不错。时间戳不是 H.264 流的一部分,因此它实际上只是通过编解码器作为元数据转发,并且您似乎正在拾取它并将其转发到所有正确的位置。最重要的是,如果您传递有效的 PTS 值并获得良好的视频,但得到垃圾 PTS 值,则说明编解码器中的某些内容对它们处理不当。
您需要通过单独传递值来解决这个问题,或者 - 如果输入帧速率始终是规则的 - 简单地计算它。理论上,编码器可以对帧进行重新排序,因此您将时间戳传递到编码器的顺序可能与它们出来的顺序不同......但是因为您知道制作电影时时间戳是升序的,所以您应该如果这在实践中是一个问题,就能够对它们进行排序。
另一方面,如果您在System.nanoTime()帧到达应用程序时进行抓取,系统中的延迟将导致您在时间戳值中看到的“摆动”。在 Android 4.3 中,使用 Surface 输入可以做得更好一些,因为它SurfaceTexture保存的时间戳设置得更接近捕获帧的时间。(我知道这对你当前的努力没有用,但想给未来带来一些希望。)