Bug 1289438: [ogg] P1. Always seek to the closest keyframe. r=brion+1012
authorJean-Yves Avenard <jyavenard@mozilla.com>
Wed, 27 Jul 2016 16:19:03 +1000
changeset 349516 1e645059025f3e7aace65f496984a095c9858aa7
parent 349515 60d5223a8d886cb7b1ff1e6d57ee1817212cf5ee
child 349517 2f6157213da23a19e508bbe2da299e540cb3e54d
push id1230
push userjlund@mozilla.com
push dateMon, 31 Oct 2016 18:13:35 +0000
treeherdermozilla-release@5e06e3766db2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbrion
bugs1289438
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1289438: [ogg] P1. Always seek to the closest keyframe. r=brion+1012 MozReview-Commit-ID: 1IGFgU1GFz5
dom/media/ogg/OggCodecState.cpp
dom/media/ogg/OggCodecState.h
dom/media/ogg/OggDemuxer.cpp
--- a/dom/media/ogg/OggCodecState.cpp
+++ b/dom/media/ogg/OggCodecState.cpp
@@ -193,16 +193,23 @@ ogg_packet* OggCodecState::PacketOut() {
 
 ogg_packet* OggCodecState::PacketPeek() {
   if (mPackets.IsEmpty()) {
     return nullptr;
   }
   return mPackets.PeekFront();
 }
 
+void OggCodecState::PushFront(OggPacketQueue &&aOther)
+{
+  while (!aOther.IsEmpty()) {
+    mPackets.PushFront(aOther.Pop());
+  }
+}
+
 RefPtr<MediaRawData> OggCodecState::PacketOutAsMediaRawData()
 {
   ogg_packet* packet = PacketOut();
   if (!packet) {
     return nullptr;
   }
 
   NS_ASSERTION(!IsHeader(packet), "PacketOutAsMediaRawData can only be called on non-header packets");
--- a/dom/media/ogg/OggCodecState.h
+++ b/dom/media/ogg/OggCodecState.h
@@ -62,16 +62,17 @@ class OggPacketDeallocator : public nsDe
 class OggPacketQueue : private nsDeque {
 public:
   OggPacketQueue() : nsDeque(new OggPacketDeallocator()) {}
   ~OggPacketQueue() { Erase(); }
   bool IsEmpty() { return nsDeque::GetSize() == 0; }
   void Append(ogg_packet* aPacket);
   ogg_packet* PopFront() { return static_cast<ogg_packet*>(nsDeque::PopFront()); }
   ogg_packet* PeekFront() { return static_cast<ogg_packet*>(nsDeque::PeekFront()); }
+  ogg_packet* Pop() { return static_cast<ogg_packet*>(nsDeque::Pop()); }
   void PushFront(ogg_packet* aPacket) { nsDeque::PushFront(aPacket); }
   void Erase() { nsDeque::Erase(); }
 };
 
 // Encapsulates the data required for decoding an ogg bitstream and for
 // converting granulepos to timestamps.
 class OggCodecState {
 public:
@@ -171,16 +172,19 @@ public:
   // OggCodecState::ReleasePacket(). The packet will have a valid granulepos.
   ogg_packet* PacketOut();
 
   // Returns the next raw packet in the stream, or nullptr if there are no more
   // packets buffered in the packet queue, without consuming it.
   // The packet will have a valid granulepos.
   ogg_packet* PacketPeek();
 
+  // Moves all raw packets from aOther to the front of the current packet queue.
+  void PushFront(OggPacketQueue&& aOther);
+
   // Releases the memory used by a cloned packet. Every packet returned by
   // PacketOut() must be free'd using this function.
   static void ReleasePacket(ogg_packet* aPacket);
 
   // Returns the next packet in the stream as a MediaRawData, or nullptr
   // if there are no more packets buffered in the packet queue. More packets
   // can be buffered by inserting one or more pages into the stream by calling
   // PageIn(). The packet will have a valid granulepos.
--- a/dom/media/ogg/OggDemuxer.cpp
+++ b/dom/media/ogg/OggDemuxer.cpp
@@ -1164,39 +1164,56 @@ OggDemuxer::SeekInternal(TrackInfo::Trac
         // search over the whole media, using the known buffered ranges to
         // reduce the search space.
         res = SeekInUnbuffered(aType, target, startTime, endTime, ranges);
         NS_ENSURE_SUCCESS(res,res);
       }
     }
   }
 
-  if (aType == TrackInfo::kVideoTrack) {
-    // Demux forwards until we find the next keyframe. This is required,
-    // as although the seek should finish on a page containing a keyframe,
-    // there may be non-keyframes in the page before the keyframe.
-    // When doing fastSeek we display the first frame after the seek, so
-    // we need to advance the decode to the keyframe otherwise we'll get
-    // visual artifacts in the first frame output after the seek.
-    while (true) {
-      DemuxUntilPacketAvailable(aType, mTheoraState);
-      ogg_packet* packet = mTheoraState->PacketPeek();
-      if (packet == nullptr) {
-        OGG_DEBUG("End of Theora stream reached before keyframe found in indexed seek");
-        break;
-      }
-      if (mTheoraState->IsKeyframe(packet)) {
-        OGG_DEBUG("Theora keyframe found after seek");
-        break;
-      }
+  // Demux forwards until we find the first keyframe prior the target.
+  // there may be non-keyframes in the page before the keyframe.
+  // Additionally, we may have seeked to the first page referenced by the
+  // page index which may be quite far off the target.
+  // When doing fastSeek we display the first frame after the seek, so
+  // we need to advance the decode to the keyframe otherwise we'll get
+  // visual artifacts in the first frame output after the seek.
+  OggCodecState* state = GetTrackCodecState(aType);
+  OggPacketQueue tempPackets;
+  bool foundKeyframe = false;
+  while (true) {
+    DemuxUntilPacketAvailable(aType, state);
+    ogg_packet* packet = state->PacketPeek();
+    if (packet == nullptr) {
+      OGG_DEBUG("End of stream reached before keyframe found in indexed seek");
+      break;
+    }
+    int64_t startTstamp = state->PacketStartTime(packet);
+    if (foundKeyframe && startTstamp > adjustedTarget) {
+      break;
+    }
+    if (state->IsKeyframe(packet)) {
+      OGG_DEBUG("keyframe found after seeking at %lld", startTstamp);
+      tempPackets.Erase();
+      foundKeyframe = true;
+    }
+    if (foundKeyframe && startTstamp == adjustedTarget) {
+      break;
+    }
+    ogg_packet* releaseMe = state->PacketOut();
+    if (foundKeyframe) {
+      tempPackets.Append(releaseMe);
+    } else {
       // Discard video packets before the first keyframe.
-      ogg_packet* releaseMe = mTheoraState->PacketOut();
       OggCodecState::ReleasePacket(releaseMe);
     }
   }
+  // Re-add all packet into the codec state in order.
+  state->PushFront(Move(tempPackets));
+
   return NS_OK;
 }
 
 OggDemuxer::IndexedSeekResult
 OggDemuxer::RollbackIndexedSeek(TrackInfo::TrackType aType, int64_t aOffset)
 {
   if (mSkeletonState) {
     mSkeletonState->Deactivate();