FFMPEG - ffplay源代码分析

FFmpeg是一个开源,免费,跨平台的视频和音频流方案,它提供了一套完整的录制、转换以及流化音视频的解决方案。而ffplay是有ffmpeg官方提供的一个基于ffmpeg的简单播放器。学习ffplay对于播放器流程、ffmpeg的调用等等是一个非常好的例子。

1.例子

这里的说明使用如下的例子:

./ffplay avm.mp4

2. read_thread()

线程read_thread负责demux,它的流程如下图:

  • avformat_alloc_context分配AVFormatContext。这是demux的上下文;
  • avformat_open_input()解析文件,确定文件的封装格式(即mux类型);
  • aformat_find_stream_info()继续解析AVFormatContext中包含的stream,根据stream类型确定其decoder,并创建AVCodecContext,这是decode的上下文;
  • 如果指定了播放位置,avformat_seek_file()将播放位置移动到指定位置;
  • av_find_best_stream()查找指定stream类型的最佳质量的stream。
  • stream_component_open()创建新的线程video_thread。Video_thread负责decode。
  • 最后,read_thread在循环中,调用av_read_frame()读packet,并调用packet_queue_put(),放入queue中,供video_thread读取。

2.1 avformat_alloc_context()

avformat_alloc_context调用avformat_get_context_defaults()给AVFormatContext设置缺省参数,其中包括AVFormtContext::io_open()和AVFormatContext::io_close()。

2.2 avformat_open_input() - input_input() - io_open_default()

avformat_open_input()调用av_probe_input_format2()确定文件的封装格式,这点后面再提。这里先看io_open_default()如何打开要播放的链接。有两个层次的Context:AVIOContext和URLContext。ffio_xxx()负责IO层,ffurl_xxx()负责URL层。

  • url_find_protocol()首先提取播放链接的前缀。如http://.../a.mp4,前缀就是http。这里只有文件名avm.mp4,没有前缀,则默认为“file”,所以avm.mp4与file:avm.mp4是等同的。
  • ffurl_get_protocols得到已注册的URL protocol列表。url_find_protocol()根据前缀在这个列表中查找。前缀”file”对应的是ff_file_protocol。
  • ffurl_alloc()调用url_alloc_for_protocol()分配相应的URLContext。URLContext保存真正的文件句柄fd。
  • ffurl_connect()调用file_open()打开文件,调用file_seek()将开始位置调整到0。
  • ffio_fdopen()调用avio_alloc_context()分配IOContext,然后初始化它。

2.3 avformat_open_input() - input_input() - av_probe_input_buffer2()

这里的重要函数是av_probe_input_format3()。它根据文件名及文件内容确定文件封装格式。它有两种工作模式,根据参数is_opened值决定,is_opened表示文件是否已经打开。

AVInputFormat *av_probe_input_format3 (AVProbeData *pd, int is_opened,  int* score_ret);

如果is_opened为false,则av_probe_input_format3()只根据文件名查找AVInputFormat,否则,av_probe_input_format3()调用AVInputFormat::read_probe(),根据文件内容的头部判断。对于mp4,其read_probe()是mov_probe()。
av_probe_input_format3()的第一个参数pd保存了文件名和文件头部的内容。
avInputFormat可能有多种选择,它们的优先级不同,用score表示。av_probe_input_format3()返回优先级最高的,优先级保存在第3个参数score_ret中。

init_input()第一次调用av_probe_input_format2()。这时文件没打开,根据文件名字找查找封装格式,没找到。av_probe_input_buffer2()先调用avio_read()读文件内容头部分,再第二次调用av_probe_input_format2(),这次mov_probe()返回的AVInputFormat是ff_mov_demuxer。

avio_read()调用IO层的io_read_packet(),和URL层的file_read()读取指定大小的文件内容。

如下是以上过程涉及的类:

2.4 avformat_open_input() - AVInputFormat::read_header()

avformat_open_input()先调用avio_skip()跳过指定字节,这里是0字节。接着调用AVInputFormat::reader_header()分析文件头部。这里调用的是mov_read_header(),它会创建MOV层的上下文MovContext。

2.5 avformat_open_input() - avformat_find_stream_info()

avformat_find_stream_info()调用av_parser_init(),为每个Stream找到AVCodecParserContext,这是用来从连续的stream数据中分割frame的。
avcodec_parameters_to_context()将Codec上下文从内部使用的结构AVCodecParameters,复制到作为对外接口的AVCodecContext中。
find_probe_decoder()根据codec_id查找解码器AVCodec,这里调用avcodec_find_decoder_by_name(),根据名字在已注册的Codec列表中找到ff_h264_decoder。
avcodec_open2()调用AVCodec::init()初始Codec的上下文。这里是h264_decode_init()创建H264Context并初始化。

以上过程涉及到的类如下:

2.6 avformat_open_input() - avformat_seek_file()

avformat_seek_file()调整播放位置。av_rescale()将对外使用的时间单位转换成内部使用的。位置调整之后,之前的frame就不需要了。ff_read_frame_flush()用于清除之前的frame。最后调用mov_read_seek()调整位置。

2.7 avformat_open_input() - av_find_best_stream()

可能包括多个同类型的stream,av_find_best_stream()为每个类型选出最好的一个stream。

2.8 avformat_open_input() - stream_component_open()

stream_component_open()首先创建avcodec_alloc_context3()创建AVCodecContext并初始化,调用avcodec_parameters_to_context()复制内部参数,调用avcodec_find_decoder()找到decoder,调用avcodec_open2()初始化AVCodecContext。

最后是应用层面的数据结构VideoState,用decoder_init()初始化。在decoder_start()中,用packet_queue_start()启动packet
queue,用SDL_CreateThread()启动新线程video_thread。

2.9 avformat_open_input() - av_read_frame()

read_thread在一个循环中调用av_read_frame()读packet,调用packet_queue_put()写入packet queue。
av_read_frame调用read_frame_internal(),最终调用mov_read_packet()读取packet。它可能将packet返回,也可能暂时保存在AVFormatContext::AVFormatInternal::packet_buffer中。在后一种情况下,下一次调用av_read_frame()会返回暂存的packet。

涉及到的类如下图:

3.video_thread()

线程video_thread负责decode,它在循环中调用get_video_frame()从packet queue中读packet,decode为frame,然后调用queue_picture()将frame推入frame queue。流程如下图:

3.1 get_video_frame()

decoder_decoder_frame()负责decode frame。packet_queue_get()从packet queue中取出packet。
在avcodec_send_packet()中,实际负责的是decode_receive_frame_internal(),它最终调用h264_decode_frame()。decode后的frame可能返回,也可能暂存在AVCodecInternal::buffer_frame中。在后一种情况下,下一次调用decode_receive_frame_internal()会直接返回暂存的frame。

3.2 queue_picture()

queue_picture()将decode得到的frame写入frame queue。frame_queue_peek_writable()得到可写的位置,frame_queue_push()将写位置推前一步。

涉及的类如下图:

4. main thread / display thread

main thread首先做初始化工作,然后调用event_loop()进入display阶段。

初始化工作包括:

  • av_register_all()注册ffmpeg的各种库,如demux,decode等。
  • parse_options()解析命令行
  • SDL_Init()初始化SDL,SDL_CreateWindow()创建SDL窗口,SDL_CreateRenderer()创建SDL渲染对象。SDL_GetRenderInfo()得到渲染对象的信息。
  • stream_open()调用frame_queue_init()初始化packet
    queue,调用frame_queue_init()初始化frame
    queue,调用init_clock()初始化同步时钟。最后调用SDL_CreateThread()创建和启动新的线程read_thread。

4.1 event_loop()

在refresh_loop_wait_event()中,再循环中最多0.01秒调用一次video_refresh()。Video_refresh()读取frame并刷新显示。
frame_queue_nb_remaining()得到frame_queue中的frame数目;
frame_queue_peek_last()和frame_queue_peek()分别得到frame queue中的最后两个frame。Frame_queue_next()将读位置推进一步。
video_display()显示frame。
调用SDL_PumpEvents()和SDL_PeepEvents()得到SDL
event。这是SDL的要求,不然SDL会以为用户无动作,所以将SDL窗口变暗。SDL
Event返回给refresh_loop_wait_event(),在这里event,如用户输入,会得到处理,如调整播放位置,改变音量等等。

4.2 event_loop() - video_display()

video_display()几乎只与SDL有关。

    • video_open()设置SDL窗口。
    • SDL_SetRenderDrawColor()和SDL_SetRenderClear()清理渲染器的背景。
    • video_image_display()调用frame_queue_peek_last()从frame
      queue中得到最后一个frame,调用calculate_display_rect()计算显示范围,调用upload_texture()创建纹理并将frame中的YUV分量,渲染到纹理中,调用SDL_RenderCopyEx()将纹理复制到渲染器。
    • SDL_RenderPresent()显示渲染器中的内容。

原文地址:https://www.cnblogs.com/schips/p/11525418.html

时间: 2025-02-01 06:54:46

FFMPEG - ffplay源代码分析的相关文章

FFplay源代码分析:整体流程图

转自:雷博 http://blog.csdn.net/leixiaohua1020/article/details/11980843

零基础读懂视频播放器控制原理: ffplay 播放器源代码分析

https://www.qcloud.com/community/article/535574001486630869 视频播放器原理其实大抵相同,都是对音视频帧序列的控制.只是一些播放器在音视频同步上可能做了更为复杂的帧预测技术,来保证音频和视频有更好的同步性. ffplay 是 FFMpeg 自带的播放器,使用了 ffmpeg 解码库和用于视频渲染显示的 sdl 库,也是业界播放器最初参考的设计标准.本文对 ffplay 源码进行分析,试图用更基础而系统的方法,来尝试解开播放器的音视频同步,

FFmpeg源代码分析:结构体成员管理系统-AVOption

本文继续上篇文章<FFmpeg源代码分析:结构体成员管理系统-AVClass>中的内容,记录FFmpeg中和AVOption相关的源代码.AVOption用于在FFmpeg中描述结构体中的成员变量.一个AVOption可以包含名称,简短的帮助信息,取值等等. 上篇文章简单回顾 上篇文章中概括了AVClass,AVOption和目标结构体之间的关系.以AVFormatContext为例,可以表示为下图. 有关上篇文章的内容,这里不再重复.总体来说,上篇文章主要概括了AVClass,AVOptio

FFmpeg源代码分析:libswscale的sws_scale()

本文继续上一篇文章<FFmpeg源代码分析:sws_getContext()>的内容,简单分析FFmpeg的图像处理(缩放,YUV/RGB格式转换)类库libswsscale中的sws_scale()函数.libswscale是一个主要用于处理图片像素数据的类库.可以完成图片像素格式的转换,图片的拉伸等工作.有关libswscale的使用可以参考文章:<最简单的基于FFmpeg的libswscale的示例(YUV转RGB)> 该类库常用的函数数量很少,一般情况下就3个: sws_g

FFmpeg源代码分析:日志输出系统(av_log()等)

本文我们看一下FFmpeg的日志(Log)输出系统的源代码.日志输出部分的核心函数只有一个:av_log().使用av_log()在控制台输出日志的效果如下图所示. 函数调用结构图 FFmpeg日志输出系统的函数调用结构图如图所示. av_log() av_log()是FFmpeg中输出日志的函数.随便打开一个FFmpeg的源代码文件,就会发现其中遍布着av_log()函数.一般情况下FFmpeg类库的源代码中是不允许使用printf()这种的函数的,所有的输出一律使用av_log().av_l

FFmpeg源代码分析:sws_getContext()

打算写两篇文章记录FFmpeg中的图像处理(缩放,YUV/RGB格式转换)类库libswsscale的源代码.libswscale是一个主要用于处理图片像素数据的类库.可以完成图片像素格式的转换,图片的拉伸等工作.有关libswscale的使用可以参考文章: <最简单的基于FFmpeg的libswscale的示例(YUV转RGB)> libswscale常用的函数数量很少,一般情况下就3个: sws_getContext():初始化一个SwsContext. sws_scale():处理图像数

ffmpeg/ffplay vc6 源码剖析

ffmpeg/ffplay是当今多媒体领域的王者,很多很多的人想研究学习ffmpeg/ffplay,但苦于ffmpeg/ffplay庞大的代码量,令人望而生畏.为帮助更多的人研习ffmpeg/ffplay,在保持ffmpeg/ffplay体系架构的完整性的前提下,把ffmpeg/ffplay大规模的瘦身后,研习门槛一下子降低了n多个数量级.附件一个是对瘦身后的ffmpeg/ffplay的代码完整的剖析pdf文档,另一个是瘦身后的ffmpeg/ffplay的完整源代码,最大化帮助各位网友研究学习f

转:SDL2源代码分析

1:初始化(SDL_Init()) SDL简介 有关SDL的简介在<最简单的视音频播放示例7:SDL2播放RGB/YUV>以及<最简单的视音频播放示例9:SDL2播放PCM>中已经叙述过了,不再重复.这两篇文章中也提到了一张SDL的原理图,如下所示: 从这个图中可以看出,SDL根据系统的不同调用不同的API完成相应的功能.至于它是如何实现的,将会在后文中详细叙述.下面不再罗嗦,直接进入正题. 使用SDL播放一个视频代码流程大体如下 初始化: SDL_Init(): 初始化SDL. 

转:ffdshow 源代码分析

ffdshow神奇的功能:视频播放时显示运动矢量和QP FFDShow可以称得上是全能的解码.编码器.最初FFDShow只是mpeg视频解码器,不过现在他能做到的远不止于此.它能够解码的视频格式已经远远超出了mpeg4的范围,包括indeo video,WMV,mpeg2等等.同时,它也提供了丰富的加工处理选项,可以锐化画面,调节画面的亮度等等.不止是视频,FFDShow现在同样可以解码音频,AC3.MP3等音频格式都可支持.并且可以外挂winamp 的DSP插件,来改善听觉效果.可以说现在的F