使用libav编码生成H264 MP4文件时帧率异常过高问题求助
问题原因与修复方案
核心原因
你遇到的帧率异常问题根源是时间基不匹配:
- 编码器输出的AVPacket的时间戳使用的是你给
AVCodecContext设置的1/60时间基,单位是1/60秒。 - MP4封装器会在调用
avformat_write_header时自动调整AVStream的时间基以适配MP4格式规范,你ffprobe输出的tbn=15360就是调整后的流时间基分母,单位是1/15360秒。 - 你没有对Packet的时间戳做转换就直接写入容器,原本每帧间隔对应1/60秒,被封装器误判为1/15360秒,最终帧率就变成了15360fps,和你的测试结果完全吻合。
另外你的编码循环逻辑不符合libav编码规范,以及未指定Packet的stream_index,也会提升异常概率。
修复步骤
1. 修正编码循环逻辑,增加时间基转换
每次从编码器拿到Packet后,先调用av_packet_rescale_ts做时间基转换,指定stream_index后再写入容器。
2. 修正YUV分量行宽
YUV420P格式的U、V分量宽高是Y分量的一半,所以linesize[1]和linesize[2]应该设为960,避免色彩异常。
修正后的核心代码片段
// ... 原有初始化代码不变 ... avio_open(&avfc->pb, filename, AVIO_FLAG_WRITE); avformat_write_header(avfc, NULL); Mat frame, nothing_u = Mat::zeros(540, 960, CV_8UC1); Mat nothing_v = Mat::zeros(540, 960, CV_8UC1); AVFrame *avf = av_frame_alloc(); AVPacket *avp = av_packet_alloc(); int ret; avf->format = AV_PIX_FMT_YUV420P; avf->width = 1920; avf->height = 1080; avf->linesize[0] = 1920; avf->linesize[1] = 960; avf->linesize[2] = 960; for(int x=0; x<1000; x++) { frame = Mat::zeros(1080, 1920, CV_8UC1); circle(frame, Point(1920/2, 1080/2), 250*(sin(2*M_PI*x/1000*3)+1.01), Scalar(255), 10); avf->data[0] = frame.data; avf->data[1] = nothing_u.data; avf->data[2] = nothing_v.data; avf->pts = x; // 标准编码流程:先发帧,再循环收包 ret = avcodec_send_frame(avcc, avf); if (ret < 0) break; while (ret >= 0) { ret = avcodec_receive_packet(avcc, avp); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; else if (ret < 0) goto clean; // 关键:转换时间基,指定流索引 av_packet_rescale_ts(avp, avcc->time_base, stream->time_base); avp->stream_index = stream->index; av_write_frame(avfc, avp); av_packet_unref(avp); } } // 刷新编码器缓存 ret = avcodec_send_frame(avcc, NULL); while (ret >= 0) { ret = avcodec_receive_packet(avcc, avp); if (ret == AVERROR_EOF) break; else if (ret < 0) break; av_packet_rescale_ts(avp, avcc->time_base, stream->time_base); avp->stream_index = stream->index; av_write_frame(avfc, avp); av_packet_unref(avp); } clean: // ... 原有释放资源代码不变 ...
内容的提问来源于stack exchange,提问作者unknownperson
相关产品推荐
相关产品推荐

