From eca6a5cb1e2beda84dfbafc31ed225e272f4f3fb Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Thu, 29 Aug 2013 22:46:57 +0200
Subject: Enhance GLMediaPlayer: Full FFMPeg support, 'dshow' camera support on
 windows, 2 more pixel formats, fail-safe data handling

- add support for ffmpeg 2 / libav 10 -> lavc55_lavf55_lavu52_lavr01

- add support for ffmpeg libswresample (similar to libavresample)

- handle BGRA (GL type) and BGR24 (texture shader)

- Change Camera URI semantics, drop 'host' and use 'path' for camera ID
  and use 'query' for options.

- add support for Window's DShow camera selection
  - our camera id -> index of list of video-input devices,
    this gives us same behavior as w/ Linux
  - requires windows libs: strmiids, uuid, ole32, oleaut32
  - Compiles w/ MingW64, works w/ libav/ffmpeg
  - TODO: test compilation w/ MingW 32bit !

- don't push data to texture if (linesize <= 0)
  this may happen due to buggy decoder / setup ..

Tested manually on GNU/Linux x64 and Windows x64:
  - GNU/Linux libav 0.8, libav 9, libav 10, ffmpeg 1.2, ffmpeg 2.0
  - Windows   libav 0.8, libav 9, ffmpeg 2.0
  - videos and camera
---
 .../av/impl/FFMPEGDynamicLibraryBundleInfo.java    | 231 ++++++++++++---------
 1 file changed, 130 insertions(+), 101 deletions(-)

(limited to 'src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java')

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 f327cddd4..400788a24 100644
--- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java
+++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java
@@ -32,10 +32,8 @@ import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
 import javax.media.opengl.GLProfile;
@@ -53,12 +51,13 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo  {
     
     private static final List<String> glueLibNames = new ArrayList<String>(); // none
     
-    private static final int symbolCount = 54;
+    private static final int symbolCount = 65;
     private static final String[] symbolNames = {
-         "avcodec_version",
-         "avformat_version",
          "avutil_version",
-/* 4 */  "avresample_version",
+         "avformat_version",
+         "avcodec_version",
+         "avresample_version",
+/* 5 */  "swresample_version",
 
          // libavcodec
          "avcodec_register_all",
@@ -69,15 +68,20 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo  {
          "avcodec_alloc_frame",
          "avcodec_get_frame_defaults",
          "avcodec_free_frame",        // 54.28.0   (opt)
-         "avcodec_default_get_buffer", 
-         "avcodec_default_release_buffer",
+         "avcodec_default_get_buffer",     // <= 54 (opt), else sp_avcodec_default_get_buffer2
+         "avcodec_default_release_buffer", // <= 54 (opt), else sp_av_frame_unref
+         "avcodec_default_get_buffer2",    // 55 (opt)
+         "avcodec_get_edge_width",
+         "av_image_fill_linesizes",
+         "avcodec_align_dimensions",
+         "avcodec_align_dimensions2",
          "avcodec_flush_buffers",
          "av_init_packet",
          "av_new_packet",
          "av_destruct_packet",
          "av_free_packet", 
          "avcodec_decode_audio4",     // 53.25.0   (opt)
-/* 21 */ "avcodec_decode_video2",     // 52.23.0
+/* 27 */ "avcodec_decode_video2",     // 52.23.0
         
          // libavutil
          "av_pix_fmt_descriptors",
@@ -91,7 +95,7 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo  {
          "av_dict_get",
          "av_dict_count",             // 54.*      (opt)
          "av_dict_set",
-/* 33 */ "av_dict_free",
+/* 28 */ "av_dict_free",
 
          // libavformat
          "avformat_alloc_context",
@@ -108,22 +112,25 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo  {
          "av_read_pause",
          "avformat_network_init",     // 53.13.0   (opt)
          "avformat_network_deinit",   // 53.13.0   (opt)
-/* 48 */ "avformat_find_stream_info", // 53.3.0    (opt)
+/* 54 */ "avformat_find_stream_info", // 53.3.0    (opt)
 
          // libavdevice
-/* 49 */ "avdevice_register_all",     // ??? 
+/* 55 */ "avdevice_register_all",     // ??? 
          
          // libavresample
          "avresample_alloc_context",  //  1.0.1
          "avresample_open",
          "avresample_close",
          "avresample_free",
-/* 54 */ "avresample_convert"
-    };
-    
-    // alternate symbol names
-    private static final String[][] altSymbolNames = {
-        // { "av_find_stream_info",   "avformat_find_stream_info" },  // old, 53.3.0       
+/* 60 */ "avresample_convert",
+                  
+         // libavresample
+         "av_opt_set_sample_fmt",     // actually lavu .. but exist only w/ swresample!
+         "swr_alloc",
+         "swr_init",       
+         "swr_free",
+/* 65 */ "swr_convert",
+
     };
     
     // optional symbol names
@@ -132,8 +139,13 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo  {
          "avcodec_free_frame",        // 54.28.0   (opt)
          "av_frame_unref",            // 55.0.0 (opt)
          "av_dict_count",             // 54.*   (opt)
+         "avcodec_default_get_buffer",     // <= 54 (opt), else sp_avcodec_default_get_buffer2
+         "avcodec_default_release_buffer", // <= 54 (opt), else sp_av_frame_unref
+         "avcodec_default_get_buffer2",    // 55 (opt)
+
          // libavdevice
          "avdevice_register_all",     // 53.0.0 (opt)
+         
          // libavresample
          "avresample_version",        //  1.0.1
          "avresample_alloc_context",  //  1.0.1
@@ -141,41 +153,60 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo  {
          "avresample_close",
          "avresample_free",
          "avresample_convert",
+         
+         // libavresample
+         "av_opt_set_sample_fmt",     // actually lavu .. but exist only w/ swresample!
+         "swresample_version",        //  0
+         "swr_alloc",
+         "swr_init",       
+         "swr_free",
+         "swr_convert",
     };
     
     private static final long[] symbolAddr = new long[symbolCount];
     private static final boolean ready;
-    private static final boolean libsLoaded;
+    private static final boolean libsUFCLoaded;
     private static final boolean avresampleLoaded;  // optional
+    private static final boolean swresampleLoaded;  // optional
     private static final boolean avdeviceLoaded; // optional
     static final VersionNumber avCodecVersion;    
     static final VersionNumber avFormatVersion;
     static final VersionNumber avUtilVersion;
     static final VersionNumber avResampleVersion;
+    static final VersionNumber swResampleVersion;
     private static final FFMPEGNatives natives;
     
+    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_AVR = 4;
+    private static final int LIB_IDX_SWR = 5;
+    
     static {
         // native ffmpeg media player implementation is included in jogl_desktop and jogl_mobile     
         GLProfile.initSingleton();
         boolean _ready = false;
-        boolean[] _libsLoaded= { false };
-        boolean[] _avdeviceLoaded= { false };
-        boolean[] _avresampleLoaded= { false };
-        VersionNumber[] _versions = new VersionNumber[4];
+        /** util, format, codec, device, avresample, swresample */  
+        boolean[] _loaded= new boolean[6]; 
+        /** util, format, codec, avresample, swresample */  
+        VersionNumber[] _versions = new VersionNumber[5];
         try {
-            _ready = initSymbols(_libsLoaded, _avdeviceLoaded, _avresampleLoaded, _versions);
+            _ready = initSymbols(_loaded, _versions);
         } catch (Throwable t) {
             t.printStackTrace();
         }
-        libsLoaded = _libsLoaded[0];
-        avdeviceLoaded = _avdeviceLoaded[0];
-        avresampleLoaded = _avresampleLoaded[0];
-        avCodecVersion = _versions[0];    
+        libsUFCLoaded = _loaded[LIB_IDX_UTI] && _loaded[LIB_IDX_FMT] && _loaded[LIB_IDX_COD];
+        avdeviceLoaded = _loaded[LIB_IDX_DEV];
+        avresampleLoaded = _loaded[LIB_IDX_AVR];
+        swresampleLoaded = _loaded[LIB_IDX_SWR];
+        avUtilVersion = _versions[0];
         avFormatVersion = _versions[1];
-        avUtilVersion = _versions[2];
+        avCodecVersion = _versions[2];
         avResampleVersion = _versions[3];
-        if(!libsLoaded) {
-            System.err.println("LIB_AV Not Available");
+        swResampleVersion = _versions[4];
+        if(!libsUFCLoaded) {
+            System.err.println("LIB_AV Not Available: lavu, lavc, lavu");
             natives = null;
             ready = false;
         } else if(!_ready) {
@@ -183,12 +214,15 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo  {
             natives = null;
             ready = false;
         } else {
-            if( avCodecVersion.getMajor() <= 53 && avFormatVersion.getMajor() <= 53 && avUtilVersion.getMajor() <= 51 ) {
+            if( avCodecVersion.getMajor() == 53 && avFormatVersion.getMajor() == 53 && avUtilVersion.getMajor() == 51 ) {
                 // lavc53.lavf53.lavu51
                 natives = new FFMPEGv08Natives();
-            } else if( avCodecVersion.getMajor() == 54 && avFormatVersion.getMajor() <= 54 && avUtilVersion.getMajor() <= 52 ) {
+            } else if( avCodecVersion.getMajor() == 54 && avFormatVersion.getMajor() == 54 && avUtilVersion.getMajor() == 52 ) {
                 // lavc54.lavf54.lavu52.lavr01
                 natives = new FFMPEGv09Natives();
+            } else if( avCodecVersion.getMajor() == 55 && avFormatVersion.getMajor() == 55 && avUtilVersion.getMajor() == 52 ) {
+                // lavc55.lavf55.lavu52.lavr01
+                natives = new FFMPEGv10Natives();
             } else {
                 System.err.println("LIB_AV No Version/Native-Impl Match");
                 natives = null;
@@ -201,29 +235,33 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo  {
         }
     }
     
-    static boolean libsLoaded() { return libsLoaded; }
+    static boolean libsLoaded() { return libsUFCLoaded; }
     static boolean avDeviceLoaded() { return avdeviceLoaded; }
     static boolean avResampleLoaded() { return avresampleLoaded; }
+    static boolean swResampleLoaded() { return swresampleLoaded; }
     static FFMPEGNatives getNatives() { return natives; }
     static boolean initSingleton() { return ready; }
-    
-    private static final boolean initSymbols(boolean[] libsLoaded, boolean[] avdeviceLoaded, boolean[] avresampleLoaded,
-                                             VersionNumber[] versions) {
-        libsLoaded[0] = false;
+
+    /**
+     * @param loaded 6: util, format, codec, device, avresample, swresample
+     * @param versions 5: util, format, codec, avresample, swresample
+     * @return
+     */
+    private static final boolean initSymbols(boolean[] loaded, VersionNumber[] versions) {
+        for(int i=0; i<6; i++) {
+            loaded[i] = false;
+        }
         final DynamicLibraryBundle dl = AccessController.doPrivileged(new PrivilegedAction<DynamicLibraryBundle>() {
                                           public DynamicLibraryBundle run() {
                                               return new DynamicLibraryBundle(new FFMPEGDynamicLibraryBundleInfo());
                                           } } );
-        final boolean avutilLoaded = dl.isToolLibLoaded(0); 
-        final boolean avformatLoaded = dl.isToolLibLoaded(1);
-        final boolean avcodecLoaded = dl.isToolLibLoaded(2);
-        if(!avutilLoaded || !avformatLoaded || !avcodecLoaded) {
-            throw new RuntimeException("FFMPEG Tool library incomplete: [ avutil "+avutilLoaded+", avformat "+avformatLoaded+", avcodec "+avcodecLoaded+"]");
+        dl.toString();
+        for(int i=0; i<6; i++) {
+            loaded[i] = dl.isToolLibLoaded(i);
+        }
+        if( !loaded[LIB_IDX_UTI] || !loaded[LIB_IDX_FMT] || !loaded[LIB_IDX_COD] ) {
+            throw new RuntimeException("FFMPEG Tool library incomplete: [ avutil "+loaded[LIB_IDX_UTI]+", avformat "+loaded[LIB_IDX_FMT]+", avcodec "+loaded[LIB_IDX_COD]+"]");
         }
-        avdeviceLoaded[0] = dl.isToolLibLoaded(3);
-        avresampleLoaded[0] = dl.isToolLibLoaded(4);
-        libsLoaded[0] = true;
-        
         if(symbolNames.length != symbolCount) {
             throw new InternalError("XXX0 "+symbolNames.length+" != "+symbolCount);
         }
@@ -232,20 +270,6 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo  {
         final Set<String> optionalSymbolNameSet = new HashSet<String>();
         optionalSymbolNameSet.addAll(Arrays.asList(optionalSymbolNames));
         
-        // alternate symbol name mapping to indexed array
-        final Map<String, Integer> mAltSymbolNames = new HashMap<String, Integer>();
-        final int[][] iAltSymbolNames = new int[altSymbolNames.length][];
-        {
-            final List<String> symbolNameList = Arrays.asList(symbolNames);
-            for(int i=0; i<altSymbolNames.length; i++) {
-                iAltSymbolNames[i] = new int[altSymbolNames[i].length];        
-                for(int j=0; j<altSymbolNames[i].length; j++) {
-                    mAltSymbolNames.put(altSymbolNames[i][j], new Integer(i));
-                    iAltSymbolNames[i][j] = symbolNameList.indexOf(altSymbolNames[i][j]); 
-                }            
-            }
-        }
-        
         // lookup
         AccessController.doPrivileged(new PrivilegedAction<Object>() {
             public Object run() {
@@ -262,33 +286,18 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo  {
                 // no symbol, check optional and alternative symbols
                 final String symbol = symbolNames[i];
                 if ( !optionalSymbolNameSet.contains(symbol) ) {
-                    // check for API changed symbols
-                    boolean ok = false;                    
-                    final Integer cI = mAltSymbolNames.get(symbol);
-                    if ( null != cI ) {
-                        // check whether alternative symbol is available
-                        final int ci = cI.intValue();
-                        for(int j=0; !ok && j<iAltSymbolNames[ci].length; j++) {
-                            final int si = iAltSymbolNames[ci][j];
-                            ok = 0 != symbolAddr[si];
-                            if(ok && DEBUG) {
-                                System.err.println("OK: Unresolved symbol <"+symbol+">, but has alternative <"+symbolNames[si]+">");
-                            }
-                        }
-                    }
-                    if(!ok) {
-                        System.err.println("Fail: Could not resolve symbol <"+symbolNames[i]+">: not optional, no alternatives.");
-                        res = false;
-                    }
+                    System.err.println("Fail: Could not resolve symbol <"+symbolNames[i]+">: not optional, no alternatives.");
+                    res = false;
                 } else if(DEBUG) {
                     System.err.println("OK: Unresolved optional symbol <"+symbolNames[i]+">");
                 }
             }
         }
-        versions[0] = FFMPEGStaticNatives.getAVVersion(FFMPEGStaticNatives.getAvCodecVersion0(symbolAddr[0]));
-        versions[1] = FFMPEGStaticNatives.getAVVersion(FFMPEGStaticNatives.getAvFormatVersion0(symbolAddr[1]));
-        versions[2] = FFMPEGStaticNatives.getAVVersion(FFMPEGStaticNatives.getAvUtilVersion0(symbolAddr[2]));
-        versions[3] = FFMPEGStaticNatives.getAVVersion(FFMPEGStaticNatives.getAvResampleVersion0(symbolAddr[3]));
+        versions[0] = FFMPEGStaticNatives.getAVVersion(FFMPEGStaticNatives.getAvVersion0(symbolAddr[0]));
+        versions[1] = FFMPEGStaticNatives.getAVVersion(FFMPEGStaticNatives.getAvVersion0(symbolAddr[1]));
+        versions[2] = FFMPEGStaticNatives.getAVVersion(FFMPEGStaticNatives.getAvVersion0(symbolAddr[2]));
+        versions[3] = FFMPEGStaticNatives.getAVVersion(FFMPEGStaticNatives.getAvVersion0(symbolAddr[3]));
+        versions[4] = FFMPEGStaticNatives.getAVVersion(FFMPEGStaticNatives.getAvVersion0(symbolAddr[4]));
         
         return res;
     }
@@ -319,16 +328,18 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo  {
     public final List<List<String>> getToolLibNames() {
         List<List<String>> libsList = new ArrayList<List<String>>();
 
+        // 6: util, format, codec, device, avresample, swresample
+        
         final List<String> avutil = new ArrayList<String>();
         avutil.add("avutil");        // default
 
         avutil.add("libavutil.so.53");     // dummy future proof
-        avutil.add("libavutil.so.52");     // 9
+        avutil.add("libavutil.so.52");     // ffmpeg 1.2 + 2 / libav 9 + 10
         avutil.add("libavutil.so.51");     // 0.8
         avutil.add("libavutil.so.50");     // 0.7
         
         avutil.add("avutil-53");     // dummy future proof
-        avutil.add("avutil-52");     // 9
+        avutil.add("avutil-52");     // ffmpeg 1.2 + 2 / libav 9 + 10
         avutil.add("avutil-51");     // 0.8
         avutil.add("avutil-50");     // 0.7
         libsList.add(avutil);
@@ -336,51 +347,69 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo  {
         final List<String> avformat = new ArrayList<String>();
         avformat.add("avformat");    // default
 
-        avformat.add("libavformat.so.55"); // dummy future proof
-        avformat.add("libavformat.so.54"); // 9
+        avformat.add("libavformat.so.56"); // dummy future proof
+        avformat.add("libavformat.so.55"); // ffmpeg 2 / libav 10
+        avformat.add("libavformat.so.54"); // ffmpeg 1.2 / libav 9
         avformat.add("libavformat.so.53"); // 0.8
         avformat.add("libavformat.so.52"); // 0.7
         
-        avformat.add("avformat-55"); // dummy future proof
-        avformat.add("avformat-54"); // 9
+        avformat.add("avformat-56"); // dummy future proof
+        avformat.add("avformat-55"); // ffmpeg 2 / libav 10
+        avformat.add("avformat-54"); // ffmpeg 1.2 / libav 9
         avformat.add("avformat-53"); // 0.8
-        avformat.add("avformat-52"); // 0.7
+        avformat.add("avformat-52"); // 0.7        
         libsList.add(avformat);
         
         final List<String> avcodec = new ArrayList<String>();
         avcodec.add("avcodec");      // default
 
-        avcodec.add("libavcodec.so.55");   // dummy future proof
-        avcodec.add("libavcodec.so.54");   // 9
+        avcodec.add("libavcodec.so.56");   // dummy future proof
+        avcodec.add("libavcodec.so.55");   // ffmpeg 2/ libav 10
+        avcodec.add("libavcodec.so.54");   // ffmpeg 1.2 / libav 9
         avcodec.add("libavcodec.so.53");   // 0.8
         avcodec.add("libavcodec.so.52");   // 0.7        
         
-        avcodec.add("avcodec-55");   // dummy future proof
-        avcodec.add("avcodec-54");   // 9
+        avcodec.add("avcodec-56");   // dummy future proof
+        avcodec.add("avcodec-55");   // ffmpeg 2/ libav 10
+        avcodec.add("avcodec-54");   // ffmpeg 1.2 / libav 9
         avcodec.add("avcodec-53");   // 0.8
-        avcodec.add("avcodec-52");   // 0.7
+        avcodec.add("avcodec-52");   // 0.7 
         libsList.add(avcodec);
         
         final List<String> avdevice = new ArrayList<String>();
         avdevice.add("avdevice");        // default
 
-        avdevice.add("libavdevice.so.54");     // dummy future proof
-        avdevice.add("libavdevice.so.53");     // 0.8 && 9
+        avdevice.add("libavdevice.so.56");     // dummy future proof
+        avdevice.add("libavdevice.so.55");     // ffmpeg 2
+        avdevice.add("libavdevice.so.54");     // ffmpeg 1.2 / libav 10
+        avdevice.add("libavdevice.so.53");     // 0.8 && libav 9
         
-        avdevice.add("avdevice-54");     // dummy future proof
-        avdevice.add("avdevice-53");     // 0.8 && 9
+        avdevice.add("avdevice-56");     // dummy future proof
+        avdevice.add("avdevice-55");     // ffmpeg 2
+        avdevice.add("avdevice-54");     // ffmpeg 1.2 / libav 10
+        avdevice.add("avdevice-53");     // 0.8 && libav 9
         libsList.add(avdevice);
         
         final List<String> avresample = new ArrayList<String>();
         avresample.add("avresample");        // default
 
         avresample.add("libavresample.so.2");     // dummy future proof
-        avresample.add("libavresample.so.1");     // 9
+        avresample.add("libavresample.so.1");     // libav 9 + 10
         
         avresample.add("avresample-2");     // dummy future proof
-        avresample.add("avresample-1");     // 9
+        avresample.add("avresample-1");     // libav 9 + 10
         libsList.add(avresample);
 
+        final List<String> swresample = new ArrayList<String>();
+        swresample.add("swresample");        // default
+
+        swresample.add("libswresample.so.1");     // dummy future proof
+        swresample.add("libswresample.so.0");     // ffmpeg 1.2 + 2.x
+        
+        swresample.add("swresample-1");     // dummy future proof
+        swresample.add("swresample-0");     // ffmpeg 1.2 + 2.x
+        libsList.add(swresample);
+        
         return libsList;
     }
 
-- 
cgit v1.2.3