diff options
Diffstat (limited to 'media/libavextensions/stagefright/AVUtils.cpp')
-rw-r--r-- | media/libavextensions/stagefright/AVUtils.cpp | 1139 |
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> ¶mSetList, 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, ¶mSetLen); + if (!gotVps) { + gotVps = true; + } + } else if (type == kHEVCNalUnitTypeSeqParamSet) { + nextStartCode = parseHEVCParamSet(tmp + 4, bytesLeft - 4, seqParamSet, ¶mSetLen); + if (!gotSps) { + gotSps = true; + } + + } else if (type == kHEVCNalUnitTypePicParamSet) { + nextStartCode = parseHEVCParamSet(tmp + 4, bytesLeft - 4, picParamSet, ¶mSetLen); + 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 + |