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

视频播放器原理其实大抵相同,都是对音视频帧序列的控制。只是一些播放器在音视频同步上可能做了更为复杂的帧预测技术,来保证音频和视频有更好的同步性。 

ffplayFFMpeg自带的播放器,使用了 ffmpeg解码库和用于视频渲染显示的sdl 库,也是业界播放器最初参考的设计标准。本文对ffplay源码进行分析,试图用更基础而系统的方法,来尝试解开播放器的音视频同步,以及播放/暂停、快进/后退的控制原理。

由于FFMpeg本身的跨平台特性,相比在移动端看音视频代码,在PC端利用VS查看和调试代码,分析播放器原理,要高效迅速很多。 

由于FFMpeg官方提供的ffmplayconsole中进行使用不够直观,本文直接分析CSDN上将ffplay移植到VC的代码(ffplay for MFC)进行分析。

文章目录:

一、初探mp4文件
二、以最简单播放器开始:FFmpeg解码 + SDL显示
三、先抛五个问题
四、ffplay代码总体结构
五、视频播放器的操作控制
5.1 ffplay所定义的关键结构体VideoState

5.2 补充基础知识——PTS和DTS

5.2 如何控制音视频同步

5.4 如何控制视频的播放和暂停?

5.5 逐帧播放是如何做的?

5.6 快进和后退

六、 这次分析ffplay代码的反省总结

一、初探mp4文件

为了让大家对视频文件有一个初步认识,首先来看对一个MP4文件的简单分析,如图1

1  对MP4文件解参 

从图一我们知道,每个视频文件都会有特定的封装格式、比特率、时长等信息。视频解复用之后,就划分为video_streamaudio_stream,分别对应视频流和音频流。

解复用之后的音视频有自己独立的参数,视频参数包括编码方式、采样率、画面大小等,音频参数包括采样率、编码方式和声道数等。

对解复用之后的音频和视频Packet进行解码之后,就变成原始的音频(PWM)和视频(YUV/RGB)数据,才可以在进行显示和播放。

其实这已经差不多涉及到了,视频解码播放的大部分流程,整个视频播放的流程如图2所示。

 

视频播放流程(图摘自http://blog.csdn.net/leixiaohua1020/article/details/50534150

 

二、以最简单播放器开始:FFmpeg解码 + SDL显示

为将问题简单化,先不考虑播放音频,只播放视频,代码流程图如图3所示:

播放器流程图(图源见水印)

流程图说明如下:

1.FFmpeg初始化的代码比较固定,主要目的就是为了设置AVFormatContext实例中相关成员变量的值,调用av_register_allavformat_open_input av_find_stream_infoavcodec_find_decoder等函数。

      如图4所示,初始化之后的AVFormatContext实例里面具体的值,调用av_find_stream_info就是找到文件中的音视频流数据,对其中的streams(包含音频、视频流)变量进行初始化。

图4 AVFormatContext初始化实例

2.av_read_frame不断读取stream中的下一帧,对其进行解复用得到视频的AVPacket,随后调用avcodec_decode_video2是视频帧AVPacket进行解码,得到图像帧AVFrame 

3.得到AVFrame之后,接下来就是放到SDL中进行渲染显示了,也很简单,流程见下面代码注释:

SDL_Overlay *bmp;
//将解析得到的AVFrame的数据拷贝到SDL_Overlay实例当中
SDL_LockYUVOverlay(bmp);
bmp->pixels[0]=pFrameYUV->data[0];
bmp->pixels[2]=pFrameYUV->data[1];
bmp->pixels[1]=pFrameYUV->data[2];    
bmp->pitches[0]=pFrameYUV->linesize[0];
bmp->pitches[2]=pFrameYUV->linesize[1];  
bmp->pitches[1]=pFrameYUV->linesize[2];

SDL_UnlockYUVOverlay(bmp);
//设置SDL_Rect,因为涉及到起始点和显示大小,用rect进行表示。
SDL_Rect rect;
rect.x = 0;   
rect.y = 0;   
rect.w = pCodecCtx->width; 
rect.h = pCodecCtx->height;   
//将SDL_Overlay数据显示到SDL_Surface当中。
SDL_DisplayYUVOverlay(bmp, &rect);
//延时40ms,留足ffmpeg取到下一帧并解码该帧的时间,随后继续读取下一帧
SDL_Delay(40);

由上面的原理可知,从帧流中获取到AVPacket,并且解码得到AVFrame,渲染到SDL窗口中。 

图5 视频播放状态图 

视频播放的流程总结一下就是:读取下一帧——>解码——>播放——>不断往复,状态图如图5所示。

 下一篇:

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



你可能感兴趣的:(多媒体音视频)