Merge "stagefright: Move CodecCapabilities querying into MediaCodec" into nyc-dev
diff --git a/include/media/MediaCodecInfo.h b/include/media/MediaCodecInfo.h
index 4067b47..e6f9661 100644
--- a/include/media/MediaCodecInfo.h
+++ b/include/media/MediaCodecInfo.h
@@ -33,7 +33,6 @@
 
 struct AMessage;
 class Parcel;
-struct CodecCapabilities;
 
 typedef KeyedVector<AString, AString> CodecSettings;
 
@@ -44,12 +43,23 @@
     };
 
     struct Capabilities : public RefBase {
+        enum {
+            // decoder flags
+            kFlagSupportsAdaptivePlayback = 1 << 0,
+            kFlagSupportsSecurePlayback = 1 << 1,
+            kFlagSupportsTunneledPlayback = 1 << 2,
+
+            // encoder flags
+            kFlagSupportsIntraRefresh = 1 << 0,
+
+        };
+
         void getSupportedProfileLevels(Vector<ProfileLevel> *profileLevels) const;
         void getSupportedColorFormats(Vector<uint32_t> *colorFormats) const;
         uint32_t getFlags() const;
         const sp<AMessage> getDetails() const;
 
-    private:
+    protected:
         Vector<ProfileLevel> mProfileLevels;
         Vector<uint32_t> mColorFormats;
         uint32_t mFlags;
@@ -57,6 +67,7 @@
 
         Capabilities();
 
+    private:
         // read object from parcel even if object creation fails
         static sp<Capabilities> FromParcel(const Parcel &parcel);
         status_t writeToParcel(Parcel *parcel) const;
@@ -66,6 +77,14 @@
         friend class MediaCodecInfo;
     };
 
+    // Use a subclass to allow setting fields on construction without allowing
+    // to do the same throughout the framework.
+    struct CapabilitiesBuilder : public Capabilities {
+        void addProfileLevel(uint32_t profile, uint32_t level);
+        void addColorFormat(uint32_t format);
+        void addFlags(uint32_t flags);
+    };
+
     bool isEncoder() const;
     bool hasQuirk(const char *name) const;
     void getSupportedMimes(Vector<AString> *mimes) const;
@@ -107,7 +126,8 @@
     void addQuirk(const char *name);
     status_t addMime(const char *mime);
     status_t updateMime(const char *mime);
-    status_t initializeCapabilities(const CodecCapabilities &caps);
+    // after this call Capabilities will be owned by MediaCodecInfo
+    status_t setCapabilities(const sp<Capabilities> &caps);
     void addDetail(const AString &key, const AString &value);
     void addFeature(const AString &key, int32_t value);
     void addFeature(const AString &key, const char *value);
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 5db70a6..349db64 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -50,6 +50,10 @@
     virtual void initiateStart();
     virtual void initiateShutdown(bool keepComponentAllocated = false);
 
+    virtual status_t queryCapabilities(
+            const AString &name, const AString &mime, bool isEncoder,
+            sp<MediaCodecInfo::Capabilities> *caps);
+
     virtual status_t setSurface(const sp<Surface> &surface);
 
     virtual void signalFlush();
@@ -312,6 +316,10 @@
             ssize_t *index = NULL);
 
     status_t setComponentRole(bool isEncoder, const char *mime);
+    static const char *getComponentRole(bool isEncoder, const char *mime);
+    static status_t setComponentRole(
+            const sp<IOMX> &omx, IOMX::node_id node, const char *role);
+
     status_t configureCodec(const char *mime, const sp<AMessage> &msg);
 
     status_t configureTunneledVideoPlayback(int32_t audioHwSync,
diff --git a/include/media/stagefright/CodecBase.h b/include/media/stagefright/CodecBase.h
index 542db71..01b744b 100644
--- a/include/media/stagefright/CodecBase.h
+++ b/include/media/stagefright/CodecBase.h
@@ -21,6 +21,7 @@
 #include <stdint.h>
 #include <media/IOMX.h>
 
+#include <media/MediaCodecInfo.h>
 #include <media/stagefright/foundation/AHandler.h>
 
 namespace android {
@@ -59,6 +60,10 @@
     // require an explicit message handler
     virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
 
+    virtual status_t queryCapabilities(
+            const AString &name, const AString &mime, bool isEncoder,
+            sp<MediaCodecInfo::Capabilities> *caps /* nonnull */) { return INVALID_OPERATION; }
+
     virtual status_t setSurface(const sp<Surface> &surface) { return INVALID_OPERATION; }
 
     virtual void signalFlush() = 0;
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index d4b8e4a..ea708e3 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -20,6 +20,7 @@
 
 #include <gui/IGraphicBufferProducer.h>
 #include <media/hardware/CryptoAPI.h>
+#include <media/MediaCodecInfo.h>
 #include <media/MediaResource.h>
 #include <media/stagefright/foundation/AHandler.h>
 #include <media/stagefright/FrameRenderTracker.h>
@@ -73,6 +74,11 @@
 
     static sp<PersistentSurface> CreatePersistentInputSurface();
 
+    // utility method to query capabilities
+    static status_t QueryCapabilities(
+            const AString &name, const AString &mime, bool isEncoder,
+            sp<MediaCodecInfo::Capabilities> *caps /* nonnull */);
+
     status_t configure(
             const sp<AMessage> &format,
             const sp<Surface> &nativeWindow,
@@ -341,6 +347,8 @@
 
     MediaCodec(const sp<ALooper> &looper, pid_t pid);
 
+    static sp<CodecBase> GetCodecBase(const AString &name, bool nameIsType = false);
+
     static status_t PostAndAwaitResponse(
             const sp<AMessage> &msg, sp<AMessage> *response);
 
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(&param);
+        param.nPortIndex = isEncoder ? kPortIndexOutput : kPortIndexInput;
+
+        for (param.nProfileIndex = 0;; ++param.nProfileIndex) {
+            status_t err = omx->getParameter(
+                    node, OMX_IndexParamVideoProfileLevelQuerySupported,
+                    &param, 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(&param);
+        param.nPortIndex = isEncoder ? kPortIndexOutput : kPortIndexInput;
+        for (param.nProfileIndex = 0;; ++param.nProfileIndex) {
+            status_t err = omx->getParameter(
+                    node, (OMX_INDEXTYPE)OMX_IndexParamAudioProfileQuerySupported,
+                    &param, 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(&params);
+        params.nPortIndex = kPortIndexOutput;
+        // TODO: should we verify if fallback is supported?
+        if (omx->getConfig(
+                node, (OMX_INDEXTYPE)OMX_IndexConfigAndroidIntraRefresh,
+                &params, 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) {