aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2013-08-24 17:56:49 +0200
committerSven Gothel <[email protected]>2013-08-24 17:56:49 +0200
commitd0e01cb5c0ec3e48b8a9b9b79a7795b214c6e3ea (patch)
tree9594bb101e06ccd5e6ea1abdd5ea72777263bc83
parentdeae6def7a818d3189bec403f8cde2ad9936d416 (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 ?
-rw-r--r--make/scripts/tests.sh5
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java12
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/texture/TextureSequence.java28
-rw-r--r--src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java9
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java253
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/NullGLMediaPlayer.java41
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java43
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/impl/OMXGLMediaPlayer.java5
-rw-r--r--src/jogl/native/libav/ffmpeg_tool.h4
-rw-r--r--src/jogl/native/libav/jogamp_opengl_util_av_impl_FFMPEGMediaPlayer.c44
-rw-r--r--src/test/com/jogamp/opengl/test/android/MovieCubeActivity0.java4
-rw-r--r--src/test/com/jogamp/opengl/test/android/MovieSimpleActivity0.java4
-rw-r--r--src/test/com/jogamp/opengl/test/android/MovieSimpleActivity1.java4
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/TextureSequenceCubeES2.java35
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieCube.java24
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieSimple.java74
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();
}
}
});