背景:
我正在构建一个记录器,用于在Ubuntu 16.04上使用FFmpeg 2.8.6在单独的线程中捕获视频和音频(使用Boost线程组)。我在这里遵循了demuxing_decoding示例:https://www.ffmpeg.org/doxygen/2.8/demuxing_decoding_8c-example.html
视频捕捉细节:
我正在阅读Logitech C920网络摄像头的H264,并将视频写入原始文件。我注意到视频的问题是,在特定帧重置之前,帧之间似乎存在伪像的累积。这是我的帧抓取和解码功能:
// Used for injecting decoding functions for different media types, allowing
// for a generic decode loop
typedef std::function<int(AVPacket*, int*, int)> PacketDecoder;
/**
* Decodes a video packet.
* If the decoding operation is successful, returns the number of bytes decoded,
* else returns the result of the decoding process from ffmpeg
*/
int decode_video_packet(AVPacket *packet,
int *got_frame,
int cached){
int ret = 0;
int decoded = packet->size;
*got_frame = 0;
//Decode video frame
ret = avcodec_decode_video2(video_decode_context,
video_frame, got_frame, packet);
if (ret < 0) {
//FFmpeg users should use av_err2str
char errbuf[128];
av_strerror(ret, errbuf, sizeof(errbuf));
std::cerr << "Error decoding video frame " << errbuf << std::endl;
decoded = ret;
} else {
if (*got_frame) {
video_frame->pts = av_frame_get_best_effort_timestamp(video_frame);
//Write to log file
AVRational *time_base = &video_decode_context->time_base;
log_frame(video_frame, time_base,
video_frame->coded_picture_number, video_log_stream);
#if( DEBUG )
std::cout << "Video frame " << ( cached ? "(cached)" : "" )
<< " coded:" << video_frame->coded_picture_number
<< " pts:" << pts << std::endl;
#endif
/*Copy decoded frame to destination buffer:
*This is required since rawvideo expects non aligned data*/
av_image_copy(video_dest_attr.video_destination_data,
video_dest_attr.video_destination_linesize,
(const uint8_t **)(video_frame->data),
video_frame->linesize,
video_decode_context->pix_fmt,
video_decode_context->width,
video_decode_context->height);
//Write to rawvideo file
fwrite(video_dest_attr.video_destination_data[0],
1,
video_dest_attr.video_destination_bufsize,
video_out_file);
//Unref the refcounted frame
av_frame_unref(video_frame);
}
}
return decoded;
}
/**
* Grabs frames in a loop and decodes them using the specified decoding function
*/
int process_frames(AVFormatContext *context,
PacketDecoder packet_decoder) {
int ret = 0;
int got_frame;
AVPacket packet;
//Initialize packet, set data to NULL, let the demuxer fill it
av_init_packet(&packet);
packet.data = NULL;
packet.size = 0;
// read frames from the file
for (;;) {
ret = av_read_frame(context, &packet);
if (ret < 0) {
if (ret == AVERROR(EAGAIN)) {
continue;
} else {
break;
}
}
//Convert timing fields to the decoder timebase
unsigned int stream_index = packet.stream_index;
av_packet_rescale_ts(&packet,
context->streams[stream_index]->time_base,
context->streams[stream_index]->codec->time_base);
AVPacket orig_packet = packet;
do {
ret = packet_decoder(&packet, &got_frame, 0);
if (ret < 0) {
break;
}
packet.data += ret;
packet.size -= ret;
} while (packet.size > 0);
av_free_packet(&orig_packet);
if(stop_recording == true) {
break;
}
}
//Flush cached frames
std::cout << "Flushing frames" << std::endl;
packet.data = NULL;
packet.size = 0;
do {
packet_decoder(&packet, &got_frame, 1);
} while (got_frame);
av_log(0, AV_LOG_INFO, "Done processing frames\n");
return ret;
}
问题:
我尝试/发现的事情:
我发现这个帖子在这里遇到了同样的问题:FFMPEG decoding artifacts between keyframes (由于隐私问题,我无法发布我的损坏帧的样本,但在该问题中链接的图像描述了我遇到的同样问题) 但是,该问题的答案由OP公布,但没有关于问题如何解决的具体细节。 OP只提到他没有“正确保存数据包”,但没有提到错误或如何修复它。我没有足够的声誉来发表评论以寻求澄清。
我最初是通过值将数据包传递给解码函数,但是切换到通过指针传递数据包释放错误的可能性。
我发现了另一个关于调试解码问题的问题,但找不到任何结论:How is video decoding corruption debugged?
我很欣赏任何见解。非常感谢!
[编辑]在回应罗纳德的回答时,我补充了一些不适合评论的信息:
我只是从线程处理视频帧调用decode_video_packet();处理音频帧的其他线程调用类似的decode_audio_packet()函数。所以只有一个线程调用该函数。我应该提一下,我已经将解码上下文中的thread_count设置为1,否则我会在刷新缓存的帧时在malloc.c中得到段错误。
如果process_frames和帧解码器函数在不同的线程上运行,我可以看到这是一个问题,但事实并非如此。是否有一个特定的原因,如果释放是在函数内完成,还是在它返回之后?我相信释放功能是传递原始数据包的副本,因为在解码器不解码整个音频数据包的情况下,音频数据包需要多次解码调用。
一般问题是腐败不会一直发生。如果它是确定性的,我可以更好地调试。否则,我甚至不能说解决方案是否有效。
答案 0 :(得分:2)
要检查的一些事项:
decode_video_packet()
的线程吗?如果你是:不要那样做! FFmpeg内置了对多线程解码的支持,你应该让FFmpeg在内部和透明地进行线程化。av_free_packet()
,但此时它可能还没有机会复制内容。在调用decode_video_packet()
。avcodec_decode_video2()
释放数据包
一般调试建议:
是的,定期清理是因为关键帧。