From 2a4d22d79e927f2245537921e10fc5fda1c47a29 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 8 Sep 2010 14:32:20 -0700 Subject: Work to support switching transport streams mid-stream and signalling discontinuities to the decoder. Change-Id: I7150e5e7342e1117c524856b204aadcb763e06ed related-to-bug: 2368598 --- media/libstagefright/AwesomePlayer.cpp | 52 +++++- media/libstagefright/OMXCodec.cpp | 132 +++++++------- media/libstagefright/httplive/LiveSource.cpp | 195 ++++++++++++++++++--- media/libstagefright/httplive/M3UParser.cpp | 74 +++++++- media/libstagefright/include/AwesomePlayer.h | 3 +- media/libstagefright/include/LiveSource.h | 14 +- media/libstagefright/include/M3UParser.h | 3 + media/libstagefright/mpeg2ts/ATSParser.cpp | 34 +++- media/libstagefright/mpeg2ts/ATSParser.h | 1 + .../libstagefright/mpeg2ts/AnotherPacketSource.cpp | 36 ++-- media/libstagefright/mpeg2ts/AnotherPacketSource.h | 1 + media/libstagefright/mpeg2ts/ESQueue.cpp | 9 +- media/libstagefright/mpeg2ts/ESQueue.h | 1 + media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp | 14 +- 14 files changed, 459 insertions(+), 110 deletions(-) (limited to 'media/libstagefright') diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 3d0d108..8c17aab 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -561,6 +561,39 @@ void AwesomePlayer::onBufferingUpdate() { postBufferingEvent_l(); } +void AwesomePlayer::partial_reset_l() { + // Only reset the video renderer and shut down the video decoder. + // Then instantiate a new video decoder and resume video playback. + + mVideoRenderer.clear(); + + if (mLastVideoBuffer) { + mLastVideoBuffer->release(); + mLastVideoBuffer = NULL; + } + + if (mVideoBuffer) { + mVideoBuffer->release(); + mVideoBuffer = NULL; + } + + { + mVideoSource->stop(); + + // The following hack is necessary to ensure that the OMX + // component is completely released by the time we may try + // to instantiate it again. + wp tmp = mVideoSource; + mVideoSource.clear(); + while (tmp.promote() != NULL) { + usleep(1000); + } + IPCThreadState::self()->flushCommands(); + } + + CHECK_EQ(OK, initVideoDecoder(OMXCodec::kIgnoreCodecSpecificData)); +} + void AwesomePlayer::onStreamDone() { // Posted whenever any stream finishes playing. @@ -570,7 +603,21 @@ void AwesomePlayer::onStreamDone() { } mStreamDoneEventPending = false; - if (mStreamDoneStatus != ERROR_END_OF_STREAM) { + if (mStreamDoneStatus == INFO_DISCONTINUITY) { + // This special status is returned because an http live stream's + // video stream switched to a different bandwidth at this point + // and future data may have been encoded using different parameters. + // This requires us to shutdown the video decoder and reinstantiate + // a fresh one. + + LOGV("INFO_DISCONTINUITY"); + + CHECK(mVideoSource != NULL); + + partial_reset_l(); + postVideoEvent_l(); + return; + } else if (mStreamDoneStatus != ERROR_END_OF_STREAM) { LOGV("MEDIA_ERROR %d", mStreamDoneStatus); notifyListener_l( @@ -939,8 +986,7 @@ void AwesomePlayer::setVideoSource(sp source) { mVideoTrack = source; } -status_t AwesomePlayer::initVideoDecoder() { - uint32_t flags = 0; +status_t AwesomePlayer::initVideoDecoder(uint32_t flags) { mVideoSource = OMXCodec::Create( mClient.interface(), mVideoTrack->getFormat(), false, // createEncoder diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index e6cdac1..4648ad3 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -504,7 +504,7 @@ sp OMXCodec::Create( observer->setCodec(codec); - err = codec->configureCodec(meta); + err = codec->configureCodec(meta, flags); if (err == OK) { return codec; @@ -517,93 +517,95 @@ sp OMXCodec::Create( return NULL; } -status_t OMXCodec::configureCodec(const sp &meta) { - uint32_t type; - const void *data; - size_t size; - if (meta->findData(kKeyESDS, &type, &data, &size)) { - ESDS esds((const char *)data, size); - CHECK_EQ(esds.InitCheck(), OK); +status_t OMXCodec::configureCodec(const sp &meta, uint32_t flags) { + if (!(flags & kIgnoreCodecSpecificData)) { + uint32_t type; + const void *data; + size_t size; + if (meta->findData(kKeyESDS, &type, &data, &size)) { + ESDS esds((const char *)data, size); + CHECK_EQ(esds.InitCheck(), OK); - const void *codec_specific_data; - size_t codec_specific_data_size; - esds.getCodecSpecificInfo( - &codec_specific_data, &codec_specific_data_size); + const void *codec_specific_data; + size_t codec_specific_data_size; + esds.getCodecSpecificInfo( + &codec_specific_data, &codec_specific_data_size); - addCodecSpecificData( - codec_specific_data, codec_specific_data_size); - } else if (meta->findData(kKeyAVCC, &type, &data, &size)) { - // Parse the AVCDecoderConfigurationRecord + addCodecSpecificData( + codec_specific_data, codec_specific_data_size); + } else if (meta->findData(kKeyAVCC, &type, &data, &size)) { + // Parse the AVCDecoderConfigurationRecord - const uint8_t *ptr = (const uint8_t *)data; + const uint8_t *ptr = (const uint8_t *)data; - CHECK(size >= 7); - CHECK_EQ(ptr[0], 1); // configurationVersion == 1 - uint8_t profile = ptr[1]; - uint8_t level = ptr[3]; + CHECK(size >= 7); + CHECK_EQ(ptr[0], 1); // configurationVersion == 1 + uint8_t profile = ptr[1]; + uint8_t level = ptr[3]; - // There is decodable content out there that fails the following - // assertion, let's be lenient for now... - // CHECK((ptr[4] >> 2) == 0x3f); // reserved + // There is decodable content out there that fails the following + // assertion, let's be lenient for now... + // CHECK((ptr[4] >> 2) == 0x3f); // reserved - size_t lengthSize = 1 + (ptr[4] & 3); + size_t lengthSize = 1 + (ptr[4] & 3); - // commented out check below as H264_QVGA_500_NO_AUDIO.3gp - // violates it... - // CHECK((ptr[5] >> 5) == 7); // reserved + // commented out check below as H264_QVGA_500_NO_AUDIO.3gp + // violates it... + // CHECK((ptr[5] >> 5) == 7); // reserved - size_t numSeqParameterSets = ptr[5] & 31; + size_t numSeqParameterSets = ptr[5] & 31; - ptr += 6; - size -= 6; + ptr += 6; + size -= 6; - for (size_t i = 0; i < numSeqParameterSets; ++i) { - CHECK(size >= 2); - size_t length = U16_AT(ptr); + for (size_t i = 0; i < numSeqParameterSets; ++i) { + CHECK(size >= 2); + size_t length = U16_AT(ptr); - ptr += 2; - size -= 2; + ptr += 2; + size -= 2; - CHECK(size >= length); + CHECK(size >= length); - addCodecSpecificData(ptr, length); + addCodecSpecificData(ptr, length); - ptr += length; - size -= length; - } + ptr += length; + size -= length; + } - CHECK(size >= 1); - size_t numPictureParameterSets = *ptr; - ++ptr; - --size; + CHECK(size >= 1); + size_t numPictureParameterSets = *ptr; + ++ptr; + --size; - for (size_t i = 0; i < numPictureParameterSets; ++i) { - CHECK(size >= 2); - size_t length = U16_AT(ptr); + for (size_t i = 0; i < numPictureParameterSets; ++i) { + CHECK(size >= 2); + size_t length = U16_AT(ptr); - ptr += 2; - size -= 2; + ptr += 2; + size -= 2; - CHECK(size >= length); + CHECK(size >= length); - addCodecSpecificData(ptr, length); + addCodecSpecificData(ptr, length); - ptr += length; - size -= length; - } + ptr += length; + size -= length; + } - CODEC_LOGV( - "AVC profile = %d (%s), level = %d", - (int)profile, AVCProfileToString(profile), level); + CODEC_LOGV( + "AVC profile = %d (%s), level = %d", + (int)profile, AVCProfileToString(profile), level); - if (!strcmp(mComponentName, "OMX.TI.Video.Decoder") - && (profile != kAVCProfileBaseline || level > 30)) { - // This stream exceeds the decoder's capabilities. The decoder - // does not handle this gracefully and would clobber the heap - // and wreak havoc instead... + if (!strcmp(mComponentName, "OMX.TI.Video.Decoder") + && (profile != kAVCProfileBaseline || level > 30)) { + // This stream exceeds the decoder's capabilities. The decoder + // does not handle this gracefully and would clobber the heap + // and wreak havoc instead... - LOGE("Profile and/or level exceed the decoder's capabilities."); - return ERROR_UNSUPPORTED; + LOGE("Profile and/or level exceed the decoder's capabilities."); + return ERROR_UNSUPPORTED; + } } } diff --git a/media/libstagefright/httplive/LiveSource.cpp b/media/libstagefright/httplive/LiveSource.cpp index 9103927..943a0fc 100644 --- a/media/libstagefright/httplive/LiveSource.cpp +++ b/media/libstagefright/httplive/LiveSource.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +//#define LOG_NDEBUG 0 #define LOG_TAG "LiveSource" #include @@ -22,18 +23,21 @@ #include "include/NuHTTPDataSource.h" #include +#include #include namespace android { LiveSource::LiveSource(const char *url) - : mURL(url), + : mMasterURL(url), mInitCheck(NO_INIT), mPlaylistIndex(0), mLastFetchTimeUs(-1), mSource(new NuHTTPDataSource), mSourceSize(0), - mOffsetBias(0) { + mOffsetBias(0), + mSignalDiscontinuity(false), + mPrevBandwidthIndex(-1) { if (switchToNext()) { mInitCheck = OK; } @@ -46,21 +50,129 @@ status_t LiveSource::initCheck() const { return mInitCheck; } -bool LiveSource::loadPlaylist() { +// static +int LiveSource::SortByBandwidth(const BandwidthItem *a, const BandwidthItem *b) { + if (a->mBandwidth < b->mBandwidth) { + return -1; + } else if (a->mBandwidth == b->mBandwidth) { + return 0; + } + + return 1; +} + +static double uniformRand() { + return (double)rand() / RAND_MAX; +} + +bool LiveSource::loadPlaylist(bool fetchMaster) { + mSignalDiscontinuity = false; + mPlaylist.clear(); mPlaylistIndex = 0; - sp buffer; - status_t err = fetchM3U(mURL.c_str(), &buffer); + if (fetchMaster) { + mPrevBandwidthIndex = -1; - if (err != OK) { - return false; + sp buffer; + status_t err = fetchM3U(mMasterURL.c_str(), &buffer); + + if (err != OK) { + return false; + } + + mPlaylist = new M3UParser( + mMasterURL.c_str(), buffer->data(), buffer->size()); + + if (mPlaylist->initCheck() != OK) { + return false; + } + + if (mPlaylist->isVariantPlaylist()) { + for (size_t i = 0; i < mPlaylist->size(); ++i) { + BandwidthItem item; + + sp meta; + mPlaylist->itemAt(i, &item.mURI, &meta); + + unsigned long bandwidth; + CHECK(meta->findInt32("bandwidth", (int32_t *)&item.mBandwidth)); + + mBandwidthItems.push(item); + } + mPlaylist.clear(); + + // fall through + if (mBandwidthItems.size() == 0) { + return false; + } + + mBandwidthItems.sort(SortByBandwidth); + + for (size_t i = 0; i < mBandwidthItems.size(); ++i) { + const BandwidthItem &item = mBandwidthItems.itemAt(i); + LOGV("item #%d: %s", i, item.mURI.c_str()); + } + } + } + + if (mBandwidthItems.size() > 0) { +#if 0 + // Change bandwidth at random() + size_t index = uniformRand() * mBandwidthItems.size(); +#elif 0 + // There's a 50% chance to stay on the current bandwidth and + // a 50% chance to switch to the next higher bandwidth (wrapping around + // to lowest) + size_t index; + if (uniformRand() < 0.5) { + index = mPrevBandwidthIndex < 0 ? 0 : (size_t)mPrevBandwidthIndex; + } else { + if (mPrevBandwidthIndex < 0) { + index = 0; + } else { + index = mPrevBandwidthIndex + 1; + if (index == mBandwidthItems.size()) { + index = 0; + } + } + } +#else + // Stay on the lowest bandwidth available. + size_t index = 0; // Lowest bandwidth stream +#endif + + mURL = mBandwidthItems.editItemAt(index).mURI; + + if (mPrevBandwidthIndex >= 0 && (size_t)mPrevBandwidthIndex != index) { + // If we switched streams because of bandwidth changes, + // we'll signal this discontinuity by inserting a + // special transport stream packet into the stream. + mSignalDiscontinuity = true; + } + + mPrevBandwidthIndex = index; + } else { + mURL = mMasterURL; } - mPlaylist = new M3UParser(mURL.c_str(), buffer->data(), buffer->size()); + if (mPlaylist == NULL) { + sp buffer; + status_t err = fetchM3U(mURL.c_str(), &buffer); - if (mPlaylist->initCheck() != OK) { - return false; + if (err != OK) { + return false; + } + + mPlaylist = new M3UParser(mURL.c_str(), buffer->data(), buffer->size()); + + if (mPlaylist->initCheck() != OK) { + return false; + } + + if (mPlaylist->isVariantPlaylist()) { + return false; + } } if (!mPlaylist->meta()->findInt32( @@ -79,6 +191,8 @@ static int64_t getNowUs() { } bool LiveSource::switchToNext() { + mSignalDiscontinuity = false; + mOffsetBias += mSourceSize; mSourceSize = 0; @@ -87,7 +201,7 @@ bool LiveSource::switchToNext() { int32_t nextSequenceNumber = mPlaylistIndex + mFirstItemSequenceNumber; - if (!loadPlaylist()) { + if (!loadPlaylist(mLastFetchTimeUs < 0)) { LOGE("failed to reload playlist"); return false; } @@ -111,35 +225,62 @@ bool LiveSource::switchToNext() { } AString uri; - CHECK(mPlaylist->itemAt(mPlaylistIndex, &uri)); - LOGI("switching to %s", uri.c_str()); + sp itemMeta; + CHECK(mPlaylist->itemAt(mPlaylistIndex, &uri, &itemMeta)); + LOGV("switching to %s", uri.c_str()); if (mSource->connect(uri.c_str()) != OK || mSource->getSize(&mSourceSize) != OK) { return false; } + int32_t val; + if (itemMeta->findInt32("discontinuity", &val) && val != 0) { + mSignalDiscontinuity = true; + } + mPlaylistIndex++; return true; } +static const ssize_t kHeaderSize = 188; + ssize_t LiveSource::readAt(off_t offset, void *data, size_t size) { CHECK(offset >= mOffsetBias); offset -= mOffsetBias; - if (offset >= mSourceSize) { - CHECK_EQ(offset, mSourceSize); + off_t delta = mSignalDiscontinuity ? kHeaderSize : 0; + + if (offset >= mSourceSize + delta) { + CHECK_EQ(offset, mSourceSize + delta); - offset -= mSourceSize; + offset -= mSourceSize + delta; if (!switchToNext()) { return ERROR_END_OF_STREAM; } + + if (mSignalDiscontinuity) { + LOGV("switchToNext changed streams"); + } else { + LOGV("switchToNext stayed within the same stream"); + } + + mOffsetBias += delta; + + delta = mSignalDiscontinuity ? kHeaderSize : 0; + } + + if (offset < delta) { + size_t avail = delta - offset; + memset(data, 0, avail); + return avail; } size_t numRead = 0; while (numRead < size) { ssize_t n = mSource->readAt( - offset + numRead, (uint8_t *)data + numRead, size - numRead); + offset + numRead - delta, + (uint8_t *)data + numRead, size - numRead); if (n <= 0) { break; @@ -154,14 +295,24 @@ ssize_t LiveSource::readAt(off_t offset, void *data, size_t size) { status_t LiveSource::fetchM3U(const char *url, sp *out) { *out = NULL; - status_t err = mSource->connect(url); + sp source; - if (err != OK) { - return err; + if (!strncasecmp(url, "file://", 7)) { + source = new FileSource(url + 7); + } else { + CHECK(!strncasecmp(url, "http://", 7)); + + status_t err = mSource->connect(url); + + if (err != OK) { + return err; + } + + source = mSource; } off_t size; - err = mSource->getSize(&size); + status_t err = source->getSize(&size); if (err != OK) { return err; @@ -170,7 +321,7 @@ status_t LiveSource::fetchM3U(const char *url, sp *out) { sp buffer = new ABuffer(size); size_t offset = 0; while (offset < (size_t)size) { - ssize_t n = mSource->readAt( + ssize_t n = source->readAt( offset, buffer->data() + offset, size - offset); if (n <= 0) { diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index edd8648..0d7daa9 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -74,7 +74,8 @@ bool M3UParser::itemAt(size_t index, AString *uri, sp *meta) { static bool MakeURL(const char *baseURL, const char *url, AString *out) { out->clear(); - if (strncasecmp("http://", baseURL, 7)) { + if (strncasecmp("http://", baseURL, 7) + && strncasecmp("file://", baseURL, 7)) { // Base URL must be absolute return false; } @@ -128,7 +129,12 @@ status_t M3UParser::parse(const void *_data, size_t size) { line.setTo(&data[offset], offsetLF - offset); } - LOGI("#%s#", line.c_str()); + // LOGI("#%s#", line.c_str()); + + if (line.empty()) { + offset = offsetLF + 1; + continue; + } if (lineNo == 0 && line == "#EXTM3U") { mIsExtM3U = true; @@ -152,11 +158,20 @@ status_t M3UParser::parse(const void *_data, size_t size) { return ERROR_MALFORMED; } err = parseMetaData(line, &itemMeta, "duration"); + } else if (line.startsWith("#EXT-X-DISCONTINUITY")) { + if (mIsVariantPlaylist) { + return ERROR_MALFORMED; + } + if (itemMeta == NULL) { + itemMeta = new AMessage; + } + itemMeta->setInt32("discontinuity", true); } else if (line.startsWith("#EXT-X-STREAM-INF")) { if (mMeta != NULL) { return ERROR_MALFORMED; } mIsVariantPlaylist = true; + err = parseStreamInf(line, &itemMeta); } if (err != OK) { @@ -215,6 +230,61 @@ status_t M3UParser::parseMetaData( } // static +status_t M3UParser::parseStreamInf( + const AString &line, sp *meta) { + ssize_t colonPos = line.find(":"); + + if (colonPos < 0) { + return ERROR_MALFORMED; + } + + size_t offset = colonPos + 1; + + while (offset < line.size()) { + ssize_t end = line.find(",", offset); + if (end < 0) { + end = line.size(); + } + + AString attr(line, offset, end - offset); + attr.trim(); + + offset = end + 1; + + ssize_t equalPos = attr.find("="); + if (equalPos < 0) { + continue; + } + + AString key(attr, 0, equalPos); + key.trim(); + + AString val(attr, equalPos + 1, attr.size() - equalPos - 1); + val.trim(); + + LOGV("key=%s value=%s", key.c_str(), val.c_str()); + + if (!strcasecmp("bandwidth", key.c_str())) { + const char *s = val.c_str(); + char *end; + unsigned long x = strtoul(s, &end, 10); + + if (end == s || *end != '\0') { + // malformed + continue; + } + + if (meta->get() == NULL) { + *meta = new AMessage; + } + (*meta)->setInt32("bandwidth", x); + } + } + + return OK; +} + +// static status_t M3UParser::ParseInt32(const char *s, int32_t *x) { char *end; long lval = strtol(s, &end, 10); diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index 40ea37d..bbf482d 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -220,6 +220,7 @@ private: status_t setDataSource_l(const sp &dataSource); status_t setDataSource_l(const sp &extractor); void reset_l(); + void partial_reset_l(); status_t seekTo_l(int64_t timeUs); status_t pause_l(bool at_eos = false); void initRenderer_l(); @@ -231,7 +232,7 @@ private: status_t initAudioDecoder(); void setVideoSource(sp source); - status_t initVideoDecoder(); + status_t initVideoDecoder(uint32_t flags = 0); void onStreamDone(); diff --git a/media/libstagefright/include/LiveSource.h b/media/libstagefright/include/LiveSource.h index c55508c..5e89581 100644 --- a/media/libstagefright/include/LiveSource.h +++ b/media/libstagefright/include/LiveSource.h @@ -44,6 +44,13 @@ protected: virtual ~LiveSource(); private: + struct BandwidthItem { + AString mURI; + unsigned long mBandwidth; + }; + Vector mBandwidthItems; + + AString mMasterURL; AString mURL; status_t mInitCheck; @@ -56,10 +63,15 @@ private: off_t mSourceSize; off_t mOffsetBias; + bool mSignalDiscontinuity; + ssize_t mPrevBandwidthIndex; + status_t fetchM3U(const char *url, sp *buffer); + static int SortByBandwidth(const BandwidthItem *a, const BandwidthItem *b); + bool switchToNext(); - bool loadPlaylist(); + bool loadPlaylist(bool fetchMaster); DISALLOW_EVIL_CONSTRUCTORS(LiveSource); }; diff --git a/media/libstagefright/include/M3UParser.h b/media/libstagefright/include/M3UParser.h index 36553de..69199ab 100644 --- a/media/libstagefright/include/M3UParser.h +++ b/media/libstagefright/include/M3UParser.h @@ -61,6 +61,9 @@ private: static status_t parseMetaData( const AString &line, sp *meta, const char *key); + static status_t parseStreamInf( + const AString &line, sp *meta); + static status_t ParseInt32(const char *s, int32_t *x); DISALLOW_EVIL_CONSTRUCTORS(M3UParser); diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index bcaab9f..7c9b83a 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -49,6 +49,8 @@ struct ATSParser::Program : public RefBase { unsigned pid, unsigned payload_unit_start_indicator, ABitReader *br); + void signalDiscontinuity(); + sp getSource(SourceType type); private: @@ -67,6 +69,8 @@ struct ATSParser::Stream : public RefBase { unsigned payload_unit_start_indicator, ABitReader *br); + void signalDiscontinuity(); + sp getSource(SourceType type); protected: @@ -124,6 +128,12 @@ bool ATSParser::Program::parsePID( return true; } +void ATSParser::Program::signalDiscontinuity() { + for (size_t i = 0; i < mStreams.size(); ++i) { + mStreams.editValueAt(i)->signalDiscontinuity(); + } +} + void ATSParser::Program::parseProgramMap(ABitReader *br) { unsigned table_id = br->getBits(8); LOGV(" table_id = %u", table_id); @@ -271,6 +281,19 @@ void ATSParser::Stream::parse( mBuffer->setRange(0, mBuffer->size() + payloadSizeBits / 8); } +void ATSParser::Stream::signalDiscontinuity() { + LOGV("Stream discontinuity"); + mPayloadStarted = false; + mBuffer->setRange(0, 0); + + mQueue.clear(); + + if (mStreamType == 0x1b && mSource != NULL) { + // Don't signal discontinuities on audio streams. + mSource->queueDiscontinuity(); + } +} + void ATSParser::Stream::parsePES(ABitReader *br) { unsigned packet_startcode_prefix = br->getBits(24); @@ -459,7 +482,10 @@ void ATSParser::Stream::onPayloadData( mSource = new AnotherPacketSource(meta); mSource->queueAccessUnit(accessUnit); } - } else { + } 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. mSource->queueAccessUnit(accessUnit); } } @@ -489,6 +515,12 @@ void ATSParser::feedTSPacket(const void *data, size_t size) { parseTS(&br); } +void ATSParser::signalDiscontinuity() { + for (size_t i = 0; i < mPrograms.size(); ++i) { + mPrograms.editItemAt(i)->signalDiscontinuity(); + } +} + void ATSParser::parseProgramAssociationTable(ABitReader *br) { unsigned table_id = br->getBits(8); LOGV(" table_id = %u", table_id); diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h index 1e22e7b..9ec6d7b 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.h +++ b/media/libstagefright/mpeg2ts/ATSParser.h @@ -33,6 +33,7 @@ struct ATSParser : public RefBase { ATSParser(); void feedTSPacket(const void *data, size_t size); + void signalDiscontinuity(); enum SourceType { AVC_VIDEO, diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp index 3d51177..3f76820 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp @@ -59,21 +59,26 @@ status_t AnotherPacketSource::read( if (!mBuffers.empty()) { const sp buffer = *mBuffers.begin(); + mBuffers.erase(mBuffers.begin()); - uint64_t timeUs; - CHECK(buffer->meta()->findInt64( - "time", (int64_t *)&timeUs)); - - MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size()); - mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs); + int32_t discontinuity; + if (buffer->meta()->findInt32("discontinuity", &discontinuity) + && discontinuity) { + return INFO_DISCONTINUITY; + } else { + uint64_t timeUs; + CHECK(buffer->meta()->findInt64( + "time", (int64_t *)&timeUs)); - // hexdump(buffer->data(), buffer->size()); + MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size()); + mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs); - memcpy(mediaBuffer->data(), buffer->data(), buffer->size()); - *out = mediaBuffer; + // hexdump(buffer->data(), buffer->size()); - mBuffers.erase(mBuffers.begin()); - return OK; + memcpy(mediaBuffer->data(), buffer->data(), buffer->size()); + *out = mediaBuffer; + return OK; + } } return mEOSResult; @@ -91,6 +96,15 @@ void AnotherPacketSource::queueAccessUnit(const sp &buffer) { mCondition.signal(); } +void AnotherPacketSource::queueDiscontinuity() { + sp buffer = new ABuffer(0); + buffer->meta()->setInt32("discontinuity", true); + + Mutex::Autolock autoLock(mLock); + mBuffers.push_back(buffer); + mCondition.signal(); +} + void AnotherPacketSource::signalEOS(status_t result) { CHECK(result != OK); diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h index ce83d21..6b43c4e 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h @@ -40,6 +40,7 @@ struct AnotherPacketSource : public MediaSource { bool hasBufferAvailable(status_t *finalResult); void queueAccessUnit(const sp &buffer); + void queueDiscontinuity(); void signalEOS(status_t result); protected: diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp index d87040b..4a75ee4 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.cpp +++ b/media/libstagefright/mpeg2ts/ESQueue.cpp @@ -115,6 +115,11 @@ static status_t getNextNALUnit( return OK; } +void ElementaryStreamQueue::clear() { + mBuffer->setRange(0, 0); + mFormat.clear(); +} + status_t ElementaryStreamQueue::appendData( const void *data, size_t size, int64_t timeUs) { if (mBuffer == NULL || mBuffer->size() == 0) { @@ -147,7 +152,7 @@ status_t ElementaryStreamQueue::appendData( if (mBuffer == NULL || neededSize > mBuffer->capacity()) { neededSize = (neededSize + 65535) & ~65535; - LOGI("resizing buffer to size %d", neededSize); + LOGV("resizing buffer to size %d", neededSize); sp buffer = new ABuffer(neededSize); if (mBuffer != NULL) { @@ -498,6 +503,8 @@ sp ElementaryStreamQueue::MakeAVCCodecSpecificData( meta->setInt32(kKeyWidth, width); meta->setInt32(kKeyHeight, height); + LOGI("found AVC codec config (%d x %d)", width, height); + return meta; } diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h index d2e87f2..246c390 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.h +++ b/media/libstagefright/mpeg2ts/ESQueue.h @@ -35,6 +35,7 @@ struct ElementaryStreamQueue { ElementaryStreamQueue(Mode mode); status_t appendData(const void *data, size_t size, int64_t timeUs); + void clear(); sp dequeueAccessUnit(); diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp index c5257bb..0d96bd1 100644 --- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp +++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp @@ -165,18 +165,26 @@ void MPEG2TSExtractor::init() { LOGI("haveAudio=%d, haveVideo=%d", haveAudio, haveVideo); } +static bool isDiscontinuity(const uint8_t *data, ssize_t size) { + return size == 188 && data[0] == 0x00; +} + status_t MPEG2TSExtractor::feedMore() { Mutex::Autolock autoLock(mLock); uint8_t packet[kTSPacketSize]; ssize_t n = mDataSource->readAt(mOffset, packet, kTSPacketSize); - if (n < (ssize_t)kTSPacketSize) { + if (isDiscontinuity(packet, n)) { + LOGI("XXX discontinuity detected"); + mParser->signalDiscontinuity(); + } else if (n < (ssize_t)kTSPacketSize) { return (n < 0) ? (status_t)n : ERROR_END_OF_STREAM; + } else { + mParser->feedTSPacket(packet, kTSPacketSize); } - mOffset += kTSPacketSize; - mParser->feedTSPacket(packet, kTSPacketSize); + mOffset += n; return OK; } -- cgit v1.1