• FFmpeg封装格式处理4-转封装例程


    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10506662.html

    FFmpeg封装格式处理相关内容分为如下几篇文章:
    [1]. FFmpeg封装格式处理-简介
    [2]. FFmpeg封装格式处理-解复用例程
    [3]. FFmpeg封装格式处理-复用例程
    [4]. FFmpeg封装格式处理-转封装例程

    5. 转封装例程

    转封装是将一种封装格式转换为另一种封装格式,不涉及编解码操作,转换速度非常快。

    remuxing

    5.1 源码

    源码修改自 FFmpeg 4.1 自带的例程 remuxing.c。代码非常简短:

    #include <libavutil/timestamp.h>
    #include <libavformat/avformat.h>
    
    int main(int argc, char **argv)
    {
        AVOutputFormat *ofmt = NULL;
        AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
        AVPacket pkt;
        const 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
    "
                   "API example program to remux a media file with libavformat and libavcodec.
    "
                   "The output format is guessed according to the file extension.
    "
                   "
    ", 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, 0)) < 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, NULL, out_filename);
        if (!ofmt_ctx) {
            printf("Could not create output context
    ");
            ret = AVERROR_UNKNOWN;
            goto end;
        }
    
        stream_mapping_size = ifmt_ctx->nb_streams;
        stream_mapping = av_mallocz_array(stream_mapping_size, sizeof(*stream_mapping));
        if (!stream_mapping) {
            ret = AVERROR(ENOMEM);
            goto end;
        }
    
        ofmt = ofmt_ctx->oformat;
    
        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;
    
            if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO &&
                in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO &&
                in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
                stream_mapping[i] = -1;
                continue;
            }
    
            stream_mapping[i] = stream_index++;
    
            // 2.2 将一个新流(out_stream)添加到输出文件(ofmt_ctx)
            out_stream = avformat_new_stream(ofmt_ctx, NULL);
            if (!out_stream) {
                printf("Failed allocating output stream
    ");
                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
    ");
                goto end;
            }
            out_stream->codecpar->codec_tag = 0;
        }
        av_dump_format(ofmt_ctx, 0, out_filename, 1);
    
        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
    ");
            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];
            if (pkt.stream_index >= stream_mapping_size ||
                stream_mapping[pkt.stream_index] < 0) {
                av_packet_unref(&pkt);
                continue;
            }
    
            pkt.stream_index = stream_mapping[pkt.stream_index];
            out_stream = ofmt_ctx->streams[pkt.stream_index];
    
            /* copy packet */
            // 3.3 更新packet中的pts和dts
            // 关于AVStream.time_base的说明:
            // 输入:输入流中含有time_base,在avformat_find_stream_info()中可取到每个流中的time_base
            // 输出:avformat_write_header()会根据输出的封装格式确定每个流的time_base并写入文件中
            // AVPacket.pts和AVPacket.dts的单位是AVStream.time_base,不同的封装格式其AVStream.time_base不同
            // 所以输出文件中,每个packet需要根据输出封装格式重新计算pts和dts
            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
    ");
                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: %s
    ", av_err2str(ret));
            return 1;
        }
    
        return 0;
    }
    

    5.2 编译

    源文件为remuxing.c,在SHELL中执行如下编译命令:

    gcc -o remuxing remuxing.c -lavformat -lavcodec -lavutil -g
    

    生成可执行文件remuxing

    5.3 验证

    测试文件下载:tnshih.flv
    林译
    先看一下测试用资源文件的格式:

    think@opensuse> ffprobe tnliny.flv 
    ffprobe version 4.1 Copyright (c) 2007-2018 the FFmpeg developers
    Input #0, flv, from 'tnliny.flv':
      Metadata:
        encoder         : Lavf58.20.100
      Duration: 00:02:26.54, start: 0.000000, bitrate: 446 kb/s
        Stream #0:0: Video: h264 (High), yuv420p(progressive), 800x450, 25 fps, 25 tbr, 1k tbn, 50 tbc
        Stream #0:1: Audio: aac (LC), 44100 Hz, stereo, fltp
    

    运行如下命令进行测试:

    ./remuxing tnliny.flv tnliny.ts
    

    使用ffprobe检测输出文件正常。使用ffplay播放输出文件正常,播放效果和原始的测试文件一致。

  • 相关阅读:
    函数的内置方法
    函数
    文件操作
    三元运算
    流程控制
    集合
    div容器内文本对齐--神奇的css
    Struts2的零配置和rest插件
    MIME类型
    ganymed-ssh2使用
  • 原文地址:https://www.cnblogs.com/leisure_chn/p/10506662.html
Copyright © 2020-2023  润新知