Skip to content

Commit

Permalink
GLMediaPlayer Multithreaded Decoding: GLMediaPlayer* (Part-3) - WIP
Browse files Browse the repository at this point in the history
- GLMediaPlayer
  - Remove State.Stopped and method stop() - redundant, use pause() / destroy()
  - Add notion of stream IDs
  - Add API doc: State / Stream-ID incl. html-anchor
  - Expose video/audio PTS, ..
  - Expose optional AudioSink
  - Min multithreaded textureCount is 4 (EGL* and FFMPEG*)

- GLMediaPlayerImpl
  - Move AudioSink rel. impl. to this class,
    allowing a tight video implementation reusing logic.

  - Remove 'synchronized' methods, synchronize on State
    where applicable

  - implement new methods (see above)

  - playSpeed is handled partially in AudioSink.
    If it exeeds AudioSink's capabilities, drop audio and rely solely on video sync.

  - video sync (WIP)
    - video pts delay based on geometric weight
    - reset video SCR if 'out of range', resync w/ PTS
    -

  - FramePusher
    - allow interruption when pausing/stopping,
      while waiting for next avail free frame to decode.

- FFMPEGMediaPlayer
  - Add proper AudioDataFormat negotiation AudioSink <-> libav
  - Parse libav's SampleFormat
  - Remove AudioSink interaction (moved to GLMediaPlayerImpl)

- Tests (MovieSimple, MovieCube):
  - Add aid/vid selection
  - Add KeyListener for actions: seek(..), play()/pause(), setPlaySpeed(..)
  - Dump perf-string each 2s

- TODO:
  - Add audio sync in AudioSink, similar to GLMediaPlayer's weighted video delay,
    here: drop audio frames.
  • Loading branch information
sgothel committed Aug 14, 2013
1 parent bc37766 commit c37629e
Show file tree
Hide file tree
Showing 18 changed files with 1,283 additions and 618 deletions.
6 changes: 4 additions & 2 deletions make/scripts/tests.sh
Expand Up @@ -131,7 +131,9 @@ function jrun() {
#D_ARGS="-Djogl.1thread=true -Djogl.debug.Threading"
#D_ARGS="-Djogl.debug.DebugGL -Djogl.debug.TraceGL -Djogl.debug.GLContext.TraceSwitch -Djogl.debug=all"
#D_ARGS="-Djogamp.debug.IOUtil -Djogl.debug.GLSLCode -Djogl.debug.GLMediaPlayer"
#D_ARGS="-Djogl.debug.GLMediaPlayer"
#D_ARGS="-Djogl.debug.GLMediaPlayer -Djogl.debug.AudioSink"
#D_ARGS="-Djogl.debug.AudioSink"
#D_ARGS="-Djogl.debug.GLArrayData"
#D_ARGS="-Djogl.debug.GLDrawable"
#D_ARGS="-Djogl.debug.EGLDisplayUtil -Dnativewindow.debug.GraphicsConfiguration -Djogl.debug.GLDrawable"
Expand Down Expand Up @@ -314,8 +316,8 @@ function testawtswt() {
# av demos
#
#testnoawt jogamp.opengl.openal.av.ALDummyUsage $*
testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.av.MovieCube $*
#testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.av.MovieSimple $*
#testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.av.MovieCube $*
testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.av.MovieSimple $*

#
# core/newt (testnoawt and testawt)
Expand Down
146 changes: 107 additions & 39 deletions src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java
Expand Up @@ -34,19 +34,37 @@
import javax.media.opengl.GLException;

import jogamp.opengl.Debug;
import jogamp.opengl.util.av.GLMediaPlayerImpl;

import com.jogamp.opengl.util.texture.TextureSequence;

/**
* Lifecycle of an GLMediaPlayer:
* GLMediaPlayer interface specifies a {@link TextureSequence}
* with a video stream as it's source.
* <p>
* 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)}.
* </p>
* <a name="lifecycle"><h5>GLMediaPlayer Lifecycle</h5></a>
* <p>
* <table border="1">
* <tr><th>action</th> <th>state before</th> <th>state after</th></tr>
* <tr><td>{@link #initGLStream(GL, int, URLConnection, int, int)}</td> <td>Uninitialized</td> <td>Paused</td></tr>
* <tr><td>{@link #play()}</td> <td>Paused</td> <td>Playing</td></tr>
* <tr><td>{@link #pause()}</td> <td>Playing</td> <td>Paused</td></tr>
* <tr><td>{@link #seek(int)}</td> <td>Playing, Paused</td> <td>Unchanged</td></tr>
* <tr><td>{@link #destroy(GL)}</td> <td>ANY</td> <td>Uninitialized</td></tr>
* </table>
* </p>
* <a name="streamIDs"><h5>Audio and video Stream IDs</h5></a>
* <p>
* <table border="1">
* <tr><th>action</th> <th>state before</th> <th>state after</th></tr>
* <tr><td>{@link #initGLStream(GL, int, URLConnection)}</td> <td>Uninitialized</td> <td>Stopped</td></tr>
* <tr><td>{@link #start()}</td> <td>Stopped, Paused</td> <td>Playing</td></tr>
* <tr><td>{@link #stop()}</td> <td>Playing, Paused</td> <td>Stopped</td></tr>
* <tr><td>{@link #pause()}</td> <td>Playing</td> <td>Paused</td></tr>
* <tr><td>{@link #destroy(GL)}</td> <td>ANY</td> <td>Uninitialized</td></tr>
* <tr><th>value</th> <th>request</th> <th>get</th></tr>
* <tr><td>{@link #STREAM_ID_NONE}</td> <td>mute</td> <td>not available</td></tr>
* <tr><td>{@link #STREAM_ID_AUTO}</td> <td>auto</td> <td>unspecified</td></tr>
* <tr><td>&ge;0</td> <td>specific stream</td> <td>specific stream</td></tr>
* </table>
* </p>
* <p>
* Current implementations (check each API doc link for details):
* <ul>
Expand Down Expand Up @@ -76,14 +94,21 @@
*/
public interface GLMediaPlayer extends TextureSequence {
public static final boolean DEBUG = Debug.debug("GLMediaPlayer");

/** Constant {@value} for <i>mute</i> or <i>not available</i>. See <a href="#streamIDs">Audio and video Stream IDs</a>. */
public static final int STREAM_ID_NONE = -2;
/** Constant {@value} for <i>auto</i> or <i>unspecified</i>. See <a href="#streamIDs">Audio and video Stream IDs</a>. */
public static final int STREAM_ID_AUTO = -1;

public interface GLMediaEventListener extends TexSeqEventListener<GLMediaPlayer> {

static final int EVENT_CHANGE_SIZE = 1<<0;
static final int EVENT_CHANGE_FPS = 1<<1;
static final int EVENT_CHANGE_BPS = 1<<2;
static final int EVENT_CHANGE_LENGTH = 1<<3;
static final int EVENT_CHANGE_CODEC = 1<<3;
static final int EVENT_CHANGE_VID = 1<<0;
static final int EVENT_CHANGE_AID = 1<<1;
static final int EVENT_CHANGE_SIZE = 1<<2;
static final int EVENT_CHANGE_FPS = 1<<3;
static final int EVENT_CHANGE_BPS = 1<<4;
static final int EVENT_CHANGE_LENGTH = 1<<5;
static final int EVENT_CHANGE_CODEC = 1<<6;

/**
* @param mp the event source
Expand All @@ -93,8 +118,11 @@ public interface GLMediaEventListener extends TexSeqEventListener<GLMediaPlayer>
public void attributesChanges(GLMediaPlayer mp, int event_mask, long when);
}

/**
* See <a href="#lifecycle">GLMediaPlayer Lifecycle</a>.
*/
public enum State {
Uninitialized(0), Stopped(1), Playing(2), Paused(3);
Uninitialized(0), Playing(1), Paused(2);

public final int id;

Expand All @@ -120,74 +148,106 @@ public enum State {
* Sets the stream to be used. Initializes all stream related states inclusive OpenGL ones,
* if <code>gl</code> is not null.
* <p>
* Uninitialized -> Stopped
* <a href="#lifecycle">GLMediaPlayer Lifecycle</a>: Uninitialized -> Paused
* </p>
* @param gl current GL object. If null, no video output and textures will be available.
* @param textureCount desired number of buffered textures to be decoded off-thread, use <code>1</code> for on-thread decoding.
* @param urlConn the stream connection
* @param vid video stream id, see <a href="#streamIDs">audio and video Stream IDs</a>
* @param aid video stream id, see <a href="#streamIDs">audio and video Stream IDs</a>
* @return the new state
*
* @throws IllegalStateException if not invoked in state Uninitialized
* @throws IOException in case of difficulties to open or process the stream
* @throws GLException in case of difficulties to initialize the GL resources
*/
public State initGLStream(GL gl, int textureCount, URLConnection urlConn) throws IllegalStateException, GLException, IOException;
public State initGLStream(GL gl, int textureCount, URLConnection urlConn, int vid, int aid) throws IllegalStateException, GLException, IOException;

/**
* If implementation uses a {@link AudioSink}, it's instance will be returned.
* <p>
* The {@link AudioSink} instance is available after {@link #initGLStream(GL, int, URLConnection, int, int)},
* if used by implementation.
* </p>
*/
public AudioSink getAudioSink();

/**
* Releases the GL and stream resources.
* <p>
* <code>ANY</code> -> Uninitialized
* <a href="#lifecycle">GLMediaPlayer Lifecycle</a>: <code>ANY</code> -> Uninitialized
* </p>
*/
public State destroy(GL gl);

public void setPlaySpeed(float rate);
/**
* Sets the playback speed.
* <p>
* Play speed is set to <i>normal</i>, i.e. <code>1.0f</code>
* if <code> abs(1.0f - rate) < 0.01f</code> to simplify test.
* </p>
* @return true if successful, otherwise false, i.e. due to unsupported value range of implementation.
*/
public boolean setPlaySpeed(float rate);

public float getPlaySpeed();

/**
* Stopped/Paused -> Playing
* <a href="#lifecycle">GLMediaPlayer Lifecycle</a>: Paused -> Playing
*/
public State start();
public State play();

/**
* Playing -> Paused
* <a href="#lifecycle">GLMediaPlayer Lifecycle</a>: Playing -> Paused
*/
public State pause();

/**
* Playing/Paused -> Stopped
* Allowed in state Playing and Paused, otherwise ignored,
* see <a href="#lifecycle">GLMediaPlayer Lifecycle</a>.
*
* @param msec absolute desired time position in milliseconds
* @return time current position in milliseconds, after seeking to the desired position
**/
public int seek(int msec);

/**
* See <a href="#lifecycle">GLMediaPlayer Lifecycle</a>.
* @return the current state, either Uninitialized, Playing, Paused
*/
public State stop();
public State getState();

/**
* @return the current state, either Uninitialized, Stopped, Playing, Paused
* Return the video stream id, see <a href="#streamIDs">audio and video Stream IDs</a>.
*/
public State getState();
public int getVID();

/**
* @return current streaming position in milliseconds
**/
public int getCurrentPosition();

* Return the audio stream id, see <a href="#streamIDs">audio and video Stream IDs</a>.
*/
public int getAID();

/**
* @return the current decoded frame count since {@link #initGLStream(GL, int, URLConnection, int, int)}.
*/
public int getDecodedFrameCount();

/**
* @return the current presented frame count since {@link #initGLStream(GL, int, URLConnection, int, int)},
* increased by {@link #getNextTexture(GL, boolean)}.
*/
public int getPresentedFrameCount();

/**
* @return current video PTS in milliseconds of {@link #getLastTexture()}
* @return current video presentation timestamp (PTS) in milliseconds of {@link #getLastTexture()}
**/
public int getVideoPTS();

/**
* @return current audio PTS in milliseconds.
* @return current audio presentation timestamp (PTS) in milliseconds.
**/
public int getAudioPTS();

/**
* Allowed in state Stopped, Playing and Paused, otherwise ignored.
*
* @param msec absolute desired time position in milliseconds
* @return time current position in milliseconds, after seeking to the desired position
**/
public int seek(int msec);

/**
* {@inheritDoc}
*/
Expand Down Expand Up @@ -225,7 +285,13 @@ public enum State {
* <i>Warning:</i> Optional information, may not be supported by implementation.
* @return the total number of video frames
*/
public long getTotalFrames();
public int getVideoFrames();

/**
* <i>Warning:</i> Optional information, may not be supported by implementation.
* @return the total number of audio frames
*/
public int getAudioFrames();

/**
* @return total duration of stream in msec.
Expand Down Expand Up @@ -262,6 +328,8 @@ public enum State {

public String toString();

public String getPerfString();

public void addEventListener(GLMediaEventListener l);

public void removeEventListener(GLMediaEventListener l);
Expand Down
Expand Up @@ -47,10 +47,13 @@ public static GLMediaPlayer createDefault() {
sink = create(cl, FFMPEGMediaPlayerClazzName);
}
if( null == sink ) {
sink = new NullGLMediaPlayer();
sink = createNull();
}
return sink;
}
public static GLMediaPlayer createNull() {
return new NullGLMediaPlayer();
}

public static GLMediaPlayer create(final ClassLoader cl, String implName) {
try {
Expand Down
Expand Up @@ -110,9 +110,12 @@ 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;

public TextureFrame(Texture t) {
texture = t;
pts = 0;
pts = INVALID_PTS;
}

public final Texture getTexture() { return texture; }
Expand Down

0 comments on commit c37629e

Please sign in to comment.