媒体编解码API使用示例
//获取相关格式文件的内容信息,如轨道数量、获取MIME信息、视频的高度与宽度、语言格式、播放总时长等
MediaExtractor mediaExtractor = new MediaExtractor();
try {mediaExtractor.setDataSource(path); // 设置数据源
} catch (IOException e1) {e1.printStackTrace();
}String mimeType = null; // "video/mp4v-es" - MPEG4 video, "audio/3gpp" - AMR narrowband audio
for (int i = 0; i < mediaExtractor.getTrackCount(); i++) { // 信道总数MediaFormat format = mediaExtractor.getTrackFormat(i); // 音频文件信息mimeType = format.getString(MediaFormat.KEY_MIME);if (mimeType.startsWith("video/")) { // 视频信道mediaExtractor.selectTrack(i); // 切换到视频信道try {mediaCodec = MediaCodec.createDecoderByType(mimeType); // 创建解码器,提供数据输出} catch (IOException e) {e.printStackTrace();}mediaCodec.configure(format, surface, null, 0);break;}
}
mediaCodec.start(); // 启动MediaCodec ,等待传入数据
1.createDecoderByType根据MimeType信息,创建相匹配的解码器。
public static MediaCodec createDecoderByType(@NonNull String type)throws IOException {return new MediaCodec(type, true /* nameIsType */, false /* encoder */);
}private MediaCodec(@NonNull String name, boolean nameIsType, boolean encoder) {Looper looper;if ((looper = Looper.myLooper()) != null) {mEventHandler = new EventHandler(this, looper);} else if ((looper = Looper.getMainLooper()) != null) {mEventHandler = new EventHandler(this, looper);} else {mEventHandler = null;}mCallbackHandler = mEventHandler;mOnFrameRenderedHandler = mEventHandler;mBufferLock = new Object();native_setup(name, nameIsType, encoder);
}
通过JNI调用到MediaCodec.cpp
static void android_media_MediaCodec_native_setup(sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);
}JMediaCodec::JMediaCodec(JNIEnv *env, jobject thiz,const char *name, bool nameIsType, bool encoder): mClass(NULL),mObject(NULL) {
...if (nameIsType) {mCodec = MediaCodec::CreateByType(mLooper, name, encoder, &mInitStatus);} else {mCodec = MediaCodec::CreateByComponentName(mLooper, name, &mInitStatus);}CHECK((mCodec != NULL) != (mInitStatus != OK));
}
2.CreateByType创建对应的MediaCodec并初始化
// frameworks\av\media\libstagefright\MediaCodec.cpp
sp<MediaCodec> MediaCodec::CreateByType(const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err, pid_t pid,uid_t uid) {Vector<AString> matchingCodecs;// 1.根据mime获取对应的codecMediaCodecList::findMatchingCodecs(mime.c_str(),encoder,0,&matchingCodecs);if (err != NULL) {*err = NAME_NOT_FOUND;}//2.创建对应的MediaCodec并初始化for (size_t i = 0; i < matchingCodecs.size(); ++i) {sp<MediaCodec> codec = new MediaCodec(looper, pid, uid);AString componentName = matchingCodecs[i];status_t ret = codec->init(componentName);if (err != NULL) {*err = ret;}if (ret == OK) {return codec;}ALOGD("Allocating component '%s' failed (%d), try next one.",componentName.c_str(), ret);}return NULL;
}
3.findMatchingCodecs获取对应的codec
先获取支持的编解码器列表,再匹配最佳的编解码器
//frameworks\av\media\libstagefright\MediaCodecList.cpp
void MediaCodecList::findMatchingCodecs(const char *mime, bool encoder, uint32_t flags,Vector<AString> *matches) {matches->clear();// 获取系统支持的编解码器列表const sp<IMediaCodecList> list = getInstance();if (list == nullptr) {return;}//匹配最佳的编解码器size_t index = 0;for (;;) {ssize_t matchIndex =list->findCodecByType(mime, encoder, index);if (matchIndex < 0) {break;}index = matchIndex + 1;const sp<MediaCodecInfo> info = list->getCodecInfo(matchIndex);CHECK(info != nullptr);AString componentName = info->getCodecName();if ((flags & kHardwareCodecsOnly) && isSoftwareCodec(componentName)) {ALOGV("skipping SW codec '%s'", componentName.c_str());} else {matches->push(componentName);ALOGV("matching '%s'", componentName.c_str());}}if (flags & kPreferSoftwareCodecs ||property_get_bool("debug.stagefright.swcodec", false)) {matches->sort(compareSoftwareCodecsFirst);}
}
4.获取系统支持的Codec列表
先通过binder调到MediaPlayerService的getCodecList函数
//frameworks\av\media\libstagefright\MediaCodecList.cpp
sp<IMediaCodecList> MediaCodecList::getInstance() {Mutex::Autolock _l(sRemoteInitMutex);if (sRemoteList == nullptr) {sp<IBinder> binder =defaultServiceManager()->getService(String16("media.player"));sp<IMediaPlayerService> service =interface_cast<IMediaPlayerService>(binder);if (service.get() != nullptr) {// 获取服务端的MediaCodecListsRemoteList = service->getCodecList();if (sRemoteList != nullptr) {sBinderDeathObserver = new BinderDeathObserver();binder->linkToDeath(sBinderDeathObserver.get());}}if (sRemoteList == nullptr) {// if failed to get remote list, create local listsRemoteList = getLocalInstance();}}return sRemoteList;
}
可以看到又回到了MediaCodecList,没错就是media/stagefright下的MediaCodecList
说明MediaCodecList的创建是在mediaplayerservice进程
//frameworks\av\media\libmediaplayerservice\MediaPlayerService.cpp
sp<IMediaCodecList> MediaPlayerService::getCodecList() const {return MediaCodecList::getLocalInstance();
}
GetBuilders()作为参数创建MediaCodecList单例并返回,关键在于GetBuilders()函数
//frameworks\av\media\libstagefright\MediaCodecList.cpp
sp<IMediaCodecList> MediaCodecList::getLocalInstance() {Mutex::Autolock autoLock(sInitMutex);if (sCodecList == nullptr) {MediaCodecList *codecList = new MediaCodecList(GetBuilders());if (codecList->initCheck() == OK) {sCodecList = codecList;if (isProfilingNeeded()) {ALOGV("Codec profiling needed, will be run in separated thread.");pthread_t profiler;if (pthread_create(&profiler, nullptr, profilerThreadWrapper, nullptr) != 0) {ALOGW("Failed to create thread for codec profiling.");}}} else {// failure to initialize may be temporary. retry on next call.delete codecList;}}return sCodecList;
}GetBuilders
std::vector<MediaCodecListBuilderBase *> GetBuilders() {std::vector<MediaCodecListBuilderBase *> builders;// if plugin provides the input surface, we cannot use OMX video encoders.// In this case, rely on plugin to provide list of OMX codecs that are usable.sp<PersistentSurface> surfaceTest =StagefrightPluginLoader::GetCCodecInstance()->createInputSurface();if (surfaceTest == nullptr) {builders.push_back(&sOmxInfoBuilder);}builders.push_back(GetCodec2InfoBuilder());return builders;
}
5.构造MediaCodecList
frameworks\av\media\libstagefright\MediaCodecList.cpp
MediaCodecList::MediaCodecList(std::vector<MediaCodecListBuilderBase*> builders) {mGlobalSettings = new AMessage();mCodecInfos.clear();MediaCodecListWriter writer;for (MediaCodecListBuilderBase *builder : builders) {if (builder == nullptr) {ALOGD("ignored a null builder");continue;}// 进入build的buildMediaCodecList函数mInitCheck = builder->buildMediaCodecList(&writer);if (mInitCheck != OK) {break;}}writer.writeGlobalSettings(mGlobalSettings);writer.writeCodecInfos(&mCodecInfos);std::stable_sort(mCodecInfos.begin(),mCodecInfos.end(),[](const sp<MediaCodecInfo> &info1, const sp<MediaCodecInfo> &info2) {if (info2 == nullptr) {return false;} else if (info1 == nullptr) {return true;} else {return info1->rank() < info2->rank();}});
}
通过OmxInfoBuilder创建MediaCodec列表(途径一)
1、通过HIDL调用到OmxStore.cpp, 通过OmxStore从xml解析支持的CodecList及Attributes
2、将支持的codec存储进swCodecName2Info, hwCodecName2Info
//frameworks\av\media\libstagefright\OmxInfoBuilder.cpp
status_t OmxInfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) {// Obtain IOmxStoresp<IOmxStore> omxStore = IOmxStore::getService();if (omxStore == nullptr) {ALOGE("Cannot find an IOmxStore service.");return NO_INIT;}// List service attributes (global settings)Status status;hidl_vec<IOmxStore::RoleInfo> roles;// 通过HIDL,获取inRoleListauto transStatus = omxStore->listRoles([&roles] (const hidl_vec<IOmxStore::RoleInfo>& inRoleList) {roles = inRoleList;});if (!transStatus.isOk()) {ALOGE("Fail to obtain codec roles from IOmxStore.");return NO_INIT;}hidl_vec<IOmxStore::ServiceAttribute> serviceAttributes;transStatus = omxStore->listServiceAttributes([&status, &serviceAttributes] (Status inStatus,const hidl_vec<IOmxStore::ServiceAttribute>& inAttributes) {status = inStatus;serviceAttributes = inAttributes;});if (!transStatus.isOk()) {ALOGE("Fail to obtain global settings from IOmxStore.");return NO_INIT;}if (status != Status::OK) {ALOGE("IOmxStore reports parsing error.");return NO_INIT;}for (const auto& p : serviceAttributes) {writer->addGlobalSetting(p.key.c_str(), p.value.c_str());}// Convert roles to lists of codecs// codec name -> index into swCodecs/hwCodecsstd::map<hidl_string, std::unique_ptr<MediaCodecInfoWriter>>swCodecName2Info, hwCodecName2Info;char rank[PROPERTY_VALUE_MAX];uint32_t defaultRank = 0x100;if (property_get("debug.stagefright.omx_default_rank", rank, nullptr)) {defaultRank = std::strtoul(rank, nullptr, 10);}//将支持的codec存储进swCodecName2Info, hwCodecName2Infofor (const IOmxStore::RoleInfo& role : roles) {const hidl_string& typeName = role.type;bool isEncoder = role.isEncoder;bool preferPlatformNodes = role.preferPlatformNodes;// If preferPlatformNodes is true, hardware nodes must be added after// platform (software) nodes. hwCodecs is used to hold hardware nodes// that need to be added after software nodes for the same role.std::vector<const IOmxStore::NodeInfo*> hwCodecs;for (const IOmxStore::NodeInfo& node : role.nodes) {const hidl_string& nodeName = node.name;// OMX.google开头的都是软解码bool isSoftware = hasPrefix(nodeName, "OMX.google");MediaCodecInfoWriter* info;if (isSoftware) {auto c2i = swCodecName2Info.find(nodeName);if (c2i == swCodecName2Info.end()) {// Create a new MediaCodecInfo for a new node.c2i = swCodecName2Info.insert(std::make_pair(nodeName, writer->addMediaCodecInfo())).first;info = c2i->second.get();info->setName(nodeName.c_str());info->setOwner(node.owner.c_str());info->setEncoder(isEncoder);info->setRank(defaultRank);} else {// The node has been seen before. Simply retrieve the// existing MediaCodecInfoWriter.info = c2i->second.get();}} else {auto c2i = hwCodecName2Info.find(nodeName);if (c2i == hwCodecName2Info.end()) {// Create a new MediaCodecInfo for a new node.if (!preferPlatformNodes) {c2i = hwCodecName2Info.insert(std::make_pair(nodeName, writer->addMediaCodecInfo())).first;info = c2i->second.get();info->setName(nodeName.c_str());info->setOwner(node.owner.c_str());info->setEncoder(isEncoder);info->setRank(defaultRank);} else {// If preferPlatformNodes is true, this node must be// added after all software nodes.hwCodecs.push_back(&node);continue;}} else {// The node has been seen before. Simply retrieve the// existing MediaCodecInfoWriter.info = c2i->second.get();}}std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =info->addMime(typeName.c_str());if (queryCapabilities(node, typeName.c_str(), isEncoder, caps.get()) != OK) {ALOGW("Fail to add mime %s to codec %s",typeName.c_str(), nodeName.c_str());info->removeMime(typeName.c_str());}}。。。}return OK;
}
通过OmxStore.cpp解析xml中的编解码器及属性
OmxStore.cpp读取的配置文件有:
实际项目,在 /vendor/etc 中
//frameworks\av\media\libstagefright\xmlparser\include\media\stagefright\xmlparser\MediaCodecsXmlParser.h
static constexpr char const* defaultSearchDirs[] ={"/odm/etc", "/vendor/etc", "/etc", nullptr};
static constexpr char const* defaultMainXmlName ="media_codecs.xml";
static constexpr char const* defaultPerformanceXmlName ="media_codecs_performance.xml";
static constexpr char const* defaultProfilingResultsXmlPath ="/data/misc/media/media_codecs_profiling_results.xml";
获取对应解码器的查询能力,实际为创建对应得解码器
//frameworks\av\media\libstagefright\OmxInfoBuilder.cpp
status_t queryCapabilities(const IOmxStore::NodeInfo& node, const char* mime, bool isEncoder,MediaCodecInfo::CapabilitiesWriter* caps) {sp<ACodec> codec = new ACodec();status_t err = codec->queryCapabilities(node.owner.c_str(), node.name.c_str(), mime, isEncoder, caps); // ...
}
依靠binder机制调用OMX服务中的allocateNode()
//frameworks\av\media\libstagefright\ACodec.cpp
status_t ACodec::queryCapabilities(const char* owner, const char* name, const char* mime, bool isEncoder,MediaCodecInfo::CapabilitiesWriter* caps) {const char *role = AVUtils::get()->getComponentRole(isEncoder, mime);if (role == NULL) {return BAD_VALUE;}OMXClient client;// 获取名为“” Omx实现status_t err = client.connect(owner);if (err != OK) {return err;}// 获取omx bp代理对象sp<IOMX> omx = client.interface();sp<CodecObserver> observer = new CodecObserver;sp<IOMXNode> omxNode;// hidl远程调用allocateNodeerr = omx->allocateNode(name, observer, &omxNoe);// ...
}
获取服务名为XXXX的OMX,并以LWOmx封装返回
frameworks\av\media\libstagefright\OMXClient.cpp
status_t OMXClient::connect(const char* name) {using namespace ::android::hardware::media::omx::V1_0;if (name == nullptr) {name = "default";}sp<IOmx> tOmx = IOmx::getService(name);if (tOmx.get() == nullptr) {ALOGE("Cannot obtain IOmx service.");return NO_INIT;}if (!tOmx->isRemote()) {ALOGE("IOmx service running in passthrough mode.");return NO_INIT;}mOMX = new utils::LWOmx(tOmx);ALOGI("IOmx service obtained");return OK;
}
6.allocateNode创建对应的解码器
LWOmx是代理,具体的实现在libstagefright_omx.so
//frameworks\av\media\libmedia\omx\1.0\WOmx.cpp
status_t LWOmx::allocateNode(char const* name,sp<IOMXObserver> const& observer,sp<IOMXNode>* omxNode) {status_t fnStatus;// allocateNodestatus_t transStatus = toStatusT(mBase->allocateNode(name, new TWOmxObserver(observer),[&fnStatus, omxNode](Status status, sp<IOmxNode> const& node) {fnStatus = toStatusT(status);*omxNode = new LWOmxNode(node);}));return transStatus == NO_ERROR ? fnStatus : transStatus;
}
6.1 创建对应的OMXNodeInstance对象
OMXNodeInstance对象持有一些关键信息,比如生成的节点id(mNodeID)、ACodec传递下来的observer、plugin里创建的解码组件handle和构造OMXNodeInstance时传入的omx对象等。还有关键的解码事件返回的kCallbacks。
//frameworks\av\media\libstagefright\omx\1.0\Omx.cpp
//libstagefright_omx.so
Return<void> Omx::allocateNode(const hidl_string& name,const sp<IOmxObserver>& observer,allocateNode_cb _hidl_cb) {using ::android::IOMXNode;using ::android::IOMXObserver;sp<OMXNodeInstance> instance;{Mutex::Autolock autoLock(mLock);if (mLiveNodes.size() == kMaxNodeInstances) {_hidl_cb(toStatus(NO_MEMORY), nullptr);return Void();}// 1. 实例化OMXNodeInstance对象,存放node id、解码组件handle、ACodec传递下来的observer等instance = new OMXNodeInstance(this, new LWOmxObserver(observer), name.c_str());OMX_COMPONENTTYPE *handle;// 2. master通知plugin创建对应的解码组件、并返回其操作句柄 OMX_ERRORTYPE err = mMaster->makeComponentInstance(name.c_str(), &OMXNodeInstance::kCallbacks,instance.get(), &handle);if (err != OMX_ErrorNone) {LOG(ERROR) << "Failed to allocate omx component ""'" << name.c_str() << "' "" err=" << asString(err) <<"(0x" << std::hex << unsigned(err) << ")";_hidl_cb(toStatus(StatusFromOMXError(err)), nullptr);return Void();}instance->setHandle(handle);// Find quirks from mParserconst auto& codec = mParser.getCodecMap().find(name.c_str());if (codec == mParser.getCodecMap().cend()) {LOG(WARNING) << "Failed to obtain quirks for omx component ""'" << name.c_str() << "' ""from XML files";} else {uint32_t quirks = 0;for (const auto& quirk : codec->second.quirkSet) {if (quirk == "requires-allocate-on-input-ports") {quirks |= OMXNodeInstance::kRequiresAllocateBufferOnInputPorts;}if (quirk == "requires-allocate-on-output-ports") {quirks |= OMXNodeInstance::kRequiresAllocateBufferOnOutputPorts;}}instance->setQuirks(quirks);}mLiveNodes.add(observer.get(), instance);mNode2Observer.add(instance.get(), observer.get());}observer->linkToDeath(this, 0);_hidl_cb(toStatus(OK), new TWOmxNode(instance));return Void();
}// frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp
// static
OMX_CALLBACKTYPE OMXNodeInstance::kCallbacks = {&OnEvent, &OnEmptyBufferDone, &OnFillBufferDone
};
6.2 makeComponentInstance
OMXMaster是解码库加载的核心
先找到指定的plugin(解码器组件,有厂商定制的及AOSP的)、再通知plugin去创建对应的解码组件。
frameworks\av\media\libstagefright\omx\OMXMaster.cpp
OMX_ERRORTYPE OMXMaster::makeComponentInstance(const char *name,const OMX_CALLBACKTYPE *callbacks,OMX_PTR appData,OMX_COMPONENTTYPE **component) {ALOGI("makeComponentInstance(%s) in %s process", name, mProcessName);Mutex::Autolock autoLock(mLock);*component = NULL;// 找到对应的Pluginssize_t index = mPluginByComponentName.indexOfKey(String8(name));if (index < 0) {return OMX_ErrorInvalidComponentName;}OMXPluginBase *plugin = mPluginByComponentName.valueAt(index);// 创建对应的解码组件OMX_ERRORTYPE err =plugin->makeComponentInstance(name, callbacks, appData, component);if (err != OMX_ErrorNone) {return err;}mPluginByInstance.add(*component, plugin);return err;
}
这些解码器是在哪加载的呢,回到OMXMaster构造函数
加载软硬编解码管理器中的所有解码器,并存在mPluginByComponentName。
1.加载厂商编解码管理器,libstagefrighthw.so
2.加载软编解码管理器,SoftOMXPlugin
OMXMaster::OMXMaster(): mVendorLibHandle(NULL) {pid_t pid = getpid();char filename[20];snprintf(filename, sizeof(filename), "/proc/%d/comm", pid);int fd = open(filename, O_RDONLY);if (fd < 0) {ALOGW("couldn't determine process name");strlcpy(mProcessName, "<unknown>", sizeof(mProcessName));} else {ssize_t len = read(fd, mProcessName, sizeof(mProcessName));if (len < 2) {ALOGW("couldn't determine process name");strlcpy(mProcessName, "<unknown>", sizeof(mProcessName));} else {// the name is newline terminated, so erase the newlinemProcessName[len - 1] = 0;}close(fd);}//加载厂商解码器SOaddVendorPlugin();//加载软解码器SOaddPlugin(new SoftOMXPlugin);
}void OMXMaster::addVendorPlugin() {addPlugin("libstagefrighthw.so");
}
7 实际创建对应解码器makeComponentInstance
如果是软件编解码器,调用SoftOMXPlugin的makeComponentInstance
如果是硬件就调用QComOMXPlugin的makeComponentInstance
Component可以理解为一个解码器实例。
7.1 软解码器
1.根据mime name匹配kComponents数据,如: { "OMX.google.aac.decoder", "aacdec", "audio_decoder.aac" },
2. 拿到的mLibNameSuffix值为aacdec,最后拼接的libName是libstagefright_soft_aacdec.so
3. 从system/lib目录下加载libstagefright_soft_aacdec.so库
4. 调用其对应的createSoftOMXComponent函数创建SoftOMXComponent
// frameworks\av\media\libstagefright\omx\SoftOMXComponent.cpp
OMX_ERRORTYPE SoftOMXPlugin::makeComponentInstance(const char *name,const OMX_CALLBACKTYPE *callbacks,OMX_PTR appData,OMX_COMPONENTTYPE **component) {ALOGV("makeComponentInstance '%s'", name);for (size_t i = 0; i < kNumComponents; ++i) {// 1.根据name匹配kComponents数据,// 如: { "OMX.google.aac.decoder", "aacdec", "audio_decoder.aac" },if (strcmp(name, kComponents[i].mName)) {continue;}// 2. 拿到的mLibNameSuffix值为aacdec,最后拼接的libName是libstagefright_soft_aacdec.soAString libName = "libstagefright_soft_";libName.append(kComponents[i].mLibNameSuffix);libName.append(".so");// RTLD_NODELETE means we keep the shared library around forever.// this eliminates thrashing during sequences like loading soundpools.// It also leaves the rest of the logic around the dlopen()/dlclose()// calls in this file unchanged.//// Implications of the change:// -- the codec process (where this happens) will have a slightly larger// long-term memory footprint as it accumulates the loaded shared libraries.// This is expected to be a small amount of memory.// -- plugin codecs can no longer (and never should have) depend on a// free reset of any static data as the library would have crossed// a dlclose/dlopen cycle.//// 3. 从system/lib目录下加载libstagefright_soft_aacdec.so库void *libHandle = dlopen(libName.c_str(), RTLD_NOW|RTLD_NODELETE);if (libHandle == NULL) {ALOGE("unable to dlopen %s: %s", libName.c_str(), dlerror());return OMX_ErrorComponentNotFound;}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. 创建对应的SoftOMXComponentsp<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;
}
SoftOmxPlugin是google提供的原生的一套编解码器插件
即通常说的软解硬解中的软解。它支持市面上常用的音视频格式,软解码支持的格式:
// frameworks\av\media\libstagefright\omx\SoftOMXPlugin.cpp
static const struct {const char *mName;const char *mLibNameSuffix;const char *mRole;} kComponents[] = {// two choices for aac decoding.// configurable in media/libstagefright/data/media_codecs_google_audio.xml// default implementation{ "OMX.google.aac.decoder", "aacdec", "audio_decoder.aac" },// alternate implementation{ "OMX.google.xaac.decoder", "xaacdec", "audio_decoder.aac" },{ "OMX.google.aac.encoder", "aacenc", "audio_encoder.aac" },{ "OMX.google.amrnb.decoder", "amrdec", "audio_decoder.amrnb" },{ "OMX.google.amrnb.encoder", "amrnbenc", "audio_encoder.amrnb" },{ "OMX.google.amrwb.decoder", "amrdec", "audio_decoder.amrwb" },{ "OMX.google.amrwb.encoder", "amrwbenc", "audio_encoder.amrwb" },{ "OMX.google.h264.decoder", "avcdec", "video_decoder.avc" },{ "OMX.google.h264.encoder", "avcenc", "video_encoder.avc" },{ "OMX.google.hevc.decoder", "hevcdec", "video_decoder.hevc" },{ "OMX.google.g711.alaw.decoder", "g711dec", "audio_decoder.g711alaw" },{ "OMX.google.g711.mlaw.decoder", "g711dec", "audio_decoder.g711mlaw" },{ "OMX.google.mpeg2.decoder", "mpeg2dec", "video_decoder.mpeg2" },{ "OMX.google.h263.decoder", "mpeg4dec", "video_decoder.h263" },{ "OMX.google.h263.encoder", "mpeg4enc", "video_encoder.h263" },{ "OMX.google.mpeg4.decoder", "mpeg4dec", "video_decoder.mpeg4" },{ "OMX.google.mpeg4.encoder", "mpeg4enc", "video_encoder.mpeg4" },{ "OMX.google.mp3.decoder", "mp3dec", "audio_decoder.mp3" },{ "OMX.google.vorbis.decoder", "vorbisdec", "audio_decoder.vorbis" },{ "OMX.google.opus.decoder", "opusdec", "audio_decoder.opus" },{ "OMX.google.vp8.decoder", "vpxdec", "video_decoder.vp8" },{ "OMX.google.vp9.decoder", "vpxdec", "video_decoder.vp9" },{ "OMX.google.vp8.encoder", "vpxenc", "video_encoder.vp8" },{ "OMX.google.vp9.encoder", "vpxenc", "video_encoder.vp9" },{ "OMX.google.raw.decoder", "rawdec", "audio_decoder.raw" },{ "OMX.google.flac.decoder", "flacdec", "audio_decoder.flac" },{ "OMX.google.flac.encoder", "flacenc", "audio_encoder.flac" },{ "OMX.google.gsm.decoder", "gsmdec", "audio_decoder.gsm" },
#ifdef QTI_FLAC_DECODER{ "OMX.qti.audio.decoder.flac", "qtiflacdec", "audio_decoder.flac" },
#endif
};
软解码器代码目录
frameworks\av\media\libstagefright\codecs,编译会生成对应的so库
车机内软解码so所在的目录
软解码器的实现
是基于“OpenMax IL的标准接口”进行,实际硬解码器也是如此。
以libstagefright_soft_aacdec为例子
libstagefright_soft_aacdec.so主体是SoftAAC2.cpp和SoftAAC2.h,所以dlsym返回的createSoftOMXComponent值如下:
//frameworks\av\media\libstagefright\codecs\aacdec\SoftAAC2.cpp
android::SoftOMXComponent *createSoftOMXComponent(const char *name, const OMX_CALLBACKTYPE *callbacks,OMX_PTR appData, OMX_COMPONENTTYPE **component) {return new android::SoftAAC2(name, callbacks, appData, component);
}
SoftAAC2继承自SimpleSoftOMXComponent
//frameworks\av\media\libstagefright\codecs\aacdec\SoftAAC2.cpp
SoftAAC2::SoftAAC2(const char *name,const OMX_CALLBACKTYPE *callbacks,OMX_PTR appData,OMX_COMPONENTTYPE **component): SimpleSoftOMXComponent(name, callbacks, appData, component),mAACDecoder(NULL),mStreamInfo(NULL),mIsADTS(false),mInputBufferCount(0),mOutputBufferCount(0),mSignalledError(false),mLastInHeader(NULL),mLastHeaderTimeUs(-1),mNextOutBufferTimeUs(0),mOutputPortSettingsChange(NONE) {initPorts();CHECK_EQ(initDecoder(), (status_t)OK);
}
SimpleSoftOMXComponent实现OpenMax IL的标准接口,例如sendCommand、setParameter
//frameworks\av\media\stagefright\omx\SimpleSoftOMXComponent.cpp
SimpleSoftOMXComponent::SimpleSoftOMXComponent(const char *name,const OMX_CALLBACKTYPE *callbacks,OMX_PTR appData,OMX_COMPONENTTYPE **component): SoftOMXComponent(name, callbacks, appData, component),mLooper(new ALooper),mHandler(new AHandlerReflector<SimpleSoftOMXComponent>(this)),mState(OMX_StateLoaded),mTargetState(OMX_StateLoaded) {mLooper->setName(name);mLooper->registerHandler(mHandler);mLooper->start(false, // runOnCallingThreadfalse, // canCallJavaANDROID_PRIORITY_VIDEO);
}
SoftOMXComponent里有对OpenMax接口的
// frameworks\av\media\stagefright\omx\SoftOMXComponent.cpp
// OMX_Component.h为 OpenMax IL version 1.1.2的接口头文件
#include <OMX_Component.h>
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) {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; 。。。*component = mComponent;
}
7.2 硬解码器
调用mGetHandle函数指针,而该函数指针是在该类构造时初始化。
//hardware\qcom\media\libstagefrighthw\QComOMXPlugin.cpp
OMX_ERRORTYPE QComOMXPlugin::makeComponentInstance(const char *name,const OMX_CALLBACKTYPE *callbacks,OMX_PTR appData,OMX_COMPONENTTYPE **component) {if (mLibHandle == NULL) {return OMX_ErrorUndefined;}return (*mGetHandle)(reinterpret_cast<OMX_HANDLETYPE *>(component),const_cast<char *>(name),appData, const_cast<OMX_CALLBACKTYPE *>(callbacks));
}
可以看到原来mGetHandle就是libOmxCore.so库中的OMX_GetHandle函数
//hardware\qcom\media\libstagefrighthw\QComOMXPlugin.cpp
QComOMXPlugin::QComOMXPlugin(): mLibHandle(dlopen("libOmxCore.so", RTLD_NOW)),mInit(NULL),mDeinit(NULL),mComponentNameEnum(NULL),mGetHandle(NULL),mFreeHandle(NULL),mGetRolesOfComponentHandle(NULL) {if (mLibHandle != NULL) {mInit = (InitFunc)dlsym(mLibHandle, "OMX_Init");mDeinit = (DeinitFunc)dlsym(mLibHandle, "OMX_Deinit");mComponentNameEnum =(ComponentNameEnumFunc)dlsym(mLibHandle, "OMX_ComponentNameEnum");mGetHandle = (GetHandleFunc)dlsym(mLibHandle, "OMX_GetHandle");mFreeHandle = (FreeHandleFunc)dlsym(mLibHandle, "OMX_FreeHandle");mGetRolesOfComponentHandle =(GetRolesOfComponentFunc)dlsym(mLibHandle, "OMX_GetRolesOfComponent");if (!mInit || !mDeinit || !mComponentNameEnum || !mGetHandle ||!mFreeHandle || !mGetRolesOfComponentHandle) {dlclose(mLibHandle);mLibHandle = NULL;} else(*mInit)();}
}
OMX_GetHandle函数
1、如果是avc解码器,加载libOmxVideoDSMode.so库,判断是否有dsmode
2、视频预处理开启情况需要加载vpp库
3、动态加载对应的编解码so
//hardware\qcom\media\mm-core\src\common\qc_omx_core.c
// libOmxCore.so
OMX_API OMX_ERRORTYPE OMX_APIENTRY
OMX_GetHandle(OMX_OUT OMX_HANDLETYPE* handle,OMX_IN OMX_STRING componentName,OMX_IN OMX_PTR appData,OMX_IN OMX_CALLBACKTYPE* callBacks)
{OMX_ERRORTYPE eRet = OMX_ErrorNone;int cmp_index = -1;int hnd_index = -1;int vpp_cmp_index = -1;DEBUG_PRINT("OMXCORE API : GetHandle %p %s %p\n", handle,componentName,appData);pthread_mutex_lock(&lock_core);if(handle){*handle = NULL;char optComponentName[OMX_MAX_STRINGNAME_SIZE];strlcpy(optComponentName, componentName, OMX_MAX_STRINGNAME_SIZE);// 如果是avc解码器,加载libOmxVideoDSMode.so库,判断是否有dsmodeif(strstr(componentName, "avc") && strstr(componentName, "decoder")){void *libhandle = dlopen("libOmxVideoDSMode.so", RTLD_NOW);if(libhandle){int (*fn_ptr)() = dlsym(libhandle, "isDSModeActive");dlclose(libhandle);}else{DEBUG_PRINT_ERROR("Failed to load dsmode library");}}if(cmp_index < 0){cmp_index = get_cmp_index(componentName);strlcpy(optComponentName, componentName, OMX_MAX_STRINGNAME_SIZE);}if(cmp_index >= 0){char value[PROPERTY_VALUE_MAX];DEBUG_PRINT("getting fn pointer\n");// Load VPP omx component for decoder if vpp// property is enabled// 视频预处理开启情况需要加载vppif ((property_get("vendor.media.vpp.enable", value, NULL))&& (!strcmp("1", value) || !strcmp("true", value))) {DEBUG_PRINT("VPP property is enabled");if (!strcmp(core[cmp_index].so_lib_name, "libOmxVdec.so")|| !strcmp(core[cmp_index].so_lib_name, "libOmxSwVdec.so")) {vpp_cmp_index = get_cmp_index("OMX.qti.vdec.vpp");if (vpp_cmp_index < 0) {DEBUG_PRINT_ERROR("Unable to find VPP OMX lib in registry ");} else {DEBUG_PRINT("Loading vpp for vdec");cmp_index = vpp_cmp_index;}}}// dynamically load the socore[cmp_index].fn_ptr =omx_core_load_cmp_library(core[cmp_index].so_lib_name,&core[cmp_index].so_lib_handle);。。。。
}
加载对应的SO库
// /hardware\qcom\media\mm-core\src\common\qc_omx_core.c
static create_qc_omx_component
omx_core_load_cmp_library(char *libname, void **handle_ptr)
{create_qc_omx_component fn_ptr = NULL;if(handle_ptr){DEBUG_PRINT("Dynamically Loading the library : %s\n",libname);if (!strcmp(libname, "libOmxVpp.so"))*handle_ptr = dlopen(libname, RTLD_NOW|RTLD_GLOBAL);else*handle_ptr = dlopen(libname, RTLD_NOW);if(*handle_ptr){fn_ptr = dlsym(*handle_ptr, "get_omx_component_factory_fn");if(fn_ptr == NULL){DEBUG_PRINT("Error: Library %s incompatible as QCOM OMX component loader - %s\n",libname, dlerror());*handle_ptr = NULL;}}else{DEBUG_PRINT("Error: Couldn't load %s: %s\n",libname,dlerror());}}return fn_ptr;
}
高通平台支持的硬解码器
注意里边有一些是软解码(包含Sw字符的)
omx_core_cb_type core[] =
{//Common entriesOMX_REGISTRY_ENTRY("OMX.qcom.video.decoder.avc", "libOmxVdec.so", "video_decoder.avc"),OMX_REGISTRY_ENTRY("OMX.qcom.video.decoder.avc.secure", "libOmxVdec.so", "video_decoder.avc"),OMX_REGISTRY_ENTRY("OMX.qcom.video.decoder.mpeg2", "libOmxVdec.so", "video_decoder.mpeg2"),OMX_REGISTRY_ENTRY("OMX.qcom.video.decoder.mpeg2.secure", "libOmxVdec.so", "video_decoder.mpeg2"),OMX_REGISTRY_ENTRY("OMX.qcom.video.decoder.hevc", "libOmxVdec.so", "video_decoder.hevc"),OMX_REGISTRY_ENTRY("OMX.qcom.video.decoder.hevc.secure", "libOmxVdec.so", "video_decoder.hevc"),OMX_REGISTRY_ENTRY("OMX.qcom.video.decoder.vp8", "libOmxVdec.so", "video_decoder.vp8"),OMX_REGISTRY_ENTRY("OMX.qcom.video.decoder.vp9", "libOmxVdec.so", "video_decoder.vp9"),OMX_REGISTRY_ENTRY("OMX.qcom.video.decoder.vp9.secure", "libOmxVdec.so", "video_decoder.vp9"),OMX_REGISTRY_ENTRY("OMX.qti.video.decoder.mpeg4sw", "libOmxSwVdec.so", "video_decoder.mpeg4"),OMX_REGISTRY_ENTRY("OMX.qti.video.decoder.divxsw", "libOmxSwVdec.so", "video_decoder.divx"),OMX_REGISTRY_ENTRY("OMX.qti.video.decoder.divx4sw", "libOmxSwVdec.so", DIVX4_MIME),OMX_REGISTRY_ENTRY("OMX.qti.video.decoder.h263sw", "libOmxSwVdec.so", "video_decoder.h263"),OMX_REGISTRY_ENTRY("OMX.qcom.video.encoder.mpeg4sw", "libOmxSwVencMpeg4.so", "video_encoder.mpeg4"),OMX_REGISTRY_ENTRY("OMX.qcom.video.encoder.h263sw", "libOmxSwVencMpeg4.so", "video_encoder.h263"),OMX_REGISTRY_ENTRY("OMX.qcom.video.encoder.avc", "libOmxVenc.so", "video_encoder.avc"),OMX_REGISTRY_ENTRY("OMX.qcom.video.encoder.avc.secure", "libOmxVenc.so", "video_encoder.avc"),OMX_REGISTRY_ENTRY("OMX.qcom.video.encoder.vp8", "libOmxVenc.so", "video_encoder.vp8"),OMX_REGISTRY_ENTRY("OMX.qcom.video.encoder.hevc", "libOmxVenc.so", "video_encoder.hevc"),OMX_REGISTRY_ENTRY("OMX.qcom.video.encoder.hevc.secure", "libOmxVenc.so", "video_encoder.hevc"),OMX_REGISTRY_ENTRY("OMX.qcom.video.encoder.heic", "libOmxVenc.so", "image_encoder.heic"),OMX_REGISTRY_ENTRY("OMX.qcom.audio.decoder.Qcelp13", "libOmxQcelp13Dec.so", "audio_decoder.Qcelp13"),OMX_REGISTRY_ENTRY("OMX.qcom.audio.decoder.evrc", "libOmxEvrcDec.so", "audio_decoder.evrc"),OMX_REGISTRY_ENTRY("OMX.qcom.audio.decoder.wma", "libOmxWmaDec.so", "audio_decoder.wma"),OMX_REGISTRY_ENTRY("OMX.qcom.audio.decoder.wma10Pro", "libOmxWmaDec.so", "audio_decoder.wma"),OMX_REGISTRY_ENTRY("OMX.qcom.audio.decoder.wmaLossLess", "libOmxWmaDec.so", "audio_decoder.wma"),OMX_REGISTRY_ENTRY("OMX.qcom.audio.decoder.amrwbplus", "libOmxAmrwbplusDec.so", "audio_decoder.awbplus"),OMX_REGISTRY_ENTRY("OMX.qcom.audio.decoder.alac", "libOmxAlacDec.so", "audio_decoder.alac"),OMX_REGISTRY_ENTRY("OMX.qti.audio.decoder.alac.sw", "libOmxAlacDecSw.so", "audio_decoder.alac"),。。。。。
};
硬解码器的实现位置
Audio encode:hardware\qcom\audio\mm-audio
Audio decode:\vendor\qcom\proprietary\mm-audio\omx
Video encode/decode: hardware\qcom\media\mm-video-v4l2
对比Audio每个解码器均有一个so,Video采用v4l2框架实现,只有libOmxVenc.so,libOmxSwVencMpeg4
libOmxSwVdec,libOmxVdec.so 这4个库。
部分缩写含义
1.VPP 视频预处理
2.avc 就是H264
3.v4l2 是:video for linux version2,是Linux内核中关于视频设备的内核驱动框架,为上层的访问底层的视频设备提供了统一的接口.凡是内核中的子系统都有抽象底层硬件的差异,为上层提供统一的接口和提取出公共代码避免代码冗余等好处.