1. 整体架构

1.1 架构框图

这个FFmpeg播放器采用多线程架构,将媒体处理流程分为解复用、解码和渲染三个主要阶段,通过队列机制实现各阶段的解耦和异步处理。

配套视频讲解:音视频入门必备项目-最新FFmpeg7.1播放器开发(播放器源码领取方式见视频)

1.2 主要组件

1. Main函数(主控制)

  • 负责初始化和协调各个模块

  • 创建和管理其他组件的生命周期

  • 处理整体程序流程控制

2. DemuxThread(解复用线程)

  • 继承自Thread基类

  • 负责打开媒体文件,分离音视频流

  • 将分离的音视频数据包放入相应的AVPacketQueue

3. DecodeThread(解码线程)

  • 继承自Thread基类

  • 从AVPacketQueue获取压缩数据包

  • 将压缩数据解码为原始音视频帧

  • 将解码后的帧放入AVFrameQueue

4. AudioOutput(声音输出)

  • 使用SDL音频库播放音频

  • 从AVFrameQueue获取音频帧

  • 进行必要的音频重采样

  • 维护主时钟,提供音视频同步基准

5. VideoOutput(画面输出)

  • 使用SDL视频库显示视频

  • 从AVFrameQueue获取视频帧

  • 处理用户界面事件

  • 根据AVSync提供的时钟控制视频帧的显示时机

6. AVSync(音视频同步)

  • 维护音频时钟

  • 提供同步机制,确保音视频同步播放

7. AVPacketQueue(数据包队列)

  • 存储解复用后的压缩音视频数据包

  • 连接DemuxThread和DecodeThread

  • 提供线程安全的队列操作

8. AVFrameQueue(帧队列)

  • 存储解码后的原始音视频帧

  • 连接DecodeThread和输出模块

  • 提供线程安全的队列操作

1.3 基础架构

1. Thread(线程基类)

  • 提供基本的线程管理功能

  • 实现启动、停止等通用线程控制方法

  • 为派生线程类提供统一的接口

1.4 外部库依赖

1. FFmpeg库

  • 提供媒体文件解析、解码等功能

  • 包括libavformat、libavcodec等组件

2. SDL库

  • 提供跨平台的音视频输出能力

  • 处理用户界面和事件

这个架构采用了生产者-消费者模式,通过队列解耦各个模块,使得解复用、解码和渲染可以在不同的线程中并行执行,提高播放性能和流畅度。同时,通过明确的音视频同步机制,确保了播放的准确性。

2. 详细流程

播放器的运行流程从初始化开始,到资源释放结束,中间经过多个处理阶段。

2.1 main函数流程图

main.cpp main()函数

2.2 流程说明

1. 初始化:创建并初始化必要的队列、线程和组件

2. 媒体处理:

  • 解复用线程从文件读取数据包

  • 解码线程将数据包解码为帧

  • 音视频输出模块将帧渲染输出

3. 用户交互:处理用户事件,如暂停、退出等

4. 资源释放:程序结束时按正确顺序释放资源,避免内存泄漏

3. 队列设计

队列是连接各处理阶段的关键组件,负责数据缓冲和线程间通信。

3.1 队列框图

3.2 队列设计原理

1. 模板设计:使用C++模板实现通用队列结构,提高代码复用率

2. 线程安全:使用互斥锁和条件变量保证多线程环境下的数据一致性

3. 特化实现:为AVPacket和AVFrame提供特化队列,处理FFmpeg资源的引用计数

4. 终止机制:通过abort标志控制队列终止,实现优雅退出

5. 资源管理:

  • AVPacketQueue负责管理AVPacket资源,使用av_packet_free释放

  • AVFrameQueue负责管理AVFrame资源,使用av_frame_free释放

4. 线程设计

播放器采用多线程架构,通过基类Thread实现通用线程控制,派生类实现具体功能。

4.1 线程框图

4.2 线程设计原理

1. 基类封装:Thread基类封装线程创建、启动和停止的通用逻辑

2. 虚函数机制:通过纯虚函数Run()要求派生类实现具体业务逻辑

3. 状态控制:使用abort_标志控制线程循环状态,实现优雅退出

4. 资源管理:

  • DemuxThread管理文件读取和格式解析资源(AVFormatContext)

  • DecodeThread管理解码器资源(AVCodecContext)

5. 线程协作:通过队列实现线程间数据传递,解耦生产者和消费者

5.音频输出设计

声音输出模块负责从帧队列获取音频帧,进行必要的重采样,并通过SDL输出音频。

5.1音频输出框图

5.2音频输出原理

1. 初始化流程:

  • 初始化SDL音频子系统

  • 设置音频参数(采样率、通道数、格式)

  • 设置音频回调函数

  • 创建重采样上下文(如需要)

2. 回调机制:

  • SDL音频系统在需要数据时调用设置的回调函数

  • 回调函数从帧队列获取音频帧

  • 根据需要进行重采样(使用SwrContext)

  • 将处理后的音频数据填充到SDL提供的缓冲区

3. 音频时钟:

  • 以音频PTS作为主时钟

  • 在每次回调中更新音频时钟

  • 作为视频同步的基准

4. 资源管理:

  • 管理重采样上下文(SwrContext)

  • 管理音频缓冲区

  • 在DeInit和析构函数中释放资源

6.视频输出设计

画面输出模块负责从帧队列获取视频帧,与音频同步,并通过SDL渲染到屏幕。

6.1视频输出框图

6.2视频输出原理

1. 初始化流程:

  • 初始化SDL视频子系统

  • 创建窗口和渲染器

  • 创建纹理用于视频渲染

2. 主循环机制:

  • 处理SDL事件(如退出、按键等)

  • 刷新视频帧

  • 控制帧率以实现音视频同步

3. 同步策略:

  • 比较视频帧PTS与音频时钟

  • 如果视频超前,等待适当时间再显示

  • 如果视频滞后,立即显示并可能丢帧

4. 渲染过程:

  • 将YUV数据更新到SDL纹理

  • 将纹理渲染到窗口

  • 释放已显示的帧

5. 资源管理:

  • 管理SDL资源(窗口、渲染器、纹理)

  • 在DeInit和析构函数中释放资源

7. 音视频同步

音视频同步是播放器的核心功能,确保音频和视频以正确的时间关系播放。

7.1 同步框图

7.2 同步原理

1. 主时钟选择:

  • 使用音频PTS作为主时钟基准

  • 音频在回调函数中更新时钟值

2. 视频同步策略:

  • 计算视频帧PTS与当前音频时钟的差值

  • 差值为正(视频超前):延迟显示

  • 差值为负(视频滞后):立即显示

  • 差值过大:考虑跳帧或重复帧

3. 时钟管理:

  • AVSync类提供时钟读写接口

  • 音频线程设置时钟

  • 视频线程读取时钟

8. 数据流向

播放器中的数据从媒体文件读取,经过多个处理阶段,最终输出到显示设备。

8.1 数据流图

8.2 数据流说明

1. 解复用阶段:

  • DemuxThread读取媒体文件

  • 分离音视频数据包

  • 将音视频包放入对应的AVPacketQueue

2. 解码阶段:

  • DecodeThread从AVPacketQueue获取数据包

  • 使用FFmpeg解码器解码数据包

  • 生成音视频帧并放入AVFrameQueue

3. 渲染阶段:

  • AudioOutput/VideoOutput从AVFrameQueue获取帧

  • 处理帧数据(重采样、格式转换等)

  • 通过SDL渲染到输出设备

9. 内存管理与资源释放

良好的内存管理和资源释放策略是保证播放器稳定性的关键。

9.1 内存管理策略

1. FFmpeg资源管理:

  • 使用适当的FFmpeg API释放资源(av_frame_free, av_packet_free等)

  • 在队列的release()方法中处理队列中残留的资源

2. SDL资源管理:

  • 在析构函数或DeInit方法中释放SDL资源

  • 按正确顺序释放(texture → renderer → window)

3. 线程资源管理:

  • 使用Thread::Stop()方法安全停止线程

  • 在Stop()中等待线程结束(join)后释放线程资源

9.2 资源释放顺序

为避免资源依赖问题,释放顺序非常重要:

1. 首先停止所有线程(先解码线程,再解复用线程)

2. 释放音视频输出资源

3. 清空并终止所有队列

4. 删除线程对象

5. 其它资源清理

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐