From 10f0fe7bcd60bdb0eceb905e84ac11555e8c1b9d Mon Sep 17 00:00:00 2001 From: Johann Date: Thu, 29 Mar 2012 16:54:03 -0700 Subject: Use Cues to seek Matroska files On the first seek, load the Cues element. Parse it incrementally until the desired seek point can be located. This allows files to begin playing immediately. However, the Browser still seeks to 0 before playing embedded YouTube files. Because YouTube stores the cues at the end of the file, this causes it to seek, load the cues, then begin playing. It is still better than the previous behavior which blocked until the entire file was loaded. BUG=5921311 Change-Id: Iad2abc64ded3b4e2c2d2c478a969f68450754282 --- .../libstagefright/matroska/MatroskaExtractor.cpp | 110 ++++++++++++++++++--- 1 file changed, 97 insertions(+), 13 deletions(-) (limited to 'media') diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp index a0db719..8657d7f 100644 --- a/media/libstagefright/matroska/MatroskaExtractor.cpp +++ b/media/libstagefright/matroska/MatroskaExtractor.cpp @@ -313,11 +313,99 @@ void BlockIterator::seek( *actualFrameTimeUs = -1ll; - int64_t seekTimeNs = seekTimeUs * 1000ll; + const int64_t seekTimeNs = seekTimeUs * 1000ll; + + mkvparser::Segment* const pSegment = mExtractor->mSegment; + + // Special case the 0 seek to avoid loading Cues when the application + // extraneously seeks to 0 before playing. + if (seekTimeNs <= 0) { + ALOGV("Seek to beginning: %lld", seekTimeUs); + mCluster = pSegment->GetFirst(); + mBlockEntryIndex = 0; + do { + advance_l(); + } while (!eos() && block()->GetTrackNumber() != mTrackNum); + return; + } - mCluster = mExtractor->mSegment->FindCluster(seekTimeNs); - mBlockEntry = NULL; - mBlockEntryIndex = 0; + ALOGV("Seeking to: %lld", seekTimeUs); + + // If the Cues have not been located then find them. + const mkvparser::Cues* pCues = pSegment->GetCues(); + const mkvparser::SeekHead* pSH = pSegment->GetSeekHead(); + if (!pCues && pSH) { + const size_t count = pSH->GetCount(); + const mkvparser::SeekHead::Entry* pEntry; + ALOGV("No Cues yet"); + + for (size_t index = 0; index < count; index++) { + pEntry = pSH->GetEntry(index); + + if (pEntry->id == 0x0C53BB6B) { // Cues ID + long len; long long pos; + pSegment->ParseCues(pEntry->pos, pos, len); + pCues = pSegment->GetCues(); + // Pull one cue point to fix loop below + ALOGV("Loading Cue points"); + pCues->LoadCuePoint(); + break; + } + } + + if (!pCues) { + ALOGE("No Cues in file"); + return; + } + } + else if (!pSH) { + ALOGE("No SeekHead"); + return; + } + + const mkvparser::CuePoint* pCP; + while (!pCues->DoneParsing()) { + // Make sure we don't have the necessary Cue already. + // If one Cue hadn't been loaded it would need to pre-emptively + // load one every time (until they are all loaded). + pCP = pCues->GetLast(); + if (pCP->GetTime(pSegment) >= seekTimeNs) { + ALOGV("Located segment"); + break; + } + + pCues->LoadCuePoint(); + } + + // Find the video track for seeking. It doesn't make sense to search the + // audio track because we'd still want to make sure we're jumping to a + // keyframe in the video track. + mkvparser::Tracks const *pTracks = pSegment->GetTracks(); + const mkvparser::Track *pTrack = NULL; + for (size_t index = 0; index < pTracks->GetTracksCount(); ++index) { + pTrack = pTracks->GetTrackByIndex(index); + if (pTrack && pTrack->GetType() == 1) { // VIDEO_TRACK + ALOGV("Video track located at %d", index); + break; + } + } + + const mkvparser::CuePoint::TrackPosition* pTP; + if (pTrack) { + pCues->Find(seekTimeNs, pTrack, pCP, pTP); + } else { + ALOGE("Did not locate a VIDEO_TRACK"); + return; + } + + mCluster = pSegment->FindOrPreloadCluster(pTP->m_pos); + if (pTP->m_block > 0) { + // m_block starts at 1, but mBlockEntryIndex is expected to start at 0 + mBlockEntryIndex = pTP->m_block - 1; + } else { + ALOGE("m_block must be > 0"); + return; + } long prevKeyFrameBlockEntryIndex = -1; @@ -593,16 +681,12 @@ MatroskaExtractor::MatroskaExtractor(const sp &source) return; } - if (isLiveStreaming()) { - ret = mSegment->ParseHeaders(); - CHECK_EQ(ret, 0); + ret = mSegment->ParseHeaders(); + CHECK_EQ(ret, 0); - long len; - ret = mSegment->LoadCluster(pos, len); - CHECK_EQ(ret, 0); - } else { - ret = mSegment->Load(); - } + long len; + ret = mSegment->LoadCluster(pos, len); + CHECK_EQ(ret, 0); if (ret < 0) { delete mSegment; -- cgit v1.1