/* * 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 "AAMRAssembler" #include #include "AAMRAssembler.h" #include "ARTPSource.h" #include #include #include #include #include namespace android { static bool GetAttribute(const char *s, const char *key, AString *value) { value->clear(); size_t keyLen = strlen(key); for (;;) { 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 (len == keyLen && !strncmp(s, key, keyLen)) { value->setTo("1"); return true; } if (colonPos == NULL) { return false; } s = colonPos + 1; } } AAMRAssembler::AAMRAssembler( const sp ¬ify, bool isWide, const AString ¶ms) : mIsWide(isWide), mNotifyMsg(notify), mNextExpectedSeqNoValid(false), mNextExpectedSeqNo(0) { AString value; CHECK(GetAttribute(params.c_str(), "octet-align", &value) && value == "1"); CHECK(!GetAttribute(params.c_str(), "crc", &value) || value == "0"); CHECK(!GetAttribute(params.c_str(), "interleaving", &value)); } AAMRAssembler::~AAMRAssembler() { } ARTPAssembler::AssemblyStatus AAMRAssembler::assembleMore( const sp &source) { return addPacket(source); } static size_t getFrameSize(bool isWide, unsigned FT) { static const size_t kFrameSizeNB[9] = { 95, 103, 118, 134, 148, 159, 204, 244, 39 }; static const size_t kFrameSizeWB[10] = { 132, 177, 253, 285, 317, 365, 397, 461, 477, 40 }; if (FT == 15) { return 1; } size_t frameSize = isWide ? kFrameSizeWB[FT] : kFrameSizeNB[FT]; // Round up bits to bytes and add 1 for the header byte. frameSize = (frameSize + 7) / 8 + 1; return frameSize; } ARTPAssembler::AssemblyStatus AAMRAssembler::addPacket( const sp &source) { List > *queue = source->queue(); if (queue->empty()) { return NOT_ENOUGH_DATA; } if (mNextExpectedSeqNoValid) { List >::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 buffer = *queue->begin(); if (!mNextExpectedSeqNoValid) { mNextExpectedSeqNoValid = true; mNextExpectedSeqNo = (uint32_t)buffer->int32Data(); } else if ((uint32_t)buffer->int32Data() != mNextExpectedSeqNo) { ALOGV("Not the sequence number I expected"); return WRONG_SEQUENCE_NUMBER; } // hexdump(buffer->data(), buffer->size()); if (buffer->size() < 1) { queue->erase(queue->begin()); ++mNextExpectedSeqNo; ALOGV("AMR packet too short."); return MALFORMED_PACKET; } unsigned payloadHeader = buffer->data()[0]; unsigned CMR = payloadHeader >> 4; Vector tableOfContents; size_t offset = 1; size_t totalSize = 0; for (;;) { if (offset >= buffer->size()) { queue->erase(queue->begin()); ++mNextExpectedSeqNo; ALOGV("Unable to parse TOC."); return MALFORMED_PACKET; } uint8_t toc = buffer->data()[offset++]; unsigned FT = (toc >> 3) & 0x0f; if ((toc & 3) != 0 || (mIsWide && FT > 9 && FT != 15) || (!mIsWide && FT > 8 && FT != 15)) { queue->erase(queue->begin()); ++mNextExpectedSeqNo; ALOGV("Illegal TOC entry."); return MALFORMED_PACKET; } totalSize += getFrameSize(mIsWide, (toc >> 3) & 0x0f); tableOfContents.push(toc); if (0 == (toc & 0x80)) { break; } } sp accessUnit = new ABuffer(totalSize); CopyTimes(accessUnit, buffer); size_t dstOffset = 0; for (size_t i = 0; i < tableOfContents.size(); ++i) { uint8_t toc = tableOfContents[i]; size_t frameSize = getFrameSize(mIsWide, (toc >> 3) & 0x0f); if (offset + frameSize - 1 > buffer->size()) { queue->erase(queue->begin()); ++mNextExpectedSeqNo; ALOGV("AMR packet too short."); return MALFORMED_PACKET; } accessUnit->data()[dstOffset++] = toc; memcpy(accessUnit->data() + dstOffset, buffer->data() + offset, frameSize - 1); offset += frameSize - 1; dstOffset += frameSize - 1; } sp msg = mNotifyMsg->dup(); msg->setBuffer("access-unit", accessUnit); msg->post(); queue->erase(queue->begin()); ++mNextExpectedSeqNo; return OK; } void AAMRAssembler::packetLost() { CHECK(mNextExpectedSeqNoValid); ++mNextExpectedSeqNo; } void AAMRAssembler::onByeReceived() { sp msg = mNotifyMsg->dup(); msg->setInt32("eos", true); msg->post(); } } // namespace android