如何通过FFmpeg编程删除MPG视频缓冲帧并保持音频同步?
我来帮你搞定这个FFmpeg视频过滤的问题——你需要精准过滤掉带缓冲图标的视频帧,同时保证音视频同步,还得保留原视频的帧率、分辨率等核心属性对吧?下面是具体的实现思路和代码调整方案:
核心思路
要实现需求,关键要做好三件事:
- 复用原视频/音频的编码参数,确保输出文件和原文件属性一致
- 基于帧检测结果过滤视频帧,同时同步跳过对应时间段的无音频缓冲段
- 正确转换并维护PTS/DTS时间戳,避免音视频不同步
具体代码实现
首先需要补充输出上下文的初始化、音视频流参数复制,然后修改帧处理逻辑:
1. 初始化输出上下文与流参数
在帧遍历循环前,先创建输出文件的格式上下文,并复制原流的编码参数:
// 初始化输出格式上下文 AVFormatContext *outFormatCtx = nullptr; // 指定输出格式为MPG,也可以自动推导 avformat_alloc_output_context2(&outFormatCtx, nullptr, "mpeg", "output.mpg"); if (!outFormatCtx) { // 错误处理:输出上下文创建失败 return -1; } // 复制视频流参数 AVStream *outVideoStream = avformat_new_stream(outFormatCtx, nullptr); if (!outVideoStream) { // 错误处理 avformat_free_context(outFormatCtx); return -1; } avcodec_parameters_copy(outVideoStream->codecpar, pFormatCtx->streams[videoStream]->codecpar); outVideoStream->codecpar->codec_id = outFormatCtx->oformat->video_codec; // 查找并复制音频流参数 int audioStream = -1; for (int i = 0; i < pFormatCtx->nb_streams; i++) { if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { audioStream = i; break; } } AVStream *outAudioStream = nullptr; if (audioStream != -1) { outAudioStream = avformat_new_stream(outFormatCtx, nullptr); avcodec_parameters_copy(outAudioStream->codecpar, pFormatCtx->streams[audioStream]->codecpar); outAudioStream->codecpar->codec_id = outFormatCtx->oformat->audio_codec; } // 打开输出文件IO if (!(outFormatCtx->oformat->flags & AVFMT_NOFILE)) { if (avio_open(&outFormatCtx->pb, "output.mpg", AVIO_FLAG_WRITE) < 0) { // 错误处理:文件打开失败 avformat_free_context(outFormatCtx); return -1; } } // 写入文件头 if (avformat_write_header(outFormatCtx, nullptr) < 0) { // 错误处理 avio_close(outFormatCtx->pb); avformat_free_context(outFormatCtx); return -1; }
2. 修改帧遍历与过滤逻辑
在原循环中加入音频同步处理,根据视频帧的检测结果决定是否保留对应音频:
// 标记是否需要保留当前音频段(对应有效视频帧) bool keepAudioSegment = false; // 记录上一个保留的视频帧时间戳,用于同步校验 int64_t lastValidVideoPts = AV_NOPTS_VALUE; AVPacket origPacket; while (av_read_frame(pFormatCtx, &origPacket) >= 0) { AVPacket packet = origPacket; // 复制原包,避免解码修改原数据 if (packet.stream_index == videoStream) { int frameFinished = 0; avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet); if (frameFinished) { // 转换为RGB帧用于图标检测 sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize); if (ThisIsAFrameIWant(pFrameRGB)) { // 转换时间戳到输出流的时间基 pFrame->pts = av_rescale_q(pFrame->pts, pFormatCtx->streams[videoStream]->time_base, outVideoStream->time_base); pFrame->dts = av_rescale_q(pFrame->dts, pFormatCtx->streams[videoStream]->time_base, outVideoStream->time_base); pFrame->duration = av_rescale_q(pFrame->duration, pFormatCtx->streams[videoStream]->time_base, outVideoStream->time_base); // 编码并写入视频帧 AVPacket outPacket = {0}; av_init_packet(&outPacket); int ret = avcodec_send_frame(outVideoStream->codec, pFrame); if (ret >= 0) { while (ret >= 0) { ret = avcodec_receive_packet(outVideoStream->codec, &outPacket); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; if (ret < 0) { // 编码错误处理 break; } outPacket.stream_index = outVideoStream->index; av_interleaved_write_frame(outFormatCtx, &outPacket); av_packet_unref(&outPacket); } } // 更新音频同步标记 lastValidVideoPts = pFrame->pts; keepAudioSegment = true; } else { // 缓冲帧,跳过对应音频段 keepAudioSegment = false; } } } else if (packet.stream_index == audioStream && keepAudioSegment) { // 转换音频包时间戳到输出流时间基 packet.pts = av_rescale_q(packet.pts, pFormatCtx->streams[audioStream]->time_base, outAudioStream->time_base); packet.dts = av_rescale_q(packet.dts, pFormatCtx->streams[audioStream]->time_base, outAudioStream->time_base); packet.duration = av_rescale_q(packet.duration, pFormatCtx->streams[audioStream]->time_base, outAudioStream->time_base); packet.stream_index = outAudioStream->index; // 直接复用原音频包(MPG音频为MP2,无需重新编码) if (av_interleaved_write_frame(outFormatCtx, &packet) < 0) { // 音频写入错误处理 } } av_packet_unref(&origPacket); } // 写入文件尾并清理资源 av_write_trailer(outFormatCtx); if (!(outFormatCtx->oformat->flags & AVFMT_NOFILE)) { avio_close(outFormatCtx->pb); } avformat_free_context(outFormatCtx);
关键细节说明
- 时间基转换:必须用
av_rescale_q将原流的PTS/DTS转换为输出流的时间基,否则会出现音视频不同步或时间戳异常 - 音频复用优化:MPG的音频通常是MP2格式,和输出格式兼容,直接复用原音频包无需重新编码,既高效又能保留音质
- 关键帧处理:MPEG是帧间编码格式,过滤帧后如果出现参考帧缺失,可以通过设置编码器的
gop_size参数,或在过滤后的第一帧强制生成关键帧(avcodec_send_frame前设置pFrame->key_frame = 1) - 资源清理:所有FFmpeg结构体(AVPacket、AVFrame、AVFormatContext等)都需要调用对应的
unref或free函数释放,避免内存泄漏
内容的提问来源于stack exchange,提问作者user7838334
相关产品推荐
相关产品推荐

