Browse Source

Improved seeking support, especially for short video files

Support inter-page seeking, in case there are multiple packets per page.
Also support seeking in single-page files.
Bart van Strien 8 years ago
parent
commit
bc8783869e
2 changed files with 52 additions and 14 deletions
  1. 1 0
      changes.txt
  2. 51 14
      src/modules/video/theora/VideoStream.cpp

+ 1 - 0
changes.txt

@@ -10,6 +10,7 @@ Released: N/A
   * Fixed BezierCurves to error instead of hanging in some situations.
 
   * Improved command line argument handling.
+  * Improved seeking support, especially for short video files.
 
   * Updated the default error handler to allow copying the error to the clipboard when the user decides to do so.
   * Updated love.filesystem.setRequirePath to support multiple template '?' characters in each path.

+ 51 - 14
src/modules/video/theora/VideoStream.cpp

@@ -270,7 +270,10 @@ void VideoStream::rewind()
 
 void VideoStream::seekDecoder(double target)
 {
-	if (target < 0.01)
+	const double rewindThreshold = 0.01;
+	const double seekThreshold = 0.0001;
+
+	if (target < rewindThreshold)
 	{
 		rewind();
 		return;
@@ -279,7 +282,7 @@ void VideoStream::seekDecoder(double target)
 	double low = 0;
 	double high = file->getSize();
 
-	while (high-low > 0.0001)
+	while (high-low > rewindThreshold)
 	{
 		// Determine our next binary search position
 		double pos = (high+low)/2;
@@ -289,23 +292,57 @@ void VideoStream::seekDecoder(double target)
 		ogg_sync_reset(&sync);
 		ogg_sync_pageseek(&sync, &page);
 
-		// Read a packet
+		// Read a page
 		readPacket(false);
 		if (eos)
-			return;
+		{
+			// EOS, so we're definitely past our target (or the target is past
+			// the end)
+			high = pos;
+			eos = false;
 
-		// Determine if this is the right place
-		double curTime = th_granule_time(decoder, packet.granulepos);
-		double nextTime = th_granule_time(decoder, packet.granulepos+1);
+			// And a workaround for single-page files:
+			if (high < rewindThreshold)
+				rewind();
+			else
+				continue;
+		}
 
-		if (curTime == -1)
-			continue; // Invalid granule position (magic?)
-		else if (curTime <= target && nextTime > target)
-			break; // the current frame should be displaying right now
-		else if (curTime > target)
-			high = pos;
-		else
+		// Now search all packets in this page
+		int result = -1;
+		for (int i = 0; i < ogg_page_packets(&page); ++i)
+		{
+			if (i > 0)
+				readPacket(true);
+
+			// Determine if this is the right place
+			double curTime = th_granule_time(decoder, packet.granulepos);
+			double nextTime = th_granule_time(decoder, packet.granulepos+1);
+
+			if (curTime == -1)
+				continue; // Invalid granule position (magic?)
+			else if (curTime <= target && nextTime > target)
+			{
+				// the current frame should be displaying right now
+				result = 0;
+				break;
+			}
+			else if (curTime > target)
+			{
+				// No need to check the other packets, they're all past
+				// this one
+				result = 1;
+				break;
+			}
+		}
+
+		// The sign of result determines the direction
+		if (result == 0)
+			break;
+		else if (result < 0)
 			low = pos;
+		else
+			high = pos;
 	}
 
 	// Now update theora and our decoder on this new position of ours