From 26fd52895688f7e4d583ab25a1fe2b15451bdb3b Mon Sep 17 00:00:00 2001 From: Ziyang Zhou Date: Tue, 24 Jun 2025 23:26:59 +0800 Subject: [PATCH 2/2] bring back OMX Change-Id: I41ad31958a4b9dd83a3c3e531bfdb3db94bcb693 --- media/libstagefright/codecs/aacdec/Android.bp | 44 + .../codecs/aacdec/DrcPresModeWrap.cpp | 371 ++++ .../codecs/aacdec/DrcPresModeWrap.h | 62 + .../codecs/aacdec/MODULE_LICENSE_APACHE2 | 0 media/libstagefright/codecs/aacdec/NOTICE | 190 ++ .../libstagefright/codecs/aacdec/SoftAAC2.cpp | 1255 ++++++++++++ media/libstagefright/codecs/aacdec/SoftAAC2.h | 109 ++ .../libstagefright/codecs/aacdec/exports.lds | 5 + media/libstagefright/codecs/aacenc/Android.bp | 37 + .../codecs/aacenc/MODULE_LICENSE_APACHE2 | 0 media/libstagefright/codecs/aacenc/NOTICE | 190 ++ .../codecs/aacenc/SoftAACEncoder2.cpp | 740 +++++++ .../codecs/aacenc/SoftAACEncoder2.h | 82 + .../libstagefright/codecs/aacenc/exports.lds | 5 + .../codecs/aacenc/patent_disclaimer.txt | 9 + .../codecs/amrnb/dec/Android.bp | 39 + .../codecs/amrnb/dec/SoftAMR.cpp | 585 ++++++ .../libstagefright/codecs/amrnb/dec/SoftAMR.h | 91 + .../codecs/amrnb/dec/exports.lds | 5 + .../codecs/amrnb/enc/Android.bp | 31 + .../codecs/amrnb/enc/SoftAMRNBEncoder.cpp | 429 +++++ .../codecs/amrnb/enc/SoftAMRNBEncoder.h | 72 + .../codecs/amrnb/enc/exports.lds | 5 + .../libstagefright/codecs/amrwbenc/Android.bp | 31 + .../codecs/amrwbenc/SoftAMRWBEncoder.cpp | 484 +++++ .../codecs/amrwbenc/SoftAMRWBEncoder.h | 76 + .../codecs/amrwbenc/exports.lds | 5 + media/libstagefright/codecs/avcdec/Android.bp | 34 + .../codecs/avcdec/SoftAVCDec.cpp | 732 +++++++ .../libstagefright/codecs/avcdec/SoftAVCDec.h | 168 ++ .../libstagefright/codecs/avcdec/exports.lds | 5 + media/libstagefright/codecs/avcenc/Android.bp | 34 + .../codecs/avcenc/SoftAVCEnc.cpp | 1515 +++++++++++++++ .../libstagefright/codecs/avcenc/SoftAVCEnc.h | 317 +++ .../libstagefright/codecs/avcenc/exports.lds | 5 + .../libstagefright/codecs/flac/dec/Android.bp | 41 + .../codecs/flac/dec/MODULE_LICENSE_APACHE2 | 0 media/libstagefright/codecs/flac/dec/NOTICE | 190 ++ .../codecs/flac/dec/SoftFlacDecoder.cpp | 500 +++++ .../codecs/flac/dec/SoftFlacDecoder.h | 81 + .../codecs/flac/dec/exports.lds | 5 + .../libstagefright/codecs/flac/enc/Android.bp | 43 + .../codecs/flac/enc/MODULE_LICENSE_APACHE2 | 0 media/libstagefright/codecs/flac/enc/NOTICE | 190 ++ .../codecs/flac/enc/SoftFlacEncoder.cpp | 601 ++++++ .../codecs/flac/enc/SoftFlacEncoder.h | 101 + .../codecs/flac/enc/exports.lds | 5 + .../libstagefright/codecs/g711/dec/Android.bp | 35 + .../codecs/g711/dec/MODULE_LICENSE_APACHE2 | 0 media/libstagefright/codecs/g711/dec/NOTICE | 190 ++ .../codecs/g711/dec/SoftG711.cpp | 391 ++++ .../libstagefright/codecs/g711/dec/SoftG711.h | 64 + .../codecs/g711/dec/exports.lds | 5 + .../libstagefright/codecs/gsm/dec/Android.bp | 37 + .../codecs/gsm/dec/MODULE_LICENSE_APACHE2 | 0 media/libstagefright/codecs/gsm/dec/NOTICE | 190 ++ .../libstagefright/codecs/gsm/dec/SoftGSM.cpp | 363 ++++ media/libstagefright/codecs/gsm/dec/SoftGSM.h | 66 + .../libstagefright/codecs/gsm/dec/exports.lds | 5 + .../libstagefright/codecs/hevcdec/Android.bp | 38 + .../codecs/hevcdec/SoftHEVC.cpp | 726 +++++++ .../libstagefright/codecs/hevcdec/SoftHEVC.h | 123 ++ .../libstagefright/codecs/hevcdec/exports.lds | 5 + .../codecs/m4v_h263/dec/Android.bp | 28 + .../codecs/m4v_h263/dec/SoftMPEG4.cpp | 440 +++++ .../codecs/m4v_h263/dec/SoftMPEG4.h | 79 + .../codecs/m4v_h263/dec/exports.lds | 5 + .../codecs/m4v_h263/enc/Android.bp | 30 + .../codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp | 550 ++++++ .../codecs/m4v_h263/enc/SoftMPEG4Encoder.h | 88 + .../codecs/m4v_h263/enc/exports.lds | 5 + media/libstagefright/codecs/mp3dec/Android.bp | 27 + .../libstagefright/codecs/mp3dec/SoftMP3.cpp | 506 +++++ media/libstagefright/codecs/mp3dec/SoftMP3.h | 84 + .../libstagefright/codecs/mp3dec/exports.lds | 5 + .../libstagefright/codecs/mpeg2dec/Android.bp | 32 + .../codecs/mpeg2dec/SoftMPEG2.cpp | 872 +++++++++ .../codecs/mpeg2dec/SoftMPEG2.h | 184 ++ .../codecs/mpeg2dec/exports.lds | 5 + .../libstagefright/codecs/on2/dec/Android.bp | 37 + .../codecs/on2/dec/MODULE_LICENSE_APACHE2 | 0 media/libstagefright/codecs/on2/dec/NOTICE | 190 ++ .../libstagefright/codecs/on2/dec/SoftVPX.cpp | 374 ++++ media/libstagefright/codecs/on2/dec/SoftVPX.h | 84 + .../libstagefright/codecs/on2/dec/exports.lds | 5 + .../libstagefright/codecs/on2/enc/Android.bp | 44 + .../codecs/on2/enc/MODULE_LICENSE_APACHE2 | 0 media/libstagefright/codecs/on2/enc/NOTICE | 190 ++ .../codecs/on2/enc/SoftVP8Encoder.cpp | 172 ++ .../codecs/on2/enc/SoftVP8Encoder.h | 93 + .../codecs/on2/enc/SoftVP9Encoder.cpp | 179 ++ .../codecs/on2/enc/SoftVP9Encoder.h | 91 + .../codecs/on2/enc/SoftVPXEncoder.cpp | 799 ++++++++ .../codecs/on2/enc/SoftVPXEncoder.h | 251 +++ .../libstagefright/codecs/on2/enc/exports.lds | 5 + .../libstagefright/codecs/opus/dec/Android.bp | 29 + .../codecs/opus/dec/SoftOpus.cpp | 674 +++++++ .../libstagefright/codecs/opus/dec/SoftOpus.h | 98 + .../codecs/opus/dec/exports.lds | 5 + media/libstagefright/codecs/raw/Android.bp | 35 + .../codecs/raw/MODULE_LICENSE_APACHE2 | 0 media/libstagefright/codecs/raw/NOTICE | 190 ++ media/libstagefright/codecs/raw/SoftRaw.cpp | 281 +++ media/libstagefright/codecs/raw/SoftRaw.h | 64 + media/libstagefright/codecs/raw/exports.lds | 5 + .../codecs/vorbis/dec/Android.bp | 38 + .../codecs/vorbis/dec/MODULE_LICENSE_APACHE2 | 0 media/libstagefright/codecs/vorbis/dec/NOTICE | 190 ++ .../codecs/vorbis/dec/SoftVorbis.cpp | 644 +++++++ .../codecs/vorbis/dec/SoftVorbis.h | 83 + .../codecs/vorbis/dec/exports.lds | 5 + .../libstagefright/codecs/xaacdec/Android.bp | 36 + .../codecs/xaacdec/SoftXAAC.cpp | 1702 +++++++++++++++++ .../libstagefright/codecs/xaacdec/SoftXAAC.h | 130 ++ media/libstagefright/omx/Android.bp | 32 + media/libstagefright/omx/OMXStore.cpp | 1 - services/mediacodec/Android.bp | 25 + 117 files changed, 21477 insertions(+), 1 deletion(-) create mode 100644 media/libstagefright/codecs/aacdec/Android.bp create mode 100644 media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp create mode 100644 media/libstagefright/codecs/aacdec/DrcPresModeWrap.h create mode 100644 media/libstagefright/codecs/aacdec/MODULE_LICENSE_APACHE2 create mode 100644 media/libstagefright/codecs/aacdec/NOTICE create mode 100644 media/libstagefright/codecs/aacdec/SoftAAC2.cpp create mode 100644 media/libstagefright/codecs/aacdec/SoftAAC2.h create mode 100644 media/libstagefright/codecs/aacdec/exports.lds create mode 100644 media/libstagefright/codecs/aacenc/Android.bp create mode 100644 media/libstagefright/codecs/aacenc/MODULE_LICENSE_APACHE2 create mode 100644 media/libstagefright/codecs/aacenc/NOTICE create mode 100644 media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp create mode 100644 media/libstagefright/codecs/aacenc/SoftAACEncoder2.h create mode 100644 media/libstagefright/codecs/aacenc/exports.lds create mode 100644 media/libstagefright/codecs/aacenc/patent_disclaimer.txt create mode 100644 media/libstagefright/codecs/amrnb/dec/Android.bp create mode 100644 media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp create mode 100644 media/libstagefright/codecs/amrnb/dec/SoftAMR.h create mode 100644 media/libstagefright/codecs/amrnb/dec/exports.lds create mode 100644 media/libstagefright/codecs/amrnb/enc/Android.bp create mode 100644 media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp create mode 100644 media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.h create mode 100644 media/libstagefright/codecs/amrnb/enc/exports.lds create mode 100644 media/libstagefright/codecs/amrwbenc/Android.bp create mode 100644 media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.cpp create mode 100644 media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.h create mode 100644 media/libstagefright/codecs/amrwbenc/exports.lds create mode 100644 media/libstagefright/codecs/avcdec/Android.bp create mode 100644 media/libstagefright/codecs/avcdec/SoftAVCDec.cpp create mode 100644 media/libstagefright/codecs/avcdec/SoftAVCDec.h create mode 100644 media/libstagefright/codecs/avcdec/exports.lds create mode 100644 media/libstagefright/codecs/avcenc/Android.bp create mode 100644 media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp create mode 100644 media/libstagefright/codecs/avcenc/SoftAVCEnc.h create mode 100644 media/libstagefright/codecs/avcenc/exports.lds create mode 100644 media/libstagefright/codecs/flac/dec/Android.bp create mode 100644 media/libstagefright/codecs/flac/dec/MODULE_LICENSE_APACHE2 create mode 100644 media/libstagefright/codecs/flac/dec/NOTICE create mode 100644 media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp create mode 100644 media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h create mode 100644 media/libstagefright/codecs/flac/dec/exports.lds create mode 100644 media/libstagefright/codecs/flac/enc/Android.bp create mode 100644 media/libstagefright/codecs/flac/enc/MODULE_LICENSE_APACHE2 create mode 100644 media/libstagefright/codecs/flac/enc/NOTICE create mode 100644 media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp create mode 100644 media/libstagefright/codecs/flac/enc/SoftFlacEncoder.h create mode 100644 media/libstagefright/codecs/flac/enc/exports.lds create mode 100644 media/libstagefright/codecs/g711/dec/Android.bp create mode 100644 media/libstagefright/codecs/g711/dec/MODULE_LICENSE_APACHE2 create mode 100644 media/libstagefright/codecs/g711/dec/NOTICE create mode 100644 media/libstagefright/codecs/g711/dec/SoftG711.cpp create mode 100644 media/libstagefright/codecs/g711/dec/SoftG711.h create mode 100644 media/libstagefright/codecs/g711/dec/exports.lds create mode 100644 media/libstagefright/codecs/gsm/dec/Android.bp create mode 100644 media/libstagefright/codecs/gsm/dec/MODULE_LICENSE_APACHE2 create mode 100644 media/libstagefright/codecs/gsm/dec/NOTICE create mode 100644 media/libstagefright/codecs/gsm/dec/SoftGSM.cpp create mode 100644 media/libstagefright/codecs/gsm/dec/SoftGSM.h create mode 100644 media/libstagefright/codecs/gsm/dec/exports.lds create mode 100644 media/libstagefright/codecs/hevcdec/Android.bp create mode 100644 media/libstagefright/codecs/hevcdec/SoftHEVC.cpp create mode 100644 media/libstagefright/codecs/hevcdec/SoftHEVC.h create mode 100644 media/libstagefright/codecs/hevcdec/exports.lds create mode 100644 media/libstagefright/codecs/m4v_h263/dec/Android.bp create mode 100644 media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp create mode 100644 media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h create mode 100644 media/libstagefright/codecs/m4v_h263/dec/exports.lds create mode 100644 media/libstagefright/codecs/m4v_h263/enc/Android.bp create mode 100644 media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp create mode 100644 media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h create mode 100644 media/libstagefright/codecs/m4v_h263/enc/exports.lds create mode 100644 media/libstagefright/codecs/mp3dec/Android.bp create mode 100644 media/libstagefright/codecs/mp3dec/SoftMP3.cpp create mode 100644 media/libstagefright/codecs/mp3dec/SoftMP3.h create mode 100644 media/libstagefright/codecs/mp3dec/exports.lds create mode 100644 media/libstagefright/codecs/mpeg2dec/Android.bp create mode 100644 media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp create mode 100644 media/libstagefright/codecs/mpeg2dec/SoftMPEG2.h create mode 100644 media/libstagefright/codecs/mpeg2dec/exports.lds create mode 100644 media/libstagefright/codecs/on2/dec/Android.bp create mode 100644 media/libstagefright/codecs/on2/dec/MODULE_LICENSE_APACHE2 create mode 100644 media/libstagefright/codecs/on2/dec/NOTICE create mode 100644 media/libstagefright/codecs/on2/dec/SoftVPX.cpp create mode 100644 media/libstagefright/codecs/on2/dec/SoftVPX.h create mode 100644 media/libstagefright/codecs/on2/dec/exports.lds create mode 100644 media/libstagefright/codecs/on2/enc/Android.bp create mode 100644 media/libstagefright/codecs/on2/enc/MODULE_LICENSE_APACHE2 create mode 100644 media/libstagefright/codecs/on2/enc/NOTICE create mode 100644 media/libstagefright/codecs/on2/enc/SoftVP8Encoder.cpp create mode 100644 media/libstagefright/codecs/on2/enc/SoftVP8Encoder.h create mode 100644 media/libstagefright/codecs/on2/enc/SoftVP9Encoder.cpp create mode 100644 media/libstagefright/codecs/on2/enc/SoftVP9Encoder.h create mode 100644 media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp create mode 100644 media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h create mode 100644 media/libstagefright/codecs/on2/enc/exports.lds create mode 100644 media/libstagefright/codecs/opus/dec/Android.bp create mode 100644 media/libstagefright/codecs/opus/dec/SoftOpus.cpp create mode 100644 media/libstagefright/codecs/opus/dec/SoftOpus.h create mode 100644 media/libstagefright/codecs/opus/dec/exports.lds create mode 100644 media/libstagefright/codecs/raw/Android.bp create mode 100644 media/libstagefright/codecs/raw/MODULE_LICENSE_APACHE2 create mode 100644 media/libstagefright/codecs/raw/NOTICE create mode 100644 media/libstagefright/codecs/raw/SoftRaw.cpp create mode 100644 media/libstagefright/codecs/raw/SoftRaw.h create mode 100644 media/libstagefright/codecs/raw/exports.lds create mode 100644 media/libstagefright/codecs/vorbis/dec/Android.bp create mode 100644 media/libstagefright/codecs/vorbis/dec/MODULE_LICENSE_APACHE2 create mode 100644 media/libstagefright/codecs/vorbis/dec/NOTICE create mode 100644 media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp create mode 100644 media/libstagefright/codecs/vorbis/dec/SoftVorbis.h create mode 100644 media/libstagefright/codecs/vorbis/dec/exports.lds create mode 100644 media/libstagefright/codecs/xaacdec/Android.bp create mode 100644 media/libstagefright/codecs/xaacdec/SoftXAAC.cpp create mode 100644 media/libstagefright/codecs/xaacdec/SoftXAAC.h diff --git a/media/libstagefright/codecs/aacdec/Android.bp b/media/libstagefright/codecs/aacdec/Android.bp new file mode 100644 index 0000000..5ab49a7 --- /dev/null +++ b/media/libstagefright/codecs/aacdec/Android.bp @@ -0,0 +1,44 @@ +package { + default_applicable_licenses: [ + "frameworks_av_media_libstagefright_codecs_aacdec_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_av_media_libstagefright_codecs_aacdec_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + +cc_library_shared { + name: "libstagefright_soft_aacdec", + defaults: ["libstagefright_softomx-defaults"], + + srcs: [ + "SoftAAC2.cpp", + "DrcPresModeWrap.cpp", + ], + + version_script: "exports.lds", + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + "unsigned-integer-overflow", + ], + cfi: true, + }, + + static_libs: ["libFraunhoferAAC"], + + shared_libs: [ + "libcutils", + ], +} diff --git a/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp new file mode 100644 index 0000000..157cab6 --- /dev/null +++ b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp @@ -0,0 +1,371 @@ +/* + * Copyright (C) 2014 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. + */ +#include "DrcPresModeWrap.h" + +#include + +#define LOG_TAG "SoftAAC2_DrcWrapper" +//#define LOG_NDEBUG 0 +#include + +//#define DRC_PRES_MODE_WRAP_DEBUG + +#define GPM_ENCODER_TARGET_LEVEL 64 +#define MAX_TARGET_LEVEL 40 + +CDrcPresModeWrapper::CDrcPresModeWrapper() +{ + mDataUpdate = true; + + /* Data from streamInfo. */ + /* Initialized to the same values as in the aac decoder */ + mStreamPRL = -1; + mStreamDRCPresMode = -1; + mStreamNrAACChan = 0; + mStreamNrOutChan = 0; + + /* Desired values (set by user). */ + /* Initialized to the same values as in the aac decoder */ + mDesTarget = -1; + mDesAttFactor = 0; + mDesBoostFactor = 0; + mDesHeavy = 0; + + mEncoderTarget = -1; + + /* Values from last time. */ + mLastTarget = -2; + mLastAttFactor = -1; + mLastBoostFactor = -1; + mLastHeavy = 0; +} + +CDrcPresModeWrapper::~CDrcPresModeWrapper() +{ +} + +void +CDrcPresModeWrapper::setDecoderHandle(const HANDLE_AACDECODER handle) +{ + mHandleDecoder = handle; +} + +void +CDrcPresModeWrapper::submitStreamData(CStreamInfo* pStreamInfo) +{ + assert(pStreamInfo); + + if (mStreamPRL != pStreamInfo->drcProgRefLev) { + mStreamPRL = pStreamInfo->drcProgRefLev; + mDataUpdate = true; +#ifdef DRC_PRES_MODE_WRAP_DEBUG + ALOGV("DRC presentation mode wrapper: drcProgRefLev is %d\n", mStreamPRL); +#endif + } + + if (mStreamDRCPresMode != pStreamInfo->drcPresMode) { + mStreamDRCPresMode = pStreamInfo->drcPresMode; + mDataUpdate = true; +#ifdef DRC_PRES_MODE_WRAP_DEBUG + ALOGV("DRC presentation mode wrapper: drcPresMode is %d\n", mStreamDRCPresMode); +#endif + } + + if (mStreamNrAACChan != pStreamInfo->aacNumChannels) { + mStreamNrAACChan = pStreamInfo->aacNumChannels; + mDataUpdate = true; +#ifdef DRC_PRES_MODE_WRAP_DEBUG + ALOGV("DRC presentation mode wrapper: aacNumChannels is %d\n", mStreamNrAACChan); +#endif + } + + if (mStreamNrOutChan != pStreamInfo->numChannels) { + mStreamNrOutChan = pStreamInfo->numChannels; + mDataUpdate = true; +#ifdef DRC_PRES_MODE_WRAP_DEBUG + ALOGV("DRC presentation mode wrapper: numChannels is %d\n", mStreamNrOutChan); +#endif + } + + + + if (mStreamNrOutChan -31 dB + if ((mIsStereoDownmix == false) && (mIsMonoDownmix == false)) { + // no stereo or mono downmixing, calculated scaling of light DRC + /* use as little compression as possible */ + newAttFactor = 0; + newBoostFactor = 0; + if (mDesTarget PRL + if (mEncoderTarget < mDesTarget) { // if mEncoderTarget > target level + // mEncoderTarget > target level > PRL + int calcFactor; + float calcFactor_norm; + // 0.0f < calcFactor_norm < 1.0f + calcFactor_norm = (float)(mDesTarget - progRefLevel) / + (float)(mEncoderTarget - progRefLevel); + calcFactor = (int)(calcFactor_norm*127.0f); // 0 <= calcFactor < 127 + // calcFactor is the lower limit + newAttFactor = (calcFactor>newAttFactor) ? calcFactor : newAttFactor; + // new AttFactor will be always = calcFactor, as it is set to 0 before. + newBoostFactor = newAttFactor; + } else { + /* target level > mEncoderTarget > PRL */ + // newTDLimiterEnable = 1; + // the time domain limiter must always be active in this case. + // It is assumed that the framework activates it by default + newAttFactor = 127; + newBoostFactor = 127; + } + } else { // target level <= PRL + // no restrictions required + // newAttFactor = newAttFactor; + } + } else { // downmixing + // if target level > -23 dB or mono downmix + if ( (mDesTarget<92) || mIsMonoDownmix ) { + newHeavy = 1; + } else { + // we perform a downmix, so, we need at least full light DRC + newAttFactor = 127; + } + } + } else { // target level <= -31 dB + // playback -31 dB: light DRC only needed if we perform downmixing + if (mIsDownmix) { // we do downmixing + newAttFactor = 127; + } + } + } + else { // handle other used encoder target levels + + // Validation check: DRC presentation mode is only specified for max. 5.1 channels + if (mStreamNrAACChan > 6) { + drcPresMode = 0; + } + + switch (drcPresMode) { + case 0: + default: // presentation mode not indicated + { + + if (mDesTarget<124) { // if target level > -31 dB + // no stereo or mono downmixing + if ((mIsStereoDownmix == false) && (mIsMonoDownmix == false)) { + if (mDesTarget PRL + // newTDLimiterEnable = 1; + // the time domain limiter must always be active in this case. + // It is assumed that the framework activates it by default + newAttFactor = 127; // at least, use light compression + } else { // target level <= PRL + // no restrictions required + // newAttFactor = newAttFactor; + } + } else { // downmixing + // newTDLimiterEnable = 1; + // the time domain limiter must always be active in this case. + // It is assumed that the framework activates it by default + + // if target level > -23 dB or mono downmix + if ( (mDesTarget < 92) || mIsMonoDownmix ) { + newHeavy = 1; + } else{ + // we perform a downmix, so, we need at least full light DRC + newAttFactor = 127; + } + } + } else { // target level <= -31 dB + if (mIsDownmix) { // we do downmixing. + // newTDLimiterEnable = 1; + // the time domain limiter must always be active in this case. + // It is assumed that the framework activates it by default + newAttFactor = 127; + } + } + } + break; + + // Presentation mode 1 and 2 according to ETSI TS 101 154: + // Digital Video Broadcasting (DVB); Specification for the use of Video and Audio Coding + // in Broadcasting Applications based on the MPEG-2 Transport Stream, + // section C.5.4., "Decoding", and Table C.33 + // ISO DRC -> newHeavy = 0 (Use light compression, MPEG-style) + // Compression_value -> newHeavy = 1 (Use heavy compression, DVB-style) + // scaling restricted -> newAttFactor = 127 + + case 1: // presentation mode 1, Light:-31/Heavy:-23 + { + if (mDesTarget < 124) { // if target level > -31 dB + // playback up to -23 dB + newHeavy = 1; + } else { // target level <= -31 dB + // playback -31 dB + if (mIsDownmix) { // we do downmixing. + newAttFactor = 127; + } + } + } + break; + + case 2: // presentation mode 2, Light:-23/Heavy:-23 + { + if (mDesTarget < 124) { // if target level > -31 dB + // playback up to -23 dB + if (mIsMonoDownmix) { // if mono downmix + newHeavy = 1; + } else { + newHeavy = 0; + newAttFactor = 127; + } + } else { // target level <= -31 dB + // playback -31 dB + newHeavy = 0; + if (mIsDownmix) { // we do downmixing. + newAttFactor = 127; + } + } + } + break; + + } // switch() + } // if (mEncoderTarget == GPM_ENCODER_TARGET_LEVEL) + + // validation check again + if (newHeavy == 1) { + newBoostFactor=127; // not really needed as the same would be done by the decoder anyway + newAttFactor = 127; + } + + // update the decoder + if (newTarget != mLastTarget) { + aacDecoder_SetParam(mHandleDecoder, AAC_DRC_REFERENCE_LEVEL, newTarget); + mLastTarget = newTarget; +#ifdef DRC_PRES_MODE_WRAP_DEBUG + if (newTarget != mDesTarget) + ALOGV("DRC presentation mode wrapper: forced target level to %d (from %d)\n", newTarget, mDesTarget); + else + ALOGV("DRC presentation mode wrapper: set target level to %d\n", newTarget); +#endif + } + + if (newAttFactor != mLastAttFactor) { + aacDecoder_SetParam(mHandleDecoder, AAC_DRC_ATTENUATION_FACTOR, newAttFactor); + mLastAttFactor = newAttFactor; +#ifdef DRC_PRES_MODE_WRAP_DEBUG + if (newAttFactor != mDesAttFactor) + ALOGV("DRC presentation mode wrapper: forced attenuation factor to %d (from %d)\n", newAttFactor, mDesAttFactor); + else + ALOGV("DRC presentation mode wrapper: set attenuation factor to %d\n", newAttFactor); +#endif + } + + if (newBoostFactor != mLastBoostFactor) { + aacDecoder_SetParam(mHandleDecoder, AAC_DRC_BOOST_FACTOR, newBoostFactor); + mLastBoostFactor = newBoostFactor; +#ifdef DRC_PRES_MODE_WRAP_DEBUG + if (newBoostFactor != mDesBoostFactor) + ALOGV("DRC presentation mode wrapper: forced boost factor to %d (from %d)\n", + newBoostFactor, mDesBoostFactor); + else + ALOGV("DRC presentation mode wrapper: set boost factor to %d\n", newBoostFactor); +#endif + } + + if (newHeavy != mLastHeavy) { + aacDecoder_SetParam(mHandleDecoder, AAC_DRC_HEAVY_COMPRESSION, newHeavy); + mLastHeavy = newHeavy; +#ifdef DRC_PRES_MODE_WRAP_DEBUG + if (newHeavy != mDesHeavy) + ALOGV("DRC presentation mode wrapper: forced heavy compression to %d (from %d)\n", + newHeavy, mDesHeavy); + else + ALOGV("DRC presentation mode wrapper: set heavy compression to %d\n", newHeavy); +#endif + } + +#ifdef DRC_PRES_MODE_WRAP_DEBUG + ALOGV("DRC config: tgt_lev: %3d, cut: %3d, boost: %3d, heavy: %d\n", newTarget, + newAttFactor, newBoostFactor, newHeavy); +#endif + mDataUpdate = false; + + } // if (mDataUpdate) +} diff --git a/media/libstagefright/codecs/aacdec/DrcPresModeWrap.h b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.h new file mode 100644 index 0000000..f0b6cf2 --- /dev/null +++ b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2014 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. + */ +#pragma once +#include "aacdecoder_lib.h" + +typedef enum +{ + DRC_PRES_MODE_WRAP_DESIRED_TARGET = 0x0000, + DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR = 0x0001, + DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR = 0x0002, + DRC_PRES_MODE_WRAP_DESIRED_HEAVY = 0x0003, + DRC_PRES_MODE_WRAP_ENCODER_TARGET = 0x0004 +} DRC_PRES_MODE_WRAP_PARAM; + + +class CDrcPresModeWrapper { +public: + CDrcPresModeWrapper(); + ~CDrcPresModeWrapper(); + void setDecoderHandle(const HANDLE_AACDECODER handle); + void setParam(const DRC_PRES_MODE_WRAP_PARAM param, const int value); + void submitStreamData(CStreamInfo*); + void update(); + +protected: + HANDLE_AACDECODER mHandleDecoder; + int mDesTarget; + int mDesAttFactor; + int mDesBoostFactor; + int mDesHeavy; + + int mEncoderTarget; + + int mLastTarget; + int mLastAttFactor; + int mLastBoostFactor; + int mLastHeavy; + + SCHAR mStreamPRL; + SCHAR mStreamDRCPresMode; + INT mStreamNrAACChan; + INT mStreamNrOutChan; + + bool mIsDownmix; + bool mIsMonoDownmix; + bool mIsStereoDownmix; + + bool mDataUpdate; +}; diff --git a/media/libstagefright/codecs/aacdec/MODULE_LICENSE_APACHE2 b/media/libstagefright/codecs/aacdec/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/media/libstagefright/codecs/aacdec/NOTICE b/media/libstagefright/codecs/aacdec/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/media/libstagefright/codecs/aacdec/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, 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/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp new file mode 100644 index 0000000..92ec94f --- /dev/null +++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp @@ -0,0 +1,1255 @@ +/* + * Copyright (C) 2012 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 "SoftAAC2" +#include + +#include "SoftAAC2.h" +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define FILEREAD_MAX_LAYERS 2 + +#define DRC_DEFAULT_MOBILE_REF_LEVEL 64 /* 64*-0.25dB = -16 dB below full scale for mobile conf */ +#define DRC_DEFAULT_MOBILE_DRC_CUT 127 /* maximum compression of dynamic range for mobile conf */ +#define DRC_DEFAULT_MOBILE_DRC_BOOST 127 /* maximum compression of dynamic range for mobile conf */ +#define DRC_DEFAULT_MOBILE_DRC_HEAVY 1 /* switch for heavy compression for mobile conf */ +#define DRC_DEFAULT_MOBILE_DRC_EFFECT 3 /* MPEG-D DRC effect type; 3 => Limited playback range */ +#define DRC_DEFAULT_MOBILE_DRC_ALBUM 0 /* MPEG-D DRC album mode; 0 => album mode is disabled, 1 => album mode is enabled */ +#define DRC_DEFAULT_MOBILE_OUTPUT_LOUDNESS -1 /* decoder output loudness; -1 => the value is unknown, otherwise dB step value (e.g. 64 for -16 dB) */ +#define DRC_DEFAULT_MOBILE_ENC_LEVEL (-1) /* encoder target level; -1 => the value is unknown, otherwise dB step value (e.g. 64 for -16 dB) */ +#define MAX_CHANNEL_COUNT 8 /* maximum number of audio channels that can be decoded */ +// names of properties that can be used to override the default DRC settings +#define PROP_DRC_OVERRIDE_REF_LEVEL "aac_drc_reference_level" +#define PROP_DRC_OVERRIDE_CUT "aac_drc_cut" +#define PROP_DRC_OVERRIDE_BOOST "aac_drc_boost" +#define PROP_DRC_OVERRIDE_HEAVY "aac_drc_heavy" +#define PROP_DRC_OVERRIDE_ENC_LEVEL "aac_drc_enc_target_level" +#define PROP_DRC_OVERRIDE_EFFECT "ro.aac_drc_effect_type" + +namespace android { + +template +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +static const OMX_U32 kSupportedProfiles[] = { + OMX_AUDIO_AACObjectLC, + OMX_AUDIO_AACObjectHE, + OMX_AUDIO_AACObjectHE_PS, + OMX_AUDIO_AACObjectLD, + OMX_AUDIO_AACObjectELD, + OMX_AUDIO_AACObjectER_Scalable, + OMX_AUDIO_AACObjectXHE, +}; + +SoftAAC2::SoftAAC2( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mAACDecoder(NULL), + mStreamInfo(NULL), + mIsADTS(false), + mInputBufferCount(0), + mOutputBufferCount(0), + mSignalledError(false), + mLastInHeader(NULL), + mOutputPortSettingsChange(NONE) { + initPorts(); + CHECK_EQ(initDecoder(), (status_t)OK); +} + +SoftAAC2::~SoftAAC2() { + aacDecoder_Close(mAACDecoder); + delete[] mOutputDelayRingBuffer; +} + +void SoftAAC2::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumInputBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.audio.cMIMEType = const_cast("audio/aac"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingAAC; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumOutputBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 4096 * MAX_CHANNEL_COUNT; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.audio.cMIMEType = const_cast("audio/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); +} + +status_t SoftAAC2::initDecoder() { + ALOGV("initDecoder()"); + status_t status = UNKNOWN_ERROR; + mAACDecoder = aacDecoder_Open(TT_MP4_ADIF, /* num layers */ 1); + if (mAACDecoder != NULL) { + mStreamInfo = aacDecoder_GetStreamInfo(mAACDecoder); + if (mStreamInfo != NULL) { + status = OK; + } + } + + mEndOfInput = false; + mEndOfOutput = false; + mOutputDelayCompensated = 0; + mOutputDelayRingBufferSize = 2048 * MAX_CHANNEL_COUNT * kNumDelayBlocksMax; + mOutputDelayRingBuffer = new int16_t[mOutputDelayRingBufferSize]; + mOutputDelayRingBufferWritePos = 0; + mOutputDelayRingBufferReadPos = 0; + mOutputDelayRingBufferFilled = 0; + + if (mAACDecoder == NULL) { + ALOGE("AAC decoder is null. TODO: Can not call aacDecoder_SetParam in the following code"); + } + + //aacDecoder_SetParam(mAACDecoder, AAC_PCM_LIMITER_ENABLE, 0); + + //init DRC wrapper + mDrcWrap.setDecoderHandle(mAACDecoder); + mDrcWrap.submitStreamData(mStreamInfo); + + // for streams that contain metadata, use the mobile profile DRC settings unless overridden by platform properties + // TODO: change the DRC settings depending on audio output device type (HDMI, loadspeaker, headphone) + char value[PROPERTY_VALUE_MAX]; + // DRC_PRES_MODE_WRAP_DESIRED_TARGET + if (property_get(PROP_DRC_OVERRIDE_REF_LEVEL, value, NULL)) { + unsigned refLevel = atoi(value); + ALOGV("AAC decoder using desired DRC target reference level of %d instead of %d", refLevel, + DRC_DEFAULT_MOBILE_REF_LEVEL); + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_TARGET, refLevel); + } else { + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_TARGET, DRC_DEFAULT_MOBILE_REF_LEVEL); + } + // DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR + if (property_get(PROP_DRC_OVERRIDE_CUT, value, NULL)) { + unsigned cut = atoi(value); + ALOGV("AAC decoder using desired DRC attenuation factor of %d instead of %d", cut, + DRC_DEFAULT_MOBILE_DRC_CUT); + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR, cut); + } else { + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR, DRC_DEFAULT_MOBILE_DRC_CUT); + } + // DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR + if (property_get(PROP_DRC_OVERRIDE_BOOST, value, NULL)) { + unsigned boost = atoi(value); + ALOGV("AAC decoder using desired DRC boost factor of %d instead of %d", boost, + DRC_DEFAULT_MOBILE_DRC_BOOST); + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR, boost); + } else { + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR, DRC_DEFAULT_MOBILE_DRC_BOOST); + } + // DRC_PRES_MODE_WRAP_DESIRED_HEAVY + if (property_get(PROP_DRC_OVERRIDE_HEAVY, value, NULL)) { + unsigned heavy = atoi(value); + ALOGV("AAC decoder using desried DRC heavy compression switch of %d instead of %d", heavy, + DRC_DEFAULT_MOBILE_DRC_HEAVY); + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_HEAVY, heavy); + } else { + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_HEAVY, DRC_DEFAULT_MOBILE_DRC_HEAVY); + } + // DRC_PRES_MODE_WRAP_ENCODER_TARGET + if (property_get(PROP_DRC_OVERRIDE_ENC_LEVEL, value, NULL)) { + unsigned encoderRefLevel = atoi(value); + ALOGV("AAC decoder using encoder-side DRC reference level of %d instead of %d", + encoderRefLevel, DRC_DEFAULT_MOBILE_ENC_LEVEL); + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_ENCODER_TARGET, encoderRefLevel); + } else { + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_ENCODER_TARGET, DRC_DEFAULT_MOBILE_ENC_LEVEL); + } + // AAC_UNIDRC_SET_EFFECT + int32_t effectType = + property_get_int32(PROP_DRC_OVERRIDE_EFFECT, DRC_DEFAULT_MOBILE_DRC_EFFECT); + if (effectType < -1 || effectType > 8) { + effectType = DRC_DEFAULT_MOBILE_DRC_EFFECT; + } + ALOGV("AAC decoder using MPEG-D DRC effect type %d (default=%d)", + effectType, DRC_DEFAULT_MOBILE_DRC_EFFECT); + aacDecoder_SetParam(mAACDecoder, AAC_UNIDRC_SET_EFFECT, effectType); + // AAC_UNIDRC_ALBUM_MODE + int32_t albumMode = DRC_DEFAULT_MOBILE_DRC_ALBUM; + ALOGV("AAC decoder using MPEG-D Album mode value %d (default=%d)", albumMode, + DRC_DEFAULT_MOBILE_DRC_ALBUM); + aacDecoder_SetParam(mAACDecoder, AAC_UNIDRC_ALBUM_MODE, albumMode); + + // By default, the decoder creates a 5.1 channel downmix signal. + // For seven and eight channel input streams, enable 6.1 and 7.1 channel output + aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, -1); + + mDrcCompressMode = DRC_DEFAULT_MOBILE_DRC_HEAVY; + mDrcTargetRefLevel = DRC_DEFAULT_MOBILE_REF_LEVEL; + mDrcEncTargetLevel = DRC_DEFAULT_MOBILE_ENC_LEVEL; + mDrcBoostFactor = DRC_DEFAULT_MOBILE_DRC_BOOST; + mDrcAttenuationFactor = DRC_DEFAULT_MOBILE_DRC_CUT; + mDrcEffectType = DRC_DEFAULT_MOBILE_DRC_EFFECT; + mDrcAlbumMode = DRC_DEFAULT_MOBILE_DRC_ALBUM; + mDrcOutputLoudness = DRC_DEFAULT_MOBILE_OUTPUT_LOUDNESS; + + return status; +} + +OMX_ERRORTYPE SoftAAC2::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch ((OMX_U32) index) { + case OMX_IndexParamAudioPortFormat: + { + OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex > 0) { + return OMX_ErrorNoMore; + } + + formatParams->eEncoding = + (formatParams->nPortIndex == 0) + ? OMX_AUDIO_CodingAAC : OMX_AUDIO_CodingPCM; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAac: + { + OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams = + (OMX_AUDIO_PARAM_AACPROFILETYPE *)params; + + if (!isValidOMXParam(aacParams)) { + return OMX_ErrorBadParameter; + } + + if (aacParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + aacParams->nBitRate = 0; + aacParams->nAudioBandWidth = 0; + aacParams->nAACtools = 0; + aacParams->nAACERtools = 0; + aacParams->eAACProfile = OMX_AUDIO_AACObjectMain; + + aacParams->eAACStreamFormat = + mIsADTS + ? OMX_AUDIO_AACStreamFormatMP4ADTS + : OMX_AUDIO_AACStreamFormatMP4FF; + + aacParams->eChannelMode = OMX_AUDIO_ChannelModeStereo; + + if (!isConfigured()) { + aacParams->nChannels = 1; + aacParams->nSampleRate = 44100; + aacParams->nFrameLength = 0; + } else { + aacParams->nChannels = mStreamInfo->numChannels; + aacParams->nSampleRate = mStreamInfo->sampleRate; + aacParams->nFrameLength = mStreamInfo->frameSize; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + pcmParams->eNumData = OMX_NumericalDataSigned; + pcmParams->eEndian = OMX_EndianBig; + pcmParams->bInterleaved = OMX_TRUE; + pcmParams->nBitPerSample = 16; + pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; + pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; + pcmParams->eChannelMapping[2] = OMX_AUDIO_ChannelCF; + pcmParams->eChannelMapping[3] = OMX_AUDIO_ChannelLFE; + pcmParams->eChannelMapping[4] = OMX_AUDIO_ChannelLS; + pcmParams->eChannelMapping[5] = OMX_AUDIO_ChannelRS; + + if (!isConfigured()) { + pcmParams->nChannels = 1; + pcmParams->nSamplingRate = 44100; + } else { + pcmParams->nChannels = mStreamInfo->numChannels; + pcmParams->nSamplingRate = mStreamInfo->sampleRate; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioProfileQuerySupported: + { + OMX_AUDIO_PARAM_ANDROID_PROFILETYPE *profileParams = + (OMX_AUDIO_PARAM_ANDROID_PROFILETYPE *)params; + + if (!isValidOMXParam(profileParams)) { + return OMX_ErrorBadParameter; + } + + if (profileParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + if (profileParams->nProfileIndex >= NELEM(kSupportedProfiles)) { + return OMX_ErrorNoMore; + } + + profileParams->eProfile = + kSupportedProfiles[profileParams->nProfileIndex]; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAndroidAacDrcPresentation: + { + OMX_AUDIO_PARAM_ANDROID_AACDRCPRESENTATIONTYPE *aacPresParams = + (OMX_AUDIO_PARAM_ANDROID_AACDRCPRESENTATIONTYPE *)params; + + ALOGD("get OMX_IndexParamAudioAndroidAacDrcPresentation"); + + if (!isValidOMXParam(aacPresParams)) { + return OMX_ErrorBadParameter; + } + aacPresParams->nDrcEffectType = mDrcEffectType; + aacPresParams->nDrcAlbumMode = mDrcAlbumMode; + aacPresParams->nDrcBoost = mDrcBoostFactor; + aacPresParams->nDrcCut = mDrcAttenuationFactor; + aacPresParams->nHeavyCompression = mDrcCompressMode; + aacPresParams->nTargetReferenceLevel = mDrcTargetRefLevel; + aacPresParams->nEncodedTargetLevel = mDrcEncTargetLevel; + aacPresParams ->nDrcOutputLoudness = mDrcOutputLoudness; + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftAAC2::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch ((int)index) { + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (!isValidOMXParam(roleParams)) { + return OMX_ErrorBadParameter; + } + + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.aac", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPortFormat: + { + const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if ((formatParams->nPortIndex == 0 + && formatParams->eEncoding != OMX_AUDIO_CodingAAC) + || (formatParams->nPortIndex == 1 + && formatParams->eEncoding != OMX_AUDIO_CodingPCM)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAac: + { + const OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams = + (const OMX_AUDIO_PARAM_AACPROFILETYPE *)params; + + if (!isValidOMXParam(aacParams)) { + return OMX_ErrorBadParameter; + } + + if (aacParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + if (aacParams->eAACStreamFormat == OMX_AUDIO_AACStreamFormatMP4FF) { + mIsADTS = false; + } else if (aacParams->eAACStreamFormat + == OMX_AUDIO_AACStreamFormatMP4ADTS) { + mIsADTS = true; + } else { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAndroidAacDrcPresentation: + { + const OMX_AUDIO_PARAM_ANDROID_AACDRCPRESENTATIONTYPE *aacPresParams = + (const OMX_AUDIO_PARAM_ANDROID_AACDRCPRESENTATIONTYPE *)params; + + if (!isValidOMXParam(aacPresParams)) { + return OMX_ErrorBadParameter; + } + + // for the following parameters of the OMX_AUDIO_PARAM_AACPROFILETYPE structure, + // a value of -1 implies the parameter is not set by the application: + // nMaxOutputChannels -1 by default + // nDrcCut uses default platform properties, see initDecoder() + // nDrcBoost idem + // nHeavyCompression idem + // nTargetReferenceLevel idem + // nEncodedTargetLevel idem + if (aacPresParams->nMaxOutputChannels >= 0) { + int max; + if (aacPresParams->nMaxOutputChannels >= 8) { max = 8; } + else if (aacPresParams->nMaxOutputChannels >= 6) { max = 6; } + else if (aacPresParams->nMaxOutputChannels >= 2) { max = 2; } + else { + // -1 or 0: disable downmix, 1: mono + max = aacPresParams->nMaxOutputChannels; + } + ALOGV("set nMaxOutputChannels=%d", max); + aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, max); + } + if (aacPresParams->nDrcEffectType >= -1) { + ALOGV("set nDrcEffectType=%d", aacPresParams->nDrcEffectType); + aacDecoder_SetParam(mAACDecoder, AAC_UNIDRC_SET_EFFECT, aacPresParams->nDrcEffectType); + mDrcEffectType = aacPresParams->nDrcEffectType; + } + if (aacPresParams->nDrcAlbumMode >= -1) { + ALOGV("set nDrcAlbumMode=%d", aacPresParams->nDrcAlbumMode); + aacDecoder_SetParam(mAACDecoder, AAC_UNIDRC_ALBUM_MODE, + aacPresParams->nDrcAlbumMode); + mDrcAlbumMode = aacPresParams->nDrcAlbumMode; + } + bool updateDrcWrapper = false; + if (aacPresParams->nDrcBoost >= 0) { + ALOGV("set nDrcBoost=%d", aacPresParams->nDrcBoost); + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR, + aacPresParams->nDrcBoost); + updateDrcWrapper = true; + mDrcBoostFactor = aacPresParams->nDrcBoost; + } + if (aacPresParams->nDrcCut >= 0) { + ALOGV("set nDrcCut=%d", aacPresParams->nDrcCut); + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR, aacPresParams->nDrcCut); + updateDrcWrapper = true; + mDrcAttenuationFactor = aacPresParams->nDrcCut; + } + if (aacPresParams->nHeavyCompression >= 0) { + ALOGV("set nHeavyCompression=%d", aacPresParams->nHeavyCompression); + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_HEAVY, + aacPresParams->nHeavyCompression); + updateDrcWrapper = true; + mDrcCompressMode = aacPresParams->nHeavyCompression; + } + if (aacPresParams->nTargetReferenceLevel >= -1) { + ALOGV("set nTargetReferenceLevel=%d", aacPresParams->nTargetReferenceLevel); + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_TARGET, + aacPresParams->nTargetReferenceLevel); + updateDrcWrapper = true; + mDrcTargetRefLevel = aacPresParams->nTargetReferenceLevel; + } + if (aacPresParams->nEncodedTargetLevel >= 0) { + ALOGV("set nEncodedTargetLevel=%d", aacPresParams->nEncodedTargetLevel); + mDrcWrap.setParam(DRC_PRES_MODE_WRAP_ENCODER_TARGET, + aacPresParams->nEncodedTargetLevel); + updateDrcWrapper = true; + mDrcEncTargetLevel = aacPresParams->nEncodedTargetLevel; + } + if (aacPresParams->nPCMLimiterEnable >= 0) { + aacDecoder_SetParam(mAACDecoder, AAC_PCM_LIMITER_ENABLE, + (aacPresParams->nPCMLimiterEnable != 0)); + } + if (aacPresParams ->nDrcOutputLoudness != DRC_DEFAULT_MOBILE_OUTPUT_LOUDNESS) { + mDrcOutputLoudness = aacPresParams ->nDrcOutputLoudness; + } + if (updateDrcWrapper) { + mDrcWrap.update(); + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + const OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +bool SoftAAC2::isConfigured() const { + return mInputBufferCount > 0; +} + +bool SoftAAC2::outputDelayRingBufferPutSamples(INT_PCM *samples, int32_t numSamples) { + if (numSamples == 0) { + return true; + } + if (outputDelayRingBufferSpaceLeft() < numSamples) { + ALOGE("RING BUFFER WOULD OVERFLOW"); + return false; + } + if (mOutputDelayRingBufferWritePos + numSamples <= mOutputDelayRingBufferSize + && (mOutputDelayRingBufferReadPos <= mOutputDelayRingBufferWritePos + || mOutputDelayRingBufferReadPos > mOutputDelayRingBufferWritePos + numSamples)) { + // faster memcopy loop without checks, if the preconditions allow this + for (int32_t i = 0; i < numSamples; i++) { + mOutputDelayRingBuffer[mOutputDelayRingBufferWritePos++] = samples[i]; + } + + if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) { + mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize; + } + } else { + ALOGV("slow SoftAAC2::outputDelayRingBufferPutSamples()"); + + for (int32_t i = 0; i < numSamples; i++) { + mOutputDelayRingBuffer[mOutputDelayRingBufferWritePos] = samples[i]; + mOutputDelayRingBufferWritePos++; + if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) { + mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize; + } + } + } + mOutputDelayRingBufferFilled += numSamples; + return true; +} + +int32_t SoftAAC2::outputDelayRingBufferGetSamples(INT_PCM *samples, int32_t numSamples) { + + if (numSamples > mOutputDelayRingBufferFilled) { + ALOGE("RING BUFFER WOULD UNDERRUN"); + return -1; + } + + if (mOutputDelayRingBufferReadPos + numSamples <= mOutputDelayRingBufferSize + && (mOutputDelayRingBufferWritePos < mOutputDelayRingBufferReadPos + || mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferReadPos + numSamples)) { + // faster memcopy loop without checks, if the preconditions allow this + if (samples != 0) { + for (int32_t i = 0; i < numSamples; i++) { + samples[i] = mOutputDelayRingBuffer[mOutputDelayRingBufferReadPos++]; + } + } else { + mOutputDelayRingBufferReadPos += numSamples; + } + if (mOutputDelayRingBufferReadPos >= mOutputDelayRingBufferSize) { + mOutputDelayRingBufferReadPos -= mOutputDelayRingBufferSize; + } + } else { + ALOGV("slow SoftAAC2::outputDelayRingBufferGetSamples()"); + + for (int32_t i = 0; i < numSamples; i++) { + if (samples != 0) { + samples[i] = mOutputDelayRingBuffer[mOutputDelayRingBufferReadPos]; + } + mOutputDelayRingBufferReadPos++; + if (mOutputDelayRingBufferReadPos >= mOutputDelayRingBufferSize) { + mOutputDelayRingBufferReadPos -= mOutputDelayRingBufferSize; + } + } + } + mOutputDelayRingBufferFilled -= numSamples; + return numSamples; +} + +int32_t SoftAAC2::outputDelayRingBufferSamplesAvailable() { + return mOutputDelayRingBufferFilled; +} + +int32_t SoftAAC2::outputDelayRingBufferSpaceLeft() { + return mOutputDelayRingBufferSize - outputDelayRingBufferSamplesAvailable(); +} + + +void SoftAAC2::onQueueFilled(OMX_U32 /* portIndex */) { + if (mSignalledError || mOutputPortSettingsChange != NONE) { + return; + } + + UCHAR* inBuffer[FILEREAD_MAX_LAYERS]; + UINT inBufferLength[FILEREAD_MAX_LAYERS] = {0}; + UINT bytesValid[FILEREAD_MAX_LAYERS] = {0}; + + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + while ((!inQueue.empty() || mEndOfInput) && !outQueue.empty()) { + if (!inQueue.empty()) { + INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT]; + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + mEndOfInput = (inHeader->nFlags & OMX_BUFFERFLAG_EOS) != 0; + + if (mInputBufferCount == 0 && !(inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG)) { + ALOGE("first buffer should have OMX_BUFFERFLAG_CODECCONFIG set"); + inHeader->nFlags |= OMX_BUFFERFLAG_CODECCONFIG; + } + if ((inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) != 0) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + inBuffer[0] = inHeader->pBuffer + inHeader->nOffset; + inBufferLength[0] = inHeader->nFilledLen; + + AAC_DECODER_ERROR decoderErr = + aacDecoder_ConfigRaw(mAACDecoder, + inBuffer, + inBufferLength); + + if (decoderErr != AAC_DEC_OK) { + ALOGW("aacDecoder_ConfigRaw decoderErr = 0x%4.4x", decoderErr); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL); + return; + } + + mInputBufferCount++; + mOutputBufferCount++; // fake increase of outputBufferCount to keep the counters aligned + + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + mLastInHeader = NULL; + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + + // Only send out port settings changed event if both sample rate + // and numChannels are valid. + if (mStreamInfo->sampleRate && mStreamInfo->numChannels) { + ALOGI("Initially configuring decoder: %d Hz, %d channels", + mStreamInfo->sampleRate, + mStreamInfo->numChannels); + + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + } + return; + } + + if (inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + mLastInHeader = NULL; + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + continue; + } + + if (mIsADTS) { + size_t adtsHeaderSize = 0; + // skip 30 bits, aac_frame_length follows. + // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll????? + + const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset; + + bool signalError = false; + if (inHeader->nFilledLen < 7) { + ALOGE("Audio data too short to contain even the ADTS header. " + "Got %d bytes.", inHeader->nFilledLen); + hexdump(adtsHeader, inHeader->nFilledLen); + signalError = true; + } else { + bool protectionAbsent = (adtsHeader[1] & 1); + + unsigned aac_frame_length = + ((adtsHeader[3] & 3) << 11) + | (adtsHeader[4] << 3) + | (adtsHeader[5] >> 5); + + if (inHeader->nFilledLen < aac_frame_length) { + ALOGE("Not enough audio data for the complete frame. " + "Got %d bytes, frame size according to the ADTS " + "header is %u bytes.", + inHeader->nFilledLen, aac_frame_length); + hexdump(adtsHeader, inHeader->nFilledLen); + signalError = true; + } else { + adtsHeaderSize = (protectionAbsent ? 7 : 9); + if (aac_frame_length < adtsHeaderSize) { + signalError = true; + } else { + inBuffer[0] = (UCHAR *)adtsHeader + adtsHeaderSize; + inBufferLength[0] = aac_frame_length - adtsHeaderSize; + + inHeader->nOffset += adtsHeaderSize; + inHeader->nFilledLen -= adtsHeaderSize; + } + } + } + + if (signalError) { + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorStreamCorrupt, ERROR_MALFORMED, NULL); + return; + } + + // insert buffer size and time stamp + mBufferSizes.add(inBufferLength[0]); + if (mLastInHeader != inHeader) { + mBufferTimestamps.add(inHeader->nTimeStamp); + mLastInHeader = inHeader; + } else { + int64_t currentTime = mBufferTimestamps.top(); + currentTime += mStreamInfo->aacSamplesPerFrame * + 1000000LL / mStreamInfo->aacSampleRate; + mBufferTimestamps.add(currentTime); + } + } else { + inBuffer[0] = inHeader->pBuffer + inHeader->nOffset; + inBufferLength[0] = inHeader->nFilledLen; + mLastInHeader = inHeader; + mBufferTimestamps.add(inHeader->nTimeStamp); + mBufferSizes.add(inHeader->nFilledLen); + } + + // Fill and decode + bytesValid[0] = inBufferLength[0]; + + INT prevSampleRate = mStreamInfo->sampleRate; + INT prevNumChannels = mStreamInfo->numChannels; + + aacDecoder_Fill(mAACDecoder, + inBuffer, + inBufferLength, + bytesValid); + + // run DRC check + mDrcWrap.submitStreamData(mStreamInfo); + mDrcWrap.update(); + + UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0]; + inHeader->nFilledLen -= inBufferUsedLength; + inHeader->nOffset += inBufferUsedLength; + + AAC_DECODER_ERROR decoderErr; + int numLoops = 0; + do { + if (outputDelayRingBufferSpaceLeft() < + (mStreamInfo->frameSize * mStreamInfo->numChannels)) { + ALOGV("skipping decode: not enough space left in ringbuffer"); + break; + } + + int numConsumed = mStreamInfo->numTotalBytes; + decoderErr = aacDecoder_DecodeFrame(mAACDecoder, + tmpOutBuffer, + 2048 * MAX_CHANNEL_COUNT, + 0 /* flags */); + + numConsumed = mStreamInfo->numTotalBytes - numConsumed; + numLoops++; + + if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) { + break; + } + mDecodedSizes.add(numConsumed); + + if (decoderErr != AAC_DEC_OK) { + ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr); + } + + if (bytesValid[0] != 0) { + ALOGE("bytesValid[0] != 0 should never happen"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + + size_t numOutBytes = + mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels; + + if (decoderErr == AAC_DEC_OK) { + if (!outputDelayRingBufferPutSamples(tmpOutBuffer, + mStreamInfo->frameSize * mStreamInfo->numChannels)) { + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL); + return; + } + } else { + ALOGW("AAC decoder returned error 0x%4.4x, substituting silence", decoderErr); + + memset(tmpOutBuffer, 0, numOutBytes); // TODO: check for overflow + + if (!outputDelayRingBufferPutSamples(tmpOutBuffer, + mStreamInfo->frameSize * mStreamInfo->numChannels)) { + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL); + return; + } + + // Discard input buffer. + if (inHeader) { + inHeader->nFilledLen = 0; + } + + aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1); + + // After an error, replace the last entry in mBufferSizes with the sum of the + // last entries from mDecodedSizes to resynchronize the in/out lists. + mBufferSizes.pop(); + int n = 0; + for (int i = 0; i < numLoops; i++) { + n += mDecodedSizes.itemAt(mDecodedSizes.size() - numLoops + i); + } + mBufferSizes.add(n); + + // fall through + } + + if ( mDrcOutputLoudness != mStreamInfo->outputLoudness) { + ALOGD("update Loudness, before = %d, now = %d", mDrcOutputLoudness, mStreamInfo->outputLoudness); + mDrcOutputLoudness = mStreamInfo->outputLoudness; + } + + /* + * AAC+/eAAC+ streams can be signalled in two ways: either explicitly + * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual + * rate system and the sampling rate in the final output is actually + * doubled compared with the core AAC decoder sampling rate. + * + * Explicit signalling is done by explicitly defining SBR audio object + * type in the bitstream. Implicit signalling is done by embedding + * SBR content in AAC extension payload specific to SBR, and hence + * requires an AAC decoder to perform pre-checks on actual audio frames. + * + * Thus, we could not say for sure whether a stream is + * AAC+/eAAC+ until the first data frame is decoded. + */ + if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) { + if ((mInputBufferCount > 2) && (mOutputBufferCount <= 1)) { + ALOGW("Invalid AAC stream"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL); + return; + } + } else if ((mStreamInfo->sampleRate != prevSampleRate) || + (mStreamInfo->numChannels != prevNumChannels)) { + ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels", + prevSampleRate, mStreamInfo->sampleRate, + prevNumChannels, mStreamInfo->numChannels); + + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + + if (inHeader && inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + mInputBufferCount++; + inQueue.erase(inQueue.begin()); + mLastInHeader = NULL; + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } + return; + } + if (inHeader && inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + mInputBufferCount++; + inQueue.erase(inQueue.begin()); + mLastInHeader = NULL; + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } else { + ALOGV("inHeader->nFilledLen = %d", inHeader ? inHeader->nFilledLen : 0); + } + } while (decoderErr == AAC_DEC_OK); + } + + int32_t outputDelay = mStreamInfo->outputDelay * mStreamInfo->numChannels; + + if (!mEndOfInput && mOutputDelayCompensated < outputDelay) { + // discard outputDelay at the beginning + int32_t toCompensate = outputDelay - mOutputDelayCompensated; + int32_t discard = outputDelayRingBufferSamplesAvailable(); + if (discard > toCompensate) { + discard = toCompensate; + } + int32_t discarded = outputDelayRingBufferGetSamples(0, discard); + mOutputDelayCompensated += discarded; + continue; + } + + if (mEndOfInput) { + while (mOutputDelayCompensated > 0) { + // a buffer big enough for MAX_CHANNEL_COUNT channels of decoded HE-AAC + INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT]; + + // run DRC check + mDrcWrap.submitStreamData(mStreamInfo); + mDrcWrap.update(); + + AAC_DECODER_ERROR decoderErr = + aacDecoder_DecodeFrame(mAACDecoder, + tmpOutBuffer, + 2048 * MAX_CHANNEL_COUNT, + AACDEC_FLUSH); + if (decoderErr != AAC_DEC_OK) { + ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr); + } + + int32_t tmpOutBufferSamples = mStreamInfo->frameSize * mStreamInfo->numChannels; + if (tmpOutBufferSamples > mOutputDelayCompensated) { + tmpOutBufferSamples = mOutputDelayCompensated; + } + outputDelayRingBufferPutSamples(tmpOutBuffer, tmpOutBufferSamples); + mOutputDelayCompensated -= tmpOutBufferSamples; + } + } + + while (!outQueue.empty() + && outputDelayRingBufferSamplesAvailable() + >= mStreamInfo->frameSize * mStreamInfo->numChannels) { + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + if (outHeader->nOffset != 0) { + ALOGE("outHeader->nOffset != 0 is not handled"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + + INT_PCM *outBuffer = + reinterpret_cast(outHeader->pBuffer + outHeader->nOffset); + int samplesize = mStreamInfo->numChannels * sizeof(int16_t); + if (outHeader->nOffset + + mStreamInfo->frameSize * samplesize + > outHeader->nAllocLen) { + ALOGE("buffer overflow"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + + } + + int available = outputDelayRingBufferSamplesAvailable(); + int numSamples = outHeader->nAllocLen / sizeof(int16_t); + if (numSamples > available) { + numSamples = available; + } + int64_t currentTime = 0; + if (available) { + + int numFrames = numSamples / (mStreamInfo->frameSize * mStreamInfo->numChannels); + numSamples = numFrames * (mStreamInfo->frameSize * mStreamInfo->numChannels); + + ALOGV("%d samples available (%d), or %d frames", + numSamples, available, numFrames); + int64_t *nextTimeStamp = &mBufferTimestamps.editItemAt(0); + currentTime = *nextTimeStamp; + int32_t *currentBufLeft = &mBufferSizes.editItemAt(0); + for (int i = 0; i < numFrames; i++) { + int32_t decodedSize = mDecodedSizes.itemAt(0); + mDecodedSizes.removeAt(0); + ALOGV("decoded %d of %d", decodedSize, *currentBufLeft); + if (*currentBufLeft > decodedSize) { + // adjust/interpolate next time stamp + *currentBufLeft -= decodedSize; + *nextTimeStamp += mStreamInfo->aacSamplesPerFrame * + 1000000LL / mStreamInfo->aacSampleRate; + ALOGV("adjusted nextTimeStamp/size to %lld/%d", + (long long) *nextTimeStamp, *currentBufLeft); + } else { + // move to next timestamp in list + if (mBufferTimestamps.size() > 0) { + mBufferTimestamps.removeAt(0); + nextTimeStamp = &mBufferTimestamps.editItemAt(0); + mBufferSizes.removeAt(0); + currentBufLeft = &mBufferSizes.editItemAt(0); + ALOGV("moved to next time/size: %lld/%d", + (long long) *nextTimeStamp, *currentBufLeft); + } + // try to limit output buffer size to match input buffers + // (e.g when an input buffer contained 4 "sub" frames, output + // at most 4 decoded units in the corresponding output buffer) + // This is optional. Remove the next three lines to fill the output + // buffer with as many units as available. + numFrames = i + 1; + numSamples = numFrames * mStreamInfo->frameSize * mStreamInfo->numChannels; + break; + } + } + + ALOGV("getting %d from ringbuffer", numSamples); + int32_t ns = outputDelayRingBufferGetSamples(outBuffer, numSamples); + if (ns != numSamples) { + ALOGE("not a complete frame of samples available"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + } + + outHeader->nFilledLen = numSamples * sizeof(int16_t); + + if (mEndOfInput && !outQueue.empty() && outputDelayRingBufferSamplesAvailable() == 0) { + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + mEndOfOutput = true; + } else { + outHeader->nFlags = 0; + } + + outHeader->nTimeStamp = currentTime; + + mOutputBufferCount++; + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + ALOGV("out timestamp %lld / %d", outHeader->nTimeStamp, outHeader->nFilledLen); + notifyFillBufferDone(outHeader); + outHeader = NULL; + } + + if (mEndOfInput) { + int ringBufAvail = outputDelayRingBufferSamplesAvailable(); + if (!outQueue.empty() + && ringBufAvail < mStreamInfo->frameSize * mStreamInfo->numChannels) { + if (!mEndOfOutput) { + // send partial or empty block signaling EOS + mEndOfOutput = true; + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + INT_PCM *outBuffer = reinterpret_cast(outHeader->pBuffer + + outHeader->nOffset); + int32_t ns = outputDelayRingBufferGetSamples(outBuffer, ringBufAvail); + if (ns < 0) { + ns = 0; + } + outHeader->nFilledLen = ns; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + + outHeader->nTimeStamp = mBufferTimestamps.itemAt(0); + mBufferTimestamps.clear(); + mBufferSizes.clear(); + mDecodedSizes.clear(); + + mOutputBufferCount++; + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + } + break; // if outQueue not empty but no more output + } + } + } +} + +void SoftAAC2::onPortFlushCompleted(OMX_U32 portIndex) { + if (portIndex == 0) { + // Make sure that the next buffer output does not still + // depend on fragments from the last one decoded. + // drain all existing data + drainDecoder(); + mBufferTimestamps.clear(); + mBufferSizes.clear(); + mDecodedSizes.clear(); + mLastInHeader = NULL; + mEndOfInput = false; + } else { + int avail; + while ((avail = outputDelayRingBufferSamplesAvailable()) > 0) { + if (avail > mStreamInfo->frameSize * mStreamInfo->numChannels) { + avail = mStreamInfo->frameSize * mStreamInfo->numChannels; + } + int32_t ns = outputDelayRingBufferGetSamples(0, avail); + if (ns != avail) { + ALOGW("not a complete frame of samples available"); + break; + } + mOutputBufferCount++; + } + mOutputDelayRingBufferReadPos = mOutputDelayRingBufferWritePos; + mEndOfOutput = false; + } +} + +void SoftAAC2::drainDecoder() { + // flush decoder until outputDelay is compensated + while (mOutputDelayCompensated > 0) { + // a buffer big enough for MAX_CHANNEL_COUNT channels of decoded HE-AAC + INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT]; + + // run DRC check + mDrcWrap.submitStreamData(mStreamInfo); + mDrcWrap.update(); + + AAC_DECODER_ERROR decoderErr = + aacDecoder_DecodeFrame(mAACDecoder, + tmpOutBuffer, + 2048 * MAX_CHANNEL_COUNT, + AACDEC_FLUSH); + if (decoderErr != AAC_DEC_OK) { + ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr); + } + + int32_t tmpOutBufferSamples = mStreamInfo->frameSize * mStreamInfo->numChannels; + if (tmpOutBufferSamples > mOutputDelayCompensated) { + tmpOutBufferSamples = mOutputDelayCompensated; + } + outputDelayRingBufferPutSamples(tmpOutBuffer, tmpOutBufferSamples); + + mOutputDelayCompensated -= tmpOutBufferSamples; + } +} + +void SoftAAC2::onReset() { + drainDecoder(); + // reset the "configured" state + mInputBufferCount = 0; + mOutputBufferCount = 0; + mOutputDelayCompensated = 0; + mOutputDelayRingBufferWritePos = 0; + mOutputDelayRingBufferReadPos = 0; + mOutputDelayRingBufferFilled = 0; + mEndOfInput = false; + mEndOfOutput = false; + mBufferTimestamps.clear(); + mBufferSizes.clear(); + mDecodedSizes.clear(); + mLastInHeader = NULL; + + // To make the codec behave the same before and after a reset, we need to invalidate the + // streaminfo struct. This does that: + mStreamInfo->sampleRate = 0; // TODO: mStreamInfo is read only + + mSignalledError = false; + mOutputPortSettingsChange = NONE; +} + +void SoftAAC2::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { + if (portIndex != 1) { + return; + } + + switch (mOutputPortSettingsChange) { + case NONE: + break; + + case AWAITING_DISABLED: + { + CHECK(!enabled); + mOutputPortSettingsChange = AWAITING_ENABLED; + break; + } + + default: + { + CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); + CHECK(enabled); + mOutputPortSettingsChange = NONE; + break; + } + } +} + +} // namespace android + +__attribute__((cfi_canonical_jump_table)) +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftAAC2(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.h b/media/libstagefright/codecs/aacdec/SoftAAC2.h new file mode 100644 index 0000000..9f98aa1 --- /dev/null +++ b/media/libstagefright/codecs/aacdec/SoftAAC2.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2012 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_AAC_2_H_ +#define SOFT_AAC_2_H_ + +#include + +#include "aacdecoder_lib.h" +#include "DrcPresModeWrap.h" + +namespace android { + +struct SoftAAC2 : public SimpleSoftOMXComponent { + SoftAAC2(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftAAC2(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + virtual void onReset(); + +private: + enum { + kNumInputBuffers = 4, + kNumOutputBuffers = 4, + kNumDelayBlocksMax = 8, + }; + + HANDLE_AACDECODER mAACDecoder; + CStreamInfo *mStreamInfo; + bool mIsADTS; + bool mIsFirst; + size_t mInputBufferCount; + size_t mOutputBufferCount; + bool mSignalledError; + OMX_BUFFERHEADERTYPE *mLastInHeader; + Vector mBufferSizes; + Vector mDecodedSizes; + Vector mBufferTimestamps; + + CDrcPresModeWrapper mDrcWrap; + + enum { + NONE, + AWAITING_DISABLED, + AWAITING_ENABLED + } mOutputPortSettingsChange; + + void initPorts(); + status_t initDecoder(); + bool isConfigured() const; + void drainDecoder(); + +// delay compensation + bool mEndOfInput; + bool mEndOfOutput; + int32_t mOutputDelayCompensated; + int32_t mOutputDelayRingBufferSize; + int16_t *mOutputDelayRingBuffer; + int32_t mOutputDelayRingBufferWritePos; + int32_t mOutputDelayRingBufferReadPos; + int32_t mOutputDelayRingBufferFilled; + + //drc + int32_t mDrcCompressMode; + int32_t mDrcTargetRefLevel; + int32_t mDrcEncTargetLevel; + int32_t mDrcBoostFactor; + int32_t mDrcAttenuationFactor; + int32_t mDrcEffectType; + int32_t mDrcAlbumMode; + int32_t mDrcOutputLoudness; + + bool outputDelayRingBufferPutSamples(INT_PCM *samples, int numSamples); + int32_t outputDelayRingBufferGetSamples(INT_PCM *samples, int numSamples); + int32_t outputDelayRingBufferSamplesAvailable(); + int32_t outputDelayRingBufferSpaceLeft(); + + DISALLOW_EVIL_CONSTRUCTORS(SoftAAC2); +}; + +} // namespace android + +#endif // SOFT_AAC_2_H_ diff --git a/media/libstagefright/codecs/aacdec/exports.lds b/media/libstagefright/codecs/aacdec/exports.lds new file mode 100644 index 0000000..e24f3fa --- /dev/null +++ b/media/libstagefright/codecs/aacdec/exports.lds @@ -0,0 +1,5 @@ +{ + global: + _Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE; + local: *; +}; diff --git a/media/libstagefright/codecs/aacenc/Android.bp b/media/libstagefright/codecs/aacenc/Android.bp new file mode 100644 index 0000000..793125f --- /dev/null +++ b/media/libstagefright/codecs/aacenc/Android.bp @@ -0,0 +1,37 @@ +package { + default_applicable_licenses: [ + "frameworks_av_media_libstagefright_codecs_aacenc_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_av_media_libstagefright_codecs_aacenc_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + +cc_library_shared { + name: "libstagefright_soft_aacenc", + defaults: ["libstagefright_softomx-defaults"], + + srcs: ["SoftAACEncoder2.cpp"], + + version_script: "exports.lds", + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + "unsigned-integer-overflow", + ], + cfi: true, + }, + + static_libs: ["libFraunhoferAAC"], +} diff --git a/media/libstagefright/codecs/aacenc/MODULE_LICENSE_APACHE2 b/media/libstagefright/codecs/aacenc/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/media/libstagefright/codecs/aacenc/NOTICE b/media/libstagefright/codecs/aacenc/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/media/libstagefright/codecs/aacenc/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, 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/aacenc/SoftAACEncoder2.cpp b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp new file mode 100644 index 0000000..90421b9 --- /dev/null +++ b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.cpp @@ -0,0 +1,740 @@ +/* + * Copyright (C) 2012 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 "SoftAACEncoder2" +#include +#include + +#include "SoftAACEncoder2.h" +#include +#include + +#include +#include +#include + +namespace android { + +template +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +static const OMX_U32 kSupportedProfiles[] = { + OMX_AUDIO_AACObjectLC, + OMX_AUDIO_AACObjectHE, + OMX_AUDIO_AACObjectHE_PS, + OMX_AUDIO_AACObjectLD, + OMX_AUDIO_AACObjectELD, +}; + +SoftAACEncoder2::SoftAACEncoder2( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mAACEncoder(NULL), + mNumChannels(1), + mSampleRate(44100), + mBitRate(0), + mSBRMode(-1), + mSBRRatio(0), + mAACProfile(OMX_AUDIO_AACObjectLC), + mSentCodecSpecificData(false), + mInputSize(0), + mInputFrame(NULL), + mAllocatedFrameSize(0), + mInputTimeUs(-1LL), + mSawInputEOS(false), + mSignalledError(false) { + initPorts(); + CHECK_EQ(initEncoder(), (status_t)OK); + setAudioParams(); +} + +SoftAACEncoder2::~SoftAACEncoder2() { + aacEncClose(&mAACEncoder); + + onReset(); +} + +void SoftAACEncoder2::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = kNumSamplesPerFrame * sizeof(int16_t) * 2; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.audio.cMIMEType = const_cast("audio/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.audio.cMIMEType = const_cast("audio/aac"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingAAC; + + addPort(def); +} + +status_t SoftAACEncoder2::initEncoder() { + if (AACENC_OK != aacEncOpen(&mAACEncoder, 0, 0)) { + ALOGE("Failed to init AAC encoder"); + return UNKNOWN_ERROR; + } + return OK; +} + +OMX_ERRORTYPE SoftAACEncoder2::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch ((OMX_U32) index) { + case OMX_IndexParamAudioPortFormat: + { + OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex > 0) { + return OMX_ErrorNoMore; + } + + formatParams->eEncoding = + (formatParams->nPortIndex == 0) + ? OMX_AUDIO_CodingPCM : OMX_AUDIO_CodingAAC; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAac: + { + OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams = + (OMX_AUDIO_PARAM_AACPROFILETYPE *)params; + + if (!isValidOMXParam(aacParams)) { + return OMX_ErrorBadParameter; + } + + if (aacParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + aacParams->nBitRate = mBitRate; + aacParams->nAudioBandWidth = 0; + aacParams->nAACtools = 0; + aacParams->nAACERtools = 0; + aacParams->eAACProfile = (OMX_AUDIO_AACPROFILETYPE) mAACProfile; + aacParams->eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF; + aacParams->eChannelMode = OMX_AUDIO_ChannelModeStereo; + + aacParams->nChannels = mNumChannels; + aacParams->nSampleRate = mSampleRate; + aacParams->nFrameLength = 0; + + switch (mSBRMode) { + case 1: // sbr on + switch (mSBRRatio) { + case 0: + // set both OMX AAC tool flags + aacParams->nAACtools |= OMX_AUDIO_AACToolAndroidSSBR; + aacParams->nAACtools |= OMX_AUDIO_AACToolAndroidDSBR; + break; + case 1: + // set single-rate SBR active + aacParams->nAACtools |= OMX_AUDIO_AACToolAndroidSSBR; + aacParams->nAACtools &= ~OMX_AUDIO_AACToolAndroidDSBR; + break; + case 2: + // set dual-rate SBR active + aacParams->nAACtools &= ~OMX_AUDIO_AACToolAndroidSSBR; + aacParams->nAACtools |= OMX_AUDIO_AACToolAndroidDSBR; + break; + default: + ALOGE("invalid SBR ratio %d", mSBRRatio); + TRESPASS(); + } + break; + case 0: // sbr off + case -1: // sbr undefined + aacParams->nAACtools &= ~OMX_AUDIO_AACToolAndroidSSBR; + aacParams->nAACtools &= ~OMX_AUDIO_AACToolAndroidDSBR; + break; + default: + ALOGE("invalid SBR mode %d", mSBRMode); + TRESPASS(); + } + + + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + pcmParams->eNumData = OMX_NumericalDataSigned; + pcmParams->eEndian = OMX_EndianBig; + pcmParams->bInterleaved = OMX_TRUE; + pcmParams->nBitPerSample = 16; + pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; + pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; + + pcmParams->nChannels = mNumChannels; + pcmParams->nSamplingRate = mSampleRate; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioProfileQuerySupported: + { + OMX_AUDIO_PARAM_ANDROID_PROFILETYPE *profileParams = + (OMX_AUDIO_PARAM_ANDROID_PROFILETYPE *)params; + + if (!isValidOMXParam(profileParams)) { + return OMX_ErrorBadParameter; + } + + if (profileParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + if (profileParams->nProfileIndex >= NELEM(kSupportedProfiles)) { + return OMX_ErrorNoMore; + } + + profileParams->eProfile = + kSupportedProfiles[profileParams->nProfileIndex]; + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftAACEncoder2::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (!isValidOMXParam(roleParams)) { + return OMX_ErrorBadParameter; + } + + if (strncmp((const char *)roleParams->cRole, + "audio_encoder.aac", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPortFormat: + { + const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if ((formatParams->nPortIndex == 0 + && formatParams->eEncoding != OMX_AUDIO_CodingPCM) + || (formatParams->nPortIndex == 1 + && formatParams->eEncoding != OMX_AUDIO_CodingAAC)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAac: + { + OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams = + (OMX_AUDIO_PARAM_AACPROFILETYPE *)params; + + if (!isValidOMXParam(aacParams)) { + return OMX_ErrorBadParameter; + } + + if (aacParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + mBitRate = aacParams->nBitRate; + mNumChannels = aacParams->nChannels; + mSampleRate = aacParams->nSampleRate; + if (aacParams->eAACProfile != OMX_AUDIO_AACObjectNull) { + mAACProfile = aacParams->eAACProfile; + } + + if (!(aacParams->nAACtools & OMX_AUDIO_AACToolAndroidSSBR) + && !(aacParams->nAACtools & OMX_AUDIO_AACToolAndroidDSBR)) { + mSBRMode = 0; + mSBRRatio = 0; + } else if ((aacParams->nAACtools & OMX_AUDIO_AACToolAndroidSSBR) + && !(aacParams->nAACtools & OMX_AUDIO_AACToolAndroidDSBR)) { + mSBRMode = 1; + mSBRRatio = 1; + } else if (!(aacParams->nAACtools & OMX_AUDIO_AACToolAndroidSSBR) + && (aacParams->nAACtools & OMX_AUDIO_AACToolAndroidDSBR)) { + mSBRMode = 1; + mSBRRatio = 2; + } else { + mSBRMode = -1; // codec default sbr mode + mSBRRatio = 0; + } + + if (setAudioParams() != OK) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + mNumChannels = pcmParams->nChannels; + mSampleRate = pcmParams->nSamplingRate; + if (setAudioParams() != OK) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +static CHANNEL_MODE getChannelMode(OMX_U32 nChannels) { + CHANNEL_MODE chMode = MODE_INVALID; + switch (nChannels) { + case 1: chMode = MODE_1; break; + case 2: chMode = MODE_2; break; + case 3: chMode = MODE_1_2; break; + case 4: chMode = MODE_1_2_1; break; + case 5: chMode = MODE_1_2_2; break; + case 6: chMode = MODE_1_2_2_1; break; + default: chMode = MODE_INVALID; + } + return chMode; +} + +static AUDIO_OBJECT_TYPE getAOTFromProfile(OMX_U32 profile) { + if (profile == OMX_AUDIO_AACObjectLC) { + return AOT_AAC_LC; + } else if (profile == OMX_AUDIO_AACObjectHE) { + return AOT_SBR; + } else if (profile == OMX_AUDIO_AACObjectHE_PS) { + return AOT_PS; + } else if (profile == OMX_AUDIO_AACObjectLD) { + return AOT_ER_AAC_LD; + } else if (profile == OMX_AUDIO_AACObjectELD) { + return AOT_ER_AAC_ELD; + } else { + ALOGW("Unsupported AAC profile - defaulting to AAC-LC"); + return AOT_AAC_LC; + } +} + +status_t SoftAACEncoder2::setAudioParams() { + // We call this whenever sample rate, number of channels, bitrate or SBR mode change + // in reponse to setParameter calls. + + ALOGV("setAudioParams: %u Hz, %u channels, %u bps, %i sbr mode, %i sbr ratio", + mSampleRate, mNumChannels, mBitRate, mSBRMode, mSBRRatio); + + if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_AOT, + getAOTFromProfile(mAACProfile))) { + ALOGE("Failed to set AAC encoder parameters"); + return UNKNOWN_ERROR; + } + + if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_SAMPLERATE, mSampleRate)) { + ALOGE("Failed to set AAC encoder parameters"); + return UNKNOWN_ERROR; + } + if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_BITRATE, mBitRate)) { + ALOGE("Failed to set AAC encoder parameters"); + return UNKNOWN_ERROR; + } + if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_CHANNELMODE, + getChannelMode(mNumChannels))) { + ALOGE("Failed to set AAC encoder parameters"); + return UNKNOWN_ERROR; + } + if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_TRANSMUX, TT_MP4_RAW)) { + ALOGE("Failed to set AAC encoder parameters"); + return UNKNOWN_ERROR; + } + + if (mSBRMode != -1 && mAACProfile == OMX_AUDIO_AACObjectELD) { + if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_SBR_MODE, mSBRMode)) { + ALOGE("Failed to set AAC encoder parameters"); + return UNKNOWN_ERROR; + } + } + + /* SBR ratio parameter configurations: + 0: Default configuration wherein SBR ratio is configured depending on audio object type by + the FDK. + 1: Downsampled SBR (default for ELD) + 2: Dualrate SBR (default for HE-AAC) + */ + if (AACENC_OK != aacEncoder_SetParam(mAACEncoder, AACENC_SBR_RATIO, mSBRRatio)) { + ALOGE("Failed to set AAC encoder parameters"); + return UNKNOWN_ERROR; + } + + return OK; +} + +void SoftAACEncoder2::onQueueFilled(OMX_U32 /* portIndex */) { + if (mSignalledError) { + return; + } + + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + if (!mSentCodecSpecificData) { + // The very first thing we want to output is the codec specific + // data. It does not require any input data but we will need an + // output buffer to store it in. + + if (outQueue.empty()) { + return; + } + + if (AACENC_OK != aacEncEncode(mAACEncoder, NULL, NULL, NULL, NULL)) { + ALOGE("Unable to initialize encoder for profile / sample-rate / bit-rate / channels"); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } + + OMX_U32 actualBitRate = aacEncoder_GetParam(mAACEncoder, AACENC_BITRATE); + if (mBitRate != actualBitRate) { + ALOGW("Requested bitrate %u unsupported, using %u", mBitRate, actualBitRate); + } + + AACENC_InfoStruct encInfo; + if (AACENC_OK != aacEncInfo(mAACEncoder, &encInfo)) { + ALOGE("Failed to get AAC encoder info"); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + if (outHeader->nOffset + encInfo.confSize > outHeader->nAllocLen) { + ALOGE("b/34617444"); + android_errorWriteLog(0x534e4554,"34617444"); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } + + outHeader->nFilledLen = encInfo.confSize; + outHeader->nFlags = OMX_BUFFERFLAG_CODECCONFIG; + + uint8_t *out = outHeader->pBuffer + outHeader->nOffset; + memcpy(out, encInfo.confBuf, encInfo.confSize); + + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + + mSentCodecSpecificData = true; + } + + size_t numBytesPerInputFrame = + mNumChannels * kNumSamplesPerFrame * sizeof(int16_t); + + // Limit input size so we only get one ELD frame + if (mAACProfile == OMX_AUDIO_AACObjectELD && numBytesPerInputFrame > 512) { + numBytesPerInputFrame = 512; + } + + for (;;) { + // We do the following until we run out of buffers. + + while (mInputSize < numBytesPerInputFrame) { + // As long as there's still input data to be read we + // will drain "kNumSamplesPerFrame * mNumChannels" samples + // into the "mInputFrame" buffer and then encode those + // as a unit into an output buffer. + + if (mSawInputEOS || inQueue.empty()) { + return; + } + + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + const void *inData = inHeader->pBuffer + inHeader->nOffset; + + size_t copy = numBytesPerInputFrame - mInputSize; + if (copy > inHeader->nFilledLen) { + copy = inHeader->nFilledLen; + } + + if (mInputFrame == NULL) { + mInputFrame = new int16_t[numBytesPerInputFrame / sizeof(int16_t)]; + mAllocatedFrameSize = numBytesPerInputFrame; + } else if (mAllocatedFrameSize != numBytesPerInputFrame) { + ALOGE("b/34621073: changed size from %d to %d", + (int)mAllocatedFrameSize, (int)numBytesPerInputFrame); + android_errorWriteLog(0x534e4554,"34621073"); + delete mInputFrame; + mInputFrame = new int16_t[numBytesPerInputFrame / sizeof(int16_t)]; + mAllocatedFrameSize = numBytesPerInputFrame; + + } + + if (mInputSize == 0) { + mInputTimeUs = inHeader->nTimeStamp; + } + + memcpy((uint8_t *)mInputFrame + mInputSize, inData, copy); + mInputSize += copy; + + inHeader->nOffset += copy; + inHeader->nFilledLen -= copy; + + // "Time" on the input buffer has in effect advanced by the + // number of audio frames we just advanced nOffset by. + inHeader->nTimeStamp += + (copy * 1000000LL / mSampleRate) + / (mNumChannels * sizeof(int16_t)); + + if (inHeader->nFilledLen == 0) { + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + mSawInputEOS = true; + + // Pad any remaining data with zeroes. + memset((uint8_t *)mInputFrame + mInputSize, + 0, + numBytesPerInputFrame - mInputSize); + + mInputSize = numBytesPerInputFrame; + } + + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + inData = NULL; + inHeader = NULL; + inInfo = NULL; + } + } + + // At this point we have all the input data necessary to encode + // a single frame, all we need is an output buffer to store the result + // in. + + if (outQueue.empty()) { + return; + } + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + uint8_t *outPtr = (uint8_t *)outHeader->pBuffer + outHeader->nOffset; + size_t outAvailable = outHeader->nAllocLen - outHeader->nOffset; + + AACENC_InArgs inargs; + AACENC_OutArgs outargs; + memset(&inargs, 0, sizeof(inargs)); + memset(&outargs, 0, sizeof(outargs)); + inargs.numInSamples = numBytesPerInputFrame / sizeof(int16_t); + + void* inBuffer[] = { (unsigned char *)mInputFrame }; + INT inBufferIds[] = { IN_AUDIO_DATA }; + INT inBufferSize[] = { (INT)numBytesPerInputFrame }; + INT inBufferElSize[] = { sizeof(int16_t) }; + + AACENC_BufDesc inBufDesc; + inBufDesc.numBufs = sizeof(inBuffer) / sizeof(void*); + inBufDesc.bufs = (void**)&inBuffer; + inBufDesc.bufferIdentifiers = inBufferIds; + inBufDesc.bufSizes = inBufferSize; + inBufDesc.bufElSizes = inBufferElSize; + + void* outBuffer[] = { outPtr }; + INT outBufferIds[] = { OUT_BITSTREAM_DATA }; + INT outBufferSize[] = { 0 }; + INT outBufferElSize[] = { sizeof(UCHAR) }; + + AACENC_BufDesc outBufDesc; + outBufDesc.numBufs = sizeof(outBuffer) / sizeof(void*); + outBufDesc.bufs = (void**)&outBuffer; + outBufDesc.bufferIdentifiers = outBufferIds; + outBufDesc.bufSizes = outBufferSize; + outBufDesc.bufElSizes = outBufferElSize; + + // Encode the mInputFrame, which is treated as a modulo buffer + AACENC_ERROR encoderErr = AACENC_OK; + size_t nOutputBytes = 0; + + do { + memset(&outargs, 0, sizeof(outargs)); + + outBuffer[0] = outPtr; + outBufferSize[0] = outAvailable - nOutputBytes; + + encoderErr = aacEncEncode(mAACEncoder, + &inBufDesc, + &outBufDesc, + &inargs, + &outargs); + + if (encoderErr == AACENC_OK) { + outPtr += outargs.numOutBytes; + nOutputBytes += outargs.numOutBytes; + + if (outargs.numInSamples > 0) { + int numRemainingSamples = inargs.numInSamples - outargs.numInSamples; + if (numRemainingSamples > 0) { + memmove(mInputFrame, + &mInputFrame[outargs.numInSamples], + sizeof(int16_t) * numRemainingSamples); + } + inargs.numInSamples -= outargs.numInSamples; + } + } + } while (encoderErr == AACENC_OK && inargs.numInSamples > 0); + + outHeader->nFilledLen = nOutputBytes; + + outHeader->nFlags = OMX_BUFFERFLAG_ENDOFFRAME; + + if (mSawInputEOS) { + // We also tag this output buffer with EOS if it corresponds + // to the final input buffer. + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + } + + outHeader->nTimeStamp = mInputTimeUs; + +#if 0 + ALOGI("sending %d bytes of data (time = %lld us, flags = 0x%08lx)", + nOutputBytes, mInputTimeUs, outHeader->nFlags); + + hexdump(outHeader->pBuffer + outHeader->nOffset, outHeader->nFilledLen); +#endif + + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + + outHeader = NULL; + outInfo = NULL; + + mInputSize = 0; + } +} + +void SoftAACEncoder2::onReset() { + delete[] mInputFrame; + mInputFrame = NULL; + mInputSize = 0; + mAllocatedFrameSize = 0; + + mSentCodecSpecificData = false; + mInputTimeUs = -1LL; + mSawInputEOS = false; + mSignalledError = false; +} + +} // namespace android + +__attribute__((cfi_canonical_jump_table)) +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftAACEncoder2(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/aacenc/SoftAACEncoder2.h b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.h new file mode 100644 index 0000000..681dcf2 --- /dev/null +++ b/media/libstagefright/codecs/aacenc/SoftAACEncoder2.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2012 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_AAC_ENCODER_2_H_ + +#define SOFT_AAC_ENCODER_2_H_ + +#include + +#include "aacenc_lib.h" + +namespace android { + +struct SoftAACEncoder2 : public SimpleSoftOMXComponent { + SoftAACEncoder2( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftAACEncoder2(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + + virtual void onReset(); + +private: + enum { + kNumBuffers = 4, + kNumSamplesPerFrame = 1024 + }; + + HANDLE_AACENCODER mAACEncoder; + + OMX_U32 mNumChannels; + OMX_U32 mSampleRate; + OMX_U32 mBitRate; + OMX_S32 mSBRMode; + OMX_S32 mSBRRatio; + OMX_U32 mAACProfile; + + bool mSentCodecSpecificData; + size_t mInputSize; + int16_t *mInputFrame; + size_t mAllocatedFrameSize; + int64_t mInputTimeUs; + + bool mSawInputEOS; + + bool mSignalledError; + + void initPorts(); + status_t initEncoder(); + + status_t setAudioParams(); + + DISALLOW_EVIL_CONSTRUCTORS(SoftAACEncoder2); +}; + +} // namespace android + +#endif // SOFT_AAC_ENCODER_2_H_ diff --git a/media/libstagefright/codecs/aacenc/exports.lds b/media/libstagefright/codecs/aacenc/exports.lds new file mode 100644 index 0000000..e24f3fa --- /dev/null +++ b/media/libstagefright/codecs/aacenc/exports.lds @@ -0,0 +1,5 @@ +{ + global: + _Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE; + local: *; +}; diff --git a/media/libstagefright/codecs/aacenc/patent_disclaimer.txt b/media/libstagefright/codecs/aacenc/patent_disclaimer.txt new file mode 100644 index 0000000..b4bf11d --- /dev/null +++ b/media/libstagefright/codecs/aacenc/patent_disclaimer.txt @@ -0,0 +1,9 @@ + +THIS IS NOT A GRANT OF PATENT RIGHTS. + +Google makes no representation or warranty that the codecs for which +source code is made available hereunder are unencumbered by +third-party patents. Those intending to use this source code in +hardware or software products are advised that implementations of +these codecs, including in open source software or shareware, may +require patent licenses from the relevant patent holders. diff --git a/media/libstagefright/codecs/amrnb/dec/Android.bp b/media/libstagefright/codecs/amrnb/dec/Android.bp new file mode 100644 index 0000000..2c0954d --- /dev/null +++ b/media/libstagefright/codecs/amrnb/dec/Android.bp @@ -0,0 +1,39 @@ +//############################################################################### + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_av_media_libstagefright_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_av_media_libstagefright_license"], +} + +cc_library_shared { + name: "libstagefright_soft_amrdec", + defaults: ["libstagefright_softomx-defaults"], + + srcs: ["SoftAMR.cpp"], + + cflags: [ + "-DOSCL_IMPORT_REF=", + ], + + version_script: "exports.lds", + + //sanitize: { + // misc_undefined: [ + // "signed-integer-overflow", + // ], + //}, + //LOCAL_SANITIZE := signed-integer-overflow + + static_libs: [ + "libstagefright_amrnbdec", + "libstagefright_amrwbdec", + ], + + shared_libs: [ + "libstagefright_amrnb_common", + ], +} diff --git a/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp new file mode 100644 index 0000000..01da3f8 --- /dev/null +++ b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp @@ -0,0 +1,585 @@ +/* + * Copyright (C) 2011 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 "SoftAMR" +#include + +#include "SoftAMR.h" + +#include + +namespace android { + +template +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +SoftAMR::SoftAMR( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mMode(MODE_NARROW), + mState(NULL), + mDecoderBuf(NULL), + mDecoderCookie(NULL), + mInputBufferCount(0), + mAnchorTimeUs(0), + mNumSamplesOutput(0), + mSignalledError(false), + mOutputPortSettingsChange(NONE) { + if (!strcmp(name, "OMX.google.amrwb.decoder")) { + mMode = MODE_WIDE; + } else { + CHECK(!strcmp(name, "OMX.google.amrnb.decoder")); + } + + initPorts(); + CHECK_EQ(initDecoder(), (status_t)OK); +} + +SoftAMR::~SoftAMR() { + if (mMode == MODE_NARROW) { + GSMDecodeFrameExit(&mState); + mState = NULL; + } else { + free(mDecoderBuf); + mDecoderBuf = NULL; + + mState = NULL; + mDecoderCookie = NULL; + } +} + +void SoftAMR::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.audio.cMIMEType = + mMode == MODE_NARROW + ? const_cast("audio/amr") + : const_cast("audio/amrwb"); + + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingAMR; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + + def.nBufferSize = + (mMode == MODE_NARROW ? kNumSamplesPerFrameNB : kNumSamplesPerFrameWB) + * sizeof(int16_t); + + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.audio.cMIMEType = const_cast("audio/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); +} + +status_t SoftAMR::initDecoder() { + if (mMode == MODE_NARROW) { + Word16 err = GSMInitDecode(&mState, (Word8 *)"AMRNBDecoder"); + + if (err != 0) { + return UNKNOWN_ERROR; + } + } else { + int32_t memReq = pvDecoder_AmrWbMemRequirements(); + mDecoderBuf = malloc(memReq); + + pvDecoder_AmrWb_Init(&mState, mDecoderBuf, &mDecoderCookie); + } + + return OK; +} + +OMX_ERRORTYPE SoftAMR::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioPortFormat: + { + OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex > 0) { + return OMX_ErrorNoMore; + } + + formatParams->eEncoding = + (formatParams->nPortIndex == 0) + ? OMX_AUDIO_CodingAMR : OMX_AUDIO_CodingPCM; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAmr: + { + OMX_AUDIO_PARAM_AMRTYPE *amrParams = + (OMX_AUDIO_PARAM_AMRTYPE *)params; + + if (!isValidOMXParam(amrParams)) { + return OMX_ErrorBadParameter; + } + + if (amrParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + amrParams->nChannels = 1; + amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff; + amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF; + + if (!isConfigured()) { + amrParams->nBitRate = 0; + amrParams->eAMRBandMode = OMX_AUDIO_AMRBandModeUnused; + } else { + amrParams->nBitRate = 0; + amrParams->eAMRBandMode = + mMode == MODE_NARROW + ? OMX_AUDIO_AMRBandModeNB0 : OMX_AUDIO_AMRBandModeWB0; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + pcmParams->nChannels = 1; + pcmParams->eNumData = OMX_NumericalDataSigned; + pcmParams->eEndian = OMX_EndianBig; + pcmParams->bInterleaved = OMX_TRUE; + pcmParams->nBitPerSample = 16; + + pcmParams->nSamplingRate = + (mMode == MODE_NARROW) ? kSampleRateNB : kSampleRateWB; + + pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; + pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftAMR::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (!isValidOMXParam(roleParams)) { + return OMX_ErrorBadParameter; + } + + if (mMode == MODE_NARROW) { + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.amrnb", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + } else { + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.amrwb", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPortFormat: + { + const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if ((formatParams->nPortIndex == 0 + && formatParams->eEncoding != OMX_AUDIO_CodingAMR) + || (formatParams->nPortIndex == 1 + && formatParams->eEncoding != OMX_AUDIO_CodingPCM)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAmr: + { + const OMX_AUDIO_PARAM_AMRTYPE *aacParams = + (const OMX_AUDIO_PARAM_AMRTYPE *)params; + + if (!isValidOMXParam(aacParams)) { + return OMX_ErrorBadParameter; + } + + if (aacParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + const OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +bool SoftAMR::isConfigured() const { + return mInputBufferCount > 0; +} + +static size_t getFrameSize(unsigned FT) { + static const size_t kFrameSizeWB[10] = { + 132, 177, 253, 285, 317, 365, 397, 461, 477, 40 + }; + + if (FT >= 10) { + return 1; + } + + size_t frameSize = kFrameSizeWB[FT]; + + // Round up bits to bytes and add 1 for the header byte. + frameSize = (frameSize + 7) / 8 + 1; + + return frameSize; +} + +void SoftAMR::onQueueFilled(OMX_U32 /* portIndex */) { + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + if (mSignalledError || mOutputPortSettingsChange != NONE) { + return; + } + + while (!inQueue.empty() && !outQueue.empty()) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + if ((inHeader->nFlags & OMX_BUFFERFLAG_EOS) && inHeader->nFilledLen == 0) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + return; + } + + if (inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + notifyEmptyBufferDone(inHeader); + continue; + } + + if (inHeader->nOffset == 0) { + mAnchorTimeUs = inHeader->nTimeStamp; + mNumSamplesOutput = 0; + } + + const uint8_t *inputPtr = inHeader->pBuffer + inHeader->nOffset; + int32_t numBytesRead; + + if (mMode == MODE_NARROW) { + if (outHeader->nAllocLen < kNumSamplesPerFrameNB * sizeof(int16_t)) { + ALOGE("b/27662364: NB expected output buffer %zu bytes vs %u", + kNumSamplesPerFrameNB * sizeof(int16_t), outHeader->nAllocLen); + android_errorWriteLog(0x534e4554, "27662364"); + notify(OMX_EventError, OMX_ErrorOverflow, 0, NULL); + mSignalledError = true; + return; + } + + int16 mode = ((inputPtr[0] >> 3) & 0x0f); + // for WMF since MIME_IETF is used when calling AMRDecode. + size_t frameSize = WmfDecBytesPerFrame[mode] + 1; + + if (inHeader->nFilledLen < frameSize) { + ALOGE("b/27662364: expected %zu bytes vs %u", frameSize, inHeader->nFilledLen); + notify(OMX_EventError, OMX_ErrorStreamCorrupt, 0, NULL); + mSignalledError = true; + return; + } + + numBytesRead = + AMRDecode(mState, + (Frame_Type_3GPP)((inputPtr[0] >> 3) & 0x0f), + (UWord8 *)&inputPtr[1], + reinterpret_cast(outHeader->pBuffer), + MIME_IETF); + + if (numBytesRead == -1) { + ALOGE("PV AMR decoder AMRDecode() call failed"); + + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + + return; + } + + ++numBytesRead; // Include the frame type header byte. + + if (static_cast(numBytesRead) > inHeader->nFilledLen) { + // This is bad, should never have happened, but did. Abort now. + + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + + return; + } + } else { + if (outHeader->nAllocLen < kNumSamplesPerFrameWB * sizeof(int16_t)) { + ALOGE("b/27662364: WB expected output buffer %zu bytes vs %u", + kNumSamplesPerFrameWB * sizeof(int16_t), outHeader->nAllocLen); + android_errorWriteLog(0x534e4554, "27662364"); + notify(OMX_EventError, OMX_ErrorOverflow, 0, NULL); + mSignalledError = true; + return; + } + + int16 mode = ((inputPtr[0] >> 3) & 0x0f); + + if (mode >= 10 && mode <= 13) { + ALOGE("encountered illegal frame type %d in AMR WB content.", + mode); + + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + + return; + } + + size_t frameSize = getFrameSize(mode); + if (inHeader->nFilledLen < frameSize) { + ALOGE("b/27662364: expected %zu bytes vs %u", frameSize, inHeader->nFilledLen); + notify(OMX_EventError, OMX_ErrorStreamCorrupt, 0, NULL); + mSignalledError = true; + return; + } + + int16_t *outPtr = (int16_t *)outHeader->pBuffer; + + if (mode >= 9) { + // Produce silence instead of comfort noise and for + // speech lost/no data. + memset(outPtr, 0, kNumSamplesPerFrameWB * sizeof(int16_t)); + } else if (mode < 9) { + int16 frameType; + mime_unsorting( + const_cast(&inputPtr[1]), + mInputSampleBuffer, + &frameType, &mode, 1, &mRxState); + + int16_t numSamplesOutput; + pvDecoder_AmrWb( + mode, mInputSampleBuffer, + outPtr, + &numSamplesOutput, + mDecoderBuf, frameType, mDecoderCookie); + + CHECK_EQ((int)numSamplesOutput, (int)kNumSamplesPerFrameWB); + + for (int i = 0; i < kNumSamplesPerFrameWB; ++i) { + /* Delete the 2 LSBs (14-bit output) */ + outPtr[i] &= 0xfffC; + } + } + + numBytesRead = frameSize; + } + + inHeader->nOffset += numBytesRead; + inHeader->nFilledLen -= numBytesRead; + + outHeader->nFlags = 0; + outHeader->nOffset = 0; + + if (mMode == MODE_NARROW) { + outHeader->nFilledLen = kNumSamplesPerFrameNB * sizeof(int16_t); + + outHeader->nTimeStamp = + mAnchorTimeUs + + (mNumSamplesOutput * 1000000LL) / kSampleRateNB; + + mNumSamplesOutput += kNumSamplesPerFrameNB; + } else { + outHeader->nFilledLen = kNumSamplesPerFrameWB * sizeof(int16_t); + + outHeader->nTimeStamp = + mAnchorTimeUs + + (mNumSamplesOutput * 1000000LL) / kSampleRateWB; + + mNumSamplesOutput += kNumSamplesPerFrameWB; + } + + if (inHeader->nFilledLen == 0 && (inHeader->nFlags & OMX_BUFFERFLAG_EOS) == 0) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + + ++mInputBufferCount; + } +} + +void SoftAMR::onPortFlushCompleted(OMX_U32 portIndex) { + ALOGV("onPortFlushCompleted portindex %d, resetting frame ", portIndex); + if (portIndex == 0) { + if (mMode == MODE_NARROW) { + Speech_Decode_Frame_reset(mState); + } else { + pvDecoder_AmrWb_Reset(mState, 0 /* reset_all */); + } + } +} + +void SoftAMR::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { + if (portIndex != 1) { + return; + } + + switch (mOutputPortSettingsChange) { + case NONE: + break; + + case AWAITING_DISABLED: + { + CHECK(!enabled); + mOutputPortSettingsChange = AWAITING_ENABLED; + break; + } + + default: + { + CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); + CHECK(enabled); + mOutputPortSettingsChange = NONE; + break; + } + } +} + +void SoftAMR::onReset() { + mSignalledError = false; + mOutputPortSettingsChange = NONE; +} + +} // namespace android + +__attribute__((cfi_canonical_jump_table)) +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftAMR(name, callbacks, appData, component); +} + diff --git a/media/libstagefright/codecs/amrnb/dec/SoftAMR.h b/media/libstagefright/codecs/amrnb/dec/SoftAMR.h new file mode 100644 index 0000000..d5aaed3 --- /dev/null +++ b/media/libstagefright/codecs/amrnb/dec/SoftAMR.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2011 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_AMR_H_ + +#define SOFT_AMR_H_ + +#include +#include "gsmamr_dec.h" +#include "pvamrwbdecoder.h" + +namespace android { + +struct SoftAMR : public SimpleSoftOMXComponent { + SoftAMR(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftAMR(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + virtual void onReset(); + +private: + enum { + kNumBuffers = 4, + kSampleRateNB = 8000, + kSampleRateWB = 16000, + kNumSamplesPerFrameNB = 160, + kNumSamplesPerFrameWB = 320, + }; + + enum { + MODE_NARROW, + MODE_WIDE + + } mMode; + + void *mState; + void *mDecoderBuf; + int16_t *mDecoderCookie; + RX_State_wb mRxState{}; + + size_t mInputBufferCount; + int64_t mAnchorTimeUs; + int64_t mNumSamplesOutput; + + bool mSignalledError; + + enum { + NONE, + AWAITING_DISABLED, + AWAITING_ENABLED + } mOutputPortSettingsChange; + + int16_t mInputSampleBuffer[477]; + + void initPorts(); + status_t initDecoder(); + bool isConfigured() const; + + DISALLOW_EVIL_CONSTRUCTORS(SoftAMR); +}; + +} // namespace android + +#endif // SOFT_AMR_H_ + diff --git a/media/libstagefright/codecs/amrnb/dec/exports.lds b/media/libstagefright/codecs/amrnb/dec/exports.lds new file mode 100644 index 0000000..e24f3fa --- /dev/null +++ b/media/libstagefright/codecs/amrnb/dec/exports.lds @@ -0,0 +1,5 @@ +{ + global: + _Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE; + local: *; +}; diff --git a/media/libstagefright/codecs/amrnb/enc/Android.bp b/media/libstagefright/codecs/amrnb/enc/Android.bp new file mode 100644 index 0000000..6bf2d39 --- /dev/null +++ b/media/libstagefright/codecs/amrnb/enc/Android.bp @@ -0,0 +1,31 @@ + +//############################################################################### + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_av_media_libstagefright_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_av_media_libstagefright_license"], +} + +cc_library_shared { + name: "libstagefright_soft_amrnbenc", + defaults: ["libstagefright_softomx-defaults"], + + srcs: ["SoftAMRNBEncoder.cpp"], + + //addressing b/25409744 + //sanitize: { + // misc_undefined: [ + // "signed-integer-overflow", + // ], + //}, + + static_libs: ["libstagefright_amrnbenc"], + + shared_libs: [ + "libstagefright_amrnb_common", + ], +} diff --git a/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp b/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp new file mode 100644 index 0000000..a1f6686 --- /dev/null +++ b/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp @@ -0,0 +1,429 @@ +/* + * Copyright (C) 2012 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 "SoftAMRNBEncoder" +#include + +#include "SoftAMRNBEncoder.h" + +#include "gsmamr_enc.h" + +#include +#include + +namespace android { + +static const int32_t kSampleRate = 8000; + +template +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +SoftAMRNBEncoder::SoftAMRNBEncoder( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mEncState(NULL), + mSidState(NULL), + mBitRate(0), + mMode(MR475), + mInputSize(0), + mInputTimeUs(-1LL), + mSawInputEOS(false), + mSignalledError(false) { + initPorts(); + CHECK_EQ(initEncoder(), (status_t)OK); +} + +SoftAMRNBEncoder::~SoftAMRNBEncoder() { + if (mEncState != NULL) { + AMREncodeExit(&mEncState, &mSidState); + mEncState = mSidState = NULL; + } +} + +void SoftAMRNBEncoder::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = kNumSamplesPerFrame * sizeof(int16_t); + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.audio.cMIMEType = const_cast("audio/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.audio.cMIMEType = const_cast("audio/3gpp"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingAMR; + + addPort(def); +} + +status_t SoftAMRNBEncoder::initEncoder() { + if (AMREncodeInit(&mEncState, &mSidState, false /* dtx_enable */) != 0) { + return UNKNOWN_ERROR; + } + + return OK; +} + +OMX_ERRORTYPE SoftAMRNBEncoder::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioPortFormat: + { + OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex > 0) { + return OMX_ErrorNoMore; + } + + formatParams->eEncoding = + (formatParams->nPortIndex == 0) + ? OMX_AUDIO_CodingPCM : OMX_AUDIO_CodingAMR; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAmr: + { + OMX_AUDIO_PARAM_AMRTYPE *amrParams = + (OMX_AUDIO_PARAM_AMRTYPE *)params; + + if (!isValidOMXParam(amrParams)) { + return OMX_ErrorBadParameter; + } + + if (amrParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + amrParams->nChannels = 1; + amrParams->nBitRate = mBitRate; + amrParams->eAMRBandMode = (OMX_AUDIO_AMRBANDMODETYPE)(mMode + 1); + amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff; + amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + pcmParams->eNumData = OMX_NumericalDataSigned; + pcmParams->eEndian = OMX_EndianBig; + pcmParams->bInterleaved = OMX_TRUE; + pcmParams->nBitPerSample = 16; + pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; + pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelCF; + + pcmParams->nChannels = 1; + pcmParams->nSamplingRate = kSampleRate; + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftAMRNBEncoder::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (!isValidOMXParam(roleParams)) { + return OMX_ErrorBadParameter; + } + + if (strncmp((const char *)roleParams->cRole, + "audio_encoder.amrnb", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPortFormat: + { + const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if ((formatParams->nPortIndex == 0 + && formatParams->eEncoding != OMX_AUDIO_CodingPCM) + || (formatParams->nPortIndex == 1 + && formatParams->eEncoding != OMX_AUDIO_CodingAMR)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAmr: + { + OMX_AUDIO_PARAM_AMRTYPE *amrParams = + (OMX_AUDIO_PARAM_AMRTYPE *)params; + + if (!isValidOMXParam(amrParams)) { + return OMX_ErrorBadParameter; + } + + if (amrParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + if (amrParams->nChannels != 1 + || amrParams->eAMRDTXMode != OMX_AUDIO_AMRDTXModeOff + || amrParams->eAMRFrameFormat + != OMX_AUDIO_AMRFrameFormatFSF + || amrParams->eAMRBandMode < OMX_AUDIO_AMRBandModeNB0 + || amrParams->eAMRBandMode > OMX_AUDIO_AMRBandModeNB7) { + return OMX_ErrorUndefined; + } + + mBitRate = amrParams->nBitRate; + mMode = amrParams->eAMRBandMode - 1; + + amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff; + amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + if (pcmParams->nChannels != 1 + || pcmParams->nSamplingRate != (OMX_U32)kSampleRate) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +void SoftAMRNBEncoder::onQueueFilled(OMX_U32 /* portIndex */) { + if (mSignalledError) { + return; + } + + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + size_t numBytesPerInputFrame = kNumSamplesPerFrame * sizeof(int16_t); + + for (;;) { + // We do the following until we run out of buffers. + + while (mInputSize < numBytesPerInputFrame) { + // As long as there's still input data to be read we + // will drain "kNumSamplesPerFrame" samples + // into the "mInputFrame" buffer and then encode those + // as a unit into an output buffer. + + if (mSawInputEOS || inQueue.empty()) { + return; + } + + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + const void *inData = inHeader->pBuffer + inHeader->nOffset; + + size_t copy = numBytesPerInputFrame - mInputSize; + if (copy > inHeader->nFilledLen) { + copy = inHeader->nFilledLen; + } + + if (mInputSize == 0) { + mInputTimeUs = inHeader->nTimeStamp; + } + + memcpy((uint8_t *)mInputFrame + mInputSize, inData, copy); + mInputSize += copy; + + inHeader->nOffset += copy; + inHeader->nFilledLen -= copy; + + // "Time" on the input buffer has in effect advanced by the + // number of audio frames we just advanced nOffset by. + inHeader->nTimeStamp += + (copy * 1000000LL / kSampleRate) / sizeof(int16_t); + + if (inHeader->nFilledLen == 0) { + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + ALOGV("saw input EOS"); + mSawInputEOS = true; + + // Pad any remaining data with zeroes. + memset((uint8_t *)mInputFrame + mInputSize, + 0, + numBytesPerInputFrame - mInputSize); + + mInputSize = numBytesPerInputFrame; + } + + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + inData = NULL; + inHeader = NULL; + inInfo = NULL; + } + } + + // At this point we have all the input data necessary to encode + // a single frame, all we need is an output buffer to store the result + // in. + + if (outQueue.empty()) { + return; + } + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + uint8_t *outPtr = outHeader->pBuffer + outHeader->nOffset; + size_t outAvailable = outHeader->nAllocLen - outHeader->nOffset; + + Frame_Type_3GPP frameType; + int res = AMREncode( + mEncState, mSidState, (Mode)mMode, + mInputFrame, outPtr, &frameType, AMR_TX_WMF); + + CHECK_GE(res, 0); + CHECK_LE((size_t)res, outAvailable); + + // Convert header byte from WMF to IETF format. + outPtr[0] = ((outPtr[0] << 3) | 4) & 0x7c; + + outHeader->nFilledLen = res; + outHeader->nFlags = OMX_BUFFERFLAG_ENDOFFRAME; + + if (mSawInputEOS) { + // We also tag this output buffer with EOS if it corresponds + // to the final input buffer. + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + } + + outHeader->nTimeStamp = mInputTimeUs; + +#if 0 + ALOGI("sending %d bytes of data (time = %lld us, flags = 0x%08lx)", + nOutputBytes, mInputTimeUs, outHeader->nFlags); + + hexdump(outHeader->pBuffer + outHeader->nOffset, outHeader->nFilledLen); +#endif + + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + + outHeader = NULL; + outInfo = NULL; + + mInputSize = 0; + } +} + +} // namespace android + +__attribute__((cfi_canonical_jump_table)) +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftAMRNBEncoder(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.h b/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.h new file mode 100644 index 0000000..c73e4dd --- /dev/null +++ b/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2012 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_AMRNB_ENCODER_H_ + +#define SOFT_AMRNB_ENCODER_H_ + +#include + +namespace android { + +struct SoftAMRNBEncoder : public SimpleSoftOMXComponent { + SoftAMRNBEncoder( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftAMRNBEncoder(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + +private: + enum { + kNumBuffers = 4, + kNumSamplesPerFrame = 160, + }; + + void *mEncState; + void *mSidState; + + OMX_U32 mBitRate; + int mMode; + + size_t mInputSize; + int16_t mInputFrame[kNumSamplesPerFrame]; + int64_t mInputTimeUs; + + bool mSawInputEOS; + bool mSignalledError; + + void initPorts(); + status_t initEncoder(); + + status_t setAudioParams(); + + DISALLOW_EVIL_CONSTRUCTORS(SoftAMRNBEncoder); +}; + +} // namespace android + +#endif // SOFT_AMRNB_ENCODER_H_ diff --git a/media/libstagefright/codecs/amrnb/enc/exports.lds b/media/libstagefright/codecs/amrnb/enc/exports.lds new file mode 100644 index 0000000..e24f3fa --- /dev/null +++ b/media/libstagefright/codecs/amrnb/enc/exports.lds @@ -0,0 +1,5 @@ +{ + global: + _Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE; + local: *; +}; diff --git a/media/libstagefright/codecs/amrwbenc/Android.bp b/media/libstagefright/codecs/amrwbenc/Android.bp new file mode 100644 index 0000000..00e7bc9 --- /dev/null +++ b/media/libstagefright/codecs/amrwbenc/Android.bp @@ -0,0 +1,31 @@ + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_av_media_libstagefright_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_av_media_libstagefright_license"], +} + +cc_library_shared { + name: "libstagefright_soft_amrwbenc", + defaults: ["libstagefright_softomx-defaults"], + + srcs: ["SoftAMRWBEncoder.cpp"], + + version_script: "exports.lds", + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + ], + cfi: true, + }, + + static_libs: ["libstagefright_amrwbenc"], + + shared_libs: [ + "libstagefright_enc_common", + ], +} diff --git a/media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.cpp b/media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.cpp new file mode 100644 index 0000000..657a5ce --- /dev/null +++ b/media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.cpp @@ -0,0 +1,484 @@ +/* + * Copyright (C) 2012 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 "SoftAMRWBEncoder" +#include + +#include "SoftAMRWBEncoder.h" + +#include "cmnMemory.h" + +#include +#include + +namespace android { + +static const int32_t kSampleRate = 16000; + +template +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +SoftAMRWBEncoder::SoftAMRWBEncoder( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mEncoderHandle(NULL), + mApiHandle(NULL), + mMemOperator(NULL), + mBitRate(0), + mMode(VOAMRWB_MD66), + mInputSize(0), + mInputTimeUs(-1LL), + mSawInputEOS(false), + mSignalledError(false) { + initPorts(); + CHECK_EQ(initEncoder(), (status_t)OK); +} + +SoftAMRWBEncoder::~SoftAMRWBEncoder() { + if (mEncoderHandle != NULL) { + CHECK_EQ((VO_U32)VO_ERR_NONE, mApiHandle->Uninit(mEncoderHandle)); + mEncoderHandle = NULL; + } + + delete mApiHandle; + mApiHandle = NULL; + + delete mMemOperator; + mMemOperator = NULL; +} + +void SoftAMRWBEncoder::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = kNumSamplesPerFrame * sizeof(int16_t); + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.audio.cMIMEType = const_cast("audio/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.audio.cMIMEType = const_cast("audio/amr-wb"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingAMR; + + addPort(def); +} + +status_t SoftAMRWBEncoder::initEncoder() { + mApiHandle = new VO_AUDIO_CODECAPI; + + if (VO_ERR_NONE != voGetAMRWBEncAPI(mApiHandle)) { + ALOGE("Failed to get api handle"); + return UNKNOWN_ERROR; + } + + mMemOperator = new VO_MEM_OPERATOR; + mMemOperator->Alloc = cmnMemAlloc; + mMemOperator->Copy = cmnMemCopy; + mMemOperator->Free = cmnMemFree; + mMemOperator->Set = cmnMemSet; + mMemOperator->Check = cmnMemCheck; + + VO_CODEC_INIT_USERDATA userData; + memset(&userData, 0, sizeof(userData)); + userData.memflag = VO_IMF_USERMEMOPERATOR; + userData.memData = (VO_PTR) mMemOperator; + + if (VO_ERR_NONE != mApiHandle->Init( + &mEncoderHandle, VO_AUDIO_CodingAMRWB, &userData)) { + ALOGE("Failed to init AMRWB encoder"); + return UNKNOWN_ERROR; + } + + VOAMRWBFRAMETYPE type = VOAMRWB_RFC3267; + if (VO_ERR_NONE != mApiHandle->SetParam( + mEncoderHandle, VO_PID_AMRWB_FRAMETYPE, &type)) { + ALOGE("Failed to set AMRWB encoder frame type to %d", type); + return UNKNOWN_ERROR; + } + + return OK; +} + +OMX_ERRORTYPE SoftAMRWBEncoder::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioPortFormat: + { + OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex > 0) { + return OMX_ErrorNoMore; + } + + formatParams->eEncoding = + (formatParams->nPortIndex == 0) + ? OMX_AUDIO_CodingPCM : OMX_AUDIO_CodingAMR; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAmr: + { + OMX_AUDIO_PARAM_AMRTYPE *amrParams = + (OMX_AUDIO_PARAM_AMRTYPE *)params; + + if (!isValidOMXParam(amrParams)) { + return OMX_ErrorBadParameter; + } + + if (amrParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + amrParams->nChannels = 1; + amrParams->nBitRate = mBitRate; + + amrParams->eAMRBandMode = + (OMX_AUDIO_AMRBANDMODETYPE)(mMode + OMX_AUDIO_AMRBandModeWB0); + + amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff; + amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + pcmParams->eNumData = OMX_NumericalDataSigned; + pcmParams->eEndian = OMX_EndianBig; + pcmParams->bInterleaved = OMX_TRUE; + pcmParams->nBitPerSample = 16; + pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; + pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelCF; + + pcmParams->nChannels = 1; + pcmParams->nSamplingRate = kSampleRate; + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftAMRWBEncoder::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (!isValidOMXParam(roleParams)) { + return OMX_ErrorBadParameter; + } + + if (strncmp((const char *)roleParams->cRole, + "audio_encoder.amrwb", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPortFormat: + { + const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if ((formatParams->nPortIndex == 0 + && formatParams->eEncoding != OMX_AUDIO_CodingPCM) + || (formatParams->nPortIndex == 1 + && formatParams->eEncoding != OMX_AUDIO_CodingAMR)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAmr: + { + OMX_AUDIO_PARAM_AMRTYPE *amrParams = + (OMX_AUDIO_PARAM_AMRTYPE *)params; + + if (!isValidOMXParam(amrParams)) { + return OMX_ErrorBadParameter; + } + + if (amrParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + if (amrParams->nChannels != 1 + || amrParams->eAMRDTXMode != OMX_AUDIO_AMRDTXModeOff + || amrParams->eAMRFrameFormat + != OMX_AUDIO_AMRFrameFormatFSF + || amrParams->eAMRBandMode < OMX_AUDIO_AMRBandModeWB0 + || amrParams->eAMRBandMode > OMX_AUDIO_AMRBandModeWB8) { + return OMX_ErrorUndefined; + } + + mBitRate = amrParams->nBitRate; + + mMode = (VOAMRWBMODE)( + amrParams->eAMRBandMode - OMX_AUDIO_AMRBandModeWB0); + + amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff; + amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF; + + if (VO_ERR_NONE != + mApiHandle->SetParam( + mEncoderHandle, VO_PID_AMRWB_MODE, &mMode)) { + ALOGE("Failed to set AMRWB encoder mode to %d", mMode); + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + if (pcmParams->nChannels != 1 + || pcmParams->nSamplingRate != (OMX_U32)kSampleRate) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +void SoftAMRWBEncoder::onQueueFilled(OMX_U32 /* portIndex */) { + if (mSignalledError) { + return; + } + + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + size_t numBytesPerInputFrame = kNumSamplesPerFrame * sizeof(int16_t); + + for (;;) { + // We do the following until we run out of buffers. + + while (mInputSize < numBytesPerInputFrame) { + // As long as there's still input data to be read we + // will drain "kNumSamplesPerFrame" samples + // into the "mInputFrame" buffer and then encode those + // as a unit into an output buffer. + + if (mSawInputEOS || inQueue.empty()) { + return; + } + + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + const void *inData = inHeader->pBuffer + inHeader->nOffset; + + size_t copy = numBytesPerInputFrame - mInputSize; + if (copy > inHeader->nFilledLen) { + copy = inHeader->nFilledLen; + } + + if (mInputSize == 0) { + mInputTimeUs = inHeader->nTimeStamp; + } + + memcpy((uint8_t *)mInputFrame + mInputSize, inData, copy); + mInputSize += copy; + + inHeader->nOffset += copy; + inHeader->nFilledLen -= copy; + + // "Time" on the input buffer has in effect advanced by the + // number of audio frames we just advanced nOffset by. + inHeader->nTimeStamp += + (copy * 1000000LL / kSampleRate) / sizeof(int16_t); + + if (inHeader->nFilledLen == 0) { + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + ALOGV("saw input EOS"); + mSawInputEOS = true; + + // Pad any remaining data with zeroes. + memset((uint8_t *)mInputFrame + mInputSize, + 0, + numBytesPerInputFrame - mInputSize); + + mInputSize = numBytesPerInputFrame; + } + + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + inData = NULL; + inHeader = NULL; + inInfo = NULL; + } + } + + // At this point we have all the input data necessary to encode + // a single frame, all we need is an output buffer to store the result + // in. + + if (outQueue.empty()) { + return; + } + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + uint8_t *outPtr = outHeader->pBuffer + outHeader->nOffset; + size_t outAvailable = outHeader->nAllocLen - outHeader->nOffset; + + VO_CODECBUFFER inputData; + memset(&inputData, 0, sizeof(inputData)); + inputData.Buffer = (unsigned char *) mInputFrame; + inputData.Length = mInputSize; + + CHECK_EQ((VO_U32)VO_ERR_NONE, + mApiHandle->SetInputData(mEncoderHandle, &inputData)); + + VO_CODECBUFFER outputData; + memset(&outputData, 0, sizeof(outputData)); + VO_AUDIO_OUTPUTINFO outputInfo; + memset(&outputInfo, 0, sizeof(outputInfo)); + + outputData.Buffer = outPtr; + outputData.Length = outAvailable; + VO_U32 ret = mApiHandle->GetOutputData( + mEncoderHandle, &outputData, &outputInfo); + CHECK(ret == VO_ERR_NONE || ret == VO_ERR_INPUT_BUFFER_SMALL); + + outHeader->nFilledLen = outputData.Length; + outHeader->nFlags = OMX_BUFFERFLAG_ENDOFFRAME; + + if (mSawInputEOS) { + // We also tag this output buffer with EOS if it corresponds + // to the final input buffer. + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + } + + outHeader->nTimeStamp = mInputTimeUs; + +#if 0 + ALOGI("sending %ld bytes of data (time = %lld us, flags = 0x%08lx)", + outHeader->nFilledLen, mInputTimeUs, outHeader->nFlags); + + hexdump(outHeader->pBuffer + outHeader->nOffset, outHeader->nFilledLen); +#endif + + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + + outHeader = NULL; + outInfo = NULL; + + mInputSize = 0; + } +} + +} // namespace android + +__attribute__((cfi_canonical_jump_table)) +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftAMRWBEncoder(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.h b/media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.h new file mode 100644 index 0000000..8950a8c --- /dev/null +++ b/media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2012 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_AMRWB_ENCODER_H_ + +#define SOFT_AMRWB_ENCODER_H_ + +#include + +#include "voAMRWB.h" + +struct VO_AUDIO_CODECAPI; +struct VO_MEM_OPERATOR; + +namespace android { + +struct SoftAMRWBEncoder : public SimpleSoftOMXComponent { + SoftAMRWBEncoder( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftAMRWBEncoder(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + +private: + enum { + kNumBuffers = 4, + kNumSamplesPerFrame = 320, + }; + + void *mEncoderHandle; + VO_AUDIO_CODECAPI *mApiHandle; + VO_MEM_OPERATOR *mMemOperator; + + OMX_U32 mBitRate; + VOAMRWBMODE mMode; + + size_t mInputSize; + int16_t mInputFrame[kNumSamplesPerFrame]; + int64_t mInputTimeUs; + + bool mSawInputEOS; + bool mSignalledError; + + void initPorts(); + status_t initEncoder(); + + DISALLOW_EVIL_CONSTRUCTORS(SoftAMRWBEncoder); +}; + +} // namespace android + +#endif // SOFT_AMRWB_ENCODER_H_ diff --git a/media/libstagefright/codecs/amrwbenc/exports.lds b/media/libstagefright/codecs/amrwbenc/exports.lds new file mode 100644 index 0000000..e24f3fa --- /dev/null +++ b/media/libstagefright/codecs/amrwbenc/exports.lds @@ -0,0 +1,5 @@ +{ + global: + _Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE; + local: *; +}; diff --git a/media/libstagefright/codecs/avcdec/Android.bp b/media/libstagefright/codecs/avcdec/Android.bp new file mode 100644 index 0000000..1c2f9be --- /dev/null +++ b/media/libstagefright/codecs/avcdec/Android.bp @@ -0,0 +1,34 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_av_media_libstagefright_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_av_media_libstagefright_license"], +} + +cc_library_shared { + name: "libstagefright_soft_avcdec", + defaults: ["libstagefright_softomx-defaults"], + + static_libs: ["libavcdec"], + srcs: ["SoftAVCDec.cpp"], + + cflags: [ + "-Wall", + ], + + version_script: "exports.lds", + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + ], + cfi: true, + config: { + cfi_assembly_support: true, + }, + }, + + ldflags: ["-Wl,-Bsymbolic"], +} diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp new file mode 100644 index 0000000..3891f23 --- /dev/null +++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp @@ -0,0 +1,732 @@ +/* + * Copyright 2015 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 "SoftAVCDec" +#include + +#include "ih264_typedefs.h" +#include "iv.h" +#include "ivd.h" +#include "ih264d.h" +#include "SoftAVCDec.h" + +#include +#include +#include +#include + +namespace android { + +#define componentName "video_decoder.avc" +#define codingType OMX_VIDEO_CodingAVC +#define CODEC_MIME_TYPE MEDIA_MIMETYPE_VIDEO_AVC + +/** Function and structure definitions to keep code similar for each codec */ +#define ivdec_api_function ih264d_api_function +#define ivdext_create_ip_t ih264d_create_ip_t +#define ivdext_create_op_t ih264d_create_op_t +#define ivdext_delete_ip_t ih264d_delete_ip_t +#define ivdext_delete_op_t ih264d_delete_op_t +#define ivdext_ctl_set_num_cores_ip_t ih264d_ctl_set_num_cores_ip_t +#define ivdext_ctl_set_num_cores_op_t ih264d_ctl_set_num_cores_op_t + +#define IVDEXT_CMD_CTL_SET_NUM_CORES \ + (IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_SET_NUM_CORES + +static const CodecProfileLevel kProfileLevels[] = { + { OMX_VIDEO_AVCProfileConstrainedBaseline, OMX_VIDEO_AVCLevel52 }, + + { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel52 }, + + { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel52 }, + + { OMX_VIDEO_AVCProfileConstrainedHigh, OMX_VIDEO_AVCLevel52 }, + + { OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCLevel52 }, +}; + +SoftAVC::SoftAVC( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SoftVideoDecoderOMXComponent( + name, componentName, codingType, + kProfileLevels, ARRAY_SIZE(kProfileLevels), + 320 /* width */, 240 /* height */, callbacks, + appData, component), + mCodecCtx(NULL), + mFlushOutBuffer(NULL), + mOmxColorFormat(OMX_COLOR_FormatYUV420Planar), + mIvColorFormat(IV_YUV_420P), + mChangingResolution(false), + mSignalledError(false), + mStride(mWidth), + mInputOffset(0){ + initPorts( + 1 /* numMinInputBuffers */, kNumBuffers, INPUT_BUF_SIZE, + 1 /* numMinOutputBuffers */, kNumBuffers, CODEC_MIME_TYPE); + + mTimeStart = mTimeEnd = systemTime(); + + // If input dump is enabled, then open create an empty file + GENERATE_FILE_NAMES(); + CREATE_DUMP_FILE(mInFile); +} + +SoftAVC::~SoftAVC() { + CHECK_EQ(deInitDecoder(), (status_t)OK); +} + +static void *ivd_aligned_malloc(void *ctxt, WORD32 alignment, WORD32 size) { + UNUSED(ctxt); + return memalign(alignment, size); +} + +static void ivd_aligned_free(void *ctxt, void *buf) { + UNUSED(ctxt); + free(buf); + return; +} + +static size_t GetCPUCoreCount() { + long 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(cpuCoreCount >= 1); + ALOGV("Number of CPU cores: %ld", cpuCoreCount); + return (size_t)cpuCoreCount; +} + +void SoftAVC::logVersion() { + ivd_ctl_getversioninfo_ip_t s_ctl_ip; + ivd_ctl_getversioninfo_op_t s_ctl_op; + UWORD8 au1_buf[512]; + IV_API_CALL_STATUS_T status; + + s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_GETVERSION; + s_ctl_ip.u4_size = sizeof(ivd_ctl_getversioninfo_ip_t); + s_ctl_op.u4_size = sizeof(ivd_ctl_getversioninfo_op_t); + s_ctl_ip.pv_version_buffer = au1_buf; + s_ctl_ip.u4_version_buffer_size = sizeof(au1_buf); + + status = + ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op); + + if (status != IV_SUCCESS) { + ALOGE("Error in getting version number: 0x%x", + s_ctl_op.u4_error_code); + } else { + ALOGV("Ittiam decoder version number: %s", + (char *)s_ctl_ip.pv_version_buffer); + } + return; +} + +status_t SoftAVC::setParams(size_t stride) { + ivd_ctl_set_config_ip_t s_ctl_ip; + ivd_ctl_set_config_op_t s_ctl_op; + IV_API_CALL_STATUS_T status; + s_ctl_ip.u4_disp_wd = (UWORD32)stride; + s_ctl_ip.e_frm_skip_mode = IVD_SKIP_NONE; + + s_ctl_ip.e_frm_out_mode = IVD_DISPLAY_FRAME_OUT; + s_ctl_ip.e_vid_dec_mode = IVD_DECODE_FRAME; + s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_SETPARAMS; + s_ctl_ip.u4_size = sizeof(ivd_ctl_set_config_ip_t); + s_ctl_op.u4_size = sizeof(ivd_ctl_set_config_op_t); + + ALOGV("Set the run-time (dynamic) parameters stride = %zu", stride); + status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op); + + if (status != IV_SUCCESS) { + ALOGE("Error in setting the run-time parameters: 0x%x", + s_ctl_op.u4_error_code); + + return UNKNOWN_ERROR; + } + return OK; +} + +status_t SoftAVC::resetPlugin() { + mIsInFlush = false; + mReceivedEOS = false; + + memset(mTimeStamps, 0, sizeof(mTimeStamps)); + memset(mTimeStampsValid, 0, sizeof(mTimeStampsValid)); + + /* Initialize both start and end times */ + mTimeStart = mTimeEnd = systemTime(); + + return OK; +} + +status_t SoftAVC::resetDecoder() { + ivd_ctl_reset_ip_t s_ctl_ip; + ivd_ctl_reset_op_t s_ctl_op; + IV_API_CALL_STATUS_T status; + + s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_RESET; + s_ctl_ip.u4_size = sizeof(ivd_ctl_reset_ip_t); + s_ctl_op.u4_size = sizeof(ivd_ctl_reset_op_t); + + status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op); + if (IV_SUCCESS != status) { + ALOGE("Error in reset: 0x%x", s_ctl_op.u4_error_code); + return UNKNOWN_ERROR; + } + mSignalledError = false; + + /* Set number of cores/threads to be used by the codec */ + setNumCores(); + + mStride = 0; + return OK; +} + +status_t SoftAVC::setNumCores() { + ivdext_ctl_set_num_cores_ip_t s_set_cores_ip; + ivdext_ctl_set_num_cores_op_t s_set_cores_op; + IV_API_CALL_STATUS_T status; + s_set_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_set_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES; + s_set_cores_ip.u4_num_cores = MIN(mNumCores, CODEC_MAX_NUM_CORES); + s_set_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t); + s_set_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t); + status = ivdec_api_function( + mCodecCtx, (void *)&s_set_cores_ip, (void *)&s_set_cores_op); + if (IV_SUCCESS != status) { + ALOGE("Error in setting number of cores: 0x%x", + s_set_cores_op.u4_error_code); + return UNKNOWN_ERROR; + } + return OK; +} + +status_t SoftAVC::setFlushMode() { + IV_API_CALL_STATUS_T status; + ivd_ctl_flush_ip_t s_video_flush_ip; + ivd_ctl_flush_op_t s_video_flush_op; + + s_video_flush_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_video_flush_ip.e_sub_cmd = IVD_CMD_CTL_FLUSH; + s_video_flush_ip.u4_size = sizeof(ivd_ctl_flush_ip_t); + s_video_flush_op.u4_size = sizeof(ivd_ctl_flush_op_t); + + /* Set the decoder in Flush mode, subsequent decode() calls will flush */ + status = ivdec_api_function( + mCodecCtx, (void *)&s_video_flush_ip, (void *)&s_video_flush_op); + + if (status != IV_SUCCESS) { + ALOGE("Error in setting the decoder in flush mode: (%d) 0x%x", status, + s_video_flush_op.u4_error_code); + return UNKNOWN_ERROR; + } + + mIsInFlush = true; + return OK; +} + +status_t SoftAVC::initDecoder() { + IV_API_CALL_STATUS_T status; + + mNumCores = GetCPUCoreCount(); + mCodecCtx = NULL; + + mStride = outputBufferWidth(); + + /* Initialize the decoder */ + { + ivdext_create_ip_t s_create_ip; + ivdext_create_op_t s_create_op; + + void *dec_fxns = (void *)ivdec_api_function; + + s_create_ip.s_ivd_create_ip_t.u4_size = sizeof(ivdext_create_ip_t); + s_create_ip.s_ivd_create_ip_t.e_cmd = IVD_CMD_CREATE; + s_create_ip.s_ivd_create_ip_t.u4_share_disp_buf = 0; + s_create_op.s_ivd_create_op_t.u4_size = sizeof(ivdext_create_op_t); + s_create_ip.s_ivd_create_ip_t.e_output_format = mIvColorFormat; + s_create_ip.s_ivd_create_ip_t.pf_aligned_alloc = ivd_aligned_malloc; + s_create_ip.s_ivd_create_ip_t.pf_aligned_free = ivd_aligned_free; + s_create_ip.s_ivd_create_ip_t.pv_mem_ctxt = NULL; + + status = ivdec_api_function(mCodecCtx, (void *)&s_create_ip, (void *)&s_create_op); + + if (status != IV_SUCCESS) { + ALOGE("Error in create: 0x%x", + s_create_op.s_ivd_create_op_t.u4_error_code); + deInitDecoder(); + mCodecCtx = NULL; + return UNKNOWN_ERROR; + } + + mCodecCtx = (iv_obj_t*)s_create_op.s_ivd_create_op_t.pv_handle; + mCodecCtx->pv_fxns = dec_fxns; + mCodecCtx->u4_size = sizeof(iv_obj_t); + } + + /* Reset the plugin state */ + resetPlugin(); + + /* Set the run time (dynamic) parameters */ + setParams(mStride); + + /* Set number of cores/threads to be used by the codec */ + setNumCores(); + + /* Get codec version */ + logVersion(); + + mFlushNeeded = false; + return OK; +} + +status_t SoftAVC::deInitDecoder() { + IV_API_CALL_STATUS_T status; + + if (mCodecCtx) { + ivdext_delete_ip_t s_delete_ip; + ivdext_delete_op_t s_delete_op; + + s_delete_ip.s_ivd_delete_ip_t.u4_size = sizeof(ivdext_delete_ip_t); + s_delete_ip.s_ivd_delete_ip_t.e_cmd = IVD_CMD_DELETE; + + s_delete_op.s_ivd_delete_op_t.u4_size = sizeof(ivdext_delete_op_t); + + status = ivdec_api_function(mCodecCtx, (void *)&s_delete_ip, (void *)&s_delete_op); + if (status != IV_SUCCESS) { + ALOGE("Error in delete: 0x%x", + s_delete_op.s_ivd_delete_op_t.u4_error_code); + return UNKNOWN_ERROR; + } + } + + + mChangingResolution = false; + + return OK; +} + +void SoftAVC::onReset() { + SoftVideoDecoderOMXComponent::onReset(); + + mSignalledError = false; + mInputOffset = 0; + resetDecoder(); + resetPlugin(); +} + +bool SoftAVC::getVUIParams() { + IV_API_CALL_STATUS_T status; + ih264d_ctl_get_vui_params_ip_t s_ctl_get_vui_params_ip; + ih264d_ctl_get_vui_params_op_t s_ctl_get_vui_params_op; + + s_ctl_get_vui_params_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_ctl_get_vui_params_ip.e_sub_cmd = + (IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_GET_VUI_PARAMS; + + s_ctl_get_vui_params_ip.u4_size = + sizeof(ih264d_ctl_get_vui_params_ip_t); + + s_ctl_get_vui_params_op.u4_size = sizeof(ih264d_ctl_get_vui_params_op_t); + + status = ivdec_api_function( + (iv_obj_t *)mCodecCtx, (void *)&s_ctl_get_vui_params_ip, + (void *)&s_ctl_get_vui_params_op); + + if (status != IV_SUCCESS) { + ALOGW("Error in getting VUI params: 0x%x", + s_ctl_get_vui_params_op.u4_error_code); + return false; + } + + int32_t primaries = s_ctl_get_vui_params_op.u1_colour_primaries; + int32_t transfer = s_ctl_get_vui_params_op.u1_tfr_chars; + int32_t coeffs = s_ctl_get_vui_params_op.u1_matrix_coeffs; + bool fullRange = s_ctl_get_vui_params_op.u1_video_full_range_flag; + + ColorAspects colorAspects; + ColorUtils::convertIsoColorAspectsToCodecAspects( + primaries, transfer, coeffs, fullRange, colorAspects); + + // Update color aspects if necessary. + if (colorAspectsDiffer(colorAspects, mBitstreamColorAspects)) { + mBitstreamColorAspects = colorAspects; + status_t err = handleColorAspectsChange(); + CHECK(err == OK); + } + return true; +} + +bool SoftAVC::setDecodeArgs( + ivd_video_decode_ip_t *ps_dec_ip, + ivd_video_decode_op_t *ps_dec_op, + OMX_BUFFERHEADERTYPE *inHeader, + OMX_BUFFERHEADERTYPE *outHeader, + size_t timeStampIx) { + size_t sizeY = outputBufferWidth() * outputBufferHeight(); + size_t sizeUV; + + ps_dec_ip->u4_size = sizeof(ivd_video_decode_ip_t); + ps_dec_op->u4_size = sizeof(ivd_video_decode_op_t); + + ps_dec_ip->e_cmd = IVD_CMD_VIDEO_DECODE; + + /* When in flush and after EOS with zero byte input, + * inHeader is set to zero. Hence check for non-null */ + if (inHeader) { + ps_dec_ip->u4_ts = timeStampIx; + ps_dec_ip->pv_stream_buffer = + inHeader->pBuffer + inHeader->nOffset + mInputOffset; + ps_dec_ip->u4_num_Bytes = inHeader->nFilledLen - mInputOffset; + } else { + ps_dec_ip->u4_ts = 0; + ps_dec_ip->pv_stream_buffer = NULL; + ps_dec_ip->u4_num_Bytes = 0; + } + + sizeUV = sizeY / 4; + ps_dec_ip->s_out_buffer.u4_min_out_buf_size[0] = sizeY; + ps_dec_ip->s_out_buffer.u4_min_out_buf_size[1] = sizeUV; + ps_dec_ip->s_out_buffer.u4_min_out_buf_size[2] = sizeUV; + + uint8_t *pBuf; + if (outHeader) { + if (outHeader->nAllocLen < sizeY + (sizeUV * 2)) { + android_errorWriteLog(0x534e4554, "27833616"); + return false; + } + pBuf = outHeader->pBuffer; + } else { + // mFlushOutBuffer always has the right size. + pBuf = mFlushOutBuffer; + } + + ps_dec_ip->s_out_buffer.pu1_bufs[0] = pBuf; + ps_dec_ip->s_out_buffer.pu1_bufs[1] = pBuf + sizeY; + ps_dec_ip->s_out_buffer.pu1_bufs[2] = pBuf + sizeY + sizeUV; + ps_dec_ip->s_out_buffer.u4_num_bufs = 3; + return true; +} +void SoftAVC::onPortFlushCompleted(OMX_U32 portIndex) { + /* Once the output buffers are flushed, ignore any buffers that are held in decoder */ + if (kOutputPortIndex == portIndex) { + setFlushMode(); + + /* Allocate a picture buffer to flushed data */ + uint32_t displayStride = outputBufferWidth(); + uint32_t displayHeight = outputBufferHeight(); + + uint32_t bufferSize = displayStride * displayHeight * 3 / 2; + mFlushOutBuffer = (uint8_t *)memalign(128, bufferSize); + if (NULL == mFlushOutBuffer) { + ALOGE("Could not allocate flushOutputBuffer of size %u", bufferSize); + return; + } + + while (true) { + ivd_video_decode_ip_t s_dec_ip; + ivd_video_decode_op_t s_dec_op; + IV_API_CALL_STATUS_T status; + + setDecodeArgs(&s_dec_ip, &s_dec_op, NULL, NULL, 0); + + status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op); + if (0 == s_dec_op.u4_output_present) { + resetPlugin(); + break; + } + } + + if (mFlushOutBuffer) { + free(mFlushOutBuffer); + mFlushOutBuffer = NULL; + } + } else { + mInputOffset = 0; + } +} + +void SoftAVC::onQueueFilled(OMX_U32 portIndex) { + UNUSED(portIndex); + OMX_BUFFERHEADERTYPE *inHeader = NULL; + BufferInfo *inInfo = NULL; + + if (mSignalledError) { + return; + } + if (mOutputPortSettingsChange != NONE) { + return; + } + + if (NULL == mCodecCtx) { + if (OK != initDecoder()) { + ALOGE("Failed to initialize decoder"); + notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL); + mSignalledError = true; + return; + } + } + if (outputBufferWidth() != mStride) { + /* Set the run-time (dynamic) parameters */ + mStride = outputBufferWidth(); + setParams(mStride); + } + + List &inQueue = getPortQueue(kInputPortIndex); + List &outQueue = getPortQueue(kOutputPortIndex); + + while (!outQueue.empty()) { + BufferInfo *outInfo; + OMX_BUFFERHEADERTYPE *outHeader; + size_t timeStampIx = 0; + + if (!mIsInFlush && (NULL == inHeader)) { + if (!inQueue.empty()) { + inInfo = *inQueue.begin(); + inHeader = inInfo->mHeader; + if (inHeader == NULL) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + continue; + } + } else { + break; + } + } + + outInfo = *outQueue.begin(); + outHeader = outInfo->mHeader; + outHeader->nFlags = 0; + outHeader->nTimeStamp = 0; + outHeader->nOffset = 0; + + if (inHeader != NULL) { + if (inHeader->nFilledLen == 0) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + if (!(inHeader->nFlags & OMX_BUFFERFLAG_EOS)) { + return; + } + + mReceivedEOS = true; + inHeader = NULL; + setFlushMode(); + } else if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + mReceivedEOS = true; + } + } + + /* Get a free slot in timestamp array to hold input timestamp */ + { + size_t i; + timeStampIx = 0; + for (i = 0; i < MAX_TIME_STAMPS; i++) { + if (!mTimeStampsValid[i]) { + timeStampIx = i; + break; + } + } + if (inHeader != NULL) { + mTimeStampsValid[timeStampIx] = true; + mTimeStamps[timeStampIx] = inHeader->nTimeStamp; + } + } + + { + ivd_video_decode_ip_t s_dec_ip; + ivd_video_decode_op_t s_dec_op; + nsecs_t timeDelay, timeTaken; + + if (!setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx)) { + ALOGE("Decoder arg setup failed"); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } + // If input dump is enabled, then write to file + DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes, mInputOffset); + + mTimeStart = systemTime(); + /* Compute time elapsed between end of previous decode() + * to start of current decode() */ + timeDelay = mTimeStart - mTimeEnd; + + IV_API_CALL_STATUS_T status; + status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op); + + bool unsupportedResolution = + (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_dec_op.u4_error_code & IVD_ERROR_MASK)); + + /* Check for unsupported dimensions */ + if (unsupportedResolution) { + ALOGE("Unsupported resolution : %dx%d", mWidth, mHeight); + notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL); + mSignalledError = true; + return; + } + + bool allocationFailed = (IVD_MEM_ALLOC_FAILED == (s_dec_op.u4_error_code & IVD_ERROR_MASK)); + if (allocationFailed) { + ALOGE("Allocation failure in decoder"); + notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL); + mSignalledError = true; + return; + } + + if (IS_IVD_FATAL_ERROR(s_dec_op.u4_error_code)) { + ALOGE("Fatal Error : 0x%x", s_dec_op.u4_error_code); + notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL); + mSignalledError = true; + return; + } + + bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & IVD_ERROR_MASK)); + + getVUIParams(); + + mTimeEnd = systemTime(); + /* Compute time taken for decode() */ + timeTaken = mTimeEnd - mTimeStart; + + ALOGV("timeTaken=%6lldus delay=%6lldus numBytes=%6d", + (long long) (timeTaken / 1000LL), (long long) (timeDelay / 1000LL), + s_dec_op.u4_num_bytes_consumed); + if (s_dec_op.u4_frame_decoded_flag && !mFlushNeeded) { + mFlushNeeded = true; + } + + if ((inHeader != NULL) && (1 != s_dec_op.u4_frame_decoded_flag)) { + /* If the input did not contain picture data, then ignore + * the associated timestamp */ + mTimeStampsValid[timeStampIx] = false; + } + + // If the decoder is in the changing resolution mode and there is no output present, + // that means the switching is done and it's ready to reset the decoder and the plugin. + if (mChangingResolution && !s_dec_op.u4_output_present) { + mChangingResolution = false; + resetDecoder(); + resetPlugin(); + mStride = outputBufferWidth(); + setParams(mStride); + continue; + } + + if (resChanged) { + mChangingResolution = true; + if (mFlushNeeded) { + setFlushMode(); + } + continue; + } + + // Combine the resolution change and coloraspects change in one PortSettingChange event + // if necessary. + if ((0 < s_dec_op.u4_pic_wd) && (0 < s_dec_op.u4_pic_ht)) { + uint32_t width = s_dec_op.u4_pic_wd; + uint32_t height = s_dec_op.u4_pic_ht; + bool portWillReset = false; + handlePortSettingsChange(&portWillReset, width, height); + if (portWillReset) { + resetDecoder(); + resetPlugin(); + return; + } + } else if (mUpdateColorAspects) { + notify(OMX_EventPortSettingsChanged, kOutputPortIndex, + kDescribeColorAspectsIndex, NULL); + mUpdateColorAspects = false; + return; + } + + if (s_dec_op.u4_output_present) { + outHeader->nFilledLen = (outputBufferWidth() * outputBufferHeight() * 3) / 2; + + outHeader->nTimeStamp = mTimeStamps[s_dec_op.u4_ts]; + mTimeStampsValid[s_dec_op.u4_ts] = false; + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + } else if (mIsInFlush) { + /* If in flush mode and no output is returned by the codec, + * then come out of flush mode */ + mIsInFlush = false; + + /* If EOS was recieved on input port and there is no output + * from the codec, then signal EOS on output port */ + if (mReceivedEOS) { + outHeader->nFilledLen = 0; + outHeader->nFlags |= OMX_BUFFERFLAG_EOS; + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + resetPlugin(); + } + } + mInputOffset += s_dec_op.u4_num_bytes_consumed; + } + // If more than 4 bytes are remaining in input, then do not release it + if (inHeader != NULL && ((inHeader->nFilledLen - mInputOffset) <= 4)) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + mInputOffset = 0; + + /* If input EOS is seen and decoder is not in flush mode, + * set the decoder in flush mode. + * There can be a case where EOS is sent along with last picture data + * In that case, only after decoding that input data, decoder has to be + * put in flush. This case is handled here */ + + if (mReceivedEOS && !mIsInFlush) { + setFlushMode(); + } + } + } +} + +int SoftAVC::getColorAspectPreference() { + return kPreferBitstream; +} + +} // namespace android + +__attribute__((cfi_canonical_jump_table)) +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, + OMX_COMPONENTTYPE **component) { + return new android::SoftAVC(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.h b/media/libstagefright/codecs/avcdec/SoftAVCDec.h new file mode 100644 index 0000000..679ed3e --- /dev/null +++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.h @@ -0,0 +1,168 @@ +/* + * Copyright 2015 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_H264_DEC_H_ + +#define SOFT_H264_DEC_H_ + +#include +#include + +namespace android { + +/** Number of entries in the time-stamp array */ +#define MAX_TIME_STAMPS 64 + +/** Maximum number of cores supported by the codec */ +#define CODEC_MAX_NUM_CORES 4 + +#define CODEC_MAX_WIDTH 1920 + +#define CODEC_MAX_HEIGHT 1088 + +/** Input buffer size */ +#define INPUT_BUF_SIZE (1024 * 1024) + +#define MIN(a, b) ((a) < (b)) ? (a) : (b) + +/** Used to remove warnings about unused parameters */ +#define UNUSED(x) ((void)(x)) + +struct SoftAVC : public SoftVideoDecoderOMXComponent { + SoftAVC(const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftAVC(); + + virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onReset(); + virtual int getColorAspectPreference(); +private: + // Number of input and output buffers + enum { + kNumBuffers = 8 + }; + + iv_obj_t *mCodecCtx; // Codec context + + size_t mNumCores; // Number of cores to be uesd by the codec + + nsecs_t mTimeStart; // Time at the start of decode() + nsecs_t mTimeEnd; // Time at the end of decode() + + // Internal buffer to be used to flush out the buffers from decoder + uint8_t *mFlushOutBuffer; + + // Status of entries in the timestamp array + bool mTimeStampsValid[MAX_TIME_STAMPS]; + + // Timestamp array - Since codec does not take 64 bit timestamps, + // they are maintained in the plugin + OMX_S64 mTimeStamps[MAX_TIME_STAMPS]; + +#ifdef FILE_DUMP_ENABLE + char mInFile[200]; +#endif /* FILE_DUMP_ENABLE */ + + OMX_COLOR_FORMATTYPE mOmxColorFormat; // OMX Color format + IV_COLOR_FORMAT_T mIvColorFormat; // Ittiam Color format + + bool mIsInFlush; // codec is flush mode + bool mReceivedEOS; // EOS is receieved on input port + + // The input stream has changed to a different resolution, which is still supported by the + // codec. So the codec is switching to decode the new resolution. + bool mChangingResolution; + bool mFlushNeeded; + bool mSignalledError; + size_t mStride; + size_t mInputOffset; + + status_t initDecoder(); + status_t deInitDecoder(); + status_t setFlushMode(); + status_t setParams(size_t stride); + void logVersion(); + status_t setNumCores(); + status_t resetDecoder(); + status_t resetPlugin(); + + + bool setDecodeArgs( + ivd_video_decode_ip_t *ps_dec_ip, + ivd_video_decode_op_t *ps_dec_op, + OMX_BUFFERHEADERTYPE *inHeader, + OMX_BUFFERHEADERTYPE *outHeader, + size_t timeStampIx); + + bool getVUIParams(); + + DISALLOW_EVIL_CONSTRUCTORS(SoftAVC); +}; + +#ifdef FILE_DUMP_ENABLE + +#define INPUT_DUMP_PATH "/sdcard/media/avcd_input" +#define INPUT_DUMP_EXT "h264" + +#define GENERATE_FILE_NAMES() { \ + strcpy(mInFile, ""); \ + sprintf(mInFile, "%s_%lld.%s", INPUT_DUMP_PATH, \ + (long long) mTimeStart, \ + INPUT_DUMP_EXT); \ +} + +#define CREATE_DUMP_FILE(m_filename) { \ + FILE *fp = fopen(m_filename, "wb"); \ + if (fp != NULL) { \ + fclose(fp); \ + } else { \ + ALOGD("Could not open file %s", m_filename); \ + } \ +} +#define DUMP_TO_FILE(m_filename, m_buf, m_size, m_offset)\ +{ \ + FILE *fp = fopen(m_filename, "ab"); \ + if (fp != NULL && m_buf != NULL && m_offset == 0) { \ + int i; \ + i = fwrite(m_buf, 1, m_size, fp); \ + ALOGD("fwrite ret %d to write %d", i, m_size); \ + if (i != (int) m_size) { \ + ALOGD("Error in fwrite, returned %d", i); \ + perror("Error in write to file"); \ + } \ + } else if (fp == NULL) { \ + ALOGD("Could not write to file %s", m_filename);\ + } \ + if (fp) { \ + fclose(fp); \ + } \ +} +#else /* FILE_DUMP_ENABLE */ +#define INPUT_DUMP_PATH +#define INPUT_DUMP_EXT +#define OUTPUT_DUMP_PATH +#define OUTPUT_DUMP_EXT +#define GENERATE_FILE_NAMES() +#define CREATE_DUMP_FILE(m_filename) +#define DUMP_TO_FILE(m_filename, m_buf, m_size, m_offset) +#endif /* FILE_DUMP_ENABLE */ + +} // namespace android + +#endif // SOFT_H264_DEC_H_ diff --git a/media/libstagefright/codecs/avcdec/exports.lds b/media/libstagefright/codecs/avcdec/exports.lds new file mode 100644 index 0000000..e24f3fa --- /dev/null +++ b/media/libstagefright/codecs/avcdec/exports.lds @@ -0,0 +1,5 @@ +{ + global: + _Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE; + local: *; +}; diff --git a/media/libstagefright/codecs/avcenc/Android.bp b/media/libstagefright/codecs/avcenc/Android.bp new file mode 100644 index 0000000..586088c --- /dev/null +++ b/media/libstagefright/codecs/avcenc/Android.bp @@ -0,0 +1,34 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_av_media_libstagefright_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_av_media_libstagefright_license"], +} + +cc_library_shared { + name: "libstagefright_soft_avcenc", + defaults: ["libstagefright_softomx-defaults"], + + static_libs: ["libavcenc"], + srcs: ["SoftAVCEnc.cpp"], + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + ], + cfi: true, + config: { + cfi_assembly_support: true, + }, + }, + + cflags: [ + "-Wall", + "-Wno-unused-variable", + ], + ldflags: ["-Wl,-Bsymbolic"], + + version_script: "exports.lds", +} diff --git a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp new file mode 100644 index 0000000..01174c9 --- /dev/null +++ b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp @@ -0,0 +1,1515 @@ +/* + * Copyright 2015 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 "SoftAVCEnc" +#include +#include + +#include "OMX_Video.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "ih264_typedefs.h" +#include "iv2.h" +#include "ive2.h" +#include "ih264e.h" +#include "SoftAVCEnc.h" + +namespace android { + + #define ive_api_function ih264e_api_function + +template +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +struct LevelConversion { + OMX_VIDEO_AVCLEVELTYPE omxLevel; + WORD32 avcLevel; +}; + +static LevelConversion ConversionTable[] = { + { OMX_VIDEO_AVCLevel1, 10 }, + { OMX_VIDEO_AVCLevel1b, 9 }, + { OMX_VIDEO_AVCLevel11, 11 }, + { OMX_VIDEO_AVCLevel12, 12 }, + { OMX_VIDEO_AVCLevel13, 13 }, + { OMX_VIDEO_AVCLevel2, 20 }, + { OMX_VIDEO_AVCLevel21, 21 }, + { OMX_VIDEO_AVCLevel22, 22 }, + { OMX_VIDEO_AVCLevel3, 30 }, + { OMX_VIDEO_AVCLevel31, 31 }, + { OMX_VIDEO_AVCLevel32, 32 }, + { OMX_VIDEO_AVCLevel4, 40 }, + { OMX_VIDEO_AVCLevel41, 41 }, + { OMX_VIDEO_AVCLevel42, 42 }, + { OMX_VIDEO_AVCLevel5, 50 }, + { OMX_VIDEO_AVCLevel51, 51 }, +}; + +static const CodecProfileLevel kProfileLevels[] = { + { OMX_VIDEO_AVCProfileConstrainedBaseline, OMX_VIDEO_AVCLevel41 }, + + { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel41 }, + + { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel41 }, +}; + +static size_t GetCPUCoreCount() { + long 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(cpuCoreCount >= 1); + ALOGV("Number of CPU cores: %ld", cpuCoreCount); + return (size_t)cpuCoreCount; +} + +static status_t ConvertOmxAvcLevelToAvcSpecLevel( + OMX_VIDEO_AVCLEVELTYPE omxLevel, WORD32 *avcLevel) { + for (size_t i = 0; i < NELEM(ConversionTable); ++i) { + if (omxLevel == ConversionTable[i].omxLevel) { + *avcLevel = ConversionTable[i].avcLevel; + return OK; + } + } + + ALOGE("ConvertOmxAvcLevelToAvcSpecLevel: %d level not supported", + (int32_t)omxLevel); + + return BAD_VALUE; +} + +static status_t ConvertAvcSpecLevelToOmxAvcLevel( + WORD32 avcLevel, OMX_VIDEO_AVCLEVELTYPE *omxLevel) { + for (size_t i = 0; i < NELEM(ConversionTable); ++i) { + if (avcLevel == ConversionTable[i].avcLevel) { + *omxLevel = ConversionTable[i].omxLevel; + return OK; + } + } + + ALOGE("ConvertAvcSpecLevelToOmxAvcLevel: %d level not supported", + (int32_t)avcLevel); + + return BAD_VALUE; +} + + +SoftAVC::SoftAVC( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SoftVideoEncoderOMXComponent( + name, "video_encoder.avc", OMX_VIDEO_CodingAVC, + kProfileLevels, NELEM(kProfileLevels), + 176 /* width */, 144 /* height */, + callbacks, appData, component), + mUpdateFlag(0), + mIvVideoColorFormat(IV_YUV_420P), + mAVCEncProfile(IV_PROFILE_BASE), + mAVCEncLevel(41), + mStarted(false), + mSawInputEOS(false), + mSawOutputEOS(false), + mSignalledError(false), + mCodecCtx(NULL) { + + initPorts(kNumBuffers, kNumBuffers, ((mWidth * mHeight * 3) >> 1), + MEDIA_MIMETYPE_VIDEO_AVC, 2); + + // If dump is enabled, then open create an empty file + GENERATE_FILE_NAMES(); + CREATE_DUMP_FILE(mInFile); + CREATE_DUMP_FILE(mOutFile); + memset(mConversionBuffers, 0, sizeof(mConversionBuffers)); + memset(mInputBufferInfo, 0, sizeof(mInputBufferInfo)); + + initEncParams(); + +} + +SoftAVC::~SoftAVC() { + releaseEncoder(); + List &outQueue = getPortQueue(1); + List &inQueue = getPortQueue(0); + CHECK(outQueue.empty()); + CHECK(inQueue.empty()); +} + +void SoftAVC::initEncParams() { + mCodecCtx = NULL; + mMemRecords = NULL; + mNumMemRecords = DEFAULT_MEM_REC_CNT; + mHeaderGenerated = 0; + mNumCores = GetCPUCoreCount(); + mArch = DEFAULT_ARCH; + mSliceMode = DEFAULT_SLICE_MODE; + mSliceParam = DEFAULT_SLICE_PARAM; + mHalfPelEnable = DEFAULT_HPEL; + mIInterval = DEFAULT_I_INTERVAL; + mIDRInterval = DEFAULT_IDR_INTERVAL; + mDisableDeblkLevel = DEFAULT_DISABLE_DEBLK_LEVEL; + mEnableFastSad = DEFAULT_ENABLE_FAST_SAD; + mEnableAltRef = DEFAULT_ENABLE_ALT_REF; + mEncSpeed = DEFAULT_ENC_SPEED; + mIntra4x4 = DEFAULT_INTRA4x4; + mConstrainedIntraFlag = DEFAULT_CONSTRAINED_INTRA; + mAIRMode = DEFAULT_AIR; + mAIRRefreshPeriod = DEFAULT_AIR_REFRESH_PERIOD; + mPSNREnable = DEFAULT_PSNR_ENABLE; + mReconEnable = DEFAULT_RECON_ENABLE; + mEntropyMode = DEFAULT_ENTROPY_MODE; + mBframes = DEFAULT_B_FRAMES; + + gettimeofday(&mTimeStart, NULL); + gettimeofday(&mTimeEnd, NULL); + +} + + +OMX_ERRORTYPE SoftAVC::setDimensions() { + ive_ctl_set_dimensions_ip_t s_dimensions_ip; + ive_ctl_set_dimensions_op_t s_dimensions_op; + IV_STATUS_T status; + + s_dimensions_ip.e_cmd = IVE_CMD_VIDEO_CTL; + s_dimensions_ip.e_sub_cmd = IVE_CMD_CTL_SET_DIMENSIONS; + s_dimensions_ip.u4_ht = mHeight; + s_dimensions_ip.u4_wd = mWidth; + + s_dimensions_ip.u4_timestamp_high = -1; + s_dimensions_ip.u4_timestamp_low = -1; + + s_dimensions_ip.u4_size = sizeof(ive_ctl_set_dimensions_ip_t); + s_dimensions_op.u4_size = sizeof(ive_ctl_set_dimensions_op_t); + + status = ive_api_function(mCodecCtx, &s_dimensions_ip, &s_dimensions_op); + if (status != IV_SUCCESS) { + ALOGE("Unable to set frame dimensions = 0x%x\n", + s_dimensions_op.u4_error_code); + return OMX_ErrorUndefined; + } + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftAVC::setNumCores() { + IV_STATUS_T status; + ive_ctl_set_num_cores_ip_t s_num_cores_ip; + ive_ctl_set_num_cores_op_t s_num_cores_op; + s_num_cores_ip.e_cmd = IVE_CMD_VIDEO_CTL; + s_num_cores_ip.e_sub_cmd = IVE_CMD_CTL_SET_NUM_CORES; + s_num_cores_ip.u4_num_cores = MIN(mNumCores, CODEC_MAX_CORES); + s_num_cores_ip.u4_timestamp_high = -1; + s_num_cores_ip.u4_timestamp_low = -1; + s_num_cores_ip.u4_size = sizeof(ive_ctl_set_num_cores_ip_t); + + s_num_cores_op.u4_size = sizeof(ive_ctl_set_num_cores_op_t); + + status = ive_api_function( + mCodecCtx, (void *) &s_num_cores_ip, (void *) &s_num_cores_op); + if (status != IV_SUCCESS) { + ALOGE("Unable to set processor params = 0x%x\n", + s_num_cores_op.u4_error_code); + return OMX_ErrorUndefined; + } + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftAVC::setFrameRate() { + ive_ctl_set_frame_rate_ip_t s_frame_rate_ip; + ive_ctl_set_frame_rate_op_t s_frame_rate_op; + IV_STATUS_T status; + + s_frame_rate_ip.e_cmd = IVE_CMD_VIDEO_CTL; + s_frame_rate_ip.e_sub_cmd = IVE_CMD_CTL_SET_FRAMERATE; + + s_frame_rate_ip.u4_src_frame_rate = mFramerate >> 16; + s_frame_rate_ip.u4_tgt_frame_rate = mFramerate >> 16; + + s_frame_rate_ip.u4_timestamp_high = -1; + s_frame_rate_ip.u4_timestamp_low = -1; + + s_frame_rate_ip.u4_size = sizeof(ive_ctl_set_frame_rate_ip_t); + s_frame_rate_op.u4_size = sizeof(ive_ctl_set_frame_rate_op_t); + + status = ive_api_function(mCodecCtx, &s_frame_rate_ip, &s_frame_rate_op); + if (status != IV_SUCCESS) { + ALOGE("Unable to set frame rate = 0x%x\n", + s_frame_rate_op.u4_error_code); + return OMX_ErrorUndefined; + } + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftAVC::setIpeParams() { + ive_ctl_set_ipe_params_ip_t s_ipe_params_ip; + ive_ctl_set_ipe_params_op_t s_ipe_params_op; + IV_STATUS_T status; + + s_ipe_params_ip.e_cmd = IVE_CMD_VIDEO_CTL; + s_ipe_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_IPE_PARAMS; + + s_ipe_params_ip.u4_enable_intra_4x4 = mIntra4x4; + s_ipe_params_ip.u4_enc_speed_preset = mEncSpeed; + s_ipe_params_ip.u4_constrained_intra_pred = mConstrainedIntraFlag; + + s_ipe_params_ip.u4_timestamp_high = -1; + s_ipe_params_ip.u4_timestamp_low = -1; + + s_ipe_params_ip.u4_size = sizeof(ive_ctl_set_ipe_params_ip_t); + s_ipe_params_op.u4_size = sizeof(ive_ctl_set_ipe_params_op_t); + + status = ive_api_function(mCodecCtx, &s_ipe_params_ip, &s_ipe_params_op); + if (status != IV_SUCCESS) { + ALOGE("Unable to set ipe params = 0x%x\n", + s_ipe_params_op.u4_error_code); + return OMX_ErrorUndefined; + } + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftAVC::setBitRate() { + ive_ctl_set_bitrate_ip_t s_bitrate_ip; + ive_ctl_set_bitrate_op_t s_bitrate_op; + IV_STATUS_T status; + + s_bitrate_ip.e_cmd = IVE_CMD_VIDEO_CTL; + s_bitrate_ip.e_sub_cmd = IVE_CMD_CTL_SET_BITRATE; + + s_bitrate_ip.u4_target_bitrate = mBitrate; + + s_bitrate_ip.u4_timestamp_high = -1; + s_bitrate_ip.u4_timestamp_low = -1; + + s_bitrate_ip.u4_size = sizeof(ive_ctl_set_bitrate_ip_t); + s_bitrate_op.u4_size = sizeof(ive_ctl_set_bitrate_op_t); + + status = ive_api_function(mCodecCtx, &s_bitrate_ip, &s_bitrate_op); + if (status != IV_SUCCESS) { + ALOGE("Unable to set bit rate = 0x%x\n", s_bitrate_op.u4_error_code); + return OMX_ErrorUndefined; + } + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftAVC::setFrameType(IV_PICTURE_CODING_TYPE_T e_frame_type) { + ive_ctl_set_frame_type_ip_t s_frame_type_ip; + ive_ctl_set_frame_type_op_t s_frame_type_op; + IV_STATUS_T status; + s_frame_type_ip.e_cmd = IVE_CMD_VIDEO_CTL; + s_frame_type_ip.e_sub_cmd = IVE_CMD_CTL_SET_FRAMETYPE; + + s_frame_type_ip.e_frame_type = e_frame_type; + + s_frame_type_ip.u4_timestamp_high = -1; + s_frame_type_ip.u4_timestamp_low = -1; + + s_frame_type_ip.u4_size = sizeof(ive_ctl_set_frame_type_ip_t); + s_frame_type_op.u4_size = sizeof(ive_ctl_set_frame_type_op_t); + + status = ive_api_function(mCodecCtx, &s_frame_type_ip, &s_frame_type_op); + if (status != IV_SUCCESS) { + ALOGE("Unable to set frame type = 0x%x\n", + s_frame_type_op.u4_error_code); + return OMX_ErrorUndefined; + } + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftAVC::setQp() { + ive_ctl_set_qp_ip_t s_qp_ip; + ive_ctl_set_qp_op_t s_qp_op; + IV_STATUS_T status; + + s_qp_ip.e_cmd = IVE_CMD_VIDEO_CTL; + s_qp_ip.e_sub_cmd = IVE_CMD_CTL_SET_QP; + + s_qp_ip.u4_i_qp = DEFAULT_I_QP; + s_qp_ip.u4_i_qp_max = DEFAULT_QP_MAX; + s_qp_ip.u4_i_qp_min = DEFAULT_QP_MIN; + + s_qp_ip.u4_p_qp = DEFAULT_P_QP; + s_qp_ip.u4_p_qp_max = DEFAULT_QP_MAX; + s_qp_ip.u4_p_qp_min = DEFAULT_QP_MIN; + + s_qp_ip.u4_b_qp = DEFAULT_P_QP; + s_qp_ip.u4_b_qp_max = DEFAULT_QP_MAX; + s_qp_ip.u4_b_qp_min = DEFAULT_QP_MIN; + + s_qp_ip.u4_timestamp_high = -1; + s_qp_ip.u4_timestamp_low = -1; + + s_qp_ip.u4_size = sizeof(ive_ctl_set_qp_ip_t); + s_qp_op.u4_size = sizeof(ive_ctl_set_qp_op_t); + + status = ive_api_function(mCodecCtx, &s_qp_ip, &s_qp_op); + if (status != IV_SUCCESS) { + ALOGE("Unable to set qp 0x%x\n", s_qp_op.u4_error_code); + return OMX_ErrorUndefined; + } + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftAVC::setEncMode(IVE_ENC_MODE_T e_enc_mode) { + IV_STATUS_T status; + ive_ctl_set_enc_mode_ip_t s_enc_mode_ip; + ive_ctl_set_enc_mode_op_t s_enc_mode_op; + + s_enc_mode_ip.e_cmd = IVE_CMD_VIDEO_CTL; + s_enc_mode_ip.e_sub_cmd = IVE_CMD_CTL_SET_ENC_MODE; + + s_enc_mode_ip.e_enc_mode = e_enc_mode; + + s_enc_mode_ip.u4_timestamp_high = -1; + s_enc_mode_ip.u4_timestamp_low = -1; + + s_enc_mode_ip.u4_size = sizeof(ive_ctl_set_enc_mode_ip_t); + s_enc_mode_op.u4_size = sizeof(ive_ctl_set_enc_mode_op_t); + + status = ive_api_function(mCodecCtx, &s_enc_mode_ip, &s_enc_mode_op); + if (status != IV_SUCCESS) { + ALOGE("Unable to set in header encode mode = 0x%x\n", + s_enc_mode_op.u4_error_code); + return OMX_ErrorUndefined; + } + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftAVC::setVbvParams() { + ive_ctl_set_vbv_params_ip_t s_vbv_ip; + ive_ctl_set_vbv_params_op_t s_vbv_op; + IV_STATUS_T status; + + s_vbv_ip.e_cmd = IVE_CMD_VIDEO_CTL; + s_vbv_ip.e_sub_cmd = IVE_CMD_CTL_SET_VBV_PARAMS; + + s_vbv_ip.u4_vbv_buf_size = 0; + s_vbv_ip.u4_vbv_buffer_delay = 1000; + + s_vbv_ip.u4_timestamp_high = -1; + s_vbv_ip.u4_timestamp_low = -1; + + s_vbv_ip.u4_size = sizeof(ive_ctl_set_vbv_params_ip_t); + s_vbv_op.u4_size = sizeof(ive_ctl_set_vbv_params_op_t); + + status = ive_api_function(mCodecCtx, &s_vbv_ip, &s_vbv_op); + if (status != IV_SUCCESS) { + ALOGE("Unable to set VBC params = 0x%x\n", s_vbv_op.u4_error_code); + return OMX_ErrorUndefined; + } + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftAVC::setAirParams() { + ive_ctl_set_air_params_ip_t s_air_ip; + ive_ctl_set_air_params_op_t s_air_op; + IV_STATUS_T status; + + s_air_ip.e_cmd = IVE_CMD_VIDEO_CTL; + s_air_ip.e_sub_cmd = IVE_CMD_CTL_SET_AIR_PARAMS; + + s_air_ip.e_air_mode = mAIRMode; + s_air_ip.u4_air_refresh_period = mAIRRefreshPeriod; + + s_air_ip.u4_timestamp_high = -1; + s_air_ip.u4_timestamp_low = -1; + + s_air_ip.u4_size = sizeof(ive_ctl_set_air_params_ip_t); + s_air_op.u4_size = sizeof(ive_ctl_set_air_params_op_t); + + status = ive_api_function(mCodecCtx, &s_air_ip, &s_air_op); + if (status != IV_SUCCESS) { + ALOGE("Unable to set air params = 0x%x\n", s_air_op.u4_error_code); + return OMX_ErrorUndefined; + } + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftAVC::setMeParams() { + IV_STATUS_T status; + ive_ctl_set_me_params_ip_t s_me_params_ip; + ive_ctl_set_me_params_op_t s_me_params_op; + + s_me_params_ip.e_cmd = IVE_CMD_VIDEO_CTL; + s_me_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_ME_PARAMS; + + s_me_params_ip.u4_enable_fast_sad = mEnableFastSad; + s_me_params_ip.u4_enable_alt_ref = mEnableAltRef; + + s_me_params_ip.u4_enable_hpel = mHalfPelEnable; + s_me_params_ip.u4_enable_qpel = DEFAULT_QPEL; + s_me_params_ip.u4_me_speed_preset = DEFAULT_ME_SPEED; + s_me_params_ip.u4_srch_rng_x = DEFAULT_SRCH_RNG_X; + s_me_params_ip.u4_srch_rng_y = DEFAULT_SRCH_RNG_Y; + + s_me_params_ip.u4_timestamp_high = -1; + s_me_params_ip.u4_timestamp_low = -1; + + s_me_params_ip.u4_size = sizeof(ive_ctl_set_me_params_ip_t); + s_me_params_op.u4_size = sizeof(ive_ctl_set_me_params_op_t); + + status = ive_api_function(mCodecCtx, &s_me_params_ip, &s_me_params_op); + if (status != IV_SUCCESS) { + ALOGE("Unable to set me params = 0x%x\n", s_me_params_op.u4_error_code); + return OMX_ErrorUndefined; + } + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftAVC::setGopParams() { + IV_STATUS_T status; + ive_ctl_set_gop_params_ip_t s_gop_params_ip; + ive_ctl_set_gop_params_op_t s_gop_params_op; + + s_gop_params_ip.e_cmd = IVE_CMD_VIDEO_CTL; + s_gop_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_GOP_PARAMS; + + s_gop_params_ip.u4_i_frm_interval = mIInterval; + s_gop_params_ip.u4_idr_frm_interval = mIDRInterval; + + s_gop_params_ip.u4_timestamp_high = -1; + s_gop_params_ip.u4_timestamp_low = -1; + + s_gop_params_ip.u4_size = sizeof(ive_ctl_set_gop_params_ip_t); + s_gop_params_op.u4_size = sizeof(ive_ctl_set_gop_params_op_t); + + status = ive_api_function(mCodecCtx, &s_gop_params_ip, &s_gop_params_op); + if (status != IV_SUCCESS) { + ALOGE("Unable to set ME params = 0x%x\n", + s_gop_params_op.u4_error_code); + return OMX_ErrorUndefined; + } + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftAVC::setProfileParams() { + IV_STATUS_T status; + ive_ctl_set_profile_params_ip_t s_profile_params_ip; + ive_ctl_set_profile_params_op_t s_profile_params_op; + + s_profile_params_ip.e_cmd = IVE_CMD_VIDEO_CTL; + s_profile_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_PROFILE_PARAMS; + + s_profile_params_ip.e_profile = DEFAULT_EPROFILE; + s_profile_params_ip.u4_entropy_coding_mode = mEntropyMode; + s_profile_params_ip.u4_timestamp_high = -1; + s_profile_params_ip.u4_timestamp_low = -1; + + s_profile_params_ip.u4_size = sizeof(ive_ctl_set_profile_params_ip_t); + s_profile_params_op.u4_size = sizeof(ive_ctl_set_profile_params_op_t); + + status = ive_api_function(mCodecCtx, &s_profile_params_ip, &s_profile_params_op); + if (status != IV_SUCCESS) { + ALOGE("Unable to set profile params = 0x%x\n", + s_profile_params_op.u4_error_code); + return OMX_ErrorUndefined; + } + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftAVC::setDeblockParams() { + IV_STATUS_T status; + ive_ctl_set_deblock_params_ip_t s_deblock_params_ip; + ive_ctl_set_deblock_params_op_t s_deblock_params_op; + + s_deblock_params_ip.e_cmd = IVE_CMD_VIDEO_CTL; + s_deblock_params_ip.e_sub_cmd = IVE_CMD_CTL_SET_DEBLOCK_PARAMS; + + s_deblock_params_ip.u4_disable_deblock_level = mDisableDeblkLevel; + + s_deblock_params_ip.u4_timestamp_high = -1; + s_deblock_params_ip.u4_timestamp_low = -1; + + s_deblock_params_ip.u4_size = sizeof(ive_ctl_set_deblock_params_ip_t); + s_deblock_params_op.u4_size = sizeof(ive_ctl_set_deblock_params_op_t); + + status = ive_api_function(mCodecCtx, &s_deblock_params_ip, &s_deblock_params_op); + if (status != IV_SUCCESS) { + ALOGE("Unable to enable/disable deblock params = 0x%x\n", + s_deblock_params_op.u4_error_code); + return OMX_ErrorUndefined; + } + return OMX_ErrorNone; +} + +void SoftAVC::logVersion() { + ive_ctl_getversioninfo_ip_t s_ctl_ip; + ive_ctl_getversioninfo_op_t s_ctl_op; + UWORD8 au1_buf[512]; + IV_STATUS_T status; + + s_ctl_ip.e_cmd = IVE_CMD_VIDEO_CTL; + s_ctl_ip.e_sub_cmd = IVE_CMD_CTL_GETVERSION; + s_ctl_ip.u4_size = sizeof(ive_ctl_getversioninfo_ip_t); + s_ctl_op.u4_size = sizeof(ive_ctl_getversioninfo_op_t); + s_ctl_ip.pu1_version = au1_buf; + s_ctl_ip.u4_version_bufsize = sizeof(au1_buf); + + status = ive_api_function(mCodecCtx, (void *) &s_ctl_ip, (void *) &s_ctl_op); + + if (status != IV_SUCCESS) { + ALOGE("Error in getting version: 0x%x", s_ctl_op.u4_error_code); + } else { + ALOGV("Ittiam encoder version: %s", (char *)s_ctl_ip.pu1_version); + } + return; +} + +OMX_ERRORTYPE SoftAVC::initEncoder() { + IV_STATUS_T status; + WORD32 level; + uint32_t displaySizeY; + + CHECK(!mStarted); + + OMX_ERRORTYPE errType = OMX_ErrorNone; + + displaySizeY = mWidth * mHeight; + if (displaySizeY > (1920 * 1088)) { + level = 50; + } else if (displaySizeY > (1280 * 720)) { + level = 40; + } else if (displaySizeY > (720 * 576)) { + level = 31; + } else if (displaySizeY > (624 * 320)) { + level = 30; + } else if (displaySizeY > (352 * 288)) { + level = 21; + } else if (displaySizeY > (176 * 144)) { + level = 20; + } else { + level = 10; + } + mAVCEncLevel = MAX(level, mAVCEncLevel); + + mStride = mWidth; + + if (mInputDataIsMeta) { + for (size_t i = 0; i < MAX_CONVERSION_BUFFERS; i++) { + if (mConversionBuffers[i] != NULL) { + free(mConversionBuffers[i]); + mConversionBuffers[i] = 0; + } + + if (((uint64_t)mStride * mHeight) > ((uint64_t)INT32_MAX / 3)) { + ALOGE("Buffer size is too big."); + return OMX_ErrorUndefined; + } + mConversionBuffers[i] = (uint8_t *)malloc(mStride * mHeight * 3 / 2); + + if (mConversionBuffers[i] == NULL) { + ALOGE("Allocating conversion buffer failed."); + return OMX_ErrorUndefined; + } + + mConversionBuffersFree[i] = 1; + } + } + + switch (mColorFormat) { + case OMX_COLOR_FormatYUV420SemiPlanar: + mIvVideoColorFormat = IV_YUV_420SP_UV; + ALOGV("colorFormat YUV_420SP"); + break; + default: + case OMX_COLOR_FormatYUV420Planar: + mIvVideoColorFormat = IV_YUV_420P; + ALOGV("colorFormat YUV_420P"); + break; + } + + ALOGD("Params width %d height %d level %d colorFormat %d", mWidth, + mHeight, mAVCEncLevel, mIvVideoColorFormat); + + /* Getting Number of MemRecords */ + { + iv_num_mem_rec_ip_t s_num_mem_rec_ip; + iv_num_mem_rec_op_t s_num_mem_rec_op; + + s_num_mem_rec_ip.u4_size = sizeof(iv_num_mem_rec_ip_t); + s_num_mem_rec_op.u4_size = sizeof(iv_num_mem_rec_op_t); + + s_num_mem_rec_ip.e_cmd = IV_CMD_GET_NUM_MEM_REC; + + status = ive_api_function(0, &s_num_mem_rec_ip, &s_num_mem_rec_op); + + if (status != IV_SUCCESS) { + ALOGE("Get number of memory records failed = 0x%x\n", + s_num_mem_rec_op.u4_error_code); + return OMX_ErrorUndefined; + } + + mNumMemRecords = s_num_mem_rec_op.u4_num_mem_rec; + } + + /* Allocate array to hold memory records */ + if (mNumMemRecords > SIZE_MAX / sizeof(iv_mem_rec_t)) { + ALOGE("requested memory size is too big."); + return OMX_ErrorUndefined; + } + mMemRecords = (iv_mem_rec_t *)malloc(mNumMemRecords * sizeof(iv_mem_rec_t)); + if (NULL == mMemRecords) { + ALOGE("Unable to allocate memory for hold memory records: Size %zu", + mNumMemRecords * sizeof(iv_mem_rec_t)); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); + return OMX_ErrorUndefined; + } + + { + iv_mem_rec_t *ps_mem_rec; + ps_mem_rec = mMemRecords; + for (size_t i = 0; i < mNumMemRecords; i++) { + ps_mem_rec->u4_size = sizeof(iv_mem_rec_t); + ps_mem_rec->pv_base = NULL; + ps_mem_rec->u4_mem_size = 0; + ps_mem_rec->u4_mem_alignment = 0; + ps_mem_rec->e_mem_type = IV_NA_MEM_TYPE; + + ps_mem_rec++; + } + } + + /* Getting MemRecords Attributes */ + { + iv_fill_mem_rec_ip_t s_fill_mem_rec_ip; + iv_fill_mem_rec_op_t s_fill_mem_rec_op; + + s_fill_mem_rec_ip.u4_size = sizeof(iv_fill_mem_rec_ip_t); + s_fill_mem_rec_op.u4_size = sizeof(iv_fill_mem_rec_op_t); + + s_fill_mem_rec_ip.e_cmd = IV_CMD_FILL_NUM_MEM_REC; + s_fill_mem_rec_ip.ps_mem_rec = mMemRecords; + s_fill_mem_rec_ip.u4_num_mem_rec = mNumMemRecords; + s_fill_mem_rec_ip.u4_max_wd = mWidth; + s_fill_mem_rec_ip.u4_max_ht = mHeight; + s_fill_mem_rec_ip.u4_max_level = mAVCEncLevel; + s_fill_mem_rec_ip.e_color_format = DEFAULT_INP_COLOR_FORMAT; + s_fill_mem_rec_ip.u4_max_ref_cnt = DEFAULT_MAX_REF_FRM; + s_fill_mem_rec_ip.u4_max_reorder_cnt = DEFAULT_MAX_REORDER_FRM; + s_fill_mem_rec_ip.u4_max_srch_rng_x = DEFAULT_MAX_SRCH_RANGE_X; + s_fill_mem_rec_ip.u4_max_srch_rng_y = DEFAULT_MAX_SRCH_RANGE_Y; + + status = ive_api_function(0, &s_fill_mem_rec_ip, &s_fill_mem_rec_op); + + if (status != IV_SUCCESS) { + ALOGE("Fill memory records failed = 0x%x\n", + s_fill_mem_rec_op.u4_error_code); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); + return OMX_ErrorUndefined; + } + } + + /* Allocating Memory for Mem Records */ + { + WORD32 total_size; + iv_mem_rec_t *ps_mem_rec; + total_size = 0; + ps_mem_rec = mMemRecords; + + for (size_t i = 0; i < mNumMemRecords; i++) { + ps_mem_rec->pv_base = ive_aligned_malloc( + ps_mem_rec->u4_mem_alignment, ps_mem_rec->u4_mem_size); + if (ps_mem_rec->pv_base == NULL) { + ALOGE("Allocation failure for mem record id %zu size %u\n", i, + ps_mem_rec->u4_mem_size); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); + return OMX_ErrorUndefined; + + } + total_size += ps_mem_rec->u4_mem_size; + + ps_mem_rec++; + } + } + + /* Codec Instance Creation */ + { + ive_init_ip_t s_init_ip; + ive_init_op_t s_init_op; + + mCodecCtx = (iv_obj_t *)mMemRecords[0].pv_base; + mCodecCtx->u4_size = sizeof(iv_obj_t); + mCodecCtx->pv_fxns = (void *)ive_api_function; + + s_init_ip.u4_size = sizeof(ive_init_ip_t); + s_init_op.u4_size = sizeof(ive_init_op_t); + + s_init_ip.e_cmd = IV_CMD_INIT; + s_init_ip.u4_num_mem_rec = mNumMemRecords; + s_init_ip.ps_mem_rec = mMemRecords; + s_init_ip.u4_max_wd = mWidth; + s_init_ip.u4_max_ht = mHeight; + s_init_ip.u4_max_ref_cnt = DEFAULT_MAX_REF_FRM; + s_init_ip.u4_max_reorder_cnt = DEFAULT_MAX_REORDER_FRM; + s_init_ip.u4_max_level = mAVCEncLevel; + s_init_ip.e_inp_color_fmt = mIvVideoColorFormat; + + if (mReconEnable || mPSNREnable) { + s_init_ip.u4_enable_recon = 1; + } else { + s_init_ip.u4_enable_recon = 0; + } + s_init_ip.e_recon_color_fmt = DEFAULT_RECON_COLOR_FORMAT; + s_init_ip.e_rc_mode = DEFAULT_RC_MODE; + s_init_ip.u4_max_framerate = DEFAULT_MAX_FRAMERATE; + s_init_ip.u4_max_bitrate = DEFAULT_MAX_BITRATE; + s_init_ip.u4_num_bframes = mBframes; + s_init_ip.e_content_type = IV_PROGRESSIVE; + s_init_ip.u4_max_srch_rng_x = DEFAULT_MAX_SRCH_RANGE_X; + s_init_ip.u4_max_srch_rng_y = DEFAULT_MAX_SRCH_RANGE_Y; + s_init_ip.e_slice_mode = mSliceMode; + s_init_ip.u4_slice_param = mSliceParam; + s_init_ip.e_arch = mArch; + s_init_ip.e_soc = DEFAULT_SOC; + + status = ive_api_function(mCodecCtx, &s_init_ip, &s_init_op); + + if (status != IV_SUCCESS) { + ALOGE("Init memory records failed = 0x%x\n", + s_init_op.u4_error_code); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0 /* arg2 */, NULL /* data */); + return OMX_ErrorUndefined; + } + } + + /* Get Codec Version */ + logVersion(); + + /* set processor details */ + setNumCores(); + + /* Video control Set Frame dimensions */ + setDimensions(); + + /* Video control Set Frame rates */ + setFrameRate(); + + /* Video control Set IPE Params */ + setIpeParams(); + + /* Video control Set Bitrate */ + setBitRate(); + + /* Video control Set QP */ + setQp(); + + /* Video control Set AIR params */ + setAirParams(); + + /* Video control Set VBV params */ + setVbvParams(); + + /* Video control Set Motion estimation params */ + setMeParams(); + + /* Video control Set GOP params */ + setGopParams(); + + /* Video control Set Deblock params */ + setDeblockParams(); + + /* Video control Set Profile params */ + setProfileParams(); + + /* Video control Set in Encode header mode */ + setEncMode(IVE_ENC_MODE_HEADER); + + ALOGV("init_codec successfull"); + + mSpsPpsHeaderReceived = false; + mStarted = true; + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftAVC::releaseEncoder() { + IV_STATUS_T status = IV_SUCCESS; + iv_retrieve_mem_rec_ip_t s_retrieve_mem_ip; + iv_retrieve_mem_rec_op_t s_retrieve_mem_op; + iv_mem_rec_t *ps_mem_rec; + + if (!mStarted) { + return OMX_ErrorNone; + } + + s_retrieve_mem_ip.u4_size = sizeof(iv_retrieve_mem_rec_ip_t); + s_retrieve_mem_op.u4_size = sizeof(iv_retrieve_mem_rec_op_t); + s_retrieve_mem_ip.e_cmd = IV_CMD_RETRIEVE_MEMREC; + s_retrieve_mem_ip.ps_mem_rec = mMemRecords; + + status = ive_api_function(mCodecCtx, &s_retrieve_mem_ip, &s_retrieve_mem_op); + + if (status != IV_SUCCESS) { + ALOGE("Unable to retrieve memory records = 0x%x\n", + s_retrieve_mem_op.u4_error_code); + return OMX_ErrorUndefined; + } + + /* Free memory records */ + ps_mem_rec = mMemRecords; + for (size_t i = 0; i < s_retrieve_mem_op.u4_num_mem_rec_filled; i++) { + ive_aligned_free(ps_mem_rec->pv_base); + ps_mem_rec++; + } + + free(mMemRecords); + + for (size_t i = 0; i < MAX_CONVERSION_BUFFERS; i++) { + if (mConversionBuffers[i]) { + free(mConversionBuffers[i]); + mConversionBuffers[i] = NULL; + } + } + + // clear other pointers into the space being free()d + mCodecCtx = NULL; + + mStarted = false; + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftAVC::internalGetParameter(OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamVideoBitrate: + { + OMX_VIDEO_PARAM_BITRATETYPE *bitRate = + (OMX_VIDEO_PARAM_BITRATETYPE *)params; + + if (!isValidOMXParam(bitRate)) { + return OMX_ErrorBadParameter; + } + + if (bitRate->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + bitRate->eControlRate = OMX_Video_ControlRateVariable; + bitRate->nTargetBitrate = mBitrate; + return OMX_ErrorNone; + } + + case OMX_IndexParamVideoAvc: + { + OMX_VIDEO_PARAM_AVCTYPE *avcParams = (OMX_VIDEO_PARAM_AVCTYPE *)params; + + if (!isValidOMXParam(avcParams)) { + return OMX_ErrorBadParameter; + } + + if (avcParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + OMX_VIDEO_AVCLEVELTYPE omxLevel = OMX_VIDEO_AVCLevel41; + if (OMX_ErrorNone + != ConvertAvcSpecLevelToOmxAvcLevel(mAVCEncLevel, &omxLevel)) { + return OMX_ErrorUndefined; + } + + // TODO: maintain profile + avcParams->eProfile = (OMX_VIDEO_AVCPROFILETYPE)OMX_VIDEO_AVCProfileConstrainedBaseline; + avcParams->eLevel = omxLevel; + avcParams->nRefFrames = 1; + avcParams->bUseHadamard = OMX_TRUE; + avcParams->nAllowedPictureTypes = (OMX_VIDEO_PictureTypeI + | OMX_VIDEO_PictureTypeP | OMX_VIDEO_PictureTypeB); + avcParams->nRefIdx10ActiveMinus1 = 0; + avcParams->nRefIdx11ActiveMinus1 = 0; + avcParams->bWeightedPPrediction = OMX_FALSE; + avcParams->bconstIpred = OMX_FALSE; + avcParams->bDirect8x8Inference = OMX_FALSE; + avcParams->bDirectSpatialTemporal = OMX_FALSE; + avcParams->nCabacInitIdc = 0; + return OMX_ErrorNone; + } + + default: + return SoftVideoEncoderOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftAVC::internalSetParameter(OMX_INDEXTYPE index, const OMX_PTR params) { + int32_t indexFull = index; + + switch (indexFull) { + case OMX_IndexParamVideoBitrate: + { + OMX_VIDEO_PARAM_BITRATETYPE *bitRate = + (OMX_VIDEO_PARAM_BITRATETYPE *)params; + + if (!isValidOMXParam(bitRate)) { + return OMX_ErrorBadParameter; + } + + return internalSetBitrateParams(bitRate); + } + + case OMX_IndexParamVideoAvc: + { + OMX_VIDEO_PARAM_AVCTYPE *avcType = (OMX_VIDEO_PARAM_AVCTYPE *)params; + + if (!isValidOMXParam(avcType)) { + return OMX_ErrorBadParameter; + } + + if (avcType->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + mEntropyMode = 0; + + if (OMX_TRUE == avcType->bEntropyCodingCABAC) + mEntropyMode = 1; + + if ((avcType->nAllowedPictureTypes & OMX_VIDEO_PictureTypeB) && + avcType->nPFrames) { + mBframes = avcType->nBFrames; + } + + mIInterval = (avcType->nPFrames + 1) * (avcType->nBFrames + 1); + mConstrainedIntraFlag = avcType->bconstIpred; + + if (OMX_VIDEO_AVCLoopFilterDisable == avcType->eLoopFilterMode) + mDisableDeblkLevel = 4; + + if (avcType->nRefFrames != 1 + || avcType->bUseHadamard != OMX_TRUE + || avcType->nRefIdx10ActiveMinus1 != 0 + || avcType->nRefIdx11ActiveMinus1 != 0 + || avcType->bWeightedPPrediction != OMX_FALSE + || avcType->bDirect8x8Inference != OMX_FALSE + || avcType->bDirectSpatialTemporal != OMX_FALSE + || avcType->nCabacInitIdc != 0) { + // OMX does not allow a way to signal what values are wrong, so it's + // best for components to just do best effort in supporting these values + ALOGV("ignoring unsupported settings"); + } + + if (OK != ConvertOmxAvcLevelToAvcSpecLevel(avcType->eLevel, &mAVCEncLevel)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + default: + return SoftVideoEncoderOMXComponent::internalSetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftAVC::getConfig( + OMX_INDEXTYPE index, OMX_PTR _params) { + switch ((int)index) { + case OMX_IndexConfigAndroidIntraRefresh: + { + OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE *intraRefreshParams = + (OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE *)_params; + + if (!isValidOMXParam(intraRefreshParams)) { + return OMX_ErrorBadParameter; + } + + if (intraRefreshParams->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUndefined; + } + + intraRefreshParams->nRefreshPeriod = + (mAIRMode == IVE_AIR_MODE_NONE) ? 0 : mAIRRefreshPeriod; + return OMX_ErrorNone; + } + + default: + return SoftVideoEncoderOMXComponent::getConfig(index, _params); + } +} + +OMX_ERRORTYPE SoftAVC::internalSetConfig( + OMX_INDEXTYPE index, const OMX_PTR _params, bool *frameConfig) { + switch ((int)index) { + case OMX_IndexConfigVideoIntraVOPRefresh: + { + OMX_CONFIG_INTRAREFRESHVOPTYPE *params = + (OMX_CONFIG_INTRAREFRESHVOPTYPE *)_params; + + if (!isValidOMXParam(params)) { + return OMX_ErrorBadParameter; + } + + if (params->nPortIndex != kOutputPortIndex) { + return OMX_ErrorBadPortIndex; + } + + if (params->IntraRefreshVOP) { + mUpdateFlag |= kRequestKeyFrame; + } + return OMX_ErrorNone; + } + + case OMX_IndexConfigVideoBitrate: + { + OMX_VIDEO_CONFIG_BITRATETYPE *params = + (OMX_VIDEO_CONFIG_BITRATETYPE *)_params; + + if (!isValidOMXParam(params)) { + return OMX_ErrorBadParameter; + } + + if (params->nPortIndex != kOutputPortIndex) { + return OMX_ErrorBadPortIndex; + } + + if (mBitrate != params->nEncodeBitrate) { + mBitrate = params->nEncodeBitrate; + mUpdateFlag |= kUpdateBitrate; + } + return OMX_ErrorNone; + } + + case OMX_IndexConfigAndroidIntraRefresh: + { + const OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE *intraRefreshParams = + (const OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE *)_params; + + if (!isValidOMXParam(intraRefreshParams)) { + return OMX_ErrorBadParameter; + } + + if (intraRefreshParams->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUndefined; + } + + if (intraRefreshParams->nRefreshPeriod == 0) { + mAIRMode = IVE_AIR_MODE_NONE; + mAIRRefreshPeriod = 0; + } else if (intraRefreshParams->nRefreshPeriod > 0) { + mAIRMode = IVE_AIR_MODE_CYCLIC; + mAIRRefreshPeriod = intraRefreshParams->nRefreshPeriod; + } + mUpdateFlag |= kUpdateAIRMode; + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetConfig(index, _params, frameConfig); + } +} + +OMX_ERRORTYPE SoftAVC::internalSetBitrateParams( + const OMX_VIDEO_PARAM_BITRATETYPE *bitrate) { + if (bitrate->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUnsupportedIndex; + } + + mBitrate = bitrate->nTargetBitrate; + mUpdateFlag |= kUpdateBitrate; + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftAVC::setEncodeArgs( + ive_video_encode_ip_t *ps_encode_ip, + ive_video_encode_op_t *ps_encode_op, + OMX_BUFFERHEADERTYPE *inputBufferHeader, + OMX_BUFFERHEADERTYPE *outputBufferHeader) { + iv_raw_buf_t *ps_inp_raw_buf; + const uint8_t *source; + UWORD8 *pu1_buf; + + ps_inp_raw_buf = &ps_encode_ip->s_inp_buf; + ps_encode_ip->s_out_buf.pv_buf = outputBufferHeader->pBuffer; + ps_encode_ip->s_out_buf.u4_bytes = 0; + ps_encode_ip->s_out_buf.u4_bufsize = outputBufferHeader->nAllocLen; + ps_encode_ip->u4_size = sizeof(ive_video_encode_ip_t); + ps_encode_op->u4_size = sizeof(ive_video_encode_op_t); + + ps_encode_ip->e_cmd = IVE_CMD_VIDEO_ENCODE; + ps_encode_ip->pv_bufs = NULL; + ps_encode_ip->pv_mb_info = NULL; + ps_encode_ip->pv_pic_info = NULL; + ps_encode_ip->u4_mb_info_type = 0; + ps_encode_ip->u4_pic_info_type = 0; + ps_encode_op->s_out_buf.pv_buf = NULL; + + /* Initialize color formats */ + ps_inp_raw_buf->e_color_fmt = mIvVideoColorFormat; + source = NULL; + if ((inputBufferHeader != NULL) && inputBufferHeader->nFilledLen) { + OMX_ERRORTYPE error = validateInputBuffer(inputBufferHeader); + if (error != OMX_ErrorNone) { + ALOGE("b/69065651"); + android_errorWriteLog(0x534e4554, "69065651"); + return error; + } + source = inputBufferHeader->pBuffer + inputBufferHeader->nOffset; + + if (mInputDataIsMeta) { + uint8_t *conversionBuffer = NULL; + for (size_t i = 0; i < MAX_CONVERSION_BUFFERS; i++) { + if (mConversionBuffersFree[i]) { + mConversionBuffersFree[i] = 0; + conversionBuffer = mConversionBuffers[i]; + break; + } + } + + if (NULL == conversionBuffer) { + ALOGE("No free buffers to hold conversion data"); + return OMX_ErrorUndefined; + } + + source = extractGraphicBuffer( + conversionBuffer, (mWidth * mHeight * 3 / 2), source, + inputBufferHeader->nFilledLen, mWidth, mHeight); + + if (source == NULL) { + ALOGE("Error in extractGraphicBuffer"); + notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); + return OMX_ErrorUndefined; + } + } + ps_encode_ip->u4_is_last = 0; + ps_encode_ip->u4_timestamp_high = (inputBufferHeader->nTimeStamp) >> 32; + ps_encode_ip->u4_timestamp_low = (inputBufferHeader->nTimeStamp) & 0xFFFFFFFF; + } + else { + if (mSawInputEOS){ + ps_encode_ip->u4_is_last = 1; + } + memset(ps_inp_raw_buf, 0, sizeof(iv_raw_buf_t)); + ps_inp_raw_buf->e_color_fmt = mIvVideoColorFormat; + ps_inp_raw_buf->u4_size = sizeof(iv_raw_buf_t); + return OMX_ErrorNone; + } + + pu1_buf = (UWORD8 *)source; + switch (mIvVideoColorFormat) { + case IV_YUV_420P: + { + ps_inp_raw_buf->apv_bufs[0] = pu1_buf; + pu1_buf += (mStride) * mHeight; + ps_inp_raw_buf->apv_bufs[1] = pu1_buf; + pu1_buf += (mStride / 2) * mHeight / 2; + ps_inp_raw_buf->apv_bufs[2] = pu1_buf; + + ps_inp_raw_buf->au4_wd[0] = mWidth; + ps_inp_raw_buf->au4_wd[1] = mWidth / 2; + ps_inp_raw_buf->au4_wd[2] = mWidth / 2; + + ps_inp_raw_buf->au4_ht[0] = mHeight; + ps_inp_raw_buf->au4_ht[1] = mHeight / 2; + ps_inp_raw_buf->au4_ht[2] = mHeight / 2; + + ps_inp_raw_buf->au4_strd[0] = mStride; + ps_inp_raw_buf->au4_strd[1] = (mStride / 2); + ps_inp_raw_buf->au4_strd[2] = (mStride / 2); + break; + } + + case IV_YUV_422ILE: + { + ps_inp_raw_buf->apv_bufs[0] = pu1_buf; + ps_inp_raw_buf->au4_wd[0] = mWidth * 2; + ps_inp_raw_buf->au4_ht[0] = mHeight; + ps_inp_raw_buf->au4_strd[0] = mStride * 2; + break; + } + + case IV_YUV_420SP_UV: + case IV_YUV_420SP_VU: + default: + { + ps_inp_raw_buf->apv_bufs[0] = pu1_buf; + pu1_buf += (mStride) * mHeight; + ps_inp_raw_buf->apv_bufs[1] = pu1_buf; + + ps_inp_raw_buf->au4_wd[0] = mWidth; + ps_inp_raw_buf->au4_wd[1] = mWidth; + + ps_inp_raw_buf->au4_ht[0] = mHeight; + ps_inp_raw_buf->au4_ht[1] = mHeight / 2; + + ps_inp_raw_buf->au4_strd[0] = mStride; + ps_inp_raw_buf->au4_strd[1] = mStride; + break; + } + } + return OMX_ErrorNone; +} + +void SoftAVC::onQueueFilled(OMX_U32 portIndex) { + IV_STATUS_T status; + WORD32 timeDelay, timeTaken; + + UNUSED(portIndex); + + // Initialize encoder if not already initialized + if (mCodecCtx == NULL) { + if (OMX_ErrorNone != initEncoder()) { + ALOGE("Failed to initialize encoder"); + notify(OMX_EventError, OMX_ErrorUndefined, 0 /* arg2 */, NULL /* data */); + return; + } + } + if (mSignalledError) { + return; + } + + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + while (!mSawOutputEOS && !outQueue.empty()) { + + OMX_ERRORTYPE error; + ive_video_encode_ip_t s_encode_ip; + ive_video_encode_op_t s_encode_op; + BufferInfo *outputBufferInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outputBufferHeader = outputBufferInfo->mHeader; + + BufferInfo *inputBufferInfo; + OMX_BUFFERHEADERTYPE *inputBufferHeader; + + if (mSawInputEOS) { + inputBufferHeader = NULL; + inputBufferInfo = NULL; + } else if (!inQueue.empty()) { + inputBufferInfo = *inQueue.begin(); + inputBufferHeader = inputBufferInfo->mHeader; + } else { + return; + } + + outputBufferHeader->nTimeStamp = 0; + outputBufferHeader->nFlags = 0; + outputBufferHeader->nOffset = 0; + outputBufferHeader->nFilledLen = 0; + outputBufferHeader->nOffset = 0; + + if (inputBufferHeader != NULL) { + outputBufferHeader->nFlags = inputBufferHeader->nFlags; + } + + uint8_t *outPtr = (uint8_t *)outputBufferHeader->pBuffer; + + if (!mSpsPpsHeaderReceived) { + error = setEncodeArgs(&s_encode_ip, &s_encode_op, NULL, outputBufferHeader); + if (error != OMX_ErrorNone) { + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); + return; + } + status = ive_api_function(mCodecCtx, &s_encode_ip, &s_encode_op); + + if (IV_SUCCESS != status) { + ALOGE("Encode Frame failed = 0x%x\n", + s_encode_op.u4_error_code); + } else { + ALOGV("Bytes Generated in header %d\n", + s_encode_op.s_out_buf.u4_bytes); + } + + mSpsPpsHeaderReceived = true; + + outputBufferHeader->nFlags = OMX_BUFFERFLAG_CODECCONFIG; + outputBufferHeader->nFilledLen = s_encode_op.s_out_buf.u4_bytes; + if (inputBufferHeader != NULL) { + outputBufferHeader->nTimeStamp = inputBufferHeader->nTimeStamp; + } + + outQueue.erase(outQueue.begin()); + outputBufferInfo->mOwnedByUs = false; + + DUMP_TO_FILE( + mOutFile, outputBufferHeader->pBuffer, + outputBufferHeader->nFilledLen); + notifyFillBufferDone(outputBufferHeader); + + setEncMode(IVE_ENC_MODE_PICTURE); + return; + } + + if (mUpdateFlag) { + if (mUpdateFlag & kUpdateBitrate) { + setBitRate(); + } + if (mUpdateFlag & kRequestKeyFrame) { + setFrameType(IV_IDR_FRAME); + } + if (mUpdateFlag & kUpdateAIRMode) { + setAirParams(); + notify(OMX_EventPortSettingsChanged, kOutputPortIndex, + OMX_IndexConfigAndroidIntraRefresh, NULL); + } + mUpdateFlag = 0; + } + + if ((inputBufferHeader != NULL) + && (inputBufferHeader->nFlags & OMX_BUFFERFLAG_EOS)) { + mSawInputEOS = true; + } + + /* In normal mode, store inputBufferInfo and this will be returned + when encoder consumes this input */ + if (!mInputDataIsMeta && (inputBufferInfo != NULL)) { + for (size_t i = 0; i < MAX_INPUT_BUFFER_HEADERS; i++) { + if (NULL == mInputBufferInfo[i]) { + mInputBufferInfo[i] = inputBufferInfo; + break; + } + } + } + error = setEncodeArgs( + &s_encode_ip, &s_encode_op, inputBufferHeader, outputBufferHeader); + + if (error != OMX_ErrorNone) { + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); + return; + } + + DUMP_TO_FILE( + mInFile, s_encode_ip.s_inp_buf.apv_bufs[0], + (mHeight * mStride * 3 / 2)); + + GETTIME(&mTimeStart, NULL); + /* Compute time elapsed between end of previous decode() + * to start of current decode() */ + TIME_DIFF(mTimeEnd, mTimeStart, timeDelay); + status = ive_api_function(mCodecCtx, &s_encode_ip, &s_encode_op); + + if (IV_SUCCESS != status) { + ALOGE("Encode Frame failed = 0x%x\n", + s_encode_op.u4_error_code); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); + return; + } + + GETTIME(&mTimeEnd, NULL); + /* Compute time taken for decode() */ + TIME_DIFF(mTimeStart, mTimeEnd, timeTaken); + + ALOGV("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay, + s_encode_op.s_out_buf.u4_bytes); + + /* In encoder frees up an input buffer, mark it as free */ + if (s_encode_op.s_inp_buf.apv_bufs[0] != NULL) { + if (mInputDataIsMeta) { + for (size_t i = 0; i < MAX_CONVERSION_BUFFERS; i++) { + if (mConversionBuffers[i] == s_encode_op.s_inp_buf.apv_bufs[0]) { + mConversionBuffersFree[i] = 1; + break; + } + } + } else { + /* In normal mode, call EBD on inBuffeHeader that is freed by the codec */ + for (size_t i = 0; i < MAX_INPUT_BUFFER_HEADERS; i++) { + uint8_t *buf = NULL; + OMX_BUFFERHEADERTYPE *bufHdr = NULL; + if (mInputBufferInfo[i] != NULL) { + bufHdr = mInputBufferInfo[i]->mHeader; + buf = bufHdr->pBuffer + bufHdr->nOffset; + } + if (s_encode_op.s_inp_buf.apv_bufs[0] == buf) { + mInputBufferInfo[i]->mOwnedByUs = false; + notifyEmptyBufferDone(bufHdr); + mInputBufferInfo[i] = NULL; + break; + } + } + } + } + + outputBufferHeader->nFilledLen = s_encode_op.s_out_buf.u4_bytes; + + if (IV_IDR_FRAME == s_encode_op.u4_encoded_frame_type) { + outputBufferHeader->nFlags |= OMX_BUFFERFLAG_SYNCFRAME; + } + + if (inputBufferHeader != NULL) { + inQueue.erase(inQueue.begin()); + + /* If in meta data, call EBD on input */ + /* In case of normal mode, EBD will be done once encoder + releases the input buffer */ + if (mInputDataIsMeta) { + inputBufferInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inputBufferHeader); + } + } + + if (s_encode_op.u4_is_last) { + outputBufferHeader->nFlags |= OMX_BUFFERFLAG_EOS; + mSawOutputEOS = true; + } else { + outputBufferHeader->nFlags &= ~OMX_BUFFERFLAG_EOS; + } + + if (outputBufferHeader->nFilledLen || s_encode_op.u4_is_last) { + outputBufferHeader->nTimeStamp = s_encode_op.u4_timestamp_high; + outputBufferHeader->nTimeStamp <<= 32; + outputBufferHeader->nTimeStamp |= s_encode_op.u4_timestamp_low; + outputBufferInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + DUMP_TO_FILE(mOutFile, outputBufferHeader->pBuffer, + outputBufferHeader->nFilledLen); + notifyFillBufferDone(outputBufferHeader); + } + + if (s_encode_op.u4_is_last == 1) { + return; + } + } + return; +} + +void SoftAVC::onReset() { + SoftVideoEncoderOMXComponent::onReset(); + + if (releaseEncoder() != OMX_ErrorNone) { + ALOGW("releaseEncoder failed"); + } +} + +} // namespace android + +__attribute__((cfi_canonical_jump_table)) +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftAVC(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/avcenc/SoftAVCEnc.h b/media/libstagefright/codecs/avcenc/SoftAVCEnc.h new file mode 100644 index 0000000..6d2e084 --- /dev/null +++ b/media/libstagefright/codecs/avcenc/SoftAVCEnc.h @@ -0,0 +1,317 @@ +/* + * Copyright 2012 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_AVC_ENC_H__ +#define __SOFT_AVC_ENC_H__ + + +#include +#include + +#include + +namespace android { + +#define MAX_INPUT_BUFFER_HEADERS 4 +#define MAX_CONVERSION_BUFFERS 4 +#define CODEC_MAX_CORES 4 +#define LEN_STATUS_BUFFER (10 * 1024) +#define MAX_VBV_BUFF_SIZE (120 * 16384) +#define MAX_NUM_IO_BUFS 3 + +#define DEFAULT_MAX_REF_FRM 2 +#define DEFAULT_MAX_REORDER_FRM 0 +#define DEFAULT_QP_MIN 10 +#define DEFAULT_QP_MAX 40 +#define DEFAULT_MAX_BITRATE 240000000 +#define DEFAULT_MAX_SRCH_RANGE_X 256 +#define DEFAULT_MAX_SRCH_RANGE_Y 256 +#define DEFAULT_MAX_FRAMERATE 120000 +#define DEFAULT_NUM_CORES 1 +#define DEFAULT_NUM_CORES_PRE_ENC 0 +#define DEFAULT_FPS 30 +#define DEFAULT_ENC_SPEED IVE_NORMAL + +#define DEFAULT_MEM_REC_CNT 0 +#define DEFAULT_RECON_ENABLE 0 +#define DEFAULT_CHKSUM_ENABLE 0 +#define DEFAULT_START_FRM 0 +#define DEFAULT_NUM_FRMS 0xFFFFFFFF +#define DEFAULT_INP_COLOR_FORMAT IV_YUV_420SP_VU +#define DEFAULT_RECON_COLOR_FORMAT IV_YUV_420P +#define DEFAULT_LOOPBACK 0 +#define DEFAULT_SRC_FRAME_RATE 30 +#define DEFAULT_TGT_FRAME_RATE 30 +#define DEFAULT_MAX_WD 1920 +#define DEFAULT_MAX_HT 1920 +#define DEFAULT_MAX_LEVEL 41 +#define DEFAULT_STRIDE 0 +#define DEFAULT_WD 1280 +#define DEFAULT_HT 720 +#define DEFAULT_PSNR_ENABLE 0 +#define DEFAULT_ME_SPEED 100 +#define DEFAULT_ENABLE_FAST_SAD 0 +#define DEFAULT_ENABLE_ALT_REF 0 +#define DEFAULT_RC_MODE IVE_RC_STORAGE +#define DEFAULT_BITRATE 6000000 +#define DEFAULT_I_QP 22 +#define DEFAULT_I_QP_MAX DEFAULT_QP_MAX +#define DEFAULT_I_QP_MIN DEFAULT_QP_MIN +#define DEFAULT_P_QP 28 +#define DEFAULT_P_QP_MAX DEFAULT_QP_MAX +#define DEFAULT_P_QP_MIN DEFAULT_QP_MIN +#define DEFAULT_B_QP 22 +#define DEFAULT_B_QP_MAX DEFAULT_QP_MAX +#define DEFAULT_B_QP_MIN DEFAULT_QP_MIN +#define DEFAULT_AIR IVE_AIR_MODE_NONE +#define DEFAULT_AIR_REFRESH_PERIOD 30 +#define DEFAULT_SRCH_RNG_X 64 +#define DEFAULT_SRCH_RNG_Y 48 +#define DEFAULT_I_INTERVAL 30 +#define DEFAULT_IDR_INTERVAL 1000 +#define DEFAULT_B_FRAMES 0 +#define DEFAULT_DISABLE_DEBLK_LEVEL 0 +#define DEFAULT_HPEL 1 +#define DEFAULT_QPEL 1 +#define DEFAULT_I4 1 +#define DEFAULT_EPROFILE IV_PROFILE_BASE +#define DEFAULT_ENTROPY_MODE 0 +#define DEFAULT_SLICE_MODE IVE_SLICE_MODE_NONE +#define DEFAULT_SLICE_PARAM 256 +#define DEFAULT_ARCH ARCH_ARM_A9Q +#define DEFAULT_SOC SOC_GENERIC +#define DEFAULT_INTRA4x4 0 +#define STRLENGTH 500 +#define DEFAULT_CONSTRAINED_INTRA 0 + +#define MIN(a, b) ((a) < (b))? (a) : (b) +#define MAX(a, b) ((a) > (b))? (a) : (b) +#define ALIGN16(x) ((((x) + 15) >> 4) << 4) +#define ALIGN128(x) ((((x) + 127) >> 7) << 7) +#define ALIGN4096(x) ((((x) + 4095) >> 12) << 12) + +/** Used to remove warnings about unused parameters */ +#define UNUSED(x) ((void)(x)) + +/** Get time */ +#define GETTIME(a, b) gettimeofday(a, b); + +/** Compute difference between start and end */ +#define TIME_DIFF(start, end, diff) \ + diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \ + ((end).tv_usec - (start).tv_usec); + +#define ive_aligned_malloc(alignment, size) memalign(alignment, size) +#define ive_aligned_free(buf) free(buf) + +struct SoftAVC : public SoftVideoEncoderOMXComponent { + SoftAVC( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + + // Override SimpleSoftOMXComponent methods + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + +protected: + virtual ~SoftAVC(); + + virtual void onReset(); + +private: + enum { + kNumBuffers = 2, + }; + + enum { + kUpdateBitrate = 1 << 0, + kRequestKeyFrame = 1 << 1, + kUpdateAIRMode = 1 << 2, + }; + + // OMX input buffer's timestamp and flags + typedef struct { + int64_t mTimeUs; + int32_t mFlags; + } InputBufferInfo; + + int32_t mStride; + + struct timeval mTimeStart; // Time at the start of decode() + struct timeval mTimeEnd; // Time at the end of decode() + + int mUpdateFlag; + +#ifdef FILE_DUMP_ENABLE + char mInFile[200]; + char mOutFile[200]; +#endif /* FILE_DUMP_ENABLE */ + + IV_COLOR_FORMAT_T mIvVideoColorFormat; + + IV_PROFILE_T mAVCEncProfile; + WORD32 mAVCEncLevel; + bool mStarted; + bool mSpsPpsHeaderReceived; + + bool mSawInputEOS; + bool mSawOutputEOS; + bool mSignalledError; + bool mIntra4x4; + bool mEnableFastSad; + bool mEnableAltRef; + bool mReconEnable; + bool mPSNREnable; + bool mEntropyMode; + bool mConstrainedIntraFlag; + IVE_SPEED_CONFIG mEncSpeed; + + uint8_t *mConversionBuffers[MAX_CONVERSION_BUFFERS]; + bool mConversionBuffersFree[MAX_CONVERSION_BUFFERS]; + BufferInfo *mInputBufferInfo[MAX_INPUT_BUFFER_HEADERS]; + iv_obj_t *mCodecCtx; // Codec context + iv_mem_rec_t *mMemRecords; // Memory records requested by the codec + size_t mNumMemRecords; // Number of memory records requested by codec + size_t mNumCores; // Number of cores used by the codec + + UWORD32 mHeaderGenerated; + UWORD32 mBframes; + IV_ARCH_T mArch; + IVE_SLICE_MODE_T mSliceMode; + UWORD32 mSliceParam; + bool mHalfPelEnable; + UWORD32 mIInterval; + UWORD32 mIDRInterval; + UWORD32 mDisableDeblkLevel; + IVE_AIR_MODE_T mAIRMode; + UWORD32 mAIRRefreshPeriod; + + void initEncParams(); + OMX_ERRORTYPE initEncoder(); + OMX_ERRORTYPE releaseEncoder(); + + // Verifies the component role tried to be set to this OMX component is + // strictly video_encoder.avc + OMX_ERRORTYPE internalSetRoleParams( + const OMX_PARAM_COMPONENTROLETYPE *role); + + // Updates bitrate to reflect port settings. + OMX_ERRORTYPE internalSetBitrateParams( + const OMX_VIDEO_PARAM_BITRATETYPE *bitrate); + + OMX_ERRORTYPE internalSetConfig( + OMX_INDEXTYPE index, const OMX_PTR _params, bool *frameConfig); + + OMX_ERRORTYPE getConfig( + OMX_INDEXTYPE index, const OMX_PTR _params); + + // Handles port definition changes. + OMX_ERRORTYPE internalSetPortParams( + const OMX_PARAM_PORTDEFINITIONTYPE *port); + + OMX_ERRORTYPE internalSetFormatParams( + const OMX_VIDEO_PARAM_PORTFORMATTYPE *format); + + OMX_ERRORTYPE setFrameType(IV_PICTURE_CODING_TYPE_T e_frame_type); + OMX_ERRORTYPE setQp(); + OMX_ERRORTYPE setEncMode(IVE_ENC_MODE_T e_enc_mode); + OMX_ERRORTYPE setDimensions(); + OMX_ERRORTYPE setNumCores(); + OMX_ERRORTYPE setFrameRate(); + OMX_ERRORTYPE setIpeParams(); + OMX_ERRORTYPE setBitRate(); + OMX_ERRORTYPE setAirParams(); + OMX_ERRORTYPE setMeParams(); + OMX_ERRORTYPE setGopParams(); + OMX_ERRORTYPE setProfileParams(); + OMX_ERRORTYPE setDeblockParams(); + OMX_ERRORTYPE setVbvParams(); + void logVersion(); + OMX_ERRORTYPE setEncodeArgs( + ive_video_encode_ip_t *ps_encode_ip, + ive_video_encode_op_t *ps_encode_op, + OMX_BUFFERHEADERTYPE *inputBufferHeader, + OMX_BUFFERHEADERTYPE *outputBufferHeader); + + DISALLOW_EVIL_CONSTRUCTORS(SoftAVC); +}; + +#ifdef FILE_DUMP_ENABLE + +#define INPUT_DUMP_PATH "/sdcard/media/avce_input" +#define INPUT_DUMP_EXT "yuv" +#define OUTPUT_DUMP_PATH "/sdcard/media/avce_output" +#define OUTPUT_DUMP_EXT "h264" + +#define GENERATE_FILE_NAMES() { \ + GETTIME(&mTimeStart, NULL); \ + strcpy(mInFile, ""); \ + sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH, \ + mTimeStart.tv_sec, mTimeStart.tv_usec, \ + INPUT_DUMP_EXT); \ + strcpy(mOutFile, ""); \ + sprintf(mOutFile, "%s_%ld.%ld.%s", OUTPUT_DUMP_PATH,\ + mTimeStart.tv_sec, mTimeStart.tv_usec, \ + OUTPUT_DUMP_EXT); \ +} + +#define CREATE_DUMP_FILE(m_filename) { \ + FILE *fp = fopen(m_filename, "wb"); \ + if (fp != NULL) { \ + ALOGD("Opened file %s", m_filename); \ + fclose(fp); \ + } else { \ + ALOGD("Could not open file %s", m_filename); \ + } \ +} +#define DUMP_TO_FILE(m_filename, m_buf, m_size) \ +{ \ + FILE *fp = fopen(m_filename, "ab"); \ + if (fp != NULL && m_buf != NULL) { \ + int i; \ + i = fwrite(m_buf, 1, m_size, fp); \ + ALOGD("fwrite ret %d to write %d", i, m_size); \ + if (i != (int)m_size) { \ + ALOGD("Error in fwrite, returned %d", i); \ + perror("Error in write to file"); \ + } \ + fclose(fp); \ + } else { \ + ALOGD("Could not write to file %s", m_filename);\ + if (fp != NULL) \ + fclose(fp); \ + } \ +} +#else /* FILE_DUMP_ENABLE */ +#define INPUT_DUMP_PATH +#define INPUT_DUMP_EXT +#define OUTPUT_DUMP_PATH +#define OUTPUT_DUMP_EXT +#define GENERATE_FILE_NAMES() +#define CREATE_DUMP_FILE(m_filename) +#define DUMP_TO_FILE(m_filename, m_buf, m_size) +#endif /* FILE_DUMP_ENABLE */ + +} // namespace android + +#endif // __SOFT_AVC_ENC_H__ diff --git a/media/libstagefright/codecs/avcenc/exports.lds b/media/libstagefright/codecs/avcenc/exports.lds new file mode 100644 index 0000000..e24f3fa --- /dev/null +++ b/media/libstagefright/codecs/avcenc/exports.lds @@ -0,0 +1,5 @@ +{ + global: + _Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE; + local: *; +}; diff --git a/media/libstagefright/codecs/flac/dec/Android.bp b/media/libstagefright/codecs/flac/dec/Android.bp new file mode 100644 index 0000000..d06e0b7 --- /dev/null +++ b/media/libstagefright/codecs/flac/dec/Android.bp @@ -0,0 +1,41 @@ +package { + default_applicable_licenses: [ + "frameworks_av_media_libstagefright_codecs_flac_dec_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_av_media_libstagefright_codecs_flac_dec_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + +cc_library_shared { + name: "libstagefright_soft_flacdec", + defaults: ["libstagefright_softomx-defaults"], + + srcs: [ + "SoftFlacDecoder.cpp", + ], + + version_script: "exports.lds", + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + "unsigned-integer-overflow", + ], + cfi: true, + }, + + shared_libs: [ + "libstagefright_flacdec", + ], +} diff --git a/media/libstagefright/codecs/flac/dec/MODULE_LICENSE_APACHE2 b/media/libstagefright/codecs/flac/dec/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/media/libstagefright/codecs/flac/dec/NOTICE b/media/libstagefright/codecs/flac/dec/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/media/libstagefright/codecs/flac/dec/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, 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/flac/dec/SoftFlacDecoder.cpp b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp new file mode 100644 index 0000000..d6448d3 --- /dev/null +++ b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.cpp @@ -0,0 +1,500 @@ +/* + * Copyright (C) 2017 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 "SoftFlacDecoder" +#include + +#include "SoftFlacDecoder.h" +#include +#include + +#include +#include +#include +#include + +namespace android { + +template +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +SoftFlacDecoder::SoftFlacDecoder( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mFLACDecoder(NULL), + mInputBufferCount(0), + mHasStreamInfo(false), + mSignalledError(false), + mSawInputEOS(false), + mFinishedDecoder(false), + mOutputPortSettingsChange(NONE) { + ALOGV("ctor:"); + memset(&mStreamInfo, 0, sizeof(mStreamInfo)); + initPorts(); + initDecoder(); +} + +SoftFlacDecoder::~SoftFlacDecoder() { + ALOGV("dtor:"); + delete mFLACDecoder; +} + +void SoftFlacDecoder::initPorts() { + ALOGV("initPorts:"); + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumInputBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 32768; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.audio.cMIMEType = const_cast("audio/flac"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingFLAC; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumOutputBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = kNumSamplesPerFrame * FLACDecoder::kMaxChannels * sizeof(float); + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = sizeof(float); + + def.format.audio.cMIMEType = const_cast("audio/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); +} + +void SoftFlacDecoder::initDecoder() { + ALOGV("initDecoder:"); + mFLACDecoder = FLACDecoder::Create(); + if (mFLACDecoder == NULL) { + ALOGE("initDecoder: failed to create FLACDecoder"); + mSignalledError = true; + } +} + +OMX_ERRORTYPE SoftFlacDecoder::initCheck() const { + if (mSignalledError) { + if (mFLACDecoder == NULL) { + ALOGE("initCheck: failed due to NULL encoder"); + return OMX_ErrorDynamicResourcesUnavailable; + } + return OMX_ErrorUndefined; + } + + return SimpleSoftOMXComponent::initCheck(); +} + +OMX_ERRORTYPE SoftFlacDecoder::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + ALOGV("internalGetParameter: index(%x)", index); + switch ((OMX_U32)index) { + case OMX_IndexParamAudioPortFormat: + { + OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex > 0) { + return OMX_ErrorNoMore; + } + + formatParams->eEncoding = + (formatParams->nPortIndex == 0) + ? OMX_AUDIO_CodingFLAC : OMX_AUDIO_CodingPCM; + + return OMX_ErrorNone; + } + case OMX_IndexParamAudioFlac: + { + OMX_AUDIO_PARAM_FLACTYPE *flacParams = + (OMX_AUDIO_PARAM_FLACTYPE *)params; + + if (!isValidOMXParam(flacParams)) { + ALOGE("internalGetParameter(OMX_IndexParamAudioFlac): invalid omx params"); + return OMX_ErrorBadParameter; + } + + if (flacParams->nPortIndex != 0) { + ALOGE("internalGetParameter(OMX_IndexParamAudioFlac): bad port index"); + return OMX_ErrorBadPortIndex; + } + + flacParams->nCompressionLevel = 0; + + if (isConfigured()) { + flacParams->nChannels = mStreamInfo.channels; + flacParams->nSampleRate = mStreamInfo.sample_rate; + } else { + flacParams->nChannels = 2; + flacParams->nSampleRate = 44100; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + ALOGE("internalGetParameter(OMX_IndexParamAudioPcm): invalid omx params"); + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 1) { + ALOGE("internalGetParameter(OMX_IndexParamAudioPcm): bad port index"); + return OMX_ErrorBadPortIndex; + } + + pcmParams->eNumData = mNumericalData; + pcmParams->eEndian = OMX_EndianBig; + pcmParams->bInterleaved = OMX_TRUE; + pcmParams->nBitPerSample = mBitsPerSample; + pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; + pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; + pcmParams->eChannelMapping[2] = OMX_AUDIO_ChannelCF; + pcmParams->eChannelMapping[3] = OMX_AUDIO_ChannelLFE; + pcmParams->eChannelMapping[4] = OMX_AUDIO_ChannelLS; + pcmParams->eChannelMapping[5] = OMX_AUDIO_ChannelRS; + + if (isConfigured()) { + pcmParams->nChannels = mStreamInfo.channels; + pcmParams->nSamplingRate = mStreamInfo.sample_rate; + } else { + pcmParams->nChannels = 2; + pcmParams->nSamplingRate = 44100; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftFlacDecoder::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + ALOGV("internalSetParameter: index(%x)", (int)index); + switch ((int)index) { + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (!isValidOMXParam(roleParams)) { + return OMX_ErrorBadParameter; + } + + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.flac", + OMX_MAX_STRINGNAME_SIZE - 1) != 0) { + return OMX_ErrorInvalidComponentName; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPortFormat: + { + const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if ((formatParams->nPortIndex == 0 + && formatParams->eEncoding != OMX_AUDIO_CodingFLAC) + || (formatParams->nPortIndex == 1 + && formatParams->eEncoding != OMX_AUDIO_CodingPCM)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + const OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 1) { + return OMX_ErrorBadPortIndex; + } + + if (pcmParams->eNumData == OMX_NumericalDataFloat && pcmParams->nBitPerSample == 32) { + mNumericalData = OMX_NumericalDataFloat; + mBitsPerSample = 32; + } else if (pcmParams->eNumData == OMX_NumericalDataSigned + && pcmParams->nBitPerSample == 16) { + mNumericalData = OMX_NumericalDataSigned; + mBitsPerSample = 16; + } else { + ALOGE("Invalid eNumData %d, nBitsPerSample %d", + pcmParams->eNumData, pcmParams->nBitPerSample); + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +bool SoftFlacDecoder::isConfigured() const { + return mHasStreamInfo; +} + +void SoftFlacDecoder::onQueueFilled(OMX_U32 /* portIndex */) { + if (mSignalledError || mOutputPortSettingsChange != NONE) { + return; + } + + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + const bool outputFloat = mNumericalData == OMX_NumericalDataFloat; + + ALOGV("onQueueFilled %d/%d:", inQueue.empty(), outQueue.empty()); + while ((!inQueue.empty() || mSawInputEOS) && !outQueue.empty() && !mFinishedDecoder) { + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + void *outBuffer = reinterpret_cast(outHeader->pBuffer + outHeader->nOffset); + size_t outBufferSize = outHeader->nAllocLen - outHeader->nOffset; + int64_t timeStamp = 0; + + if (!inQueue.empty()) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + uint8_t* inBuffer = inHeader->pBuffer + inHeader->nOffset; + uint32_t inBufferLength = inHeader->nFilledLen; + ALOGV("input: %u bytes", inBufferLength); + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + ALOGV("saw EOS"); + mSawInputEOS = true; + if (mInputBufferCount == 0 && inHeader->nFilledLen == 0) { + // first buffer was empty and EOS: signal EOS on output and return + ALOGV("empty first EOS"); + outHeader->nFilledLen = 0; + outHeader->nTimeStamp = inHeader->nTimeStamp; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + notifyFillBufferDone(outHeader); + mFinishedDecoder = true; + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + notifyEmptyBufferDone(inHeader); + return; + } + } + + if (mInputBufferCount == 0 && !(inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG)) { + ALOGE("onQueueFilled: first buffer should have OMX_BUFFERFLAG_CODECCONFIG set"); + inHeader->nFlags |= OMX_BUFFERFLAG_CODECCONFIG; + } + if ((inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) != 0) { + ALOGV("received config buffer of size %u", inBufferLength); + status_t decoderErr = mFLACDecoder->parseMetadata(inBuffer, inBufferLength); + mInputBufferCount++; + + if (decoderErr != OK && decoderErr != WOULD_BLOCK) { + ALOGE("onQueueFilled: FLACDecoder parseMetaData returns error %d", decoderErr); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorStreamCorrupt, decoderErr, NULL); + return; + } + + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + notifyEmptyBufferDone(inHeader); + + if (decoderErr == WOULD_BLOCK) { + continue; + } + mStreamInfo = mFLACDecoder->getStreamInfo(); + mHasStreamInfo = true; + + // Only send out port settings changed event if both sample rate + // and numChannels are valid. + if (mStreamInfo.sample_rate && mStreamInfo.channels) { + ALOGD("onQueueFilled: initially configuring decoder: %d Hz, %d channels", + mStreamInfo.sample_rate, mStreamInfo.channels); + + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + } + return; + } + + status_t decoderErr = mFLACDecoder->decodeOneFrame( + inBuffer, inBufferLength, outBuffer, &outBufferSize, outputFloat); + if (decoderErr != OK) { + ALOGE("onQueueFilled: FLACDecoder decodeOneFrame returns error %d", decoderErr); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorStreamCorrupt, decoderErr, NULL); + return; + } + + mInputBufferCount++; + timeStamp = inHeader->nTimeStamp; + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + notifyEmptyBufferDone(inHeader); + + if (outBufferSize == 0) { + ALOGV("no output, trying again"); + continue; + } + } else if (mSawInputEOS) { + status_t decoderErr = mFLACDecoder->decodeOneFrame( + nullptr /* inBuffer */, 0 /* inBufferLen */, + outBuffer, &outBufferSize, outputFloat); + mFinishedDecoder = true; + if (decoderErr != OK) { + ALOGE("onQueueFilled: FLACDecoder finish returns error %d", decoderErr); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorStreamCorrupt, decoderErr, NULL); + return; + } + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + } else { + // no more input buffers at this time, loop and see if there is more output + continue; + } + + outHeader->nFilledLen = outBufferSize; + outHeader->nTimeStamp = timeStamp; + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + notifyFillBufferDone(outHeader); + } +} + +void SoftFlacDecoder::onPortFlushCompleted(OMX_U32 portIndex) { + ALOGV("onPortFlushCompleted: portIndex(%u)", portIndex); + if (portIndex == 0) { + drainDecoder(); + } +} + +void SoftFlacDecoder::drainDecoder() { + mFLACDecoder->flush(); + mSawInputEOS = false; + mFinishedDecoder = false; +} + +void SoftFlacDecoder::onReset() { + ALOGV("onReset"); + drainDecoder(); + + memset(&mStreamInfo, 0, sizeof(mStreamInfo)); + mHasStreamInfo = false; + mInputBufferCount = 0; + mSignalledError = false; + mOutputPortSettingsChange = NONE; +} + +void SoftFlacDecoder::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { + ALOGV("onPortEnableCompleted: portIndex(%u), enabled(%d)", portIndex, enabled); + if (portIndex != 1) { + return; + } + + switch (mOutputPortSettingsChange) { + case NONE: + break; + + case AWAITING_DISABLED: + { + CHECK(!enabled); + mOutputPortSettingsChange = AWAITING_ENABLED; + PortInfo *info = editPortInfo(1 /* portIndex */); + if (!info->mDef.bEnabled) { + info->mDef.nBufferSize = + mStreamInfo.max_blocksize * mStreamInfo.channels * sizeof(float); + } + break; + } + + default: + { + CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); + CHECK(enabled); + mOutputPortSettingsChange = NONE; + break; + } + } +} + +} // namespace android + +__attribute__((cfi_canonical_jump_table)) +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + ALOGV("createSoftOMXComponent: flac decoder"); + return new android::SoftFlacDecoder(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h new file mode 100644 index 0000000..ba02074 --- /dev/null +++ b/media/libstagefright/codecs/flac/dec/SoftFlacDecoder.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2017 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_FLAC_DECODER_H +#define SOFT_FLAC_DECODER_H + +#include "FLACDecoder.h" +#include + +namespace android { + +struct SoftFlacDecoder : public SimpleSoftOMXComponent { + SoftFlacDecoder(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + + virtual OMX_ERRORTYPE initCheck() const override; + +protected: + virtual ~SoftFlacDecoder(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) override; + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) override; + + virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex) override; + virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled) override; + virtual void onReset() override; + +private: + static constexpr unsigned int kNumSamplesPerFrame = 2048; // adjusted based on stream. + + enum { + kNumInputBuffers = 4, + kNumOutputBuffers = 4, + }; + OMX_NUMERICALDATATYPE mNumericalData = OMX_NumericalDataSigned; + OMX_U32 mBitsPerSample = 16; + + FLACDecoder *mFLACDecoder; + FLAC__StreamMetadata_StreamInfo mStreamInfo; + size_t mInputBufferCount; + bool mHasStreamInfo; + bool mSignalledError; + bool mSawInputEOS; + bool mFinishedDecoder; + + enum { + NONE, + AWAITING_DISABLED, + AWAITING_ENABLED + } mOutputPortSettingsChange; + + void initPorts(); + void initDecoder(); + bool isConfigured() const; + void drainDecoder(); + + DISALLOW_EVIL_CONSTRUCTORS(SoftFlacDecoder); +}; + +} // namespace android + +#endif // SOFT_FLAC_DECODER_H diff --git a/media/libstagefright/codecs/flac/dec/exports.lds b/media/libstagefright/codecs/flac/dec/exports.lds new file mode 100644 index 0000000..e24f3fa --- /dev/null +++ b/media/libstagefright/codecs/flac/dec/exports.lds @@ -0,0 +1,5 @@ +{ + global: + _Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE; + local: *; +}; diff --git a/media/libstagefright/codecs/flac/enc/Android.bp b/media/libstagefright/codecs/flac/enc/Android.bp new file mode 100644 index 0000000..59a4675 --- /dev/null +++ b/media/libstagefright/codecs/flac/enc/Android.bp @@ -0,0 +1,43 @@ +package { + default_applicable_licenses: [ + "frameworks_av_media_libstagefright_codecs_flac_enc_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_av_media_libstagefright_codecs_flac_enc_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + +cc_library_shared { + name: "libstagefright_soft_flacenc", + defaults: ["libstagefright_softomx-defaults"], + + srcs: ["SoftFlacEncoder.cpp"], + + version_script: "exports.lds", + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + "unsigned-integer-overflow", + ], + cfi: true, + }, + + header_libs: ["libbase_headers"], + shared_libs: [ + "libaudioutils", + ], + static_libs: [ + "libFLAC", + ], +} diff --git a/media/libstagefright/codecs/flac/enc/MODULE_LICENSE_APACHE2 b/media/libstagefright/codecs/flac/enc/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/media/libstagefright/codecs/flac/enc/NOTICE b/media/libstagefright/codecs/flac/enc/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/media/libstagefright/codecs/flac/enc/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, 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/flac/enc/SoftFlacEncoder.cpp b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp new file mode 100644 index 0000000..24216a2 --- /dev/null +++ b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp @@ -0,0 +1,601 @@ +/* + * Copyright (C) 2012 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 "SoftFlacEncoder" +#include +#include + +#include "SoftFlacEncoder.h" +#include +#include +#include + +#define FLAC_COMPRESSION_LEVEL_MIN 0 +#define FLAC_COMPRESSION_LEVEL_DEFAULT 5 +#define FLAC_COMPRESSION_LEVEL_MAX 8 + +#if LOG_NDEBUG +#define UNUSED_UNLESS_VERBOSE(x) (void)(x) +#else +#define UNUSED_UNLESS_VERBOSE(x) +#endif + +namespace android { + +template +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +SoftFlacEncoder::SoftFlacEncoder( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mSignalledError(false), + mNumChannels(1), + mSampleRate(44100), + mCompressionLevel(FLAC_COMPRESSION_LEVEL_DEFAULT), + mEncoderWriteData(false), + mEncoderReturnedEncodedData(false), + mSawInputEOS(false), + mSentOutputEOS(false), + mEncoderReturnedNbBytes(0), + mInputBufferPcm32(NULL), + mHeaderOffset(0), + mHeaderComplete(false), + mWroteHeader(false) +{ + ALOGV("SoftFlacEncoder::SoftFlacEncoder(name=%s)", name); + initPorts(); + + mFlacStreamEncoder = FLAC__stream_encoder_new(); + if (mFlacStreamEncoder == NULL) { + ALOGE("SoftFlacEncoder::SoftFlacEncoder(name=%s) error instantiating FLAC encoder", name); + mSignalledError = true; + } + + if (!mSignalledError) { // no use allocating input buffer if we had an error above + // 2x the pcm16 samples can exist with the same size as pcmFloat samples. + mInputBufferPcm32 = (FLAC__int32*) malloc( + sizeof(FLAC__int32) * kNumSamplesPerFrame * kMaxChannels * 2); + if (mInputBufferPcm32 == NULL) { + ALOGE("SoftFlacEncoder::SoftFlacEncoder(name=%s) error allocating internal input buffer", name); + mSignalledError = true; + } + } +} + +SoftFlacEncoder::~SoftFlacEncoder() { + ALOGV("SoftFlacEncoder::~SoftFlacEncoder()"); + if (mFlacStreamEncoder != NULL) { + FLAC__stream_encoder_delete(mFlacStreamEncoder); + mFlacStreamEncoder = NULL; + } + free(mInputBufferPcm32); + mInputBufferPcm32 = NULL; +} + +OMX_ERRORTYPE SoftFlacEncoder::initCheck() const { + if (mSignalledError) { + if (mFlacStreamEncoder == NULL) { + ALOGE("initCheck() failed due to NULL encoder"); + } else if (mInputBufferPcm32 == NULL) { + ALOGE("initCheck() failed due to error allocating internal input buffer"); + } + return OMX_ErrorUndefined; + } else { + return SimpleSoftOMXComponent::initCheck(); + } +} + +void SoftFlacEncoder::initPorts() { + ALOGV("SoftFlacEncoder::initPorts()"); + + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + // configure input port of the encoder + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = kMaxInputBufferSize; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = sizeof(float); + + def.format.audio.cMIMEType = const_cast("audio/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); + + // configure output port of the encoder + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = kMaxOutputBufferSize; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.audio.cMIMEType = const_cast(MEDIA_MIMETYPE_AUDIO_FLAC); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingFLAC; + + addPort(def); +} + +OMX_ERRORTYPE SoftFlacEncoder::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + ALOGV("SoftFlacEncoder::internalGetParameter(index=0x%x)", index); + + switch (index) { + case OMX_IndexParamAudioPortFormat: + { + OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex > 0) { + return OMX_ErrorNoMore; + } + + formatParams->eEncoding = + (formatParams->nPortIndex == 0) + ? OMX_AUDIO_CodingPCM : OMX_AUDIO_CodingFLAC; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + pcmParams->eNumData = mNumericalData; + pcmParams->eEndian = OMX_EndianBig; + pcmParams->bInterleaved = OMX_TRUE; + pcmParams->nBitPerSample = mBitsPerSample; + pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; + pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; + + pcmParams->nChannels = mNumChannels; + pcmParams->nSamplingRate = mSampleRate; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioFlac: + { + OMX_AUDIO_PARAM_FLACTYPE *flacParams = (OMX_AUDIO_PARAM_FLACTYPE *)params; + + if (!isValidOMXParam(flacParams)) { + return OMX_ErrorBadParameter; + } + + if (flacParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + flacParams->nCompressionLevel = mCompressionLevel; + flacParams->nChannels = mNumChannels; + flacParams->nSampleRate = mSampleRate; + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftFlacEncoder::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioPortFormat: + { + const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if ((formatParams->nPortIndex == 0 + && formatParams->eEncoding != OMX_AUDIO_CodingPCM) + || (formatParams->nPortIndex == 1 + && formatParams->eEncoding != OMX_AUDIO_CodingFLAC)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + ALOGV("SoftFlacEncoder::internalSetParameter(OMX_IndexParamAudioPcm)"); + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 0) { + ALOGE("SoftFlacEncoder::internalSetParameter() Error #1"); + return OMX_ErrorUndefined; + } + + if (pcmParams->nChannels < 1 || pcmParams->nChannels > kMaxChannels) { + return OMX_ErrorUndefined; + } + + mNumChannels = pcmParams->nChannels; + mSampleRate = pcmParams->nSamplingRate; + + if (pcmParams->eNumData == OMX_NumericalDataFloat && pcmParams->nBitPerSample == 32) { + mNumericalData = OMX_NumericalDataFloat; + mBitsPerSample = 32; + } else if (pcmParams->eNumData == OMX_NumericalDataSigned + && pcmParams->nBitPerSample == 16) { + mNumericalData = OMX_NumericalDataSigned; + mBitsPerSample = 16; + } else { + ALOGE("%s: invalid eNumData %d, nBitsPerSample %d", + __func__, pcmParams->eNumData, pcmParams->nBitPerSample); + return OMX_ErrorUndefined; + } + + ALOGV("will encode %d channels at %dHz", mNumChannels, mSampleRate); + + return configureEncoder(); + } + + case OMX_IndexParamStandardComponentRole: + { + ALOGV("SoftFlacEncoder::internalSetParameter(OMX_IndexParamStandardComponentRole)"); + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (!isValidOMXParam(roleParams)) { + return OMX_ErrorBadParameter; + } + + if (strncmp((const char *)roleParams->cRole, + "audio_encoder.flac", + OMX_MAX_STRINGNAME_SIZE - 1)) { + ALOGE("SoftFlacEncoder::internalSetParameter(OMX_IndexParamStandardComponentRole)" + "error"); + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioFlac: + { + // used only for setting the compression level + OMX_AUDIO_PARAM_FLACTYPE *flacParams = (OMX_AUDIO_PARAM_FLACTYPE *)params; + + if (!isValidOMXParam(flacParams)) { + return OMX_ErrorBadParameter; + } + + if (flacParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + mCompressionLevel = flacParams->nCompressionLevel; // range clamping done inside encoder + return OMX_ErrorNone; + } + + case OMX_IndexParamPortDefinition: + { + OMX_PARAM_PORTDEFINITIONTYPE *defParams = + (OMX_PARAM_PORTDEFINITIONTYPE *)params; + + if (!isValidOMXParam(defParams)) { + return OMX_ErrorBadParameter; + } + + if (defParams->nPortIndex == 0) { + if (defParams->nBufferSize > kMaxInputBufferSize) { + ALOGE("Input buffer size must be at most %d bytes", + kMaxInputBufferSize); + return OMX_ErrorUnsupportedSetting; + } + } + + FALLTHROUGH_INTENDED; + } + + default: + ALOGV("SoftFlacEncoder::internalSetParameter(default)"); + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +void SoftFlacEncoder::onQueueFilled(OMX_U32 portIndex) { + UNUSED_UNLESS_VERBOSE(portIndex); + ALOGV("SoftFlacEncoder::onQueueFilled(portIndex=%d)", portIndex); + + if (mSignalledError) { + return; + } + + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + const bool inputFloat = mNumericalData == OMX_NumericalDataFloat; + const size_t sampleSize = inputFloat ? sizeof(float) : sizeof(int16_t); + const size_t frameSize = sampleSize * mNumChannels; + + FLAC__bool ok = true; + + while ((!inQueue.empty() || mSawInputEOS) && !outQueue.empty() && !mSentOutputEOS) { + if (!inQueue.empty()) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + ALOGV("saw EOS on buffer of size %u", inHeader->nFilledLen); + mSawInputEOS = true; + } + + if (inHeader->nFilledLen > kMaxInputBufferSize) { + ALOGE("input buffer too large (%d).", inHeader->nFilledLen); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + + assert(mNumChannels != 0); + mEncoderWriteData = true; + mEncoderReturnedEncodedData = false; + mEncoderReturnedNbBytes = 0; + if (inHeader->nFilledLen) { + mCurrentInputTimeStamp = inHeader->nTimeStamp; + + const unsigned nbInputFrames = inHeader->nFilledLen / frameSize; + const unsigned nbInputSamples = inHeader->nFilledLen / sampleSize; + + if (inputFloat) { + CHECK_LE(nbInputSamples, kNumSamplesPerFrame * kMaxChannels); + const float * const pcmFloat = reinterpret_cast(inHeader->pBuffer); + memcpy_to_q8_23_from_float_with_clamp( + mInputBufferPcm32, pcmFloat, nbInputSamples); + } else { + // note nbInputSamples may be 2x as large for pcm16 data. + CHECK_LE(nbInputSamples, kNumSamplesPerFrame * kMaxChannels * 2); + const int16_t * const pcm16 = reinterpret_cast(inHeader->pBuffer); + for (unsigned i = 0; i < nbInputSamples; ++i) { + mInputBufferPcm32[i] = (FLAC__int32) pcm16[i]; + } + } + ALOGV(" about to encode %u samples per channel", nbInputFrames); + ok = FLAC__stream_encoder_process_interleaved( + mFlacStreamEncoder, + mInputBufferPcm32, + nbInputFrames /*samples per channel*/ ); + } + + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + if (ok) { + ALOGV("encoded %d, bytes %lld, eos %d", mEncoderReturnedEncodedData, + (long long )mEncoderReturnedNbBytes, mSawInputEOS); + if (mSawInputEOS && !mEncoderReturnedEncodedData) { + ALOGV("finishing encoder"); + mSentOutputEOS = true; + FLAC__stream_encoder_finish(mFlacStreamEncoder); + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + } + if (mSawInputEOS || mEncoderReturnedEncodedData) { + ALOGV(" dequeueing buffer on output port after writing data"); + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + mEncoderReturnedEncodedData = false; + } + } else { + ALOGE(" error encountered during encoding"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + + } +} + +FLAC__StreamEncoderWriteStatus SoftFlacEncoder::onEncodedFlacAvailable( + const FLAC__byte buffer[], + size_t bytes, unsigned samples, + unsigned current_frame) { + UNUSED_UNLESS_VERBOSE(current_frame); + ALOGV("SoftFlacEncoder::onEncodedFlacAvailable(bytes=%zu, samples=%u, curr_frame=%u)", + bytes, samples, current_frame); + + if (samples == 0) { + ALOGV("saving %zu bytes of header", bytes); + if (mHeaderOffset + bytes > sizeof(mHeader) || mHeaderComplete) { + ALOGW("header is too big, or header already received"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + } else { + memcpy(mHeader + mHeaderOffset, buffer, bytes); + mHeaderOffset += bytes;// will contain header size when finished receiving header + if (buffer[0] & 0x80) { + mHeaderComplete = true; + } + } + return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; + } + + if ((samples == 0) || !mEncoderWriteData) { + // called by the encoder because there's header data to save, but it's not the role + // of this component (unless WRITE_FLAC_HEADER_IN_FIRST_BUFFER is defined) + ALOGV("ignoring %zu bytes of header data (samples=%d)", bytes, samples); + return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; + } + + List &outQueue = getPortQueue(1); + CHECK(!outQueue.empty()); + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + if (mHeaderComplete && !mWroteHeader) { + ALOGV(" writing %d bytes of header on output port", mHeaderOffset); + memcpy(outHeader->pBuffer + outHeader->nOffset + outHeader->nFilledLen, + mHeader, mHeaderOffset); + outHeader->nFilledLen += mHeaderOffset; + mWroteHeader = true; + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outHeader->nFlags = OMX_BUFFERFLAG_CODECCONFIG; + notifyFillBufferDone(outHeader); + outInfo = NULL; + outHeader = NULL; + // get the next buffer for the rest of the data + CHECK(!outQueue.empty()); + outInfo = *outQueue.begin(); + outHeader = outInfo->mHeader; + } + + // write encoded data + ALOGV(" writing %zu bytes of encoded data on output port", bytes); + if (bytes > outHeader->nAllocLen - outHeader->nOffset - outHeader->nFilledLen) { + ALOGE(" not enough space left to write encoded data, dropping %zu bytes", bytes); + // a fatal error would stop the encoding + return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; + } + memcpy(outHeader->pBuffer + outHeader->nOffset, buffer, bytes); + + outHeader->nTimeStamp = mCurrentInputTimeStamp; + outHeader->nOffset = 0; + outHeader->nFilledLen += bytes; + outHeader->nFlags = 0; + + mEncoderReturnedEncodedData = true; + mEncoderReturnedNbBytes += bytes; + + return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; +} + + +OMX_ERRORTYPE SoftFlacEncoder::configureEncoder() { + ALOGV("SoftFlacEncoder::configureEncoder() numChannel=%d, sampleRate=%d", + mNumChannels, mSampleRate); + + if (mSignalledError || (mFlacStreamEncoder == NULL)) { + ALOGE("can't configure encoder: no encoder or invalid state"); + return OMX_ErrorInvalidState; + } + + const bool inputFloat = mNumericalData == OMX_NumericalDataFloat; + const int codecBitsPerSample = inputFloat ? 24 : 16; + FLAC__bool ok = true; + ok = ok && FLAC__stream_encoder_set_channels(mFlacStreamEncoder, mNumChannels); + ok = ok && FLAC__stream_encoder_set_sample_rate(mFlacStreamEncoder, mSampleRate); + ok = ok && FLAC__stream_encoder_set_bits_per_sample(mFlacStreamEncoder, codecBitsPerSample); + ok = ok && FLAC__stream_encoder_set_compression_level(mFlacStreamEncoder, + (unsigned)mCompressionLevel); + ok = ok && FLAC__stream_encoder_set_verify(mFlacStreamEncoder, false); + if (!ok) { goto return_result; } + + ok &= FLAC__STREAM_ENCODER_INIT_STATUS_OK == + FLAC__stream_encoder_init_stream(mFlacStreamEncoder, + flacEncoderWriteCallback /*write_callback*/, + NULL /*seek_callback*/, + NULL /*tell_callback*/, + NULL /*metadata_callback*/, + (void *) this /*client_data*/); + +return_result: + if (ok) { + ALOGV("encoder successfully configured"); + return OMX_ErrorNone; + } else { + ALOGE("unknown error when configuring encoder"); + return OMX_ErrorUndefined; + } +} + + +// static +FLAC__StreamEncoderWriteStatus SoftFlacEncoder::flacEncoderWriteCallback( + const FLAC__StreamEncoder * /* encoder */, + const FLAC__byte buffer[], + size_t bytes, + unsigned samples, + unsigned current_frame, + void *client_data) { + return ((SoftFlacEncoder*) client_data)->onEncodedFlacAvailable( + buffer, bytes, samples, current_frame); +} + +} // namespace android + + +__attribute__((cfi_canonical_jump_table)) +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftFlacEncoder(name, callbacks, appData, component); +} + diff --git a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.h b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.h new file mode 100644 index 0000000..722fc13 --- /dev/null +++ b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2012 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_FLAC_ENC_H_ + +#define SOFT_FLAC_ENC_H_ + +#include + +#include "FLAC/stream_encoder.h" + +namespace android { + +struct SoftFlacEncoder : public SimpleSoftOMXComponent { + SoftFlacEncoder(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + + virtual OMX_ERRORTYPE initCheck() const; + +protected: + virtual ~SoftFlacEncoder(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + +private: + const unsigned int kNumBuffers = 2; + static constexpr unsigned int kMaxChannels = 2; + static constexpr unsigned int kNumSamplesPerFrame = 1152; + static constexpr unsigned int kMaxInputBufferSize = + kNumSamplesPerFrame * kMaxChannels * sizeof(float); + const unsigned int kMaxOutputBufferSize = 65536; //TODO check if this can be reduced + + bool mSignalledError; + + OMX_U32 mNumChannels; + OMX_U32 mSampleRate; + OMX_U32 mCompressionLevel; + OMX_NUMERICALDATATYPE mNumericalData = OMX_NumericalDataSigned; + OMX_U32 mBitsPerSample = 16; + + // should the data received by the callback be written to the output port + bool mEncoderWriteData; + bool mEncoderReturnedEncodedData; + bool mSawInputEOS; + bool mSentOutputEOS; + size_t mEncoderReturnedNbBytes; + OMX_TICKS mCurrentInputTimeStamp; + + FLAC__StreamEncoder* mFlacStreamEncoder; + + void initPorts(); + + OMX_ERRORTYPE configureEncoder(); + + // FLAC encoder callbacks + // maps to encoderEncodeFlac() + static FLAC__StreamEncoderWriteStatus flacEncoderWriteCallback( + const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], + size_t bytes, unsigned samples, unsigned current_frame, void *client_data); + + FLAC__StreamEncoderWriteStatus onEncodedFlacAvailable( + const FLAC__byte buffer[], + size_t bytes, unsigned samples, unsigned current_frame); + + // FLAC takes samples aligned on 32bit boundaries, use this buffer for the conversion + // before passing the input data to the encoder + FLAC__int32* mInputBufferPcm32; + + unsigned mHeaderOffset; + bool mHeaderComplete; + bool mWroteHeader; + char mHeader[128]; + + DISALLOW_EVIL_CONSTRUCTORS(SoftFlacEncoder); +}; + +} // namespace android + +#endif // SOFT_FLAC_ENC_H_ + diff --git a/media/libstagefright/codecs/flac/enc/exports.lds b/media/libstagefright/codecs/flac/enc/exports.lds new file mode 100644 index 0000000..e24f3fa --- /dev/null +++ b/media/libstagefright/codecs/flac/enc/exports.lds @@ -0,0 +1,5 @@ +{ + global: + _Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE; + local: *; +}; diff --git a/media/libstagefright/codecs/g711/dec/Android.bp b/media/libstagefright/codecs/g711/dec/Android.bp new file mode 100644 index 0000000..1dc34c3 --- /dev/null +++ b/media/libstagefright/codecs/g711/dec/Android.bp @@ -0,0 +1,35 @@ +package { + default_applicable_licenses: [ + "frameworks_av_media_libstagefright_codecs_g711_dec_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_av_media_libstagefright_codecs_g711_dec_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + +cc_library_shared { + name: "libstagefright_soft_g711dec", + defaults: ["libstagefright_softomx-defaults"], + + srcs: ["SoftG711.cpp"], + + version_script: "exports.lds", + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + "unsigned-integer-overflow", + ], + cfi: true, + }, +} diff --git a/media/libstagefright/codecs/g711/dec/MODULE_LICENSE_APACHE2 b/media/libstagefright/codecs/g711/dec/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/media/libstagefright/codecs/g711/dec/NOTICE b/media/libstagefright/codecs/g711/dec/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/media/libstagefright/codecs/g711/dec/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, 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/g711/dec/SoftG711.cpp b/media/libstagefright/codecs/g711/dec/SoftG711.cpp new file mode 100644 index 0000000..fe91510 --- /dev/null +++ b/media/libstagefright/codecs/g711/dec/SoftG711.cpp @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2011 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 "SoftG711" +#include + +#include "SoftG711.h" + +#include +#include + +#define MAX_CHANNEL_COUNT 6 /* maximum number of audio channels that can be decoded */ + +namespace android { + +template +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +SoftG711::SoftG711( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mIsMLaw(true), + mSignalledError(false), + mNumChannels(1), + mSamplingRate(8000) { + if (!strcmp(name, "OMX.google.g711.alaw.decoder")) { + mIsMLaw = false; + } else { + CHECK(!strcmp(name, "OMX.google.g711.mlaw.decoder")); + } + + initPorts(); +} + +SoftG711::~SoftG711() { +} + +void SoftG711::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.audio.cMIMEType = + const_cast( + mIsMLaw + ? MEDIA_MIMETYPE_AUDIO_G711_MLAW + : MEDIA_MIMETYPE_AUDIO_G711_ALAW); + + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingG711; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = kMaxNumSamplesPerFrame * sizeof(int16_t); + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.audio.cMIMEType = const_cast("audio/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); +} + +OMX_ERRORTYPE SoftG711::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioPortFormat: + { + OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex > 0) { + return OMX_ErrorNoMore; + } + + formatParams->eEncoding = + (formatParams->nPortIndex == 0) + ? OMX_AUDIO_CodingG711 : OMX_AUDIO_CodingPCM; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + pcmParams->eNumData = OMX_NumericalDataSigned; + pcmParams->eEndian = OMX_EndianBig; + pcmParams->bInterleaved = OMX_TRUE; + pcmParams->nBitPerSample = 16; + if (pcmParams->nPortIndex == 0) { + // input port + pcmParams->ePCMMode = mIsMLaw ? OMX_AUDIO_PCMModeMULaw + : OMX_AUDIO_PCMModeALaw; + } else { + // output port + pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; + } + pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; + + pcmParams->nChannels = mNumChannels; + pcmParams->nSamplingRate = mSamplingRate; + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftG711::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 0 && pcmParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + if (pcmParams->nChannels < 1 || pcmParams->nChannels > MAX_CHANNEL_COUNT) { + return OMX_ErrorUndefined; + } + + if(pcmParams->nPortIndex == 0) { + mNumChannels = pcmParams->nChannels; + } + + mSamplingRate = pcmParams->nSamplingRate; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPortFormat: + { + const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if ((formatParams->nPortIndex == 0 + && formatParams->eEncoding != OMX_AUDIO_CodingG711) + || (formatParams->nPortIndex == 1 + && formatParams->eEncoding != OMX_AUDIO_CodingPCM)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (!isValidOMXParam(roleParams)) { + return OMX_ErrorBadParameter; + } + + if (mIsMLaw) { + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.g711mlaw", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + } else { + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.g711alaw", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +void SoftG711::onQueueFilled(OMX_U32 /* portIndex */) { + if (mSignalledError) { + return; + } + + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + while (!inQueue.empty() && !outQueue.empty()) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + if ((inHeader->nFlags & OMX_BUFFERFLAG_EOS) && inHeader->nFilledLen == 0) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + return; + } + + if (inHeader->nFilledLen > kMaxNumSamplesPerFrame) { + ALOGE("input buffer too large (%d).", inHeader->nFilledLen); + + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + } + + if (inHeader->nFilledLen * sizeof(int16_t) > outHeader->nAllocLen) { + ALOGE("output buffer too small (%d).", outHeader->nAllocLen); + android_errorWriteLog(0x534e4554, "27793163"); + + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } + + const uint8_t *inputptr = inHeader->pBuffer + inHeader->nOffset; + + if (mIsMLaw) { + DecodeMLaw( + reinterpret_cast(outHeader->pBuffer), + inputptr, inHeader->nFilledLen); + } else { + DecodeALaw( + reinterpret_cast(outHeader->pBuffer), + inputptr, inHeader->nFilledLen); + } + + outHeader->nTimeStamp = inHeader->nTimeStamp; + outHeader->nOffset = 0; + outHeader->nFilledLen = inHeader->nFilledLen * sizeof(int16_t); + outHeader->nFlags = 0; + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + inHeader->nFilledLen = 0; + } else { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + } +} + +// static +void SoftG711::DecodeALaw( + int16_t *out, const uint8_t *in, size_t inSize) { + while (inSize > 0) { + inSize--; + int32_t x = *in++; + + int32_t ix = x ^ 0x55; + ix &= 0x7f; + + int32_t iexp = ix >> 4; + int32_t mant = ix & 0x0f; + + if (iexp > 0) { + mant += 16; + } + + mant = (mant << 4) + 8; + + if (iexp > 1) { + mant = mant << (iexp - 1); + } + + *out++ = (x > 127) ? mant : -mant; + } +} + +// static +void SoftG711::DecodeMLaw( + int16_t *out, const uint8_t *in, size_t inSize) { + while (inSize > 0) { + inSize--; + int32_t x = *in++; + + int32_t mantissa = ~x; + int32_t exponent = (mantissa >> 4) & 7; + int32_t segment = exponent + 1; + mantissa &= 0x0f; + + int32_t step = 4 << segment; + + int32_t abs = (0x80L << exponent) + step * mantissa + step / 2 - 4 * 33; + + *out++ = (x < 0x80) ? -abs : abs; + } +} + +} // namespace android + +__attribute__((cfi_canonical_jump_table)) +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftG711(name, callbacks, appData, component); +} + diff --git a/media/libstagefright/codecs/g711/dec/SoftG711.h b/media/libstagefright/codecs/g711/dec/SoftG711.h new file mode 100644 index 0000000..3ece246 --- /dev/null +++ b/media/libstagefright/codecs/g711/dec/SoftG711.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2011 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_G711_H_ + +#define SOFT_G711_H_ + +#include + +namespace android { + +struct SoftG711 : public SimpleSoftOMXComponent { + SoftG711(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftG711(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + +private: + enum { + kNumBuffers = 4, + kMaxNumSamplesPerFrame = 16384, + }; + + bool mIsMLaw; + bool mSignalledError; + OMX_U32 mNumChannels; + int32_t mSamplingRate; + + void initPorts(); + + static void DecodeALaw(int16_t *out, const uint8_t *in, size_t inSize); + static void DecodeMLaw(int16_t *out, const uint8_t *in, size_t inSize); + + DISALLOW_EVIL_CONSTRUCTORS(SoftG711); +}; + +} // namespace android + +#endif // SOFT_G711_H_ + diff --git a/media/libstagefright/codecs/g711/dec/exports.lds b/media/libstagefright/codecs/g711/dec/exports.lds new file mode 100644 index 0000000..e24f3fa --- /dev/null +++ b/media/libstagefright/codecs/g711/dec/exports.lds @@ -0,0 +1,5 @@ +{ + global: + _Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE; + local: *; +}; diff --git a/media/libstagefright/codecs/gsm/dec/Android.bp b/media/libstagefright/codecs/gsm/dec/Android.bp new file mode 100644 index 0000000..efa2f83 --- /dev/null +++ b/media/libstagefright/codecs/gsm/dec/Android.bp @@ -0,0 +1,37 @@ +package { + default_applicable_licenses: [ + "frameworks_av_media_libstagefright_codecs_gsm_dec_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_av_media_libstagefright_codecs_gsm_dec_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + +cc_library_shared { + name: "libstagefright_soft_gsmdec", + defaults: ["libstagefright_softomx-defaults"], + + srcs: ["SoftGSM.cpp"], + + version_script: "exports.lds", + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + "unsigned-integer-overflow", + ], + cfi: true, + }, + + static_libs: ["libgsm"], +} diff --git a/media/libstagefright/codecs/gsm/dec/MODULE_LICENSE_APACHE2 b/media/libstagefright/codecs/gsm/dec/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/media/libstagefright/codecs/gsm/dec/NOTICE b/media/libstagefright/codecs/gsm/dec/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/media/libstagefright/codecs/gsm/dec/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, 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/gsm/dec/SoftGSM.cpp b/media/libstagefright/codecs/gsm/dec/SoftGSM.cpp new file mode 100644 index 0000000..330cb8a --- /dev/null +++ b/media/libstagefright/codecs/gsm/dec/SoftGSM.cpp @@ -0,0 +1,363 @@ +/* + * Copyright (C) 2012 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 "SoftGSM" +#include + +#include "SoftGSM.h" + +#include +#include + +namespace android { + +template +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +// Microsoft WAV GSM encoding packs two GSM frames into 65 bytes. +static const int kMSGSMFrameSize = 65; + +SoftGSM::SoftGSM( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mSignalledError(false) { + + CHECK(!strcmp(name, "OMX.google.gsm.decoder")); + + mGsm = gsm_create(); + CHECK(mGsm); + int msopt = 1; + gsm_option(mGsm, GSM_OPT_WAV49, &msopt); + + initPorts(); +} + +SoftGSM::~SoftGSM() { + gsm_destroy(mGsm); +} + +void SoftGSM::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 1024 / kMSGSMFrameSize * kMSGSMFrameSize; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.audio.cMIMEType = + const_cast(MEDIA_MIMETYPE_AUDIO_MSGSM); + + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingGSMFR; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = kMaxNumSamplesPerFrame * sizeof(int16_t); + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.audio.cMIMEType = const_cast("audio/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); +} + +OMX_ERRORTYPE SoftGSM::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioPortFormat: + { + OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex > 0) { + return OMX_ErrorNoMore; + } + + formatParams->eEncoding = + (formatParams->nPortIndex == 0) + ? OMX_AUDIO_CodingGSMFR : OMX_AUDIO_CodingPCM; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + pcmParams->eNumData = OMX_NumericalDataSigned; + pcmParams->eEndian = OMX_EndianBig; + pcmParams->bInterleaved = OMX_TRUE; + pcmParams->nBitPerSample = 16; + pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; + pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; + + pcmParams->nChannels = 1; + pcmParams->nSamplingRate = 8000; + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftGSM::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 0 && pcmParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + if (pcmParams->nChannels != 1) { + return OMX_ErrorUndefined; + } + + if (pcmParams->nSamplingRate != 8000) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPortFormat: + { + const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if ((formatParams->nPortIndex == 0 + && formatParams->eEncoding != OMX_AUDIO_CodingGSMFR) + || (formatParams->nPortIndex == 1 + && formatParams->eEncoding != OMX_AUDIO_CodingPCM)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (!isValidOMXParam(roleParams)) { + return OMX_ErrorBadParameter; + } + + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.gsm", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +void SoftGSM::onQueueFilled(OMX_U32 /* portIndex */) { + if (mSignalledError) { + return; + } + + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + while (!inQueue.empty() && !outQueue.empty()) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + if ((inHeader->nFlags & OMX_BUFFERFLAG_EOS) && inHeader->nFilledLen == 0) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + return; + } + + if (inHeader->nFilledLen > kMaxNumSamplesPerFrame) { + ALOGE("input buffer too large (%d).", inHeader->nFilledLen); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + } + + if(((inHeader->nFilledLen / kMSGSMFrameSize) * kMSGSMFrameSize) != inHeader->nFilledLen) { + ALOGE("input buffer not multiple of %d (%d).", kMSGSMFrameSize, inHeader->nFilledLen); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + } + + if (outHeader->nAllocLen < (inHeader->nFilledLen / kMSGSMFrameSize) * 320) { + ALOGE("output buffer is not large enough (%d).", outHeader->nAllocLen); + android_errorWriteLog(0x534e4554, "27793367"); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } + + uint8_t *inputptr = inHeader->pBuffer + inHeader->nOffset; + + int n = mSignalledError ? 0 : DecodeGSM(mGsm, + reinterpret_cast(outHeader->pBuffer), inputptr, inHeader->nFilledLen); + + outHeader->nTimeStamp = inHeader->nTimeStamp; + outHeader->nOffset = 0; + outHeader->nFilledLen = n * sizeof(int16_t); + outHeader->nFlags = 0; + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + inHeader->nFilledLen = 0; + } else { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + } +} + + +// static +int SoftGSM::DecodeGSM(gsm handle, + int16_t *out, uint8_t *in, size_t inSize) { + + int ret = 0; + while (inSize > 0) { + gsm_decode(handle, in, out); + in += 33; + inSize -= 33; + out += 160; + ret += 160; + gsm_decode(handle, in, out); + in += 32; + inSize -= 32; + out += 160; + ret += 160; + } + return ret; +} + +void SoftGSM::onPortFlushCompleted(OMX_U32 portIndex) { + if (portIndex == 0) { + gsm_destroy(mGsm); + mGsm = gsm_create(); + int msopt = 1; + gsm_option(mGsm, GSM_OPT_WAV49, &msopt); + } +} + +void SoftGSM::onReset() { + gsm_destroy(mGsm); + mGsm = gsm_create(); + int msopt = 1; + gsm_option(mGsm, GSM_OPT_WAV49, &msopt); + mSignalledError = false; +} + + + + +} // namespace android + +__attribute__((cfi_canonical_jump_table)) +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftGSM(name, callbacks, appData, component); +} + diff --git a/media/libstagefright/codecs/gsm/dec/SoftGSM.h b/media/libstagefright/codecs/gsm/dec/SoftGSM.h new file mode 100644 index 0000000..d5885a6 --- /dev/null +++ b/media/libstagefright/codecs/gsm/dec/SoftGSM.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012 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_GSM_H_ + +#define SOFT_GSM_H_ + +#include + +#include "gsm.h" + +namespace android { + +struct SoftGSM : public SimpleSoftOMXComponent { + SoftGSM(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftGSM(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onReset(); + +private: + enum { + kNumBuffers = 4, + kMaxNumSamplesPerFrame = 16384, + }; + + bool mSignalledError; + gsm mGsm; + + void initPorts(); + + static int DecodeGSM(gsm handle, int16_t *out, uint8_t *in, size_t inSize); + + DISALLOW_EVIL_CONSTRUCTORS(SoftGSM); +}; + +} // namespace android + +#endif // SOFT_GSM_H_ + diff --git a/media/libstagefright/codecs/gsm/dec/exports.lds b/media/libstagefright/codecs/gsm/dec/exports.lds new file mode 100644 index 0000000..e24f3fa --- /dev/null +++ b/media/libstagefright/codecs/gsm/dec/exports.lds @@ -0,0 +1,5 @@ +{ + global: + _Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE; + local: *; +}; diff --git a/media/libstagefright/codecs/hevcdec/Android.bp b/media/libstagefright/codecs/hevcdec/Android.bp new file mode 100644 index 0000000..2c4091b --- /dev/null +++ b/media/libstagefright/codecs/hevcdec/Android.bp @@ -0,0 +1,38 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_av_media_libstagefright_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_av_media_libstagefright_license"], +} + +cc_library_shared { + name: "libstagefright_soft_hevcdec", + defaults: ["libstagefright_softomx-defaults"], + + static_libs: ["libhevcdec"], + srcs: ["SoftHEVC.cpp"], + + cflags: [ + "-Wall", + "-Wno-unused-variable", + ], + + version_script: "exports.lds", + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + ], + cfi: true, + config: { + cfi_assembly_support: true, + }, + }, + + // We need this because the current asm generates the following link error: + // requires unsupported dynamic reloc R_ARM_REL32; recompile with -fPIC + // Bug: 16853291 + ldflags: ["-Wl,-Bsymbolic"], +} diff --git a/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp b/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp new file mode 100644 index 0000000..176da47 --- /dev/null +++ b/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp @@ -0,0 +1,726 @@ +/* + * Copyright (C) 2014 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 "SoftHEVC" +#include + +#include "ihevc_typedefs.h" +#include "iv.h" +#include "ivd.h" +#include "ihevcd_cxa.h" +#include "SoftHEVC.h" + +#include +#include +#include +#include + +namespace android { + +#define componentName "video_decoder.hevc" +#define codingType OMX_VIDEO_CodingHEVC +#define CODEC_MIME_TYPE MEDIA_MIMETYPE_VIDEO_HEVC + +/** Function and structure definitions to keep code similar for each codec */ +#define ivdec_api_function ihevcd_cxa_api_function +#define ivdext_create_ip_t ihevcd_cxa_create_ip_t +#define ivdext_create_op_t ihevcd_cxa_create_op_t +#define ivdext_delete_ip_t ihevcd_cxa_delete_ip_t +#define ivdext_delete_op_t ihevcd_cxa_delete_op_t +#define ivdext_ctl_set_num_cores_ip_t ihevcd_cxa_ctl_set_num_cores_ip_t +#define ivdext_ctl_set_num_cores_op_t ihevcd_cxa_ctl_set_num_cores_op_t + +#define IVDEXT_CMD_CTL_SET_NUM_CORES \ + (IVD_CONTROL_API_COMMAND_TYPE_T)IHEVCD_CXA_CMD_CTL_SET_NUM_CORES + +static const CodecProfileLevel kProfileLevels[] = { + { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel51 }, + { OMX_VIDEO_HEVCProfileMainStill, OMX_VIDEO_HEVCMainTierLevel51 }, +}; + +SoftHEVC::SoftHEVC( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SoftVideoDecoderOMXComponent(name, componentName, codingType, + kProfileLevels, ARRAY_SIZE(kProfileLevels), + 320 /* width */, 240 /* height */, callbacks, + appData, component), + mCodecCtx(NULL), + mFlushOutBuffer(NULL), + mOmxColorFormat(OMX_COLOR_FormatYUV420Planar), + mIvColorFormat(IV_YUV_420P), + mChangingResolution(false), + mSignalledError(false), + mStride(mWidth) { + const size_t kMinCompressionRatio = 4 /* compressionRatio (for Level 4+) */; + const size_t kMaxOutputBufferSize = 2048 * 2048 * 3 / 2; + // INPUT_BUF_SIZE is given by HEVC codec as minimum input size + initPorts( + kNumBuffers, max(kMaxOutputBufferSize / kMinCompressionRatio, (size_t)INPUT_BUF_SIZE), + kNumBuffers, CODEC_MIME_TYPE, kMinCompressionRatio); +} + +status_t SoftHEVC::init() { + return initDecoder(); +} + +SoftHEVC::~SoftHEVC() { + ALOGV("In SoftHEVC::~SoftHEVC"); + CHECK_EQ(deInitDecoder(), (status_t)OK); +} + +static void *ivd_aligned_malloc(void *ctxt, WORD32 alignment, WORD32 size) { + UNUSED(ctxt); + return memalign(alignment, size); +} + +static void ivd_aligned_free(void *ctxt, void *buf) { + UNUSED(ctxt); + free(buf); + return; +} + +static size_t GetCPUCoreCount() { + long 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(cpuCoreCount >= 1); + ALOGV("Number of CPU cores: %ld", cpuCoreCount); + return (size_t)cpuCoreCount; +} + +void SoftHEVC::logVersion() { + ivd_ctl_getversioninfo_ip_t s_ctl_ip; + ivd_ctl_getversioninfo_op_t s_ctl_op; + UWORD8 au1_buf[512]; + IV_API_CALL_STATUS_T status; + + s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_GETVERSION; + s_ctl_ip.u4_size = sizeof(ivd_ctl_getversioninfo_ip_t); + s_ctl_op.u4_size = sizeof(ivd_ctl_getversioninfo_op_t); + s_ctl_ip.pv_version_buffer = au1_buf; + s_ctl_ip.u4_version_buffer_size = sizeof(au1_buf); + + status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, + (void *)&s_ctl_op); + + if (status != IV_SUCCESS) { + ALOGE("Error in getting version number: 0x%x", + s_ctl_op.u4_error_code); + } else { + ALOGV("Ittiam decoder version number: %s", + (char *)s_ctl_ip.pv_version_buffer); + } + return; +} + +status_t SoftHEVC::setParams(size_t stride) { + ivd_ctl_set_config_ip_t s_ctl_ip; + ivd_ctl_set_config_op_t s_ctl_op; + IV_API_CALL_STATUS_T status; + s_ctl_ip.u4_disp_wd = (UWORD32)stride; + s_ctl_ip.e_frm_skip_mode = IVD_SKIP_NONE; + + s_ctl_ip.e_frm_out_mode = IVD_DISPLAY_FRAME_OUT; + s_ctl_ip.e_vid_dec_mode = IVD_DECODE_FRAME; + s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_SETPARAMS; + s_ctl_ip.u4_size = sizeof(ivd_ctl_set_config_ip_t); + s_ctl_op.u4_size = sizeof(ivd_ctl_set_config_op_t); + + ALOGV("Set the run-time (dynamic) parameters stride = %zu", stride); + status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, + (void *)&s_ctl_op); + + if (status != IV_SUCCESS) { + ALOGE("Error in setting the run-time parameters: 0x%x", + s_ctl_op.u4_error_code); + + return UNKNOWN_ERROR; + } + return OK; +} + +status_t SoftHEVC::resetPlugin() { + mIsInFlush = false; + mReceivedEOS = false; + memset(mTimeStamps, 0, sizeof(mTimeStamps)); + memset(mTimeStampsValid, 0, sizeof(mTimeStampsValid)); + + /* Initialize both start and end times */ + gettimeofday(&mTimeStart, NULL); + gettimeofday(&mTimeEnd, NULL); + + return OK; +} + +bool SoftHEVC::getVUIParams() { + IV_API_CALL_STATUS_T status; + ihevcd_cxa_ctl_get_vui_params_ip_t s_ctl_get_vui_params_ip; + ihevcd_cxa_ctl_get_vui_params_op_t s_ctl_get_vui_params_op; + + s_ctl_get_vui_params_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_ctl_get_vui_params_ip.e_sub_cmd = + (IVD_CONTROL_API_COMMAND_TYPE_T)IHEVCD_CXA_CMD_CTL_GET_VUI_PARAMS; + + s_ctl_get_vui_params_ip.u4_size = + sizeof(ihevcd_cxa_ctl_get_vui_params_ip_t); + + s_ctl_get_vui_params_op.u4_size = sizeof(ihevcd_cxa_ctl_get_vui_params_op_t); + + status = ivdec_api_function( + (iv_obj_t *)mCodecCtx, (void *)&s_ctl_get_vui_params_ip, + (void *)&s_ctl_get_vui_params_op); + + if (status != IV_SUCCESS) { + ALOGW("Error in getting VUI params: 0x%x", + s_ctl_get_vui_params_op.u4_error_code); + return false; + } + + int32_t primaries = s_ctl_get_vui_params_op.u1_colour_primaries; + int32_t transfer = s_ctl_get_vui_params_op.u1_transfer_characteristics; + int32_t coeffs = s_ctl_get_vui_params_op.u1_matrix_coefficients; + bool fullRange = s_ctl_get_vui_params_op.u1_video_full_range_flag; + + ColorAspects colorAspects; + ColorUtils::convertIsoColorAspectsToCodecAspects( + primaries, transfer, coeffs, fullRange, colorAspects); + + // Update color aspects if necessary. + if (colorAspectsDiffer(colorAspects, mBitstreamColorAspects)) { + mBitstreamColorAspects = colorAspects; + status_t err = handleColorAspectsChange(); + CHECK(err == OK); + } + return true; +} + +status_t SoftHEVC::resetDecoder() { + ivd_ctl_reset_ip_t s_ctl_ip; + ivd_ctl_reset_op_t s_ctl_op; + IV_API_CALL_STATUS_T status; + + s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_RESET; + s_ctl_ip.u4_size = sizeof(ivd_ctl_reset_ip_t); + s_ctl_op.u4_size = sizeof(ivd_ctl_reset_op_t); + + status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, + (void *)&s_ctl_op); + if (IV_SUCCESS != status) { + ALOGE("Error in reset: 0x%x", s_ctl_op.u4_error_code); + return UNKNOWN_ERROR; + } + mSignalledError = false; + + /* Set number of cores/threads to be used by the codec */ + setNumCores(); + + mStride = 0; + return OK; +} + +status_t SoftHEVC::setNumCores() { + ivdext_ctl_set_num_cores_ip_t s_set_cores_ip; + ivdext_ctl_set_num_cores_op_t s_set_cores_op; + IV_API_CALL_STATUS_T status; + s_set_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_set_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES; + s_set_cores_ip.u4_num_cores = MIN(mNumCores, CODEC_MAX_NUM_CORES); + s_set_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t); + s_set_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t); + ALOGV("Set number of cores to %u", s_set_cores_ip.u4_num_cores); + status = ivdec_api_function(mCodecCtx, (void *)&s_set_cores_ip, + (void *)&s_set_cores_op); + if (IV_SUCCESS != status) { + ALOGE("Error in setting number of cores: 0x%x", + s_set_cores_op.u4_error_code); + return UNKNOWN_ERROR; + } + return OK; +} + +status_t SoftHEVC::setFlushMode() { + IV_API_CALL_STATUS_T status; + ivd_ctl_flush_ip_t s_video_flush_ip; + ivd_ctl_flush_op_t s_video_flush_op; + + s_video_flush_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_video_flush_ip.e_sub_cmd = IVD_CMD_CTL_FLUSH; + s_video_flush_ip.u4_size = sizeof(ivd_ctl_flush_ip_t); + s_video_flush_op.u4_size = sizeof(ivd_ctl_flush_op_t); + ALOGV("Set the decoder in flush mode "); + + /* Set the decoder in Flush mode, subsequent decode() calls will flush */ + status = ivdec_api_function(mCodecCtx, (void *)&s_video_flush_ip, + (void *)&s_video_flush_op); + + if (status != IV_SUCCESS) { + ALOGE("Error in setting the decoder in flush mode: (%d) 0x%x", status, + s_video_flush_op.u4_error_code); + return UNKNOWN_ERROR; + } + + mIsInFlush = true; + return OK; +} + +status_t SoftHEVC::initDecoder() { + IV_API_CALL_STATUS_T status; + + mNumCores = GetCPUCoreCount(); + mCodecCtx = NULL; + + mStride = outputBufferWidth(); + + /* Initialize the decoder */ + { + ivdext_create_ip_t s_create_ip; + ivdext_create_op_t s_create_op; + + void *dec_fxns = (void *)ivdec_api_function; + + s_create_ip.s_ivd_create_ip_t.u4_size = sizeof(ivdext_create_ip_t); + s_create_ip.s_ivd_create_ip_t.e_cmd = IVD_CMD_CREATE; + s_create_ip.s_ivd_create_ip_t.u4_share_disp_buf = 0; + s_create_op.s_ivd_create_op_t.u4_size = sizeof(ivdext_create_op_t); + s_create_ip.s_ivd_create_ip_t.e_output_format = mIvColorFormat; + s_create_ip.s_ivd_create_ip_t.pf_aligned_alloc = ivd_aligned_malloc; + s_create_ip.s_ivd_create_ip_t.pf_aligned_free = ivd_aligned_free; + s_create_ip.s_ivd_create_ip_t.pv_mem_ctxt = NULL; + + status = ivdec_api_function(mCodecCtx, (void *)&s_create_ip, (void *)&s_create_op); + + if (status != IV_SUCCESS) { + ALOGE("Error in create: 0x%x", + s_create_op.s_ivd_create_op_t.u4_error_code); + deInitDecoder(); + mCodecCtx = NULL; + return UNKNOWN_ERROR; + } + + mCodecCtx = (iv_obj_t*)s_create_op.s_ivd_create_op_t.pv_handle; + mCodecCtx->pv_fxns = dec_fxns; + mCodecCtx->u4_size = sizeof(iv_obj_t); + } + + /* Reset the plugin state */ + resetPlugin(); + + /* Set the run time (dynamic) parameters */ + setParams(mStride); + + /* Set number of cores/threads to be used by the codec */ + setNumCores(); + + /* Get codec version */ + logVersion(); + + mFlushNeeded = false; + return OK; +} + +status_t SoftHEVC::deInitDecoder() { + size_t i; + IV_API_CALL_STATUS_T status; + + if (mCodecCtx) { + ivdext_delete_ip_t s_delete_ip; + ivdext_delete_op_t s_delete_op; + + s_delete_ip.s_ivd_delete_ip_t.u4_size = sizeof(ivdext_delete_ip_t); + s_delete_ip.s_ivd_delete_ip_t.e_cmd = IVD_CMD_DELETE; + + s_delete_op.s_ivd_delete_op_t.u4_size = sizeof(ivdext_delete_op_t); + + status = ivdec_api_function(mCodecCtx, (void *)&s_delete_ip, (void *)&s_delete_op); + if (status != IV_SUCCESS) { + ALOGE("Error in delete: 0x%x", + s_delete_op.s_ivd_delete_op_t.u4_error_code); + return UNKNOWN_ERROR; + } + } + + + mChangingResolution = false; + + return OK; +} + +void SoftHEVC::onReset() { + ALOGV("onReset called"); + SoftVideoDecoderOMXComponent::onReset(); + + mSignalledError = false; + resetDecoder(); + resetPlugin(); +} + +bool SoftHEVC::setDecodeArgs(ivd_video_decode_ip_t *ps_dec_ip, + ivd_video_decode_op_t *ps_dec_op, + OMX_BUFFERHEADERTYPE *inHeader, + OMX_BUFFERHEADERTYPE *outHeader, + size_t timeStampIx) { + size_t sizeY = outputBufferWidth() * outputBufferHeight(); + size_t sizeUV; + + ps_dec_ip->u4_size = sizeof(ivd_video_decode_ip_t); + ps_dec_op->u4_size = sizeof(ivd_video_decode_op_t); + + ps_dec_ip->e_cmd = IVD_CMD_VIDEO_DECODE; + + /* When in flush and after EOS with zero byte input, + * inHeader is set to zero. Hence check for non-null */ + if (inHeader) { + ps_dec_ip->u4_ts = timeStampIx; + ps_dec_ip->pv_stream_buffer = inHeader->pBuffer + + inHeader->nOffset; + ps_dec_ip->u4_num_Bytes = inHeader->nFilledLen; + } else { + ps_dec_ip->u4_ts = 0; + ps_dec_ip->pv_stream_buffer = NULL; + ps_dec_ip->u4_num_Bytes = 0; + } + + sizeUV = sizeY / 4; + ps_dec_ip->s_out_buffer.u4_min_out_buf_size[0] = sizeY; + ps_dec_ip->s_out_buffer.u4_min_out_buf_size[1] = sizeUV; + ps_dec_ip->s_out_buffer.u4_min_out_buf_size[2] = sizeUV; + + uint8_t *pBuf; + if (outHeader) { + if (outHeader->nAllocLen < sizeY + (sizeUV * 2)) { + android_errorWriteLog(0x534e4554, "27833616"); + return false; + } + pBuf = outHeader->pBuffer; + } else { + // mFlushOutBuffer always has the right size. + pBuf = mFlushOutBuffer; + } + + ps_dec_ip->s_out_buffer.pu1_bufs[0] = pBuf; + ps_dec_ip->s_out_buffer.pu1_bufs[1] = pBuf + sizeY; + ps_dec_ip->s_out_buffer.pu1_bufs[2] = pBuf + sizeY + sizeUV; + ps_dec_ip->s_out_buffer.u4_num_bufs = 3; + return true; +} +void SoftHEVC::onPortFlushCompleted(OMX_U32 portIndex) { + /* Once the output buffers are flushed, ignore any buffers that are held in decoder */ + if (kOutputPortIndex == portIndex) { + setFlushMode(); + + /* Allocate a picture buffer to flushed data */ + uint32_t displayStride = outputBufferWidth(); + uint32_t displayHeight = outputBufferHeight(); + + uint32_t bufferSize = displayStride * displayHeight * 3 / 2; + mFlushOutBuffer = (uint8_t *)memalign(128, bufferSize); + if (NULL == mFlushOutBuffer) { + ALOGE("Could not allocate flushOutputBuffer of size %u", bufferSize); + return; + } + + while (true) { + ivd_video_decode_ip_t s_dec_ip; + ivd_video_decode_op_t s_dec_op; + IV_API_CALL_STATUS_T status; + size_t sizeY, sizeUV; + + setDecodeArgs(&s_dec_ip, &s_dec_op, NULL, NULL, 0); + + status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, + (void *)&s_dec_op); + if (0 == s_dec_op.u4_output_present) { + resetPlugin(); + break; + } + } + + if (mFlushOutBuffer) { + free(mFlushOutBuffer); + mFlushOutBuffer = NULL; + } + + } +} + +void SoftHEVC::onQueueFilled(OMX_U32 portIndex) { + UNUSED(portIndex); + + if (mSignalledError) { + return; + } + if (mOutputPortSettingsChange != NONE) { + return; + } + + if (NULL == mCodecCtx) { + if (OK != initDecoder()) { + ALOGE("Failed to initialize decoder"); + notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL); + mSignalledError = true; + return; + } + } + if (outputBufferWidth() != mStride) { + /* Set the run-time (dynamic) parameters */ + mStride = outputBufferWidth(); + setParams(mStride); + } + + List &inQueue = getPortQueue(kInputPortIndex); + List &outQueue = getPortQueue(kOutputPortIndex); + + while (!outQueue.empty()) { + BufferInfo *inInfo; + OMX_BUFFERHEADERTYPE *inHeader; + + BufferInfo *outInfo; + OMX_BUFFERHEADERTYPE *outHeader; + size_t timeStampIx; + + inInfo = NULL; + inHeader = NULL; + + if (!mIsInFlush) { + if (!inQueue.empty()) { + inInfo = *inQueue.begin(); + inHeader = inInfo->mHeader; + } else { + break; + } + } + + outInfo = *outQueue.begin(); + outHeader = outInfo->mHeader; + outHeader->nFlags = 0; + outHeader->nTimeStamp = 0; + outHeader->nOffset = 0; + + if (inHeader != NULL && (inHeader->nFlags & OMX_BUFFERFLAG_EOS)) { + mReceivedEOS = true; + if (inHeader->nFilledLen == 0) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + setFlushMode(); + } + } + + /* Get a free slot in timestamp array to hold input timestamp */ + { + size_t i; + timeStampIx = 0; + for (i = 0; i < MAX_TIME_STAMPS; i++) { + if (!mTimeStampsValid[i]) { + timeStampIx = i; + break; + } + } + if (inHeader != NULL) { + mTimeStampsValid[timeStampIx] = true; + mTimeStamps[timeStampIx] = inHeader->nTimeStamp; + } + } + + { + ivd_video_decode_ip_t s_dec_ip; + ivd_video_decode_op_t s_dec_op; + WORD32 timeDelay, timeTaken; + size_t sizeY, sizeUV; + + if (!setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx)) { + ALOGE("Decoder arg setup failed"); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } + + GETTIME(&mTimeStart, NULL); + /* Compute time elapsed between end of previous decode() + * to start of current decode() */ + TIME_DIFF(mTimeEnd, mTimeStart, timeDelay); + + IV_API_CALL_STATUS_T status; + status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op); + + bool unsupportedResolution = + (IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_dec_op.u4_error_code & IVD_ERROR_MASK)); + + /* Check for unsupported dimensions */ + if (unsupportedResolution) { + ALOGE("Unsupported resolution : %dx%d", mWidth, mHeight); + notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL); + mSignalledError = true; + return; + } + + bool allocationFailed = + (IVD_MEM_ALLOC_FAILED == (s_dec_op.u4_error_code & IVD_ERROR_MASK)); + if (allocationFailed) { + ALOGE("Allocation failure in decoder"); + notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL); + mSignalledError = true; + return; + } + + if (IS_IVD_FATAL_ERROR(s_dec_op.u4_error_code)) { + ALOGE("Fatal Error : 0x%x", s_dec_op.u4_error_code); + notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL); + mSignalledError = true; + return; + } + + bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & IVD_ERROR_MASK)); + + getVUIParams(); + + GETTIME(&mTimeEnd, NULL); + /* Compute time taken for decode() */ + TIME_DIFF(mTimeStart, mTimeEnd, timeTaken); + + ALOGV("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay, + s_dec_op.u4_num_bytes_consumed); + if (s_dec_op.u4_frame_decoded_flag && !mFlushNeeded) { + mFlushNeeded = true; + } + + if ((inHeader != NULL) && (1 != s_dec_op.u4_frame_decoded_flag)) { + /* If the input did not contain picture data, then ignore + * the associated timestamp */ + mTimeStampsValid[timeStampIx] = false; + } + + // If the decoder is in the changing resolution mode and there is no output present, + // that means the switching is done and it's ready to reset the decoder and the plugin. + if (mChangingResolution && !s_dec_op.u4_output_present) { + mChangingResolution = false; + resetDecoder(); + resetPlugin(); + mStride = outputBufferWidth(); + setParams(mStride); + continue; + } + + if (resChanged) { + mChangingResolution = true; + if (mFlushNeeded) { + setFlushMode(); + } + continue; + } + + // Combine the resolution change and coloraspects change in one PortSettingChange event + // if necessary. + if ((0 < s_dec_op.u4_pic_wd) && (0 < s_dec_op.u4_pic_ht)) { + uint32_t width = s_dec_op.u4_pic_wd; + uint32_t height = s_dec_op.u4_pic_ht; + bool portWillReset = false; + handlePortSettingsChange(&portWillReset, width, height); + + if (portWillReset) { + resetDecoder(); + resetPlugin(); + return; + } + } else if (mUpdateColorAspects) { + notify(OMX_EventPortSettingsChanged, kOutputPortIndex, + kDescribeColorAspectsIndex, NULL); + mUpdateColorAspects = false; + return; + } + + if (s_dec_op.u4_output_present) { + outHeader->nFilledLen = (outputBufferWidth() * outputBufferHeight() * 3) / 2; + + outHeader->nTimeStamp = mTimeStamps[s_dec_op.u4_ts]; + mTimeStampsValid[s_dec_op.u4_ts] = false; + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + } else if (mIsInFlush) { + /* If in flush mode and no output is returned by the codec, + * then come out of flush mode */ + mIsInFlush = false; + + /* If EOS was recieved on input port and there is no output + * from the codec, then signal EOS on output port */ + if (mReceivedEOS) { + outHeader->nFilledLen = 0; + outHeader->nFlags |= OMX_BUFFERFLAG_EOS; + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + resetPlugin(); + } + } + } + + /* If input EOS is seen and decoder is not in flush mode, + * set the decoder in flush mode. + * There can be a case where EOS is sent along with last picture data + * In that case, only after decoding that input data, decoder has to be + * put in flush. This case is handled here */ + + if (mReceivedEOS && !mIsInFlush) { + setFlushMode(); + } + + // TODO: Handle more than one picture data + if (inHeader != NULL) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } + } +} + +int SoftHEVC::getColorAspectPreference() { + return kPreferBitstream; +} + +} // namespace android + +__attribute__((cfi_canonical_jump_table)) +android::SoftOMXComponent *createSoftOMXComponent(const char *name, + const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, + OMX_COMPONENTTYPE **component) { + android::SoftHEVC *codec = new android::SoftHEVC(name, callbacks, appData, component); + if (codec->init() != android::OK) { + android::sp release = codec; + return NULL; + } + return codec; +} diff --git a/media/libstagefright/codecs/hevcdec/SoftHEVC.h b/media/libstagefright/codecs/hevcdec/SoftHEVC.h new file mode 100644 index 0000000..5800490 --- /dev/null +++ b/media/libstagefright/codecs/hevcdec/SoftHEVC.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2014 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_HEVC_H_ + +#define SOFT_HEVC_H_ + +#include +#include + +namespace android { + +/** Number of entries in the time-stamp array */ +#define MAX_TIME_STAMPS 64 + +/** Maximum number of cores supported by the codec */ +#define CODEC_MAX_NUM_CORES 4 + +#define CODEC_MAX_WIDTH 1920 + +#define CODEC_MAX_HEIGHT 1088 + +/** Input buffer size */ +#define INPUT_BUF_SIZE (1024 * 1024) + +#define MIN(a, b) ((a) < (b)) ? (a) : (b) + +/** Used to remove warnings about unused parameters */ +#define UNUSED(x) ((void)(x)) + +/** Get time */ +#define GETTIME(a, b) gettimeofday(a, b); + +/** Compute difference between start and end */ +#define TIME_DIFF(start, end, diff) \ + diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \ + ((end).tv_usec - (start).tv_usec); + +struct SoftHEVC: public SoftVideoDecoderOMXComponent { + SoftHEVC(const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component); + + status_t init(); + +protected: + virtual ~SoftHEVC(); + + virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onReset(); + virtual int getColorAspectPreference(); +private: + // Number of input and output buffers + enum { + kNumBuffers = 8 + }; + + iv_obj_t *mCodecCtx; // Codec context + + size_t mNumCores; // Number of cores to be uesd by the codec + + struct timeval mTimeStart; // Time at the start of decode() + struct timeval mTimeEnd; // Time at the end of decode() + + // Internal buffer to be used to flush out the buffers from decoder + uint8_t *mFlushOutBuffer; + + // Status of entries in the timestamp array + bool mTimeStampsValid[MAX_TIME_STAMPS]; + + // Timestamp array - Since codec does not take 64 bit timestamps, + // they are maintained in the plugin + OMX_S64 mTimeStamps[MAX_TIME_STAMPS]; + + OMX_COLOR_FORMATTYPE mOmxColorFormat; // OMX Color format + IV_COLOR_FORMAT_T mIvColorFormat; // Ittiam Color format + + bool mIsInFlush; // codec is flush mode + bool mReceivedEOS; // EOS is receieved on input port + + // The input stream has changed to a different resolution, which is still supported by the + // codec. So the codec is switching to decode the new resolution. + bool mChangingResolution; + bool mFlushNeeded; + bool mSignalledError; + size_t mStride; + + status_t initDecoder(); + status_t deInitDecoder(); + status_t setFlushMode(); + status_t setParams(size_t stride); + void logVersion(); + status_t setNumCores(); + status_t resetDecoder(); + status_t resetPlugin(); + + bool setDecodeArgs(ivd_video_decode_ip_t *ps_dec_ip, + ivd_video_decode_op_t *ps_dec_op, + OMX_BUFFERHEADERTYPE *inHeader, + OMX_BUFFERHEADERTYPE *outHeader, + size_t timeStampIx); + + bool getVUIParams(); + + DISALLOW_EVIL_CONSTRUCTORS (SoftHEVC); +}; + +} // namespace android + +#endif // SOFT_HEVC_H_ diff --git a/media/libstagefright/codecs/hevcdec/exports.lds b/media/libstagefright/codecs/hevcdec/exports.lds new file mode 100644 index 0000000..e24f3fa --- /dev/null +++ b/media/libstagefright/codecs/hevcdec/exports.lds @@ -0,0 +1,5 @@ +{ + global: + _Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE; + local: *; +}; diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.bp b/media/libstagefright/codecs/m4v_h263/dec/Android.bp new file mode 100644 index 0000000..725c79c --- /dev/null +++ b/media/libstagefright/codecs/m4v_h263/dec/Android.bp @@ -0,0 +1,28 @@ + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_av_media_libstagefright_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_av_media_libstagefright_license"], +} + +cc_library_shared { + name: "libstagefright_soft_mpeg4dec", + defaults: ["libstagefright_softomx-defaults"], + + srcs: ["SoftMPEG4.cpp"], + + cflags: [ + ], + + static_libs: ["libstagefright_m4vh263dec"], + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + ], + cfi: true, + }, +} diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp new file mode 100644 index 0000000..800e2e1 --- /dev/null +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp @@ -0,0 +1,440 @@ +/* + * Copyright (C) 2011 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 "SoftMPEG4" +#include + +#include "SoftMPEG4.h" + +#include +#include +#include +#include + +#include "mp4dec_api.h" + +namespace android { + +static const CodecProfileLevel kM4VProfileLevels[] = { + { OMX_VIDEO_MPEG4ProfileSimple, OMX_VIDEO_MPEG4Level3 }, +}; + +static const CodecProfileLevel kH263ProfileLevels[] = { + { OMX_VIDEO_H263ProfileBaseline, OMX_VIDEO_H263Level30 }, + { OMX_VIDEO_H263ProfileBaseline, OMX_VIDEO_H263Level45 }, + { OMX_VIDEO_H263ProfileISWV2, OMX_VIDEO_H263Level30 }, + { OMX_VIDEO_H263ProfileISWV2, OMX_VIDEO_H263Level45 }, +}; + +SoftMPEG4::SoftMPEG4( + const char *name, + const char *componentRole, + OMX_VIDEO_CODINGTYPE codingType, + const CodecProfileLevel *profileLevels, + size_t numProfileLevels, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SoftVideoDecoderOMXComponent( + name, componentRole, codingType, profileLevels, numProfileLevels, + 352 /* width */, 288 /* height */, callbacks, appData, component), + mMode(codingType == OMX_VIDEO_CodingH263 ? MODE_H263 : MODE_MPEG4), + mHandle(new tagvideoDecControls), + mInputBufferCount(0), + mSignalledError(false), + mInitialized(false), + mFramesConfigured(false), + mNumSamplesOutput(0), + mPvTime(0) { + initPorts( + kNumInputBuffers, + 352 * 288 * 3 / 2 /* minInputBufferSize */, + kNumOutputBuffers, + (mMode == MODE_MPEG4) + ? MEDIA_MIMETYPE_VIDEO_MPEG4 : MEDIA_MIMETYPE_VIDEO_H263); + CHECK_EQ(initDecoder(), (status_t)OK); +} + +SoftMPEG4::~SoftMPEG4() { + if (mInitialized) { + PVCleanUpVideoDecoder(mHandle); + } + + delete mHandle; + mHandle = NULL; +} + +status_t SoftMPEG4::initDecoder() { + memset(mHandle, 0, sizeof(tagvideoDecControls)); + return OK; +} + +void SoftMPEG4::onQueueFilled(OMX_U32 /* portIndex */) { + if (mSignalledError || mOutputPortSettingsChange != NONE) { + return; + } + + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + while (!inQueue.empty() && outQueue.size() == kNumOutputBuffers) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + if (inHeader == NULL) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + continue; + } + + PortInfo *port = editPortInfo(1); + + OMX_BUFFERHEADERTYPE *outHeader = + port->mBuffers.editItemAt(mNumSamplesOutput & 1).mHeader; + + if (inHeader->nFilledLen == 0) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + ++mInputBufferCount; + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + + List::iterator it = outQueue.begin(); + while (it != outQueue.end() && (*it)->mHeader != outHeader) { + ++it; + } + if (it == outQueue.end()) { + ALOGE("couldn't find port buffer %d in outQueue: b/109891727", mNumSamplesOutput & 1); + android_errorWriteLog(0x534e4554, "109891727"); + return; + } + + BufferInfo *outInfo = *it; + outInfo->mOwnedByUs = false; + outQueue.erase(it); + outInfo = NULL; + + notifyFillBufferDone(outHeader); + outHeader = NULL; + } + return; + } + + uint8_t *bitstream = inHeader->pBuffer + inHeader->nOffset; + uint32_t *start_code = (uint32_t *)bitstream; + bool volHeader = *start_code == 0xB0010000; + if (volHeader) { + PVCleanUpVideoDecoder(mHandle); + mInitialized = false; + } + + if (!mInitialized) { + uint8_t *vol_data[1]; + int32_t vol_size = 0; + + vol_data[0] = NULL; + + if ((inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) || volHeader) { + vol_data[0] = bitstream; + vol_size = inHeader->nFilledLen; + } + + MP4DecodingMode mode = + (mMode == MODE_MPEG4) ? MPEG4_MODE : H263_MODE; + + Bool success = PVInitVideoDecoder( + mHandle, vol_data, &vol_size, 1, + outputBufferWidth(), outputBufferHeight(), mode); + + if (!success) { + ALOGW("PVInitVideoDecoder failed. Unsupported content?"); + + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } + + MP4DecodingMode actualMode = PVGetDecBitstreamMode(mHandle); + if (mode != actualMode) { + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } + + PVSetPostProcType((VideoDecControls *) mHandle, 0); + + bool hasFrameData = false; + if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } else if (volHeader) { + hasFrameData = true; + } + + mInitialized = true; + + if (mode == MPEG4_MODE && handlePortSettingsChange()) { + return; + } + + if (!hasFrameData) { + continue; + } + } + + if (!mFramesConfigured) { + PortInfo *port = editPortInfo(1); + OMX_BUFFERHEADERTYPE *outHeader = port->mBuffers.editItemAt(1).mHeader; + + OMX_U32 yFrameSize = sizeof(uint8) * mHandle->size; + if ((outHeader->nAllocLen < yFrameSize) || + (outHeader->nAllocLen - yFrameSize < yFrameSize / 2)) { + ALOGE("Too small output buffer for reference frame: %lu bytes", + (unsigned long)outHeader->nAllocLen); + android_errorWriteLog(0x534e4554, "30033990"); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } + PVSetReferenceYUV(mHandle, outHeader->pBuffer); + mFramesConfigured = true; + } + + uint32_t useExtTimestamp = (inHeader->nOffset == 0); + + // decoder deals in ms (int32_t), OMX in us (int64_t) + // so use fake timestamp instead + uint32_t timestamp = 0xFFFFFFFF; + if (useExtTimestamp) { + mPvToOmxTimeMap.add(mPvTime, inHeader->nTimeStamp); + timestamp = mPvTime; + mPvTime++; + } + + int32_t bufferSize = inHeader->nFilledLen; + int32_t tmp = bufferSize; + + OMX_U32 frameSize; + OMX_U64 yFrameSize = (OMX_U64)mWidth * (OMX_U64)mHeight; + if (yFrameSize > ((OMX_U64)UINT32_MAX / 3) * 2) { + ALOGE("Frame size too large"); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } + frameSize = (OMX_U32)(yFrameSize + (yFrameSize / 2)); + + if (outHeader->nAllocLen < frameSize) { + android_errorWriteLog(0x534e4554, "27833616"); + ALOGE("Insufficient output buffer size"); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } + + // Need to check if header contains new info, e.g., width/height, etc. + VopHeaderInfo header_info; + uint8_t *bitstreamTmp = bitstream; + if (PVDecodeVopHeader( + mHandle, &bitstreamTmp, ×tamp, &tmp, + &header_info, &useExtTimestamp, + outHeader->pBuffer) != PV_TRUE) { + ALOGE("failed to decode vop header."); + + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } + if (handlePortSettingsChange()) { + return; + } + + // The PV decoder is lying to us, sometimes it'll claim to only have + // consumed a subset of the buffer when it clearly consumed all of it. + // ignore whatever it says... + if (PVDecodeVopBody(mHandle, &tmp) != PV_TRUE) { + ALOGE("failed to decode video frame."); + + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } + + // H263 doesn't have VOL header, the frame size information is in short header, i.e. the + // decoder may detect size change after PVDecodeVideoFrame. + if (handlePortSettingsChange()) { + return; + } + + if (mPvToOmxTimeMap.indexOfKey(timestamp) >= 0) { + // decoder deals in ms, OMX in us. + outHeader->nTimeStamp = mPvToOmxTimeMap.valueFor(timestamp); + mPvToOmxTimeMap.removeItem(timestamp); + } + + inHeader->nOffset += bufferSize; + inHeader->nFilledLen = 0; + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + } else { + outHeader->nFlags = 0; + } + + if (inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } + + ++mInputBufferCount; + + outHeader->nOffset = 0; + outHeader->nFilledLen = frameSize; + + List::iterator it = outQueue.begin(); + while (it != outQueue.end() && (*it)->mHeader != outHeader) { + ++it; + } + if (it == outQueue.end()) { + return; + } + + BufferInfo *outInfo = *it; + outInfo->mOwnedByUs = false; + outQueue.erase(it); + outInfo = NULL; + + notifyFillBufferDone(outHeader); + outHeader = NULL; + + ++mNumSamplesOutput; + } +} + +bool SoftMPEG4::handlePortSettingsChange() { + uint32_t disp_width, disp_height; + PVGetVideoDimensions(mHandle, (int32 *)&disp_width, (int32 *)&disp_height); + + uint32_t buf_width, buf_height; + PVGetBufferDimensions(mHandle, (int32 *)&buf_width, (int32 *)&buf_height); + + CHECK_LE(disp_width, buf_width); + CHECK_LE(disp_height, buf_height); + + ALOGV("disp_width = %d, disp_height = %d, buf_width = %d, buf_height = %d", + disp_width, disp_height, buf_width, buf_height); + + CropSettingsMode cropSettingsMode = kCropUnSet; + if (disp_width != buf_width || disp_height != buf_height) { + cropSettingsMode = kCropSet; + + if (mCropWidth != disp_width || mCropHeight != disp_height) { + mCropLeft = 0; + mCropTop = 0; + mCropWidth = disp_width; + mCropHeight = disp_height; + cropSettingsMode = kCropChanged; + } + } + + bool portWillReset = false; + const bool fakeStride = true; + SoftVideoDecoderOMXComponent::handlePortSettingsChange( + &portWillReset, buf_width, buf_height, + OMX_COLOR_FormatYUV420Planar, cropSettingsMode, fakeStride); + if (portWillReset) { + if (mMode == MODE_H263) { + PVCleanUpVideoDecoder(mHandle); + + uint8_t *vol_data[1]; + int32_t vol_size = 0; + + vol_data[0] = NULL; + if (!PVInitVideoDecoder( + mHandle, vol_data, &vol_size, 1, outputBufferWidth(), outputBufferHeight(), + H263_MODE)) { + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return true; + } + } + + mFramesConfigured = false; + } + + return portWillReset; +} + +void SoftMPEG4::onPortFlushCompleted(OMX_U32 portIndex) { + if (portIndex == 0 && mInitialized) { + CHECK_EQ((int)PVResetVideoDecoder(mHandle), (int)PV_TRUE); + } + mFramesConfigured = false; +} + +void SoftMPEG4::onReset() { + SoftVideoDecoderOMXComponent::onReset(); + mPvToOmxTimeMap.clear(); + mSignalledError = false; + mFramesConfigured = false; + if (mInitialized) { + PVCleanUpVideoDecoder(mHandle); + mInitialized = false; + } +} + +void SoftMPEG4::updatePortDefinitions(bool updateCrop, bool updateInputSize) { + SoftVideoDecoderOMXComponent::updatePortDefinitions(updateCrop, updateInputSize); + + /* We have to align our width and height - this should affect stride! */ + OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kOutputPortIndex)->mDef; + def->format.video.nStride = align(def->format.video.nStride, 16); + def->format.video.nSliceHeight = align(def->format.video.nSliceHeight, 16); + def->nBufferSize = (def->format.video.nStride * def->format.video.nSliceHeight * 3) / 2; +} + +} // namespace android + +__attribute__((cfi_canonical_jump_table)) +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + using namespace android; + if (!strcmp(name, "OMX.google.h263.decoder")) { + return new android::SoftMPEG4( + name, "video_decoder.h263", OMX_VIDEO_CodingH263, + kH263ProfileLevels, ARRAY_SIZE(kH263ProfileLevels), + callbacks, appData, component); + } else if (!strcmp(name, "OMX.google.mpeg4.decoder")) { + return new android::SoftMPEG4( + name, "video_decoder.mpeg4", OMX_VIDEO_CodingMPEG4, + kM4VProfileLevels, ARRAY_SIZE(kM4VProfileLevels), + callbacks, appData, component); + } else { + CHECK(!"Unknown component"); + } + return NULL; +} + diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h new file mode 100644 index 0000000..e399ac9 --- /dev/null +++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2011 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_MPEG4_H_ + +#define SOFT_MPEG4_H_ + +#include + +struct tagvideoDecControls; + +namespace android { + +struct SoftMPEG4 : public SoftVideoDecoderOMXComponent { + SoftMPEG4(const char *name, + const char *componentRole, + OMX_VIDEO_CODINGTYPE codingType, + const CodecProfileLevel *profileLevels, + size_t numProfileLevels, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftMPEG4(); + + virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onReset(); + +private: + enum { + kNumInputBuffers = 4, + kNumOutputBuffers = 2, + }; + + enum { + MODE_MPEG4, + MODE_H263, + } mMode; + + tagvideoDecControls *mHandle; + + size_t mInputBufferCount; + + bool mSignalledError; + bool mInitialized; + bool mFramesConfigured; + + int32_t mNumSamplesOutput; + int32_t mPvTime; + KeyedVector mPvToOmxTimeMap; + + status_t initDecoder(); + + virtual void updatePortDefinitions(bool updateCrop = true, bool updateInputSize = false); + bool handlePortSettingsChange(); + + DISALLOW_EVIL_CONSTRUCTORS(SoftMPEG4); +}; + +} // namespace android + +#endif // SOFT_MPEG4_H_ + + diff --git a/media/libstagefright/codecs/m4v_h263/dec/exports.lds b/media/libstagefright/codecs/m4v_h263/dec/exports.lds new file mode 100644 index 0000000..e24f3fa --- /dev/null +++ b/media/libstagefright/codecs/m4v_h263/dec/exports.lds @@ -0,0 +1,5 @@ +{ + global: + _Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE; + local: *; +}; diff --git a/media/libstagefright/codecs/m4v_h263/enc/Android.bp b/media/libstagefright/codecs/m4v_h263/enc/Android.bp new file mode 100644 index 0000000..d10e40d --- /dev/null +++ b/media/libstagefright/codecs/m4v_h263/enc/Android.bp @@ -0,0 +1,30 @@ +//############################################################################### + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_av_media_libstagefright_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_av_media_libstagefright_license"], +} + +cc_library_shared { + name: "libstagefright_soft_mpeg4enc", + defaults: ["libstagefright_softomx-defaults"], + + srcs: ["SoftMPEG4Encoder.cpp"], + + cflags: [ + "-DBX_RC", + ], + + static_libs: ["libstagefright_m4vh263enc"], + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + ], + cfi: true, + }, +} diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp new file mode 100644 index 0000000..bb1cb0b --- /dev/null +++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp @@ -0,0 +1,550 @@ +/* + * Copyright (C) 2012 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 "SoftMPEG4Encoder" +#include +#include + +#include "mp4enc_api.h" +#include "OMX_Video.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "SoftMPEG4Encoder.h" + +#include + +#ifndef INT32_MAX +#define INT32_MAX 2147483647 +#endif + +namespace android { + +template +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +static const CodecProfileLevel kMPEG4ProfileLevels[] = { + { OMX_VIDEO_MPEG4ProfileCore, OMX_VIDEO_MPEG4Level2 }, +}; + +static const CodecProfileLevel kH263ProfileLevels[] = { + { OMX_VIDEO_H263ProfileBaseline, OMX_VIDEO_H263Level45 }, +}; + +SoftMPEG4Encoder::SoftMPEG4Encoder( + const char *name, + const char *componentRole, + OMX_VIDEO_CODINGTYPE codingType, + const char *mime, + const CodecProfileLevel *profileLevels, + size_t numProfileLevels, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SoftVideoEncoderOMXComponent( + name, componentRole, codingType, + profileLevels, numProfileLevels, + 176 /* width */, 144 /* height */, + callbacks, appData, component), + mEncodeMode(COMBINE_MODE_WITH_ERR_RES), + mKeyFrameInterval(30), + mNumInputFrames(-1), + mStarted(false), + mSawInputEOS(false), + mSignalledError(false), + mHandle(new tagvideoEncControls), + mEncParams(new tagvideoEncOptions), + mInputFrameData(NULL) { + + if (codingType == OMX_VIDEO_CodingH263) { + mEncodeMode = H263_MODE; + } + + // 256 * 1024 is a magic number for PV's encoder, not sure why + const size_t kOutputBufferSize = 256 * 1024; + + initPorts(kNumBuffers, kNumBuffers, kOutputBufferSize, mime); + + ALOGI("Construct SoftMPEG4Encoder"); +} + +SoftMPEG4Encoder::~SoftMPEG4Encoder() { + ALOGV("Destruct SoftMPEG4Encoder"); + onReset(); + releaseEncoder(); + List &outQueue = getPortQueue(1); + List &inQueue = getPortQueue(0); + CHECK(outQueue.empty()); + CHECK(inQueue.empty()); +} + +OMX_ERRORTYPE SoftMPEG4Encoder::initEncParams() { + CHECK(mHandle != NULL); + memset(mHandle, 0, sizeof(tagvideoEncControls)); + + CHECK(mEncParams != NULL); + memset(mEncParams, 0, sizeof(tagvideoEncOptions)); + if (!PVGetDefaultEncOption(mEncParams, 0)) { + ALOGE("Failed to get default encoding parameters"); + return OMX_ErrorUndefined; + } + if (mFramerate == 0) { + ALOGE("Framerate should not be 0"); + return OMX_ErrorUndefined; + } + mEncParams->encMode = mEncodeMode; + mEncParams->encWidth[0] = mWidth; + mEncParams->encHeight[0] = mHeight; + mEncParams->encFrameRate[0] = mFramerate >> 16; // mFramerate is in Q16 format + mEncParams->rcType = VBR_1; + mEncParams->vbvDelay = 5.0f; + + // FIXME: + // Add more profile and level support for MPEG4 encoder + mEncParams->profile_level = CORE_PROFILE_LEVEL2; + mEncParams->packetSize = 32; + mEncParams->rvlcEnable = PV_OFF; + mEncParams->numLayers = 1; + mEncParams->timeIncRes = 1000; + mEncParams->tickPerSrc = ((int64_t)mEncParams->timeIncRes << 16) / mFramerate; + + mEncParams->bitRate[0] = mBitrate; + mEncParams->iQuant[0] = 15; + mEncParams->pQuant[0] = 12; + mEncParams->quantType[0] = 0; + mEncParams->noFrameSkipped = PV_OFF; + + if (mColorFormat != OMX_COLOR_FormatYUV420Planar || mInputDataIsMeta) { + // Color conversion is needed. + free(mInputFrameData); + mInputFrameData = NULL; + if (((uint64_t)mWidth * mHeight) > ((uint64_t)INT32_MAX / 3)) { + ALOGE("b/25812794, Buffer size is too big."); + return OMX_ErrorBadParameter; + } + mInputFrameData = + (uint8_t *) malloc((mWidth * mHeight * 3 ) >> 1); + CHECK(mInputFrameData != NULL); + } + + // PV's MPEG4 encoder requires the video dimension of multiple + if (mWidth % 16 != 0 || mHeight % 16 != 0) { + ALOGE("Video frame size %dx%d must be a multiple of 16", + mWidth, mHeight); + return OMX_ErrorBadParameter; + } + + // Set IDR frame refresh interval + mEncParams->intraPeriod = mKeyFrameInterval; + + mEncParams->numIntraMB = 0; + mEncParams->sceneDetect = PV_ON; + mEncParams->searchRange = 16; + mEncParams->mv8x8Enable = PV_OFF; + mEncParams->gobHeaderInterval = 0; + mEncParams->useACPred = PV_ON; + mEncParams->intraDCVlcTh = 0; + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftMPEG4Encoder::initEncoder() { + CHECK(!mStarted); + + OMX_ERRORTYPE errType = OMX_ErrorNone; + if (OMX_ErrorNone != (errType = initEncParams())) { + ALOGE("Failed to initialized encoder params"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); + return errType; + } + + if (!PVInitVideoEncoder(mHandle, mEncParams)) { + ALOGE("Failed to initialize the encoder"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); + return OMX_ErrorUndefined; + } + + mNumInputFrames = -1; // 1st buffer for codec specific data + mStarted = true; + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftMPEG4Encoder::releaseEncoder() { + if (mEncParams) { + delete mEncParams; + mEncParams = NULL; + } + + if (mHandle) { + delete mHandle; + mHandle = NULL; + } + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftMPEG4Encoder::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamVideoBitrate: + { + OMX_VIDEO_PARAM_BITRATETYPE *bitRate = + (OMX_VIDEO_PARAM_BITRATETYPE *) params; + + if (!isValidOMXParam(bitRate)) { + return OMX_ErrorBadParameter; + } + + if (bitRate->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + bitRate->eControlRate = OMX_Video_ControlRateVariable; + bitRate->nTargetBitrate = mBitrate; + return OMX_ErrorNone; + } + + case OMX_IndexParamVideoH263: + { + OMX_VIDEO_PARAM_H263TYPE *h263type = + (OMX_VIDEO_PARAM_H263TYPE *)params; + + if (!isValidOMXParam(h263type)) { + return OMX_ErrorBadParameter; + } + + if (h263type->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + h263type->nAllowedPictureTypes = + (OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP); + h263type->eProfile = OMX_VIDEO_H263ProfileBaseline; + h263type->eLevel = OMX_VIDEO_H263Level45; + h263type->bPLUSPTYPEAllowed = OMX_FALSE; + h263type->bForceRoundingTypeToZero = OMX_FALSE; + h263type->nPictureHeaderRepetition = 0; + h263type->nGOBHeaderInterval = 0; + + return OMX_ErrorNone; + } + + case OMX_IndexParamVideoMpeg4: + { + OMX_VIDEO_PARAM_MPEG4TYPE *mpeg4type = + (OMX_VIDEO_PARAM_MPEG4TYPE *)params; + + if (!isValidOMXParam(mpeg4type)) { + return OMX_ErrorBadParameter; + } + + if (mpeg4type->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + mpeg4type->eProfile = OMX_VIDEO_MPEG4ProfileCore; + mpeg4type->eLevel = OMX_VIDEO_MPEG4Level2; + mpeg4type->nAllowedPictureTypes = + (OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP); + mpeg4type->nBFrames = 0; + mpeg4type->nIDCVLCThreshold = 0; + mpeg4type->bACPred = OMX_TRUE; + mpeg4type->nMaxPacketSize = 256; + mpeg4type->nTimeIncRes = 1000; + mpeg4type->nHeaderExtension = 0; + mpeg4type->bReversibleVLC = OMX_FALSE; + + return OMX_ErrorNone; + } + + default: + return SoftVideoEncoderOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftMPEG4Encoder::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + int32_t indexFull = index; + + switch (indexFull) { + case OMX_IndexParamVideoBitrate: + { + OMX_VIDEO_PARAM_BITRATETYPE *bitRate = + (OMX_VIDEO_PARAM_BITRATETYPE *) params; + + if (!isValidOMXParam(bitRate)) { + return OMX_ErrorBadParameter; + } + + if (bitRate->nPortIndex != 1 || + bitRate->eControlRate != OMX_Video_ControlRateVariable) { + return OMX_ErrorUndefined; + } + + mBitrate = bitRate->nTargetBitrate; + return OMX_ErrorNone; + } + + case OMX_IndexParamVideoH263: + { + OMX_VIDEO_PARAM_H263TYPE *h263type = + (OMX_VIDEO_PARAM_H263TYPE *)params; + + if (!isValidOMXParam(h263type)) { + return OMX_ErrorBadParameter; + } + + if (h263type->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + if (h263type->eProfile != OMX_VIDEO_H263ProfileBaseline || + h263type->eLevel != OMX_VIDEO_H263Level45 || + (h263type->nAllowedPictureTypes & OMX_VIDEO_PictureTypeB) || + h263type->bPLUSPTYPEAllowed != OMX_FALSE || + h263type->bForceRoundingTypeToZero != OMX_FALSE || + h263type->nPictureHeaderRepetition != 0 || + h263type->nGOBHeaderInterval != 0) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamVideoMpeg4: + { + OMX_VIDEO_PARAM_MPEG4TYPE *mpeg4type = + (OMX_VIDEO_PARAM_MPEG4TYPE *)params; + + if (!isValidOMXParam(mpeg4type)) { + return OMX_ErrorBadParameter; + } + + if (mpeg4type->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + if (mpeg4type->eProfile != OMX_VIDEO_MPEG4ProfileCore || + mpeg4type->eLevel > OMX_VIDEO_MPEG4Level2 || + (mpeg4type->nAllowedPictureTypes & OMX_VIDEO_PictureTypeB) || + mpeg4type->nBFrames != 0 || + mpeg4type->nIDCVLCThreshold != 0 || + mpeg4type->bACPred != OMX_TRUE || + mpeg4type->nMaxPacketSize != 256 || + mpeg4type->nTimeIncRes != 1000 || + mpeg4type->nHeaderExtension != 0 || + mpeg4type->bReversibleVLC != OMX_FALSE) { + return OMX_ErrorUndefined; + } + + mKeyFrameInterval = int32_t(mpeg4type->nPFrames + 1); + + return OMX_ErrorNone; + } + + default: + return SoftVideoEncoderOMXComponent::internalSetParameter(index, params); + } +} + +void SoftMPEG4Encoder::onQueueFilled(OMX_U32 /* portIndex */) { + if (mSignalledError || mSawInputEOS) { + return; + } + + if (!mStarted) { + if (OMX_ErrorNone != initEncoder()) { + return; + } + } + + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + while (!mSawInputEOS && !inQueue.empty() && !outQueue.empty()) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + outHeader->nTimeStamp = 0; + outHeader->nFlags = 0; + outHeader->nOffset = 0; + outHeader->nFilledLen = 0; + outHeader->nOffset = 0; + + uint8_t *outPtr = (uint8_t *) outHeader->pBuffer; + int32_t dataLength = outHeader->nAllocLen; + + if (mNumInputFrames < 0) { + if (!PVGetVolHeader(mHandle, outPtr, &dataLength, 0)) { + ALOGE("Failed to get VOL header"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); + return; + } + ALOGV("Output VOL header: %d bytes", dataLength); + ++mNumInputFrames; + outHeader->nFlags |= OMX_BUFFERFLAG_CODECCONFIG; + outHeader->nFilledLen = dataLength; + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + return; + } + + // Save the input buffer info so that it can be + // passed to an output buffer + InputBufferInfo info; + info.mTimeUs = inHeader->nTimeStamp; + info.mFlags = inHeader->nFlags; + mInputBufferInfoVec.push(info); + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + mSawInputEOS = true; + } + + if (inHeader->nFilledLen > 0) { + OMX_ERRORTYPE error = validateInputBuffer(inHeader); + if (error != OMX_ErrorNone) { + ALOGE("b/69065651"); + android_errorWriteLog(0x534e4554, "69065651"); + mSignalledError = true; + notify(OMX_EventError, error, 0, 0); + return; + } + const uint8_t *inputData = NULL; + if (mInputDataIsMeta) { + inputData = + extractGraphicBuffer( + mInputFrameData, (mWidth * mHeight * 3) >> 1, + inHeader->pBuffer + inHeader->nOffset, inHeader->nFilledLen, + mWidth, mHeight); + if (inputData == NULL) { + ALOGE("Unable to extract gralloc buffer in metadata mode"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); + return; + } + } else { + inputData = (const uint8_t *)inHeader->pBuffer + inHeader->nOffset; + if (mColorFormat != OMX_COLOR_FormatYUV420Planar) { + ConvertYUV420SemiPlanarToYUV420Planar( + inputData, mInputFrameData, mWidth, mHeight); + inputData = mInputFrameData; + } + } + + CHECK(inputData != NULL); + + VideoEncFrameIO vin, vout; + memset(&vin, 0, sizeof(vin)); + memset(&vout, 0, sizeof(vout)); + vin.height = align(mHeight, 16); + vin.pitch = align(mWidth, 16); + vin.timestamp = (inHeader->nTimeStamp + 500) / 1000; // in ms + vin.yChan = (uint8_t *)inputData; + vin.uChan = vin.yChan + vin.height * vin.pitch; + vin.vChan = vin.uChan + ((vin.height * vin.pitch) >> 2); + + ULong modTimeMs = 0; + int32_t nLayer = 0; + MP4HintTrack hintTrack; + if (!PVEncodeVideoFrame(mHandle, &vin, &vout, + &modTimeMs, outPtr, &dataLength, &nLayer) || + !PVGetHintTrack(mHandle, &hintTrack)) { + ALOGE("Failed to encode frame or get hink track at frame %" PRId64, + mNumInputFrames); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); + } + CHECK(NULL == PVGetOverrunBuffer(mHandle)); + if (hintTrack.CodeType == 0) { // I-frame serves as sync frame + outHeader->nFlags |= OMX_BUFFERFLAG_SYNCFRAME; + } + + ++mNumInputFrames; + } else { + dataLength = 0; + } + + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + outQueue.erase(outQueue.begin()); + CHECK(!mInputBufferInfoVec.empty()); + InputBufferInfo *inputBufInfo = mInputBufferInfoVec.begin(); + outHeader->nTimeStamp = inputBufInfo->mTimeUs; + outHeader->nFlags |= (inputBufInfo->mFlags | OMX_BUFFERFLAG_ENDOFFRAME); + outHeader->nFilledLen = dataLength; + mInputBufferInfoVec.erase(mInputBufferInfoVec.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + } +} + +void SoftMPEG4Encoder::onReset() { + if (!mStarted) { + return; + } + + PVCleanUpVideoEncoder(mHandle); + + free(mInputFrameData); + mInputFrameData = NULL; + + mStarted = false; +} + +} // namespace android + +__attribute__((cfi_canonical_jump_table)) +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + using namespace android; + if (!strcmp(name, "OMX.google.h263.encoder")) { + return new android::SoftMPEG4Encoder( + name, "video_encoder.h263", OMX_VIDEO_CodingH263, MEDIA_MIMETYPE_VIDEO_H263, + kH263ProfileLevels, NELEM(kH263ProfileLevels), + callbacks, appData, component); + } else if (!strcmp(name, "OMX.google.mpeg4.encoder")) { + return new android::SoftMPEG4Encoder( + name, "video_encoder.mpeg4", OMX_VIDEO_CodingMPEG4, MEDIA_MIMETYPE_VIDEO_MPEG4, + kMPEG4ProfileLevels, NELEM(kMPEG4ProfileLevels), + callbacks, appData, component); + } else { + CHECK(!"Unknown component"); + } + return NULL; +} diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h new file mode 100644 index 0000000..71e1170 --- /dev/null +++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2012 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_MPEG4_ENCODER_H_ +#define SOFT_MPEG4_ENCODER_H_ + +#include +#include +#include "mp4enc_api.h" + + +namespace android { + +struct CodecProfileLevel; + +struct SoftMPEG4Encoder : public SoftVideoEncoderOMXComponent { + SoftMPEG4Encoder( + const char *name, + const char *componentRole, + OMX_VIDEO_CODINGTYPE codingType, + const char *mime, + const CodecProfileLevel *profileLevels, + size_t numProfileLevels, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + + // Override SimpleSoftOMXComponent methods + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + + virtual void onReset(); + +protected: + virtual ~SoftMPEG4Encoder(); + +private: + enum { + kNumBuffers = 2, + }; + + // OMX input buffer's timestamp and flags + typedef struct { + int64_t mTimeUs; + int32_t mFlags; + } InputBufferInfo; + + MP4EncodingMode mEncodeMode; + int32_t mKeyFrameInterval; // 1: all I-frames, <0: infinite + + int64_t mNumInputFrames; + bool mStarted; + bool mSawInputEOS; + bool mSignalledError; + + tagvideoEncControls *mHandle; + tagvideoEncOptions *mEncParams; + uint8_t *mInputFrameData; + Vector mInputBufferInfoVec; + + OMX_ERRORTYPE initEncParams(); + OMX_ERRORTYPE initEncoder(); + OMX_ERRORTYPE releaseEncoder(); + + DISALLOW_EVIL_CONSTRUCTORS(SoftMPEG4Encoder); +}; + +} // namespace android + +#endif // SOFT_MPEG4_ENCODER_H_ diff --git a/media/libstagefright/codecs/m4v_h263/enc/exports.lds b/media/libstagefright/codecs/m4v_h263/enc/exports.lds new file mode 100644 index 0000000..e24f3fa --- /dev/null +++ b/media/libstagefright/codecs/m4v_h263/enc/exports.lds @@ -0,0 +1,5 @@ +{ + global: + _Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE; + local: *; +}; diff --git a/media/libstagefright/codecs/mp3dec/Android.bp b/media/libstagefright/codecs/mp3dec/Android.bp new file mode 100644 index 0000000..b669c84 --- /dev/null +++ b/media/libstagefright/codecs/mp3dec/Android.bp @@ -0,0 +1,27 @@ + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_av_media_libstagefright_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_av_media_libstagefright_license"], +} + +cc_library_shared { + name: "libstagefright_soft_mp3dec", + defaults: ["libstagefright_softomx-defaults"], + + srcs: ["SoftMP3.cpp"], + + version_script: "exports.lds", + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + ], + cfi: true, + }, + + static_libs: ["libstagefright_mp3dec"], +} diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp new file mode 100644 index 0000000..07bb45a --- /dev/null +++ b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp @@ -0,0 +1,506 @@ +/* + * Copyright (C) 2011 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 "SoftMP3" +#include + +#include "SoftMP3.h" + +#include +#include + +#include + +namespace android { + +template +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +SoftMP3::SoftMP3( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mConfig(new tPVMP3DecoderExternal), + mDecoderBuf(NULL), + mAnchorTimeUs(0), + mNumFramesOutput(0), + mNumChannels(2), + mSamplingRate(44100), + mSignalledError(false), + mSawInputEos(false), + mSignalledOutputEos(false), + mOutputPortSettingsChange(NONE) { + initPorts(); + initDecoder(); +} + +SoftMP3::~SoftMP3() { + if (mDecoderBuf != NULL) { + free(mDecoderBuf); + mDecoderBuf = NULL; + } + + delete mConfig; + mConfig = NULL; +} + +void SoftMP3::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.audio.cMIMEType = + const_cast(MEDIA_MIMETYPE_AUDIO_MPEG); + + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingMP3; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = kOutputBufferSize; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.audio.cMIMEType = const_cast("audio/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); +} + +void SoftMP3::initDecoder() { + mConfig->equalizerType = flat; + mConfig->crcEnabled = false; + + uint32_t memRequirements = pvmp3_decoderMemRequirements(); + mDecoderBuf = calloc(1, memRequirements); + + pvmp3_InitDecoder(mConfig, mDecoderBuf); + mIsFirst = true; +} + +void *SoftMP3::memsetSafe(OMX_BUFFERHEADERTYPE *outHeader, int c, size_t len) { + if (len > outHeader->nAllocLen) { + ALOGE("memset buffer too small: got %u, expected %zu", outHeader->nAllocLen, len); + android_errorWriteLog(0x534e4554, "29422022"); + notify(OMX_EventError, OMX_ErrorUndefined, OUTPUT_BUFFER_TOO_SMALL, NULL); + mSignalledError = true; + return NULL; + } + return memset(outHeader->pBuffer, c, len); +} + +OMX_ERRORTYPE SoftMP3::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioPortFormat: + { + OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex > 0) { + return OMX_ErrorNoMore; + } + + formatParams->eEncoding = + (formatParams->nPortIndex == 0) + ? OMX_AUDIO_CodingMP3 : OMX_AUDIO_CodingPCM; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + pcmParams->eNumData = OMX_NumericalDataSigned; + pcmParams->eEndian = OMX_EndianBig; + pcmParams->bInterleaved = OMX_TRUE; + pcmParams->nBitPerSample = 16; + pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; + pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; + + pcmParams->nChannels = mNumChannels; + pcmParams->nSamplingRate = mSamplingRate; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioMp3: + { + OMX_AUDIO_PARAM_MP3TYPE *mp3Params = + (OMX_AUDIO_PARAM_MP3TYPE *)params; + + if (!isValidOMXParam(mp3Params)) { + return OMX_ErrorBadParameter; + } + + if (mp3Params->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + mp3Params->nChannels = mNumChannels; + mp3Params->nBitRate = 0 /* unknown */; + mp3Params->nSampleRate = mSamplingRate; + // other fields are encoder-only + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftMP3::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (!isValidOMXParam(roleParams)) { + return OMX_ErrorBadParameter; + } + + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.mp3", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPortFormat: + { + const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if ((formatParams->nPortIndex == 0 + && formatParams->eEncoding != OMX_AUDIO_CodingMP3) + || (formatParams->nPortIndex == 1 + && formatParams->eEncoding != OMX_AUDIO_CodingPCM)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + const OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (const OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + mNumChannels = pcmParams->nChannels; + mSamplingRate = pcmParams->nSamplingRate; + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +void SoftMP3::onQueueFilled(OMX_U32 /* portIndex */) { + if (mSignalledError || mOutputPortSettingsChange != NONE) { + return; + } + + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + while ((!inQueue.empty() || (mSawInputEos && !mSignalledOutputEos)) && !outQueue.empty()) { + BufferInfo *inInfo = NULL; + OMX_BUFFERHEADERTYPE *inHeader = NULL; + if (!inQueue.empty()) { + inInfo = *inQueue.begin(); + inHeader = inInfo->mHeader; + } + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + outHeader->nFlags = 0; + + if (inHeader) { + if (inHeader->nOffset == 0 && inHeader->nFilledLen) { + mAnchorTimeUs = inHeader->nTimeStamp; + mNumFramesOutput = 0; + } + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + mSawInputEos = true; + if (mIsFirst && !inHeader->nFilledLen) { + ALOGV("empty first EOS"); + outHeader->nFilledLen = 0; + outHeader->nTimeStamp = inHeader->nTimeStamp; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + mSignalledOutputEos = true; + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + notifyFillBufferDone(outHeader); + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + notifyEmptyBufferDone(inHeader); + return; + } + } + + mConfig->pInputBuffer = + inHeader->pBuffer + inHeader->nOffset; + + mConfig->inputBufferCurrentLength = inHeader->nFilledLen; + } else { + mConfig->pInputBuffer = NULL; + mConfig->inputBufferCurrentLength = 0; + } + mConfig->inputBufferMaxLength = 0; + mConfig->inputBufferUsedLength = 0; + + mConfig->outputFrameSize = kOutputBufferSize / sizeof(int16_t); + if ((int32_t)outHeader->nAllocLen < mConfig->outputFrameSize) { + ALOGE("input buffer too small: got %u, expected %u", + outHeader->nAllocLen, mConfig->outputFrameSize); + android_errorWriteLog(0x534e4554, "27793371"); + notify(OMX_EventError, OMX_ErrorUndefined, OUTPUT_BUFFER_TOO_SMALL, NULL); + mSignalledError = true; + return; + } + + mConfig->pOutputBuffer = + reinterpret_cast(outHeader->pBuffer); + + ERROR_CODE decoderErr; + if ((decoderErr = pvmp3_framedecoder(mConfig, mDecoderBuf)) + != NO_DECODING_ERROR) { + ALOGV("mp3 decoder returned error %d", decoderErr); + + if (decoderErr != NO_ENOUGH_MAIN_DATA_ERROR + && decoderErr != SIDE_INFO_ERROR) { + ALOGE("mp3 decoder returned error %d", decoderErr); + + notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL); + mSignalledError = true; + return; + } + + if (mConfig->outputFrameSize == 0) { + mConfig->outputFrameSize = kOutputBufferSize / sizeof(int16_t); + } + + if (decoderErr == NO_ENOUGH_MAIN_DATA_ERROR && mSawInputEos) { + if (!mIsFirst) { + // pad the end of the stream with 529 samples, since that many samples + // were trimmed off the beginning when decoding started + outHeader->nOffset = 0; + outHeader->nFilledLen = kPVMP3DecoderDelay * mNumChannels * sizeof(int16_t); + + if (!memsetSafe(outHeader, 0, outHeader->nFilledLen)) { + return; + } + + } + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + mSignalledOutputEos = true; + } else { + // This is recoverable, just ignore the current frame and + // play silence instead. + + // TODO: should we skip silence (and consume input data) + // if mIsFirst is true as we may not have a valid + // mConfig->samplingRate and mConfig->num_channels? + ALOGV_IF(mIsFirst, "insufficient data for first frame, sending silence"); + if (!memsetSafe(outHeader, 0, mConfig->outputFrameSize * sizeof(int16_t))) { + return; + } + + if (inHeader) { + mConfig->inputBufferUsedLength = inHeader->nFilledLen; + } + } + } else if (mConfig->samplingRate != mSamplingRate + || mConfig->num_channels != mNumChannels) { + mSamplingRate = mConfig->samplingRate; + mNumChannels = mConfig->num_channels; + + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + return; + } + + if (mIsFirst) { + mIsFirst = false; + // The decoder delay is 529 samples, so trim that many samples off + // the start of the first output buffer. This essentially makes this + // decoder have zero delay, which the rest of the pipeline assumes. + outHeader->nOffset = + kPVMP3DecoderDelay * mNumChannels * sizeof(int16_t); + + outHeader->nFilledLen = + mConfig->outputFrameSize * sizeof(int16_t) - outHeader->nOffset; + } else if (!mSignalledOutputEos) { + outHeader->nOffset = 0; + outHeader->nFilledLen = mConfig->outputFrameSize * sizeof(int16_t); + } + + outHeader->nTimeStamp = + mAnchorTimeUs + (mNumFramesOutput * 1000000LL) / mSamplingRate; + + if (inHeader) { + CHECK_GE((int32_t)inHeader->nFilledLen, mConfig->inputBufferUsedLength); + + inHeader->nOffset += mConfig->inputBufferUsedLength; + inHeader->nFilledLen -= mConfig->inputBufferUsedLength; + + + if (inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } + } + + mNumFramesOutput += mConfig->outputFrameSize / mNumChannels; + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + } +} + +void SoftMP3::onPortFlushCompleted(OMX_U32 portIndex) { + if (portIndex == 0) { + // Make sure that the next buffer output does not still + // depend on fragments from the last one decoded. + pvmp3_InitDecoder(mConfig, mDecoderBuf); + mIsFirst = true; + mSignalledError = false; + mSawInputEos = false; + mSignalledOutputEos = false; + } +} + +void SoftMP3::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { + if (portIndex != 1) { + return; + } + + switch (mOutputPortSettingsChange) { + case NONE: + break; + + case AWAITING_DISABLED: + { + CHECK(!enabled); + mOutputPortSettingsChange = AWAITING_ENABLED; + break; + } + + default: + { + CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); + CHECK(enabled); + mOutputPortSettingsChange = NONE; + break; + } + } +} + +void SoftMP3::onReset() { + pvmp3_InitDecoder(mConfig, mDecoderBuf); + mIsFirst = true; + mSignalledError = false; + mSawInputEos = false; + mSignalledOutputEos = false; + mOutputPortSettingsChange = NONE; +} + +} // namespace android + +__attribute__((cfi_canonical_jump_table)) +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftMP3(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.h b/media/libstagefright/codecs/mp3dec/SoftMP3.h new file mode 100644 index 0000000..976fd00 --- /dev/null +++ b/media/libstagefright/codecs/mp3dec/SoftMP3.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2011 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_MP3_H_ + +#define SOFT_MP3_H_ + +#include + +struct tPVMP3DecoderExternal; + +namespace android { + +struct SoftMP3 : public SimpleSoftOMXComponent { + SoftMP3(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftMP3(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + virtual void onReset(); + +private: + enum { + kNumBuffers = 4, + kOutputBufferSize = 4608 * 2, + kPVMP3DecoderDelay = 529 // frames + }; + + tPVMP3DecoderExternal *mConfig; + void *mDecoderBuf; + int64_t mAnchorTimeUs; + int64_t mNumFramesOutput; + + int32_t mNumChannels; + int32_t mSamplingRate; + + bool mIsFirst; + bool mSignalledError; + bool mSawInputEos; + bool mSignalledOutputEos; + + enum { + NONE, + AWAITING_DISABLED, + AWAITING_ENABLED + } mOutputPortSettingsChange; + + void initPorts(); + void initDecoder(); + void *memsetSafe(OMX_BUFFERHEADERTYPE *outHeader, int c, size_t len); + + DISALLOW_EVIL_CONSTRUCTORS(SoftMP3); +}; + +} // namespace android + +#endif // SOFT_MP3_H_ + + diff --git a/media/libstagefright/codecs/mp3dec/exports.lds b/media/libstagefright/codecs/mp3dec/exports.lds new file mode 100644 index 0000000..e24f3fa --- /dev/null +++ b/media/libstagefright/codecs/mp3dec/exports.lds @@ -0,0 +1,5 @@ +{ + global: + _Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE; + local: *; +}; diff --git a/media/libstagefright/codecs/mpeg2dec/Android.bp b/media/libstagefright/codecs/mpeg2dec/Android.bp new file mode 100644 index 0000000..abd1379 --- /dev/null +++ b/media/libstagefright/codecs/mpeg2dec/Android.bp @@ -0,0 +1,32 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_av_media_libstagefright_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_av_media_libstagefright_license"], +} + +cc_library_shared { + name: "libstagefright_soft_mpeg2dec", + defaults: ["libstagefright_softomx-defaults"], + + static_libs: ["libmpeg2dec"], + srcs: ["SoftMPEG2.cpp"], + + cflags: [ + "-Wall", + "-Wno-unused-variable", + ], + + version_script: "exports.lds", + + ldflags: ["-Wl,-Bsymbolic"], + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + ], + cfi: true, + }, +} diff --git a/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp new file mode 100644 index 0000000..9f8001f --- /dev/null +++ b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp @@ -0,0 +1,872 @@ +/* + * Copyright 2015 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 "SoftMPEG2" +#include + +#include "iv_datatypedef.h" +#include "iv.h" +#include "ivd.h" +#include "impeg2d.h" +#include "SoftMPEG2.h" + +#include +#include +#include + +namespace android { + +#define componentName "video_decoder.mpeg2" +#define codingType OMX_VIDEO_CodingMPEG2 +#define CODEC_MIME_TYPE MEDIA_MIMETYPE_VIDEO_MPEG2 + +/** Function and structure definitions to keep code similar for each codec */ +#define ivdec_api_function impeg2d_api_function +#define ivdext_init_ip_t impeg2d_init_ip_t +#define ivdext_init_op_t impeg2d_init_op_t +#define ivdext_fill_mem_rec_ip_t impeg2d_fill_mem_rec_ip_t +#define ivdext_fill_mem_rec_op_t impeg2d_fill_mem_rec_op_t +#define ivdext_ctl_set_num_cores_ip_t impeg2d_ctl_set_num_cores_ip_t +#define ivdext_ctl_set_num_cores_op_t impeg2d_ctl_set_num_cores_op_t + +#define IVDEXT_CMD_CTL_SET_NUM_CORES \ + (IVD_CONTROL_API_COMMAND_TYPE_T)IMPEG2D_CMD_CTL_SET_NUM_CORES + +static const CodecProfileLevel kProfileLevels[] = { + { OMX_VIDEO_MPEG2ProfileSimple, OMX_VIDEO_MPEG2LevelHL }, + + { OMX_VIDEO_MPEG2ProfileMain , OMX_VIDEO_MPEG2LevelHL }, +}; + +SoftMPEG2::SoftMPEG2( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SoftVideoDecoderOMXComponent( + name, componentName, codingType, + kProfileLevels, ARRAY_SIZE(kProfileLevels), + 320 /* width */, 240 /* height */, callbacks, + appData, component), + mCodecCtx(NULL), + mMemRecords(NULL), + mFlushOutBuffer(NULL), + mOmxColorFormat(OMX_COLOR_FormatYUV420Planar), + mIvColorFormat(IV_YUV_420P), + mNewWidth(mWidth), + mNewHeight(mHeight), + mChangingResolution(false), + mSignalledError(false), + mStride(mWidth) { + initPorts(kNumBuffers, INPUT_BUF_SIZE, kNumBuffers, CODEC_MIME_TYPE); + + // If input dump is enabled, then open create an empty file + GENERATE_FILE_NAMES(); + CREATE_DUMP_FILE(mInFile); +} + +SoftMPEG2::~SoftMPEG2() { + if (OK != deInitDecoder()) { + ALOGE("Failed to deinit decoder"); + notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL); + mSignalledError = true; + return; + } +} + + +static ssize_t getMinTimestampIdx(OMX_S64 *pNTimeStamp, bool *pIsTimeStampValid) { + OMX_S64 minTimeStamp = LLONG_MAX; + ssize_t idx = -1; + for (ssize_t i = 0; i < MAX_TIME_STAMPS; i++) { + if (pIsTimeStampValid[i]) { + if (pNTimeStamp[i] < minTimeStamp) { + minTimeStamp = pNTimeStamp[i]; + idx = i; + } + } + } + return idx; +} + +static size_t GetCPUCoreCount() { + long 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(cpuCoreCount >= 1); + ALOGV("Number of CPU cores: %ld", cpuCoreCount); + return (size_t)cpuCoreCount; +} + +void SoftMPEG2::logVersion() { + ivd_ctl_getversioninfo_ip_t s_ctl_ip; + ivd_ctl_getversioninfo_op_t s_ctl_op; + UWORD8 au1_buf[512]; + IV_API_CALL_STATUS_T status; + + s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_GETVERSION; + s_ctl_ip.u4_size = sizeof(ivd_ctl_getversioninfo_ip_t); + s_ctl_op.u4_size = sizeof(ivd_ctl_getversioninfo_op_t); + s_ctl_ip.pv_version_buffer = au1_buf; + s_ctl_ip.u4_version_buffer_size = sizeof(au1_buf); + + status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op); + + if (status != IV_SUCCESS) { + ALOGE("Error in getting version number: 0x%x", + s_ctl_op.u4_error_code); + } else { + ALOGV("Ittiam decoder version number: %s", + (char *)s_ctl_ip.pv_version_buffer); + } + return; +} + +status_t SoftMPEG2::setParams(size_t stride) { + ivd_ctl_set_config_ip_t s_ctl_ip; + ivd_ctl_set_config_op_t s_ctl_op; + IV_API_CALL_STATUS_T status; + s_ctl_ip.u4_disp_wd = (UWORD32)stride; + s_ctl_ip.e_frm_skip_mode = IVD_SKIP_NONE; + + s_ctl_ip.e_frm_out_mode = IVD_DISPLAY_FRAME_OUT; + s_ctl_ip.e_vid_dec_mode = IVD_DECODE_FRAME; + s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_SETPARAMS; + s_ctl_ip.u4_size = sizeof(ivd_ctl_set_config_ip_t); + s_ctl_op.u4_size = sizeof(ivd_ctl_set_config_op_t); + + ALOGV("Set the run-time (dynamic) parameters stride = %zu", stride); + status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op); + + if (status != IV_SUCCESS) { + ALOGE("Error in setting the run-time parameters: 0x%x", + s_ctl_op.u4_error_code); + + return UNKNOWN_ERROR; + } + return OK; +} + +status_t SoftMPEG2::resetPlugin() { + mIsInFlush = false; + mReceivedEOS = false; + memset(mTimeStamps, 0, sizeof(mTimeStamps)); + memset(mTimeStampsValid, 0, sizeof(mTimeStampsValid)); + + /* Initialize both start and end times */ + gettimeofday(&mTimeStart, NULL); + gettimeofday(&mTimeEnd, NULL); + + return OK; +} + +status_t SoftMPEG2::resetDecoder() { + ivd_ctl_reset_ip_t s_ctl_ip; + ivd_ctl_reset_op_t s_ctl_op; + IV_API_CALL_STATUS_T status; + + s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_RESET; + s_ctl_ip.u4_size = sizeof(ivd_ctl_reset_ip_t); + s_ctl_op.u4_size = sizeof(ivd_ctl_reset_op_t); + + status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip, (void *)&s_ctl_op); + if (IV_SUCCESS != status) { + ALOGE("Error in reset: 0x%x", s_ctl_op.u4_error_code); + return UNKNOWN_ERROR; + } + + /* Set the run-time (dynamic) parameters */ + setParams(outputBufferWidth()); + + /* Set number of cores/threads to be used by the codec */ + setNumCores(); + + mStride = 0; + mSignalledError = false; + + return OK; +} + +status_t SoftMPEG2::setNumCores() { + ivdext_ctl_set_num_cores_ip_t s_set_cores_ip; + ivdext_ctl_set_num_cores_op_t s_set_cores_op; + IV_API_CALL_STATUS_T status; + s_set_cores_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_set_cores_ip.e_sub_cmd = IVDEXT_CMD_CTL_SET_NUM_CORES; + s_set_cores_ip.u4_num_cores = MIN(mNumCores, CODEC_MAX_NUM_CORES); + s_set_cores_ip.u4_size = sizeof(ivdext_ctl_set_num_cores_ip_t); + s_set_cores_op.u4_size = sizeof(ivdext_ctl_set_num_cores_op_t); + + status = ivdec_api_function(mCodecCtx, (void *)&s_set_cores_ip, (void *)&s_set_cores_op); + if (IV_SUCCESS != status) { + ALOGE("Error in setting number of cores: 0x%x", + s_set_cores_op.u4_error_code); + return UNKNOWN_ERROR; + } + return OK; +} + +status_t SoftMPEG2::setFlushMode() { + IV_API_CALL_STATUS_T status; + ivd_ctl_flush_ip_t s_video_flush_ip; + ivd_ctl_flush_op_t s_video_flush_op; + + s_video_flush_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_video_flush_ip.e_sub_cmd = IVD_CMD_CTL_FLUSH; + s_video_flush_ip.u4_size = sizeof(ivd_ctl_flush_ip_t); + s_video_flush_op.u4_size = sizeof(ivd_ctl_flush_op_t); + + /* Set the decoder in Flush mode, subsequent decode() calls will flush */ + status = ivdec_api_function( + mCodecCtx, (void *)&s_video_flush_ip, (void *)&s_video_flush_op); + + if (status != IV_SUCCESS) { + ALOGE("Error in setting the decoder in flush mode: (%d) 0x%x", status, + s_video_flush_op.u4_error_code); + return UNKNOWN_ERROR; + } + + mWaitForI = true; + mIsInFlush = true; + return OK; +} + +status_t SoftMPEG2::initDecoder() { + IV_API_CALL_STATUS_T status; + + UWORD32 u4_num_reorder_frames; + UWORD32 u4_num_ref_frames; + UWORD32 u4_share_disp_buf; + + mNumCores = GetCPUCoreCount(); + mWaitForI = true; + + /* Initialize number of ref and reorder modes (for MPEG2) */ + u4_num_reorder_frames = 16; + u4_num_ref_frames = 16; + u4_share_disp_buf = 0; + + uint32_t displayStride = outputBufferWidth(); + uint32_t displayHeight = outputBufferHeight(); + uint32_t displaySizeY = displayStride * displayHeight; + + { + iv_num_mem_rec_ip_t s_num_mem_rec_ip; + iv_num_mem_rec_op_t s_num_mem_rec_op; + + s_num_mem_rec_ip.u4_size = sizeof(s_num_mem_rec_ip); + s_num_mem_rec_op.u4_size = sizeof(s_num_mem_rec_op); + s_num_mem_rec_ip.e_cmd = IV_CMD_GET_NUM_MEM_REC; + + status = ivdec_api_function( + mCodecCtx, (void *)&s_num_mem_rec_ip, (void *)&s_num_mem_rec_op); + if (IV_SUCCESS != status) { + ALOGE("Error in getting mem records: 0x%x", + s_num_mem_rec_op.u4_error_code); + return UNKNOWN_ERROR; + } + + mNumMemRecords = s_num_mem_rec_op.u4_num_mem_rec; + } + + mMemRecords = (iv_mem_rec_t *)ivd_aligned_malloc( + 128, mNumMemRecords * sizeof(iv_mem_rec_t)); + if (mMemRecords == NULL) { + ALOGE("Allocation failure"); + return NO_MEMORY; + } + + memset(mMemRecords, 0, mNumMemRecords * sizeof(iv_mem_rec_t)); + + { + size_t i; + ivdext_fill_mem_rec_ip_t s_fill_mem_ip; + ivdext_fill_mem_rec_op_t s_fill_mem_op; + iv_mem_rec_t *ps_mem_rec; + + s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_size = + sizeof(ivdext_fill_mem_rec_ip_t); + + s_fill_mem_ip.u4_share_disp_buf = u4_share_disp_buf; + s_fill_mem_ip.e_output_format = mIvColorFormat; + s_fill_mem_ip.u4_deinterlace = 1; + s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.e_cmd = IV_CMD_FILL_NUM_MEM_REC; + s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.pv_mem_rec_location = mMemRecords; + s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_max_frm_wd = displayStride; + s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_max_frm_ht = displayHeight; + s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_size = + sizeof(ivdext_fill_mem_rec_op_t); + + ps_mem_rec = mMemRecords; + for (i = 0; i < mNumMemRecords; i++) { + ps_mem_rec[i].u4_size = sizeof(iv_mem_rec_t); + } + + status = ivdec_api_function( + mCodecCtx, (void *)&s_fill_mem_ip, (void *)&s_fill_mem_op); + + if (IV_SUCCESS != status) { + ALOGE("Error in filling mem records: 0x%x", + s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_error_code); + return UNKNOWN_ERROR; + } + mNumMemRecords = + s_fill_mem_op.s_ivd_fill_mem_rec_op_t.u4_num_mem_rec_filled; + + ps_mem_rec = mMemRecords; + + for (i = 0; i < mNumMemRecords; i++) { + ps_mem_rec->pv_base = ivd_aligned_malloc( + ps_mem_rec->u4_mem_alignment, ps_mem_rec->u4_mem_size); + if (ps_mem_rec->pv_base == NULL) { + ALOGE("Allocation failure for memory record #%zu of size %u", + i, ps_mem_rec->u4_mem_size); + status = IV_FAIL; + return NO_MEMORY; + } + + ps_mem_rec++; + } + } + + /* Initialize the decoder */ + { + ivdext_init_ip_t s_init_ip; + ivdext_init_op_t s_init_op; + + void *dec_fxns = (void *)ivdec_api_function; + + s_init_ip.s_ivd_init_ip_t.u4_size = sizeof(ivdext_init_ip_t); + s_init_ip.s_ivd_init_ip_t.e_cmd = (IVD_API_COMMAND_TYPE_T)IV_CMD_INIT; + s_init_ip.s_ivd_init_ip_t.pv_mem_rec_location = mMemRecords; + s_init_ip.s_ivd_init_ip_t.u4_frm_max_wd = displayStride; + s_init_ip.s_ivd_init_ip_t.u4_frm_max_ht = displayHeight; + + s_init_ip.u4_share_disp_buf = u4_share_disp_buf; + s_init_ip.u4_deinterlace = 1; + + s_init_op.s_ivd_init_op_t.u4_size = sizeof(s_init_op); + + s_init_ip.s_ivd_init_ip_t.u4_num_mem_rec = mNumMemRecords; + s_init_ip.s_ivd_init_ip_t.e_output_format = mIvColorFormat; + + mCodecCtx = (iv_obj_t *)mMemRecords[0].pv_base; + mCodecCtx->pv_fxns = dec_fxns; + mCodecCtx->u4_size = sizeof(iv_obj_t); + + status = ivdec_api_function(mCodecCtx, (void *)&s_init_ip, (void *)&s_init_op); + if (status != IV_SUCCESS) { + ALOGE("Error in init: 0x%x", + s_init_op.s_ivd_init_op_t.u4_error_code); + return UNKNOWN_ERROR; + } + } + + /* Reset the plugin state */ + resetPlugin(); + + /* Set the run time (dynamic) parameters */ + mStride = outputBufferWidth(); + setParams(mStride); + + /* Set number of cores/threads to be used by the codec */ + setNumCores(); + + /* Get codec version */ + logVersion(); + + /* Allocate internal picture buffer */ + uint32_t bufferSize = displaySizeY * 3 / 2; + mFlushOutBuffer = (uint8_t *)ivd_aligned_malloc(128, bufferSize); + if (NULL == mFlushOutBuffer) { + ALOGE("Could not allocate flushOutputBuffer of size %u", bufferSize); + return NO_MEMORY; + } + + mInitNeeded = false; + mFlushNeeded = false; + return OK; +} + +status_t SoftMPEG2::deInitDecoder() { + size_t i; + + if (mMemRecords) { + iv_mem_rec_t *ps_mem_rec; + + ps_mem_rec = mMemRecords; + for (i = 0; i < mNumMemRecords; i++) { + if (ps_mem_rec->pv_base) { + ivd_aligned_free(ps_mem_rec->pv_base); + } + ps_mem_rec++; + } + ivd_aligned_free(mMemRecords); + mMemRecords = NULL; + } + + if (mFlushOutBuffer) { + ivd_aligned_free(mFlushOutBuffer); + mFlushOutBuffer = NULL; + } + + mInitNeeded = true; + mChangingResolution = false; + mCodecCtx = NULL; + + return OK; +} + +status_t SoftMPEG2::reInitDecoder() { + status_t ret; + + deInitDecoder(); + + ret = initDecoder(); + if (OK != ret) { + ALOGE("Failed to initialize decoder"); + deInitDecoder(); + return ret; + } + mSignalledError = false; + return OK; +} + +void SoftMPEG2::onReset() { + SoftVideoDecoderOMXComponent::onReset(); + + mWaitForI = true; + + resetDecoder(); + resetPlugin(); +} + +bool SoftMPEG2::getSeqInfo() { + IV_API_CALL_STATUS_T status; + impeg2d_ctl_get_seq_info_ip_t s_ctl_get_seq_info_ip; + impeg2d_ctl_get_seq_info_op_t s_ctl_get_seq_info_op; + + s_ctl_get_seq_info_ip.e_cmd = IVD_CMD_VIDEO_CTL; + s_ctl_get_seq_info_ip.e_sub_cmd = + (IVD_CONTROL_API_COMMAND_TYPE_T)IMPEG2D_CMD_CTL_GET_SEQ_INFO; + + s_ctl_get_seq_info_ip.u4_size = sizeof(impeg2d_ctl_get_seq_info_ip_t); + s_ctl_get_seq_info_op.u4_size = sizeof(impeg2d_ctl_get_seq_info_op_t); + + status = ivdec_api_function( + (iv_obj_t *)mCodecCtx, (void *)&s_ctl_get_seq_info_ip, + (void *)&s_ctl_get_seq_info_op); + + if (status != IV_SUCCESS) { + ALOGW("Error in getting Sequence info: 0x%x", + s_ctl_get_seq_info_op.u4_error_code); + return false; + } + + + int32_t primaries = s_ctl_get_seq_info_op.u1_colour_primaries; + int32_t transfer = s_ctl_get_seq_info_op.u1_transfer_characteristics; + int32_t coeffs = s_ctl_get_seq_info_op.u1_matrix_coefficients; + bool fullRange = false; // mpeg2 video has limited range. + + ColorAspects colorAspects; + ColorUtils::convertIsoColorAspectsToCodecAspects( + primaries, transfer, coeffs, fullRange, colorAspects); + + // Update color aspects if necessary. + if (colorAspectsDiffer(colorAspects, mBitstreamColorAspects)) { + mBitstreamColorAspects = colorAspects; + status_t err = handleColorAspectsChange(); + CHECK(err == OK); + } + return true; +} + +OMX_ERRORTYPE SoftMPEG2::internalSetParameter(OMX_INDEXTYPE index, const OMX_PTR params) { + const uint32_t oldWidth = mWidth; + const uint32_t oldHeight = mHeight; + OMX_ERRORTYPE ret = SoftVideoDecoderOMXComponent::internalSetParameter(index, params); + if (mWidth != oldWidth || mHeight != oldHeight) { + reInitDecoder(); + } + return ret; +} + +bool SoftMPEG2::setDecodeArgs( + ivd_video_decode_ip_t *ps_dec_ip, + ivd_video_decode_op_t *ps_dec_op, + OMX_BUFFERHEADERTYPE *inHeader, + OMX_BUFFERHEADERTYPE *outHeader, + size_t timeStampIx) { + size_t sizeY = outputBufferWidth() * outputBufferHeight(); + size_t sizeUV; + + ps_dec_ip->u4_size = sizeof(ivd_video_decode_ip_t); + ps_dec_op->u4_size = sizeof(ivd_video_decode_op_t); + + ps_dec_ip->e_cmd = IVD_CMD_VIDEO_DECODE; + + /* When in flush and after EOS with zero byte input, + * inHeader is set to zero. Hence check for non-null */ + if (inHeader) { + ps_dec_ip->u4_ts = timeStampIx; + ps_dec_ip->pv_stream_buffer = inHeader->pBuffer + + inHeader->nOffset; + ps_dec_ip->u4_num_Bytes = inHeader->nFilledLen; + } else { + ps_dec_ip->u4_ts = 0; + ps_dec_ip->pv_stream_buffer = NULL; + ps_dec_ip->u4_num_Bytes = 0; + } + + sizeUV = sizeY / 4; + ps_dec_ip->s_out_buffer.u4_min_out_buf_size[0] = sizeY; + ps_dec_ip->s_out_buffer.u4_min_out_buf_size[1] = sizeUV; + ps_dec_ip->s_out_buffer.u4_min_out_buf_size[2] = sizeUV; + + uint8_t *pBuf; + if (outHeader) { + if (outHeader->nAllocLen < sizeY + (sizeUV * 2)) { + android_errorWriteLog(0x534e4554, "27833616"); + return false; + } + pBuf = outHeader->pBuffer; + } else { + // mFlushOutBuffer always has the right size. + pBuf = mFlushOutBuffer; + } + + ps_dec_ip->s_out_buffer.pu1_bufs[0] = pBuf; + ps_dec_ip->s_out_buffer.pu1_bufs[1] = pBuf + sizeY; + ps_dec_ip->s_out_buffer.pu1_bufs[2] = pBuf + sizeY + sizeUV; + ps_dec_ip->s_out_buffer.u4_num_bufs = 3; + return true; +} +void SoftMPEG2::onPortFlushCompleted(OMX_U32 portIndex) { + /* Once the output buffers are flushed, ignore any buffers that are held in decoder */ + if (kOutputPortIndex == portIndex) { + setFlushMode(); + + while (true) { + ivd_video_decode_ip_t s_dec_ip; + ivd_video_decode_op_t s_dec_op; + IV_API_CALL_STATUS_T status; + size_t sizeY, sizeUV; + + setDecodeArgs(&s_dec_ip, &s_dec_op, NULL, NULL, 0); + + status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op); + if (0 == s_dec_op.u4_output_present) { + resetPlugin(); + break; + } + } + } +} + +void SoftMPEG2::onQueueFilled(OMX_U32 portIndex) { + UNUSED(portIndex); + + if (mSignalledError) { + return; + } + if (mOutputPortSettingsChange != NONE) { + return; + } + + if (NULL == mCodecCtx) { + if (OK != initDecoder()) { + ALOGE("Failed to initialize decoder"); + notify(OMX_EventError, OMX_ErrorUnsupportedSetting, 0, NULL); + mSignalledError = true; + return; + } + } + + List &inQueue = getPortQueue(kInputPortIndex); + List &outQueue = getPortQueue(kOutputPortIndex); + + if (outputBufferWidth() != mStride) { + /* Set the run-time (dynamic) parameters */ + mStride = outputBufferWidth(); + setParams(mStride); + } + + while (!outQueue.empty()) { + BufferInfo *inInfo; + OMX_BUFFERHEADERTYPE *inHeader; + + BufferInfo *outInfo; + OMX_BUFFERHEADERTYPE *outHeader; + size_t timeStampIx; + + inInfo = NULL; + inHeader = NULL; + + if (!mIsInFlush) { + if (!inQueue.empty()) { + inInfo = *inQueue.begin(); + inHeader = inInfo->mHeader; + } else { + break; + } + } + + outInfo = *outQueue.begin(); + outHeader = outInfo->mHeader; + outHeader->nFlags = 0; + outHeader->nTimeStamp = 0; + outHeader->nOffset = 0; + + if (inHeader != NULL && (inHeader->nFlags & OMX_BUFFERFLAG_EOS)) { + mReceivedEOS = true; + if (inHeader->nFilledLen == 0) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + setFlushMode(); + } + } + + // When there is an init required and the decoder is not in flush mode, + // update output port's definition and reinitialize decoder. + if (mInitNeeded && !mIsInFlush) { + bool portWillReset = false; + handlePortSettingsChange(&portWillReset, mNewWidth, mNewHeight); + + if (OK != reInitDecoder()) { + ALOGE("Failed to reinitialize decoder"); + } + return; + } + + /* Get a free slot in timestamp array to hold input timestamp */ + { + size_t i; + timeStampIx = 0; + for (i = 0; i < MAX_TIME_STAMPS; i++) { + if (!mTimeStampsValid[i]) { + timeStampIx = i; + break; + } + } + if (inHeader != NULL) { + mTimeStampsValid[timeStampIx] = true; + mTimeStamps[timeStampIx] = inHeader->nTimeStamp; + } + } + + { + ivd_video_decode_ip_t s_dec_ip; + ivd_video_decode_op_t s_dec_op; + WORD32 timeDelay, timeTaken; + size_t sizeY, sizeUV; + + if (!setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx)) { + ALOGE("Decoder arg setup failed"); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + // If input dump is enabled, then write to file + DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes); + + if (s_dec_ip.u4_num_Bytes > 0) { + char *ptr = (char *)s_dec_ip.pv_stream_buffer; + } + + GETTIME(&mTimeStart, NULL); + /* Compute time elapsed between end of previous decode() + * to start of current decode() */ + TIME_DIFF(mTimeEnd, mTimeStart, timeDelay); + + IV_API_CALL_STATUS_T status; + status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op); + + bool unsupportedDimensions = (IMPEG2D_UNSUPPORTED_DIMENSIONS == s_dec_op.u4_error_code); + bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & 0xFF)); + + getSeqInfo(); + + GETTIME(&mTimeEnd, NULL); + /* Compute time taken for decode() */ + TIME_DIFF(mTimeStart, mTimeEnd, timeTaken); + + ALOGV("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay, + s_dec_op.u4_num_bytes_consumed); + if (s_dec_op.u4_frame_decoded_flag && !mFlushNeeded) { + mFlushNeeded = true; + } + + if ((inHeader != NULL) && (1 != s_dec_op.u4_frame_decoded_flag)) { + /* If the input did not contain picture data, then ignore + * the associated timestamp */ + mTimeStampsValid[timeStampIx] = false; + } + + // This is needed to handle CTS DecoderTest testCodecResetsMPEG2WithoutSurface, + // which is not sending SPS/PPS after port reconfiguration and flush to the codec. + if (unsupportedDimensions && !mFlushNeeded) { + bool portWillReset = false; + handlePortSettingsChange(&portWillReset, s_dec_op.u4_pic_wd, s_dec_op.u4_pic_ht); + + if (OK != reInitDecoder()) { + ALOGE("Failed to reinitialize decoder"); + return; + } + + if (setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx)) { + ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op); + } + return; + } + + // If the decoder is in the changing resolution mode and there is no output present, + // that means the switching is done and it's ready to reset the decoder and the plugin. + if (mChangingResolution && !s_dec_op.u4_output_present) { + mChangingResolution = false; + resetDecoder(); + resetPlugin(); + mStride = outputBufferWidth(); + setParams(mStride); + continue; + } + + if (unsupportedDimensions || resChanged) { + mChangingResolution = true; + if (mFlushNeeded) { + setFlushMode(); + } + + if (unsupportedDimensions) { + mNewWidth = s_dec_op.u4_pic_wd; + mNewHeight = s_dec_op.u4_pic_ht; + mInitNeeded = true; + } + continue; + } + + // Combine the resolution change and coloraspects change in one PortSettingChange event + // if necessary. + if ((0 < s_dec_op.u4_pic_wd) && (0 < s_dec_op.u4_pic_ht)) { + uint32_t width = s_dec_op.u4_pic_wd; + uint32_t height = s_dec_op.u4_pic_ht; + bool portWillReset = false; + handlePortSettingsChange(&portWillReset, width, height); + + if (portWillReset) { + resetDecoder(); + resetPlugin(); + return; + } + } else if (mUpdateColorAspects) { + notify(OMX_EventPortSettingsChanged, kOutputPortIndex, + kDescribeColorAspectsIndex, NULL); + mUpdateColorAspects = false; + return; + } + + if (s_dec_op.u4_output_present) { + ssize_t timeStampIdx; + outHeader->nFilledLen = (outputBufferWidth() * outputBufferHeight() * 3) / 2; + + timeStampIdx = getMinTimestampIdx(mTimeStamps, mTimeStampsValid); + if (timeStampIdx < 0) { + ALOGE("b/62872863, Invalid timestamp index!"); + android_errorWriteLog(0x534e4554, "62872863"); + return; + } + outHeader->nTimeStamp = mTimeStamps[timeStampIdx]; + mTimeStampsValid[timeStampIdx] = false; + + /* mWaitForI waits for the first I picture. Once made FALSE, it + has to remain false till explicitly set to TRUE. */ + mWaitForI = mWaitForI && !(IV_I_FRAME == s_dec_op.e_pic_type); + + if (mWaitForI) { + s_dec_op.u4_output_present = false; + } else { + ALOGV("Output timestamp: %lld, res: %ux%u", + (long long)outHeader->nTimeStamp, mWidth, mHeight); + DUMP_TO_FILE(mOutFile, outHeader->pBuffer, outHeader->nFilledLen); + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + } + } else if (mIsInFlush) { + /* If in flush mode and no output is returned by the codec, + * then come out of flush mode */ + mIsInFlush = false; + + /* If EOS was recieved on input port and there is no output + * from the codec, then signal EOS on output port */ + if (mReceivedEOS) { + outHeader->nFilledLen = 0; + outHeader->nFlags |= OMX_BUFFERFLAG_EOS; + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + resetPlugin(); + } + } + } + + /* If input EOS is seen and decoder is not in flush mode, + * set the decoder in flush mode. + * There can be a case where EOS is sent along with last picture data + * In that case, only after decoding that input data, decoder has to be + * put in flush. This case is handled here */ + + if (mReceivedEOS && !mIsInFlush) { + setFlushMode(); + } + + // TODO: Handle more than one picture data + if (inHeader != NULL) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } + } +} + +int SoftMPEG2::getColorAspectPreference() { + return kPreferBitstream; +} + +} // namespace android + +__attribute__((cfi_canonical_jump_table)) +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, + OMX_COMPONENTTYPE **component) { + return new android::SoftMPEG2(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.h b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.h new file mode 100644 index 0000000..338fc30 --- /dev/null +++ b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.h @@ -0,0 +1,184 @@ +/* + * Copyright 2015 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_MPEG2_H_ + +#define SOFT_MPEG2_H_ + +#include +#include + +namespace android { + +#define ivd_aligned_malloc(alignment, size) memalign(alignment, size) +#define ivd_aligned_free(buf) free(buf) + +/** Number of entries in the time-stamp array */ +#define MAX_TIME_STAMPS 64 + +/** Maximum number of cores supported by the codec */ +#define CODEC_MAX_NUM_CORES 4 + +#define CODEC_MAX_WIDTH 1920 + +#define CODEC_MAX_HEIGHT 1088 + +/** Input buffer size */ +#define INPUT_BUF_SIZE (1024 * 1024) + +#define MIN(a, b) ((a) < (b)) ? (a) : (b) + +/** Used to remove warnings about unused parameters */ +#define UNUSED(x) ((void)(x)) + +/** Get time */ +#define GETTIME(a, b) gettimeofday(a, b); + +/** Compute difference between start and end */ +#define TIME_DIFF(start, end, diff) \ + diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \ + ((end).tv_usec - (start).tv_usec); + +struct SoftMPEG2 : public SoftVideoDecoderOMXComponent { + SoftMPEG2( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftMPEG2(); + + virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onReset(); + virtual int getColorAspectPreference(); + virtual OMX_ERRORTYPE internalSetParameter(OMX_INDEXTYPE index, const OMX_PTR params); +private: + // Number of input and output buffers + enum { + kNumBuffers = 8 + }; + + iv_obj_t *mCodecCtx; // Codec context + iv_mem_rec_t *mMemRecords; // Memory records requested by the codec + size_t mNumMemRecords; // Number of memory records requested by the codec + + size_t mNumCores; // Number of cores to be uesd by the codec + + struct timeval mTimeStart; // Time at the start of decode() + struct timeval mTimeEnd; // Time at the end of decode() + + // Internal buffer to be used to flush out the buffers from decoder + uint8_t *mFlushOutBuffer; + + // Status of entries in the timestamp array + bool mTimeStampsValid[MAX_TIME_STAMPS]; + + // Timestamp array - Since codec does not take 64 bit timestamps, + // they are maintained in the plugin + OMX_S64 mTimeStamps[MAX_TIME_STAMPS]; + +#ifdef FILE_DUMP_ENABLE + char mInFile[200]; +#endif /* FILE_DUMP_ENABLE */ + + OMX_COLOR_FORMATTYPE mOmxColorFormat; // OMX Color format + IV_COLOR_FORMAT_T mIvColorFormat; // Ittiam Color format + + bool mIsInFlush; // codec is flush mode + bool mReceivedEOS; // EOS is receieved on input port + bool mInitNeeded; + uint32_t mNewWidth; + uint32_t mNewHeight; + // The input stream has changed to a different resolution, which is still supported by the + // codec. So the codec is switching to decode the new resolution. + bool mChangingResolution; + bool mFlushNeeded; + bool mSignalledError; + bool mWaitForI; + size_t mStride; + + status_t initDecoder(); + status_t deInitDecoder(); + status_t setFlushMode(); + status_t setParams(size_t stride); + void logVersion(); + status_t setNumCores(); + status_t resetDecoder(); + status_t resetPlugin(); + status_t reInitDecoder(); + + bool setDecodeArgs( + ivd_video_decode_ip_t *ps_dec_ip, + ivd_video_decode_op_t *ps_dec_op, + OMX_BUFFERHEADERTYPE *inHeader, + OMX_BUFFERHEADERTYPE *outHeader, + size_t timeStampIx); + + bool getSeqInfo(); + + DISALLOW_EVIL_CONSTRUCTORS(SoftMPEG2); +}; + +#ifdef FILE_DUMP_ENABLE + +#define INPUT_DUMP_PATH "/sdcard/media/mpeg2d_input" +#define INPUT_DUMP_EXT "m2v" + +#define GENERATE_FILE_NAMES() { \ + GETTIME(&mTimeStart, NULL); \ + strcpy(mInFile, ""); \ + sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH, \ + mTimeStart.tv_sec, mTimeStart.tv_usec, \ + INPUT_DUMP_EXT); \ +} + +#define CREATE_DUMP_FILE(m_filename) { \ + FILE *fp = fopen(m_filename, "wb"); \ + if (fp != NULL) { \ + fclose(fp); \ + } else { \ + ALOGD("Could not open file %s", m_filename); \ + } \ +} +#define DUMP_TO_FILE(m_filename, m_buf, m_size) \ +{ \ + FILE *fp = fopen(m_filename, "ab"); \ + if (fp != NULL && m_buf != NULL) { \ + int i; \ + i = fwrite(m_buf, 1, m_size, fp); \ + ALOGD("fwrite ret %d to write %d", i, m_size); \ + if (i != (int)m_size) { \ + ALOGD("Error in fwrite, returned %d", i); \ + perror("Error in write to file"); \ + } \ + fclose(fp); \ + } else { \ + ALOGD("Could not write to file %s", m_filename);\ + } \ +} +#else /* FILE_DUMP_ENABLE */ +#define INPUT_DUMP_PATH +#define INPUT_DUMP_EXT +#define OUTPUT_DUMP_PATH +#define OUTPUT_DUMP_EXT +#define GENERATE_FILE_NAMES() +#define CREATE_DUMP_FILE(m_filename) +#define DUMP_TO_FILE(m_filename, m_buf, m_size) +#endif /* FILE_DUMP_ENABLE */ + +} // namespace android + +#endif // SOFT_MPEG2_H_ diff --git a/media/libstagefright/codecs/mpeg2dec/exports.lds b/media/libstagefright/codecs/mpeg2dec/exports.lds new file mode 100644 index 0000000..e24f3fa --- /dev/null +++ b/media/libstagefright/codecs/mpeg2dec/exports.lds @@ -0,0 +1,5 @@ +{ + global: + _Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE; + local: *; +}; diff --git a/media/libstagefright/codecs/on2/dec/Android.bp b/media/libstagefright/codecs/on2/dec/Android.bp new file mode 100644 index 0000000..ba6dc2a --- /dev/null +++ b/media/libstagefright/codecs/on2/dec/Android.bp @@ -0,0 +1,37 @@ +package { + default_applicable_licenses: [ + "frameworks_av_media_libstagefright_codecs_on2_dec_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_av_media_libstagefright_codecs_on2_dec_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + +cc_library_shared { + name: "libstagefright_soft_vpxdec", + defaults: ["libstagefright_softomx-defaults"], + + srcs: ["SoftVPX.cpp"], + + shared_libs: ["libvpx"], + + version_script: "exports.lds", + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + "unsigned-integer-overflow", + ], + cfi: true, + }, +} diff --git a/media/libstagefright/codecs/on2/dec/MODULE_LICENSE_APACHE2 b/media/libstagefright/codecs/on2/dec/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/media/libstagefright/codecs/on2/dec/NOTICE b/media/libstagefright/codecs/on2/dec/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/media/libstagefright/codecs/on2/dec/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, 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/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp new file mode 100644 index 0000000..bffc23a --- /dev/null +++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2011 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 "SoftVPX" +#include +#include +#include "OMX_VideoExt.h" + +#include "SoftVPX.h" + +#include +#include + + +namespace android { + +// Only need to declare the highest supported profile and level here. +static const CodecProfileLevel kVP9ProfileLevels[] = { + { OMX_VIDEO_VP9Profile0, OMX_VIDEO_VP9Level5 }, + { OMX_VIDEO_VP9Profile2, OMX_VIDEO_VP9Level5 }, + { OMX_VIDEO_VP9Profile2HDR, OMX_VIDEO_VP9Level5 }, + { OMX_VIDEO_VP9Profile2HDR10Plus, OMX_VIDEO_VP9Level5 }, +}; + +SoftVPX::SoftVPX( + const char *name, + const char *componentRole, + OMX_VIDEO_CODINGTYPE codingType, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SoftVideoDecoderOMXComponent( + name, componentRole, codingType, + codingType == OMX_VIDEO_CodingVP8 ? NULL : kVP9ProfileLevels, + codingType == OMX_VIDEO_CodingVP8 ? 0 : NELEM(kVP9ProfileLevels), + 320 /* width */, 240 /* height */, callbacks, appData, component), + mMode(codingType == OMX_VIDEO_CodingVP8 ? MODE_VP8 : MODE_VP9), + mEOSStatus(INPUT_DATA_AVAILABLE), + mCtx(NULL), + mFrameParallelMode(false), + mTimeStampIdx(0), + mImg(NULL) { + // arbitrary from avc/hevc as vpx does not specify a min compression ratio + const size_t kMinCompressionRatio = mMode == MODE_VP8 ? 2 : 4; + const char *mime = mMode == MODE_VP8 ? MEDIA_MIMETYPE_VIDEO_VP8 : MEDIA_MIMETYPE_VIDEO_VP9; + const size_t kMaxOutputBufferSize = 2048 * 2048 * 3 / 2; + initPorts( + kNumBuffers, kMaxOutputBufferSize / kMinCompressionRatio /* inputBufferSize */, + kNumBuffers, mime, kMinCompressionRatio); + CHECK_EQ(initDecoder(), (status_t)OK); +} + +SoftVPX::~SoftVPX() { + destroyDecoder(); +} + +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(cpuCoreCount >= 1); + ALOGV("Number of CPU cores: %d", cpuCoreCount); + return cpuCoreCount; +} + +bool SoftVPX::supportDescribeHdrStaticInfo() { + return true; +} + +bool SoftVPX::supportDescribeHdr10PlusInfo() { + return true; +} + +status_t SoftVPX::initDecoder() { + mCtx = new vpx_codec_ctx_t; + vpx_codec_err_t vpx_err; + vpx_codec_dec_cfg_t cfg; + vpx_codec_flags_t flags; + memset(&cfg, 0, sizeof(vpx_codec_dec_cfg_t)); + memset(&flags, 0, sizeof(vpx_codec_flags_t)); + cfg.threads = GetCPUCoreCount(); + + if (mFrameParallelMode) { + flags |= VPX_CODEC_USE_FRAME_THREADING; + } + + if ((vpx_err = vpx_codec_dec_init( + (vpx_codec_ctx_t *)mCtx, + mMode == MODE_VP8 ? &vpx_codec_vp8_dx_algo : &vpx_codec_vp9_dx_algo, + &cfg, flags))) { + ALOGE("on2 decoder failed to initialize. (%d)", vpx_err); + return UNKNOWN_ERROR; + } + + return OK; +} + +status_t SoftVPX::destroyDecoder() { + vpx_codec_destroy((vpx_codec_ctx_t *)mCtx); + delete (vpx_codec_ctx_t *)mCtx; + mCtx = NULL; + return OK; +} + +bool SoftVPX::outputBuffers(bool flushDecoder, bool display, bool eos, bool *portWillReset) { + List &outQueue = getPortQueue(1); + BufferInfo *outInfo = NULL; + OMX_BUFFERHEADERTYPE *outHeader = NULL; + vpx_codec_iter_t iter = NULL; + + if (flushDecoder && mFrameParallelMode) { + // Flush decoder by passing NULL data ptr and 0 size. + // Ideally, this should never fail. + if (vpx_codec_decode((vpx_codec_ctx_t *)mCtx, NULL, 0, NULL, 0)) { + ALOGE("Failed to flush on2 decoder."); + return false; + } + } + + if (!display) { + if (!flushDecoder) { + ALOGE("Invalid operation."); + return false; + } + // Drop all the decoded frames in decoder. + while ((mImg = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter))) { + } + return true; + } + + while (!outQueue.empty()) { + if (mImg == NULL) { + mImg = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter); + if (mImg == NULL) { + break; + } + } + uint32_t width = mImg->d_w; + uint32_t height = mImg->d_h; + outInfo = *outQueue.begin(); + outHeader = outInfo->mHeader; + CHECK(mImg->fmt == VPX_IMG_FMT_I420 || mImg->fmt == VPX_IMG_FMT_I42016); + OMX_COLOR_FORMATTYPE outputColorFormat = OMX_COLOR_FormatYUV420Planar; + int32_t bpp = 1; + if (mImg->fmt == VPX_IMG_FMT_I42016) { + outputColorFormat = OMX_COLOR_FormatYUV420Planar16; + bpp = 2; + } + handlePortSettingsChange(portWillReset, width, height, outputColorFormat); + if (*portWillReset) { + return true; + } + + outHeader->nOffset = 0; + outHeader->nFlags = 0; + outHeader->nFilledLen = (outputBufferWidth() * outputBufferHeight() * bpp * 3) / 2; + PrivInfo *privInfo = (PrivInfo *)mImg->user_priv; + outHeader->nTimeStamp = privInfo->mTimeStamp; + if (privInfo->mHdr10PlusInfo != nullptr) { + queueOutputFrameConfig(privInfo->mHdr10PlusInfo); + } + + if (outputBufferSafe(outHeader)) { + uint8_t *dst = outHeader->pBuffer; + const uint8_t *srcY = (const uint8_t *)mImg->planes[VPX_PLANE_Y]; + const uint8_t *srcU = (const uint8_t *)mImg->planes[VPX_PLANE_U]; + const uint8_t *srcV = (const uint8_t *)mImg->planes[VPX_PLANE_V]; + size_t srcYStride = mImg->stride[VPX_PLANE_Y]; + size_t srcUStride = mImg->stride[VPX_PLANE_U]; + size_t srcVStride = mImg->stride[VPX_PLANE_V]; + copyYV12FrameToOutputBuffer(dst, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride); + } else { + outHeader->nFilledLen = 0; + } + + mImg = NULL; + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + } + + if (!eos) { + return true; + } + + if (!outQueue.empty()) { + outInfo = *outQueue.begin(); + outQueue.erase(outQueue.begin()); + outHeader = outInfo->mHeader; + outHeader->nTimeStamp = 0; + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + mEOSStatus = OUTPUT_FRAMES_FLUSHED; + } + return true; +} + +bool SoftVPX::outputBufferSafe(OMX_BUFFERHEADERTYPE *outHeader) { + uint32_t width = outputBufferWidth(); + uint32_t height = outputBufferHeight(); + uint64_t nFilledLen = width; + nFilledLen *= height; + if (nFilledLen > UINT32_MAX / 3) { + ALOGE("b/29421675, nFilledLen overflow %llu w %u h %u", + (unsigned long long)nFilledLen, width, height); + android_errorWriteLog(0x534e4554, "29421675"); + return false; + } else if (outHeader->nAllocLen < outHeader->nFilledLen) { + ALOGE("b/27597103, buffer too small"); + android_errorWriteLog(0x534e4554, "27597103"); + return false; + } + + return true; +} + +void SoftVPX::onQueueFilled(OMX_U32 /* portIndex */) { + if (mOutputPortSettingsChange != NONE || mEOSStatus == OUTPUT_FRAMES_FLUSHED) { + return; + } + + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + bool EOSseen = false; + bool portWillReset = false; + + while ((mEOSStatus == INPUT_EOS_SEEN || !inQueue.empty()) + && !outQueue.empty()) { + // Output the pending frames that left from last port reset or decoder flush. + if (mEOSStatus == INPUT_EOS_SEEN || mImg != NULL) { + if (!outputBuffers( + mEOSStatus == INPUT_EOS_SEEN, true /* display */, + mEOSStatus == INPUT_EOS_SEEN, &portWillReset)) { + ALOGE("on2 decoder failed to output frame."); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + if (portWillReset || mEOSStatus == OUTPUT_FRAMES_FLUSHED || + mEOSStatus == INPUT_EOS_SEEN) { + return; + } + // Continue as outQueue may be empty now. + continue; + } + + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + // Software VP9 Decoder does not need the Codec Specific Data (CSD) + // (specified in http://www.webmproject.org/vp9/profiles/). Ignore it if + // it was passed. + if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) { + // Only ignore CSD buffer for VP9. + if (mMode == MODE_VP9) { + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + continue; + } else { + // Tolerate the CSD buffer for VP8. This is a workaround + // for b/28689536. + ALOGW("WARNING: Got CSD buffer for VP8."); + } + } + + mPrivInfo[mTimeStampIdx].mTimeStamp = inHeader->nTimeStamp; + + if (inInfo->mFrameConfig) { + mPrivInfo[mTimeStampIdx].mHdr10PlusInfo = dequeueInputFrameConfig(); + } else { + mPrivInfo[mTimeStampIdx].mHdr10PlusInfo.clear(); + } + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + mEOSStatus = INPUT_EOS_SEEN; + EOSseen = true; + } + + if (inHeader->nFilledLen > 0) { + vpx_codec_err_t err = vpx_codec_decode( + (vpx_codec_ctx_t *)mCtx, inHeader->pBuffer + inHeader->nOffset, + inHeader->nFilledLen, &mPrivInfo[mTimeStampIdx], 0); + if (err == VPX_CODEC_OK) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } else { + ALOGE("on2 decoder failed to decode frame. err: %d", err); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + } + + mTimeStampIdx = (mTimeStampIdx + 1) % kNumBuffers; + + if (!outputBuffers( + EOSseen /* flushDecoder */, true /* display */, EOSseen, &portWillReset)) { + ALOGE("on2 decoder failed to output frame."); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + if (portWillReset) { + return; + } + } +} + +void SoftVPX::onPortFlushCompleted(OMX_U32 portIndex) { + if (portIndex == kInputPortIndex) { + bool portWillReset = false; + if (!outputBuffers( + true /* flushDecoder */, false /* display */, false /* eos */, &portWillReset)) { + ALOGE("Failed to flush decoder."); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + mEOSStatus = INPUT_DATA_AVAILABLE; + } +} + +void SoftVPX::onReset() { + bool portWillReset = false; + if (!outputBuffers( + true /* flushDecoder */, false /* display */, false /* eos */, &portWillReset)) { + ALOGW("Failed to flush decoder. Try to hard reset decoder"); + destroyDecoder(); + initDecoder(); + } + mEOSStatus = INPUT_DATA_AVAILABLE; +} + +} // namespace android + +__attribute__((cfi_canonical_jump_table)) +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + if (!strcmp(name, "OMX.google.vp8.decoder")) { + return new android::SoftVPX( + name, "video_decoder.vp8", OMX_VIDEO_CodingVP8, + callbacks, appData, component); + } else if (!strcmp(name, "OMX.google.vp9.decoder")) { + return new android::SoftVPX( + name, "video_decoder.vp9", OMX_VIDEO_CodingVP9, + callbacks, appData, component); + } else { + CHECK(!"Unknown component"); + } + return NULL; +} diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.h b/media/libstagefright/codecs/on2/dec/SoftVPX.h new file mode 100644 index 0000000..0aa8e9c --- /dev/null +++ b/media/libstagefright/codecs/on2/dec/SoftVPX.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2011 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_H_ + +#define SOFT_VPX_H_ + +#include + +#include "vpx/vpx_decoder.h" +#include "vpx/vpx_codec.h" +#include "vpx/vp8dx.h" + +namespace android { + +struct ABuffer; + +struct SoftVPX : public SoftVideoDecoderOMXComponent { + SoftVPX(const char *name, + const char *componentRole, + OMX_VIDEO_CODINGTYPE codingType, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftVPX(); + + virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onReset(); + virtual bool supportDescribeHdrStaticInfo(); + virtual bool supportDescribeHdr10PlusInfo(); + +private: + enum { + kNumBuffers = 10 + }; + + enum { + MODE_VP8, + MODE_VP9 + } mMode; + + enum { + INPUT_DATA_AVAILABLE, // VPX component is ready to decode data. + INPUT_EOS_SEEN, // VPX component saw EOS and is flushing On2 decoder. + OUTPUT_FRAMES_FLUSHED // VPX component finished flushing On2 decoder. + } mEOSStatus; + + void *mCtx; + bool mFrameParallelMode; // Frame parallel is only supported by VP9 decoder. + struct PrivInfo { + OMX_TICKS mTimeStamp; + sp mHdr10PlusInfo; + }; + PrivInfo mPrivInfo[kNumBuffers]; + uint8_t mTimeStampIdx; + vpx_image_t *mImg; + + status_t initDecoder(); + status_t destroyDecoder(); + bool outputBuffers(bool flushDecoder, bool display, bool eos, bool *portWillReset); + bool outputBufferSafe(OMX_BUFFERHEADERTYPE *outHeader); + + DISALLOW_EVIL_CONSTRUCTORS(SoftVPX); +}; + +} // namespace android + +#endif // SOFT_VPX_H_ diff --git a/media/libstagefright/codecs/on2/dec/exports.lds b/media/libstagefright/codecs/on2/dec/exports.lds new file mode 100644 index 0000000..e24f3fa --- /dev/null +++ b/media/libstagefright/codecs/on2/dec/exports.lds @@ -0,0 +1,5 @@ +{ + global: + _Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE; + local: *; +}; diff --git a/media/libstagefright/codecs/on2/enc/Android.bp b/media/libstagefright/codecs/on2/enc/Android.bp new file mode 100644 index 0000000..e85ff98 --- /dev/null +++ b/media/libstagefright/codecs/on2/enc/Android.bp @@ -0,0 +1,44 @@ +package { + default_applicable_licenses: [ + "frameworks_av_media_libstagefright_codecs_on2_enc_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_av_media_libstagefright_codecs_on2_enc_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + +cc_library_shared { + name: "libstagefright_soft_vpxenc", + defaults: ["libstagefright_softomx-defaults"], + + srcs: [ + "SoftVPXEncoder.cpp", + "SoftVP8Encoder.cpp", + "SoftVP9Encoder.cpp", + ], + + cflags: ["-Wall"], + + version_script: "exports.lds", + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + "unsigned-integer-overflow", + ], + cfi: true, + }, + + shared_libs: ["libvpx"], + header_libs: ["libbase_headers"], +} diff --git a/media/libstagefright/codecs/on2/enc/MODULE_LICENSE_APACHE2 b/media/libstagefright/codecs/on2/enc/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/media/libstagefright/codecs/on2/enc/NOTICE b/media/libstagefright/codecs/on2/enc/NOTICE new file mode 100644 index 0000000..faed58a --- /dev/null +++ b/media/libstagefright/codecs/on2/enc/NOTICE @@ -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/SoftVP8Encoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVP8Encoder.cpp new file mode 100644 index 0000000..9198b7c --- /dev/null +++ b/media/libstagefright/codecs/on2/enc/SoftVP8Encoder.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2016 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 "SoftVP8Encoder" +#include "SoftVP8Encoder.h" + +#include +#include + +#include +#include +#include +#include + +#ifndef INT32_MAX +#define INT32_MAX 2147483647 +#endif + +namespace android { + +static const CodecProfileLevel kVp8ProfileLevels[] = { + { OMX_VIDEO_VP8ProfileMain, OMX_VIDEO_VP8Level_Version0 }, + { OMX_VIDEO_VP8ProfileMain, OMX_VIDEO_VP8Level_Version1 }, + { OMX_VIDEO_VP8ProfileMain, OMX_VIDEO_VP8Level_Version2 }, + { OMX_VIDEO_VP8ProfileMain, OMX_VIDEO_VP8Level_Version3 }, +}; + +SoftVP8Encoder::SoftVP8Encoder(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SoftVPXEncoder( + name, callbacks, appData, component, "video_encoder.vp8", + OMX_VIDEO_CodingVP8, MEDIA_MIMETYPE_VIDEO_VP8, 2, + kVp8ProfileLevels, NELEM(kVp8ProfileLevels)), + mDCTPartitions(0), + mLevel(OMX_VIDEO_VP8Level_Version0) { +} + +void SoftVP8Encoder::setCodecSpecificInterface() { + mCodecInterface = vpx_codec_vp8_cx(); +} + +void SoftVP8Encoder::setCodecSpecificConfiguration() { + 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; + } +} + +vpx_codec_err_t SoftVP8Encoder::setCodecSpecificControls() { + vpx_codec_err_t 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 codec_return; +} + +OMX_ERRORTYPE SoftVP8Encoder::internalGetParameter(OMX_INDEXTYPE index, + OMX_PTR param) { + // can include extension index OMX_INDEXEXTTYPE + const int32_t indexFull = index; + + switch (indexFull) { + case OMX_IndexParamVideoVp8: + return internalGetVp8Params( + (OMX_VIDEO_PARAM_VP8TYPE *)param); + + default: + return SoftVPXEncoder::internalGetParameter(index, param); + } +} + +OMX_ERRORTYPE SoftVP8Encoder::internalSetParameter(OMX_INDEXTYPE index, + const OMX_PTR param) { + // can include extension index OMX_INDEXEXTTYPE + const int32_t indexFull = index; + + switch (indexFull) { + case OMX_IndexParamVideoVp8: + return internalSetVp8Params( + (const OMX_VIDEO_PARAM_VP8TYPE *)param); + + default: + return SoftVPXEncoder::internalSetParameter(index, param); + } +} + +OMX_ERRORTYPE SoftVP8Encoder::internalGetVp8Params( + OMX_VIDEO_PARAM_VP8TYPE* vp8Params) { + if (!isValidOMXParam(vp8Params)) { + android_errorWriteLog(0x534e4554, "273936274"); + return OMX_ErrorBadParameter; + } + + if (vp8Params->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUnsupportedIndex; + } + + vp8Params->eProfile = OMX_VIDEO_VP8ProfileMain; + vp8Params->eLevel = mLevel; + vp8Params->bErrorResilientMode = mErrorResilience; + vp8Params->nDCTPartitions = mDCTPartitions; + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftVP8Encoder::internalSetVp8Params( + const OMX_VIDEO_PARAM_VP8TYPE* vp8Params) { + if (!isValidOMXParam(vp8Params)) { + android_errorWriteLog(0x534e4554, "273937171"); + return OMX_ErrorBadParameter; + } + + 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; + } + + mErrorResilience = vp8Params->bErrorResilientMode; + if (vp8Params->nDCTPartitions <= kMaxDCTPartitions) { + mDCTPartitions = vp8Params->nDCTPartitions; + } else { + return OMX_ErrorBadParameter; + } + return OMX_ErrorNone; +} + +} // namespace android diff --git a/media/libstagefright/codecs/on2/enc/SoftVP8Encoder.h b/media/libstagefright/codecs/on2/enc/SoftVP8Encoder.h new file mode 100644 index 0000000..c5c2abf --- /dev/null +++ b/media/libstagefright/codecs/on2/enc/SoftVP8Encoder.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2016 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_VP8_ENCODER_H_ + +#define SOFT_VP8_ENCODER_H_ + +#include "SoftVPXEncoder.h" + +#include +#include + +#include "vpx/vpx_encoder.h" +#include "vpx/vpx_codec.h" +#include "vpx/vp8cx.h" + +namespace android { + +// Exposes a vp8 encoder as an OMX Component +// +// In addition to the base class settings, Only following encoder settings are +// available: +// - token partitioning +struct SoftVP8Encoder : public SoftVPXEncoder { + SoftVP8Encoder(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + // 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); + + // Populates |mCodecInterface| with codec specific settings. + virtual void setCodecSpecificInterface(); + + // Sets codec specific configuration. + virtual void setCodecSpecificConfiguration(); + + // Initializes codec specific encoder settings. + virtual vpx_codec_err_t setCodecSpecificControls(); + + // Gets vp8 specific parameters. + OMX_ERRORTYPE internalGetVp8Params( + OMX_VIDEO_PARAM_VP8TYPE* vp8Params); + + // Handles vp8 specific parameters. + OMX_ERRORTYPE internalSetVp8Params( + const OMX_VIDEO_PARAM_VP8TYPE* vp8Params); + +private: + // Max value supported for DCT partitions + static const uint32_t kMaxDCTPartitions = 3; + + // vp8 specific configuration parameter + // that enables token partitioning of + // the stream into substreams + int32_t mDCTPartitions; + + // 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; + + DISALLOW_EVIL_CONSTRUCTORS(SoftVP8Encoder); +}; + +} // namespace android + +#endif // SOFT_VP8_ENCODER_H_ diff --git a/media/libstagefright/codecs/on2/enc/SoftVP9Encoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVP9Encoder.cpp new file mode 100644 index 0000000..f8495c2 --- /dev/null +++ b/media/libstagefright/codecs/on2/enc/SoftVP9Encoder.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2016 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 "SoftVP9Encoder" +#include "SoftVP9Encoder.h" + +#include +#include + +#include +#include +#include +#include + +namespace android { + +static const CodecProfileLevel kVp9ProfileLevels[] = { + { OMX_VIDEO_VP9Profile0, OMX_VIDEO_VP9Level41 }, +}; + +SoftVP9Encoder::SoftVP9Encoder( + const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SoftVPXEncoder( + name, callbacks, appData, component, "video_encoder.vp9", + OMX_VIDEO_CodingVP9, MEDIA_MIMETYPE_VIDEO_VP9, 4, + kVp9ProfileLevels, NELEM(kVp9ProfileLevels)), + mLevel(OMX_VIDEO_VP9Level1), + mTileColumns(0), + mFrameParallelDecoding(OMX_FALSE) { +} + +void SoftVP9Encoder::setCodecSpecificInterface() { + mCodecInterface = vpx_codec_vp9_cx(); +} + +void SoftVP9Encoder::setCodecSpecificConfiguration() { + mCodecConfiguration->g_profile = 0; +} + +vpx_codec_err_t SoftVP9Encoder::setCodecSpecificControls() { + vpx_codec_err_t codecReturn = vpx_codec_control( + mCodecContext, VP9E_SET_TILE_COLUMNS, mTileColumns); + if (codecReturn != VPX_CODEC_OK) { + ALOGE("Error setting VP9E_SET_TILE_COLUMNS to %d. vpx_codec_control() " + "returned %d", mTileColumns, codecReturn); + return codecReturn; + } + codecReturn = vpx_codec_control( + mCodecContext, VP9E_SET_FRAME_PARALLEL_DECODING, + mFrameParallelDecoding); + if (codecReturn != VPX_CODEC_OK) { + ALOGE("Error setting VP9E_SET_FRAME_PARALLEL_DECODING to %d." + "vpx_codec_control() returned %d", mFrameParallelDecoding, + codecReturn); + return codecReturn; + } + codecReturn = vpx_codec_control(mCodecContext, VP9E_SET_ROW_MT, 1); + if (codecReturn != VPX_CODEC_OK) { + ALOGE("Error setting VP9E_SET_ROW_MT to 1. vpx_codec_control() " + "returned %d", codecReturn); + return codecReturn; + } + + // For VP9, we always set CPU_USED to 8 (because the realtime default is 0 + // which is too slow). + codecReturn = vpx_codec_control(mCodecContext, VP8E_SET_CPUUSED, 8); + if (codecReturn != VPX_CODEC_OK) { + ALOGE("Error setting VP8E_SET_CPUUSED to 8. vpx_codec_control() " + "returned %d", codecReturn); + return codecReturn; + } + return codecReturn; +} + +OMX_ERRORTYPE SoftVP9Encoder::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR param) { + // can include extension index OMX_INDEXEXTTYPE + const int32_t indexFull = index; + + switch (indexFull) { + case OMX_IndexParamVideoVp9: + return internalGetVp9Params( + (OMX_VIDEO_PARAM_VP9TYPE *)param); + + default: + return SoftVPXEncoder::internalGetParameter(index, param); + } +} + +OMX_ERRORTYPE SoftVP9Encoder::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR param) { + // can include extension index OMX_INDEXEXTTYPE + const int32_t indexFull = index; + + switch (indexFull) { + case OMX_IndexParamVideoVp9: + return internalSetVp9Params( + (const OMX_VIDEO_PARAM_VP9TYPE *)param); + + default: + return SoftVPXEncoder::internalSetParameter(index, param); + } +} + +OMX_ERRORTYPE SoftVP9Encoder::internalGetVp9Params( + OMX_VIDEO_PARAM_VP9TYPE *vp9Params) { + if (!isValidOMXParam(vp9Params)) { + android_errorWriteLog(0x534e4554, "273936553"); + return OMX_ErrorBadParameter; + } + + if (vp9Params->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUnsupportedIndex; + } + + vp9Params->eProfile = OMX_VIDEO_VP9Profile0; + vp9Params->eLevel = mLevel; + vp9Params->bErrorResilientMode = mErrorResilience; + vp9Params->nTileColumns = mTileColumns; + vp9Params->bEnableFrameParallelDecoding = mFrameParallelDecoding; + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftVP9Encoder::internalSetVp9Params( + const OMX_VIDEO_PARAM_VP9TYPE *vp9Params) { + if (!isValidOMXParam(vp9Params)) { + android_errorWriteLog(0x534e4554, "273937136"); + return OMX_ErrorBadParameter; + } + + if (vp9Params->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUnsupportedIndex; + } + + if (vp9Params->eProfile != OMX_VIDEO_VP9Profile0) { + return OMX_ErrorBadParameter; + } + + if (vp9Params->eLevel == OMX_VIDEO_VP9Level1 || + vp9Params->eLevel == OMX_VIDEO_VP9Level11 || + vp9Params->eLevel == OMX_VIDEO_VP9Level2 || + vp9Params->eLevel == OMX_VIDEO_VP9Level21 || + vp9Params->eLevel == OMX_VIDEO_VP9Level3 || + vp9Params->eLevel == OMX_VIDEO_VP9Level31 || + vp9Params->eLevel == OMX_VIDEO_VP9Level4 || + vp9Params->eLevel == OMX_VIDEO_VP9Level41 || + vp9Params->eLevel == OMX_VIDEO_VP9Level5 || + vp9Params->eLevel == OMX_VIDEO_VP9Level51 || + vp9Params->eLevel == OMX_VIDEO_VP9Level52 || + vp9Params->eLevel == OMX_VIDEO_VP9Level6 || + vp9Params->eLevel == OMX_VIDEO_VP9Level61 || + vp9Params->eLevel == OMX_VIDEO_VP9Level62) { + mLevel = vp9Params->eLevel; + } else { + return OMX_ErrorBadParameter; + } + + mErrorResilience = vp9Params->bErrorResilientMode; + mTileColumns = vp9Params->nTileColumns; + mFrameParallelDecoding = vp9Params->bEnableFrameParallelDecoding; + return OMX_ErrorNone; +} + +} // namespace android diff --git a/media/libstagefright/codecs/on2/enc/SoftVP9Encoder.h b/media/libstagefright/codecs/on2/enc/SoftVP9Encoder.h new file mode 100644 index 0000000..308a9ac --- /dev/null +++ b/media/libstagefright/codecs/on2/enc/SoftVP9Encoder.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2016 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_VP9_ENCODER_H_ + +#define SOFT_VP9_ENCODER_H_ + +#include "SoftVPXEncoder.h" + +#include +#include + +#include "vpx/vpx_encoder.h" +#include "vpx/vpx_codec.h" +#include "vpx/vp8cx.h" + +namespace android { + +// Exposes a VP9 encoder as an OMX Component +// +// In addition to the base class settings, Only following encoder settings are +// available: +// - tile rows +// - tile columns +// - frame parallel mode +struct SoftVP9Encoder : public SoftVPXEncoder { + SoftVP9Encoder(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + // 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); + + // Populates |mCodecInterface| with codec specific settings. + virtual void setCodecSpecificInterface(); + + // Sets codec specific configuration. + virtual void setCodecSpecificConfiguration(); + + // Initializes codec specific encoder settings. + virtual vpx_codec_err_t setCodecSpecificControls(); + + // Gets vp9 specific parameters. + OMX_ERRORTYPE internalGetVp9Params( + OMX_VIDEO_PARAM_VP9TYPE* vp9Params); + + // Handles vp9 specific parameters. + OMX_ERRORTYPE internalSetVp9Params( + const OMX_VIDEO_PARAM_VP9TYPE* vp9Params); + +private: + // 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_VP9LEVELTYPE mLevel; + + int32_t mTileColumns; + + OMX_BOOL mFrameParallelDecoding; + + DISALLOW_EVIL_CONSTRUCTORS(SoftVP9Encoder); +}; + +} // namespace android + +#endif // SOFT_VP9_ENCODER_H_ diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp new file mode 100644 index 0000000..cbedb72 --- /dev/null +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.cpp @@ -0,0 +1,799 @@ +/* + * 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 "SoftVP8Encoder.h" +#include "SoftVP9Encoder.h" + +#include +#include +#include + +#include +#include +#include +#include + +#ifndef INT32_MAX +#define INT32_MAX 2147483647 +#endif + +namespace android { + +template +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; +} + +SoftVPXEncoder::SoftVPXEncoder(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component, + const char* role, + OMX_VIDEO_CODINGTYPE codingType, + const char* mimeType, + int32_t minCompressionRatio, + const CodecProfileLevel *profileLevels, + size_t numProfileLevels) + : SoftVideoEncoderOMXComponent( + name, role, codingType, profileLevels, numProfileLevels, + 176 /* width */, 144 /* height */, + callbacks, appData, component), + mCodecContext(NULL), + mCodecConfiguration(NULL), + mCodecInterface(NULL), + mBitrateUpdated(false), + mBitrateControlMode(VPX_VBR), + mErrorResilience(OMX_FALSE), + mKeyFrameInterval(0), + mMinQuantizer(0), + mMaxQuantizer(0), + mTemporalLayers(0), + mTemporalPatternType(OMX_VIDEO_VPXTemporalLayerPatternNone), + mTemporalPatternLength(0), + mTemporalPatternIdx(0), + mLastTimestamp(0x7FFFFFFFFFFFFFFFLL), + mConversionBuffer(NULL), + mKeyFrameRequested(false) { + memset(mTemporalLayerBitrateRatio, 0, sizeof(mTemporalLayerBitrateRatio)); + mTemporalLayerBitrateRatio[0] = 100; + + const size_t kMinOutputBufferSize = 1024 * 1024; // arbitrary + + initPorts( + kNumBuffers, kNumBuffers, kMinOutputBufferSize, + mimeType, minCompressionRatio); +} + +SoftVPXEncoder::~SoftVPXEncoder() { + releaseEncoder(); +} + +status_t SoftVPXEncoder::initEncoder() { + vpx_codec_err_t codec_return; + status_t result = UNKNOWN_ERROR; + + setCodecSpecificInterface(); + if (mCodecInterface == NULL) { + goto CLEAN_UP; + } + ALOGD("VPx: initEncoder. BRMode: %u. TSLayers: %zu. KF: %u. QP: %u - %u", + (uint32_t)mBitrateControlMode, mTemporalLayers, mKeyFrameInterval, + mMinQuantizer, mMaxQuantizer); + + mCodecConfiguration = new vpx_codec_enc_cfg_t; + codec_return = vpx_codec_enc_config_default(mCodecInterface, + mCodecConfiguration, + 0); + + if (codec_return != VPX_CODEC_OK) { + ALOGE("Error populating default configuration for vpx encoder."); + goto CLEAN_UP; + } + + mCodecConfiguration->g_w = mWidth; + mCodecConfiguration->g_h = mHeight; + mCodecConfiguration->g_threads = GetCPUCoreCount(); + mCodecConfiguration->g_error_resilient = mErrorResilience; + + // 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 + 500) / 1000; + mCodecConfiguration->rc_end_usage = mBitrateControlMode; + // Disable frame drop - not allowed in MediaCodec now. + mCodecConfiguration->rc_dropframe_thresh = 0; + // Disable lagged encoding. + mCodecConfiguration->g_lag_in_frames = 0; + if (mBitrateControlMode == VPX_CBR) { + // Disable spatial resizing. + mCodecConfiguration->rc_resize_allowed = 0; + // Single-pass mode. + mCodecConfiguration->g_pass = VPX_RC_ONE_PASS; + // Maximum amount of bits that can be subtracted from the target + // bitrate - expressed as percentage of the target bitrate. + mCodecConfiguration->rc_undershoot_pct = 100; + // Maximum amount of bits that can be added to the target + // bitrate - expressed as percentage of the target bitrate. + mCodecConfiguration->rc_overshoot_pct = 15; + // Initial value of the buffer level in ms. + mCodecConfiguration->rc_buf_initial_sz = 500; + // Amount of data that the encoder should try to maintain in ms. + mCodecConfiguration->rc_buf_optimal_sz = 600; + // The amount of data that may be buffered by the decoding + // application in ms. + mCodecConfiguration->rc_buf_sz = 1000; + // Enable error resilience - needed for packet loss. + mCodecConfiguration->g_error_resilient = 1; + // Maximum key frame interval - for CBR boost to 3000 + mCodecConfiguration->kf_max_dist = 3000; + // Encoder determines optimal key frame placement automatically. + mCodecConfiguration->kf_mode = VPX_KF_AUTO; + } + + // Frames temporal pattern - for now WebRTC like pattern is only supported. + switch (mTemporalLayers) { + case 0: + { + mTemporalPatternLength = 0; + break; + } + case 1: + { + mCodecConfiguration->ts_number_layers = 1; + mCodecConfiguration->ts_rate_decimator[0] = 1; + mCodecConfiguration->ts_periodicity = 1; + mCodecConfiguration->ts_layer_id[0] = 0; + mTemporalPattern[0] = kTemporalUpdateLastRefAll; + mTemporalPatternLength = 1; + break; + } + case 2: + { + mCodecConfiguration->ts_number_layers = 2; + mCodecConfiguration->ts_rate_decimator[0] = 2; + mCodecConfiguration->ts_rate_decimator[1] = 1; + mCodecConfiguration->ts_periodicity = 2; + mCodecConfiguration->ts_layer_id[0] = 0; + mCodecConfiguration->ts_layer_id[1] = 1; + mTemporalPattern[0] = kTemporalUpdateLastAndGoldenRefAltRef; + mTemporalPattern[1] = kTemporalUpdateGoldenWithoutDependencyRefAltRef; + mTemporalPattern[2] = kTemporalUpdateLastRefAltRef; + mTemporalPattern[3] = kTemporalUpdateGoldenRefAltRef; + mTemporalPattern[4] = kTemporalUpdateLastRefAltRef; + mTemporalPattern[5] = kTemporalUpdateGoldenRefAltRef; + mTemporalPattern[6] = kTemporalUpdateLastRefAltRef; + mTemporalPattern[7] = kTemporalUpdateNone; + mTemporalPatternLength = 8; + break; + } + case 3: + { + mCodecConfiguration->ts_number_layers = 3; + mCodecConfiguration->ts_rate_decimator[0] = 4; + mCodecConfiguration->ts_rate_decimator[1] = 2; + mCodecConfiguration->ts_rate_decimator[2] = 1; + mCodecConfiguration->ts_periodicity = 4; + mCodecConfiguration->ts_layer_id[0] = 0; + mCodecConfiguration->ts_layer_id[1] = 2; + mCodecConfiguration->ts_layer_id[2] = 1; + mCodecConfiguration->ts_layer_id[3] = 2; + mTemporalPattern[0] = kTemporalUpdateLastAndGoldenRefAltRef; + mTemporalPattern[1] = kTemporalUpdateNoneNoRefGoldenRefAltRef; + mTemporalPattern[2] = kTemporalUpdateGoldenWithoutDependencyRefAltRef; + mTemporalPattern[3] = kTemporalUpdateNone; + mTemporalPattern[4] = kTemporalUpdateLastRefAltRef; + mTemporalPattern[5] = kTemporalUpdateNone; + mTemporalPattern[6] = kTemporalUpdateGoldenRefAltRef; + mTemporalPattern[7] = kTemporalUpdateNone; + mTemporalPatternLength = 8; + break; + } + default: + { + ALOGE("Wrong number of temporal layers %zu", mTemporalLayers); + goto CLEAN_UP; + } + } + // Set bitrate values for each layer + for (size_t i = 0; i < mCodecConfiguration->ts_number_layers; i++) { + mCodecConfiguration->ts_target_bitrate[i] = + mCodecConfiguration->rc_target_bitrate * + mTemporalLayerBitrateRatio[i] / 100; + } + if (mKeyFrameInterval > 0) { + mCodecConfiguration->kf_max_dist = mKeyFrameInterval; + mCodecConfiguration->kf_min_dist = mKeyFrameInterval; + mCodecConfiguration->kf_mode = VPX_KF_AUTO; + } + if (mMinQuantizer > 0) { + mCodecConfiguration->rc_min_quantizer = mMinQuantizer; + } + if (mMaxQuantizer > 0) { + mCodecConfiguration->rc_max_quantizer = mMaxQuantizer; + } + setCodecSpecificConfiguration(); + mCodecContext = new vpx_codec_ctx_t; + codec_return = vpx_codec_enc_init(mCodecContext, + mCodecInterface, + mCodecConfiguration, + 0); // flags + + if (codec_return != VPX_CODEC_OK) { + ALOGE("Error initializing vpx encoder"); + goto CLEAN_UP; + } + + // Extra CBR settings + if (mBitrateControlMode == VPX_CBR) { + codec_return = vpx_codec_control(mCodecContext, + VP8E_SET_STATIC_THRESHOLD, + 1); + if (codec_return == VPX_CODEC_OK) { + uint32_t rc_max_intra_target = + mCodecConfiguration->rc_buf_optimal_sz * (mFramerate >> 17) / 10; + // Don't go below 3 times per frame bandwidth. + if (rc_max_intra_target < 300) { + rc_max_intra_target = 300; + } + codec_return = vpx_codec_control(mCodecContext, + VP8E_SET_MAX_INTRA_BITRATE_PCT, + rc_max_intra_target); + } + if (codec_return == VPX_CODEC_OK) { + codec_return = vpx_codec_control(mCodecContext, + VP8E_SET_CPUUSED, + -8); + } + if (codec_return != VPX_CODEC_OK) { + ALOGE("Error setting cbr parameters for vpx encoder."); + goto CLEAN_UP; + } + } + + codec_return = setCodecSpecificControls(); + + if (codec_return != VPX_CODEC_OK) { + // The codec specific method would have logged the error. + goto CLEAN_UP; + } + + if (mColorFormat != OMX_COLOR_FormatYUV420Planar || mInputDataIsMeta) { + free(mConversionBuffer); + mConversionBuffer = NULL; + if (((uint64_t)mWidth * mHeight) > ((uint64_t)INT32_MAX / 3)) { + ALOGE("b/25812794, Buffer size is too big, width=%d, height=%d.", mWidth, mHeight); + goto CLEAN_UP; + } + mConversionBuffer = (uint8_t *)malloc(mWidth * mHeight * 3 / 2); + if (mConversionBuffer == NULL) { + ALOGE("Allocating conversion buffer failed."); + goto CLEAN_UP; + } + } + return OK; + +CLEAN_UP: + releaseEncoder(); + return result; +} + +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) { + free(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_IndexParamVideoBitrate: { + OMX_VIDEO_PARAM_BITRATETYPE *bitrate = + (OMX_VIDEO_PARAM_BITRATETYPE *)param; + + if (!isValidOMXParam(bitrate)) { + return OMX_ErrorBadParameter; + } + + 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; + } + + case OMX_IndexParamVideoAndroidVp8Encoder: + return internalGetAndroidVpxParams( + (OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE *)param); + + default: + return SoftVideoEncoderOMXComponent::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_IndexParamVideoBitrate: { + const OMX_VIDEO_PARAM_BITRATETYPE *bitRate = + (const OMX_VIDEO_PARAM_BITRATETYPE*) param; + + if (!isValidOMXParam(bitRate)) { + return OMX_ErrorBadParameter; + } + + return internalSetBitrateParams(bitRate); + } + + case OMX_IndexParamVideoAndroidVp8Encoder: + return internalSetAndroidVpxParams( + (const OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE *)param); + + default: + return SoftVideoEncoderOMXComponent::internalSetParameter(index, param); + } +} + +OMX_ERRORTYPE SoftVPXEncoder::internalSetConfig( + OMX_INDEXTYPE index, const OMX_PTR _params, bool *frameConfig) { + switch (index) { + case OMX_IndexConfigVideoIntraVOPRefresh: + { + OMX_CONFIG_INTRAREFRESHVOPTYPE *params = + (OMX_CONFIG_INTRAREFRESHVOPTYPE *)_params; + + if (!isValidOMXParam(params)) { + return OMX_ErrorBadParameter; + } + + if (params->nPortIndex != kOutputPortIndex) { + return OMX_ErrorBadPortIndex; + } + + mKeyFrameRequested = params->IntraRefreshVOP; + return OMX_ErrorNone; + } + + case OMX_IndexConfigVideoBitrate: + { + OMX_VIDEO_CONFIG_BITRATETYPE *params = + (OMX_VIDEO_CONFIG_BITRATETYPE *)_params; + + if (!isValidOMXParam(params)) { + return OMX_ErrorBadParameter; + } + + if (params->nPortIndex != kOutputPortIndex) { + return OMX_ErrorBadPortIndex; + } + + if (mBitrate != params->nEncodeBitrate) { + mBitrate = params->nEncodeBitrate; + mBitrateUpdated = true; + } + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetConfig(index, _params, frameConfig); + } +} + +OMX_ERRORTYPE SoftVPXEncoder::internalGetBitrateParams( + OMX_VIDEO_PARAM_BITRATETYPE* bitrate) { + 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; +} + +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; +} + +OMX_ERRORTYPE SoftVPXEncoder::internalGetAndroidVpxParams( + OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE *vpxAndroidParams) { + if (!isValidOMXParam(vpxAndroidParams)) { + android_errorWriteLog(0x534e4554, "273936601"); + return OMX_ErrorBadParameter; + } + + if (vpxAndroidParams->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUnsupportedIndex; + } + + vpxAndroidParams->nKeyFrameInterval = mKeyFrameInterval; + vpxAndroidParams->eTemporalPattern = mTemporalPatternType; + vpxAndroidParams->nTemporalLayerCount = mTemporalLayers; + vpxAndroidParams->nMinQuantizer = mMinQuantizer; + vpxAndroidParams->nMaxQuantizer = mMaxQuantizer; + memcpy(vpxAndroidParams->nTemporalLayerBitrateRatio, + mTemporalLayerBitrateRatio, sizeof(mTemporalLayerBitrateRatio)); + return OMX_ErrorNone; +} + +OMX_ERRORTYPE SoftVPXEncoder::internalSetAndroidVpxParams( + const OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE *vpxAndroidParams) { + if (!isValidOMXParam(vpxAndroidParams)) { + android_errorWriteLog(0x534e4554, "273937551"); + return OMX_ErrorBadParameter; + } + if (vpxAndroidParams->nPortIndex != kOutputPortIndex) { + return OMX_ErrorUnsupportedIndex; + } + if (vpxAndroidParams->eTemporalPattern != OMX_VIDEO_VPXTemporalLayerPatternNone && + vpxAndroidParams->eTemporalPattern != OMX_VIDEO_VPXTemporalLayerPatternWebRTC) { + return OMX_ErrorBadParameter; + } + if (vpxAndroidParams->nTemporalLayerCount > OMX_VIDEO_ANDROID_MAXVP8TEMPORALLAYERS) { + return OMX_ErrorBadParameter; + } + if (vpxAndroidParams->nMinQuantizer > vpxAndroidParams->nMaxQuantizer) { + return OMX_ErrorBadParameter; + } + + mTemporalPatternType = vpxAndroidParams->eTemporalPattern; + if (vpxAndroidParams->eTemporalPattern == OMX_VIDEO_VPXTemporalLayerPatternWebRTC) { + mTemporalLayers = vpxAndroidParams->nTemporalLayerCount; + } else if (vpxAndroidParams->eTemporalPattern == OMX_VIDEO_VPXTemporalLayerPatternNone) { + mTemporalLayers = 0; + } + // Check the bitrate distribution between layers is in increasing order + if (mTemporalLayers > 1) { + for (size_t i = 0; i < mTemporalLayers - 1; i++) { + if (vpxAndroidParams->nTemporalLayerBitrateRatio[i + 1] <= + vpxAndroidParams->nTemporalLayerBitrateRatio[i]) { + ALOGE("Wrong bitrate ratio - should be in increasing order."); + return OMX_ErrorBadParameter; + } + } + } + mKeyFrameInterval = vpxAndroidParams->nKeyFrameInterval; + mMinQuantizer = vpxAndroidParams->nMinQuantizer; + mMaxQuantizer = vpxAndroidParams->nMaxQuantizer; + memcpy(mTemporalLayerBitrateRatio, vpxAndroidParams->nTemporalLayerBitrateRatio, + sizeof(mTemporalLayerBitrateRatio)); + ALOGD("VPx: internalSetAndroidVpxParams. BRMode: %u. TS: %zu. KF: %u." + " QP: %u - %u BR0: %u. BR1: %u. BR2: %u", + (uint32_t)mBitrateControlMode, mTemporalLayers, mKeyFrameInterval, + mMinQuantizer, mMaxQuantizer, mTemporalLayerBitrateRatio[0], + mTemporalLayerBitrateRatio[1], mTemporalLayerBitrateRatio[2]); + return OMX_ErrorNone; +} + +vpx_enc_frame_flags_t SoftVPXEncoder::getEncodeFlags() { + vpx_enc_frame_flags_t flags = 0; + if (mTemporalPatternLength > 0) { + int patternIdx = mTemporalPatternIdx % mTemporalPatternLength; + mTemporalPatternIdx++; + switch (mTemporalPattern[patternIdx]) { + case kTemporalUpdateLast: + flags |= VP8_EFLAG_NO_UPD_GF; + flags |= VP8_EFLAG_NO_UPD_ARF; + flags |= VP8_EFLAG_NO_REF_GF; + flags |= VP8_EFLAG_NO_REF_ARF; + break; + case kTemporalUpdateGoldenWithoutDependency: + flags |= VP8_EFLAG_NO_REF_GF; + FALLTHROUGH_INTENDED; + case kTemporalUpdateGolden: + flags |= VP8_EFLAG_NO_REF_ARF; + flags |= VP8_EFLAG_NO_UPD_ARF; + flags |= VP8_EFLAG_NO_UPD_LAST; + break; + case kTemporalUpdateAltrefWithoutDependency: + flags |= VP8_EFLAG_NO_REF_ARF; + flags |= VP8_EFLAG_NO_REF_GF; + FALLTHROUGH_INTENDED; + case kTemporalUpdateAltref: + flags |= VP8_EFLAG_NO_UPD_GF; + flags |= VP8_EFLAG_NO_UPD_LAST; + break; + case kTemporalUpdateNoneNoRefAltref: + flags |= VP8_EFLAG_NO_REF_ARF; + FALLTHROUGH_INTENDED; + case kTemporalUpdateNone: + flags |= VP8_EFLAG_NO_UPD_GF; + flags |= VP8_EFLAG_NO_UPD_ARF; + flags |= VP8_EFLAG_NO_UPD_LAST; + flags |= VP8_EFLAG_NO_UPD_ENTROPY; + break; + case kTemporalUpdateNoneNoRefGoldenRefAltRef: + flags |= VP8_EFLAG_NO_REF_GF; + flags |= VP8_EFLAG_NO_UPD_GF; + flags |= VP8_EFLAG_NO_UPD_ARF; + flags |= VP8_EFLAG_NO_UPD_LAST; + flags |= VP8_EFLAG_NO_UPD_ENTROPY; + break; + case kTemporalUpdateGoldenWithoutDependencyRefAltRef: + flags |= VP8_EFLAG_NO_REF_GF; + flags |= VP8_EFLAG_NO_UPD_ARF; + flags |= VP8_EFLAG_NO_UPD_LAST; + break; + case kTemporalUpdateLastRefAltRef: + flags |= VP8_EFLAG_NO_UPD_GF; + flags |= VP8_EFLAG_NO_UPD_ARF; + flags |= VP8_EFLAG_NO_REF_GF; + break; + case kTemporalUpdateGoldenRefAltRef: + flags |= VP8_EFLAG_NO_UPD_ARF; + flags |= VP8_EFLAG_NO_UPD_LAST; + break; + case kTemporalUpdateLastAndGoldenRefAltRef: + flags |= VP8_EFLAG_NO_UPD_ARF; + flags |= VP8_EFLAG_NO_REF_GF; + break; + case kTemporalUpdateLastRefAll: + flags |= VP8_EFLAG_NO_UPD_ARF; + flags |= VP8_EFLAG_NO_UPD_GF; + break; + } + } + return flags; +} + +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 &inputBufferInfoQueue = getPortQueue(kInputPortIndex); + List &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) && + inputBufferHeader->nFilledLen == 0) { + 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; + } + + OMX_ERRORTYPE error = validateInputBuffer(inputBufferHeader); + if (error != OMX_ErrorNone) { + ALOGE("b/27569635"); + android_errorWriteLog(0x534e4554, "27569635"); + notify(OMX_EventError, error, 0, 0); + return; + } + const uint8_t *source = + inputBufferHeader->pBuffer + inputBufferHeader->nOffset; + + size_t frameSize = mWidth * mHeight * 3 / 2; + if (mInputDataIsMeta) { + source = extractGraphicBuffer( + mConversionBuffer, frameSize, + source, inputBufferHeader->nFilledLen, + mWidth, mHeight); + if (source == NULL) { + ALOGE("Unable to extract gralloc buffer in metadata mode"); + notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); + return; + } + } else { + if (mColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) { + ConvertYUV420SemiPlanarToYUV420Planar( + source, mConversionBuffer, mWidth, mHeight); + + source = mConversionBuffer; + } + } + vpx_image_t raw_frame; + vpx_img_wrap(&raw_frame, VPX_IMG_FMT_I420, mWidth, mHeight, + kInputBufferAlignment, (uint8_t *)source); + + vpx_enc_frame_flags_t flags = getEncodeFlags(); + if (mKeyFrameRequested) { + flags |= VPX_EFLAG_FORCE_KF; + mKeyFrameRequested = false; + } + + if (mBitrateUpdated) { + mCodecConfiguration->rc_target_bitrate = mBitrate/1000; + vpx_codec_err_t res = vpx_codec_enc_config_set(mCodecContext, + mCodecConfiguration); + if (res != VPX_CODEC_OK) { + ALOGE("vpx encoder failed to update bitrate: %s", + vpx_codec_err_to_string(res)); + notify(OMX_EventError, + OMX_ErrorUndefined, + 0, // Extra notification data + NULL); // Notification data pointer + } + mBitrateUpdated = false; + } + + uint32_t frameDuration; + if (inputBufferHeader->nTimeStamp > mLastTimestamp) { + frameDuration = (uint32_t)(inputBufferHeader->nTimeStamp - mLastTimestamp); + } else { + // Use default of 30 fps in case of 0 frame rate. + uint32_t framerate = mFramerate ?: (30 << 16); + frameDuration = (uint32_t)(((uint64_t)1000000 << 16) / framerate); + } + mLastTimestamp = inputBufferHeader->nTimeStamp; + codec_return = vpx_codec_encode( + mCodecContext, + &raw_frame, + inputBufferHeader->nTimeStamp, // in timebase units + frameDuration, // frame duration in timebase units + flags, // 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; + if (encoded_packet->data.frame.flags & VPX_FRAME_IS_KEY) + outputBufferHeader->nFlags |= OMX_BUFFERFLAG_SYNCFRAME; + outputBufferHeader->nOffset = 0; + outputBufferHeader->nFilledLen = encoded_packet->data.frame.sz; + if (outputBufferHeader->nFilledLen > outputBufferHeader->nAllocLen) { + android_errorWriteLog(0x534e4554, "27569635"); + notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); + return; + } + memcpy(outputBufferHeader->pBuffer, + encoded_packet->data.frame.buf, + encoded_packet->data.frame.sz); + outputBufferInfo->mOwnedByUs = false; + outputBufferInfoQueue.erase(outputBufferInfoQueue.begin()); + if (inputBufferHeader->nFlags & OMX_BUFFERFLAG_EOS) { + outputBufferHeader->nFlags |= OMX_BUFFERFLAG_EOS; + } + notifyFillBufferDone(outputBufferHeader); + } + } + + inputBufferInfo->mOwnedByUs = false; + inputBufferInfoQueue.erase(inputBufferInfoQueue.begin()); + notifyEmptyBufferDone(inputBufferHeader); + } +} + +void SoftVPXEncoder::onReset() { + releaseEncoder(); + mLastTimestamp = 0x7FFFFFFFFFFFFFFFLL; +} + +} // namespace android + +__attribute__((cfi_canonical_jump_table)) +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + if (!strcmp(name, "OMX.google.vp8.encoder")) { + return new android::SoftVP8Encoder(name, callbacks, appData, component); + } else if (!strcmp(name, "OMX.google.vp9.encoder")) { + return new android::SoftVP9Encoder(name, callbacks, appData, component); + } else { + CHECK(!"Unknown component"); + } + return NULL; +} diff --git a/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h new file mode 100644 index 0000000..7208d69 --- /dev/null +++ b/media/libstagefright/codecs/on2/enc/SoftVPXEncoder.h @@ -0,0 +1,251 @@ +/* + * 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 + +#include +#include + +#include "vpx/vpx_encoder.h" +#include "vpx/vpx_codec.h" +#include "vpx/vp8cx.h" + +namespace android { + +// Base class for a VPX Encoder OMX Component +// +// Boilerplate for callback bindings are taken care +// by the base class SimpleSoftOMXComponent and its +// parent SoftOMXComponent. +// +// Only following encoder settings are available (codec specific settings might +// be available in the sub-classes): +// - target bitrate +// - rate control (constant / variable) +// - frame rate +// - error resilience +// - 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 decided by the sub-class in use +// - fractional bits of frame rate is discarded +// - OMX timestamps are in microseconds, therefore +// encoder timebase is fixed to 1/1000000 + +struct SoftVPXEncoder : public SoftVideoEncoderOMXComponent { + SoftVPXEncoder(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component, + const char* role, + OMX_VIDEO_CODINGTYPE codingType, + const char* mimeType, + int32_t minCompressionRatio, + const CodecProfileLevel *profileLevels, + size_t numProfileLevels); + +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); + + virtual OMX_ERRORTYPE internalSetConfig( + OMX_INDEXTYPE index, const OMX_PTR params, bool *frameConfig); + + // 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); + + virtual void onReset(); + + // Initializes vpx encoder with available settings. + status_t initEncoder(); + + // Populates mCodecInterface with codec specific settings. + virtual void setCodecSpecificInterface() = 0; + + // Sets codec specific configuration. + virtual void setCodecSpecificConfiguration() = 0; + + // Sets codec specific encoder controls. + virtual vpx_codec_err_t setCodecSpecificControls() = 0; + + // Get current encode flags. + virtual vpx_enc_frame_flags_t getEncodeFlags(); + + // Releases vpx encoder instance, with it's associated + // data structures. + // + // Unless called earlier, this is handled by the + // dtor. + status_t releaseEncoder(); + + // Get bitrate parameters. + virtual OMX_ERRORTYPE internalGetBitrateParams( + OMX_VIDEO_PARAM_BITRATETYPE* bitrate); + + // Updates bitrate to reflect port settings. + virtual OMX_ERRORTYPE internalSetBitrateParams( + const OMX_VIDEO_PARAM_BITRATETYPE* bitrate); + + // Gets Android vpx specific parameters. + OMX_ERRORTYPE internalGetAndroidVpxParams( + OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE *vpxAndroidParams); + + // Handles Android vpx specific parameters. + OMX_ERRORTYPE internalSetAndroidVpxParams( + const OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE *vpxAndroidParams); + + enum TemporalReferences { + // For 1 layer case: reference all (last, golden, and alt ref), but only + // update last. + kTemporalUpdateLastRefAll = 12, + // First base layer frame for 3 temporal layers, which updates last and + // golden with alt ref dependency. + kTemporalUpdateLastAndGoldenRefAltRef = 11, + // First enhancement layer with alt ref dependency. + kTemporalUpdateGoldenRefAltRef = 10, + // First enhancement layer with alt ref dependency. + kTemporalUpdateGoldenWithoutDependencyRefAltRef = 9, + // Base layer with alt ref dependency. + kTemporalUpdateLastRefAltRef = 8, + // Highest enhacement layer without dependency on golden with alt ref + // dependency. + kTemporalUpdateNoneNoRefGoldenRefAltRef = 7, + // Second layer and last frame in cycle, for 2 layers. + kTemporalUpdateNoneNoRefAltref = 6, + // Highest enhancement layer. + kTemporalUpdateNone = 5, + // Second enhancement layer. + kTemporalUpdateAltref = 4, + // Second enhancement layer without dependency on previous frames in + // the second enhancement layer. + kTemporalUpdateAltrefWithoutDependency = 3, + // First enhancement layer. + kTemporalUpdateGolden = 2, + // First enhancement layer without dependency on previous frames in + // the first enhancement layer. + kTemporalUpdateGoldenWithoutDependency = 1, + // Base layer. + kTemporalUpdateLast = 0, + }; + enum { + kMaxTemporalPattern = 8 + }; + + // 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; + + // 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; + + // If a request for a change it bitrate has been received. + bool mBitrateUpdated; + + // Bitrate control mode, either constant or variable + vpx_rc_mode mBitrateControlMode; + + // Parameter that denotes whether error resilience + // is enabled in encoder + OMX_BOOL mErrorResilience; + + // Key frame interval in frames + uint32_t mKeyFrameInterval; + + // Minimum (best quality) quantizer + uint32_t mMinQuantizer; + + // Maximum (worst quality) quantizer + uint32_t mMaxQuantizer; + + // Number of coding temporal layers to be used. + size_t mTemporalLayers; + + // Temporal layer bitrare ratio in percentage + uint32_t mTemporalLayerBitrateRatio[OMX_VIDEO_ANDROID_MAXVP8TEMPORALLAYERS]; + + // Temporal pattern type + OMX_VIDEO_ANDROID_VPXTEMPORALLAYERPATTERNTYPE mTemporalPatternType; + + // Temporal pattern length + size_t mTemporalPatternLength; + + // Temporal pattern current index + size_t mTemporalPatternIdx; + + // Frame type temporal pattern + TemporalReferences mTemporalPattern[kMaxTemporalPattern]; + + // Last input buffer timestamp + OMX_TICKS mLastTimestamp; + + // 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; + + bool mKeyFrameRequested; + + DISALLOW_EVIL_CONSTRUCTORS(SoftVPXEncoder); +}; + +} // namespace android + +#endif // SOFT_VPX_ENCODER_H_ diff --git a/media/libstagefright/codecs/on2/enc/exports.lds b/media/libstagefright/codecs/on2/enc/exports.lds new file mode 100644 index 0000000..e24f3fa --- /dev/null +++ b/media/libstagefright/codecs/on2/enc/exports.lds @@ -0,0 +1,5 @@ +{ + global: + _Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE; + local: *; +}; diff --git a/media/libstagefright/codecs/opus/dec/Android.bp b/media/libstagefright/codecs/opus/dec/Android.bp new file mode 100644 index 0000000..3d8af69 --- /dev/null +++ b/media/libstagefright/codecs/opus/dec/Android.bp @@ -0,0 +1,29 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_av_media_libstagefright_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_av_media_libstagefright_license"], +} + +cc_library_shared { + name: "libstagefright_soft_opusdec", + defaults: ["libstagefright_softomx-defaults"], + + srcs: ["SoftOpus.cpp"], + + shared_libs: [ + "libopus", + ], + + version_script: "exports.lds", + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + "unsigned-integer-overflow", + ], + cfi: true, + }, +} diff --git a/media/libstagefright/codecs/opus/dec/SoftOpus.cpp b/media/libstagefright/codecs/opus/dec/SoftOpus.cpp new file mode 100644 index 0000000..dcd8dda --- /dev/null +++ b/media/libstagefright/codecs/opus/dec/SoftOpus.cpp @@ -0,0 +1,674 @@ +/* + * Copyright (C) 2014 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 "SoftOpus" +#include + +#include "SoftOpus.h" +#include +#include + +#include +#include + +extern "C" { + #include + #include +} + +namespace android { + +static const int kRate = 48000; + +// Opus uses Vorbis channel mapping, and Vorbis channel mapping specifies +// mappings for up to 8 channels. This information is part of the Vorbis I +// Specification: +// http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html +static const int kMaxChannels = 8; + +template +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +SoftOpus::SoftOpus( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mInputBufferCount(0), + mDecoder(NULL), + mHeader(NULL), + mNumChannels(1), + mSamplingRate(kRate), + mCodecDelay(0), + mSeekPreRoll(0), + mAnchorTimeUs(0), + mNumFramesOutput(0), + mHaveEOS(false), + mOutputPortSettingsChange(NONE) { + initPorts(); + CHECK_EQ(initDecoder(), (status_t)OK); +} + +SoftOpus::~SoftOpus() { + if (mDecoder != NULL) { + opus_multistream_decoder_destroy(mDecoder); + mDecoder = NULL; + } + if (mHeader != NULL) { + delete mHeader; + mHeader = NULL; + } +} + +void SoftOpus::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 960 * 6; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.audio.cMIMEType = + const_cast(MEDIA_MIMETYPE_AUDIO_OPUS); + + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = + (OMX_AUDIO_CODINGTYPE)OMX_AUDIO_CodingAndroidOPUS; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = kMaxNumSamplesPerBuffer * sizeof(int16_t) * kMaxChannels; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.audio.cMIMEType = const_cast("audio/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); +} + +status_t SoftOpus::initDecoder() { + return OK; +} + +OMX_ERRORTYPE SoftOpus::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch ((int)index) { + case OMX_IndexParamAudioPortFormat: + { + OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex > 0) { + return OMX_ErrorNoMore; + } + + formatParams->eEncoding = + (formatParams->nPortIndex == 0) + ? (OMX_AUDIO_CODINGTYPE)OMX_AUDIO_CodingAndroidOPUS : + OMX_AUDIO_CodingPCM; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAndroidOpus: + { + OMX_AUDIO_PARAM_ANDROID_OPUSTYPE *opusParams = + (OMX_AUDIO_PARAM_ANDROID_OPUSTYPE *)params; + + if (!isValidOMXParam(opusParams)) { + return OMX_ErrorBadParameter; + } + + if (opusParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + opusParams->nAudioBandWidth = 0; + opusParams->nSampleRate = mSamplingRate; + opusParams->nBitRate = 0; + + if (!isConfigured()) { + opusParams->nChannels = mNumChannels; + } else { + opusParams->nChannels = mHeader->channels; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + pcmParams->eNumData = OMX_NumericalDataSigned; + pcmParams->eEndian = OMX_EndianBig; + pcmParams->bInterleaved = OMX_TRUE; + pcmParams->nBitPerSample = 16; + pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; + pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; + pcmParams->nSamplingRate = kRate; + + if (!isConfigured()) { + pcmParams->nChannels = 1; + } else { + pcmParams->nChannels = mHeader->channels; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftOpus::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch ((int)index) { + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (!isValidOMXParam(roleParams)) { + return OMX_ErrorBadParameter; + } + + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.opus", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPortFormat: + { + const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if ((formatParams->nPortIndex == 0 + && formatParams->eEncoding != + (OMX_AUDIO_CODINGTYPE)OMX_AUDIO_CodingAndroidOPUS) + || (formatParams->nPortIndex == 1 + && formatParams->eEncoding != OMX_AUDIO_CodingPCM)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAndroidOpus: + { + const OMX_AUDIO_PARAM_ANDROID_OPUSTYPE *opusParams = + (const OMX_AUDIO_PARAM_ANDROID_OPUSTYPE *)params; + + if (!isValidOMXParam(opusParams)) { + return OMX_ErrorBadParameter; + } + + if (opusParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + mNumChannels = opusParams->nChannels; + mSamplingRate = opusParams->nSampleRate; + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +bool SoftOpus::isConfigured() const { + return mInputBufferCount >= 1; +} + +static uint16_t ReadLE16(const uint8_t *data, size_t data_size, + uint32_t read_offset) { + if (read_offset + 1 > data_size) + return 0; + uint16_t val; + val = data[read_offset]; + val |= data[read_offset + 1] << 8; + return val; +} + +// Maximum packet size used in Xiph's opusdec. +static const int kMaxOpusOutputPacketSizeSamples = 960 * 6; + +// Default audio output channel layout. Used to initialize |stream_map| in +// OpusHeader, and passed to opus_multistream_decoder_create() when the header +// does not contain mapping information. The values are valid only for mono and +// stereo output: Opus streams with more than 2 channels require a stream map. +static const int kMaxChannelsWithDefaultLayout = 2; +static const uint8_t kDefaultOpusChannelLayout[kMaxChannelsWithDefaultLayout] = { 0, 1 }; + +// Parses Opus Header. Header spec: http://wiki.xiph.org/OggOpus#ID_Header +static bool ParseOpusHeader(const uint8_t *data, size_t data_size, + OpusHeader* header) { + // Size of the Opus header excluding optional mapping information. + const size_t kOpusHeaderSize = 19; + + // Offset to the channel count byte in the Opus header. + const size_t kOpusHeaderChannelsOffset = 9; + + // Offset to the pre-skip value in the Opus header. + const size_t kOpusHeaderSkipSamplesOffset = 10; + + // Offset to the gain value in the Opus header. + const size_t kOpusHeaderGainOffset = 16; + + // Offset to the channel mapping byte in the Opus header. + const size_t kOpusHeaderChannelMappingOffset = 18; + + // Opus Header contains a stream map. The mapping values are in the header + // beyond the always present |kOpusHeaderSize| bytes of data. The mapping + // data contains stream count, coupling information, and per channel mapping + // values: + // - Byte 0: Number of streams. + // - Byte 1: Number coupled. + // - Byte 2: Starting at byte 2 are |header->channels| uint8 mapping + // values. + const size_t kOpusHeaderNumStreamsOffset = kOpusHeaderSize; + const size_t kOpusHeaderNumCoupledOffset = kOpusHeaderNumStreamsOffset + 1; + const size_t kOpusHeaderStreamMapOffset = kOpusHeaderNumStreamsOffset + 2; + + if (data_size < kOpusHeaderSize) { + ALOGV("Header size is too small."); + return false; + } + header->channels = *(data + kOpusHeaderChannelsOffset); + + if (header->channels <= 0 || header->channels > kMaxChannels) { + ALOGV("Invalid Header, wrong channel count: %d", header->channels); + return false; + } + header->skip_samples = ReadLE16(data, data_size, + kOpusHeaderSkipSamplesOffset); + header->gain_db = static_cast( + ReadLE16(data, data_size, + kOpusHeaderGainOffset)); + header->channel_mapping = *(data + kOpusHeaderChannelMappingOffset); + if (!header->channel_mapping) { + if (header->channels > kMaxChannelsWithDefaultLayout) { + ALOGV("Invalid Header, missing stream map."); + return false; + } + header->num_streams = 1; + header->num_coupled = header->channels > 1; + header->stream_map[0] = 0; + header->stream_map[1] = 1; + return true; + } + if (data_size < kOpusHeaderStreamMapOffset + header->channels) { + ALOGV("Invalid stream map; insufficient data for current channel " + "count: %d", header->channels); + return false; + } + header->num_streams = *(data + kOpusHeaderNumStreamsOffset); + header->num_coupled = *(data + kOpusHeaderNumCoupledOffset); + if (header->num_streams + header->num_coupled != header->channels) { + ALOGV("Inconsistent channel mapping."); + return false; + } + for (int i = 0; i < header->channels; ++i) + header->stream_map[i] = *(data + kOpusHeaderStreamMapOffset + i); + return true; +} + +// Convert nanoseconds to number of samples. +static uint64_t ns_to_samples(uint64_t ns, int kRate) { + return static_cast(ns) * kRate / 1000000000; +} + +void SoftOpus::handleEOS() { + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + CHECK(!inQueue.empty() && !outQueue.empty()); + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + mHaveEOS = true; + + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + ++mInputBufferCount; +} + +void SoftOpus::onQueueFilled(OMX_U32 /* portIndex */) { + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + if (mOutputPortSettingsChange != NONE) { + return; + } + + while (!mHaveEOS && !inQueue.empty() && !outQueue.empty()) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + if (mInputBufferCount < 3) { + const uint8_t *data = inHeader->pBuffer + inHeader->nOffset; + size_t size = inHeader->nFilledLen; + + if ((inHeader->nFlags & OMX_BUFFERFLAG_EOS) && size == 0) { + handleEOS(); + return; + } + + if (size < sizeof(int64_t)) { + // The 2nd and 3rd input buffer are expected to contain + // an int64_t (see below), so make sure we get at least + // that much. The first input buffer must contain 19 bytes, + // but that is checked already. + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + + if (mInputBufferCount == 0) { + delete mHeader; + mHeader = new OpusHeader(); + memset(mHeader, 0, sizeof(*mHeader)); + if (!ParseOpusHeader(data, size, mHeader)) { + ALOGV("Parsing Opus Header failed."); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + + uint8_t channel_mapping[kMaxChannels] = {0}; + if (mHeader->channels <= kMaxChannelsWithDefaultLayout) { + memcpy(&channel_mapping, + kDefaultOpusChannelLayout, + kMaxChannelsWithDefaultLayout); + } else { + memcpy(&channel_mapping, + mHeader->stream_map, + mHeader->channels); + } + + int status = OPUS_INVALID_STATE; + if (mDecoder != NULL) { + opus_multistream_decoder_destroy(mDecoder); + } + mDecoder = opus_multistream_decoder_create(kRate, + mHeader->channels, + mHeader->num_streams, + mHeader->num_coupled, + channel_mapping, + &status); + if (!mDecoder || status != OPUS_OK) { + ALOGV("opus_multistream_decoder_create failed status=%s", + opus_strerror(status)); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + status = + opus_multistream_decoder_ctl(mDecoder, + OPUS_SET_GAIN(mHeader->gain_db)); + if (status != OPUS_OK) { + ALOGV("Failed to set OPUS header gain; status=%s", + opus_strerror(status)); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + } else if (mInputBufferCount == 1) { + mCodecDelay = ns_to_samples( + *(reinterpret_cast(inHeader->pBuffer + + inHeader->nOffset)), + kRate); + mSamplesToDiscard = mCodecDelay; + } else { + mSeekPreRoll = ns_to_samples( + *(reinterpret_cast(inHeader->pBuffer + + inHeader->nOffset)), + kRate); + mSamplingRate = kRate; + mNumChannels = mHeader->channels; + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + } + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + handleEOS(); + return; + } + + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + ++mInputBufferCount; + + continue; + } + + // Ignore CSD re-submissions. + if (mInputBufferCount >= 3 && (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG)) { + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + handleEOS(); + return; + } + + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + continue; + } + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + if ((inHeader->nFlags & OMX_BUFFERFLAG_EOS) && inHeader->nFilledLen == 0) { + handleEOS(); + return; + } + + if (inHeader->nOffset == 0) { + mAnchorTimeUs = inHeader->nTimeStamp; + mNumFramesOutput = 0; + } + + // When seeking to zero, |mCodecDelay| samples has to be discarded + // instead of |mSeekPreRoll| samples (as we would when seeking to any + // other timestamp). + if (inHeader->nTimeStamp == 0) { + mSamplesToDiscard = mCodecDelay; + } + + const uint8_t *data = inHeader->pBuffer + inHeader->nOffset; + const uint32_t size = inHeader->nFilledLen; + size_t frameSize = kMaxOpusOutputPacketSizeSamples; + if (frameSize > outHeader->nAllocLen / sizeof(int16_t) / mHeader->channels) { + frameSize = outHeader->nAllocLen / sizeof(int16_t) / mHeader->channels; + android_errorWriteLog(0x534e4554, "27833616"); + } + + int numFrames = opus_multistream_decode(mDecoder, + data, + size, + (int16_t *)outHeader->pBuffer, + frameSize, + 0); + if (numFrames < 0) { + ALOGE("opus_multistream_decode returned %d", numFrames); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + + outHeader->nOffset = 0; + if (mSamplesToDiscard > 0) { + if (mSamplesToDiscard > numFrames) { + mSamplesToDiscard -= numFrames; + numFrames = 0; + } else { + numFrames -= mSamplesToDiscard; + outHeader->nOffset = mSamplesToDiscard * sizeof(int16_t) * + mHeader->channels; + mSamplesToDiscard = 0; + } + } + + outHeader->nFilledLen = numFrames * sizeof(int16_t) * mHeader->channels; + + outHeader->nTimeStamp = mAnchorTimeUs + + (mNumFramesOutput * 1000000LL) / + kRate; + + mNumFramesOutput += numFrames; + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + mHaveEOS = true; + } else { + outHeader->nFlags = 0; + } + + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + notifyEmptyBufferDone(inHeader); + ++mInputBufferCount; + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + notifyFillBufferDone(outHeader); + } +} + +void SoftOpus::onPortFlushCompleted(OMX_U32 portIndex) { + if (portIndex == 0 && mDecoder != NULL) { + // Make sure that the next buffer output does not still + // depend on fragments from the last one decoded. + mNumFramesOutput = 0; + opus_multistream_decoder_ctl(mDecoder, OPUS_RESET_STATE); + mAnchorTimeUs = 0; + mSamplesToDiscard = mSeekPreRoll; + mHaveEOS = false; + } +} + +void SoftOpus::onReset() { + mInputBufferCount = 0; + mNumFramesOutput = 0; + if (mDecoder != NULL) { + opus_multistream_decoder_destroy(mDecoder); + mDecoder = NULL; + } + if (mHeader != NULL) { + delete mHeader; + mHeader = NULL; + } + + mOutputPortSettingsChange = NONE; + mHaveEOS = false; +} + +void SoftOpus::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { + if (portIndex != 1) { + return; + } + + switch (mOutputPortSettingsChange) { + case NONE: + break; + + case AWAITING_DISABLED: + { + CHECK(!enabled); + mOutputPortSettingsChange = AWAITING_ENABLED; + break; + } + + default: + { + CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); + CHECK(enabled); + mOutputPortSettingsChange = NONE; + break; + } + } +} + +} // namespace android + +__attribute__((cfi_canonical_jump_table)) +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftOpus(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/opus/dec/SoftOpus.h b/media/libstagefright/codecs/opus/dec/SoftOpus.h new file mode 100644 index 0000000..00058c8 --- /dev/null +++ b/media/libstagefright/codecs/opus/dec/SoftOpus.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2014 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. + */ + +/* + * The Opus specification is part of IETF RFC 6716: + * http://tools.ietf.org/html/rfc6716 + */ + +#ifndef SOFT_OPUS_H_ + +#define SOFT_OPUS_H_ + +#include + +struct OpusMSDecoder; + +namespace android { + +struct OpusHeader { + int channels; + int skip_samples; + int channel_mapping; + int num_streams; + int num_coupled; + int16_t gain_db; + uint8_t stream_map[8]; +}; + +struct SoftOpus : public SimpleSoftOMXComponent { + SoftOpus(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftOpus(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + virtual void onReset(); + +private: + enum { + kNumBuffers = 4, + kMaxNumSamplesPerBuffer = 960 * 6 + }; + + size_t mInputBufferCount; + + OpusMSDecoder *mDecoder; + OpusHeader *mHeader; + + int32_t mNumChannels; + int32_t mSamplingRate; + int64_t mCodecDelay; + int64_t mSeekPreRoll; + int64_t mSamplesToDiscard; + int64_t mAnchorTimeUs; + int64_t mNumFramesOutput; + bool mHaveEOS; + + enum { + NONE, + AWAITING_DISABLED, + AWAITING_ENABLED + } mOutputPortSettingsChange; + + void initPorts(); + status_t initDecoder(); + bool isConfigured() const; + void handleEOS(); + + DISALLOW_EVIL_CONSTRUCTORS(SoftOpus); +}; + +} // namespace android + +#endif // SOFT_OPUS_H_ diff --git a/media/libstagefright/codecs/opus/dec/exports.lds b/media/libstagefright/codecs/opus/dec/exports.lds new file mode 100644 index 0000000..e24f3fa --- /dev/null +++ b/media/libstagefright/codecs/opus/dec/exports.lds @@ -0,0 +1,5 @@ +{ + global: + _Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE; + local: *; +}; diff --git a/media/libstagefright/codecs/raw/Android.bp b/media/libstagefright/codecs/raw/Android.bp new file mode 100644 index 0000000..3673786 --- /dev/null +++ b/media/libstagefright/codecs/raw/Android.bp @@ -0,0 +1,35 @@ +package { + default_applicable_licenses: [ + "frameworks_av_media_libstagefright_codecs_raw_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_av_media_libstagefright_codecs_raw_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + +cc_library_shared { + name: "libstagefright_soft_rawdec", + defaults: ["libstagefright_softomx-defaults"], + + srcs: ["SoftRaw.cpp"], + + version_script: "exports.lds", + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + "unsigned-integer-overflow", + ], + cfi: true, + }, +} diff --git a/media/libstagefright/codecs/raw/MODULE_LICENSE_APACHE2 b/media/libstagefright/codecs/raw/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/media/libstagefright/codecs/raw/NOTICE b/media/libstagefright/codecs/raw/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/media/libstagefright/codecs/raw/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, 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/raw/SoftRaw.cpp b/media/libstagefright/codecs/raw/SoftRaw.cpp new file mode 100644 index 0000000..82dd171 --- /dev/null +++ b/media/libstagefright/codecs/raw/SoftRaw.cpp @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2012 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 "SoftRaw" +#include + +#include "SoftRaw.h" + +#include +#include + +namespace android { + +template +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +SoftRaw::SoftRaw( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mSignalledError(false), + mChannelCount(2), + mSampleRate(44100), + mNumericalData(OMX_NumericalDataSigned), + mBitsPerSample(16) { + initPorts(); + CHECK_EQ(initDecoder(), (status_t)OK); +} + +SoftRaw::~SoftRaw() { +} + +void SoftRaw::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 192 * 1024; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.audio.cMIMEType = const_cast("audio/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 192 * 1024; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.audio.cMIMEType = const_cast("audio/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); +} + +status_t SoftRaw::initDecoder() { + return OK; +} + +OMX_ERRORTYPE SoftRaw::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioPortFormat: + { + OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex > 0) { + return OMX_ErrorNoMore; + } + + formatParams->eEncoding = OMX_AUDIO_CodingPCM; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 0 && pcmParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + pcmParams->eNumData = (OMX_NUMERICALDATATYPE)mNumericalData; + pcmParams->eEndian = OMX_EndianBig; + pcmParams->bInterleaved = OMX_TRUE; + pcmParams->nBitPerSample = mBitsPerSample; + pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; + pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; + + pcmParams->nChannels = mChannelCount; + pcmParams->nSamplingRate = mSampleRate; + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftRaw::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (!isValidOMXParam(roleParams)) { + return OMX_ErrorBadParameter; + } + + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.raw", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPortFormat: + { + const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->eEncoding != OMX_AUDIO_CodingPCM) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + const OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + mChannelCount = pcmParams->nChannels; + mSampleRate = pcmParams->nSamplingRate; + mNumericalData = pcmParams->eNumData; + mBitsPerSample = pcmParams->nBitPerSample; + + return OMX_ErrorNone; + } + + default: + { + OMX_ERRORTYPE err = SimpleSoftOMXComponent::internalSetParameter( + index, params); + // In case inPort->mDef.nBufferSize changed, the output buffer size + // should match the input buffer size. + PortInfo *inPort = editPortInfo(0); + PortInfo *outPort = editPortInfo(1); + outPort->mDef.nBufferSize = inPort->mDef.nBufferSize; + return err; + } + } +} + +void SoftRaw::onQueueFilled(OMX_U32 /* portIndex */) { + if (mSignalledError) { + return; + } + + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + while (!inQueue.empty() && !outQueue.empty()) { + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + CHECK_GE(outHeader->nAllocLen, inHeader->nFilledLen); + memcpy(outHeader->pBuffer, + inHeader->pBuffer + inHeader->nOffset, + inHeader->nFilledLen); + + outHeader->nFlags = inHeader->nFlags; + outHeader->nOffset = 0; + outHeader->nFilledLen = inHeader->nFilledLen; + outHeader->nTimeStamp = inHeader->nTimeStamp; + + bool sawEOS = (inHeader->nFlags & OMX_BUFFERFLAG_EOS) != 0; + + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + + if (sawEOS) { + break; + } + } +} + +} // namespace android + +__attribute__((cfi_canonical_jump_table)) +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftRaw(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/raw/SoftRaw.h b/media/libstagefright/codecs/raw/SoftRaw.h new file mode 100644 index 0000000..ebc2741 --- /dev/null +++ b/media/libstagefright/codecs/raw/SoftRaw.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2012 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_RAW_H_ + +#define SOFT_RAW_H_ + +#include + +struct tPVMP4AudioDecoderExternal; + +namespace android { + +struct SoftRaw : public SimpleSoftOMXComponent { + SoftRaw(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftRaw(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + +private: + enum { + kNumBuffers = 4 + }; + + bool mSignalledError; + + int32_t mChannelCount; + int32_t mSampleRate; + int32_t mNumericalData; + int32_t mBitsPerSample; + + void initPorts(); + status_t initDecoder(); + + DISALLOW_EVIL_CONSTRUCTORS(SoftRaw); +}; + +} // namespace android + +#endif // SOFT_RAW_H_ diff --git a/media/libstagefright/codecs/raw/exports.lds b/media/libstagefright/codecs/raw/exports.lds new file mode 100644 index 0000000..e24f3fa --- /dev/null +++ b/media/libstagefright/codecs/raw/exports.lds @@ -0,0 +1,5 @@ +{ + global: + _Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE; + local: *; +}; diff --git a/media/libstagefright/codecs/vorbis/dec/Android.bp b/media/libstagefright/codecs/vorbis/dec/Android.bp new file mode 100644 index 0000000..7764294 --- /dev/null +++ b/media/libstagefright/codecs/vorbis/dec/Android.bp @@ -0,0 +1,38 @@ +package { + default_applicable_licenses: [ + "frameworks_av_media_libstagefright_codecs_vorbis_dec_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_av_media_libstagefright_codecs_vorbis_dec_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + +cc_library_shared { + name: "libstagefright_soft_vorbisdec", + defaults: ["libstagefright_softomx-defaults"], + + srcs: ["SoftVorbis.cpp"], + + shared_libs: [ + "libvorbisidec", + ], + + version_script: "exports.lds", + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + "unsigned-integer-overflow", + ], + }, +} diff --git a/media/libstagefright/codecs/vorbis/dec/MODULE_LICENSE_APACHE2 b/media/libstagefright/codecs/vorbis/dec/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/media/libstagefright/codecs/vorbis/dec/NOTICE b/media/libstagefright/codecs/vorbis/dec/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/media/libstagefright/codecs/vorbis/dec/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, 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/vorbis/dec/SoftVorbis.cpp b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp new file mode 100644 index 0000000..3daed10 --- /dev/null +++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp @@ -0,0 +1,644 @@ +/* + * Copyright (C) 2011 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 "SoftVorbis" +#include + +#include "SoftVorbis.h" + +#include +#include + +static int kDefaultChannelCount = 1; +static int kDefaultSamplingRate = 48000; + +extern "C" { + #include + + int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb); + int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb); + int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb); +} + +namespace android { + +template +static void InitOMXParams(T *params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +SoftVorbis::SoftVorbis( + const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mInputBufferCount(0), + mState(NULL), + mVi(NULL), + mAnchorTimeUs(0), + mNumFramesOutput(0), + mNumFramesLeftOnPage(-1), + mSawInputEos(false), + mSignalledOutputEos(false), + mSignalledError(false), + mOutputPortSettingsChange(NONE) { + initPorts(); + CHECK_EQ(initDecoder(), (status_t)OK); +} + +SoftVorbis::~SoftVorbis() { + if (mState != NULL) { + vorbis_dsp_clear(mState); + delete mState; + mState = NULL; + } + + if (mVi != NULL) { + vorbis_info_clear(mVi); + delete mVi; + mVi = NULL; + } +} + +void SoftVorbis::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = kMaxNumSamplesPerBuffer * sizeof(int16_t); + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.audio.cMIMEType = + const_cast(MEDIA_MIMETYPE_AUDIO_VORBIS); + + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingVORBIS; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = kMaxNumSamplesPerBuffer * sizeof(int16_t); + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.audio.cMIMEType = const_cast("audio/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); +} + +status_t SoftVorbis::initDecoder() { + return OK; +} + +OMX_ERRORTYPE SoftVorbis::internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params) { + switch (index) { + case OMX_IndexParamAudioPortFormat: + { + OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex > 0) { + return OMX_ErrorNoMore; + } + + formatParams->eEncoding = + (formatParams->nPortIndex == 0) + ? OMX_AUDIO_CodingVORBIS : OMX_AUDIO_CodingPCM; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioVorbis: + { + OMX_AUDIO_PARAM_VORBISTYPE *vorbisParams = + (OMX_AUDIO_PARAM_VORBISTYPE *)params; + + if (!isValidOMXParam(vorbisParams)) { + return OMX_ErrorBadParameter; + } + + if (vorbisParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + vorbisParams->nBitRate = 0; + vorbisParams->nMinBitRate = 0; + vorbisParams->nMaxBitRate = 0; + vorbisParams->nAudioBandWidth = 0; + vorbisParams->nQuality = 3; + vorbisParams->bManaged = OMX_FALSE; + vorbisParams->bDownmix = OMX_FALSE; + + if (!isConfigured()) { + vorbisParams->nChannels = kDefaultChannelCount; + vorbisParams->nSampleRate = kDefaultSamplingRate; + } else { + vorbisParams->nChannels = mVi->channels; + vorbisParams->nSampleRate = mVi->rate; + vorbisParams->nBitRate = mVi->bitrate_nominal; + vorbisParams->nMinBitRate = mVi->bitrate_lower; + vorbisParams->nMaxBitRate = mVi->bitrate_upper; + } + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: + { + OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + pcmParams->eNumData = OMX_NumericalDataSigned; + pcmParams->eEndian = OMX_EndianBig; + pcmParams->bInterleaved = OMX_TRUE; + pcmParams->nBitPerSample = 16; + pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; + pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; + + if (!isConfigured()) { + pcmParams->nChannels = kDefaultChannelCount; + pcmParams->nSamplingRate = kDefaultSamplingRate; + } else { + pcmParams->nChannels = mVi->channels; + pcmParams->nSamplingRate = mVi->rate; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftVorbis::internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params) { + switch (index) { + case OMX_IndexParamStandardComponentRole: + { + const OMX_PARAM_COMPONENTROLETYPE *roleParams = + (const OMX_PARAM_COMPONENTROLETYPE *)params; + + if (!isValidOMXParam(roleParams)) { + return OMX_ErrorBadParameter; + } + + if (strncmp((const char *)roleParams->cRole, + "audio_decoder.vorbis", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPortFormat: + { + const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = + (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if ((formatParams->nPortIndex == 0 + && formatParams->eEncoding != OMX_AUDIO_CodingVORBIS) + || (formatParams->nPortIndex == 1 + && formatParams->eEncoding != OMX_AUDIO_CodingPCM)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioVorbis: + { + const OMX_AUDIO_PARAM_VORBISTYPE *vorbisParams = + (const OMX_AUDIO_PARAM_VORBISTYPE *)params; + + if (!isValidOMXParam(vorbisParams)) { + return OMX_ErrorBadParameter; + } + + if (vorbisParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +bool SoftVorbis::isConfigured() const { + return (mState != NULL && mVi != NULL); +} + +static void makeBitReader( + const void *data, size_t size, + ogg_buffer *buf, ogg_reference *ref, oggpack_buffer *bits) { + buf->data = (uint8_t *)data; + buf->size = size; + buf->refcount = 1; + buf->ptr.owner = NULL; + + ref->buffer = buf; + ref->begin = 0; + ref->length = size; + ref->next = NULL; + + oggpack_readinit(bits, ref); +} + +void SoftVorbis::handleEOS() { + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + CHECK(!inQueue.empty() && !outQueue.empty()); + + mSawInputEos = true; + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + + outQueue.erase(outQueue.begin()); + outInfo->mOwnedByUs = false; + notifyFillBufferDone(outHeader); + mSignalledOutputEos = true; + + BufferInfo *inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + ++mInputBufferCount; +} + +void SoftVorbis::onQueueFilled(OMX_U32 /* portIndex */) { + List &inQueue = getPortQueue(0); + List &outQueue = getPortQueue(1); + + if (mSignalledError || mOutputPortSettingsChange != NONE) { + return; + } + + while (!mSignalledOutputEos && (!inQueue.empty() || mSawInputEos) && !outQueue.empty()) { + BufferInfo *inInfo = NULL; + OMX_BUFFERHEADERTYPE *inHeader = NULL; + if (!inQueue.empty()) { + inInfo = *inQueue.begin(); + inHeader = inInfo->mHeader; + } + + BufferInfo *outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; + + int32_t numPageSamples = 0; + + if (inHeader) { + // Assume the very first 2 buffers are always codec config (in this case mState is NULL) + // After flush, handle CSD + if (mInputBufferCount < 2 && + (mState == NULL || (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG))) { + const uint8_t *data = inHeader->pBuffer + inHeader->nOffset; + size_t size = inHeader->nFilledLen; + + if ((inHeader->nFlags & OMX_BUFFERFLAG_EOS) && size == 0) { + handleEOS(); + return; + } + + if (size < 7) { + ALOGE("Too small input buffer: %zu bytes", size); + android_errorWriteLog(0x534e4554, "27833616"); + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + mSignalledError = true; + return; + } + + ogg_buffer buf; + ogg_reference ref; + oggpack_buffer bits; + + makeBitReader((const uint8_t *)data + 7, size - 7, &buf, &ref, &bits); + + // Assume very first frame is identification header - or reset identification + // header after flush, but allow only specifying setup header after flush if + // identification header was already set up. + if (mInputBufferCount == 0 && + (mVi == NULL || data[0] == 1 /* identification header */)) { + // remove any prior state + if (mVi != NULL) { + // also clear mState as it may refer to the old mVi + if (mState != NULL) { + vorbis_dsp_clear(mState); + delete mState; + mState = NULL; + } + vorbis_info_clear(mVi); + delete mVi; + mVi = NULL; + } + + CHECK(mVi == NULL); + mVi = new vorbis_info; + vorbis_info_init(mVi); + + int ret = _vorbis_unpack_info(mVi, &bits); + if (ret != 0) { + notify(OMX_EventError, OMX_ErrorUndefined, ret, NULL); + mSignalledError = true; + return; + } + } else { + // remove any prior state + if (mState != NULL) { + vorbis_dsp_clear(mState); + delete mState; + mState = NULL; + } + + int ret = _vorbis_unpack_books(mVi, &bits); + if (ret != 0 || mState != NULL) { + notify(OMX_EventError, OMX_ErrorUndefined, ret, NULL); + mSignalledError = true; + return; + } + + CHECK(mState == NULL); + mState = new vorbis_dsp_state; + CHECK_EQ(0, vorbis_dsp_init(mState, mVi)); + + if (mVi->rate != kDefaultSamplingRate || + mVi->channels != kDefaultChannelCount) { + ALOGV("vorbis: rate/channels changed: %ld/%d", mVi->rate, mVi->channels); + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + } + mInputBufferCount = 1; + } + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + handleEOS(); + return; + } + + inQueue.erase(inQueue.begin()); + inInfo->mOwnedByUs = false; + notifyEmptyBufferDone(inHeader); + ++mInputBufferCount; + + continue; + } + + if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { + mSawInputEos = true; + } + + if (inHeader->nFilledLen || !mSawInputEos) { + if (inHeader->nFilledLen < sizeof(numPageSamples)) { + notify(OMX_EventError, OMX_ErrorBadParameter, 0, NULL); + mSignalledError = true; + ALOGE("onQueueFilled, input header has nFilledLen %u, expected %zu", + inHeader->nFilledLen, sizeof(numPageSamples)); + return; + } + memcpy(&numPageSamples, + inHeader->pBuffer + inHeader->nOffset + inHeader->nFilledLen - 4, + sizeof(numPageSamples)); + + if (inHeader->nOffset == 0) { + mAnchorTimeUs = inHeader->nTimeStamp; + mNumFramesOutput = 0; + } + + inHeader->nFilledLen -= sizeof(numPageSamples);; + } + } + + if (numPageSamples >= 0) { + mNumFramesLeftOnPage = numPageSamples; + } + + ogg_buffer buf; + buf.data = inHeader ? inHeader->pBuffer + inHeader->nOffset : NULL; + buf.size = inHeader ? inHeader->nFilledLen : 0; + buf.refcount = 1; + buf.ptr.owner = NULL; + + ogg_reference ref; + ref.buffer = &buf; + ref.begin = 0; + ref.length = buf.size; + ref.next = NULL; + + ogg_packet pack; + pack.packet = &ref; + pack.bytes = ref.length; + pack.b_o_s = 0; + pack.e_o_s = 0; + pack.granulepos = 0; + pack.packetno = 0; + + int numFrames = 0; + + outHeader->nFlags = 0; + + if (mState == nullptr || mVi == nullptr) { + notify(OMX_EventError, OMX_ErrorStreamCorrupt, 0, NULL); + mSignalledError = true; + ALOGE("onQueueFilled, input does not have CSD"); + return; + } + + int err = vorbis_dsp_synthesis(mState, &pack, 1); + if (err != 0) { + // FIXME temporary workaround for log spam +#if !defined(__arm__) && !defined(__aarch64__) + ALOGV("vorbis_dsp_synthesis returned %d", err); +#else + ALOGW("vorbis_dsp_synthesis returned %d", err); +#endif + } else { + size_t numSamplesPerBuffer = kMaxNumSamplesPerBuffer; + if (numSamplesPerBuffer > outHeader->nAllocLen / sizeof(int16_t)) { + numSamplesPerBuffer = outHeader->nAllocLen / sizeof(int16_t); + android_errorWriteLog(0x534e4554, "27833616"); + } + numFrames = vorbis_dsp_pcmout( + mState, (int16_t *)outHeader->pBuffer, + (numSamplesPerBuffer / mVi->channels)); + + if (numFrames < 0) { + ALOGE("vorbis_dsp_pcmout returned %d", numFrames); + numFrames = 0; + } + } + + if (mNumFramesLeftOnPage >= 0) { + if (numFrames > mNumFramesLeftOnPage) { + ALOGV("discarding %d frames at end of page", + numFrames - mNumFramesLeftOnPage); + numFrames = mNumFramesLeftOnPage; + if (mSawInputEos) { + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + mSignalledOutputEos = true; + } + } + mNumFramesLeftOnPage -= numFrames; + } + + outHeader->nFilledLen = numFrames * sizeof(int16_t) * mVi->channels; + outHeader->nOffset = 0; + + outHeader->nTimeStamp = + mAnchorTimeUs + + (mNumFramesOutput * 1000000LL) / mVi->rate; + + mNumFramesOutput += numFrames; + + if (inHeader) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + notifyEmptyBufferDone(inHeader); + ++mInputBufferCount; + } + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + notifyFillBufferDone(outHeader); + } +} + +void SoftVorbis::onPortFlushCompleted(OMX_U32 portIndex) { + if (portIndex == 0) { + mInputBufferCount = 0; + mNumFramesOutput = 0; + mSawInputEos = false; + mSignalledOutputEos = false; + mNumFramesLeftOnPage = -1; + if (mState != NULL) { + // Make sure that the next buffer output does not still + // depend on fragments from the last one decoded. + vorbis_dsp_restart(mState); + } + } +} + +void SoftVorbis::onReset() { + mInputBufferCount = 0; + mNumFramesOutput = 0; + if (mState != NULL) { + vorbis_dsp_clear(mState); + delete mState; + mState = NULL; + } + + if (mVi != NULL) { + vorbis_info_clear(mVi); + delete mVi; + mVi = NULL; + } + + mSawInputEos = false; + mSignalledOutputEos = false; + mSignalledError = false; + mNumFramesLeftOnPage = -1; + mOutputPortSettingsChange = NONE; +} + +void SoftVorbis::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { + if (portIndex != 1) { + return; + } + + switch (mOutputPortSettingsChange) { + case NONE: + break; + + case AWAITING_DISABLED: + { + CHECK(!enabled); + mOutputPortSettingsChange = AWAITING_ENABLED; + break; + } + + default: + { + CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); + CHECK(enabled); + mOutputPortSettingsChange = NONE; + break; + } + } +} + +} // namespace android + +__attribute__((cfi_canonical_jump_table)) +android::SoftOMXComponent *createSoftOMXComponent( + const char *name, const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE **component) { + return new android::SoftVorbis(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h new file mode 100644 index 0000000..5ff8ea4 --- /dev/null +++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2011 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_VORBIS_H_ + +#define SOFT_VORBIS_H_ + +#include + +struct vorbis_dsp_state; +struct vorbis_info; + +namespace android { + +struct SoftVorbis : public SimpleSoftOMXComponent { + SoftVorbis(const char *name, + const OMX_CALLBACKTYPE *callbacks, + OMX_PTR appData, + OMX_COMPONENTTYPE **component); + +protected: + virtual ~SoftVorbis(); + + virtual OMX_ERRORTYPE internalGetParameter( + OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter( + OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + virtual void onReset(); + +private: + enum { + kNumBuffers = 4, + kMaxNumSamplesPerBuffer = 8192 * 2 + }; + + size_t mInputBufferCount; + + vorbis_dsp_state *mState; + vorbis_info *mVi; + + int64_t mAnchorTimeUs; + int64_t mNumFramesOutput; + int32_t mNumFramesLeftOnPage; + bool mSawInputEos; + bool mSignalledOutputEos; + bool mSignalledError; + + enum { + NONE, + AWAITING_DISABLED, + AWAITING_ENABLED + } mOutputPortSettingsChange; + + void initPorts(); + status_t initDecoder(); + bool isConfigured() const; + void handleEOS(); + + DISALLOW_EVIL_CONSTRUCTORS(SoftVorbis); +}; + +} // namespace android + +#endif // SOFT_VORBIS_H_ + diff --git a/media/libstagefright/codecs/vorbis/dec/exports.lds b/media/libstagefright/codecs/vorbis/dec/exports.lds new file mode 100644 index 0000000..e24f3fa --- /dev/null +++ b/media/libstagefright/codecs/vorbis/dec/exports.lds @@ -0,0 +1,5 @@ +{ + global: + _Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPEPvPP17OMX_COMPONENTTYPE; + local: *; +}; diff --git a/media/libstagefright/codecs/xaacdec/Android.bp b/media/libstagefright/codecs/xaacdec/Android.bp new file mode 100644 index 0000000..1d03c16 --- /dev/null +++ b/media/libstagefright/codecs/xaacdec/Android.bp @@ -0,0 +1,36 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_av_media_libstagefright_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_av_media_libstagefright_license"], +} + +cc_library_shared { + name: "libstagefright_soft_xaacdec", + defaults: ["libstagefright_softomx-defaults"], + + srcs: [ + "SoftXAAC.cpp", + ], + + cflags: [ + "-DENABLE_MPEG_D_DRC" + ], + + sanitize: { + // integer_overflow: true, + misc_undefined: [ "signed-integer-overflow", "unsigned-integer-overflow", ], + cfi: true, + config: { + cfi_assembly_support: true, + }, + }, + + static_libs: ["libxaacdec"], + + shared_libs: [ + "libcutils", + ], +} diff --git a/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp b/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp new file mode 100644 index 0000000..a478642 --- /dev/null +++ b/media/libstagefright/codecs/xaacdec/SoftXAAC.cpp @@ -0,0 +1,1702 @@ +/* + * Copyright (C) 2018 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 "SoftXAAC" +#include + +#include "SoftXAAC.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/* 64*-0.25dB = -16 dB below full scale for mobile conf */ +#define DRC_DEFAULT_MOBILE_REF_LEVEL 64 +/* maximum compression of dynamic range for mobile conf */ +#define DRC_DEFAULT_MOBILE_DRC_CUT 127 +/* maximum compression of dynamic range for mobile conf */ +#define DRC_DEFAULT_MOBILE_DRC_BOOST 127 +/* switch for heavy compression for mobile conf */ +#define DRC_DEFAULT_MOBILE_DRC_HEAVY 1 +/* encoder target level; -1 => the value is unknown, + * otherwise dB step value (e.g. 64 for -16 dB) */ +#define DRC_DEFAULT_MOBILE_ENC_LEVEL (-1) + +/* Default Effect type is "Limited playback" */ +#define DRC_KEY_AAC_DRC_EFFECT_TYPE (3) + +/* REF_LEVEL of 64 pairs well with EFFECT_TYPE of 3. */ +/* Default loudness value for MPEG-D DRC */ +#define DRC_DEFAULT_MOBILE_LOUDNESS_LEVEL (64) + +#define PROP_DRC_OVERRIDE_REF_LEVEL "aac_drc_reference_level" +#define PROP_DRC_OVERRIDE_CUT "aac_drc_cut" +#define PROP_DRC_OVERRIDE_BOOST "aac_drc_boost" +#define PROP_DRC_OVERRIDE_HEAVY "aac_drc_heavy" +#define PROP_DRC_OVERRIDE_ENC_LEVEL "aac_drc_enc_target_level" +#define PROP_DRC_OVERRIDE_EFFECT_TYPE "ro.aac_drc_effect_type" + +/* maximum number of audio channels that can be decoded */ +#define MAX_CHANNEL_COUNT 8 + +#define RETURN_IF_FATAL(retval, str) \ + if (retval & IA_FATAL_ERROR) { \ + ALOGE("Error in %s: Returned: %d", str, retval); \ + return retval; \ + } else if (retval != IA_NO_ERROR) { \ + ALOGW("Warning in %s: Returned: %d", str, retval); \ + } + +namespace android { + +template +static void InitOMXParams(T* params) { + params->nSize = sizeof(T); + params->nVersion.s.nVersionMajor = 1; + params->nVersion.s.nVersionMinor = 0; + params->nVersion.s.nRevision = 0; + params->nVersion.s.nStep = 0; +} + +static const OMX_U32 kSupportedProfiles[] = { + OMX_AUDIO_AACObjectLC, OMX_AUDIO_AACObjectHE, OMX_AUDIO_AACObjectHE_PS, + OMX_AUDIO_AACObjectLD, OMX_AUDIO_AACObjectELD, OMX_AUDIO_AACObjectXHE +}; + +SoftXAAC::SoftXAAC(const char* name, const OMX_CALLBACKTYPE* callbacks, OMX_PTR appData, + OMX_COMPONENTTYPE** component) + : SimpleSoftOMXComponent(name, callbacks, appData, component), + mIsADTS(false), + mInputBufferCount(0), + mOutputBufferCount(0), + mSignalledError(false), + mLastInHeader(NULL), + mPrevTimestamp(0), + mCurrentTimestamp(0), + mOutputPortSettingsChange(NONE), + mXheaacCodecHandle(NULL), + mMpegDDrcHandle(NULL), + mInputBufferSize(0), + mOutputFrameLength(1024), + mInputBuffer(NULL), + mOutputBuffer(NULL), + mSampFreq(0), + mNumChannels(0), + mPcmWdSz(0), + mChannelMask(0), + mIsCodecInitialized(false), + mIsCodecConfigFlushRequired(false), + mMpegDDRCPresent(0), + mDRCFlag(0) + +{ + initPorts(); + mMemoryVec.clear(); + mDrcMemoryVec.clear(); + + CHECK_EQ(initDecoder(), IA_NO_ERROR); +} + +SoftXAAC::~SoftXAAC() { + IA_ERRORCODE err_code = deInitXAACDecoder(); + if (IA_NO_ERROR != err_code) { + ALOGE("deInitXAACDecoder() failed %d", err_code); + } + + err_code = deInitMPEGDDDrc(); + if (IA_NO_ERROR != err_code) { + ALOGE("deInitMPEGDDDrc() failed %d", err_code); + } + mIsCodecInitialized = false; + mIsCodecConfigFlushRequired = false; +} + +void SoftXAAC::initPorts() { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + def.nPortIndex = 0; + def.eDir = OMX_DirInput; + def.nBufferCountMin = kNumInputBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 8192; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 1; + + def.format.audio.cMIMEType = const_cast("audio/aac"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingAAC; + + addPort(def); + + def.nPortIndex = 1; + def.eDir = OMX_DirOutput; + def.nBufferCountMin = kNumOutputBuffers; + def.nBufferCountActual = def.nBufferCountMin; + def.nBufferSize = 4096 * MAX_CHANNEL_COUNT; + def.bEnabled = OMX_TRUE; + def.bPopulated = OMX_FALSE; + def.eDomain = OMX_PortDomainAudio; + def.bBuffersContiguous = OMX_FALSE; + def.nBufferAlignment = 2; + + def.format.audio.cMIMEType = const_cast("audio/raw"); + def.format.audio.pNativeRender = NULL; + def.format.audio.bFlagErrorConcealment = OMX_FALSE; + def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; + + addPort(def); +} + +IA_ERRORCODE SoftXAAC::initDecoder() { + int ui_drc_val; + IA_ERRORCODE err_code = IA_NO_ERROR; + int loop = 0; + + err_code = initXAACDecoder(); + if (err_code != IA_NO_ERROR) { + ALOGE("initXAACDecoder failed with error %d", err_code); + deInitXAACDecoder(); + return err_code; + } + + mEndOfInput = false; + mEndOfOutput = false; + + char value[PROPERTY_VALUE_MAX]; + if (property_get(PROP_DRC_OVERRIDE_REF_LEVEL, value, NULL)) { + ui_drc_val = atoi(value); + ALOGV("AAC decoder using desired DRC target reference level of %d instead of %d", + ui_drc_val, DRC_DEFAULT_MOBILE_REF_LEVEL); + } else { + ui_drc_val = DRC_DEFAULT_MOBILE_REF_LEVEL; + } + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LEVEL, &ui_drc_val); + + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LEVEL"); +#ifdef ENABLE_MPEG_D_DRC + /* Use ui_drc_val from PROP_DRC_OVERRIDE_REF_LEVEL or DRC_DEFAULT_MOBILE_REF_LEVEL + * for IA_ENHAACPLUS_DEC_DRC_TARGET_LOUDNESS too */ + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_DRC_TARGET_LOUDNESS, &ui_drc_val); + + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_DRC_TARGET_LOUDNESS"); +#endif + + if (property_get(PROP_DRC_OVERRIDE_CUT, value, NULL)) { + ui_drc_val = atoi(value); + ALOGV("AAC decoder using desired DRC attenuation factor of %d instead of %d", ui_drc_val, + DRC_DEFAULT_MOBILE_DRC_CUT); + } else { + ui_drc_val = DRC_DEFAULT_MOBILE_DRC_CUT; + } + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_CUT, &ui_drc_val); + + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_CUT"); + + if (property_get(PROP_DRC_OVERRIDE_BOOST, value, NULL)) { + ui_drc_val = atoi(value); + ALOGV("AAC decoder using desired DRC boost factor of %d instead of %d", ui_drc_val, + DRC_DEFAULT_MOBILE_DRC_BOOST); + } else { + ui_drc_val = DRC_DEFAULT_MOBILE_DRC_BOOST; + } + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_BOOST, &ui_drc_val); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_BOOST"); + + if (property_get(PROP_DRC_OVERRIDE_HEAVY, value, NULL)) { + ui_drc_val = atoi(value); + ALOGV("AAC decoder using desired Heavy compression factor of %d instead of %d", ui_drc_val, + DRC_DEFAULT_MOBILE_DRC_HEAVY); + } else { + ui_drc_val = DRC_DEFAULT_MOBILE_DRC_HEAVY; + } + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_HEAVY_COMP, &ui_drc_val); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_HEAVY_COMP"); + +#ifdef ENABLE_MPEG_D_DRC + if (property_get(PROP_DRC_OVERRIDE_EFFECT_TYPE, value, NULL)) { + ui_drc_val = atoi(value); + ALOGV("AAC decoder using desired DRC effect type of %d instead of %d", ui_drc_val, + DRC_KEY_AAC_DRC_EFFECT_TYPE); + } else { + ui_drc_val = DRC_KEY_AAC_DRC_EFFECT_TYPE; + } + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_DRC_EFFECT_TYPE, &ui_drc_val); + + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_DRC_EFFECT_TYPE"); + +#endif + return IA_NO_ERROR; +} + +OMX_ERRORTYPE SoftXAAC::internalGetParameter(OMX_INDEXTYPE index, OMX_PTR params) { + switch ((OMX_U32)index) { + case OMX_IndexParamAudioPortFormat: { + OMX_AUDIO_PARAM_PORTFORMATTYPE* formatParams = (OMX_AUDIO_PARAM_PORTFORMATTYPE*)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if (formatParams->nIndex > 0) { + return OMX_ErrorNoMore; + } + + formatParams->eEncoding = + (formatParams->nPortIndex == 0) ? OMX_AUDIO_CodingAAC : OMX_AUDIO_CodingPCM; + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAac: { + OMX_AUDIO_PARAM_AACPROFILETYPE* aacParams = (OMX_AUDIO_PARAM_AACPROFILETYPE*)params; + + if (!isValidOMXParam(aacParams)) { + return OMX_ErrorBadParameter; + } + + if (aacParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + aacParams->nBitRate = 0; + aacParams->nAudioBandWidth = 0; + aacParams->nAACtools = 0; + aacParams->nAACERtools = 0; + aacParams->eAACProfile = OMX_AUDIO_AACObjectMain; + + aacParams->eAACStreamFormat = + mIsADTS ? OMX_AUDIO_AACStreamFormatMP4ADTS : OMX_AUDIO_AACStreamFormatMP4FF; + + aacParams->eChannelMode = OMX_AUDIO_ChannelModeStereo; + + if (!isConfigured()) { + aacParams->nChannels = 1; + aacParams->nSampleRate = 44100; + aacParams->nFrameLength = 0; + } else { + aacParams->nChannels = mNumChannels; + aacParams->nSampleRate = mSampFreq; + aacParams->nFrameLength = mOutputFrameLength; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: { + OMX_AUDIO_PARAM_PCMMODETYPE* pcmParams = (OMX_AUDIO_PARAM_PCMMODETYPE*)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + pcmParams->eNumData = OMX_NumericalDataSigned; + pcmParams->eEndian = OMX_EndianBig; + pcmParams->bInterleaved = OMX_TRUE; + pcmParams->nBitPerSample = 16; + pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; + pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; + pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; + pcmParams->eChannelMapping[2] = OMX_AUDIO_ChannelCF; + pcmParams->eChannelMapping[3] = OMX_AUDIO_ChannelLFE; + pcmParams->eChannelMapping[4] = OMX_AUDIO_ChannelLS; + pcmParams->eChannelMapping[5] = OMX_AUDIO_ChannelRS; + + if (!isConfigured()) { + pcmParams->nChannels = 1; + pcmParams->nSamplingRate = 44100; + } else { + pcmParams->nChannels = mNumChannels; + pcmParams->nSamplingRate = mSampFreq; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioProfileQuerySupported: { + OMX_AUDIO_PARAM_ANDROID_PROFILETYPE* profileParams = + (OMX_AUDIO_PARAM_ANDROID_PROFILETYPE*)params; + + if (!isValidOMXParam(profileParams)) { + return OMX_ErrorBadParameter; + } + + if (profileParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + if (profileParams->nProfileIndex >= NELEM(kSupportedProfiles)) { + return OMX_ErrorNoMore; + } + + profileParams->eProfile = kSupportedProfiles[profileParams->nProfileIndex]; + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalGetParameter(index, params); + } +} + +OMX_ERRORTYPE SoftXAAC::internalSetParameter(OMX_INDEXTYPE index, const OMX_PTR params) { + switch ((int)index) { + case OMX_IndexParamStandardComponentRole: { + const OMX_PARAM_COMPONENTROLETYPE* roleParams = + (const OMX_PARAM_COMPONENTROLETYPE*)params; + + if (!isValidOMXParam(roleParams)) { + return OMX_ErrorBadParameter; + } + + if (strncmp((const char*)roleParams->cRole, "audio_decoder.aac", + OMX_MAX_STRINGNAME_SIZE - 1)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPortFormat: { + const OMX_AUDIO_PARAM_PORTFORMATTYPE* formatParams = + (const OMX_AUDIO_PARAM_PORTFORMATTYPE*)params; + + if (!isValidOMXParam(formatParams)) { + return OMX_ErrorBadParameter; + } + + if (formatParams->nPortIndex > 1) { + return OMX_ErrorUndefined; + } + + if ((formatParams->nPortIndex == 0 && formatParams->eEncoding != OMX_AUDIO_CodingAAC) || + (formatParams->nPortIndex == 1 && formatParams->eEncoding != OMX_AUDIO_CodingPCM)) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAac: { + const OMX_AUDIO_PARAM_AACPROFILETYPE* aacParams = + (const OMX_AUDIO_PARAM_AACPROFILETYPE*)params; + + if (!isValidOMXParam(aacParams)) { + return OMX_ErrorBadParameter; + } + + if (aacParams->nPortIndex != 0) { + return OMX_ErrorUndefined; + } + + if (aacParams->eAACStreamFormat == OMX_AUDIO_AACStreamFormatMP4FF) { + mIsADTS = false; + } else if (aacParams->eAACStreamFormat == OMX_AUDIO_AACStreamFormatMP4ADTS) { + mIsADTS = true; + } else { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioAndroidAacDrcPresentation: { + const OMX_AUDIO_PARAM_ANDROID_AACDRCPRESENTATIONTYPE* aacPresParams = + (const OMX_AUDIO_PARAM_ANDROID_AACDRCPRESENTATIONTYPE*)params; + + if (!isValidOMXParam(aacPresParams)) { + ALOGE("set OMX_ErrorBadParameter"); + return OMX_ErrorBadParameter; + } + + // for the following parameters of the OMX_AUDIO_PARAM_AACPROFILETYPE structure, + // a value of -1 implies the parameter is not set by the application: + // nMaxOutputChannels -1 by default + // nDrcCut uses default platform properties, see initDecoder() + // nDrcBoost idem + // nHeavyCompression idem + // nTargetReferenceLevel idem + // nEncodedTargetLevel idem + if (aacPresParams->nMaxOutputChannels >= 0) { + int max; + if (aacPresParams->nMaxOutputChannels >= 8) { + max = 8; + } else if (aacPresParams->nMaxOutputChannels >= 6) { + max = 6; + } else if (aacPresParams->nMaxOutputChannels >= 2) { + max = 2; + } else { + // -1 or 0: disable downmix, 1: mono + max = aacPresParams->nMaxOutputChannels; + } + } + /* Apply DRC Changes */ + IA_ERRORCODE err_code = setXAACDRCInfo(aacPresParams->nDrcCut, aacPresParams->nDrcBoost, + aacPresParams->nTargetReferenceLevel, + aacPresParams->nHeavyCompression +#ifdef ENABLE_MPEG_D_DRC + , + aacPresParams->nDrcEffectType +#endif + ); // TOD0 : Revert this change + if (err_code != IA_NO_ERROR) { + ALOGE("Error in OMX_IndexParamAudioAndroidAacDrcPresentation"); + return OMX_ErrorBadParameter; + } + + return OMX_ErrorNone; + } + + case OMX_IndexParamAudioPcm: { + const OMX_AUDIO_PARAM_PCMMODETYPE* pcmParams = (OMX_AUDIO_PARAM_PCMMODETYPE*)params; + + if (!isValidOMXParam(pcmParams)) { + return OMX_ErrorBadParameter; + } + + if (pcmParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + + default: + return SimpleSoftOMXComponent::internalSetParameter(index, params); + } +} + +bool SoftXAAC::isConfigured() const { + return mInputBufferCount > 0; +} + +void SoftXAAC::onQueueFilled(OMX_U32 /* portIndex */) { + if (mSignalledError || mOutputPortSettingsChange != NONE) { + ALOGE("onQueueFilled do not process %d %d", mSignalledError, mOutputPortSettingsChange); + return; + } + + uint8_t* inBuffer = NULL; + uint32_t inBufferLength = 0; + + List& inQueue = getPortQueue(0); + List& outQueue = getPortQueue(1); + + signed int numOutBytes = 0; + + /* If decoder call fails in between, then mOutputFrameLength is used */ + /* Decoded output for AAC is 1024/2048 samples / channel */ + /* TODO: For USAC mOutputFrameLength can go up to 4096 */ + /* Note: entire buffer logic to save and retrieve assumes 2 bytes per*/ + /* sample currently */ + if (mIsCodecInitialized) { + numOutBytes = mOutputFrameLength * (mPcmWdSz / 8) * mNumChannels; + } + + while ((!inQueue.empty() || mEndOfInput) && !outQueue.empty()) { + if (!inQueue.empty()) { + BufferInfo* inInfo = *inQueue.begin(); + OMX_BUFFERHEADERTYPE* inHeader = inInfo->mHeader; + + /* No need to check inHeader != NULL, as inQueue is not empty */ + mEndOfInput = (inHeader->nFlags & OMX_BUFFERFLAG_EOS) != 0; + + if (mInputBufferCount == 0 && !(inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG)) { + ALOGW("first buffer should have OMX_BUFFERFLAG_CODECCONFIG set"); + inHeader->nFlags |= OMX_BUFFERFLAG_CODECCONFIG; + } + if ((inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) != 0) { + inBuffer = inHeader->pBuffer + inHeader->nOffset; + inBufferLength = inHeader->nFilledLen; + + /* GA header configuration sent to Decoder! */ + IA_ERRORCODE err_code = configXAACDecoder(inBuffer, inBufferLength); + if (IA_NO_ERROR != err_code) { + ALOGW("configXAACDecoder err_code = %d", err_code); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, err_code, NULL); + return; + } + mInputBufferCount++; + mOutputBufferCount++; // fake increase of outputBufferCount to keep the counters + // aligned + + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + mLastInHeader = NULL; + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + + // Only send out port settings changed event if both sample rate + // and mNumChannels are valid. + if (mSampFreq && mNumChannels && !mIsCodecConfigFlushRequired) { + ALOGV("Configuring decoder: %d Hz, %d channels", mSampFreq, mNumChannels); + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + } + + return; + } + + if (inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + inQueue.erase(inQueue.begin()); + mLastInHeader = NULL; + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + continue; + } + + // Restore Offset and Length for Port reconfig case + size_t tempOffset = inHeader->nOffset; + size_t tempFilledLen = inHeader->nFilledLen; + if (mIsADTS) { + size_t adtsHeaderSize = 0; + // skip 30 bits, aac_frame_length follows. + // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll????? + + const uint8_t* adtsHeader = inHeader->pBuffer + inHeader->nOffset; + + bool signalError = false; + if (inHeader->nFilledLen < 7) { + ALOGE( + "Audio data too short to contain even the ADTS header. " + "Got %d bytes.", + inHeader->nFilledLen); + hexdump(adtsHeader, inHeader->nFilledLen); + signalError = true; + } else { + bool protectionAbsent = (adtsHeader[1] & 1); + + unsigned aac_frame_length = + ((adtsHeader[3] & 3) << 11) | (adtsHeader[4] << 3) | (adtsHeader[5] >> 5); + + if (inHeader->nFilledLen < aac_frame_length) { + ALOGE( + "Not enough audio data for the complete frame. " + "Got %d bytes, frame size according to the ADTS " + "header is %u bytes.", + inHeader->nFilledLen, aac_frame_length); + hexdump(adtsHeader, inHeader->nFilledLen); + signalError = true; + } else { + adtsHeaderSize = (protectionAbsent ? 7 : 9); + if (aac_frame_length < adtsHeaderSize) { + signalError = true; + } else { + inBuffer = (uint8_t*)adtsHeader + adtsHeaderSize; + inBufferLength = aac_frame_length - adtsHeaderSize; + + inHeader->nOffset += adtsHeaderSize; + inHeader->nFilledLen -= adtsHeaderSize; + } + } + } + + if (signalError) { + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorStreamCorrupt, ERROR_MALFORMED, NULL); + return; + } + + // insert buffer size and time stamp + if (mLastInHeader != inHeader) { + mCurrentTimestamp = inHeader->nTimeStamp; + mLastInHeader = inHeader; + } else { + mCurrentTimestamp = mPrevTimestamp + mOutputFrameLength * 1000000LL / mSampFreq; + } + } else { + inBuffer = inHeader->pBuffer + inHeader->nOffset; + inBufferLength = inHeader->nFilledLen; + mLastInHeader = inHeader; + mCurrentTimestamp = inHeader->nTimeStamp; + } + + int numLoops = 0; + signed int prevSampleRate = mSampFreq; + signed int prevNumChannels = mNumChannels; + + /* XAAC decoder expects first frame to be fed via configXAACDecoder API */ + /* which should initialize the codec. Once this state is reached, call the */ + /* decodeXAACStream API with same frame to decode! */ + if (!mIsCodecInitialized) { + IA_ERRORCODE err_code = configXAACDecoder(inBuffer, inBufferLength); + if (IA_NO_ERROR != err_code) { + ALOGW("configXAACDecoder Failed 2 err_code = %d", err_code); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, err_code, NULL); + return; + } + } + + if (!mSampFreq || !mNumChannels) { + if ((mInputBufferCount > 2) && (mOutputBufferCount <= 1)) { + ALOGW("Invalid AAC stream"); + ALOGW("mSampFreq %d mNumChannels %d ", mSampFreq, mNumChannels); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + } else if ((mSampFreq != prevSampleRate) || (mNumChannels != prevNumChannels)) { + ALOGV("Reconfiguring decoder: %d->%d Hz, %d->%d channels", prevSampleRate, + mSampFreq, prevNumChannels, mNumChannels); + inHeader->nOffset = tempOffset; + inHeader->nFilledLen = tempFilledLen; + notify(OMX_EventPortSettingsChanged, 1, 0, NULL); + mOutputPortSettingsChange = AWAITING_DISABLED; + return; + } + + signed int bytesConsumed = 0; + int errorCode = 0; + if (mIsCodecInitialized) { + mIsCodecConfigFlushRequired = true; + errorCode = + decodeXAACStream(inBuffer, inBufferLength, &bytesConsumed, &numOutBytes); + } else if (!mIsCodecConfigFlushRequired) { + ALOGW("Assumption that first frame after header initializes decoder failed!"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, -1, NULL); + return; + } + inHeader->nFilledLen -= bytesConsumed; + inHeader->nOffset += bytesConsumed; + + if (inHeader->nFilledLen != 0) { + ALOGE("All data not consumed"); + } + + /* In case of error, decoder would have given out empty buffer */ + if ((0 != errorCode) && (0 == numOutBytes) && mIsCodecInitialized) { + numOutBytes = mOutputFrameLength * (mPcmWdSz / 8) * mNumChannels; + } + numLoops++; + + if (0 == bytesConsumed) { + ALOGW("bytesConsumed is zero"); + } + + if (errorCode) { + /* Clear buffer for output buffer is done inside XAAC codec */ + /* TODO - Check if below memset is on top of reset inside codec */ + memset(mOutputBuffer, 0, numOutBytes); // TODO: check for overflow, ASAN + // Discard input buffer. + inHeader->nFilledLen = 0; + // fall through + } + + if (inHeader->nFilledLen == 0) { + inInfo->mOwnedByUs = false; + mInputBufferCount++; + inQueue.erase(inQueue.begin()); + mLastInHeader = NULL; + inInfo = NULL; + notifyEmptyBufferDone(inHeader); + inHeader = NULL; + } else { + ALOGV("inHeader->nFilledLen = %d", inHeader->nFilledLen); + } + + if (!outQueue.empty() && numOutBytes) { + BufferInfo* outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE* outHeader = outInfo->mHeader; + + if (outHeader->nOffset != 0) { + ALOGE("outHeader->nOffset != 0 is not handled"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + + signed short* outBuffer = + reinterpret_cast(outHeader->pBuffer + outHeader->nOffset); + int samplesize = mNumChannels * sizeof(int16_t); + if (outHeader->nOffset + mOutputFrameLength * samplesize > outHeader->nAllocLen) { + ALOGE("buffer overflow"); + mSignalledError = true; + notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); + return; + } + memcpy(outBuffer, mOutputBuffer, numOutBytes); + outHeader->nFilledLen = numOutBytes; + + if (mEndOfInput && !outQueue.empty()) { + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + mEndOfOutput = true; + } else { + outHeader->nFlags = 0; + } + outHeader->nTimeStamp = mCurrentTimestamp; + mPrevTimestamp = mCurrentTimestamp; + + mOutputBufferCount++; + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + } + } + + if (mEndOfInput) { + if (!outQueue.empty()) { + if (!mEndOfOutput) { + ALOGV(" empty block signaling EOS"); + // send partial or empty block signaling EOS + mEndOfOutput = true; + BufferInfo* outInfo = *outQueue.begin(); + OMX_BUFFERHEADERTYPE* outHeader = outInfo->mHeader; + + outHeader->nFilledLen = 0; + outHeader->nFlags = OMX_BUFFERFLAG_EOS; + outHeader->nTimeStamp = mPrevTimestamp; + + mOutputBufferCount++; + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + } + break; // if outQueue not empty but no more output + } + } + } +} + +void SoftXAAC::onPortFlushCompleted(OMX_U32 portIndex) { + if (portIndex == 0) { + // Make sure that the next buffer output does not still + // depend on fragments from the last one decoded. + // drain all existing data + if (mIsCodecInitialized) { + IA_ERRORCODE err_code = configflushDecode(); + if (err_code != IA_NO_ERROR) { + ALOGE("Error in configflushDecode: Error %d", err_code); + } + } + drainDecoder(); + mLastInHeader = NULL; + mEndOfInput = false; + } else { + mEndOfOutput = false; + } +} + +IA_ERRORCODE SoftXAAC::configflushDecode() { + IA_ERRORCODE err_code; + UWORD32 ui_init_done; + uint32_t inBufferLength = 8203; + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_INIT, IA_CMD_TYPE_FLUSH_MEM, NULL); + RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_FLUSH_MEM"); + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_INPUT_BYTES, 0, &inBufferLength); + RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_INPUT_BYTES"); + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_INIT, IA_CMD_TYPE_FLUSH_MEM, NULL); + RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_FLUSH_MEM"); + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_INIT, IA_CMD_TYPE_INIT_DONE_QUERY, + &ui_init_done); + RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_DONE_QUERY"); + + if (ui_init_done) { + err_code = getXAACStreamInfo(); + RETURN_IF_FATAL(err_code, "getXAACStreamInfo"); + + ALOGV( + "Found Codec with below config---\nsampFreq %d\nnumChannels %d\npcmWdSz " + "%d\nchannelMask %d\noutputFrameLength %d", + mSampFreq, mNumChannels, mPcmWdSz, mChannelMask, mOutputFrameLength); + + mIsCodecInitialized = true; + } + return IA_NO_ERROR; +} +IA_ERRORCODE SoftXAAC::drainDecoder() { + return IA_NO_ERROR; +} + +void SoftXAAC::onReset() { + drainDecoder(); + + // reset the "configured" state + mInputBufferCount = 0; + mOutputBufferCount = 0; + mEndOfInput = false; + mEndOfOutput = false; + mLastInHeader = NULL; + + mSignalledError = false; + mOutputPortSettingsChange = NONE; +} + +void SoftXAAC::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { + if (portIndex != 1) { + return; + } + + switch (mOutputPortSettingsChange) { + case NONE: + break; + + case AWAITING_DISABLED: { + CHECK(!enabled); + mOutputPortSettingsChange = AWAITING_ENABLED; + break; + } + + default: { + CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); + CHECK(enabled); + mOutputPortSettingsChange = NONE; + break; + } + } +} + +IA_ERRORCODE SoftXAAC::initXAACDecoder() { + LOOPIDX i; + + /* Error code */ + IA_ERRORCODE err_code = IA_NO_ERROR; + + /* First part */ + /* Error Handler Init */ + /* Get Library Name, Library Version and API Version */ + /* Initialize API structure + Default config set */ + /* Set config params from user */ + /* Initialize memory tables */ + /* Get memory information and allocate memory */ + + /* Memory variables */ + UWORD32 ui_proc_mem_tabs_size; + /* API size */ + UWORD32 pui_api_size; + pVOID pv_alloc_ptr; + + mInputBufferSize = 0; + mInputBuffer = 0; + mOutputBuffer = 0; + + /* Process struct initing end */ + /* ******************************************************************/ + /* Initialize API structure and set config params to default */ + /* ******************************************************************/ + + /* Get the API size */ + err_code = ixheaacd_dec_api(NULL, IA_API_CMD_GET_API_SIZE, 0, &pui_api_size); + RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_API_SIZE"); + + /* Allocate memory for API */ + mXheaacCodecHandle = memalign(4, pui_api_size); + if (mXheaacCodecHandle == NULL) { + ALOGE("malloc for pui_api_size + 4 >> %d Failed", pui_api_size + 4); + return IA_FATAL_ERROR; + } + mMemoryVec.push(mXheaacCodecHandle); + + /* Set the config params to default values */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_INIT, + IA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS, NULL); + RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS"); +#ifdef ENABLE_MPEG_D_DRC + /* Get the API size */ + err_code = ia_drc_dec_api(NULL, IA_API_CMD_GET_API_SIZE, 0, &pui_api_size); + + RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_API_SIZE"); + + /* Allocate memory for API */ + mMpegDDrcHandle = memalign(4, pui_api_size); + + if (mMpegDDrcHandle == NULL) { + ALOGE("malloc for drc api structure Failed"); + return IA_FATAL_ERROR; + } + mMemoryVec.push(mMpegDDrcHandle); + + memset(mMpegDDrcHandle, 0, pui_api_size); + + /* Set the config params to default values */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT, + IA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS, NULL); + + RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_API_PRE_CONFIG_PARAMS"); +#endif + + /* ******************************************************************/ + /* Set config parameters */ + /* ******************************************************************/ + UWORD32 ui_mp4_flag = 1; + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_ISMP4, &ui_mp4_flag); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_ISMP4"); + + /* ******************************************************************/ + /* Initialize Memory info tables */ + /* ******************************************************************/ + + /* Get memory info tables size */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_MEMTABS_SIZE, 0, + &ui_proc_mem_tabs_size); + RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_MEMTABS_SIZE"); + + pv_alloc_ptr = memalign(4, ui_proc_mem_tabs_size); + if (pv_alloc_ptr == NULL) { + ALOGE("Malloc for size (ui_proc_mem_tabs_size + 4) = %d failed!", + ui_proc_mem_tabs_size + 4); + return IA_FATAL_ERROR; + } + mMemoryVec.push(pv_alloc_ptr); + + /* Set pointer for process memory tables */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_MEMTABS_PTR, 0, pv_alloc_ptr); + RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_MEMTABS_PTR"); + + /* initialize the API, post config, fill memory tables */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_INIT, + IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS, NULL); + RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS"); + + /* ******************************************************************/ + /* Allocate Memory with info from library */ + /* ******************************************************************/ + /* There are four different types of memories, that needs to be allocated */ + /* persistent,scratch,input and output */ + for (i = 0; i < 4; i++) { + int ui_size = 0, ui_alignment = 0, ui_type = 0; + pVOID pv_alloc_ptr; + + /* Get memory size */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_MEM_INFO_SIZE, i, &ui_size); + RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_MEM_INFO_SIZE"); + + /* Get memory alignment */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_MEM_INFO_ALIGNMENT, i, + &ui_alignment); + RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_MEM_INFO_ALIGNMENT"); + + /* Get memory type */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_MEM_INFO_TYPE, i, &ui_type); + RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_MEM_INFO_TYPE"); + + pv_alloc_ptr = memalign(ui_alignment, ui_size); + if (pv_alloc_ptr == NULL) { + ALOGE("Malloc for size (ui_size + ui_alignment) = %d failed!", ui_size + ui_alignment); + return IA_FATAL_ERROR; + } + mMemoryVec.push(pv_alloc_ptr); + + /* Set the buffer pointer */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_MEM_PTR, i, pv_alloc_ptr); + RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_MEM_PTR"); + if (ui_type == IA_MEMTYPE_INPUT) { + mInputBuffer = (pWORD8)pv_alloc_ptr; + mInputBufferSize = ui_size; + } + + if (ui_type == IA_MEMTYPE_OUTPUT) { + mOutputBuffer = (pWORD8)pv_alloc_ptr; + } + } + /* End first part */ + + return IA_NO_ERROR; +} + +IA_ERRORCODE SoftXAAC::configXAACDecoder(uint8_t* inBuffer, uint32_t inBufferLength) { + UWORD32 ui_init_done; + int32_t i_bytes_consumed; + + if (mInputBufferSize < inBufferLength) { + ALOGE("Cannot config AAC, input buffer size %d < inBufferLength %d", mInputBufferSize, + inBufferLength); + return false; + } + + /* Copy the buffer passed by Android plugin to codec input buffer */ + memcpy(mInputBuffer, inBuffer, inBufferLength); + + /* Set number of bytes to be processed */ + IA_ERRORCODE err_code = + ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_INPUT_BYTES, 0, &inBufferLength); + RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_INPUT_BYTES"); + + if (mIsCodecConfigFlushRequired) { + /* If codec is already initialized, then GA header is passed again */ + /* Need to call the Flush API instead of INIT_PROCESS */ + mIsCodecInitialized = false; /* Codec needs to be Reinitialized after flush */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_INIT, IA_CMD_TYPE_GA_HDR, NULL); + RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_GA_HDR"); + } else { + /* Initialize the process */ + err_code = + ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_INIT, IA_CMD_TYPE_INIT_PROCESS, NULL); + RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_PROCESS"); + } + + /* Checking for end of initialization */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_INIT, IA_CMD_TYPE_INIT_DONE_QUERY, + &ui_init_done); + RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_DONE_QUERY"); + + /* How much buffer is used in input buffers */ + err_code = + ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CURIDX_INPUT_BUF, 0, &i_bytes_consumed); + RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_CURIDX_INPUT_BUF"); + + if (ui_init_done) { + err_code = getXAACStreamInfo(); + RETURN_IF_FATAL(err_code, "getXAACStreamInfo"); + + ALOGI( + "Found Codec with below config---\nsampFreq %d\nnumChannels %d\npcmWdSz " + "%d\nchannelMask %d\noutputFrameLength %d", + mSampFreq, mNumChannels, mPcmWdSz, mChannelMask, mOutputFrameLength); + mIsCodecInitialized = true; + +#ifdef ENABLE_MPEG_D_DRC + err_code = configMPEGDDrc(); + RETURN_IF_FATAL(err_code, "configMPEGDDrc"); +#endif + } + + return IA_NO_ERROR; +} +IA_ERRORCODE SoftXAAC::initMPEGDDDrc() { + IA_ERRORCODE err_code = IA_NO_ERROR; + int i; + + for (i = 0; i < (WORD32)2; i++) { + WORD32 ui_size, ui_alignment, ui_type; + pVOID pv_alloc_ptr; + + /* Get memory size */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_GET_MEM_INFO_SIZE, i, &ui_size); + + RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_MEM_INFO_SIZE"); + + /* Get memory alignment */ + err_code = + ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_GET_MEM_INFO_ALIGNMENT, i, &ui_alignment); + + RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_MEM_INFO_ALIGNMENT"); + + /* Get memory type */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_GET_MEM_INFO_TYPE, i, &ui_type); + RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_MEM_INFO_TYPE"); + + pv_alloc_ptr = memalign(4, ui_size); + if (pv_alloc_ptr == NULL) { + ALOGE(" Cannot create requested memory %d", ui_size); + return IA_FATAL_ERROR; + } + mDrcMemoryVec.push(pv_alloc_ptr); + + /* Set the buffer pointer */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_MEM_PTR, i, pv_alloc_ptr); + + RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_MEM_PTR"); + } + + WORD32 ui_size; + ui_size = 8192 * 2; + + mDrcInBuf = (int8_t*)memalign(4, ui_size); + if (mDrcInBuf == NULL) { + ALOGE(" Cannot create requested memory %d", ui_size); + return IA_FATAL_ERROR; + } + mDrcMemoryVec.push(mDrcInBuf); + + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_MEM_PTR, 2, mDrcInBuf); + RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_MEM_PTR"); + + mDrcOutBuf = (int8_t*)memalign(4, ui_size); + if (mDrcOutBuf == NULL) { + ALOGE(" Cannot create requested memory %d", ui_size); + return IA_FATAL_ERROR; + } + mDrcMemoryVec.push(mDrcOutBuf); + + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_MEM_PTR, 3, mDrcOutBuf); + RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_MEM_PTR"); + + return IA_NO_ERROR; +} +IA_ERRORCODE SoftXAAC::configMPEGDDrc() { + IA_ERRORCODE err_code = IA_NO_ERROR; + int i_effect_type; + int i_loud_norm; + int i_target_loudness; + unsigned int i_sbr_mode; + int i; + int ui_proc_mem_tabs_size = 0; + pVOID pv_alloc_ptr = NULL; + +#ifdef ENABLE_MPEG_D_DRC + { + /* Sampling Frequency */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_PARAM_SAMP_FREQ, &mSampFreq); + RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_SAMP_FREQ"); + /* Total Number of Channels */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_PARAM_NUM_CHANNELS, &mNumChannels); + RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_NUM_CHANNELS"); + + /* PCM word size */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_PARAM_PCM_WDSZ, &mPcmWdSz); + RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_PCM_WDSZ"); + + /*Set Effect Type*/ + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_EFFECT_TYPE, &i_effect_type); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_EFFECT_TYPE"); + + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_DRC_EFFECT_TYPE, &i_effect_type); + RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_DRC_EFFECT_TYPE"); + + /*Set target loudness */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LOUDNESS, + &i_target_loudness); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LOUDNESS"); + + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_DRC_TARGET_LOUDNESS, &i_target_loudness); + RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_DRC_TARGET_LOUDNESS"); + + /*Set loud_norm_flag*/ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_LOUD_NORM, &i_loud_norm); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_LOUD_NORM"); + + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_DRC_LOUD_NORM, &i_loud_norm); + RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_DRC_LOUD_NORM"); + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_SBR_MODE, &i_sbr_mode); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_SBR_MODE"); + + /* Get memory info tables size */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_GET_MEMTABS_SIZE, 0, + &ui_proc_mem_tabs_size); + + RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_MEMTABS_SIZE"); + + pv_alloc_ptr = memalign(4, ui_proc_mem_tabs_size); + + if (pv_alloc_ptr == NULL) { + ALOGE("Cannot create requested memory %d", ui_proc_mem_tabs_size); + return IA_FATAL_ERROR; + } + + memset(pv_alloc_ptr, 0, ui_proc_mem_tabs_size); + + mMemoryVec.push(pv_alloc_ptr); + + /* Set pointer for process memory tables */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_MEMTABS_PTR, 0, + pv_alloc_ptr); + + RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_MEMTABS_PTR"); + + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT, + IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS, NULL); + + RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_API_POST_CONFIG_PARAMS"); + + /* Free any memory that is allocated for MPEG D Drc so far */ + deInitMPEGDDDrc(); + + err_code = initMPEGDDDrc(); + if (err_code != IA_NO_ERROR) { + ALOGE("initMPEGDDDrc failed with error %d", err_code); + deInitMPEGDDDrc(); + return err_code; + } + + /* DRC buffers + buf[0] - contains extension element pay load loudness related + buf[1] - contains extension element pay load*/ + { + VOID* p_array[2][16]; + WORD32 ii; + WORD32 buf_sizes[2][16]; + WORD32 num_elements; + WORD32 num_config_ext; + WORD32 bit_str_fmt = 1; + + WORD32 uo_num_chan; + + memset(buf_sizes, 0, 32 * sizeof(WORD32)); + + err_code = + ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_EXT_ELE_BUF_SIZES, &buf_sizes[0][0]); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_EXT_ELE_BUF_SIZES"); + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_EXT_ELE_PTR, &p_array); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_EXT_ELE_PTR"); + + err_code = + ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT, IA_CMD_TYPE_INIT_SET_BUFF_PTR, 0); + RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_SET_BUFF_PTR"); + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_NUM_ELE, &num_elements); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_NUM_ELE"); + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_NUM_CONFIG_EXT, &num_config_ext); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_NUM_CONFIG_EXT"); + + for (ii = 0; ii < num_config_ext; ii++) { + /*copy loudness bitstream*/ + if (buf_sizes[0][ii] > 0) { + memcpy(mDrcInBuf, p_array[0][ii], buf_sizes[0][ii]); + + /*Set bitstream_split_format */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT, &bit_str_fmt); + RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT"); + + /* Set number of bytes to be processed */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES_IL_BS, 0, + &buf_sizes[0][ii]); + RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_INPUT_BYTES_IL_BS"); + + /* Execute process */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT, + IA_CMD_TYPE_INIT_CPY_IL_BSF_BUFF, NULL); + RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_CPY_IL_BSF_BUFF"); + + mDRCFlag = 1; + } + } + + for (ii = 0; ii < num_elements; ii++) { + /*copy config bitstream*/ + if (buf_sizes[1][ii] > 0) { + memcpy(mDrcInBuf, p_array[1][ii], buf_sizes[1][ii]); + /* Set number of bytes to be processed */ + + /*Set bitstream_split_format */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT, &bit_str_fmt); + RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT"); + + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES_IC_BS, 0, + &buf_sizes[1][ii]); + RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_INPUT_BYTES_IC_BS"); + + /* Execute process */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT, + IA_CMD_TYPE_INIT_CPY_IC_BSF_BUFF, NULL); + + RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_CPY_IC_BSF_BUFF"); + + mDRCFlag = 1; + } + } + + if (mDRCFlag == 1) { + mMpegDDRCPresent = 1; + } else { + mMpegDDRCPresent = 0; + } + + /*Read interface buffer config file bitstream*/ + if (mMpegDDRCPresent == 1) { + WORD32 interface_is_present = 1; + WORD32 frame_length; + + if (i_sbr_mode != 0) { + if (i_sbr_mode == 1) { + frame_length = 2048; + } else if (i_sbr_mode == 3) { + frame_length = 4096; + } else { + frame_length = 1024; + } + } else { + frame_length = 4096; + } + + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_PARAM_FRAME_SIZE, &frame_length); + RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_FRAME_SIZE"); + + err_code = + ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_PARAM_INT_PRESENT, &interface_is_present); + RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_INT_PRESENT"); + + /* Execute process */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT, + IA_CMD_TYPE_INIT_CPY_IN_BSF_BUFF, NULL); + RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_CPY_IN_BSF_BUFF"); + + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT, + IA_CMD_TYPE_INIT_PROCESS, NULL); + RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_INIT_PROCESS"); + + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_PARAM_NUM_CHANNELS, &uo_num_chan); + RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_NUM_CHANNELS"); + } + } + } +#endif + + return IA_NO_ERROR; +} +IA_ERRORCODE SoftXAAC::decodeXAACStream(uint8_t* inBuffer, uint32_t inBufferLength, + int32_t* bytesConsumed, int32_t* outBytes) { + if (mInputBufferSize < inBufferLength) { + ALOGE("Cannot config AAC, input buffer size %d < inBufferLength %d", mInputBufferSize, + inBufferLength); + return -1; + } + + /* Copy the buffer passed by Android plugin to codec input buffer */ + memcpy(mInputBuffer, inBuffer, inBufferLength); + + /* Set number of bytes to be processed */ + IA_ERRORCODE err_code = + ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_INPUT_BYTES, 0, &inBufferLength); + RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_INPUT_BYTES"); + + /* Execute process */ + err_code = + ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_EXECUTE, IA_CMD_TYPE_DO_EXECUTE, NULL); + RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DO_EXECUTE"); + + UWORD32 ui_exec_done; + WORD32 i_num_preroll = 0; + /* Checking for end of processing */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_EXECUTE, IA_CMD_TYPE_DONE_QUERY, + &ui_exec_done); + RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DONE_QUERY"); + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_GET_NUM_PRE_ROLL_FRAMES, + &i_num_preroll); + + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GET_NUM_PRE_ROLL_FRAMES"); + { + int32_t pi_preroll_frame_offset = 0; + do { +#ifdef ENABLE_MPEG_D_DRC + if (ui_exec_done != 1) { + VOID* p_array; // ITTIAM:buffer to handle gain payload + WORD32 buf_size = 0; // ITTIAM:gain payload length + WORD32 bit_str_fmt = 1; + WORD32 gain_stream_flag = 1; + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN, &buf_size); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_LEN"); + + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF, &p_array); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_GAIN_PAYLOAD_BUF"); + + if (buf_size > 0) { + /*Set bitstream_split_format */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT, &bit_str_fmt); + RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT"); + + memcpy(mDrcInBuf, p_array, buf_size); + /* Set number of bytes to be processed */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES_BS, + 0, &buf_size); + RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT"); + + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_GAIN_STREAM_FLAG, + &gain_stream_flag); + RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT"); + + /* Execute process */ + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_INIT, + IA_CMD_TYPE_INIT_CPY_BSF_BUFF, NULL); + RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_PARAM_BITS_FORMAT"); + + mMpegDDRCPresent = 1; + } + } +#endif + /* How much buffer is used in input buffers */ + err_code = + ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CURIDX_INPUT_BUF, + 0, bytesConsumed); + RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_CURIDX_INPUT_BUF"); + + /* Get the output bytes */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, + IA_API_CMD_GET_OUTPUT_BYTES, 0, outBytes); + RETURN_IF_FATAL(err_code, "IA_API_CMD_GET_OUTPUT_BYTES"); +#ifdef ENABLE_MPEG_D_DRC + + if (mMpegDDRCPresent == 1) { + memcpy(mDrcInBuf, mOutputBuffer + pi_preroll_frame_offset, *outBytes); + pi_preroll_frame_offset += *outBytes; + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_INPUT_BYTES, + 0, outBytes); + RETURN_IF_FATAL(err_code, "IA_API_CMD_SET_INPUT_BYTES"); + + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_EXECUTE, + IA_CMD_TYPE_DO_EXECUTE, NULL); + RETURN_IF_FATAL(err_code, "IA_CMD_TYPE_DO_EXECUTE"); + + memcpy(mOutputBuffer, mDrcOutBuf, *outBytes); + } +#endif + i_num_preroll--; + } while (i_num_preroll > 0); + } + return IA_NO_ERROR; +} + +IA_ERRORCODE SoftXAAC::deInitXAACDecoder() { + ALOGI("deInitXAACDecoder"); + + /* Tell that the input is over in this buffer */ + IA_ERRORCODE err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_INPUT_OVER, 0, NULL); + + /* Irrespective of error returned in IA_API_CMD_INPUT_OVER, free allocated memory */ + for (void* buf : mMemoryVec) { + free(buf); + } + mMemoryVec.clear(); + return err_code; +} + +IA_ERRORCODE SoftXAAC::deInitMPEGDDDrc() { + ALOGI("deInitMPEGDDDrc"); + + for (void* buf : mDrcMemoryVec) { + free(buf); + } + mDrcMemoryVec.clear(); + return IA_NO_ERROR; +} + +IA_ERRORCODE SoftXAAC::getXAACStreamInfo() { + IA_ERRORCODE err_code = IA_NO_ERROR; + + /* Sampling frequency */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_SAMP_FREQ, &mSampFreq); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_SAMP_FREQ"); + + /* Total Number of Channels */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_NUM_CHANNELS, &mNumChannels); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_NUM_CHANNELS"); + if (mNumChannels > MAX_CHANNEL_COUNT) { + ALOGE(" No of channels are more than max channels\n"); + return IA_FATAL_ERROR; + } + + /* PCM word size */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_PCM_WDSZ, &mPcmWdSz); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_PCM_WDSZ"); + if ((mPcmWdSz / 8) != 2) { + ALOGE("Invalid Number of bytes per sample: %d, Expected is 2", mPcmWdSz); + return IA_FATAL_ERROR; + } + + /* channel mask to tell the arrangement of channels in bit stream */ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_CHANNEL_MASK, &mChannelMask); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_CHANNEL_MASK"); + + /* Channel mode to tell MONO/STEREO/DUAL-MONO/NONE_OF_THESE */ + UWORD32 ui_channel_mode; + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_CHANNEL_MODE, &ui_channel_mode); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_CHANNEL_MODE"); + if (ui_channel_mode == 0) + ALOGV("Channel Mode: MONO_OR_PS\n"); + else if (ui_channel_mode == 1) + ALOGV("Channel Mode: STEREO\n"); + else if (ui_channel_mode == 2) + ALOGV("Channel Mode: DUAL-MONO\n"); + else + ALOGV("Channel Mode: NONE_OF_THESE or MULTICHANNEL\n"); + + /* Channel mode to tell SBR PRESENT/NOT_PRESENT */ + UWORD32 ui_sbr_mode; + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_SBR_MODE, &ui_sbr_mode); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_SBR_MODE"); + if (ui_sbr_mode == 0) + ALOGV("SBR Mode: NOT_PRESENT\n"); + else if (ui_sbr_mode == 1) + ALOGV("SBR Mode: PRESENT\n"); + else + ALOGV("SBR Mode: ILLEGAL\n"); + + /* mOutputFrameLength = 1024 * (1 + SBR_MODE) for AAC */ + /* For USAC it could be 1024 * 3 , support to query */ + /* not yet added in codec */ + mOutputFrameLength = 1024 * (1 + ui_sbr_mode); + + ALOGI("mOutputFrameLength %d ui_sbr_mode %d", mOutputFrameLength, ui_sbr_mode); + + return IA_NO_ERROR; +} + +IA_ERRORCODE SoftXAAC::setXAACDRCInfo(int32_t drcCut, int32_t drcBoost, int32_t drcRefLevel, + int32_t drcHeavyCompression +#ifdef ENABLE_MPEG_D_DRC + , + int32_t drEffectType +#endif +) { + IA_ERRORCODE err_code = IA_NO_ERROR; + + int32_t ui_drc_enable = 1; + int32_t i_effect_type, i_target_loudness, i_loud_norm; + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_ENABLE, &ui_drc_enable); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_ENABLE"); + if (drcCut != -1) { + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_CUT, &drcCut); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_CUT"); + } + + if (drcBoost != -1) { + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_BOOST, &drcBoost); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_BOOST"); + } + + if (drcRefLevel != -1) { + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LEVEL, &drcRefLevel); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LEVEL"); + } +#ifdef ENABLE_MPEG_D_DRC + if (drcRefLevel != -1) { + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_DRC_TARGET_LOUDNESS, &drcRefLevel); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_DRC_TARGET_LOUDNESS"); + } +#endif + if (drcHeavyCompression != -1) { + err_code = + ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_HEAVY_COMP, &drcHeavyCompression); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_HEAVY_COMP"); + } + +#ifdef ENABLE_MPEG_D_DRC + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_DRC_EFFECT_TYPE, &drEffectType); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_DRC_EFFECT_TYPE"); +#endif + +#ifdef ENABLE_MPEG_D_DRC + /*Set Effect Type*/ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_EFFECT_TYPE, &i_effect_type); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_EFFECT_TYPE"); + + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_DRC_EFFECT_TYPE, &i_effect_type); + + RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_DRC_EFFECT_TYPE"); + + /*Set target loudness */ + err_code = + ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LOUDNESS, &i_target_loudness); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_TARGET_LOUDNESS"); + + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_DRC_TARGET_LOUDNESS, &i_target_loudness); + RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_DRC_TARGET_LOUDNESS"); + + /*Set loud_norm_flag*/ + err_code = ixheaacd_dec_api(mXheaacCodecHandle, IA_API_CMD_GET_CONFIG_PARAM, + IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_LOUD_NORM, &i_loud_norm); + RETURN_IF_FATAL(err_code, "IA_ENHAACPLUS_DEC_CONFIG_PARAM_DRC_LOUD_NORM"); + + err_code = ia_drc_dec_api(mMpegDDrcHandle, IA_API_CMD_SET_CONFIG_PARAM, + IA_DRC_DEC_CONFIG_DRC_LOUD_NORM, &i_loud_norm); + + RETURN_IF_FATAL(err_code, "IA_DRC_DEC_CONFIG_DRC_LOUD_NORM"); + +#endif + + return IA_NO_ERROR; +} + +} // namespace android + +__attribute__((cfi_canonical_jump_table)) +android::SoftOMXComponent* createSoftOMXComponent(const char* name, + const OMX_CALLBACKTYPE* callbacks, + OMX_PTR appData, OMX_COMPONENTTYPE** component) { + ALOGI("createSoftOMXComponent for SoftXAACDEC"); + return new android::SoftXAAC(name, callbacks, appData, component); +} diff --git a/media/libstagefright/codecs/xaacdec/SoftXAAC.h b/media/libstagefright/codecs/xaacdec/SoftXAAC.h new file mode 100644 index 0000000..a62a797 --- /dev/null +++ b/media/libstagefright/codecs/xaacdec/SoftXAAC.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2018 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 SOFTXAAC_H_ +#define SOFTXAAC_H_ + +#include + +#include +#include +#include + +#include "ixheaacd_type_def.h" +#include "ixheaacd_error_standards.h" +#include "ixheaacd_error_handler.h" +#include "ixheaacd_apicmd_standards.h" +#include "ixheaacd_memory_standards.h" +#include "ixheaacd_aac_config.h" + +#include "impd_apicmd_standards.h" +#include "impd_drc_config_params.h" + +extern "C" IA_ERRORCODE ixheaacd_dec_api(pVOID p_ia_module_obj, WORD32 i_cmd, WORD32 i_idx, + pVOID pv_value); +extern "C" IA_ERRORCODE ia_drc_dec_api(pVOID p_ia_module_obj, WORD32 i_cmd, WORD32 i_idx, + pVOID pv_value); +extern "C" IA_ERRORCODE ixheaacd_get_config_param(pVOID p_ia_process_api_obj, pWORD32 pi_samp_freq, + pWORD32 pi_num_chan, pWORD32 pi_pcm_wd_sz, + pWORD32 pi_channel_mask); + +namespace android { + +struct SoftXAAC : public SimpleSoftOMXComponent { + SoftXAAC(const char* name, const OMX_CALLBACKTYPE* callbacks, OMX_PTR appData, + OMX_COMPONENTTYPE** component); + + protected: + virtual ~SoftXAAC(); + + virtual OMX_ERRORTYPE internalGetParameter(OMX_INDEXTYPE index, OMX_PTR params); + + virtual OMX_ERRORTYPE internalSetParameter(OMX_INDEXTYPE index, const OMX_PTR params); + + virtual void onQueueFilled(OMX_U32 portIndex); + virtual void onPortFlushCompleted(OMX_U32 portIndex); + virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled); + virtual void onReset(); + + private: + enum { + kNumInputBuffers = 4, + kNumOutputBuffers = 4, + kNumDelayBlocksMax = 8, + }; + + bool mIsADTS; + size_t mInputBufferCount; + size_t mOutputBufferCount; + bool mSignalledError; + OMX_BUFFERHEADERTYPE* mLastInHeader; + int64_t mPrevTimestamp; + int64_t mCurrentTimestamp; + uint32_t mBufSize; + + enum { NONE, AWAITING_DISABLED, AWAITING_ENABLED } mOutputPortSettingsChange; + + void initPorts(); + IA_ERRORCODE initDecoder(); + bool isConfigured() const; + IA_ERRORCODE drainDecoder(); + IA_ERRORCODE initXAACDecoder(); + IA_ERRORCODE deInitXAACDecoder(); + IA_ERRORCODE initMPEGDDDrc(); + IA_ERRORCODE deInitMPEGDDDrc(); + IA_ERRORCODE configXAACDecoder(uint8_t* inBuffer, uint32_t inBufferLength); + IA_ERRORCODE configMPEGDDrc(); + IA_ERRORCODE decodeXAACStream(uint8_t* inBuffer, uint32_t inBufferLength, + int32_t* bytesConsumed, int32_t* outBytes); + + IA_ERRORCODE configflushDecode(); + IA_ERRORCODE getXAACStreamInfo(); + IA_ERRORCODE setXAACDRCInfo(int32_t drcCut, int32_t drcBoost, int32_t drcRefLevel, + int32_t drcHeavyCompression +#ifdef ENABLE_MPEG_D_DRC + , + int32_t drEffectType +#endif + ); + + bool mEndOfInput; + bool mEndOfOutput; + + void* mXheaacCodecHandle; + void* mMpegDDrcHandle; + uint32_t mInputBufferSize; + uint32_t mOutputFrameLength; + int8_t* mInputBuffer; + int8_t* mOutputBuffer; + int32_t mSampFreq; + int32_t mNumChannels; + int32_t mPcmWdSz; + int32_t mChannelMask; + bool mIsCodecInitialized; + bool mIsCodecConfigFlushRequired; + int8_t* mDrcInBuf; + int8_t* mDrcOutBuf; + int32_t mMpegDDRCPresent; + int32_t mDRCFlag; + Vector mMemoryVec; + Vector mDrcMemoryVec; + + DISALLOW_EVIL_CONSTRUCTORS(SoftXAAC); +}; + +} // namespace android + +#endif // SOFTXAAC_H_ diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp index 6ba7896..a15148a 100644 --- a/media/libstagefright/omx/Android.bp +++ b/media/libstagefright/omx/Android.bp @@ -179,6 +179,38 @@ cc_library_shared { }, } +cc_defaults { + name: "libstagefright_softomx-defaults", + // TODO (b/316432618) Software OMX codecs are no longer used, disable building them till + // this code is removed completely. + vendor_available: true, + + cflags: [ + "-Werror", + ], + + header_libs: [ + "media_plugin_headers" + ], + + shared_libs: [ + "libstagefright_softomx", + "libstagefright_foundation", + "libutils", + "liblog", + ], + + sanitize: { + misc_undefined: [ + "signed-integer-overflow", + "unsigned-integer-overflow", + ], + cfi: true, + }, + + compile_multilib: "32", +} + cc_library_shared { name: "libstagefright_omx_utils", vendor_available: true, diff --git a/media/libstagefright/omx/OMXStore.cpp b/media/libstagefright/omx/OMXStore.cpp index b2d5a70..3be56c8 100644 --- a/media/libstagefright/omx/OMXStore.cpp +++ b/media/libstagefright/omx/OMXStore.cpp @@ -161,7 +161,6 @@ void OMXStore::addPlugin(OMXPluginBase *plugin) { } } if (skip) { - continue; } } diff --git a/services/mediacodec/Android.bp b/services/mediacodec/Android.bp index 506b3bc..a2f17c2 100644 --- a/services/mediacodec/Android.bp +++ b/services/mediacodec/Android.bp @@ -95,6 +95,31 @@ cc_binary { "android.hidl.memory@1.0", ], + runtime_libs: [ + "libstagefright_soft_aacdec", + "libstagefright_soft_aacenc", + "libstagefright_soft_amrdec", + "libstagefright_soft_amrnbenc", + "libstagefright_soft_amrwbenc", + "libstagefright_soft_avcdec", + "libstagefright_soft_avcenc", + "libstagefright_soft_flacdec", + "libstagefright_soft_flacenc", + "libstagefright_soft_g711dec", + "libstagefright_soft_gsmdec", + "libstagefright_soft_hevcdec", + "libstagefright_soft_mp3dec", + "libstagefright_soft_mpeg2dec", + "libstagefright_soft_mpeg4dec", + "libstagefright_soft_mpeg4enc", + "libstagefright_soft_opusdec", + "libstagefright_soft_rawdec", + "libstagefright_soft_vorbisdec", + "libstagefright_soft_vpxdec", + "libstagefright_soft_vpxenc", + "libstagefright_softomx_plugin", + ], + // OMX interfaces force this to stay in 32-bit mode; compile_multilib: "32", -- 2.49.0