如何使用 libavformat 连接 2 个具有相同编解码器的视频文件(重新混合)? [英] How to use libavformat to concat 2 video files with same codec (re-muxing)?

查看:22
本文介绍了如何使用 libavformat 连接 2 个具有相同编解码器的视频文件(重新混合)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经从 CDN 下载了 flv 格式的视频(视频 H264 和音频 AAC)并将它们重新混合为 MP4 格式.但是视频有长度限制.所以我将每个视频分几个部分下载:从起点开始,在点 1,在点 2(通过在 url 中使用 seek 参数).每个点的开始都比前一个点的结束早一点.
使用 av_read_frame 我扫描了所有部分,发现相交的数据包不仅具有相同的大小和顺序,而且它们的 dts/pts 彼此偏移恒定值.因此,要使用从第 1 点开始的视频连接开始视频,我必须执行以下操作:
1.在输出文件中创建输出头
2.从起始视频复制所有非相交数据包.
3. 复制从第 1 点开始的视频中所有不相交的数据包,dts 值发生变化,将其移动常量

I have downloaded videos from CDN in flv format (video H264 and audio AAC) and remux to them to MP4 format. But videos are limited by length. So i've downloaded each video in several parts: started at start point, at point 1, at point 2 (by using seek parameter in url). Each point starts little earlier than ending of previous one.
Using av_read_frame i scanned all parts and found that intersecting packets not only have same sizes and order but also their dts/pts shifted from each other by constant value. So to concat starting video with video started at point 1 I must do following:
1. Create output header in output file
2. Copy all non-intersecting packets from starting video.
3. Copy all non-intersecting packets from video started at point 1 with changed dts values by shifted it by constant

如何使用 libav(而不是 ffmpeg)完成所有这些工作?我阅读了 如何在不使用其他 libav 库的情况下使用 libavformat.但是在 libav 中它不起作用,因为在 libav 中没有 avformat_alloc_output_context2.另外源 avconv.c 源对于像我这样的新手来说太复杂了,无法隔离与流复制操作相关的部分.
有人可以为我提供以下示例:
- 打开 input_file1input_file2(仅当程序与通用教程中的标准不同时才需要)
- 使用相同的容器格式和相同的视频和音频格式为 output_file 打开和写入标头
- 将数据包从 input_file1 写入 output_file 到数据包,例如 pos == XXX
- 将数据包从 input_file2 写入 output_file 以恒定值更改其 dts(或任何需要的)
- 编写正确的trailer

How to do all of this using libav (not ffmpeg)? I read How can libavformat be used without using other libav libraries. But in libav it is not working since there not avformat_alloc_output_context2 in libav. Also source avconv.c source is too complex for newbie like me to isolate parts related to stream copy operations.
Can someone provide me example to:
- open input_file1 and input_file2 (only needed if procedure differs from standard in generic tutorials)
- open and write header for output_file with same container format and same video and audio formats
- write packets from input_file1 to output_file up to packet with for example pos == XXX
- write packets from input_file2 to output_file changing their dts (or whatever needed) by constant value
- write correct trailer

计算我之前所做的 dts 中的时间偏移.

Calculating of time shift in dts i made before.

推荐答案

我替换了不推荐使用的方法,并添加了 ALiang 的更正.它有效.

I have replaced deprecated methods, and corrections by ALiang are added too. It works.

`/*
 * merge.c
 *
 *  Created on: Nov 17, 2012
 *  Author: arash
 *
 *  Modified on: April 5, 2018
 *  Editor: Chebotarev Michael
 */
/* merge multiple "IDENTICAL" video file into one file */
#include <stdio.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
AVFormatContext *i_fmt_ctx;
AVStream *i_video_stream;
AVFormatContext *o_fmt_ctx;
AVStream *o_video_stream;

int main(int argc, char* argv[])
{
if (argc < 3)
{
    fprintf(stderr, "usage : %s <input> [<input>...] <output>
", argv[0]);
    return -1;
}

avcodec_register_all();
av_register_all();

/* should set to NULL so that avformat_open_input() allocate a new one */
i_fmt_ctx = NULL;
if (avformat_open_input(&i_fmt_ctx, argv[1], NULL, NULL) != 0)
{
    fprintf(stderr, "could not open input file
");
    return -1;
}

if (avformat_find_stream_info(i_fmt_ctx, NULL) < 0)
{
    fprintf(stderr, "could not find stream info
");
    return -1;
}

//av_dump_format(i_fmt_ctx, 0, argv[1], 0);

/* find first video stream */
for (unsigned i = 0; i<i_fmt_ctx->nb_streams; i++)
    if (i_fmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
    {
        i_video_stream = i_fmt_ctx->streams[i];
        break;
    }
if (i_video_stream == NULL)
{
    fprintf(stderr, "didn't find any video stream
");
    return -1;
}

avformat_alloc_output_context2(&o_fmt_ctx, NULL, NULL, argv[argc - 1]);

/*
* since all input files are supposed to be identical (framerate, dimension, color format, ...)
* we can safely set output codec values from first input file
*/
o_video_stream = avformat_new_stream(o_fmt_ctx, 0);
{
    AVCodecContext *c;
    c = o_video_stream->codec;
    c->bit_rate = 400000;
    c->codec_id = i_video_stream->codec->codec_id;
    c->codec_type = i_video_stream->codec->codec_type;
    c->time_base.num = i_video_stream->time_base.num;
    c->time_base.den = i_video_stream->time_base.den;
    fprintf(stderr, "time_base.num = %d time_base.den = %d
", c->time_base.num, c->time_base.den);
    c->width = i_video_stream->codec->width;
    c->height = i_video_stream->codec->height;
    c->pix_fmt = i_video_stream->codec->pix_fmt;
    printf("%d %d %d", c->width, c->height, c->pix_fmt);
    c->flags = i_video_stream->codec->flags;
    c->flags |= CODEC_FLAG_GLOBAL_HEADER;
    c->me_range = i_video_stream->codec->me_range;
    c->max_qdiff = i_video_stream->codec->max_qdiff;

    c->qmin = i_video_stream->codec->qmin;
    c->qmax = i_video_stream->codec->qmax;

    c->qcompress = i_video_stream->codec->qcompress;

    c->extradata = i_video_stream->codec->extradata;
    c->extradata_size = i_video_stream->codec->extradata_size;
}

avio_open(&o_fmt_ctx->pb, argv[argc - 1], AVIO_FLAG_WRITE);

/* yes! this is redundant */
avformat_close_input(&i_fmt_ctx);

avformat_write_header(o_fmt_ctx, NULL);

int last_pts = 0;
int last_dts = 0;
for (int i = 1; i<argc - 1; i++)
{
    i_fmt_ctx = NULL;
    if (avformat_open_input(&i_fmt_ctx, argv[i], NULL, NULL) != 0)
    {
        fprintf(stderr, "could not open input file
");
        return -1;
    }

    if (avformat_find_stream_info(i_fmt_ctx, NULL) < 0)
    {
        fprintf(stderr, "could not find stream info
");
        return -1;
    }
    av_dump_format(i_fmt_ctx, 0, argv[i], 0);

    /* we only use first video stream of each input file */
    i_video_stream = NULL;
    for (unsigned s = 0; s<i_fmt_ctx->nb_streams; s++)
        if (i_fmt_ctx->streams[s]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            i_video_stream = i_fmt_ctx->streams[s];
            break;
        }

    if (i_video_stream == NULL)
    {
        fprintf(stderr, "didn't find any video stream
");
        return -1;
    }

    int64_t pts, dts;
    while (1)
    {
        AVPacket i_pkt;
        av_init_packet(&i_pkt);
        i_pkt.size = 0;
        i_pkt.data = NULL;
        if (av_read_frame(i_fmt_ctx, &i_pkt) <0)
            break;
        /*
        * pts and dts should increase monotonically
        * pts should be >= dts
        */
        i_pkt.flags |= AV_PKT_FLAG_KEY;
        pts = i_pkt.pts;
        i_pkt.pts += last_pts;
        dts = i_pkt.dts;
        i_pkt.dts += last_dts;
        i_pkt.stream_index = 0;

        //printf("%lld %lld
", i_pkt.pts, i_pkt.dts);
        static int num = 1;
        printf("frame %d
", num++);
        av_interleaved_write_frame(o_fmt_ctx, &i_pkt);
        //av_free_packet(&i_pkt);
        //av_init_packet(&i_pkt);
    }
    last_dts += dts;
    last_pts += pts;

    avformat_close_input(&i_fmt_ctx);
}

av_write_trailer(o_fmt_ctx);

avcodec_close(o_fmt_ctx->streams[0]->codec);
av_freep(&o_fmt_ctx->streams[0]->codec);
av_freep(&o_fmt_ctx->streams[0]);

avio_close(o_fmt_ctx->pb);
av_free(o_fmt_ctx);

return 0;
}`

这篇关于如何使用 libavformat 连接 2 个具有相同编解码器的视频文件(重新混合)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆