From c200045aa661cf82474c2b3c1db0ac69db40452a Mon Sep 17 00:00:00 2001
From: Sven Gothel
Date: Fri, 16 Aug 2013 20:18:36 +0200
Subject: GLMediaPlayer Multithreaded Decoding: GLMediaPlayer* (Part-4) - WIP
- Use Platform.currentTimeMillis() for accurate timing!
- GLMediaPlayer / GLMediaPlayerImpl
- Add DEBUG_NATIVE property jogl.debug.GLMediaPlayer.Native
for verbose impl. messages, i.e. ffmpeg/libav
- Add 'synchronization' section in GLMediaPlayer API doc (WIP)
- Use passive non-blocking video synchronization,
i.e. repeat frames instead of 'sleep'.
Thx to Xerxes's suggestion.
- Add flushing of cached decoded frames,
allowing to remove complicated 'videoSCR_reset_latch'
- FramePusher (threaded decoding):
- Always create a shared context!
- Release context while pausing
- Pre/post 'getNextTextureImpl()' actions only
at makeCurrent/release.
- newFrameAvailable(..) signal after decoded frame is enqueued
- FFMPEGDynamicLibraryBundleInfo
- Bind add. functions of libavcodec:
+ "av_init_packet",
+ "av_new_packet",
+ "av_destruct_packet",
- Bind add. functions of libavformat:
+ "avformat_seek_file",
+ "av_read_play",
+ "av_read_pause",
- DEBUG property := FFMPEGMediaPlayer.DEBUG || DynamicLibraryBundleInfo.DEBUG;
- FFMPEGMediaPlayer
- Use libavformat's 'av_read_play()' and 'av_read_pause()',
which may get utilized for network streams, e.g. RTSP
- getNextTextureImpl(..):
- Fix retry loop
- Use postNextTextureImpl/preNextTextureImpl if desired (PSM)
- Native:
- Use fixed my_av_q2i32(..) macro (again)
- Use INVALID_PTS marker (synced w/ Java code)
- DEBUG: Dump more detailed frame information
- TODO: Consider passing frame_delay, especially for repeated frames!
- Tests (MovieSimple, MovieCube):
- Refine KeyEvents control for seek and speed.
- TODO:
- Proper audio clock calculation - difficult w/ OpenAL !
- Video / Audio sync:
- seek !
- streams w/ very async A/V frames
- Test Streams:
- Five-minute-sync-test.mp4
- Audio-Video-Sync-Test-Calibration-23.98fps-24fps.mp4
- sound_in_sync_test.mp4
- big_buck_bunny_1080p_surround.avi
---
.../com/jogamp/opengl/util/av/AudioSink.java | 3 ++
.../com/jogamp/opengl/util/av/GLMediaPlayer.java | 51 +++++++++++++++++++---
.../opengl/util/texture/TextureSequence.java | 14 ++++--
3 files changed, 60 insertions(+), 8 deletions(-)
(limited to 'src/jogl/classes/com/jogamp')
diff --git a/src/jogl/classes/com/jogamp/opengl/util/av/AudioSink.java b/src/jogl/classes/com/jogamp/opengl/util/av/AudioSink.java
index ba785ac31..d5db73c6b 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/av/AudioSink.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/av/AudioSink.java
@@ -71,6 +71,9 @@ public interface AudioSink {
public static final AudioDataFormat DefaultFormat = new AudioDataFormat(AudioDataType.PCM, 44100, 16, 2, true /* signed */, true /* fixed point */, true /* littleEndian */);
public static class AudioFrame {
+ /** Constant marking an invalid PTS, i.e. Integer.MIN_VALUE 0x80000000 {@value}. */
+ public static final int INVALID_PTS = 0x80000000 ; // == -2147483648 == Integer.MIN_VALUE;
+
public final ByteBuffer data;
public final int dataSize;
public final int pts;
diff --git a/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java b/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java
index fae88ea18..1b82bb994 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java
@@ -34,7 +34,6 @@ import javax.media.opengl.GL;
import javax.media.opengl.GLException;
import jogamp.opengl.Debug;
-import jogamp.opengl.util.av.GLMediaPlayerImpl;
import com.jogamp.opengl.util.texture.TextureSequence;
@@ -44,7 +43,8 @@ import com.jogamp.opengl.util.texture.TextureSequence;
*
* Audio maybe supported and played back internally or via an {@link AudioSink} implementation,
* if an audio stream is selected in {@link #initGLStream(GL, int, URLConnection, int, int)}.
- *
+ *
+ *
* GLMediaPlayer Lifecycle
*
*
@@ -56,6 +56,7 @@ import com.jogamp.opengl.util.texture.TextureSequence;
* {@link #destroy(GL)} | ANY | Uninitialized |
*
*
+ *
* Audio and video Stream IDs
*
*
@@ -91,14 +92,47 @@ import com.jogamp.opengl.util.texture.TextureSequence;
* Milliseconds granularity is also more than enough to deal with A-V synchronization,
* where the threshold usually lies within 100ms.
*
+ *
+ * Audio and video synchronization
+ *
+ * The class follows a passive A/V synchronization pattern.
+ * Audio is being untouched, while {@link #getNextTexture(GL, boolean)} delivers a new video frame
+ * only, if its timestamp is less than 22ms ahead of time.
+ * Otherwise the early frame is cached for later retrieval and the previous frame is returned.
+ * FIXME: Refine!
+ *
+ *
+ * https://en.wikipedia.org/wiki/Audio_to_video_synchronization
+ *
+ * d_av = v_pts - a_pts;
+ *
+ *
+ *
+ * Recommendation of audio/video pts time lead/lag at production:
+ *
+ * - Overall: +40ms and -60ms audio ahead video / audio after video
+ * - Each stage: +5ms and -15ms. audio ahead video / audio after video
+ *
+ *
+ *
+ * Recommendation of av pts time lead/lag at presentation:
+ *
+ * - TV: +15ms and -45ms. audio ahead video / audio after video.
+ * - Film: +22ms and -22ms. audio ahead video / audio after video.
+ *
+ *
*/
public interface GLMediaPlayer extends TextureSequence {
public static final boolean DEBUG = Debug.debug("GLMediaPlayer");
+ public static final boolean DEBUG_NATIVE = Debug.debug("GLMediaPlayer.Native");
/** Constant {@value} for mute or not available. See Audio and video Stream IDs. */
public static final int STREAM_ID_NONE = -2;
/** Constant {@value} for auto or unspecified. See Audio and video Stream IDs. */
public static final int STREAM_ID_AUTO = -1;
+
+ /** Maximum video frame async .. */
+ public static final int MAXIMUM_VIDEO_ASYNC = 22;
public interface GLMediaEventListener extends TexSeqEventListener {
@@ -228,13 +262,14 @@ public interface GLMediaPlayer extends TextureSequence {
public int getAID();
/**
- * @return the current decoded frame count since {@link #initGLStream(GL, int, URLConnection, int, int)}.
+ * @return the current decoded frame count since {@link #play()} and {@link #seek(int)}
+ * as increased by {@link #getNextTexture(GL, boolean)} or the decoding thread.
*/
public int getDecodedFrameCount();
/**
- * @return the current presented frame count since {@link #initGLStream(GL, int, URLConnection, int, int)},
- * increased by {@link #getNextTexture(GL, boolean)}.
+ * @return the current presented frame count since {@link #play()} and {@link #seek(int)}
+ * as increased by {@link #getNextTexture(GL, boolean)} for new frames.
*/
public int getPresentedFrameCount();
@@ -250,6 +285,9 @@ public interface GLMediaPlayer extends TextureSequence {
/**
* {@inheritDoc}
+ *
+ * See audio and video synchronization.
+ *
*/
@Override
public TextureSequence.TextureFrame getLastTexture() throws IllegalStateException;
@@ -260,6 +298,9 @@ public interface GLMediaPlayer extends TextureSequence {
*
* In case the current state is not {@link State#Playing}, {@link #getLastTexture()} is returned.
*
+ *
+ * See audio and video synchronization.
+ *
*
* @see #addEventListener(GLMediaEventListener)
* @see GLMediaEventListener#newFrameAvailable(GLMediaPlayer, TextureFrame, long)
diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureSequence.java b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureSequence.java
index 50801e791..05fda99ae 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureSequence.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureSequence.java
@@ -110,23 +110,31 @@ public interface TextureSequence {
* to associated related data.
*/
public static class TextureFrame {
- /** Constant marking an invalid PTS, i.e. Integer.MIN_VALUE {@value}. */
- public static final int INVALID_PTS = Integer.MIN_VALUE;
+ /** Constant marking an invalid PTS, i.e. Integer.MIN_VALUE 0x80000000 {@value}. */
+ public static final int INVALID_PTS = 0x80000000 ; // == -2147483648 == Integer.MIN_VALUE;
public TextureFrame(Texture t) {
texture = t;
pts = INVALID_PTS;
+ duration = 0;
}
public final Texture getTexture() { return texture; }
+ /** Get this frame's presentation timestamp (PTS) in milliseconds. */
public final int getPTS() { return pts; }
+ /** Set this frame's presentation timestamp (PTS) in milliseconds. */
public final void setPTS(int pts) { this.pts = pts; }
+ /** Get this frame's duration in milliseconds. */
+ public final int getDuration() { return duration; }
+ /** Set this frame's duration in milliseconds. */
+ public final void setDuration(int duration) { this.duration = duration; }
public String toString() {
- return "TextureFrame[" + pts + "ms: " + texture + "]";
+ return "TextureFrame[pts " + pts + " ms, l " + duration + " ms, "+ texture + "]";
}
protected final Texture texture;
protected int pts;
+ protected int duration;
}
public interface TexSeqEventListener {
--
cgit v1.2.3