最简单的基于FFmpeg的封装格式转换器(无编解码)

本文介绍一个基于FFMPEG的封装格式转换器。所谓的封装格式转换,就是在AVI,FLV,MKV,MP4这些格式之间转换(对应.avi,.flv,.mkv,.mp4文件)。需要注意的是,本程序并不进行视音频的编码和解码工作。而是直接将视音频压缩码流从一种封装格式文件中获取出来然后打包成另外一种封装格式的文件。传统的转码程序工作原理如下图所示:

上图例举了一个举例:FLV(视频:H.264,音频:AAC)转码为AVI(视频:MPEG2,音频MP3)的例子。可见视频转码的过程通俗地讲相当于把视频和音频重新“录”了一遍。

本程序的工作原理如下图所示:

由图可见,本程序并不进行视频和音频的编解码工作,因此本程序和普通的转码软件相比,有以下两个特点:

处理速度极快。视音频编解码算法十分复杂,占据了转码的绝大部分时间。因为不需要进行视音频的编码和解码,所以节约了大量的时间。

视音频质量无损。因为不需要进行视音频的编码和解码,所以不会有视音频的压缩损伤。

下面贴上代码,代码是从FFmpeg的例子改编的,平台是VC2010。

/*
 *最简单的基于FFmpeg的封装格式转换器
 *Simplest FFmpeg Remuxer
 *
 *雷霄骅 Lei Xiaohua
 *[email protected]
 *中国传媒大学/数字电视技术
 *Communication University of China / Digital TV Technology
 *http://blog.csdn.net/leixiaohua1020
 *
 *本程序实现了视频封装格式之间的转换。
 *需要注意的是本程序并不改变视音频的编码格式。
 *
 * This software converts a media file from one container format
 * to another container format without encoding/decoding video files.
 */
 
#include "stdafx.h"
 
extern "C"
{
#include "libavformat/avformat.h"
};
 
 
int _tmain(int argc, _TCHAR* argv[])
{
    AVOutputFormat *ofmt = NULL;
    //输入对应一个AVFormatContext,输出对应一个AVFormatContext
    //(Input AVFormatContext and Output AVFormatContext)
    AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
    AVPacket pkt;
    const char *in_filename, *out_filename;
    int ret, i;
    if (argc < 3) {
        printf("usage: %s input output\n"
            "Remux a media file with libavformat and libavcodec.\n"
            "The output format is guessed according to the file extension.\n"
            "Modified by Lei Xiaohua, [email protected]\n"
            "Communication University of China / Digital TV Technology\n"
            "http://blog.csdn.net/leixiaohua1020", argv[0]);
        return 1;
    }
    in_filename  = argv[1];//输入文件名(Input file URL)
    out_filename = argv[2];//输出文件名(Output file URL)
    av_register_all();
    //输入(Input)
    if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
        printf( "Could not open input file.");
        goto end;
    }
    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);
    //输出(Output)
    avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
    if (!ofmt_ctx) {
        printf( "Could not create output context\n");
        ret = AVERROR_UNKNOWN;
        goto end;
    }
    ofmt = ofmt_ctx->oformat;
    for (i = 0; i < ifmt_ctx->nb_streams; i++) {
        //根据输入流创建输出流(Create output AVStream according to input AVStream)
        AVStream *in_stream = ifmt_ctx->streams[i];
        AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);
        if (!out_stream) {
            printf( "Failed allocating output stream\n");
            ret = AVERROR_UNKNOWN;
            goto end;
        }
        //复制AVCodecContext的设置(Copy the settings of AVCodecContext)
        ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
        if (ret < 0) {
            printf( "Failed to copy context from input to output stream codec context\n");
            goto end;
        }
        out_stream->codec->codec_tag = 0;
        if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
            out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
    }
    //输出一下格式------------------
    av_dump_format(ofmt_ctx, 0, out_filename, 1);
    //打开输出文件(Open output file)
    if (!(ofmt->flags & AVFMT_NOFILE)) {
        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;
        }
    }
    //写文件头(Write file header)
    ret = avformat_write_header(ofmt_ctx, NULL);
    if (ret < 0) {
        printf( "Error occurred when opening output file\n");
        goto end;
    }
    int frame_index=0;
    while (1) {
        AVStream *in_stream, *out_stream;
        //获取一个AVPacket(Get an AVPacket)
        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];
        /* copy packet */
        //转换PTS/DTS(Convert PTS/DTS)
        pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
        pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
        pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
        pkt.pos = -1;
        //写入(Write)
        ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
        if (ret < 0) {
            printf( "Error muxing packet\n");
            break;
        }
        printf("Write %8d frames to output file\n",frame_index);
        av_free_packet(&pkt);
        frame_index++;
    }
    //写文件尾(Write file trailer)
    av_write_trailer(ofmt_ctx);
end:
    avformat_close_input(&ifmt_ctx);
    /* close output */
    if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
        avio_close(ofmt_ctx->pb);
    avformat_free_context(ofmt_ctx);
    if (ret < 0 && ret != AVERROR_EOF) {
        printf( "Error occurred.\n");
        return -1;
    }
    return 0;
}

调试的时候,只需要“右键工程->调试->命令行参数”里面设置输入的文件名和输出的文件名就可以了。

下图显示了一个测试的输入文件的视音频参数。

下图显示了输出文件的视音频参数。可以看出除了视频的封装格式从flv转换成了mp4,其他有关视音频编码的参数没有任何变化。

完整工程下载地址:

http://download.csdn.net/detail/leixiaohua1020/7323491

最简单的基于FFmpeg的封装格式转换器(无编解码)

时间: 2024-10-14 00:30:39

最简单的基于FFmpeg的封装格式转换器(无编解码)的相关文章

最简单的基于FFmpeg的封装格式处理:视音频分离器简化版(demuxer-simple)

打算记录一下基于FFmpeg的封装格式处理方面的例子.包括了视音频分离,复用,封装格式转换.有关封转格式转换的例子在之前的文章:<最简单的基于FFMPEG的封装格式转换器(无编解码)>中已经有过记录,不再重复.因此计划写3篇文章分别记录视音频的复用器(Muxer)和分离器(Demuxer).其中视音频分离器(Demuxer)记录2篇:一篇简单的,一篇标准的.简单的版本更适合初学者学习.本文是第1篇.首先记录一个基于FFmpeg的视音频分离器简单版(Simplest FFmpeg Demuxer

最简单的基于FFmpeg的封装格式处理:视音频分离器(demuxer)

打算记录一下基于FFmpeg的封装格式处理方面的例子.包括了视音频分离,复用,封装格式转换.这是第2篇. 本文记录一个基于FFmpeg的视音频分离器(Simplest FFmpeg demuxer).视音频分离器(Demuxer)即是将封装格式数据(例如MKV)中的视频压缩数据(例如H.264)和音频压缩数据(例如AAC)分离开.如图所示.在这个过程中并不涉及到编码和解码. 本文记录的程序可以将一个MPEG2TS封装的视频文件(其中视频编码为H.264,音频编码为AAC)分离成为两个文件:一个H

最简单的基于FFmpeg的封装格式处理:视音频复用器(muxer)

打算记录一下基于FFmpeg的封装格式处理方面的例子.包括了视音频分离,复用,封装格式转换.这是第3篇. 本文记录一个基于FFmpeg的视音频复用器(Simplest FFmpeg muxer).视音频复用器(Muxer)即是将视频压缩数据(例如H.264)和音频压缩数据(例如AAC)合并到一个封装格式数据(例如MKV)中去.如图所示.在这个过程中并不涉及到编码和解码. 本文记录的程序将一个H.264编码的视频码流文件和一个MP3编码的音频码流文件,合成为一个MP4封装格式的文件. 流程 程序的

最简单的基于FFmpeg的推流器(以推送RTMP为例)

本文记录一个最简单的基于FFmpeg的推流器(simplest ffmpeg streamer).推流器的作用就是将本地的视频数据推送至流媒体服务器.本文记录的推流器,可以将本地的 MOV / AVI / MKV / MP4 / FLV 等格式的媒体文件,通过流媒体协议(例如RTMP,HTTP,UDP,TCP,RTP等等)以直播流的形式推送出去.由于流媒体协议种类繁多,不一一记录.在这里记录将本地文件以RTMP直播流的形式推送至RTMP流媒体服务器(例如 Flash Media Server,R

最简单的基于FFMPEG+SDL的视频播放器 ver2 (採用SDL2.0)

===================================================== 最简单的基于FFmpeg的视频播放器系列文章列表: 100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x) 最简单的基于FFMPEG+SDL的视频播放器 ver2 (採用SDL2.0) 最简单的基于FFmpeg的解码器-纯净版(不包括libavformat) 最简单的基于FFMPEG+SDL的视频播放器:拆分-解码器和播放器 最简单的基于FFMPEG的Hellowor

最简单的基于FFMPEG的音频编码器(PCM编码为AAC)

本文介绍一个最简单的基于FFMPEG的音频编码器.该编码器实现了PCM音频採样数据编码为AAC的压缩编码数据.编码器代码十分简单,可是每一行代码都非常重要.通过看本编码器的源码.能够了解FFMPEG音频编码的流程. 本程序使用最新版的类库(编译时间为2014.5.6).开发平台为VC2010.全部的配置都已经做好,仅仅须要执行就能够了. 流程(2014.9.29更新) 以下附一张使用FFmpeg编码音频的流程图. 使用该流程.不仅能够编码AAC的音频,并且能够编码MP3,MP2等等各种FFmpe

最简单的基于FFmpeg的移动端例子:Windows Phone HelloWorld

===================================================== 最简单的基于FFmpeg的移动端例子系列文章列表: 最简单的基于FFmpeg的移动端例子:Android HelloWorld 最简单的基于FFmpeg的移动端例子:Android 视频解码器 最简单的基于FFmpeg的移动端例子:Android 视频解码器-单个库版 最简单的基于FFmpeg的移动端例子:Android 推流器 最简单的基于FFmpeg的移动端例子:Android 视频转

最简单的基于FFMPEG的Helloworld程序

===================================================== 最简单的基于FFmpeg的视频播放器系列文章列表: 100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x) 最简单的基于FFMPEG+SDL的视频播放器 ver2 (採用SDL2.0) 最简单的基于FFmpeg的解码器-纯净版(不包括libavformat) 最简单的基于FFMPEG+SDL的视频播放器:拆分-解码器和播放器 最简单的基于FFMPEG的Hellowor

最简单的基于FFmpeg的移动端例子:Android HelloWorld

从本文开始打算记录一系列FFmpeg在Android/IOS开发的示例程序.前面几篇文章记录FFmpeg安卓端开发的例子,后面几篇文章记录FFmpeg IOS端开发的例子.这些例子中FFmpeg相关的代码源自于<FFmpeg示例合集>中的程序.本文记录第一个程序:安卓平台下基于FFmpeg的HelloWorld程序. Android程序FFmpeg类库使用说明 Android应用程序使用FFmpeg类库的流程图如下所示. 上图中的流程可以分为"编译FFmpeg类库".&qu