diff options
author | Sven Gothel <[email protected]> | 2013-08-24 17:56:49 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2013-08-24 17:56:49 +0200 |
commit | d0e01cb5c0ec3e48b8a9b9b79a7795b214c6e3ea (patch) | |
tree | 9594bb101e06ccd5e6ea1abdd5ea72777263bc83 | |
parent | deae6def7a818d3189bec403f8cde2ad9936d416 (diff) |
GLMediaPlayer Multithreaded Decoding: GLMediaPlayer* (Part-6) - DONE
Multithreaded decoding and API should be considered stable by now,
minor changes may apply if Android/OMX impl. requires it.
We still need to solve TODO's as listed below, copied from 474ce65081ecd452215bc07ab866666cb11ca8b1.
+++
- *TextureFrame OO changes:
- TextureFrame extends TimeFrameI
- GLMediaPlayerImpl*
- Adapt to Ringbuffer changes of GlueGen commit f9f881e59c78e3036cb3f956bc97cfc3197f620d
- Fix impl. method's API doc
- getNextTextureImpl(..) returns video PTS
- Fix audio-only playback
- frame dropping shall only happen if:
- previous frame has not been dropped
- frame is too later
- one decoded frame is already available
- Don't block for decoder anymore:
- nextFrame = "videoFramesDecoded.getBlocking() -> videoFramesDecoded.get()";
No 'next decoded frame avail' only could mean:
- slow decoding/hardware
- slow transport
hence we shall not block rendering.
- Add DEBUG output if using last frame
- Add integer property 'jogl.debug.GLMediaPlayer.StreamWorker.delay' in milliseconds
to simulate slow decoding, i.e. delay is added in StreamWorker after decoding
before pushing new frame to Ringbuffer.
- FFMPEGMediaPlayer:
- audioFrameLimitWithVideo 128 -> 64
- audioFrameLimitAudioOnly 128 -> 32
- uses AudioSink's 'enqueueData(int pts, ByteBuffer bytes, int byteCount)'
- fixes for audio-only playback
+++
Working Tests: MovieSimple and MovieCube
TODO-1: Fix
- Android
- OMXGLMediaPlayer
TODO-2:
- Fix issue where async audio frames arrive much later than 1st video frame, i.e. around 300ms.
- Default TextureCount .. maybe 3 ?
- Adding Audio synchronization ?
- Find 'truth' about correlation of audio and video PTS values,
currently, we assume both to be unrelated ?
16 files changed, 322 insertions, 267 deletions
diff --git a/make/scripts/tests.sh b/make/scripts/tests.sh index 6475f06b4..c7207bbcf 100644 --- a/make/scripts/tests.sh +++ b/make/scripts/tests.sh @@ -134,6 +134,7 @@ function jrun() { #D_ARGS="-Djogl.debug.GLMediaPlayer -Djogl.debug.AudioSink" #D_ARGS="-Djogl.debug.GLMediaPlayer -Djogl.debug.GLMediaPlayer.Native" #D_ARGS="-Djogl.debug.GLMediaPlayer" + #D_ARGS="-Djogl.debug.GLMediaPlayer.StreamWorker.delay=25 -Djogl.debug.GLMediaPlayer" #D_ARGS="-Djogl.debug.GLMediaPlayer.Native" #D_ARGS="-Djogl.debug.AudioSink" #D_ARGS="-Djogl.debug.GLArrayData" @@ -318,8 +319,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) 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 726eddb01..02fbd721c 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java +++ b/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java @@ -35,6 +35,7 @@ import javax.media.opengl.GLException; import jogamp.opengl.Debug; import com.jogamp.opengl.util.texture.TextureSequence; +import com.jogamp.opengl.util.TimeFrameI; /** * GLMediaPlayer interface specifies a {@link TextureSequence} state machine @@ -116,14 +117,11 @@ import com.jogamp.opengl.util.texture.TextureSequence; * to be properly considered by {@link GLMediaPlayerFactory#create(ClassLoader, String)} * and {@link GLMediaPlayerFactory#createDefault()}. * </p> + * <a name="timestampaccuracy"><h5>Timestamp Accuracy</h5></a> * <p> - * Variable type, value range and dimension has been chosen to suit embedded CPUs - * and characteristics of audio and video streaming. - * Milliseconds of type integer with a maximum value of {@link Integer#MAX_VALUE} - * will allow tracking time up 2,147,483.647 seconds or - * 24 days 20 hours 31 minutes and 23 seconds. - * Milliseconds granularity is also more than enough to deal with A-V synchronization, - * where the threshold usually lies within 22ms. + * <p> + * Timestamp type and value range has been chosen to suit embedded CPUs + * and characteristics of audio and video streaming. See {@link TimeFrameI}. * </p> * * <a name="synchronization"><h5>Audio and video synchronization</h5></a> 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 e13e5ff13..8b6cc1bf9 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureSequence.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureSequence.java @@ -29,6 +29,8 @@ package com.jogamp.opengl.util.texture; import javax.media.opengl.GL; +import com.jogamp.opengl.util.TimeFrameI; + /** * Protocol for texture sequences, like animations, movies, etc. * <p> @@ -109,35 +111,21 @@ public interface TextureSequence { * Texture holder interface, maybe specialized by implementation * to associated related data. */ - public static class TextureFrame { - /** Constant marking an invalid PTS, i.e. Integer.MIN_VALUE == 0x80000000 == {@value}. Sync w/ native code. */ - public static final int INVALID_PTS = 0x80000000; - - /** Constant marking the end of the stream PTS, i.e. Integer.MIN_VALUE - 1 == 0x7FFFFFFF == {@value}. Sync w/ native code. */ - public static final int END_OF_STREAM_PTS = 0x7FFFFFFF; - + public static class TextureFrame extends TimeFrameI { + public TextureFrame(Texture t, int pts, int duration) { + super(pts, duration); + texture = t; + } 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 " + pts + " ms, l " + duration + " ms, texID "+ texture.getTextureObject() + "]"; + return "TextureFrame[pts " + pts + " ms, l " + duration + " ms, texID "+ (null != texture ? texture.getTextureObject() : 0) + "]"; } protected final Texture texture; - protected int pts; - protected int duration; } public interface TexSeqEventListener<T extends TextureSequence> { diff --git a/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java b/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java index 578a219e9..63d9c8d22 100644 --- a/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java +++ b/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java @@ -35,6 +35,7 @@ import javax.media.opengl.GLException; import com.jogamp.common.os.AndroidVersion; import com.jogamp.common.os.Platform; +import com.jogamp.opengl.util.TimeFrameI; import com.jogamp.opengl.util.av.GLMediaPlayer; import com.jogamp.opengl.util.texture.Texture; import com.jogamp.opengl.util.texture.TextureSequence; @@ -227,7 +228,8 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl { } @Override - protected final boolean getNextTextureImpl(GL gl, TextureFrame nextFrame) { + protected final int getNextTextureImpl(GL gl, TextureFrame nextFrame) { + int pts = TimeFrameI.INVALID_PTS; if(null != stex && null != mp) { final SurfaceTextureFrame nextSFrame = (SurfaceTextureFrame) nextFrame; final Surface nextSurface = nextSFrame.getSurface(); @@ -254,11 +256,12 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl { final SurfaceTexture nextSTex = nextSFrame.getSurfaceTexture(); nextSTex.updateTexImage(); // nextFrame.setPTS( (int) ( nextSTex.getTimestamp() / 1000000L ) ); // nano -9 -> milli -3 - nextFrame.setPTS( mp.getCurrentPosition() ); + pts = mp.getCurrentPosition(); + nextFrame.setPTS( pts ); // stex.getTransformMatrix(atex.getSTMatrix()); } } - return true; + return pts; } @Override diff --git a/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java b/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java index 8193175b7..238595d45 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java +++ b/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java @@ -42,14 +42,17 @@ import javax.media.opengl.GLES2; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; +import jogamp.opengl.Debug; + import com.jogamp.common.os.Platform; import com.jogamp.common.util.LFRingbuffer; import com.jogamp.common.util.Ringbuffer; +import com.jogamp.opengl.util.TimeFrameI; import com.jogamp.opengl.util.av.AudioSink; -import com.jogamp.opengl.util.av.AudioSink.AudioFrame; import com.jogamp.opengl.util.av.GLMediaPlayer; import com.jogamp.opengl.util.texture.Texture; import com.jogamp.opengl.util.texture.TextureSequence; +import com.jogamp.opengl.util.texture.TextureSequence.TextureFrame; /** * After object creation an implementation may customize the behavior: @@ -64,6 +67,7 @@ import com.jogamp.opengl.util.texture.TextureSequence; * </p> */ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { + private static final int STREAM_WORKER_DELAY = Debug.getIntProperty("jogl.debug.GLMediaPlayer.StreamWorker.delay", false, 0); protected static final String unknown = "unknown"; @@ -151,13 +155,6 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { private ArrayList<GLMediaEventListener> eventListeners = new ArrayList<GLMediaEventListener>(); - private static Ringbuffer.AllocEmptyArray<TextureFrame> rbAllocTextureFrameArray = new Ringbuffer.AllocEmptyArray<TextureFrame>() { - @Override - public TextureFrame[] newArray(int size) { - return new TextureFrame[size]; - } - }; - protected GLMediaPlayerImpl() { this.textureCount=0; this.textureTarget=GL.GL_TEXTURE_2D; @@ -284,6 +281,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { @Override public final State play() { synchronized( stateLock ) { + final State preState = state; switch( state ) { case Paused: if( playImpl() ) { @@ -296,7 +294,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { } default: } - if(DEBUG) { System.err.println("Play: "+toString()); } + if(DEBUG) { System.err.println("Play: "+preState+" -> "+state+", "+toString()); } return state; } } @@ -308,6 +306,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { } private final State pauseImpl(int event_mask) { synchronized( stateLock ) { + final State preState = state; if( State.Playing == state ) { event_mask = addStateEventMask(event_mask, GLMediaPlayer.State.Paused); state = State.Paused; @@ -320,7 +319,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { play(); } } - if(DEBUG) { System.err.println("Pause: "+toString()); } + if(DEBUG) { System.err.println("Pause: "+preState+" -> "+state+", "+toString()); } return state; } } @@ -329,6 +328,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { @Override public final int seek(int msec) { synchronized( stateLock ) { + final State preState = state; final int pts1; switch(state) { case Playing: @@ -348,7 +348,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { default: pts1 = 0; } - if(DEBUG) { System.err.println("Seek("+msec+"): "+toString()); } + if(DEBUG) { System.err.println("Seek("+msec+"): "+preState+" -> "+state+", "+toString()); } return pts1; } } @@ -362,6 +362,8 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { @Override public final boolean setPlaySpeed(float rate) { synchronized( stateLock ) { + final State preState = state; + final float preSpeed = playSpeed; boolean res = false; if(State.Uninitialized != state ) { if( rate > 0.01f ) { @@ -371,11 +373,11 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { if( setPlaySpeedImpl(rate) ) { resetAudioVideoPTS(); playSpeed = rate; - if(DEBUG) { System.err.println("SetPlaySpeed: "+toString()); } res = true; } } } + if(DEBUG) { System.err.println("setPlaySpeed("+rate+"): "+preState+"/"+preSpeed+" -> "+state+"/"+playSpeed+", "+toString()); } return res; } } @@ -464,14 +466,16 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { removeAllTextureFrames(gl); initGLImpl(gl); videoFramesOrig = createTexFrames(gl, textureCount); - videoFramesFree = new LFRingbuffer<TextureFrame>(videoFramesOrig, rbAllocTextureFrameArray); - videoFramesDecoded = new LFRingbuffer<TextureFrame>(textureCount, rbAllocTextureFrameArray); + videoFramesFree = new LFRingbuffer<TextureFrame>(videoFramesOrig); + videoFramesDecoded = new LFRingbuffer<TextureFrame>(TextureFrame[].class, textureCount); lastFrame = videoFramesFree.getBlocking( ); streamWorker.initGL(gl); } else { + removeAllTextureFrames(null); initGLImpl(null); setTextureFormat(-1, -1); setTextureType(-1); + videoFramesOrig = null; videoFramesFree = null; videoFramesDecoded = null; lastFrame = null; @@ -482,6 +486,15 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { } } } + /** + * Shall initialize all GL related resources, if not audio-only. + * <p> + * Shall also take care of {@link AudioSink} initialization if appropriate. + * </p> + * @param gl null for audio-only, otherwise a valid and current GL object. + * @throws IOException + * @throws GLException + */ protected abstract void initGLImpl(GL gl) throws IOException, GLException; /** @@ -570,12 +583,12 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { } private final void removeAllTextureFrames(GL gl) { - if( null != videoFramesOrig ) { - final TextureFrame[] texFrames = videoFramesOrig; - videoFramesOrig = null; - videoFramesFree = null; - videoFramesDecoded = null; - lastFrame = null; + final TextureFrame[] texFrames = videoFramesOrig; + videoFramesOrig = null; + videoFramesFree = null; + videoFramesDecoded = null; + lastFrame = null; + if( null != texFrames ) { for(int i=0; i<texFrames.length; i++) { final TextureFrame frame = texFrames[i]; if(null != frame) { @@ -605,28 +618,32 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { do { final long currentTimeMillis; final boolean playCached = null != cachedFrame; + final boolean droppedFrame; if( dropFrame ) { presentedFrameCount--; dropFrame = false; + droppedFrame = true; + } else { + droppedFrame = false; } if( playCached ) { nextFrame = cachedFrame; cachedFrame = null; presentedFrameCount--; } else if( STREAM_ID_NONE != vid ) { - nextFrame = videoFramesDecoded.getBlocking(); + nextFrame = videoFramesDecoded.get(); } currentTimeMillis = Platform.currentTimeMillis(); if( null != nextFrame ) { presentedFrameCount++; final int video_pts = nextFrame.getPTS(); - if( video_pts == TextureFrame.END_OF_STREAM_PTS ) { + if( video_pts == TimeFrameI.END_OF_STREAM_PTS ) { pauseImpl(GLMediaEventListener.EVENT_CHANGE_EOS); - } else if( video_pts != TextureFrame.INVALID_PTS ) { + } else if( video_pts != TimeFrameI.INVALID_PTS ) { final int audio_pts = getAudioPTSImpl(); final int audio_scr = (int) ( ( currentTimeMillis - audio_scr_t0 ) * playSpeed ); final int d_apts; - if( audio_pts != AudioFrame.INVALID_PTS ) { + if( audio_pts != TimeFrameI.INVALID_PTS ) { d_apts = audio_pts - audio_scr; } else { d_apts = 0; @@ -662,7 +679,9 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { if( dt > maxVideoDelay ) { cachedFrame = nextFrame; nextFrame = null; - } else if ( dt < -maxVideoDelay ) { + } else if ( !droppedFrame && dt < -maxVideoDelay && videoFramesDecoded.size() > 0 ) { + // only drop if prev. frame has not been dropped and + // frame is too late and one decoded frame is already available. dropFrame = true; } video_pts_last = video_pts; @@ -682,6 +701,20 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { lastFrame = nextFrame; videoFramesFree.putBlocking(_lastFrame); } + } else if( DEBUG ) { + final int video_pts = lastFrame.getPTS(); + final int audio_pts = getAudioPTSImpl(); + final int audio_scr = (int) ( ( currentTimeMillis - audio_scr_t0 ) * playSpeed ); + final int d_apts; + if( audio_pts != TimeFrameI.INVALID_PTS ) { + d_apts = audio_pts - audio_scr; + } else { + d_apts = 0; + } + final int video_scr = video_scr_pts + (int) ( ( currentTimeMillis - video_scr_t0 ) * playSpeed ); + final int d_vpts = video_pts - video_scr; + System.err.println( "AV~: dT "+(currentTimeMillis-lastTimeMillis)+", "+ + getPerfStringImpl( video_scr, video_pts, d_vpts, audio_scr, audio_pts, d_apts, 0 ) + ", droppedFrame "+droppedFrame); } lastTimeMillis = currentTimeMillis; } while( dropFrame ); @@ -696,12 +729,27 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { protected void preNextTextureImpl(GL gl) {} protected void postNextTextureImpl(GL gl) {} /** + * Process stream until the next video frame, i.e. {@link TextureFrame}, has been reached. + * Audio frames, i.e. {@link AudioSink.AudioFrame}, shall be handled in the process. + * <p> + * Video frames shall be ignored, if {@link #getVID()} is {@link #STREAM_ID_NONE}. + * </p> + * <p> + * Audio frames shall be ignored, if {@link #getAID()} is {@link #STREAM_ID_NONE}. + * </p> + * <p> + * Methods is invoked on the <a href="#streamworker"><i>StreamWorker</i> decoding thread</a>. + * </p> + * <p> * Implementation shall care of OpenGL synchronization as required, e.g. glFinish()/glFlush()! - * @param gl - * @param nextFrame - * @return + * </p> + * @param gl valid and current GL instance, shall be <code>null</code> for audio only. + * @param nextFrame the {@link TextureFrame} to store the video PTS and texture data, + * shall be <code>null</code> for audio only. + * @return the last processed video PTS value, maybe {@link TimeFrameI#INVALID_PTS} if video frame is invalid or n/a. + * Will be {@link TimeFrameI#END_OF_STREAM_PTS} if end of stream reached. */ - protected abstract boolean getNextTextureImpl(GL gl, TextureFrame nextFrame); + protected abstract int getNextTextureImpl(GL gl, TextureFrame nextFrame); /** * {@inheritDoc} @@ -731,12 +779,12 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { private void flushAllVideoFrames() { if( null != videoFramesFree ) { videoFramesFree.resetFull(videoFramesOrig); + lastFrame = videoFramesFree.get(); + if( null == lastFrame ) { throw new InternalError("XXX"); } } if( null != videoFramesDecoded ) { videoFramesDecoded.clear(); } - lastFrame = videoFramesFree.get( ); - if( null == lastFrame ) { throw new InternalError("XXX"); } cachedFrame = null; } private void resetAllAudioVideoSync() { @@ -842,14 +890,16 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { public synchronized void doPause() { if( isActive ) { shallPause = true; - if( isBlocked && isActive ) { - this.interrupt(); - } - while( isActive ) { - try { - this.wait(); // wait until paused - } catch (InterruptedException e) { - e.printStackTrace(); + if( Thread.currentThread() != this ) { + if( isBlocked && isActive ) { + this.interrupt(); + } + while( isActive ) { + try { + this.wait(); // wait until paused + } catch (InterruptedException e) { + e.printStackTrace(); + } } } } @@ -857,12 +907,14 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { 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(); + if( Thread.currentThread() != this ) { + while( !isActive ) { + this.notify(); // wake-up pause-block + try { + this.wait(); // wait until resumed + } catch (InterruptedException e) { + e.printStackTrace(); + } } } } @@ -870,15 +922,17 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { public synchronized void doStop() { if( isRunning ) { shallStop = true; - if( isBlocked && isRunning ) { - this.interrupt(); - } - while( isRunning ) { - this.notify(); // wake-up pause-block (opt) - try { - this.wait(); // wait until stopped - } catch (InterruptedException e) { - e.printStackTrace(); + if( Thread.currentThread() != this ) { + if( isBlocked && isRunning ) { + this.interrupt(); + } + while( isRunning ) { + this.notify(); // wake-up pause-block (opt) + try { + this.wait(); // wait until stopped + } catch (InterruptedException e) { + e.printStackTrace(); + } } } } @@ -939,51 +993,68 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { preNextTextureImpl(sharedGLCtx.getGL()); sharedGLCtxCurrent = true; } + if( null == videoFramesFree ) { + throw new InternalError("XXX videoFramesFree is null"); + } } } if( !shallStop ) { TextureFrame nextFrame = null; - if( null != sharedGLCtx ) { - try { - isBlocked = true; - nextFrame = videoFramesFree.getBlocking( ); - isBlocked = false; - nextFrame.setPTS( TextureFrame.INVALID_PTS ); // mark invalid until processed! - final GL gl = sharedGLCtxCurrent ? sharedGLCtx.getGL() : null; - if( getNextTextureImpl(gl, nextFrame) ) { + try { + final GL gl; + isBlocked = true; + if( null != videoFramesFree ) { + nextFrame = videoFramesFree.getBlocking(); + nextFrame.setPTS( TimeFrameI.INVALID_PTS ); // mark invalid until processed! + gl = sharedGLCtx.getGL(); + } else { + gl = null; + } + isBlocked = false; + final int vPTS = getNextTextureImpl(gl, nextFrame); + if( TimeFrameI.INVALID_PTS != vPTS ) { + if( null != nextFrame ) { + if( STREAM_WORKER_DELAY > 0 ) { + Thread.sleep(STREAM_WORKER_DELAY); + } if( !videoFramesDecoded.put(nextFrame) ) { throw new InternalError("XXX: free "+videoFramesFree+", decoded "+videoFramesDecoded+", "+GLMediaPlayerImpl.this); } newFrameAvailable(nextFrame, Platform.currentTimeMillis()); nextFrame = null; - } - } catch (InterruptedException e) { - isBlocked = false; - if( !shallStop && !shallPause ) { - streamErr = new StreamException("InterruptedException while decoding: "+GLMediaPlayerImpl.this.toString(), e); - } - } catch (Throwable t) { - streamErr = new StreamException(t.getClass().getSimpleName()+" while decoding: "+GLMediaPlayerImpl.this.toString(), t); - } finally { - if( null != nextFrame ) { // put back - videoFramesFree.put(nextFrame); - } - if( null != streamErr ) { - if( DEBUG ) { - final Throwable t = null != streamErr.getCause() ? streamErr.getCause() : streamErr; - System.err.println("Caught StreamException: "+t.getMessage()); - t.printStackTrace(); + } else { + // audio only + if( TimeFrameI.END_OF_STREAM_PTS == vPTS ) { + // state transition incl. notification + shallPause = true; + isActive = false; + pauseImpl(GLMediaEventListener.EVENT_CHANGE_EOS); } - // state transition incl. notification - shallPause = true; - isActive = false; - pause(); } } - } else { - // audio only - getNextTextureImpl(null, null); + } catch (InterruptedException e) { + isBlocked = false; + if( !shallStop && !shallPause ) { + streamErr = new StreamException("InterruptedException while decoding: "+GLMediaPlayerImpl.this.toString(), e); + } + } catch (Throwable t) { + streamErr = new StreamException(t.getClass().getSimpleName()+" while decoding: "+GLMediaPlayerImpl.this.toString(), t); + } finally { + if( null != nextFrame ) { // put back + videoFramesFree.put(nextFrame); + } + if( null != streamErr ) { + if( DEBUG ) { + final Throwable t = null != streamErr.getCause() ? streamErr.getCause() : streamErr; + System.err.println("Caught StreamException: "+t.getMessage()); + t.printStackTrace(); + } + // state transition incl. notification + shallPause = true; + isActive = false; + pauseImpl(GLMediaEventListener.EVENT_CHANGE_ERR); + } } } } @@ -1106,13 +1177,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { destroyImpl(gl); removeAllTextureFrames(gl); textureCount=0; - if( null != videoFramesFree ) { - videoFramesFree.clear(); - } - if( null != videoFramesDecoded ) { - videoFramesDecoded.clear(); - } - changeState(0, State.Uninitialized); + changeState(0, State.Uninitialized); return state; } } @@ -1215,7 +1280,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { final String audioSinkInfo; final AudioSink audioSink = getAudioSink(); if( null != audioSink ) { - audioSinkInfo = "AudioSink[frames [d "+audioSink.getEnqueuedFrameCount()+", q "+audioSink.getQueuedFrameCount()+", f "+audioSink.getFreeFrameCount()+"], time "+audioSink.getQueuedTime()+", bytes "+audioSink.getQueuedByteCount()+"]"; + audioSinkInfo = "AudioSink[frames [p "+audioSink.getEnqueuedFrameCount()+", q "+audioSink.getQueuedFrameCount()+", f "+audioSink.getFreeFrameCount()+", c "+audioSink.getFrameCount()+"], time "+audioSink.getQueuedTime()+", bytes "+audioSink.getQueuedByteCount()+"]"; } else { audioSinkInfo = ""; } diff --git a/src/jogl/classes/jogamp/opengl/util/av/NullGLMediaPlayer.java b/src/jogl/classes/jogamp/opengl/util/av/NullGLMediaPlayer.java index 31ac55ec3..9066f3dd1 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/NullGLMediaPlayer.java +++ b/src/jogl/classes/jogamp/opengl/util/av/NullGLMediaPlayer.java @@ -83,9 +83,10 @@ public class NullGLMediaPlayer extends GLMediaPlayerImpl { } @Override - protected final boolean getNextTextureImpl(GL gl, TextureFrame nextFrame) { - nextFrame.setPTS( getAudioPTSImpl() ); - return true; + protected final int getNextTextureImpl(GL gl, TextureFrame nextFrame) { + final int pts = getAudioPTSImpl(); + nextFrame.setPTS( pts ); + return pts; } @Override @@ -102,40 +103,42 @@ public class NullGLMediaPlayer extends GLMediaPlayerImpl { texData = null; } } - - @Override - protected final void initStreamImpl(int vid, int aid) throws IOException { + + public final static TextureData createTestTextureData() { + TextureData res = null; try { - URLConnection urlConn = IOUtil.getResource("jogl/util/data/av/test-ntsc01-160x90.png", this.getClass().getClassLoader()); + URLConnection urlConn = IOUtil.getResource("jogl/util/data/av/test-ntsc01-160x90.png", NullGLMediaPlayer.class.getClassLoader()); if(null != urlConn) { - texData = TextureIO.newTextureData(GLProfile.getGL2ES2(), urlConn.getInputStream(), false, TextureIO.PNG); + res = TextureIO.newTextureData(GLProfile.getGL2ES2(), urlConn.getInputStream(), false, TextureIO.PNG); } } catch (Exception e) { e.printStackTrace(); } - final int _w, _h; - if(null != texData) { - _w = texData.getWidth(); - _h = texData.getHeight(); - } else { - _w = 640; - _h = 480; - ByteBuffer buffer = Buffers.newDirectByteBuffer(_w*_h*4); + if(null == res) { + final int w = 160; + final int h = 90; + 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(); - texData = new TextureData(GLProfile.getGL2ES2(), - GL.GL_RGBA, _w, _h, 0, + res = new TextureData(GLProfile.getGL2ES2(), + GL.GL_RGBA, w, h, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, false, false, false, buffer, null); } + return res; + } + + @Override + protected final void initStreamImpl(int vid, int aid) throws IOException { + texData = createTestTextureData(); final int r_aid = GLMediaPlayer.STREAM_ID_NONE == aid ? GLMediaPlayer.STREAM_ID_NONE : GLMediaPlayer.STREAM_ID_AUTO; final float _fps = 24f; final int _duration = 10*60*1000; // msec final int _totalFrames = (int) ( (_duration/1000)*_fps ); updateAttributes(GLMediaPlayer.STREAM_ID_AUTO, r_aid, - _w, _h, 0, + texData.getWidth(), texData.getHeight(), 0, 0, 0, _fps, _totalFrames, 0, _duration, "png-static", 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 f1213d751..a800f2a31 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java @@ -40,6 +40,7 @@ import javax.media.opengl.GLException; import com.jogamp.common.util.VersionNumber; import com.jogamp.gluegen.runtime.ProcAddressTable; +import com.jogamp.opengl.util.TimeFrameI; import com.jogamp.opengl.util.GLPixelStorageModes; import com.jogamp.opengl.util.av.AudioSink; import com.jogamp.opengl.util.av.AudioSink.AudioDataFormat; @@ -169,7 +170,8 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { /** Initial audio frame count, ALAudioSink may grow buffer! */ private int initialAudioFrameCount = AV_DEFAULT_AFRAMES; private final int audioFrameGrowAmount = 8; - private final int audioFrameLimit = 128; + private final int audioFrameLimitWithVideo = 64; // 128; + private final int audioFrameLimitAudioOnly = 32; // 64; private SampleFormat aSampleFmt = null; private AudioSink.AudioDataFormat avChosenAudioFormat; private AudioSink.AudioDataFormat sinkChosenAudioFormat; @@ -235,6 +237,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { if(null == audioSink) { throw new GLException("AudioSink null"); } + final int audioFrameLimit; if( null != gl ) { final GLContextImpl ctx = (GLContextImpl)gl.getContext(); AccessController.doPrivileged(new PrivilegedAction<Object>() { @@ -247,7 +250,10 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { setGLFuncs0(moviePtr, procAddrGLTexSubImage2D, procAddrGLGetError, procAddrGLFlush, procAddrGLFinish); return null; } } ); - } + audioFrameLimit = audioFrameLimitWithVideo; + } else { + audioFrameLimit = audioFrameLimitAudioOnly; + } sinkChosenAudioFormat = audioSink.initSink(avChosenAudioFormat, initialAudioFrameCount, audioFrameGrowAmount, audioFrameLimit); if(DEBUG) { @@ -303,7 +309,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { */ private void updateAttributes2(int pixFmt, int planes, int bitsPerPixel, int bytesPerPixelPerPlane, int lSz0, int lSz1, int lSz2, - int tWd0, int tWd1, int tWd2, + int tWd0, int tWd1, int tWd2, int tH, int audioFrameCount, int sampleFmt, int sampleRate, int channels) { vPixelFmt = PixelFormat.valueOf(pixFmt); vPlanes = planes; @@ -320,7 +326,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { // w*h + 2 ( w/2 * h/2 ) // w*h + w*h/2 // 2*w/2 * h - texWidth = vTexWidth[0] + vTexWidth[1]; texHeight = height; + texWidth = vTexWidth[0] + vTexWidth[1]; texHeight = tH; break; // case PIX_FMT_YUYV422: case RGB24: @@ -329,7 +335,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { case RGBA: case ABGR: case BGRA: - texWidth = vTexWidth[0]; texHeight = height; + texWidth = vTexWidth[0]; texHeight = tH; break; default: // FIXME: Add more formats ! throw new RuntimeException("Unsupported pixelformat: "+vPixelFmt); @@ -487,42 +493,33 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { } @Override - protected final boolean getNextTextureImpl(GL gl, TextureFrame nextFrame) { + protected final int getNextTextureImpl(GL gl, TextureFrame nextFrame) { if(0==moviePtr) { throw new GLException("FFMPEG native instance null"); } - int vPTS = TextureFrame.INVALID_PTS; - if( null != nextFrame ) { + int vPTS = TimeFrameI.INVALID_PTS; + if( null != gl ) { final Texture tex = nextFrame.getTexture(); tex.enable(gl); tex.bind(gl); } /** Try decode up to 10 packets to find one containing video. */ - for(int i=0; TextureFrame.INVALID_PTS == vPTS && 10 > i; i++) { + for(int i=0; TimeFrameI.INVALID_PTS == vPTS && 10 > i; i++) { vPTS = readNextPacket0(moviePtr, textureTarget, textureFormat, textureType); } - if( TextureFrame.INVALID_PTS != vPTS ) { - if( null != nextFrame ) { - nextFrame.setPTS(vPTS); - } - return true; - } else { - return false; + if( null != nextFrame ) { + nextFrame.setPTS(vPTS); } - } + return vPTS; + } private final void pushSound(ByteBuffer sampleData, int data_size, int audio_pts) { setFirstAudioPTS2SCR( audio_pts ); if( 1.0f == playSpeed || audioSinkPlaySpeedSet ) { - audioSink.enqueueData( new AudioSink.AudioFrame(sampleData, data_size, audio_pts ) ); + audioSink.enqueueData( audio_pts, sampleData, data_size); } } - - private final int getBytesPerMS(int time) { - final int bytesPerSample = sinkChosenAudioFormat.sampleSize >>> 3; // /8 - return time * ( sinkChosenAudioFormat.channelCount * bytesPerSample * ( sinkChosenAudioFormat.sampleRate / 1000 ) ); - } private static native int getAvUtilVersion0(); private static native int getAvFormatVersion0(); 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 e1b773e9b..faa6a56c4 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/OMXGLMediaPlayer.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/OMXGLMediaPlayer.java @@ -164,12 +164,13 @@ public class OMXGLMediaPlayer extends EGLMediaPlayerImpl { } @Override - protected boolean getNextTextureImpl(GL gl, TextureFrame nextFrame) { + protected int getNextTextureImpl(GL gl, TextureFrame nextFrame) { if(0==moviePtr) { throw new GLException("OMX native instance null"); } final int nextTex = _getNextTextureID(moviePtr, true); if(0 < nextTex) { + // FIXME set pts ! /* FIXME final TextureSequence.TextureFrame eglImgTex = texFrameMap.get(new Integer(_getNextTextureID(moviePtr, blocking))); @@ -177,7 +178,7 @@ public class OMXGLMediaPlayer extends EGLMediaPlayerImpl { lastTex = eglImgTex; } */ } - return true; + return 0; // FIXME: return pts } private String replaceAll(String orig, String search, String repl) { diff --git a/src/jogl/native/libav/ffmpeg_tool.h b/src/jogl/native/libav/ffmpeg_tool.h index 76de406ae..013cc0cf2 100644 --- a/src/jogl/native/libav/ffmpeg_tool.h +++ b/src/jogl/native/libav/ffmpeg_tool.h @@ -73,10 +73,10 @@ typedef void (APIENTRYP PFNGLFINISH) (void); /** Default number of audio frames per video frame. Sync w/ FFMPEGMediaPlayer.AV_DEFAULT_AFRAMES. */ #define AV_DEFAULT_AFRAMES 8 -/** Constant PTS marking an invalid PTS, i.e. Integer.MIN_VALUE == 0x80000000 == {@value}. Sync w/ TextureFrame.INVALID_PTS */ +/** Constant PTS marking an invalid PTS, i.e. Integer.MIN_VALUE == 0x80000000 == {@value}. Sync w/ TimeFrameI.INVALID_PTS */ #define INVALID_PTS 0x80000000 -/** Constant PTS marking the end of the stream, i.e. Integer.MIN_VALUE - 1 == 0x7FFFFFFF == {@value}. Sync w/ TextureFrame.END_OF_STREAM_PTS */ +/** Constant PTS marking the end of the stream, i.e. Integer.MIN_VALUE - 1 == 0x7FFFFFFF == {@value}. Sync w/ TimeFrameI.END_OF_STREAM_PTS */ #define END_OF_STREAM_PTS 0x7FFFFFFF /** Until 55.0.0 */ diff --git a/src/jogl/native/libav/jogamp_opengl_util_av_impl_FFMPEGMediaPlayer.c b/src/jogl/native/libav/jogamp_opengl_util_av_impl_FFMPEGMediaPlayer.c index 99ef02da5..63164e547 100644 --- a/src/jogl/native/libav/jogamp_opengl_util_av_impl_FFMPEGMediaPlayer.c +++ b/src/jogl/native/libav/jogamp_opengl_util_av_impl_FFMPEGMediaPlayer.c @@ -234,6 +234,12 @@ static void _updateJavaAttributes(JNIEnv *env, jobject instance, FFMPEGToolBasic w = 0; h = 0; } + (*env)->CallVoidMethod(env, instance, jni_mid_updateAttributes2, + pAV->vPixFmt, pAV->vBufferPlanes, + pAV->vBitsPerPixel, pAV->vBytesPerPixelPerPlane, + pAV->vLinesize[0], pAV->vLinesize[1], pAV->vLinesize[2], + pAV->vTexWidth[0], pAV->vTexWidth[1], pAV->vTexWidth[2], h, + pAV->aFramesPerVideoFrame, pAV->aSampleFmt, pAV->aSampleRate, pAV->aChannels); (*env)->CallVoidMethod(env, instance, jni_mid_updateAttributes1, pAV->vid, pAV->aid, w, h, @@ -241,12 +247,6 @@ static void _updateJavaAttributes(JNIEnv *env, jobject instance, FFMPEGToolBasic pAV->fps, pAV->frames_video, pAV->frames_audio, pAV->duration, (*env)->NewStringUTF(env, pAV->vcodec), (*env)->NewStringUTF(env, pAV->acodec) ); - (*env)->CallVoidMethod(env, instance, jni_mid_updateAttributes2, - pAV->vPixFmt, pAV->vBufferPlanes, - pAV->vBitsPerPixel, pAV->vBytesPerPixelPerPlane, - pAV->vLinesize[0], pAV->vLinesize[1], pAV->vLinesize[2], - pAV->vTexWidth[0], pAV->vTexWidth[1], pAV->vTexWidth[2], - pAV->aFramesPerVideoFrame, pAV->aSampleFmt, pAV->aSampleRate, pAV->aChannels); } } @@ -392,7 +392,7 @@ JNIEXPORT jboolean JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_ini jni_mid_pushSound = (*env)->GetMethodID(env, ffmpegMediaPlayerClazz, "pushSound", "(Ljava/nio/ByteBuffer;II)V"); jni_mid_updateAttributes1 = (*env)->GetMethodID(env, ffmpegMediaPlayerClazz, "updateAttributes", "(IIIIIIIFIIILjava/lang/String;Ljava/lang/String;)V"); - jni_mid_updateAttributes2 = (*env)->GetMethodID(env, ffmpegMediaPlayerClazz, "updateAttributes2", "(IIIIIIIIIIIIII)V"); + jni_mid_updateAttributes2 = (*env)->GetMethodID(env, ffmpegMediaPlayerClazz, "updateAttributes2", "(IIIIIIIIIIIIIII)V"); if(jni_mid_pushSound == NULL || jni_mid_updateAttributes1 == NULL || @@ -858,9 +858,10 @@ JNIEXPORT jint JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_readNex if( AVERROR_EOF == avRes || ( pAV->pFormatCtx->pb && pAV->pFormatCtx->pb->eof_reached ) ) { resPTS = END_OF_STREAM_PTS; } else if( 0 <= avRes ) { + /** if( pAV->verbose ) { fprintf(stderr, "P: ptr %p, size %d\n", packet.data, packet.size); - } + } */ if(packet.stream_index==pAV->aid) { // Decode audio frame if(NULL == pAV->pAFrames) { // no audio registered @@ -1206,38 +1207,43 @@ JNIEXPORT jint JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_seek0 { const FFMPEGToolBasicAV_t *pAV = (FFMPEGToolBasicAV_t *)((void *)((intptr_t)ptr)); const int64_t pos0 = pAV->vPTS; - const int64_t pts0 = pAV->pVFrame->pkt_pts; + int64_t pts0; int streamID; AVRational time_base; if( pAV->vid >= 0 ) { streamID = pAV->vid; time_base = pAV->pVStream->time_base; + pts0 = pAV->pVFrame->pkt_pts; } else if( pAV->aid >= 0 ) { streamID = pAV->aid; time_base = pAV->pAStream->time_base; + pts0 = pAV->pAFrames[pAV->aFrameCurrent]->pkt_pts; } else { return pAV->vPTS; } int64_t pts1 = (int64_t) (pos1 * (int64_t) time_base.den) / (1000 * (int64_t) time_base.num); + if(pAV->verbose) { + fprintf(stderr, "SEEK: vid %d, aid %d, pos1 %d, pts: %ld -> %ld\n", pAV->vid, pAV->aid, pos1, pts0, pts1); + } int flags = 0; if(pos1 < pos0) { flags |= AVSEEK_FLAG_BACKWARD; } int res; if(HAS_FUNC(sp_av_seek_frame)) { - if(pos1 < pos0) { - flags |= AVSEEK_FLAG_BACKWARD; + if(pAV->verbose) { + fprintf(stderr, "SEEK.0: pre : s %ld / %ld -> t %d / %ld\n", pos0, pts0, pos1, pts1); } - fprintf(stderr, "SEEK.0: pre : s %ld / %ld -> t %ld / %ld\n", pos0, pts0, pos1, pts1); sp_av_seek_frame(pAV->pFormatCtx, streamID, pts1, flags); - } else if(HAS_FUNC(sp_avformat_seek_file)) { int64_t ptsD = pts1 - pts0; int64_t seek_min = ptsD > 0 ? pts1 - ptsD : INT64_MIN; int64_t seek_max = ptsD < 0 ? pts1 - ptsD : INT64_MAX; - fprintf(stderr, "SEEK.1: pre : s %ld / %ld -> t %ld / %ld [%ld .. %ld]\n", - pos0, pts0, pos1, pts1, seek_min, seek_max); + if(pAV->verbose) { + fprintf(stderr, "SEEK.1: pre : s %ld / %ld -> t %d / %ld [%ld .. %ld]\n", + pos0, pts0, pos1, pts1, seek_min, seek_max); + } res = sp_avformat_seek_file(pAV->pFormatCtx, -1, seek_min, pts1, seek_max, flags); } if(NULL != pAV->pVCodecCtx) { @@ -1246,9 +1252,11 @@ JNIEXPORT jint JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_seek0 if(NULL != pAV->pACodecCtx) { sp_avcodec_flush_buffers( pAV->pACodecCtx ); } - const jint vPTS = my_av_q2i32( pAV->pVFrame->pkt_pts * 1000, pAV->pVStream->time_base); - fprintf(stderr, "SEEK: post : res %d, u %ld, p %ld\n", res, vPTS, pAV->pVFrame->pkt_pts); - return vPTS; + const jint rPTS = my_av_q2i32( ( pAV->vid >= 0 ? pAV->pVFrame->pkt_pts : pAV->pAFrames[pAV->aFrameCurrent]->pkt_pts ) * 1000, time_base); + if(pAV->verbose) { + fprintf(stderr, "SEEK: post : res %d, u %ld\n", res, rPTS); + } + return rPTS; } JNIEXPORT jint JNICALL Java_jogamp_opengl_util_av_impl_FFMPEGMediaPlayer_getVideoPTS0 diff --git a/src/test/com/jogamp/opengl/test/android/MovieCubeActivity0.java b/src/test/com/jogamp/opengl/test/android/MovieCubeActivity0.java index c9acab64b..783742fec 100644 --- a/src/test/com/jogamp/opengl/test/android/MovieCubeActivity0.java +++ b/src/test/com/jogamp/opengl/test/android/MovieCubeActivity0.java @@ -107,8 +107,8 @@ public class MovieCubeActivity0 extends NewtBaseActivity { @Override public void attributesChanged(final GLMediaPlayer mp, int event_mask, long when) { - System.err.println("Player AttributesChanges: events_mask 0x"+Integer.toHexString(event_mask)+", when "+when); - System.err.println("Player State: "+mp); + System.err.println("MovieCubeActivity0 AttributesChanges: events_mask 0x"+Integer.toHexString(event_mask)+", when "+when); + System.err.println("MovieCubeActivity0 State: "+mp); if( 0 != ( GLMediaEventListener.EVENT_CHANGE_INIT & event_mask ) ) { glWindowMain.addGLEventListener(demoMain); anim.setUpdateFPSFrames(60, System.err); diff --git a/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity0.java b/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity0.java index d4cb226f2..467ad1e75 100644 --- a/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity0.java +++ b/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity0.java @@ -104,8 +104,8 @@ public class MovieSimpleActivity0 extends NewtBaseActivity { @Override public void attributesChanged(GLMediaPlayer mp, int event_mask, long when) { - System.err.println("Player AttributesChanges: events_mask 0x"+Integer.toHexString(event_mask)+", when "+when); - System.err.println("Player State: "+mp); + System.err.println("MovieSimpleActivity0 AttributesChanges: events_mask 0x"+Integer.toHexString(event_mask)+", when "+when); + System.err.println("MovieSimpleActivity0 State: "+mp); if( 0 != ( GLMediaEventListener.EVENT_CHANGE_INIT & event_mask ) ) { glWindowMain.addGLEventListener(demoMain); anim.setUpdateFPSFrames(60, System.err); diff --git a/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity1.java b/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity1.java index 4e86883e4..84e691e76 100644 --- a/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity1.java +++ b/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity1.java @@ -141,8 +141,8 @@ public class MovieSimpleActivity1 extends NewtBaseActivity { @Override public void attributesChanged(GLMediaPlayer mp, int event_mask, long when) { - System.err.println("Player AttributesChanges: events_mask 0x"+Integer.toHexString(event_mask)+", when "+when); - System.err.println("Player State: "+mp); + System.err.println("MovieSimpleActivity1 AttributesChanges: events_mask 0x"+Integer.toHexString(event_mask)+", when "+when); + System.err.println("MovieSimpleActivity1 State: "+mp); if( 0 != ( GLMediaEventListener.EVENT_CHANGE_INIT & event_mask ) ) { glWindowMain.addGLEventListener(demoMain); anim.setUpdateFPSFrames(60, System.err); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/TextureSequenceCubeES2.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/TextureSequenceCubeES2.java index 4172a2c20..e38b9c6e3 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/TextureSequenceCubeES2.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/TextureSequenceCubeES2.java @@ -52,6 +52,7 @@ import com.jogamp.opengl.util.glsl.ShaderState; import com.jogamp.opengl.util.texture.Texture; import com.jogamp.opengl.util.texture.TextureCoords; import com.jogamp.opengl.util.texture.TextureSequence; +import com.jogamp.opengl.util.texture.TextureSequence.TextureFrame; public class TextureSequenceCubeES2 implements GLEventListener { public TextureSequenceCubeES2 (TextureSequence texSource, boolean innerCube, float zoom0, float rotx, float roty) { @@ -188,7 +189,11 @@ public class TextureSequenceCubeES2 implements GLEventListener { public void init(GLAutoDrawable drawable) { GL2ES2 gl = drawable.getGL().getGL2ES2(); System.err.println(JoglVersion.getGLInfo(gl, null)); - final Texture tex= texSeq.getLastTexture().getTexture(); + final TextureFrame frame = texSeq.getLastTexture(); + if( null == frame ) { + return; + } + final Texture tex= frame.getTexture(); final boolean useExternalTexture = GLES2.GL_TEXTURE_EXTERNAL_OES == tex.getTarget(); if(useExternalTexture && !gl.isExtensionAvailable("GL_OES_EGL_image_external")) { @@ -315,20 +320,22 @@ public class TextureSequenceCubeES2 implements GLEventListener { private void reshapePMV(int width, int height) { - pmvMatrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); - pmvMatrix.glLoadIdentity(); - if(!innerCube) { - pmvMatrix.gluPerspective(45.0f, (float)width / (float)height, 1f, 10.0f); - nearPlaneNormalized = 1f/(100f-1f); - } else { - pmvMatrix.glOrthof(-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 10.0f); - nearPlaneNormalized = 0f; + if(null != pmvMatrix) { + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); + pmvMatrix.glLoadIdentity(); + if(!innerCube) { + pmvMatrix.gluPerspective(45.0f, (float)width / (float)height, 1f, 10.0f); + nearPlaneNormalized = 1f/(100f-1f); + } else { + pmvMatrix.glOrthof(-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 10.0f); + nearPlaneNormalized = 0f; + } + System.err.println("XXX0: Perspective nearPlaneNormalized: "+nearPlaneNormalized); + + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + pmvMatrix.glLoadIdentity(); + pmvMatrix.glTranslatef(0, 0, zoom); } - System.err.println("XXX0: Perspective nearPlaneNormalized: "+nearPlaneNormalized); - - pmvMatrix.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); - pmvMatrix.glLoadIdentity(); - pmvMatrix.glTranslatef(0, 0, zoom); } diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieCube.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieCube.java index c48c53189..7fa55f861 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieCube.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieCube.java @@ -60,7 +60,7 @@ import com.jogamp.opengl.util.texture.TextureSequence.TextureFrame; /** * Simple cube movie player w/ aspect ration true projection on a cube. */ -public class MovieCube implements GLEventListener, GLMediaEventListener { +public class MovieCube implements GLEventListener { private static boolean waitForKey = false; private final float zoom0, rotx, roty; private TextureSequenceCubeES2 cube=null; @@ -89,11 +89,9 @@ public class MovieCube implements GLEventListener, GLMediaEventListener { this.rotx = rotx; this.roty = roty; mPlayer = GLMediaPlayerFactory.createDefault(); - mPlayer.addEventListener(this); } public void initStream(URI streamLoc, int vid, int aid, int textureCount) { - mPlayer.addEventListener(this); mPlayer.initStream(streamLoc, vid, aid, textureCount); System.out.println("pC.1b "+mPlayer); } @@ -161,16 +159,6 @@ public class MovieCube implements GLEventListener, GLMediaEventListener { }; @Override - public void attributesChanged(GLMediaPlayer mp, int event_mask, long when) { - System.out.println("attributesChanges: "+mp+", 0x"+Integer.toHexString(event_mask)+", when "+when); - } - - @Override - public void newFrameAvailable(GLMediaPlayer mp, TextureFrame newFrame, long when) { - // System.out.println("newFrameAvailable: "+mp+", when "+when); - } - - @Override public void init(GLAutoDrawable drawable) { if(null == mPlayer) { throw new InternalError("mPlayer null"); @@ -179,8 +167,7 @@ public class MovieCube implements GLEventListener, GLMediaEventListener { throw new IllegalStateException("mPlayer not in state initialized: "+mPlayer); } if( GLMediaPlayer.STREAM_ID_NONE == mPlayer.getVID() ) { - System.err.println("MovieCube: No VID/stream selected - no GL: "+mPlayer); - return; + // throw new IllegalStateException("mPlayer has no VID/stream selected: "+mPlayer); } GL2ES2 gl = drawable.getGL().getGL2ES2(); System.err.println(JoglVersion.getGLInfo(gl, null)); @@ -203,7 +190,7 @@ public class MovieCube implements GLEventListener, GLMediaEventListener { } cube.init(drawable); mPlayer.play(); - System.out.println("pStart "+mPlayer); + System.out.println("play.0 "+mPlayer); boolean added; final Object upstreamWidget = drawable.getUpstreamWidget(); @@ -245,7 +232,6 @@ public class MovieCube implements GLEventListener, GLMediaEventListener { System.err.println( mPlayer.getPerfString() ); lastPerfPos = currentPos; } - cube.display(drawable); } @@ -353,8 +339,8 @@ public class MovieCube implements GLEventListener, GLMediaEventListener { @Override public void attributesChanged(final GLMediaPlayer mp, int event_mask, long when) { - System.err.println("Player AttributesChanges: events_mask 0x"+Integer.toHexString(event_mask)+", when "+when); - System.err.println("Player State: "+mp); + System.err.println("MovieCube AttributesChanges: events_mask 0x"+Integer.toHexString(event_mask)+", when "+when); + System.err.println("MovieCube State: "+mp); if( 0 != ( GLMediaEventListener.EVENT_CHANGE_SIZE & event_mask ) && origSize ) { window.setSize(mp.getWidth(), mp.getHeight()); } diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieSimple.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieSimple.java index ecf95f069..672500b1b 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieSimple.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieSimple.java @@ -75,7 +75,7 @@ import com.jogamp.opengl.util.texture.TextureSequence.TextureFrame; /** * Simple planar movie player w/ orthogonal 1:1 projection. */ -public class MovieSimple implements GLEventListener, GLMediaEventListener { +public class MovieSimple implements GLEventListener { public static final int EFFECT_NORMAL = 0; public static final int EFFECT_GRADIENT_BOTTOM2TOP = 1<<1; public static final int EFFECT_TRANSPARENT = 1<<3; @@ -234,12 +234,10 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { mPlayerShared = true; mPlayerExternal = true; mPlayer = sharedMediaPlayer; - mPlayer.addEventListener(this); System.out.println("pC.2 shared "+mPlayerShared+", "+mPlayer); } public void initStream(URI streamLoc, int vid, int aid, int textureCount) { - mPlayer.addEventListener(this); mPlayer.initStream(streamLoc, vid, aid, textureCount); System.out.println("pC.1b "+mPlayer); } @@ -263,16 +261,6 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { this.alpha = alpha; } - @Override - public void attributesChanged(GLMediaPlayer mp, int event_mask, long when) { - System.out.println("attributesChanges: "+mp+", 0x"+Integer.toHexString(event_mask)+", when "+when); - } - - @Override - public void newFrameAvailable(GLMediaPlayer mp, TextureFrame newFrame, long when) { - // System.out.println("newFrameAvailable: "+mp+", when "+when); - } - private void initShader(GL2ES2 gl) { // Create & Compile the shader objects ShaderCode rsVp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, MovieSimple.class, @@ -316,8 +304,7 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { throw new IllegalStateException("mPlayer not in state initialized: "+mPlayer); } if( GLMediaPlayer.STREAM_ID_NONE == mPlayer.getVID() ) { - System.err.println("MovieSimple: No VID/stream selected - no GL: "+mPlayer); - return; + throw new IllegalStateException("mPlayer has no VID/stream selected: "+mPlayer); } zoom0 = orthoProjection ? 0f : -2.5f; zoom1 = orthoProjection ? 0f : -5f; @@ -338,8 +325,12 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { if(!mPlayerShared) { mPlayer.initGL(gl); } - tex = mPlayer.getLastTexture().getTexture(); System.out.println("p1 "+mPlayer+", shared "+mPlayerShared); + final TextureFrame frame = mPlayer.getLastTexture(); + if( null == frame ) { + throw new InternalError("XXX: "+mPlayer); + } + tex = mPlayer.getLastTexture().getTexture(); useExternalTexture = GLES2.GL_TEXTURE_EXTERNAL_OES == tex.getTarget(); if(useExternalTexture && !gl.isExtensionAvailable("GL_OES_EGL_image_external")) { throw new GLException("GL_OES_EGL_image_external requested but not available"); @@ -415,10 +406,9 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { interleavedVBO.addGLSLSubArray("mgl_MultiTexCoord", 2, GL.GL_ARRAY_BUFFER); final FloatBuffer ib = (FloatBuffer)interleavedVBO.getBuffer(); - final TextureCoords tc = tex.getImageTexCoords(); - final float aspect = tex.getAspectRatio(); + final TextureCoords tc = tex.getImageTexCoords(); System.err.println("XXX0: "+tc); - System.err.println("XXX0: tex aspect: "+aspect); + System.err.println("XXX0: tex aspect: "+tex.getAspectRatio()); System.err.println("XXX0: tex y-flip: "+tex.getMustFlipVertically()); // left-bottom @@ -472,7 +462,7 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { if(!mPlayerShared) { mPlayer.play(); - System.out.println("pStart "+mPlayer); + System.out.println("play.0 "+mPlayer); } startTime = System.currentTimeMillis(); @@ -493,9 +483,6 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { gl.setSwapInterval(swapInterval); // in case switching the drawable (impl. may bound attribute there) } if(null == mPlayer) { return; } - if( GLMediaPlayer.STREAM_ID_NONE == mPlayer.getVID() ) { - return; - } winWidth = width; winHeight = height; @@ -535,7 +522,6 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { System.out.println("pD.1 "+mPlayer); GL2ES2 gl = drawable.getGL().getGL2ES2(); if( null != mPlayer ) { - mPlayer.removeEventListener(this); if(!mPlayerExternal) { mPlayer.destroy(gl); } @@ -563,11 +549,7 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { if( currentPos - lastPerfPos > 2000 ) { System.err.println( mPlayer.getPerfString() ); lastPerfPos = currentPos; - } - - if( GLMediaPlayer.STREAM_ID_NONE == mPlayer.getVID() ) { - return; - } + } GL2ES2 gl = drawable.getGL().getGL2ES2(); @@ -721,31 +703,47 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { anim.start(); ms.mPlayer.addEventListener(new GLMediaEventListener() { + void destroyWindow() { + new Thread() { + public void run() { + window.destroy(); + } }.start(); + } + @Override public void newFrameAvailable(GLMediaPlayer ts, TextureFrame newFrame, long when) { } @Override public void attributesChanged(final GLMediaPlayer mp, int event_mask, long when) { - System.err.println("Player AttributesChanges: events_mask 0x"+Integer.toHexString(event_mask)+", when "+when); - System.err.println("Player State: "+mp); + System.err.println("MovieSimple AttributesChanges: events_mask 0x"+Integer.toHexString(event_mask)+", when "+when); + System.err.println("MovieSimple State: "+mp); if( 0 != ( GLMediaEventListener.EVENT_CHANGE_SIZE & event_mask ) && origSize ) { window.setSize(mp.getWidth(), mp.getHeight()); } if( 0 != ( GLMediaEventListener.EVENT_CHANGE_INIT & event_mask ) ) { - window.addGLEventListener(ms); - anim.setUpdateFPSFrames(60, System.err); - anim.resetFPSCounter(); + if( GLMediaPlayer.STREAM_ID_NONE != ms.mPlayer.getVID() ) { + window.addGLEventListener(ms); + anim.setUpdateFPSFrames(60, System.err); + anim.resetFPSCounter(); + } else { + try { + ms.mPlayer.initGL(null); + } catch (Exception e) { + e.printStackTrace(); + destroyWindow(); + return; + } + ms.mPlayer.play(); + System.out.println("play.1 "+ms.mPlayer); + } } if( 0 != ( ( GLMediaEventListener.EVENT_CHANGE_ERR | GLMediaEventListener.EVENT_CHANGE_EOS ) & event_mask ) ) { final StreamException se = ms.mPlayer.getStreamException(); if( null != se ) { se.printStackTrace(); } - new Thread() { - public void run() { - window.destroy(); - } }.start(); + destroyWindow(); } } }); |