aboutsummaryrefslogtreecommitdiffstats
path: root/src/jogl/classes
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2023-03-13 05:59:34 +0100
committerSven Gothel <[email protected]>2023-03-13 05:59:34 +0100
commit913b00f8b876e29af91677ef61b3eb35d6853e6e (patch)
tree17bc28ad255f1e086b2aa80028d4e6fb1ffd0456 /src/jogl/classes
parent5efd3a6d9cf12d38ce6d7c91f9c5968927f3253a (diff)
GLMediaPlayer: Overhaul and simplify states, allow usage before stream ready showing test-texture. Adding stop(); (API Change)
- allow multiple initGL(..) @ uninitialized and initialized - allows usage before stream is ready - using a test-texture @ uninitialized - adding stop() API change - initStream() -> playStream() - play() -> resume() FFMPEG: Added 'ready' check for robustness
Diffstat (limited to 'src/jogl/classes')
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java45
-rw-r--r--src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java10
-rw-r--r--src/jogl/classes/jogamp/opengl/openal/av/ALAudioSink.java1
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java206
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/NullGLMediaPlayer.java8
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java35
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/impl/OMXGLMediaPlayer.java10
7 files changed, 219 insertions, 96 deletions
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 c2de32372..5740dae4b 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java
@@ -37,6 +37,7 @@ import com.jogamp.common.net.Uri;
import com.jogamp.opengl.util.texture.Texture;
import com.jogamp.opengl.util.texture.TextureSequence;
import com.jogamp.opengl.util.TimeFrameI;
+import com.jogamp.opengl.util.av.GLMediaPlayer.State;
/**
* GLMediaPlayer interface specifies a {@link TextureSequence} state machine
@@ -45,7 +46,7 @@ import com.jogamp.opengl.util.TimeFrameI;
* Audio maybe supported and played back internally or via an {@link AudioSink} implementation.
* </p>
* <p>
- * Audio and video streams can be selected or muted via {@link #initStream(Uri, int, int, int)}
+ * Audio and video streams can be selected or muted via {@link #playStream(Uri, int, int, int)}
* using the appropriate <a href="#streamIDs">stream id</a>'s.
* </p>
* <p>
@@ -56,7 +57,7 @@ import com.jogamp.opengl.util.TimeFrameI;
* <p>
* Most of the stream processing is performed on the decoding thread, a.k.a. <i>StreamWorker</i>:
* <ul>
- * <li>Stream initialization triggered by {@link #initStream(Uri, int, int, int) initStream(..)} - User gets notified whether the stream has been initialized or not via {@link GLMediaEventListener#attributesChanged(GLMediaPlayer, int, long) attributesChanges(..)}.</li>
+ * <li>Stream initialization triggered by {@link #playStream(Uri, int, int, int) playStream(..)} - User gets notified whether the stream has been initialized or not via {@link GLMediaEventListener#attributesChanged(GLMediaPlayer, int, long) attributesChanges(..)}.</li>
* <li>Stream decoding - User gets notified of a new frame via {@link GLMediaEventListener#newFrameAvailable(GLMediaPlayer, com.jogamp.opengl.util.texture.TextureSequence.TextureFrame, long) newFrameAvailable(...)}.</li>
* <li>Caught <a href="#streamerror">exceptions on the decoding thread</a> are delivered as {@link StreamException}s.</li>
* </ul>
@@ -82,16 +83,17 @@ import com.jogamp.opengl.util.TimeFrameI;
* <p>
* <table border="1">
* <tr><th>Action</th> <th>{@link State} Before</th> <th>{@link State} After</th> <th>{@link GLMediaEventListener Event}</th></tr>
- * <tr><td>{@link #initStream(Uri, int, int, int)}</td> <td>{@link State#Uninitialized Uninitialized}</td> <td>{@link State#Initialized Initialized}<sup><a href="#streamworker">1</a></sup>, {@link State#Uninitialized Uninitialized}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_INIT EVENT_CHANGE_INIT} or ( {@link GLMediaEventListener#EVENT_CHANGE_ERR EVENT_CHANGE_ERR} + {@link GLMediaEventListener#EVENT_CHANGE_UNINIT EVENT_CHANGE_UNINIT} )</td></tr>
- * <tr><td>{@link #initGL(GL)}</td> <td>{@link State#Initialized Initialized}</td> <td>{@link State#Paused Paused}, , {@link State#Uninitialized Uninitialized}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_PAUSE EVENT_CHANGE_PAUSE} or ( {@link GLMediaEventListener#EVENT_CHANGE_ERR EVENT_CHANGE_ERR} + {@link GLMediaEventListener#EVENT_CHANGE_UNINIT EVENT_CHANGE_UNINIT} )</td></tr>
- * <tr><td>{@link #play()}</td> <td>{@link State#Paused Paused}</td> <td>{@link State#Playing Playing}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_PLAY EVENT_CHANGE_PLAY}</td></tr>
+ * <tr><td>{@link #playStream(Uri, int, int, int)}</td> <td>{@link State#Uninitialized Uninitialized}</td> <td>{@link State#Initialized Initialized}<sup><a href="#streamworker">1</a></sup>, {@link State#Uninitialized Uninitialized}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_INIT EVENT_CHANGE_INIT} or ( {@link GLMediaEventListener#EVENT_CHANGE_ERR EVENT_CHANGE_ERR} + {@link GLMediaEventListener#EVENT_CHANGE_UNINIT EVENT_CHANGE_UNINIT} )</td></tr>
+ * <tr><td>{@link #initGL(GL)}</td> <td>{@link State#Initialized Initialized}, {@link State#Uninitialized Uninitialized} </td> <td>{@link State#Playing Playing}, {@link State#Uninitialized Uninitialized}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_PLAY EVENT_CHANGE_PLAY} or ( {@link GLMediaEventListener#EVENT_CHANGE_ERR EVENT_CHANGE_ERR} + {@link GLMediaEventListener#EVENT_CHANGE_UNINIT EVENT_CHANGE_UNINIT} )</td></tr>
* <tr><td>{@link #pause(boolean)}</td> <td>{@link State#Playing Playing}</td> <td>{@link State#Paused Paused}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_PAUSE EVENT_CHANGE_PAUSE}</td></tr>
+ * <tr><td>{@link #resume()}</td> <td>{@link State#Paused Paused}</td> <td>{@link State#Playing Playing}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_PLAY EVENT_CHANGE_PLAY}</td></tr>
+ * <tr><td>{@link #stop()}</td> <td>{@link State#Playing Playing}, {@link State#Paused Paused}</td> <td>{@link State#Uninitialized Uninitialized}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_PAUSE EVENT_CHANGE_PAUSE}</td></tr>
* <tr><td>{@link #seek(int)}</td> <td>{@link State#Paused Paused}, {@link State#Playing Playing}</td> <td>{@link State#Paused Paused}, {@link State#Playing Playing}</td> <td>none</td></tr>
- * <tr><td>{@link #getNextTexture(GL)}</td> <td>{@link State#Paused Paused}, {@link State#Playing Playing}</td> <td>{@link State#Paused Paused}, {@link State#Playing Playing}</td> <td>none</td></tr>
- * <tr><td>{@link #getLastTexture()}</td> <td>{@link State#Paused Paused}, {@link State#Playing Playing}</td> <td>{@link State#Paused Paused}, {@link State#Playing Playing}</td> <td>none</td></tr>
+ * <tr><td>{@link #getNextTexture(GL)}</td> <td><i>any</i></td> <td><i>same</i></td> <td>none</td></tr>
+ * <tr><td>{@link #getLastTexture()}</td> <td><i>any</i></td> <td><i>same</i></td> <td>none</td></tr>
* <tr><td>{@link TextureFrame#END_OF_STREAM_PTS END_OF_STREAM}</td> <td>{@link State#Playing Playing}</td> <td>{@link State#Paused Paused}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_EOS EVENT_CHANGE_EOS} + {@link GLMediaEventListener#EVENT_CHANGE_PAUSE EVENT_CHANGE_PAUSE}</td></tr>
- * <tr><td>{@link StreamException}</td> <td>ANY</td> <td>{@link State#Paused Paused}, {@link State#Uninitialized Uninitialized}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_ERR EVENT_CHANGE_ERR} + ( {@link GLMediaEventListener#EVENT_CHANGE_PAUSE EVENT_CHANGE_PAUSE} or {@link GLMediaEventListener#EVENT_CHANGE_UNINIT EVENT_CHANGE_UNINIT} )</td></tr>
- * <tr><td>{@link #destroy(GL)}</td> <td>ANY</td> <td>{@link State#Uninitialized Uninitialized}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_UNINIT EVENT_CHANGE_UNINIT}</td></tr>
+ * <tr><td>{@link StreamException}</td> <td><i>any</i></td> <td>{@link State#Paused Paused}, {@link State#Uninitialized Uninitialized}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_ERR EVENT_CHANGE_ERR} + ( {@link GLMediaEventListener#EVENT_CHANGE_PAUSE EVENT_CHANGE_PAUSE} or {@link GLMediaEventListener#EVENT_CHANGE_UNINIT EVENT_CHANGE_UNINIT} )</td></tr>
+ * <tr><td>{@link #destroy(GL)}</td> <td><i>any</i></td> <td>{@link State#Uninitialized Uninitialized}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_UNINIT EVENT_CHANGE_UNINIT}</td></tr>
* </table>
* </p>
*
@@ -188,6 +190,7 @@ import com.jogamp.opengl.util.TimeFrameI;
*/
public interface GLMediaPlayer extends TextureSequence {
public static final boolean DEBUG = Debug.debug("GLMediaPlayer");
+ public static final boolean DEBUG_AVSYNC = Debug.debug("GLMediaPlayer.AVSync");
public static final boolean DEBUG_NATIVE = Debug.debug("GLMediaPlayer.Native");
/** Default texture count, value {@value}. */
@@ -372,7 +375,7 @@ public interface GLMediaPlayer extends TextureSequence {
* @throws IllegalArgumentException if arguments are invalid
* @since 2.3.0
*/
- public void initStream(Uri streamLoc, int vid, int aid, int textureCount) throws IllegalStateException, IllegalArgumentException;
+ public void playStream(Uri streamLoc, int vid, int aid, int textureCount) throws IllegalStateException, IllegalArgumentException;
/**
* Returns the {@link StreamException} caught in the decoder thread, or <code>null</code> if none occured.
@@ -389,7 +392,7 @@ public interface GLMediaPlayer extends TextureSequence {
* <p>
* <a href="#lifecycle">Lifecycle</a>: {@link State#Initialized} -> {@link State#Paused} or {@link State#Initialized}
* </p>
- * Argument <code>gl</code> is ignored if video is muted, see {@link #initStream(Uri, int, int, int)}.
+ * Argument <code>gl</code> is ignored if video is muted, see {@link #playStream(Uri, int, int, int)}.
*
* @param gl current GL object. Maybe <code>null</code>, for audio only.
* @throws IllegalStateException if not invoked in {@link State#Initialized}.
@@ -401,7 +404,7 @@ public interface GLMediaPlayer extends TextureSequence {
/**
* If implementation uses a {@link AudioSink}, it's instance will be returned.
* <p>
- * The {@link AudioSink} instance is available after {@link #initStream(Uri, int, int, int)},
+ * The {@link AudioSink} instance is available after {@link #playStream(Uri, int, int, int)},
* if used by implementation.
* </p>
*/
@@ -416,6 +419,14 @@ public interface GLMediaPlayer extends TextureSequence {
public State destroy(GL gl);
/**
+ * Stops streaming and releases the GL, stream and other resources, but keeps {@link #attachObject(String, Object) attached user objects}.
+ * <p>
+ * <a href="#lifecycle">Lifecycle</a>: <code>ANY</code> -> {@link State#Uninitialized}
+ * </p>
+ */
+ public State stop();
+
+ /**
* Sets the playback speed.
* <p>
* To simplify test, play speed is <i>normalized</i>, i.e.
@@ -452,7 +463,7 @@ public interface GLMediaPlayer extends TextureSequence {
* <a href="#lifecycle">Lifecycle</a>: {@link State#Paused} -> {@link State#Playing}
* </p>
*/
- public State play();
+ public State resume();
/**
* Pauses the <i>StreamWorker</i> decoding thread.
@@ -460,7 +471,7 @@ public interface GLMediaPlayer extends TextureSequence {
* <a href="#lifecycle">Lifecycle</a>: {@link State#Playing} -> {@link State#Paused}
* </p>
* <p>
- * If a <i>new</i> frame is desired after the next {@link #play()} call,
+ * If a <i>new</i> frame is desired after the next {@link #resume()} call,
* e.g. to make a snapshot of a camera input stream,
* <code>flush</code> shall be set to <code>true</code>.
* </p>
@@ -498,13 +509,13 @@ public interface GLMediaPlayer extends TextureSequence {
public int getAID();
/**
- * @return the current decoded frame count since {@link #play()} and {@link #seek(int)}
+ * @return the current decoded frame count since {@link #resume()} and {@link #seek(int)}
* as increased by {@link #getNextTexture(GL)} or the decoding thread.
*/
public int getDecodedFrameCount();
/**
- * @return the current presented frame count since {@link #play()} and {@link #seek(int)}
+ * @return the current presented frame count since {@link #resume()} and {@link #seek(int)}
* as increased by {@link #getNextTexture(GL)} for new frames.
*/
public int getPresentedFrameCount();
@@ -547,7 +558,7 @@ public interface GLMediaPlayer extends TextureSequence {
public TextureSequence.TextureFrame getNextTexture(GL gl) throws IllegalStateException;
/**
- * Return the stream location, as set by {@link #initStream(Uri, int, int, int)}.
+ * Return the stream location, as set by {@link #playStream(Uri, int, int, int)}.
* @since 2.3.0
*/
public Uri getUri();
diff --git a/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java b/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java
index 09d2dfda0..06eca7128 100644
--- a/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java
+++ b/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java
@@ -136,7 +136,7 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl {
}
@Override
- protected final boolean playImpl() {
+ protected final boolean resumeImpl() {
playStart = Platform.currentTimeMillis();
if(null != mp) {
try {
@@ -212,7 +212,7 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl {
protected final int getAudioPTSImpl() { return null != mp ? mp.getCurrentPosition() : 0; }
@Override
- protected final void destroyImpl(final GL gl) {
+ protected final void destroyImpl() {
if(null != mp) {
wakeUp(false);
try {
@@ -239,12 +239,18 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl {
}
}
+ @Override
+ protected void stopImpl() {
+ destroyImpl();
+ }
+
public static class SurfaceTextureFrame extends TextureSequence.TextureFrame {
public SurfaceTextureFrame(final Texture t, final SurfaceTexture stex) {
super(t);
this.surfaceTex = stex;
}
+ @Override
public String toString() {
return "SurfaceTextureFrame[pts " + pts + " ms, l " + duration + " ms, texID "+ texture.getTextureObject() + ", " + surfaceTex + "]";
}
diff --git a/src/jogl/classes/jogamp/opengl/openal/av/ALAudioSink.java b/src/jogl/classes/jogamp/opengl/openal/av/ALAudioSink.java
index bdd9b6c95..c52c59f17 100644
--- a/src/jogl/classes/jogamp/opengl/openal/av/ALAudioSink.java
+++ b/src/jogl/classes/jogamp/opengl/openal/av/ALAudioSink.java
@@ -89,6 +89,7 @@ public class ALAudioSink implements AudioSink {
/** Get this frame's OpenAL buffer name */
public final int getALBuffer() { return alBuffer; }
+ @Override
public String toString() {
return "ALAudioFrame[pts " + pts + " ms, l " + duration + " ms, " + byteSize + " bytes, buffer "+alBuffer+"]";
}
diff --git a/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java b/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java
index 01e04f6de..d29464552 100644
--- a/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java
+++ b/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java
@@ -28,6 +28,9 @@
package jogamp.opengl.util.av;
import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URLConnection;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -46,9 +49,11 @@ import com.jogamp.opengl.GLProfile;
import jogamp.opengl.Debug;
import com.jogamp.common.net.UriQueryProps;
+import com.jogamp.common.nio.Buffers;
import com.jogamp.common.ExceptionUtils;
import com.jogamp.common.net.Uri;
import com.jogamp.common.os.Platform;
+import com.jogamp.common.util.IOUtil;
import com.jogamp.common.util.InterruptSource;
import com.jogamp.common.util.InterruptedRuntimeException;
import com.jogamp.common.util.LFRingbuffer;
@@ -60,6 +65,8 @@ import com.jogamp.opengl.util.av.AudioSink;
import com.jogamp.opengl.util.av.GLMediaPlayer;
import com.jogamp.opengl.util.glsl.ShaderCode;
import com.jogamp.opengl.util.texture.Texture;
+import com.jogamp.opengl.util.texture.TextureData;
+import com.jogamp.opengl.util.texture.TextureIO;
import com.jogamp.opengl.util.texture.TextureSequence;
import com.jogamp.opengl.util.texture.TextureSequence.TextureFrame;
@@ -96,12 +103,12 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
private final int[] texWrapST = { GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE };
/** User requested URI stream location. */
- private Uri streamLoc = null;
+ private Uri streamLoc;
/**
* In case {@link #streamLoc} is a {@link GLMediaPlayer#CameraInputScheme},
* {@link #cameraPath} holds the URI's path portion
- * as parsed in {@link #initStream(Uri, int, int, int)}.
+ * as parsed in {@link #playStream(Uri, int, int, int)}.
* @see #cameraProps
*/
protected Uri.Encoded cameraPath = null;
@@ -112,9 +119,9 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
private float audioVolume = 1.0f;
/** Shall be set by the {@link #initStreamImpl(int, int)} method implementation. */
- private int vid = GLMediaPlayer.STREAM_ID_AUTO;
+ private int vid = GLMediaPlayer.STREAM_ID_NONE;
/** Shall be set by the {@link #initStreamImpl(int, int)} method implementation. */
- private int aid = GLMediaPlayer.STREAM_ID_AUTO;
+ private int aid = GLMediaPlayer.STREAM_ID_NONE;
/** Shall be set by the {@link #initStreamImpl(int, int)} method implementation. */
private int width = 0;
/** Shall be set by the {@link #initStreamImpl(int, int)} method implementation. */
@@ -202,6 +209,9 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
this.texUnit = 0;
this.textureFragmentShaderHashCode = 0;
this.state = State.Uninitialized;
+ try {
+ streamLoc = Uri.cast("https://no/stream/");
+ } catch (final URISyntaxException e) { }
}
@Override
@@ -237,15 +247,8 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
@Override
public final int[] getTextureWrapST() { return texWrapST; }
- private final void checkGLInit() {
- if(State.Uninitialized == state || State.Initialized == state ) {
- throw new IllegalStateException("GL not initialized: "+this);
- }
- }
-
@Override
- public String getRequiredExtensionsShaderStub() throws IllegalStateException {
- checkGLInit();
+ public String getRequiredExtensionsShaderStub() {
if(GLES2.GL_TEXTURE_EXTERNAL_OES == textureTarget) {
return ShaderCode.createExtensionDirective(GLExtensions.OES_EGL_image_external, ShaderCode.ENABLE);
}
@@ -253,8 +256,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
}
@Override
- public String getTextureSampler2DType() throws IllegalStateException {
- checkGLInit();
+ public String getTextureSampler2DType() {
switch(textureTarget) {
case GL.GL_TEXTURE_2D:
case GL2GL3.GL_TEXTURE_RECTANGLE:
@@ -273,8 +275,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
* if not overridden by specialization.
*/
@Override
- public String getTextureLookupFunctionName(final String desiredFuncName) throws IllegalStateException {
- checkGLInit();
+ public String getTextureLookupFunctionName(final String desiredFuncName) {
return "texture2D";
}
@@ -286,8 +287,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
* if not overridden by specialization.
*/
@Override
- public String getTextureLookupFragmentShaderImpl() throws IllegalStateException {
- checkGLInit();
+ public String getTextureLookupFragmentShaderImpl() {
return "";
}
@@ -335,12 +335,12 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
protected final void setState(final State s) { state=s; }
@Override
- public final State play() {
+ public final State resume() {
synchronized( stateLock ) {
final State preState = state;
switch( state ) {
case Paused:
- if( playImpl() ) {
+ if( resumeImpl() ) {
resetAVPTS();
if( null != audioSink ) {
audioSink.play(); // cont. w/ new data
@@ -356,7 +356,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
return state;
}
}
- protected abstract boolean playImpl();
+ protected abstract boolean resumeImpl();
@Override
public final State pause(final boolean flush) {
@@ -378,7 +378,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
}
attributesUpdated( event_mask );
if( !pauseImpl() ) {
- play();
+ resume();
}
}
if(DEBUG) { System.err.println("Pause: "+preState+" -> "+state+", "+toString()); }
@@ -388,6 +388,24 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
protected abstract boolean pauseImpl();
@Override
+ public final State stop() {
+ synchronized( stateLock ) {
+ final State preState = state;
+ if( null != streamWorker ) {
+ streamWorker.doStop();
+ streamWorker = null;
+ }
+ resetAVPTSAndFlush();
+ stopImpl();
+ changeState(0, State.Uninitialized);
+ // attachedObjects.clear();
+ if(DEBUG) { System.err.println("Stop: "+preState+" -> "+state+", "+toString()); }
+ return state;
+ }
+ }
+ protected abstract void stopImpl();
+
+ @Override
public final State destroy(final GL gl) {
return destroyImpl(gl, 0, true);
}
@@ -397,15 +415,17 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
streamWorker.doStopImpl(wait);
streamWorker = null;
}
- destroyImpl(gl);
+ resetAVPTSAndFlush();
+ destroyImpl();
removeAllTextureFrames(gl);
+ lastFrame = null;
textureCount=0;
changeState(event_mask, State.Uninitialized);
attachedObjects.clear();
return state;
}
}
- protected abstract void destroyImpl(GL gl);
+ protected abstract void destroyImpl();
@Override
public final int seek(int msec) {
@@ -534,13 +554,14 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
}
@Override
- public final void initStream(final Uri streamLoc, final int vid, final int aid, final int reqTextureCount) throws IllegalStateException, IllegalArgumentException {
+ public final void playStream(final Uri streamLoc, final int vid, final int aid, final int reqTextureCount) throws IllegalStateException, IllegalArgumentException {
synchronized( stateLock ) {
if(State.Uninitialized != state) {
throw new IllegalStateException("Instance not in state unintialized: "+this);
}
if(null == streamLoc) {
- throw new IllegalArgumentException("streamLock is null");
+ initTestStream();
+ return;
}
if( STREAM_ID_NONE != vid ) {
textureCount = validateTextureCount(reqTextureCount);
@@ -617,9 +638,9 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
@Override
public final void initGL(final GL gl) throws IllegalStateException, StreamException, GLException {
synchronized( stateLock ) {
- if(State.Initialized != state ) {
- throw new IllegalStateException("Stream not in state initialized: "+this);
- }
+ // if(State.Initialized != state && State.Uninitialized != state) {
+ // throw new IllegalStateException("Stream not in state [un]initialized: "+this);
+ // }
if( null != streamWorker ) {
final StreamException streamInitErr = getStreamException();
if( null != streamInitErr ) {
@@ -629,7 +650,8 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
}
}
try {
- if( STREAM_ID_NONE != vid ) {
+ if( STREAM_ID_NONE != vid && State.Uninitialized != state ) {
+ resetAVPTSAndFlush();
removeAllTextureFrames(gl);
initGLImpl(gl);
if(DEBUG) {
@@ -645,20 +667,40 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
videoFramesDecoded = new LFRingbuffer<TextureFrame>(TextureFrame[].class, textureCount);
lastFrame = videoFramesFree.getBlocking( );
}
- if( null != streamWorker ) {
- streamWorker.initGL(gl);
+ if( null == streamWorker &&
+ ( TEXTURE_COUNT_MIN < textureCount || STREAM_ID_NONE == vid ) ) // Enable StreamWorker for 'audio only' as well (Bug 918).
+ {
+ streamWorker = new StreamWorker();
}
+ streamWorker.initGL(gl);
+ streamWorker.doResume();
+ changeState(0, State.Paused);
+ resume();
} else {
- removeAllTextureFrames(null);
- initGLImpl(null);
- setTextureFormat(-1, -1);
- setTextureType(-1);
- videoFramesOrig = null;
+ resetAVPTSAndFlush();
+ removeAllTextureFrames(gl);
+ // initGLImpl(gl);
+ // Using a dummy test frame
+ width = TestTexture.singleton.getWidth();
+ height = TestTexture.singleton.getHeight();
+ setTextureFormat(GL.GL_RGBA, GL.GL_RGBA);
+ setTextureType(GL.GL_UNSIGNED_BYTE);
+ textureCount = Math.max(TEXTURE_COUNT_MIN, textureCount);
+ videoFramesOrig = createTestTexFrames(gl, textureCount);
+ if( TEXTURE_COUNT_MIN == textureCount ) {
+ videoFramesFree = null;
+ videoFramesDecoded = null;
+ lastFrame = videoFramesOrig[0];
+ } else {
+ videoFramesFree = new LFRingbuffer<TextureFrame>(videoFramesOrig);
+ videoFramesDecoded = new LFRingbuffer<TextureFrame>(TextureFrame[].class, textureCount);
+ lastFrame = videoFramesFree.getBlocking( );
+ }
videoFramesFree = null;
videoFramesDecoded = null;
- lastFrame = null;
+ lastFrame = videoFramesOrig[0];
+ // changeState(0, State.Paused);
}
- changeState(0, State.Paused);
} catch (final Throwable t) {
destroyImpl(gl, GLMediaEventListener.EVENT_CHANGE_ERR, false /* wait */); // -> GLMediaPlayer.State.Uninitialized
throw new GLException("Error initializing GL resources", t);
@@ -703,6 +745,64 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
}
return texFrames;
}
+ protected TextureFrame[] createTestTexFrames(final GL gl, final int count) {
+ final int[] texNames = new int[count];
+ gl.glGenTextures(count, texNames, 0);
+ final int err = gl.glGetError();
+ if( GL.GL_NO_ERROR != err ) {
+ throw new RuntimeException("TextureNames creation failed (num: "+count+"): err "+toHexString(err));
+ }
+ final TextureFrame[] texFrames = new TextureFrame[count];
+ for(int i=0; i<count; i++) {
+ texFrames[i] = createTestTexImage(gl, texNames[i]);
+ }
+ return texFrames;
+ }
+
+ private static class TestTexture {
+ private static final TextureData singleton;
+ static {
+ TextureData data = null;
+ try {
+ final URLConnection urlConn = IOUtil.getResource("jogamp/opengl/assets/test-ntsc01-28x16.png", NullGLMediaPlayer.class.getClassLoader());
+ if(null != urlConn) {
+ data = TextureIO.newTextureData(GLProfile.getGL2ES2(), urlConn.getInputStream(), false, TextureIO.PNG);
+ }
+ } catch (final Exception e) {
+ e.printStackTrace();
+ }
+ if(null == data) {
+ final int w = 28;
+ final int h = 16;
+ final ByteBuffer buffer = Buffers.newDirectByteBuffer(w*h*4);
+ while(buffer.hasRemaining()) {
+ buffer.put((byte) 0xEA); buffer.put((byte) 0xEA); buffer.put((byte) 0xEA); buffer.put((byte) 0xEA);
+ }
+ buffer.rewind();
+ data = new TextureData(GLProfile.getGL2ES2(),
+ GL.GL_RGBA, w, h, 0,
+ GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, false,
+ false, false, buffer, null);
+ }
+ singleton = data;
+ }
+ }
+ private final TextureSequence.TextureFrame createTestTexImage(final GL gl, final int texName) {
+ final Texture texture = createTexImageImpl(gl, texName, TestTexture.singleton.getWidth(), TestTexture.singleton.getHeight());
+ texture.updateImage(gl, TestTexture.singleton);
+ return new TextureSequence.TextureFrame( texture );
+ }
+ private void initTestStream() {
+ textureCount = TEXTURE_COUNT_MIN;
+ final float _fps = 24f;
+ final int _duration = 10*60*1000; // msec
+ final int _totalFrames = (int) ( (_duration/1000)*_fps );
+ updateAttributes(GLMediaPlayer.STREAM_ID_NONE, GLMediaPlayer.STREAM_ID_NONE,
+ TestTexture.singleton.getWidth(), TestTexture.singleton.getHeight(), 0,
+ 0, 0, _fps,
+ _totalFrames, 0, _duration, "png-static", null);
+ }
+
protected abstract TextureFrame createTexImage(GL gl, int texName);
protected final Texture createTexImageImpl(final GL gl, final int texName, final int tWidth, final int tHeight) {
@@ -763,9 +863,6 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
@Override
public final TextureFrame getLastTexture() throws IllegalStateException {
- if( State.Paused != state && State.Playing != state ) {
- throw new IllegalStateException("Instance not paused or playing: "+this);
- }
return lastFrame;
}
@@ -774,7 +871,6 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
videoFramesOrig = null;
videoFramesFree = null;
videoFramesDecoded = null;
- lastFrame = null;
if( null != texFrames ) {
for(int i=0; i<texFrames.length; i++) {
final TextureFrame frame = texFrames[i];
@@ -799,9 +895,6 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
@Override
public final TextureFrame getNextTexture(final GL gl) throws IllegalStateException {
synchronized( stateLock ) {
- if( State.Paused != state && State.Playing != state ) {
- throw new IllegalStateException("Instance not paused or playing: "+this);
- }
if(State.Playing == state) {
boolean dropFrame = false;
try {
@@ -857,7 +950,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
if( null == videoFramesDecoded || !videoFramesDecoded.isEmpty() ) {
nullFrameCount++;
}
- if( DEBUG ) {
+ if( DEBUG_AVSYNC ) {
final int audio_pts = getAudioPTSImpl();
final int audio_scr = (int) ( ( currentTimeMillis - audio_scr_t0 ) * playSpeed );
final int d_apts;
@@ -897,7 +990,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
// final int d_avpts = d_vpts - d_apts;
if( -VIDEO_DPTS_MAX > d_vpts || d_vpts > VIDEO_DPTS_MAX ) {
// if( -VIDEO_DPTS_MAX > d_avpts || d_avpts > VIDEO_DPTS_MAX ) {
- if( DEBUG ) {
+ if( DEBUG_AVSYNC ) {
System.err.println( "AV*: dT "+(currentTimeMillis-lastTimeMillis)+", "+
getPerfStringImpl( video_scr, video_pts, d_vpts, audio_scr, audio_pts, d_apts, 0 ) + ", "+nextFrame+", playCached " + playCached+ ", dropFrame "+dropFrame);
}
@@ -922,7 +1015,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
dropFrame = true;
}
video_pts_last = video_pts;
- if( DEBUG ) {
+ if( DEBUG_AVSYNC ) {
System.err.println( "AV_: dT "+(currentTimeMillis-lastTimeMillis)+", "+
getPerfStringImpl( video_scr, video_pts, d_vpts,
audio_scr, audio_pts, d_apts,
@@ -1002,7 +1095,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
* {@inheritDoc}
* <p>
* Note: All {@link AudioSink} operations are performed from {@link GLMediaPlayerImpl},
- * i.e. {@link #play()}, {@link #pause(boolean)}, {@link #seek(int)}, {@link #setPlaySpeed(float)}, {@link #getAudioPTS()}.
+ * i.e. {@link #resume()}, {@link #pause(boolean)}, {@link #seek(int)}, {@link #setPlaySpeed(float)}, {@link #getAudioPTS()}.
* </p>
* <p>
* Implementations using an {@link AudioSink} shall write it's instance to {@link #audioSink}
@@ -1251,7 +1344,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
preNextTextureImpl(sharedGLCtx.getGL());
sharedGLCtxCurrent = true;
}
- if( null == videoFramesFree ) {
+ if( null == videoFramesFree && STREAM_ID_NONE != vid ) {
throw new InternalError("XXX videoFramesFree is null");
}
}
@@ -1393,7 +1486,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
/**
* Called initially by {@link #initStreamImpl(int, int)}, which
- * is called off-thread by {@link #initStream(Uri, int, int, int)}.
+ * is called off-thread by {@link #playStream(Uri, int, int, int)}.
* <p>
* The latter catches an occurring exception and set the state delivers the error events.
* </p>
@@ -1470,11 +1563,10 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
return;
}
if( wasUninitialized ) {
- if( null != streamWorker ) {
- throw new InternalError("XXX: StreamWorker not null - "+this);
- }
- if( TEXTURE_COUNT_MIN < textureCount || STREAM_ID_NONE == vid ) { // Enable StreamWorker for 'audio only' as well (Bug 918).
- streamWorker = new StreamWorker();
+ if( null == streamWorker ) {
+ if( TEXTURE_COUNT_MIN < textureCount || STREAM_ID_NONE == vid ) { // Enable StreamWorker for 'audio only' as well (Bug 918).
+ streamWorker = new StreamWorker();
+ }
}
if( DEBUG ) {
System.err.println("XXX Initialize @ updateAttributes: "+this);
diff --git a/src/jogl/classes/jogamp/opengl/util/av/NullGLMediaPlayer.java b/src/jogl/classes/jogamp/opengl/util/av/NullGLMediaPlayer.java
index 59dba68f2..528b62db6 100644
--- a/src/jogl/classes/jogamp/opengl/util/av/NullGLMediaPlayer.java
+++ b/src/jogl/classes/jogamp/opengl/util/av/NullGLMediaPlayer.java
@@ -66,7 +66,7 @@ public class NullGLMediaPlayer extends GLMediaPlayerImpl {
}
@Override
- protected final boolean playImpl() {
+ protected final boolean resumeImpl() {
pos_start = Platform.currentTimeMillis();
return true;
}
@@ -77,6 +77,10 @@ public class NullGLMediaPlayer extends GLMediaPlayerImpl {
}
@Override
+ protected final void stopImpl() {
+ }
+
+ @Override
protected final int seekImpl(final int msec) {
pos_ms = msec;
validatePos();
@@ -98,7 +102,7 @@ public class NullGLMediaPlayer extends GLMediaPlayerImpl {
}
@Override
- protected final void destroyImpl(final GL gl) {
+ protected final void destroyImpl() {
if(null != texData) {
texData.destroy();
texData = null;
diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java
index aae1d75d0..91bef005a 100644
--- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java
+++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java
@@ -297,16 +297,19 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
if(!available) {
throw new RuntimeException("FFMPEGMediaPlayer not available");
}
+ psm = new GLPixelStorageModes();
+ initSelf();
+ }
+ private void initSelf() {
moviePtr = natives.createInstance0(this, DEBUG_NATIVE);
if(0==moviePtr) {
throw new GLException("Couldn't create FFMPEGInstance");
}
- psm = new GLPixelStorageModes();
audioSink = null;
}
@Override
- protected final void destroyImpl(final GL gl) {
+ protected final void destroyImpl() {
if (moviePtr != 0) {
natives.destroyInstance0(moviePtr);
moviePtr = 0;
@@ -321,6 +324,12 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
}
}
+ @Override
+ protected void stopImpl() {
+ destroyImpl();
+ initSelf();
+ }
+
public static final String dev_video_linux = "/dev/video";
@Override
@@ -394,9 +403,6 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
if(0==moviePtr) {
throw new GLException("FFMPEG native instance null");
}
- if(null == audioSink) {
- throw new GLException("AudioSink null");
- }
final int audioQueueLimit;
if( null != gl && STREAM_ID_NONE != getVID() ) {
final GLContextImpl ctx = (GLContextImpl)gl.getContext();
@@ -419,8 +425,10 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
System.err.println("initGL: p3 avChosen "+avChosenAudioFormat);
}
- if( STREAM_ID_NONE == getAID() ) {
- audioSink.destroy();
+ if( STREAM_ID_NONE == getAID() || null == audioSink ) {
+ if(null != audioSink) {
+ audioSink.destroy();
+ }
audioSink = AudioSinkFactory.createNull();
audioSink.init(AudioSink.DefaultFormat, 0, AudioSink.DefaultInitialQueueSize, AudioSink.DefaultQueueGrowAmount, audioQueueLimit);
} else {
@@ -475,6 +483,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
}
setTextureFormat(tif, tf);
setTextureType(tt);
+ setIsGLOriented(false);
if(DEBUG) {
System.err.println("initGL: p5: video "+vPixelFmt+", planes "+vPlanes+", bpp "+vBitsPerPixel+"/"+vBytesPerPixelPerPlane+
", tex "+texWidth+"x"+texHeight+", usesTexLookupShader "+usesTexLookupShader);
@@ -694,10 +703,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
* Otherwise the call is delegated to it's super class.
*/
@Override
- public final String getTextureLookupFunctionName(final String desiredFuncName) throws IllegalStateException {
- if( State.Uninitialized == getState() ) {
- throw new IllegalStateException("Instance not initialized: "+this);
- }
+ public final String getTextureLookupFunctionName(final String desiredFuncName) {
if( usesTexLookupShader ) {
if(null != desiredFuncName && desiredFuncName.length()>0) {
texLookupFuncName = desiredFuncName;
@@ -714,10 +720,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
* e.g. YUV420P to RGB. Otherwise the call is delegated to it's super class.
*/
@Override
- public final String getTextureLookupFragmentShaderImpl() throws IllegalStateException {
- if( State.Uninitialized == getState() ) {
- throw new IllegalStateException("Instance not initialized: "+this);
- }
+ public final String getTextureLookupFragmentShaderImpl() {
if( !usesTexLookupShader ) {
return super.getTextureLookupFragmentShaderImpl();
}
@@ -825,7 +828,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
}
@Override
- public final boolean playImpl() {
+ public final boolean resumeImpl() {
if(0==moviePtr) {
return false;
}
diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/OMXGLMediaPlayer.java b/src/jogl/classes/jogamp/opengl/util/av/impl/OMXGLMediaPlayer.java
index c69108b04..336084734 100644
--- a/src/jogl/classes/jogamp/opengl/util/av/impl/OMXGLMediaPlayer.java
+++ b/src/jogl/classes/jogamp/opengl/util/av/impl/OMXGLMediaPlayer.java
@@ -86,7 +86,7 @@ public class OMXGLMediaPlayer extends EGLMediaPlayerImpl {
}
@Override
- protected void destroyImpl(final GL gl) {
+ protected void destroyImpl() {
if (moviePtr != 0) {
_stop(moviePtr);
_detachVideoRenderer(moviePtr);
@@ -137,7 +137,7 @@ public class OMXGLMediaPlayer extends EGLMediaPlayerImpl {
}
@Override
- public synchronized boolean playImpl() {
+ public synchronized boolean resumeImpl() {
if(0==moviePtr) {
return false;
}
@@ -155,6 +155,12 @@ public class OMXGLMediaPlayer extends EGLMediaPlayerImpl {
return true;
}
+ @Override
+ protected final void stopImpl() {
+ destroyImpl();
+ initOMX();
+ }
+
/** @return time position after issuing the command */
@Override
protected int seekImpl(final int msec) {