Merge "Support for MP4V-ES packetization format according to RFC3016." into gingerbread
This commit is contained in:
committed by
Android (Google) Code Review
commit
223e4f732a
166
media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
Normal file
166
media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
Normal 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> ¬ify)
|
||||
: 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
|
||||
58
media/libstagefright/rtsp/AMPEG4ElementaryAssembler.h
Normal file
58
media/libstagefright/rtsp/AMPEG4ElementaryAssembler.h
Normal 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> ¬ify);
|
||||
|
||||
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_
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ LOCAL_SRC_FILES:= \
|
||||
AAVCAssembler.cpp \
|
||||
AH263Assembler.cpp \
|
||||
AMPEG4AudioAssembler.cpp \
|
||||
AMPEG4ElementaryAssembler.cpp \
|
||||
APacketSource.cpp \
|
||||
ARTPAssembler.cpp \
|
||||
ARTPConnection.cpp \
|
||||
|
||||
Reference in New Issue
Block a user