Merge "Support for MP4V-ES packetization format according to RFC3016." into gingerbread

This commit is contained in:
Andreas Huber
2010-08-19 11:19:51 -07:00
committed by Android (Google) Code Review
6 changed files with 417 additions and 0 deletions

View File

@@ -0,0 +1,166 @@
/*
* 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.
*/
#include "AMPEG4ElementaryAssembler.h"
#include "ARTPSource.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 <stdint.h>
#define BE_VERBOSE 0
namespace android {
// static
AMPEG4ElementaryAssembler::AMPEG4ElementaryAssembler(const sp<AMessage> &notify)
: mNotifyMsg(notify),
mAccessUnitRTPTime(0),
mNextExpectedSeqNoValid(false),
mNextExpectedSeqNo(0),
mAccessUnitDamaged(false) {
}
AMPEG4ElementaryAssembler::~AMPEG4ElementaryAssembler() {
}
ARTPAssembler::AssemblyStatus AMPEG4ElementaryAssembler::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 BE_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);
// hexdump(buffer->data(), buffer->size());
queue->erase(queue->begin());
++mNextExpectedSeqNo;
return OK;
}
void AMPEG4ElementaryAssembler::submitAccessUnit() {
CHECK(!mPackets.empty());
#if BE_VERBOSE
LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " nal units)";
#endif
uint64_t ntpTime;
CHECK((*mPackets.begin())->meta()->findInt64(
"ntp-time", (int64_t *)&ntpTime));
size_t totalSize = 0;
for (List<sp<ABuffer> >::iterator it = mPackets.begin();
it != mPackets.end(); ++it) {
totalSize += (*it)->size();
}
sp<ABuffer> accessUnit = new ABuffer(totalSize);
size_t offset = 0;
for (List<sp<ABuffer> >::iterator it = mPackets.begin();
it != mPackets.end(); ++it) {
sp<ABuffer> nal = *it;
memcpy(accessUnit->data() + offset, nal->data(), nal->size());
offset += nal->size();
}
accessUnit->meta()->setInt64("ntp-time", ntpTime);
#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->setObject("access-unit", accessUnit);
msg->post();
}
ARTPAssembler::AssemblyStatus AMPEG4ElementaryAssembler::assembleMore(
const sp<ARTPSource> &source) {
AssemblyStatus status = addPacket(source);
if (status == MALFORMED_PACKET) {
mAccessUnitDamaged = true;
}
return status;
}
void AMPEG4ElementaryAssembler::packetLost() {
CHECK(mNextExpectedSeqNoValid);
LOG(VERBOSE) << "packetLost (expected " << mNextExpectedSeqNo << ")";
++mNextExpectedSeqNo;
mAccessUnitDamaged = true;
}
void AMPEG4ElementaryAssembler::onByeReceived() {
sp<AMessage> msg = mNotifyMsg->dup();
msg->setInt32("eos", true);
msg->post();
}
} // namespace android

View File

@@ -0,0 +1,58 @@
/*
* 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.
*/
#ifndef A_MPEG4_ELEM_ASSEMBLER_H_
#define A_MPEG4_ELEM_ASSEMBLER_H_
#include "ARTPAssembler.h"
#include <utils/List.h>
#include <utils/RefBase.h>
namespace android {
struct ABuffer;
struct AMessage;
struct AMPEG4ElementaryAssembler : public ARTPAssembler {
AMPEG4ElementaryAssembler(const sp<AMessage> &notify);
protected:
virtual ~AMPEG4ElementaryAssembler();
virtual AssemblyStatus assembleMore(const sp<ARTPSource> &source);
virtual void onByeReceived();
virtual void packetLost();
private:
sp<AMessage> mNotifyMsg;
uint32_t mAccessUnitRTPTime;
bool mNextExpectedSeqNoValid;
uint32_t mNextExpectedSeqNo;
bool mAccessUnitDamaged;
List<sp<ABuffer> > mPackets;
AssemblyStatus addPacket(const sp<ARTPSource> &source);
void submitAccessUnit();
DISALLOW_EVIL_CONSTRUCTORS(AMPEG4ElementaryAssembler);
};
} // namespace android
#endif // A_MPEG4_ELEM_ASSEMBLER_H_

View File

@@ -22,6 +22,7 @@
#include <ctype.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>
@@ -246,6 +247,161 @@ sp<ABuffer> MakeAACCodecSpecificData(const char *params) {
return csd;
}
static size_t GetSizeWidth(size_t x) {
size_t n = 1;
while (x > 127) {
++n;
x >>= 7;
}
return n;
}
static uint8_t *EncodeSize(uint8_t *dst, size_t x) {
while (x > 127) {
*dst++ = (x & 0x7f) | 0x80;
x >>= 7;
}
*dst++ = x;
return dst;
}
static bool ExtractDimensionsFromVOLHeader(
const sp<ABuffer> &config, int32_t *width, int32_t *height) {
*width = 0;
*height = 0;
const uint8_t *ptr = config->data();
size_t offset = 0;
bool foundVOL = false;
while (offset + 3 < config->size()) {
if (memcmp("\x00\x00\x01", &ptr[offset], 3)
|| (ptr[offset + 3] & 0xf0) != 0x20) {
++offset;
continue;
}
foundVOL = true;
break;
}
if (!foundVOL) {
return false;
}
ABitReader br(&ptr[offset + 4], config->size() - offset - 4);
br.skipBits(1); // random_accessible_vol
unsigned video_object_type_indication = br.getBits(8);
CHECK_NE(video_object_type_indication,
0x21u /* Fine Granularity Scalable */);
unsigned video_object_layer_verid;
unsigned video_object_layer_priority;
if (br.getBits(1)) {
video_object_layer_verid = br.getBits(4);
video_object_layer_priority = br.getBits(3);
}
unsigned aspect_ratio_info = br.getBits(4);
if (aspect_ratio_info == 0x0f /* extended PAR */) {
br.skipBits(8); // par_width
br.skipBits(8); // par_height
}
if (br.getBits(1)) { // vol_control_parameters
br.skipBits(2); // chroma_format
br.skipBits(1); // low_delay
if (br.getBits(1)) { // vbv_parameters
TRESPASS();
}
}
unsigned video_object_layer_shape = br.getBits(2);
CHECK_EQ(video_object_layer_shape, 0x00u /* rectangular */);
CHECK(br.getBits(1)); // marker_bit
unsigned vop_time_increment_resolution = br.getBits(16);
CHECK(br.getBits(1)); // marker_bit
if (br.getBits(1)) { // fixed_vop_rate
// range [0..vop_time_increment_resolution)
// vop_time_increment_resolution
// 2 => 0..1, 1 bit
// 3 => 0..2, 2 bits
// 4 => 0..3, 2 bits
// 5 => 0..4, 3 bits
// ...
CHECK_GT(vop_time_increment_resolution, 0u);
--vop_time_increment_resolution;
unsigned numBits = 0;
while (vop_time_increment_resolution > 0) {
++numBits;
vop_time_increment_resolution >>= 1;
}
br.skipBits(numBits); // fixed_vop_time_increment
}
CHECK(br.getBits(1)); // marker_bit
unsigned video_object_layer_width = br.getBits(13);
CHECK(br.getBits(1)); // marker_bit
unsigned video_object_layer_height = br.getBits(13);
CHECK(br.getBits(1)); // marker_bit
unsigned interlaced = br.getBits(1);
*width = video_object_layer_width;
*height = video_object_layer_height;
LOG(INFO) << "VOL dimensions = " << *width << "x" << *height;
return true;
}
sp<ABuffer> MakeMPEG4VideoCodecSpecificData(
const char *params, int32_t *width, int32_t *height) {
*width = 0;
*height = 0;
AString val;
CHECK(GetAttribute(params, "config", &val));
sp<ABuffer> config = decodeHex(val);
CHECK(config != NULL);
if (!ExtractDimensionsFromVOLHeader(config, width, height)) {
return NULL;
}
size_t len1 = config->size() + GetSizeWidth(config->size()) + 1;
size_t len2 = len1 + GetSizeWidth(len1) + 1 + 13;
size_t len3 = len2 + GetSizeWidth(len2) + 1 + 3;
sp<ABuffer> csd = new ABuffer(len3);
uint8_t *dst = csd->data();
*dst++ = 0x03;
dst = EncodeSize(dst, len2 + 3);
*dst++ = 0x00; // ES_ID
*dst++ = 0x00;
*dst++ = 0x00; // streamDependenceFlag, URL_Flag, OCRstreamFlag
*dst++ = 0x04;
dst = EncodeSize(dst, len1 + 13);
*dst++ = 0x01; // Video ISO/IEC 14496-2 Simple Profile
for (size_t i = 0; i < 12; ++i) {
*dst++ = 0x00;
}
*dst++ = 0x05;
dst = EncodeSize(dst, config->size());
memcpy(dst, config->data(), config->size());
dst += config->size();
// hexdump(csd->data(), csd->size());
return csd;
}
APacketSource::APacketSource(
const sp<ASessionDescription> &sessionDesc, size_t index)
: mInitCheck(NO_INIT),
@@ -351,6 +507,36 @@ APacketSource::APacketSource(
if (sampleRate != 16000 || numChannels != 1) {
mInitCheck = ERROR_UNSUPPORTED;
}
} else if (!strncmp(desc.c_str(), "MP4V-ES/", 8)) {
mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
int32_t width, height;
if (!sessionDesc->getDimensions(index, PT, &width, &height)) {
width = -1;
height = -1;
}
int32_t encWidth, encHeight;
sp<ABuffer> codecSpecificData =
MakeMPEG4VideoCodecSpecificData(
params.c_str(), &encWidth, &encHeight);
if (codecSpecificData != NULL) {
mFormat->setData(
kKeyESDS, 0,
codecSpecificData->data(), codecSpecificData->size());
if (width < 0) {
width = encWidth;
height = encHeight;
}
} else if (width < 0) {
mInitCheck = ERROR_UNSUPPORTED;
return;
}
mFormat->setInt32(kKeyWidth, width);
mFormat->setInt32(kKeyHeight, height);
} else {
mInitCheck = ERROR_UNSUPPORTED;
}

View File

@@ -321,6 +321,8 @@ status_t ARTPConnection::receive(StreamInfo *s, bool receiveRTP) {
buffer->setRange(0, nbytes);
// LOG(INFO) << "received " << buffer->size() << " bytes.";
status_t err;
if (receiveRTP) {
err = parseRTP(s, buffer);

View File

@@ -20,6 +20,7 @@
#include "AAVCAssembler.h"
#include "AH263Assembler.h"
#include "AMPEG4AudioAssembler.h"
#include "AMPEG4ElementaryAssembler.h"
#include "ASessionDescription.h"
#include <media/stagefright/foundation/ABuffer.h>
@@ -63,6 +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);
mIssueFIRRequests = true;
} else {
TRESPASS();
}

View File

@@ -7,6 +7,7 @@ LOCAL_SRC_FILES:= \
AAVCAssembler.cpp \
AH263Assembler.cpp \
AMPEG4AudioAssembler.cpp \
AMPEG4ElementaryAssembler.cpp \
APacketSource.cpp \
ARTPAssembler.cpp \
ARTPConnection.cpp \