| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| //#define LOG_NDEBUG 0 |
| #define LOG_TAG "AMPEG4AudioAssembler" |
| |
| #include "AMPEG4AudioAssembler.h" |
| |
| #include "ARTPSource.h" |
| |
| #include <media/stagefright/foundation/hexdump.h> |
| #include <media/stagefright/foundation/ABitReader.h> |
| #include <media/stagefright/foundation/ABuffer.h> |
| #include <media/stagefright/foundation/ADebug.h> |
| #include <media/stagefright/foundation/AMessage.h> |
| #include <media/stagefright/MediaErrors.h> |
| |
| #include <ctype.h> |
| |
| namespace android { |
| |
| static bool GetAttribute(const char *s, const char *key, AString *value) { |
| value->clear(); |
| |
| size_t keyLen = strlen(key); |
| |
| for (;;) { |
| while (isspace(*s)) { |
| ++s; |
| } |
| |
| const char *colonPos = strchr(s, ';'); |
| |
| size_t len = |
| (colonPos == NULL) ? strlen(s) : colonPos - s; |
| |
| if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) { |
| value->setTo(&s[keyLen + 1], len - keyLen - 1); |
| return true; |
| } |
| |
| if (colonPos == NULL) { |
| return false; |
| } |
| |
| s = colonPos + 1; |
| } |
| } |
| |
| static sp<ABuffer> decodeHex(const AString &s) { |
| if ((s.size() % 2) != 0) { |
| return NULL; |
| } |
| |
| size_t outLen = s.size() / 2; |
| sp<ABuffer> buffer = new ABuffer(outLen); |
| uint8_t *out = buffer->data(); |
| |
| uint8_t accum = 0; |
| for (size_t i = 0; i < s.size(); ++i) { |
| char c = s.c_str()[i]; |
| unsigned value; |
| if (c >= '0' && c <= '9') { |
| value = c - '0'; |
| } else if (c >= 'a' && c <= 'f') { |
| value = c - 'a' + 10; |
| } else if (c >= 'A' && c <= 'F') { |
| value = c - 'A' + 10; |
| } else { |
| return NULL; |
| } |
| |
| accum = (accum << 4) | value; |
| |
| if (i & 1) { |
| *out++ = accum; |
| |
| accum = 0; |
| } |
| } |
| |
| return buffer; |
| } |
| |
| static status_t parseAudioObjectType( |
| ABitReader *bits, unsigned *audioObjectType) { |
| *audioObjectType = bits->getBits(5); |
| if ((*audioObjectType) == 31) { |
| *audioObjectType = 32 + bits->getBits(6); |
| } |
| |
| return OK; |
| } |
| |
| static status_t parseGASpecificConfig( |
| ABitReader *bits, |
| unsigned audioObjectType, unsigned channelConfiguration) { |
| unsigned frameLengthFlag = bits->getBits(1); |
| unsigned dependsOnCoreCoder = bits->getBits(1); |
| if (dependsOnCoreCoder) { |
| /* unsigned coreCoderDelay = */bits->getBits(1); |
| } |
| unsigned extensionFlag = bits->getBits(1); |
| |
| if (!channelConfiguration) { |
| // program_config_element |
| return ERROR_UNSUPPORTED; // XXX to be implemented |
| } |
| |
| if (audioObjectType == 6 || audioObjectType == 20) { |
| /* unsigned layerNr = */bits->getBits(3); |
| } |
| |
| if (extensionFlag) { |
| if (audioObjectType == 22) { |
| /* unsigned numOfSubFrame = */bits->getBits(5); |
| /* unsigned layerLength = */bits->getBits(11); |
| } else if (audioObjectType == 17 || audioObjectType == 19 |
| || audioObjectType == 20 || audioObjectType == 23) { |
| /* unsigned aacSectionDataResilienceFlag = */bits->getBits(1); |
| /* unsigned aacScalefactorDataResilienceFlag = */bits->getBits(1); |
| /* unsigned aacSpectralDataResilienceFlag = */bits->getBits(1); |
| } |
| |
| unsigned extensionFlag3 = bits->getBits(1); |
| CHECK_EQ(extensionFlag3, 0u); // TBD in version 3 |
| } |
| |
| return OK; |
| } |
| |
| static status_t parseAudioSpecificConfig(ABitReader *bits, sp<ABuffer> *asc) { |
| const uint8_t *dataStart = bits->data(); |
| size_t totalNumBits = bits->numBitsLeft(); |
| |
| unsigned audioObjectType; |
| CHECK_EQ(parseAudioObjectType(bits, &audioObjectType), (status_t)OK); |
| |
| unsigned samplingFreqIndex = bits->getBits(4); |
| if (samplingFreqIndex == 0x0f) { |
| /* unsigned samplingFrequency = */bits->getBits(24); |
| } |
| |
| unsigned channelConfiguration = bits->getBits(4); |
| |
| unsigned extensionAudioObjectType = 0; |
| unsigned sbrPresent = 0; |
| |
| if (audioObjectType == 5) { |
| extensionAudioObjectType = audioObjectType; |
| sbrPresent = 1; |
| unsigned extensionSamplingFreqIndex = bits->getBits(4); |
| if (extensionSamplingFreqIndex == 0x0f) { |
| /* unsigned extensionSamplingFrequency = */bits->getBits(24); |
| } |
| CHECK_EQ(parseAudioObjectType(bits, &audioObjectType), (status_t)OK); |
| } |
| |
| CHECK((audioObjectType >= 1 && audioObjectType <= 4) |
| || (audioObjectType >= 6 && audioObjectType <= 7) |
| || audioObjectType == 17 |
| || (audioObjectType >= 19 && audioObjectType <= 23)); |
| |
| CHECK_EQ(parseGASpecificConfig( |
| bits, audioObjectType, channelConfiguration), (status_t)OK); |
| |
| if (audioObjectType == 17 |
| || (audioObjectType >= 19 && audioObjectType <= 27)) { |
| unsigned epConfig = bits->getBits(2); |
| if (epConfig == 2 || epConfig == 3) { |
| // ErrorProtectionSpecificConfig |
| return ERROR_UNSUPPORTED; // XXX to be implemented |
| |
| if (epConfig == 3) { |
| unsigned directMapping = bits->getBits(1); |
| CHECK_EQ(directMapping, 1u); |
| } |
| } |
| } |
| |
| if (extensionAudioObjectType != 5 && bits->numBitsLeft() >= 16) { |
| size_t numBitsLeftAtStart = bits->numBitsLeft(); |
| |
| unsigned syncExtensionType = bits->getBits(11); |
| if (syncExtensionType == 0x2b7) { |
| ALOGI("found syncExtension"); |
| |
| CHECK_EQ(parseAudioObjectType(bits, &extensionAudioObjectType), |
| (status_t)OK); |
| |
| sbrPresent = bits->getBits(1); |
| |
| if (sbrPresent == 1) { |
| unsigned extensionSamplingFreqIndex = bits->getBits(4); |
| if (extensionSamplingFreqIndex == 0x0f) { |
| /* unsigned extensionSamplingFrequency = */bits->getBits(24); |
| } |
| } |
| |
| size_t numBitsInExtension = |
| numBitsLeftAtStart - bits->numBitsLeft(); |
| |
| if (numBitsInExtension & 7) { |
| // Apparently an extension is always considered an even |
| // multiple of 8 bits long. |
| |
| ALOGI("Skipping %d bits after sync extension", |
| 8 - (numBitsInExtension & 7)); |
| |
| bits->skipBits(8 - (numBitsInExtension & 7)); |
| } |
| } else { |
| bits->putBits(syncExtensionType, 11); |
| } |
| } |
| |
| if (asc != NULL) { |
| size_t bitpos = totalNumBits & 7; |
| |
| ABitReader bs(dataStart, (totalNumBits + 7) / 8); |
| |
| totalNumBits -= bits->numBitsLeft(); |
| |
| size_t numBytes = (totalNumBits + 7) / 8; |
| |
| *asc = new ABuffer(numBytes); |
| |
| if (bitpos & 7) { |
| bs.skipBits(8 - (bitpos & 7)); |
| } |
| |
| uint8_t *dstPtr = (*asc)->data(); |
| while (numBytes > 0) { |
| *dstPtr++ = bs.getBits(8); |
| --numBytes; |
| } |
| } |
| |
| return OK; |
| } |
| |
| static status_t parseStreamMuxConfig( |
| ABitReader *bits, |
| unsigned *numSubFrames, |
| unsigned *frameLengthType, |
| ssize_t *fixedFrameLength, |
| bool *otherDataPresent, |
| unsigned *otherDataLenBits) { |
| unsigned audioMuxVersion = bits->getBits(1); |
| |
| unsigned audioMuxVersionA = 0; |
| if (audioMuxVersion == 1) { |
| audioMuxVersionA = bits->getBits(1); |
| } |
| |
| CHECK_EQ(audioMuxVersionA, 0u); // otherwise future spec |
| |
| if (audioMuxVersion != 0) { |
| return ERROR_UNSUPPORTED; // XXX to be implemented; |
| } |
| CHECK_EQ(audioMuxVersion, 0u); // XXX to be implemented |
| |
| unsigned allStreamsSameTimeFraming = bits->getBits(1); |
| CHECK_EQ(allStreamsSameTimeFraming, 1u); // There's only one stream. |
| |
| *numSubFrames = bits->getBits(6); |
| unsigned numProgram = bits->getBits(4); |
| CHECK_EQ(numProgram, 0u); // disabled in RTP LATM |
| |
| unsigned numLayer = bits->getBits(3); |
| CHECK_EQ(numLayer, 0u); // disabled in RTP LATM |
| |
| if (audioMuxVersion == 0) { |
| // AudioSpecificConfig |
| CHECK_EQ(parseAudioSpecificConfig(bits, NULL /* asc */), (status_t)OK); |
| } else { |
| TRESPASS(); // XXX to be implemented |
| } |
| |
| *frameLengthType = bits->getBits(3); |
| *fixedFrameLength = -1; |
| |
| switch (*frameLengthType) { |
| case 0: |
| { |
| /* unsigned bufferFullness = */bits->getBits(8); |
| |
| // The "coreFrameOffset" does not apply since there's only |
| // a single layer. |
| break; |
| } |
| |
| case 1: |
| { |
| *fixedFrameLength = bits->getBits(9); |
| break; |
| } |
| |
| case 2: |
| { |
| return ERROR_UNSUPPORTED; |
| } |
| |
| case 3: |
| case 4: |
| case 5: |
| { |
| /* unsigned CELPframeLengthTableIndex = */bits->getBits(6); |
| break; |
| } |
| |
| case 6: |
| case 7: |
| { |
| /* unsigned HVXCframeLengthTableIndex = */bits->getBits(1); |
| break; |
| } |
| |
| default: |
| break; |
| } |
| |
| *otherDataPresent = bits->getBits(1); |
| *otherDataLenBits = 0; |
| if (*otherDataPresent) { |
| if (audioMuxVersion == 1) { |
| TRESPASS(); // XXX to be implemented |
| } else { |
| *otherDataLenBits = 0; |
| |
| unsigned otherDataLenEsc; |
| do { |
| (*otherDataLenBits) <<= 8; |
| otherDataLenEsc = bits->getBits(1); |
| unsigned otherDataLenTmp = bits->getBits(8); |
| (*otherDataLenBits) += otherDataLenTmp; |
| } while (otherDataLenEsc); |
| } |
| } |
| |
| unsigned crcCheckPresent = bits->getBits(1); |
| if (crcCheckPresent) { |
| /* unsigned crcCheckSum = */bits->getBits(8); |
| } |
| |
| return OK; |
| } |
| |
| sp<ABuffer> AMPEG4AudioAssembler::removeLATMFraming(const sp<ABuffer> &buffer) { |
| CHECK(!mMuxConfigPresent); // XXX to be implemented |
| |
| sp<ABuffer> out = new ABuffer(buffer->size()); |
| out->setRange(0, 0); |
| |
| size_t offset = 0; |
| uint8_t *ptr = buffer->data(); |
| |
| for (size_t i = 0; i <= mNumSubFrames; ++i) { |
| // parse PayloadLengthInfo |
| |
| unsigned payloadLength = 0; |
| |
| switch (mFrameLengthType) { |
| case 0: |
| { |
| unsigned muxSlotLengthBytes = 0; |
| unsigned tmp; |
| do { |
| CHECK_LT(offset, buffer->size()); |
| tmp = ptr[offset++]; |
| muxSlotLengthBytes += tmp; |
| } while (tmp == 0xff); |
| |
| payloadLength = muxSlotLengthBytes; |
| break; |
| } |
| |
| case 2: |
| { |
| // reserved |
| |
| TRESPASS(); |
| break; |
| } |
| |
| default: |
| { |
| CHECK_GE(mFixedFrameLength, 0); |
| |
| payloadLength = mFixedFrameLength; |
| break; |
| } |
| } |
| |
| CHECK_LE(offset + payloadLength, buffer->size()); |
| |
| memcpy(out->data() + out->size(), &ptr[offset], payloadLength); |
| out->setRange(0, out->size() + payloadLength); |
| |
| offset += payloadLength; |
| |
| if (mOtherDataPresent) { |
| // We want to stay byte-aligned. |
| |
| CHECK((mOtherDataLenBits % 8) == 0); |
| CHECK_LE(offset + (mOtherDataLenBits / 8), buffer->size()); |
| offset += mOtherDataLenBits / 8; |
| } |
| } |
| |
| if (offset < buffer->size()) { |
| ALOGI("ignoring %d bytes of trailing data", buffer->size() - offset); |
| } |
| CHECK_LE(offset, buffer->size()); |
| |
| return out; |
| } |
| |
| AMPEG4AudioAssembler::AMPEG4AudioAssembler( |
| const sp<AMessage> ¬ify, const AString ¶ms) |
| : mNotifyMsg(notify), |
| mMuxConfigPresent(false), |
| mAccessUnitRTPTime(0), |
| mNextExpectedSeqNoValid(false), |
| mNextExpectedSeqNo(0), |
| mAccessUnitDamaged(false) { |
| AString val; |
| if (!GetAttribute(params.c_str(), "cpresent", &val)) { |
| mMuxConfigPresent = true; |
| } else if (val == "0") { |
| mMuxConfigPresent = false; |
| } else { |
| CHECK(val == "1"); |
| mMuxConfigPresent = true; |
| } |
| |
| CHECK(GetAttribute(params.c_str(), "config", &val)); |
| |
| sp<ABuffer> config = decodeHex(val); |
| CHECK(config != NULL); |
| |
| ABitReader bits(config->data(), config->size()); |
| status_t err = parseStreamMuxConfig( |
| &bits, &mNumSubFrames, &mFrameLengthType, |
| &mFixedFrameLength, |
| &mOtherDataPresent, &mOtherDataLenBits); |
| |
| if (err == ERROR_UNSUPPORTED) { |
| ALOGW("Failed to parse stream mux config, using default values for playback."); |
| mMuxConfigPresent = false; |
| mNumSubFrames = 0; |
| mFrameLengthType = 0; |
| mOtherDataPresent = false; |
| mOtherDataLenBits = 0; |
| return; |
| } |
| CHECK_EQ(err, (status_t)NO_ERROR); |
| } |
| |
| AMPEG4AudioAssembler::~AMPEG4AudioAssembler() { |
| } |
| |
| ARTPAssembler::AssemblyStatus AMPEG4AudioAssembler::assembleMore( |
| const sp<ARTPSource> &source) { |
| AssemblyStatus status = addPacket(source); |
| if (status == MALFORMED_PACKET) { |
| mAccessUnitDamaged = true; |
| } |
| return status; |
| } |
| |
| ARTPAssembler::AssemblyStatus AMPEG4AudioAssembler::addPacket( |
| const sp<ARTPSource> &source) { |
| List<sp<ABuffer> > *queue = source->queue(); |
| |
| if (queue->empty()) { |
| return NOT_ENOUGH_DATA; |
| } |
| |
| if (mNextExpectedSeqNoValid) { |
| List<sp<ABuffer> >::iterator it = queue->begin(); |
| while (it != queue->end()) { |
| if ((uint32_t)(*it)->int32Data() >= mNextExpectedSeqNo) { |
| break; |
| } |
| |
| it = queue->erase(it); |
| } |
| |
| if (queue->empty()) { |
| return NOT_ENOUGH_DATA; |
| } |
| } |
| |
| sp<ABuffer> buffer = *queue->begin(); |
| |
| if (!mNextExpectedSeqNoValid) { |
| mNextExpectedSeqNoValid = true; |
| mNextExpectedSeqNo = (uint32_t)buffer->int32Data(); |
| } else if ((uint32_t)buffer->int32Data() != mNextExpectedSeqNo) { |
| #if VERBOSE |
| LOG(VERBOSE) << "Not the sequence number I expected"; |
| #endif |
| |
| return WRONG_SEQUENCE_NUMBER; |
| } |
| |
| uint32_t rtpTime; |
| CHECK(buffer->meta()->findInt32("rtp-time", (int32_t *)&rtpTime)); |
| |
| if (mPackets.size() > 0 && rtpTime != mAccessUnitRTPTime) { |
| submitAccessUnit(); |
| } |
| mAccessUnitRTPTime = rtpTime; |
| |
| mPackets.push_back(buffer); |
| |
| queue->erase(queue->begin()); |
| ++mNextExpectedSeqNo; |
| |
| return OK; |
| } |
| |
| void AMPEG4AudioAssembler::submitAccessUnit() { |
| CHECK(!mPackets.empty()); |
| |
| #if VERBOSE |
| LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " packets)"; |
| #endif |
| |
| sp<ABuffer> accessUnit = MakeCompoundFromPackets(mPackets); |
| accessUnit = removeLATMFraming(accessUnit); |
| CopyTimes(accessUnit, *mPackets.begin()); |
| |
| #if 0 |
| printf(mAccessUnitDamaged ? "X" : "."); |
| fflush(stdout); |
| #endif |
| |
| if (mAccessUnitDamaged) { |
| accessUnit->meta()->setInt32("damaged", true); |
| } |
| |
| mPackets.clear(); |
| mAccessUnitDamaged = false; |
| |
| sp<AMessage> msg = mNotifyMsg->dup(); |
| msg->setBuffer("access-unit", accessUnit); |
| msg->post(); |
| } |
| |
| void AMPEG4AudioAssembler::packetLost() { |
| CHECK(mNextExpectedSeqNoValid); |
| ++mNextExpectedSeqNo; |
| |
| mAccessUnitDamaged = true; |
| } |
| |
| void AMPEG4AudioAssembler::onByeReceived() { |
| sp<AMessage> msg = mNotifyMsg->dup(); |
| msg->setInt32("eos", true); |
| msg->post(); |
| } |
| |
| } // namespace android |