diff options
author | Sven Göthel <[email protected]> | 2024-02-02 08:27:49 +0100 |
---|---|---|
committer | Sven Göthel <[email protected]> | 2024-02-02 08:27:49 +0100 |
commit | 23cf5279472d3ae1b2d8d1904e6b1f1e7fd8f012 (patch) | |
tree | 7f943da4a1a6c8fd917eff160b45b700a125f258 /src/jogl/classes/jogamp/opengl/util | |
parent | 9ff736464e0d2762c424bab66bc6d03ccc6e6d11 (diff) |
Bug 1494 - GLMediaPlayer/GraphUI: Support Displaying Bitmap'ed Subtitles (PGS ..) via FFMPEGFMediaPlayer/FFmpeg
FFMPEGFMediaPlayer related changes:
- Add libswscale (6th FFmpeg lib used) for sws_getCachedContext(), sws_scale() and sws_freeContext(),
used natively to convert the palette'ed bitmap into RGBA colorspace -> GL texture
- Handling AVSubtitleRect.type SUBTITLE_BITMAP
-- only handled if libswscale is available
-- config/adjust texture object
-- sws_scale palette'ed bitmap to texture
-- intermediate memory is cached, may be resized and free'ed at destroy
-- texture objects are managed and passed from GLMediaPlayerImpl,
as they are also forwarded to player client via SubBitmapEvent
- Passing the AVCodecID to GLMediaPlayerImpl, converted to our CodecID enum.
- Unifying creation and opening of AVCodecContext with 'createOpenedAVCodecContext(..)'
+++
SubtitleEvent*
- SubTextEvent now also handles ASS.Dialogue (FFmpeg 4)
besides ASS.Event (FFmpeg 5, 6, ..).
+++
GLMediaPlayerImpl
- Added ringbuffer subTexFree, managing Texture for bitmap'ed subtitles
-- Uses 1 bitmap-subtitle Texture per used textureCount in cache,
as one bitmap-subtile can be displayed per frame.
Could be potentially reduced to just 2 .. but resources used are
relatively low here.
- Validating subTexFree + videoFramesFree usage,
use blocking get/put ringbuffer due to utilization from different threads.
- Receives subtitle content from native getNextPacket0() via callback,
creates SubtitleEvent instance and passes it to a SubtitleEventListener - if exists.
(See MediaButton example)
-- SubBitmapEvent also gets its special SubBitmapEvent.TextureOwner to handle client releasing
the event and allowing us to put back the Texture resource to 'subTexFree'.
This passing through of the Texture object is probably a weakness of this lifecycle
and requires the client to ensure SubtitleEvent.release() gets called.
See MediaButton example!
- Exposing CodecID, allowing clients like MediaButton to handle SubtitleEvent content according to codec
Diffstat (limited to 'src/jogl/classes/jogamp/opengl/util')
10 files changed, 318 insertions, 97 deletions
diff --git a/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java b/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java index 219cfb3e5..a52efe2ac 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java +++ b/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java @@ -64,13 +64,15 @@ import com.jogamp.common.util.Ringbuffer; import com.jogamp.common.util.TSPrinter; import com.jogamp.common.util.WorkerThread; import com.jogamp.math.FloatUtil; +import com.jogamp.math.Vec2i; +import com.jogamp.math.Vec4f; import com.jogamp.opengl.GLExtensions; import com.jogamp.opengl.util.av.SubtitleEventListener; +import com.jogamp.opengl.util.av.CodecID; import com.jogamp.opengl.util.av.GLMediaPlayer; -import com.jogamp.opengl.util.av.SubASSEventLine; +import com.jogamp.opengl.util.av.SubTextEvent; import com.jogamp.opengl.util.av.SubEmptyEvent; -import com.jogamp.opengl.util.av.SubTextureEvent; -import com.jogamp.opengl.util.av.SubtitleEvent; +import com.jogamp.opengl.util.av.SubBitmapEvent; import com.jogamp.opengl.util.glsl.ShaderCode; import com.jogamp.opengl.util.texture.Texture; import com.jogamp.opengl.util.texture.TextureData; @@ -178,6 +180,12 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { /** In ms. Shall be set by the {@link #initStreamImpl(int, int, int)} method implementation. */ private int duration = 0; /** Shall be set by the {@link #initStreamImpl(int, int, int)} method implementation. */ + private CodecID acodecID = CodecID.NONE; + /** Shall be set by the {@link #initStreamImpl(int, int, int)} method implementation. */ + private CodecID vcodecID = CodecID.NONE; + /** Shall be set by the {@link #initStreamImpl(int, int, int)} method implementation. */ + private CodecID scodecID = CodecID.NONE; + /** Shall be set by the {@link #initStreamImpl(int, int, int)} method implementation. */ private String acodec = unknown; /** Shall be set by the {@link #initStreamImpl(int, int, int)} method implementation. */ private String vcodec = unknown; @@ -238,6 +246,11 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { private Ringbuffer<TextureFrame> videoFramesFree = null; private Ringbuffer<TextureFrame> videoFramesDecoded = null; private volatile TextureFrame lastFrame = null; + private Texture[] subTexOrig = null; + private Ringbuffer<Texture> subTexFree = null; + private static final int SUB_TEX_IMAGES_MIN = TEXTURE_COUNT_MIN + 1; + private static final boolean subDEBUG = true; + /** * @see #isGLOriented() */ @@ -387,12 +400,12 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { public final PTS getPTS() { return av_scr_cpy; } @Override - public final int getVideoPTS() { return video_pts_last.get(Clock.currentMillis()); } + public final int getVideoPTS() { return video_pts_last.getCurrent(); } @Override public final int getAudioPTS() { if( State.Uninitialized != state && null != audioSink ) { - return audioSink.getPTS().get(Clock.currentMillis()); + return audioSink.getPTS().getCurrent(); } return 0; } @@ -746,9 +759,9 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { } /** * Implementation shall set the following set of data here - * @param sid TODO * @see #vid * @see #aid + * @see #sid * @see #width * @see #height * @see #fps @@ -821,11 +834,20 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { videoFramesDecoded = new LFRingbuffer<TextureFrame>(TextureFrame[].class, textureCount); lastFrame = videoFramesFree.getBlocking(); } + if( STREAM_ID_NONE != sid ) { + subTexOrig = createSubTextures(gl, Math.max(SUB_TEX_IMAGES_MIN, textureCount)); // minimum 2 textures + subTexFree = new LFRingbuffer<Texture>(subTexOrig); + } else { + subTexOrig = null; + subTexFree = null; + } } else { videoFramesOrig = null; videoFramesFree = null; videoFramesDecoded = null; lastFrame = null; + subTexOrig = null; + subTexFree = null; } if( null == streamWorker && ( TEXTURE_COUNT_MIN < textureCount || STREAM_ID_NONE == vid ) ) // Enable StreamWorker for 'audio only' as well (Bug 918). @@ -848,6 +870,8 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { videoFramesFree = null; videoFramesDecoded = null; lastFrame = null; + subTexOrig = null; + subTexFree = null; } else { // Using a dummy test frame width = TestTexture.singleton.getWidth(); @@ -865,6 +889,8 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { videoFramesDecoded = new LFRingbuffer<TextureFrame>(TextureFrame[].class, textureCount); lastFrame = videoFramesFree.getBlocking( ); } + subTexOrig = null; + subTexFree = null; // changeState(0, State.Paused); } } catch (final Throwable t) { @@ -924,6 +950,20 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { } return texFrames; } + protected Texture[] createSubTextures(final GL gl, final int count) { + final int[] texNames = new int[count]; + gl.glGenTextures(count, texNames, 0); + final int err = gl.glGetError(); + if( GL.GL_NO_ERROR != err ) { + throw new RuntimeException("TextureNames creation failed (num: "+count+"): err "+toHexString(err)); + } + final Texture[] textures = new Texture[count]; + for(int i=0; i<count; i++) { + textures[i] = new Texture(texNames[i], true /* ownsTextureID */, + textureTarget, 1, 1, 1, 1, true); + } + return textures; + } private static class TestTexture { private static final TextureData singleton; @@ -968,7 +1008,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { new int[0], new String[0], GLMediaPlayer.STREAM_ID_NONE, new int[0], new String[0], GLMediaPlayer.STREAM_ID_NONE, TestTexture.singleton.getWidth(), TestTexture.singleton.getHeight(), 0, 0, 0, _fps, _totalFrames, 0, _duration, - "png-static", null, null); + "png-static", null, null, CodecID.toFFmpeg(CodecID.PNG), -1, -1); } protected abstract TextureFrame createTexImage(GL gl, int texName); @@ -1063,11 +1103,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { return lastFrame; } - private final void removeAllTextureFrames(final GL gl) { - final TextureFrame[] texFrames = videoFramesOrig; - videoFramesOrig = null; - videoFramesFree = null; - videoFramesDecoded = null; + private final void destroyTexFrames(final GL gl, final TextureFrame[] texFrames) { if( null != texFrames ) { for(int i=0; i<texFrames.length; i++) { final TextureFrame frame = texFrames[i]; @@ -1083,6 +1119,36 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { } } } + private final void destroyTextures(final GL gl, final Texture[] textures) { + if( null != textures ) { + for(int i=0; i<textures.length; i++) { + final Texture tex = textures[i]; + if(null != tex) { + if( null != gl ) { + tex.destroy(gl); + } + textures[i] = null; + } + if( DEBUG ) { + logout.println(Thread.currentThread().getName()+"> Clear Texture["+i+"]: "+tex+" -> null"); + } + } + } + } + private final void removeAllTextureFrames(final GL gl) { + destroyTexFrames(gl, videoFramesOrig); + videoFramesOrig = null; + videoFramesFree = null; + videoFramesDecoded = null; + lastFrame = null; + cachedFrame = null; + if( subDEBUG ) { + System.err.println("GLMediaPlayer: removeAllTextureFrames: subTexFree "+subTexFree); + } + destroyTextures(gl, subTexOrig); // can crash, if event obj w/ texture-copy still in use + subTexOrig = null; + subTexFree = null; + } private TextureFrame cachedFrame = null; private long lastMillis = 0; @@ -1357,35 +1423,47 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { * Audio frames shall be ignored, if {@link #getAID()} is {@link #STREAM_ID_NONE}. * </p> * <p> + * Subtitle frames shall be ignored, if {@link #getSID()} is {@link #STREAM_ID_NONE}. + * </p> + * <p> * Method may be invoked on the <a href="#streamworker"><i>StreamWorker</i> decoding thread</a>. * </p> * <p> * Implementation shall care of OpenGL synchronization as required, e.g. glFinish()/glFlush()! * </p> * @param gl valid and current GL instance, shall be <code>null</code> for audio only. - * @param nextFrame the {@link TextureFrame} to store the video PTS and texture data, - * shall be <code>null</code> for audio only. + * @param vFrame next video {@link TextureFrame} to store the video PTS and texture data, + * shall be <code>null</code> for audio only. + * @param sTex {@link Texture} instance as bitmap subtitle target element. + * May be {@code null} for no desired bitmap subtitle. + * @param sTexUsed Result value. If the {@link Texture} {@code sTex} is used and {@link #pushSubtitleTex(Object, int, int, int, int, int, int, int, int, int, int)}, + * {@code true} must be written into {@code sTexUsed} * @return the last processed video PTS value, maybe {@link TimeFrameI#INVALID_PTS} if video frame is invalid or n/a. * Will be {@link TimeFrameI#END_OF_STREAM_PTS} if end of stream reached. * @throws InterruptedException if waiting for next frame fails */ - protected abstract int getNextTextureImpl(GL gl, TextureFrame nextFrame) throws InterruptedException; + protected abstract int getNextTextureImpl(GL gl, TextureFrame vFrame, Texture sTex, boolean[] sTexUsed) throws InterruptedException; - protected final int getNextSingleThreaded(final GL gl, final TextureFrame nextFrame, final boolean[] gotVFrame) throws InterruptedException { + protected final int getNextSingleThreaded(final GL gl, final TextureFrame nextVFrame, final boolean[] gotVFrame) throws InterruptedException { final int pts; if( STREAM_ID_NONE != vid ) { preNextTextureImpl(gl); - pts = getNextTextureImpl(gl, nextFrame); + final boolean[] sTexUsed = { false }; + final Texture subTex = ( null != subTexFree && STREAM_ID_NONE != sid ) ? subTexFree.get() : null; + pts = getNextTextureImpl(gl, nextVFrame, subTex, sTexUsed); postNextTextureImpl(gl); + if( null != subTex && !sTexUsed[0] ) { + subTexFree.putBlocking(subTex); // return unused + } if( TimeFrameI.INVALID_PTS != pts ) { - newFrameAvailable(nextFrame, Clock.currentMillis()); + newFrameAvailable(nextVFrame, Clock.currentMillis()); gotVFrame[0] = true; } else { gotVFrame[0] = false; } } else { // audio only - pts = getNextTextureImpl(null, null); + pts = getNextTextureImpl(null, null, null, null); gotVFrame[0] = false; } return pts; @@ -1603,6 +1681,8 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { private final WorkerThread.Callback action = (final WorkerThread self) -> { final GL gl; TextureFrame nextFrame = null; + final boolean[] subTexUsed = { false }; + Texture subTex = null; try { if( STREAM_ID_NONE != vid ) { nextFrame = videoFramesFree.getBlocking(); @@ -1611,7 +1691,17 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { } else { gl = null; } - final int vPTS = getNextTextureImpl(gl, nextFrame); + if( null != gl && STREAM_ID_NONE != sid && null != subTexFree ) { + subTex = subTexFree.getBlocking(); + } + final int vPTS = getNextTextureImpl(gl, nextFrame, subTex, subTexUsed); + if( null != subTex ) { + if( !subTexUsed[0] ) { + subTexFree.putBlocking(subTex);// return unused + } else if( subDEBUG ) { + System.err.println("GLMediaPlayer: Consumed SubTex: sid "+sid+", free "+subTexFree+", subTex "+subTex); + } + } boolean audioEOS = false; if( TimeFrameI.INVALID_PTS != vPTS ) { if( null != nextFrame ) { @@ -1652,7 +1742,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { } } finally { if( null != nextFrame ) { // put back - videoFramesFree.put(nextFrame); + videoFramesFree.putBlocking(nextFrame); } } }; @@ -1688,19 +1778,28 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { } protected final void pushSubtitleText(final String text, final int start_display_pts, final int end_display_pts) { if( null != subEventListener ) { - subEventListener.run( new SubASSEventLine(SubtitleEvent.Format.ASS_TEXT, text, start_display_pts, end_display_pts) ); + subEventListener.run( new SubTextEvent(this.scodecID, SubTextEvent.TextFormat.TEXT, text, start_display_pts, end_display_pts) ); } } protected final void pushSubtitleASS(final String ass, final int start_display_pts, final int end_display_pts) { if( null != subEventListener ) { - subEventListener.run( new SubASSEventLine(SubtitleEvent.Format.ASS_FFMPEG, ass, start_display_pts, end_display_pts) ); + subEventListener.run( new SubTextEvent(this.scodecID, SubTextEvent.TextFormat.ASS, ass, start_display_pts, end_display_pts) ); } } - private final SubTextureEvent.TextureOwner subTexRelease = new SubTextureEvent.TextureOwner() { + /** {@link GLMediaPlayerImpl#pushSubtitleTex(Object, int, int, int, int, int, int, int, int, int)} */ + private final SubBitmapEvent.TextureOwner subTexRelease = new SubBitmapEvent.TextureOwner() { @Override - public void release(final Texture tex) { - if( null != subTexFree && null != tex ) { // put back - subTexFree.put(tex); + public void release(final Texture subTex) { + if( null != subTexFree && null != subTex ) { + // return unused + try { + subTexFree.putBlocking(subTex); + if( subDEBUG ) { + System.err.println("GLMediaPlayer: Released SubTex: sid "+sid+", free "+subTexFree+", subTex "+subTex); + } + } catch (final InterruptedException e) { + throw new InternalError("GLMediaPlayer.SubTexRelease: Release failed, all full: sid "+sid+", free "+subTexFree+", subTex "+subTex+", "+GLMediaPlayerImpl.this, e); + } } } @@ -1709,15 +1808,15 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { final int x, final int y, final int width, final int height, final int start_display_pts, final int end_display_pts) { + final Texture subTex = (Texture)texObj; + if( null != subTex ) { + subTex.set(texWidth, texHeight, width, height); + } if( null != subEventListener ) { - final Texture tex = (Texture)texObj; - if( null != tex ) { - tex.set(texWidth, texHeight, width, height); - } - subEventListener.run( new SubTextureEvent(new Vec2i(x, y), new Vec2i(width, height), tex, - start_display_pts, end_display_pts, subTexRelease) ); + subEventListener.run( new SubBitmapEvent(this.scodecID, new Vec2i(x, y), new Vec2i(width, height), + subTex, start_display_pts, end_display_pts, subTexRelease) ); } else { - subTexRelease.release((Texture)texObj); // release right away + subTexRelease.release(subTex); // release right away } } @@ -1779,13 +1878,15 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { * </p> */ protected final void updateAttributes(final String title, - final int[] v_streams, final String[] v_langs, - int vid, final int[] a_streams, final String[] a_langs, - int aid, final int[] s_streams, final String[] s_langs, - int sid, final int width, final int height, + final int[] v_streams, final String[] v_langs, int vid, + final int[] a_streams, final String[] a_langs, int aid, + final int[] s_streams, final String[] s_langs, int sid, + final int width, final int height, final int bps_stream, final int bps_video, final int bps_audio, final float fps, final int videoFrames, final int audioFrames, final int duration, - final String vcodec, final String acodec, final String scodec) { + final String vcodec, final String acodec, final String scodec, + final int ffmpegVCodecID, final int ffmpegACodecID, final int ffmpegSCodecID) + { final GLMediaPlayer.EventMask eventMask = new GLMediaPlayer.EventMask(); final boolean wasUninitialized = state == State.Uninitialized; @@ -1878,16 +1979,37 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { } if( (null!=acodec && acodec.length()>0 && !this.acodec.equals(acodec)) ) { eventMask.setBit(GLMediaPlayer.EventMask.Bit.Codec); + eventMask.setBit(GLMediaPlayer.EventMask.Bit.ACodec); this.acodec = acodec; } if( (null!=vcodec && vcodec.length()>0 && !this.vcodec.equals(vcodec)) ) { eventMask.setBit(GLMediaPlayer.EventMask.Bit.Codec); + eventMask.setBit(GLMediaPlayer.EventMask.Bit.VCodec); this.vcodec = vcodec; } if( (null!=scodec && scodec.length()>0 && !this.scodec.equals(scodec)) ) { eventMask.setBit(GLMediaPlayer.EventMask.Bit.Codec); + eventMask.setBit(GLMediaPlayer.EventMask.Bit.SCodec); this.scodec = scodec; } + final CodecID acodecID = CodecID.fromFFmpeg(ffmpegACodecID); + final CodecID vcodecID = CodecID.fromFFmpeg(ffmpegVCodecID); + final CodecID scodecID = CodecID.fromFFmpeg(ffmpegSCodecID); + if( (0<ffmpegACodecID && CodecID.isAudioCodec(acodecID, true) && this.acodecID != acodecID) ) { + eventMask.setBit(GLMediaPlayer.EventMask.Bit.Codec); + eventMask.setBit(GLMediaPlayer.EventMask.Bit.ACodec); + this.acodecID = acodecID; + } + if( (0<ffmpegVCodecID && CodecID.isVideoCodec(vcodecID) && this.vcodecID != vcodecID) ) { + eventMask.setBit(GLMediaPlayer.EventMask.Bit.Codec); + eventMask.setBit(GLMediaPlayer.EventMask.Bit.VCodec); + this.vcodecID = vcodecID; + } + if( (0<ffmpegSCodecID && CodecID.isSubtitleCodec(scodecID) && this.scodecID != scodecID) ) { + eventMask.setBit(GLMediaPlayer.EventMask.Bit.Codec); + eventMask.setBit(GLMediaPlayer.EventMask.Bit.SCodec); + this.scodecID = scodecID; + } if( eventMask.isZero() ) { return; } @@ -2007,6 +2129,15 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { } @Override + public final CodecID getVideoCodecID() { return vcodecID; } + + @Override + public final CodecID getAudioCodecID() { return acodecID; } + + @Override + public CodecID getSubtitleCodecID() { return scodecID; } + + @Override public final String getVideoCodec() { return vcodec; } @Override @@ -2069,18 +2200,18 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { @Override public final String toString() { - final String tt = PTS.millisToTimeStr(getDuration()); + final String tt = PTS.toTimeStr(getDuration()); final String loc = ( null != streamLoc ) ? streamLoc.toString() : "<undefined stream>" ; final int freeVideoFrames = null != videoFramesFree ? videoFramesFree.size() : 0; final int decVideoFrames = null != videoFramesDecoded ? videoFramesDecoded.size() : 0; - final int video_scr_ms = av_scr.get(Clock.currentMillis()); + final int video_scr_ms = av_scr.getCurrent(); final String camPath = null != cameraPath ? ", camera: "+cameraPath : ""; return getClass().getSimpleName()+"["+state+", vSCR "+video_scr_ms+", "+getChapters().length+" chapters, duration "+tt+", frames[p "+presentedFrameCount+", d "+decodedFrameCount+", t "+videoFrames+", z "+nullFrameCount+" / "+maxNullFrameCountUntilEOS+"], "+ "speed "+playSpeed+", "+bps_stream+" bps, hasSW "+(null!=streamWorker)+ ", Texture[count "+textureCount+", free "+freeVideoFrames+", dec "+decVideoFrames+", tagt "+toHexString(textureTarget)+", ifmt "+toHexString(textureInternalFormat)+", fmt "+toHexString(textureFormat)+", type "+toHexString(textureType)+"], "+ - "Video[id "+vid+"/"+Arrays.toString(v_streams)+"/"+Arrays.toString(v_langs)+", <"+vcodec+">, "+width+"x"+height+", glOrient "+isInGLOrientation+", "+fps+" fps, "+frame_duration+" fdur, "+bps_video+" bps], "+ - "Audio[id "+aid+"/"+Arrays.toString(a_streams)+"/"+Arrays.toString(a_langs)+", <"+acodec+">, "+bps_audio+" bps, "+audioFrames+" frames], "+ - "Subs[id "+sid+"/"+Arrays.toString(s_streams)+"/"+Arrays.toString(s_langs)+", <"+scodec+">], uri "+loc+camPath+"]"; + "Video[id "+vid+"/"+Arrays.toString(v_streams)+"/"+Arrays.toString(v_langs)+", "+vcodecID+"/'"+vcodec+"', "+width+"x"+height+", glOrient "+isInGLOrientation+", "+fps+" fps, "+frame_duration+" fdur, "+bps_video+" bps], "+ + "Audio[id "+aid+"/"+Arrays.toString(a_streams)+"/"+Arrays.toString(a_langs)+", "+acodecID+"/'"+acodec+"', "+bps_audio+" bps, "+audioFrames+" frames], "+ + "Subs[id "+sid+"/"+Arrays.toString(s_streams)+"/"+Arrays.toString(s_langs)+", "+scodecID+"/'"+scodec+"'], uri "+loc+camPath+"]"; } @Override diff --git a/src/jogl/classes/jogamp/opengl/util/av/NullGLMediaPlayer.java b/src/jogl/classes/jogamp/opengl/util/av/NullGLMediaPlayer.java index 44031372f..b8394dace 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/NullGLMediaPlayer.java +++ b/src/jogl/classes/jogamp/opengl/util/av/NullGLMediaPlayer.java @@ -39,6 +39,7 @@ import com.jogamp.common.av.PTS; import com.jogamp.common.nio.Buffers; import com.jogamp.common.os.Clock; import com.jogamp.common.util.IOUtil; +import com.jogamp.opengl.util.av.CodecID; import com.jogamp.opengl.util.av.GLMediaPlayer; import com.jogamp.opengl.util.texture.Texture; import com.jogamp.opengl.util.texture.TextureData; @@ -91,9 +92,9 @@ public class NullGLMediaPlayer extends GLMediaPlayerImpl { } @Override - protected final int getNextTextureImpl(final GL gl, final TextureFrame nextFrame) { + protected final int getNextTextureImpl(final GL gl, final TextureFrame vFrame, final Texture sTex, final boolean[] sTexUsed) { final int pts = getLastBufferedAudioPTS(); - nextFrame.setPTS( pts ); + vFrame.setPTS( pts ); return pts; } @@ -154,7 +155,7 @@ public class NullGLMediaPlayer extends GLMediaPlayerImpl { new int[0], new String[0], GLMediaPlayer.STREAM_ID_NONE, new int[0], new String[0], GLMediaPlayer.STREAM_ID_NONE, texData.getWidth(), texData.getHeight(), 0, 0, 0, _fps, _totalFrames, 0, _duration, - "png-static", null, null); + "png-static", null, null, CodecID.toFFmpeg(CodecID.PNG), -1, -1); } @Override protected final void initGLImpl(final GL gl) throws IOException, GLException { diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java index cfe0f72af..e33822697 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java @@ -52,14 +52,15 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { private static final List<String> glueLibNames = new ArrayList<String>(); // none - private static final int symbolCount = 63; + private static final int symbolCount = 67; private static final String[] symbolNames = { "avutil_version", "avformat_version", "avcodec_version", "avdevice_version", // (opt) "swresample_version", - /* 5 */ + "swscale_version", // (opt) + /* 6 */ // libavcodec "avcodec_close", @@ -82,7 +83,7 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { "avcodec_receive_frame", // 57 "avcodec_decode_subtitle2", // 52.23.0 "avsubtitle_free", // 52.82.0 - /* +20 = 25 */ + /* +20 = 26 */ // libavutil "av_pix_fmt_desc_get", // >= lavu 51.45 @@ -102,7 +103,7 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { "av_channel_layout_uninit", // >= 59 (opt) "av_channel_layout_describe", // >= 59 (opt) "av_opt_set_chlayout", // >= 59 - /* +17 = 42 */ + /* +17 = 43 */ // libavformat "avformat_alloc_context", @@ -119,11 +120,11 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { "avformat_network_init", // 53.13.0 (opt) "avformat_network_deinit", // 53.13.0 (opt) "avformat_find_stream_info", // 53.3.0 (opt) - /* +14 = 56 */ + /* +14 = 57 */ // libavdevice "avdevice_register_all", // supported in all versions (opt) - /* +1 = 57 */ + /* +1 = 58 */ // libswresample "av_opt_set_sample_fmt", // actually lavu .. but exist only w/ swresample! @@ -132,7 +133,13 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { "swr_free", "swr_convert", "swr_get_out_samples", - /* +6 = 63 */ + /* +6 = 64 */ + + // libswscale + "sws_getCachedContext", // opt + "sws_scale", // opt + "sws_freeContext", // opt + /* +3 = 67 */ }; // optional symbol names @@ -158,15 +165,22 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { "swr_free", "swr_convert", "swr_get_out_samples", + + // libswscale + "swscale_version", // opt + "sws_getCachedContext", // opt + "sws_scale", // opt + "sws_freeContext", // opt }; - /** 5: util, format, codec, device, swresample */ - private static final int LIB_COUNT = 5; + /** 6: util, format, codec, device, swresample, swscale */ + private static final int LIB_COUNT = 6; private static final int LIB_IDX_UTI = 0; private static final int LIB_IDX_FMT = 1; private static final int LIB_IDX_COD = 2; private static final int LIB_IDX_DEV = 3; private static final int LIB_IDX_SWR = 4; + private static final int LIB_IDX_SWS = 5; /** util, format, codec, device, swresample */ private static final boolean[] libLoaded = new boolean[LIB_COUNT]; @@ -200,6 +214,7 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { static final VersionedLib avCodec; static final VersionedLib avDevice; static final VersionedLib swResample; + static final VersionedLib swScale; private static final FFMPEGNatives natives; private static final PrivilegedAction<DynamicLibraryBundle> privInitSymbolsAction = new PrivilegedAction<DynamicLibraryBundle>() { @@ -226,7 +241,7 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { } }; /** - * @param versions 5: util, format, codec, device, swresample + * @param versions 6: util, format, codec, device, swresample, swscale * @return */ private static final boolean initSymbols(final VersionNumber[] versions, final List<NativeLibrary> nativeLibs) { @@ -271,7 +286,11 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { versions[LIB_IDX_DEV] = new VersionNumber(0, 0, 0); } versions[LIB_IDX_SWR] = FFMPEGStaticNatives.getAVVersion(FFMPEGStaticNatives.getAvVersion0(symbolAddr[LIB_IDX_SWR])); - + if( 0 != symbolAddr[LIB_IDX_SWS] ) { + versions[LIB_IDX_SWS] = FFMPEGStaticNatives.getAVVersion(FFMPEGStaticNatives.getAvVersion0(symbolAddr[LIB_IDX_SWS])); + } else { + versions[LIB_IDX_SWS] = new VersionNumber(0, 0, 0); + } return res; } @@ -280,7 +299,7 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { GLProfile.initSingleton(); boolean _ready = false; - /** 5: util, format, codec, device, swresample */ + /** 6: util, format, codec, device, swresample, swscale */ final VersionNumber[] _versions = new VersionNumber[LIB_COUNT]; final List<NativeLibrary> _nativeLibs = new ArrayList<NativeLibrary>(); try { @@ -294,6 +313,7 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { avCodec = new VersionedLib(_nativeLibs.get(LIB_IDX_COD), _versions[LIB_IDX_COD]); avDevice = new VersionedLib(_nativeLibs.get(LIB_IDX_DEV), _versions[LIB_IDX_DEV]); swResample = new VersionedLib(_nativeLibs.get(LIB_IDX_SWR), _versions[LIB_IDX_SWR]); + swScale = new VersionedLib(_nativeLibs.get(LIB_IDX_SWS), _versions[LIB_IDX_SWS]); if(!libsCFUSLoaded) { String missing = ""; if( !libLoaded[LIB_IDX_COD] ) { @@ -321,13 +341,20 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { final int avCodecMajor = avCodec.version.getMajor(); final int avDeviceMajor = avDevice.version.getMajor(); final int swResampleMajor = swResample.version.getMajor(); - if( avCodecMajor == 58 && avFormatMajor == 58 && ( avDeviceMajor == 58 || avDeviceMajor == 0 ) && avUtilMajor == 56 && swResampleMajor == 3) { + final int swScaleMajor = swScale.version.getMajor(); + if( avCodecMajor == 58 && avFormatMajor == 58 && ( avDeviceMajor == 58 || avDeviceMajor == 0 ) && avUtilMajor == 56 && + swResampleMajor == 3 && ( swScaleMajor == 5 || swScaleMajor == 0 ) ) + { // Exact match: ffmpeg 4.x.y natives = new FFMPEGv0400Natives(); - } else if( avCodecMajor == 59 && avFormatMajor == 59 && ( avDeviceMajor == 59 || avDeviceMajor == 0 ) && avUtilMajor == 57 && swResampleMajor == 4) { + } else if( avCodecMajor == 59 && avFormatMajor == 59 && ( avDeviceMajor == 59 || avDeviceMajor == 0 ) && avUtilMajor == 57 && + swResampleMajor == 4 && ( swScaleMajor == 6 || swScaleMajor == 0 ) ) + { // Exact match: ffmpeg 5.x.y natives = new FFMPEGv0500Natives(); - } else if( avCodecMajor == 60 && avFormatMajor == 60 && ( avDeviceMajor == 60 || avDeviceMajor == 0 ) && avUtilMajor == 58 && swResampleMajor == 4) { + } else if( avCodecMajor == 60 && avFormatMajor == 60 && ( avDeviceMajor == 60 || avDeviceMajor == 0 ) && avUtilMajor == 58 && + swResampleMajor == 4 && ( swScaleMajor == 7 || swScaleMajor == 0 ) ) + { // Exact match: ffmpeg 6.x.y natives = new FFMPEGv0600Natives(); } else { @@ -351,6 +378,7 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { static boolean libsLoaded() { return libsCFUSLoaded; } static boolean avDeviceLoaded() { return libLoaded[LIB_IDX_DEV]; } static boolean swResampleLoaded() { return libLoaded[LIB_IDX_SWR]; } + static boolean swScaleLoaded() { return libLoaded[LIB_IDX_SWS]; } static FFMPEGNatives getNatives() { return natives; } static boolean initSingleton() { return ready; } @@ -390,7 +418,7 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { public final List<List<String>> getToolLibNames() { final List<List<String>> libsList = new ArrayList<List<String>>(); - // 5: util, format, codec, device, swresample + // 6: util, format, codec, device, swresample, swscale final List<String> avutil = new ArrayList<String>(); if( FFMPEGMediaPlayer.PREFER_SYSTEM_LIBS ) { @@ -490,12 +518,31 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { } libsList.add(swresample); + final List<String> swscale = new ArrayList<String>(); + if( FFMPEGMediaPlayer.PREFER_SYSTEM_LIBS ) { + swscale.add("swscale"); // system default + } else { + swscale.add("internal_swscale");// internal + } + swscale.add("libswscale.so.7"); // ffmpeg 6.[0-x] + swscale.add("libswscale.so.6"); // ffmpeg 5.[0-x] + swscale.add("libswscale.so.5"); // ffmpeg 4.[0-x] (Debian-11) + + swscale.add("swscale-7"); // ffmpeg 6.[0-x] + swscale.add("swscale-6"); // ffmpeg 5.[0-x] + swscale.add("swscale-5"); // ffmpeg 4.[0-x] + if( FFMPEGMediaPlayer.PREFER_SYSTEM_LIBS ) { + swscale.add("internal_swscale");// internal + } else { + swscale.add("swscale"); // system default + } + libsList.add(swscale); return libsList; } @Override public List<String> getSymbolForToolLibPath() { - // 5: util, format, codec, device, swresample - return Arrays.asList("av_free", "av_read_frame", "avcodec_close", "avdevice_register_all", "swr_convert"); + // 6: util, format, codec, device, swresample, swscale + return Arrays.asList("av_free", "av_read_frame", "avcodec_close", "avdevice_register_all", "swr_convert", "swscale_version"); } @Override diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java index e6784273e..974bdc10b 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java @@ -30,7 +30,6 @@ package jogamp.opengl.util.av.impl; import java.io.IOException; import java.io.PrintStream; -import java.nio.ByteBuffer; import java.security.PrivilegedAction; import com.jogamp.opengl.GL; @@ -41,21 +40,19 @@ import com.jogamp.common.av.AudioFormat; import com.jogamp.common.av.AudioSink; import com.jogamp.common.av.AudioSinkFactory; import com.jogamp.common.av.TimeFrameI; -import com.jogamp.common.os.Clock; import com.jogamp.common.util.IOUtil; import com.jogamp.common.util.PropertyAccess; import com.jogamp.common.util.SecurityUtil; import com.jogamp.gluegen.runtime.ProcAddressTable; import com.jogamp.opengl.util.GLPixelStorageModes; -import com.jogamp.opengl.util.av.ASSEventLine; import com.jogamp.opengl.util.av.GLMediaPlayer; +import com.jogamp.opengl.util.av.VideoPixelFormat; import com.jogamp.opengl.util.texture.Texture; import jogamp.common.os.PlatformPropsImpl; import jogamp.opengl.GLContextImpl; import jogamp.opengl.util.av.AudioSampleFormat; import jogamp.opengl.util.av.GLMediaPlayerImpl; -import jogamp.opengl.util.av.VideoPixelFormat; import jogamp.opengl.util.av.impl.FFMPEGDynamicLibraryBundleInfo.VersionedLib; /*** @@ -98,6 +95,7 @@ import jogamp.opengl.util.av.impl.FFMPEGDynamicLibraryBundleInfo.VersionedLib; * <li>avutil</li> * <li>avdevice (optional for video input devices)</li> * <li>swresample</li> + * <li>swscale (optional for bitmap'ed subtitles)</li> * </ul> * </p> * @@ -105,10 +103,10 @@ import jogamp.opengl.util.av.impl.FFMPEGDynamicLibraryBundleInfo.VersionedLib; * <p> * Currently we are binary compatible with the following major versions: * <table border="1"> - * <tr><th>ffmpeg</th><th>avcodec</th><th>avformat</th><th>avdevice</th><th>avutil</th><th>swresample</th> <th>FFMPEG* class</th></tr> - * <tr><td>4</td> <td>58</td> <td>58</td> <td>58</td> <td>56</td> <td>03</td> <td>FFMPEGv0400</td></tr> - * <tr><td>5</td> <td>59</td> <td>59</td> <td>59</td> <td>57</td> <td>04</td> <td>FFMPEGv0500</td></tr> - * <tr><td>6</td> <td>60</td> <td>60</td> <td>60</td> <td>58</td> <td>04</td> <td>FFMPEGv0600</td></tr> + * <tr><th>ffmpeg</th><th>avcodec</th><th>avformat</th><th>avdevice</th><th>avutil</th><th>swresample</th><th>swscale</th> <th>FFMPEG* class</th></tr> + * <tr><td>4</td> <td>58</td> <td>58</td> <td>58</td> <td>56</td> <td>03</td> <td>05</td> <td>FFMPEGv0400</td></tr> + * <tr><td>5</td> <td>59</td> <td>59</td> <td>59</td> <td>57</td> <td>04</td> <td>06</td> <td>FFMPEGv0500</td></tr> + * <tr><td>6</td> <td>60</td> <td>60</td> <td>60</td> <td>58</td> <td>04</td> <td>07</td> <td>FFMPEGv0600</td></tr> * </table> * </p> * <p> @@ -203,6 +201,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { private static final int avCodecMajorVersionCC; private static final int avDeviceMajorVersionCC; private static final int swResampleMajorVersionCC; + private static final int swScaleMajorVersionCC; private static final boolean available; static { @@ -220,18 +219,21 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { avUtilMajorVersionCC = natives.getAvUtilMajorVersionCC0(); avDeviceMajorVersionCC = natives.getAvDeviceMajorVersionCC0(); swResampleMajorVersionCC = natives.getSwResampleMajorVersionCC0(); + swScaleMajorVersionCC = natives.getSwScaleMajorVersionCC0(); } else { avUtilMajorVersionCC = 0; avFormatMajorVersionCC = 0; avCodecMajorVersionCC = 0; avDeviceMajorVersionCC = 0; swResampleMajorVersionCC = 0; + swScaleMajorVersionCC = 0; } final VersionedLib avCodec = FFMPEGDynamicLibraryBundleInfo.avCodec; final VersionedLib avFormat = FFMPEGDynamicLibraryBundleInfo.avFormat; final VersionedLib avUtil = FFMPEGDynamicLibraryBundleInfo.avUtil; final VersionedLib avDevice = FFMPEGDynamicLibraryBundleInfo.avDevice; final VersionedLib swResample = FFMPEGDynamicLibraryBundleInfo.swResample; + final VersionedLib swScale = FFMPEGDynamicLibraryBundleInfo.swScale; // final boolean avDeviceLoaded = FFMPEGDynamicLibraryBundleInfo.avDeviceLoaded(); // final boolean swResampleLoaded = FFMPEGDynamicLibraryBundleInfo.swResampleLoaded(); final int avCodecMajor = avCodec.version.getMajor(); @@ -239,11 +241,13 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { final int avUtilMajor = avUtil.version.getMajor(); final int avDeviceMajor = avDevice.version.getMajor(); final int swResampleMajor = swResample.version.getMajor(); + final int swScaleMajor = swScale.version.getMajor(); libAVVersionGood = avCodecMajorVersionCC == avCodecMajor && avFormatMajorVersionCC == avFormatMajor && avUtilMajorVersionCC == avUtilMajor && ( avDeviceMajorVersionCC == avDeviceMajor || 0 == avDeviceMajor ) && - swResampleMajorVersionCC == swResampleMajor; + swResampleMajorVersionCC == swResampleMajor && + ( swScaleMajorVersionCC == swScaleMajor || 0 == swScaleMajor ); if( !libAVVersionGood ) { System.err.println("FFmpeg Not Matching Compile-Time / Runtime Major-Version"); } @@ -257,6 +261,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { avCodecMajorVersionCC = 0; avDeviceMajorVersionCC = 0; swResampleMajorVersionCC = 0; + swScaleMajorVersionCC = 0; libAVVersionGood = false; } available = libAVGood && libAVVersionGood && null != natives; @@ -270,6 +275,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { out.println("FFmpeg Util : "+FFMPEGDynamicLibraryBundleInfo.avUtil+" [cc "+avUtilMajorVersionCC+"]"); out.println("FFmpeg Device : "+FFMPEGDynamicLibraryBundleInfo.avDevice+" [cc "+avDeviceMajorVersionCC+", loaded "+FFMPEGDynamicLibraryBundleInfo.avDeviceLoaded()+"]"); out.println("FFmpeg Resample: "+FFMPEGDynamicLibraryBundleInfo.swResample+" [cc "+swResampleMajorVersionCC+", loaded "+FFMPEGDynamicLibraryBundleInfo.swResampleLoaded()+"]"); + out.println("FFmpeg Scale : "+FFMPEGDynamicLibraryBundleInfo.swScale+" [cc "+swScaleMajorVersionCC+", loaded "+FFMPEGDynamicLibraryBundleInfo.swScaleLoaded()+"]"); out.println("FFmpeg Class : "+(null!= natives ? natives.getClass().getSimpleName() : "n/a")); } @Override @@ -447,6 +453,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { @Override public Object run() { final ProcAddressTable pt = ctx.getGLProcAddressTable(); + final long procAddrGLTexImage2D = pt.getAddressFor("glTexImage2D"); final long procAddrGLTexSubImage2D = pt.getAddressFor("glTexSubImage2D"); final long procAddrGLGetError = pt.getAddressFor("glGetError"); final long procAddrGLFlush = pt.getAddressFor("glFlush"); @@ -458,7 +465,9 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { procAddrGLEnable = 0; } final long procAddrGLBindTexture = pt.getAddressFor("glBindTexture"); - natives.setGLFuncs0(moviePtr, procAddrGLTexSubImage2D, procAddrGLGetError, procAddrGLFlush, procAddrGLFinish, procAddrGLEnable, procAddrGLBindTexture); + natives.setGLFuncs0(moviePtr, procAddrGLTexImage2D, procAddrGLTexSubImage2D, + procAddrGLGetError, procAddrGLFlush, procAddrGLFinish, + procAddrGLEnable, procAddrGLBindTexture, gl.isNPOTTextureAvailable()); return null; } } ); audioQueueSize = AudioSink.DefaultQueueSizeWithVideo; @@ -983,7 +992,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { } @Override - protected final int getNextTextureImpl(final GL gl, final TextureFrame nextFrame) { + protected final int getNextTextureImpl(final GL gl, final TextureFrame vFrame, final Texture sTex, final boolean[] sTexUsed) { if(0==moviePtr) { throw new GLException("FFMPEG native instance null"); } @@ -994,15 +1003,32 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { // final Texture tex = nextFrame.getTexture(); // tex.enable(gl); // tex.bind(gl); - vTexID = nextFrame.getTexture().getTextureObject(); + vTexID = vFrame.getTexture().getTextureObject(); } /** Try decode up to 10 packets to find one containing video. */ for(int i=0; TimeFrameI.INVALID_PTS == vPTS && 10 > i; i++) { - vPTS = natives.readNextPacket0(moviePtr, getTextureTarget(), vTexID, getTextureFormat(), getTextureType(), GL.GL_TEXTURE_2D, 0); + int sTexID = 0; // invalid + int sTexWidth = 0; + int sTexHeight = 0; + if( null != gl && !sTexUsed[0] ) { + // glEnable() and glBindTexture() are performed in native readNextPacket0() + // final Texture tex = nextFrame.getTexture(); + // tex.enable(gl); + // tex.bind(gl); + vTexID = vFrame.getTexture().getTextureObject(); + if( null != sTex ) { + sTexID = sTex.getTextureObject(); + // FIXME: Disabled in native code, buggy on AMD GPU corrupting texture content + sTexWidth = sTex.getWidth(); + sTexHeight = sTex.getHeight(); + } + } + vPTS = natives.readNextPacket0(moviePtr, getTextureTarget(), vTexID, getTextureFormat(), getTextureType(), + GL.GL_TEXTURE_2D, sTexID, sTexWidth, sTexHeight, sTex, sTexUsed); } - if( null != nextFrame ) { - nextFrame.setPTS(vPTS); + if( null != vFrame ) { + vFrame.setPTS(vPTS); } return vPTS; } diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGNatives.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGNatives.java index c28028bde..2fe78cbc6 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGNatives.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGNatives.java @@ -1,5 +1,5 @@ /** - * Copyright 2013-2023 JogAmp Community. All rights reserved. + * Copyright 2013-2024 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: @@ -28,6 +28,7 @@ package jogamp.opengl.util.av.impl; import com.jogamp.opengl.util.texture.TextureSequence.TextureFrame; +import jogamp.opengl.util.av.GLMediaPlayerImpl; /* pp */ abstract class FFMPEGNatives { @@ -42,6 +43,7 @@ import com.jogamp.opengl.util.texture.TextureSequence.TextureFrame; abstract int getAvCodecMajorVersionCC0(); abstract int getAvDeviceMajorVersionCC0(); abstract int getSwResampleMajorVersionCC0(); + abstract int getSwScaleMajorVersionCC0(); abstract long createInstance0(FFMPEGMediaPlayer upstream, boolean verbose); abstract void destroyInstance0(long moviePtr); @@ -67,8 +69,8 @@ import com.jogamp.opengl.util.texture.TextureSequence.TextureFrame; int aid, int aMaxChannelCount, int aPrefSampleRate, int sid); - abstract void setGLFuncs0(long moviePtr, long procAddrGLTexSubImage2D, long procAddrGLGetError, long procAddrGLFlush, long procAddrGLFinish, - long procAddrGLEnable, long procAddrGLBindTexture); + abstract void setGLFuncs0(long moviePtr, long procAddrGLTexImage2D, long procAddrGLTexSubImage2D, long procAddrGLGetError, long procAddrGLFlush, + long procAddrGLFinish, long procAddrGLEnable, long procAddrGLBindTexture, boolean hasNPOT); abstract int getVideoPTS0(long moviePtr); @@ -89,9 +91,13 @@ import com.jogamp.opengl.util.texture.TextureSequence.TextureFrame; * @param vTexType video texture data type * @param sTexTarget subtitle texture target * @param sTexID subtitle texture ID/name + * @param sTexWidthPre current texture size, may be increased and notified via {@link GLMediaPlayerImpl#pushSubtitleTex(Object, int, int, int, int, int, int, int, int, int)} + * @param sTexHeightPre current texture size, may be increased and notified via {@link GLMediaPlayerImpl#pushSubtitleTex(Object, int, int, int, int, int, int, int, int, int)} + * @param sTexObj subtitle texture Object to be passed to caller + * @param sTexUsed result value, if {@code sTexObj} is being used {@code true} must be written into it * @return resulting current video PTS, or {@link TextureFrame#INVALID_PTS} */ - abstract int readNextPacket0(long moviePtr, int vTexTarget, int vTexID, int vTexFmt, int vTexType, int sTexTarget, int sTexID); + abstract int readNextPacket0(long moviePtr, int vTexTarget, int vTexID, int vTexFmt, int vTexType, int sTexTarget, int sTexID, int sTexWidthPre, int sTexHeightPre, Object sTexObj, boolean[] sTexUsed); abstract int play0(long moviePtr); abstract int pause0(long moviePtr); diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGStaticNatives.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGStaticNatives.java index 65a7e3618..854de7ad7 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGStaticNatives.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGStaticNatives.java @@ -1,5 +1,5 @@ /** - * Copyright 2013 JogAmp Community. All rights reserved. + * Copyright 2013-2024 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: diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv0400Natives.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv0400Natives.java index 8a567fa94..a7e6de270 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv0400Natives.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv0400Natives.java @@ -1,5 +1,5 @@ /** - * Copyright 2015-2023 JogAmp Community. All rights reserved. + * Copyright 2015-2024 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: @@ -47,6 +47,9 @@ class FFMPEGv0400Natives extends FFMPEGNatives { native int getSwResampleMajorVersionCC0(); @Override + native int getSwScaleMajorVersionCC0(); + + @Override native long createInstance0(FFMPEGMediaPlayer upstream, boolean verbose); @Override @@ -56,7 +59,7 @@ class FFMPEGv0400Natives extends FFMPEGNatives { native void setStream0(long moviePtr, String url, boolean isCameraInput, int vid, String sizes, int vWidth, int vHeight, int vRate, int aid, int aMaxChannelCount, int aPrefSampleRate, int sid); @Override - native void setGLFuncs0(long moviePtr, long procAddrGLTexSubImage2D, long procAddrGLGetError, long procAddrGLFlush, long procAddrGLFinish, long procAddrGLEnable, long procAddrGLBindTexture); + native void setGLFuncs0(long moviePtr, long procAddrGLTexImage2D, long procAddrGLTexSubImage2D, long procAddrGLGetError, long procAddrGLFlush, long procAddrGLFinish, long procAddrGLEnable, long procAddrGLBindTexture, boolean hasNPOT); @Override native int getVideoPTS0(long moviePtr); @@ -65,7 +68,7 @@ class FFMPEGv0400Natives extends FFMPEGNatives { native int getAudioPTS0(long moviePtr); @Override - native int readNextPacket0(long moviePtr, int vTexTarget, int vTexID, int vTexFmt, int vTexType, int sTexTarget, int sTexID); + native int readNextPacket0(long moviePtr, int vTexTarget, int vTexID, int vTexFmt, int vTexType, int sTexTarget, int sTexID, int sTexWidthPre, int sTexHeightPre, Object sTexObj, boolean[] sTexUsed); @Override native int play0(long moviePtr); diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv0500Natives.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv0500Natives.java index 09ae6b7b7..7268b0627 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv0500Natives.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv0500Natives.java @@ -1,5 +1,5 @@ /** - * Copyright 2015-2023 JogAmp Community. All rights reserved. + * Copyright 2015-2024 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: @@ -47,6 +47,9 @@ class FFMPEGv0500Natives extends FFMPEGNatives { native int getSwResampleMajorVersionCC0(); @Override + native int getSwScaleMajorVersionCC0(); + + @Override native long createInstance0(FFMPEGMediaPlayer upstream, boolean verbose); @Override @@ -56,7 +59,7 @@ class FFMPEGv0500Natives extends FFMPEGNatives { native void setStream0(long moviePtr, String url, boolean isCameraInput, int vid, String sizes, int vWidth, int vHeight, int vRate, int aid, int aMaxChannelCount, int aPrefSampleRate, int sid); @Override - native void setGLFuncs0(long moviePtr, long procAddrGLTexSubImage2D, long procAddrGLGetError, long procAddrGLFlush, long procAddrGLFinish, long procAddrGLEnable, long procAddrGLBindTexture); + native void setGLFuncs0(long moviePtr, long procAddrGLTexImage2D, long procAddrGLTexSubImage2D, long procAddrGLGetError, long procAddrGLFlush, long procAddrGLFinish, long procAddrGLEnable, long procAddrGLBindTexture, boolean hasNPOT); @Override native int getVideoPTS0(long moviePtr); @@ -65,7 +68,7 @@ class FFMPEGv0500Natives extends FFMPEGNatives { native int getAudioPTS0(long moviePtr); @Override - native int readNextPacket0(long moviePtr, int vTexTarget, int vTexID, int vTexFmt, int vTexType, int sTexTarget, int sTexID); + native int readNextPacket0(long moviePtr, int vTexTarget, int vTexID, int vTexFmt, int vTexType, int sTexTarget, int sTexID, int sTexWidthPre, int sTexHeightPre, Object sTexObj, boolean[] sTexUsed); @Override native int play0(long moviePtr); diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv0600Natives.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv0600Natives.java index 663e9cbd0..a87c98dbc 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv0600Natives.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv0600Natives.java @@ -1,5 +1,5 @@ /** - * Copyright 2015-2023 JogAmp Community. All rights reserved. + * Copyright 2015-2024 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: @@ -47,6 +47,9 @@ class FFMPEGv0600Natives extends FFMPEGNatives { native int getSwResampleMajorVersionCC0(); @Override + native int getSwScaleMajorVersionCC0(); + + @Override native long createInstance0(FFMPEGMediaPlayer upstream, boolean verbose); @Override @@ -56,7 +59,7 @@ class FFMPEGv0600Natives extends FFMPEGNatives { native void setStream0(long moviePtr, String url, boolean isCameraInput, int vid, String sizes, int vWidth, int vHeight, int vRate, int aid, int aMaxChannelCount, int aPrefSampleRate, int sid); @Override - native void setGLFuncs0(long moviePtr, long procAddrGLTexSubImage2D, long procAddrGLGetError, long procAddrGLFlush, long procAddrGLFinish, long procAddrGLEnable, long procAddrGLBindTexture); + native void setGLFuncs0(long moviePtr, long procAddrGLTexImage2D, long procAddrGLTexSubImage2D, long procAddrGLGetError, long procAddrGLFlush, long procAddrGLFinish, long procAddrGLEnable, long procAddrGLBindTexture, boolean hasNPOT); @Override native int getVideoPTS0(long moviePtr); @@ -65,7 +68,7 @@ class FFMPEGv0600Natives extends FFMPEGNatives { native int getAudioPTS0(long moviePtr); @Override - native int readNextPacket0(long moviePtr, int vTexTarget, int vTexID, int vTexFmt, int vTexType, int sTexTarget, int sTexID); + native int readNextPacket0(long moviePtr, int vTexTarget, int vTexID, int vTexFmt, int vTexType, int sTexTarget, int sTexID, int sTexWidthPre, int sTexHeightPre, Object sTexObj, boolean[] sTexUsed); @Override native int play0(long moviePtr); diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/OMXGLMediaPlayer.java b/src/jogl/classes/jogamp/opengl/util/av/impl/OMXGLMediaPlayer.java index 24b1ad4a9..f9786a5ab 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/OMXGLMediaPlayer.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/OMXGLMediaPlayer.java @@ -1,5 +1,5 @@ /** - * Copyright 2012 JogAmp Community. All rights reserved. + * Copyright 2012-2024 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: @@ -38,6 +38,7 @@ import com.jogamp.opengl.GLException; import com.jogamp.opengl.egl.EGL; import com.jogamp.opengl.util.av.GLMediaPlayer.State; +import com.jogamp.opengl.util.texture.Texture; import com.jogamp.opengl.util.texture.TextureSequence; import jogamp.opengl.util.av.EGLMediaPlayerImpl; @@ -105,7 +106,7 @@ public class OMXGLMediaPlayer extends EGLMediaPlayerImpl { } @Override - protected void initStreamImpl(final int vid, final int aid, int sid) throws IOException { + protected void initStreamImpl(final int vid, final int aid, final int sid) throws IOException { if(0==moviePtr) { throw new GLException("OMX native instance null"); } @@ -192,7 +193,7 @@ public class OMXGLMediaPlayer extends EGLMediaPlayerImpl { } @Override - protected int getNextTextureImpl(final GL gl, final TextureFrame nextFrame) { + protected int getNextTextureImpl(final GL gl, final TextureFrame vFrame, final Texture sTex, final boolean[] sTexUsed) { if(0==moviePtr) { throw new GLException("OMX native instance null"); } |