FFmpeg C Demo出现时间戳更新失败警告的原因及解决方法
关于FFmpeg解码时出现"Could not update timestamps for skipped samples"警告的问题
问题现象
- 测试WebM格式视频时,Opus解码器抛出如下警告:
[opus @ 0x5ec0fc1b4580] Could not update timestamps for skipped samples.
[opus @ 0x5ec0fc1b4580] Could not update timestamps for discarded samples.
- 测试MP4格式视频时,AAC解码器也会出现类似警告:
[aac @ 0x61326fb83700] Could not update timestamps for skipped samples.
疑问
- 该警告是否确实由FFmpeg跳过数据包导致?若判断有误,请指出真实原因。
- 为何会出现样本跳过的情况?如何解决该问题?
测试Demo代码
#include <libavcodec/avcodec.h> #include <libavformat/avformat.h> int main() { int ret = 0; const AVCodec* codec; AVFormatContext* fmt_ctx = avformat_alloc_context(); const char* file2 = "/home/aabiji/Videos/sync-test.webm"; if ((ret = avformat_open_input(&fmt_ctx, file2, NULL, NULL)) < 0) { av_log(NULL, AV_LOG_ERROR, "Couldn't open input file\n"); return -1; } ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Couldn't find a media stream\n"); return -1; } int stream_index = ret; AVStream* media = fmt_ctx->streams[stream_index]; AVCodecContext* codec_context = avcodec_alloc_context3(codec); if (avcodec_parameters_to_context(codec_context, media->codecpar) < 0) { return -1; } if ((ret = avcodec_open2(codec_context, codec, NULL)) < 0) { av_log(NULL, AV_LOG_ERROR, "Couldn't open media decoder\n"); return -1; } AVPacket* packet = av_packet_alloc(); AVFrame* frame = av_frame_alloc(); while ((ret = av_read_frame(fmt_ctx, packet)) >= 0) { if (packet->stream_index != stream_index) { continue; } ret = avcodec_send_packet(codec_context, packet); if (ret < 0) { break; // Error } while (ret >= 0) { ret = avcodec_receive_frame(codec_context, frame); if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) { break; } else if (ret < 0) { fprintf(stderr, "Error during decoding\n"); break; } av_frame_unref(frame); } av_packet_unref(packet); } avcodec_flush_buffers(codec_context); av_packet_unref(packet); av_frame_free(&frame); av_packet_free(&packet); avcodec_free_context(&codec_context); avformat_close_input(&fmt_ctx); return 0; }
原因分析
该警告并非由跳过数据包导致,本质是解码器处理音频帧时,遇到无效/冗余样本需要丢弃,但无法正确更新时间戳映射引发的。常见触发场景:
- 音频帧存在损坏、不完整的样本数据,解码器主动丢弃无效样本
- 音频流时间戳存在不连续、偏移,解码器同步时需丢弃部分样本对齐时间线
- Demo代码未正确处理解码器的时间戳同步逻辑,也未处理
avcodec_send_packet返回的非致命错误(如部分数据无效但可继续解码的情况)
解决方案
1. 正确处理avcodec_send_packet的非致命错误
原代码中avcodec_send_packet返回<0就直接终止解码,这会中断后续正常数据包的处理。对于AVERROR_INVALIDDATA这类非致命错误,仅需打印警告并继续处理下一个数据包:
ret = avcodec_send_packet(codec_context, packet); if (ret < 0) { if (ret == AVERROR_INVALIDDATA) { av_log(codec_context, AV_LOG_WARNING, "Invalid data in packet: %s\n", av_err2str(ret)); av_packet_unref(packet); continue; } else { av_log(codec_context, AV_LOG_ERROR, "Failed to send packet: %s\n", av_err2str(ret)); break; } }
2. 配置解码器时间基准
在打开解码器前,将解码器的数据包时间基准设置为对应音频流的时间基准,帮助解码器正确处理时间戳:
// 在avcodec_open2调用前添加 codec_context->pkt_timebase = media->time_base;
3. 正确处理解码器剩余帧
原代码中avcodec_flush_buffers的使用时机错误,应在av_read_frame循环结束后,发送NULL数据包触发解码器输出剩余帧:
// 替换原代码中的avcodec_flush_buffers调用 // 发送NULL表示输入结束,获取解码器缓存的剩余帧 ret = avcodec_send_packet(codec_context, NULL); while (ret >= 0) { ret = avcodec_receive_frame(codec_context, frame); if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) { break; } else if (ret < 0) { fprintf(stderr, "Error flushing decoder: %s\n", av_err2str(ret)); break; } av_frame_unref(frame); }
4. 可选:屏蔽非致命警告
若确认这些警告不影响实际解码结果,可通过设置日志级别屏蔽:
// 在main函数开头添加 av_log_set_level(AV_LOG_ERROR);
内容的提问来源于stack exchange,提问作者aabiji
相关产品推荐
相关产品推荐

