OSDN Git Service

Adds VPX encoding support for stagefright.
authorKunter Gultekin <kuntergultekin@google.com>
Fri, 1 Feb 2013 15:01:15 +0000 (17:01 +0200)
committerJames Dong <jdong@google.com>
Wed, 13 Feb 2013 01:24:04 +0000 (17:24 -0800)
Only following encoder settings are available
    - target bitrate
    - rate control (constant / variable)
    - frame rate
    - token partitioning
    - error resilience
    - reconstruction & loop filters

Only following color formats are recognized
    - YUV420Planar
    - YUV420SemiPlanar
    - AndroidOpaque

Following settings are not configurable by the client
    - encoding deadline is realtime
    - the algorithm interface for encoder is vp8
    - fractional bits of frame rate is discarded
    - timebase is fixed to 1/1000000

Requires libvpx to be built with encoder support enabled.
Requires openmax 1.1.2 extension headers.

Relevant tests exist in cts repo.

Change-Id: I650f1aca83e7dc93f79d7e6cba7ac24f26e66d40
Signed-off-by: Kunter Gultekin <kuntergultekin@google.com>
media/libstagefright/codecs/on2/enc/Android.mk [new file with mode: 0644]
media/libstagefright/codecs/on2/enc/MODULE_LICENSE_APACHE2 [new file with mode: 0644]
media/libstagefright/codecs/on2/enc/NOTICE [new file with mode: 0644]
media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp [new file with mode: 0644]
media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h [new file with mode: 0644]

diff --git a/media/libstagefright/codecs/on2/enc/Android.mk b/media/libstagefright/codecs/on2/enc/Android.mk
new file mode 100644 (file)
index 0000000..5d3317c
--- /dev/null
@@ -0,0 +1,24 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+        SoftVPXEncoder.cpp
+
+LOCAL_C_INCLUDES := \
+        $(TOP)/external/libvpx/libvpx \
+        $(TOP)/external/openssl/include \
+        $(TOP)/external/libvpx/libvpx/vpx_codec \
+        $(TOP)/external/libvpx/libvpx/vpx_ports \
+        frameworks/av/media/libstagefright/include \
+        frameworks/native/include/media/openmax \
+
+LOCAL_STATIC_LIBRARIES := \
+        libvpx
+
+LOCAL_SHARED_LIBRARIES := \
+        libstagefright libstagefright_omx libstagefright_foundation libutils \
+
+LOCAL_MODULE := libstagefright_soft_vpxenc
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/on2/enc/MODULE_LICENSE_APACHE2 b/media/libstagefright/codecs/on2/enc/MODULE_LICENSE_APACHE2
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/media/libstagefright/codecs/on2/enc/NOTICE b/media/libstagefright/codecs/on2/enc/NOTICE
new file mode 100644 (file)
index 0000000..faed58a
--- /dev/null
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2013, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp
new file mode 100644 (file)
index 0000000..cc38dc3
--- /dev/null
@@ -0,0 +1,685 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "SoftVPXEncoder"
+#include "SoftVPXEncoder.h"
+
+#include <utils/Log.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+
+namespace android {
+
+
+template<class T>
+static void InitOMXParams(T *params) {
+    params->nSize = sizeof(T);
+    // OMX IL 1.1.2
+    params->nVersion.s.nVersionMajor = 1;
+    params->nVersion.s.nVersionMinor = 1;
+    params->nVersion.s.nRevision = 2;
+    params->nVersion.s.nStep = 0;
+}
+
+
+static int GetCPUCoreCount() {
+    int cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+    cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+    // _SC_NPROC_ONLN must be defined...
+    cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+    CHECK_GE(cpuCoreCount, 1);
+    return cpuCoreCount;
+}
+
+
+// This color conversion utility is copied from SoftMPEG4Encoder.cpp
+inline static void ConvertSemiPlanarToPlanar(uint8_t *inyuv,
+                                             uint8_t* outyuv,
+                                             int32_t width,
+                                             int32_t height) {
+    int32_t outYsize = width * height;
+    uint32_t *outy =  (uint32_t *) outyuv;
+    uint16_t *outcb = (uint16_t *) (outyuv + outYsize);
+    uint16_t *outcr = (uint16_t *) (outyuv + outYsize + (outYsize >> 2));
+
+    /* Y copying */
+    memcpy(outy, inyuv, outYsize);
+
+    /* U & V copying */
+    uint32_t *inyuv_4 = (uint32_t *) (inyuv + outYsize);
+    for (int32_t i = height >> 1; i > 0; --i) {
+        for (int32_t j = width >> 2; j > 0; --j) {
+            uint32_t temp = *inyuv_4++;
+            uint32_t tempU = temp & 0xFF;
+            tempU = tempU | ((temp >> 8) & 0xFF00);
+
+            uint32_t tempV = (temp >> 8) & 0xFF;
+            tempV = tempV | ((temp >> 16) & 0xFF00);
+
+            // Flip U and V
+            *outcb++ = tempV;
+            *outcr++ = tempU;
+        }
+    }
+}
+
+
+SoftVPXEncoder::SoftVPXEncoder(const char *name,
+                               const OMX_CALLBACKTYPE *callbacks,
+                               OMX_PTR appData,
+                               OMX_COMPONENTTYPE **component)
+    : SimpleSoftOMXComponent(name, callbacks, appData, component),
+      mCodecContext(NULL),
+      mCodecConfiguration(NULL),
+      mCodecInterface(NULL),
+      mWidth(176),
+      mHeight(144),
+      mBitrate(192000),  // in bps
+      mBitrateControlMode(VPX_VBR),  // variable bitrate
+      mFrameDurationUs(33333),  // Defaults to 30 fps
+      mDCTPartitions(0),
+      mErrorResilience(OMX_FALSE),
+      mColorFormat(OMX_COLOR_FormatYUV420Planar),
+      mLevel(OMX_VIDEO_VP8Level_Version0),
+      mConversionBuffer(NULL) {
+
+    initPorts();
+}
+
+
+SoftVPXEncoder::~SoftVPXEncoder() {
+    releaseEncoder();
+}
+
+
+void SoftVPXEncoder::initPorts() {
+    OMX_PARAM_PORTDEFINITIONTYPE inputPort;
+    OMX_PARAM_PORTDEFINITIONTYPE outputPort;
+
+    InitOMXParams(&inputPort);
+    InitOMXParams(&outputPort);
+
+    inputPort.nBufferCountMin = kNumBuffers;
+    inputPort.nBufferCountActual = inputPort.nBufferCountMin;
+    inputPort.bEnabled = OMX_TRUE;
+    inputPort.bPopulated = OMX_FALSE;
+    inputPort.eDomain = OMX_PortDomainVideo;
+    inputPort.bBuffersContiguous = OMX_FALSE;
+    inputPort.format.video.pNativeRender = NULL;
+    inputPort.format.video.nFrameWidth = mWidth;
+    inputPort.format.video.nFrameHeight = mHeight;
+    inputPort.format.video.nStride = inputPort.format.video.nFrameWidth;
+    inputPort.format.video.nSliceHeight = inputPort.format.video.nFrameHeight;
+    inputPort.format.video.nBitrate = 0;
+    // frameRate is reciprocal of frameDuration, which is
+    // in microseconds. It is also in Q16 format.
+    inputPort.format.video.xFramerate = (1000000/mFrameDurationUs) << 16;
+    inputPort.format.video.bFlagErrorConcealment = OMX_FALSE;
+    inputPort.nPortIndex = kInputPortIndex;
+    inputPort.eDir = OMX_DirInput;
+    inputPort.nBufferAlignment = kInputBufferAlignment;
+    inputPort.format.video.cMIMEType =
+        const_cast<char *>(MEDIA_MIMETYPE_VIDEO_RAW);
+    inputPort.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
+    inputPort.format.video.eColorFormat = mColorFormat;
+    inputPort.format.video.pNativeWindow = NULL;
+    inputPort.nBufferSize =
+        (inputPort.format.video.nStride *
+        inputPort.format.video.nSliceHeight * 3) / 2;
+
+    addPort(inputPort);
+
+    outputPort.nBufferCountMin = kNumBuffers;
+    outputPort.nBufferCountActual = outputPort.nBufferCountMin;
+    outputPort.bEnabled = OMX_TRUE;
+    outputPort.bPopulated = OMX_FALSE;
+    outputPort.eDomain = OMX_PortDomainVideo;
+    outputPort.bBuffersContiguous = OMX_FALSE;
+    outputPort.format.video.pNativeRender = NULL;
+    outputPort.format.video.nFrameWidth = mWidth;
+    outputPort.format.video.nFrameHeight = mHeight;
+    outputPort.format.video.nStride = outputPort.format.video.nFrameWidth;
+    outputPort.format.video.nSliceHeight = outputPort.format.video.nFrameHeight;
+    outputPort.format.video.nBitrate = mBitrate;
+    outputPort.format.video.xFramerate = 0;
+    outputPort.format.video.bFlagErrorConcealment = OMX_FALSE;
+    outputPort.nPortIndex = kOutputPortIndex;
+    outputPort.eDir = OMX_DirOutput;
+    outputPort.nBufferAlignment = kOutputBufferAlignment;
+    outputPort.format.video.cMIMEType =
+        const_cast<char *>(MEDIA_MIMETYPE_VIDEO_VPX);
+    outputPort.format.video.eCompressionFormat = OMX_VIDEO_CodingVPX;
+    outputPort.format.video.eColorFormat = OMX_COLOR_FormatUnused;
+    outputPort.format.video.pNativeWindow = NULL;
+    outputPort.nBufferSize = 256 * 1024;  // arbitrary
+
+    addPort(outputPort);
+}
+
+
+status_t SoftVPXEncoder::initEncoder() {
+    vpx_codec_err_t codec_return;
+
+    mCodecContext = new vpx_codec_ctx_t;
+    mCodecConfiguration = new vpx_codec_enc_cfg_t;
+    mCodecInterface = vpx_codec_vp8_cx();
+
+    if (mCodecInterface == NULL) {
+        return UNKNOWN_ERROR;
+    }
+
+    codec_return = vpx_codec_enc_config_default(mCodecInterface,
+                                                mCodecConfiguration,
+                                                0);  // Codec specific flags
+
+    if (codec_return != VPX_CODEC_OK) {
+        ALOGE("Error populating default configuration for vpx encoder.");
+        return UNKNOWN_ERROR;
+    }
+
+    mCodecConfiguration->g_w = mWidth;
+    mCodecConfiguration->g_h = mHeight;
+    mCodecConfiguration->g_threads = GetCPUCoreCount();
+    mCodecConfiguration->g_error_resilient = mErrorResilience;
+
+    switch (mLevel) {
+        case OMX_VIDEO_VP8Level_Version0:
+            mCodecConfiguration->g_profile = 0;
+            break;
+
+        case OMX_VIDEO_VP8Level_Version1:
+            mCodecConfiguration->g_profile = 1;
+            break;
+
+        case OMX_VIDEO_VP8Level_Version2:
+            mCodecConfiguration->g_profile = 2;
+            break;
+
+        case OMX_VIDEO_VP8Level_Version3:
+            mCodecConfiguration->g_profile = 3;
+            break;
+
+        default:
+            mCodecConfiguration->g_profile = 0;
+    }
+
+    // OMX timebase unit is microsecond
+    // g_timebase is in seconds (i.e. 1/1000000 seconds)
+    mCodecConfiguration->g_timebase.num = 1;
+    mCodecConfiguration->g_timebase.den = 1000000;
+    // rc_target_bitrate is in kbps, mBitrate in bps
+    mCodecConfiguration->rc_target_bitrate = mBitrate/1000;
+    mCodecConfiguration->rc_end_usage = mBitrateControlMode;
+
+    codec_return = vpx_codec_enc_init(mCodecContext,
+                                      mCodecInterface,
+                                      mCodecConfiguration,
+                                      0);  // flags
+
+    if (codec_return != VPX_CODEC_OK) {
+        ALOGE("Error initializing vpx encoder");
+        return UNKNOWN_ERROR;
+    }
+
+    codec_return = vpx_codec_control(mCodecContext,
+                                     VP8E_SET_TOKEN_PARTITIONS,
+                                     mDCTPartitions);
+    if (codec_return != VPX_CODEC_OK) {
+        ALOGE("Error setting dct partitions for vpx encoder.");
+        return UNKNOWN_ERROR;
+    }
+
+    if (mColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) {
+        if (mConversionBuffer == NULL) {
+            mConversionBuffer = (uint8_t *)malloc(mWidth * mHeight * 3 / 2);
+            if (mConversionBuffer == NULL) {
+                ALOGE("Allocating conversion buffer failed.");
+                return UNKNOWN_ERROR;
+            }
+        }
+    }
+    return OK;
+}
+
+
+status_t SoftVPXEncoder::releaseEncoder() {
+    if (mCodecContext != NULL) {
+        vpx_codec_destroy(mCodecContext);
+        delete mCodecContext;
+        mCodecContext = NULL;
+    }
+
+    if (mCodecConfiguration != NULL) {
+        delete mCodecConfiguration;
+        mCodecConfiguration = NULL;
+    }
+
+    if (mConversionBuffer != NULL) {
+        delete mConversionBuffer;
+        mConversionBuffer = NULL;
+    }
+
+    // this one is not allocated by us
+    mCodecInterface = NULL;
+
+    return OK;
+}
+
+
+OMX_ERRORTYPE SoftVPXEncoder::internalGetParameter(OMX_INDEXTYPE index,
+                                                   OMX_PTR param) {
+    // can include extension index OMX_INDEXEXTTYPE
+    const int32_t indexFull = index;
+
+    switch (indexFull) {
+        case OMX_IndexParamVideoPortFormat: {
+            OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
+                (OMX_VIDEO_PARAM_PORTFORMATTYPE *)param;
+
+            if (formatParams->nPortIndex == kInputPortIndex) {
+                if (formatParams->nIndex >= kNumberOfSupportedColorFormats) {
+                    return OMX_ErrorNoMore;
+                }
+
+                // Color formats, in order of preference
+                if (formatParams->nIndex == 0) {
+                    formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar;
+                } else if (formatParams->nIndex == 1) {
+                    formatParams->eColorFormat =
+                        OMX_COLOR_FormatYUV420SemiPlanar;
+                } else {
+                    formatParams->eColorFormat = OMX_COLOR_FormatAndroidOpaque;
+                }
+
+                formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused;
+                // Converting from microseconds
+                // Also converting to Q16 format
+                formatParams->xFramerate = (1000000/mFrameDurationUs) << 16;
+                return OMX_ErrorNone;
+            } else if (formatParams->nPortIndex == kOutputPortIndex) {
+                formatParams->eCompressionFormat = OMX_VIDEO_CodingVPX;
+                formatParams->eColorFormat = OMX_COLOR_FormatUnused;
+                formatParams->xFramerate = 0;
+                return OMX_ErrorNone;
+            } else {
+                return OMX_ErrorBadPortIndex;
+            }
+        }
+
+        case OMX_IndexParamVideoBitrate: {
+            OMX_VIDEO_PARAM_BITRATETYPE *bitrate =
+                (OMX_VIDEO_PARAM_BITRATETYPE *)param;
+
+                if (bitrate->nPortIndex != kOutputPortIndex) {
+                    return OMX_ErrorUnsupportedIndex;
+                }
+
+                bitrate->nTargetBitrate = mBitrate;
+
+                if (mBitrateControlMode == VPX_VBR) {
+                    bitrate->eControlRate = OMX_Video_ControlRateVariable;
+                } else if (mBitrateControlMode == VPX_CBR) {
+                    bitrate->eControlRate = OMX_Video_ControlRateConstant;
+                } else {
+                    return OMX_ErrorUnsupportedSetting;
+                }
+                return OMX_ErrorNone;
+        }
+
+        // VP8 specific parameters that use extension headers
+        case OMX_IndexParamVideoVp8: {
+            OMX_VIDEO_PARAM_VP8TYPE *vp8Params =
+                (OMX_VIDEO_PARAM_VP8TYPE *)param;
+
+                if (vp8Params->nPortIndex != kOutputPortIndex) {
+                    return OMX_ErrorUnsupportedIndex;
+                }
+
+                vp8Params->eProfile = OMX_VIDEO_VP8ProfileMain;
+                vp8Params->eLevel = mLevel;
+                vp8Params->nDCTPartitions = mDCTPartitions;
+                vp8Params->bErrorResilientMode = mErrorResilience;
+                return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamVideoProfileLevelQuerySupported: {
+            OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileAndLevel =
+                (OMX_VIDEO_PARAM_PROFILELEVELTYPE *)param;
+
+            if (profileAndLevel->nPortIndex != kOutputPortIndex) {
+                return OMX_ErrorUnsupportedIndex;
+            }
+
+            switch (profileAndLevel->nProfileIndex) {
+                case 0:
+                    profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version0;
+                    break;
+
+                case 1:
+                    profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version1;
+                    break;
+
+                case 2:
+                    profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version2;
+                    break;
+
+                case 3:
+                    profileAndLevel->eLevel = OMX_VIDEO_VP8Level_Version3;
+                    break;
+
+                default:
+                    return OMX_ErrorNoMore;
+            }
+
+            profileAndLevel->eProfile = OMX_VIDEO_VP8ProfileMain;
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamVideoProfileLevelCurrent: {
+            OMX_VIDEO_PARAM_PROFILELEVELTYPE *profileAndLevel =
+                (OMX_VIDEO_PARAM_PROFILELEVELTYPE *)param;
+
+            if (profileAndLevel->nPortIndex != kOutputPortIndex) {
+                return OMX_ErrorUnsupportedIndex;
+            }
+
+            profileAndLevel->eLevel = mLevel;
+            profileAndLevel->eProfile = OMX_VIDEO_VP8ProfileMain;
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::internalGetParameter(index, param);
+    }
+}
+
+
+OMX_ERRORTYPE SoftVPXEncoder::internalSetParameter(OMX_INDEXTYPE index,
+                                                   const OMX_PTR param) {
+    // can include extension index OMX_INDEXEXTTYPE
+    const int32_t indexFull = index;
+
+    switch (indexFull) {
+        case OMX_IndexParamStandardComponentRole:
+            return internalSetRoleParams(
+                (const OMX_PARAM_COMPONENTROLETYPE *)param);
+
+        case OMX_IndexParamVideoBitrate:
+            return internalSetBitrateParams(
+                (const OMX_VIDEO_PARAM_BITRATETYPE *)param);
+
+        case OMX_IndexParamPortDefinition:
+            return internalSetPortParams(
+                (const OMX_PARAM_PORTDEFINITIONTYPE *)param);
+
+        case OMX_IndexParamVideoPortFormat:
+            return internalSetFormatParams(
+                (const OMX_VIDEO_PARAM_PORTFORMATTYPE *)param);
+
+        case OMX_IndexParamVideoVp8:
+            return internalSetVp8Params(
+                (const OMX_VIDEO_PARAM_VP8TYPE *)param);
+
+        case OMX_IndexParamVideoProfileLevelCurrent:
+            return internalSetProfileLevel(
+                (const OMX_VIDEO_PARAM_PROFILELEVELTYPE *)param);
+
+        default:
+            return SimpleSoftOMXComponent::internalSetParameter(index, param);
+    }
+}
+
+OMX_ERRORTYPE SoftVPXEncoder::internalSetProfileLevel(
+        const OMX_VIDEO_PARAM_PROFILELEVELTYPE* profileAndLevel) {
+    if (profileAndLevel->nPortIndex != kOutputPortIndex) {
+        return OMX_ErrorUnsupportedIndex;
+    }
+
+    if (profileAndLevel->eProfile != OMX_VIDEO_VP8ProfileMain) {
+        return OMX_ErrorBadParameter;
+    }
+
+    if (profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version0 ||
+        profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version1 ||
+        profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version2 ||
+        profileAndLevel->eLevel == OMX_VIDEO_VP8Level_Version3) {
+        mLevel = (OMX_VIDEO_VP8LEVELTYPE)profileAndLevel->eLevel;
+    } else {
+        return OMX_ErrorBadParameter;
+    }
+
+    return OMX_ErrorNone;
+}
+
+
+OMX_ERRORTYPE SoftVPXEncoder::internalSetVp8Params(
+        const OMX_VIDEO_PARAM_VP8TYPE* vp8Params) {
+    if (vp8Params->nPortIndex != kOutputPortIndex) {
+        return OMX_ErrorUnsupportedIndex;
+    }
+
+    if (vp8Params->eProfile != OMX_VIDEO_VP8ProfileMain) {
+        return OMX_ErrorBadParameter;
+    }
+
+    if (vp8Params->eLevel == OMX_VIDEO_VP8Level_Version0 ||
+        vp8Params->eLevel == OMX_VIDEO_VP8Level_Version1 ||
+        vp8Params->eLevel == OMX_VIDEO_VP8Level_Version2 ||
+        vp8Params->eLevel == OMX_VIDEO_VP8Level_Version3) {
+        mLevel = vp8Params->eLevel;
+    } else {
+        return OMX_ErrorBadParameter;
+    }
+
+    if (vp8Params->nDCTPartitions <= kMaxDCTPartitions) {
+        mDCTPartitions = vp8Params->nDCTPartitions;
+    } else {
+        return OMX_ErrorBadParameter;
+    }
+
+    mErrorResilience = vp8Params->bErrorResilientMode;
+    return OMX_ErrorNone;
+}
+
+
+OMX_ERRORTYPE SoftVPXEncoder::internalSetFormatParams(
+        const OMX_VIDEO_PARAM_PORTFORMATTYPE* format) {
+    if (format->nPortIndex == kInputPortIndex) {
+        if (format->eColorFormat == OMX_COLOR_FormatYUV420Planar ||
+            format->eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar ||
+            format->eColorFormat == OMX_COLOR_FormatAndroidOpaque) {
+            mColorFormat = format->eColorFormat;
+            return OMX_ErrorNone;
+        } else {
+            ALOGE("Unsupported color format %i", format->eColorFormat);
+            return OMX_ErrorUnsupportedSetting;
+        }
+    } else if (format->nPortIndex == kOutputPortIndex) {
+        if (format->eCompressionFormat == OMX_VIDEO_CodingVPX) {
+            return OMX_ErrorNone;
+        } else {
+            return OMX_ErrorUnsupportedSetting;
+        }
+    } else {
+        return OMX_ErrorBadPortIndex;
+    }
+}
+
+
+OMX_ERRORTYPE SoftVPXEncoder::internalSetRoleParams(
+        const OMX_PARAM_COMPONENTROLETYPE* role) {
+    const char* roleText = (const char*)role->cRole;
+    const size_t roleTextMaxSize = OMX_MAX_STRINGNAME_SIZE - 1;
+
+    if (strncmp(roleText, "video_encoder.vpx", roleTextMaxSize)) {
+        ALOGE("Unsupported component role");
+        return OMX_ErrorBadParameter;
+    }
+
+    return OMX_ErrorNone;
+}
+
+
+OMX_ERRORTYPE SoftVPXEncoder::internalSetPortParams(
+        const OMX_PARAM_PORTDEFINITIONTYPE* port) {
+    if (port->nPortIndex == kInputPortIndex) {
+        mWidth = port->format.video.nFrameWidth;
+        mHeight = port->format.video.nFrameHeight;
+
+        // xFramerate comes in Q16 format, in frames per second unit
+        const uint32_t framerate = port->format.video.xFramerate >> 16;
+        // frame duration is in microseconds
+        mFrameDurationUs = (1000000/framerate);
+
+        if (port->format.video.eColorFormat == OMX_COLOR_FormatYUV420Planar ||
+            port->format.video.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar ||
+            port->format.video.eColorFormat == OMX_COLOR_FormatAndroidOpaque) {
+                mColorFormat = port->format.video.eColorFormat;
+        } else {
+            return OMX_ErrorUnsupportedSetting;
+        }
+
+        return OMX_ErrorNone;
+    } else if (port->nPortIndex == kOutputPortIndex) {
+        mBitrate = port->format.video.nBitrate;
+        return OMX_ErrorNone;
+    } else {
+        return OMX_ErrorBadPortIndex;
+    }
+}
+
+
+OMX_ERRORTYPE SoftVPXEncoder::internalSetBitrateParams(
+        const OMX_VIDEO_PARAM_BITRATETYPE* bitrate) {
+    if (bitrate->nPortIndex != kOutputPortIndex) {
+        return OMX_ErrorUnsupportedIndex;
+    }
+
+    mBitrate = bitrate->nTargetBitrate;
+
+    if (bitrate->eControlRate == OMX_Video_ControlRateVariable) {
+        mBitrateControlMode = VPX_VBR;
+    } else if (bitrate->eControlRate == OMX_Video_ControlRateConstant) {
+        mBitrateControlMode = VPX_CBR;
+    } else {
+        return OMX_ErrorUnsupportedSetting;
+    }
+
+    return OMX_ErrorNone;
+}
+
+
+void SoftVPXEncoder::onQueueFilled(OMX_U32 portIndex) {
+    // Initialize encoder if not already
+    if (mCodecContext == NULL) {
+        if (OK != initEncoder()) {
+            ALOGE("Failed to initialize encoder");
+            notify(OMX_EventError,
+                   OMX_ErrorUndefined,
+                   0,  // Extra notification data
+                   NULL);  // Notification data pointer
+            return;
+        }
+    }
+
+    vpx_codec_err_t codec_return;
+    List<BufferInfo *> &inputBufferInfoQueue = getPortQueue(kInputPortIndex);
+    List<BufferInfo *> &outputBufferInfoQueue = getPortQueue(kOutputPortIndex);
+
+    while (!inputBufferInfoQueue.empty() && !outputBufferInfoQueue.empty()) {
+        BufferInfo *inputBufferInfo = *inputBufferInfoQueue.begin();
+        OMX_BUFFERHEADERTYPE *inputBufferHeader = inputBufferInfo->mHeader;
+
+        BufferInfo *outputBufferInfo = *outputBufferInfoQueue.begin();
+        OMX_BUFFERHEADERTYPE *outputBufferHeader = outputBufferInfo->mHeader;
+
+        if (inputBufferHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+            inputBufferInfoQueue.erase(inputBufferInfoQueue.begin());
+            inputBufferInfo->mOwnedByUs = false;
+            notifyEmptyBufferDone(inputBufferHeader);
+
+            outputBufferHeader->nFilledLen = 0;
+            outputBufferHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+            outputBufferInfoQueue.erase(outputBufferInfoQueue.begin());
+            outputBufferInfo->mOwnedByUs = false;
+            notifyFillBufferDone(outputBufferHeader);
+            return;
+        }
+
+        uint8_t* source = inputBufferHeader->pBuffer + inputBufferHeader->nOffset;
+
+        // NOTE: As much as nothing is known about color format
+        // when it is denoted as AndroidOpaque, it is at least
+        // assumed to be planar.
+        if (mColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) {
+            ConvertSemiPlanarToPlanar(source, mConversionBuffer, mWidth, mHeight);
+            source = mConversionBuffer;
+        }
+        vpx_image_t raw_frame;
+        vpx_img_wrap(&raw_frame, VPX_IMG_FMT_I420, mWidth, mHeight,
+                     kInputBufferAlignment, source);
+        codec_return = vpx_codec_encode(mCodecContext,
+                                        &raw_frame,
+                                        inputBufferHeader->nTimeStamp,  // in timebase units
+                                        mFrameDurationUs,  // frame duration in timebase units
+                                        0,  // frame flags
+                                        VPX_DL_REALTIME);  // encoding deadline
+        if (codec_return != VPX_CODEC_OK) {
+            ALOGE("vpx encoder failed to encode frame");
+            notify(OMX_EventError,
+                   OMX_ErrorUndefined,
+                   0,  // Extra notification data
+                   NULL);  // Notification data pointer
+            return;
+        }
+
+        vpx_codec_iter_t encoded_packet_iterator = NULL;
+        const vpx_codec_cx_pkt_t* encoded_packet;
+
+        while (encoded_packet = vpx_codec_get_cx_data(mCodecContext, &encoded_packet_iterator)) {
+            if (encoded_packet->kind == VPX_CODEC_CX_FRAME_PKT) {
+                outputBufferHeader->nTimeStamp = encoded_packet->data.frame.pts;
+                outputBufferHeader->nFlags = 0;
+                outputBufferHeader->nOffset = 0;
+                outputBufferHeader->nFilledLen = encoded_packet->data.frame.sz;
+                memcpy(outputBufferHeader->pBuffer,
+                       encoded_packet->data.frame.buf,
+                       encoded_packet->data.frame.sz);
+                outputBufferInfo->mOwnedByUs = false;
+                outputBufferInfoQueue.erase(outputBufferInfoQueue.begin());
+                notifyFillBufferDone(outputBufferHeader);
+            }
+        }
+
+        inputBufferInfo->mOwnedByUs = false;
+        inputBufferInfoQueue.erase(inputBufferInfoQueue.begin());
+        notifyEmptyBufferDone(inputBufferHeader);
+    }
+}
+}  // namespace android
+
+
+android::SoftOMXComponent *createSoftOMXComponent(
+        const char *name, const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+    return new android::SoftVPXEncoder(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h
new file mode 100644 (file)
index 0000000..3bc05c0
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SOFT_VPX_ENCODER_H_
+
+#define SOFT_VPX_ENCODER_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+#include <OMX_VideoExt.h>
+#include <OMX_IndexExt.h>
+
+#include "vpx/vpx_encoder.h"
+#include "vpx/vpx_codec.h"
+#include "vpx/vp8cx.h"
+
+namespace android {
+
+// Exposes a vpx encoder as an OMX Component
+//
+// Boilerplate for callback bindings are taken care
+// by the base class SimpleSoftOMXComponent and its
+// parent SoftOMXComponent.
+//
+// Only following encoder settings are available
+//    - target bitrate
+//    - rate control (constant / variable)
+//    - frame rate
+//    - error resilience
+//    - token partitioning
+//    - reconstruction & loop filters (g_profile)
+//
+// Only following color formats are recognized
+//    - YUV420Planar
+//    - YUV420SemiPlanar
+//    - AndroidOpaque
+//
+// Following settings are not configurable by the client
+//    - encoding deadline is realtime
+//    - multithreaded encoding utilizes a number of threads equal
+// to online cpu's available
+//    - the algorithm interface for encoder is vp8
+//    - fractional bits of frame rate is discarded
+//    - OMX timestamps are in microseconds, therefore
+// encoder timebase is fixed to 1/1000000
+
+class SoftVPXEncoder : public SimpleSoftOMXComponent {
+ public:
+    SoftVPXEncoder(const char *name,
+                   const OMX_CALLBACKTYPE *callbacks,
+                   OMX_PTR appData,
+                   OMX_COMPONENTTYPE **component);
+
+ protected:
+    virtual ~SoftVPXEncoder();
+
+    // Returns current values for requested OMX
+    // parameters
+    virtual OMX_ERRORTYPE internalGetParameter(
+            OMX_INDEXTYPE index, OMX_PTR param);
+
+    // Validates, extracts and stores relevant OMX
+    // parameters
+    virtual OMX_ERRORTYPE internalSetParameter(
+            OMX_INDEXTYPE index, const OMX_PTR param);
+
+    // OMX callback when buffers available
+    // Note that both an input and output buffer
+    // is expected to be available to carry out
+    // encoding of the frame
+    virtual void onQueueFilled(OMX_U32 portIndex);
+
+ private:
+    // number of buffers allocated per port
+    static const uint32_t kNumBuffers = 4;
+
+    // OMX port indexes that refer to input and
+    // output ports respectively
+    static const uint32_t kInputPortIndex = 0;
+    static const uint32_t kOutputPortIndex = 1;
+
+    // Byte-alignment required for buffers
+    static const uint32_t kInputBufferAlignment = 1;
+    static const uint32_t kOutputBufferAlignment = 2;
+
+    // Max value supported for DCT partitions
+    static const uint32_t kMaxDCTPartitions = 3;
+
+    // Number of supported input color formats
+    static const uint32_t kNumberOfSupportedColorFormats = 3;
+
+    // vpx specific opaque data structure that
+    // stores encoder state
+    vpx_codec_ctx_t* mCodecContext;
+
+    // vpx specific data structure that
+    // stores encoder configuration
+    vpx_codec_enc_cfg_t* mCodecConfiguration;
+
+    // vpx specific read-only data structure
+    // that specifies algorithm interface (e.g. vp8)
+    vpx_codec_iface_t* mCodecInterface;
+
+    // Width of the input frames
+    int32_t mWidth;
+
+    // Height of the input frames
+    int32_t mHeight;
+
+    // Target bitrate set for the encoder, in bits per second.
+    int32_t mBitrate;
+
+    // Bitrate control mode, either constant or variable
+    vpx_rc_mode mBitrateControlMode;
+
+    // Frame duration is the reciprocal of framerate, denoted
+    // in microseconds
+    uint64_t mFrameDurationUs;
+
+    // vp8 specific configuration parameter
+    // that enables token partitioning of
+    // the stream into substreams
+    int32_t mDCTPartitions;
+
+    // Parameter that denotes whether error resilience
+    // is enabled in encoder
+    OMX_BOOL mErrorResilience;
+
+    // Color format for the input port
+    OMX_COLOR_FORMATTYPE mColorFormat;
+
+    // Encoder profile corresponding to OMX level parameter
+    //
+    // The inconsistency in the naming is caused by
+    // OMX spec referring vpx profiles (g_profile)
+    // as "levels" whereas using the name "profile" for
+    // something else.
+    OMX_VIDEO_VP8LEVELTYPE mLevel;
+
+    // Conversion buffer is needed to convert semi
+    // planar yuv420 to planar format
+    // It is only allocated if input format is
+    // indeed YUV420SemiPlanar.
+    uint8_t* mConversionBuffer;
+
+    // Initializes input and output OMX ports with sensible
+    // default values.
+    void initPorts();
+
+    // Initializes vpx encoder with available settings.
+    status_t initEncoder();
+
+    // Releases vpx encoder instance, with it's associated
+    // data structures.
+    //
+    // Unless called earlier, this is handled by the
+    // dtor.
+    status_t releaseEncoder();
+
+    // Handles port changes with respect to color formats
+    OMX_ERRORTYPE internalSetFormatParams(
+        const OMX_VIDEO_PARAM_PORTFORMATTYPE* format);
+
+    // Verifies the component role tried to be set to this OMX component is
+    // strictly video_encoder.vpx
+    OMX_ERRORTYPE internalSetRoleParams(
+        const OMX_PARAM_COMPONENTROLETYPE* role);
+
+    // Updates bitrate to reflect port settings.
+    OMX_ERRORTYPE internalSetBitrateParams(
+        const OMX_VIDEO_PARAM_BITRATETYPE* bitrate);
+
+    // Handles port definition changes.
+    OMX_ERRORTYPE internalSetPortParams(
+        const OMX_PARAM_PORTDEFINITIONTYPE* port);
+
+    // Handles vp8 specific parameters.
+    OMX_ERRORTYPE internalSetVp8Params(
+        const OMX_VIDEO_PARAM_VP8TYPE* vp8Params);
+
+    // Updates encoder profile
+    OMX_ERRORTYPE internalSetProfileLevel(
+        const OMX_VIDEO_PARAM_PROFILELEVELTYPE* profileAndLevel);
+
+    DISALLOW_EVIL_CONSTRUCTORS(SoftVPXEncoder);
+};
+
+}  // namespace android
+
+#endif  // SOFT_VPX_ENCODER_H_