前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android Codec2处理流程适配和解析

Android Codec2处理流程适配和解析

原创
作者头像
colourfate
修改2024-03-08 10:01:20
5570
修改2024-03-08 10:01:20

1 介绍

Codec2是Android中多媒体相关的软件框架,是MediaCodec的中间件,往上对接MediaCodec Native层,往下提供新的API标准供芯片底层的编解码去实现,也就是说适配了Codec2,就可以通过MediaCodec来调用芯片的硬件编解码的能力,来完成一些多媒体相关的功能。这篇文章先从下到上讲解适配Codec2需要实现的接口,然后再从上到下分析MediaCodec的流程来分析这些接口是如何调用的。主要抓住以下两条主线

  1. 输入buffer是如何送到编解码组件的
  2. 编解码完成之后输入buffer和输出buffer是如何上报的

开始之前需要如下前置知识

  1. Android异步消息机制
  2. Android HIDL
  3. 视频编解码基本流程

2 适配

下面以Android中的软件Hevc编码器的实现为例,分析如何适配Codec2接口,首先看codec2的基本架构,分为4层,第一层是sfplugin,负责和上层的stage fright对接,下面是HIDL,是各个组件的硬件抽象层,再往下是Core,封装了一个组件需要实现的接口,最后是具体的Component实现,这里以Hevc软编码器为例,再往下就是具体的编解码库了,软编码器调用的是ihevce相关的接口

2.1 目录结构

Android中的codec2目录在frameworks/av/media/codec2

core层组织了components的运行方式,这里先分析core层,其中主要的文件是:core/include/C2Component.h,其中包含了C2ComponentC2ComponentInterface两个类

2.2 C2Component

C2Component中定义了一个组件需要实现的接口,定义如下,这里需要关注的两个重要接口

  1. queue_nb:可以看作送帧/送流的接口,在编解码之前,将需要处理的原始数据送入,该接口必现设计为非阻塞
  2. onWorkDone_nb:当一帧数据处理完了之后会回调该接口

2.3 C2ComponentInterface

TODO

2.4 SimpleC2Component

SimpleC2Component提供了一种组件的实现,后面不同的实现只需要继承该实现即可,其中重要的接口设计如下。可以看到SimpleC2Component继承了C2Component,并将实现了一个AMessage和AHandle的异步消息机制,下面分别对相应的函数进行分析

2.4.1 setListener_vb

该接口将上面的传递的listener设置到状态机中,后续时机合适时再回调相应接口

2.4.2 queue_nb

该函数实现了C2Component.queue_nb,实际只是将work放到队列中,并且发起一个异步消息然后返回,满足非阻塞的要求

2.4.3 onMessageReceived

该函数是异步消息处理接口,当发出对应的消息时最终会调用到该函数中,这里processQueue()函数是处理一帧数据,然后返回当前队列是否还有未处理的数据

2.4.4 processQueue

该函数完成编解码的实际操作,代码如下,其中processonWorkDone_nb都由子类实现,work先从之前的mWorkQueue队列中拿出,再调用process进行处理,process是一个虚函数,由子类实现,处理完成之后再调用listener->onWorkDone_nb通知处理完成事件onWorkDone_nb也是一个虚函数,由子类实现

2.5 C2SoftHevcEnc

下面分析process的实现,以Android Hevc软编码器为例,类继承自SimpleC2Component,实现如下,主要流程是从work中取出输入buffer,然后进行一帧编码,然后再把输出设置到work中

下面分析finishWork,可见软编码输出的buffer最终是拷贝到了一个C2Buffer中,最终再放入work->worklets.front()->output.buffers队列

3 HIDL

3.1 概念

C2Component的上层是HIDL层,可以理解为Android的HAL层,这一层的头文件所继承的接口由一种叫做HIDL(Hardware Interface Definition Language)的语言动态生成,输出到out目录下,例如其中的IComponent头文件位于:

out/soong/.intermediates/hardware/interfaces/media/c2/1.0/android.hardware.media.c2@1.0_genc++_headers/gen/android/hardware/media/c2/1.0/IComponent.h

其定义如下

对应的HIDL文件为hardware/interfaces/media/c2/1.0/IComponent.hal

3.2 Component

Component继承自IComponent.h,也就会实现其中的接口,通过Component就可以调用上面讲到的C2Component,其定义如下,我们重点看下其中的queueListener

3.2.1 queue

queue的实现如下,这里的mComponent实际上就是C2Component,这里如何实现的暂且不表,后面再分析,以软编码为例,因此这里最终调用的queue_nb实际上调用的是SimpleC2Component.queue_nb

3.2.1 Listener

再看Listener的定义,这里实际继承的是C2Component::Listener,并且对onWorkDone_nb进行了实现,因为onWorkDone_nb是回调函数,因此由调用者实现也是符合预期的。这里其实就是调用了另一个回调listener->onWorkDone

以上的Listener只是一个定义,还要看该Listener是在哪里声明的,以及是什么时候注册的。首先第一个问题,Listener声明是在SimpleC2Component::ExecState.mListener,只要继承了SimpleC2Component内部就有该成员,第二个问题,注册是在Component::initListener函数中,定义如下,同样以软编码为例,这里mComponent->setListener_vb调用实际是SimpleC2Component.setListener_vb

关于HIDL还有很长的一个调用流程,这里暂且分析到这里,后续再从MediaCodec从上往下分析,看如何调用到HIDL的

4 MediaCodec

MediaCodec是Android app层来进行多媒体编解码的模块,分为java层和cpp层,这里只从cpp层切入

4.1 调用流程

首先来看MediaCodec是如何使用的,由于MediaCodec也基于AMessage机制,因此先要创建一个ALooper,然后传递到MediaCodec中,下面以创建Hevc编码器为例,伪代码如下

4.2 发送输入buffer流程

4.2.1 queueInputBuffer

首先分析queueInputBuffer,看YUV是如何送到具体的编码器组件的,可以看到实际这里只是发了一个异步消息,将index送进去,然后调用PostAndAwaitResponse阻塞等待消息响应

4.2.2 QueueInputBuffer消息处理

再查看MediaCodec的消息处理函数,由于queueInputBuffer是阻塞等待的,因此这里要调用PostReplyWithError之后,queueInputBuffer才返回,这里往后是调用了onQueueInputBuffer函数

下面分析onQueueInputBuffer,可以看到这里实际是通过index获取到MediaCodecBuffer,并且送到了mBufferChannel->queueInputBuffer中,注意执行到此处最外层的queueInputBuffer仍然在等待消息响应,因此到这里为止都是阻塞的

4.3 获取输入buffer流程

4.3.1 dequeueInputBuffer

下面看dequeueInputBuffer函数,该函数是获取一个空闲的输入buffer,这里是否空闲仍然需要底层的组件来通知,因此需要分析这里的向上通知的流程。可以看到该函数仍然是发起一个异步消息,然后阻塞等待响应

4.3.2 kWhatDequeueInputBuffer消息处理

再回到onMessageReceived函数,最终会调用到dequeuePortBuffer函数,可以看到MediaCodec中有一个mAvailPortBuffers链表,存储着当前可用的buffer的index,当dequeue的时候只需要从这个链表中拿出第一个index就行了

4.3.3 updateBuffers

继续分析availBuffers是什么时候更新的,查看updateBuffers函数,发现其中的index是通过异步消息上报的,updateBuffers可以更新输入队列和输出队列,我们只看输入队列,发现是在收到kWhatFillThisBuffer消息时更新的

4.3.4 BufferCallback

至此可以发现,MediaCodec中的输入和输出buffer是否可用时由异步消息通知的,而异步消息又是底层的组件通过回调MediaCodec的接口发送的,这部分代码在BufferCallback中,实际上BufferCallback是继承了CodecBase::BufferCallback类,并实现了其中的接口,可以看到输入和输出buffer都是在这里回调的

CodecBase实际上又是codec2中的接口了,属于sfplugin模块,篇幅原因这里暂时不往下分析了

4.4 获取输出buffer流程

4.4.1 dequeueOutputBuffer

获取输出buffer和获取输入buffer是一个流程,都是发出一个消息然后等待响应,这里输出buffer是底层组件处理好的数据,因此也需要底层组件来通知

4.4.2 kWhatDequeueOutputBuffer消息处理

获取输出buffer和获取输入buffer相同,都是调用的dequeuePortBuffer函数,只不过传入参数不同,也就是这里仍然是从mAvailPortBuffers队列中获取以及处理完成的buffer index

4.4.3 mAvailPortBuffers的更新

获取输入buffer仍然是调用updateBuffers进行mAvailPortBuffers的更新,只不过这里是从kWhatDrainThisBuffer更新的,而发出该消息的地方也是另一个onOutputBufferAvailable回调

4.5 释放输出buffer

4.5.1 releaseOutputBuffer

释放输出buffer流程比较简单,也是发一个异步消息然后阻塞等待,在消息处理中将mPortBuffers队列对应的buffer清除,然后最后调用mBufferChannel->discardBuffer通知底层组件

5 总结

总结以上流程,除开HIDL和sfplugin,这里关于使用MediaCodec调用codec2进行编解码的流程进行了大体的分析,首先看整体的运作流程和实现关系如下

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 介绍
  • 2 适配
  • 2.1 目录结构
    • 2.2 C2Component
      • 2.3 C2ComponentInterface
        • 2.4 SimpleC2Component
          • 2.4.1 setListener_vb
          • 2.4.2 queue_nb
          • 2.4.3 onMessageReceived
          • 2.4.4 processQueue
        • 2.5 C2SoftHevcEnc
        • 3 HIDL
          • 3.1 概念
            • 3.2 Component
              • 3.2.1 queue
              • 3.2.1 Listener
          • 4 MediaCodec
            • 4.1 调用流程
              • 4.2 发送输入buffer流程
                • 4.2.1 queueInputBuffer
              • 4.2.2 QueueInputBuffer消息处理
                • 4.3 获取输入buffer流程
                  • 4.3.1 dequeueInputBuffer
                  • 4.3.2 kWhatDequeueInputBuffer消息处理
                  • 4.3.3 updateBuffers
                  • 4.3.4 BufferCallback
                • 4.4 获取输出buffer流程
                  • 4.4.1 dequeueOutputBuffer
                  • 4.4.2 kWhatDequeueOutputBuffer消息处理
                  • 4.4.3 mAvailPortBuffers的更新
                • 4.5 释放输出buffer
                  • 4.5.1 releaseOutputBuffer
              • 5 总结
              相关产品与服务
              多媒体处理
              多媒体处理(Multimedia Processing,MMP)是数据万象推出的音视频处理服务,集成音视频转码、极速高清、精彩集锦、超分辨率、数字水印等能力,满足传媒、文旅、电商等各行业多媒体处理需求。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档