FFmpeg源代码简单分析-解码-avformat_close_input()

参考链接

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

avformat_close_input()

  • 本文简单分析FFmpeg的avformat_close_input()函数。
  • 该函数用于关闭一个AVFormatContext,一般情况下是和avformat_open_input()成对使用的。
  • avformat_close_input()的声明位于libavformat\avformat.h,如下所示。
/*** Close an opened input AVFormatContext. Free it and all its contents* and set *s to NULL.*/
void avformat_close_input(AVFormatContext **s);
  • 下面看一下avformat_close_input()的源代码,位于demux.c文件中。 
void avformat_close_input(AVFormatContext **ps)
{AVFormatContext *s;AVIOContext *pb;if (!ps || !*ps)return;s  = *ps;pb = s->pb;if ((s->iformat && strcmp(s->iformat->name, "image2") && s->iformat->flags & AVFMT_NOFILE) ||(s->flags & AVFMT_FLAG_CUSTOM_IO))pb = NULL;if (s->iformat)if (s->iformat->read_close)s->iformat->read_close(s);avformat_free_context(s);*ps = NULL;avio_close(pb);
}

函数调用关系图

  • 函数的调用关系如下图所示

  •  从源代码中可以看出,avformat_close_input()主要做了以下几步工作:
    • (1)调用AVInputFormat的read_close()方法关闭输入流
    • (2)调用avformat_free_context()释放AVFormatContext
    • (3)调用avio_close()关闭并且释放AVIOContext

AVInputFormat-> read_close()

  • AVInputFormat的read_close()是一个函数指针,指向关闭输入流的函数。
  • 不同的AVInputFormat包含有不同的read_close()方法。
  • 例如,FLV格式对应的AVInputFormat的定义如下。
const AVInputFormat ff_flv_demuxer = {.name           = "flv",.long_name      = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),.priv_data_size = sizeof(FLVContext),.read_probe     = flv_probe,.read_header    = flv_read_header,.read_packet    = flv_read_packet,.read_seek      = flv_read_seek,.read_close     = flv_read_close,.extensions     = "flv",.priv_class     = &flv_kux_class,
};
  • 从ff_flv_demuxer的定义中可以看出,read_close()指向的函数是flv_read_close()
  • 我们可以看一下flv_read_close()的定义,如下所示。
  • 从flv_read_close()的定义可以看出,该函数释放了FLVContext中的new_extradata数组中每个元素指向的内存。
static int flv_read_close(AVFormatContext *s)
{int i;FLVContext *flv = s->priv_data;for (i=0; i<FLV_STREAM_TYPE_NB; i++)av_freep(&flv->new_extradata[i]);av_freep(&flv->keyframe_times);av_freep(&flv->keyframe_filepositions);return 0;
}

avformat_free_context()

  • avformat_free_context()是一个FFmpeg的API函数,用于释放一个AVFormatContext。
  • 在这里要注意搞清楚avformat_free_context()和avformat_close_input()之间的区别与联系。
  • 有关avformat_free_context()可以参考文章:FFmpeg源代码简单分析-通用-常见结构体的初始化和销毁(AVFormatContext,AVFrame等)_MY CUP OF TEA的博客-CSDN博客

avio_close()

  • avio_close()是一个FFmpeg的API函数,用于关闭和释放AVIOContext。
  • 它的声明位于libavformat\avio.h,如下所示
/*** Close the resource accessed by the AVIOContext s and free it.* This function can only be used if s was opened by avio_open().** The internal buffer is automatically flushed before closing the* resource.** @return 0 on success, an AVERROR < 0 on error.* @see avio_closep*/
int avio_close(AVIOContext *s);
int avio_close(AVIOContext *s)
{FFIOContext *const ctx = ffiocontext(s);URLContext *h;int ret, error;if (!s)return 0;avio_flush(s);h         = s->opaque;s->opaque = NULL;av_freep(&s->buffer);if (s->write_flag)av_log(s, AV_LOG_VERBOSE,"Statistics: %"PRId64" bytes written, %d seeks, %d writeouts\n",ctx->bytes_written, ctx->seek_count, ctx->writeout_count);elseav_log(s, AV_LOG_VERBOSE, "Statistics: %"PRId64" bytes read, %d seeks\n",ctx->bytes_read, ctx->seek_count);av_opt_free(s);error = s->error;avio_context_free(&s);ret = ffurl_close(h);if (ret < 0)return ret;return error;
}
  • 从源代码可以看出,avio_close()按照顺序做了以下几个步骤:
    • (1)调用avio_flush()强制清除缓存中的数据
    • (2)调用av_freep()释放掉AVIOContext种的buffer
    • (3)调用av_free()释放掉AVIOContext结构体  av_free函数弃用,av_opt_free和avio_context_free
    • (4)调用ffurl_close()关闭并且释放掉URLContext
  • 下面按照顺序分别看看avio_flush()和ffurl_close()这两个函数

avio_flush()

  • avio_flush()是一个FFmpeg的API函数,声明位于libavformat\avio.h,如下所示。
void avio_flush(AVIOContext *s)
{int seekback = s->write_flag ? FFMIN(0, s->buf_ptr - s->buf_ptr_max) : 0;flush_buffer(s);if (seekback)avio_seek(s, seekback, SEEK_CUR);
}
  • 可以看出avio_flush()简单调用了flush_buffer()函数。我们看一下flush_buffer()的定义。
static void flush_buffer(AVIOContext *s)
{s->buf_ptr_max = FFMAX(s->buf_ptr, s->buf_ptr_max);if (s->write_flag && s->buf_ptr_max > s->buffer) {writeout(s, s->buffer, s->buf_ptr_max - s->buffer);if (s->update_checksum) {s->checksum     = s->update_checksum(s->checksum, s->checksum_ptr,s->buf_ptr_max - s->checksum_ptr);s->checksum_ptr = s->buffer;}}s->buf_ptr = s->buf_ptr_max = s->buffer;if (!s->write_flag)s->buf_end = s->buffer;
}
  • 从flush_buffer()定义我们可以看出,该函数将当前缓存指针buf_ptr的位置重新设置到缓存buffer的首部,然后根据AVIOContext对应的流是否可写分别做不同的处理。
  • 如果AVIOContext对应的流是只读的(write_flag取值为0),就将缓存的尾部buf_end设定到缓存首部位置;
  • 如果AVIOContext对应的流如果是可写的(write_flag取值非0),则会调用writeout()函数输出缓存中剩余的数据。
  • 在这里我们看一下writeout()函数的定义,如下所示。
static void writeout(AVIOContext *s, const uint8_t *data, int len)
{FFIOContext *const ctx = ffiocontext(s);if (!s->error) {int ret = 0;if (s->write_data_type)ret = s->write_data_type(s->opaque, (uint8_t *)data,len,ctx->current_type,ctx->last_time);else if (s->write_packet)ret = s->write_packet(s->opaque, (uint8_t *)data, len);if (ret < 0) {s->error = ret;} else {ctx->bytes_written += len;s->bytes_written = ctx->bytes_written;if (s->pos + len > ctx->written_output_size) {ctx->written_output_size = s->pos + len;
#if FF_API_AVIOCONTEXT_WRITTEN
FF_DISABLE_DEPRECATION_WARNINGSs->written = ctx->written_output_size;
FF_ENABLE_DEPRECATION_WARNINGS
#endif}}}if (ctx->current_type == AVIO_DATA_MARKER_SYNC_POINT ||ctx->current_type == AVIO_DATA_MARKER_BOUNDARY_POINT) {ctx->current_type = AVIO_DATA_MARKER_UNKNOWN;}ctx->last_time = AV_NOPTS_VALUE;ctx->writeout_count++;s->pos += len;
}
  • 从定义可以看出,writeout()调用了AVIOContext的write_packet()方法。根据此前文章《FFmpeg源代码简单分析:avio_open2()》中的分析我们可以了解到,AVIOContext的write_packet()实际指向了ffurl_write()函数,而ffurl_write()经过retry_transfer_wrapper()函数最终调用了URLProtocol的url_write()函数。url_write()是一个函数指针,不同的URLProtocol的url_write()指向不同的函数。
  • 例如,file(文件)对应的URLProtocol的定义位于libavformat\file.c,如下所示。
const URLProtocol ff_file_protocol = {.name                = "file",.url_open            = file_open,.url_read            = file_read,.url_write           = file_write,.url_seek            = file_seek,.url_close           = file_close,.url_get_file_handle = file_get_handle,.url_check           = file_check,.url_delete          = file_delete,.url_move            = file_move,.priv_data_size      = sizeof(FileContext),.priv_data_class     = &file_class,.url_open_dir        = file_open_dir,.url_read_dir        = file_read_dir,.url_close_dir       = file_close_dir,.default_whitelist   = "file,crypto,data"
};
  • 可以看出ff_file_protocol中的url_write()指向的是file_write()函数。
  • 我们继续看一下file_write()的源代码,如下所示。
  • 从源代码中可以看出file_write()调用了系统的write()方法向文件中写数据(很多人可能对write()函数很陌生,可以简单理解为它等同于fwrite())。
static int file_write(URLContext *h, const unsigned char *buf, int size)
{FileContext *c = h->priv_data;int ret;size = FFMIN(size, c->blocksize);ret = write(c->fd, buf, size);return (ret == -1) ? AVERROR(errno) : ret;
}

ffurl_close()和ffurl_closep()

  • ffurl_close()和ffurl_closep()是FFmpeg内部的两个函数,它们的声明位于libavformat\url.h,如下所示。
  • 其实这两个函数是等同的
/*** Close the resource accessed by the URLContext h, and free the* memory used by it. Also set the URLContext pointer to NULL.** @return a negative value if an error condition occurred, 0* otherwise*/
int ffurl_closep(URLContext **h);
int ffurl_close(URLContext *h);
  • 可见ffurl_close()调用了ffurl_closep()。
int ffurl_close(URLContext *h)
{return ffurl_closep(&h);
}
  • ffurl_closep()的定义如下所示。
int ffurl_closep(URLContext **hh)
{URLContext *h= *hh;int ret = 0;if (!h)return 0;     /* can happen when ffurl_open fails */if (h->is_connected && h->prot->url_close)ret = h->prot->url_close(h);
#if CONFIG_NETWORKif (h->prot->flags & URL_PROTOCOL_FLAG_NETWORK)ff_network_close();
#endifif (h->prot->priv_data_size) {if (h->prot->priv_data_class)av_opt_free(h->priv_data);av_freep(&h->priv_data);}av_opt_free(h);av_freep(hh);return ret;
}
  • 从ffurl_closep()的定义可以看出,它主要做了两步工作:
    • (1)调用URLProtocol的url_close()
    • (2)调用av_freep()释放URLContext结构体
  • 其中URLProtocol的url_close()是一个函数指针,其指向的函数与具体的URLProtocol有关,这里我们还是看一下file(文件)对应的URLProtocol,如下所示。
const URLProtocol ff_file_protocol = {.name                = "file",.url_open            = file_open,.url_read            = file_read,.url_write           = file_write,.url_seek            = file_seek,.url_close           = file_close,.url_get_file_handle = file_get_handle,.url_check           = file_check,.url_delete          = file_delete,.url_move            = file_move,.priv_data_size      = sizeof(FileContext),.priv_data_class     = &file_class,.url_open_dir        = file_open_dir,.url_read_dir        = file_read_dir,.url_close_dir       = file_close_dir,.default_whitelist   = "file,crypto,data"
};
  • 从ff_file_protocol中可以看出,url_close()指向file_close()函数。我们再看一下file_close()的定义,如下所示。、
static int file_close(URLContext *h)
{FileContext *c = h->priv_data;int ret = close(c->fd);return (ret == -1) ? AVERROR(errno) : 0;
}
  • 可见file_close()最终调用了系统函数close()关闭了文件指针(不熟悉close()的可以简单把它理解为fclose())。
  • 至此avio_close()函数分析完毕。

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

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

相关文章

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

参考链接 FFmpeg源代码简单分析&#xff1a;avformat_alloc_output_context2()_雷霄骅的博客-CSDN博客_avformat_alloc_context avformat_alloc_output_context2() 在基于FFmpeg的视音频编码器程序中&#xff0c;该函数通常是第一个调用的函数&#xff08;除了组件注册函数av…

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…