代码复制多节目流

#include <QtCore/QCoreApplication>

extern "C"
{
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    AVOutputFormat *ofmt = NULL;
    AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
    AVPacket pkt;
    char *in_filename, *out_filename;
    int ret, i;
    int stream_index = 0;
    int *stream_mapping = NULL;
    int stream_mapping_size = 0;

    if (argc < 3) {
        printf("usage: %s input output\n"
            "API example program to remux a media file with libavformat and libavcodec.\n"
            "The output format is guessed according to the file extension.\n"
            "\n", argv[0]);
        return 1;
    }

    in_filename = argv[1];
    out_filename = argv[2];

    // 1. 打开输入
    // 1.1 读取文件头,获取封装格式相关信息
    if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
        printf("Could not open input file '%s'", in_filename);
        goto end;
    }

    // 1.2 解码一段数据,获取流相关信息
    if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0) {
        printf("Failed to retrieve input stream information");
        goto end;
    }

    av_dump_format(ifmt_ctx, 0, in_filename, 0);

    // 2. 打开输出
    // 2.1 分配输出ctx
    avformat_alloc_output_context2(&ofmt_ctx, NULL, "mpegts", out_filename);
    if (!ofmt_ctx) {
        printf("Could not create output context\n");
        ret = AVERROR_UNKNOWN;
        goto end;
    }

    for (i = 0; i < ifmt_ctx->nb_streams; i++) {
        AVStream *out_stream;
        AVStream *in_stream = ifmt_ctx->streams[i];
        AVCodecParameters *in_codecpar = in_stream->codecpar;

        // 2.2 将一个新流(out_stream)添加到输出文件(ofmt_ctx)
        out_stream = avformat_new_stream(ofmt_ctx, NULL);
        if (!out_stream) {
            printf("Failed allocating output stream\n");
            ret = AVERROR_UNKNOWN;
            goto end;
        }

        // 2.3 将当前输入流中的参数拷贝到输出流中
        ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar);
        if (ret < 0) {
            printf("Failed to copy codec parameters\n");
            goto end;
        }
        out_stream->id = in_stream->id;
        //printf("lvyunxiang test %d\n", out_stream->id);
        out_stream->codecpar->codec_tag = 0;
    }

    int pmt_start_id = 0;
    //重新设置节目号等信息
    if (0 == strcmp(ifmt_ctx->iformat->name, "mpegts"))   //如果输入封装是mpegts,拷贝节目信息
    {
        AVStream *out_stream = NULL;
        AVStream *in_stream = NULL;
        
        //遍历所有节目号
        if (ifmt_ctx->nb_programs) {
            int j, k, total = 0;
            for (j = 0; j < ifmt_ctx->nb_programs; j++) {
                AVDictionaryEntry *name = av_dict_get(ifmt_ctx->programs[j]->metadata,
                    "service_name", NULL, 0);
                av_log(NULL, AV_LOG_INFO, "  Program %d %s\n", ifmt_ctx->programs[j]->id,
                    name ? name->value : "");
                if (j == 0)
                {
                    pmt_start_id = ifmt_ctx->programs[j]->pmt_pid;
                    av_opt_set(ofmt_ctx->priv_data, "mpegts_pmt_start_pid",QString::number(pmt_start_id).toStdString().c_str(), 1);  //设置pmt起始id
                }
                //为输出创建节目
                AVProgram* program = av_new_program(ofmt_ctx, ifmt_ctx->programs[j]->id);  //设置节目id
                if(name)
                    av_dict_set(&program->metadata, "service_name", name->value, 0);
                else
                    av_dict_set(&program->metadata, "service_name", "lvyunxiang test", 0);  //设置节目名称
                av_dict_set(&program->metadata, "service_provider", "lvyunxiang provide", 0);   //服务提供者
                program->pcr_pid = ifmt_ctx->programs[j]->pcr_pid;
                program->pmt_version = ifmt_ctx->programs[j]->pmt_version;

                //复制节目流信息
                for (k = 0; k < ifmt_ctx->programs[j]->nb_stream_indexes; k++)  //遍历该节目流下所有的流
                {
                    int index = ifmt_ctx->programs[j]->stream_index[k];
                    av_program_add_stream_index(ofmt_ctx, ifmt_ctx->programs[j]->id,index);
                }
            }
        }
    }
    else   //如果输入类型不是mpegts  ,创建节目号  节目id 1
    {
        AVProgram* program = av_new_program(ifmt_ctx, 1);
        //av_dict_set(&program->metadata, "title", "", 0);
        av_dict_set(&program->metadata, "service_name", "", 0);
        av_dict_set(&program->metadata, "service_provider", "lvyunxiang provide", 0);   //服务提供者
        for (int i = 0; i < ofmt_ctx->nb_streams; i++)
        {
            av_program_add_stream_index(ofmt_ctx,1, ofmt_ctx->streams[i]->index);
        }
        program->pmt_version = 1;
    }
    av_dump_format(ofmt_ctx, 0, out_filename, 1);
    //if(pmt_start_id != 0)
        //av_opt_set(ofmt_ctx->priv_data, "mpegts_pmt_start_pid", QString::number(pmt_start_id).toStdString().c_str(), 0);  //设置pmt起始id

    ofmt = ofmt_ctx->oformat;
    if (!(ofmt->flags & AVFMT_NOFILE)) {    // TODO: 研究AVFMT_NOFILE标志
        // 2.4 创建并初始化一个AVIOContext,用以访问URL(out_filename)指定的资源
        ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
        if (ret < 0) {
            printf("Could not open output file '%s'", out_filename);
            goto end;
        }
    }

    // 3. 数据处理
    // 3.1 写输出文件头
    ret = avformat_write_header(ofmt_ctx, NULL);
    if (ret < 0) {
        printf("Error occurred when opening output file\n");
        goto end;
    }

    while (1) {
        AVStream *in_stream, *out_stream;

        // 3.2 从输出流读取一个packet
        ret = av_read_frame(ifmt_ctx, &pkt);
        if (ret < 0)
            break;

        in_stream = ifmt_ctx->streams[pkt.stream_index];
        out_stream = ofmt_ctx->streams[pkt.stream_index];
        av_packet_rescale_ts(&pkt, in_stream->time_base, out_stream->time_base);
        pkt.pos = -1;
        // 3.4 将packet写入输出
        ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
        if (ret < 0) {
            printf("Error muxing packet\n");
            break;
        }
        av_packet_unref(&pkt);
    }
    // 3.5 写输出文件尾
    av_write_trailer(ofmt_ctx);
end:

    avformat_close_input(&ifmt_ctx);

    /* close output */
    if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
        avio_closep(&ofmt_ctx->pb);
    avformat_free_context(ofmt_ctx);

    av_freep(&stream_mapping);

    if (ret < 0 && ret != AVERROR_EOF) {
        printf("Error occurred: %d\n", ret);
        return 1;
    }
    printf("write ok\n");
    return a.exec();
}

上一篇:AtCoder AGC004F Namori (图论)


下一篇:ffmpeg从入门到放弃(三) 打开本地文件和网络文件