summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/mpeg2ts/ATSParser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/libstagefright/mpeg2ts/ATSParser.cpp')
-rw-r--r--media/libstagefright/mpeg2ts/ATSParser.cpp289
1 files changed, 241 insertions, 48 deletions
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 482ccff..0a868bc 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -35,6 +35,7 @@
#include <media/stagefright/Utils.h>
#include <media/IStreamSource.h>
#include <utils/KeyedVector.h>
+#include <utils/Vector.h>
#include <inttypes.h>
@@ -47,7 +48,8 @@ namespace android {
static const size_t kTSPacketSize = 188;
struct ATSParser::Program : public RefBase {
- Program(ATSParser *parser, unsigned programNumber, unsigned programMapPID);
+ Program(ATSParser *parser, unsigned programNumber, unsigned programMapPID,
+ int64_t lastRecoveredPTS);
bool parsePSISection(
unsigned pid, ABitReader *br, status_t *err);
@@ -86,14 +88,22 @@ struct ATSParser::Program : public RefBase {
}
private:
+ struct StreamInfo {
+ unsigned mType;
+ unsigned mPID;
+ };
+
ATSParser *mParser;
unsigned mProgramNumber;
unsigned mProgramMapPID;
KeyedVector<unsigned, sp<Stream> > mStreams;
bool mFirstPTSValid;
uint64_t mFirstPTS;
+ int64_t mLastRecoveredPTS;
status_t parseProgramMap(ABitReader *br);
+ int64_t recoverPTS(uint64_t PTS_33bit);
+ bool switchPIDs(const Vector<StreamInfo> &infos);
DISALLOW_EVIL_CONSTRUCTORS(Program);
};
@@ -157,10 +167,12 @@ struct ATSParser::PSISection : public RefBase {
PSISection();
status_t append(const void *data, size_t size);
+ void setSkipBytes(uint8_t skip);
void clear();
bool isComplete() const;
bool isEmpty() const;
+ bool isCRCOkay() const;
const uint8_t *data() const;
size_t size() const;
@@ -170,6 +182,8 @@ protected:
private:
sp<ABuffer> mBuffer;
+ uint8_t mSkipBytes;
+ static uint32_t CRC_TABLE[];
DISALLOW_EVIL_CONSTRUCTORS(PSISection);
};
@@ -177,12 +191,14 @@ private:
////////////////////////////////////////////////////////////////////////////////
ATSParser::Program::Program(
- ATSParser *parser, unsigned programNumber, unsigned programMapPID)
+ ATSParser *parser, unsigned programNumber, unsigned programMapPID,
+ int64_t lastRecoveredPTS)
: mParser(parser),
mProgramNumber(programNumber),
mProgramMapPID(programMapPID),
mFirstPTSValid(false),
- mFirstPTS(0) {
+ mFirstPTS(0),
+ mLastRecoveredPTS(lastRecoveredPTS) {
ALOGV("new program number %u", programNumber);
}
@@ -237,10 +253,71 @@ void ATSParser::Program::signalEOS(status_t finalResult) {
}
}
-struct StreamInfo {
- unsigned mType;
- unsigned mPID;
-};
+bool ATSParser::Program::switchPIDs(const Vector<StreamInfo> &infos) {
+ bool success = false;
+
+ if (mStreams.size() == infos.size()) {
+ // build type->PIDs map for old and new mapping
+ size_t i;
+ KeyedVector<int32_t, Vector<int32_t> > oldType2PIDs, newType2PIDs;
+ for (i = 0; i < mStreams.size(); ++i) {
+ ssize_t index = oldType2PIDs.indexOfKey(mStreams[i]->type());
+ if (index < 0) {
+ oldType2PIDs.add(mStreams[i]->type(), Vector<int32_t>());
+ }
+ oldType2PIDs.editValueFor(mStreams[i]->type()).push_back(mStreams[i]->pid());
+ }
+ for (i = 0; i < infos.size(); ++i) {
+ ssize_t index = newType2PIDs.indexOfKey(infos[i].mType);
+ if (index < 0) {
+ newType2PIDs.add(infos[i].mType, Vector<int32_t>());
+ }
+ newType2PIDs.editValueFor(infos[i].mType).push_back(infos[i].mPID);
+ }
+
+ // we can recover if the number of streams for each type hasn't changed
+ if (oldType2PIDs.size() == newType2PIDs.size()) {
+ success = true;
+ for (i = 0; i < oldType2PIDs.size(); ++i) {
+ // KeyedVector is sorted, we just compare key and size of each index
+ if (oldType2PIDs.keyAt(i) != newType2PIDs.keyAt(i)
+ || oldType2PIDs[i].size() != newType2PIDs[i].size()) {
+ success = false;
+ break;
+ }
+ }
+ }
+
+ if (success) {
+ // save current streams to temp
+ KeyedVector<int32_t, sp<Stream> > temp;
+ for (i = 0; i < mStreams.size(); ++i) {
+ temp.add(mStreams.keyAt(i), mStreams.editValueAt(i));
+ }
+
+ mStreams.clear();
+ for (i = 0; i < temp.size(); ++i) {
+ // The two checks below shouldn't happen,
+ // we already checked above the stream count matches
+ ssize_t index = newType2PIDs.indexOfKey(temp[i]->type());
+ CHECK(index >= 0);
+ Vector<int32_t> &newPIDs = newType2PIDs.editValueAt(index);
+ CHECK(newPIDs.size() > 0);
+
+ // get the next PID for temp[i]->type() in the new PID map
+ Vector<int32_t>::iterator it = newPIDs.begin();
+
+ // change the PID of the stream, and add it back
+ temp.editValueAt(i)->setPID(*it);
+ mStreams.add(temp[i]->pid(), temp.editValueAt(i));
+
+ // removed the used PID
+ newPIDs.erase(it);
+ }
+ }
+ }
+ return success;
+}
status_t ATSParser::Program::parseProgramMap(ABitReader *br) {
unsigned table_id = br->getBits(8);
@@ -369,39 +446,8 @@ status_t ATSParser::Program::parseProgramMap(ABitReader *br) {
}
#endif
- // The only case we can recover from is if we have two streams
- // and they switched PIDs.
-
- bool success = false;
-
- if (mStreams.size() == 2 && infos.size() == 2) {
- const StreamInfo &info1 = infos.itemAt(0);
- const StreamInfo &info2 = infos.itemAt(1);
-
- sp<Stream> s1 = mStreams.editValueAt(0);
- sp<Stream> s2 = mStreams.editValueAt(1);
-
- bool caseA =
- info1.mPID == s1->pid() && info1.mType == s2->type()
- && info2.mPID == s2->pid() && info2.mType == s1->type();
-
- bool caseB =
- info1.mPID == s2->pid() && info1.mType == s1->type()
- && info2.mPID == s1->pid() && info2.mType == s2->type();
-
- if (caseA || caseB) {
- unsigned pid1 = s1->pid();
- unsigned pid2 = s2->pid();
- s1->setPID(pid2);
- s2->setPID(pid1);
-
- mStreams.clear();
- mStreams.add(s1->pid(), s1);
- mStreams.add(s2->pid(), s2);
-
- success = true;
- }
- }
+ // we can recover if number of streams for each type remain the same
+ bool success = switchPIDs(infos);
if (!success) {
ALOGI("Stream PIDs changed and we cannot recover.");
@@ -425,6 +471,32 @@ status_t ATSParser::Program::parseProgramMap(ABitReader *br) {
return OK;
}
+int64_t ATSParser::Program::recoverPTS(uint64_t PTS_33bit) {
+ // We only have the lower 33-bit of the PTS. It could overflow within a
+ // reasonable amount of time. To handle the wrap-around, use fancy math
+ // to get an extended PTS that is within [-0xffffffff, 0xffffffff]
+ // of the latest recovered PTS.
+ if (mLastRecoveredPTS < 0ll) {
+ // Use the original 33bit number for 1st frame, the reason is that
+ // if 1st frame wraps to negative that's far away from 0, we could
+ // never start. Only start wrapping around from 2nd frame.
+ mLastRecoveredPTS = static_cast<int64_t>(PTS_33bit);
+ } else {
+ mLastRecoveredPTS = static_cast<int64_t>(
+ ((mLastRecoveredPTS - PTS_33bit + 0x100000000ll)
+ & 0xfffffffe00000000ull) | PTS_33bit);
+ // We start from 0, but recovered PTS could be slightly below 0.
+ // Clamp it to 0 as rest of the pipeline doesn't take negative pts.
+ // (eg. video is read first and starts at 0, but audio starts at 0xfffffff0)
+ if (mLastRecoveredPTS < 0ll) {
+ ALOGI("Clamping negative recovered PTS (%" PRId64 ") to 0", mLastRecoveredPTS);
+ mLastRecoveredPTS = 0ll;
+ }
+ }
+
+ return mLastRecoveredPTS;
+}
+
sp<MediaSource> ATSParser::Program::getSource(SourceType type) {
size_t index = (type == AUDIO) ? 0 : 0;
@@ -455,6 +527,8 @@ bool ATSParser::Program::hasSource(SourceType type) const {
}
int64_t ATSParser::Program::convertPTSToTimestamp(uint64_t PTS) {
+ PTS = recoverPTS(PTS);
+
if (!(mParser->mFlags & TS_TIMESTAMPS_ARE_ABSOLUTE)) {
if (!mFirstPTSValid) {
mFirstPTSValid = true;
@@ -969,6 +1043,7 @@ ATSParser::ATSParser(uint32_t flags)
mAbsoluteTimeAnchorUs(-1ll),
mTimeOffsetValid(false),
mTimeOffsetUs(0ll),
+ mLastRecoveredPTS(-1ll),
mNumTSPacketsParsed(0),
mNumPCRs(0) {
mPSISections.add(0 /* PID */, new PSISection);
@@ -987,11 +1062,21 @@ status_t ATSParser::feedTSPacket(const void *data, size_t size) {
void ATSParser::signalDiscontinuity(
DiscontinuityType type, const sp<AMessage> &extra) {
int64_t mediaTimeUs;
- if ((type & DISCONTINUITY_TIME)
- && extra != NULL
- && extra->findInt64(
- IStreamListener::kKeyMediaTimeUs, &mediaTimeUs)) {
- mAbsoluteTimeAnchorUs = mediaTimeUs;
+ if ((type & DISCONTINUITY_TIME) && extra != NULL) {
+ if (extra->findInt64(IStreamListener::kKeyMediaTimeUs, &mediaTimeUs)) {
+ mAbsoluteTimeAnchorUs = mediaTimeUs;
+ }
+ if ((mFlags & TS_TIMESTAMPS_ARE_ABSOLUTE)
+ && extra->findInt64(
+ IStreamListener::kKeyRecentMediaTimeUs, &mediaTimeUs)) {
+ if (mAbsoluteTimeAnchorUs >= 0ll) {
+ mediaTimeUs -= mAbsoluteTimeAnchorUs;
+ }
+ if (mTimeOffsetValid) {
+ mediaTimeUs -= mTimeOffsetUs;
+ }
+ mLastRecoveredPTS = (mediaTimeUs * 9) / 100;
+ }
} else if (type == DISCONTINUITY_ABSOLUTE_TIME) {
int64_t timeUs;
CHECK(extra->findInt64("timeUs", &timeUs));
@@ -1075,7 +1160,7 @@ void ATSParser::parseProgramAssociationTable(ABitReader *br) {
if (!found) {
mPrograms.push(
- new Program(this, program_number, programMapPID));
+ new Program(this, program_number, programMapPID, mLastRecoveredPTS));
}
if (mPSISections.indexOfKey(programMapPID) < 0) {
@@ -1098,10 +1183,12 @@ status_t ATSParser::parsePID(
if (payload_unit_start_indicator) {
if (!section->isEmpty()) {
- return ERROR_UNSUPPORTED;
+ ALOGW("parsePID encounters payload_unit_start_indicator when section is not empty");
+ section->clear();
}
unsigned skip = br->getBits(8);
+ section->setSkipBytes(skip + 1); // skip filler bytes + pointer field itself
br->skipBits(skip * 8);
}
@@ -1116,6 +1203,9 @@ status_t ATSParser::parsePID(
return OK;
}
+ if (!section->isCRCOkay()) {
+ return BAD_VALUE;
+ }
ABitReader sectionBits(section->data(), section->size());
if (PID == 0) {
@@ -1338,7 +1428,79 @@ void ATSParser::updatePCR(
////////////////////////////////////////////////////////////////////////////////
-ATSParser::PSISection::PSISection() {
+
+// CRC32 used for PSI section. The table was generated by following command:
+// $ python pycrc.py --model crc-32-mpeg --algorithm table-driven --generate c
+// Visit http://www.tty1.net/pycrc/index_en.html for more details.
+uint32_t ATSParser::PSISection::CRC_TABLE[] = {
+ 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
+ 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
+ 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
+ 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
+ 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
+ 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
+ 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
+ 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
+ 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+ 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
+ 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
+ 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
+ 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
+ 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
+ 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
+ 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
+ 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
+ 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
+ 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
+ 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
+ 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
+ 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
+ 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
+ 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+ 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
+ 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
+ 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
+ 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
+ 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
+ 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
+ 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
+ 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
+ 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
+ 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
+ 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
+ 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
+ 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
+ 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
+ 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+ 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
+ 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
+ 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
+ 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
+ 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
+ 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
+ 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
+ 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
+ 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
+ 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
+ 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
+ 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
+ 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
+ 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
+ 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+ 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
+ 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
+ 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
+ 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
+ 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
+ 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
+ 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
+ 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
+ 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
+ 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
+ };
+
+ATSParser::PSISection::PSISection() :
+ mSkipBytes(0) {
}
ATSParser::PSISection::~PSISection() {
@@ -1369,10 +1531,15 @@ status_t ATSParser::PSISection::append(const void *data, size_t size) {
return OK;
}
+void ATSParser::PSISection::setSkipBytes(uint8_t skip) {
+ mSkipBytes = skip;
+}
+
void ATSParser::PSISection::clear() {
if (mBuffer != NULL) {
mBuffer->setRange(0, 0);
}
+ mSkipBytes = 0;
}
bool ATSParser::PSISection::isComplete() const {
@@ -1396,4 +1563,30 @@ size_t ATSParser::PSISection::size() const {
return mBuffer == NULL ? 0 : mBuffer->size();
}
+bool ATSParser::PSISection::isCRCOkay() const {
+ if (!isComplete()) {
+ return false;
+ }
+ uint8_t* data = mBuffer->data();
+
+ // Return true if section_syntax_indicator says no section follows the field section_length.
+ if ((data[1] & 0x80) == 0) {
+ return true;
+ }
+
+ unsigned sectionLength = U16_AT(data + 1) & 0xfff;
+ ALOGV("sectionLength %u, skip %u", sectionLength, mSkipBytes);
+
+ // Skip the preceding field present when payload start indicator is on.
+ sectionLength -= mSkipBytes;
+
+ uint32_t crc = 0xffffffff;
+ for(unsigned i = 0; i < sectionLength + 4 /* crc */; i++) {
+ uint8_t b = data[i];
+ int index = ((crc >> 24) ^ (b & 0xff)) & 0xff;
+ crc = CRC_TABLE[index] ^ (crc << 8);
+ }
+ ALOGV("crc: %08x\n", crc);
+ return (crc == 0);
+}
} // namespace android