aboutsummaryrefslogtreecommitdiffstats
path: root/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2013-08-10 09:14:19 +0200
committerSven Gothel <[email protected]>2013-08-10 09:14:19 +0200
commit6332e13b2f0aa9818d37802302f04c90a4fa4239 (patch)
treeb615630b4a886270721f82636a323ec36dac341c /src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java
parent590d78dc2ff24ce80976a30e35a99c06ef6750b0 (diff)
GLMediaPlayer: Add multithreaded decoding w/ textureCount > 2 where available EGL/FFMPeg. WIP!
Off-thread decoding: If validated (impl) textureCount > 2, decoding happens on extra thread. If decoding requires GL context, a shared context is created for decoding thread. API Changes: - initGLStream(..): Adds 'textureCount' as argument. - TextureSequence.TexSeqEventListener.newFrameAvailable(..) exposes the new frame available - TextureSequence.TextureFrame exposes the PTS (video) Implementation: - 'int validateTextureCount(int)': implementation decides whether textureCount can be > 2, i.e. off-thread decoding allowed, default is NO w/ textureCount==2! - 'boolean requiresOffthreadGLCtx()': implementation decides whether shared context is required for off-thread decoding - 'syncFrame2Audio(TextureFrame frame)': implementation shall handle a/v sync, due to audio stream details (pts, buffered frames) - FFMPEGMediaPlayer extends GLMediaPlayerImpl, no more EGLMediaPlayerImpl (redundant) +++ - SyncedRingbuffer: Expose T[] array +++ TODO: - syncAV! - test Android
Diffstat (limited to 'src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java')
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java501
1 files changed, 385 insertions, 116 deletions
diff --git a/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java b/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java
index 2ff91a3f6..bc297dc21 100644
--- a/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java
+++ b/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java
@@ -30,13 +30,17 @@ package jogamp.opengl.util.av;
import java.io.IOException;
import java.net.URLConnection;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.Iterator;
+import javax.media.nativewindow.AbstractGraphicsDevice;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
+import javax.media.opengl.GLContext;
+import javax.media.opengl.GLDrawable;
+import javax.media.opengl.GLDrawableFactory;
import javax.media.opengl.GLES2;
import javax.media.opengl.GLException;
+import javax.media.opengl.GLProfile;
import com.jogamp.opengl.util.av.GLMediaPlayer;
import com.jogamp.opengl.util.texture.Texture;
@@ -45,7 +49,7 @@ import com.jogamp.opengl.util.texture.TextureSequence;
/**
* After object creation an implementation may customize the behavior:
* <ul>
- * <li>{@link #setTextureCount(int)}</li>
+ * <li>{@link #setDesTextureCount(int)}</li>
* <li>{@link #setTextureTarget(int)}</li>
* <li>{@link EGLMediaPlayerImpl#setEGLTexImageAttribs(boolean, boolean)}.</li>
* </ul>
@@ -59,6 +63,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
protected static final String unknown = "unknown";
protected State state;
+
protected int textureCount;
protected int textureTarget;
protected int textureFormat;
@@ -74,35 +79,38 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
protected volatile float playSpeed = 1.0f;
- /** Shall be set by the {@link #initGLStreamImpl(GL, int[])} method implementation. */
+ /** Shall be set by the {@link #initGLStreamImpl(GL)} method implementation. */
protected int width = 0;
- /** Shall be set by the {@link #initGLStreamImpl(GL, int[])} method implementation. */
+ /** Shall be set by the {@link #initGLStreamImpl(GL)} method implementation. */
protected int height = 0;
- /** Video fps. Shall be set by the {@link #initGLStreamImpl(GL, int[])} method implementation. */
+ /** Video fps. Shall be set by the {@link #initGLStreamImpl(GL)} method implementation. */
protected float fps = 0;
- /** Stream bps. Shall be set by the {@link #initGLStreamImpl(GL, int[])} method implementation. */
+ /** Stream bps. Shall be set by the {@link #initGLStreamImpl(GL)} method implementation. */
protected int bps_stream = 0;
- /** Video bps. Shall be set by the {@link #initGLStreamImpl(GL, int[])} method implementation. */
+ /** Video bps. Shall be set by the {@link #initGLStreamImpl(GL)} method implementation. */
protected int bps_video = 0;
- /** Audio bps. Shall be set by the {@link #initGLStreamImpl(GL, int[])} method implementation. */
+ /** Audio bps. Shall be set by the {@link #initGLStreamImpl(GL)} method implementation. */
protected int bps_audio = 0;
- /** In frames. Shall be set by the {@link #initGLStreamImpl(GL, int[])} method implementation. */
+ /** In frames. Shall be set by the {@link #initGLStreamImpl(GL)} method implementation. */
protected int totalFrames = 0;
- /** In ms. Shall be set by the {@link #initGLStreamImpl(GL, int[])} method implementation. */
+ /** In ms. Shall be set by the {@link #initGLStreamImpl(GL)} method implementation. */
protected int duration = 0;
- /** Shall be set by the {@link #initGLStreamImpl(GL, int[])} method implementation. */
+ /** Shall be set by the {@link #initGLStreamImpl(GL)} method implementation. */
protected String acodec = unknown;
- /** Shall be set by the {@link #initGLStreamImpl(GL, int[])} method implementation. */
+ /** Shall be set by the {@link #initGLStreamImpl(GL)} method implementation. */
protected String vcodec = unknown;
protected int frameNumber = 0;
+ protected int currentVideoPTS = 0;
- protected TextureSequence.TextureFrame[] texFrames = null;
- protected HashMap<Integer, TextureSequence.TextureFrame> texFrameMap = new HashMap<Integer, TextureSequence.TextureFrame>();
+ protected SyncedRingbuffer<TextureFrame> videoFramesFree = null;
+ protected SyncedRingbuffer<TextureFrame> videoFramesDecoded = null;
+ protected volatile TextureFrame lastFrame = null;
+
private ArrayList<GLMediaEventListener> eventListeners = new ArrayList<GLMediaEventListener>();
protected GLMediaPlayerImpl() {
- this.textureCount=3;
+ this.textureCount=0;
this.textureTarget=GL.GL_TEXTURE_2D;
this.textureFormat = GL.GL_RGBA;
this.textureInternalFormat = GL.GL_RGBA;
@@ -112,14 +120,14 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
}
@Override
- public void setTextureUnit(int u) { texUnit = u; }
+ public final void setTextureUnit(int u) { texUnit = u; }
@Override
- public int getTextureUnit() { return texUnit; }
+ public final int getTextureUnit() { return texUnit; }
+
+ @Override
+ public final int getTextureTarget() { return textureTarget; }
- protected final void setTextureCount(int textureCount) {
- this.textureCount=textureCount;
- }
@Override
public final int getTextureCount() { return textureCount; }
@@ -134,29 +142,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
public final int[] getTextureMinMagFilter() { return texMinMagFilter; }
public final void setTextureWrapST(int[] wrapST) { texWrapST[0] = wrapST[0]; texWrapST[1] = wrapST[1];}
- public final int[] getTextureWrapST() { return texWrapST; }
-
- @Override
- public final TextureSequence.TextureFrame getLastTexture() throws IllegalStateException {
- if(State.Uninitialized == state) {
- throw new IllegalStateException("Instance not initialized: "+this);
- }
- return getLastTextureImpl();
- }
- protected abstract TextureSequence.TextureFrame getLastTextureImpl();
-
- @Override
- public final synchronized TextureSequence.TextureFrame getNextTexture(GL gl, boolean blocking) throws IllegalStateException {
- if(State.Uninitialized == state) {
- throw new IllegalStateException("Instance not initialized: "+this);
- }
- if(State.Playing == state) {
- final TextureSequence.TextureFrame f = getNextTextureImpl(gl, blocking);
- return f;
- }
- return getLastTextureImpl();
- }
- protected abstract TextureSequence.TextureFrame getNextTextureImpl(GL gl, boolean blocking);
+ public final int[] getTextureWrapST() { return texWrapST; }
@Override
public String getRequiredExtensionsShaderStub() throws IllegalStateException {
@@ -229,12 +215,15 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
protected abstract boolean setPlaySpeedImpl(float rate);
public final State start() {
- switch(state) {
+ switch( state ) {
case Stopped:
+ /** fall-through intended */
case Paused:
- if(startImpl()) {
+ if( startImpl() ) {
+ resumeFramePusher();
state = State.Playing;
}
+ default:
}
if(DEBUG) { System.err.println("Start: "+toString()); }
return state;
@@ -242,7 +231,8 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
protected abstract boolean startImpl();
public final State pause() {
- if(State.Playing == state && pauseImpl()) {
+ if( State.Playing == state && pauseImpl() ) {
+ pauseFramePusher();
state = State.Paused;
}
if(DEBUG) { System.err.println("Pause: "+toString()); }
@@ -251,12 +241,15 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
protected abstract boolean pauseImpl();
public final State stop() {
- switch(state) {
+ switch( state ) {
case Playing:
+ /** fall-through intended */
case Paused:
- if(stopImpl()) {
+ if( stopImpl() ) {
+ pauseFramePusher();
state = State.Stopped;
}
+ default:
}
if(DEBUG) { System.err.println("Stop: "+toString()); }
return state;
@@ -265,61 +258,70 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
@Override
public final int getCurrentPosition() {
- if(State.Uninitialized != state) {
+ if( State.Uninitialized != state ) {
return getCurrentPositionImpl();
}
return 0;
}
protected abstract int getCurrentPositionImpl();
+ @Override
+ public final int getVideoPTS() { return currentVideoPTS; }
+
+ @Override
+ public final int getAudioPTS() {
+ if( State.Uninitialized != state ) {
+ return getAudioPTSImpl();
+ }
+ return 0;
+ }
+ protected abstract int getAudioPTSImpl();
+
public final int seek(int msec) {
- final int cp;
+ final int pts1;
switch(state) {
case Stopped:
case Playing:
case Paused:
- cp = seekImpl(msec);
+ pauseFramePusher();
+ pts1 = seekImpl(msec);
+ currentVideoPTS=pts1;
+ resumeFramePusher();
break;
default:
- cp = 0;
+ pts1 = 0;
}
if(DEBUG) { System.err.println("Seek("+msec+"): "+toString()); }
- return cp;
+ return pts1;
}
protected abstract int seekImpl(int msec);
public final State getState() { return state; }
@Override
- public final State initGLStream(GL gl, URLConnection urlConn) throws IllegalStateException, GLException, IOException {
+ public final State initGLStream(GL gl, int reqTextureCount, URLConnection urlConn) throws IllegalStateException, GLException, IOException {
if(State.Uninitialized != state) {
throw new IllegalStateException("Instance not in state "+State.Uninitialized+", but "+state+", "+this);
}
this.urlConn = urlConn;
if (this.urlConn != null) {
try {
- if(null != gl) {
- if(null!=texFrames) {
- // re-init ..
- removeAllImageTextures(gl);
- } else {
- texFrames = new TextureSequence.TextureFrame[textureCount];
- }
- final int[] tex = new int[textureCount];
- {
- gl.glGenTextures(textureCount, tex, 0);
- final int err = gl.glGetError();
- if( GL.GL_NO_ERROR != err ) {
- throw new RuntimeException("TextureNames creation failed (num: "+textureCount+"): err "+toHexString(err));
- }
+ if( null != gl ) {
+ removeAllTextureFrames(gl);
+ textureCount = validateTextureCount(reqTextureCount);
+ if( textureCount < 2 ) {
+ throw new InternalError("Validated texture count < 2: "+textureCount);
}
- initGLStreamImpl(gl, tex);
-
- for(int i=0; i<textureCount; i++) {
- final TextureSequence.TextureFrame tf = createTexImage(gl, i, tex);
- texFrames[i] = tf;
- texFrameMap.put(tex[i], tf);
+ initGLStreamImpl(gl); // also initializes width, height, .. etc
+ videoFramesFree = new SyncedRingbuffer<TextureFrame>(createTexFrames(gl, textureCount), true /* full */);
+ if( 2 < textureCount ) {
+ videoFramesDecoded = new SyncedRingbuffer<TextureFrame>(new TextureFrame[textureCount], false /* full */);
+ framePusher = new FramePusher(gl, requiresOffthreadGLCtx());
+ framePusher.doStart();
+ } else {
+ videoFramesDecoded = null;
}
+ lastFrame = videoFramesFree.getBlocking(false /* clearRef */ );
}
state = State.Stopped;
return state;
@@ -329,35 +331,42 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
}
return state;
}
+ /**
+ * Returns the validated number of textures to be handled.
+ * <p>
+ * Default is always 2 textures, last texture and the decoding texture.
+ * </p>
+ */
+ protected int validateTextureCount(int desiredTextureCount) {
+ return 2;
+ }
+ protected boolean requiresOffthreadGLCtx() { return false; }
- /**
- * Implementation shall set the following set of data here
- * @param gl TODO
- * @param texNames TODO
- * @see #width
- * @see #height
- * @see #fps
- * @see #bps_stream
- * @see #totalFrames
- * @see #acodec
- * @see #vcodec
- */
- protected abstract void initGLStreamImpl(GL gl, int[] texNames) throws IOException;
-
- protected TextureSequence.TextureFrame createTexImage(GL gl, int idx, int[] tex) {
- return new TextureSequence.TextureFrame( createTexImageImpl(gl, idx, tex, width, height, false) );
+ private final TextureFrame[] createTexFrames(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] = createTexImage(gl, texNames[i]);
+ }
+ return texFrames;
}
+ protected abstract TextureFrame createTexImage(GL gl, int texName);
- protected Texture createTexImageImpl(GL gl, int idx, int[] tex, int tWidth, int tHeight, boolean mustFlipVertically) {
- if( 0 > tex[idx] ) {
- throw new RuntimeException("TextureName "+toHexString(tex[idx])+" invalid.");
+ protected final Texture createTexImageImpl(GL gl, int texName, int tWidth, int tHeight, boolean mustFlipVertically) {
+ if( 0 > texName ) {
+ throw new RuntimeException("TextureName "+toHexString(texName)+" invalid.");
}
gl.glActiveTexture(GL.GL_TEXTURE0+getTextureUnit());
- gl.glBindTexture(textureTarget, tex[idx]);
+ gl.glBindTexture(textureTarget, texName);
{
final int err = gl.glGetError();
if( GL.GL_NO_ERROR != err ) {
- throw new RuntimeException("Couldn't bind textureName "+toHexString(tex[idx])+" to 2D target, err "+toHexString(err));
+ throw new RuntimeException("Couldn't bind textureName "+toHexString(texName)+" to 2D target, err "+toHexString(err));
}
}
@@ -389,30 +398,297 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_WRAP_S, texWrapST[0]);
gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_WRAP_T, texWrapST[1]);
- return com.jogamp.opengl.util.texture.TextureIO.newTexture(tex[idx],
- textureTarget,
+ return com.jogamp.opengl.util.texture.TextureIO.newTexture(
+ texName, textureTarget,
tWidth, tHeight,
width, height,
mustFlipVertically);
}
+
+ private final void removeAllTextureFrames(GL gl) {
+ if( null != videoFramesFree ) {
+ final TextureFrame[] texFrames = videoFramesFree.getArray();
+ videoFramesFree = null;
+ videoFramesDecoded = null;
+ lastFrame = null;
+ for(int i=0; i<texFrames.length; i++) {
+ final TextureFrame frame = texFrames[i];
+ if(null != frame) {
+ destroyTexFrame(gl, frame);
+ texFrames[i] = null;
+ }
+ }
+ }
+ textureCount=0;
+ }
+ protected void destroyTexFrame(GL gl, TextureFrame frame) {
+ frame.getTexture().destroy(gl);
+ }
+
+ /**
+ * Implementation shall set the following set of data here
+ * @param gl TODO
+ * @see #width
+ * @see #height
+ * @see #fps
+ * @see #bps_stream
+ * @see #totalFrames
+ * @see #acodec
+ * @see #vcodec
+ */
+ protected abstract void initGLStreamImpl(GL gl) throws IOException;
- protected void destroyTexImage(GL gl, TextureSequence.TextureFrame imgTex) {
- imgTex.getTexture().destroy(gl);
+ @Override
+ public final TextureFrame getLastTexture() throws IllegalStateException {
+ if(State.Uninitialized == state) {
+ throw new IllegalStateException("Instance not initialized: "+this);
+ }
+ return lastFrame;
}
- protected void removeAllImageTextures(GL gl) {
- if(null != texFrames) {
- for(int i=0; i<textureCount; i++) {
- final TextureSequence.TextureFrame imgTex = texFrames[i];
- if(null != imgTex) {
- destroyTexImage(gl, imgTex);
- texFrames[i] = null;
+ @Override
+ public final synchronized TextureFrame getNextTexture(GL gl, boolean blocking) throws IllegalStateException {
+ if(State.Uninitialized == state) {
+ throw new IllegalStateException("Instance not initialized: "+this);
+ }
+ if(State.Playing == state) {
+ TextureFrame nextFrame = null;
+ boolean ok = true;
+ try {
+ if( 2 < textureCount ) {
+ nextFrame = videoFramesDecoded.getBlocking(false /* clearRef */ );
+ } else {
+ nextFrame = videoFramesFree.getBlocking(false /* clearRef */ );
+ if( getNextTextureImpl(gl, nextFrame, blocking) ) {
+ newFrameAvailable(nextFrame);
+ } else {
+ ok = false;
+ }
+ }
+ if( ok ) {
+ currentVideoPTS = nextFrame.getPTS();
+ if( blocking ) {
+ syncFrame2Audio(nextFrame);
+ }
+ final TextureFrame _lastFrame = lastFrame;
+ lastFrame = nextFrame;
+ videoFramesFree.putBlocking(_lastFrame);
+ }
+ } catch (InterruptedException e) {
+ ok = false;
+ e.printStackTrace();
+ } finally {
+ if( !ok && null != nextFrame ) { // put back
+ videoFramesFree.put(nextFrame);
}
}
}
- texFrameMap.clear();
+ return lastFrame;
}
+ protected abstract boolean getNextTextureImpl(GL gl, TextureFrame nextFrame, boolean blocking);
+ protected abstract void syncFrame2Audio(TextureFrame frame);
+
+ private final void newFrameAvailable(TextureFrame frame) {
+ frameNumber++;
+ synchronized(eventListenersLock) {
+ for(Iterator<GLMediaEventListener> i = eventListeners.iterator(); i.hasNext(); ) {
+ i.next().newFrameAvailable(this, frame, System.currentTimeMillis());
+ }
+ }
+ }
+
+ class FramePusher extends Thread {
+ private volatile boolean isRunning = false;
+ private volatile boolean isActive = false;
+
+ private volatile boolean shallPause = true;
+ private volatile boolean shallStop = false;
+
+ private final GL gl;
+ private GLDrawable dummyDrawable = null;
+ private GLContext sharedGLCtx = null;
+
+ FramePusher(GL gl, boolean createSharedCtx) {
+ setDaemon(true);
+ this.gl = createSharedCtx ? createSharedGL(gl) : gl;
+ }
+
+ private GL createSharedGL(GL gl) {
+ final GLContext glCtx = gl.getContext();
+ final boolean glCtxCurrent = glCtx.isCurrent();
+ final GLProfile glp = gl.getGLProfile();
+ final GLDrawableFactory factory = GLDrawableFactory.getFactory(glp);
+ final AbstractGraphicsDevice device = glCtx.getGLDrawable().getNativeSurface().getGraphicsConfiguration().getScreen().getDevice();
+ dummyDrawable = factory.createDummyDrawable(device, true, glp); // own device!
+ dummyDrawable.setRealized(true);
+ sharedGLCtx = dummyDrawable.createContext(glCtx);
+ makeCurrent(sharedGLCtx);
+ if( glCtxCurrent ) {
+ makeCurrent(glCtx);
+ } else {
+ sharedGLCtx.release();
+ }
+ return sharedGLCtx.getGL();
+ }
+ private void makeCurrent(GLContext ctx) {
+ if( GLContext.CONTEXT_NOT_CURRENT >= ctx.makeCurrent() ) {
+ throw new GLException("Couldn't make ctx current: "+ctx);
+ }
+ }
+
+ private void destroySharedGL() {
+ if( null != sharedGLCtx ) {
+ if( sharedGLCtx.isCreated() ) {
+ // Catch dispose GLExceptions by GLEventListener, just 'print' them
+ // so we can continue with the destruction.
+ try {
+ sharedGLCtx.destroy();
+ } catch (GLException gle) {
+ gle.printStackTrace();
+ }
+ }
+ sharedGLCtx = null;
+ }
+ if( null != dummyDrawable ) {
+ final AbstractGraphicsDevice device = dummyDrawable.getNativeSurface().getGraphicsConfiguration().getScreen().getDevice();
+ dummyDrawable.setRealized(false);
+ dummyDrawable = null;
+ device.close();
+ }
+ }
+
+ public synchronized void doPause() {
+ if( isActive ) {
+ shallPause = true;
+ while( isActive ) {
+ try {
+ this.wait(); // wait until paused
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ public synchronized void doResume() {
+ if( isRunning && !isActive ) {
+ shallPause = false;
+ while( !isActive ) {
+ this.notify(); // wake-up pause-block
+ try {
+ this.wait(); // wait until resumed
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ public synchronized void doStart() {
+ start();
+ while( !isRunning ) {
+ try {
+ this.wait(); // wait until started
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ public synchronized void doStop() {
+ if( isRunning ) {
+ shallStop = true;
+ while( isRunning ) {
+ this.notify(); // wake-up pause-block (opt)
+ try {
+ this.wait(); // wait until stopped
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ public boolean isRunning() { return isRunning; }
+ public boolean isActive() { return isActive; }
+
+ public void run() {
+ setName(getName()+"-FramePusher_"+FramePusherInstanceId);
+ FramePusherInstanceId++;
+
+ synchronized ( this ) {
+ if( null != sharedGLCtx ) {
+ makeCurrent( sharedGLCtx );
+ }
+ isRunning = true;
+ this.notify(); // wake-up doStart()
+ }
+
+ while( !shallStop ){
+ if( shallPause ) {
+ synchronized ( this ) {
+ while( shallPause && !shallStop ) {
+ isActive = false;
+ this.notify(); // wake-up doPause()
+ try {
+ this.wait(); // wait until resumed
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ isActive = true;
+ this.notify(); // wake-up doResume()
+ }
+ }
+
+ if( !shallStop ) {
+ TextureFrame nextFrame = null;
+ boolean ok = false;
+ try {
+ nextFrame = videoFramesFree.getBlocking(true /* clearRef */ );
+ if( getNextTextureImpl(gl, nextFrame, true) ) {
+ gl.glFinish();
+ videoFramesDecoded.putBlocking(nextFrame);
+ newFrameAvailable(nextFrame);
+ ok = true;
+ }
+ } catch (InterruptedException e) {
+ if( !shallStop && !shallPause ) {
+ e.printStackTrace(); // oops
+ shallPause = false;
+ shallStop = true;
+ }
+ } finally {
+ if( !ok && null != nextFrame ) { // put back
+ videoFramesFree.put(nextFrame);
+ }
+ }
+ }
+ }
+ destroySharedGL();
+ synchronized ( this ) {
+ isRunning = false;
+ isActive = false;
+ this.notify(); // wake-up doStop()
+ }
+ }
+ }
+ static int FramePusherInstanceId = 0;
+ private FramePusher framePusher = null;
+ private final void pauseFramePusher() {
+ if( null != framePusher ) {
+ framePusher.doPause();
+ }
+ }
+ private final void resumeFramePusher() {
+ if( null != framePusher ) {
+ framePusher.doResume();
+ }
+ }
+ private final void destroyFramePusher() {
+ if( null != framePusher ) {
+ framePusher.doStop();
+ framePusher = null;
+ }
+ }
+
protected final void updateAttributes(int width, int height, int bps_stream, int bps_video, int bps_audio,
float fps, int totalFrames, int duration,
String vcodec, String acodec) {
@@ -458,19 +734,12 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
}
}
}
- protected final void newFrameAvailable() {
- frameNumber++;
- synchronized(eventListenersLock) {
- for(Iterator<GLMediaEventListener> i = eventListeners.iterator(); i.hasNext(); ) {
- i.next().newFrameAvailable(this, System.currentTimeMillis());
- }
- }
- }
@Override
public final synchronized State destroy(GL gl) {
+ destroyFramePusher();
destroyImpl(gl);
- removeAllImageTextures(gl);
+ removeAllTextureFrames(gl);
state = State.Uninitialized;
return state;
}