首页 > 解决方案 > 使用 ffmpeg 将实际时间戳添加到 mp4

问题描述

我正在使用 ffmpeg 将 h264 流写入 mp4 文件。一切正常,但现在我需要在这个文件中嵌入每帧的实际时间戳(以毫秒为单位)。

可能吗?

这是我的代码:

void mp4_file_create(mp4_par * par, t_image * img_in)
{
    AVCodec * codec = NULL;
    AVCodecContext *    cc_in;
    AVFormatContext *   av_fmt_ctx_out;
    AVStream *          av_stream;
    AVPacket            av_pkt;
    AVFormatContext *   ifmt_ctx;


    unsigned long long last_frame_ts_utc;
    unsigned long long last_frame_ts_absolute;
    unsigned long long last_pts;

    t_mp4_dict_metadata metadata;
    char file_name[1024];
    char TSstrdate[128];

    av_register_all();

    cc_in = NULL;
    av_stream = NULL;

    if (avformat_alloc_output_context2(&mp4h->av_fmt_ctx_out, NULL, NULL, file_name) < 0) {
        trace_error("avformat_alloc_output_context2 failed");
        goto FnExit;
    }

    /* find the H264 RAW encoder */
    codec = avcodec_find_encoder(AV_CODEC_ID_H264);
    if (!codec) {
        int ret;
        AVStream *in_stream = NULL;

        if (av_fmt_ctx_in == NULL)
        {
            trace_error("av_fmt_ctx_in is NULL");
            goto FnExit;
        }
        in_stream = av_fmt_ctx_in->streams[0];

        in_stream->codec->width = par.width;
        in_stream->codec->height = par.height;
        in_stream->codec->coded_width = par.width;
        in_stream->codec->coded_height = par.height;
        in_stream->codec->bit_rate = 1024;
        in_stream->codec->flags = CODEC_FLAG_GLOBAL_HEADER;
        in_stream->codec->time_base.num = 1;
        in_stream->codec->time_base.den = par.frame_rate;
        in_stream->codec->gop_size = par.gop;
        in_stream->codec->pix_fmt = AV_PIX_FMT_YUV420P;

        av_stream = avformat_new_stream(mp4h->av_fmt_ctx_out, in_stream->codec->codec);
        if (!av_stream) {
            trace_error("Failed allocating output stream");
            goto FnExit;
        }

        ret = avcodec_copy_context(av_stream->codec, in_stream->codec);
        if (ret != 0) {
            goto FnExit;
        }
    }
    else {
        int ret;
        av_stream = avformat_new_stream(mp4h->av_fmt_ctx_out, NULL);
        if (!av_stream) {
            goto FnExit;
        }

        cc_in = avcodec_alloc_context3(codec);
        if (cc_in == NULL) {
            goto FnExit;
        }

        cc_in->width = par.width;
        cc_in->height = par.height;
        cc_in->bit_rate = 1024;
        cc_in->flags = CODEC_FLAG_GLOBAL_HEADER;
        cc_in->time_base.num = 1;
        cc_in->time_base.den = par.frame_rate;
        cc_in->gop_size = par.gop;
        cc_in->pix_fmt = AV_PIX_FMT_YUVJ420P;

        cc_in->extradata = (unsigned char*)av_mallocz(sizeof(sample_spspps));
        cc_in->extradata_size = sizeof(sample_spspps);
        memcpy(cc_in->extradata, sample_spspps, cc_in->extradata_size);

        ret = avcodec_copy_context(av_stream->codec, cc_in);
        if (ret != 0) {
            goto FnExit;
        }
    }

    av_stream->codec->codec_tag = 0;
    if (av_fmt_ctx_out->oformat->flags & AVFMT_GLOBALHEADER)
        av_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;

    if (!(av_fmt_ctx_out->flags & AVFMT_NOFILE)) {
        int ret = avio_open(&av_fmt_ctx_out->pb, file_name, AVIO_FLAG_READ_WRITE);
        if (ret < 0) {
            trace_error("Could not open output file '%s'", file_name);
            goto FnExit;
        }
    }

    av_fmt_ctx_out->streams[0]->time_base.num = 1;
    av_fmt_ctx_out->streams[0]->time_base.den = par.frame_rate;
    av_fmt_ctx_out->streams[0]->codec->time_base.num = 1;
    av_fmt_ctx_out->streams[0]->codec->time_base.den = par.frame_rate;
    AVRational fps;
    fps.num = 1;
    fps.den = par.frame_rate;
    av_stream_set_r_frame_rate(av_fmt_ctx_out->streams[0], fps);

    mp4h->av_fmt_ctx_out->streams[0]->first_dts = AV_TIME_BASE;
    av_dict_set(&pMetaData, "title", par.guid_video_function, 0);
    av_dict_set(&pMetaData, "artist", "Test Artist", 0);
    av_dict_set(&pMetaData, "date", TSstrdate, 0);
    av_fmt_ctx_out->metadata = pMetaData;

    if (avformat_write_header(av_fmt_ctx_out, NULL) < 0) {
        goto FnExit;
    }

    //............. Now for each frame_rate........

    av_init_packet(&av_pkt);

    if (first_frame)
    {
        av_pkt.pts = 0;
        av_pkt.dts = 0;
    }
    else
    {
        av_pkt.pts = last_pts + (long long int)((img->timestamp_absolute - last_frame_ts_absolute) *  (unsigned long long)av_stream->time_base.den / 1000000ULL);
        av_pkt.dts = last_pts + (long long int)((img->timestamp_absolute - last_frame_ts_absolute) *  (unsigned long long)av_stream->time_base.den / 1000000ULL);
    }


    mp4h->av_pkt.duration = 0;
    mp4h->av_pkt.pos = -1;



    last_frame_ts_utc = img->timestamp_utc.t;
    last_frame_ts_absolute = img->timestamp_absolute.t;
    last_pts = av_pkt.pts;

    if (img->type == keyframe)
    {
        av_pkt.flags |= AV_PKT_FLAG_KEY;
    }

    av_pkt.data = img->ptr;
    av_pkt.size = img->size;
    av_pkt.stream_index = av_stream->index;

    ret = av_interleaved_write_frame(av_fmt_ctx_out, &av_pkt);
    if (ret != 0) {
        char strE[256];
        av_strerror(ret, strE, sizeof(strE));
        trace_error("av_write_frame returns %d - %s", ret, strE);
        return;
    }



    //........then I will close the file
FnExit:

    if (av_fmt_ctx_out && av_fmt_ctx_out->pb != NULL) {
        if (av_write_trailer(mp4h->av_fmt_ctx_out) != 0) {
            trace_error("av_write_trailer Error!");
        }
    }

    if (ifmt_ctx)
        avformat_close_input(&ifmt_ctx);

    avio_closep(&av_fmt_ctx_out->pb);
    avcodec_close(av_stream->codec);
    avformat_free_context(av_fmt_ctx_out);
}

如何修改它以嵌入每帧的实际时间戳?

我试图将实际时间戳添加到第一帧 pts 而不是将其设置为零,但它不起作用。

标签: c++cffmpeg

解决方案


推荐阅读