请选择 进入手机版 | 继续访问电脑版

[C 语言] 一文搞懂Codec2解码组件

[复制链接]
查看127 | 回复9 | 2021-9-12 18:33:26 | 显示全部楼层 |阅读模式
目次

1 媒介

在本篇中,我们将关注Codec 2.0以下几个标题 :

1.从顶而下,一个解码组件是怎样 创建的

2.组件的接口有哪些,分别是什么含义

3.组件是怎样 运行的,输入与输出的数据流是怎样的

2 组件的创建

CCodec在allocate中,通过CreateComponentByName创建了详细 的解码组件。

  1. //android/frameworks/av/media/codec2/sfplguin/CCodec.cpp
  2. void CCodec::allocate(const sp<MediaCodecInfo> &codecInfo) {
  3. ...
  4. AString componentName = codecInfo->getCodecName();
  5. std::shared_ptr<Codec2Client> client;
  6. // set up preferred component store to access vendor store parameters
  7. //从CCodec调用到component是通过HAL层服务的,默认谷歌的原生服务为
  8. //android.hardware.media.c2@IComponentStore/software,默认厂商的服务为
  9. //android.hardware.media.c2@IComponentStore/default,在android小机shell中通过lshal|grep media可以查询
  10. //到正在运行的codec2服务,如果厂商已支持codec2,则可以查询到default服务。如果CCodec中能够创建到default
  11. //服务,则可以将该服务设置为Preferred Codec2 ComponentStore,也就是将其作为目标组件。
  12. client = Codec2Client::CreateFromService("default");
  13. if (client) {
  14. ALOGI("setting up '%s' as default (vendor) store", client->getServiceName().c_str());
  15. SetPreferredCodec2ComponentStore(
  16. std::make_shared<Codec2ClientInterfaceWrapper>(client));
  17. }
  18. //创建具体的解码组件或者编码组件,譬如c2.android.avc.decoder
  19. //所有omx与codec2的编解码组件支持列表可以在libstagefright/data目录下的xml中查询得到,它们的加载与
  20. //排序情况可以在libstagefright/MediaCodecList.cpp中追踪
  21. std::shared_ptr<Codec2Client::Component> comp =
  22. Codec2Client::CreateComponentByName(
  23. componentName.c_str(),
  24. mClientListener,
  25. &client);
  26. ...
  27. ALOGI("Created component [%s]", componentName.c_str());
  28. mChannel->setComponent(comp);
  29. auto setAllocated = [this, comp, client] {
  30. Mutexed<State>::Locked state(mState);
  31. if (state->get() != ALLOCATING) {
  32. state->set(RELEASED);
  33. return UNKNOWN_ERROR;
  34. }
  35. state->set(ALLOCATED);
  36. state->comp = comp;
  37. mClient = client;
  38. return OK;
  39. };
  40. ...
  41. // initialize config here in case setParameters is called prior to configure
  42. Mutexed<Config>::Locked config(mConfig);
  43. status_t err = config->initialize(mClient, comp);
  44. ...
  45. config->queryConfiguration(comp);
  46. mCallback->onComponentAllocated(componentName.c_str());
  47. }
复制代码

继续追踪Codec2Client::CreateComponentByName接口。

  1. //android/frameworks/av/media/codec2/hidl/client/client.cpp
  2. std::shared_ptr<Codec2Client::Component>
  3. Codec2Client::CreateComponentByName(
  4. const char* componentName,
  5. const std::shared_ptr<Listener>& listener,
  6. std::shared_ptr<Codec2Client>* owner,
  7. size_t numberOfAttempts) {
  8. std::string key{"create:"};
  9. key.append(componentName);
  10. std::shared_ptr<Component> component;
  11. c2_status_t status = ForAllServices(
  12. key,
  13. numberOfAttempts,
  14. [owner, &component, componentName, &listener](
  15. const std::shared_ptr<Codec2Client> &client)
  16. -> c2_status_t {
  17. //调用Codec2Client类的createComponent接口,获取component
  18. c2_status_t status = client->createComponent(componentName,
  19. listener,
  20. &component);
  21. ...
  22. return status;
  23. });
  24. ...
  25. return component;
  26. }
复制代码

追踪Codec2Client类的createComponent接口。

  1. \\av\media\codec2\hidl\client\client.cpp
  2. c2_status_t Codec2Client::createComponent(
  3. const C2String& name,
  4. const std::shared_ptr<Codec2Client::Listener>& listener,
  5. std::shared_ptr<Codec2Client::Component>* const component) {
  6. c2_status_t status;
  7. sp<Component::HidlListener> hidlListener = new Component::HidlListener{};
  8. hidlListener->base = listener;
  9. //这里的mBase是什么?这里调用的是IComponentStore的createComponent接口
  10. Return<void> transStatus = mBase->createComponent(
  11. name,
  12. hidlListener,
  13. ClientManager::getInstance(),
  14. [&status, component, hidlListener](
  15. Status s,
  16. const sp<IComponent>& c) {
  17. status = static_cast<c2_status_t>(s);
  18. if (status != C2_OK) {
  19. return;
  20. }
  21. *component = std::make_shared<Codec2Client::Component>(c);
  22. hidlListener->component = *component;
  23. });
  24. ...
  25. return status;
  26. }
复制代码

我们先看一下IComponentStore的createComponent接口。

  1. \\av\media\codec2\hidl\1.0\utils\include\codec2\hidl\1.0\ComponentStore.h
  2. struct ComponentStore : public IComponentStore {
  3. ComponentStore(const std::shared_ptr<C2ComponentStore>& store);
  4. virtual ~ComponentStore() = default;
  5. // Methods from ::android::hardware::media::c2::V1_0::IComponentStore.
  6. virtual Return<void> createComponent(
  7. const hidl_string& name,
  8. const sp<IComponentListener>& listener,
  9. const sp<IClientManager>& pool,
  10. createComponent_cb _hidl_cb) override;
  11. virtual Return<void> createInterface(
  12. const hidl_string& name,
  13. createInterface_cb _hidl_cb) override;、
  14. ...
  15. }
复制代码

该接口的实现为:

  1. \\av\media\codec2\hidl\1.0\utils\ComponentStore.cpp
  2. // Methods from ::android::hardware::media::c2::V1_0::IComponentStore
  3. Return<void> ComponentStore::createComponent(
  4. const hidl_string& name,
  5. const sp<IComponentListener>& listener,
  6. const sp<IClientManager>& pool,
  7. createComponent_cb _hidl_cb) {
  8. sp<Component> component;
  9. std::shared_ptr<C2Component> c2component;
  10. //C2PlatformComponentStore的createComponent调用
  11. //调用C2PlatformComponentStore的createComponent接口,返回的是一个C2Component对象
  12. //譬如,这个对象可以是C2SoftAvcDec Component对象,也可以是VendorHwAvcDec Component对象
  13. Status status = static_cast<Status>(
  14. mStore->createComponent(name, &c2component));
  15. if (status == Status::OK) {
  16. onInterfaceLoaded(c2component->intf());
  17. //把前面创建的C2SoftAvcDec“装载”到Component类中,Client调用Component
  18. //Component内部会调用到C2SoftAvcDec
  19. //Component相当于对原生编解码组件/厂商编解码组件的统一封装
  20. component = new Component(c2component, listener, this, pool);
  21. if (!component) {
  22. status = Status::CORRUPTED;
  23. } else {
  24. reportComponentBirth(component.get());
  25. if (component->status() != C2_OK) {
  26. status = static_cast<Status>(component->status());
  27. } else {
  28. component->initListener(component);
  29. if (component->status() != C2_OK) {
  30. status = static_cast<Status>(component->status());
  31. }
  32. }
  33. }
  34. }
  35. _hidl_cb(status, component);
  36. return Void();
  37. }
复制代码

关于C2PlatformComponentStore的createComponent调用,它的实如今 C2Store.cpp中,它继承于C2ComponentStore类,有几个告急 成员对象,ComponentModule,ComponentLoader,有几个告急 的接口,listComponents(),createComponent(),createInterface()。ComponentLoader包含ComponentModule对象,而ComponentModule告急 提供两个接口,createComponent()与createInterface(),内部也包含着C2ComponentFactory成员以及它的创建与烧毁 接口,分别是C2ComponentFactory::CreateCodec2FactoryFunc,C2ComponentFactory::DestroyCodec2FactoryFunc。

  1. \\av\media\codec2\vndk\C2Store.cpp
  2. class C2PlatformComponentStore : public C2ComponentStore {
  3. public:
  4. virtual std::vector<std::shared_ptr<const C2Component::Traits>> listComponents() override;
  5. ...
  6. virtual c2_status_t createInterface(
  7. C2String name, std::shared_ptr<C2ComponentInterface> *const interface) override;
  8. virtual c2_status_t createComponent(
  9. C2String name, std::shared_ptr<C2Component> *const component) override;
  10. virtual ~C2PlatformComponentStore() override = default;
  11. private:
  12. /**
  13. * An object encapsulating a loaded component module.
  14. */
  15. struct ComponentModule : public C2ComponentFactory,
  16. public std::enable_shared_from_this<ComponentModule> {
  17. virtual c2_status_t createComponent(
  18. c2_node_id_t id, std::shared_ptr<C2Component> *component,
  19. ComponentDeleter deleter = std::default_delete<C2Component>()) override;
  20. virtual c2_status_t createInterface(
  21. c2_node_id_t id, std::shared_ptr<C2ComponentInterface> *interface,
  22. InterfaceDeleter deleter = std::default_delete<C2ComponentInterface>()) override;
  23. ...
  24. protected:
  25. ...
  26. void *mLibHandle; ///< loaded library handle
  27. C2ComponentFactory::CreateCodec2FactoryFunc createFactory; ///< loaded create function
  28. C2ComponentFactory::DestroyCodec2FactoryFunc destroyFactory; ///< loaded destroy function
  29. C2ComponentFactory *mComponentFactory; ///< loaded/created component factory
  30. };
  31. /**
  32. * An object encapsulating a loadable component module.
  33. */
  34. struct ComponentLoader {
  35. /**
  36. * Load the component module.
  37. *
  38. * This method simply returns the component module if it is already currently loaded, or
  39. * attempts to load it if it is not.
  40. */
  41. c2_status_t fetchModule(std::shared_ptr<ComponentModule> *module) {
  42. c2_status_t res = C2_OK;
  43. std::lock_guard<std::mutex> lock(mMutex);
  44. std::shared_ptr<ComponentModule> localModule = mModule.lock();
  45. if (localModule == nullptr) {
  46. localModule = std::make_shared<ComponentModule>();
  47. res = localModule->init(mLibPath);
  48. if (res == C2_OK) {
  49. mModule = localModule;
  50. }
  51. }
  52. *module = localModule;
  53. return res;
  54. }
  55. /**
  56. * Creates a component loader for a specific library path (or name).
  57. */
  58. ComponentLoader(std::string libPath)
  59. : mLibPath(libPath) {}
  60. private:
  61. std::weak_ptr<ComponentModule> mModule; ///< weak reference to the loaded module
  62. };
  63. struct Interface : public C2InterfaceHelper {
  64. ...
  65. };
  66. /**
  67. * Retrieves the component module for a component.
  68. */
  69. c2_status_t findComponent(C2String name, std::shared_ptr<ComponentModule> *module);
  70. /**
  71. * Loads each component module and discover its contents.
  72. */
  73. void visitComponents();
  74. std::map<C2String, ComponentLoader> mComponents; ///< path -> component module
  75. std::map<C2String, C2String> mComponentNameToPath; ///< name -> path
  76. std::vector<std::shared_ptr<const C2Component::Traits>> mComponentList;
  77. ...
  78. };
复制代码

C2PlatformComponentStore::createComponent调用findComponent(name, &module)找到拥有component的ComponentModule,再通过module->createComponent(0, component)调用,找到相应的component。

  1. \\av\media\codec2\vndk\C2Store.cpp
  2. c2_status_t C2PlatformComponentStore::createComponent(
  3. C2String name, std::shared_ptr<C2Component> *const component) {
  4. // This method SHALL return within 100ms.
  5. component->reset();
  6. std::shared_ptr<ComponentModule> module;
  7. c2_status_t res = findComponent(name, &module);
  8. if (res == C2_OK) {
  9. // TODO: get a unique node ID
  10. res = module->createComponent(0, component);
  11. }
  12. return res;
  13. }
复制代码

findComponent(name, &module)有两步,先通过visitComponents()枚举 出全部 可用的components,再调用ComponentLoader的fetchModule(),找到拥有component的ComponentModule。module可以看作是组件,加载某个module,也就是加载对应的组件,module提供的 createComponent()接口就是用来创建详细 component的,譬如C2SoftAvcDec。

  1. \\av\media\codec2\vndk\C2Store.cpp
  2. c2_status_t C2PlatformComponentStore::findComponent(
  3. C2String name, std::shared_ptr<ComponentModule> *module) {
  4. (*module).reset();
  5. visitComponents();
  6. auto pos = mComponentNameToPath.find(name);
  7. if (pos != mComponentNameToPath.end()) {
  8. return mComponents.at(pos->second).fetchModule(module);
  9. }
  10. return C2_NOT_FOUND;
  11. }
复制代码

visitComponents()访问mComponents对象(这是一个map对象,将path与component module映射关联,这一映射工作在C2PlatformComponentStore初始化时举行 ),遍历全部 的mComponents,即pathAndLoader对象,假如 一个对象的loader可以或许 加载成功,则添加到mComponentNameToPath对象中。

  1. \\av\media\codec2\vndk\C2Store.cpp
  2. void C2PlatformComponentStore::visitComponents() {
  3. std::lock_guard<std::mutex> lock(mMutex);
  4. if (mVisited) {
  5. return;
  6. }
  7. //参考定义 std::map<C2String, ComponentLoader> mComponents; ///< path -> component module
  8. for (auto &pathAndLoader : mComponents) {
  9. const C2String &path = pathAndLoader.first;
  10. ComponentLoader &loader = pathAndLoader.second;
  11. std::shared_ptr<ComponentModule> module;
  12. if (loader.fetchModule(&module) == C2_OK) {
  13. std::shared_ptr<const C2Component::Traits> traits = module->getTraits();
  14. if (traits) {
  15. mComponentList.push_back(traits);
  16. mComponentNameToPath.emplace(traits->name, path);
  17. for (const C2String &alias : traits->aliases) {
  18. mComponentNameToPath.emplace(alias, path);
  19. }
  20. }
  21. }
  22. }
  23. mVisited = true;
  24. }
复制代码

loader.fetchModule(&module)这个函数定义在ComponentLoader类中,在这里再贴一次代码。

  1. \\av\media\codec2\vndk\C2Store.cpp
  2. c2_status_t fetchModule(std::shared_ptr<ComponentModule> *module) {
  3. c2_status_t res = C2_OK;
  4. std::lock_guard<std::mutex> lock(mMutex);
  5. std::shared_ptr<ComponentModule> localModule = mModule.lock();
  6. if (localModule == nullptr) {
  7. localModule = std::make_shared<ComponentModule>();
  8. res = localModule->init(mLibPath);
  9. if (res == C2_OK) {
  10. mModule = localModule;
  11. }
  12. }
  13. *module = localModule;
  14. return res;
  15. }
复制代码

对于module,会调用初始化函数,初始化成功就算是fetch到了。初始化作了什么工作,参见C2PlatformComponentStore::ComponentModule::init函数,也就是对编解码库dlopen成功,可获得相应的函数地址,譬如,C2SoftAvcDec.cpp中的C2ComponentFactory* CreateCodec2Factory()与void DestroyCodec2Factory()。当然还有其他,不面面俱道了。

  1. \\av\media\codec2\vndk\C2Store.cpp
  2. c2_status_t C2PlatformComponentStore::ComponentModule::init(
  3. std::string libPath) {
  4. ALOGV("in %s", __func__);
  5. ALOGV("loading dll");
  6. mLibHandle = dlopen(libPath.c_str(), RTLD_NOW|RTLD_NODELETE);
  7. createFactory =
  8. (C2ComponentFactory::CreateCodec2FactoryFunc)dlsym(mLibHandle, "CreateCodec2Factory");
  9. LOG_ALWAYS_FATAL_IF(createFactory == nullptr,
  10. "createFactory is null in %s", libPath.c_str());
  11. destroyFactory =
  12. (C2ComponentFactory::DestroyCodec2FactoryFunc)dlsym(mLibHandle, "DestroyCodec2Factory");
  13. LOG_ALWAYS_FATAL_IF(destroyFactory == nullptr,
  14. "destroyFactory is null in %s", libPath.c_str());
  15. mComponentFactory = createFactory();
  16. ...
  17. std::shared_ptr<C2ComponentInterface> intf;
  18. c2_status_t res = createInterface(0, &intf);
  19. ...
  20. return mInit;
  21. }
复制代码

那么标题 来了,为什么谷歌对它本身 的codec2插件组C2PlatformComponentStore计划 得这么复杂,能不能简化一点。

3 组件接口

在codec2/components目次 下,有base, avc, aom, hevc, aac等文件夹,base目次 下是SimpleC2Component.cpp与SimpleC2Interface.cpp以及对应的头文件,avc目次 下是C2SoftAvcDec.cpp,C2SoftAvcEnc.cpp以及对应的头文件,其他编解码器文件夹亦同样道理。C2SoftAvcDec,C2SoftHevcDec等编解码器类都是继承于SimpleC2Component类的,也就是说,SimpleC2Component是components的顶层类,它对接了component类的接口,实现了编解码器的公共流程部分,C2SoftAvcDec,C2SoftHevcDec等子类继承SimpleC2Component的一些接口,实现各自的编解码操作。

SimpleC2Component实现的component的接口如下:

  1. \\av\media\codec2\components\base\include\SimpleC2Component.h
  2. // C2Component
  3. // From C2Component
  4. //设置回调
  5. virtual c2_status_t setListener_vb(
  6. const std::shared_ptr<Listener> &listener, c2_blocking_t mayBlock) override;
  7. //送数据到component,数据打包成某种对象,叫C2Work,这个对象很关键,它包含input与output
  8. virtual c2_status_t queue_nb(std::list<std::unique_ptr<C2Work>>* const items) override;
  9. //暂时没有多大用处,不管它
  10. virtual c2_status_t announce_nb(const std::vector<C2WorkOutline> &items) override;
  11. //跳播使用,将当前数据冲刷掉
  12. virtual c2_status_t flush_sm(
  13. flush_mode_t mode, std::list<std::unique_ptr<C2Work>>* const flushedWork) override;
  14. //渲染可用的帧
  15. virtual c2_status_t drain_nb(drain_mode_t mode) override;
  16. virtual c2_status_t start() override;
  17. virtual c2_status_t stop() override;
  18. virtual c2_status_t reset() override;
  19. virtual c2_status_t release() override;
  20. virtual std::shared_ptr<C2ComponentInterface> intf() override;
  21. 而C2SoftAvcDec,C2SoftHevcDec等子类继承SimpleC2Component的接口如下:
  22. \\av\media\codec2\components\base\include\SimpleC2Component.h
  23. virtual c2_status_t onInit() = 0;
  24. virtual c2_status_t onStop() = 0;
  25. virtual void onReset() = 0;
  26. virtual void onRelease() = 0;
  27. virtual c2_status_t onFlush_sm() = 0;
  28. //最重要的处理函数,处理的对象是C2Work,它包含着输入输出,交互配置方面的类。
  29. virtual void process(
  30. const std::unique_ptr<C2Work> &work,
  31. const std::shared_ptr<C2BlockPool> &pool) = 0;
  32. virtual c2_status_t drain(
  33. uint32_t drainMode,
  34. const std::shared_ptr<C2BlockPool> &pool) = 0;
复制代码

4 组件运行原理

SimpleC2Component有一个成员对象WorkHandler,这个类继承于AHandler,也就是说,SimpleC2Component内部运行一个线程,来自上层的接口调用,都可以发送消息到onMessageReceived中列队 处理,譬如初始化、克制 、重置、开释 以及数据处理等工作,都在队列中列队 处理,相应的处理都是调用到子类的实现,譬如,onInit(),onStop(),onReset(),onRelease(),以及processQueue()。

我们可以看一下onMessageReceived的实现。

  1. \\av\media\codec2\components\base\SimpleC2Component.cpp
  2. void SimpleC2Component::WorkHandler::onMessageReceived(const sp<AMessage> &msg) {
  3. std::shared_ptr<SimpleC2Component> thiz = mThiz.lock();
  4. ...
  5. switch (msg->what()) {
  6. case kWhatProcess: {
  7. if (mRunning) {
  8. if (thiz->processQueue()) {
  9. (new AMessage(kWhatProcess, this))->post();
  10. }
  11. } else {
  12. ALOGV("Ignore process message as we're not running");
  13. }
  14. break;
  15. }
  16. case kWhatInit: {
  17. int32_t err = thiz->onInit();
  18. Reply(msg, &err);
  19. [[fallthrough]];
  20. }
  21. case kWhatStart: {
  22. mRunning = true;
  23. break;
  24. }
  25. case kWhatStop: {
  26. int32_t err = thiz->onStop();
  27. Reply(msg, &err);
  28. break;
  29. }
  30. case kWhatReset: {
  31. thiz->onReset();
  32. mRunning = false;
  33. Reply(msg);
  34. break;
  35. }
  36. case kWhatRelease: {
  37. thiz->onRelease();
  38. mRunning = false;
  39. Reply(msg);
  40. break;
  41. }
  42. default: {
  43. ALOGD("Unrecognized msg: %d", msg->what());
  44. break;
  45. }
  46. }
  47. }
复制代码

我们看一下AVC解码器内部是怎样 处理输入与输出数据的,在这个process中,处理完输入,解码,处理输出,在处理output buffer时,process的思绪 是如许 的:从内存池申请一个GraphicBlock,对应地设置给解码器Buffer地址以供解码输出,假如 解码后有帧输出,则将当前的GraphicBlock转换为C2Buffer对象,返回给上层。雷同 于FFMPEG,你给它一个output frame,它就将解码图片添补 到frame,你取走表现 。可以推断,软解码器内部应该也有申请一个队列的buffer,这个队列维护着解码所必要 的参考图像。

  1. \\av\media\codec2\components\avc\C2SoftAvcDec.cpp
  2. //省略了部分不影响理解主要流程的代码
  3. void C2SoftAvcDec::process(
  4. const std::unique_ptr<C2Work> &work,
  5. const std::shared_ptr<C2BlockPool> &pool) {
  6. // Initialize output work
  7. work->result = C2_OK;
  8. work->workletsProcessed = 0u;
  9. work->worklets.front()->output.flags = work->input.flags;
  10. size_t inOffset = 0u;
  11. size_t inSize = 0u;
  12. uint32_t workIndex = work->input.ordinal.frameIndex.peeku() & 0xFFFFFFFF;
  13. C2ReadView rView = mDummyReadView;
  14. if (!work->input.buffers.empty()) {
  15. //为了得到输入数据,层层访问,真正放数据的地址在rView.data()[]中
  16. //把work这个对象用思维导图画出来,我们可以更容易的理解work,到底拥有哪些成员,如何访问
  17. rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
  18. inSize = rView.capacity();
  19. ...
  20. }
  21. bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
  22. bool hasPicture = false;
  23. ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x",
  24. inSize, (int)work->input.ordinal.timestamp.peeku(),
  25. (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
  26. size_t inPos = 0;
  27. while (inPos < inSize) {
  28. //ensureDecoderState会从内存池中fetch一个GraphicBlock
  29. //实质上也就是调用Gralloc接口取得一个output buffer
  30. if (C2_OK != ensureDecoderState(pool)) {
  31. mSignalledError = true;
  32. work->workletsProcessed = 1u;
  33. work->result = C2_CORRUPTED;
  34. return;
  35. }
  36. ivd_video_decode_ip_t s_decode_ip;
  37. ivd_video_decode_op_t s_decode_op;
  38. {
  39. //mOutBlock即是上述fetch到的output buffer,通过map映射可以得到一个wView,类似于rView
  40. //wView.data()[]指向out buffer的真正地址
  41. //wView.data()[C2PlanarLayout::PLANE_Y]就是要存在Y变量的地址
  42. //wView.data()[C2PlanarLayout::PLANE_U]就是要存在U变量的地址
  43. C2GraphicView wView = mOutBlock->map().get();
  44. ...
  45. //setDecodeArgs所作的主要工作是,告诉解码器,输入数据的地址是什么,输出地址包括Y/U/V
  46. //分量的地址是什么,输入数据的长度是多少
  47. if (!setDecodeArgs(&s_decode_ip, &s_decode_op, &rView, &wView,
  48. inOffset + inPos, inSize - inPos, workIndex)) {
  49. mSignalledError = true;
  50. work->workletsProcessed = 1u;
  51. work->result = C2_CORRUPTED;
  52. return;
  53. }
  54. if (false == mHeaderDecoded) {
  55. /* Decode header and get dimensions */
  56. setParams(mStride, IVD_DECODE_HEADER);
  57. }
  58. //解码器库是用了第三方的,已经被谷歌收购
  59. (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op);
  60. }
  61. if (s_decode_op.i4_reorder_depth >= 0 && mOutputDelay != s_decode_op.i4_reorder_depth) {
  62. //目前不清楚把这个重排序长度告诉上层有什么作用,TODO
  63. mOutputDelay = s_decode_op.i4_reorder_depth;
  64. ALOGV("New Output delay %d ", mOutputDelay);
  65. C2PortActualDelayTuning::output outputDelay(mOutputDelay);
  66. std::vector<std::unique_ptr<C2SettingResult>> failures;
  67. c2_status_t err =
  68. mIntf->config({&outputDelay}, C2_MAY_BLOCK, &failures);
  69. if (err == OK) {
  70. work->worklets.front()->output.configUpdate.push_back(
  71. C2Param::Copy(outputDelay));
  72. }
  73. continue;
  74. }
  75. if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) {
  76. if (mHeaderDecoded == false) {
  77. mHeaderDecoded = true;
  78. setParams(ALIGN64(s_decode_op.u4_pic_wd), IVD_DECODE_FRAME);
  79. }
  80. if (s_decode_op.u4_pic_wd != mWidth || s_decode_op.u4_pic_ht != mHeight) {
  81. mWidth = s_decode_op.u4_pic_wd;
  82. mHeight = s_decode_op.u4_pic_ht;
  83. CHECK_EQ(0u, s_decode_op.u4_output_present);
  84. C2StreamPictureSizeInfo::output size(0u, mWidth, mHeight);
  85. std::vector<std::unique_ptr<C2SettingResult>> failures;
  86. c2_status_t err = mIntf->config({&size}, C2_MAY_BLOCK, &failures);
  87. if (err == OK) {
  88. work->worklets.front()->output.configUpdate.push_back(
  89. C2Param::Copy(size));
  90. }
  91. continue;
  92. }
  93. }
  94. (void)getVuiParams();
  95. hasPicture |= (1 == s_decode_op.u4_frame_decoded_flag);
  96. if (s_decode_op.u4_output_present) {
  97. //通过createGraphicBuffer调用,将mOutBlock"转换"成C2Buffer对象
  98. //把C2Buffer添加到work对象的输出队列中
  99. //通过listener->onWorkDone_nb回调,可以将work返回到CCodec层
  100. //以上是这个函数以及其内部调用的主要实现内容,内部调用的finish()函数属于SimpleC2Component
  101. finishWork(s_decode_op.u4_ts, work);
  102. }
  103. inPos += s_decode_op.u4_num_bytes_consumed;
  104. }
  105. if (eos) {
  106. drainInternal(DRAIN_COMPONENT_WITH_EOS, pool, work);
  107. mSignalledOutputEos = true;
  108. } else if (!hasPicture) {
  109. fillEmptyWork(work);
  110. }
  111. work->input.buffers.clear();
  112. }
复制代码

在Component中,输入与输出对象都封装在work对象中,以致 上下层的设置 交互对象也包括在work对象中,与OMX是不一样的,OMX的数据对象是BufferHeader,输入是一个Input BufferHeader,输出是一个Output BufferHeader,对象中包括buffer地址,分配的buffer大小,有用 数据长度,有用 数据长度的偏移量,buffer标志等。 那么,work对象也应该会包括雷同 的成员。

我们来看两张头脑 导图,全局观察work对象。

在这里插入图片形貌

在这里插入图片形貌

C2SoftAvcDec::process中有一句代码,从work中访问rView。

  1. rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
复制代码

从上述两图中,我们可以追踪这一条访问线路,访问C2Work对象的成员C2FrameData,继续访问C2FrameData对外的成员vector linearBlocks(),C2ConstLinearBlock有一个方法C2Acquirable map(),这个映射方法返回一个C2ReadView对象,这个C2ReadView对象有一个data()[]数组,指向了Y/U/V的向量地址,也就是真正存放解码数据的内存地址。而Input与Output都是以C2FrameData来形貌 ,Output并非像Input一样,直接作为C2Work的成员,而是作为C2Work->worklets的成员。worklet是一个list范例 ,C2SoftAvcDec在存放output buffer的时间 ,总是存放在第一个worklets的output中,参见头脑 导图,output是C2FrameData范例 ,它拥有一个C2Buffer容器,C2SoftAvcDec总是将新的output buffer丢进容器中,它可以一次丢很多个output buffer,然后一次性通过work回送到上层,上层可以一次性从work中取到多个output buffer去作渲染。C2WorkOrdinalStruct ordinal包括着buffer的pts与frameIndex信息。这里有个疑问待办理 ,为什么output buffer总是存放在第一个worklets的output中,worklets作为一个队列对象,有什么其他的意义?

上面我们分析了两个点,一个是模块的消息处理机制,另一个是怎样 送数据到解码器再取出帧数据回送到上层,接下来看第三点,CCodec每次送多少输入数据下来,component每次处理多少数据,回送输出数据给CCodec作渲染在哪些地方。

上层是调用SimpleC2Component::queue_nb接口送数据下来的。

  1. \\av\media\codec2\components\base\SimpleC2Component.cpp
  2. c2_status_t SimpleC2Component::queue_nb(std::list<std::unique_ptr<C2Work>> * const items) {
  3. {
  4. Mutexed<ExecState>::Locked state(mExecState);
  5. if (state->mState != RUNNING) {
  6. return C2_BAD_STATE;
  7. }
  8. }
  9. bool queueWasEmpty = false;
  10. {
  11. Mutexed<WorkQueue>::Locked queue(mWorkQueue);
  12. queueWasEmpty = queue->empty();
  13. while (!items->empty()) {
  14. queue->push_back(std::move(items->front()));
  15. items->pop_front();
  16. }
  17. }
  18. if (queueWasEmpty) {
  19. (new AMessage(WorkHandler::kWhatProcess, mHandler))->post();
  20. }
  21. return C2_OK;
  22. }
复制代码

观察上面的代码,入参是一个列表对象,也就是说,每次送多个work,一个work可以包括一个C2Buffer容器,码流都是放在容器的第一个元素,固然 一个容器可以放多个C2Buffer,但它就只放了一个C2Buffer。我们可以从下面的代码中发现,每一次的process,都只从work中取一个C2Buffer。

  1. \\av\media\codec2\components\avc\C2SoftAvcDec.cpp
  2. void C2SoftAvcDec::process(
  3. const std::unique_ptr<C2Work> &work,
  4. const std::shared_ptr<C2BlockPool> &pool) {
  5. ...
  6. uint32_t workIndex = work->input.ordinal.frameIndex.peeku() & 0xFFFFFFFF;
  7. C2ReadView rView = mDummyReadView;
  8. if (!work->input.buffers.empty()) {
  9. //关注buffers[0]
  10. rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
  11. inSize = rView.capacity();
  12. }
  13. }
复制代码

SimpleC2Component::processQueue()每次只处理一个work,处理完就把work回奉上 去。

  1. \\av\media\codec2\components\base\SimpleC2Component.cpp
  2. bool SimpleC2Component::processQueue() {
  3. ....
  4. ALOGV("start processing frame #%" PRIu64, work->input.ordinal.frameIndex.peeku());
  5. //处理work
  6. process(work, mOutputBlockPool);
  7. ALOGV("processed frame #%" PRIu64, work->input.ordinal.frameIndex.peeku());
  8. Mutexed<WorkQueue>::Locked queue(mWorkQueue);
  9. if (work->workletsProcessed != 0u) {
  10. queue.unlock();
  11. Mutexed<ExecState>::Locked state(mExecState);
  12. ALOGV("returning this work");
  13. std::shared_ptr<C2Component::Listener> listener = state->mListener;
  14. state.unlock();
  15. //回送work
  16. listener->onWorkDone_nb(shared_from_this(), vec(work));
  17. }
  18. ...
  19. }
复制代码

在没有新送下来的work必要 处理的时间 ,processQueue()会调用drain接口作“渲染”操作,它会看解码器是否有帧数据天生 ,有的话,就添补 到work中回送到上层。

  1. \\av\media\codec2\components\base\SimpleC2Component.cpp
  2. bool SimpleC2Component::processQueue() {
  3. ....
  4. if (!work) {
  5. c2_status_t err = drain(drainMode, mOutputBlockPool);
  6. if (err != C2_OK) {
  7. Mutexed<ExecState>::Locked state(mExecState);
  8. std::shared_ptr<C2Component::Listener> listener = state->mListener;
  9. state.unlock();
  10. listener->onError_nb(shared_from_this(), err);
  11. }
  12. return hasQueuedWork;
  13. }
  14. ...
  15. }
复制代码

另一个渲染的地方是在process()中,解码完发现有帧数据的时间 ,就调用finishWork()将work回送。

  1. \\av\media\codec2\components\avc\C2SoftAvcDec.cpp
  2. void C2SoftAvcDec::process(
  3. const std::unique_ptr<C2Work> &work,
  4. const std::shared_ptr<C2BlockPool> &pool) {
  5. ...
  6. if (s_decode_op.u4_output_present) {
  7. finishWork(s_decode_op.u4_ts, work);
  8. }
  9. ...
  10. }
复制代码

5 小结

Component内部的逻辑还是比较好明白 的,重点在于它是怎样 申请buffer的,怎样 将buffer“送”给解码器,解码完后是怎样 取得buffer并返回上层,难点在于work对象层层封装,当你要访问实际 内存地址时,怎样 访问,假如 要取得内存的handle,又要怎样 访问,这一点通过将work对象一层一层的“绘制”出来,就好懂得多。接下来标题 来了,在OMX中,上下层的交互设置 是通过setParamerter/getParamerter等接口举行 的,那么在Codec2中是怎样 举行 的?Codec2中到底有没有像OMX一样的BufferCountActual计划 ?Codec2在调用nativewindow的setMaxDequeuedBufferCount时是怎样 确定maxDequeueBufferCount的?GraphicBuffer的生命周期是怎样 控制的?

到此这篇关于一文搞懂Codec2解码组件的文章就先容 到这了,更多相干 Codec2解码组件内容请搜索 脚本之家从前 的文章或继续欣赏 下面的相干 文章盼望 大家以后多多支持脚本之家!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

avatar 我们还在一起 | 2021-9-21 18:46:57 | 显示全部楼层
太邪乎了吧?
回复

使用道具 举报

avatar 爱过就是完全脖 | 2021-9-24 10:15:25 | 显示全部楼层
admin楼主,我告诉你一个你不知道的的秘密,有一个牛逼的网站,影视频道的网站所有电影和连续剧都可以免费看的。访问地址:http://tv.mxswl.com
回复

使用道具 举报

avatar lc泠辰 | 2021-9-24 10:15:29 | 显示全部楼层
我默默的回帖,从不声张!
回复

使用道具 举报

avatar 笑到呆萌 | 2021-10-1 05:36:09 | 显示全部楼层
论坛的帖子越来越有深度了!
回复

使用道具 举报

avatar 静美人2017 | 2021-10-4 09:45:00 | 显示全部楼层
admin楼主写的很经典!
回复

使用道具 举报

avatar 司驴迁咏 | 2021-10-9 05:31:38 | 显示全部楼层
以后就跟admin楼主混了!
回复

使用道具 举报

admin楼主是男的还是女的?
回复

使用道具 举报

有内涵!
回复

使用道具 举报

avatar 天剑和nee | 前天 15:05 | 显示全部楼层
不错的帖子,值得收藏!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则