【文章网站源码】【爱尚代理源码】【免更新源码】ffplay源码下载

时间:2024-12-23 02:47:20 编辑:诱导充值网站源码 来源:vb抽签程序源码

1.FFMPEG音视频同步-音频实时采集编码封装成MP4
2.FFmpeg详细安装步骤和使用笔记
3.ffplay.c-框架及数据结构
4.FFplay源码分析-nobuffer
5.2024年 C++音视频开发学习路线(ffmpeg/rtsp/srs/webrtc/hls)
6.FFmpeg 简单介绍及使用

ffplay源码下载

FFMPEG音视频同步-音频实时采集编码封装成MP4

       音视频同步录制相关文章

       1、源码 ffmpeg-摄像头采集保存

       2、下载 ffmpeg音视频同步-摄像头采集编码封装

       3、源码 ffmpeg-音频正弦产生并编码封装

       4、下载 ffmpeg-音频实时采集保存

       5、源码 ffmpeg音视频同步-音频实时采集编码封装

       6、下载文章网站源码 ffmpeg音视频同步-音视频实时采集编码封装

       7、源码 ffmpeg音视频同步-音视频实时采集编码推流

       8、下载 ffmpeg音视频同步-音视频实时采集编码推流-优化版本

       系统环境:

       系统版本:lubuntu .

       Ffmpge版本:ffmpeg version N--g

       摄像头:1.3M HD WebCan

       虚拟机:Oracle VM VirtualBox 5.2.

       指令查看设备 ffmpeg -devices

       指令播放实时音频 ffplay -f alsa -showmode 1 -ac 2 -i default -ar

       Linux使用ffplay实时采集音频并实时播放_唐传林的源码博客-CSDN博客

       指令录制实时音频ffmpeg -f alsa -ar -i hw:0,0 audio.wav

       ffmpeg -f alsa -ar -i default ffmpeg_record_audio.wav

       ubuntu下用ffmpeg录制音频文件_ubuntu使用指令录制mp3音频_dingjianfeng的博客-CSDN博客

       指令分离音视频:ffmpeg -i test.mp4 -vn -y -acodec copy test.aac

       ffmpeg -i test.mp4 -vn -y -avcodec copy test.h

       本章文档基于ubuntu alsa格式。实时采集音频一帧数据并将其转化为pcm,下载编码成AAC,封装成mp4保存下来

       1.简介

       FFmpeg中有一个和多媒体设备交互的类库:Libavdevice。使用这个库可以读取电脑(或者其他设备上)的源码多媒体设备的数据,或者输出数据到指定的下载多媒体设备上。

       ffmpeg中的源码音频相关内容

       ffmpeg中AVPacket中可能会含有多个音频帧(AVFrame),一个音频帧含有多个采样,采样率决定了1s有多少个采样(或者说一个音频帧可以播放多久)。下载对于aac,源码一帧有个采样,mp3一帧则固定为个字节。

       对于音频,由于有多声道的音频,那么音频解码出来的数据不同声道也储存在不同的指针,如data[0]是左声道,data[1]是右声道,由于各个声道的数据长度是一样的,所以linesize[0]就代表了所有声道数据的长度。

       成员extended_data则指向了data,是一个拓展,上面可以看到data 是包含8个指针的数组,也就是说对于音频,最多只支持8个声道

       如果是以下格式:

       只能保存再AVFrame的uint8_t *data[0]; 音频保持格式如下: LRLRLR。。。。

       如果是以下格式

       plane 0: LLLLLLLLLLLLLLLLLLLLLLLLLL…

       plane 1: RRRRRRRRRRRRRRRRRRRR…

       plane 0对于uint8_t *data[0];

       plane 1对于uint8_t *data[1];

       相关学习资料推荐,点击下方链接免费报名,先码住不迷路~

       音视频免费学习地址:FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发

       免费分享音视频学习资料包、大厂面试题、技术视频和学习路线图,资料包括(C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等)有需要的可以点击 加群免费领取~

       2.源码

       最简单的基于Libavdevice的摄像头数据读取一帧帧pcm数据,经过音频重采样获取目标AAC的音频源数据参数,保存成MP4文件

       3.验证

       3.1编译

       3.2结果

       使用软件mp4打开test.mp4,可以听到实时采集的音频

       3.3存在的问题

       1、本文档源码,采集ALSA获取的源数据是K,stereo声道,采样大小,经转换成pcm的格式,同样是K,stereo声道大小;不同的采样率会出现部分声音数据丢失,具体原因,在 《ffmpeg-音频实时采集保存》具体分析 ,涉及其他分辨率转换,不在本文讨论范围里。

       2、由于本例程没有没有对ALSA音频进行重采样,(采样率,个采样点,双声道,bit),故若保存成ts,flv等封装格式是,默认采用的音频编码格式分别是mpga,mp3,其采样点为。所以运行此程序会有问题,数据无法写入。而且目前暂不探讨音频重采样问题。故想到一种解决方法是,指定AAC音频编码格式(ts,mp4等都支持AAC音频编码,参考《ffmpeg-摄像头采集编码封装》的表格)。

       4.附件

       无

       5.参考链接

       [1] ffmpeg之PCM转AAC

       FFmpeg()PCM编码AAC_pkt.data 就是pcm吗_mengzhengjie的博客-CSDN博客

       [2]官方Encode pcm file to aac

       [FFmpeg-user] Encode pcm file to aac

       [3]PCM编码AAC,参考其普通PCM格式与AAC转格式差异 FFmpeg()PCM编码AAC_pkt.data 就是pcm吗_mengzhengjie的博客-CSDN博客

       [4] 音频编码(一)--FFmpeg编码-腾讯云开发者社区-腾讯云

       原文链接: FFMPEG音视频同步-音频实时采集编码封装成MP4_qinyi_style的博客-CSDN博客

FFmpeg详细安装步骤和使用笔记

       FFmpeg安装步骤与使用指南

       FFmpeg是一款强大的多媒体处理工具,它支持音频、视频、流媒体和图像的跨平台操作,功能涵盖解码、编码、爱尚代理源码转换、流处理等,适用于多种格式,如MP4、AVI、MKV、MP3等,就像一个多功能的多媒体工具箱。

       安装版本

       FFmpeg提供了GPL许可证和GPL Shared两种版本,GPL适用于要求源代码公开的应用,而GPL Shared则允许以库形式嵌入到专有软件中,无需公开源代码。

       Windows安装

       下载解压到D:\Software\ffmpeg-master-latest-win-gpl

       找到bin目录中的ffmpeg、ffplay和ffprobe工具

       添加ffmpeg到系统环境变量的path中

       验证安装,通过cmd输入ffmpeg -version

       Linux安装

       在Debian/Ubuntu、Fedora、CentOS和openSUSE等系统中,FFmpeg安装步骤有所不同

       使用功能

       -

       转码视频和音频格式

       -

       剪切、合并视频

       -

       查看解码器和编码器列表

       -

       控制比特率以影响文件大小、质量与传输带宽

       硬件加速

       利用NVIDIA CUDA、AMD AMF或Intel Quick Sync Video等加速功能提升转码效率

       常见编码格式对比

       H./AVC:普及广泛,兼容性佳

       H./HEVC:高效编码,适合高清视频

       VP9和AV1:新兴格式,可能需要特定设备支持

       其他格式如MPEG-2、MPEG-4等也有适用场景

ffplay.c-框架及数据结构

       ffplay.c是FFmpeg源码中自带的实用播放器,它利用FFmpeg和SDL API构建,对独立开发播放器非常有帮助,如ijkplayer这样的知名开源项目便是基于ffplay.c进行扩展开发的。

       ffplay的核心框架主要分为几个部分:首先,初始化过程包括数据读取、音频和视频解码线程的划分。音频解码线程从packet queue获取数据,解码后放入frame queue;视频解码线程同样如此,还包括字幕解码。音频和视频播放通常在主线程中进行,而控制响应如播放暂停、快进快退也在此进行。

       数据处理中,packet和frame队列的设计至关重要。PacketQueue用于保存AVPacket,如MyAVPacketList结构,通过next字段连接,serial字段用于标记数据连续性。FrameQueue作为环形缓冲区,用于存储解码后的音视频数据,其内部结构如Frame,包含serial和sar等信息,通过初始化、销毁、读写操作实现音视频数据的管理和播放控制。

       音频参数(AudioParams)、解码器封装(struct Decoder)等细节也是ffplay的重要组成部分,它们共同协作,确保了播放过程的流畅和性能优化。学习这些内容有助于深入理解ffplay的工作原理,提高开发自己的播放器的能力。

FFplay源码分析-nobuffer

       在使用 FFplay 播放 RTMP 流时,不开启 nobuffer 选项会导致画面延迟高达7秒左右,而开启此选项后,局域网延迟可降低到毫秒左右。因此,本文将深入探讨nobuffer的实现细节,以及播放端缓存7秒数据的作用。

       fflags 的定义在 libavformat/options_table.h 文件中,这是一个通用选项,所有解复用器均包含此选项。在调用 avformat_open_input() 函数时,会将该命令行参数传入,其位置与所有格式参数相同,免更新源码如在之前的文章《FFplay源码分析》中所述。记得在调试参数中添加-fflags nobuffer。

       在 avformat_open_input() 函数内部,fflags 这个 AVOption 会被传递给 AVClass,该类存储了多个 AVOption,而fflags 的索引为5。在 av_opt_set_dict() 函数中,fflags 的值会被应用并清除其他选项。在 avformat_open_input() 执行完毕后,AVFormatContext::flags 的第7位应被置为1,即二进制的 。通过下图可以清晰地看到这个过程。

       在 avformat_find_stream_info() 函数内部,如果没有设置nobuffer标记,探测的数据包将被丢入队列。avformat_find_stream_info() 首先读取一段数据包以分析输入流的编码器等信息,为了重用这些数据包,它们会被放入队列中。然而,整个探测过程长达5秒,这意味着 FFplay 大概会读取5秒的数据来分析输入流。若开启nobuffer,则不会重复使用这些探测数据,FFplay 探测完输入流后,会读取新的数据包进行播放。无需缓存,从而降低了延迟。

       通过在 ffpaly.c 文件中的 avformat_find_stream_info() 函数前后输出时间,可以发现两者相差5秒,直观展示了nobuffer对于降低延迟的作用。在实时场景下,缓存功能变得多余,它原本是为了分析本地文件,避免重复读取,但在实时场景中反而影响了性能。因此,在实时场景中,关闭缓存更为合适。

       补充说明:若在本地虚拟机环境下,不启用缓存也能实现流畅播放。然而,如果 SRS 部署在局域网的另一台机器上,不开启缓存可能导致视频卡顿,原因可能是解码前未能及时读取视频帧,FFplay 不断丢弃视频帧,尤其是当视频比音频慢时,这种情况下缓存功能反而成为瓶颈。

年 C++音视频开发学习路线(ffmpeg/rtsp/srs/webrtc/hls)

       音视频工作领域繁复多样,自学时易陷入迷茫。本文整理出九个前景不错的方向:直播、传输、算法、视频播放器、流媒体后端、短视频、音频播放、视频编辑、图像处理。以下为详细学习路线:

       音视频基础

音频基础知识

视频基础知识

解复用基础知识

FFmpeg开发环境搭建

音视频开发常用工具

       FFmpeg实战教程

FFmpeg命令

SDL跨平台

FFmpeg基石精讲

FFmpeg过滤器

FFmpeg音视频解复用+解码

ffplay播放器

FFmpeg音视频编码+复用合成视频

ffmpeg多媒体

FFmpeg+ QT播放器

       流媒体客户端

RTMP推拉流项目实战

RTSP流媒体实战

HLS拉流分析

       流媒体服务器

SRS源码剖析协程

ZLMediaKit源码剖析

       WebRTC项目实战

WebRTC中级开发实践指南

WebRTC高级开发-SRS 4.0/5.0源码分析

WebRTC高级开发-MESH模型多人通话

WebRTC高级开发-Janus SFU模型多人通话

       Android NDK

Android NDK开发基础

Android FFmpeg编译和应用

Android RTMP推拉流

Android Ijkplayer源码分析

       iOS音视频开发

iOS FFmpeg 6.0编译和应用

iOS FFmpeg RTMP推拉流

VideoToolbox硬件编解码

iOS jkplayer编译和应用

iOS ijkplayer编译和应用

       音视频项目实战

       相关开源网站与地址

       本文涵盖音视频全栈开发技术,适合各类技术人员。

FFmpeg 简单介绍及使用

       FFmpeg是一套强大的音频、视频处理工具,提供录制、转换和流化功能,采用LGPL或GPL许可证。它包含的libavcodec库,具有从头开发的高效编解码能力,适用于各种操作系统,包括Linux、趣闲帮源码Windows和Mac OS X。该项目由Fabrice Bellard发起,主要由MPlayer项目团队维护。

       FFmpeg官网和Github项目地址分别为:ffmpeg.org/ 和 FFmpeg/FFmpeg。

       FFmpeg主要由三个部分组成:工具软件、SDK和源代码。工具软件包括ffmpeg.exe(转码器)、ffplay.exe(播放器)、ffserver.exe(流媒体服务器)和ffprobe.exe(分析器)。SDK则包含libavcodec(编码解码)、libavutil(常用工具)、libavformat(封装工具)、libavfilter(滤镜)、libavdevice(设备相关)和libswscale、libswresample(图像处理和音频转换)等库。源代码是所有功能实现的基础。

       安装FFmpeg(Windows版):下载静态版本,解压到C:\ffmpeg,添加环境变量(win):右键我的电脑 → 属性 → 高级系统设置 → 环境变量,添加路径 C:\ffmpeg\bin。在命令行输入ffmpeg -version验证安装。

       命令行使用:ffmpeg.exe用于转码,如将input.avi转为output.ts,设置视频码率为kbps;ffplay.exe用于播放文件,如播放test.avi;ffprobe.exe用于查看文件格式。

       详细使用方法请参考相关文档和教程,如CSDN博客等。

       技术交流群:,提供学习资源和讨论交流。

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

       视频播放器的核心原理在于控制音视频帧序列,其中ffplay作为FFmpeg自带的播放器,利用ffmpeg解码库和sdl库进行视频渲染。本文将通过分析ffplay源代码,深入解析音视频同步、播放控制的原理。

       FFmpeg的跨平台特性使得在PC端分析代码更为高效,本文则主要聚焦于ffplay for MFC的移植代码。首先,理解视频文件结构,每个MP4文件包含封装格式、比特率等信息,音视频被区分为独立的stream,并有各自的参数。解复用后,音频和视频帧转化为原始数据,进入播放流程,如图2所示。

       简化播放器,仅考虑视频解码和SDL显示,其流程图显示了FFmpeg初始化、读取并解码帧、然后渲染到窗口的过程。为了实现音视频同步,播放器需要处理帧率、音频采样率和视频帧显示时间的关系,以及不同流的帧数差异。

       文章接下来提出五个关键问题,涉及画面、字幕和声音的组合,音视频同步的具体机制,以及快进/后退操作的实现。ffplay通过定义VideoState结构体,将播放控制分发到不同线程,利用PTS时间戳确保音视频同步。视频播放器操作的实现包括控制暂停和播放,以及通过时间而非帧数进行快进/后退,以保持同步。

       分析ffplay代码时,整体结构包括定时器刷新、汇容克源码多线程解码和显示,以及关键控制函数的使用。在深入理解PTS和DTS后,我们看到ffplay如何动态调整PTS以实现音视频同步。最后,文章总结了通过ffplay源码学习到的基础概念和实用技巧,强调了从基础开始理解、代码架构分析和平台选择的重要性。

ijkplayer源码分析 视频解码流程

       深入ijkplayer源码,本文聚焦视频解码流程。在video_thread中,我们首先审视IJKFF_Pipenode结构体,定义于ff_ffpipenode.h和ff_ffpipenode.c。pipenode封装软解与硬解功能,初始化流程在stream_component_open中启动,调用pipeline.ffpipeline_open_video_decoder实现。

       在视频解码流程中,视频帧处理在video_thread线程下进行。从packet_queue读取视频packet,然后通过软/硬解码,最终将解码结果放入frame_queue。软解通过ffpipenode_ffplay_vdec.c实现,硬解则在ffpipenode_android_mediacodec_vdec.c中执行。不论软解还是硬解,解码后的结果均被引导至ff_ffplay.c#queue_picture进行队列化,准备渲染。

       对于LinuxC++音视频开发者,学习资源尤为关键。免费音视频开发资料、视频、学习路线图以及面试题,涵盖C/C++、Linux、FFmpeg、WebRTC、RTMP、NDK和Android音视频流媒体高级开发,免费提供给有需求者。学习交流君羊群,点击加入即可获取资料。

       最后,渲染流程在stream_open方法中启动,创建video_refresh_thread线程。此线程从frame_queue中读取视频帧,进行音视频同步后,完成渲染。此环节聚焦渲染流程,音视频同步细节暂不展开。

ffplay视频播放原理分析

       作者|赵家祝

       FFmpeg框架由命令行工具和函数库组成,ffplay是其中的一种命令行工具,提供了播放音视频文件的功能,不仅可以播放本地多媒体文件,还可以播放网络流媒体文件。本文从ffplay的整体播放流程出发,借鉴其设计思路,学习如何设计一款简易的播放器。

一、播放器工作流程

       在学习ffplay源码之前,为了方便理解,我们先宏观了解一下播放器在播放媒体文件时的工作流程。

       解协议:媒体文件在网络上传输时,需要经过流媒体协议将媒体数据分段成若干个数据包,这样就可以满足用户一边下载一边观看的需求,而不需要等整个媒体文件都下载完成才能观看。常见的流媒体协议有RTMP、HTTP、HLS、MPEG-DASH、MSS、HDS等。由于流媒体协议中不仅仅包含媒体数据,还包含控制播放的信令数据。因此,解协议是移除协议中的信令数据,输出音视频封装格式数据。

       解封装:封装格式也叫容器,就是将已经编码压缩好的视频流和音频流按照一定的格式放到一个文件中,常见的封装格式有MP4、FLV、MPEG2-TS、AVI、MKV、MOV等。解封装是将封装格式数据中的音频流压缩编码数据和视频流压缩编码数据分离,方便在解码阶段使用不同的解码器解码。

       解码:压缩编码数据是在原始数据基础上采用不同的编码压缩得到的数据,而解码阶段就是编码的逆向操作。常见的视频压缩编码标准有H./H.、MPEG-2、AV1、V8/9等,音频压缩编码标准有AAC、MP3等。解压后得到的视频图像数据是YUV或RGB,音频采样数据是PCM。

       音视频同步:解码后的视频数据和音频数据是独立的,在送给显卡和声卡播放前,需要将视频和音频同步,避免播放进度不一致。

二、main函数

       ffplay的使用非常简单,以ffplay-iinput.mp4-loop2为例,表示使用ffplay播放器循环播放input.mp4文件两遍。执行该命令时,对应的源码在fftools/ffplay.c中,程序入口函数是main函数。

       注:本文ffplay源码基于ffmpeg4.4。

       2.1环境初始化

       初始化部分主要调用以下函数:

       init_dynload:调用SetDllDirectory("")删除动态链接库(DLL)搜索路径中的当前工作目录,是Windows平台下的一种安全预防措施。

       av_log_set_flag:设置log打印的标记为AV_LOG_SKIP_REPEATED,即跳过重复消息。

       parse_loglevel:解析log的级别,会匹配命令中的-loglevel字段。如果命令中添加-report,会将播放日志输出成文件。

       avdevice_register_all:注册特殊设备的封装库。

       avformat_network_init:初始化网络资源,可以从网络中拉流。

       parse_options:解析命令行参数,示例中的-iinput.mp4和-loop2就是通过这个函数解析的,支持的选项定义在options静态数组中。解析得到的文件名、文件格式分别保存在全局变量input_filename和file_iformat中。

       2.2SDL初始化

       SDL的全称是SimpleDirectMediaLayer,是一个跨平台的多媒体开发库,支持Linux、Windows、MacOS等多个平台,实际上是对DirectX、OpenGL、Xlib再封装,在不同操作系统上提供了相同的函数。ffplay的播放显示是通过SDL实现的。

       main函数中主要调用了以下三个SDL函数:

       SDL_Init:初始化SDL库,传入的参数flags,默认支持视频、音频和定时器,如果命令中配置了-an则禁用音频,配置了-vn则禁用视频。

       SDL_CreateWindow:创建播放视频的窗口,该函数可以指定窗口的位置、大小,默认是*大小。

       SDL_CreateRenderer:为指定的窗口创建渲染器上下文,对应的结构体是SDL_Render。我们既可以使用渲染器创建纹理,也可以渲染视图。

       2.3解析媒体流

       stream_open函数是ffplay开始播放流程的起点,该函数传入两个参数,分别是文件名input_filename和文件格式file_iformat。下面是函数内部的处理流程:

       (1)初始化VideoState:VideoState是ffplay中最大的结构体,所有的视频信息都定义在其中。初始化VideoState时,先定义VideoState结构体指针类型的局部变量is,分配堆内存。然后初始化结构体中的变量,例如视频流、音频流、字幕流的索引,并赋值函数入参filename和iformat。

       (2)初始化FrameQueue:FrameQueue是解码后的Frame队列,Frame是解码后的数据,例如视频解码后是YUV或RGB数据,音频解码后是PCM数据。初始化FrameQueue时,会对VideoState中的pictq(视频帧队列)、subpq(字幕帧队列)、sampq(音频帧队列)依次调用frame_queue_init函数进行初始化。FrameQueue内部是通过数组实现了一个先进先出的环形缓冲区,windex是写指针,被解码线程使用;rindex是读指针,被播放线程使用。使用环形缓冲区的好处是,缓冲区内的元素被移除后,其它元素不需要移动位置,适用于事先知道缓冲区最大容量的场景。

       (3)初始化PacketQueue:PacketQueue是解码前的Packet队列,用于保存解封装后的数据。初始化PacketQueue时,会对VideoState中的videoq(视频包队列)、audio(音频包队列)、subtitleq(字幕包队列)依次调用packet_queue_init函数进行初始化。不同于FrameQueue,PacketQueue采用链表的方式实现队列。由于解码前的包大小不可控,无法明确缓冲区的最大容量,如果使用环形缓冲区,容易触发缓冲区扩容,需要移动缓冲区内的数据。因此,使用链表实现队列更加合适。

       (4)初始化Clock:Clock是时钟,在音视频同步阶段,有三种同步方法:视频同步到音频,音频同步到视频,以及音频和视频同步到外部时钟。初始化Clock时,会对VideoState中的vidclk(视频时钟)、audclk(音频时钟)、extclk(外部时钟)依次调用init_clock函数进行初始化。

       (5)限制音量范围:先限制音量范围在0~之间,然后再根据SDL的音量范围作进一步限制。

       (6)设置音视频同步方式:ffplay默认采用AV_SYNC_AUDIO_MASTER,即视频同步到音频。

       (7)创建读线程:调用SDL_CreateThread创建读线程,同时设置了线程创建成功的回调read_thread函数以及接收参数is(stream_open函数最开始创建的VideoState指针类型的局部变量)。如果线程创建失败,则调用stream_close做销毁逻辑。

       (8)返回值:将局部变量is作为函数返回值返回,用于处理下面的各种SDL事件。

       2.4SDL事件处理

       event_loop函数内部是一个for循环,使用SDL监听用户的键盘按键事件、鼠标点击事件、窗口事件、退出事件等。

三、read_thread函数

       read_thread函数的作用是从磁盘或者网络中获取流,包括音频流、视频流和字幕流,然后根据可用性创建对应流的解码线程。因此read_thread所在的线程实际上起到了解协议/解封装的作用。核心处理流程可以分为以下步骤:

       3.1创建AVFormatContext

       AVFormatContext是封装上下文,描述了媒体文件或媒体流的构成和基本信息。avformat_alloc_context函数用于分配内存创建AVFormatContext对象ic。

       拿到AVFormatContext对象后,在调用avformat_open_input函数打开文件前,需要设置中断回调函数,用于检查是否应该中断IO操作。

?ic->interrupt_callback.callback=decode_interrupt_cb;ic->interrupt_callback.opaque=is;

       decode_interrupt_cb内部返回了一个VideoState的abort_request变量,该变量在调用stream_close函数关闭流时会被置为1。

       3.2打开输入文件

       在准备好前面的一些赋值操作后,就可以开始根据filename打开文件了。avformat_open_input函数用于打开一个文件,并对文件进行解析。如果文件是一个网络链接,则发起网络请求,在网络数据返回后解析音频流、视频流相关的数据。

       3.3搜索流信息

       搜索流信息使用avformat_find_stream_info函数,该从媒体文件中读取若干个包,然后从其中搜索流相关的信息,最后将搜索到的流信息放到ic->streams指针数组中,数组的大小为ic->nb_streams。

       由于在实际播放过程中,用户可以指定是否禁用音频流、视频流、字幕流。因此在解码要处理的流之前,会判断对应的流是否处于不可用状态,如果是可用状态则调用av_find_best_stream函数查找对应流的索引,并保存在st_index数组中。

       3.4设置窗口大小

       如果找到了视频流的索引,则需要渲染视频画面。由于窗体的大小一般使用默认值*,这个值和视频帧真正的大小可能是不相等的。为了正确显示承载视频画面的窗体,需要计算视频帧的宽高比。调用av_guess_sample_aspect_ration函数猜测帧样本的宽高比,调用set_default_window_size函数重新设置显示窗口的大小和宽高比。

       3.5创建解码线程

       根据st_index判断音频流、视频流、字幕流的索引是否找到,如果找到了就依次调用stream_component_open创建对应流的解码线程。

       3.6解封装处理

       接下来是一个for(;;)循环:

       (1)响应中断停止、暂停/继续、Seek操作;

       (2)判断PacketQueue队列是否满了,如果满了就休眠ms,继续循环;

       (3)调用av_read_frame从码流中读取若干个音频帧或一个视频帧;

       (4)从输入文件中读取一个AVPacket,判断当前AVPacket是否在播放时间范围内,如果是则调用packet_queue_put函数,根据类型将其放在音频/视频/字幕的PacketQueue中。

四、stream_component_open函数

       3.5小节讲到,stream_component_open函数负责创建不同流的解码线程。那么它是如何创建解码线程的呢?

       4.1创建AVCodecContext

       AVCodecContext是编解码器上下文,保存音视频编解码相关的信息。使用avcodec_alloc_context3函数分配空间,使用avcodec_free_context函数释放空间。

       4.2查找解码器

       根据解码器的id,调用avcodec_find_decoder函数,查找对应的解码器。与之类似的一个函数是avcodec_find_encoder,用于查找FFmpeg的编码器。两个函数返回的结构体都是AVCodec。

       如果指定了解码器名称,则需要调用avcodec_find_decoder_by_name函数查找解码器。

       不管是哪种方式查找解码器,如果没有找到解码器,都会抛异常退出流程。

       4.3解码器初始化

       找到解码器后,需要打开解码器,并对解码器初始化,对应的函数是avcodec_open2,该函数也支持编码器的初始化。

       4.4创建解码线程

       判断解码类型,创建不同的解码线程。

switch(avctx->codec_type){ caseAVMEDIA_TYPE_AUDIO://音频...if((ret=decoder_init(&is->auddec,avctx,&is->audioq,is->continue_read_thread))<0)gotofail;...if((ret=decoder_start(&is->auddec,audio_thread,"audio_decoder",is))<0)gotoout;...caseAVMEDIA_TYPE_VIDEO://视频...if((ret=decoder_init(&is->viddec,avctx,&is->videoq,is->continue_read_thread))<0)gotofail;if((ret=decoder_start(&is->viddec,video_thread,"video_decoder",is))<0)gotoout;...caseAVMEDIA_TYPE_SUBTITLE://字幕...if((ret=decoder_init(&is->subdec,avctx,&is->subtitleq,is->continue_read_thread))<0)gotofail;if((ret=decoder_start(&is->subdec,subtitle_thread,"subtitle_decoder",is))<0)gotoout;...}

       线程创建在decoder_start函数中,依然使用SDL创建线程的方式,调用SDL_CreateThread函数。

五、video_thread函数

       视频解码线程从视频的PacketQueue中不断读取AVPacket,解码完成后将AVFrame放入视频FrameQueue。音频的解码实现和视频类似,这里仅介绍视频的解码过程。

       5.1创建AVFrame

       AVFrame描述解码后的原始音频数据或视频数据,通过av_frame_alloc函数分配内存,通过av_frame_free函数释放内存。

       5.2视频解码

       开启for(;;)循环,不断调用get_video_frame函数解码一个视频帧。该函数主要调用了decoder_decode_frame函数解码,decoder_decode_frame函数对音频、视频、字幕都进行了处理,主要依靠FFmpeg的avcodec_receive_frame函数获取解码器解码输出的数据。

       拿到解码后的视频帧后,会根据音视频同步的方式和命令行的-framedrop选项,判断是否需要丢弃失去同步的视频帧。

       命令行带-framedrop选项,无论哪种音视频同步机制,都会丢弃失去同步的视频帧。

       命令行带-noframedrop选项,无论哪种音视频同步机制,都不会丢弃失去同步的视频帧。

       命令行不带-framedrop或-noframedrop选项,若音视频同步机制为同步到视频,则不丢弃失去同步的视频帧,否则会丢弃失去同步的视频帧。

       5.3放入FrameQueue

       调用queue_picture函数,将AVFrame放入FrameQueue。该函数内部调用了frame_queue_push函数,采用了环形缓冲区的处理方式,对写指针windex累加。

staticvoidframe_queue_push(FrameQueue*f){ if(++f->windex==f->max_size)f->windex=0;SDL_LockMutex(f->mutex);f->size++;SDL_CondSignal(f->cond);SDL_UnlockMutex(f->mutex);}六、音视频同步

       ffplay默认采用将视频同步到音频的方式,分以下三种情况:

       如果视频和音频进度一致,不需要同步;

       如果视频落后音频,则丢弃当前帧直接播放下一帧,人眼感觉跳帧了;

       如果视频超前音频,则重复显示上一帧,等待音频,人眼感觉视频画面停止了,但是有声音在播放;

       ffplay视频同步到音频的逻辑在视频播放函数video_refresh中实现。该函数的调用链是:main()->event_loop()->refresh_loop_wait_event()->video_refresh。

       6.1判断播放完成

       调用frame_queue_nb_remaing函数计算剩余没有显示的帧数是否等于0,如果是,则不需要走剩下的步骤。计算过程比较简单,用FrameQueue的size-rindex_shown,size是FrameQueue的大小,rindex_shown表示rindex指向的节点是否已经显示,如果已经显示则为1,否则为0。

       6.2播放序列匹配

****

       分别调用frame_queue_peek_last和frame_queue_peek函数从FrameQueue中获取上一帧和当前帧,上一帧是上次已经显示的帧,当前帧是当前待显示的帧。

       (1)比较当前帧和当前PacketQueue的播放序列serial是否相等:

       如果不等,重试视频播放的逻辑;

       如果相等,则进入(2)流程判断;

       注:serial是用来区分是不是连续的数据,如果发生了seek,会开始一个新的播放序列,

       (2)比较上一帧和当前帧的播放序列serial是否相等:

       如果不相等,则将frame_timer更新为当前时间;

       如果相等,不处理并进入下一流程

       6.3判断是否重复上一帧

       (1)将上一帧lastvp和当前帧vp传入vp_duration函数,通过vp->pts-lastvp->pts计算上一帧的播放时长。

       注:pts全称是PresentationTimeStamp,显示时间戳,表示解码后得到的帧的显示时间。

       (2)在compute_target_delay函数中,调用get_clock函数获取视频时钟,调用get_master_clock函数获取同步时钟,计算两个时钟的差值,根据差值计算需要delay的时间。

       (3)如果当前帧播放时刻(is->frame_timer+delay)大于当前时刻(time),表示当前帧的播放时间还没有到,相当于当前视频超前音频了,则需要将上一帧再播放一遍。

last_duration=vp_duration(is,lastvp,vp);delay=compute_target_delay(last_duration,is);time=av_gettime_relative()/.0;if(time<is->frame_timer+delay){ *remaining_time=FFMIN(is->frame_timer+delay-time,*remaining_time);gotodisplay;}

       6.4判断是否丢弃未播放的帧

       如果当前队列中的帧数大于1,则需要考虑丢帧,只有一帧的时候不考虑丢帧。

       (1)调用frame_queue_peek_next函数获取下一帧(下一个待显示的帧),根据当前帧和下一帧计算当前帧的播放时长,计算过程和6.3相同。

       (2)满足以下条件时,开始丢帧:

       当前播放模式不是步进模式;

       丢帧策略生效:framedrop>0,或者当前音视频同步策略不是音频到视频。

       当前帧vp还没有来得及播放,但是下一帧的播放时刻(is->frame_timer+duration)已经小于当前系统时刻(time)了。

       (3)丢帧时,将is->frame_drops_late++,并调用frame_queue_next函数将上一帧删除,更新FrameQueue的读指针rindex和size。

if(frame_queue_nb_remaining(&is->pictq)>1){ Frame*nextvp=frame_queue_peek_next(&is->pictq);duration=vp_duration(is,vp,nextvp);if(!is->step&&(framedrop>0||(framedrop&&get_master_sync_type(is)!=AV_SYNC_VIDEO_MASTER))&&time>is->frame_timer+duration){ is->frame_drops_late++;frame_queue_next(&is->pictq);gotoretry;}}七、渲染

       ffplay最终的图像渲染是由SDL完成的,在video_display中调用了SDL_RenderPresent(render)函数,其中render参数是最开始在main函数中创建的。在渲染之前,需要将解码得到的视频帧数据转换为SDL支持的图像格式。转换过程在upload_texture函数中实现,细节不在此处分析。

       音频类似,如果解码得到的音频不能被SDL支持,需要对音频进行重采样,将音频帧格式转换为SDL支持的格式。

八、小结

       本文从整体播放流程出发,介绍了ffplay播放器播放媒体文件的主要流程,不深陷于代码细节。同时,对FFmpeg的一些常用函数有了一些了解,对我们自己手写一个简单的播放器有很大的帮助。

----------END----------

搜索关键词:新浪上传源码