diff options
author | Sven Gothel <[email protected]> | 2012-04-02 08:36:38 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2012-04-02 08:36:38 +0200 |
commit | 5911b729b69b7fb327e441b33f22eb1ef31a03df (patch) | |
tree | 21d91c7d84bc99e6341029e9d233d37f5a1adc23 /src | |
parent | de2b129a56335262a44a05541a3ab2e35668cc6e (diff) |
Initial commit for AudioVideo (com.jogamp.opengl.av) rework, introducing Android API 14 MediaPlayer impl of GLMediaPlayer.
Android API 14 MediaPlayer allows usage of OMX AL direct decode to texture via libstagefright (OMX AL usage included).
Status: Untested, not working - Need to fix native OMX IL (stream detect and split) and/or GStreamer implementation.
Diffstat (limited to 'src')
15 files changed, 1409 insertions, 545 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/av/GLMediaEventListener.java b/src/jogl/classes/com/jogamp/opengl/av/GLMediaEventListener.java new file mode 100644 index 000000000..a02c8a362 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/av/GLMediaEventListener.java @@ -0,0 +1,17 @@ + +package com.jogamp.opengl.av; + +import com.jogamp.opengl.av.GLMediaPlayer.TextureFrame; + +public interface GLMediaEventListener { + + static final int EVENT_CHANGE_SIZE = 1<<0; + static final int EVENT_CHANGE_FPS = 1<<1; + static final int EVENT_CHANGE_BPS = 1<<2; + static final int EVENT_CHANGE_LENGTH = 1<<3; + + public void attributesChanges(GLMediaPlayer mp, int event_mask); + public void newFrameAvailable(GLMediaPlayer mp, TextureFrame frame); + +} + diff --git a/src/jogl/classes/com/jogamp/opengl/av/GLMediaPlayer.java b/src/jogl/classes/com/jogamp/opengl/av/GLMediaPlayer.java new file mode 100644 index 000000000..71e0e16d9 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/av/GLMediaPlayer.java @@ -0,0 +1,90 @@ +package com.jogamp.opengl.av; + +import java.io.IOException; +import java.net.URL; + +import javax.media.opengl.GL; + +import com.jogamp.opengl.util.texture.Texture; + +/** + * Lifecycle of an GLMediaPlayer: + * <ul> + * <li>{@link #setStream(GL, URL)}</li> + * <li>{@link #start()}</li> + * <li>{@link #stop()}</li> + * <li>{@link #destroy(GL)}</li> + * </ul> + */ +public interface GLMediaPlayer { + + public static class TextureFrame { + public TextureFrame(Texture t) { + texture = t; + } + + public final Texture getTexture() { return texture; } + + public String toString() { + return "TextureFrame[" + texture + "]"; + } + protected final Texture texture; + } + + /** Sets the stream to be used. Initializes all stream related states and GL resources. */ + public void setStream(GL gl, URL url) throws IOException; + + /** Releases the GL and stream resources. */ + public void destroy(GL gl); + + public void setPlaySpeed(float rate); + + public float getPlaySpeed(); + + public void start(); + + public void pause(); + + public void stop(); + + /** + * @return time current position in milliseconds + **/ + public int getCurrentPosition(); + + /** + * @param msec absolute desired time position in milliseconds + * @return time current position in milliseconds, after seeking to the desired position + **/ + public int seek(int msec); + + public Texture getLastTextureID(); + + public Texture getNextTextureID(); + + public boolean isValid(); + + public URL getURL(); + + public String getVideoCodec(); + + public String getAudioCodec(); + + public long getTotalFrames(); + + public long getBitrate(); + + public int getFramerate(); + + public int getWidth(); + + public int getHeight(); + + public String toString(); + + public void addEventListener(GLMediaEventListener l); + + public void removeEventListener(GLMediaEventListener l); + + public GLMediaEventListener[] getEventListeners(); +} diff --git a/src/jogl/classes/com/jogamp/opengl/av/GLMediaPlayerFactory.java b/src/jogl/classes/com/jogamp/opengl/av/GLMediaPlayerFactory.java new file mode 100644 index 000000000..0025a70ba --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/av/GLMediaPlayerFactory.java @@ -0,0 +1,18 @@ +package com.jogamp.opengl.av; + +import com.jogamp.common.os.AndroidVersion; +import com.jogamp.common.os.Platform; +import com.jogamp.common.util.ReflectionUtil; + +public class GLMediaPlayerFactory { + private static final String AndroidGLMediaPlayerAPI14ClazzName = "jogamp.opengl.android.av.AndroidGLMediaPlayerAPI14"; + + public static GLMediaPlayer create() { + if(Platform.OS_TYPE.equals(Platform.OSType.ANDROID)) { + if(AndroidVersion.SDK_INT >= 14) { + return (GLMediaPlayer) ReflectionUtil.createInstance(AndroidGLMediaPlayerAPI14ClazzName, GLMediaPlayerFactory.class.getClassLoader()); + } + } + return null; + } +} diff --git a/src/jogl/classes/com/jogamp/openmax/OMXEventListener.java b/src/jogl/classes/com/jogamp/openmax/OMXEventListener.java deleted file mode 100644 index 08cfeccde..000000000 --- a/src/jogl/classes/com/jogamp/openmax/OMXEventListener.java +++ /dev/null @@ -1,14 +0,0 @@ - -package com.jogamp.openmax; - -public interface OMXEventListener { - - static final int EVENT_CHANGE_SIZE = 1<<0; - static final int EVENT_CHANGE_FPS = 1<<1; - static final int EVENT_CHANGE_BPS = 1<<2; - static final int EVENT_CHANGE_LENGTH = 1<<3; - - public void changedAttributes(OMXInstance omx, int event_mask); - -} - diff --git a/src/jogl/classes/com/jogamp/openmax/OMXInstance.java b/src/jogl/classes/com/jogamp/openmax/OMXInstance.java deleted file mode 100644 index 7c373a0ef..000000000 --- a/src/jogl/classes/com/jogamp/openmax/OMXInstance.java +++ /dev/null @@ -1,509 +0,0 @@ - -package com.jogamp.openmax; - -import javax.media.opengl.*; -import javax.media.opengl.glu.GLU; -import com.jogamp.opengl.util.texture.*; - -import jogamp.opengl.egl.EGL; -import jogamp.opengl.egl.EGLContext; -import jogamp.opengl.egl.EGLDrawable; -import jogamp.opengl.egl.EGLExt; - -import java.net.URL; -import java.nio.ByteBuffer; -import java.io.File; -import java.io.FileNotFoundException; -import java.util.*; - -public class OMXInstance { - private long moviePtr = 0; - - protected String path = null; - protected URL url = null; - - // *** Texture impl - protected Texture texture = null; // holds the last fetched texture - - protected float playSpeed = 1.0f; - - /** - * The following data is set by the setStream function, - * and may be set by the native OMX implementation, - * in case the stream attributes changes (see attributesUpdated) - */ - protected int width = 0; - protected int height = 0; - protected int fps = 0; // frames per seconds - protected long bps = 0; // bits per seconds - protected long totalFrames = 0; // duration in frames - protected String acodec = null; - protected String vcodec = null; - - /** - * Old stream values, before the last attributesUpdated) - */ - protected int o_width = 0; - protected int o_height = 0; - protected int o_fps = 0; // frames per seconds - protected long o_bps = 0; // bits per seconds - protected long o_totalFrames = 0; // duration in frames - - static class EGLImageTexture { - public EGLImageTexture(com.jogamp.opengl.util.texture.Texture t, long i, long s) { - texture = t; image = i; sync = s; - } - public String toString() { - return "EGLImageTexture[" + texture + ", image " + image + ", sync "+sync+"]"; - } - protected com.jogamp.opengl.util.texture.Texture texture; - protected long image; - protected long sync; - } - private EGLImageTexture[] eglImgTexs=null; - private HashMap eglImgTexsMap = new HashMap(); - protected int textureNum; - - private EGLExt eglExt = null; - private long eglSurface = 0; - private long eglDisplay = 0; - private long eglContext = 0; - private int sWidth=0, sHeight=0; - - private GL initGLData(GL gl) { - if(null==gl) { - throw new RuntimeException("No current GL"); - } - EGLContext eglCtx = (EGLContext) gl.getContext(); - if(null==eglCtx) { - throw new RuntimeException("No current EGL context"); - } - EGLDrawable eglDrawable = (EGLDrawable) eglCtx.getGLDrawable(); - if(null==eglDrawable) { - throw new RuntimeException("No valid drawable"); - } - eglContext = eglCtx.getHandle(); - eglDisplay = eglDrawable.getDisplay(); - eglSurface = eglDrawable.getHandle(); - eglExt = eglCtx.getEGLExt(); - if(null==eglExt) { - throw new RuntimeException("No valid EGLExt"); - } - - int iTmp[] = new int[1]; - EGL.eglQuerySurface(eglDisplay, eglSurface, EGL.EGL_WIDTH, iTmp, 0); - sWidth=iTmp[0]; - EGL.eglQuerySurface(eglDisplay, eglSurface, EGL.EGL_HEIGHT, iTmp, 0); - sHeight=iTmp[0]; - System.out.println("surface size: "+width+"x"+height); - System.out.println(eglDrawable); - System.out.println(eglCtx); - System.out.println("EGL Extensions : "+EGL.eglQueryString(eglDisplay, EGL.EGL_EXTENSIONS)); - System.out.println("EGL CLIENT APIs: "+EGL.eglQueryString(eglDisplay, EGL.EGL_CLIENT_APIS)); - return gl; - } - - public OMXInstance() { - moviePtr = _createInstance(); - if(0==moviePtr) { - throw new GLException("Couldn't create OMXInstance"); - } - } - native long _createInstance(); - - public synchronized void setStream(int textureNum, URL u) { - if(0==moviePtr) { - throw new GLException("OMX native instance null"); - } - this.textureNum=textureNum; - url = u; - if (url == null) { - System.out.println("setURL (null)"); - stop(); - return; - } - path=null; - if (url.getProtocol() == null || "file".equals(url.getProtocol())) { - // CV only accepts absolute paths - try { - File file = new File(url.getPath()); - if (!file.exists()) { - throw new RuntimeException(new FileNotFoundException(file.toString())); - } - path = file.getCanonicalPath(); - System.out.println("setURL: path "+path); - } catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - } - path = replaceAll(path, "\\", "/").trim(); - if(null==path) { - throw new RuntimeException("Couldn't parse stream URL: "+url); - } - System.out.println("setURL: clean path "+path); - - System.out.println("setURL: p1 "+this); - _setStream(moviePtr, textureNum, path); - System.out.println("setURL: p2 "+this); - } - native void _setStream(long moviePtr, int textureNum, String path); - - public synchronized void setStreamAllEGLImageTexture2D(GL gl) { - if(0==moviePtr) { - throw new GLException("OMX native instance null"); - } - if(null==vcodec) { - return; - } - gl = initGLData(gl); - - if(null!=eglImgTexs) { - removeAllEGLImageTexture2D(gl); - } else { - eglImgTexs = new EGLImageTexture[textureNum]; - } - - int[] tmp = new int[1]; - int tex, e; - - errorCheckGL(gl, "i.1"); - gl.glEnable(gl.GL_TEXTURE_2D); - errorCheckGL(gl, "i.2"); - - for(int i=0; i<textureNum; i++) { - String s0 = String.valueOf(i); - gl.glGenTextures(1, tmp, 0); - tex=tmp[0]; - if( (e=gl.glGetError()) != GL.GL_NO_ERROR || 0>tex ) { - throw new RuntimeException("TextureName creation failed: "+e); - } - gl.glBindTexture(gl.GL_TEXTURE_2D, tex); - - // create space for buffer with a texture - gl.glTexImage2D( - gl.GL_TEXTURE_2D, // target - 0, // level - gl.GL_RGBA, // internal format - width, // width - height, // height - 0, // border - gl.GL_RGBA, // format - gl.GL_UNSIGNED_BYTE, // type - null); // pixels -- will be provided later - gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_NEAREST); - gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_NEAREST); - - long image=0, sync=0; - - // create EGLImage from texture - tmp[0] = EGL.EGL_NONE; - image = eglExt.eglCreateImageKHR( - eglDisplay, - eglContext, - eglExt.EGL_GL_TEXTURE_2D_KHR, - tex, - tmp, 0); - if (0==image) { - throw new RuntimeException("EGLImage creation failed: "+EGL.eglGetError()+", dpy "+eglDisplay+", ctx "+eglContext+", tex "+tex); - } - - // Create sync object so that we can be sure that gl has finished - // rendering the EGLImage texture before we tell OpenMAX to fill - // it with a new frame. - tmp[0] = EGL.EGL_NONE; - sync = eglExt.eglCreateSyncKHR( - eglDisplay, - eglExt.EGL_SYNC_FENCE_KHR, tmp, 0); - - _setStreamEGLImageTexture2D(moviePtr, i, tex, image, sync); - - eglImgTexs[i] = new EGLImageTexture( - com.jogamp.opengl.util.texture.TextureIO.newTexture(tex, - javax.media.opengl.GL2.GL_TEXTURE_2D, - width, - height, - width, - height, - true), - image, sync); - eglImgTexsMap.put(new Integer(tex), eglImgTexs[i]); - } - gl.glDisable(gl.GL_TEXTURE_2D); - } - native void _setStreamEGLImageTexture2D(long moviePtr, int i, int tex, long image, long sync); - - public synchronized void activateStream() { - if(0==moviePtr) { - throw new GLException("OMX native instance null"); - } - _activateStream(moviePtr); - } - native void _activateStream(long moviePtr); - - public synchronized void detachVideoRenderer() { - if(0==moviePtr) { - throw new GLException("OMX native instance null"); - } - _detachVideoRenderer(moviePtr); - } - native void _detachVideoRenderer(long moviePtr); // stop before - - public synchronized void attachVideoRenderer() { - if(0==moviePtr) { - throw new GLException("OMX native instance null"); - } - _attachVideoRenderer(moviePtr); - } - native void _attachVideoRenderer(long moviePtr); // detach before - - public synchronized void setPlaySpeed(float rate) { - if(0==moviePtr) { - throw new GLException("OMX native instance null"); - } - _setPlaySpeed(moviePtr, rate); - playSpeed = rate; - } - public synchronized float getPlaySpeed() { - return playSpeed; - } - native void _setPlaySpeed(long moviePtr, float rate); - - /** @return time position after issuing the command */ - public synchronized float play() { - if(0==moviePtr) { - throw new GLException("OMX native instance null"); - } - return _play(moviePtr); - } - native float _play(long moviePtr); - - /** @return time position after issuing the command */ - public synchronized float pause() { - if(0==moviePtr) { - throw new GLException("OMX native instance null"); - } - return _pause(moviePtr); - } - native float _pause(long moviePtr); - - /** @return time position after issuing the command */ - public synchronized float stop() { - if(0==moviePtr) { - throw new GLException("OMX native instance null"); - } - return _stop(moviePtr); - } - native float _stop(long moviePtr); - - /** @return time position after issuing the command */ - public synchronized float seek(float pos) { - if(0==moviePtr) { - throw new GLException("OMX native instance null"); - } - return _seek(moviePtr, pos); - } - native float _seek(long moviePtr, float position); - - public synchronized Texture getLastTextureID() { - return texture; - } - public synchronized Texture getNextTextureID() { - if(0==moviePtr) { - throw new GLException("OMX native instance null"); - } - texture=null; - EGLImageTexture eglImgTex = (EGLImageTexture) eglImgTexsMap.get(new Integer(_getNextTextureID(moviePtr))); - if(null!=eglImgTex) { - texture = eglImgTex.texture; - } - return texture; - } - native int _getNextTextureID(long moviePtr); - - public synchronized float getCurrentPosition() { - if(0==moviePtr) { - throw new GLException("OMX native instance null"); - } - return _getCurrentPosition(moviePtr); - } - native float _getCurrentPosition(long moviePtr); - - public synchronized void destroy(GL gl) { - removeAllEGLImageTexture2D(gl); - if (moviePtr != 0) { - long ptr = moviePtr; - moviePtr = 0; - _destroyInstance(ptr); - - eglExt=null; - eglSurface=0; - eglDisplay=0; - eglContext=0; - } - } - protected synchronized void finalize() { - if (moviePtr != 0) { - destroy(null); - } - } - native void _destroyInstance(long moviePtr); - - public synchronized boolean isValid() { - return (moviePtr != 0); - } - public synchronized String getPath() { - return path; - } - public synchronized URL getURL() { - return url; - } - public synchronized String getVideoCodec() { - return vcodec; - } - public synchronized String getAudioCodec() { - return acodec; - } - public synchronized long getTotalFrames() { - return totalFrames; - } - public synchronized long getBitrate() { - return bps; - } - public synchronized int getFramerate() { - return fps; - } - public synchronized int getWidth() { - return width; - } - public synchronized int getHeight() { - return height; - } - public synchronized String toString() { - return "OMXInstance [ stream [ video [ "+vcodec+", "+width+"x"+height+", "+fps+"fps, "+bps+"bsp, "+totalFrames+"f ] ] ]"; - } - - /** - * Java callback method issued by the native OMX backend - */ - private void saveAttributes() { - o_width = width; - o_height = height; - o_fps = fps; - o_bps = bps; - o_totalFrames = totalFrames; - } - - private void attributesUpdated() { - int event_mask = 0; - if( o_width != width || o_height != height ) { - event_mask |= OMXEventListener.EVENT_CHANGE_SIZE; - } - if( o_fps != fps ) { - event_mask |= OMXEventListener.EVENT_CHANGE_FPS; - } - if( o_bps != bps ) { - event_mask |= OMXEventListener.EVENT_CHANGE_BPS; - } - if( o_totalFrames != totalFrames ) { - event_mask |= OMXEventListener.EVENT_CHANGE_LENGTH; - } - if(0==event_mask) { - return; - } - - ArrayList listeners = null; - synchronized(this) { - listeners = eventListeners; - } - for(Iterator i = listeners.iterator(); i.hasNext(); ) { - OMXEventListener l = (OMXEventListener) i.next(); - l.changedAttributes(this, event_mask); - } - } - - private String replaceAll(String orig, String search, String repl) { - String dest=null; - // In case replaceAll / java.util.regex.* is not supported (-> CVM) - int i=0,j; - dest = new String(); - while((j=orig.indexOf(search, i))>=0) { - dest=dest.concat(orig.substring(i, j)); - dest=dest.concat(repl); - i=j+1; - } - return dest.concat(orig.substring(i, orig.length())); - } - - private void removeAllEGLImageTexture2D(GL gl) { - if (moviePtr != 0) { - if(null==eglExt) { - throw new RuntimeException("No valid EGLExt"); - } - - texture = null; - for(int i=0; i<textureNum; i++) { - if(null!=eglImgTexs[i]) { - if(0!=eglImgTexs[i].image) { - eglExt.eglDestroyImageKHR( - eglDisplay, - eglImgTexs[i].image); - } - if(0!=eglImgTexs[i].sync) { - eglExt.eglDestroySyncKHR(eglDisplay, eglImgTexs[i].sync); - } - if(null!=gl) { - eglImgTexs[i].texture.destroy(gl); - } - eglImgTexs[i]=null; - } - } - eglImgTexsMap.clear(); - } - } - - private void errorCheckGL(GL gl, String s) { - int e; - if( (e=gl.glGetError()) != GL.GL_NO_ERROR ) { - System.out.println("GL Error: ("+s+"): "+e); - } - } - - private void errorCheckEGL(String s) { - int e; - if( (e=EGL.eglGetError()) != EGL.EGL_SUCCESS ) { - System.out.println("EGL Error: ("+s+"): 0x"+Integer.toHexString(e)); - } - } - - - // - // OMXEventListener Support - // - - public synchronized void addEventListener(OMXEventListener l) { - if(l == null) { - return; - } - ArrayList newEventListeners = (ArrayList) eventListeners.clone(); - newEventListeners.add(l); - eventListeners = newEventListeners; - } - - public synchronized void removeEventListener(OMXEventListener l) { - if (l == null) { - return; - } - ArrayList newEventListeners = (ArrayList) eventListeners.clone(); - newEventListeners.remove(l); - eventListeners = newEventListeners; - } - - public synchronized OMXEventListener[] getEventListeners() { - return (OMXEventListener[]) eventListeners.toArray(); - } - - private ArrayList eventListeners = new ArrayList(); - -} - diff --git a/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java b/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java new file mode 100644 index 000000000..adfe2a048 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java @@ -0,0 +1,195 @@ +package jogamp.opengl.android.av; + +import java.io.IOException; +import java.net.URL; + +import javax.media.opengl.GL; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLES2; + +import jogamp.common.os.android.StaticContext; +import jogamp.opengl.av.GLMediaPlayerImpl; + +import android.graphics.SurfaceTexture; +import android.graphics.SurfaceTexture.OnFrameAvailableListener; +import android.media.MediaPlayer; +import android.net.Uri; +import android.view.Surface; + +import com.jogamp.opengl.av.GLMediaEventListener; +import com.jogamp.opengl.av.GLMediaPlayer; +import com.jogamp.opengl.av.GLMediaPlayer.TextureFrame; +import com.jogamp.opengl.util.PMVMatrix; +import com.jogamp.opengl.util.texture.Texture; +import com.jogamp.opengl.util.texture.TextureCoords; + +/*** + * Android API Level 14: {@link MediaPlayer#setSurface(Surface)} + * Android API Level 14: {@link Surface#Surface(android.graphics.SurfaceTexture)} + */ +public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl { + MediaPlayer mp; + boolean updateSurface = false; + + public static class AndroidTextureFrame extends TextureFrame { + public AndroidTextureFrame(Texture t, SurfaceTexture stex) { + super(t); + this.stex = stex; + } + + public final SurfaceTexture getSurfaceTexture() { return stex; } + + public String toString() { + return "AndroidTextureFrame[" + texture + ", "+ stex + "]"; + } + protected SurfaceTexture stex; + } + + public AndroidGLMediaPlayerAPI14() { + super(); + this.setTextureTarget(GLES2.GL_TEXTURE_EXTERNAL_OES); + this.setTextureCount(1); + mp = new MediaPlayer(); + } + + @Override + public void setPlaySpeed(float rate) { + // n/a + } + + @Override + public void start() { + mp.start(); + } + + @Override + public void pause() { + mp.pause(); + } + + @Override + public void stop() { + mp.stop(); + } + + @Override + public int seek(int msec) { + mp.seekTo(msec); + return mp.getCurrentPosition(); + } + + @Override + public Texture getNextTextureID() { + // TODO Auto-generated method stub + return null; + } + + @Override + public int getCurrentPosition() { + return mp.getCurrentPosition(); + } + + @Override + public boolean isValid() { + return true; + } + + AndroidTextureFrame androidTextureFrame = null; + + @Override + protected void destroyImpl(GL gl) { + mp.release(); + mp = null; + } + + @Override + protected void setStreamImpl() throws IOException { + try { + final Uri uri = Uri.parse(url.toExternalForm()); + mp.setDataSource(StaticContext.getContext(), uri); + } catch (IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (IllegalStateException e) { + throw new RuntimeException(e); + } + mp.prepare(); + + width = mp.getVideoWidth(); + height = mp.getVideoHeight(); + fps = 0; + bps = 0; + totalFrames = mp.getDuration(); + acodec = "unknown"; + vcodec = "unknown"; + } + + @Override + protected void destroyTexImage(GLContext ctx, TextureFrame imgTex) { + final AndroidTextureFrame atf = (AndroidTextureFrame) imgTex; + atf.getSurfaceTexture().release(); + super.destroyTexImage(ctx, imgTex); + } + + @Override + protected AndroidTextureFrame createTexImage(GLContext ctx, int idx, int[] tex) { + final GL gl = ctx.getGL(); + + if( 0 > tex[idx] ) { + throw new RuntimeException("TextureName "+toHexString(tex[idx])+" invalid."); + } + gl.glBindTexture(GLES2.GL_TEXTURE_EXTERNAL_OES, tex[idx]); + { + final int err = gl.glGetError(); + if( GL.GL_NO_ERROR != err ) { + throw new RuntimeException("Couldn't bind textureName "+toHexString(tex[idx])+" to 2D target, err "+toHexString(err)); + } + } + // gl.glTexParameterf(GLES2.GL_TEXTURE_EXTERNAL_OES, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); + // gl.glTexParameterf(GLES2.GL_TEXTURE_EXTERNAL_OES, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); + gl.glTexParameterf(GLES2.GL_TEXTURE_EXTERNAL_OES, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST); + gl.glTexParameterf(GLES2.GL_TEXTURE_EXTERNAL_OES, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST); + // Clamp to edge is only option. + gl.glTexParameteri(GLES2.GL_TEXTURE_EXTERNAL_OES, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE); + gl.glTexParameteri(GLES2.GL_TEXTURE_EXTERNAL_OES, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE); + + SurfaceTexture stex = new SurfaceTexture(tex[idx]); + stex.setOnFrameAvailableListener(onFrameAvailableListener); + + return new AndroidTextureFrame( com.jogamp.opengl.util.texture.TextureIO.newTexture(tex[idx], + GLES2.GL_TEXTURE_EXTERNAL_OES, + width, height, + width, height, + true), stex); + } + + protected OnFrameAvailableListener onFrameAvailableListener = new OnFrameAvailableListener() { + @Override + public void onFrameAvailable(SurfaceTexture surfaceTexture) { + frameNumber++; + updateSurface = true; + } + }; + + @Override + public float getPlaySpeed() { + // TODO Auto-generated method stub + return 0; + } + + private float[] mSTMatrix = new float[16]; + + @Override + public Texture getLastTextureID() { + if(updateSurface) { + androidTextureFrame.getSurfaceTexture().updateTexImage(); + androidTextureFrame.getSurfaceTexture().getTransformMatrix(mSTMatrix); + TextureCoords tc = androidTextureFrame.getTexture().getImageTexCoords(); + PMVMatrix pmv; + } + return androidTextureFrame.getTexture(); + } + +} diff --git a/src/jogl/classes/jogamp/opengl/av/EGLMediaPlayerImpl.java b/src/jogl/classes/jogamp/opengl/av/EGLMediaPlayerImpl.java new file mode 100644 index 000000000..ef3d5072c --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/av/EGLMediaPlayerImpl.java @@ -0,0 +1,109 @@ +package jogamp.opengl.av; + +import javax.media.opengl.GLContext; + +import com.jogamp.opengl.util.texture.Texture; + +import jogamp.opengl.egl.EGL; +import jogamp.opengl.egl.EGLContext; +import jogamp.opengl.egl.EGLDrawable; +import jogamp.opengl.egl.EGLExt; + +public abstract class EGLMediaPlayerImpl extends GLMediaPlayerImpl { + TextureType texType; + boolean useKHRSync; + + public enum TextureType { + GL(0), KHRImage(1); + + public final int id; + + TextureType(int id){ + this.id = id; + } + } + + public static class EGLTextureFrame extends TextureFrame { + + public EGLTextureFrame(Texture t, long khrImage, long khrSync) { + super(t); + this.image = khrImage; + this.sync = khrSync; + } + + public final long getImage() { return image; } + public final long getSync() { return sync; } + + public String toString() { + return "EGLTextureFrame[" + texture + ", img "+ image + ", sync "+ sync+"]"; + } + protected final long image; + protected final long sync; + } + + + protected EGLMediaPlayerImpl() { + this(TextureType.GL, false); + } + + protected EGLMediaPlayerImpl(TextureType texType, boolean useKHRSync) { + super(); + this.texType = texType; + this.useKHRSync = useKHRSync; + } + + @Override + protected TextureFrame createTexImage(GLContext ctx, int idx, int[] tex) { + final Texture texture = super.createTexImageImpl(ctx, idx, tex, true); + final long image; + final long sync; + + final EGLContext eglCtx = (EGLContext) ctx; + final EGLExt eglExt = eglCtx.getEGLExt(); + final EGLDrawable eglDrawable = (EGLDrawable) eglCtx.getGLDrawable(); + int[] tmp = new int[1]; + + if(TextureType.KHRImage == texType) { + // create EGLImage from texture + tmp[0] = EGL.EGL_NONE; + image = eglExt.eglCreateImageKHR( eglDrawable.getDisplay(), eglCtx.getHandle(), + EGLExt.EGL_GL_TEXTURE_2D_KHR, + tex[idx], tmp, 0); + if (0==image) { + throw new RuntimeException("EGLImage creation failed: "+EGL.eglGetError()+", ctx "+eglCtx+", tex "+tex[idx]+", err "+toHexString(EGL.eglGetError())); + } + } else { + image = 0; + } + + if(useKHRSync) { + // Create sync object so that we can be sure that gl has finished + // rendering the EGLImage texture before we tell OpenMAX to fill + // it with a new frame. + tmp[0] = EGL.EGL_NONE; + sync = eglExt.eglCreateSyncKHR(eglDrawable.getDisplay(), EGLExt.EGL_SYNC_FENCE_KHR, tmp, 0); + if (0==sync) { + throw new RuntimeException("EGLSync creation failed: "+EGL.eglGetError()+", ctx "+eglCtx+", err "+toHexString(EGL.eglGetError())); + } + } else { + sync = 0; + } + return new EGLTextureFrame(texture, image, sync); + } + + @Override + protected void destroyTexImage(GLContext ctx, TextureFrame imgTex) { + final EGLContext eglCtx = (EGLContext) ctx; + final EGLExt eglExt = eglCtx.getEGLExt(); + final EGLDrawable eglDrawable = (EGLDrawable) eglCtx.getGLDrawable(); + final EGLTextureFrame eglTex = (EGLTextureFrame) imgTex; + + if(0!=eglTex.getImage()) { + eglExt.eglDestroyImageKHR(eglDrawable.getDisplay(), eglTex.getImage()); + } + if(0!=eglTex.getSync()) { + eglExt.eglDestroySyncKHR(eglDrawable.getDisplay(), eglTex.getSync()); + } + super.destroyTexImage(ctx, imgTex); + } +} diff --git a/src/jogl/classes/jogamp/opengl/av/GLMediaPlayerImpl.java b/src/jogl/classes/jogamp/opengl/av/GLMediaPlayerImpl.java new file mode 100644 index 000000000..826bb6953 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/av/GLMediaPlayerImpl.java @@ -0,0 +1,307 @@ +package jogamp.opengl.av; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +import javax.media.opengl.GL; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLES2; + +import com.jogamp.opengl.av.GLMediaPlayer; +import com.jogamp.opengl.av.GLMediaEventListener; +import com.jogamp.opengl.util.texture.Texture; + +/** + * After object creation an implementation may customize the behavior: + * <ul> + * <li>{@link #setTextureCount(int)}</li> + * <li>{@link #setTextureTarget(int)}</li> + * <li>{@link EGLMediaPlayerImpl#setEGLTexImageAttribs(boolean, boolean)}.</li> + * </ul> + * + * <p> + * See {@link GLMediaPlayer}. + * </p> + */ +public abstract class GLMediaPlayerImpl implements GLMediaPlayer { + + protected int textureCount; + protected int textureTarget; + + private int sWidth = 0; + private int sHeight = 0; + protected URL url = null; + + protected Texture texture = null; + protected float playSpeed = 1.0f; + + /** Shall be set by the {@link #setStreamImpl()} method implementation. */ + protected int width = 0; + /** Shall be set by the {@link #setStreamImpl()} method implementation. */ + protected int height = 0; + /** Shall be set by the {@link #setStreamImpl()} method implementation. */ + protected int fps = 0; + /** Shall be set by the {@link #setStreamImpl()} method implementation. */ + protected long bps = 0; + /** Shall be set by the {@link #setStreamImpl()} method implementation. */ + protected long totalFrames = 0; + /** Shall be set by the {@link #setStreamImpl()} method implementation. */ + protected String acodec = null; + /** Shall be set by the {@link #setStreamImpl()} method implementation. */ + protected String vcodec = null; + + protected long frameNumber = 0; + + private TextureFrame[] texFrames = null; + protected HashMap<Integer, TextureFrame> texFrameMap = new HashMap<Integer, TextureFrame>(); + private ArrayList<GLMediaEventListener> eventListeners = new ArrayList<GLMediaEventListener>(); + + protected GLMediaPlayerImpl() { + this.textureCount=3; + this.textureTarget=GL.GL_TEXTURE_2D; + } + + protected final void setTextureCount(int textureCount) { + this.textureCount=textureCount; + } + protected final void setTextureTarget(int textureTarget) { + this.textureTarget=textureTarget; + } + + @Override + public final void setStream(GL gl, URL url) throws IOException { + this.url = url; + if (this.url == null) { + System.out.println("setURL (null)"); + stop(); + return; + } + setStreamImpl(); + init(gl); + } + + /** + * Implementation shall set the following set of data: + * @see #width + * @see #height + * @see #fps + * @see #bps + * @see #totalFrames + * @see #acodec + * @see #vcodec + */ + protected abstract void setStreamImpl() throws IOException; + + protected final void init(GL gl) { + final GLContext ctx = gl.getContext(); + if(!ctx.isCurrent()) { + throw new RuntimeException("Not current: "+ctx); + } + + final GLDrawable drawable = ctx.getGLDrawable(); + sWidth = drawable.getWidth(); + sHeight = drawable.getHeight(); + System.out.println("surface size: "+sWidth+"x"+sHeight); + System.out.println("Platform Extensions : "+ctx.getPlatformExtensionsString()); + + if(null!=texFrames) { + removeAllImageTextures(ctx); + } else { + texFrames = new TextureFrame[textureCount]; + } + + final int[] tex = new int[textureCount]; + { + gl.glGenTextures(textureCount, tex, 0); + final int err = gl.glGetError(); + if( GL.GL_NO_ERROR != err ) { + throw new RuntimeException("TextureNames creation failed (num: "+textureCount+"): err "+toHexString(err)); + } + } + + for(int i=0; i<textureCount; i++) { + final TextureFrame tf = createTexImage(ctx, i, tex); + texFrames[i] = tf; + texFrameMap.put(tex[i], tf); + } + } + + protected TextureFrame createTexImage(GLContext ctx, int idx, int[] tex) { + return new TextureFrame( createTexImageImpl(ctx, idx, tex, true) ); + } + + protected Texture createTexImageImpl(GLContext ctx, int idx, int[] tex, boolean mustFlipVertically) { + final GL gl = ctx.getGL(); + if( 0 > tex[idx] ) { + throw new RuntimeException("TextureName "+toHexString(tex[idx])+" invalid."); + } + gl.glBindTexture(textureTarget, tex[idx]); + { + final int err = gl.glGetError(); + if( GL.GL_NO_ERROR != err ) { + throw new RuntimeException("Couldn't bind textureName "+toHexString(tex[idx])+" to 2D target, err "+toHexString(err)); + } + } + + // create space for buffer with a texture + gl.glTexImage2D( + textureTarget, // target + 0, // level + GL.GL_RGBA, // internal format + width, // width + height, // height + 0, // border + GL.GL_RGBA, // format + GL.GL_UNSIGNED_BYTE, // type + null); // pixels -- will be provided later + { + final int err = gl.glGetError(); + if( GL.GL_NO_ERROR != err ) { + throw new RuntimeException("Couldn't create TexImage2D RGBA "+width+"x"+height+", err "+toHexString(err)); + } + } + gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST); + gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST); + // Clamp to edge is only option. + gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE); + gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE); + + return com.jogamp.opengl.util.texture.TextureIO.newTexture(tex[idx], + textureTarget, + width, height, + width, height, + mustFlipVertically); + } + + protected void destroyTexImage(GLContext ctx, TextureFrame imgTex) { + imgTex.getTexture().destroy(ctx.getGL()); + } + + protected void removeAllImageTextures(GLContext ctx) { + texture = null; + for(int i=0; i<textureCount; i++) { + final TextureFrame imgTex = texFrames[i]; + destroyTexImage(ctx, imgTex); + texFrames[i] = null; + } + texFrameMap.clear(); + } + + protected void attributesUpdated(int event_mask) { + synchronized(eventListenersLock) { + for(Iterator<GLMediaEventListener> i = eventListeners.iterator(); i.hasNext(); ) { + i.next().attributesChanges(this, event_mask); + } + } + } + protected void newFrameAvailable(TextureFrame frame) { + synchronized(eventListenersLock) { + for(Iterator<GLMediaEventListener> i = eventListeners.iterator(); i.hasNext(); ) { + i.next().newFrameAvailable(this, frame); + } + } + } + + @Override + public synchronized float getPlaySpeed() { + return playSpeed; + } + + @Override + public synchronized Texture getLastTextureID() { + return texture; + } + + @Override + public synchronized void destroy(GL gl) { + destroyImpl(gl); + removeAllImageTextures(gl.getContext()); + } + protected abstract void destroyImpl(GL gl); + + @Override + public synchronized URL getURL() { + return url; + } + + @Override + public synchronized String getVideoCodec() { + return vcodec; + } + + @Override + public synchronized String getAudioCodec() { + return acodec; + } + + @Override + public synchronized long getTotalFrames() { + return totalFrames; + } + + @Override + public synchronized long getBitrate() { + return bps; + } + + @Override + public synchronized int getFramerate() { + return fps; + } + + @Override + public synchronized int getWidth() { + return width; + } + + @Override + public synchronized int getHeight() { + return height; + } + + @Override + public synchronized String toString() { + return "GLMediaPlayer [ stream [ video [ "+vcodec+", "+width+"x"+height+", "+fps+"fps, "+bps+"bsp, "+totalFrames+"f ] ] ]"; + } + + @Override + public void addEventListener(GLMediaEventListener l) { + if(l == null) { + return; + } + synchronized(eventListenersLock) { + eventListeners.add(l); + } + } + + @Override + public void removeEventListener(GLMediaEventListener l) { + if (l == null) { + return; + } + synchronized(eventListenersLock) { + eventListeners.remove(l); + } + } + + @Override + public synchronized GLMediaEventListener[] getEventListeners() { + synchronized(eventListenersLock) { + return eventListeners.toArray(new GLMediaEventListener[eventListeners.size()]); + } + } + + private Object eventListenersLock = new Object(); + + protected static final String toHexString(long v) { + return "0x"+Long.toHexString(v); + } + protected static final String toHexString(int v) { + return "0x"+Integer.toHexString(v); + } + +}
\ No newline at end of file diff --git a/src/jogl/classes/jogamp/opengl/omx/OMXGLMediaPlayer.java b/src/jogl/classes/jogamp/opengl/omx/OMXGLMediaPlayer.java new file mode 100644 index 000000000..8f5f9c3ab --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/omx/OMXGLMediaPlayer.java @@ -0,0 +1,238 @@ + +package jogamp.opengl.omx; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; + +import javax.media.opengl.GL; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLException; + +import com.jogamp.opengl.av.GLMediaEventListener; +import com.jogamp.opengl.util.texture.Texture; + +import jogamp.opengl.av.EGLMediaPlayerImpl; +import jogamp.opengl.egl.EGL; + +public class OMXGLMediaPlayer extends EGLMediaPlayerImpl { + protected long moviePtr = 0; + + /** + * Old stream values, before the last attributesUpdated) + */ + protected int o_width = 0; + protected int o_height = 0; + protected int o_fps = 0; + protected long o_bps = 0; + protected long o_totalFrames = 0; + + public OMXGLMediaPlayer() { + super(TextureType.KHRImage, true); + initOMX(); + } + + protected void initOMX() { + moviePtr = _createInstance(); + if(0==moviePtr) { + throw new GLException("Couldn't create OMXInstance"); + } + } + + @Override + protected TextureFrame createTexImage(GLContext ctx, int idx, int[] tex) { + final EGLTextureFrame eglTex = (EGLTextureFrame) super.createTexImage(ctx, idx, tex); + _setStreamEGLImageTexture2D(moviePtr, idx, tex[idx], eglTex.getImage(), eglTex.getSync()); + return eglTex; + } + + @Override + protected void destroyTexImage(GLContext ctx, TextureFrame imgTex) { + super.destroyTexImage(ctx, imgTex); + } + + @Override + protected void destroyImpl(GL gl) { + _detachVideoRenderer(moviePtr); + if (moviePtr != 0) { + _destroyInstance(moviePtr); + moviePtr = 0; + } + } + + @Override + protected void setStreamImpl() throws IOException { + if(0==moviePtr) { + throw new GLException("OMX native instance null"); + } + String path=null; + if (url.getProtocol() == null || "file".equals(url.getProtocol())) { + // CV only accepts absolute paths + try { + File file = new File(url.getPath()); + if (!file.exists()) { + throw new FileNotFoundException(file.toString()); + } + path = file.getCanonicalPath(); + System.out.println("setURL: path "+path); + } catch (Exception e) { + e.printStackTrace(); + throw new IOException(e); + } + } + path = replaceAll(path, "\\", "/").trim(); + if(null==path) { + throw new IOException("Couldn't parse stream URL: "+url); + } + System.out.println("setURL: clean path "+path); + + System.out.println("setURL: p1 "+this); + _setStream(moviePtr, textureCount, path); + System.out.println("setURL: p2 "+this); + } + + + @Override + public synchronized int getCurrentPosition() { + if(0==moviePtr) { + throw new GLException("OMX native instance null"); + } + return _getCurrentPosition(moviePtr); + } + + @Override + public synchronized boolean isValid() { + return (moviePtr != 0); + } + + @Override + public synchronized void setPlaySpeed(float rate) { + if(0==moviePtr) { + throw new GLException("OMX native instance null"); + } + _setPlaySpeed(moviePtr, rate); + playSpeed = rate; + } + + /** @return time position after issuing the command */ + @Override + public synchronized void start() { + if(0==moviePtr) { + throw new GLException("OMX native instance null"); + } + _play(moviePtr); + } + + /** @return time position after issuing the command */ + @Override + public synchronized void pause() { + if(0==moviePtr) { + throw new GLException("OMX native instance null"); + } + _pause(moviePtr); + } + + /** @return time position after issuing the command */ + @Override + public synchronized void stop() { + if(0==moviePtr) { + throw new GLException("OMX native instance null"); + } + _stop(moviePtr); + } + + /** @return time position after issuing the command */ + @Override + public synchronized int seek(int msec) { + if(0==moviePtr) { + throw new GLException("OMX native instance null"); + } + return _seek(moviePtr, msec); + } + + @Override + public synchronized Texture getNextTextureID() { + if(0==moviePtr) { + throw new GLException("OMX native instance null"); + } + texture=null; + TextureFrame eglImgTex = texFrameMap.get(new Integer(_getNextTextureID(moviePtr))); + if(null!=eglImgTex) { + texture = eglImgTex.getTexture(); + } + return texture; + } + + protected void attributesUpdated() { + int event_mask = 0; + if( o_width != width || o_height != height ) { + event_mask |= GLMediaEventListener.EVENT_CHANGE_SIZE; + } + if( o_fps != fps ) { + event_mask |= GLMediaEventListener.EVENT_CHANGE_FPS; + } + if( o_bps != bps ) { + event_mask |= GLMediaEventListener.EVENT_CHANGE_BPS; + } + if( o_totalFrames != totalFrames ) { + event_mask |= GLMediaEventListener.EVENT_CHANGE_LENGTH; + } + if(0==event_mask) { + return; + } + super.attributesUpdated(event_mask); + } + + /** + * Java callback method issued by the native OMX backend + */ + private void saveAttributes() { + o_width = width; + o_height = height; + o_fps = fps; + o_bps = bps; + o_totalFrames = totalFrames; + } + + private String replaceAll(String orig, String search, String repl) { + String dest=null; + // In case replaceAll / java.util.regex.* is not supported (-> CVM) + int i=0,j; + dest = new String(); + while((j=orig.indexOf(search, i))>=0) { + dest=dest.concat(orig.substring(i, j)); + dest=dest.concat(repl); + i=j+1; + } + return dest.concat(orig.substring(i, orig.length())); + } + + private void errorCheckEGL(String s) { + int e; + if( (e=EGL.eglGetError()) != EGL.EGL_SUCCESS ) { + System.out.println("EGL Error: ("+s+"): 0x"+Integer.toHexString(e)); + } + } + + // + // OMXEventListener Support + // + + native long _createInstance(); + native void _destroyInstance(long moviePtr); + + native void _detachVideoRenderer(long moviePtr); // stop before + native void _attachVideoRenderer(long moviePtr); // detach before + native void _setStream(long moviePtr, int textureNum, String path); + native void _activateStream(long moviePtr); + + native void _setStreamEGLImageTexture2D(long moviePtr, int i, int tex, long image, long sync); + native int _seek(long moviePtr, int position); + native void _setPlaySpeed(long moviePtr, float rate); + native void _play(long moviePtr); + native void _pause(long moviePtr); + native void _stop(long moviePtr); + native int _getNextTextureID(long moviePtr); + native int _getCurrentPosition(long moviePtr); +} + diff --git a/src/jogl/native/openmax/com_sun_openmax_OMXInstance.c b/src/jogl/native/openmax/jogamp.opengl.omx.OMXGLMediaPlayer.c index 5317c2abc..00c0ca562 100644 --- a/src/jogl/native/openmax/com_sun_openmax_OMXInstance.c +++ b/src/jogl/native/openmax/jogamp.opengl.omx.OMXGLMediaPlayer.c @@ -13,7 +13,7 @@ // http://developer.apple.com/qa/qa2001/qa1149.html // http://developer.apple.com/qa/qa2001/qa1262.html -#include "com_jogamp_openmax_OMXInstance.h" +#include "jogamp_opengl_omx_OMXGLMediaPlayer.h" #include "omx_tool.h" #include <stdarg.h> @@ -105,7 +105,7 @@ void OMXInstance_UpdateJavaAttributes(OMXToolBasicAV_t *pOMXAV, KDboolean issueJ } } -JNIEXPORT jlong JNICALL Java_com_jogamp_openmax_OMXInstance__1createInstance +JNIEXPORT jlong JNICALL Java_jogamp_opengl_omx_OMXGLMediaPlayer__1createInstance (JNIEnv *env, jobject instance) { OMXToolBasicAV_t * pOMXAV; @@ -132,7 +132,7 @@ JNIEXPORT jlong JNICALL Java_com_jogamp_openmax_OMXInstance__1createInstance return (jlong) (intptr_t) (void *)pOMXAV; } -JNIEXPORT void JNICALL Java_com_jogamp_openmax_OMXInstance__1setStream +JNIEXPORT void JNICALL Java_jogamp_opengl_omx_OMXGLMediaPlayer__1setStream (JNIEnv *env, jobject instance, jlong ptr, jint vBufferNum, jstring jpath) { jboolean iscopy; @@ -150,7 +150,7 @@ JNIEXPORT void JNICALL Java_com_jogamp_openmax_OMXInstance__1setStream fprintf(stdout, "setStream 3 ..\n"); fflush(stdout); // JAU } -JNIEXPORT void JNICALL Java_com_jogamp_openmax_OMXInstance__1setStreamEGLImageTexture2D +JNIEXPORT void JNICALL Java_jogamp_opengl_omx_OMXGLMediaPlayer__1setStreamEGLImageTexture2D (JNIEnv *env, jobject instance, jlong ptr, jint i, jint tex, jlong image, jlong sync) { OMXToolBasicAV_t *pOMXAV = (OMXToolBasicAV_t *)((void *)((intptr_t)ptr)); @@ -161,7 +161,7 @@ JNIEXPORT void JNICALL Java_com_jogamp_openmax_OMXInstance__1setStreamEGLImageTe } } -JNIEXPORT void JNICALL Java_com_jogamp_openmax_OMXInstance__1activateStream +JNIEXPORT void JNICALL Java_jogamp_opengl_omx_OMXGLMediaPlayer__1activateStream (JNIEnv *env, jobject instance, jlong ptr) { OMXToolBasicAV_t *pOMXAV = (OMXToolBasicAV_t *)((void *)((intptr_t)ptr)); @@ -171,52 +171,49 @@ JNIEXPORT void JNICALL Java_com_jogamp_openmax_OMXInstance__1activateStream } } -JNIEXPORT void JNICALL Java_com_jogamp_openmax_OMXInstance__1attachVideoRenderer +JNIEXPORT void JNICALL Java_jogamp_opengl_omx_OMXGLMediaPlayer__1attachVideoRenderer (JNIEnv *env, jobject instance, jlong ptr) { OMXToolBasicAV_t *pOMXAV = (OMXToolBasicAV_t *)((void *)((intptr_t)ptr)); OMXToolBasicAV_AttachVideoRenderer(pOMXAV); } -JNIEXPORT void JNICALL Java_com_jogamp_openmax_OMXInstance__1detachVideoRenderer +JNIEXPORT void JNICALL Java_jogamp_opengl_omx_OMXGLMediaPlayer__1detachVideoRenderer (JNIEnv *env, jobject instance, jlong ptr) { OMXToolBasicAV_t *pOMXAV = (OMXToolBasicAV_t *)((void *)((intptr_t)ptr)); OMXToolBasicAV_DetachVideoRenderer(pOMXAV); } -JNIEXPORT void JNICALL Java_com_jogamp_openmax_OMXInstance__1setPlaySpeed +JNIEXPORT void JNICALL Java_jogamp_opengl_omx_OMXGLMediaPlayer__1setPlaySpeed (JNIEnv *env, jobject instance, jlong ptr, jfloat scale) { OMXToolBasicAV_t *pOMXAV = (OMXToolBasicAV_t *)((void *)((intptr_t)ptr)); OMXToolBasicAV_SetPlaySpeed(pOMXAV, scale); } -JNIEXPORT jfloat JNICALL Java_com_jogamp_openmax_OMXInstance__1play +JNIEXPORT void JNICALL Java_jogamp_opengl_omx_OMXGLMediaPlayer__1play (JNIEnv *env, jobject instance, jlong ptr) { OMXToolBasicAV_t *pOMXAV = (OMXToolBasicAV_t *)((void *)((intptr_t)ptr)); OMXToolBasicAV_PlayStart(pOMXAV); - return OMXToolBasicAV_GetCurrentPosition(pOMXAV); } -JNIEXPORT jfloat JNICALL Java_com_jogamp_openmax_OMXInstance__1pause +JNIEXPORT void JNICALL Java_jogamp_opengl_omx_OMXGLMediaPlayer__1pause (JNIEnv *env, jobject instance, jlong ptr) { OMXToolBasicAV_t *pOMXAV = (OMXToolBasicAV_t *)((void *)((intptr_t)ptr)); OMXToolBasicAV_PlayPause(pOMXAV); - return OMXToolBasicAV_GetCurrentPosition(pOMXAV); } -JNIEXPORT jfloat JNICALL Java_com_jogamp_openmax_OMXInstance__1stop +JNIEXPORT void JNICALL Java_jogamp_opengl_omx_OMXGLMediaPlayer__1stop (JNIEnv *env, jobject instance, jlong ptr) { OMXToolBasicAV_t *pOMXAV = (OMXToolBasicAV_t *)((void *)((intptr_t)ptr)); OMXToolBasicAV_PlayStop(pOMXAV); - return OMXToolBasicAV_GetCurrentPosition(pOMXAV); } -JNIEXPORT jfloat JNICALL Java_com_jogamp_openmax_OMXInstance__1seek +JNIEXPORT jint JNICALL Java_jogamp_opengl_omx_OMXGLMediaPlayer__1seek (JNIEnv *env, jobject instance, jlong ptr, jfloat pos) { OMXToolBasicAV_t *pOMXAV = (OMXToolBasicAV_t *)((void *)((intptr_t)ptr)); @@ -224,7 +221,7 @@ JNIEXPORT jfloat JNICALL Java_com_jogamp_openmax_OMXInstance__1seek return OMXToolBasicAV_GetCurrentPosition(pOMXAV); } -JNIEXPORT jint JNICALL Java_com_jogamp_openmax_OMXInstance__1getNextTextureID +JNIEXPORT jint JNICALL Java_jogamp_opengl_omx_OMXGLMediaPlayer__1getNextTextureID (JNIEnv *env, jobject instance, jlong ptr) { jint textureID = 0xffffffff; @@ -235,7 +232,7 @@ JNIEXPORT jint JNICALL Java_com_jogamp_openmax_OMXInstance__1getNextTextureID return textureID; } -JNIEXPORT jfloat JNICALL Java_com_jogamp_openmax_OMXInstance__1getCurrentPosition +JNIEXPORT jint JNICALL Java_jogamp_opengl_omx_OMXGLMediaPlayer__1getCurrentPosition (JNIEnv *env, jobject instance, jlong ptr) { OMXToolBasicAV_t *pOMXAV = (OMXToolBasicAV_t *)((void *)((intptr_t)ptr)); @@ -243,7 +240,7 @@ JNIEXPORT jfloat JNICALL Java_com_jogamp_openmax_OMXInstance__1getCurrentPositio } -JNIEXPORT void JNICALL Java_com_jogamp_openmax_OMXInstance__1destroyInstance +JNIEXPORT void JNICALL Java_jogamp_opengl_omx_OMXGLMediaPlayer__1destroyInstance (JNIEnv *env, jobject instance, jlong ptr) { OMXToolBasicAV_t *pOMXAV = (OMXToolBasicAV_t *)((void *)((intptr_t)ptr)); diff --git a/src/jogl/native/openmax/omx_tool.c b/src/jogl/native/openmax/omx_tool.c index 57fa8ad21..1f2ce7da7 100644 --- a/src/jogl/native/openmax/omx_tool.c +++ b/src/jogl/native/openmax/omx_tool.c @@ -627,14 +627,14 @@ static int StartClock(OMXToolBasicAV_t * pOMXAV, KDboolean start, KDfloat32 time return (OMX_ErrorNotReady == eError)?-1:0; } -static KDfloat32 GetClockPosition(OMXToolBasicAV_t * pOMXAV) +static KDint GetClockPosition(OMXToolBasicAV_t * pOMXAV) { OMX_TIME_CONFIG_TIMESTAMPTYPE stamp; INIT_PARAM(stamp); stamp.nPortIndex = 0; OMX_GetConfig(pOMXAV->comp[OMXAV_H_CLOCK], OMX_IndexConfigTimeCurrentMediaTime, &stamp); - return (KDfloat32) (stamp.nTimestamp * (1.0f/(1000.0f*1000.0f))); + return (int) ( stamp.nTimestamp / 1000 ); } static KDfloat32 GetClockScale(OMXToolBasicAV_t * pOMXAV) @@ -1474,7 +1474,7 @@ GLuint OMXToolBasicAV_GetNextTextureID(OMXToolBasicAV_t * pOMXAV) { return texID; } -KDfloat32 OMXToolBasicAV_GetCurrentPosition(OMXToolBasicAV_t * pOMXAV) { +KDint OMXToolBasicAV_GetCurrentPosition(OMXToolBasicAV_t * pOMXAV) { KDfloat32 res = -1.0f; if(NULL==pOMXAV) { java_throwNewRuntimeException(0, "OMX instance null\n"); diff --git a/src/jogl/native/openmax/omx_tool.h b/src/jogl/native/openmax/omx_tool.h index d566507c1..cb0f125ec 100644 --- a/src/jogl/native/openmax/omx_tool.h +++ b/src/jogl/native/openmax/omx_tool.h @@ -141,7 +141,7 @@ void OMXToolBasicAV_PlayStop(OMXToolBasicAV_t * pOMXAV); void OMXToolBasicAV_PlaySeek(OMXToolBasicAV_t * pOMXAV, KDfloat32 time); GLuint OMXToolBasicAV_GetNextTextureID(OMXToolBasicAV_t * pOMXAV); -KDfloat32 OMXToolBasicAV_GetCurrentPosition(OMXToolBasicAV_t * pOMXAV); +KDint OMXToolBasicAV_GetCurrentPosition(OMXToolBasicAV_t * pOMXAV); void OMXToolBasicAV_DestroyInstance(OMXToolBasicAV_t * pOMXAV); 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 new file mode 100755 index 000000000..f58938399 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieSimple.java @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. 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 of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - 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. + * + */ + + +package com.jogamp.opengl.test.junit.jogl.demos.es2.av; + +import java.io.IOException; +import java.net.URL; +import java.nio.FloatBuffer; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLException; +import javax.media.opengl.GLProfile; +import javax.media.opengl.GLUniformData; +import javax.media.opengl.fixedfunc.GLMatrixFunc; + +import com.jogamp.newt.event.MouseEvent; +import com.jogamp.newt.event.MouseListener; +import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.opengl.av.GLMediaPlayer; +import com.jogamp.opengl.av.GLMediaEventListener; +import com.jogamp.opengl.av.GLMediaPlayer.TextureFrame; +import com.jogamp.opengl.av.GLMediaPlayerFactory; +import com.jogamp.opengl.util.GLArrayDataServer; +import com.jogamp.opengl.util.PMVMatrix; +import com.jogamp.opengl.util.glsl.ShaderCode; +import com.jogamp.opengl.util.glsl.ShaderProgram; +import com.jogamp.opengl.util.glsl.ShaderState; + +public class MovieSimple implements MouseListener, GLEventListener, GLMediaEventListener { + private GLWindow window; + private boolean quit = false; + private boolean rotate = false; + private float zoom = -2.5f; + private float ang = 0f; + private long startTime; + private long curTime; + private String stream; + + public void changedAttributes(GLMediaPlayer omx, int event_mask) { + System.out.println("changed stream attr ("+event_mask+"): "+omx); + } + + public void mouseClicked(MouseEvent e) { + switch(e.getClickCount()) { + case 2: + quit=true; + break; + } + } + public void mouseEntered(MouseEvent e) { + } + public void mouseExited(MouseEvent e) { + } + public void mousePressed(MouseEvent e) { + } + public void mouseReleased(MouseEvent e) { + rotate = false; + zoom = -2.5f; + } + public void mouseMoved(MouseEvent e) { + } + public void mouseDragged(MouseEvent e) { + rotate = true; + zoom = -5; + } + public void mouseWheelMoved(MouseEvent e) { + } + + public MovieSimple(String stream) { + this.stream = stream ; + } + + private void run() { + System.err.println("MovieSimple.run()"); + try { + GLCapabilities caps = new GLCapabilities(GLProfile.getGL2ES2()); + // For emulation library, use 16 bpp + caps.setRedBits(5); + caps.setGreenBits(6); + caps.setBlueBits(5); + caps.setDepthBits(16); + + window = GLWindow.create(caps); + + window.addMouseListener(this); + window.addGLEventListener(this); + // window.setEventHandlerMode(GLWindow.EVENT_HANDLER_GL_CURRENT); // default + // window.setEventHandlerMode(GLWindow.EVENT_HANDLER_GL_NONE); // no current .. + + // Size OpenGL to Video Surface + window.setFullscreen(true); + window.setVisible(true); + + startTime = System.currentTimeMillis(); + while (!quit) { + window.display(); + } + + // Shut things down cooperatively + if(null!=movie) { + movie.destroy(window.getGL()); + movie=null; + } + window.destroy(); + System.out.println("MovieSimple shut down cleanly."); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + ShaderState st; + PMVMatrix pmvMatrix; + + private void initShader(GL2ES2 gl) { +// Create & Compile the shader objects + ShaderCode rsVp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, 1, MovieSimple.class, + "shader", "shader/bin", "moviesimple"); + ShaderCode rsFp = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, 1, MovieSimple.class, + "shader", "shader/bin", "moviesimple"); + + // Create & Link the shader program + ShaderProgram sp = new ShaderProgram(); + sp.add(rsVp); + sp.add(rsFp); + if(!sp.link(gl, System.err)) { + throw new GLException("Couldn't link program: "+sp); + } + + // Let's manage all our states using ShaderState. + st = new ShaderState(); + st.attachShaderProgram(gl, sp, false); + } + + GLMediaPlayer movie=null; + + public void init(GLAutoDrawable drawable) { + GL2ES2 gl = drawable.getGL().getGL2ES2(); + System.err.println("Entering initialization"); + System.err.println("GL_VERSION=" + gl.glGetString(GL.GL_VERSION)); + System.err.println("GL_EXTENSIONS:"); + System.err.println(" " + gl.glGetString(GL.GL_EXTENSIONS)); + + pmvMatrix = new PMVMatrix(); + + initShader(gl); + + // Push the 1st uniform down the path + st.useProgram(gl, true); + + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); + pmvMatrix.glLoadIdentity(); + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + pmvMatrix.glLoadIdentity(); + + if(!st.uniform(gl, new GLUniformData("mgl_PMVMatrix", 4, 4, pmvMatrix.glGetPMvMatrixf()))) { + throw new GLException("Error setting PMVMatrix in shader: "+st); + } + if(!st.uniform(gl, new GLUniformData("mgl_ActiveTexture", 0))) { + throw new GLException("Error setting mgl_ActiveTexture in shader: "+st); + } + gl.glActiveTexture(GL.GL_TEXTURE0); + + float aspect = 16.0f/9.0f; + float xs=1f, ys=1f; // scale object + float ss=1f, ts=1f; // scale tex-coord + + xs = aspect; // b > h + ys = 1f; // b > h + // ss = 1f/aspect; // b > h, crop width + // ts = 1f; // b > h + + // Allocate vertex array + GLArrayDataServer vertices = GLArrayDataServer.createGLSL("mgl_Vertex", 3, GL.GL_FLOAT, false, 4, GL.GL_STATIC_DRAW); + { + // Fill them up + FloatBuffer verticeb = (FloatBuffer)vertices.getBuffer(); + verticeb.put(-1f*xs); verticeb.put( -1f*ys); verticeb.put( 0); + verticeb.put(-1f*xs); verticeb.put( 1f*ys); verticeb.put( 0); + verticeb.put( 1f*xs); verticeb.put( -1f*ys); verticeb.put( 0); + verticeb.put( 1f*xs); verticeb.put( 1f*ys); verticeb.put( 0); + } + vertices.seal(gl, true); + + // Allocate texcoord array + GLArrayDataServer texcoord = GLArrayDataServer.createGLSL("mgl_MultiTexCoord", 2, GL.GL_FLOAT, false, 4, GL.GL_STATIC_DRAW); + { + // Fill them up + FloatBuffer texcoordb = (FloatBuffer)texcoord.getBuffer(); + texcoordb.put( 0f*ss); texcoordb.put( 0f*ts); + texcoordb.put( 0f*ss); texcoordb.put( 1f*ts); + texcoordb.put( 1f*ss); texcoordb.put( 0f*ts); + texcoordb.put( 1f*ss); texcoordb.put( 1f*ts); + } + texcoord.seal(gl, true); + + GLArrayDataServer colors = GLArrayDataServer.createGLSL("mgl_Color", 4, GL.GL_FLOAT, false, 4, GL.GL_STATIC_DRAW); + { + // Fill them up + FloatBuffer colorb = (FloatBuffer)colors.getBuffer(); + colorb.put( 0); colorb.put( 0); colorb.put( 0); colorb.put( 1); + colorb.put( 1); colorb.put( 1); colorb.put( 1); colorb.put( 1); + colorb.put( 0); colorb.put( 0); colorb.put( 0); colorb.put( 1); + colorb.put( 1); colorb.put( 1); colorb.put( 1); colorb.put( 1); + } + colors.seal(gl, true); + + // OpenGL Render Settings + gl.glClearColor(0.2f, 0.2f, 0.2f, 1); + gl.glEnable(GL2ES2.GL_DEPTH_TEST); + + st.useProgram(gl, false); + + // Let's show the completed shader state .. + System.out.println(st); + + try { + movie = GLMediaPlayerFactory.create(); + movie.addEventListener(this); + // movie.setStream(4, new URL(stream)); + movie.setStream(gl, new URL(stream)); + System.out.println("p0 "+movie); + } catch (IOException ioe) { ioe.printStackTrace(); } + if(null!=movie) { + //movie.setStreamAllEGLImageTexture2D(gl); + //movie.activateStream(); + //System.out.println("p1 "+movie); + movie.start(); + } + } + + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + GL2ES2 gl = drawable.getGL().getGL2ES2(); + + st.useProgram(gl, true); + + // Set location in front of camera + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); + pmvMatrix.glLoadIdentity(); + pmvMatrix.gluPerspective(45.0f, (float)width / (float)height, 1.0f, 100.0f); + //pmvMatrix.glOrthof(-4.0f, 4.0f, -4.0f, 4.0f, 1.0f, 100.0f); + + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + pmvMatrix.glLoadIdentity(); + pmvMatrix.glTranslatef(0, 0, zoom); + + GLUniformData ud = st.getUniform("mgl_PMVMatrix"); + if(null!=ud) { + // same data object + st.uniform(gl, ud); + } + + st.useProgram(gl, false); + } + + public void dispose(GLAutoDrawable drawable) { + GL2ES2 gl = drawable.getGL().getGL2ES2(); + + movie.destroy(gl); + movie=null; + pmvMatrix.destroy(); + pmvMatrix=null; + st.destroy(gl); + st=null; + quit=true; + } + + public void display(GLAutoDrawable drawable) { + GL2ES2 gl = drawable.getGL().getGL2ES2(); + + st.useProgram(gl, true); + + gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); + + if(rotate) { + curTime = System.currentTimeMillis(); + ang = ((float) (curTime - startTime) * 360.0f) / 8000.0f; + } + + if(rotate || zoom!=0f) { + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + pmvMatrix.glLoadIdentity(); + pmvMatrix.glTranslatef(0, 0, zoom); + pmvMatrix.glRotatef(ang, 0, 0, 1); + // pmvMatrix.glRotatef(ang, 0, 1, 0); + + GLUniformData ud = st.getUniform("mgl_PMVMatrix"); + if(null!=ud) { + // same data object + st.uniform(gl, ud); + } + + if(!rotate) { + zoom=0f; + } + } + + + com.jogamp.opengl.util.texture.Texture tex = null; + if(null!=movie) { + tex=movie.getNextTextureID(); + if(null!=tex) { + tex.enable(gl); + tex.bind(gl); + } + } + + // Draw a square + gl.glDrawArrays(GL.GL_TRIANGLE_STRIP, 0, 4); + + if(null!=tex) { + tex.disable(gl); + } + + st.useProgram(gl, false); + } + + public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) { + } + + public static void main(String[] args) { + String fname="file:///Storage Card/resources/a.mp4"; + if(args.length>0) fname=args[0]; + new MovieSimple(fname).run(); + System.exit(0); + } + + @Override + public void attributesChanges(GLMediaPlayer mp, int event_mask) { + // TODO Auto-generated method stub + + } + + @Override + public void newFrameAvailable(GLMediaPlayer mp, TextureFrame frame) { + // TODO Auto-generated method stub + + } +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/shader/moviesimple.fp b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/shader/moviesimple.fp new file mode 100644 index 000000000..c71164105 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/shader/moviesimple.fp @@ -0,0 +1,21 @@ + +#ifdef GL_ES + #define MEDIUMP mediump + #define HIGHP highp +#else + #define MEDIUMP + #define HIGHP +#endif + +uniform sampler2D mgl_ActiveTexture; +varying HIGHP vec4 mgl_texCoord; +varying HIGHP vec4 frontColor; + +void main (void) +{ + vec4 texColor = texture2D(mgl_ActiveTexture, mgl_texCoord.st); + + // mix frontColor with texture .. + gl_FragColor = vec4(frontColor.rgb*texColor.rgb, frontColor.a); +} + diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/shader/moviesimple.vp b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/shader/moviesimple.vp new file mode 100644 index 000000000..0b78eb913 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/shader/moviesimple.vp @@ -0,0 +1,22 @@ + +#ifdef GL_ES + #define MEDIUMP mediump + #define HIGHP highp +#else + #define MEDIUMP + #define HIGHP +#endif + +uniform MEDIUMP mat4 mgl_PMVMatrix[2]; +attribute HIGHP vec4 mgl_Vertex; +attribute HIGHP vec4 mgl_Color; +attribute HIGHP vec4 mgl_MultiTexCoord; +varying HIGHP vec4 frontColor; +varying HIGHP vec4 mgl_texCoord; + +void main(void) +{ + frontColor=mgl_Color; + mgl_texCoord = mgl_MultiTexCoord; + gl_Position = mgl_PMVMatrix[0] * mgl_PMVMatrix[1] * mgl_Vertex; +} |