FFmpeg source code analysis: avformat_alloc_output_context2()

The general process of audio and video encoding (recording) is as follows:
(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) function_, con When encoding, first obtain an AVFormatContext through this function , and its declaration is located in libavformat\avformat.h, as shown below.

/** 
 * 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);  

Parameters:
ctx: AVFormatContext structure created after the function call is successful.
oformat: Specify AVOutputFormat in AVFormatContext to determine the output format. If NULL is specified, the last two parameters (format_name or filename) can be set to let FFmpeg guess the output format.
PS: To use this parameter, you need to manually obtain the AVOutputFormat, which is more troublesome than using the last two parameters.
format_name: Specifies the name of the output format. From the format name, FFmpeg will guess the output format. The output format can be "flv", "mkv" and so on.
filename: Specifies the name of the output file. Based on the file name, FFmpeg will guess the output format. The file name can be "xx.flv", "yy.mkv" and so on.
If the function executes successfully, its return value is greater than or equal to 0.
The definition of this function is located in 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;  
}

The main calling process is as follows

  1. Call avformat_alloc_context() to initialize a default AVFormatContext.
  2. If the input AVOutputFormat is specified, the input AVOutputFormat is directly assigned to the AVOutputFormat ofoformat. If the input AVOutputFormat is not specified, the output AVOutputFormat needs to be guessed based on the file format name or file name. Whether the output format is guessed by the file format name or the file name, a function av_guess_format() will be called.

av_guess_format() is an API of FFmpeg. It is declared as follows.

/** 
 * 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);  

Parameters:
short_name: the name of the format.
filename: The name of the file.
mime_type: MIME type.
Returns the best matching AVOutputFormat. Returns NULL if there is no good matching AVOutputFormat.

The code for av_guess_format() is shown below.

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;  
} 

As can be seen from the code, an integer variable score is used in av_guess_format() to record the matching degree of each output format. The function contains a while() loop, which uses the function av_oformat_next() to traverse all AVOutputFormats in FFmpeg and calculate the score of each output format one by one. The specific calculation process is divided into the following steps:

  1. If the encapsulation format name matches, the score is incremented by 100. The function av_match_name() is used in matching.
  2. If the mime type matches, the score is incremented by 10. Matching uses the string comparison function strcmp() directly.
  3. If the suffix of the file name matches, the score is incremented by 5. The function av_match_ext() is used in matching.
    After the while() loop ends, the format with the highest score is obtained, which is the most matching format.

Take avi as an example:
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,
};

Guess you like

Origin blog.csdn.net/chanlp129/article/details/128637903