1. ffmpeg是音视频界的瑞士军刀:

  1. 它提供了从录制、编码、封装、推流到拉流、解封装、解码、滤镜、播放的完整解决方案。
  2. 它是跨平台的解决方案,一套代码适配Windows、Linux、Mac OS、iOS、Andriod。
  3. 它原生支持hls、dash、rtmp、rtsp、http、tcp等主流协议,并提供统一的扩展方式方便扩展自己的协议,比如tutk、webrtc等,目前已经有开源库metaRTC可通过扩展集成到ffmpeg中实现webrtc的推拉流。
  4. 它支持或插件支持所有封装格式的封装和解封装。
  5. 它支持或插件支持所有音视频编码格式的编码和解码。主要视频代表有H264、H265,主要音频代表有aac、opus。
  6. 它已经内置一百多种滤镜并提供方式可以添加自定义滤镜。也可以在这个地方添加OpenGL,支持自定义GPU管线。
  7. 它解码后的音视频数据可以通过SDL(对不同平台提供一套统一的接口,调用不同的底层API库)去渲染视频和播放音频。可以通过OpenGLES去渲染视频,OpenGLES是OpenGL的子集,所以OpenGLES也可以在PC端渲染。

2. ffplay实现原理

1. ffplay是ffmpeg官方提供的播放器

它基于ffmpeg的强大功能,结合SDL实现了从拉流、解协议、解封装、音频解码、视频解码、音频播放、视频播放的一体化功能。

2. 以下是ffplay整体播放流程图:

Created with Raphaël 2.3.0 开始/©王方帅 拉流 解协议 是音频包? 音频解码 音频播放(音视频同步) 结束/©王方帅 是视频包? 视频解码 视频播放(音视频同步) yes no yes

3. ffplay有5个主要线程:

  1. 解协议、解封装线程: read_thread
  2. 音频解码线程:audio_thread
  3. 音频播放线程:sdl_audio_callback
  4. 视频解码线程:video_thread
  5. 视频播放线程(主线程):video_refresh

以上各线程之间各司其职、通力合作、有任务处理任务、无任务休眠等待其他线程唤醒继续执行任务。

4. 以下是各线程执行结构体关系图:

  1. read_thread

    负责解协议、解封装、将AVPacket放入音频PacketQueue或视频PacketQueue:

#mermaid-svg-dwXFHp8YEZBBpkpa {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-dwXFHp8YEZBBpkpa .error-icon{fill:#552222;}#mermaid-svg-dwXFHp8YEZBBpkpa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dwXFHp8YEZBBpkpa .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-dwXFHp8YEZBBpkpa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dwXFHp8YEZBBpkpa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dwXFHp8YEZBBpkpa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dwXFHp8YEZBBpkpa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dwXFHp8YEZBBpkpa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dwXFHp8YEZBBpkpa .marker.cross{stroke:#333333;}#mermaid-svg-dwXFHp8YEZBBpkpa svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dwXFHp8YEZBBpkpa .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-dwXFHp8YEZBBpkpa .cluster-label text{fill:#333;}#mermaid-svg-dwXFHp8YEZBBpkpa .cluster-label span{color:#333;}#mermaid-svg-dwXFHp8YEZBBpkpa .label text,#mermaid-svg-dwXFHp8YEZBBpkpa span{fill:#333;color:#333;}#mermaid-svg-dwXFHp8YEZBBpkpa .node rect,#mermaid-svg-dwXFHp8YEZBBpkpa .node circle,#mermaid-svg-dwXFHp8YEZBBpkpa .node ellipse,#mermaid-svg-dwXFHp8YEZBBpkpa .node polygon,#mermaid-svg-dwXFHp8YEZBBpkpa .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dwXFHp8YEZBBpkpa .node .label{text-align:center;}#mermaid-svg-dwXFHp8YEZBBpkpa .node.clickable{cursor:pointer;}#mermaid-svg-dwXFHp8YEZBBpkpa .arrowheadPath{fill:#333333;}#mermaid-svg-dwXFHp8YEZBBpkpa .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-dwXFHp8YEZBBpkpa .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-dwXFHp8YEZBBpkpa .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-dwXFHp8YEZBBpkpa .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-dwXFHp8YEZBBpkpa .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-dwXFHp8YEZBBpkpa .cluster text{fill:#333;}#mermaid-svg-dwXFHp8YEZBBpkpa .cluster span{color:#333;}#mermaid-svg-dwXFHp8YEZBBpkpa div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-dwXFHp8YEZBBpkpa :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

通过avformat_open_input解协议+解封装
no
yes
yes
no
yes
read_thread/王方帅
filename
AVFormatContext
stream_component_open根据解封装后音视频codec_id初始化Decoder
for循环
stream_has_enough_packets/audioq/videoq
enough
av_read_frame
AVPacket
SDL_CondWait
音频包?
packet_queue_put/audioq
put_finish/王方帅
视频包?
packet_queue_put/videoq

  2. audio_thread

    负责将音频队列里的AVPacket解码成AVFrame,经过滤镜处理后放入音频FrameQueue:

   1. decoder_decode_frame:

#mermaid-svg-ej1EJegvipvGTc9r {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-ej1EJegvipvGTc9r .error-icon{fill:#552222;}#mermaid-svg-ej1EJegvipvGTc9r .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ej1EJegvipvGTc9r .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-ej1EJegvipvGTc9r .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ej1EJegvipvGTc9r .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ej1EJegvipvGTc9r .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ej1EJegvipvGTc9r .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ej1EJegvipvGTc9r .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ej1EJegvipvGTc9r .marker.cross{stroke:#333333;}#mermaid-svg-ej1EJegvipvGTc9r svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ej1EJegvipvGTc9r .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ej1EJegvipvGTc9r .cluster-label text{fill:#333;}#mermaid-svg-ej1EJegvipvGTc9r .cluster-label span{color:#333;}#mermaid-svg-ej1EJegvipvGTc9r .label text,#mermaid-svg-ej1EJegvipvGTc9r span{fill:#333;color:#333;}#mermaid-svg-ej1EJegvipvGTc9r .node rect,#mermaid-svg-ej1EJegvipvGTc9r .node circle,#mermaid-svg-ej1EJegvipvGTc9r .node ellipse,#mermaid-svg-ej1EJegvipvGTc9r .node polygon,#mermaid-svg-ej1EJegvipvGTc9r .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ej1EJegvipvGTc9r .node .label{text-align:center;}#mermaid-svg-ej1EJegvipvGTc9r .node.clickable{cursor:pointer;}#mermaid-svg-ej1EJegvipvGTc9r .arrowheadPath{fill:#333333;}#mermaid-svg-ej1EJegvipvGTc9r .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ej1EJegvipvGTc9r .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ej1EJegvipvGTc9r .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-ej1EJegvipvGTc9r .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-ej1EJegvipvGTc9r .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ej1EJegvipvGTc9r .cluster text{fill:#333;}#mermaid-svg-ej1EJegvipvGTc9r .cluster span{color:#333;}#mermaid-svg-ej1EJegvipvGTc9r div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ej1EJegvipvGTc9r :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

no
yes
no
yes
decoder_decode_frame/王方帅
for循环
avcodec_receive_frame接收解码后的帧
接收到帧
dowhile循环
returnFrame/王方帅
PacketQueue.nb_packets为0?
为0
调用packet_queue_get获取pkt
avcodec_send_packet将pkt发送到Decoder中
SDL_CondSignal通知read_thread读取

   2. audio_thread:

#mermaid-svg-6Kq1sMp2N3WZjQIF {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-6Kq1sMp2N3WZjQIF .error-icon{fill:#552222;}#mermaid-svg-6Kq1sMp2N3WZjQIF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6Kq1sMp2N3WZjQIF .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-6Kq1sMp2N3WZjQIF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6Kq1sMp2N3WZjQIF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6Kq1sMp2N3WZjQIF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6Kq1sMp2N3WZjQIF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6Kq1sMp2N3WZjQIF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6Kq1sMp2N3WZjQIF .marker.cross{stroke:#333333;}#mermaid-svg-6Kq1sMp2N3WZjQIF svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6Kq1sMp2N3WZjQIF .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6Kq1sMp2N3WZjQIF .cluster-label text{fill:#333;}#mermaid-svg-6Kq1sMp2N3WZjQIF .cluster-label span{color:#333;}#mermaid-svg-6Kq1sMp2N3WZjQIF .label text,#mermaid-svg-6Kq1sMp2N3WZjQIF span{fill:#333;color:#333;}#mermaid-svg-6Kq1sMp2N3WZjQIF .node rect,#mermaid-svg-6Kq1sMp2N3WZjQIF .node circle,#mermaid-svg-6Kq1sMp2N3WZjQIF .node ellipse,#mermaid-svg-6Kq1sMp2N3WZjQIF .node polygon,#mermaid-svg-6Kq1sMp2N3WZjQIF .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6Kq1sMp2N3WZjQIF .node .label{text-align:center;}#mermaid-svg-6Kq1sMp2N3WZjQIF .node.clickable{cursor:pointer;}#mermaid-svg-6Kq1sMp2N3WZjQIF .arrowheadPath{fill:#333333;}#mermaid-svg-6Kq1sMp2N3WZjQIF .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6Kq1sMp2N3WZjQIF .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6Kq1sMp2N3WZjQIF .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-6Kq1sMp2N3WZjQIF .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-6Kq1sMp2N3WZjQIF .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6Kq1sMp2N3WZjQIF .cluster text{fill:#333;}#mermaid-svg-6Kq1sMp2N3WZjQIF .cluster span{color:#333;}#mermaid-svg-6Kq1sMp2N3WZjQIF div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6Kq1sMp2N3WZjQIF :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

yes
no
audio_thread/王方帅
decoder_decode_frame传入音频Decoder拿到音频帧
av_buffersrc_add_frame将帧传入滤镜
av_buffersink_get_frame从滤镜取出结果帧
frame_queue_peek_writable
FrameQueue满了?
满了
SDL_CondWait直到播放线程调用frame_queue_next后Singal继续执行
frame_queue_push到sampq
SDL_CondSignal通知sdl_audio_callback去继续播放/王方帅

  3. sdl_audio_callback

    音频播放回调线程:
   1. synchronize_audio:

#mermaid-svg-1jHK6FC1gVsycId4 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-1jHK6FC1gVsycId4 .error-icon{fill:#552222;}#mermaid-svg-1jHK6FC1gVsycId4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1jHK6FC1gVsycId4 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-1jHK6FC1gVsycId4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1jHK6FC1gVsycId4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1jHK6FC1gVsycId4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1jHK6FC1gVsycId4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1jHK6FC1gVsycId4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1jHK6FC1gVsycId4 .marker.cross{stroke:#333333;}#mermaid-svg-1jHK6FC1gVsycId4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1jHK6FC1gVsycId4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-1jHK6FC1gVsycId4 .cluster-label text{fill:#333;}#mermaid-svg-1jHK6FC1gVsycId4 .cluster-label span{color:#333;}#mermaid-svg-1jHK6FC1gVsycId4 .label text,#mermaid-svg-1jHK6FC1gVsycId4 span{fill:#333;color:#333;}#mermaid-svg-1jHK6FC1gVsycId4 .node rect,#mermaid-svg-1jHK6FC1gVsycId4 .node circle,#mermaid-svg-1jHK6FC1gVsycId4 .node ellipse,#mermaid-svg-1jHK6FC1gVsycId4 .node polygon,#mermaid-svg-1jHK6FC1gVsycId4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1jHK6FC1gVsycId4 .node .label{text-align:center;}#mermaid-svg-1jHK6FC1gVsycId4 .node.clickable{cursor:pointer;}#mermaid-svg-1jHK6FC1gVsycId4 .arrowheadPath{fill:#333333;}#mermaid-svg-1jHK6FC1gVsycId4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1jHK6FC1gVsycId4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1jHK6FC1gVsycId4 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-1jHK6FC1gVsycId4 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-1jHK6FC1gVsycId4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1jHK6FC1gVsycId4 .cluster text{fill:#333;}#mermaid-svg-1jHK6FC1gVsycId4 .cluster span{color:#333;}#mermaid-svg-1jHK6FC1gVsycId4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1jHK6FC1gVsycId4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

yes
no
synchronize_audio/王方帅
wanted_nb_samples赋初值为nb_samples
判断不等于AV_SYNC_AUDIO_MASTER则需要进行同步音频处理
不等于?
同步音频
获取音频与主MASTER时钟的diff
根据diff乘以采样率得到需要增加或减少的样本数
跟原有样本数累加后限制一下最小值和最大值
返回同步计算后需要的样本数wanted_nb_samples/王方帅
返回wanted_nb_samples/王方帅

   2. audio_decode_frame:

#mermaid-svg-Ao21bZfEJeO6Cs6X {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Ao21bZfEJeO6Cs6X .error-icon{fill:#552222;}#mermaid-svg-Ao21bZfEJeO6Cs6X .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Ao21bZfEJeO6Cs6X .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-Ao21bZfEJeO6Cs6X .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Ao21bZfEJeO6Cs6X .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Ao21bZfEJeO6Cs6X .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Ao21bZfEJeO6Cs6X .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Ao21bZfEJeO6Cs6X .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Ao21bZfEJeO6Cs6X .marker.cross{stroke:#333333;}#mermaid-svg-Ao21bZfEJeO6Cs6X svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Ao21bZfEJeO6Cs6X .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Ao21bZfEJeO6Cs6X .cluster-label text{fill:#333;}#mermaid-svg-Ao21bZfEJeO6Cs6X .cluster-label span{color:#333;}#mermaid-svg-Ao21bZfEJeO6Cs6X .label text,#mermaid-svg-Ao21bZfEJeO6Cs6X span{fill:#333;color:#333;}#mermaid-svg-Ao21bZfEJeO6Cs6X .node rect,#mermaid-svg-Ao21bZfEJeO6Cs6X .node circle,#mermaid-svg-Ao21bZfEJeO6Cs6X .node ellipse,#mermaid-svg-Ao21bZfEJeO6Cs6X .node polygon,#mermaid-svg-Ao21bZfEJeO6Cs6X .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Ao21bZfEJeO6Cs6X .node .label{text-align:center;}#mermaid-svg-Ao21bZfEJeO6Cs6X .node.clickable{cursor:pointer;}#mermaid-svg-Ao21bZfEJeO6Cs6X .arrowheadPath{fill:#333333;}#mermaid-svg-Ao21bZfEJeO6Cs6X .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Ao21bZfEJeO6Cs6X .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Ao21bZfEJeO6Cs6X .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-Ao21bZfEJeO6Cs6X .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-Ao21bZfEJeO6Cs6X .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Ao21bZfEJeO6Cs6X .cluster text{fill:#333;}#mermaid-svg-Ao21bZfEJeO6Cs6X .cluster span{color:#333;}#mermaid-svg-Ao21bZfEJeO6Cs6X div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Ao21bZfEJeO6Cs6X :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

yes
no
yes
no
audio_decode_frame/王方帅
dowhile循环
调用frame_queue_peek_readable方法从sampq中取AVFrame
aframe的serial不等于audioq.serial?
不等于
根据音频格式,样本数,通道数计算data_size
调用synchronize_audio计算wanted_nb_samples
aframe跟SDL_OpenAudioDevice得到的音频格式,采样率,声道数,wanted_nb_samples做比较
有任意一个不同?
任意一个不同则初始化音频重采样上下文swr_ctx
调用swr_set_compensation设置重采样参数
调用swr_convert进行重采样
设置audio_buf为重采样后的buf
使用pts显示时间戳更新audio_clock
设置audio_buf为原始buf
使用pts显示时间戳更新音频时钟
返回重采样后的或原始的data_size/王方帅

   3. sdl_audio_callback:

#mermaid-svg-QJaSBUTPkkBlDext {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-QJaSBUTPkkBlDext .error-icon{fill:#552222;}#mermaid-svg-QJaSBUTPkkBlDext .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QJaSBUTPkkBlDext .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-QJaSBUTPkkBlDext .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QJaSBUTPkkBlDext .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QJaSBUTPkkBlDext .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QJaSBUTPkkBlDext .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QJaSBUTPkkBlDext .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QJaSBUTPkkBlDext .marker.cross{stroke:#333333;}#mermaid-svg-QJaSBUTPkkBlDext svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QJaSBUTPkkBlDext .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-QJaSBUTPkkBlDext .cluster-label text{fill:#333;}#mermaid-svg-QJaSBUTPkkBlDext .cluster-label span{color:#333;}#mermaid-svg-QJaSBUTPkkBlDext .label text,#mermaid-svg-QJaSBUTPkkBlDext span{fill:#333;color:#333;}#mermaid-svg-QJaSBUTPkkBlDext .node rect,#mermaid-svg-QJaSBUTPkkBlDext .node circle,#mermaid-svg-QJaSBUTPkkBlDext .node ellipse,#mermaid-svg-QJaSBUTPkkBlDext .node polygon,#mermaid-svg-QJaSBUTPkkBlDext .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-QJaSBUTPkkBlDext .node .label{text-align:center;}#mermaid-svg-QJaSBUTPkkBlDext .node.clickable{cursor:pointer;}#mermaid-svg-QJaSBUTPkkBlDext .arrowheadPath{fill:#333333;}#mermaid-svg-QJaSBUTPkkBlDext .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-QJaSBUTPkkBlDext .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-QJaSBUTPkkBlDext .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-QJaSBUTPkkBlDext .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-QJaSBUTPkkBlDext .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-QJaSBUTPkkBlDext .cluster text{fill:#333;}#mermaid-svg-QJaSBUTPkkBlDext .cluster span{color:#333;}#mermaid-svg-QJaSBUTPkkBlDext div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-QJaSBUTPkkBlDext :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

yes
no
sdl_audio_callback/王方帅
获取audio_callback_time
往steam指针指向的内存中存大小为len的音频数据
while循环如果没存满
调用audio_decode_frame方法获取一帧buffer数据填入steam中
steam指针往前偏移,len减去对应buf_size
若一帧buf_size大于len则仅存len大小数据,多余的放到VideoState中以便下次继续填充
根据audio_decode_frame设置的audio_clock
经过计算更新audio_callback_time的clock
结束callback/王方帅

  3. video_thread

    负责将视频队列里的AVPacket解码成AVFrame,经过滤镜处理后放入视频FrameQueue:

#mermaid-svg-QoCKs6BkCYwDcGXB {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-QoCKs6BkCYwDcGXB .error-icon{fill:#552222;}#mermaid-svg-QoCKs6BkCYwDcGXB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QoCKs6BkCYwDcGXB .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-QoCKs6BkCYwDcGXB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QoCKs6BkCYwDcGXB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QoCKs6BkCYwDcGXB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QoCKs6BkCYwDcGXB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QoCKs6BkCYwDcGXB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QoCKs6BkCYwDcGXB .marker.cross{stroke:#333333;}#mermaid-svg-QoCKs6BkCYwDcGXB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QoCKs6BkCYwDcGXB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-QoCKs6BkCYwDcGXB .cluster-label text{fill:#333;}#mermaid-svg-QoCKs6BkCYwDcGXB .cluster-label span{color:#333;}#mermaid-svg-QoCKs6BkCYwDcGXB .label text,#mermaid-svg-QoCKs6BkCYwDcGXB span{fill:#333;color:#333;}#mermaid-svg-QoCKs6BkCYwDcGXB .node rect,#mermaid-svg-QoCKs6BkCYwDcGXB .node circle,#mermaid-svg-QoCKs6BkCYwDcGXB .node ellipse,#mermaid-svg-QoCKs6BkCYwDcGXB .node polygon,#mermaid-svg-QoCKs6BkCYwDcGXB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-QoCKs6BkCYwDcGXB .node .label{text-align:center;}#mermaid-svg-QoCKs6BkCYwDcGXB .node.clickable{cursor:pointer;}#mermaid-svg-QoCKs6BkCYwDcGXB .arrowheadPath{fill:#333333;}#mermaid-svg-QoCKs6BkCYwDcGXB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-QoCKs6BkCYwDcGXB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-QoCKs6BkCYwDcGXB .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-QoCKs6BkCYwDcGXB .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-QoCKs6BkCYwDcGXB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-QoCKs6BkCYwDcGXB .cluster text{fill:#333;}#mermaid-svg-QoCKs6BkCYwDcGXB .cluster span{color:#333;}#mermaid-svg-QoCKs6BkCYwDcGXB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-QoCKs6BkCYwDcGXB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

yes
no
video_thread/王方帅
for循环
调用decoder_decode_frame从videoq中取AVPacket解码成AVFrame
调用av_buffersrc_add_frame传入AVFrame
调用av_buffersink_get_frame_flags获取滤镜处理后的AVFrame
调用queue_picture
调用frame_queue_peek_writable判断pictq是否已满
已满?
SDL_CondWait等待播放线程取走AVFrame
将封装了AVFrame的Frame对象放入pictq队列
通知视频播放线程取AVFrame进行播放/王方帅

  4. video_refresh

    视频播放线程负责从pictq队列取AVFrame渲染到SDLWindow中:

#mermaid-svg-04oyY1Xyj6IpUSvQ {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-04oyY1Xyj6IpUSvQ .error-icon{fill:#552222;}#mermaid-svg-04oyY1Xyj6IpUSvQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-04oyY1Xyj6IpUSvQ .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-04oyY1Xyj6IpUSvQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-04oyY1Xyj6IpUSvQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-04oyY1Xyj6IpUSvQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-04oyY1Xyj6IpUSvQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-04oyY1Xyj6IpUSvQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-04oyY1Xyj6IpUSvQ .marker.cross{stroke:#333333;}#mermaid-svg-04oyY1Xyj6IpUSvQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-04oyY1Xyj6IpUSvQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-04oyY1Xyj6IpUSvQ .cluster-label text{fill:#333;}#mermaid-svg-04oyY1Xyj6IpUSvQ .cluster-label span{color:#333;}#mermaid-svg-04oyY1Xyj6IpUSvQ .label text,#mermaid-svg-04oyY1Xyj6IpUSvQ span{fill:#333;color:#333;}#mermaid-svg-04oyY1Xyj6IpUSvQ .node rect,#mermaid-svg-04oyY1Xyj6IpUSvQ .node circle,#mermaid-svg-04oyY1Xyj6IpUSvQ .node ellipse,#mermaid-svg-04oyY1Xyj6IpUSvQ .node polygon,#mermaid-svg-04oyY1Xyj6IpUSvQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-04oyY1Xyj6IpUSvQ .node .label{text-align:center;}#mermaid-svg-04oyY1Xyj6IpUSvQ .node.clickable{cursor:pointer;}#mermaid-svg-04oyY1Xyj6IpUSvQ .arrowheadPath{fill:#333333;}#mermaid-svg-04oyY1Xyj6IpUSvQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-04oyY1Xyj6IpUSvQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-04oyY1Xyj6IpUSvQ .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-04oyY1Xyj6IpUSvQ .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-04oyY1Xyj6IpUSvQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-04oyY1Xyj6IpUSvQ .cluster text{fill:#333;}#mermaid-svg-04oyY1Xyj6IpUSvQ .cluster span{color:#333;}#mermaid-svg-04oyY1Xyj6IpUSvQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-04oyY1Xyj6IpUSvQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

yes
no
no
yes
yes
no
no
yes
video_refresh/王方帅
retry
调用frame_queue_nb_remaining判断pictq是否为空
为空?
什么也不做/王方帅
判断Frame的serial是否不等于videoq的serial
不等?
调用compute_target_delay方法进行视频时钟同主时钟的同步
如果视频时钟慢,则返回delay为0,以便丢弃帧追赶时钟
如果视频时钟快的超过一个同步阈值,则将正常delay加上时钟的diff返回
如果视频时钟快但在阈值内,则进行2倍delay返回处理
如果delay加帧起始时间大于当前时间
大于?
调用frame_queue_next取下一帧
通知视频解码线程继续解码存pictq
当前帧继续展示/王方帅
调用frame_queue_peek_next取下一帧视频
计算duration加帧起始时间依然大于当前时间
大于?
说明这一帧就是要重新渲染到窗口的帧
设置force_refresh为1
调用video_display方法渲染帧到SDL窗口
渲染时调用video_image_display从pictq取出要渲染的帧,计算帧要显示的x,y,width,height,将帧数据转为SDL_Texture,渲染到SDL_Renderer上/王方帅
调用frame_queue_next将帧从picq中移除,然后SDL_CondSignal视频解码线程继续解码帧

3. 优势分析

  1. ffmpeg实际是一个音视频框架,如果想要自定义一个协议进行播放只要按照规则实现一套对应的协议即可实现ffmpeg对此协议的支持,可以参考metaRTC这个例子。
  2. ffmpeg有统一的编解码代码实现,使得切换编解码器简直不要太容易,只需要将此编解码器编译到ffmpeg库中,然后修改一下编解码器对应的ID即可实现底层编解码器的切换,比如H264升级到H265将大幅降低服务器存储空间及传输带宽的占用,对直播和录播场景将节约不可估量的成本。
  3. ffmpeg配合SDL、OpenGLES将实现全平台的视频播放及特效处理。

ffplay实现原理相关推荐

  1. 至简播放器ffplay工作原理

    下载,编译及运行 参考博文 http://blog.csdn.net/ericbar/article/details/79382783 即可完成ffplay的基本测试. 如果要进行GDB调试,需要先编 ...

  2. FFPLAY的原理(一)

    概要 电影文件有很多基本的组成部分.首先,文件本身被称为容器Container,容器的类型决定了信息被存放在文件中的位置.AVI和Quicktime就是容器的例子.接着,你有一组流,例如,你经常有的是 ...

  3. 直播--FFPLAY 的原理

    电影文件有很多基本的组成部分.首先,文件本身被称为容器Container,容器的类型决定了信息被存放在文件中的位置.AVI和 Quicktime就是容器的例子.接着,你有一组流,例如,你经常有的是一个 ...

  4. FFPLAY的原理(二)

    关于包Packets的注释 从技术上讲一个包可以包含部分或者其它的数据,但是ffmpeg的解释器保证了我们得到的包Packets包含的要么是完整的要么是多种完整的帧. 现在我们需要做的是让SaveFr ...

  5. ffmpeg相关资源

    FFPLAY的原理(一) http://blog.csdn.net/shenbin1430/article/details/4291893 ubuntu12.04下命令安装ffplay等: sudo ...

  6. 面向大规模数据的云端管理,百度沧海存储产品解析

    导读:本文整理自同名<云智公开课>系列分享,详细介绍百度沧海·存储如何进行大规模数据,流转上云.智能分级存储.安全管理.多业务应用. 全文5657字,预计阅读时间20分钟. 01 ABC ...

  7. 从零到一了解APP速度测评

    作者 | 龙霸天 一.引言 为了知道「为什么会打雷下雨」,我们拿起了手机,使用百度 APP 进行搜索: 小小的一个搜索诉求,却需要经历一个不短的交互过程.就跟去银行办业务一样,只想改个银行预留手机号, ...

  8. 基于 TLS 1.3的百度安全通信协议 bdtls 介绍

    作者 | 金媛宝.孤独键盘手 导读: 百度小程序已经在百度开源联盟的多家宿主APP上运行,为了保证小程序框架.小程序.宿主APP等业务方相互之间的通信服务不被恶意攻击,运营类活动不被薅羊毛,基于最新的 ...

  9. ffplay.c源码阅读之解码模块实现原理

    前言 解码作为渲染模块和拉流模块的中间模块,它一方面要不停的从拉流模块的压缩数据缓冲区中获取待解码数据包,让后将这个数据包送入自己的解码模块,获得解码数据后再送入自己的解码缓冲区,这就是整个解码模块的 ...

最新文章

  1. 高德地图-搜索服务-POI搜索
  2. 【蓝桥杯】基础练习 数列排序
  3. 工业级光纤收发器和协议转换器有什么区别呢?
  4. 【图说word】宏二
  5. SAP License:心情
  6. QLabel显示图像
  7. iis php7页面空白,iis 无法显示htm页面问题解决
  8. 关系型数据库中主键(primary key)和外键(foreign key)的概念。
  9. fixdown down,一个时代的终结。
  10. 前端需要学习c语言吗,我应该在学习C语言之前学习HTML或CSS吗?
  11. 读书笔记-计算机视觉
  12. Python unrar解压rar压缩文件
  13. 对话阿里云张建锋:云计算正在内卷吗?丨钛度专访
  14. C#窗体程序随电脑分辨率自动调整大小
  15. 后端优化 | VINS-Mono 论文公式推导与代码解析分讲
  16. One-Lin3r:懒人的福音,渗透测试单行化工具
  17. OneNote 与 OneNote 2016 有什么区别?
  18. 【BT】经典蓝牙连接速度优化
  19. Makefile depend规则
  20. 分享66个JS时间轴特效,总有一款适合您

热门文章

  1. 纪念我去年故去的同事
  2. PAT1002 写出这个数 分数 20
  3. java校验身份证号实例
  4. Spark读取日志文件集中文乱码解决方法
  5. lucene原理讲解
  6. 如何选择一家正规的期货公司开户?
  7. 麒麟v10系统开启ssh服务
  8. C语言判断素数,判断质素算法
  9. 百度和高德手机导航全部免费使用,其他导航厂商将何去何从?
  10. 数组的浅拷贝和深拷贝