`
superonion
  • 浏览: 128265 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

FFmpeg媒体打开过程分析

阅读更多

从打开文件开始,入口函数是avformat_open_input(),下面是对此函数的分析:

//参数ps包含一切媒体相关的上下文结构,有它就有了一切,本函数如果打开媒体成功,
//会返回一个AVFormatContext的实例.
//参数filename是媒体文件名或URL.
//参数fmt是要打开的媒体格式的操作结构,因为是读,所以是inputFormat.此处可以
//传入一个调用者定义的inputFormat,对应命令行中的 -f xxx段,如果指定了它,
//在打开文件中就不会探测文件的实际格式了,以它为准了.
//参数options是对某种格式的一些操作,是为了在命令行中可以对不同的格式传入
//特殊的操作参数而建的, 为了了解流程,完全可以无视它.
int avformat_open_input(AVFormatContext **ps,
		const char *filename,
		AVInputFormat *fmt,
		AVDictionary **options)
{
	AVFormatContext *s = *ps;
	int ret = 0;
	AVFormatParameters ap = { { 0 } };
	AVDictionary *tmp = NULL;

	//创建上下文结构
	if (!s && !(s = avformat_alloc_context()))
		return AVERROR(ENOMEM);
	//如果用户指定了输入格式,直接使用它
	if (fmt)
		s->iformat = fmt;

	//忽略
	if (options)
		av_dict_copy(&tmp, *options, 0);

	if ((ret = av_opt_set_dict(s, &tmp)) < 0)
		goto fail;

	//打开输入媒体(如果需要的话),初始化所有与媒体读写有关的结构们,比如
	//AVIOContext,AVInputFormat等等
	if ((ret = init_input(s, filename)) < 0)
		goto fail;
	//执行完此函数后,s->pb和s->iformat都已经指向了有效实例.pb是用于读写数据的,它
	//把媒体数据当做流来读写,不管是什么媒体格式,而iformat把pb读出来的流按某种媒体格
	//式进行分析,也就是说pb在底层,iformat在上层.

	//很多静态图像文件格式,都被当作一个格式处理,比如要打开.jpeg文件,需要的格式
	//名为image2.此处还不是很了解具体细节,作不得准哦.
	/* check filename in case an image number is expected */
	if (s->iformat->flags & AVFMT_NEEDNUMBER) {
		if (!av_filename_number_test(filename)) {
			ret = AVERROR(EINVAL);
			goto fail;
		}
	}

	s->duration = s->start_time = AV_NOPTS_VALUE;
	//上下文中保存下文件名
	av_strlcpy(s->filename, filename, sizeof(s->filename));

	/* allocate private data */
	//为当前格式分配私有数据,主要用于某格式的读写操作时所用的私有结构.
	//此结构的大小在定义AVInputFormat时已指定了.
	if (s->iformat->priv_data_size > 0) {
		if (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) {
			ret = AVERROR(ENOMEM);
			goto fail;
		}
		//这个可以先不必管它
		if (s->iformat->priv_class) {
			*(const AVClass**) s->priv_data = s->iformat->priv_class;
			av_opt_set_defaults(s->priv_data);
			if ((ret = av_opt_set_dict(s->priv_data, &tmp)) < 0)
				goto fail;
		}
	}

	/* e.g. AVFMT_NOFILE formats will not have a AVIOContext */
	//从mp3文件中读ID3数据并保存之.
	if (s->pb)
		ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC);

	//读一下媒体的头部,在read_header()中主要是做某种格式的初始化工作,比如填充自己的
	//私有结构,根据流的数量分配流结构并初始化,把文件指针指向数据区开始处等.
	if (!(s->flags & AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header)
		if ((ret = s->iformat->read_header(s, &ap)) < 0)
			goto fail;

	//保存数据区开始的位置
	if (!(s->flags & AVFMT_FLAG_PRIV_OPT) && s->pb && !s->data_offset)
		s->data_offset = avio_tell(s->pb);

	s->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;

	if (options) {
		av_dict_free(options);
		*options = tmp;
	}
	*ps = s;
	//执行成功
	return 0;

	//执行失败
	fail: av_dict_free(&tmp);
	if (s->pb && !(s->flags & AVFMT_FLAG_CUSTOM_IO))
		avio_close(s->pb);
	avformat_free_context(s);
	*ps = NULL;
	return ret;
}

 

下面分析init_input():

//打开输入媒体并填充其AVInputFormat结构
static int init_input(AVFormatContext *s, const char *filename)
{
	int ret;
	AVProbeData pd = { filename, NULL, 0 };

	//当调用者已指定了pb(数据取得的方式)--一般不会这样.
	if (s->pb) {
		s->flags |= AVFMT_FLAG_CUSTOM_IO;
		if (!s->iformat)
			//如果已指定了pb但没指定iformat,以pb读取媒体数据进行探测,取得.取得iformat.
			return av_probe_input_buffer(s->pb, &s->iformat, filename, s, 0, 0);
		else if (s->iformat->flags & AVFMT_NOFILE)
			//如果已指定pb也指定了iformat,但是又指定了不需要文件(也包括URL指定的地址),这就矛盾了,
			//此时应是不需要pb的,因为不需操作文件,提示一下吧,也不算错.
			av_log(s, AV_LOG_WARNING, "Custom AVIOContext makes no sense and "
					"will be ignored with AVFMT_NOFILE format.\n");
		return 0;
	}

	//一般会执行到这里
	if ((s->iformat && s->iformat->flags & AVFMT_NOFILE)
			|| (!s->iformat && (s->iformat = av_probe_input_format(&pd, 0))))
		//如果已指定了iformat并且不需要文件,也就不需要pb了,可以直接返回
		//如果没指定iformat,但是可以从文件名中猜出iformat,也成功.
		return 0;

	//如果从文件名中也猜不出媒体格式,则只能打开这个文件进行探测了,先打开文件
	if ((ret = avio_open(&s->pb, filename, AVIO_FLAG_READ)) < 0)
		return ret;
	if (s->iformat)
		return 0;
	//再探测之
	return av_probe_input_buffer(s->pb, &s->iformat, filename, s, 0, 0);
}
 

再看一下文件打开过程:

//打开一个地址指向的媒体
int avio_open(AVIOContext **s, const char *filename, int flags)
{
	//URLContext代表一个URL地址指向的媒体文件,本地路径也算一种.它封装了
	//操作一个媒体文件的相关数据,最重要的是prot变量,是URLProtocol型的.
	//prot代表一个特定的协义和协议操作函数们,URLContext包含不同的prot,
	//就可以通过URLContext使用不同的协议读写媒体数据,比如tcp,http,本地
	//文件用file协议.
    URLContext *h;
    int err;

    //创建并初始化URLContext,其prot通过文件名确定.然后打开这个媒体文件
    err = ffurl_open(&h, filename, flags);
    if (err < 0)
        return err;
    //其实文件已经在上边真正打开了.这里只是填充AVIOContext.使它记录下
    //URLContext,以及填充读写数据的函数指针.
    err = ffio_fdopen(s, h);
    if (err < 0) {
        ffurl_close(h);
        return err;
    }
    return 0;
}
 

下面是探测函数:

int av_probe_input_buffer(AVIOContext *pb,
		AVInputFormat **fmt,
		const char *filename,
		void *logctx,
		unsigned int offset,
		unsigned int max_probe_size)
{
	AVProbeData pd = { filename ? filename : "", NULL, -offset };
	unsigned char *buf = NULL;
	int ret = 0, probe_size;

	//计算最多探测数据的字节数
	if (!max_probe_size) {
		max_probe_size = PROBE_BUF_MAX;
	} else if (max_probe_size > PROBE_BUF_MAX) {
		max_probe_size = PROBE_BUF_MAX;
	} else if (max_probe_size < PROBE_BUF_MIN) {
		return AVERROR(EINVAL);
	}

	if (offset >= max_probe_size) {
		return AVERROR(EINVAL);
	}

	//循环直到探测完指定的数据
	for (probe_size = PROBE_BUF_MIN;
			probe_size <= max_probe_size && !*fmt;
			probe_size =
					FFMIN(probe_size<<1, FFMAX(max_probe_size, probe_size+1))) {
		int score = probe_size < max_probe_size ? AVPROBE_SCORE_MAX / 4 : 0;
		int buf_offset = (probe_size == PROBE_BUF_MIN) ? 0 : probe_size >> 1;
		void *buftmp;

		if (probe_size < offset) {
			continue;
		}

		/* read probe data */
		//分配读取数据存放的缓冲
		buftmp = av_realloc(buf, probe_size + AVPROBE_PADDING_SIZE);
		if (!buftmp) {
			av_free(buf);
			return AVERROR(ENOMEM);
		}
		buf = buftmp;
		//利用pb读数据到缓冲的剩余空间中
		if ((ret = avio_read(pb, buf + buf_offset, probe_size - buf_offset))
				< 0) {
			/* fail if error was not end of file, otherwise, lower score */
			if (ret != AVERROR_EOF) {
				av_free(buf);
				return ret;
			}
			score = 0;
			ret = 0; /* error was end of file, nothing read */
		}
		pd.buf_size += ret;
		pd.buf = &buf[offset];

		//缓冲中没有数据的部分要清0
		memset(pd.buf + pd.buf_size, 0, AVPROBE_PADDING_SIZE);

		/* guess file format */
		//从一个打开的文件只探测媒体格式
		*fmt = av_probe_input_format2(&pd, 1, &score);
		if (*fmt) {
			if (score <= AVPROBE_SCORE_MAX / 4) { //this can only be true in the last iteration
				av_log(
						logctx,
						AV_LOG_WARNING,
						"Format %s detected only with low score of %d, misdetection possible!\n",
						(*fmt)->name, score);
			} else
				av_log(logctx, AV_LOG_DEBUG,
						"Format %s probed with size=%d and score=%d\n",
						(*fmt)->name, probe_size, score);
		}
		//不成功,继续
	}

	if (!*fmt) {
		av_free(buf);
		return AVERROR_INVALIDDATA;
	}

	/* rewind. reuse probe buffer to avoid seeking */
	//把探测时读入的数据保存到pb中,为的是真正读时直接利用之.
	if ((ret = ffio_rewind_with_probe_data(pb, buf, pd.buf_size)) < 0)
		av_free(buf);

	return ret;
}
 

 

 

1
1
分享到:
评论

相关推荐

    【原创】ffmpeg解码端代码分析-分析到宏块层

    ### FFMpeg解码端代码分析—宏块层面解析 #### 概述 本文将针对FFMpeg解码端的代码进行深入分析,重点聚焦于解码框架、关键流程及重要函数等方面,直至宏块层级的理解。对于视频解码领域的初学者而言,这将是一篇...

    FFmpeg解码端代码及语义分析.rar

    本压缩包文件“FFmpeg解码端代码及语义分析.rar”主要涵盖了FFmpeg在解码端的核心原理和代码分析,对于理解FFmpeg的工作机制以及如何进行音视频解码具有重要的参考价值。 首先,我们要理解FFmpeg解码的基本流程。...

    ffmpeg-4.1-1.4.4_ffmpeg+rtsp_ffmpeg4_ffmpeg-1.4.4_ffmpeg_Libvide

    - **数据分析**:通过FFmpeg抓取RTSP流并进行分析,如人脸识别、行为识别等。 FFmpeg提供了丰富的命令行工具和API,开发者可以根据需求进行定制化开发。使用FFmpeg时,需要了解如何配置参数,以正确处理不同的编码...

    C++ 视频录制播放 FFMPEG_Player

    FFMPEG_Player是一款基于C++实现的视频录制与播放软件,它利用了FFmpeg库的强大功能。...通过分析和学习这个项目,你可以了解到如何使用FFmpeg库进行视频处理,并且掌握C++开发多媒体应用的基本流程。

    ffmpeg5.1.2-windows

    此外,熟悉FFmpeg的命令行选项和参数配置至关重要,这将帮助你更精确地控制转换过程,如比特率控制、质量设置、裁剪、缩放、旋转等。 总的来说,“ffmpeg5.1.2-windows”提供了一个全面的多媒体解决方案,无论是...

    ffmpeg编译后成品

    - `ffplay.exe`:这是FFmpeg自带的一个简单播放器,它可以打开各种视频和音频文件,并利用FFmpeg库进行解码和播放。用户可以通过命令行参数调整播放设置。 - `ffmpeg.exe`:这是FFmpeg的主要命令行工具,可以执行...

    ffmpeg入门学习1

    例如,使用`avformat_open_input`打开一个媒体文件,`avformat_find_stream_info`获取流信息,`avcodec_decode_video2`进行解码,然后用SDL的函数将解码后的像素数据渲染到屏幕上。 6. **滤镜和转码**:FFmpeg支持...

    FFMPEG/FFPLAY源码剖析(作者 杨书良)完整清晰pdf

    7. **FFplay源码分析**:重点分析FFplay播放器的代码,包括如何初始化、打开文件、播放控制、音视频同步等关键部分。 8. **过滤器系统**:介绍FFmpeg强大的过滤器系统,如何利用过滤器进行音视频处理,如裁剪、缩放...

    ffmpeg 安装编译解码

    文件提到了对FFmpeg源码的分析,这包括对源码中一些关键函数的定义和分析,如AV_REGISTER_ALL()、AVFORMAT_NETWORK_INIT()、AVFORMAT_OPEN_INPUT()等。这些函数是FFmpeg处理多媒体数据时的核心功能。 1. 数据结构:...

    易语言ffmpeg进度转码源码

    ffmpeg是一个强大的跨平台多媒体处理工具,它支持多种编码格式,能进行音视频的解码、编码、转换、流媒体处理等操作。在易语言环境中,通过调用ffmpeg的动态链接库(DLL),我们可以实现这些功能。 首先,了解...

    ffmpeg-4.1.3_Win源码库 + FFMPEG/FFPLAY 源码剖析PDF学习文档

    5. **FFPLAY 分析**:FFplay 的工作流程,包括如何打开媒体文件、解析流、解码数据、同步音频和视频,以及如何使用 SDL 在屏幕上显示。 6. **命令行工具**:FFmpeg 提供的一系列命令行工具,如 ffmpeg、ffprobe 和 ...

    FFMPEG的完整教程pdf

    FFplay是FFmpeg项目中的一个简易播放器示例,用于展示如何使用FFmpeg库来播放媒体文件。本教程将深入剖析FFmpeg及其相关组件的源码,以帮助开发者更好地理解和使用这个强大的多媒体处理工具。 ### 第一部分:FFMPEG...

    ffmpeg 音频数据采集

    FFmpeg 是一个强大的开源工具套件,用于处理音视频数据,包括编码、解码、转换、流媒体等任务。在音视频编程中,FFmpeg 提供了丰富的 API 和库,使得开发者可以方便地进行音频数据的采集。在这个“ffmpeg 音频数据...

    FFMpeg对mpeg2ts流解码的流程分析

    "FFMpeg对mpeg2ts流解码的流程分析" ...FFMpeg中mpeg2-ts流的解码流程是一个复杂的过程,需要了解mpeg2-ts流的格式和FFMpeg的解码机制。通过本文档的学习,读者可以快速掌握相关知识,并应用于实际项目中。

    opencv_ffmpeg.dll

    OpenCV(Open Source Computer Vision Library) 是一个开源的计算机视觉库,它包含了众多图像处理和计算机视觉的算法,广泛应用于图像分析、机器学习等领域。在Windows操作系统上进行OpenCV的开发,经常需要与FFmpeg...

    ffmpeg vs2013可调试版.rar

    6. **调试技巧**:学会使用GDB或Visual Studio的调试工具,分析FFmpeg的运行过程。 通过深入学习这些知识点,并结合提供的可调试版本,你将能够更好地利用FFmpeg开发出高效、稳定的多媒体应用。

    opencv_ffmpeg_64和opencv_ffmpeg.rar

    FFmpeg是一个开源项目,提供了一套完整的工具集,包括解码器、编码器、转换器和流媒体服务器等,用于处理音频和视频文件。在Windows系统上,`opencv_ffmpeg_64.dll`是64位版本的库文件,而`opencv_ffmpeg.dll`是32位...

    ffmpeg2.2.1

    这个过程中涉及的关键步骤可能包括打开输入文件、初始化编码器和解码器、处理数据包、以及写入输出文件。 在学习FFmpeg时,开发者会接触到许多概念和技术,如: 1. 视频编码:理解如何通过压缩算法减少视频数据的...

    ffmpeg探测网络流格式方法源码

    FFmpeg 是一个强大的开源多媒体处理框架,用于处理音频和视频数据。在FFmpeg中,探测网络流格式是一项关键任务,特别是...这个过程对于实时音视频传输和处理非常重要,确保了FFmpeg能够适应各种不同的网络和媒体环境。

    ffmpeg-win32

    FFmpeg 是一个开源的多媒体处理框架,广泛应用于音频和视频的编码、解码、转换以及流媒体处理。在Windows环境下,FFmpeg 提供了适用于开发的包,包含了运行时所需的动态链接库(dll)、静态库(lib)以及头文件(.h...

Global site tag (gtag.js) - Google Analytics