[Patch] Drop Frames in Cortado
"Benjamin M. Schwartz" <[email protected]>
| Newsgroups | gmane.comp.multimedia.ogg.theora.devel |
|---|---|
| Message-ID | <[email protected]> |
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 The attached patch allows frames to be dropped in Cortado: Why: Currently, frames cannot truly be dropped in Cortado. If the decoded YUV arrives too late at the videosink, it will not be converted to RGB, but every frame must still be decoded. On slow systems, once the queue of undecoded Theora packets fills up, the video decoder will block the ogg demuxer, with the result that audio will not be decoded in real time. Once the audio gets sufficiently late, it is rejected by the audio sink, resulting in the appearance of a total lockup. In fact, both video and audio are being decoded, but too slowly to be worth displaying. How: This patch is a slavish transcription of the "leakiness" framework from Gstreamer's Queue element. A leaky queue drops buffers if it fills up, rather than blocking entirely. By making the queue of undecoded video buffers leaky, we can ensure that audio continues to play correctly even when there is not enough CPU for video. Intriguingly, jst's TheoraDec element was already correctly wired to notice the discontinuity introduced by a dropped buffer and skip all frames until the next keyframe. What: The effect of this patch is approximately to make Cortado fall back to keyframe-only decode when it runs out of CPU. Where: I tested the modified code in Sun JDK 1.1.8, running in Wine with JIT disabled (i.e. pure slow interpreted mode). Without this patch, the player runs for two seconds before appearing to hang, churning 100% CPU. With this patch, I was able to watch a 720p Theora/Vorbis file with uninterrupted audio and keyframe-only video. - --Ben P.S. Tuning max-buffers and max-lateness is welcome. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.11 (GNU/Linux) iEYEARECAAYFAkrsugUACgkQUJT6e6HFtqT/KQCffkdFpm0K8Ht78zFKc6Qrh9iS 35kAn3tIxYZQcXZR+NUYTq6gygFhoiyk =8Qv3 -----END PGP SIGNATURE----- _______________________________________________ theora-dev mailing list [email protected] http://lists.xiph.org/mailman/listinfo/theora-dev
drop_frames.patch
(text/plain, 4.8 KB)
diff --git a/src/com/fluendo/player/CortadoPipeline.java b/src/com/fluendo/player/CortadoPipeline.java
index 7a2cb0d..114039a 100644
--- a/src/com/fluendo/player/CortadoPipeline.java
+++ b/src/com/fluendo/player/CortadoPipeline.java
@@ -121,6 +121,7 @@ public class CortadoPipeline extends Pipeline implements PadListener, CapsListen
if (!setupVideoDec ("theoradec"))
return;
+ v_queue.setProperty("leaky", "2"); // 2 == Queue.LEAK_DOWNSTREAM
v_queue2.setProperty("maxBuffers","1");
add(v_queue);
diff --git a/src/com/fluendo/plugin/Queue.java b/src/com/fluendo/plugin/Queue.java
index 6a7e96a..b34dde9 100644
--- a/src/com/fluendo/plugin/Queue.java
+++ b/src/com/fluendo/plugin/Queue.java
@@ -24,23 +24,32 @@ import com.fluendo.utils.*;
public class Queue extends Element
{
+ /* leaky types */
+ public static final int NO_LEAK = 0;
+ public static final int LEAK_UPSTREAM = 1;
+ public static final int LEAK_DOWNSTREAM = 2;
+
private static final int DEFAULT_MAX_BUFFERS = 100;
private static final int DEFAULT_MAX_SIZE = -1;
private static final boolean DEFAULT_IS_BUFFER = false;
private static final int DEFAULT_LOW_PERCENT = 10;
private static final int DEFAULT_HIGH_PERCENT = 70;
-
+ private static final int DEFAULT_LEAKY = NO_LEAK;
+
private Vector queue = new Vector();
private int srcResult = Pad.WRONG_STATE;
private int size;
private boolean isBuffering;
private boolean isEOS;
+ private boolean headNeedsDiscont = false;
+ private boolean tailNeedsDiscont = false;
private int maxBuffers = DEFAULT_MAX_BUFFERS;
private int maxSize = DEFAULT_MAX_SIZE;
private boolean isBuffer = DEFAULT_IS_BUFFER;
private int lowPercent = DEFAULT_LOW_PERCENT;
private int highPercent = DEFAULT_HIGH_PERCENT;
+ private int leaky = DEFAULT_LEAKY;
private boolean isFilled() {
if (maxSize != -1) {
@@ -94,6 +103,24 @@ public class Queue extends Element
}
}
}
+
+ private void leakDownstream () {
+ /* for as long as the queue is filled, dequeue an item and discard it */
+ java.lang.Object leak;
+ while (isFilled()) {
+ synchronized (queue) {
+ leak = queue.lastElement();
+ if (leak == null) {
+ Debug.error("There is nothing to dequeue and the queue is still filled. This should not happen.");
+ }
+ queue.removeElementAt(queue.size()-1);
+ if (leak instanceof Buffer)
+ ((Buffer)leak).free();
+ headNeedsDiscont = true;
+ queue.notifyAll();
+ }
+ }
+ }
private Pad srcpad = new Pad(Pad.SRC, "src") {
protected void taskFunc() {
@@ -128,6 +155,11 @@ public class Queue extends Element
}
else {
Buffer buf = (Buffer) obj;
+
+ if (headNeedsDiscont) {
+ buf.setFlag(Buffer.FLAG_DISCONT, true);
+ headNeedsDiscont = false;
+ }
size -= buf.length;
@@ -267,20 +299,42 @@ public class Queue extends Element
}
while (isFilled()) {
- try {
- Debug.debug(parent.getName() + " full, waiting...");
- queue.wait();
- if (srcResult != OK) {
+ switch (leaky) {
+ case LEAK_UPSTREAM:
+ tailNeedsDiscont = true;
+ Debug.debug(parent.getName() + "is full, leaking buffer on upstream end");
buf.free();
- return srcResult;
- }
- }
- catch (InterruptedException ie) {
- ie.printStackTrace();
- buf.free();
- return WRONG_STATE;
+ queue.notifyAll();
+ return OK;
+ case LEAK_DOWNSTREAM:
+ leakDownstream();
+ break;
+ default:
+ Debug.warn("Unknown leaky type, using default");
+ /* fall-through */
+ case NO_LEAK:
+ try {
+ Debug.debug(parent.getName() + " full, waiting...");
+ queue.wait();
+ if (srcResult != OK) {
+ buf.free();
+ return srcResult;
+ }
+ }
+ catch (InterruptedException ie) {
+ ie.printStackTrace();
+ buf.free();
+ return WRONG_STATE;
+ }
+ break;
}
}
+
+ if (tailNeedsDiscont) {
+ buf.setFlag(Buffer.FLAG_DISCONT, true);
+ tailNeedsDiscont = false;
+ }
+
size += buf.length;
updateBuffering();
@@ -318,6 +372,8 @@ public class Queue extends Element
lowPercent = Integer.valueOf(value.toString()).intValue();
else if (name.equals("highPercent"))
highPercent = Integer.valueOf(value.toString()).intValue();
+ else if (name.equals("leaky"))
+ leaky = Integer.valueOf(value.toString()).intValue();
else
return false;
@@ -335,6 +391,8 @@ public class Queue extends Element
return new Integer (lowPercent);
else if (name.equals("highPercent"))
return new Integer (highPercent);
+ else if (name.equals("leaky"))
+ return new Integer (leaky);
return null;
}