1.android中用openmax来干啥?

android中的AwesomePlayer就 是用openmax来做(code)编解码,其实在openmax接口设计中,他不光能用来当编解码。通过他的组件可以组成一个完整的播放器,包括 sourc、demux、decode、output。但是为什么android只用他来做code呢?我认为有以下几方面:
1.在整个播放器中,解码器不得不说是最重要的一部分,而且也是最耗资源的一块。
如果全靠软解,直接通过cpu来运算,特别是高清视频。别的事你就可以啥都不干了。所以解码器是最需要硬件提供加速的部分。现在的高清解码芯片都是主芯
片+DSP结构,解码的工作都是通过DSP来做,不会在过多的占用主芯片。所有将芯片中DSP硬件编解码的能力通过openmax标准接口呈现出来,提供
上层播放器来用。我认为这块是openmax最重要的意义。
2.source 主要是和协议打交道,demux
分解容器部分,大多数的容器格式的分解是不需要通过硬件来支持。只是ts流这种格式最可能用到硬件的支持。因为ts格式比较特殊,单包的大小太小了,只有
188字节。所以也是为什么现在常见的解码芯片都会提供硬件ts demux 的支持。
3.音视频输出部分video\audio output
这块和操作系统关系十分紧密。可以看看著名开源播放器vlc。vlc
在mac、linux、Windows都有,功能上差别也不大。所以说他是跨平台的,他跨平台跨在哪?主要的工作量还是在音视频解码完之后的输出模块。因
为各个系统的图像渲染和音频输出实现方法不同,所以vlc需要针对每个平台实现不同的output。这部分内容放在openmax来显然不合适。
所以openmax 中硬件抽象的编解码是最为常用的,也是为什么android中只用它来抽象code。

2.android中openmax实现框架

1.上面已经说过了,android系统中只用openmax来做code,所以android向上抽象了一层OMXCodec,提供给上层播放器用。
播放器中音视频解码器mVideosource、mAudiosource都是OMXCodec的实例。
2.OMXCodec通过IOMX 依赖binder机制 获得 OMX服务,OMX服务 才是openmax 在android中 实现。
3. OMX把软编解码和硬件编解码统一看作插件的形式管理起来。
 

AwesomePlayer 中有个变量

  1. OMXClient mClient;

让我们看看   OMXClient

  1. class OMXClient {
  2. public:
  3. OMXClient();
  4. status_t connect();
  5. void disconnect();
  6. sp<IOMX> interface() {
  7. return mOMX;
  8. }
  9. private:
  10. sp<IOMX> mOMX;
  11. OMXClient(const OMXClient &);
  12. OMXClient &operator=(const OMXClient &);
  13. };

OMXClient 有个IOMX 的变量 mOMX ,这个就是和OMX服务进行binder通讯的。

在 AwesomePlayer 的构造函数中会调用 
  1. CHECK_EQ(mClient.connect(), (status_t)OK);
  1. status_t OMXClient::connect() {
  2. sp<IServiceManager> sm = defaultServiceManager();
  3. sp<IBinder> binder = sm->getService(String16("media.player"));
  4. sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
  5. CHECK(service.get() != NULL);
  6. mOMX = service->getOMX();
  7. CHECK(mOMX.get() != NULL);
  8. if (!mOMX->livesLocally(NULL /* node */, getpid())) {
  9. ALOGI("Using client-side OMX mux.");
  10. mOMX = new MuxOMX(mOMX);
  11. }
  12. return OK;
  13. }
  1. sp<IOMX> MediaPlayerService::getOMX() {
  2. Mutex::Autolock autoLock(mLock);
  3. if (mOMX.get() == NULL) {
  4. mOMX = new OMX;
  5. }
  6. return mOMX;
  7. }

OMXClient::connect函数是通过binder机制 获得到MediaPlayerService,然后通过MediaPlayerService来创建OMX的实例。这样OMXClient就获得到了OMX的入口,接下来就可以通过binder机制来获得OMX提供的服务。

也就是说OMXClient 是android中 openmax 的入口。
在创建音视频解码mVideoSource、mAudioSource的时候会把OMXClient中的sp<IOMX> mOMX的实例 传给mVideoSource、mAudioSource来共享使用这个OMX的入口。
也就是说一个AwesomePlayer对应着 一个IOMX 变量,AwesomePlayer中的音视频解码器共用这个IOMX变量来获得OMX服务。
  1. sp<IOMX> interface() {
  2. return mOMX;
  3. }
  1. mAudioSource = OMXCodec::Create(
  2. mClient.interface(), mAudioTrack->getFormat(),
  3. false, // createEncoder
  4. mAudioTrack);
  1. mVideoSource = OMXCodec::Create(
  2. mClient.interface(), mVideoTrack->getFormat(),
  3. false, // createEncoder
  4. mVideoTrack,
  5. NULL, flags, USE_SURFACE_ALLOC ? mNativeWindow : NULL);

    通过上文知道了,每个AwesomePlayer 只有一个OMX服务的入口,但是AwesomePlayer不一定就只需要1种解码器。有可能音视频都有,或者有很多种。这个时候这些解码器都需要OMX的服务,也就是OMX那头需要建立不同的解码器的组件来对应着AwesomePlayer中不同的code。OMX中非常重要的2个成员就是 OMXMaster 和 OMXNodeInstance。OMX通过这俩个成员来创建和维护不同的openmax 解码器组件,为AwesomePlayer中不同解码提供服务。让我们看看他们是怎么实现这些工作的。

    1.
    OMX中 OMXNodeInstance 负责创建并维护不同的实例,这些实例是根据上面需求创建的,以node作为唯一标识。这样播放器中每个OMXCodec在OMX服务端都对应有了自己的OMXNodeInstance实例。

    2.OMXMaster
    维护底层软硬件解码库,根据OMXNodeInstance中想要的解码器来创建解码实体组件。

    接下来我们假设视频解码器需要的是AVC,来看看解码器创建的流程。

    (默认走软解码)

    1.准备工作初始化OMXMaster

    OMX构造函数中会进行初始化。

    1. OMXMaster *mMaster;
    1. OMX::OMX()
    2. : mMaster(new OMXMaster),
    3. mNodeCounter(0) {
    4. }
    1. OMXMaster::OMXMaster()
    2. : mVendorLibHandle(NULL) {
    3. addVendorPlugin();
    4. addPlugin(new SoftOMXPlugin);
    5. }

    OMXMaster 负责OMX中编解码器插件管理,软件解码和硬件解码都是使用OMX标准,挂载plugins的方式来进行管理。

    软解通过 addPlugin(new SoftOMXPlugin);会把这些编解码器的名字都放在mPluginByComponentName中。
    android 默认会提供一系列的软件解码器。目前支持这些格式的软编解码。
    1. kComponents[] = {
    2. { "OMX.google.aac.decoder", "aacdec", "audio_decoder.aac" },
    3. { "OMX.google.aac.encoder", "aacenc", "audio_encoder.aac" },
    4. { "OMX.google.amrnb.decoder", "amrdec", "audio_decoder.amrnb" },
    5. { "OMX.google.amrnb.encoder", "amrnbenc", "audio_encoder.amrnb" },
    6. { "OMX.google.amrwb.decoder", "amrdec", "audio_decoder.amrwb" },
    7. { "OMX.google.amrwb.encoder", "amrwbenc", "audio_encoder.amrwb" },
    8. { "OMX.google.h264.decoder", "h264dec", "video_decoder.avc" },
    9. { "OMX.google.h264.encoder", "h264enc", "video_encoder.avc" },
    10. { "OMX.google.g711.alaw.decoder", "g711dec", "audio_decoder.g711alaw" },
    11. { "OMX.google.g711.mlaw.decoder", "g711dec", "audio_decoder.g711mlaw" },
    12. { "OMX.google.h263.decoder", "mpeg4dec", "video_decoder.h263" },
    13. { "OMX.google.h263.encoder", "mpeg4enc", "video_encoder.h263" },
    14. { "OMX.google.mpeg4.decoder", "mpeg4dec", "video_decoder.mpeg4" },
    15. { "OMX.google.mpeg4.encoder", "mpeg4enc", "video_encoder.mpeg4" },
    16. { "OMX.google.mp3.decoder", "mp3dec", "audio_decoder.mp3" },
    17. { "OMX.google.vorbis.decoder", "vorbisdec", "audio_decoder.vorbis" },
    18. { "OMX.google.vpx.decoder", "vpxdec", "video_decoder.vpx" },
    19. { "OMX.google.raw.decoder", "rawdec", "audio_decoder.raw" },
    20. { "OMX.google.flac.encoder", "flacenc", "audio_encoder.flac" },
    21. };

    硬件编解码是通过 addVendorPlugin();加载libstagefrighthw.so.各个芯片平台可以遵循openmax
    标准,生成libstagefrighthw.so的库来提供android应用。

    1. void OMXMaster::addVendorPlugin() {
    2. addPlugin("libstagefrighthw.so");
    3. }

    然后通过dlopen、dlsym来调用库中的函数。

    这部分准备工作是在AwesomePlayer的构造函数中
    CHECK_EQ(mClient.connect(), (status_t)OK); 已经完成了。

    2.创建mVideoSource

    有了上面的OMX,接下来会在AwesomePlayer::initVideoDecoder中创建mVideoSource
    实例,下面代码只保留的主要部分:
    1. status_t AwesomePlayer::initVideoDecoder(uint32_t flags) {
    2. ATRACE_CALL();
    3. mVideoSource = OMXCodec::Create(
    4. mClient.interface(), mVideoTrack->getFormat(),
    5. false, // createEncoder
    6. mVideoTrack,
    7. NULL, flags, USE_SURFACE_ALLOC ? mNativeWindow : NULL);
    8. status_t err = mVideoSource->start();
    9. return mVideoSource != NULL ? OK : UNKNOWN_ERROR;
    10. }

    保留主要部分,去除编码相关

    1. sp<MediaSource> OMXCodec::Create(
    2. const sp<IOMX> &omx,
    3. const sp<MetaData> &meta, bool createEncoder,
    4. const sp<MediaSource> &source,
    5. const char *matchComponentName,
    6. uint32_t flags,
    7. const sp<ANativeWindow> &nativeWindow) {
    8. int32_t requiresSecureBuffers;
    9. const char *mime;
    10. bool success = meta->findCString(kKeyMIMEType, &mime);
    11. CHECK(success);
    12. Vector<String8> matchingCodecs;
    13. Vector<uint32_t> matchingCodecQuirks;
    14. findMatchingCodecs(
    15. mime, createEncoder, matchComponentName, flags,
    16. &matchingCodecs, &matchingCodecQuirks);
    17. sp<OMXCodecObserver> observer = new OMXCodecObserver;
    18. IOMX::node_id node = 0;
    19. for (size_t i = 0; i < matchingCodecs.size(); ++i) {
    20. const char *componentNameBase = matchingCodecs[i].string();
    21. uint32_t quirks = matchingCodecQuirks[i];
    22. const char *componentName = componentNameBase;
    23. AString tmp;
    24. status_t err = omx->allocateNode(componentName, observer, &node);
    25. if (err == OK) {
    26. ALOGV("Successfully allocated OMX node '%s'", componentName);
    27. sp<OMXCodec> codec = new OMXCodec(
    28. omx, node, quirks, flags,
    29. createEncoder, mime, componentName,
    30. source, nativeWindow);
    31. observer->setCodec(codec);
    32. err = codec->configureCodec(meta);
    33. if (err == OK) {
    34. if (!strcmp("OMX.Nvidia.mpeg2v.decode", componentName)) {
    35. codec->mFlags |= kOnlySubmitOneInputBufferAtOneTime;
    36. }
    37. return codec;
    38. }
    39. ALOGV("Failed to configure codec '%s'", componentName);
    40. }
    41. }
    42. return NULL;
    43. }

    1.根据mVideoTrack传进来的视频信息,查找相匹配的解码器。

    1. bool success = meta->findCString(kKeyMIMEType, &mime);
    2. findMatchingCodecs(
    3. mime, createEncoder, matchComponentName, flags,
    4. &matchingCodecs, &matchingCodecQuirks);

    2. 创建OMXCodecObserver 实例,OMXCodecObserver功能后续会详细介绍。创建一个node 并初始化为0.

    1. sp<OMXCodecObserver> observer = new OMXCodecObserver;
    2. IOMX::node_id node = 0;

    3. 通过omx入口 依靠binder 机制调用OMX服务中的allocateNode(),这一步把匹配得到的解码器组件名、OMXCodecObserver实例和初始化为0的node一并传入。

    1. status_t err = omx->allocateNode(componentName, observer, &node);

    这个allocateNode 就是文章最开始讲的,在OMX那头创建一个和mVideoSource相匹配的解码实例。用node值作为唯一标识。
    让我们来看看真正的omx中allocateNode做了啥?

    1. status_t OMX::allocateNode(
    2. const char *name, const sp<IOMXObserver> &observer, node_id *node) {
    3. Mutex::Autolock autoLock(mLock);
    4. *node = 0;
    5. OMXNodeInstance *instance = new OMXNodeInstance(this, observer);
    6. OMX_COMPONENTTYPE *handle;
    7. OMX_ERRORTYPE err = mMaster->makeComponentInstance(
    8. name, &OMXNodeInstance::kCallbacks,
    9. instance, &handle);
    10. if (err != OMX_ErrorNone) {
    11. ALOGV("FAILED to allocate omx component '%s'", name);
    12. instance->onGetHandleFailed();
    13. return UNKNOWN_ERROR;
    14. }
    15. *node = makeNodeID(instance);
    16. mDispatchers.add(*node, new CallbackDispatcher(instance));
    17. instance->setHandle(*node, handle);
    18. mLiveNodes.add(observer->asBinder(), instance);
    19. observer->asBinder()->linkToDeath(this);
    20. return OK;
    21. }

    创建一个OMXNodeInstance实例。

    通过mMaster->makeComponentInstance创建真正解码器的组件,并通过handle与OMXNodeInstance关联。
    所以说mMaster->makeComponentInstance这里是建立解码器组件的核心。会把mVideoSource需要的解码器name一直传递下去。
    1. OMX_ERRORTYPE OMXMaster::makeComponentInstance(
    2. const char *name,
    3. const OMX_CALLBACKTYPE *callbacks,
    4. OMX_PTR appData,
    5. OMX_COMPONENTTYPE **component) {
    6. Mutex::Autolock autoLock(mLock);
    7. *component = NULL;
    8. ssize_t index = mPluginByComponentName.indexOfKey(String8(name));
    9. if (index < 0) {
    10. return OMX_ErrorInvalidComponentName;
    11. }
    12. OMXPluginBase *plugin = mPluginByComponentName.valueAt(index);
    13. OMX_ERRORTYPE err =
    14. plugin->makeComponentInstance(name, callbacks, appData, component);
    15. if (err != OMX_ErrorNone) {
    16. return err;
    17. }
    18. mPluginByInstance.add(*component, plugin);
    19. return err;
    20. }

    最开始OMXMaster通过 addPlugin(new SoftOMXPlugin);把支持的软解码放在mPluginByComponentName中,在makeComponentInstance中通过上面传下来的解码器的name值从mPluginByComponentName找到相对应的plugin,然后调用 
    plugin->makeComponentInstance(name, callbacks, appData, component);

    这里的plugin 值得就是软解SoftOMXPlugin 也就是调用了
    1. OMX_ERRORTYPE SoftOMXPlugin::makeComponentInstance(
    2. const char *name,
    3. const OMX_CALLBACKTYPE *callbacks,
    4. OMX_PTR appData,
    5. OMX_COMPONENTTYPE **component) {
    6. ALOGV("makeComponentInstance '%s'", name);
    7. for (size_t i = 0; i < kNumComponents; ++i) {
    8. if (strcmp(name, kComponents[i].mName)) {
    9. continue;
    10. }
    11. AString libName = "libstagefright_soft_";
    12. libName.append(kComponents[i].mLibNameSuffix);
    13. libName.append(".so");
    14. void *libHandle = dlopen(libName.c_str(), RTLD_NOW);
    15. if (libHandle == NULL) {
    16. ALOGE("unable to dlopen %s", libName.c_str());
    17. return OMX_ErrorComponentNotFound;
    18. }
    19. typedef SoftOMXComponent *(*CreateSoftOMXComponentFunc)(
    20. const char *, const OMX_CALLBACKTYPE *,
    21. OMX_PTR, OMX_COMPONENTTYPE **);
    22. CreateSoftOMXComponentFunc createSoftOMXComponent =
    23. (CreateSoftOMXComponentFunc)dlsym(
    24. libHandle,
    25. "_Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPE"
    26. "PvPP17OMX_COMPONENTTYPE");
    27. if (createSoftOMXComponent == NULL) {
    28. dlclose(libHandle);
    29. libHandle = NULL;
    30. return OMX_ErrorComponentNotFound;
    31. }
    32. sp<SoftOMXComponent> codec =
    33. (*createSoftOMXComponent)(name, callbacks, appData, component);
    34. if (codec == NULL) {
    35. dlclose(libHandle);
    36. libHandle = NULL;
    37. return OMX_ErrorInsufficientResources;
    38. }
    39. OMX_ERRORTYPE err = codec->initCheck();
    40. if (err != OMX_ErrorNone) {
    41. dlclose(libHandle);
    42. libHandle = NULL;
    43. return err;
    44. }
    45. codec->incStrong(this);
    46. codec->setLibHandle(libHandle);
    47. return OMX_ErrorNone;
    48. }
    49. return OMX_ErrorInvalidComponentName;
    50. }

    通过上面传下来的解码器的name,找到对应库的名字。假如是264的话,要加载的库就是
    libstagefright_soft_h264dec.so,也就是对应上层264解码的话,omx解码组件会加载对应的
    libstagefright_soft_h264dec.so库。相对应的软解代码在
    Android4.1.1\frameworks\av\media\libstagefright\codecs\on2\h264dec 中。

    加载完264解码库后
    通过dlopen、dlsym来调用库中函数。
    通过调用 SoftAVC 中的 createSoftOMXComponent 来创建真正264解码器实例SoftOMXComponent。以后真正视频解码的工作都是通过avc 这个SoftAVC实例完成的
    1. android::SoftOMXComponent *createSoftOMXComponent(
    2. const char *name, const OMX_CALLBACKTYPE *callbacks,
    3. OMX_PTR appData, OMX_COMPONENTTYPE **component) {
    4. return new android::SoftAVC(name, callbacks, appData, component);
    5. }

    经过这一路下来,终于完成了解码器的创建工作。简单总结一下。

    1.AwesomePlayer中通过initVideoDecoder 来创建video解码器mVideoSource
    2.mVideoSource 中通过上部分demux后的视频流 mVideoTrack来获得解码器的类型,通过类型调用omx->allocateNode 创建omx node实例与自己对应。以后都是通过node实例来操作解码器。
    3.在 omx->allocateNode中 通过mMaster->makeComponentInstance 来创建真正对应的解码器组件。这个解码器组件是完成之后解码实际工作的。
    4.在创建mMaster->makeComponentInstance过程中,也是通过上面mVideoTrack
    过来的解码器类型名,找到相对应的解码器的库,然后实例化。

    Android多媒体开发-- android中OpenMax的实现整体框架的更多相关文章

    1. Android多媒体开发-- OpenMax IL简介

      1.openmax 简介 http://www.khronos.org/openmax/ OpenMax是一个多媒体应用程序的框架标准,由NVIDIA公司和Khronos在2006年推出. OpenM ...

    2. Android多媒体开发-stagefright及AwesomePlayer相关知识梳理

      android的多媒体框架中, stagefright其实是AwesomePlayer的代理,就是个皮包公司. status_t StagefrightPlayer::setDataSource( c ...

    3. Android多媒体开发(3)————使用Android NKD编译havlenapetr-FFMpeg-7c27aa2

      1. 使用NDK去编译官方的FFmpeg原版的话,还得自己实现JNI层与Java层,工程量比较大.所以移植FFmpeg到Android平台时,可以移植一些已经实现JNI与JAVA层的开源项目,毕竟软件 ...

    4. 【Android 多媒体开发】 MediaPlayer 网络视频播放器

      作者 : 万境绝尘 (octopus_truth@163.com) 转载请著名出处 : http://blog.csdn.net/shulianghan/article/details/3889514 ...

    5. 码农人生——从未学过Android如何开发Android App 案例讲解-第002期案例

      标题有点晃眼,本次分享是002期博文的实践故事,不会有任何代码.也不会教别人android 如何开发,类似博文已经有大批大批,而且还会有陆陆续续的人写,我写的文章,主要是经验之谈,希望总结出的一些方法 ...

    6. android studio 开发android app 真机调试

      大家都知道开发android app 的时候可以有2种调试方式, 一种是Android Virtual Device(虚拟模拟器) ,另一种就是真机调试. 这里要说的是真机调试的一些安装步骤: 1. ...

    7. 【Android 多媒体开发】 MediaPlayer 状态机 接口 方法 解析

      作者 : 韩曙亮 转载请著名出处 :  http://blog.csdn.net/shulianghan/article/details/38487967 一. MediaPlayer 状态机 介绍 ...

    8. Android NDK开发 Jni中打日志LOG(二)

      HelloJni.c文件中,加入头文件和函数声明.最终文件如下: #include <jni.h> #include <string.h> #include<androi ...

    9. Android NDK开发 Jni中Debug(三)

      下载LLDB 配置Android Native - Debugger 调式结果如下 #include <jni.h> #include <string.h> #include& ...

    随机推荐

    1. [Immutable.js] Using fromJS() to Convert Plain JavaScript Objects into Immutable Data

      Immutable.js offers the fromJS() method to build immutable structures from objects and array. Object ...

    2. Android加载图片OOM错误解决方式

      前几天做项目的时候,甲方要求是PAD (SAMSUNG P600 10.1寸 2560*1600)的PAD上显示高分辨率的大图片. SQLITE採用BOLD方式存储图片,这个存取过程就不说了哈,网上一 ...

    3. Flashback Query(函数示例)

      Flashback Query 函数,存储过程,包,触发器等对象Flashback Drop 可以闪回与表相关联的对象, 如果是其他的对象,比如function,procedure,trigger等. ...

    4. box-shadow讲解1

      谈谈box-shadow的具体使用方法 语法: E {box-shadow: <length> <length> <length>?<length>?| ...

    5. codeforces 340E Iahub and Permutations(错排or容斥)

      转载请注明出处: http://www.cnblogs.com/fraud/          ——by fraud Iahub and Permutations Iahub is so happy ...

    6. [hdu5113]Black And White2014北京赛区现场赛B题(搜索加剪枝)

      转载请注明出处: http://www.cnblogs.com/fraud/          ——by fraud Black And White Time Limit: 2000/2000 MS ...

    7. Qt 'void QWidget::show()' is inaccessible

      今天在编写Qt窗体头文件时,尽然碰到了这样的报错,'void QWidget::show()' is inaccessible,'QWidget' is not an accessible base ...

    8. CDZSC_2015寒假新人(2)——数学 B

      B - B Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status ...

    9. (原)使用mkl计算特征值和特征向量

      转载请注明出处: http://www.cnblogs.com/darkknightzh/p/5585271.html 参考文档:mkl官方文档 lapack_int LAPACKE_sgeev(in ...

    10. js 对日期加减

      function getDate(days) { var now = new Date(), newDate = new Date(now.getTime() - 86400000 * days), ...