summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/mpeg2ts
diff options
context:
space:
mode:
authorAndreas Huber <andih@google.com>2011-05-19 08:37:39 -0700
committerAndreas Huber <andih@google.com>2011-05-25 15:57:07 -0700
commit386d609dc513e838c7e7c4c46c604493ccd560be (patch)
treed7352f19380578d525ad4a667e51a66ed84e2d58 /media/libstagefright/mpeg2ts
parentade8c2d682d4f8c9424dece53e39400c237d22bf (diff)
downloadframeworks_av-386d609dc513e838c7e7c4c46c604493ccd560be.zip
frameworks_av-386d609dc513e838c7e7c4c46c604493ccd560be.tar.gz
frameworks_av-386d609dc513e838c7e7c4c46c604493ccd560be.tar.bz2
Support mpeg1,2 audio and mpeg1,2,4 video content extraction from .ts streams.
Change-Id: I9d2ee63495f161e30daba7c3aab16cb9d8ced6a5
Diffstat (limited to 'media/libstagefright/mpeg2ts')
-rw-r--r--media/libstagefright/mpeg2ts/ATSParser.cpp130
-rw-r--r--media/libstagefright/mpeg2ts/ATSParser.h15
-rw-r--r--media/libstagefright/mpeg2ts/ESQueue.cpp485
-rw-r--r--media/libstagefright/mpeg2ts/ESQueue.h8
-rw-r--r--media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp6
5 files changed, 614 insertions, 30 deletions
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 7d4bc6e..5bbc2b4 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -44,7 +44,7 @@ namespace android {
static const size_t kTSPacketSize = 188;
struct ATSParser::Program : public RefBase {
- Program(ATSParser *parser, unsigned programMapPID);
+ Program(ATSParser *parser, unsigned programNumber, unsigned programMapPID);
bool parsePID(
unsigned pid, unsigned payload_unit_start_indicator,
@@ -63,8 +63,15 @@ struct ATSParser::Program : public RefBase {
return mFirstPTSValid;
}
+ unsigned number() const { return mProgramNumber; }
+
+ void updateProgramMapPID(unsigned programMapPID) {
+ mProgramMapPID = programMapPID;
+ }
+
private:
ATSParser *mParser;
+ unsigned mProgramNumber;
unsigned mProgramMapPID;
KeyedVector<unsigned, sp<Stream> > mStreams;
bool mFirstPTSValid;
@@ -107,7 +114,7 @@ private:
DiscontinuityType mPendingDiscontinuity;
sp<AMessage> mPendingDiscontinuityExtra;
- ElementaryStreamQueue mQueue;
+ ElementaryStreamQueue *mQueue;
void flush();
void parsePES(ABitReader *br);
@@ -126,11 +133,14 @@ private:
////////////////////////////////////////////////////////////////////////////////
-ATSParser::Program::Program(ATSParser *parser, unsigned programMapPID)
+ATSParser::Program::Program(
+ ATSParser *parser, unsigned programNumber, unsigned programMapPID)
: mParser(parser),
+ mProgramNumber(programNumber),
mProgramMapPID(programMapPID),
mFirstPTSValid(false),
mFirstPTS(0) {
+ LOGV("new program number %u", programNumber);
}
bool ATSParser::Program::parsePID(
@@ -299,7 +309,7 @@ void ATSParser::Program::parseProgramMap(ABitReader *br) {
}
sp<MediaSource> ATSParser::Program::getSource(SourceType type) {
- size_t index = (type == MPEG2ADTS_AUDIO) ? 0 : 0;
+ size_t index = (type == AUDIO) ? 0 : 0;
for (size_t i = 0; i < mStreams.size(); ++i) {
sp<MediaSource> source = mStreams.editValueAt(i)->getSource(type);
@@ -338,14 +348,43 @@ ATSParser::Stream::Stream(
mBuffer(new ABuffer(192 * 1024)),
mPayloadStarted(false),
mPendingDiscontinuity(DISCONTINUITY_NONE),
- mQueue(streamType == 0x1b
- ? ElementaryStreamQueue::H264 : ElementaryStreamQueue::AAC) {
+ mQueue(NULL) {
mBuffer->setRange(0, 0);
+ switch (mStreamType) {
+ case STREAMTYPE_H264:
+ mQueue = new ElementaryStreamQueue(ElementaryStreamQueue::H264);
+ break;
+ case STREAMTYPE_MPEG2_AUDIO_ATDS:
+ mQueue = new ElementaryStreamQueue(ElementaryStreamQueue::AAC);
+ break;
+ case STREAMTYPE_MPEG1_AUDIO:
+ case STREAMTYPE_MPEG2_AUDIO:
+ mQueue = new ElementaryStreamQueue(
+ ElementaryStreamQueue::MPEG_AUDIO);
+ break;
+
+ case STREAMTYPE_MPEG1_VIDEO:
+ case STREAMTYPE_MPEG2_VIDEO:
+ mQueue = new ElementaryStreamQueue(
+ ElementaryStreamQueue::MPEG_VIDEO);
+ break;
+
+ case STREAMTYPE_MPEG4_VIDEO:
+ mQueue = new ElementaryStreamQueue(
+ ElementaryStreamQueue::MPEG4_VIDEO);
+ break;
+
+ default:
+ break;
+ }
+
LOGV("new stream PID 0x%02x, type 0x%02x", elementaryPID, streamType);
}
ATSParser::Stream::~Stream() {
+ delete mQueue;
+ mQueue = NULL;
}
void ATSParser::Stream::parse(
@@ -397,7 +436,7 @@ void ATSParser::Stream::signalDiscontinuity(
{
bool isASeek = (type == DISCONTINUITY_SEEK);
- mQueue.clear(!isASeek);
+ mQueue->clear(!isASeek);
uint64_t resumeAtPTS;
if (extra != NULL
@@ -444,6 +483,12 @@ void ATSParser::Stream::parsePES(ABitReader *br) {
LOGV("packet_startcode_prefix = 0x%08x", packet_startcode_prefix);
+ if (packet_startcode_prefix != 1) {
+ LOGV("Supposedly payload_unit_start=1 unit does not start "
+ "with startcode.");
+ return;
+ }
+
CHECK_EQ(packet_startcode_prefix, 0x000001u);
unsigned stream_id = br->getBits(8);
@@ -611,22 +656,28 @@ void ATSParser::Stream::onPayloadData(
const uint8_t *data, size_t size) {
LOGV("onPayloadData mStreamType=0x%02x", mStreamType);
+ if (mQueue == NULL) {
+ return;
+ }
+
CHECK(PTS_DTS_flags == 2 || PTS_DTS_flags == 3);
int64_t timeUs = mProgram->convertPTSToTimestamp(PTS);
- status_t err = mQueue.appendData(data, size, timeUs);
+ status_t err = mQueue->appendData(data, size, timeUs);
if (err != OK) {
return;
}
sp<ABuffer> accessUnit;
- while ((accessUnit = mQueue.dequeueAccessUnit()) != NULL) {
+ while ((accessUnit = mQueue->dequeueAccessUnit()) != NULL) {
if (mSource == NULL) {
- sp<MetaData> meta = mQueue.getFormat();
+ sp<MetaData> meta = mQueue->getFormat();
if (meta != NULL) {
- LOGV("created source!");
+ LOGV("Stream PID 0x%08x of type 0x%02x now has data.",
+ mElementaryPID, mStreamType);
+
mSource = new AnotherPacketSource(meta);
if (mPendingDiscontinuity != DISCONTINUITY_NONE) {
@@ -638,13 +689,13 @@ void ATSParser::Stream::onPayloadData(
mSource->queueAccessUnit(accessUnit);
}
- } else if (mQueue.getFormat() != NULL) {
+ } else if (mQueue->getFormat() != NULL) {
// After a discontinuity we invalidate the queue's format
// and won't enqueue any access units to the source until
// the queue has reestablished the new format.
if (mSource->getFormat() == NULL) {
- mSource->setFormat(mQueue.getFormat());
+ mSource->setFormat(mQueue->getFormat());
}
mSource->queueAccessUnit(accessUnit);
}
@@ -652,9 +703,30 @@ void ATSParser::Stream::onPayloadData(
}
sp<MediaSource> ATSParser::Stream::getSource(SourceType type) {
- if ((type == AVC_VIDEO && mStreamType == 0x1b)
- || (type == MPEG2ADTS_AUDIO && mStreamType == 0x0f)) {
- return mSource;
+ switch (type) {
+ case VIDEO:
+ {
+ if (mStreamType == STREAMTYPE_H264
+ || mStreamType == STREAMTYPE_MPEG1_VIDEO
+ || mStreamType == STREAMTYPE_MPEG2_VIDEO
+ || mStreamType == STREAMTYPE_MPEG4_VIDEO) {
+ return mSource;
+ }
+ break;
+ }
+
+ case AUDIO:
+ {
+ if (mStreamType == STREAMTYPE_MPEG1_AUDIO
+ || mStreamType == STREAMTYPE_MPEG2_AUDIO
+ || mStreamType == STREAMTYPE_MPEG2_AUDIO_ATDS) {
+ return mSource;
+ }
+ break;
+ }
+
+ default:
+ break;
}
return NULL;
@@ -729,7 +801,21 @@ void ATSParser::parseProgramAssociationTable(ABitReader *br) {
LOGV(" program_map_PID = 0x%04x", programMapPID);
- mPrograms.push(new Program(this, programMapPID));
+ bool found = false;
+ for (size_t index = 0; index < mPrograms.size(); ++index) {
+ const sp<Program> &program = mPrograms.itemAt(index);
+
+ if (program->number() == program_number) {
+ program->updateProgramMapPID(programMapPID);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ mPrograms.push(
+ new Program(this, program_number, programMapPID));
+ }
}
}
@@ -805,8 +891,16 @@ void ATSParser::parseTS(ABitReader *br) {
}
sp<MediaSource> ATSParser::getSource(SourceType type) {
+ int which = -1; // any
+
for (size_t i = 0; i < mPrograms.size(); ++i) {
- sp<MediaSource> source = mPrograms.editItemAt(i)->getSource(type);
+ const sp<Program> &program = mPrograms.editItemAt(i);
+
+ if (which >= 0 && (int)program->number() != which) {
+ continue;
+ }
+
+ sp<MediaSource> source = program->getSource(type);
if (source != NULL) {
return source;
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 3936f05..1e6451d 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -48,8 +48,8 @@ struct ATSParser : public RefBase {
void signalEOS(status_t finalResult);
enum SourceType {
- AVC_VIDEO,
- MPEG2ADTS_AUDIO
+ VIDEO,
+ AUDIO
};
sp<MediaSource> getSource(SourceType type);
@@ -59,6 +59,17 @@ protected:
virtual ~ATSParser();
private:
+ enum {
+ // From ISO/IEC 13818-1: 2000 (E), Table 2-29
+ STREAMTYPE_MPEG1_VIDEO = 0x01,
+ STREAMTYPE_MPEG2_VIDEO = 0x02,
+ STREAMTYPE_MPEG1_AUDIO = 0x03,
+ STREAMTYPE_MPEG2_AUDIO = 0x04,
+ STREAMTYPE_MPEG2_AUDIO_ATDS = 0x0f,
+ STREAMTYPE_MPEG4_VIDEO = 0x10,
+ STREAMTYPE_H264 = 0x1b,
+ };
+
struct Program;
struct Stream;
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index dcaf9f7..f8a1d84 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -27,6 +27,7 @@
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
#include "include/avc_utils.h"
@@ -79,11 +80,49 @@ static bool IsSeeminglyValidADTSHeader(const uint8_t *ptr, size_t size) {
return true;
}
+static bool IsSeeminglyValidMPEGAudioHeader(const uint8_t *ptr, size_t size) {
+ if (size < 3) {
+ // Not enough data to verify header.
+ return false;
+ }
+
+ if (ptr[0] != 0xff || (ptr[1] >> 5) != 0x07) {
+ return false;
+ }
+
+ unsigned ID = (ptr[1] >> 3) & 3;
+
+ if (ID == 1) {
+ return false; // reserved
+ }
+
+ unsigned layer = (ptr[1] >> 1) & 3;
+
+ if (layer == 0) {
+ return false; // reserved
+ }
+
+ unsigned bitrateIndex = (ptr[2] >> 4);
+
+ if (bitrateIndex == 0x0f) {
+ return false; // reserved
+ }
+
+ unsigned samplingRateIndex = (ptr[2] >> 2) & 3;
+
+ if (samplingRateIndex == 3) {
+ return false; // reserved
+ }
+
+ return true;
+}
+
status_t ElementaryStreamQueue::appendData(
const void *data, size_t size, int64_t timeUs) {
if (mBuffer == NULL || mBuffer->size() == 0) {
switch (mMode) {
case H264:
+ case MPEG_VIDEO:
{
#if 0
if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) {
@@ -105,7 +144,40 @@ status_t ElementaryStreamQueue::appendData(
}
if (startOffset > 0) {
- LOGI("found something resembling an H.264 syncword at "
+ LOGI("found something resembling an H.264/MPEG syncword at "
+ "offset %ld",
+ startOffset);
+ }
+
+ data = &ptr[startOffset];
+ size -= startOffset;
+#endif
+ break;
+ }
+
+ case MPEG4_VIDEO:
+ {
+#if 0
+ if (size < 3 || memcmp("\x00\x00\x01", data, 3)) {
+ return ERROR_MALFORMED;
+ }
+#else
+ uint8_t *ptr = (uint8_t *)data;
+
+ ssize_t startOffset = -1;
+ for (size_t i = 0; i + 2 < size; ++i) {
+ if (!memcmp("\x00\x00\x01", &ptr[i], 3)) {
+ startOffset = i;
+ break;
+ }
+ }
+
+ if (startOffset < 0) {
+ return ERROR_MALFORMED;
+ }
+
+ if (startOffset > 0) {
+ LOGI("found something resembling an H.264/MPEG syncword at "
"offset %ld",
startOffset);
}
@@ -148,6 +220,33 @@ status_t ElementaryStreamQueue::appendData(
break;
}
+ case MPEG_AUDIO:
+ {
+ uint8_t *ptr = (uint8_t *)data;
+
+ ssize_t startOffset = -1;
+ for (size_t i = 0; i < size; ++i) {
+ if (IsSeeminglyValidMPEGAudioHeader(&ptr[i], size - i)) {
+ startOffset = i;
+ break;
+ }
+ }
+
+ if (startOffset < 0) {
+ return ERROR_MALFORMED;
+ }
+
+ if (startOffset > 0) {
+ LOGI("found something resembling an MPEG audio "
+ "syncword at offset %ld",
+ startOffset);
+ }
+
+ data = &ptr[startOffset];
+ size -= startOffset;
+ break;
+ }
+
default:
TRESPASS();
break;
@@ -190,11 +289,18 @@ status_t ElementaryStreamQueue::appendData(
}
sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnit() {
- if (mMode == H264) {
- return dequeueAccessUnitH264();
- } else {
- CHECK_EQ((unsigned)mMode, (unsigned)AAC);
- return dequeueAccessUnitAAC();
+ switch (mMode) {
+ case H264:
+ return dequeueAccessUnitH264();
+ case AAC:
+ return dequeueAccessUnitAAC();
+ case MPEG_VIDEO:
+ return dequeueAccessUnitMPEGVideo();
+ case MPEG4_VIDEO:
+ return dequeueAccessUnitMPEG4Video();
+ default:
+ CHECK_EQ((unsigned)mMode, (unsigned)MPEG_AUDIO);
+ return dequeueAccessUnitMPEGAudio();
}
}
@@ -455,4 +561,371 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitH264() {
return NULL;
}
+sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitMPEGAudio() {
+ const uint8_t *data = mBuffer->data();
+ size_t size = mBuffer->size();
+
+ if (size < 4) {
+ return NULL;
+ }
+
+ uint32_t header = U32_AT(data);
+
+ size_t frameSize;
+ int samplingRate, numChannels, bitrate, numSamples;
+ CHECK(GetMPEGAudioFrameSize(
+ header, &frameSize, &samplingRate, &numChannels,
+ &bitrate, &numSamples));
+
+ if (size < frameSize) {
+ return NULL;
+ }
+
+ sp<ABuffer> accessUnit = new ABuffer(frameSize);
+ memcpy(accessUnit->data(), data, frameSize);
+
+ memmove(mBuffer->data(),
+ mBuffer->data() + frameSize,
+ mBuffer->size() - frameSize);
+
+ mBuffer->setRange(0, mBuffer->size() - frameSize);
+
+ int64_t timeUs = fetchTimestamp(frameSize);
+ CHECK_GE(timeUs, 0ll);
+
+ accessUnit->meta()->setInt64("timeUs", timeUs);
+
+ if (mFormat == NULL) {
+ mFormat = new MetaData;
+ mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
+ mFormat->setInt32(kKeySampleRate, samplingRate);
+ mFormat->setInt32(kKeyChannelCount, numChannels);
+ }
+
+ return accessUnit;
+}
+
+static void EncodeSize14(uint8_t **_ptr, size_t size) {
+ CHECK_LE(size, 0x3fff);
+
+ uint8_t *ptr = *_ptr;
+
+ *ptr++ = 0x80 | (size >> 7);
+ *ptr++ = size & 0x7f;
+
+ *_ptr = ptr;
+}
+
+static sp<ABuffer> MakeMPEGVideoESDS(const sp<ABuffer> &csd) {
+ sp<ABuffer> esds = new ABuffer(csd->size() + 25);
+
+ uint8_t *ptr = esds->data();
+ *ptr++ = 0x03;
+ EncodeSize14(&ptr, 22 + csd->size());
+
+ *ptr++ = 0x00; // ES_ID
+ *ptr++ = 0x00;
+
+ *ptr++ = 0x00; // streamDependenceFlag, URL_Flag, OCRstreamFlag
+
+ *ptr++ = 0x04;
+ EncodeSize14(&ptr, 16 + csd->size());
+
+ *ptr++ = 0x40; // Audio ISO/IEC 14496-3
+
+ for (size_t i = 0; i < 12; ++i) {
+ *ptr++ = 0x00;
+ }
+
+ *ptr++ = 0x05;
+ EncodeSize14(&ptr, csd->size());
+
+ memcpy(ptr, csd->data(), csd->size());
+
+ return esds;
+}
+
+sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitMPEGVideo() {
+ const uint8_t *data = mBuffer->data();
+ size_t size = mBuffer->size();
+
+ bool sawPictureStart = false;
+ int pprevStartCode = -1;
+ int prevStartCode = -1;
+ int currentStartCode = -1;
+
+ size_t offset = 0;
+ while (offset + 3 < size) {
+ if (memcmp(&data[offset], "\x00\x00\x01", 3)) {
+ ++offset;
+ continue;
+ }
+
+ pprevStartCode = prevStartCode;
+ prevStartCode = currentStartCode;
+ currentStartCode = data[offset + 3];
+
+ if (currentStartCode == 0xb3 && mFormat == NULL) {
+ memmove(mBuffer->data(), mBuffer->data() + offset, size - offset);
+ size -= offset;
+ (void)fetchTimestamp(offset);
+ offset = 0;
+ mBuffer->setRange(0, size);
+ }
+
+ if ((prevStartCode == 0xb3 && currentStartCode != 0xb5)
+ || (pprevStartCode == 0xb3 && prevStartCode == 0xb5)) {
+ // seqHeader without/with extension
+
+ if (mFormat == NULL) {
+ CHECK_GE(size, 7u);
+
+ unsigned width =
+ (data[4] << 4) | data[5] >> 4;
+
+ unsigned height =
+ ((data[5] & 0x0f) << 8) | data[6];
+
+ mFormat = new MetaData;
+ mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG2);
+ mFormat->setInt32(kKeyWidth, width);
+ mFormat->setInt32(kKeyHeight, height);
+
+ LOGI("found MPEG2 video codec config (%d x %d)", width, height);
+
+ sp<ABuffer> csd = new ABuffer(offset);
+ memcpy(csd->data(), data, offset);
+
+ memmove(mBuffer->data(),
+ mBuffer->data() + offset,
+ mBuffer->size() - offset);
+
+ mBuffer->setRange(0, mBuffer->size() - offset);
+ size -= offset;
+ (void)fetchTimestamp(offset);
+ offset = 0;
+
+ // hexdump(csd->data(), csd->size());
+
+ sp<ABuffer> esds = MakeMPEGVideoESDS(csd);
+ mFormat->setData(
+ kKeyESDS, kTypeESDS, esds->data(), esds->size());
+
+ return NULL;
+ }
+ }
+
+ if (mFormat != NULL && currentStartCode == 0x00) {
+ // Picture start
+
+ if (!sawPictureStart) {
+ sawPictureStart = true;
+ } else {
+ sp<ABuffer> accessUnit = new ABuffer(offset);
+ memcpy(accessUnit->data(), data, offset);
+
+ memmove(mBuffer->data(),
+ mBuffer->data() + offset,
+ mBuffer->size() - offset);
+
+ mBuffer->setRange(0, mBuffer->size() - offset);
+
+ int64_t timeUs = fetchTimestamp(offset);
+ CHECK_GE(timeUs, 0ll);
+
+ offset = 0;
+
+ accessUnit->meta()->setInt64("timeUs", timeUs);
+
+ LOGV("returning MPEG video access unit at time %lld us",
+ timeUs);
+
+ // hexdump(accessUnit->data(), accessUnit->size());
+
+ return accessUnit;
+ }
+ }
+
+ ++offset;
+ }
+
+ return NULL;
+}
+
+static ssize_t getNextChunkSize(
+ const uint8_t *data, size_t size) {
+ static const char kStartCode[] = "\x00\x00\x01";
+
+ if (size < 3) {
+ return -EAGAIN;
+ }
+
+ if (memcmp(kStartCode, data, 3)) {
+ TRESPASS();
+ }
+
+ size_t offset = 3;
+ while (offset + 2 < size) {
+ if (!memcmp(&data[offset], kStartCode, 3)) {
+ return offset;
+ }
+
+ ++offset;
+ }
+
+ return -EAGAIN;
+}
+
+sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitMPEG4Video() {
+ uint8_t *data = mBuffer->data();
+ size_t size = mBuffer->size();
+
+ enum {
+ SKIP_TO_VISUAL_OBJECT_SEQ_START,
+ EXPECT_VISUAL_OBJECT_START,
+ EXPECT_VO_START,
+ EXPECT_VOL_START,
+ WAIT_FOR_VOP_START,
+ SKIP_TO_VOP_START,
+
+ } state;
+
+ if (mFormat == NULL) {
+ state = SKIP_TO_VISUAL_OBJECT_SEQ_START;
+ } else {
+ state = SKIP_TO_VOP_START;
+ }
+
+ int32_t width = -1, height = -1;
+
+ size_t offset = 0;
+ ssize_t chunkSize;
+ while ((chunkSize = getNextChunkSize(
+ &data[offset], size - offset)) > 0) {
+ bool discard = false;
+
+ unsigned chunkType = data[offset + 3];
+
+ switch (state) {
+ case SKIP_TO_VISUAL_OBJECT_SEQ_START:
+ {
+ if (chunkType == 0xb0) {
+ // Discard anything before this marker.
+
+ state = EXPECT_VISUAL_OBJECT_START;
+ } else {
+ discard = true;
+ }
+ break;
+ }
+
+ case EXPECT_VISUAL_OBJECT_START:
+ {
+ CHECK_EQ(chunkType, 0xb5);
+ state = EXPECT_VO_START;
+ break;
+ }
+
+ case EXPECT_VO_START:
+ {
+ CHECK_LE(chunkType, 0x1f);
+ state = EXPECT_VOL_START;
+ break;
+ }
+
+ case EXPECT_VOL_START:
+ {
+ CHECK((chunkType & 0xf0) == 0x20);
+
+ CHECK(ExtractDimensionsFromVOLHeader(
+ &data[offset], chunkSize,
+ &width, &height));
+
+ state = WAIT_FOR_VOP_START;
+ break;
+ }
+
+ case WAIT_FOR_VOP_START:
+ {
+ if (chunkType == 0xb3 || chunkType == 0xb6) {
+ // group of VOP or VOP start.
+
+ mFormat = new MetaData;
+ mFormat->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+
+ mFormat->setInt32(kKeyWidth, width);
+ mFormat->setInt32(kKeyHeight, height);
+
+ LOGI("found MPEG4 video codec config (%d x %d)",
+ width, height);
+
+ sp<ABuffer> csd = new ABuffer(offset);
+ memcpy(csd->data(), data, offset);
+
+ // hexdump(csd->data(), csd->size());
+
+ sp<ABuffer> esds = MakeMPEGVideoESDS(csd);
+ mFormat->setData(
+ kKeyESDS, kTypeESDS,
+ esds->data(), esds->size());
+
+ discard = true;
+ state = SKIP_TO_VOP_START;
+ }
+
+ break;
+ }
+
+ case SKIP_TO_VOP_START:
+ {
+ if (chunkType == 0xb6) {
+ offset += chunkSize;
+
+ sp<ABuffer> accessUnit = new ABuffer(offset);
+ memcpy(accessUnit->data(), data, offset);
+
+ memmove(data, &data[offset], size - offset);
+ size -= offset;
+ mBuffer->setRange(0, size);
+
+ int64_t timeUs = fetchTimestamp(offset);
+ CHECK_GE(timeUs, 0ll);
+
+ offset = 0;
+
+ accessUnit->meta()->setInt64("timeUs", timeUs);
+
+ LOGV("returning MPEG4 video access unit at time %lld us",
+ timeUs);
+
+ // hexdump(accessUnit->data(), accessUnit->size());
+
+ return accessUnit;
+ } else if (chunkType != 0xb3) {
+ offset += chunkSize;
+ discard = true;
+ }
+
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+
+ if (discard) {
+ (void)fetchTimestamp(offset);
+ memmove(data, &data[offset], size - offset);
+ size -= offset;
+ offset = 0;
+ mBuffer->setRange(0, size);
+ } else {
+ offset += chunkSize;
+ }
+ }
+
+ return NULL;
+}
+
} // namespace android
diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h
index 153cfe6..4035ed3 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.h
+++ b/media/libstagefright/mpeg2ts/ESQueue.h
@@ -31,7 +31,10 @@ struct MetaData;
struct ElementaryStreamQueue {
enum Mode {
H264,
- AAC
+ AAC,
+ MPEG_AUDIO,
+ MPEG_VIDEO,
+ MPEG4_VIDEO,
};
ElementaryStreamQueue(Mode mode);
@@ -57,6 +60,9 @@ private:
sp<ABuffer> dequeueAccessUnitH264();
sp<ABuffer> dequeueAccessUnitAAC();
+ sp<ABuffer> dequeueAccessUnitMPEGAudio();
+ sp<ABuffer> dequeueAccessUnitMPEGVideo();
+ sp<ABuffer> dequeueAccessUnitMPEG4Video();
// consume a logical (compressed) access unit of size "size",
// returns its timestamp in us (or -1 if no time information).
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index dfec47f..8250ad1 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -175,7 +175,7 @@ void MPEG2TSExtractor::init() {
if (!haveVideo) {
sp<AnotherPacketSource> impl =
(AnotherPacketSource *)mParser->getSource(
- ATSParser::AVC_VIDEO).get();
+ ATSParser::VIDEO).get();
if (impl != NULL) {
haveVideo = true;
@@ -186,7 +186,7 @@ void MPEG2TSExtractor::init() {
if (!haveAudio) {
sp<AnotherPacketSource> impl =
(AnotherPacketSource *)mParser->getSource(
- ATSParser::MPEG2ADTS_AUDIO).get();
+ ATSParser::AUDIO).get();
if (impl != NULL) {
haveAudio = true;
@@ -194,7 +194,7 @@ void MPEG2TSExtractor::init() {
}
}
- if (++numPacketsParsed > 2500) {
+ if (++numPacketsParsed > 10000) {
break;
}
}