FFmpeg源码分析: avformat_alloc_output_context2()

音视频编码(录像)的大致流程如下:
(1)avformat_alloc_output_context2()
(2)avfomat_write_header()
(3)avcodec_send_frame()/avcodec_receive_packet()
(4)av_write_frame()
(5)av_write_trailer()
avformat_alloc_output_context2()函数,在编码的时候首先通过这个函数获得一个AVFormatContext,它的声明位于libavformat\avformat.h,如下所示。

/** 
 * Allocate an AVFormatContext for an output format. 
 * avformat_free_context() can be used to free the context and 
 * everything allocated by the framework within it. 
 * 
 * @param *ctx is set to the created format context, or to NULL in 
 * case of failure 
 * @param oformat format to use for allocating the context, if NULL 
 * format_name and filename are used instead 
 * @param format_name the name of output format to use for allocating the 
 * context, if NULL filename is used instead 
 * @param filename the name of the filename to use for allocating the 
 * context, may be NULL 
 * @return >= 0 in case of success, a negative AVERROR code in case of 
 * failure 
 */  
int avformat_alloc_output_context2(AVFormatContext **ctx, AVOutputFormat *oformat,  const char *format_name, const char *filename);  

参数:
ctx:函数调用成功之后创建的AVFormatContext结构体。
oformat:指定AVFormatContext中的AVOutputFormat,用于确定输出格式。如果指定为NULL,可以设定后两个参数(format_name或者filename)由FFmpeg猜测输出格式。
PS:使用该参数需要自己手动获取AVOutputFormat,相对于使用后两个参数来说要麻烦一些。
format_name:指定输出格式的名称。根据格式名称,FFmpeg会推测输出格式。输出格式可以是“flv”,“mkv”等等。
filename:指定输出文件的名称。根据文件名称,FFmpeg会推测输出格式。文件名称可以是“xx.flv”,“yy.mkv”等等。
函数执行成功的话,其返回值大于等于0。
该函数的定义位于libavformat\mux.c中。

int avformat_alloc_output_context2(AVFormatContext **avctx, AVOutputFormat *oformat,const char *format, const char *filename)  
{  //申请内存
    AVFormatContext *s = avformat_alloc_context();  
    int ret = 0;  
    *avctx = NULL;  
    if (!s)  
        goto nomem;  
     //如果没有指定oformat,使用av_guess_format来找出
    if (!oformat) {  
        if (format) {  
        //如果给出format,则根据format来判断
            oformat = av_guess_format(format, NULL, NULL);  
            if (!oformat) {  
                av_log(s, AV_LOG_ERROR, "Requested output format '%s' is not a suitable output format\n", format);  
                ret = AVERROR(EINVAL);  
                goto error;  
            }  
        } else {  
        //没有给出format,则根据filename来判断
            oformat = av_guess_format(NULL, filename, NULL);  
            if (!oformat) {  
                ret = AVERROR(EINVAL);  
                av_log(s, AV_LOG_ERROR, "Unable to find a suitable output format for '%s'\n",  
                       filename);  
                goto error;  
            }  
        }  
    }    
    s->oformat = oformat;  
    if (s->oformat->priv_data_size > 0) {  
        s->priv_data = av_mallocz(s->oformat->priv_data_size);  
        if (!s->priv_data)  
            goto nomem;  
        if (s->oformat->priv_class) {  
            *(const AVClass**)s->priv_data= s->oformat->priv_class;  
            av_opt_set_defaults(s->priv_data);  
        }  
    } else  
        s->priv_data = NULL;  
    if (filename)  
        av_strlcpy(s->filename, filename, sizeof(s->filename));  
    *avctx = s;  
    return 0;  
nomem:  
    av_log(s, AV_LOG_ERROR, "Out of memory\n");  
    ret = AVERROR(ENOMEM);  
error:  
    avformat_free_context(s);  
    return ret;  
}

主要调用流程如下

  1. 调用avformat_alloc_context()初始化一个默认的AVFormatContext。
  2. 如果指定了输入的AVOutputFormat,则直接将输入的AVOutputFormat赋值给AVOutputFormat的oformat。如果没有指定输入的AVOutputFormat,就需要根据文件格式名称或者文件名推测输出的AVOutputFormat。无论是通过文件格式名称还是文件名推测输出格式,都会调用一个函数av_guess_format()。

av_guess_format()是FFmpeg的一个API。它的声明如下。

/** 
 * Return the output format in the list of registered output formats 
 * which best matches the provided parameters, or return NULL if 
 * there is no match. 
 * 
 * @param short_name if non-NULL checks if short_name matches with the 
 * names of the registered formats 
 * @param filename if non-NULL checks if filename terminates with the 
 * extensions of the registered formats 
 * @param mime_type if non-NULL checks if mime_type matches with the 
 * MIME type of the registered formats 
 */  
AVOutputFormat *av_guess_format(const char *short_name,  
                                const char *filename,  
                                const char *mime_type);  

参数:
short_name:格式的名称。
filename:文件的名称。
mime_type:MIME类型。
返回最匹配的AVOutputFormat。如果没有很匹配的AVOutputFormat,则返回NULL。

av_guess_format()的代码如下所示。

AVOutputFormat *av_guess_format(const char *short_name, const char *filename,  const char *mime_type)  
{  
    AVOutputFormat *fmt = NULL, *fmt_found;  
    int score_max, score;  
    /* specific test for image sequences */  
#if CONFIG_IMAGE2_MUXER  
    if (!short_name && filename &&  
        av_filename_number_test(filename) &&  
        ff_guess_image2_codec(filename) != AV_CODEC_ID_NONE) {  
        return av_guess_format("image2", NULL, NULL);  
    }  
#endif  
    /* Find the proper file type. */  
    fmt_found = NULL;  
    score_max = 0;  
    //所有支持的oformat都被加到了一个全局链表中,通过av_oformat_next来遍历链表查找符合的fmt
    while ((fmt = av_oformat_next(fmt))) {  
        score = 0;  
        //下面是匹配的三种规则,name、mime_type、extensions
        if (fmt->name && short_name && av_match_name(short_name, fmt->name))  
            score += 100;  
        if (fmt->mime_type && mime_type && !strcmp(fmt->mime_type, mime_type))  
            score += 10;  
        if (filename && fmt->extensions &&  
            av_match_ext(filename, fmt->extensions)) {  
            score += 5;  
        }  
        if (score > score_max) {  
            score_max = score;  
            fmt_found = fmt;  
        }  
    }  
    return fmt_found;  
} 

从代码中可以看出,av_guess_format()中使用一个整型变量score记录每种输出格式的匹配程度。函数中包含了一个while()循环,该循环利用函数av_oformat_next()遍历FFmpeg中所有的AVOutputFormat,并逐一计算每个输出格式的score。具体的计算过程分成如下几步:

  1. 如果封装格式名称匹配,score增加100。匹配中使用了函数av_match_name()。
  2. 如果mime类型匹配,score增加10。匹配直接使用字符串比较函数strcmp()。
  3. 如果文件名称的后缀匹配,score增加5。匹配中使用了函数av_match_ext()。
    while()循环结束后,得到得分最高的格式,就是最匹配的格式。

以avi为例:
name=”avi”
mime_type =”video/x-msvideo”
extensions = “avi”

AVOutputFormat ff_avi_muxer = {
    .name           = "avi",
    .long_name      = NULL_IF_CONFIG_SMALL("AVI (Audio Video Interleaved)"),
    .mime_type      = "video/x-msvideo",
    .extensions     = "avi",
    .priv_data_size = sizeof(AVIContext),
    .audio_codec    = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_AC3,
    .video_codec    = AV_CODEC_ID_MPEG4,
    .init           = avi_init,
    .write_header   = avi_write_header,
    .write_packet   = avi_write_packet,
    .write_trailer  = avi_write_trailer,
    .codec_tag      = (const AVCodecTag * const []) {
        ff_codec_bmp_tags, ff_codec_wav_tags, 0
    },
    .priv_class     = &avi_muxer_class,
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值