diff options
author | Sven Gothel <[email protected]> | 2023-12-30 21:18:16 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2023-12-30 21:18:16 +0100 |
commit | 68b2dad19bd6d84f18b22f967ce320b448e8a270 (patch) | |
tree | e4d49a1eadc12ef9a456f1eb1d5303eddfe192c8 | |
parent | c6e39c6e313a34688ca0164d7a34b6465e92396f (diff) |
GLMediaPlayer/FFMPEGMediaPlayer: Add chapter metadata support and use com.jogamp.common.av.PTS.millisToTimeStr(..)
Chapter metadata is now supported via our FFMPEGMediaPlayer implementation.
Added public method: 'Chapters[] GLMediaPlayer.getChapters()'
10 files changed, 184 insertions, 55 deletions
diff --git a/src/graphui/classes/com/jogamp/graph/ui/widgets/MediaPlayer.java b/src/graphui/classes/com/jogamp/graph/ui/widgets/MediaPlayer.java index 799a9e8ad..fd455695a 100644 --- a/src/graphui/classes/com/jogamp/graph/ui/widgets/MediaPlayer.java +++ b/src/graphui/classes/com/jogamp/graph/ui/widgets/MediaPlayer.java @@ -29,8 +29,8 @@ package com.jogamp.graph.ui.widgets; import java.io.IOException; import java.util.List; -import java.util.concurrent.TimeUnit; +import com.jogamp.common.av.PTS; import com.jogamp.common.net.Uri; import com.jogamp.common.os.Clock; import com.jogamp.common.util.InterruptSource; @@ -156,8 +156,11 @@ public class MediaPlayer extends Widget { // System.err.println("MediaButton AttributesChanges: "+eventMask+", when "+when); // System.err.println("MediaButton State: "+mp); if( eventMask.isSet(GLMediaPlayer.EventMask.Bit.Init) ) { - System.err.println(mp.toString()); ctrlSlider.setMinMax(new Vec2f(0, mp.getDuration()), 0); + System.err.println(mp.toString()); + for(final GLMediaPlayer.Chapter c : mp.getChapters()) { + System.err.println(c); + } } else if( eventMask.isSet(GLMediaPlayer.EventMask.Bit.Play) ) { playButton.setToggle(true); } else if( eventMask.isSet(GLMediaPlayer.EventMask.Bit.Pause) ) { @@ -257,7 +260,7 @@ public class MediaPlayer extends Widget { } @Override public void clicked(final RangeSlider w, final MouseEvent e) { - System.err.println("Clicked "+w.getName()+": "+millisToTimeStr(Math.round(w.getValue()), true)+"ms, "+(w.getValuePct()*100f)+"%"); + System.err.println("Clicked "+w.getName()+": "+PTS.millisToTimeStr(Math.round(w.getValue()), true)+"ms, "+(w.getValuePct()*100f)+"%"); seekPlayer( Math.round( w.getValue() ) ); } @Override @@ -274,7 +277,7 @@ public class MediaPlayer extends Widget { @Override public void dragged(final RangeSlider w, final float old_val, final float val, final float old_val_pct, final float val_pct) { - System.err.println("Dragged "+w.getName()+": "+millisToTimeStr(Math.round(val), true)+"ms, "+(val_pct*100f)+"%"); + System.err.println("Dragged "+w.getName()+": "+PTS.millisToTimeStr(Math.round(val), true)+"ms, "+(val_pct*100f)+"%"); seekPlayer( Math.round( val ) ); } }); @@ -512,35 +515,6 @@ public class MediaPlayer extends Widget { } } - public static String millisToTimeStr(final long millis, final boolean addFractions) { - final long h = TimeUnit.MILLISECONDS.toHours(millis); - final long m = TimeUnit.MILLISECONDS.toMinutes(millis); - if( addFractions ) { - if( 0 < h ) { - return String.format("%02d:%02d:%02d.%02d", - h, - m - TimeUnit.HOURS.toMinutes(h), - TimeUnit.MILLISECONDS.toSeconds(millis) - TimeUnit.MINUTES.toSeconds(m), - millis%1000); - } else { - return String.format("%02d:%02d.%02d", - m, - TimeUnit.MILLISECONDS.toSeconds(millis) - TimeUnit.MINUTES.toSeconds(m), - millis%1000); - } - } else { - if( 0 < h ) { - return String.format("%02d:%02d:%02d", - h, - m - TimeUnit.HOURS.toMinutes(h), - TimeUnit.MILLISECONDS.toSeconds(millis) - TimeUnit.MINUTES.toSeconds(m)); - } else { - return String.format("%02d:%02d", - m, - TimeUnit.MILLISECONDS.toSeconds(millis) - TimeUnit.MINUTES.toSeconds(m)); - } - } - } public static String getInfo(final long currentMillis, final GLMediaPlayer mPlayer, final boolean full) { return getInfo(mPlayer.getPTS().get(currentMillis), mPlayer.getDuration(), mPlayer, full); } @@ -566,7 +540,7 @@ public class MediaPlayer extends Widget { final float pct = (float)ptsMS / (float)durationMS; if( full ) { final String text1 = String.format("%s / %s (%.0f %%), %s (%01.1fx, vol %1.2f), A/R %0.2f, fps %02.1f", - millisToTimeStr(ptsMS, false), millisToTimeStr(durationMS, false), pct*100, + PTS.millisToTimeStr(ptsMS, false), PTS.millisToTimeStr(durationMS, false), pct*100, mPlayer.getState().toString().toLowerCase(), mPlayer.getPlaySpeed(), mPlayer.getAudioVolume(), aspect, mPlayer.getFramerate()); final String text2 = String.format("audio: id %d, kbps %d, codec %s", mPlayer.getAID(), mPlayer.getAudioBitrate()/1000, mPlayer.getAudioCodec()); @@ -575,7 +549,7 @@ public class MediaPlayer extends Widget { return text1+"\n"+text2+"\n"+text3+"\n"+name; } else { final String text1 = String.format("%s / %s (%.0f %%), %s (%01.1fx, vol %1.2f), A/R %.2f", - millisToTimeStr(ptsMS, false), millisToTimeStr(durationMS, false), pct*100, + PTS.millisToTimeStr(ptsMS, false), PTS.millisToTimeStr(durationMS, false), pct*100, mPlayer.getState().toString().toLowerCase(), mPlayer.getPlaySpeed(), mPlayer.getAudioVolume(), aspect); return text1+"\n"+name; } @@ -586,6 +560,6 @@ public class MediaPlayer extends Widget { public static String getMultilineTime(final int ptsMS, final int durationMS) { final float pct = (float)ptsMS / (float)durationMS; return String.format("%.0f %%%n%s%n%s", - pct*100, millisToTimeStr(ptsMS, false), millisToTimeStr(durationMS, false)); + pct*100, PTS.millisToTimeStr(ptsMS, false), PTS.millisToTimeStr(durationMS, false)); } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java b/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java index 7be088c3b..e5351af03 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java +++ b/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java @@ -36,12 +36,10 @@ import jogamp.opengl.Debug; import java.io.PrintStream; import java.util.List; -import com.jogamp.common.av.AudioFormat; import com.jogamp.common.av.AudioSink; import com.jogamp.common.av.PTS; import com.jogamp.common.av.TimeFrameI; import com.jogamp.common.net.Uri; -import com.jogamp.common.os.Clock; import com.jogamp.opengl.util.texture.Texture; import com.jogamp.opengl.util.texture.TextureSequence; @@ -273,6 +271,25 @@ public interface GLMediaPlayer extends TextureSequence { } } + /** Chapter meta-data of stream, see {@link GLMediaPlayer#getChapters()}. */ + public static class Chapter { + /** Chapter ID */ + public final int id; + /** Chapter start PTS in ms */ + public final int start; + /** Chapter end PTS in ms */ + public final int end; + /** Chapter title */ + public final String title; + public Chapter(final int i, final int s, final int e, final String t) { + id = i; start = s; end = e; title = t; + } + @Override + public String toString() { + return String.format("%02d: [%s .. %s] %s", id, PTS.millisToTimeStr(start), PTS.millisToTimeStr(end), title); + } + } + /** * {@inheritDoc} * <p> @@ -770,11 +787,14 @@ public interface GLMediaPlayer extends TextureSequence { /** Returns the height of the video. */ public int getHeight(); - /** Returns a string represantation of this player, incl. state and audio/video details. */ + /** Returns {@link Chapter} meta-data from stream, available after {@link State#Initialized} is reached after issuing {@link #playStream(Uri, int, int, int)}. */ + public Chapter[] getChapters(); + + /** Returns a string representation of this player, incl. state and audio/video details. */ @Override public String toString(); - /** Returns a string represantation of this player's performance values. */ + /** Returns a string representation of this player's performance values. */ public String getPerfString(); /** Adds a {@link GLMediaEventListener} to this player. */ diff --git a/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java b/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java index e493689da..e1472102c 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java +++ b/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java @@ -1718,6 +1718,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { return; } if( wasUninitialized ) { + updateMetadata(); if( DEBUG ) { logout.println("XXX Initialize @ updateAttributes: "+this); } @@ -1785,15 +1786,21 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { @Override public final int getHeight() { return height; } + /** Implementation shall update metadata, e.g. {@link #getChapters()} if supported. Called after {@link State#Initialized} is reached. */ + protected void updateMetadata() {} + + @Override + public Chapter[] getChapters() { return new Chapter[0]; } + @Override public final String toString() { - final float tt = getDuration() / 1000.0f; + final String tt = PTS.millisToTimeStr(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 String camPath = null != cameraPath ? ", camera: "+cameraPath : ""; - return getClass().getSimpleName()+"["+state+", vSCR "+video_scr_ms+", frames[p "+presentedFrameCount+", d "+decodedFrameCount+", t "+videoFrames+" ("+tt+" s), z "+nullFrameCount+" / "+maxNullFrameCountUntilEOS+"], "+ + 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+", <"+vcodec+">, "+width+"x"+height+", glOrient "+isInGLOrientation+", "+fps+" fps, "+frame_duration+" fdur, "+bps_video+" bps], "+ 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 9b1782993..05a0ddb64 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java @@ -52,7 +52,7 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { private static final List<String> glueLibNames = new ArrayList<String>(); // none - private static final int symbolCount = 60; + private static final int symbolCount = 61; private static final String[] symbolNames = { "avutil_version", "avformat_version", @@ -91,6 +91,7 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { "av_samples_get_buffer_size", "av_get_bytes_per_sample", // 51.4.0 "av_opt_set_int", // 51.12.0 + "av_dict_iterate", // 57.42.100 "av_dict_get", "av_dict_count", // 54.* (opt) "av_dict_set", @@ -99,7 +100,7 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { "av_channel_layout_uninit", // >= 59 (opt) "av_channel_layout_describe", // >= 59 (opt) "av_opt_set_chlayout", // >= 59 - /* +16 = 39 */ + /* +16 = 40 */ // libavformat "avformat_alloc_context", @@ -116,11 +117,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 = 53 */ + /* +14 = 54 */ // libavdevice "avdevice_register_all", // supported in all versions (opt) - /* +1 = 54 */ + /* +1 = 55 */ // libswresample "av_opt_set_sample_fmt", // actually lavu .. but exist only w/ swresample! @@ -129,7 +130,7 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { "swr_free", "swr_convert", "swr_get_out_samples", - /* +6 = 60 */ + /* +6 = 61 */ }; // optional symbol names @@ -138,6 +139,7 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { "av_dict_count", // 54.* (opt) // libavutil + "av_dict_iterate", // >= 57.42.100 "av_channel_layout_default", // >= 59 (opt) "av_channel_layout_uninit", // >= 59 (opt) "av_channel_layout_describe", // >= 59 (opt) 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 8124ca6ca..f091056c2 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java @@ -43,7 +43,6 @@ import com.jogamp.common.av.TimeFrameI; import com.jogamp.common.util.IOUtil; import com.jogamp.common.util.PropertyAccess; import com.jogamp.common.util.SecurityUtil; -import com.jogamp.common.util.VersionNumber; import com.jogamp.gluegen.runtime.ProcAddressTable; import com.jogamp.opengl.util.GLPixelStorageModes; import com.jogamp.opengl.util.av.GLMediaPlayer; @@ -414,6 +413,21 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { } @Override + protected void updateMetadata() { + final Chapter[] chapters = new Chapter[natives.getChapterCount0(moviePtr)]; + for(int i=0; i<chapters.length; ++i) { + chapters[i] = new Chapter(natives.getChapterID0(moviePtr, i), + natives.getChapterStartPTS0(moviePtr, i), natives.getChapterEndPTS0(moviePtr, i), + natives.getChapterTitle0(moviePtr, i)); + } + this.chapters = chapters; + } + private volatile Chapter[] chapters = new Chapter[0]; + + @Override + public Chapter[] getChapters() { return chapters; } + + @Override protected final void initGLImpl(final GL gl) throws IOException, GLException { if(0==moviePtr) { throw new GLException("FFMPEG native instance null"); @@ -973,6 +987,5 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { audioSink.enqueueData( audio_pts, sampleData, data_size); } } - } 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 9099bfb08..03b61b9ef 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGNatives.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGNatives.java @@ -71,6 +71,12 @@ import com.jogamp.opengl.util.texture.TextureSequence.TextureFrame; abstract int getAudioPTS0(long moviePtr); + abstract int getChapterCount0(long moviePtr); + abstract int getChapterID0(long moviePtr, int idx); + abstract int getChapterStartPTS0(long moviePtr, int idx); + abstract int getChapterEndPTS0(long moviePtr, int idx); + abstract String getChapterTitle0(long moviePtr, int idx); + /** * @return resulting current video PTS, or {@link TextureFrame#INVALID_PTS} */ 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 0f7d02904..bb60cbcc9 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv0400Natives.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv0400Natives.java @@ -75,4 +75,15 @@ class FFMPEGv0400Natives extends FFMPEGNatives { @Override native int seek0(long moviePtr, int position); + + @Override + native int getChapterCount0(long moviePtr); + @Override + native int getChapterID0(long moviePtr, int idx); + @Override + native int getChapterStartPTS0(long moviePtr, int idx); + @Override + native int getChapterEndPTS0(long moviePtr, int idx); + @Override + native String getChapterTitle0(long moviePtr, int idx); } 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 8d62ac1c5..1ab4ee50a 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv0500Natives.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv0500Natives.java @@ -75,4 +75,15 @@ class FFMPEGv0500Natives extends FFMPEGNatives { @Override native int seek0(long moviePtr, int position); + + @Override + native int getChapterCount0(long moviePtr); + @Override + native int getChapterID0(long moviePtr, int idx); + @Override + native int getChapterStartPTS0(long moviePtr, int idx); + @Override + native int getChapterEndPTS0(long moviePtr, int idx); + @Override + native String getChapterTitle0(long moviePtr, int idx); } 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 0de167285..bf68002ff 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv0600Natives.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv0600Natives.java @@ -75,4 +75,15 @@ class FFMPEGv0600Natives extends FFMPEGNatives { @Override native int seek0(long moviePtr, int position); + + @Override + native int getChapterCount0(long moviePtr); + @Override + native int getChapterID0(long moviePtr, int idx); + @Override + native int getChapterStartPTS0(long moviePtr, int idx); + @Override + native int getChapterEndPTS0(long moviePtr, int idx); + @Override + native String getChapterTitle0(long moviePtr, int idx); } diff --git a/src/jogl/native/libav/ffmpeg_impl_template.c b/src/jogl/native/libav/ffmpeg_impl_template.c index 7b46bfd7c..6d09a9d60 100644 --- a/src/jogl/native/libav/ffmpeg_impl_template.c +++ b/src/jogl/native/libav/ffmpeg_impl_template.c @@ -100,6 +100,7 @@ typedef int (APIENTRYP AV_GET_BITS_PER_PIXEL)(const AVPixFmtDescriptor *pixdesc) typedef int (APIENTRYP AV_SAMPLES_GET_BUFFER_SIZE)(int *linesize, int nb_channels, int nb_samples, enum AVSampleFormat sample_fmt, int align); typedef int (APIENTRYP AV_GET_BYTES_PER_SAMPLE)(enum AVSampleFormat sample_fmt); typedef int (APIENTRYP AV_OPT_SET_INT)(void *obj, const char *name, int64_t val, int search_flags); +typedef AVDictionaryEntry* (APIENTRYP AV_DICT_ITERATE)(AVDictionary *m, const AVDictionaryEntry *prev); typedef AVDictionaryEntry* (APIENTRYP AV_DICT_GET)(AVDictionary *m, const char *key, const AVDictionaryEntry *prev, int flags); typedef int (APIENTRYP AV_DICT_COUNT)(AVDictionary *m); typedef int (APIENTRYP AV_DICT_SET)(AVDictionary **pm, const char *key, const char *value, int flags); @@ -117,6 +118,7 @@ static AV_GET_BITS_PER_PIXEL sp_av_get_bits_per_pixel; static AV_SAMPLES_GET_BUFFER_SIZE sp_av_samples_get_buffer_size; static AV_GET_BYTES_PER_SAMPLE sp_av_get_bytes_per_sample; static AV_OPT_SET_INT sp_av_opt_set_int; +static AV_DICT_ITERATE sp_av_dict_iterate; static AV_DICT_GET sp_av_dict_get; static AV_DICT_COUNT sp_av_dict_count; static AV_DICT_SET sp_av_dict_set; @@ -125,7 +127,7 @@ static AV_CHANNEL_LAYOUT_DEFAULT sp_av_channel_layout_default; // >= 59 static AV_CHANNEL_LAYOUT_UNINIT sp_av_channel_layout_uninit; // >= 59 static AV_CHANNEL_LAYOUT_DESCRIBE sp_av_channel_layout_describe; // >= 59 static AV_OPT_SET_CHLAYOUT sp_av_opt_set_chlayout; // >= 59 -// count: +16 = 39 +// count: +17 = 40 // libavformat typedef AVFormatContext *(APIENTRYP AVFORMAT_ALLOC_CONTEXT)(void); @@ -157,12 +159,12 @@ static AV_READ_PAUSE sp_av_read_pause; static AVFORMAT_NETWORK_INIT sp_avformat_network_init; // 53.13.0 static AVFORMAT_NETWORK_DEINIT sp_avformat_network_deinit; // 53.13.0 static AVFORMAT_FIND_STREAM_INFO sp_avformat_find_stream_info; // 53.3.0 -// count: +14 = 53 +// count: +14 = 54 // libavdevice [53.0.0] typedef int (APIENTRYP AVDEVICE_REGISTER_ALL)(void); static AVDEVICE_REGISTER_ALL sp_avdevice_register_all; -// count: +1 = 54 +// count: +1 = 55 // libswresample [1...] typedef int (APIENTRYP AV_OPT_SET_SAMPLE_FMT)(void *obj, const char *name, enum AVSampleFormat fmt, int search_flags); // actually lavu .. but exist only w/ swresample! @@ -178,7 +180,7 @@ static SWR_INIT sp_swr_init; static SWR_FREE sp_swr_free; static SWR_CONVERT sp_swr_convert; static SWR_GET_OUT_SAMPLES sp_swr_get_out_samples; -// count: +6 = 60 +// count: +6 = 61 // We use JNI Monitor Locking, since this removes the need // to statically link-in pthreads on window .. @@ -202,7 +204,7 @@ static SWR_GET_OUT_SAMPLES sp_swr_get_out_samples; #define MY_MUTEX_UNLOCK(e,s) #endif -#define SYMBOL_COUNT 60 +#define SYMBOL_COUNT 61 JNIEXPORT jboolean JNICALL FF_FUNC(initSymbols0) (JNIEnv *env, jobject instance, jobject jmutex_avcodec_openclose, jobject jSymbols, jint count) @@ -256,6 +258,7 @@ JNIEXPORT jboolean JNICALL FF_FUNC(initSymbols0) sp_av_samples_get_buffer_size = (AV_SAMPLES_GET_BUFFER_SIZE) (intptr_t) symbols[i++]; sp_av_get_bytes_per_sample = (AV_GET_BYTES_PER_SAMPLE) (intptr_t) symbols[i++]; sp_av_opt_set_int = (AV_OPT_SET_INT) (intptr_t) symbols[i++]; + sp_av_dict_iterate = (AV_DICT_ITERATE) (intptr_t) symbols[i++]; sp_av_dict_get = (AV_DICT_GET) (intptr_t) symbols[i++]; sp_av_dict_count = (AV_DICT_COUNT) (intptr_t) symbols[i++]; sp_av_dict_set = (AV_DICT_SET) (intptr_t) symbols[i++]; @@ -1180,8 +1183,8 @@ JNIEXPORT void JNICALL FF_FUNC(setStream0) pAV->aPTS=0; initPTSStats(&pAV->vPTSStats); initPTSStats(&pAV->aPTSStats); - _updateJavaAttributes(env, pAV); pAV->ready = 1; + _updateJavaAttributes(env, pAV); } JNIEXPORT void JNICALL FF_FUNC(setGLFuncs0) @@ -1706,3 +1709,74 @@ JNIEXPORT jint JNICALL FF_FUNC(getAudioPTS0) return pAV->aPTS; } +static inline const char* meta_get_value(AVDictionary *tags, const char* key) +{ + // SECTION_ID_CHAPTER_TAGS + if (!tags) { + return NULL; + } + const AVDictionaryEntry *entry = NULL; + if ((entry = sp_av_dict_get(tags, key, entry, AV_DICT_IGNORE_SUFFIX))) { + return entry->value; + } + return NULL; +} +static inline const char* meta_get_chapter_title(AVChapter *chapter) +{ + return meta_get_value(chapter->metadata, "title"); +} +JNIEXPORT jint JNICALL FF_FUNC(getChapterCount0) + (JNIEnv *env, jobject instance, jlong ptr) +{ + FFMPEGToolBasicAV_t *pAV = (FFMPEGToolBasicAV_t *)((void *)((intptr_t)ptr)); + if( 0 == pAV->ready ) { + return 0; + } + return pAV->pFormatCtx->nb_chapters; +} + +JNIEXPORT jint JNICALL FF_FUNC(getChapterID0) + (JNIEnv *env, jobject instance, jlong ptr, jint idx) +{ + FFMPEGToolBasicAV_t *pAV = (FFMPEGToolBasicAV_t *)((void *)((intptr_t)ptr)); + if( 0 == pAV->ready || idx >= pAV->pFormatCtx->nb_chapters ) { + return 0; + } + AVChapter *chapter = pAV->pFormatCtx->chapters[idx]; + return chapter->id; +} + +JNIEXPORT jint JNICALL FF_FUNC(getChapterStartPTS0) + (JNIEnv *env, jobject instance, jlong ptr, jint idx) +{ + FFMPEGToolBasicAV_t *pAV = (FFMPEGToolBasicAV_t *)((void *)((intptr_t)ptr)); + if( 0 == pAV->ready || idx >= pAV->pFormatCtx->nb_chapters ) { + return 0; + } + AVChapter *chapter = pAV->pFormatCtx->chapters[idx]; + return my_av_q2i32( chapter->start * 1000, chapter->time_base); +} + +JNIEXPORT jint JNICALL FF_FUNC(getChapterEndPTS0) + (JNIEnv *env, jobject instance, jlong ptr, jint idx) +{ + FFMPEGToolBasicAV_t *pAV = (FFMPEGToolBasicAV_t *)((void *)((intptr_t)ptr)); + if( 0 == pAV->ready || idx >= pAV->pFormatCtx->nb_chapters ) { + return 0; + } + AVChapter *chapter = pAV->pFormatCtx->chapters[idx]; + return my_av_q2i32( chapter->end * 1000, chapter->time_base); +} + +JNIEXPORT jstring JNICALL FF_FUNC(getChapterTitle0) + (JNIEnv *env, jobject instance, jlong ptr, jint idx) +{ + FFMPEGToolBasicAV_t *pAV = (FFMPEGToolBasicAV_t *)((void *)((intptr_t)ptr)); + if( 0 == pAV->ready || idx >= pAV->pFormatCtx->nb_chapters ) { + return NULL; + } + AVChapter *chapter = pAV->pFormatCtx->chapters[idx]; + const char* title = meta_get_chapter_title(chapter); + return NULL != title ? (*env)->NewStringUTF(env, title) : NULL; +} + |