FFmpeg 是一个C程序的项目,C语言是需要手动管理内存的。内存管理有一个技巧,只要你分清楚这个变量是在栈上,还是在堆上的就可以了。在栈上的变量不需要手动释放,而在堆上的变量需要自己释放。 下面通过一个实例,通过反汇编来讲解一下 栈变量为什么不需要自己释放,代码如下: intadd_something(inta,intb,intc){intd=4;inte=5;intf=6;returna+b+c+d+e+f;}intsub_something(inta,intb,intc){intg=7;inth=8;intj=9;returna+b+cghj;}intmain(...
内存对齐是计算机编程提高性能的一种方式,我们平时用的 struct,malloc() 都会进行内存对齐,如下代码: struct{intx;chary;}s;intmain(){printf("%d\n",sizeof(s));//输出8return0;} 32位系统中,上面的代码中 int 占4字节,char 占1字节,但是结果会输出8,这是因为编译器自动帮我们做了内存对齐。 而 malloc() 函数也会进行内存对齐,32位系统里面,malloc 函...
在开始分析之前,讲一个 makefile 的调试技巧,推荐阅读《如何调试MAKEFILE变量》 make-fMakefile-fvars.mkHOSTPROGS 这里我对 vars.mk 做了点修改,因为源 vars.mk 没处理特殊字符,直接 echo 会报错。ffmpeg 的 makefile 的变量有很多特殊字符。我用了 warning 来输出,就不会报错。vars.mk&n...
read_thread() 线程的主要作用从MP4里面读取 AVPacket,然后丢进去 PacketQueue 队列。所以需要先学习一下 strcutPacketQueue 跟 structMyAVPacketList 数据结构。如下: typedefstructMyAVPacketList{AVPacketpkt;intserial;}MyAVPacketList; typedefstructPacketQueue{AVFifoBufferpkt_list...
stream_component_open() 函数主要作用是打开音频流或者视频流对应的解码器,开启解码线程去解码。 流程图如下: stream_component_open() 的函数定义如下: /openagivenstream.Return0ifOK/staticintstream_component_open(VideoStateis,intstream_index) 可以看到,函数的参数非常简单,第一个参数是 VideoStateis 全局管理器,第二个参数 stream_index&nb...
音频流的滤镜是通过 configure_audio_filters() 函数来创建的,因为 ffplay 为了代码的通用性,即便命令行参数不使用滤镜,AVFrame 也会过一遍空滤镜做下样子。 configure_audio_filters() 函数的流程图如下: configure_audio_filters() 函数的定义如下: staticintconfigure_audio_filters(VideoStateis,constcharafilters,intforc...
audio_open() 的作用,就如同它的名字那样,就是打开音频设备。流程图如下: SDL库播放音频数据有两种方式。 1,调用层定时往SDL接口塞数据。 2,设置SDL回调函数,让SDL来主动执行回调函数来取数据。 第二种方式的实时性更好,ffplay 也是用的第二种。 先介绍一下 audio_open() 函数的各个参数,如下: staticintaudio_open(voidopaque,int64_twanted_channel_layout,intwanted_nb_channels,intwanted_sa...
之前在 stream_component_open() 里面的 decode_start() 函数开启了 audio_thread 线程,如下: audio_thread 线程主要是负责 解码 PacketQueue 队列里面的 AVPacket 的,解码出来 AVFrame,然后丢给入口滤镜,再从出口滤镜把 AVFrame 读出来,再插入 FrameQueue...
decoder_decode_frame() 其实是一个通用的解码函数,可以解码音频,视频,字幕的 AVPacket。不过本文主要侧重于分析音频流的解码,但其他的流也是类似的逻辑。 decoder_decode_frame() 函数的参数如下: staticintdecoder_decode_frame(Decoderd,AVFrameframe,AVSubtitlesub) 1,Decoderd,这个 Decoder 结构,我把它称为解码管理实例。由于 Decoder&nb...
FFplay播放器有两种队列,PacketQueue 跟 FrameQueue。FrameQueue 的数据就是从 PacketQueue 里面解码出来的(会经过滤镜)。 PakceQueue 是用 FifoBuffer 来实现环形队列的,而FrameQueue 是用数组来实现一个环形队列的,但是更复杂一些。FrameQueue 跟 PakceQueue 的关系如下: FrameQueue&nb...
在之前的几篇文章里面,都零零散散提及过序列号这个概念。但是序列号这个概念对于 FFplay 播放器非常重要的,很多代码都跟序列号有关。所以单独写一篇文章介绍序列号。 序列号主要是给 快进快退 这个功能准备的。如果不能快进快退,那其实就不需要序列号。只要解复用线程不断读取 AVPacket 放进去 PacketQueue 队列,解码线程不断从 PacketQueue 取数据来解码放进去 FrameQueue,最后有播放线程来取 Fra...
音频播放线程是之前在 audio_open() 函数里面创建的,实际上就是回调函数 ( wanted_spec.callback)。当用SDL打开音频硬件设备的时候,SDL库就会创建一个线程,来及时执行回调函数 sdl_audio_callback(),至于SDL线程多久回调一次函数,这个我们不需要太关心,只要调 SDL_OpenAudioDevice() 函数的时候设置好相关参数即可。如下: 上图中,设置了每次回调取的样本数,设置了样本数就相当于设置了回调次数,ffplay&nb...
首先说明一下,audio_decode_frame() 函数跟解码毫无关系,真正的解码函数是 decoder_decode_frame 。 audio_decode_frame() 函数的主要作用是从 FrameQueue 队列里面读取 AVFrame ,然后把 is->audio_buf 指向 AVFrame 的 data。如果 AVFrame 的音频信息跟 ...
之前在 stream_component_open() 里面的 decode_start() 函数开启了 video_thread 线程,如下: video_thread 线程主要是负责 解码 PacketQueue 队列里面的 AVPacket 的,解码出来 AVFrame,然后丢给入口滤镜,再从出口滤镜把 AVFrame 读出来,再插入 FrameQueue...
视频播放线程就是 main 主线程,对于 FFplay 播放器,就是在 主线程 里面播放视频流的,如下: 如上图所示,event_loop() 会不断用 refresh_loop_wait_event() 函数检测是否有键盘事件发生,如果有键盘事件发生, refresh_loop_wait_event() 就会返回,然后跑到 switch{event.type}{...} 来处理键盘事件。 如果没有键盘事件发生,&...
之前的文章,已经把音频,视频的解码线程,播放线程通通讲了一遍,现在到了播放器实现最复杂的功能之一,就是音视频同步。 FFplay支持3种同步方式,如下: 1,以音频时钟为主时钟,默认方式。 2,以视频时钟为主时钟。 3,以外部时钟为主时钟。 因为音频流的连续性非常强,例如一秒需要播放48000个样本。而视频流通常1秒只需播放24帧。而且人的耳朵对音频特别敏感。 所以第一种方式以音频时钟为准是最常用的,其他两种方式几乎没有应用场景。 先普及一下音视频同步的一些基本概念。 1,为什么需要进行音视频同步? 答:由于计算机系统大部分是分时系统,所以当负载过高或者设备性能差的时候,音频播放线程或者视频...
以音频时钟为主时钟,是最常用的同步方式,也是FFplay里面默认的同步方式。当以音频时钟为主时钟,视频就会向音频同步。 视频播放线程,会缩短或者拉长当前视频帧的播放时长,或者丢弃视频帧来向音频同步。 FFplay 是用 structClock 数据结构记录音频流,视频流当前播放到哪里的。Clock 结构体的定义如下: typedefstructClock{doublepts;/clockbase/单位是妙。doublepts_drift;/clockbaseminustimeatwhichweupdatedtheclock/dou...
本文是讲解当视频时钟设置为主时钟的时候,音视频同步的逻辑。可以通过以下命令设置视频时钟为主时钟: ffplay-syncvideo-ijuren-30s.mp4 当视频时钟设置为主时钟的时候,is->av_sync_type 等于 AV_SYNC_VIDEO_MASTER,之前在《FFplay视频同步分析》提到的3处视频同步的代码,全部都会失效,如下: 因此当视频是主时钟的时候,视频流永远不会丢帧,即使视频播放线程卡顿,也不会丢帧。不过如果视频播放线程卡顿,可能会导致某些帧的播放时长缩短。因为一般情况是0.01s检查一次,如果...
FFplay播放器有3个时间: 1,视频流的播放时刻,(视频时钟) 2,音频流的播放时刻,(音频时钟) 3,预定的时间,预定的播放时刻,实际上就是物理世界的自然时间,(外部时钟) 上图中的 14:00:00:123 就是其中一个预定的时间,完全准确的情况下,会在 14:00:00:123 播放第4帧视频,播放第7帧音频。 但是,计算机系统是分时系统,大部分情况不会那么准确,有可能发生以下的情况。 在 14:00:00:133 的时候才播放第4帧视频。 在 14:00:00:143 ...
播放器的最常用的功能之一就是快进快退,快进快退的本质就是让mp4文件跳转到另一个时间点来播放。 FFplay播放器有两种方式可以让mp4文件跳转到另一个时间点来播放。 1,在命令行里里使用 -ss 参数,如下,跳到到第60秒的地方开始播放。 ffplay-x400-ss60-ijuren.mp4 2,在播放过程中,按上下左右减,进行快进后退操作。 上面这两种跳转时间点播放的方式,其实都是调 avformat_seek_file() 来实现的,都是在 read_thread() 线程函数里面进行see...