stagefright: Move CodecCapabilities querying into MediaCodec
1. We cannot assume that codecs are OMX based
2. We still have a strange dependency where MediaCodec's
create methods depend on MediaCodecList, which depends on
MediaCodec's static method. This is to be untangled later.
Bug: 17108024
Change-Id: Idd619ee959627539a65f5f7da586108883dcb5f2
diff --git a/media/libmedia/MediaCodecInfo.cpp b/media/libmedia/MediaCodecInfo.cpp
index 8d3fa7b..3b53f4c 100644
--- a/media/libmedia/MediaCodecInfo.cpp
+++ b/media/libmedia/MediaCodecInfo.cpp
@@ -26,8 +26,6 @@
#include <media/stagefright/foundation/AMessage.h>
#include <binder/Parcel.h>
-#include <media/stagefright/OMXCodec.h>
-
namespace android {
void MediaCodecInfo::Capabilities::getSupportedProfileLevels(
@@ -101,6 +99,21 @@
return OK;
}
+void MediaCodecInfo::CapabilitiesBuilder::addProfileLevel(uint32_t profile, uint32_t level) {
+ ProfileLevel profileLevel;
+ profileLevel.mProfile = profile;
+ profileLevel.mLevel = level;
+ mProfileLevels.push_back(profileLevel);
+}
+
+void MediaCodecInfo::CapabilitiesBuilder::addColorFormat(uint32_t format) {
+ mColorFormats.push(format);
+}
+
+void MediaCodecInfo::CapabilitiesBuilder::addFlags(uint32_t flags) {
+ mFlags |= flags;
+}
+
bool MediaCodecInfo::isEncoder() const {
return mIsEncoder;
}
@@ -225,26 +238,8 @@
}
}
-status_t MediaCodecInfo::initializeCapabilities(const CodecCapabilities &caps) {
- mCurrentCaps->mProfileLevels.clear();
- mCurrentCaps->mColorFormats.clear();
-
- for (size_t i = 0; i < caps.mProfileLevels.size(); ++i) {
- const CodecProfileLevel &src = caps.mProfileLevels.itemAt(i);
-
- ProfileLevel profileLevel;
- profileLevel.mProfile = src.mProfile;
- profileLevel.mLevel = src.mLevel;
- mCurrentCaps->mProfileLevels.push_back(profileLevel);
- }
-
- for (size_t i = 0; i < caps.mColorFormats.size(); ++i) {
- mCurrentCaps->mColorFormats.push_back(caps.mColorFormats.itemAt(i));
- }
-
- mCurrentCaps->mFlags = caps.mFlags;
- mCurrentCaps->mDetails = new AMessage;
-
+status_t MediaCodecInfo::setCapabilities(const sp<Capabilities> &caps) {
+ mCurrentCaps = caps;
return OK;
}
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index fa6703e..73177c1 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -1528,6 +1528,21 @@
status_t ACodec::setComponentRole(
bool isEncoder, const char *mime) {
+ const char *role = getComponentRole(isEncoder, mime);
+ if (role == NULL) {
+ return BAD_VALUE;
+ }
+ status_t err = setComponentRole(mOMX, mNode, role);
+ if (err != OK) {
+ ALOGW("[%s] Failed to set standard component role '%s'.",
+ mComponentName.c_str(), role);
+ }
+ return err;
+}
+
+//static
+const char *ACodec::getComponentRole(
+ bool isEncoder, const char *mime) {
struct MimeToRole {
const char *mime;
const char *decoderRole;
@@ -1594,35 +1609,27 @@
}
if (i == kNumMimeToRole) {
- return ERROR_UNSUPPORTED;
+ return NULL;
}
- const char *role =
- isEncoder ? kMimeToRole[i].encoderRole
+ return isEncoder ? kMimeToRole[i].encoderRole
: kMimeToRole[i].decoderRole;
+}
- if (role != NULL) {
- OMX_PARAM_COMPONENTROLETYPE roleParams;
- InitOMXParams(&roleParams);
+//static
+status_t ACodec::setComponentRole(
+ const sp<IOMX> &omx, IOMX::node_id node, const char *role) {
+ OMX_PARAM_COMPONENTROLETYPE roleParams;
+ InitOMXParams(&roleParams);
- strncpy((char *)roleParams.cRole,
- role, OMX_MAX_STRINGNAME_SIZE - 1);
+ strncpy((char *)roleParams.cRole,
+ role, OMX_MAX_STRINGNAME_SIZE - 1);
- roleParams.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
+ roleParams.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
- status_t err = mOMX->setParameter(
- mNode, OMX_IndexParamStandardComponentRole,
- &roleParams, sizeof(roleParams));
-
- if (err != OK) {
- ALOGW("[%s] Failed to set standard component role '%s'.",
- mComponentName.c_str(), role);
-
- return err;
- }
- }
-
- return OK;
+ return omx->setParameter(
+ node, OMX_IndexParamStandardComponentRole,
+ &roleParams, sizeof(roleParams));
}
status_t ACodec::configureCodec(
@@ -7004,6 +7011,147 @@
}
}
+status_t ACodec::queryCapabilities(
+ const AString &name, const AString &mime, bool isEncoder,
+ sp<MediaCodecInfo::Capabilities> *caps) {
+ (*caps).clear();
+ const char *role = getComponentRole(isEncoder, mime.c_str());
+ if (role == NULL) {
+ return BAD_VALUE;
+ }
+
+ OMXClient client;
+ status_t err = client.connect();
+ if (err != OK) {
+ return err;
+ }
+
+ sp<IOMX> omx = client.interface();
+ sp<CodecObserver> observer = new CodecObserver;
+ IOMX::node_id node = 0;
+
+ err = omx->allocateNode(name.c_str(), observer, &node);
+ if (err != OK) {
+ client.disconnect();
+ return err;
+ }
+
+ err = setComponentRole(omx, node, role);
+ if (err != OK) {
+ omx->freeNode(node);
+ client.disconnect();
+ return err;
+ }
+
+ sp<MediaCodecInfo::CapabilitiesBuilder> builder = new MediaCodecInfo::CapabilitiesBuilder();
+ bool isVideo = mime.startsWithIgnoreCase("video/");
+
+ if (isVideo) {
+ OMX_VIDEO_PARAM_PROFILELEVELTYPE param;
+ InitOMXParams(¶m);
+ param.nPortIndex = isEncoder ? kPortIndexOutput : kPortIndexInput;
+
+ for (param.nProfileIndex = 0;; ++param.nProfileIndex) {
+ status_t err = omx->getParameter(
+ node, OMX_IndexParamVideoProfileLevelQuerySupported,
+ ¶m, sizeof(param));
+ if (err != OK) {
+ break;
+ }
+ builder->addProfileLevel(param.eProfile, param.eLevel);
+ }
+
+ // Color format query
+ // return colors in the order reported by the OMX component
+ // prefix "flexible" standard ones with the flexible equivalent
+ OMX_VIDEO_PARAM_PORTFORMATTYPE portFormat;
+ InitOMXParams(&portFormat);
+ param.nPortIndex = isEncoder ? kPortIndexInput : kPortIndexOutput;
+ Vector<uint32_t> supportedColors; // shadow copy to check for duplicates
+ for (portFormat.nIndex = 0;; ++portFormat.nIndex) {
+ status_t err = omx->getParameter(
+ node, OMX_IndexParamVideoPortFormat,
+ &portFormat, sizeof(portFormat));
+ if (err != OK) {
+ break;
+ }
+
+ OMX_U32 flexibleEquivalent;
+ if (isFlexibleColorFormat(
+ omx, node, portFormat.eColorFormat, false /* usingNativeWindow */,
+ &flexibleEquivalent)) {
+ bool marked = false;
+ for (size_t i = 0; i < supportedColors.size(); ++i) {
+ if (supportedColors[i] == flexibleEquivalent) {
+ marked = true;
+ break;
+ }
+ }
+ if (!marked) {
+ supportedColors.push(flexibleEquivalent);
+ builder->addColorFormat(flexibleEquivalent);
+ }
+ }
+ supportedColors.push(portFormat.eColorFormat);
+ builder->addColorFormat(portFormat.eColorFormat);
+ }
+ } else if (mime.equalsIgnoreCase(MEDIA_MIMETYPE_AUDIO_AAC)) {
+ // More audio codecs if they have profiles.
+ OMX_AUDIO_PARAM_ANDROID_PROFILETYPE param;
+ InitOMXParams(¶m);
+ param.nPortIndex = isEncoder ? kPortIndexOutput : kPortIndexInput;
+ for (param.nProfileIndex = 0;; ++param.nProfileIndex) {
+ status_t err = omx->getParameter(
+ node, (OMX_INDEXTYPE)OMX_IndexParamAudioProfileQuerySupported,
+ ¶m, sizeof(param));
+ if (err != OK) {
+ break;
+ }
+ // For audio, level is ignored.
+ builder->addProfileLevel(param.eProfile, 0 /* level */);
+ }
+
+ // NOTE: Without Android extensions, OMX does not provide a way to query
+ // AAC profile support
+ if (param.nProfileIndex == 0) {
+ ALOGW("component %s doesn't support profile query.", name.c_str());
+ }
+ }
+
+ if (isVideo && !isEncoder) {
+ native_handle_t *sidebandHandle = NULL;
+ if (omx->configureVideoTunnelMode(
+ node, kPortIndexOutput, OMX_TRUE, 0, &sidebandHandle) == OK) {
+ // tunneled playback includes adaptive playback
+ builder->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback
+ | MediaCodecInfo::Capabilities::kFlagSupportsTunneledPlayback);
+ } else if (omx->storeMetaDataInBuffers(
+ node, kPortIndexOutput, OMX_TRUE) == OK ||
+ omx->prepareForAdaptivePlayback(
+ node, kPortIndexOutput, OMX_TRUE,
+ 1280 /* width */, 720 /* height */) == OK) {
+ builder->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback);
+ }
+ }
+
+ if (isVideo && isEncoder) {
+ OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE params;
+ InitOMXParams(¶ms);
+ params.nPortIndex = kPortIndexOutput;
+ // TODO: should we verify if fallback is supported?
+ if (omx->getConfig(
+ node, (OMX_INDEXTYPE)OMX_IndexConfigAndroidIntraRefresh,
+ ¶ms, sizeof(params)) == OK) {
+ builder->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsIntraRefresh);
+ }
+ }
+
+ *caps = builder;
+ omx->freeNode(node);
+ client.disconnect();
+ return OK;
+}
+
// These are supposed be equivalent to the logic in
// "audio_channel_out_mask_from_count".
//static
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 8893e9c..ce67d78 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -193,6 +193,22 @@
}
// static
+status_t MediaCodec::QueryCapabilities(
+ const AString &name, const AString &mime, bool isEncoder,
+ sp<MediaCodecInfo::Capabilities> *caps /* nonnull */) {
+ // TRICKY: this method is used by MediaCodecList/Info during its
+ // initialization. As such, we cannot create a MediaCodec instance
+ // because that requires an initialized MediaCodecList.
+
+ sp<CodecBase> codec = GetCodecBase(name);
+ if (codec == NULL) {
+ return NAME_NOT_FOUND;
+ }
+
+ return codec->queryCapabilities(name, mime, isEncoder, caps);
+}
+
+// static
sp<PersistentSurface> MediaCodec::CreatePersistentInputSurface() {
OMXClient client;
CHECK_EQ(client.connect(), (status_t)OK);
@@ -298,6 +314,18 @@
response->postReply(replyID);
}
+//static
+sp<CodecBase> MediaCodec::GetCodecBase(const AString &name, bool nameIsType) {
+ // at this time only ACodec specifies a mime type.
+ if (nameIsType || name.startsWithIgnoreCase("omx.")) {
+ return new ACodec;
+ } else if (name.startsWithIgnoreCase("android.filter.")) {
+ return new MediaFilter;
+ } else {
+ return NULL;
+ }
+}
+
status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) {
mResourceManagerService->init();
@@ -311,12 +339,8 @@
// we need to invest in an extra looper to free the main event
// queue.
- if (nameIsType || !strncasecmp(name.c_str(), "omx.", 4)) {
- mCodec = new ACodec;
- } else if (!nameIsType
- && !strncasecmp(name.c_str(), "android.filter.", 15)) {
- mCodec = new MediaFilter;
- } else {
+ mCodec = GetCodecBase(name, nameIsType);
+ if (mCodec == NULL) {
return NAME_NOT_FOUND;
}
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index c049097..34d85e7 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -31,10 +31,10 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/ACodec.h>
+#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/OMXClient.h>
-#include <media/stagefright/OMXCodec.h>
#include <sys/stat.h>
#include <utils/threads.h>
@@ -752,18 +752,22 @@
ALOGV("initializeCapabilities %s:%s",
mCurrentInfo->mName.c_str(), type);
- CodecCapabilities caps;
- status_t err = QueryCodec(
- mOMX,
- mCurrentInfo->mName.c_str(),
+ sp<MediaCodecInfo::Capabilities> caps;
+ status_t err = MediaCodec::QueryCapabilities(
+ mCurrentInfo->mName,
type,
mCurrentInfo->mIsEncoder,
&caps);
if (err != OK) {
return err;
+ } else if (caps == NULL) {
+ ALOGE("MediaCodec::QueryCapabilities returned OK but no capabilities for '%s':'%s':'%s'",
+ mCurrentInfo->mName.c_str(), type,
+ mCurrentInfo->mIsEncoder ? "encoder" : "decoder");
+ return UNKNOWN_ERROR;
}
- return mCurrentInfo->initializeCapabilities(caps);
+ return mCurrentInfo->setCapabilities(caps);
}
status_t MediaCodecList::addQuirk(const char **attrs) {