Merge "Support for RFC3640 - mpeg4-generic RTP packet type, AAC-lbr and AAC-hbr." into gingerbread
This commit is contained in:
committed by
Android (Google) Code Review
commit
48ac68e1b1
@@ -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> ¬ify)
|
||||
AMPEG4ElementaryAssembler::AMPEG4ElementaryAssembler(
|
||||
const sp<AMessage> ¬ify, const AString &desc, const AString ¶ms)
|
||||
: 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;
|
||||
|
||||
@@ -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> ¬ify);
|
||||
AMPEG4ElementaryAssembler(
|
||||
const sp<AMessage> ¬ify, const AString &desc,
|
||||
const AString ¶ms);
|
||||
|
||||
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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user