diff options
author | Sven Gothel <[email protected]> | 2013-08-23 01:02:33 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2013-08-23 01:02:33 +0200 |
commit | 474ce65081ecd452215bc07ab866666cb11ca8b1 (patch) | |
tree | 8ce81c8132a4412d5d27748ddbc087094ad4c462 /src/test/com | |
parent | 4dc4a32720e7b176e6811c0eaa8ddc060e1468da (diff) |
GLMediaPlayer Multithreaded Decoding: GLMediaPlayer* (Part-5) - WIP
- Update/fix GLMediaPlayer API doc
- GLMediaEventListener: Add event bits for all state changes to be delivered via attributesChanged(..)
- StreamWorker / Decoder Thread:
- Use StreamWorker only !
- Handle exceptions on StreamWorker via StreamException
- Handles stream initialization and decoding (-> initStream(..))
- Split initGLStream(..) -> initStream(..) + initGL(GL)
- allow initStream(..)'s implementation being executed on StreamWorker
- allow GL initialization to be 'postponed' when stream is read,
i.e. non blocking stream initialization (UI .. etc)
- Handle EOS via END_OF_STREAM_PTS -> pause/event
- Video: Use lock-free LFRingbuffer, similar to
ALAudioSink (commit f18a94b3defef16e98badd6d99f2422609aa56c5)
+++
- FFMPEGDynamicLibraryBundleInfo
- Add avcodec's:
- avcodec_get_frame_defaults, avcodec_free_frame (54.28.0), avcodec_flush_buffers,
- Add avutil's:
- av_frame_unref (55.0.0)
- Add avformat's:
- avformat_seek_file (??)
+++
- FFMPEGMediaPlayer Native:
- add 'snoop' video frames for a/v frame count relation.
disabled per default, since no more needed due to ALAudioSink's
grow-buffer usage of LFRingbuffer.
- use sp_avcodec_free_frame if available
- 'useRefCountedFrames=1' for libav 55.0 to cache more than one audio frame,
not used since ALAudioSink's OpenAL usage does not require it (copies data once).
Note: the above snooped-video frame count is used here.
- use only one cached audio-frame (-> see above, OpenAL copies data once),
while reusing the NIO buffer!
- Perform OpenGL sync (glFinish) in native code!
- find proper PTS value, i.e. either frame's PTS or DTS,
see 'PTSStats'.
- FFMPEGMediaPlayer Java:
- use private fields
- simplified code due to above changes.
+++
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 ?
Diffstat (limited to 'src/test/com')
5 files changed, 436 insertions, 246 deletions
diff --git a/src/test/com/jogamp/opengl/test/android/MovieCubeActivity0.java b/src/test/com/jogamp/opengl/test/android/MovieCubeActivity0.java index 2c434f38a..c9acab64b 100644 --- a/src/test/com/jogamp/opengl/test/android/MovieCubeActivity0.java +++ b/src/test/com/jogamp/opengl/test/android/MovieCubeActivity0.java @@ -48,6 +48,9 @@ import com.jogamp.newt.opengl.GLWindow; import com.jogamp.opengl.test.junit.jogl.demos.es2.av.MovieCube; import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.av.GLMediaPlayer; +import com.jogamp.opengl.util.av.GLMediaPlayer.GLMediaEventListener; +import com.jogamp.opengl.util.av.GLMediaPlayer.StreamException; +import com.jogamp.opengl.util.texture.TextureSequence.TextureFrame; import android.os.Bundle; import android.util.Log; @@ -85,21 +88,45 @@ public class MovieCubeActivity0 extends NewtBaseActivity { scrn.addReference(); try { - final Animator animator = new Animator(); + final Animator anim = new Animator(); // Main - final MovieCube demoMain = new MovieCube(streamLoc, GLMediaPlayer.STREAM_ID_AUTO, GLMediaPlayer.STREAM_ID_AUTO, -2.3f, 0f, 0f); final GLWindow glWindowMain = GLWindow.create(scrn, capsMain); glWindowMain.setFullscreen(true); setContentView(getWindow(), glWindowMain); - glWindowMain.addMouseListener(showKeyboardMouseListener); - glWindowMain.addGLEventListener(demoMain); - animator.add(glWindowMain); + anim.add(glWindowMain); glWindowMain.setVisible(true); + glWindowMain.addMouseListener(showKeyboardMouseListener); - // animator.setUpdateFPSFrames(60, System.err); - animator.setUpdateFPSFrames(-1, null); - animator.resetFPSCounter(); + final MovieCube demoMain = new MovieCube(-2.3f, 0f, 0f); + final GLMediaPlayer mPlayer = demoMain.getGLMediaPlayer(); + mPlayer.addEventListener(new GLMediaEventListener() { + @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); + if( 0 != ( GLMediaEventListener.EVENT_CHANGE_INIT & event_mask ) ) { + glWindowMain.addGLEventListener(demoMain); + anim.setUpdateFPSFrames(60, System.err); + anim.resetFPSCounter(); + } + if( 0 != ( ( GLMediaEventListener.EVENT_CHANGE_ERR | GLMediaEventListener.EVENT_CHANGE_EOS ) & event_mask ) ) { + final StreamException se = mPlayer.getStreamException(); + if( null != se ) { + se.printStackTrace(); + } + new Thread() { + public void run() { + glWindowMain.destroy(); + } }.start(); + } + } + }); + demoMain.initStream(streamLoc, GLMediaPlayer.STREAM_ID_AUTO, GLMediaPlayer.STREAM_ID_AUTO, 0); } catch (IOException e) { e.printStackTrace(); } diff --git a/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity0.java b/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity0.java index 2c8f0eb50..d4cb226f2 100644 --- a/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity0.java +++ b/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity0.java @@ -27,7 +27,6 @@ */ package com.jogamp.opengl.test.android; -import java.io.IOException; import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; @@ -49,6 +48,9 @@ import com.jogamp.newt.opengl.GLWindow; import com.jogamp.opengl.test.junit.jogl.demos.es2.av.MovieSimple; import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.av.GLMediaPlayer; +import com.jogamp.opengl.util.av.GLMediaPlayer.GLMediaEventListener; +import com.jogamp.opengl.util.av.GLMediaPlayer.StreamException; +import com.jogamp.opengl.util.texture.TextureSequence.TextureFrame; import android.os.Bundle; import android.util.Log; @@ -84,25 +86,44 @@ public class MovieSimpleActivity0 extends NewtBaseActivity { final com.jogamp.newt.Screen scrn = NewtFactory.createScreen(dpy, 0); scrn.addReference(); - try { - final Animator animator = new Animator(); - - // Main - final MovieSimple demoMain = new MovieSimple(streamLoc, GLMediaPlayer.STREAM_ID_AUTO, GLMediaPlayer.STREAM_ID_AUTO); - demoMain.setScaleOrig(true); - final GLWindow glWindowMain = GLWindow.create(scrn, capsMain); - glWindowMain.setFullscreen(true); - setContentView(getWindow(), glWindowMain); - glWindowMain.addGLEventListener(demoMain); - animator.add(glWindowMain); - glWindowMain.setVisible(true); - - animator.setUpdateFPSFrames(60, System.err); - // animator.setUpdateFPSFrames(-1, null); - animator.resetFPSCounter(); - } catch (IOException e) { - e.printStackTrace(); - } + final Animator anim = new Animator(); + + // Main + final GLWindow glWindowMain = GLWindow.create(scrn, capsMain); + glWindowMain.setFullscreen(true); + setContentView(getWindow(), glWindowMain); + anim.add(glWindowMain); + glWindowMain.setVisible(true); + + final MovieSimple demoMain = new MovieSimple(); + demoMain.setScaleOrig(true); + final GLMediaPlayer mPlayer = demoMain.getGLMediaPlayer(); + mPlayer.addEventListener( new GLMediaPlayer.GLMediaEventListener() { + @Override + public void newFrameAvailable(GLMediaPlayer ts, TextureFrame newFrame, long when) { } + + @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); + if( 0 != ( GLMediaEventListener.EVENT_CHANGE_INIT & event_mask ) ) { + glWindowMain.addGLEventListener(demoMain); + anim.setUpdateFPSFrames(60, System.err); + anim.resetFPSCounter(); + } + if( 0 != ( ( GLMediaEventListener.EVENT_CHANGE_ERR | GLMediaEventListener.EVENT_CHANGE_EOS ) & event_mask ) ) { + final StreamException se = mPlayer.getStreamException(); + if( null != se ) { + se.printStackTrace(); + } + new Thread() { + public void run() { + glWindowMain.destroy(); + } }.start(); + } + } + }); + demoMain.initStream(streamLoc, GLMediaPlayer.STREAM_ID_AUTO, GLMediaPlayer.STREAM_ID_AUTO, 0); scrn.removeReference(); diff --git a/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity1.java b/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity1.java index df6b91582..4e86883e4 100644 --- a/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity1.java +++ b/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity1.java @@ -27,7 +27,6 @@ */ package com.jogamp.opengl.test.android; -import java.io.IOException; import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; @@ -51,6 +50,9 @@ import com.jogamp.newt.opengl.GLWindow; import com.jogamp.opengl.test.junit.jogl.demos.es2.av.MovieSimple; import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.av.GLMediaPlayer; +import com.jogamp.opengl.util.av.GLMediaPlayer.GLMediaEventListener; +import com.jogamp.opengl.util.av.GLMediaPlayer.StreamException; +import com.jogamp.opengl.util.texture.TextureSequence.TextureFrame; import android.os.Bundle; import android.util.Log; @@ -110,82 +112,123 @@ public class MovieSimpleActivity1 extends NewtBaseActivity { final com.jogamp.newt.Screen scrn = NewtFactory.createScreen(dpy, 0); scrn.addReference(); - try { - final Animator animator = new Animator(); - - // Main - final MovieSimple demoMain = new MovieSimple(streamLoc0, GLMediaPlayer.STREAM_ID_AUTO, GLMediaPlayer.STREAM_ID_AUTO); - if(mPlayerHUD) { - demoMain.setEffects(MovieSimple.EFFECT_GRADIENT_BOTTOM2TOP); - demoMain.setTransparency(0.9f); - } - demoMain.setScaleOrig(mPlayerNoZoom); - final GLWindow glWindowMain = GLWindow.create(scrn, capsMain); - { - final int padding = mPlayerHUD ? 32 : 0; - final android.view.View androidView = ((jogamp.newt.driver.android.WindowDriver)glWindowMain.getDelegatedWindow()).getAndroidView(); - glWindowMain.setSize(scrn.getWidth()-padding, scrn.getHeight()-padding); - glWindowMain.setUndecorated(true); - // setContentView(getWindow(), glWindowMain); - viewGroup.addView(androidView, new android.widget.FrameLayout.LayoutParams(glWindowMain.getWidth(), glWindowMain.getHeight(), Gravity.BOTTOM|Gravity.RIGHT)); - registerNEWTWindow(glWindowMain); + final Animator anim = new Animator(); + + // Main + final GLWindow glWindowMain = GLWindow.create(scrn, capsMain); + { + final int padding = mPlayerHUD ? 32 : 0; + final android.view.View androidView = ((jogamp.newt.driver.android.WindowDriver)glWindowMain.getDelegatedWindow()).getAndroidView(); + glWindowMain.setSize(scrn.getWidth()-padding, scrn.getHeight()-padding); + glWindowMain.setUndecorated(true); + // setContentView(getWindow(), glWindowMain); + viewGroup.addView(androidView, new android.widget.FrameLayout.LayoutParams(glWindowMain.getWidth(), glWindowMain.getHeight(), Gravity.BOTTOM|Gravity.RIGHT)); + registerNEWTWindow(glWindowMain); + } + anim.add(glWindowMain); + glWindowMain.setVisible(true); + + final MovieSimple demoMain = new MovieSimple(); + final GLMediaPlayer mPlayerMain = demoMain.getGLMediaPlayer(); + if(mPlayerHUD) { + demoMain.setEffects(MovieSimple.EFFECT_GRADIENT_BOTTOM2TOP); + demoMain.setTransparency(0.9f); + } + demoMain.setScaleOrig(mPlayerNoZoom); + mPlayerMain.addEventListener( new GLMediaPlayer.GLMediaEventListener() { + @Override + public void newFrameAvailable(GLMediaPlayer ts, TextureFrame newFrame, long when) { } + + @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); + if( 0 != ( GLMediaEventListener.EVENT_CHANGE_INIT & event_mask ) ) { + glWindowMain.addGLEventListener(demoMain); + anim.setUpdateFPSFrames(60, System.err); + anim.resetFPSCounter(); + } + if( 0 != ( ( GLMediaEventListener.EVENT_CHANGE_ERR | GLMediaEventListener.EVENT_CHANGE_EOS ) & event_mask ) ) { + final StreamException se = mPlayerMain.getStreamException(); + if( null != se ) { + se.printStackTrace(); + } + new Thread() { + public void run() { + glWindowMain.destroy(); + } }.start(); + } } - - glWindowMain.addGLEventListener(demoMain); - animator.add(glWindowMain); - glWindowMain.setVisible(true); - - if(mPlayerHUD) { - final GLMediaPlayer sharedPlayer = mPlayerSharedHUD ? demoMain.getGLMediaPlayer() : null; - final GLCapabilities capsHUD = new GLCapabilities(GLProfile.getGL2ES2()); - capsHUD.setBackgroundOpaque(false); - final GLWindow glWindowHUD = GLWindow.create(scrn, capsHUD); - glWindowMain.invoke(false, new GLRunnable() { - @Override - public boolean run(GLAutoDrawable drawable) { - int x2 = scrn.getX(); - int y2 = scrn.getY(); - int w2 = scrn.getWidth()/3; - int h2 = scrn.getHeight()/3; - if(null != sharedPlayer) { - if(0 < sharedPlayer.getWidth() && sharedPlayer.getWidth()<scrn.getWidth()/2 && - 0 < sharedPlayer.getHeight() && sharedPlayer.getHeight()<scrn.getHeight()/2) { - w2 = sharedPlayer.getWidth(); - h2 = sharedPlayer.getHeight(); - } - glWindowHUD.setSharedContext(glWindowMain.getContext()); - glWindowHUD.addGLEventListener(new MovieSimple(sharedPlayer)); - } else { - try { - glWindowHUD.addGLEventListener(new MovieSimple(streamLoc1, GLMediaPlayer.STREAM_ID_AUTO, GLMediaPlayer.STREAM_ID_AUTO)); - } catch (IOException e) { - e.printStackTrace(); + }); + demoMain.initStream(streamLoc0, GLMediaPlayer.STREAM_ID_AUTO, GLMediaPlayer.STREAM_ID_AUTO, 0); + + if(mPlayerHUD) { + final GLMediaPlayer mPlayerShared = mPlayerSharedHUD ? mPlayerMain : null; + final GLCapabilities capsHUD = new GLCapabilities(GLProfile.getGL2ES2()); + capsHUD.setBackgroundOpaque(false); + final GLWindow glWindowHUD = GLWindow.create(scrn, capsHUD); + glWindowMain.invoke(false, new GLRunnable() { + @Override + public boolean run(GLAutoDrawable drawable) { + final GLMediaPlayer mPlayerSub; + final MovieSimple demoHUD; + int x2 = scrn.getX(); + int y2 = scrn.getY(); + int w2 = scrn.getWidth()/3; + int h2 = scrn.getHeight()/3; + if(null != mPlayerShared) { + if(0 < mPlayerShared.getWidth() && mPlayerShared.getWidth()<scrn.getWidth()/2 && + 0 < mPlayerShared.getHeight() && mPlayerShared.getHeight()<scrn.getHeight()/2) { + w2 = mPlayerShared.getWidth(); + h2 = mPlayerShared.getHeight(); + } + glWindowHUD.setSharedContext(glWindowMain.getContext()); + demoHUD = new MovieSimple(mPlayerShared); + mPlayerSub = mPlayerShared; + } else { + demoHUD = new MovieSimple(); + mPlayerSub = demoHUD.getGLMediaPlayer(); + } + mPlayerSub.addEventListener( new GLMediaPlayer.GLMediaEventListener() { + @Override + public void newFrameAvailable(GLMediaPlayer ts, TextureFrame newFrame, long when) { } + + @Override + public void attributesChanged(GLMediaPlayer mp, int event_mask, long when) { + if( 0 != ( GLMediaEventListener.EVENT_CHANGE_INIT & event_mask ) ) { + glWindowHUD.addGLEventListener(demoHUD); + } + if( 0 != ( ( GLMediaEventListener.EVENT_CHANGE_ERR | GLMediaEventListener.EVENT_CHANGE_EOS ) & event_mask ) ) { + final StreamException se = mPlayerMain.getStreamException(); + if( null != se ) { + se.printStackTrace(); + } + new Thread() { + public void run() { + glWindowHUD.destroy(); + } }.start(); } - } - glWindowHUD.setPosition(x2, y2); - glWindowHUD.setSize(w2, h2); - System.err.println("HUD: "+mPlayerHUD); - System.err.println("HUD: "+w2+"x"+h2); - glWindowHUD.addMouseListener(toFrontMouseListener); + } + }); + demoHUD.initStream(streamLoc1, GLMediaPlayer.STREAM_ID_AUTO, GLMediaPlayer.STREAM_ID_AUTO, 0); + + glWindowHUD.setPosition(x2, y2); + glWindowHUD.setSize(w2, h2); + System.err.println("HUD: "+mPlayerHUD); + System.err.println("HUD: "+w2+"x"+h2); + glWindowHUD.addMouseListener(toFrontMouseListener); - viewGroup.post(new Runnable() { - public void run() { - final android.view.View androidView = ((jogamp.newt.driver.android.WindowDriver)glWindowHUD.getDelegatedWindow()).getAndroidView(); - // addContentView(getWindow(), glWindowHUD, new android.view.ViewGroup.LayoutParams(glWindowHUD.getWidth(), glWindowHUD.getHeight())); - viewGroup.addView(androidView, new android.widget.FrameLayout.LayoutParams(glWindowHUD.getWidth(), glWindowHUD.getHeight(), Gravity.TOP|Gravity.LEFT)); - registerNEWTWindow(glWindowHUD); - animator.add(glWindowHUD); - glWindowHUD.setVisible(true); - } } ); - return true; - } } ); - } - - animator.setUpdateFPSFrames(60, System.err); - // animator.setUpdateFPSFrames(-1, null); - animator.resetFPSCounter(); - } catch (IOException e) { - e.printStackTrace(); + viewGroup.post(new Runnable() { + public void run() { + final android.view.View androidView = ((jogamp.newt.driver.android.WindowDriver)glWindowHUD.getDelegatedWindow()).getAndroidView(); + // addContentView(getWindow(), glWindowHUD, new android.view.ViewGroup.LayoutParams(glWindowHUD.getWidth(), glWindowHUD.getHeight())); + viewGroup.addView(androidView, new android.widget.FrameLayout.LayoutParams(glWindowHUD.getWidth(), glWindowHUD.getHeight(), Gravity.TOP|Gravity.LEFT)); + registerNEWTWindow(glWindowHUD); + anim.add(glWindowHUD); + glWindowHUD.setVisible(true); + } } ); + return true; + } } ); } scrn.removeReference(); 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 a9c200943..c48c53189 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 @@ -1,34 +1,29 @@ -/* - * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. */ package com.jogamp.opengl.test.junit.jogl.demos.es2.av; @@ -58,38 +53,55 @@ import com.jogamp.opengl.test.junit.util.UITestCase; import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.av.GLMediaPlayer; import com.jogamp.opengl.util.av.GLMediaPlayer.GLMediaEventListener; +import com.jogamp.opengl.util.av.GLMediaPlayer.StreamException; import com.jogamp.opengl.util.av.GLMediaPlayerFactory; 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 { - static boolean waitForKey = false; - int textureCount = 3; // default - threaded - final URI streamLoc; - final int vid, aid; - final float zoom0, rotx, roty; - TextureSequenceCubeES2 cube=null; - GLMediaPlayer mPlayer=null; + private static boolean waitForKey = false; + private final float zoom0, rotx, roty; + private TextureSequenceCubeES2 cube=null; + private GLMediaPlayer mPlayer=null; + private int swapInterval = 1; + private long lastPerfPos = 0; + /** Blender's Big Buck Bunny Trailer: 24f 640p VP8, Vorbis 44100Hz mono, WebM/Matroska Stream. */ + public static final URI defURI; + static { + URI _defURI = null; + try { + _defURI = new URI("http://video.webmfiles.org/big-buck-bunny_trailer.webm"); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + defURI = _defURI; + } + public MovieCube() throws IOException, URISyntaxException { - this(new URI("http://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_320x180.mp4"), - GLMediaPlayer.STREAM_ID_AUTO, GLMediaPlayer.STREAM_ID_AUTO, -2.3f, 0f, 0f); + this(-2.3f, 0f, 0f); } - public MovieCube(URI streamLoc, int vid, int aid, float zoom0, float rotx, float roty) throws IOException { - this.streamLoc = streamLoc; + public MovieCube(float zoom0, float rotx, float roty) throws IOException { this.zoom0 = zoom0; this.rotx = rotx; this.roty = roty; - this.vid = vid; - this.aid = aid; mPlayer = GLMediaPlayerFactory.createDefault(); mPlayer.addEventListener(this); } - public void setTextureCount(int v) { - textureCount = v; + 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); } + public void setSwapInterval(int v) { this.swapInterval = v; } + + public GLMediaPlayer getGLMediaPlayer() { return mPlayer; } + private final KeyListener keyAction = new KeyAdapter() { public void keyReleased(KeyEvent e) { if( e.isAutoRepeat() ) { @@ -149,7 +161,7 @@ public class MovieCube implements GLEventListener, GLMediaEventListener { }; @Override - public void attributesChanges(GLMediaPlayer mp, int event_mask, long when) { + public void attributesChanged(GLMediaPlayer mp, int event_mask, long when) { System.out.println("attributesChanges: "+mp+", 0x"+Integer.toHexString(event_mask)+", when "+when); } @@ -160,6 +172,16 @@ public class MovieCube implements GLEventListener, GLMediaEventListener { @Override public void init(GLAutoDrawable drawable) { + if(null == mPlayer) { + throw new InternalError("mPlayer null"); + } + if( GLMediaPlayer.State.Initialized != mPlayer.getState() ) { + 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; + } GL2ES2 gl = drawable.getGL().getGL2ES2(); System.err.println(JoglVersion.getGLInfo(gl, null)); @@ -168,19 +190,20 @@ public class MovieCube implements GLEventListener, GLMediaEventListener { if(waitForKey) { UITestCase.waitForKey("Init>"); } + try { - mPlayer.initGLStream(gl, textureCount, streamLoc, vid, aid); - } catch (Exception e) { - e.printStackTrace(); + mPlayer.initGL(gl); + } catch (Exception e) { + e.printStackTrace(); if(null != mPlayer) { mPlayer.destroy(gl); mPlayer = null; } throw new GLException(e); } - cube.init(drawable); mPlayer.play(); + System.out.println("pStart "+mPlayer); boolean added; final Object upstreamWidget = drawable.getUpstreamWidget(); @@ -194,6 +217,10 @@ public class MovieCube implements GLEventListener, GLMediaEventListener { @Override public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + final GL2ES2 gl = drawable.getGL().getGL2ES2(); + if(-1 != swapInterval) { + gl.setSwapInterval(swapInterval); // in case switching the drawable (impl. may bound attribute there) + } if(null == mPlayer) { return; } cube.reshape(drawable, x, y, width, height); } @@ -209,8 +236,6 @@ public class MovieCube implements GLEventListener, GLMediaEventListener { cube=null; } - long lastPerfPos = 0; - @Override public void display(GLAutoDrawable drawable) { if(null == mPlayer) { return; } @@ -225,6 +250,7 @@ public class MovieCube implements GLEventListener, GLMediaEventListener { } public static void main(String[] args) throws IOException, InterruptedException, URISyntaxException { + int swapInterval = 1; int width = 510; int height = 300; int textureCount = 3; // default - threaded @@ -237,7 +263,7 @@ public class MovieCube implements GLEventListener, GLMediaEventListener { int aid = GLMediaPlayer.STREAM_ID_AUTO; final boolean origSize; - String url_s="http://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_320x180.mp4"; + String url_s=null; { boolean _origSize = false; for(int i=0; i<args.length; i++) { @@ -269,20 +295,32 @@ public class MovieCube implements GLEventListener, GLMediaEventListener { forceGL3 = true; } else if(args[i].equals("-gldef")) { forceGLDef = true; + } else if(args[i].equals("-vsync")) { + i++; + swapInterval = MiscUtils.atoi(args[i], swapInterval); } else if(args[i].equals("-wait")) { waitForKey = true; } } origSize = _origSize; } + final URI streamLoc; + if( null == url_s ) { + streamLoc = defURI; + } else { + streamLoc = new URI(url_s); + } + System.err.println("stream "+streamLoc); System.err.println("vid "+vid+", aid "+aid); System.err.println("textureCount "+textureCount); System.err.println("forceES2 "+forceES2); System.err.println("forceES3 "+forceES3); System.err.println("forceGL3 "+forceGL3); System.err.println("forceGLDef "+forceGLDef); + System.err.println("swapInterval "+swapInterval); - final MovieCube mc = new MovieCube(new URI(url_s), vid, aid, -2.3f, 0f, 0f); + final MovieCube mc = new MovieCube(-2.3f, 0f, 0f); + mc.setSwapInterval(swapInterval); final GLProfile glp; if(forceGLDef) { @@ -298,9 +336,15 @@ public class MovieCube implements GLEventListener, GLMediaEventListener { } System.err.println("GLProfile: "+glp); final GLWindow window = GLWindow.create(new GLCapabilities(glp)); - // Size OpenGL to Video Surface + final Animator anim = new Animator(window); + window.addWindowListener(new WindowAdapter() { + public void windowDestroyed(WindowEvent e) { + anim.stop(); + } + }); window.setSize(width, height); - window.addGLEventListener(mc); + window.setVisible(true); + anim.start(); mc.mPlayer.addEventListener(new GLMediaEventListener() { @Override @@ -308,22 +352,30 @@ public class MovieCube implements GLEventListener, GLMediaEventListener { } @Override - public void attributesChanges(final GLMediaPlayer mp, int event_mask, long when) { + 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); 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(mc); + anim.setUpdateFPSFrames(60, System.err); + anim.resetFPSCounter(); + } + if( 0 != ( ( GLMediaEventListener.EVENT_CHANGE_ERR | GLMediaEventListener.EVENT_CHANGE_EOS ) & event_mask ) ) { + final StreamException se = mc.mPlayer.getStreamException(); + if( null != se ) { + se.printStackTrace(); + } + new Thread() { + public void run() { + window.destroy(); + } }.start(); + } } - }); - - final Animator anim = new Animator(window); - window.addWindowListener(new WindowAdapter() { - public void windowDestroyed(WindowEvent e) { - anim.stop(); - } - }); - window.setVisible(true); - anim.setUpdateFPSFrames(60, System.err); - anim.start(); + }); + mc.initStream(streamLoc, vid, aid, textureCount); } } 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 9d91ce8c8..ecf95f069 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 @@ -62,6 +62,7 @@ import com.jogamp.opengl.util.GLArrayDataServer; import com.jogamp.opengl.util.PMVMatrix; import com.jogamp.opengl.util.av.GLMediaPlayer; import com.jogamp.opengl.util.av.GLMediaPlayer.GLMediaEventListener; +import com.jogamp.opengl.util.av.GLMediaPlayer.StreamException; import com.jogamp.opengl.util.av.GLMediaPlayerFactory; import com.jogamp.opengl.util.glsl.ShaderCode; import com.jogamp.opengl.util.glsl.ShaderProgram; @@ -72,12 +73,15 @@ import com.jogamp.opengl.util.texture.TextureSequence; import com.jogamp.opengl.util.texture.TextureSequence.TextureFrame; /** - * + * Simple planar movie player w/ orthogonal 1:1 projection. */ public class MovieSimple implements GLEventListener, GLMediaEventListener { - static boolean waitForKey = false; + public static final int EFFECT_NORMAL = 0; + public static final int EFFECT_GRADIENT_BOTTOM2TOP = 1<<1; + public static final int EFFECT_TRANSPARENT = 1<<3; + + private static boolean waitForKey = false; private int winWidth, winHeight; - int textureCount = 3; // default - threaded private int prevMouseX; // , prevMouseY; private int rotate = 0; private boolean orthoProjection = true; @@ -88,30 +92,32 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { private long startTime; private int effects = EFFECT_NORMAL; private float alpha = 1.0f; - - public static final int EFFECT_NORMAL = 0; - public static final int EFFECT_GRADIENT_BOTTOM2TOP = 1<<1; - public static final int EFFECT_TRANSPARENT = 1<<3; - - /** defaults to true */ - public void setOrthoProjection(boolean v) { orthoProjection=v; } - public boolean getOrthoProjection() { return orthoProjection; } + private int swapInterval = 1; + + private GLMediaPlayer mPlayer; + private boolean mPlayerExternal; + private boolean mPlayerShared; + private boolean mPlayerScaleOrig; + private GLArrayDataServer interleavedVBO; + + private ShaderState st; + private PMVMatrix pmvMatrix; + private GLUniformData pmvMatrixUniform; + private static final String shaderBasename = "texsequence_xxx"; + private static final String myTextureLookupName = "myTexture2D"; + + /** Blender's Big Buck Bunny Trailer: 24f 640p VP8, Vorbis 44100Hz mono, WebM/Matroska Stream. */ + public static final URI defURI; + static { + URI _defURI = null; + try { + _defURI = new URI("http://video.webmfiles.org/big-buck-bunny_trailer.webm"); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + defURI = _defURI; + } - public boolean hasEffect(int e) { return 0 != ( effects & e ) ; } - public void setEffects(int e) { effects = e; }; - public void setTransparency(float alpha) { - this.effects |= EFFECT_TRANSPARENT; - this.alpha = alpha; - } - - GLMediaPlayer mPlayer; - final URI stream; - final int vid, aid; - boolean mPlayerExternal; - boolean mPlayerShared; - boolean mPlayerScaleOrig; - GLArrayDataServer interleavedVBO; - private final MouseListener mouseAction = new MouseAdapter() { public void mousePressed(MouseEvent e) { if(e.getY()<=winHeight/2 && null!=mPlayer && 1 == e.getClickCount()) { @@ -215,16 +221,12 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { } }; - public MovieSimple(URI streamLoc, int vid, int aid) throws IOException { + public MovieSimple() { mPlayerScaleOrig = false; mPlayerShared = false; mPlayerExternal = false; mPlayer = GLMediaPlayerFactory.createDefault(); - mPlayer.addEventListener(this); - this.stream = streamLoc; - this.vid = vid; - this.aid = aid; - System.out.println("pC.1 "+mPlayer); + System.out.println("pC.1a "+mPlayer); } public MovieSimple(GLMediaPlayer sharedMediaPlayer) throws IllegalStateException { @@ -233,23 +235,36 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { mPlayerExternal = true; mPlayer = sharedMediaPlayer; mPlayer.addEventListener(this); - this.stream = null; - this.vid = sharedMediaPlayer.getVID(); - this.aid = sharedMediaPlayer.getAID(); 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); + } + + public void setSwapInterval(int v) { this.swapInterval = v; } + public GLMediaPlayer getGLMediaPlayer() { return mPlayer; } - public void setTextureCount(int v) { - textureCount = v; - } public void setScaleOrig(boolean v) { mPlayerScaleOrig = v; } + /** defaults to true */ + public void setOrthoProjection(boolean v) { orthoProjection=v; } + public boolean getOrthoProjection() { return orthoProjection; } + + public boolean hasEffect(int e) { return 0 != ( effects & e ) ; } + public void setEffects(int e) { effects = e; }; + public void setTransparency(float alpha) { + this.effects |= EFFECT_TRANSPARENT; + this.alpha = alpha; + } + @Override - public void attributesChanges(GLMediaPlayer mp, int event_mask, long when) { + public void attributesChanged(GLMediaPlayer mp, int event_mask, long when) { System.out.println("attributesChanges: "+mp+", 0x"+Integer.toHexString(event_mask)+", when "+when); } @@ -258,19 +273,6 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { // System.out.println("newFrameAvailable: "+mp+", when "+when); } - public void play() { - if(null!=mPlayer) { - mPlayer.play(); - System.out.println("pStart "+mPlayer); - } - } - - ShaderState st; - PMVMatrix pmvMatrix; - GLUniformData pmvMatrixUniform; - static final String shaderBasename = "texsequence_xxx"; - static final String myTextureLookupName = "myTexture2D"; - private void initShader(GL2ES2 gl) { // Create & Compile the shader objects ShaderCode rsVp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, MovieSimple.class, @@ -307,6 +309,16 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { @Override public void init(GLAutoDrawable drawable) { + if(null == mPlayer) { + throw new InternalError("mPlayer null"); + } + if( GLMediaPlayer.State.Initialized != mPlayer.getState() ) { + 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; + } zoom0 = orthoProjection ? 0f : -2.5f; zoom1 = orthoProjection ? 0f : -5f; zoom = zoom0; @@ -324,7 +336,7 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { try { System.out.println("p0 "+mPlayer+", shared "+mPlayerShared); if(!mPlayerShared) { - mPlayer.initGLStream(gl, textureCount, stream, vid, aid); + mPlayer.initGL(gl); } tex = mPlayer.getLastTexture().getTexture(); System.out.println("p1 "+mPlayer+", shared "+mPlayerShared); @@ -405,9 +417,9 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { final FloatBuffer ib = (FloatBuffer)interleavedVBO.getBuffer(); final TextureCoords tc = tex.getImageTexCoords(); final float aspect = tex.getAspectRatio(); + System.err.println("XXX0: "+tc); System.err.println("XXX0: tex aspect: "+aspect); System.err.println("XXX0: tex y-flip: "+tex.getMustFlipVertically()); - System.err.println("XXX0: "+tex.getImageTexCoords()); // left-bottom ib.put(verts[0]); ib.put(verts[1]); ib.put(verts[2]); @@ -458,11 +470,10 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { System.out.println("iVBO: "+interleavedVBO); System.out.println(st); - if(null!=mPlayer) { - play(); - System.out.println("p2 "+mPlayer); - } - + if(!mPlayerShared) { + mPlayer.play(); + System.out.println("pStart "+mPlayer); + } startTime = System.currentTimeMillis(); final Object upstreamWidget = drawable.getUpstreamWidget(); @@ -477,13 +488,19 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { @Override public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + final GL2ES2 gl = drawable.getGL().getGL2ES2(); + if(-1 != swapInterval) { + 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; if(null != st) { reshapePMV(width, height); - GL2ES2 gl = drawable.getGL().getGL2ES2(); st.useProgram(gl, true); st.uniform(gl, pmvMatrixUniform); st.useProgram(gl, false); @@ -548,6 +565,10 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { lastPerfPos = currentPos; } + if( GLMediaPlayer.STREAM_ID_NONE == mPlayer.getVID() ) { + return; + } + GL2ES2 gl = drawable.getGL().getGL2ES2(); gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); @@ -575,7 +596,7 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { if(mPlayerShared) { texFrame=mPlayer.getLastTexture(); } else { - texFrame=mPlayer.getNextTexture(gl, true); + texFrame=mPlayer.getNextTexture(gl); } if(null != texFrame) { tex = texFrame.getTexture(); @@ -593,6 +614,7 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { } public static void main(String[] args) throws IOException, URISyntaxException { + int swapInterval = 1; int width = 640; int height = 600; int textureCount = 3; // default - threaded @@ -607,7 +629,7 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { int aid = GLMediaPlayer.STREAM_ID_AUTO; final boolean origSize; - String url_s="http://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_320x180.mp4"; + String url_s=null; { boolean _origSize = false; for(int i=0; i<args.length; i++) { @@ -636,6 +658,9 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { forceGL3 = true; } else if(args[i].equals("-gldef")) { forceGLDef = true; + } else if(args[i].equals("-vsync")) { + i++; + swapInterval = MiscUtils.atoi(args[i], swapInterval); } else if(args[i].equals("-projection")) { ortho=false; } else if(args[i].equals("-zoom")) { @@ -649,16 +674,23 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { } origSize = _origSize; } + final URI streamLoc; + if( null == url_s ) { + streamLoc = defURI; + } else { + streamLoc = new URI(url_s); + } + System.err.println("stream "+streamLoc); System.err.println("vid "+vid+", aid "+aid); System.err.println("textureCount "+textureCount); System.err.println("forceES2 "+forceES2); System.err.println("forceES3 "+forceES3); System.err.println("forceGL3 "+forceGL3); System.err.println("forceGLDef "+forceGLDef); + System.err.println("swapInterval "+swapInterval); - final URI streamURI = new URI(url_s); - final MovieSimple ms = new MovieSimple(streamURI, vid, aid); - ms.setTextureCount(textureCount); + final MovieSimple ms = new MovieSimple(); + ms.setSwapInterval(swapInterval); ms.setScaleOrig(!zoom); ms.setOrthoProjection(ortho); @@ -678,31 +710,46 @@ public class MovieSimple implements GLEventListener, GLMediaEventListener { System.err.println("GLProfile: "+glp); GLCapabilities caps = new GLCapabilities(glp); final GLWindow window = GLWindow.create(caps); + final Animator anim = new Animator(window); + window.addWindowListener(new WindowAdapter() { + public void windowDestroyed(WindowEvent e) { + anim.stop(); + } + }); + window.setSize(width, height); + window.setVisible(true); + anim.start(); - window.addGLEventListener(ms); ms.mPlayer.addEventListener(new GLMediaEventListener() { @Override public void newFrameAvailable(GLMediaPlayer ts, TextureFrame newFrame, long when) { } @Override - public void attributesChanges(final GLMediaPlayer mp, int event_mask, long when) { + 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); if( 0 != ( GLMediaEventListener.EVENT_CHANGE_SIZE & event_mask ) && origSize ) { window.setSize(mp.getWidth(), mp.getHeight()); } - } - }); - - window.setSize(width, height); - window.setVisible(true); - final Animator anim = new Animator(window); - anim.setUpdateFPSFrames(60, System.err); - anim.start(); - window.addWindowListener(new WindowAdapter() { - public void windowDestroyed(WindowEvent e) { - anim.stop(); - } + if( 0 != ( GLMediaEventListener.EVENT_CHANGE_INIT & event_mask ) ) { + window.addGLEventListener(ms); + anim.setUpdateFPSFrames(60, System.err); + anim.resetFPSCounter(); + } + 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(); + } + } }); + ms.initStream(streamLoc, vid, aid, textureCount); } catch (Throwable t) { t.printStackTrace(); } |