Openmax IL (二)Android多媒体编解码Component

带着三个问题学习这个部分: 
问题1:Android中间各类编解码库的加载与管理? 
问题1:Android如何肯定使用那个编解码器而且初始化? 
问题2:Android如何集成一个新的编解码器,硬件平台相关/非硬件平台相关两种状况?css

按照OpenMax IL的简述,编解码架构中间相当重要的是以下两点: 
1,各个不一样功能的conponent 
2,平台商实现的“media.resouce_manager”,用来管理conponent须要的资源。从而控制component状态的变化。node

PS: 
本文是基于Android7.0+QCOM8909高通平台,播放本地视频的状况分析代码,先对架构有一个总体的认识,后面再开始对流媒体以及协议的学习与分析。android

1,编解码加载与管理流程

1.1,系统相关的uml类图—看编解码相关成员模块


分析类图: 
其中主要包括个模块 
1,nuplay 
2,MediaCodec 
3,OMXConponent 
4,FileSouce数组

1.2编解码库的加载和初始化管理


二,加载初始化时序图,以及流程分析bash

一,系统包含的编解码库文件的描述文件 
“/etc/media_codecs.xml” 
“/etc/media_codecs_performance.xml”架构

这两个文件列举了这个系统支持的全部音视频编解码对应的支持格式,仅仅在开机初始化的时候加载一次 
project 编译文件:app

# media_profiles and media_codecs xmls for msm8909 ifeq ($(TARGET_ENABLE_QC_AV_ENHANCEMENTS), true) PRODUCT_COPY_FILES += device/Project/media/media_profiles_8909.xml:system/etc/media_profiles.xml \ device/Project/media/media_codecs_8909.xml:system/etc/media_codecs.xml \ device/Project/media/media_codecs_performance_8909.xml:system/etc/media_codecs_performance.xml endif
  • 1
  • 2
  • 3
  • 4
  • 5

qcom/base.mk编译文件ide

PRODUCT_COPY_FILES += \    frameworks/av/media/libstagefright/data/media_codecs_google_audio.xml:system/etc/media_codecs_google_audio.xml \ frameworks/av/media/libstagefright/data/media_codecs_google_telephony.xml:system/etc/media_codecs_google_telephony.xml \ frameworks/av/media/libstagefright/data/media_codecs_google_video.xml:system/etc/media_codecs_google_video.xml \ device/qcom/common/media/media_profiles.xml:system/etc/media_profiles.xml \ #覆盖 device/qcom/common/media/media_codecs.xml:system/etc/media_codecs.xml #覆盖 
  • 1
  • 2
  • 3
  • 4

相关文件描述函数

//media_codecs.xml(media_codecs_8909.xml)
<CodecList> <Include href="media_codecs_google_audio.xml" /> <Include href="media_codecs_google_telephony.xml" /> <Settings> <Setting name="max-video-encoder-input-buffers" value="9" /> </Settings> <Encoders> <!-- Video Hardware --> <MediaCodec name="OMX.qcom.video.encoder.avc" type="video/avc" > <Quirk name="requires-allocate-on-input-ports" /> <Quirk name="requires-allocate-on-output-ports" /> <Quirk name="requires-loaded-to-idle-after-allocation" /> <Limit name="size" min="96x64" max="1280x720" /> <Limit name="alignment" value="2x2" /> <Limit name="block-size" value="16x16" /> <Limit name="blocks-per-second" min="1" max="108000" /> <Limit name="bitrate" range="1-14000000" /> <Limit name="concurrent-instances" max="8" /> <Feature name="intra-refresh" /> </MediaCodec> </Encoders> <Decoders> <!-- Audio Software --> <MediaCodec name="OMX.qti.audio.decoder.flac" type="audio/flac" > <Limit name="concurrent-instances" max="10" /> </MediaCodec> <!-- Video Hardware --> <MediaCodec name="OMX.qcom.video.decoder.avc" type="video/avc" > <Quirk name="requires-allocate-on-input-ports" /> <Quirk name="requires-allocate-on-output-ports" /> <Quirk name="defers-output-buffer-allocation" /> <Limit name="size" min="64x64" max="1920x1088" /> <Limit name="alignment" value="2x2" /> <Limit name="block-size" value="16x16" /> <Limit name="blocks-per-second" min="1" max="244800" /> <Limit name="bitrate" range="1-20000000" /> <Feature name="adaptive-playback" /> <Limit name="concurrent-instances" max="8" /> </MediaCodec> </Decoders> <Include href="media_codecs_google_video.xml" /> </CodecList> // media_codecs_performance.xml (media_codecs_performance_8909.xml) <MediaCodecs> <Encoders> <MediaCodec name="OMX.qcom.video.encoder.avc" type="video/avc" update="true"> <Limit name="measured-frame-rate-320x240" range="183-183" /> <Limit name="measured-frame-rate-720x480" range="56-56" /> <Limit name="measured-frame-rate-1280x720" range="25-25" /> </MediaCodec> <!--还有不少MediaCodec成员--> </Encoders> <Decoders> <MediaCodec name="OMX.qcom.video.decoder.avc" type="video/avc" update="true"> <Limit name="measured-frame-rate-320x240" range="457-457" /> <Limit name="measured-frame-rate-720x480" range="274-274" /> <Limit name="measured-frame-rate-1280x720" range="168-168" /> <Limit name="measured-frame-rate-1920x1088" range="54-54" /> </MediaCodec> </Decoders> // media_codecs_google_audio.xml //media_codecs_google_telephony.xml //media_codecs_google_video.xml //media_codecs_performance.xml //media_profiles.xml 
  • 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
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

二,初始化加载流程如分析 
上图: 
厂商和google支持的compont初始化加载时序图post

从上面的交互能够看出,MediaCodecList初始化的时候: 
1,实质就是经过MediaPlayerService,MediaCodecService去支配依次建立OMX实例(交互10),OMXMaster实例(交互11),从而初始化MediaCodecList::mOMX成员。而OMXMaster构造的时候,实质是去加载,遍历vendor和google的OMXPlugin,加载各个component。 
这部分重点函数在交互12:addPlugin()

//OMXMaster.c OMXMaster::OMXMaster() : mVendorLibHandle(NULL) { //省略不重要代码 addVendorPlugin(); addPlugin(new SoftOMXPlugin);//Soft就是google支持的软解 } //看看加载厂商支持的,就一句话 void OMXMaster::addVendorPlugin() { addPlugin("libstagefrighthw.so"); } // void OMXMaster::addPlugin(const char *libname) { mVendorLibHandle = dlopen(libname, RTLD_NOW); typedef OMXPluginBase *(*CreateOMXPluginFunc)(); //这里映射hardware/qcom/media/libstagefrighthw/QComOMXPlugin.cpp //实质是建立一个厂商实现的OMXPluginBase子类,厂商硬解,实例化 CreateOMXPluginFunc createOMXPlugin = (CreateOMXPluginFunc)dlsym( mVendorLibHandle, "createOMXPlugin"); if (!createOMXPlugin) createOMXPlugin = (CreateOMXPluginFunc)dlsym( mVendorLibHandle, "_ZN7android15createOMXPluginEv"); if (createOMXPlugin) { addPlugin((*createOMXPlugin)());//经过vendor库文件函数映射,建立OMXPluginBase,仍是调到这个接口 } } 
  • 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
  • 26
  • 27
  • 28
  • 29

看了上面的代码,咱们再来分析addPlugin

void OMXMaster::addPlugin(OMXPluginBase *plugin) {
    Mutex::Autolock autoLock(mLock);
    mPlugins.push_back(plugin);//将全部的plugin保存
    OMX_U32 index = 0; char name[128]; OMX_ERRORTYPE err; //调用plugin接口,实质是枚举出plugin支持的全部的component,稍后在具体分析 while ((err = plugin->enumerateComponents( name, sizeof(name), index++)) == OMX_ErrorNone) { String8 name8(name); //... // KeyedVector<String8, OMXPluginBase *> mPluginByComponentName; mPluginByComponentName.add(name8, plugin); } // ... }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

mPluginByComponentName,这个数组包含系统全部的编解码库的信息。关于plugin->enumerateComponents,咱们具体状况具体分析,加入是google软解,SoftOMXPlugin,查阅代码SoftOMXPlugin.cpp,发现就是根据index返回compont:

OMX_ERRORTYPE SoftOMXPlugin::enumerateComponents(
        OMX_STRING name,
        size_t /* size */,
        OMX_U32 index) {
    //... strcpy(name, kComponents[index].mName); return OMX_ErrorNone; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

那看Component是啥?就是一个数组!列举了全部的软解component信息,相似于这样的:

static const struct { const char *mName;//名字,要和xml文件中间一致的! const char *mLibNameSuffix;//软解库文件后缀 const char *mRole; //角色,编解码?音视频? } kComponents[] = { { "OMX.google.aac.decoder", "aacdec", "audio_decoder.aac" }, { "OMX.google.aac.encoder", "aacenc", "audio_encoder.aac" }, //全部软解支持的都在这里 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

那若是是平台商的硬解码呢?以MSM8909为例: 
在平台实现的OMXPluginBase子类,枚举component函数实际是映射到libOmxCore.so中间的方法:

mComponentNameEnum =
            (ComponentNameEnumFunc)dlsym(mLibHandle, "OMX_ComponentNameEnum");
  • 1
  • 2

查阅hardware/qcom/media/mm-core/src/common/qc_omx_core.c这只文件发现:

typedef struct _omx_core_cb_type { char* name;// Component name ,对应在xml文件中间有值 create_qc_omx_component fn_ptr;// create instance fn ptr void* inst[OMX_COMP_MAX_INST];// Instance handle void* so_lib_handle;// So Library handle char* so_lib_name;// so directory char* roles[OMX_CORE_MAX_CMP_ROLES];// roles played }omx_core_cb_type; //对应初始化 omx_core_cb_type core[] = { { "OMX.qcom.video.decoder.avc", NULL, // Create instance function // Unique instance handle { NULL }, NULL, // Shared object library handle "libOmxVdec.so", { "video_decoder.avc" } }, //... }
  • 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
  • 26
  • 27

2,加载完component,而后加载解析各个xml文件,根据xml文件描述,主要是初始化MediaCodecList:: mCodecInfos (MediaCodecInfo类型的Vector)。 
注意:经过研究全部的xml文件结构以及解析流程,一个Audio Encoder/Decoder只会有一个”type“标签,而video能够有多个,这个就是支持的mineType。

总结一下: 
1,初始化工做的大体流程,能够理解为OMXClient和OMXMaster的交互,OMXMaster管理全部的conponent信息,提供接口给client,用来对component的操做: 
OMXClient类图主要成员函数以及变量

2,至于IOMX这个角色是干吗的,具体咱们在后面分析,剧透,这个就是控制编解码流程,经过ACodec->MuxOMX->OMX->ComponentInstance ->OMX Core ->Concrate component 这条控制线,进行编解码流程控制。

3,这个流程分析,若是咱们要集成一个软/硬 编解码库,咱们的工做有以下,固然这三点还不够,还须要作什么还要往下看:

  1. 添加信息到xml文件
  2. 根据软硬解码类型,添加到数组core/kComponent
  3. 同时集成相关的编解码库文件到系统

1.3,如何匹配而且初始化一个编解码库


初始化一个编解码库文件,在以前的文章中间已经有分析到,若是是一个视频文件,先经过MediaExtractor解封装,得到到一个MineType,这个就是咱们决定要用何种编解码的重要依据,也是惟一依据。下面开始分析。

首先,上初始化时序图:

这里写图片描述

在nuplayer::instantiateDecoder调用的时候,mineType是已知的,这个是查找解码componnent的惟一依据。 
按照nuplayer::decoder->MediaCodec->ACodec的调用封装顺序分析 
交互5:nuplayer::decoder::onConfig中间作的主要工做以下:

1,初始化一个MediaCodec,实质是实例化一个ACodec而且至于ACodec::UninitializedState状态。 
2 ,MediaCodec 经过交互11(initiateAllocateComponent)触发ACodec的系列操做,去根据mineType,最终是构建一个OMXNodeInstance,这是控制OMX core的接口。那这部分的重点,就是看OMXNodeInstance究竟是封装了什么东西,以及后面的交互33-42的控制component流程能够体现其做用。下面重点分析这个实例化流程,学习OMXNodeInstance在Android Openmax中间的关键做用。

相关调用参数说明: 
交互18: 主要是传入mineTpe,返回一组知足的matchingCodecs,随后在其中遍历,根据遍历成功的第一个,allocatenode。

MediaCodecList::findMatchingCodecs( mime.c_str(), encoder, // createEncoder 0, // flags &matchingCodecs);
  • 1
  • 2
  • 3
  • 4
  • 5

交互21:OMX::allocateNode传入参数主要是为CodecName,返回一个node_id

status_t OMX::allocateNode(
        const char *name, const sp<IOMXObserver> &observer, sp<IBinder> *nodeBinder, node_id *node) { Mutex::Autolock autoLock(mLock); *node = 0; if (nodeBinder != NULL) { *nodeBinder = NULL; } if (mNodeIDToInstance.size() == kMaxNodeInstances) { // all possible node IDs are in use return NO_MEMORY; }//系统支持的node instance是有上限的 OMXNodeInstance *instance = new OMXNodeInstance(this, observer, name); //建立一个实例,第三个参数是codec name OMX_COMPONENTTYPE *handle; //得到一个handle,这个继续跟代码 OMX_ERRORTYPE err = mMaster->makeComponentInstance( name, &OMXNodeInstance::kCallbacks, instance, &handle); //... *node = makeNodeID_l(instance);//得到node_id mDispatchers.add(*node, new CallbackDispatcher(instance)); instance->setHandle(*node, handle);//设置操做句柄 mLiveNodes.add(IInterface::asBinder(observer), instance); IInterface::asBinder(observer)->linkToDeath(this); return OK; } //下面看OMXMaster::makeComponentInstance,实质是根据名字找到对应的OMXPluginBase,而后调用OMXPluginBase::makeComponentInstance,以AAC软件为例。则是调用以下: /* @name:"OMX.google.mp3.decoder" */ OMX_ERRORTYPE SoftOMXPlugin::makeComponentInstance( const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component) { for (size_t i = 0; i < kNumComponents; ++i) { if (strcmp(name, kComponents[i].mName)) { continue; } //1,遍历kComponent,找到这个解码库名字对应信息,找到继续下走,不然查找下一个。 AString libName = "libstagefright_soft_"; libName.append(kComponents[i].mLibNameSuffix); libName.append(".so"); //2,解码库名字对应信息,合成一个解码库的名字,这里解码库后缀"aacdec",那么编解码库就是libstagefright_soft_aacdec void *libHandle = dlopen(libName.c_str(), RTLD_NOW); if (libHandle == NULL) { ALOGE("unable to dlopen %s: %s", libName.c_str(), dlerror()); return OMX_ErrorComponentNotFound; } //3,读解码库,libstagefright_soft_aacdec,映射出其中名字为“createSoftOMXComponent”的建立方法 typedef SoftOMXComponent *(*CreateSoftOMXComponentFunc)( const char *, const OMX_CALLBACKTYPE *, OMX_PTR, OMX_COMPONENTTYPE **); CreateSoftOMXComponentFunc createSoftOMXComponent = (CreateSoftOMXComponentFunc)dlsym( libHandle, "_Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPE" "PvPP17OMX_COMPONENTTYPE"); if (createSoftOMXComponent == NULL) { dlclose(libHandle); libHandle = NULL; return OMX_ErrorComponentNotFound; } //4,执行“createSoftOMXComponent”的建立方法,建立一个SoftOMXComponent,关注下component赋值 sp<SoftOMXComponent> codec = (*createSoftOMXComponent)(name, callbacks, appData, component); if (codec == NULL) { dlclose(libHandle); libHandle = NULL; return OMX_ErrorInsufficientResources; } OMX_ERRORTYPE err = codec->initCheck(); if (err != OMX_ErrorNone) { dlclose(libHandle); libHandle = NULL; return err; } codec->incStrong(this); codec->setLibHandle(libHandle); return OMX_ErrorNone; } return OMX_ErrorInvalidComponentName; } //5,查阅这个AAC解码库,找到建立codec对应的方法,其实就是实例化一个SoftAAC2,关注component赋值 android::SoftOMXComponent *createSoftOMXComponent( const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component) { return new android::SoftAAC2(name, callbacks, appData, component); } //6,追查component赋值,发现其赋值是调用的基类的赋值方法继承关系为:SoftAAC2 : public SimpleSoftOMXComponent:SoftOMXComponent,那么其赋值为: SoftOMXComponent::SoftOMXComponent( const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, OMX_COMPONENTTYPE **component) : mName(name), mCallbacks(callbacks), mComponent(new OMX_COMPONENTTYPE), mLibHandle(NULL) { //可见,就是建立一个OMX_COMPONENTTYPE,赋值后,传给component mComponent->nSize = sizeof(*mComponent); mComponent->nVersion.s.nVersionMajor = 1; mComponent->nVersion.s.nVersionMinor = 0; mComponent->nVersion.s.nRevision = 0; mComponent->nVersion.s.nStep = 0; mComponent->pComponentPrivate = this;//重点 mComponent->pApplicationPrivate = appData; mComponent->GetComponentVersion = NULL; mComponent->SendCommand = SendCommandWrapper; mComponent->GetParameter = GetParameterWrapper; mComponent->SetParameter = SetParameterWrapper; mComponent->GetConfig = GetConfigWrapper; mComponent->SetConfig = SetConfigWrapper; mComponent->GetExtensionIndex = GetExtensionIndexWrapper; mComponent->GetState = GetStateWrapper; mComponent->ComponentTunnelRequest = NULL; mComponent->UseBuffer = UseBufferWrapper; mComponent->AllocateBuffer = AllocateBufferWrapper; mComponent->FreeBuffer = FreeBufferWrapper; //以此为例,查看这里封装的什么 mComponent->EmptyThisBuffer = EmptyThisBufferWrapper; mComponent->FillThisBuffer = FillThisBufferWrapper; mComponent->SetCallbacks = NULL; mComponent->ComponentDeInit = NULL; mComponent->UseEGLImage = NULL; mComponent->ComponentRoleEnum = NULL; *component = mComponent; } // static OMX_ERRORTYPE SoftOMXComponent::EmptyThisBufferWrapper( OMX_HANDLETYPE component, OMX_BUFFERHEADERTYPE *buffer) { SoftOMXComponent *me = (SoftOMXComponent *) ((OMX_COMPONENTTYPE *)component)->pComponentPrivate; //基类无此方法实现,查看子类,看下面 return me->emptyThisBuffer(buffer); } OMX_ERRORTYPE SimpleSoftOMXComponent::emptyThisBuffer( OMX_BUFFERHEADERTYPE *buffer) { sp<AMessage> msg = new AMessage(kWhatEmptyThisBuffer, mHandler); msg->setPointer("header", buffer); msg->post(); return OMX_ErrorNone; } //根据这个kWhatEmptyThisBuffer消息查看,其中会发现不少SimpleSoftOMXComponent也没有实现的方法,这个时候,就须要查看SoftAAC2中间的具体实现了。 
  • 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
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176

从上面的代码分析来看从makeComponentInstance初始化一个component,实质是将SoftOMXComponent与具体的解码库结合。每一个解码库,都必须实现一个SimpleSoftOMXComponent接口,如SoftAAC2,这个类是和具体的解码库关联的接口文件,那么集成一个解码库,还须要实现一个SimpleSoftOMXComponent子类,建立要点:

  1. 实现一个createSoftOMXComponent,用于初始化
  2. 实现一个SimpleSoftOMXComponent子类,调用libstagefright_soft_xxx 中间的编解码方法,实现编解码流程,具体接口参考SimpleSoftOMXComponent

固然若是是视频的解码库,那么就须要去查阅SoftVideoDecoderOMXComponent的封装与实现

能够看下OMX_CALLBACKTYPE和OMX_COMPONENTTYPE在OMX中间的声明与定义。

1.4 从控制Conponent分析OMXNodeInstance到底封装了什么?

在ACodec经过OMX建立 OMXNodeInstance实例的时候,关注这个成员:

// static OMX_CALLBACKTYPE OMXNodeInstance::kCallbacks = { &OnEvent, &OnEmptyBufferDone, &OnFillBufferDone };
  • 1
  • 2
  • 3
  • 4

这个OMX_CALLBACKTYPE最终是赋值给哪里查看SoftOMXComponent的三个相关方法。以下:

void SoftOMXComponent::notify( OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2, OMX_PTR data) { (*mCallbacks->EventHandler)( mComponent, mComponent->pApplicationPrivate, event, data1, data2, data); } void SoftOMXComponent::notifyEmptyBufferDone(OMX_BUFFERHEADERTYPE *header) { (*mCallbacks->EmptyBufferDone)( mComponent, mComponent->pApplicationPrivate, header); } void SoftOMXComponent::notifyFillBufferDone(OMX_BUFFERHEADERTYPE *header) { (*mCallbacks->FillBufferDone)( mComponent, mComponent->pApplicationPrivate, header); }
相关文章
相关标签/搜索