【FFmpeg】avformat_alloc_output_context2函数
- 1.avformat_alloc_output_context2
- 1.1 初始化AVFormatContext(avformat_alloc_context)
- 1.2 格式猜测(av_guess_format)
- 1.2.1 遍历可用的fmt(av_muxer_iterate)
- 1.2.2 name匹配检查(av_match_name)
- 1.2.3 扩展匹配检查(av_match_ext)
- 2.小结
参考:
FFmpeg源代码简单分析:avformat_alloc_output_context2()
示例工程:
【FFmpeg】调用ffmpeg库实现264软编
【FFmpeg】调用ffmpeg库实现264软解
【FFmpeg】调用ffmpeg库进行RTMP推流和拉流
【FFmpeg】调用ffmpeg库进行SDL2解码后渲染
流程分析:
【FFmpeg】编码链路上主要函数的简单分析
【FFmpeg】解码链路上主要函数的简单分析
结构体分析:
【FFmpeg】AVCodec结构体
【FFmpeg】AVCodecContext结构体
【FFmpeg】AVStream结构体
【FFmpeg】AVFormatContext结构体
【FFmpeg】AVIOContext结构体
【FFmpeg】AVPacket结构体
函数分析:
【FFmpeg】avformat_open_input函数
【FFmpeg】avformat_find_stream_info函数
avformat_alloc_output_context2内函数调用关系如下
1.avformat_alloc_output_context2
函数的主要功能是分配一个输出的context,利用输入的format和filename来确定output format,主要工作流程为:
(1)初始化AVFormatContext(avformat_alloc_context)
(2)如果没有指定输出format,需要根据输入信息来猜测一个format(av_guess_format)
(3)根据输出的format,来对AVFormatContext中的priv_data初始化
int avformat_alloc_output_context2(AVFormatContext **avctx, const AVOutputFormat *oformat,
const char *format, const char *filename)
{
AVFormatContext *s = avformat_alloc_context();
int ret = 0;
*avctx = NULL;
if (!s)
goto nomem;
// 没有指定输出format
if (!oformat) {
// 但是指定了格式名称,例如rtp,flv,udp等,则进行输出format的猜测
if (format) {
oformat = av_guess_format(format, NULL, NULL);
if (!oformat) {
av_log(s, AV_LOG_ERROR, "Requested output format '%s' is not known.\n", format);
ret = AVERROR(EINVAL);
goto error;
}
} else { // 也没有指定格式名称,使用filename进行输出format的猜测
oformat = av_guess_format(NULL, filename, NULL);
if (!oformat) {
ret = AVERROR(EINVAL);
av_log(s, AV_LOG_ERROR,
"Unable to choose an output format for '%s'; "
"use a standard extension for the filename or specify "
"the format manually.\n", filename);
goto error;
}
}
}
// 根据确定的输出format,对AVFormatContext中priv_data信息进行初始化
s->oformat = oformat;
if (ffofmt(s->oformat)->priv_data_size > 0) {
s->priv_data = av_mallocz(ffofmt(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 (!(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;
}
1.1 初始化AVFormatContext(avformat_alloc_context)
函数用于初始化AVFormatContext结构体
AVFormatContext *avformat_alloc_context(void)
{
FFFormatContext *const si = av_mallocz(sizeof(*si));
AVFormatContext *s;
if (!si)
return NULL;
s = &si->pub;
// 初始化函数指针
s->av_class = &av_format_context_class;
s->io_open = io_open_default;
s->io_close2= io_close2_default;
av_opt_set_defaults(s);
// 分配packet的空间
si->pkt = av_packet_alloc();
si->parse_pkt = av_packet_alloc();
if (!si->pkt || !si->parse_pkt) {
avformat_free_context(s);
return NULL;
}
#if FF_API_LAVF_SHORTEST
si->shortest_end = AV_NOPTS_VALUE;
#endif
return s;
}
1.2 格式猜测(av_guess_format)
该函数用于猜测一个format,类型为AVOutputFormat,
const AVOutputFormat *av_guess_format(const char *short_name, const char *filename,
const char *mime_type)
{
const AVOutputFormat *fmt = NULL;
const AVOutputFormat *fmt_found = NULL;
void *i = 0;
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. */
// 寻找合适的文件类型
score_max = 0;
// 遍历每个可用的fmt
while ((fmt = av_muxer_iterate(&i))) {
score = 0;
// 如果当前封装格式匹配,则置信度增加100
if (fmt->name && short_name && av_match_name(short_name, fmt->name))
score += 100;
// 如果fmt的mime类型与mime_type匹配,则置信度增加10
if (fmt->mime_type && mime_type && !strcmp(fmt->mime_type, mime_type))
score += 10;
if (filename && fmt->extensions &&
av_match_ext(filename, fmt->extensions)) {
// 如果扩展名匹配,则置信度增加5
score += 5;
}
if (score > score_max) {
score_max = score;
fmt_found = fmt;
}
}
return fmt_found;
}
其中,fmt中name、mime_type和extension的定义如下(以FLV格式为例)。在新版本FFmpeg中,FLV级别封装的格式不再是AVOutputFormat而是FFOutputFormat,但是FFOutputFormat之中的信息可以转换成AVOutputFormat,下面的.p就是AVOutputFormat的指针。对于FLV格式而言,name=“flv”,mime_type=“video/x-flv”,extensions=“flv”
const FFOutputFormat ff_flv_muxer = {
.p.name = "flv",
.p.long_name = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),
.p.mime_type = "video/x-flv",
.p.extensions = "flv",
.priv_data_size = sizeof(FLVContext),
.p.audio_codec = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_ADPCM_SWF,
.p.video_codec = AV_CODEC_ID_FLV1,
.init = flv_init,
.write_header = flv_write_header,
.write_packet = flv_write_packet,
.write_trailer = flv_write_trailer,
.deinit = flv_deinit,
.check_bitstream= flv_check_bitstream,
.p.codec_tag = (const AVCodecTag* const []) {
flv_video_codec_ids, flv_audio_codec_ids, 0
},
.p.flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS |
AVFMT_TS_NONSTRICT,
.p.priv_class = &flv_muxer_class,
};
1.2.1 遍历可用的fmt(av_muxer_iterate)
const AVOutputFormat *av_muxer_iterate(void **opaque)
{
static const uintptr_t size = sizeof(muxer_list)/sizeof(muxer_list[0]) - 1;
uintptr_t i = (uintptr_t)*opaque;
const FFOutputFormat *f = NULL;
uintptr_t tmp;
// 从muxer的list中获取一个fmt,list当中有180个muxer
if (i < size) {
f = muxer_list[i];
} else if (tmp = atomic_load_explicit(&outdev_list_intptr, memory_order_relaxed)) {
const FFOutputFormat *const *outdev_list = (const FFOutputFormat *const *)tmp;
f = outdev_list[i - size];
}
// 如果找到了fmt,i = i + 1
if (f) {
*opaque = (void*)(i + 1);
return &f->p;
}
return NULL;
}
1.2.2 name匹配检查(av_match_name)
/**
* Match instances of a name in a comma-separated list of names.
* List entries are checked from the start to the end of the names list,
* the first match ends further processing. If an entry prefixed with '-'
* matches, then 0 is returned. The "ALL" list entry is considered to
* match all names.
*
* @param name Name to look for.
* @param names List of names.
* @return 1 on match, 0 otherwise.
*/
// 在逗号分隔的名称列表中匹配名称的实例(字符串匹配)
// 从名字列表的开始到结束检查列表条目,第一个匹配结束进一步处理。
// 如果匹配前缀为'-'的条目,则返回0。“ALL”列表项被认为匹配所有名称
int av_match_name(const char *name, const char *names)
{
const char *p;
size_t len, namelen;
if (!name || !names)
return 0;
namelen = strlen(name);
while (*names) {
int negate = '-' == *names;
p = strchr(names, ','); // 因为有多个,分隔的字号
if (!p)
p = names + strlen(names);
names += negate;
len = FFMAX(p - names, namelen);
if (!av_strncasecmp(name, names, len) || !strncmp("ALL", names, FFMAX(3, p - names)))
return !negate;
names = p + (*p == ',');
}
return 0;
}
1.2.3 扩展匹配检查(av_match_ext)
函数会提取出扩展名,然后调用av_match_name进行扩展名的匹配
/**
* @file
* Format register and lookup
*/
int av_match_ext(const char *filename, const char *extensions)
{
const char *ext;
if (!filename)
return 0;
ext = strrchr(filename, '.');
if (ext)
return av_match_name(ext + 1, extensions);
return 0;
}
2.小结
avformat_alloc_output_context2函数是FFmpeg使用过程中重要的函数,它创建了输出的AVFormatContext,能够用于数据的输出,需要注意的是,新版本的FFmpeg中对上层格式的封装不再使用AVOutputFormat而是使用FFOutputFormat,将AVOutputFormat封装到了FFOutputFormat之中
CSDN : https://blog.csdn.net/weixin_42877471
Github : https://github.com/DoFulangChen