--- /dev/null
+
+ 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
+
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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_