Enable OMX components when CCodec is not available
This CL also removes OMX components completely when Codec2 input surface
is enabled.
Test: adb shell setprop debug.stagefright.ccodec 1 \
adb shell setprop debug.stagefright.c2inputsurface 0 \
adb shell killall mediaserver \
adb shell stagefright -p
Test: adb shell setprop debug.stagefright.ccodec 1 \
adb shell setprop debug.stagefright.c2inputsurface 2 \
adb shell killall mediaserver \
adb shell stagefright -p
Test: adb shell setprop debug.stagefright.ccodec 0 \
adb shell setprop debug.stagefright.c2inputsurface 0 \
adb shell killall mediaserver \
adb shell stagefright -p
Bug: 111101148
Change-Id: I4af5a20de8186a76c3dcba544d7e7bacf9f37dfb
(cherry picked from commit 1b4d712c951d642b3dad45f2535a6864d5670f2a)
diff --git a/media/sfplugin/CCodec.cpp b/media/sfplugin/CCodec.cpp
index de24f8f..dd33a4a 100644
--- a/media/sfplugin/CCodec.cpp
+++ b/media/sfplugin/CCodec.cpp
@@ -896,35 +896,6 @@
}
std::shared_ptr<PersistentSurface> persistentSurface(CreateInputSurface());
- if (!persistentSurface) {
- using namespace android::hardware::media::omx::V1_0;
- using namespace android::hardware::media::omx::V1_0::utils;
- using namespace android::hardware::graphics::bufferqueue::V1_0::utils;
- typedef android::hardware::media::omx::V1_0::Status OmxStatus;
- android::sp<IOmx> omx = IOmx::getService();
- typedef android::hardware::graphics::bufferqueue::V1_0::
- IGraphicBufferProducer HGraphicBufferProducer;
- typedef android::hardware::media::omx::V1_0::
- IGraphicBufferSource HGraphicBufferSource;
- OmxStatus s;
- android::sp<HGraphicBufferProducer> gbp;
- android::sp<HGraphicBufferSource> gbs;
- android::Return<void> transStatus = omx->createInputSurface(
- [&s, &gbp, &gbs](
- OmxStatus status,
- const android::sp<HGraphicBufferProducer>& producer,
- const android::sp<HGraphicBufferSource>& source) {
- s = status;
- gbp = producer;
- gbs = source;
- });
- if (transStatus.isOk() && s == OmxStatus::OK) {
- persistentSurface = std::make_shared<android::PersistentSurface>(
- new H2BGraphicBufferProducer(gbp),
- sp<::android::IGraphicBufferSource>(
- new LWGraphicBufferSource(gbs)));
- }
- }
if (persistentSurface->getHidlTarget()) {
sp<IInputSurface> inputSurface = IInputSurface::castFrom(
@@ -1617,6 +1588,7 @@
}
extern "C" android::PersistentSurface *CreateInputSurface() {
+ // Attempt to create a Codec2's input surface.
std::shared_ptr<android::Codec2Client::InputSurface> inputSurface =
android::Codec2Client::CreateInputSurface();
if (inputSurface) {
@@ -1625,6 +1597,36 @@
static_cast<android::sp<android::hidl::base::V1_0::IBase>>(
inputSurface->getHalInterface()));
}
+
+ // Fall back to OMX.
+ using namespace android::hardware::media::omx::V1_0;
+ using namespace android::hardware::media::omx::V1_0::utils;
+ using namespace android::hardware::graphics::bufferqueue::V1_0::utils;
+ typedef android::hardware::media::omx::V1_0::Status OmxStatus;
+ android::sp<IOmx> omx = IOmx::getService();
+ typedef android::hardware::graphics::bufferqueue::V1_0::
+ IGraphicBufferProducer HGraphicBufferProducer;
+ typedef android::hardware::media::omx::V1_0::
+ IGraphicBufferSource HGraphicBufferSource;
+ OmxStatus s;
+ android::sp<HGraphicBufferProducer> gbp;
+ android::sp<HGraphicBufferSource> gbs;
+ android::Return<void> transStatus = omx->createInputSurface(
+ [&s, &gbp, &gbs](
+ OmxStatus status,
+ const android::sp<HGraphicBufferProducer>& producer,
+ const android::sp<HGraphicBufferSource>& source) {
+ s = status;
+ gbp = producer;
+ gbs = source;
+ });
+ if (transStatus.isOk() && s == OmxStatus::OK) {
+ return new android::PersistentSurface(
+ new H2BGraphicBufferProducer(gbp),
+ sp<::android::IGraphicBufferSource>(
+ new LWGraphicBufferSource(gbs)));
+ }
+
return nullptr;
}
diff --git a/media/sfplugin/Codec2InfoBuilder.cpp b/media/sfplugin/Codec2InfoBuilder.cpp
index 20aa577..8f927ad 100644
--- a/media/sfplugin/Codec2InfoBuilder.cpp
+++ b/media/sfplugin/Codec2InfoBuilder.cpp
@@ -18,7 +18,7 @@
#define LOG_TAG "Codec2InfoBuilder"
#include <log/log.h>
-#include <codec2/hidl/client.h>
+#include <strings.h>
#include <C2Component.h>
#include <C2Config.h>
@@ -26,9 +26,26 @@
#include <C2PlatformSupport.h>
#include <Codec2Mapper.h>
+#include <OMX_Audio.h>
+#include <OMX_AudioExt.h>
+#include <OMX_IndexExt.h>
+#include <OMX_Types.h>
+#include <OMX_Video.h>
+#include <OMX_VideoExt.h>
+#include <OMX_AsString.h>
+
+#include <android/hardware/media/omx/1.0/IOmx.h>
+#include <android/hardware/media/omx/1.0/IOmxObserver.h>
+#include <android/hardware/media/omx/1.0/IOmxNode.h>
+#include <android/hardware/media/omx/1.0/types.h>
+
#include <android-base/properties.h>
+#include <codec2/hidl/client.h>
+#include <cutils/native_handle.h>
+#include <media/omx/1.0/WOmxNode.h>
#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/foundation/MediaDefs.h>
+#include <media/stagefright/omx/OMXUtils.h>
#include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
#include "Codec2InfoBuilder.h"
@@ -39,6 +56,8 @@
namespace /* unnamed */ {
+uint32_t kOmxRankOverride = 1;
+
bool hasPrefix(const std::string& s, const char* prefix) {
size_t prefixLen = strlen(prefix);
return s.compare(0, prefixLen, prefix) == 0;
@@ -50,6 +69,239 @@
s.compare(s.size() - suffixLen, suffixLen, suffix) == 0;
}
+// Constants from ACodec
+constexpr OMX_U32 kPortIndexInput = 0;
+constexpr OMX_U32 kPortIndexOutput = 0;
+constexpr OMX_U32 kMaxIndicesToCheck = 32;
+
+status_t queryOmxCapabilities(
+ const char* name, const char* mime, bool isEncoder,
+ MediaCodecInfo::CapabilitiesWriter* caps) {
+
+ const char *role = GetComponentRole(isEncoder, mime);
+ if (role == NULL) {
+ return BAD_VALUE;
+ }
+
+ using namespace ::android::hardware::media::omx::V1_0;
+ using ::android::hardware::Return;
+ using ::android::hardware::Void;
+ using ::android::hardware::hidl_vec;
+ using ::android::hardware::media::omx::V1_0::utils::LWOmxNode;
+
+ sp<IOmx> omx = IOmx::getService();
+ if (!omx) {
+ ALOGW("Could not obtain IOmx service.");
+ return NO_INIT;
+ }
+
+ struct Observer : IOmxObserver {
+ virtual Return<void> onMessages(const hidl_vec<Message>&) override {
+ return Void();
+ }
+ };
+
+ sp<Observer> observer = new Observer();
+ Status status;
+ sp<IOmxNode> tOmxNode;
+ Return<void> transStatus = omx->allocateNode(
+ name, observer,
+ [&status, &tOmxNode](Status s, const sp<IOmxNode>& n) {
+ status = s;
+ tOmxNode = n;
+ });
+ if (!transStatus.isOk()) {
+ ALOGW("IOmx::allocateNode -- transaction failed.");
+ return NO_INIT;
+ }
+ if (status != Status::OK) {
+ ALOGW("IOmx::allocateNode -- error returned: %d.",
+ static_cast<int>(status));
+ return NO_INIT;
+ }
+
+ sp<LWOmxNode> omxNode = new LWOmxNode(tOmxNode);
+
+ status_t err = SetComponentRole(omxNode, role);
+ if (err != OK) {
+ omxNode->freeNode();
+ ALOGW("Failed to SetComponentRole: component = %s, role = %s.",
+ name, role);
+ return err;
+ }
+
+ bool isVideo = hasPrefix(mime, "video/") == 0;
+ bool isImage = hasPrefix(mime, "image/") == 0;
+
+ if (isVideo || isImage) {
+ OMX_VIDEO_PARAM_PROFILELEVELTYPE param;
+ InitOMXParams(¶m);
+ param.nPortIndex = isEncoder ? kPortIndexOutput : kPortIndexInput;
+
+ for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) {
+ param.nProfileIndex = index;
+ status_t err = omxNode->getParameter(
+ OMX_IndexParamVideoProfileLevelQuerySupported,
+ ¶m, sizeof(param));
+ if (err != OK) {
+ break;
+ }
+ caps->addProfileLevel(param.eProfile, param.eLevel);
+
+ // AVC components may not list the constrained profiles explicitly, but
+ // decoders that support a profile also support its constrained version.
+ // Encoders must explicitly support constrained profiles.
+ if (!isEncoder && strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC) == 0) {
+ if (param.eProfile == OMX_VIDEO_AVCProfileHigh) {
+ caps->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedHigh, param.eLevel);
+ } else if (param.eProfile == OMX_VIDEO_AVCProfileBaseline) {
+ caps->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedBaseline, param.eLevel);
+ }
+ }
+
+ if (index == kMaxIndicesToCheck) {
+ ALOGW("[%s] stopping checking profiles after %u: %x/%x",
+ name, index,
+ 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);
+ portFormat.nPortIndex = isEncoder ? kPortIndexInput : kPortIndexOutput;
+ for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) {
+ portFormat.nIndex = index;
+ status_t err = omxNode->getParameter(
+ OMX_IndexParamVideoPortFormat,
+ &portFormat, sizeof(portFormat));
+ if (err != OK) {
+ break;
+ }
+
+ OMX_U32 flexibleEquivalent;
+ if (IsFlexibleColorFormat(
+ omxNode, portFormat.eColorFormat, false /* usingNativeWindow */,
+ &flexibleEquivalent)) {
+ caps->addColorFormat(flexibleEquivalent);
+ }
+ caps->addColorFormat(portFormat.eColorFormat);
+
+ if (index == kMaxIndicesToCheck) {
+ ALOGW("[%s] stopping checking formats after %u: %s(%x)",
+ name, index,
+ asString(portFormat.eColorFormat), portFormat.eColorFormat);
+ }
+ }
+ } else if (strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC) == 0) {
+ // More audio codecs if they have profiles.
+ OMX_AUDIO_PARAM_ANDROID_PROFILETYPE param;
+ InitOMXParams(¶m);
+ param.nPortIndex = isEncoder ? kPortIndexOutput : kPortIndexInput;
+ for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) {
+ param.nProfileIndex = index;
+ status_t err = omxNode->getParameter(
+ (OMX_INDEXTYPE)OMX_IndexParamAudioProfileQuerySupported,
+ ¶m, sizeof(param));
+ if (err != OK) {
+ break;
+ }
+ // For audio, level is ignored.
+ caps->addProfileLevel(param.eProfile, 0 /* level */);
+
+ if (index == kMaxIndicesToCheck) {
+ ALOGW("[%s] stopping checking profiles after %u: %x",
+ name, index,
+ param.eProfile);
+ }
+ }
+
+ // 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);
+ }
+ }
+
+ if (isVideo && !isEncoder) {
+ native_handle_t *sidebandHandle = NULL;
+ if (omxNode->configureVideoTunnelMode(
+ kPortIndexOutput, OMX_TRUE, 0, &sidebandHandle) == OK) {
+ // tunneled playback includes adaptive playback
+ caps->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback
+ | MediaCodecInfo::Capabilities::kFlagSupportsTunneledPlayback);
+ } else if (omxNode->setPortMode(
+ kPortIndexOutput, IOMX::kPortModeDynamicANWBuffer) == OK ||
+ omxNode->prepareForAdaptivePlayback(
+ kPortIndexOutput, OMX_TRUE,
+ 1280 /* width */, 720 /* height */) == OK) {
+ caps->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 (omxNode->getConfig(
+ (OMX_INDEXTYPE)OMX_IndexConfigAndroidIntraRefresh,
+ ¶ms, sizeof(params)) == OK) {
+ caps->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsIntraRefresh);
+ }
+ }
+
+ omxNode->freeNode();
+ return OK;
+}
+
+void buildOmxInfo(const MediaCodecsXmlParser& parser,
+ MediaCodecListWriter* writer) {
+ for (const MediaCodecsXmlParser::Codec& codec : parser.getCodecMap()) {
+ const std::string &name = codec.first;
+ if (!hasPrefix(codec.first, "OMX.")) {
+ continue;
+ }
+ const MediaCodecsXmlParser::CodecProperties &properties = codec.second;
+ bool encoder = properties.isEncoder;
+ std::unique_ptr<MediaCodecInfoWriter> info =
+ writer->addMediaCodecInfo();
+ info->setName(name.c_str());
+ info->setOwner("default");
+ info->setEncoder(encoder);
+ info->setRank(kOmxRankOverride);
+ for (const MediaCodecsXmlParser::Type& type : properties.typeMap) {
+ const std::string &mime = type.first;
+ std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
+ info->addMime(mime.c_str());
+ const MediaCodecsXmlParser::AttributeMap &attrMap = type.second;
+ for (const MediaCodecsXmlParser::Attribute& attr : attrMap) {
+ const std::string &key = attr.first;
+ const std::string &value = attr.second;
+ if (hasPrefix(key, "feature-") &&
+ !hasPrefix(key, "feature-bitrate-modes")) {
+ caps->addDetail(key.c_str(), hasPrefix(value, "1") ? 1 : 0);
+ } else {
+ caps->addDetail(key.c_str(), value.c_str());
+ }
+ }
+ status_t err = queryOmxCapabilities(
+ name.c_str(),
+ mime.c_str(),
+ encoder,
+ caps.get());
+ if (err != OK) {
+ ALOGE("Failed to query capabilities for %s (mime: %s). Error: %d",
+ name.c_str(),
+ mime.c_str(),
+ static_cast<int>(err));
+ }
+ }
+ }
+}
+
} // unnamed namespace
status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) {
@@ -88,20 +340,29 @@
MediaCodecsXmlParser parser(
MediaCodecsXmlParser::defaultSearchDirs,
- "media_codecs_c2.xml",
- "media_codecs_performance_c2.xml");
+ option == 0 ? "media_codecs.xml" :
+ "media_codecs_c2.xml",
+ option == 0 ? "media_codecs_performance.xml" :
+ "media_codecs_performance_c2.xml");
if (parser.getParsingStatus() != OK) {
ALOGD("XML parser no good");
return OK;
}
+
+ bool surfaceTest(Codec2Client::CreateInputSurface());
+ if (option == 0 || !surfaceTest) {
+ buildOmxInfo(parser, writer);
+ }
+
for (const Traits& trait : traits) {
+ C2Component::rank_t rank = trait.rank;
+
if (parser.getCodecMap().count(trait.name.c_str()) == 0) {
ALOGD("%s not found in xml", trait.name.c_str());
continue;
}
// TODO: Remove this block once all codecs are enabled by default.
- C2Component::rank_t rank = trait.rank;
switch (option) {
case 0:
continue;