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