快手 · 移动端音视频开发实战
展晓凯
快手回森技术负责人
12246 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 25 讲
快手 · 移动端音视频开发实战
15
15
1.0x
00:00/00:00
登录|注册

08|播放器项目实战(二):底层核心模块的实现

你好,我是展晓凯。今天我们来学习实现播放器中的各个核心模块。
上一节课我们设计了播放器的架构,架构中包含各种模块,其中解码模块、音视频播放模块以及画面播放模块是架构中的核心模块。为了让我们设计的架构快速落地,这节课我会带你来分别实现这三个核心模块。我们先来看看解码模块是如何实现的吧。

解码模块的实现

我们一起来构建输入模块,也就是来做类图中的 VideoDecoder 类的实现。这里我们使用 FFmpeg 这个开源库来完成输入模块的协议解析、封装格式拆分、解码操作等行为,你可以看一下 FFmpeg 在解码场景下的核心流程。
FFmpeg在解码场景下的核心流程
整体的运行流程分为以下几个阶段:
建立连接、准备资源阶段:使用 openInput 方法向外提供接口。
读取数据进行拆封装、解码、处理数据阶段:使用 decodeFrames 方法向外提供接口。
释放资源阶段:使用 releaseResource 方法向外提供接口。
以上就是我们输入端的整体流程,其中第二个阶段是一个循环并且会放在单独的线程中来运行。接下来我们具体看一下这个类中最重要的几个接口是如何设计与实现的。

openInput

这个方法的职责是建立与媒体资源的连接通道,并且分配一些全局需要用到的资源,最后将建立连接通道与分配资源的结果返回给调用端。这个方法的实现主要分为三个核心部分。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入介绍了播放器项目实战中底层核心模块的实现过程,包括解码模块、音频播放模块和视频播放模块的具体实现细节。作者首先详细讲解了解码模块的实现,使用FFmpeg开源库完成输入模块的协议解析、封装格式拆分、解码操作等行为。在音频播放模块的实现中,强调了Android和iOS平台的实现差异,并重点放在了接口设计与内部实现上。在视频播放模块的实现中,依赖于平台构建自己的上下文环境以及窗口管理。文章通过具体的代码实现和技术细节,深入浅出地介绍了播放器项目中底层核心模块的实现过程,对于想要深入了解播放器技术实现的读者具有很高的参考价值。在Android平台上,使用OpenGL ES渲染视频画面需要单独开辟一个线程,并为这个线程绑定一个OpenGL ES的上下文。而在iOS平台上,使用OpenGL ES需要继承自UIView的VideoOutput类,并重写父类的layerClass方法,返回CAEAGLLayer类型。整体而言,本文通过具体的代码实现和技术细节,深入浅出地介绍了播放器项目中底层核心模块的实现过程。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《快手 · 移动端音视频开发实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(4)

  • 最新
  • 精选
  • 大土豆
    demux感觉某种程度上算不上IO密集型操作,如果是网络资源,网络IO这块是IO密集型操作,但这块不包含在demux的范围,网络模块读取数据输入给demux模块,比如socket read读取了1024 * 20字节,输入给demux模块,demux负责解析,比如20个字节是头部啦,先解析出来各种元数据,然后第21-1024字节是一帧数据,解析完存下来,再解析下段,这种还是属于CPU密集型的操作。

    作者回复: 你的理解是正确的,我这里没有拆得太细,我把protocol与muxer统一的都叫“demux”了,在这里只是给大家提出要把av_read_frame这个既有Protocol的IO又有demuxer的操作是占用网络IO资源更多,独立出一个线程会更合适;解码部分独立成一个线程,不要把这两个放到一个线程中。

    2022-08-13归属地:北京
    1
  • peter
    请教老师几个问题: Q1:文中第一个图,就是FFmpeg的处理流程图,Packet为Video时,调用的是“avcodec_decode_audio2”吗?从名字看,似乎是和音频有关。 Q2:“视频部分只需要解码一次,就可以得到 AVFrame 的视频裸数据(一般是 YUV 格式)”,从这句话的上下文来看,这句话中的AVFrame应该是AVPacket吧。这里用AVFrame,是笔误?还是说两者等价? Q3:“Android 平台我们选用 OpenSL ES 来渲染音频”。既然引入了FFmpeg,为什么不用FFmpeg的音频渲染方法?后面视频渲染部分,采用OpenGL ES,也没有采用FFmpeg。我的理解是:1 OpenSL ES和FFmpeg都可以完成音频渲染,选用OpenSL ES只是一种选择而已。2 OpenGL ES和FFmpeg都可以完成视频渲染,选用OpenGL ES只是一种选择而已。3 引入FFmpeg主要目的是为了解码,而不是音视频渲染。音视频的处理,采用OpenSL/GL ES就足够了。我的理解是否对? Q4:手机上YUV格式不能用于实际显示,必须转换为RGBA才能显示,对吗? YUV是通用的视频表示格式,在在手机上,这种数据并不能用来在显卡上显示,需要转换为RGBA才能被显卡显示。或者说,手机的显卡不支持YUV。(但也许某些其他设备可以直接支持YUV显示)。 是这样子吗?

    作者回复: A1: 这个图片里面的内容,应该是编辑老师写错了,我让老师修改一下; A2:不是笔误,AVPacket是压缩后的数据(简单理解为H264或者AAC),AVFrame是原始数据(简单理解为YUV或者PCM),AVPacket经过解码形成AVFrame; A3:FFmpeg框架不具有渲染音频的能力,FFmpeg是集协议(protocol,比如rtmp、http、file)、封装(muxer/demuxer,比如mp4、flv、m4a)、编解码(Codec,比如H264、AAC、Mp3)为一体的多媒体框架,本身不具备渲染音频以及渲染视频的能力;多说一句,ffmpeg提供了一个命令行叫ffplay,里面渲染音视频使用的是sdl这个库来进行的。 A4:是的,都要转换为RGBA才可以显示。

    2022-08-10归属地:北京
    1
  • keepgoing
    一般在销毁资源的时候,直接让这个函数返回 0,或者为弱网也提供一个超时的读取时间,这个设置是非常有用的,它可以保证你的解码模块不会因为一些阻塞的 IO 调用变得不可用。 老师请教一下,这里应该是“直接让这个函数返回1”吧?直接取消IO阻塞

    作者回复: 你说的是对的,返回1代表中断IO操作。

    2022-12-10归属地:北京
  • geek
    请教老师没太理解片段shader的代码。 对于yuv420p的数据是如何做成一张纹理的?yuv420p是4个y数据和2个uv数据,这段数据是如何用顶点坐标得到yuv的三个分量?

    作者回复: 使用texture2D方法就可以拿出对应的这个像素点,至于是Y还是U还是V,是从对应不同的纹理ID上拿出来的,类似于这样: mediump vec3 yuv; yuv.x = texture2D(inputImageTexture, v_texcoord).r - (16.0 / 255.0); yuv.y = texture2D(s_texture_u, v_texcoord).r - 0.5; yuv.z = texture2D(s_texture_v, v_texcoord).r - 0.5;

    2022-08-11归属地:北京
    3
收起评论
显示
设置
留言
4
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部