• Android MediaCodec 框架 基于codec2


    系列文章的目的是什么?

    粗略:

    • 解码需要哪些基础的服务?
    • 标准解码的调用流程?
    • 各个流程的作用是什么?
    • 解码框架的层次?
    • 各个层次的作用?

    细化:

    • 解码参数的配置?
    • 解码输入数据包的流转?
    • 解码输出帧内存的申请和管理?

    在这里插入图片描述

    首先从MediaCodec 到具体的解码Component 梳理出一条路径,然后在具体理解里面的细节。 本文就从MediaCodec出发 理解Android 解码框架的各个层次, 总的来说可以分为三个部分

    • HIDL 层之上,这一层主要是对外部的应用提供接口 并提供输入和输出buffer的管理,流程的控制等等。
    • HIDL 层之下,提供创建具体的解码组件,解码组件的实现,对数据包解码 并返会解码后的图像。
    • codec service,提供创建解码组件的服务,HIDL层之上通过这个服务调用到HIDL 之下。
      在这里插入图片描述

    HIDL上游

    如图所示 上游主要包含了以下这些部分。

    • MediaCodec

    MediaCodec 首先会创建出ccodec,后续的操作都是通过ccodec 这个codecbase进行调用(这里是为了兼容ACodec 和 Codec2的情况)。同时也通过这个ccodec获取codec创建的CCodecBufferChannel。

    1. 创建codecbase 这里就是CCodec, 在MediaCodec 这一级是调用到CCodec。
    2. 将创建好的ccodec 注册到looper 中。这个looper是应用层设置到mediacodec中的。
    3. 注册CodecCallback到ccodec,注册BufferCallback到CCodecBufferChannel。
    • CCodec
      1. 创建 CCodecBufferChannel 和 CCodecConfig。
      2. 通过codec2的service 获取componentStore, 并通过componentStore来创建解码器的component。这里面主要是通过codec2client 这个类来完成的。
      3. 将创建好的组件设置到CCodecBufferChannel,以便后续调用。
      4. 回调一些错误等信息到MediaCodec。

    codec2client:

    和下游HIDL进行交互 的客户端, 主要是调用IComponetStore 和IComponet的接口。

    1. 查看codec2client是如何创建出来的?
    std::shared_ptr client = _CreateFromIndex(index);
    
    
    std::shared_ptr Codec2Client::_CreateFromIndex(size_t index) {
        std::string const& name = GetServiceNames()[index];
        LOG(WARNING) << "Creating a Codec2 client to service \"" << name << "\"";
        sp baseStore = Base::getService(name);
        CHECK(baseStore) << "Codec2 service \"" << name << "\""
                            " inaccessible for unknown reasons.";
        LOG(WARNING) << "Client to Codec2 service \"" << name << "\" created";
        return std::make_shared(baseStore, index);
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • GetServiceNames()。 通过Manifest来获取hal的名字

    Manifest定义了HAL的名字”android.hardware.media.c2”, hidl传输方式”hwbinder”,interface的名字”IComponentStore”,instance的名字”default”。而GetServiceNames也是通过这些信息去定位到具体的HAL。

    • Base::getService(name): 其中Base是IComponentStore类型,也就是service 端。通过名字获取到service端的服务。 然后赋值到baseStore。

    • 接着用这个baseStore初始化创建codec2client(也就是mBase 是baseStore)。

      std::make_shared(baseStore, index)

    • 所以说codecclinet 调用的接口会调用到service 端的ComponentStore。

    CCodecBufferChannel:管理输入和输出buffer的地方,当时有输入和输出buffer的时候通过回调上报到MediaCodec ,随后MediaCodec上报到应用

    HIDL 下游

    下游包括两个方面 一个是componentStore 另一个是Componet

    • componetStore

      调用关系以createComponent 为例。调用流程如下

      codec2client----->(HIDL)compometStore(获取真正的store)------>C2PlatformComponentStore(或者vendor自己实现的componetstore) -----> C2SoftAvcDecFactory .

      在HIDL 上层 codec2clinet 获取componetStore服务的时候 会调用下面的函数返回C2PlatformComponentStore。而后调用createCompoent就调用到这个类当中。

      在这个类的创建componet中会根据具体的名字找到componet调用其的createComponent,比如avc的C2SoftAvcDecFactory 的 createComponent

    c2store.cpp
    std::shared_ptr GetCodec2PlatformComponentStore() {
        static std::mutex mutex;
        static std::weak_ptr platformStore;
        std::lock_guard lock(mutex);
        std::shared_ptr store = platformStore.lock();
        if (store == nullptr) {
            store = std::make_shared();
            platformStore = store;
        }
        return store;
    }
    
    c2_status_t C2PlatformComponentStore::createComponent(
            C2String name, std::shared_ptr *const component) {
        // This method SHALL return within 100ms.
        component->reset();
        std::shared_ptr module;
        c2_status_t res = findComponent(name, &module);
        if (res == C2_OK) {
            // TODO: get a unique node ID
            res = module->createComponent(0, component);
        }
        return res;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • component

      compont的调用 也是通过HIDL的接口调用到 SimpleC2Component ,然后 SimpleC2Component 调用具体的avc、hevc等等的componet。 SimpleC2Component 是每个compont的基类。

      以queue接口为例 HIDL上层的codec2bufferChannel 会调用具体解码组件的queue接口 将待解码的数据包放入的具体的component中 首先调用到Codec2Client 这个调用componet的queue,然后调用到SimpleC2Component的queue_nb, queue_nb发送消息, 在消息处理线程中调用子类的process函数。

      c2_status_t Codec2Client::Component::queue(
              std::list>* const items) {
          Return transStatus = mBase1_0->queue(workBundle);
      }
      
      // Methods from ::android::hardware::media::c2::V1_1::IComponent
      Return Component::queue(const WorkBundle& workBundle) {
          return static_cast(mComponent->queue_nb(&c2works));
      }
      
      c2_status_t SimpleC2Component::queue_nb(std::list> * const items) {
          {
          if (queueWasEmpty) {
              (new AMessage(WorkHandler::kWhatProcess, mHandler))->post();
          }
      }
      
      bool SimpleC2Component::processQueue() {
          }
          process(work, mOutputBlockPool);
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21

    HIDL接口

    • IComponentStore

      C2ComponentStore(这定义了各种接口, codec2client/C2PlatformComponentStore都继承他并实现里面的接口。)
      有哪些接口 主要是
      createComponent: 创建各种编解码器组件
      createInterface:创建定义各种组件的配置
      listComponents:列出所有的组件。

    • IComponent

      主要定义了对组件的各种操作 实际可以分为数据流和控制流, 数据流包括配置编码输入surface,解码输出surface,输入解码包,清空编解码数据。控制流:启动组件、退出组件、释放组件等等操作

      connectToInputSurface:使用surface启动组件

      queue: 将work 放到组件中。
      drain: 清空组件,不是堵塞运行的。

      setOutputSurface: 设置输出的surface。

      start: 启动组件。

      stop: stop组件。

    基础的codec2 服务

    frameworks\av\media\codec2\hidl\services\vendor.cpp
    在这里面的rc 中会启动一个android.hardware.media.c2@1.2-default-service
    这个main函数中实现的是一个componentStore。

            store = new utils::ComponentStore(
                    std::make_shared());
                constexpr char const* serviceName = "default";
                if (store->registerAsService(serviceName) != OK) {
                    LOG(ERROR) << "Cannot register Codec2's IComponentStore service"
                                  " with instance name << \""
                               << serviceName << "\".";
                } else {
                    LOG(DEBUG) << "Codec2's IComponentStore service registered. "
                                  "Instance name: \"" << serviceName << "\".";
                }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  • 相关阅读:
    【TensorFlow1.X】系列学习笔记【基础一】
    有效降低数据库存储成本方案与实践
    《Effective C++》《构造/析构/赋值运算——9、绝不在构造和析构过程中调用virtual函数》
    (附源码)springboot炼糖厂地磅全自动控制系统 毕业设计 341357
    秋招我借这份PDF的复习思路,收获美团,小米,京东等Java岗offer
    解决关键词这个问题,ASO优化效果事半功倍
    【刷题笔记】之牛客面试必刷TOP101(1)
    本地存储localStorage及商品管理系统
    java计算机毕业设计基于安卓Android微信小程序的应急求救信息发布系统小程序uniAPP
    Flutter 中的 Table 小部件:全面指南
  • 原文地址:https://blog.csdn.net/H2008066215019910120/article/details/133847138