/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #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 #include #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 &meta, sp *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 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 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 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 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& 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 &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 &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 &format, int32_t bitWidth) { format->setInt32("bits-per-sample", bitWidth); } void AVUtils::setPcmSampleBits(const sp &meta, int32_t bitWidth) { meta->setInt32(kKeyBitsPerSample, bitWidth); } audio_format_t AVUtils::updateAudioFormat(audio_format_t audioFormat, const sp &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 &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 &, String8 *, float *, sp *) { return false; } DataSource::SnifferFunc AVUtils::getExtendedSniffer() { return dumbSniffer; } sp AVUtils::createCustomComponentByName( const sp &, const char* , bool, const sp &) { return NULL; } bool AVUtils::canOffloadAPE(const sp &) { return true; } int32_t AVUtils::getAudioMaxInputBufferSize(audio_format_t, const sp &) { return 0; } bool AVUtils::mapAACProfileToAudioFormat(const sp &, audio_format_t &, uint64_t /*eAacProfile*/) { return false ; } bool AVUtils::mapAACProfileToAudioFormat(const sp &, audio_format_t &, uint64_t /*eAacProfile*/) { return false ; } bool AVUtils::isEnhancedExtension(const char *) { return false; } bool AVUtils::HEVCMuxer::reassembleHEVCCSD(const AString &mime, sp csd0, sp &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 vidParamSets; List seqParamSets; List 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::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::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::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 ¶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 &vidParamSet, List &seqParamSet, List &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 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 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::node_id) { return; } #ifdef QCOM_HARDWARE void AVUtils::HFR::setHFRIfEnabled( const CameraParameters& params, sp &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 &meta, sp &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 &meta, const int32_t hfrRatio) { if (hfrRatio > 0) { meta->setInt32(kKeyHFR, hfrRatio); } } int32_t AVUtils::HFR::getHFRRatio( const sp &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 & /*meta*/) {} status_t AVUtils::HFR::initializeHFR( const sp & /*meta*/, sp & /*format*/, int64_t & /*maxFileDurationUs*/, video_encoder /*videoEncoder*/) { return OK; } void AVUtils::HFR::setHFRRatio( sp & /*meta*/, const int32_t /*hfrRatio*/) {} int32_t AVUtils::HFR::getHFRRatio( const sp & /*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 &meta) { mHFR.setHFRIfEnabled(params, meta); } // ----- NO TRESSPASSING BEYOND THIS LINE ------ AVUtils::AVUtils() {} AVUtils::~AVUtils() {} //static AVUtils *AVUtils::sInst = ExtensionsLoader::createInstance("createAVUtils"); } //namespace android