am 48ac68e1: Merge "Support for RFC3640 - mpeg4-generic RTP packet type, AAC-lbr and AAC-hbr." into gingerbread

Merge commit '48ac68e1b117b6b55f06daced7d9d5d550853306' into gingerbread-plus-aosp

* commit '48ac68e1b117b6b55f06daced7d9d5d550853306':
  Support for RFC3640 - mpeg4-generic RTP packet type, AAC-lbr and AAC-hbr.
This commit is contained in:
Andreas Huber
2010-08-31 14:57:56 -07:00
committed by Android Git Automerger
4 changed files with 345 additions and 6 deletions

View File

@@ -18,29 +18,160 @@
#include "ARTPSource.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/foundation/hexdump.h>
#include <media/stagefright/Utils.h>
#include <ctype.h>
#include <stdint.h>
#define BE_VERBOSE 0
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] == '='
&& !strncasecmp(s, key, keyLen)) {
value->setTo(&s[keyLen + 1], len - keyLen - 1);
return true;
}
if (colonPos == NULL) {
return false;
}
s = colonPos + 1;
}
}
static bool GetIntegerAttribute(
const char *s, const char *key, unsigned *x) {
*x = 0;
AString val;
if (!GetAttribute(s, key, &val)) {
return false;
}
s = val.c_str();
char *end;
unsigned y = strtoul(s, &end, 10);
if (end == s || *end != '\0') {
return false;
}
*x = y;
return true;
}
// static
AMPEG4ElementaryAssembler::AMPEG4ElementaryAssembler(const sp<AMessage> &notify)
AMPEG4ElementaryAssembler::AMPEG4ElementaryAssembler(
const sp<AMessage> &notify, const AString &desc, const AString &params)
: mNotifyMsg(notify),
mIsGeneric(false),
mParams(params),
mSizeLength(0),
mIndexLength(0),
mIndexDeltaLength(0),
mCTSDeltaLength(0),
mDTSDeltaLength(0),
mRandomAccessIndication(false),
mStreamStateIndication(0),
mAuxiliaryDataSizeLength(0),
mHasAUHeader(false),
mAccessUnitRTPTime(0),
mNextExpectedSeqNoValid(false),
mNextExpectedSeqNo(0),
mAccessUnitDamaged(false) {
mIsGeneric = desc.startsWith("mpeg4-generic/");
if (mIsGeneric) {
AString value;
CHECK(GetAttribute(params.c_str(), "mode", &value));
if (!GetIntegerAttribute(params.c_str(), "sizeLength", &mSizeLength)) {
mSizeLength = 0;
}
if (!GetIntegerAttribute(
params.c_str(), "indexLength", &mIndexLength)) {
mIndexLength = 0;
}
if (!GetIntegerAttribute(
params.c_str(), "indexDeltaLength", &mIndexDeltaLength)) {
mIndexDeltaLength = 0;
}
if (!GetIntegerAttribute(
params.c_str(), "CTSDeltaLength", &mCTSDeltaLength)) {
mCTSDeltaLength = 0;
}
if (!GetIntegerAttribute(
params.c_str(), "DTSDeltaLength", &mDTSDeltaLength)) {
mDTSDeltaLength = 0;
}
unsigned x;
if (!GetIntegerAttribute(
params.c_str(), "randomAccessIndication", &x)) {
mRandomAccessIndication = false;
} else {
CHECK(x == 0 || x == 1);
mRandomAccessIndication = (x != 0);
}
if (!GetIntegerAttribute(
params.c_str(), "streamStateIndication",
&mStreamStateIndication)) {
mStreamStateIndication = 0;
}
if (!GetIntegerAttribute(
params.c_str(), "auxiliaryDataSizeLength",
&mAuxiliaryDataSizeLength)) {
mAuxiliaryDataSizeLength = 0;
}
mHasAUHeader =
mSizeLength > 0
|| mIndexLength > 0
|| mIndexDeltaLength > 0
|| mCTSDeltaLength > 0
|| mDTSDeltaLength > 0
|| mRandomAccessIndication
|| mStreamStateIndication > 0;
}
}
AMPEG4ElementaryAssembler::~AMPEG4ElementaryAssembler() {
}
struct AUHeader {
unsigned mSize;
unsigned mSerial;
};
ARTPAssembler::AssemblyStatus AMPEG4ElementaryAssembler::addPacket(
const sp<ARTPSource> &source) {
List<sp<ABuffer> > *queue = source->queue();
@@ -85,8 +216,116 @@ ARTPAssembler::AssemblyStatus AMPEG4ElementaryAssembler::addPacket(
}
mAccessUnitRTPTime = rtpTime;
mPackets.push_back(buffer);
// hexdump(buffer->data(), buffer->size());
if (!mIsGeneric) {
mPackets.push_back(buffer);
} else {
// hexdump(buffer->data(), buffer->size());
CHECK_GE(buffer->size(), 2u);
unsigned AU_headers_length = U16_AT(buffer->data()); // in bits
CHECK_GE(buffer->size(), 2 + (AU_headers_length + 7) / 8);
List<AUHeader> headers;
ABitReader bits(buffer->data() + 2, buffer->size() - 2);
unsigned numBitsLeft = AU_headers_length;
unsigned AU_serial = 0;
for (;;) {
if (numBitsLeft < mSizeLength) { break; }
unsigned AU_size = bits.getBits(mSizeLength);
numBitsLeft -= mSizeLength;
size_t n = headers.empty() ? mIndexLength : mIndexDeltaLength;
if (numBitsLeft < n) { break; }
unsigned AU_index = bits.getBits(n);
numBitsLeft -= n;
if (headers.empty()) {
AU_serial = AU_index;
} else {
AU_serial += 1 + AU_index;
}
if (mCTSDeltaLength > 0) {
if (numBitsLeft < 1) {
break;
}
--numBitsLeft;
if (bits.getBits(1)) {
if (numBitsLeft < mCTSDeltaLength) {
break;
}
bits.skipBits(mCTSDeltaLength);
numBitsLeft -= mCTSDeltaLength;
}
}
if (mDTSDeltaLength > 0) {
if (numBitsLeft < 1) {
break;
}
--numBitsLeft;
if (bits.getBits(1)) {
if (numBitsLeft < mDTSDeltaLength) {
break;
}
bits.skipBits(mDTSDeltaLength);
numBitsLeft -= mDTSDeltaLength;
}
}
if (mRandomAccessIndication) {
if (numBitsLeft < 1) {
break;
}
bits.skipBits(1);
--numBitsLeft;
}
if (mStreamStateIndication > 0) {
if (numBitsLeft < mStreamStateIndication) {
break;
}
bits.skipBits(mStreamStateIndication);
}
AUHeader header;
header.mSize = AU_size;
header.mSerial = AU_serial;
headers.push_back(header);
}
size_t offset = 2 + (AU_headers_length + 7) / 8;
if (mAuxiliaryDataSizeLength > 0) {
ABitReader bits(buffer->data() + offset, buffer->size() - offset);
unsigned auxSize = bits.getBits(mAuxiliaryDataSizeLength);
offset += (mAuxiliaryDataSizeLength + auxSize + 7) / 8;
}
for (List<AUHeader>::iterator it = headers.begin();
it != headers.end(); ++it) {
const AUHeader &header = *it;
CHECK_LE(offset + header.mSize, buffer->size());
sp<ABuffer> accessUnit = new ABuffer(header.mSize);
memcpy(accessUnit->data(), buffer->data() + offset, header.mSize);
offset += header.mSize;
CopyTimes(accessUnit, buffer);
mPackets.push_back(accessUnit);
}
CHECK_EQ(offset, buffer->size());
}
queue->erase(queue->begin());
++mNextExpectedSeqNo;

View File

@@ -20,6 +20,8 @@
#include "ARTPAssembler.h"
#include <media/stagefright/foundation/AString.h>
#include <utils/List.h>
#include <utils/RefBase.h>
@@ -29,7 +31,9 @@ struct ABuffer;
struct AMessage;
struct AMPEG4ElementaryAssembler : public ARTPAssembler {
AMPEG4ElementaryAssembler(const sp<AMessage> &notify);
AMPEG4ElementaryAssembler(
const sp<AMessage> &notify, const AString &desc,
const AString &params);
protected:
virtual ~AMPEG4ElementaryAssembler();
@@ -40,6 +44,18 @@ protected:
private:
sp<AMessage> mNotifyMsg;
bool mIsGeneric;
AString mParams;
unsigned mSizeLength;
unsigned mIndexLength;
unsigned mIndexDeltaLength;
unsigned mCTSDeltaLength;
unsigned mDTSDeltaLength;
bool mRandomAccessIndication;
unsigned mStreamStateIndication;
unsigned mAuxiliaryDataSizeLength;
bool mHasAUHeader;
uint32_t mAccessUnitRTPTime;
bool mNextExpectedSeqNoValid;

View File

@@ -247,6 +247,65 @@ sp<ABuffer> MakeAACCodecSpecificData(const char *params) {
return csd;
}
// From mpeg4-generic configuration data.
sp<ABuffer> MakeAACCodecSpecificData2(const char *params) {
AString val;
unsigned long objectType;
if (GetAttribute(params, "objectType", &val)) {
const char *s = val.c_str();
char *end;
objectType = strtoul(s, &end, 10);
CHECK(end > s && *end == '\0');
} else {
objectType = 0x40; // Audio ISO/IEC 14496-3
}
CHECK(GetAttribute(params, "config", &val));
sp<ABuffer> config = decodeHex(val);
CHECK(config != NULL);
// Make sure size fits into a single byte and doesn't have to
// be encoded.
CHECK_LT(20 + config->size(), 128u);
const uint8_t *data = config->data();
static const uint8_t kStaticESDS[] = {
0x03, 22,
0x00, 0x00, // ES_ID
0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag
0x04, 17,
0x40, // Audio ISO/IEC 14496-3
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x05, 2,
// AudioSpecificInfo follows
};
sp<ABuffer> csd = new ABuffer(sizeof(kStaticESDS) + config->size());
uint8_t *dst = csd->data();
*dst++ = 0x03;
*dst++ = 20 + config->size();
*dst++ = 0x00; // ES_ID
*dst++ = 0x00;
*dst++ = 0x00; // streamDependenceFlag, URL_Flag, OCRstreamFlag
*dst++ = 0x04;
*dst++ = 15 + config->size();
*dst++ = objectType;
for (int i = 0; i < 12; ++i) { *dst++ = 0x00; }
*dst++ = 0x05;
*dst++ = config->size();
memcpy(dst, config->data(), config->size());
// hexdump(csd->data(), csd->size());
return csd;
}
static size_t GetSizeWidth(size_t x) {
size_t n = 1;
while (x > 127) {
@@ -560,6 +619,30 @@ APacketSource::APacketSource(
mFormat->setInt32(kKeyWidth, width);
mFormat->setInt32(kKeyHeight, height);
} else if (!strncmp(desc.c_str(), "mpeg4-generic/", 14)) {
AString val;
if (!GetAttribute(params.c_str(), "mode", &val)
|| (strcasecmp(val.c_str(), "AAC-lbr")
&& strcasecmp(val.c_str(), "AAC-hbr"))) {
mInitCheck = ERROR_UNSUPPORTED;
return;
}
mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
int32_t sampleRate, numChannels;
ASessionDescription::ParseFormatDesc(
desc.c_str(), &sampleRate, &numChannels);
mFormat->setInt32(kKeySampleRate, sampleRate);
mFormat->setInt32(kKeyChannelCount, numChannels);
sp<ABuffer> codecSpecificData =
MakeAACCodecSpecificData2(params.c_str());
mFormat->setData(
kKeyESDS, 0,
codecSpecificData->data(), codecSpecificData->size());
} else {
mInitCheck = ERROR_UNSUPPORTED;
}

View File

@@ -64,8 +64,9 @@ ARTPSource::ARTPSource(
mAssembler = new AAMRAssembler(notify, false /* isWide */, params);
} else if (!strncmp(desc.c_str(), "AMR-WB/", 7)) {
mAssembler = new AAMRAssembler(notify, true /* isWide */, params);
} else if (!strncmp(desc.c_str(), "MP4V-ES/", 8)) {
mAssembler = new AMPEG4ElementaryAssembler(notify);
} else if (!strncmp(desc.c_str(), "MP4V-ES/", 8)
|| !strncmp(desc.c_str(), "mpeg4-generic/", 14)) {
mAssembler = new AMPEG4ElementaryAssembler(notify, desc, params);
mIssueFIRRequests = true;
} else {
TRESPASS();