FFmpeg源代码简单分析-编码-avformat_alloc_output_context2()

参考链接

  • FFmpeg源代码简单分析:avformat_alloc_output_context2()_雷霄骅的博客-CSDN博客_avformat_alloc_context

avformat_alloc_output_context2()

  • 在基于FFmpeg的视音频编码器程序中,该函数通常是第一个调用的函数(除了组件注册函数av_register_all())
  • 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, const 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。 
  • avformat_alloc_output_context2()的函数定义。该函数的定义位于libavformat\mux.c中
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;if (!oformat) {if (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 {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);}} elses->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;
}
  • 从代码中可以看出,avformat_alloc_output_context2()的流程如要包含以下2步:
  • 1)  调用avformat_alloc_context()初始化一个默认的AVFormatContext。
  • 2) 如果指定了输入的AVOutputFormat,则直接将输入的AVOutputFormat赋值给AVOutputFormat的oformat。如果没有指定输入的AVOutputFormat,就需要根据文件格式名称或者文件名推测输出的AVOutputFormat。无论是通过文件格式名称还是文件名推测输出格式,都会调用一个函数av_guess_format()。 

函数调用结构图

  • 函数调用结构图如下所示

 

avformat_alloc_context()

  • avformat_alloc_context()的是一个FFmpeg的API,它的定义如下。
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_close = ff_format_io_close_default;s->io_close2= io_close2_default;av_opt_set_defaults(s);si->pkt = av_packet_alloc();si->parse_pkt = av_packet_alloc();if (!si->pkt || !si->parse_pkt) {avformat_free_context(s);return NULL;}si->shortest_end = AV_NOPTS_VALUE;return s;
}
  • 从代码中可以看出,avformat_alloc_context()首先调用av_malloc()为AVFormatContext分配一块内存。
  • 然后调用了一个函数av_opt_set_defaults()用于给AVFormatContext设置默认值。
  • av_opt_set_defaults()的定义如下。 
void av_opt_set_defaults(void *s)
{av_opt_set_defaults2(s, 0, 0);
}void av_opt_set_defaults2(void *s, int mask, int flags)
{const AVOption *opt = NULL;while ((opt = av_opt_next(s, opt))) {void *dst = ((uint8_t*)s) + opt->offset;if ((opt->flags & mask) != flags)continue;if (opt->flags & AV_OPT_FLAG_READONLY)continue;switch (opt->type) {case AV_OPT_TYPE_CONST:/* Nothing to be done here */break;case AV_OPT_TYPE_BOOL:case AV_OPT_TYPE_FLAGS:case AV_OPT_TYPE_INT:case AV_OPT_TYPE_INT64:case AV_OPT_TYPE_UINT64:case AV_OPT_TYPE_DURATION:
#if FF_API_OLD_CHANNEL_LAYOUT
FF_DISABLE_DEPRECATION_WARNINGScase AV_OPT_TYPE_CHANNEL_LAYOUT:
FF_ENABLE_DEPRECATION_WARNINGS
#endifcase AV_OPT_TYPE_PIXEL_FMT:case AV_OPT_TYPE_SAMPLE_FMT:write_number(s, opt, dst, 1, 1, opt->default_val.i64);break;case AV_OPT_TYPE_DOUBLE:case AV_OPT_TYPE_FLOAT: {double val;val = opt->default_val.dbl;write_number(s, opt, dst, val, 1, 1);}break;case AV_OPT_TYPE_RATIONAL: {AVRational val;val = av_d2q(opt->default_val.dbl, INT_MAX);write_number(s, opt, dst, 1, val.den, val.num);}break;case AV_OPT_TYPE_COLOR:set_string_color(s, opt, opt->default_val.str, dst);break;case AV_OPT_TYPE_STRING:set_string(s, opt, opt->default_val.str, dst);break;case AV_OPT_TYPE_IMAGE_SIZE:set_string_image_size(s, opt, opt->default_val.str, dst);break;case AV_OPT_TYPE_VIDEO_RATE:set_string_video_rate(s, opt, opt->default_val.str, dst);break;case AV_OPT_TYPE_BINARY:set_string_binary(s, opt, opt->default_val.str, dst);break;case AV_OPT_TYPE_CHLAYOUT:set_string_channel_layout(s, opt, opt->default_val.str, dst);break;case AV_OPT_TYPE_DICT:set_string_dict(s, opt, opt->default_val.str, dst);break;default:av_log(s, AV_LOG_DEBUG, "AVOption type %d of option %s not implemented yet\n",opt->type, opt->name);}}
}
  • 从代码中可以看出,avformat_alloc_context()首先调用memset()将AVFormatContext的内存置零
  • 然后指定它的AVClass(指定了AVClass之后,该结构体就支持和AVOption相关的功能)
  • 最后调用av_opt_set_defaults()给AVFormatContext的成员变量设置默认值(av_opt_set_defaults()就是和AVOption有关的一个函数,专门用于给指定的结构体设定默认值,此处暂不分析)。 

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*/
const 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()的代码如下所示。
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_MUXERif (!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;while ((fmt = av_muxer_iterate(&i))) {score = 0;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_muxer_iterate()遍历FFmpeg中所有的AVOutputFormat,并逐一计算每个输出格式的score。
  • 具体的计算过程分成如下几步:
    • 1)  如果封装格式名称匹配,score增加100。匹配中使用了函数av_match_name()。
    • 2)  如果mime类型匹配,score增加10。匹配直接使用字符串比较函数strcmp()。
    • 3)  如果文件名称的后缀匹配,score增加5。匹配中使用了函数av_match_ext()。
  • while()循环结束后,得到得分最高的格式,就是最匹配的格式。
  • 下面看一下一个AVOutputFormat的实例,就可以理解“封装格式名称”,“mine类型”,“文件名称后缀”这些概念了。
  • 下面是flv格式的视音频复用器(Muxer)对应的AVOutputFormat格式的变量ff_flv_muxer。
const AVOutputFormat ff_flv_muxer = {.name           = "flv",.long_name      = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),.mime_type      = "video/x-flv",.extensions     = "flv",.priv_data_size = sizeof(FLVContext),.audio_codec    = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_ADPCM_SWF,.video_codec    = AV_CODEC_ID_FLV1,.init           = flv_init,.write_header   = flv_write_header,.write_packet   = flv_write_packet,.write_trailer  = flv_write_trailer,.check_bitstream= flv_check_bitstream,.codec_tag      = (const AVCodecTag* const []) {flv_video_codec_ids, flv_audio_codec_ids, 0},.flags          = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS |AVFMT_TS_NONSTRICT,.priv_class     = &flv_muxer_class,
};

av_muxer_iterate

  • 参数不为NULL的时候用于获得下一个AVOutputFormat
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 AVOutputFormat *f = NULL;uintptr_t tmp;if (i < size) {f = muxer_list[i];} else if (tmp = atomic_load_explicit(&outdev_list_intptr, memory_order_relaxed)) {const AVOutputFormat *const *outdev_list = (const AVOutputFormat *const *)tmp;f = outdev_list[i - size];}if (f)*opaque = (void*)(i + 1);return f;
}

av_match_name()

  • av_match_name()是一个API函数,定义如下所示。
  • av_match_name()用于比较两个格式的名称。简单地说就是比较字符串。
  • 注意该函数的字符串是不区分大小写的:字符都转换为小写进行比较。
int av_match_name(const char *name, const char *names)
{const char *p;int 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;
}
  • 上述函数还有一点需要注意,其中使用了一个while()循环,用于搜索“,”。
  • 这是因为FFmpeg中有些格式是对应多种格式名称的,例如MKV格式的解复用器(Demuxer)的定义如下。
const AVInputFormat ff_matroska_demuxer = {.name           = "matroska,webm",.long_name      = NULL_IF_CONFIG_SMALL("Matroska / WebM"),.extensions     = "mkv,mk3d,mka,mks,webm",.priv_data_size = sizeof(MatroskaDemuxContext),.flags_internal = FF_FMT_INIT_CLEANUP,.read_probe     = matroska_probe,.read_header    = matroska_read_header,.read_packet    = matroska_read_packet,.read_close     = matroska_read_close,.read_seek      = matroska_read_seek,.mime_type      = "audio/webm,audio/x-matroska,video/webm,video/x-matroska"
};
  • 从代码可以看出,ff_matroska_demuxer中的name字段对应“matroska,webm”。
  • av_match_name()函数对于这样的字符串,会把它按照“,”截断成一个个封装格式名称,然后一一进行比较。

av_match_ext()

  • av_match_ext()是一个API函数,声明如下所示。
/*** Return a positive value if the given filename has one of the given* extensions, 0 otherwise.** @param filename   file name to check against the given extensions* @param extensions a comma-separated list of filename extensions*/
int av_match_ext(const char *filename, const char *extensions);
  • 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;
}
  • 经过以上几步之后,av_guess_format()最终可以得到最合适的AVOutputFormat并且返回给avformat_alloc_output_context2()。
  • avformat_alloc_output_context2()接下来将获得的AVOutputFormat赋值给刚刚新建的AVFormatContext,即可完成初始化工作

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/445948.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

FFmpeg源代码简单分析-编码-avformat_write_header()

参考链接 FFmpeg源代码简单分析&#xff1a;avformat_write_header()_雷霄骅的博客-CSDN博客_avformat_write_header avformat_write_header() FFmpeg写文件用到的3个函数&#xff1a;avformat_write_header()&#xff0c;av_write_frame()以及av_write_trailer()其中av_writ…

《深入理解JVM.2nd》笔记(二):Java内存区域与内存溢出异常

文章目录概述运行时数据区域程序计数器Java虚拟机栈本地方法栈Java堆方法区运行时常量池直接内存HotSpot虚拟机对象探秘对象的创建第一步第二步第三步第四步最后一脚对象的内存布局对象头Header第一部分第二部分实例数据Instance对齐填充Padding对象的访问定位句柄直接指针对象…

《深入理解JVM.2nd》笔记(三):垃圾收集器与垃圾回收策略

文章目录概述对象已死吗引用计数算法可达性分析算法再谈引用finalize()&#xff1a;生存还是死亡回收方法区垃圾收集算法标记-清除算法复制算法标记-整理算法分代收集算法HotSpot的算法实现枚举根结点安全点安全区域垃圾收集器SerialParNewParallel ScavengeSerial OldParallel…

FFmpeg源代码简单分析-编码-av_write_frame()

参考链接 FFmpeg源代码简单分析&#xff1a;av_write_frame()_雷霄骅的博客-CSDN博客_av_write_frame av_write_frame() av_write_frame()用于输出一帧视音频数据&#xff0c;它的声明位于libavformat\avformat.h&#xff0c;如下所示。 /*** Write a packet to an output me…

《深入理解JVM.2nd》笔记(四):虚拟机性能监控与故障处理工具

文章目录概述JDK的命令行工具jps&#xff1a;虚拟机进程状况工具jstat&#xff1a;虚拟机统计信息监视工具jinfo&#xff1a;Java配置信息工具jmap&#xff1a;Java内存映像工具jhat&#xff1a;虚拟机堆转储快照分析工具jstack&#xff1a;Java堆栈跟踪工具HSDIS&#xff1a;J…

FFmpeg源代码简单分析-编码-av_write_trailer()

参考链接&#xff1a; FFmpeg源代码简单分析&#xff1a;av_write_trailer()_雷霄骅的博客-CSDN博客_av_malloc av_write_trailer() av_write_trailer()用于输出文件尾&#xff0c;它的声明位于libavformat\avformat.h&#xff0c;如下所示 /*** Write the stream trailer to…

FFmpeg源代码简单分析-其他-日志输出系统(av_log()等)

参考链接 FFmpeg源代码简单分析&#xff1a;日志输出系统&#xff08;av_log()等&#xff09;_雷霄骅的博客-CSDN博客_ffmpeg源码分析 日志输出系统&#xff08;av_log()等&#xff09; 本文分析一下FFmpeg的日志&#xff08;Log&#xff09;输出系统的源代码。日志输出部分的…

FFmpeg源代码简单分析-其他-AVClass和AVoption

参考链接 FFmpeg源代码简单分析&#xff1a;结构体成员管理系统-AVClass_雷霄骅的博客-CSDN博客FFmpeg源代码简单分析&#xff1a;结构体成员管理系统-AVOption_雷霄骅的博客-CSDN博客 概述 AVOption用于在FFmpeg中描述结构体中的成员变量。它最主要的作用可以概括为两个字&a…

FFmpeg源代码简单分析-其他-libswscale的sws_getContext()

参考链接 FFmpeg源代码简单分析&#xff1a;libswscale的sws_getContext()_雷霄骅的博客-CSDN博客 libswscale的sws_getContext() FFmpeg中类库libswsscale用于图像处理&#xff08;缩放&#xff0c;YUV/RGB格式转换&#xff09;libswscale是一个主要用于处理图片像素数据的类…

FFmpeg源代码简单分析-其他-libswscale的sws_scale()

参考链接 FFmpeg源代码简单分析&#xff1a;libswscale的sws_scale()_雷霄骅的博客-CSDN博客_bad dst image pointers libswscale的sws_scale() FFmpeg的图像处理&#xff08;缩放&#xff0c;YUV/RGB格式转换&#xff09;类库libswsscale中的sws_scale()函数。libswscale是一…

FFmpeg源代码简单分析-其他-libavdevice的gdigrab

参考链接 FFmpeg源代码简单分析&#xff1a;libavdevice的gdigrab_雷霄骅的博客-CSDN博客_gdigrab libavdevice的gdigrab GDIGrab用于在Windows下屏幕录像&#xff08;抓屏&#xff09;gdigrab的源代码位于libavdevice\gdigrab.c。关键函数的调用关系图如下图所示。图中绿色背…

Ubuntu安装GmSSL库适用于ubuntu18和ubuntu20版本

参考链接 编译与安装【GmSSL】GmSSL 与 OpenSSL 共存的安装方法_阿卡基YUAN的博客-CSDN博客_openssl和gmssl在Linux下安装GmSSL_百里杨的博客-CSDN博客_安装gmssl ubuntu18操作 需要超级管理员权限本人将下载的安装包master.zip和安装的位置都设定在/usr/local下创建文件夹/u…

Windows7右键菜单栏添加打开cmd项

背景简介 众所周知&#xff0c;在Linux桌面操作系统中的工作目录窗口中&#xff0c;单击鼠标右键&#xff0c;弹出的菜单栏通常有一项“打开终端”&#xff0c;然后移动鼠标点击该项&#xff0c;就可以打开Shell窗口&#xff0c;在当前工作目录进行命令行操作。 但是&#xf…

在ubuntu环境下执行openssl编译和安装

参考链接 工具系列 | Ubuntu18.04安装Openssl-1.1.1_Tinywan的技术博客_51CTO博客密码学专题 openssl编译和安装_MY CUP OF TEA的博客-CSDN博客_openssl 编译安装 下载 /source/index.html编译 使用命令sudo tar -xvzf openssl-1.1.1q.tar.gz 解压。使用cd openssl-1.1.1q/进…

chrome 使用gpu 加速_一招解决 Chrome / Edge 卡顿缓慢 让浏览器重回流畅顺滑

最近一段时间,我发现电脑上的 Chrome 谷歌浏览器越用越卡了。特别是网页打开比较多,同时还有视频播放时,整个浏览器的响应速度都会变得非常缓慢,视频也会卡顿掉帧。 我用的是 iMac / 32GB 内存 / Intel 四核 i7 4Ghz CPU,硬件性能应该足以让 Chrome 流畅打开几十个网页标签…

CLion运行程序时添加命令行参数 即设置argv输入参数

参考链接 CLion运行程序时添加命令行参数_三丰杂货铺的博客-CSDN博客_clion命令行参数 操作流程 Run -> Edit -> Configuration -> Program arguments那里添内容最快捷的方式是&#xff0c;点击锤子编译图标和运行图标之间的的图标&#xff0c;进行Edit Configurati…

openssl实现双向认证教程(服务端代码+客户端代码+证书生成)

参考链接 openssl实现双向认证教程&#xff08;服务端代码客户端代码证书生成&#xff09;_huang714的博客-CSDN博客_ssl_ctx_load_verify_locations基于openssl实现https双向身份认证及安全通信_tutu-hu的博客-CSDN博客_基于openssl实现 注意事项 openssl版本差异很可能导致程…

基于openssl和国密算法生成CA、服务器和客户端证书

参考链接 国密自签名证书生成_三雷科技的博客-CSDN博客_国密证书生成openssl采用sm2进行自签名的方法_dong_beijing的博客-CSDN博客_openssl sm 前提说明 OpenSSL 1.1.1q 5 Jul 2022 已经实现了国密算法查看是否支持SM2算法openssl ecparam -list_curves | grep -i sm2参考…

基于Gmssl库静态编译,实现服务端和客户端之间的SSL通信

前情提要 将gmssl库采取静态编译的方式&#xff0c;存储在/usr/local/gmssl路径下&#xff0c;核心文件涵盖 include、lib和bin等Ubuntu安装GmSSL库适用于ubuntu18和ubuntu20版本_MY CUP OF TEA的博客-CSDN博客 代码 server #include <stdio.h> #include <stdlib.h&g…

基于SM2证书实现SSL通信

参考链接 ​​​​​基于openssl和国密算法生成CA、服务器和客户端证书_MY CUP OF TEA的博客-CSDN博客基于上述链接&#xff0c;使用国密算法生成CA、服务器和客户端证书&#xff0c;并实现签名认证openssl实现双向认证教程&#xff08;服务端代码客户端代码证书生成&#xff…