From 2641ec2a91e2c78d81e0ee00e94139f23168c0d6 Mon Sep 17 00:00:00 2001 From: Iris Chang Date: Mon, 14 Jan 2019 14:50:56 +0800 Subject: [PATCH] Handle mkv file without cue point Some mkv or mka files don't have any cue point, especially mka file which is transformed from ffmpeg. When user plays such mkv or mka file, user cannot seek to correct position. To handle this issue, we ignore cues and load clusters. When we seek and find that it has no cue point, we call seekwithoutcue function, which will find correct position from loaded clusters. Bug: 123150866 Test: Test with a mkv or mka file without CUES and check if we can can seek to correct position and play normally. Change-Id: I77cb64ebee6261545ab3eff6a38538d854408b47 --- media/extractors/mkv/MatroskaExtractor.cpp | 99 +++++++++++++++++++--- 1 file changed, 86 insertions(+), 13 deletions(-) diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp index 7239302f57..a399940f99 100644 --- a/media/extractors/mkv/MatroskaExtractor.cpp +++ b/media/extractors/mkv/MatroskaExtractor.cpp @@ -119,6 +119,9 @@ private: const mkvparser::BlockEntry *mBlockEntry; long mBlockEntryIndex; + unsigned long mTrackType; + void seekwithoutcue_l(int64_t seekTimeUs, int64_t *actualFrameTimeUs); + void advance_l(); BlockIterator(const BlockIterator &); @@ -290,6 +293,7 @@ BlockIterator::BlockIterator( mCluster(NULL), mBlockEntry(NULL), mBlockEntryIndex(0) { + mTrackType = mExtractor->mSegment->GetTracks()->GetTrackByNumber(trackNum)->GetType(); reset(); } @@ -442,12 +446,14 @@ void BlockIterator::seek( } if (!pCues) { - ALOGE("No Cues in file"); + ALOGV("No Cues in file,seek without cue data"); + seekwithoutcue_l(seekTimeUs, actualFrameTimeUs); return; } } else if (!pSH) { - ALOGE("No SeekHead"); + ALOGV("No SeekHead, seek without cue data"); + seekwithoutcue_l(seekTimeUs, actualFrameTimeUs); return; } @@ -456,7 +462,9 @@ void BlockIterator::seek( while (!pCues->DoneParsing()) { pCues->LoadCuePoint(); pCP = pCues->GetLast(); - CHECK(pCP); + ALOGV("pCP = %s", pCP == NULL ? "NULL" : "not NULL"); + if (pCP == NULL) + continue; size_t trackCount = mExtractor->mTracks.size(); for (size_t index = 0; index < trackCount; ++index) { @@ -494,6 +502,7 @@ void BlockIterator::seek( // Always *search* based on the video track, but finalize based on mTrackNum if (!pTP) { ALOGE("Did not locate the video track for seeking"); + seekwithoutcue_l(seekTimeUs, actualFrameTimeUs); return; } @@ -537,6 +546,31 @@ int64_t BlockIterator::blockTimeUs() const { return (mBlockEntry->GetBlock()->GetTime(mCluster) + 500ll) / 1000ll; } +void BlockIterator::seekwithoutcue_l(int64_t seekTimeUs, int64_t *actualFrameTimeUs) { + mCluster = mExtractor->mSegment->FindCluster(seekTimeUs * 1000ll); + const long status = mCluster->GetFirst(mBlockEntry); + if (status < 0) { // error + ALOGE("get last blockenry failed!"); + mCluster = NULL; + return; + } + mBlockEntryIndex = 0; + while (!eos() && ((block()->GetTrackNumber() != mTrackNum) || (blockTimeUs() < seekTimeUs))) { + advance_l(); + } + + // video track will seek to the next key frame. + if (mTrackType == 1) { + while (!eos() && ((block()->GetTrackNumber() != mTrackNum) || + !mBlockEntry->GetBlock()->IsKey())) { + advance_l(); + } + } + *actualFrameTimeUs = blockTimeUs(); + ALOGV("seekTimeUs:%lld, actualFrameTimeUs:%lld, tracknum:%lld", + (long long)seekTimeUs, (long long)*actualFrameTimeUs, (long long)mTrackNum); +} + //////////////////////////////////////////////////////////////////////////////// static unsigned U24_AT(const uint8_t *ptr) { @@ -956,17 +990,56 @@ MatroskaExtractor::MatroskaExtractor(DataSourceHelper *source) return; } - // from mkvparser::Segment::Load(), but stop at first cluster - ret = mSegment->ParseHeaders(); - if (ret == 0) { - long len; - ret = mSegment->LoadCluster(pos, len); - if (ret >= 1) { - // no more clusters - ret = 0; + if (mIsLiveStreaming) { + // from mkvparser::Segment::Load(), but stop at first cluster + ret = mSegment->ParseHeaders(); + if (ret == 0) { + long len; + ret = mSegment->LoadCluster(pos, len); + if (ret >= 1) { + // no more clusters + ret = 0; + } + } else if (ret > 0) { + ret = mkvparser::E_BUFFER_NOT_FULL; + } + } else { + ret = mSegment->ParseHeaders(); + if (ret < 0) { + ALOGE("Segment parse header return fail %lld", ret); + delete mSegment; + mSegment = NULL; + return; + } else if (ret == 0) { + const mkvparser::Cues* mCues = mSegment->GetCues(); + const mkvparser::SeekHead* mSH = mSegment->GetSeekHead(); + if ((mCues == NULL) && (mSH != NULL)) { + size_t count = mSH->GetCount(); + const mkvparser::SeekHead::Entry* mEntry; + for (size_t index = 0; index < count; index++) { + mEntry = mSH->GetEntry(index); + if (mEntry->id == 0x0C53BB6B) { // Cues ID + long len; + long long pos; + mSegment->ParseCues(mEntry->pos, pos, len); + mCues = mSegment->GetCues(); + ALOGV("find cue data by seekhead"); + break; + } + } + } + + if (mCues) { + long len; + ret = mSegment->LoadCluster(pos, len); + ALOGV("has Cue data, Cluster num=%ld", mSegment->GetCount()); + } else { + long status_Load = mSegment->Load(); + ALOGW("no Cue data,Segment Load status:%ld",status_Load); + } + } else if (ret > 0) { + ret = mkvparser::E_BUFFER_NOT_FULL; } - } else if (ret > 0) { - ret = mkvparser::E_BUFFER_NOT_FULL; } if (ret < 0) {