搞音视频开发好些年,分享过许多博客文章,比如:前几年发布的《FFmpeg Tips》系列,《Android 音频开发》系列,《直播疑难杂症排查》系列等等。最近想把多年来开发和优化播放器的经验也分享出来,同时也考虑把自己业余时间开发的基于 ffmpeg 的播放器内核开源出来,希望能帮助到音视频领域的初学者。第一期文章要推出的内容主要涉及到播放器比较核心的几个技术点,大概的目录如下:

1. 播放器技术分享(1): 架构设计2. 播放器技术分享(2):缓冲区管理3. 播放器技术分享(3):音画同步4. 播放器技术分享(4):首开时间5. 播放器技术分享(5):延时优化

本篇是系列文章的第一篇,主要聊一聊播放器的架构设计。

1 概述

首先,我们了解一下播放器的定义是什么 ?

“播放器,是指能播放以数字信号形式存储的视频或音频文件的软件,也指具有播放视频或音频文件功能的电子器件产品。”  —— 《百度百科》

我的解读如下:“播放器,是指能读取、解析、渲染存储在本地或者服务器上的音视频文件的软件,或者电子产品。”

归纳起来,它主要有如下 3 个方面的功能特性:

  1. 读取(IO):“获取” 内容 -> 从 “本地” or “服务器” 上获取
  2. 解析(Parser):“理解” 内容 -> 参考 “格式&协议” 来 “理解” 内容
  3. 渲染(Render):“展示” 内容 -> 通过扬声器/屏幕来 “展示” 内容

把这 3 个方面的功能串起来,就构成了整个播放器的数据流,如图所示:

IO:负责数据的读取。从数据源读取数据有多种标准协议,比如常见的有:File,HTTP(s),RTMP,RTSP 等

Parser & Demuxer:负责数据的解析。音视频数据的封装格式,都有着各种业界标准,只需要参考这些行业标准文档,即可解析各种封装格式,比如常见的格式:mp4,flv,m3u8,avi 等

Decoder:其实也属于数据解析的一种,只不过更多的是负责对压缩的音视频数据进行解码,拿到原始的 YUV 和 PCM 数据,常见的视频压缩格式如:H.264、MPEG4、VP8/VP9,音频压缩格式如 G.711、AAC、Speex 等

Render:负责视频数据的绘制和渲染,是一个平台相关的特性,不同的平台有不同的渲染 API 和方法,比如:Windows 的 DDraw/DirectSound,Android 的 SurfaceView/AudioTrack,跨平台的如:OpenGL 和 ALSA 等

下面我们逐一剖析一下播放器整个数据流的每一个模块的输入和输出,并一起设计一下每一个模块的接口 API。

2 模块设计

2.1 IO 模块

IO 模块的输入:数据源的地址(URL),这个 URL 可以是一个本地的文件路径,也可以是一个网络的流地址。

IO 模块的输出:二进制的数据,即通过 IO 协议读取的音视频二进制数据。

视频数据源的 URL 示例如下:

file:///c:/WINDOWS/clock.avi
rtmp://live.hkstv.hk.lxdns.com/live/hks
http://www.w3school.com.cn/i/movie.mp4
http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8

综上,播放器 IO 模块的接口设计如下所示:

Open/Close 方法主要是用于打开/关闭视频流,播放器内核可以通过 URL 的头(Schemes)知道需要采用哪一种 IO 协议来拉流(如:FILE/RTMP/HTTP),然后通过继承本接口的子类去完成实际的协议解析和数据读取。

IO 模块读取数据,则定义了 2 个方法,Read 方法用于顺序读取数据,ReadAt 用于从指定的 Offset 偏移的位置读取数据,后者主要用于文件或者视频点播,为播放器提供 Seek 能力。

对于网络流,可能出现断线的情况,因此独立出一个 Reconnect 接口,用于提供重连的能力。

2.2 解析模块

从 IO 模块读到的音视频二进制数据,其实都是用如 mp4、flv、avi 等格式封装起来的,如果想分离出音频包和视频包,则需要通过一个 Parser & Demuxer 模块进行解析。

解析模块的输入:由 IO 模块读取出来的 bytes 二进制数据
解析模块的输出:音视频的媒体信息,未解码的音频数据包,未解码的视频数据包

音视频的媒体信息主要包括如下内容:

  • 视频时长、码率、帧率等
  • 音频的格式:编码算法,采样率,通道数等
  • 视频的格式:编码算法,宽高,长宽比等

综上,解析模块的接口设计如下图所示:

创建好解析对象后,通过 Parse 函数输入音视频数据解析出基本的音视频媒体信息,通过 Read 函数读取分离的音视频数据包,然后分别送入音频和视频×××,通过 Get 方法获取各种音视频参数信息。

2.3 解码模块

解析模块分离好音频和视频包以后,就可以分配送入到音频×××和视频×××了

解码模块的输入:未解压的音频/视频包
解码模块的输出:解压好的音频/图像的原始数据,即 PCM 和 YUV

由于音视频的解码,往往不是每送入×××一帧数据就一定能输出一帧数据,而是经常需要缓存几帧参考帧才能拿到输出,所以编码器的接口设计常常采用一种 “生产者-消费者” 模型,通过一个公共的 buffer 队列来串联 “生产者-消费者”,如下图所述(截取自 Android MediaCodec 编解码库的设计):

综上,解码模块的接口设计如下所示:

解析模块输出的媒体信息,包含有该使用什么类型的音频/视频×××,可利用该信息完成×××的初始化。剩下的过程,就是通过 Queue 和 Dequeue 不断跟×××交互,送入未解码的数据,拿到解码后的数据了。

2.4 渲染模块

×××输出原始的图像和音频数据后,下一步就是送入到渲染模块进行图像的渲染和音频的播放了。

一般视频数据渲染是输出到显卡展示在窗口上,音频数据则是送入声卡利用扬声器播放出来。虽然不同平台的窗口绘制和扬声器播放的系统层 API 都不太一样,但是接口层面的流程也都差不多,如图所示:

对于视频渲染而言,流程则是:Init 初始化 -> SetView 设置窗口对象 -> SetParam 设置渲染参数 -> Render 执行渲染/绘制

对于音频播放而言,流程则是:Init 初始化 -> SetParam 设置播放参数 -> Render 执行播放操作

2.5 把模块串起来

如图所示,把各个模块这样串起来后,就是播放器的整个数据流走向了,但这是一个单线程的结构,从 IO 读到数据后,立马送入解析 -> 解码 -> 渲染,这样的单线程结构的播放器设计,会存在如下几个问题:

 1. 音视频分离后 -> 解码 -> 播放,中间无法插入逻辑进行音画同步2. 无数据缓冲区,一旦网络/解码抖动 -> 导致频繁的卡顿3. 单线程运行,没有充分利用 CPU 多核

要想解决单线程结构的问题,可以以数据的 “生产者 - 消费者” 为边界,添加数据缓冲区,将单线程模型,改造为多线程模型(IO 线程、解码线程、渲染线程),如图所示:

改造为多线程模型后,其优势如下:

 4. 帧队列(Packet Queue):可抵抗网络抖动5. 显示队列(Frame Queue):可抵抗解码/渲染的抖动6. 渲染线程:添加 AV Sync 逻辑,可支持音画同步的处理7. 并行工作,高效,充分利用多核 CPU

注:我们将在下一篇文章专门来聊一聊这 2 个新增的缓冲区该如何设计和管理。

3 播放器 SDK 接口设计

前面详细介绍了播放器内涵的关键架构设计和数据流,如果期望以该播放器内核作为 SDK 给 APP 提供底层能力的话,还需要设计一套易用的 API 接口,这套 API 接口,其实可抽象为如下 5 大部分:

 1. 创建/销毁播放器2. 配置参数(如:窗口句柄、视频 URL、循环播放等)3. 发送命令(如:初始化,开始播放,暂停播放,拖动,停止等)4. 音视频数据回调(如:解码后的音视频数据回调)5. 消息/状态消息回调(如:缓冲开始/结束、播放完成等)

综上,播放器常见接口列表如下:

 1. Create/Release/Reset2. SetDataSource/SetOptions/SetView/SetVolume3. Prepare/Start/Pause/Stop/SeekTo4. SetXXXListener/OnXXXCallback
4 播放器的状态模型

总体来说,播放器其实是一个状态机,被创建出来了以后,会根据应用层发送给它的命令以及自身产生的事件在各个状态之间切换,可以用如下这张图来展示:

播放器一共有 9 种状态,其中,Idle 是创建后/重置后的到达的初始状态,End 和 Error 分别是主动销毁播放器和发生错误后进入的最终状态(通过 reset 重置后可恢复 Idle 状态)

其他的状态切换和达到方式,图中已经标注得比较清楚了,这里就不再赘述了。

5 总结

播放器的架构设计,就分享到这里了,有些内容没有展开讲,但比较关键的点应该都基本阐述清楚了。

转载于:https://blog.51cto.com/ticktick/2324928

播放器技术分享(1):架构设计相关推荐

  1. 播放器技术分享(2):缓冲区管理

    搞音视频开发好些年,分享过许多博客文章,比如:前几年发布的<FFmpeg Tips>系列,<Android 音频开发>系列,<直播疑难杂症排查>系列等等.最近想把多 ...

  2. 播放器/短视频 SDK 架构设计,点播服务 (Demo)

    在Android中,我们可以直接使用MediaRecord来进行录像,但是在很多适合MediaRecord并不能满足我们的需求,比如我们需要对录制的视频加水印或者其他处理后,所有的平台都按照同一的大小 ...

  3. 播放器技术演进与探索,Web开播系统的技术演进,大屏终端音视频播放,音视频效果插件开放平台建设...

    播放器技术演进与探索 Topic <QPlayer2播放器-用扩展性支撑起未来需求> 陈军奇  七牛云 资深开发工程师.播放器负责人 随着这些年音视频的应用场景越来越丰富,用户对于播放器能 ...

  4. 小米资深工程师瞿晋萍(男):米聊服务器的技术选型和架构设计

    小米资深工程师瞿晋萍:米聊服务器的技术选型和架构设计 - 资讯频道 - CSDN.NET 小米资深工程师瞿晋萍:米聊服务器的技术选型和架构设计 2012-07-07 11:04 | 238次阅读 | ...

  5. 客户端GUI测试技术和自动化测试架构设计简谈

    客户端GUI测试技术和自动化测试架构设计简谈 http://www.cnblogs.com/wiki-royzhang/p/3785033.html 客户端GUI测试技术和自动化测试架构设计简谈 客户 ...

  6. j2me在线音乐播放器技术分析

    j2me在线音乐播放器技术分析 1.在线音乐播放器: 音乐资源存储在服务器端,手机客户端通过无线网络读取服务器端音乐资源进行播放,播放器具有播放.暂停.快进.快退.循环播放.显示播放进度.时间等功能. ...

  7. css3仿QQ网页播放器及分享功能实现

    css3仿QQ网页播放器及分享功能实现  <!doctype html> <html lang="en">  <head>  <!--声明 ...

  8. 米聊服务器的技术选型和架构设计

    http://wenku.it168.com/d_000434507.shtml米聊服务器的技术选型和架构设计

  9. 电子商务平台技术选型和架构设计

    作者:禅与计算机程序设计艺术 1.简介 在这个时代,在线购物网站和电子商务平台已经成为促进互联网经济增长.服务用户和客户的重要平台.如何搭建一个具备高可用性.安全可靠的电商系统,是一个复杂的工程.作为 ...

最新文章

  1. HIVE入门_3_数据导入导出
  2. 华为服务器修改SN,服务器渠道货SN配置
  3. 院士领衔,大咖云集!航天智慧物流单项赛决赛倒计时2天!
  4. 简析ThinkSNS+ 计算字符显示长度的方法!【社交系统研发日记】
  5. ExecutorService对象的shutdown()和shutdownNow()的区别
  6. 当同时使用bootstrap-datepicker.js和jquery.validate.js这两款插件,至少要选择两次时间,才能验证成功的问题...
  7. c语言中fr,关于frwite()函数的一个问题,弄了好久就是不行,求解啊!
  8. c++矩阵类_数据结构-JavaScript矩阵类的设计与实现
  9. 使用Open Liberty的开发模式最大程度地缩短周转时间
  10. _编程语言_C++_Lambda函数与表达式
  11. 小米集团公布新任CFO人选:系原瑞信亚太区高管
  12. Linux下HTTP Server
  13. 【python】rvm库安装问题解决
  14. PHP之JWT接口鉴权(一)
  15. 为什么调试的时候需要编译选项中添加 -g,readelf命令.
  16. AMEsim:车辆动力经济性建模分析三个分享点
  17. 基于SSM的大学生助学贷款管理系统
  18. matlab绘制图像的直方图、杆状图和折线图等
  19. 安装黑苹果系统前请看:macOS Mojave 的硬件兼容性列表
  20. IDEA社区版详细安装2022最新版(保姆式)

热门文章

  1. 计算机组成原理字发生器及跑马灯,060仪器(标书)
  2. 利用LaTeX写学校的学位论文模板(一)
  3. NLP中的对话机器人——预训练基准模型
  4. 园林景观设计要学计算机吗,成为一名专业的园林景观设计师到底需要学什么?...
  5. mysql数据什么格式_Mysql数据格式
  6. B2B,B2C和C2C的区别
  7. 如何在html自动播放提示音,ajax实现web页面的消息实时提醒时播放提示音
  8. LaneCatTM网猫软件简介和主要功能_20060101
  9. m基于CNN卷积网络和GEI步态能量图的步态识别算法MATLAB仿真,测试样本采用现实拍摄的场景进行测试,带GUI界面
  10. 软件测试工程师项目业绩,如何有效的对测试人员进行业绩考核?