首页 > 代码库 > 最简单的基于FFMPEG的转码程序

最简单的基于FFMPEG的转码程序

本文介绍一个简单的基于FFmpeg的转码器。转码器在视音频编解码处理的程序中,属于一个比较复杂的东西。因为它结合了视频的解码和编码。一个视频播放器,一般只包含解码功能;一个视频编码工具,一般只包含编码功能;而一个视频转码器,则需要先对视频进行解码,然后再对视频进行编码,因而相当于解码器和编码器的结合。下图例举了一个视频的转码流程。输入视频的封装格式是FLV,视频编码标准是H.264,音频编码标准是AAC;输出视频的封装格式是AVI,视频编码标准是MPEG2,音频编码标准是MP3。从流程中可以看出,首先从输入视频中分离出视频码流和音频压缩码流,然后分别将视频码流和音频码流进行解码,获取到非压缩的像素数据/音频采样数据,接着将非压缩的像素数据/音频采样数据重新进行编码,获得重新编码后的视频码流和音频码流,最后将视频码流和音频码流重新封装成一个文件。

 

本文介绍的视频转码器正是使用FFMPEG类库从编程的角度实现了上述流程。

下面贴上代码,代码是从FFmpeg的例子改编的,平台是VC2010,类库版本是2014.5.6。

 

/*
 *最简单的基于FFmpeg的转码器
 *Simplest FFmpeg Transcoder
 *
 *雷霄骅 Lei Xiaohua
 *leixiaohua1020@126.com
 *中国传媒大学/数字电视技术
 *Communication University of China / DigitalTV Technology
 *http://blog.csdn.net/leixiaohua1020
 *
 *本程序实现了视频格式之间的转换。是一个最简单的视频转码程序。
 *
 */
 
#include "stdafx.h"
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavfilter/avfiltergraph.h"
#include "libavfilter/avcodec.h"
#include "libavfilter/buffersink.h"
#include "libavfilter/buffersrc.h"
#include "libavutil/avutil.h"
#include "libavutil/opt.h"
#include "libavutil/pixdesc.h"
};
 
 
 
static AVFormatContext *ifmt_ctx;
static AVFormatContext *ofmt_ctx;
typedef struct FilteringContext{
    AVFilterContext*buffersink_ctx;
    AVFilterContext*buffersrc_ctx;
    AVFilterGraph*filter_graph;
} FilteringContext;
static FilteringContext *filter_ctx;
static int open_input_file(const char *filename)
{
    int ret;
    unsigned int i;
    ifmt_ctx =NULL;
    if ((ret = avformat_open_input(&ifmt_ctx,filename, NULL, NULL)) < 0) {
       av_log(NULL, AV_LOG_ERROR, "Cannot openinput file\n");
        return ret;
    }
    if ((ret = avformat_find_stream_info(ifmt_ctx, NULL))< 0) {
       av_log(NULL, AV_LOG_ERROR, "Cannot findstream information\n");
        return ret;
    }
    for (i = 0; i < ifmt_ctx->nb_streams; i++) {
        AVStream*stream;
       AVCodecContext *codec_ctx;
        stream =ifmt_ctx->streams[i];
        codec_ctx =stream->codec;
        /* Reencode video & audio and remux subtitles etc. */
        if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO
                ||codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
            /* Open decoder */
            ret =avcodec_open2(codec_ctx,
                   avcodec_find_decoder(codec_ctx->codec_id), NULL);
            if (ret < 0) {
               av_log(NULL, AV_LOG_ERROR, "Failed toopen decoder for stream #%u\n", i);
                return ret;
            }
        }
    }
   av_dump_format(ifmt_ctx, 0, filename, 0);
    return 0;
}
static int open_output_file(const char *filename)
{
    AVStream*out_stream;
    AVStream*in_stream;
    AVCodecContext*dec_ctx, *enc_ctx;
    AVCodec*encoder;
    int ret;
    unsigned int i;
    ofmt_ctx =NULL;
   avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, filename);
    if (!ofmt_ctx) {
       av_log(NULL, AV_LOG_ERROR, "Could notcreate output context\n");
        return AVERROR_UNKNOWN;
    }
    for (i = 0; i < ifmt_ctx->nb_streams; i++) {
        out_stream= avformat_new_stream(ofmt_ctx, NULL);
        if (!out_stream) {
           av_log(NULL, AV_LOG_ERROR, "Failedallocating output stream\n");
            return AVERROR_UNKNOWN;
        }
        in_stream =ifmt_ctx->streams[i];
        dec_ctx =in_stream->codec;
        enc_ctx =out_stream->codec;
        if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO
                ||dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
            /* in this example, we choose transcoding to same codec */
            encoder= avcodec_find_encoder(dec_ctx->codec_id);
            /* In this example, we transcode to same properties(picture size,
            * sample rate etc.). These properties can be changed for output
            * streams easily using filters */
            if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {
               enc_ctx->height = dec_ctx->height;
               enc_ctx->width = dec_ctx->width;
               enc_ctx->sample_aspect_ratio = dec_ctx->sample_aspect_ratio;
                /* take first format from list of supported formats */
               enc_ctx->pix_fmt = encoder->pix_fmts[0];
                /* video time_base can be set to whatever is handy andsupported by encoder */
               enc_ctx->time_base = dec_ctx->time_base;
            } else {
               enc_ctx->sample_rate = dec_ctx->sample_rate;
               enc_ctx->channel_layout = dec_ctx->channel_layout;
               enc_ctx->channels = av_get_channel_layout_nb_channels(enc_ctx->channel_layout);
                /* take first format from list of supported formats */
               enc_ctx->sample_fmt = encoder->sample_fmts[0];
                AVRationaltime_base={1, enc_ctx->sample_rate};
               enc_ctx->time_base = time_base;
            }
            /* Third parameter can be used to pass settings to encoder*/
            ret =avcodec_open2(enc_ctx, encoder, NULL);
            if (ret < 0) {
               av_log(NULL, AV_LOG_ERROR, "Cannot openvideo encoder for stream #%u\n", i);
                return ret;
            }
        } else if(dec_ctx->codec_type == AVMEDIA_TYPE_UNKNOWN) {
           av_log(NULL, AV_LOG_FATAL, "Elementarystream #%d is of unknown type, cannot proceed\n", i);
            return AVERROR_INVALIDDATA;
        } else {
            /* if this stream must be remuxed */
            ret =avcodec_copy_context(ofmt_ctx->streams[i]->codec,
                   ifmt_ctx->streams[i]->codec);
            if (ret < 0) {
               av_log(NULL, AV_LOG_ERROR, "Copyingstream context failed\n");
                return ret;
            }
        }
        if (ofmt_ctx->oformat->flags &AVFMT_GLOBALHEADER)
           enc_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
    }
   av_dump_format(ofmt_ctx, 0, filename, 1);
    if (!(ofmt_ctx->oformat->flags &AVFMT_NOFILE)) {
        ret =avio_open(&ofmt_ctx->pb, filename, AVIO_FLAG_WRITE);
        if (ret < 0) {
           av_log(NULL, AV_LOG_ERROR, "Could notopen output file '%s'", filename);
            return ret;
        }
    }
    /* init muxer, write output file header */
    ret =avformat_write_header(ofmt_ctx, NULL);
    if (ret < 0) {
        av_log(NULL,AV_LOG_ERROR, "Error occurred when openingoutput file\n");
        return ret;
    }
    return 0;
}
static intinit_filter(FilteringContext* fctx, AVCodecContext *dec_ctx,
       AVCodecContext *enc_ctx, const char *filter_spec)
{
    char args[512];
    int ret = 0;
    AVFilter*buffersrc = http://www.mamicode.com/NULL;>

程序运行截图:


默认情况下运行程序,会将“cuc_ieschool.ts”转换为“cuc_ieschool.avi”。调试的时候,可以修改“配置属性->调试->命令参数”中的参数,即可改变转码的输入输出文件。



工程下载地址(VC2010):http://download.csdn.net/detail/leixiaohua1020/7394649