ffmpeg (转码时间戳pts dts问题)最简单的转码封装mp4文件

本例简单实现了解码后的video重新编码264之后在mux成MP4文件的过程,主要是用来记录muxing的方法。
下面详细说一下细节:
大家都知道一般解码出来的数据都是播放顺序,解码器是将编码顺序的数据重新按照解码后的播放顺序输出的。而编码器是把数据根据解码需要的顺序重新排序保存的。
当然,以上情况只在有帧的情况下才有用,否则只有IP帧的话解码和编码的顺序是一样的
比如:解码后的数据是IBBP,那要将这个数据编码的话,编码后的数据保存的格式就是IPBB
这只是内部的处理,对于用ffmpeg的库的我们不用太过关心 ,但是 , 要注意,我们将数据塞给编码器的时候,要给顺序的播放加上顺序的时间标记,其实很简单只要保证你送给编码器的每一frame的pts都是顺序的就可以了, 否则编码器会报 “non-strictly-monotonic pts at frame” , 究其原因,是因为编码器需要送进来的frame时间上是递增的,为什么需要这个就得去本研究编码器了

点击(此处)折叠或打开

if( pic.i_pts <= largest_pts )
{
if( cli_log_level >= X264_LOG_DEBUG || pts_warning_cnt < MAX_PTS_WARNING )
x264_cli_log( “x264”, X264_LOG_WARNING, “non-strictly-monotonic pts at frame %d (%“PRId64” <= %“PRId64”)\n”,
i_frame, pic.i_pts, largest_pts );
else if( pts_warning_cnt == MAX_PTS_WARNING )
x264_cli_log( “x264”, X264_LOG_WARNING, “too many nonmonotonic pts warnings, suppressing further ones\n” );
pts_warning_cnt++;
pic.i_pts = largest_pts + ticks_per_frame;
}

在将数据送到编码器后,进行编码输出得到的pkt有自己的pts和dts等数据,但是这个数据记得吗?是用我们自己送进去的pts来表示的,所以在和原来 的audio mux的时候,会出现严重的音视频不同步,现在想想这个问题,就很容易理解了,两边的pts差距很大,当然解码后做同步的时候会差很多。
其实ffmpeg在解码的时候将解码出来的顺序时间戳给了frame的pkt_pts这个成员,所以我们可以直接用这个值赋值给frame的pts,在送进编码器,这样编码出来的pkt中的时间戳就和原来的audio对上了。

点击(此处)折叠或打开

ret = avcodec_decode_video2(video_dec_ctx, pFrame, &got_picture, pkt);
if (ret < 0)
{
delete pkt;
return 0;
}
pFrame->pts = pFrame->pkt_pts; //赋值解码后的pts
最后在进行mux成mp4文件就ok了
在mux的过程中,有个接口av_rescale_q_rnd,这个是用来换算pts的,因为在设定mp4输出格式的时候time_base这个值是和 原来的文件不一样的,所以要用这个来重新算分装数据的在新的mp4中的pts和dts等数据,具体原因后续会继续往里研究

直接上代码:

点击(此处)折叠或打开

const char* SRC_FILE = “1.mkv”;
const char* OUT_FILE = “outfile.h264”;
const char* OUT_FMT_FILE = “outfmtfile.mp4”;
int main()
{
av_register_all();

AVFormatContext* pFormat = NULL;
if (avformat_open_input(&pFormat, SRC_FILE, NULL, NULL) <</span> 0)
{
    return 0;
}
AVCodecContext* video_dec_ctx = NULL;
AVCodec* video_dec = NULL;
if (avformat_find_stream_info(pFormat, NULL) <</span> 0)
{
    return 0;
}
av_dump_format(pFormat, 0, SRC_FILE, 0);
video_dec_ctx = pFormat->streams[0]->codec;
video_dec = avcodec_find_decoder(video_dec_ctx->codec_id);
if (avcodec_open2(video_dec_ctx, video_dec, NULL) <</span> 0)
{
    return 0;
}

AVFormatContext* pOFormat = NULL;
AVOutputFormat* ofmt = NULL;
if (avformat_alloc_output_context2(&pOFormat, NULL, NULL, OUT_FILE) <</span> 0)
{
    return 0;
}
ofmt = pOFormat->oformat;
if (avio_open(&(pOFormat->pb), OUT_FILE, AVIO_FLAG_READ_WRITE) <</span> 0)
{
    return 0;
}
AVCodecContext *video_enc_ctx = NULL;
AVCodec *video_enc = NULL;
video_enc = avcodec_find_encoder(AV_CODEC_ID_H264);
AVStream *video_st = avformat_new_stream(pOFormat, video_enc);
if (!video_st)
    return 0;
video_enc_ctx = video_st->codec;
video_enc_ctx->width = video_dec_ctx->width;
video_enc_ctx->height = video_dec_ctx->height;
video_enc_ctx->pix_fmt = PIX_FMT_YUV420P;
video_enc_ctx->time_base.num = 1;
video_enc_ctx->time_base.den = 25;
video_enc_ctx->bit_rate = video_dec_ctx->bit_rate;
video_enc_ctx->gop_size = 250;
video_enc_ctx->max_b_frames = 10;
//H264
//pCodecCtx->me_range = 16;
//pCodecCtx->max_qdiff = 4;
video_enc_ctx->qmin = 10;
video_enc_ctx->qmax = 51;
if (avcodec_open2(video_enc_ctx, video_enc, NULL) <</span> 0)
{
    printf("编码器打开失败!\n");
    return 0;
}
printf("Output264video Information====================\n");
av_dump_format(pOFormat, 0, OUT_FILE, 1);
printf("Output264video Information====================\n");

//mp4 file
AVFormatContext* pMp4Format = NULL;
AVOutputFormat* pMp4OFormat = NULL;
if (avformat_alloc_output_context2(&pMp4Format, NULL, NULL, OUT_FMT_FILE) <</span> 0)
{
    return 0;
}
pMp4OFormat = pMp4Format->oformat;
if (avio_open(&(pMp4Format->pb), OUT_FMT_FILE, AVIO_FLAG_READ_WRITE) <</span> 0)
{
    return 0;
}

for (int i = 0; i <</span> pFormat->nb_streams; i++) {
    AVStream *in_stream = pFormat->streams[i];
    AVStream *out_stream = avformat_new_stream(pMp4Format, in_stream->codec->codec);
    if (!out_stream) {
        return 0;
    }
    int ret = 0;
    ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
    if (ret <</span> 0) {
        fprintf(stderr, "Failed to copy context from input to output stream codec context\n");
        return 0;
    }
    out_stream->codec->codec_tag = 0;
    if (pMp4Format->oformat->flags & AVFMT_GLOBALHEADER)
        out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
}


av_dump_format(pMp4Format, 0, OUT_FMT_FILE, 1);

if (avformat_write_header(pMp4Format, NULL) <</span> 0)
{
    return 0;
}






av_opt_set(video_enc_ctx->priv_data, "preset", "superfast", 0);
av_opt_set(video_enc_ctx->priv_data, "tune", "zerolatency", 0);
avformat_write_header(pOFormat, NULL);
AVPacket *pkt = new AVPacket();
av_init_packet(pkt);
AVFrame *pFrame = avcodec_alloc_frame();
int ts = 0;
while (1)
{
    if (av_read_frame(pFormat, pkt) <</span> 0)
    {
        avio_close(pOFormat->pb);
        av_write_trailer(pMp4Format);
        avio_close(pMp4Format->pb);
        delete pkt;
        return 0;
    }
    if (pkt->stream_index == 0)
    {
        
        int got_picture = 0, ret = 0;
        ret = avcodec_decode_video2(video_dec_ctx, pFrame, &got_picture, pkt);
        if (ret <</span> 0)
        {
            delete pkt;
            return 0;
        }
        pFrame->pts = pFrame->pkt_pts;//ts++;
        if (got_picture)
        {
            AVPacket *tmppkt = new AVPacket;
            av_init_packet(tmppkt);
            int size = video_enc_ctx->width*video_enc_ctx->height * 3 / 2;
            char* buf = new char[size];
            memset(buf, 0, size);
            tmppkt->data = (uint8_t*)buf;
            tmppkt->size = size;
            ret = avcodec_encode_video2(video_enc_ctx, tmppkt, pFrame, &got_picture);
            if (ret <</span> 0)
            {
                avio_close(pOFormat->pb);
                delete buf;
                return 0;
            }
            if (got_picture)
            {
                //ret = av_interleaved_write_frame(pOFormat, tmppkt);
                AVStream *in_stream = pFormat->streams[pkt->stream_index];
                AVStream *out_stream = pMp4Format->streams[pkt->stream_index];

                tmppkt->pts = av_rescale_q_rnd(tmppkt->pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF);
                tmppkt->dts = av_rescale_q_rnd(tmppkt->dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF);
                tmppkt->duration = av_rescale_q(tmppkt->duration, in_stream->time_base, out_stream->time_base);
                tmppkt->pos = -1;
                ret = av_interleaved_write_frame(pMp4Format, tmppkt);
                if (ret <</span> 0)
                    return 0;
                delete tmppkt;
                delete buf;
            }
        }
        //avcodec_free_frame(&pFrame);
    }
    else if (pkt->stream_index == 1)
    {
        AVStream *in_stream = pFormat->streams[pkt->stream_index];
        AVStream *out_stream = pMp4Format->streams[pkt->stream_index];

        pkt->pts = av_rescale_q_rnd(pkt->pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF);
        pkt->dts = av_rescale_q_rnd(pkt->dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF);
        pkt->duration = av_rescale_q(pkt->duration, in_stream->time_base, out_stream->time_base);
        pkt->pos = -1;
        if (av_interleaved_write_frame(pMp4Format, pkt) <</span> 0)
            return 0;
    }
}
avcodec_free_frame(&pFrame);
return 0;

}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值