本文所使用的是FFmpeg n4.4的源码,所有分析均来自博主瞎猜,如果有误,欢迎批评指正。
avformat_alloc_output_context2 作用
先看一下FFmpeg是怎么定义这个函数的
/**
* 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, ff_const59 AVOutputFormat *oformat,
const char *format_name, const char *filename);
这个函数就是为输出文件创建AVFormatContext,最后使用avformat_free_context函数释放AVFormatContext。
avformat_alloc_output_context2源码分析
int avformat_alloc_output_context2(AVFormatContext **avctx, ff_const59 AVOutputFormat *oformat,
const char *format, const char *filename)
{
//创建一个空的AVFormatContext 这个和在avformat_open_input 方法中,创建
//input的AVFormatContext调用的方法是一致的。
AVFormatContext *s = avformat_alloc_context();
int ret = 0;
*avctx = NULL;
if (!s)
goto nomem;
if (!oformat) {
if (format) {
// 比较重要的就是这个方法,根据foramt或者filename找到对应的AVOuputFormat
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 {
//找到对应的AVOuputFormat
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;
// 创建AVOuputFormat中Context大小的空间,在接下来的write_header等流程中会使用到
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) {
#if FF_API_FORMAT_FILENAME
FF_DISABLE_DEPRECATION_WARNINGS
av_strlcpy(s->filename, filename, sizeof(s->filename));
FF_ENABLE_DEPRECATION_WARNINGS
#endif
if (!(s->url = av_strdup(filename)))
goto nomem;
}
*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;
}
这个函数较之前的几个相比来说,还是比较简单的,里面总共就干了三件事
- 初始化一个空的AVFormatContext
- 根据输入的filename或者format找到对应的AVOuputFormat
- 初始化AVOuputFormat中context所需要的空间
av_guess_format源码
//short_name 相当于format,就比如说mp4视频格式,short_name就是mp4
ff_const59 AVOutputFormat *av_guess_format(const char *short_name, const char *filename,
const char *mime_type)
{
const AVOutputFormat *fmt = NULL;
AVOutputFormat *fmt_found = NULL;
void *i = 0;
int score_max, score;
/* specific test for image sequences */
#if CONFIG_IMAGE2_MUXER
//这里判断输出是否为image类型,ff_guess_image2_codec里面就是列举
//所有的image的format(比如png)和对应的codec id(比如AV_CODEC_ID_PNG)
//所有的image类型的AVOuputformat都是使用的“image2”
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. */
score_max = 0;
// 和打开input的时候类似,寻找output也是依靠这种方法
// av_muxer_iterate会枚举所有AVOuputFormat类型,
// 看看哪种和short_name或者filename匹配。
while ((fmt = av_muxer_iterate(&i))) {
score = 0;
//根据short_name进行匹配,也就是根据format进行匹配
if (fmt->name && short_name && av_match_name(short_name, fmt->name))
score += 100;
//mime_type感觉就是在format之前加一个video或者audio标识,标志这种格式的类型。
//调用avformat_alloc_output_context2并没有设置这个
if (fmt->mime_type && mime_type && !strcmp(fmt->mime_type, mime_type))
score += 10;
//av_match_ext会从最后一个'.'符号开始,和fmt->extensions进行匹配,如果一致,就返回1
//所以这里就是判断输出地址的格式和AVformatOutput的后缀名是否一样
if (filename && fmt->extensions &&
av_match_ext(filename, fmt->extensions)) {
score += 5;
}
if (score > score_max) {
score_max = score;
fmt_found = (AVOutputFormat*)fmt;
}
}
return fmt_found;
}
总体而言,av_guess_format就是根据输入的format或者filename找到最匹配的AVOutputFormat,总体而言还是比较简单。