aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2023-12-30 21:18:16 +0100
committerSven Gothel <[email protected]>2023-12-30 21:18:16 +0100
commit68b2dad19bd6d84f18b22f967ce320b448e8a270 (patch)
treee4d49a1eadc12ef9a456f1eb1d5303eddfe192c8
parentc6e39c6e313a34688ca0164d7a34b6465e92396f (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()'
-rw-r--r--src/graphui/classes/com/jogamp/graph/ui/widgets/MediaPlayer.java46
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java28
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java11
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java12
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java17
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGNatives.java6
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv0400Natives.java11
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv0500Natives.java11
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv0600Natives.java11
-rw-r--r--src/jogl/native/libav/ffmpeg_impl_template.c86
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;
+}
+