summaryrefslogtreecommitdiffstats
path: root/media/libavextensions/stagefright/AVUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/libavextensions/stagefright/AVUtils.cpp')
-rw-r--r--media/libavextensions/stagefright/AVUtils.cpp1139
1 files changed, 1139 insertions, 0 deletions
diff --git a/media/libavextensions/stagefright/AVUtils.cpp b/media/libavextensions/stagefright/AVUtils.cpp
new file mode 100644
index 0000000..a9cd4b2
--- /dev/null
+++ b/media/libavextensions/stagefright/AVUtils.cpp
@@ -0,0 +1,1139 @@
+/*
+ * Copyright (c) 2013 - 2015, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "AVUtils"
+#include <utils/Log.h>
+#include <utils/StrongPointer.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaCodecList.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/ACodec.h>
+#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/MPEG4Writer.h>
+#include <media/stagefright/Utils.h>
+#include <media/MediaProfiles.h>
+
+#if defined(QCOM_HARDWARE) || defined(FLAC_OFFLOAD_ENABLED)
+#include "QCMediaDefs.h"
+#include "QCMetaData.h"
+#ifdef FLAC_OFFLOAD_ENABLED
+#include "audio_defs.h"
+#endif
+#endif
+
+#include <binder/IPCThreadState.h>
+#include <camera/CameraParameters.h>
+
+#include "common/ExtensionsLoader.hpp"
+#include "stagefright/AVExtensions.h"
+
+namespace android {
+
+static const uint8_t kHEVCNalUnitTypeVidParamSet = 0x20;
+static const uint8_t kHEVCNalUnitTypeSeqParamSet = 0x21;
+static const uint8_t kHEVCNalUnitTypePicParamSet = 0x22;
+
+enum MetaKeyType{
+ INT32, INT64, STRING, DATA, CSD
+};
+
+struct MetaKeyEntry{
+ int MetaKey;
+ const char* MsgKey;
+ MetaKeyType KeyType;
+};
+
+static const MetaKeyEntry MetaKeyTable[] {
+#ifdef QCOM_HARDWARE
+ {kKeyAacCodecSpecificData , "aac-codec-specific-data", CSD},
+ {kKeyDivXVersion , "divx-version" , INT32}, // int32_t
+ {kKeyDivXDrm , "divx-drm" , DATA}, // void *
+ {kKeyWMAEncodeOpt , "wma-encode-opt" , INT32}, // int32_t
+ {kKeyWMABlockAlign , "wma-block-align" , INT32}, // int32_t
+ {kKeyWMAAdvEncOpt1 , "wma-adv-enc-opt1" , INT32}, // int16_t
+ {kKeyWMAAdvEncOpt2 , "wma-adv-enc-opt2" , INT32}, // int32_t
+ {kKeyWMAFormatTag , "wma-format-tag" , INT32}, // int32_t
+ {kKeyWMABitspersample , "wma-bits-per-sample" , INT32}, // int32_t
+ {kKeyWMAVirPktSize , "wma-vir-pkt-size" , INT32}, // int32_t
+ {kKeyWMAChannelMask , "wma-channel-mask" , INT32}, // int32_t
+ {kKeyFileFormat , "file-format" , STRING}, // cstring
+
+ {kkeyAacFormatAdif , "aac-format-adif" , INT32}, // bool (int32_t)
+ {kkeyAacFormatLtp , "aac-format-ltp" , INT32},
+
+ //DTS subtype
+ {kKeyDTSSubtype , "dts-subtype" , INT32}, //int32_t
+
+ //Extractor sets this
+ {kKeyUseArbitraryMode , "use-arbitrary-mode" , INT32}, //bool (int32_t)
+ {kKeySmoothStreaming , "smooth-streaming" , INT32}, //bool (int32_t)
+ {kKeyHFR , "hfr" , INT32}, // int32_t
+#endif
+#ifdef FLAC_OFFLOAD_ENABLED
+ {kKeyMinBlkSize , "min-block-size" , INT32},
+ {kKeyMaxBlkSize , "max-block-size" , INT32},
+ {kKeyMinFrmSize , "min-frame-size" , INT32},
+ {kKeyMaxFrmSize , "max-frame-size" , INT32},
+#endif
+
+
+ {kKeyBitRate , "bitrate" , INT32},
+ {kKeySampleRate , "sample-rate" , INT32},
+ {kKeyChannelCount , "channel-count" , INT32},
+ {kKeyRawCodecSpecificData , "raw-codec-specific-data", CSD},
+
+ {kKeyBitsPerSample , "bits-per-sample" , INT32},
+ {kKeyCodecId , "codec-id" , INT32},
+ {kKeySampleFormat , "sample-format" , INT32},
+ {kKeyBlockAlign , "block-align" , INT32},
+ {kKeyCodedSampleBits , "coded-sample-bits" , INT32},
+ {kKeyAACAOT , "aac-profile" , INT32},
+ {kKeyRVVersion , "rv-version" , INT32},
+ {kKeyWMAVersion , "wma-version" , INT32}, // int32_t
+ {kKeyWMVVersion , "wmv-version" , INT32},
+};
+
+status_t AVUtils::convertMetaDataToMessage(
+ const sp<MetaData> &meta, sp<AMessage> *format) {
+ const char * str_val;
+ int32_t int32_val;
+ int64_t int64_val;
+ uint32_t data_type;
+ const void * data;
+ size_t size;
+ static const size_t numMetaKeys =
+ sizeof(MetaKeyTable) / sizeof(MetaKeyTable[0]);
+ size_t i;
+ for (i = 0; i < numMetaKeys; ++i) {
+ if (MetaKeyTable[i].KeyType == INT32 &&
+ meta->findInt32(MetaKeyTable[i].MetaKey, &int32_val)) {
+ ALOGV("found metakey %s of type int32", MetaKeyTable[i].MsgKey);
+ format->get()->setInt32(MetaKeyTable[i].MsgKey, int32_val);
+ } else if (MetaKeyTable[i].KeyType == INT64 &&
+ meta->findInt64(MetaKeyTable[i].MetaKey, &int64_val)) {
+ ALOGV("found metakey %s of type int64", MetaKeyTable[i].MsgKey);
+ format->get()->setInt64(MetaKeyTable[i].MsgKey, int64_val);
+ } else if (MetaKeyTable[i].KeyType == STRING &&
+ meta->findCString(MetaKeyTable[i].MetaKey, &str_val)) {
+ ALOGV("found metakey %s of type string", MetaKeyTable[i].MsgKey);
+ format->get()->setString(MetaKeyTable[i].MsgKey, str_val);
+ } else if ( (MetaKeyTable[i].KeyType == DATA ||
+ MetaKeyTable[i].KeyType == CSD) &&
+ meta->findData(MetaKeyTable[i].MetaKey, &data_type, &data, &size)) {
+ ALOGV("found metakey %s of type data", MetaKeyTable[i].MsgKey);
+ if (MetaKeyTable[i].KeyType == CSD) {
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+ if (strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+ sp<ABuffer> buffer = new ABuffer(size);
+ memcpy(buffer->data(), data, size);
+ buffer->meta()->setInt32("csd", true);
+ buffer->meta()->setInt64("timeUs", 0);
+ format->get()->setBuffer("csd-0", buffer);
+ } else {
+ const uint8_t *ptr = (const uint8_t *)data;
+ CHECK(size >= 8);
+ int seqLength = 0, picLength = 0;
+ for (size_t i = 4; i < (size - 4); i++)
+ {
+ if ((*(ptr + i) == 0) && (*(ptr + i + 1) == 0) &&
+ (*(ptr + i + 2) == 0) && (*(ptr + i + 3) == 1))
+ seqLength = i;
+ }
+ sp<ABuffer> buffer = new ABuffer(seqLength);
+ memcpy(buffer->data(), data, seqLength);
+ buffer->meta()->setInt32("csd", true);
+ buffer->meta()->setInt64("timeUs", 0);
+ format->get()->setBuffer("csd-0", buffer);
+ picLength=size-seqLength;
+ sp<ABuffer> buffer1 = new ABuffer(picLength);
+ memcpy(buffer1->data(), (const uint8_t *)data + seqLength, picLength);
+ buffer1->meta()->setInt32("csd", true);
+ buffer1->meta()->setInt64("timeUs", 0);
+ format->get()->setBuffer("csd-1", buffer1);
+ }
+ } else {
+ sp<ABuffer> buffer = new ABuffer(size);
+ memcpy(buffer->data(), data, size);
+ format->get()->setBuffer(MetaKeyTable[i].MsgKey, buffer);
+ }
+ }
+ }
+ return OK;
+}
+
+struct mime_conv_t {
+ const char* mime;
+ audio_format_t format;
+};
+
+static const struct mime_conv_t mimeLookup[] = {
+ { MEDIA_MIMETYPE_AUDIO_MPEG, AUDIO_FORMAT_MP3 },
+ { MEDIA_MIMETYPE_AUDIO_RAW, AUDIO_FORMAT_PCM_16_BIT },
+ { MEDIA_MIMETYPE_AUDIO_AMR_NB, AUDIO_FORMAT_AMR_NB },
+ { MEDIA_MIMETYPE_AUDIO_AMR_WB, AUDIO_FORMAT_AMR_WB },
+ { MEDIA_MIMETYPE_AUDIO_AAC, AUDIO_FORMAT_AAC },
+ { MEDIA_MIMETYPE_AUDIO_VORBIS, AUDIO_FORMAT_VORBIS },
+ { MEDIA_MIMETYPE_AUDIO_OPUS, AUDIO_FORMAT_OPUS},
+#ifdef QCOM_HARDWARE
+ { MEDIA_MIMETYPE_AUDIO_AC3, AUDIO_FORMAT_AC3 },
+ { MEDIA_MIMETYPE_AUDIO_AMR_WB_PLUS, AUDIO_FORMAT_AMR_WB_PLUS },
+ { MEDIA_MIMETYPE_AUDIO_DTS, AUDIO_FORMAT_DTS },
+ { MEDIA_MIMETYPE_AUDIO_EAC3, AUDIO_FORMAT_E_AC3 },
+ { MEDIA_MIMETYPE_AUDIO_EVRC, AUDIO_FORMAT_EVRC },
+ { MEDIA_MIMETYPE_AUDIO_QCELP, AUDIO_FORMAT_QCELP },
+ { MEDIA_MIMETYPE_AUDIO_WMA, AUDIO_FORMAT_WMA },
+ { MEDIA_MIMETYPE_AUDIO_FLAC, AUDIO_FORMAT_FLAC },
+ { MEDIA_MIMETYPE_CONTAINER_QTIFLAC, AUDIO_FORMAT_FLAC },
+#ifdef DOLBY_UDC
+ { MEDIA_MIMETYPE_AUDIO_EAC3_JOC, AUDIO_FORMAT_E_AC3_JOC },
+#endif
+#endif
+ { 0, AUDIO_FORMAT_INVALID }
+};
+
+status_t AVUtils::mapMimeToAudioFormat(
+ audio_format_t& format, const char* mime) {
+ const struct mime_conv_t* p = &mimeLookup[0];
+ while (p->mime != NULL) {
+ if (0 == strcasecmp(mime, p->mime)) {
+ format = p->format;
+ return OK;
+ }
+ ++p;
+ }
+
+ return BAD_VALUE;
+}
+
+status_t AVUtils::sendMetaDataToHal(
+ const sp<MetaData>& meta, AudioParameter *param){
+#ifdef FLAC_OFFLOAD_ENABLED
+ int32_t minBlkSize, maxBlkSize, minFrmSize, maxFrmSize; //FLAC params
+ if (meta->findInt32(kKeyMinBlkSize, &minBlkSize)) {
+ param->addInt(String8(AUDIO_OFFLOAD_CODEC_FLAC_MIN_BLK_SIZE), minBlkSize);
+ }
+ if (meta->findInt32(kKeyMaxBlkSize, &maxBlkSize)) {
+ param->addInt(String8(AUDIO_OFFLOAD_CODEC_FLAC_MAX_BLK_SIZE), maxBlkSize);
+ }
+ if (meta->findInt32(kKeyMinFrmSize, &minFrmSize)) {
+ param->addInt(String8(AUDIO_OFFLOAD_CODEC_FLAC_MIN_FRAME_SIZE), minFrmSize);
+ }
+ if (meta->findInt32(kKeyMaxFrmSize, &maxFrmSize)) {
+ param->addInt(String8(AUDIO_OFFLOAD_CODEC_FLAC_MAX_FRAME_SIZE), maxFrmSize);
+ }
+#else
+ (void)meta;
+ (void)param;
+#endif
+ return OK;
+}
+
+bool AVUtils::is24bitPCMOffloadEnabled() {
+ char propPCMOfload[PROPERTY_VALUE_MAX] = {0};
+ property_get("audio.offload.pcm.24bit.enable", propPCMOfload, "0");
+ if (!strncmp(propPCMOfload, "true", 4) || atoi(propPCMOfload))
+ return true;
+ else
+ return false;
+}
+
+bool AVUtils::is16bitPCMOffloadEnabled() {
+ char propPCMOfload[PROPERTY_VALUE_MAX] = {0};
+ property_get("audio.offload.pcm.16bit.enable", propPCMOfload, "0");
+ if (!strncmp(propPCMOfload, "true", 4) || atoi(propPCMOfload))
+ return true;
+ else
+ return false;
+}
+
+
+int AVUtils::getAudioSampleBits(const sp<MetaData> &meta) {
+ int32_t bits = 16;
+ audio_format_t audioFormat = AUDIO_FORMAT_INVALID;
+ if (meta->findInt32('pfmt', (int32_t *)&audioFormat)) {
+ bits = audio_bytes_per_sample(audioFormat) * 8;
+ } else if (meta->findInt32(kKeyBitsPerSample, &bits)) {
+ return bits;
+ }
+ return bits;
+}
+
+int AVUtils::getAudioSampleBits(const sp<AMessage> &format) {
+ int32_t bits = 16;
+ audio_format_t audioFormat = AUDIO_FORMAT_INVALID;
+ if (format->findInt32("pcm-format", (int32_t *)&audioFormat)) {
+ bits = audio_bytes_per_sample(audioFormat) * 8;
+ } else if (format->findInt32("bits-per-sample", &bits)) {
+ return bits;
+ }
+ return bits;
+}
+
+void AVUtils::setPcmSampleBits(const sp<AMessage> &format, int32_t bitWidth) {
+ format->setInt32("bits-per-sample", bitWidth);
+}
+
+void AVUtils::setPcmSampleBits(const sp<MetaData> &meta, int32_t bitWidth) {
+ meta->setInt32(kKeyBitsPerSample, bitWidth);
+}
+
+audio_format_t AVUtils::updateAudioFormat(audio_format_t audioFormat,
+ const sp<MetaData> &meta){
+ int32_t bits = getAudioSampleBits(meta);
+
+ ALOGV("updateAudioFormat %x %d", audioFormat, bits);
+ meta->dumpToLog();
+
+ // Override audio format for PCM offload
+ if (audio_is_linear_pcm(audioFormat)) {
+ if (bits > 16 && is24bitPCMOffloadEnabled()) {
+ audioFormat = AUDIO_FORMAT_PCM_24_BIT_OFFLOAD;
+ meta->setInt32(kKeyBitsPerSample, 24);
+ } else if (bits == 16 && is16bitPCMOffloadEnabled()) {
+ audioFormat = AUDIO_FORMAT_PCM_16_BIT_OFFLOAD;
+ }
+ }
+
+ return audioFormat;
+}
+
+audio_format_t AVUtils::updateAudioFormat(audio_format_t audioFormat,
+ const sp<AMessage> &format){
+ int32_t bits = getAudioSampleBits(format);
+
+ ALOGV("updateAudioFormat %x %d %s", audioFormat, bits, format->debugString().c_str());
+
+ // Override audio format for PCM offload
+ if (audio_is_linear_pcm(audioFormat)) {
+ if (bits > 16 && is24bitPCMOffloadEnabled()) {
+ audioFormat = AUDIO_FORMAT_PCM_24_BIT_OFFLOAD;
+ format->setInt32("bits-per-sample", 24);
+ } else if (bits == 16 && is16bitPCMOffloadEnabled()) {
+ audioFormat = AUDIO_FORMAT_PCM_16_BIT_OFFLOAD;
+ }
+ }
+
+ return audioFormat;
+}
+
+static bool dumbSniffer(
+ const sp<DataSource> &, String8 *,
+ float *, sp<AMessage> *) {
+ return false;
+}
+
+DataSource::SnifferFunc AVUtils::getExtendedSniffer() {
+ return dumbSniffer;
+}
+
+sp<MediaCodec> AVUtils::createCustomComponentByName(
+ const sp<ALooper> &, const char* , bool, const sp<AMessage> &) {
+ return NULL;
+}
+
+bool AVUtils::canOffloadAPE(const sp<MetaData> &) {
+ return true;
+}
+
+int32_t AVUtils::getAudioMaxInputBufferSize(audio_format_t, const sp<AMessage> &) {
+ return 0;
+}
+
+bool AVUtils::mapAACProfileToAudioFormat(const sp<MetaData> &, audio_format_t &,
+ uint64_t /*eAacProfile*/) {
+ return false ;
+}
+
+bool AVUtils::mapAACProfileToAudioFormat(const sp<AMessage> &, audio_format_t &,
+ uint64_t /*eAacProfile*/) {
+ return false ;
+}
+
+bool AVUtils::isEnhancedExtension(const char *) {
+ return false;
+}
+
+bool AVUtils::HEVCMuxer::reassembleHEVCCSD(const AString &mime, sp<ABuffer> csd0, sp<MetaData> &meta) {
+ if (!isVideoHEVC(mime.c_str())) {
+ return false;
+ }
+ void *csd = NULL;
+ size_t size = 0;
+
+ if (makeHEVCCodecSpecificData(csd0->data(), csd0->size(), &csd, &size) == OK) {
+ meta->setData(kKeyHVCC, kTypeHVCC, csd, size);
+ free(csd);
+ return true;
+ }
+ ALOGE("Failed to reassemble HVCC data");
+ return false;
+}
+
+void AVUtils::HEVCMuxer::writeHEVCFtypBox(MPEG4Writer *writer) {
+ ALOGV("writeHEVCFtypBox called");
+ writer->writeFourcc("3gp5");
+ writer->writeInt32(0);
+ writer->writeFourcc("hvc1");
+ writer->writeFourcc("hev1");
+ writer->writeFourcc("3gp5");
+}
+
+status_t AVUtils::HEVCMuxer::makeHEVCCodecSpecificData(
+ const uint8_t *data, size_t size, void** codecSpecificData,
+ size_t *codecSpecificDataSize) {
+ ALOGV("makeHEVCCodecSpecificData called");
+
+ if (*codecSpecificData != NULL) {
+ ALOGE("Already have codec specific data");
+ return ERROR_MALFORMED;
+ }
+
+ if (size < 4) {
+ ALOGE("Codec specific data length too short: %zu", size);
+ return ERROR_MALFORMED;
+ }
+
+ // Data is in the form of HVCCodecSpecificData
+ if (memcmp("\x00\x00\x00\x01", data, 4)) {
+ // 23 byte fixed header
+ if (size < 23) {
+ ALOGE("Codec specific data length too short: %zu", size);
+ return ERROR_MALFORMED;
+ }
+
+ *codecSpecificData = malloc(size);
+
+ if (*codecSpecificData != NULL) {
+ *codecSpecificDataSize = size;
+ memcpy(*codecSpecificData, data, size);
+ return OK;
+ }
+
+ return NO_MEMORY;
+ }
+
+ List<HEVCParamSet> vidParamSets;
+ List<HEVCParamSet> seqParamSets;
+ List<HEVCParamSet> picParamSets;
+
+ if ((*codecSpecificDataSize = parseHEVCCodecSpecificData(data, size,
+ vidParamSets, seqParamSets, picParamSets)) == 0) {
+ ALOGE("cannot parser codec specific data, bailing out");
+ return ERROR_MALFORMED;
+ }
+
+ size_t numOfNALArray = 0;
+ bool doneWritingVPS = true, doneWritingSPS = true, doneWritingPPS = true;
+
+ if (!vidParamSets.empty()) {
+ doneWritingVPS = false;
+ ++numOfNALArray;
+ }
+
+ if (!seqParamSets.empty()) {
+ doneWritingSPS = false;
+ ++numOfNALArray;
+ }
+
+ if (!picParamSets.empty()) {
+ doneWritingPPS = false;
+ ++numOfNALArray;
+ }
+
+ //additional 23 bytes needed (22 bytes for hvc1 header + 1 byte for number of arrays)
+ *codecSpecificDataSize += 23;
+ //needed 3 bytes per NAL array
+ *codecSpecificDataSize += 3 * numOfNALArray;
+
+ int count = 0;
+ void *codecConfigData = malloc(*codecSpecificDataSize);
+ if (codecSpecificData == NULL) {
+ ALOGE("Failed to allocate memory, bailing out");
+ return NO_MEMORY;
+ }
+
+ uint8_t *header = (uint8_t *)codecConfigData;
+ // 8 - bit version
+ header[0] = 1;
+ //Profile space 2 bit, tier flag 1 bit and profile IDC 5 bit
+ header[1] = 0x00;
+ // 32 - bit compatibility flag
+ header[2] = 0x00;
+ header[3] = 0x00;
+ header[4] = 0x00;
+ header[5] = 0x00;
+ // 48 - bit general constraint indicator flag
+ header[6] = header[7] = header[8] = 0x00;
+ header[9] = header[10] = header[11] = 0x00;
+ // 8 - bit general IDC level
+ header[12] = 0x00;
+ // 4 - bit reserved '1111'
+ // 12 - bit spatial segmentation idc
+ header[13] = 0xf0;
+ header[14] = 0x00;
+ // 6 - bit reserved '111111'
+ // 2 - bit parallelism Type
+ header[15] = 0xfc;
+ // 6 - bit reserved '111111'
+ // 2 - bit chromaFormat
+ header[16] = 0xfc;
+ // 5 - bit reserved '11111'
+ // 3 - bit DepthLumaMinus8
+ header[17] = 0xf8;
+ // 5 - bit reserved '11111'
+ // 3 - bit DepthChromaMinus8
+ header[18] = 0xf8;
+ // 16 - bit average frame rate
+ header[19] = header[20] = 0x00;
+ // 2 - bit constant frame rate
+ // 3 - bit num temporal layers
+ // 1 - bit temoral nested
+ // 2 - bit lengthSizeMinusOne
+ header[21] = 0x07;
+
+ // 8-bit number of NAL types
+ header[22] = (uint8_t)numOfNALArray;
+
+ header += 23;
+ count += 23;
+
+ bool ifProfileIDCAlreadyFilled = false;
+
+ if (!doneWritingVPS) {
+ doneWritingVPS = true;
+ ALOGV("Writing VPS");
+ //8-bit, last 6 bit for NAL type
+ header[0] = 0x20; // NAL type is VPS
+ //16-bit, number of nal Units
+ uint16_t vidParamSetLength = vidParamSets.size();
+ header[1] = vidParamSetLength >> 8;
+ header[2] = vidParamSetLength & 0xff;
+
+ header += 3;
+ count += 3;
+
+ for (List<HEVCParamSet>::iterator it = vidParamSets.begin();
+ it != vidParamSets.end(); ++it) {
+ // 16-bit video parameter set length
+ uint16_t vidParamSetLength = it->mLength;
+ header[0] = vidParamSetLength >> 8;
+ header[1] = vidParamSetLength & 0xff;
+
+ extractNALRBSPData(it->mData, it->mLength,
+ (uint8_t **)&codecConfigData,
+ &ifProfileIDCAlreadyFilled);
+
+ // VPS NAL unit (video parameter length bytes)
+ memcpy(&header[2], it->mData, vidParamSetLength);
+ header += (2 + vidParamSetLength);
+ count += (2 + vidParamSetLength);
+ }
+ }
+
+ if (!doneWritingSPS) {
+ doneWritingSPS = true;
+ ALOGV("Writting SPS");
+ //8-bit, last 6 bit for NAL type
+ header[0] = 0x21; // NAL type is SPS
+ //16-bit, number of nal Units
+ uint16_t seqParamSetLength = seqParamSets.size();
+ header[1] = seqParamSetLength >> 8;
+ header[2] = seqParamSetLength & 0xff;
+
+ header += 3;
+ count += 3;
+
+ for (List<HEVCParamSet>::iterator it = seqParamSets.begin();
+ it != seqParamSets.end(); ++it) {
+ // 16-bit sequence parameter set length
+ uint16_t seqParamSetLength = it->mLength;
+
+ // 16-bit number of NAL units of this type
+ header[0] = seqParamSetLength >> 8;
+ header[1] = seqParamSetLength & 0xff;
+
+ extractNALRBSPData(it->mData, it->mLength,
+ (uint8_t **)&codecConfigData,
+ &ifProfileIDCAlreadyFilled);
+
+ // SPS NAL unit (sequence parameter length bytes)
+ memcpy(&header[2], it->mData, seqParamSetLength);
+ header += (2 + seqParamSetLength);
+ count += (2 + seqParamSetLength);
+ }
+ }
+
+ if (!doneWritingPPS) {
+ doneWritingPPS = true;
+ ALOGV("writing PPS");
+ //8-bit, last 6 bit for NAL type
+ header[0] = 0x22; // NAL type is PPS
+ //16-bit, number of nal Units
+ uint16_t picParamSetLength = picParamSets.size();
+ header[1] = picParamSetLength >> 8;
+ header[2] = picParamSetLength & 0xff;
+
+ header += 3;
+ count += 3;
+
+ for (List<HEVCParamSet>::iterator it = picParamSets.begin();
+ it != picParamSets.end(); ++it) {
+ // 16-bit picture parameter set length
+ uint16_t picParamSetLength = it->mLength;
+ header[0] = picParamSetLength >> 8;
+ header[1] = picParamSetLength & 0xff;
+
+ // PPS Nal unit (picture parameter set length bytes)
+ memcpy(&header[2], it->mData, picParamSetLength);
+ header += (2 + picParamSetLength);
+ count += (2 + picParamSetLength);
+ }
+ }
+ *codecSpecificData = codecConfigData;
+ return OK;
+}
+
+const char *AVUtils::HEVCMuxer::getFourCCForMime(const char * mime) {
+ if (isVideoHEVC(mime)) {
+ return "hvc1";
+ }
+ return NULL;
+}
+
+void AVUtils::HEVCMuxer::writeHvccBox(MPEG4Writer *writer,
+ void *codecSpecificData, size_t codecSpecificDataSize,
+ bool useNalLengthFour) {
+ ALOGV("writeHvccBox called");
+ CHECK(codecSpecificData);
+ CHECK_GE(codecSpecificDataSize, 23);
+
+ // Patch hvcc's lengthSize field to match the number
+ // of bytes we use to indicate the size of a nal unit.
+ uint8_t *ptr = (uint8_t *)codecSpecificData;
+ ptr[21] = (ptr[21] & 0xfc) | (useNalLengthFour? 3 : 1);
+ writer->beginBox("hvcC");
+ writer->write(codecSpecificData, codecSpecificDataSize);
+ writer->endBox(); // hvcC
+}
+
+bool AVUtils::HEVCMuxer::isVideoHEVC(const char * mime) {
+ return (!strncasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC,
+ strlen(MEDIA_MIMETYPE_VIDEO_HEVC)));
+}
+
+status_t AVUtils::HEVCMuxer::extractNALRBSPData(const uint8_t *data,
+ size_t size,
+ uint8_t **header,
+ bool *alreadyFilled) {
+ ALOGV("extractNALRBSPData called");
+ CHECK_GE(size, 2);
+
+ uint8_t type = data[0] >> 1;
+ type = 0x3f & type;
+
+ //start parsing here
+ size_t rbspSize = 0;
+ uint8_t *rbspData = (uint8_t *) malloc(size);
+
+ if (rbspData == NULL) {
+ ALOGE("allocation failed");
+ return UNKNOWN_ERROR;
+ }
+
+ //populate rbsp data start from i+2, search for 0x000003,
+ //and ignore emulation_prevention byte
+ size_t itt = 2;
+ while (itt < size) {
+ if ((itt+2 < size) && (!memcmp("\x00\x00\x03", &data[itt], 3) )) {
+ rbspData[rbspSize++] = data[itt++];
+ rbspData[rbspSize++] = data[itt++];
+ itt++;
+ } else {
+ rbspData[rbspSize++] = data[itt++];
+ }
+ }
+
+ uint8_t maxSubLayerMinus1 = 0;
+
+ //parser profileTierLevel
+ if (type == kHEVCNalUnitTypeVidParamSet) { // if VPS
+ ALOGV("its VPS ... start with 5th byte");
+ if (rbspSize < 5) {
+ free(rbspData);
+ return ERROR_MALFORMED;
+ }
+
+ maxSubLayerMinus1 = 0x0E & rbspData[1];
+ maxSubLayerMinus1 = maxSubLayerMinus1 >> 1;
+ parserProfileTierLevel(&rbspData[4], rbspSize - 4, header, alreadyFilled);
+
+ } else if (type == kHEVCNalUnitTypeSeqParamSet) {
+ ALOGV("its SPS .. start with 2nd byte");
+ if (rbspSize < 2) {
+ free(rbspData);
+ return ERROR_MALFORMED;
+ }
+
+ maxSubLayerMinus1 = 0x0E & rbspData[0];
+ maxSubLayerMinus1 = maxSubLayerMinus1 >> 1;
+
+ parserProfileTierLevel(&rbspData[1], rbspSize - 1, header, alreadyFilled);
+ }
+ free(rbspData);
+ return OK;
+}
+
+status_t AVUtils::HEVCMuxer::parserProfileTierLevel(const uint8_t *data, size_t size,
+ uint8_t **header, bool *alreadyFilled) {
+ CHECK_GE(size, 12);
+ uint8_t *tmpHeader = *header;
+ ALOGV("parserProfileTierLevel called");
+ uint8_t generalProfileSpace; //2 bit
+ uint8_t generalTierFlag; //1 bit
+ uint8_t generalProfileIdc; //5 bit
+ uint8_t generalProfileCompatibilityFlag[4];
+ uint8_t generalConstraintIndicatorFlag[6];
+ uint8_t generalLevelIdc; //8 bit
+
+ // Need first 12 bytes
+
+ // First byte will give below info
+ generalProfileSpace = 0xC0 & data[0];
+ generalProfileSpace = generalProfileSpace > 6;
+ generalTierFlag = 0x20 & data[0];
+ generalTierFlag = generalTierFlag > 5;
+ generalProfileIdc = 0x1F & data[0];
+
+ // Next 4 bytes is compatibility flag
+ memcpy(&generalProfileCompatibilityFlag, &data[1], 4);
+
+ // Next 6 bytes is constraint indicator flag
+ memcpy(&generalConstraintIndicatorFlag, &data[5], 6);
+
+ // Next 1 byte is general Level IDC
+ generalLevelIdc = data[11];
+
+ if (*alreadyFilled) {
+ bool overwriteTierValue = false;
+
+ //find profile space
+ uint8_t prvGeneralProfileSpace; //2 bit
+ prvGeneralProfileSpace = 0xC0 & tmpHeader[1];
+ prvGeneralProfileSpace = prvGeneralProfileSpace > 6;
+ //prev needs to be same as current
+ if (prvGeneralProfileSpace != generalProfileSpace) {
+ ALOGW("Something wrong!!! profile space mismatch");
+ }
+
+ uint8_t prvGeneralTierFlag = 0x20 & tmpHeader[1];
+ prvGeneralTierFlag = prvGeneralTierFlag > 5;
+
+ if (prvGeneralTierFlag < generalTierFlag) {
+ overwriteTierValue = true;
+ ALOGV("Found higher tier value, replacing old one");
+ }
+
+ uint8_t prvGeneralProfileIdc = 0x1F & tmpHeader[1];
+
+ if (prvGeneralProfileIdc != generalProfileIdc) {
+ ALOGW("Something is wrong!!! profile space mismatch");
+ }
+
+ if (overwriteTierValue) {
+ tmpHeader[1] = data[0];
+ }
+
+ //general level IDC should be set highest among all
+ if (tmpHeader[12] < data[11]) {
+ tmpHeader[12] = data[11];
+ ALOGV("Found higher level IDC value, replacing old one");
+ }
+
+ } else {
+ *alreadyFilled = true;
+ tmpHeader[1] = data[0];
+ memcpy(&tmpHeader[2], &data[1], 4);
+ memcpy(&tmpHeader[6], &data[5], 6);
+ tmpHeader[12] = data[11];
+ }
+
+ char printCodecConfig[PROPERTY_VALUE_MAX];
+ property_get("hevc.mux.print.codec.config", printCodecConfig, "0");
+
+ if (atoi(printCodecConfig)) {
+ //if property enabled, print these values
+ ALOGI("Start::-----------------");
+ ALOGI("generalProfileSpace = %2x", generalProfileSpace);
+ ALOGI("generalTierFlag = %2x", generalTierFlag);
+ ALOGI("generalProfileIdc = %2x", generalProfileIdc);
+ ALOGI("generalLevelIdc = %2x", generalLevelIdc);
+ ALOGI("generalProfileCompatibilityFlag = %2x %2x %2x %2x", generalProfileCompatibilityFlag[0],
+ generalProfileCompatibilityFlag[1], generalProfileCompatibilityFlag[2],
+ generalProfileCompatibilityFlag[3]);
+ ALOGI("generalConstraintIndicatorFlag = %2x %2x %2x %2x %2x %2x", generalConstraintIndicatorFlag[0],
+ generalConstraintIndicatorFlag[1], generalConstraintIndicatorFlag[2],
+ generalConstraintIndicatorFlag[3], generalConstraintIndicatorFlag[4],
+ generalConstraintIndicatorFlag[5]);
+ ALOGI("End::-----------------");
+ }
+
+ return OK;
+}
+static const uint8_t *findNextStartCode(
+ const uint8_t *data, size_t length) {
+ ALOGV("findNextStartCode: %p %zu", data, length);
+
+ size_t bytesLeft = length;
+
+ while (bytesLeft > 4 &&
+ memcmp("\x00\x00\x00\x01", &data[length - bytesLeft], 4)) {
+ --bytesLeft;
+ }
+
+ if (bytesLeft <= 4) {
+ bytesLeft = 0; // Last parameter set
+ }
+
+ return &data[length - bytesLeft];
+}
+
+const uint8_t *AVUtils::HEVCMuxer::parseHEVCParamSet(
+ const uint8_t *data, size_t length, List<HEVCParamSet> &paramSetList, size_t *paramSetLen) {
+ ALOGV("parseHEVCParamSet called");
+ const uint8_t *nextStartCode = findNextStartCode(data, length);
+ *paramSetLen = nextStartCode - data;
+ if (*paramSetLen == 0) {
+ ALOGE("Param set is malformed, since its length is 0");
+ return NULL;
+ }
+
+ HEVCParamSet paramSet(*paramSetLen, data);
+ paramSetList.push_back(paramSet);
+
+ return nextStartCode;
+}
+
+static void getHEVCNalUnitType(uint8_t byte, uint8_t* type) {
+ ALOGV("getNalUnitType: %d", (int)byte);
+ // nal_unit_type: 6-bit unsigned integer
+ *type = (byte & 0x7E) >> 1;
+}
+
+size_t AVUtils::HEVCMuxer::parseHEVCCodecSpecificData(
+ const uint8_t *data, size_t size,List<HEVCParamSet> &vidParamSet,
+ List<HEVCParamSet> &seqParamSet, List<HEVCParamSet> &picParamSet ) {
+ ALOGV("parseHEVCCodecSpecificData called");
+ // Data starts with a start code.
+ // VPS, SPS and PPS are separated with start codes.
+ uint8_t type = kHEVCNalUnitTypeVidParamSet;
+ bool gotVps = false;
+ bool gotSps = false;
+ bool gotPps = false;
+ const uint8_t *tmp = data;
+ const uint8_t *nextStartCode = data;
+ size_t bytesLeft = size;
+ size_t paramSetLen = 0;
+ size_t codecSpecificDataSize = 0;
+ while (bytesLeft > 4 && !memcmp("\x00\x00\x00\x01", tmp, 4)) {
+ getHEVCNalUnitType(*(tmp + 4), &type);
+ if (type == kHEVCNalUnitTypeVidParamSet) {
+ nextStartCode = parseHEVCParamSet(tmp + 4, bytesLeft - 4, vidParamSet, &paramSetLen);
+ if (!gotVps) {
+ gotVps = true;
+ }
+ } else if (type == kHEVCNalUnitTypeSeqParamSet) {
+ nextStartCode = parseHEVCParamSet(tmp + 4, bytesLeft - 4, seqParamSet, &paramSetLen);
+ if (!gotSps) {
+ gotSps = true;
+ }
+
+ } else if (type == kHEVCNalUnitTypePicParamSet) {
+ nextStartCode = parseHEVCParamSet(tmp + 4, bytesLeft - 4, picParamSet, &paramSetLen);
+ if (!gotPps) {
+ gotPps = true;
+ }
+ } else {
+ ALOGE("Only VPS, SPS and PPS Nal units are expected");
+ return 0;
+ }
+
+ if (nextStartCode == NULL) {
+ ALOGE("Next start code is NULL");
+ return 0;
+ }
+
+ // Move on to find the next parameter set
+ bytesLeft -= nextStartCode - tmp;
+ tmp = nextStartCode;
+ codecSpecificDataSize += (2 + paramSetLen);
+ }
+
+#if 0
+//not adding this check now, but might be needed
+ if (!gotVps || !gotVps || !gotVps ) {
+ return 0;
+ }
+#endif
+
+ return codecSpecificDataSize;
+}
+
+void AVUtils::HEVCMuxer::getHEVCCodecSpecificDataFromInputFormatIfPossible(
+ sp<MetaData> meta, void ** codecSpecificData,
+ size_t * codecSpecificDataSize, bool * gotAllCodecSpecificData) {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ //kKeyHVCC needs to be populated
+ if (meta->findData(kKeyHVCC, &type, &data, &size)) {
+ *codecSpecificData = malloc(size);
+ CHECK(*codecSpecificData != NULL);
+ *codecSpecificDataSize = size;
+ memcpy(*codecSpecificData, data, size);
+ *gotAllCodecSpecificData = true;
+ } else {
+ ALOGW("getHEVCCodecConfigData:: failed to find kKeyHvcc");
+ }
+}
+
+bool AVUtils::isAudioMuxFormatSupported(const char * mime) {
+ if (mime == NULL) {
+ ALOGE("NULL audio mime type");
+ return false;
+ }
+
+ if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)
+ || !strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)
+ || !strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
+ return true;
+ }
+ return false;
+}
+
+void AVUtils::cacheCaptureBuffers(sp<ICamera> camera, video_encoder encoder) {
+ if (camera != NULL) {
+ char mDeviceName[PROPERTY_VALUE_MAX];
+ property_get("ro.board.platform", mDeviceName, "0");
+ if (!strncmp(mDeviceName, "msm8909", 7)) {
+ int64_t token = IPCThreadState::self()->clearCallingIdentity();
+ String8 s = camera->getParameters();
+ CameraParameters params(s);
+ const char *enable;
+ if (encoder == VIDEO_ENCODER_H263 ||
+ encoder == VIDEO_ENCODER_MPEG_4_SP) {
+ enable = "1";
+ } else {
+ enable = "0";
+ }
+ params.set("cache-video-buffers", enable);
+ if (camera->setParameters(params.flatten()) != OK) {
+ ALOGE("Failed to enabled cached camera buffers");
+ }
+ IPCThreadState::self()->restoreCallingIdentity(token);
+ }
+ }
+}
+
+const char *AVUtils::getCustomCodecsLocation() {
+ return "/etc/media_codecs.xml";
+}
+
+void AVUtils::setIntraPeriod(
+ int, int, const sp<IOMX>,
+ IOMX::node_id) {
+ return;
+}
+
+#ifdef QCOM_HARDWARE
+void AVUtils::HFR::setHFRIfEnabled(
+ const CameraParameters& params,
+ sp<MetaData> &meta) {
+ const char *hfrParam = params.get("video-hfr");
+ int32_t hfr = -1;
+ if (hfrParam != NULL) {
+ hfr = atoi(hfrParam);
+ if (hfr > 0) {
+ ALOGI("Enabling HFR @ %d fps", hfr);
+ meta->setInt32(kKeyHFR, hfr);
+ return;
+ } else {
+ ALOGI("Invalid HFR rate specified : %d", hfr);
+ }
+ }
+
+ const char *hsrParam = params.get("video-hsr");
+ int32_t hsr = -1;
+ if (hsrParam != NULL ) {
+ hsr = atoi(hsrParam);
+ if (hsr > 0) {
+ ALOGI("Enabling HSR @ %d fps", hsr);
+ meta->setInt32(kKeyHSR, hsr);
+ } else {
+ ALOGI("Invalid HSR rate specified : %d", hfr);
+ }
+ }
+}
+
+status_t AVUtils::HFR::initializeHFR(
+ const sp<MetaData> &meta, sp<AMessage> &format,
+ int64_t & /*maxFileDurationUs*/, video_encoder videoEncoder) {
+ status_t retVal = OK;
+
+ int32_t hsr = 0;
+ if (meta->findInt32(kKeyHSR, &hsr) && hsr > 0) {
+ ALOGI("HSR cue found. Override encode fps to %d", hsr);
+ format->setInt32("frame-rate", hsr);
+ return retVal;
+ }
+
+ int32_t hfr = 0;
+ if (!meta->findInt32(kKeyHFR, &hfr) || (hfr <= 0)) {
+ ALOGW("Invalid HFR rate specified");
+ return retVal;
+ }
+
+ int32_t width = 0, height = 0;
+ CHECK(meta->findInt32(kKeyWidth, &width));
+ CHECK(meta->findInt32(kKeyHeight, &height));
+
+ int maxW, maxH, MaxFrameRate, maxBitRate = 0;
+ if (getHFRCapabilities(videoEncoder,
+ maxW, maxH, MaxFrameRate, maxBitRate) < 0) {
+ ALOGE("Failed to query HFR target capabilities");
+ return ERROR_UNSUPPORTED;
+ }
+
+ if ((width * height * hfr) > (maxW * maxH * MaxFrameRate)) {
+ ALOGE("HFR request [%d x %d @%d fps] exceeds "
+ "[%d x %d @%d fps]. Will stay disabled",
+ width, height, hfr, maxW, maxH, MaxFrameRate);
+ return ERROR_UNSUPPORTED;
+ }
+
+ int32_t frameRate = 0, bitRate = 0;
+ CHECK(meta->findInt32(kKeyFrameRate, &frameRate));
+ CHECK(format->findInt32("bitrate", &bitRate));
+
+ if (frameRate) {
+ // scale the bitrate proportional to the hfr ratio
+ // to maintain quality, but cap it to max-supported.
+ bitRate = (hfr * bitRate) / frameRate;
+ bitRate = bitRate > maxBitRate ? maxBitRate : bitRate;
+ format->setInt32("bitrate", bitRate);
+
+ int32_t hfrRatio = hfr / frameRate;
+ format->setInt32("frame-rate", hfr);
+ format->setInt32("hfr-ratio", hfrRatio);
+ } else {
+ ALOGE("HFR: Invalid framerate");
+ return BAD_VALUE;
+ }
+
+ return retVal;
+}
+
+void AVUtils::HFR::setHFRRatio(
+ sp<MetaData> &meta, const int32_t hfrRatio) {
+ if (hfrRatio > 0) {
+ meta->setInt32(kKeyHFR, hfrRatio);
+ }
+}
+
+int32_t AVUtils::HFR::getHFRRatio(
+ const sp<MetaData> &meta) {
+ int32_t hfrRatio = 0;
+ meta->findInt32(kKeyHFR, &hfrRatio);
+ return hfrRatio ? hfrRatio : 1;
+}
+
+int32_t AVUtils::HFR::getHFRCapabilities(
+ video_encoder codec,
+ int& maxHFRWidth, int& maxHFRHeight, int& maxHFRFps,
+ int& maxBitRate) {
+ maxHFRWidth = maxHFRHeight = maxHFRFps = maxBitRate = 0;
+ MediaProfiles *profiles = MediaProfiles::getInstance();
+
+ if (profiles) {
+ maxHFRWidth = profiles->getVideoEncoderParamByName("enc.vid.hfr.width.max", codec);
+ maxHFRHeight = profiles->getVideoEncoderParamByName("enc.vid.hfr.height.max", codec);
+ maxHFRFps = profiles->getVideoEncoderParamByName("enc.vid.hfr.mode.max", codec);
+ maxBitRate = profiles->getVideoEncoderParamByName("enc.vid.bps.max", codec);
+ }
+
+ return (maxHFRWidth > 0) && (maxHFRHeight > 0) &&
+ (maxHFRFps > 0) && (maxBitRate > 0) ? 1 : -1;
+}
+#else
+void AVUtils::HFR::setHFRIfEnabled(
+ const CameraParameters& /*params*/,
+ sp<MetaData> & /*meta*/) {}
+
+status_t AVUtils::HFR::initializeHFR(
+ const sp<MetaData> & /*meta*/, sp<AMessage> & /*format*/,
+ int64_t & /*maxFileDurationUs*/, video_encoder /*videoEncoder*/) {
+ return OK;
+}
+
+void AVUtils::HFR::setHFRRatio(
+ sp<MetaData> & /*meta*/, const int32_t /*hfrRatio*/) {}
+
+int32_t AVUtils::HFR::getHFRRatio(
+ const sp<MetaData> & /*meta */) {
+ return 1;
+}
+
+int32_t AVUtils::HFR::getHFRCapabilities(
+ video_encoder /*codec*/,
+ int& /*maxHFRWidth*/, int& /*maxHFRHeight*/, int& /*maxHFRFps*/,
+ int& /*maxBitRate*/) {
+ return -1;
+}
+#endif
+
+void AVUtils::extractCustomCameraKeys(
+ const CameraParameters& params, sp<MetaData> &meta) {
+ mHFR.setHFRIfEnabled(params, meta);
+}
+
+// ----- NO TRESSPASSING BEYOND THIS LINE ------
+AVUtils::AVUtils() {}
+
+AVUtils::~AVUtils() {}
+
+//static
+AVUtils *AVUtils::sInst =
+ ExtensionsLoader<AVUtils>::createInstance("createAVUtils");
+
+} //namespace android
+