1 /**
2  * Copyright 2012 JogAmp Community. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification, are
5  * permitted provided that the following conditions are met:
6  *
7  *    1. Redistributions of source code must retain the above copyright notice, this list of
8  *       conditions and the following disclaimer.
9  *
10  *    2. Redistributions in binary form must reproduce the above copyright notice, this list
11  *       of conditions and the following disclaimer in the documentation and/or other materials
12  *       provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  * The views and conclusions contained in the software and documentation are those of the
25  * authors and should not be interpreted as representing official policies, either expressed
26  * or implied, of JogAmp Community.
27  */
28 
29 package jogamp.opengl.util.av.impl;
30 
31 import java.security.AccessController;
32 import java.security.PrivilegedAction;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.HashSet;
36 import java.util.List;
37 import java.util.Set;
38 
39 import com.jogamp.opengl.GLProfile;
40 import com.jogamp.common.ExceptionUtils;
41 import com.jogamp.common.os.DynamicLibraryBundle;
42 import com.jogamp.common.os.DynamicLibraryBundleInfo;
43 import com.jogamp.common.util.RunnableExecutor;
44 import com.jogamp.common.util.VersionNumber;
45 
46 /**
47  * See {@link FFMPEGMediaPlayer#compatibility}.
48  */
49 class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo  {
50     private static final boolean DEBUG = FFMPEGMediaPlayer.DEBUG || DynamicLibraryBundleInfo.DEBUG;
51 
52     private static final List<String> glueLibNames = new ArrayList<String>(); // none
53 
54     private static final int symbolCount = 65;
55     private static final String[] symbolNames = {
56          "avutil_version",
57          "avformat_version",
58          "avcodec_version",
59          "avresample_version",
60 /* 5 */  "swresample_version",
61 
62          // libavcodec
63          "avcodec_register_all",
64          "avcodec_close",
65          "avcodec_string",
66          "avcodec_find_decoder",
67          "avcodec_open2",             // 53.6.0    (opt)
68          "avcodec_alloc_frame",
69          "avcodec_get_frame_defaults",
70          "avcodec_free_frame",        // 54.28.0   (opt)
71          "avcodec_default_get_buffer",     // <= 54 (opt), else sp_avcodec_default_get_buffer2
72          "avcodec_default_release_buffer", // <= 54 (opt), else sp_av_frame_unref
73          "avcodec_default_get_buffer2",    // 55 (opt)
74          "avcodec_get_edge_width",
75          "av_image_fill_linesizes",
76          "avcodec_align_dimensions",
77          "avcodec_align_dimensions2",
78          "avcodec_flush_buffers",
79          "av_init_packet",
80          "av_new_packet",
81          "av_destruct_packet",
82          "av_free_packet",
83          "avcodec_decode_audio4",     // 53.25.0   (opt)
84 /* 27 */ "avcodec_decode_video2",     // 52.23.0
85 
86          // libavutil
87          "av_pix_fmt_descriptors",
88          "av_frame_unref",            // 55.0.0 (opt)
89          "av_realloc",
90          "av_free",
91          "av_get_bits_per_pixel",
92          "av_samples_get_buffer_size",
93          "av_get_bytes_per_sample",   // 51.4.0
94          "av_opt_set_int",            // 51.12.0
95          "av_dict_get",
96          "av_dict_count",             // 54.*      (opt)
97          "av_dict_set",
98 /* 28 */ "av_dict_free",
99 
100          // libavformat
101          "avformat_alloc_context",
102          "avformat_free_context",     // 52.96.0   (opt)
103          "avformat_close_input",      // 53.17.0   (opt)
104          "av_register_all",
105          "av_find_input_format",
106          "avformat_open_input",
107          "av_dump_format",
108          "av_read_frame",
109          "av_seek_frame",
110          "avformat_seek_file",        // ???       (opt)
111          "av_read_play",
112          "av_read_pause",
113          "avformat_network_init",     // 53.13.0   (opt)
114          "avformat_network_deinit",   // 53.13.0   (opt)
115 /* 54 */ "avformat_find_stream_info", // 53.3.0    (opt)
116 
117          // libavdevice
118 /* 55 */ "avdevice_register_all",     // supported in all version <= 56
119 
120          // libavresample
121          "avresample_alloc_context",  //  1.0.1
122          "avresample_open",
123          "avresample_close",
124          "avresample_free",
125 /* 60 */ "avresample_convert",
126 
127          // libavresample
128          "av_opt_set_sample_fmt",     // actually lavu .. but exist only w/ swresample!
129          "swr_alloc",
130          "swr_init",
131          "swr_free",
132 /* 65 */ "swr_convert",
133 
134     };
135 
136     // optional symbol names
137     private static final String[] optionalSymbolNames = {
138          "avformat_seek_file",        // ???       (opt)
139          "avcodec_free_frame",        // 54.28.0   (opt)
140          "av_frame_unref",            // 55.0.0 (opt)
141          "av_dict_count",             // 54.*   (opt)
142          "avcodec_default_get_buffer",     // <= 54 (opt), else sp_avcodec_default_get_buffer2
143          "avcodec_default_release_buffer", // <= 54 (opt), else sp_av_frame_unref
144          "avcodec_default_get_buffer2",    // 55 (opt)
145 
146          // libavdevice
147          "avdevice_register_all",     // 53.0.0 (opt)
148 
149          // libavresample
150          "avresample_version",        //  1.0.1
151          "avresample_alloc_context",  //  1.0.1
152          "avresample_open",
153          "avresample_close",
154          "avresample_free",
155          "avresample_convert",
156 
157          // libswresample
158          "av_opt_set_sample_fmt",     // actually lavu .. but exist only w/ swresample!
159          "swresample_version",        //  0
160          "swr_alloc",
161          "swr_init",
162          "swr_free",
163          "swr_convert",
164     };
165 
166     /** util, format, codec, device, avresample, swresample */
167     private static final boolean[] libLoaded = new boolean[6];
168     private static final long[] symbolAddr = new long[symbolCount];
169     private static final boolean ready;
170     private static final boolean libsUFCLoaded;
171     static final VersionNumber avCodecVersion;
172     static final VersionNumber avFormatVersion;
173     static final VersionNumber avUtilVersion;
174     static final VersionNumber avResampleVersion;
175     static final VersionNumber swResampleVersion;
176     private static final FFMPEGNatives natives;
177 
178     private static final int LIB_IDX_UTI = 0;
179     private static final int LIB_IDX_FMT = 1;
180     private static final int LIB_IDX_COD = 2;
181     private static final int LIB_IDX_DEV = 3;
182     private static final int LIB_IDX_AVR = 4;
183     private static final int LIB_IDX_SWR = 5;
184 
185     private static final PrivilegedAction<DynamicLibraryBundle> privInitSymbolsAction = new PrivilegedAction<DynamicLibraryBundle>() {
186         @Override
187         public DynamicLibraryBundle run() {
188             final DynamicLibraryBundle dl = new DynamicLibraryBundle(new FFMPEGDynamicLibraryBundleInfo());
189             for(int i=0; i<6; i++) {
190                 libLoaded[i] = dl.isToolLibLoaded(i);
191             }
192             if( !libLoaded[LIB_IDX_UTI] || !libLoaded[LIB_IDX_FMT] || !libLoaded[LIB_IDX_COD] ) {
193                 System.err.println("FFMPEG Tool library incomplete: [ avutil "+libLoaded[LIB_IDX_UTI]+", avformat "+libLoaded[LIB_IDX_FMT]+", avcodec "+libLoaded[LIB_IDX_COD]+"]");
194                 return null;
195             }
196             dl.claimAllLinkPermission();
197             try {
198                 for(int i = 0; i<symbolCount; i++) {
199                     symbolAddr[i] = dl.dynamicLookupFunction(symbolNames[i]);
200                 }
201             } finally {
202                 dl.releaseAllLinkPermission();
203             }
204             return dl;
205         } };
206 
207     /**
208      * @param loaded 6: util, format, codec, device, avresample, swresample
209      * @param versions 5: util, format, codec, avresample, swresample
210      * @return
211      */
initSymbols(final VersionNumber[] versions)212     private static final boolean initSymbols(final VersionNumber[] versions) {
213         for(int i=0; i<6; i++) {
214             libLoaded[i] = false;
215         }
216         if(symbolNames.length != symbolCount) {
217             throw new InternalError("XXX0 "+symbolNames.length+" != "+symbolCount);
218         }
219 
220         final DynamicLibraryBundle dl = AccessController.doPrivileged(privInitSymbolsAction);
221         if( null == dl ) {
222             return false;
223         }
224 
225         // optional symbol name set
226         final Set<String> optionalSymbolNameSet = new HashSet<String>();
227         optionalSymbolNameSet.addAll(Arrays.asList(optionalSymbolNames));
228 
229         // validate results
230         boolean res = true;
231         for(int i = 0; i<symbolCount; i++) {
232             if( 0 == symbolAddr[i] ) {
233                 // no symbol, check optional and alternative symbols
234                 final String symbol = symbolNames[i];
235                 if ( !optionalSymbolNameSet.contains(symbol) ) {
236                     System.err.println("Fail: Could not resolve symbol <"+symbolNames[i]+">: not optional, no alternatives.");
237                     res = false;
238                 } else if(DEBUG) {
239                     System.err.println("OK: Unresolved optional symbol <"+symbolNames[i]+">");
240                 }
241             }
242         }
243         versions[0] = FFMPEGStaticNatives.getAVVersion(FFMPEGStaticNatives.getAvVersion0(symbolAddr[0]));
244         versions[1] = FFMPEGStaticNatives.getAVVersion(FFMPEGStaticNatives.getAvVersion0(symbolAddr[1]));
245         versions[2] = FFMPEGStaticNatives.getAVVersion(FFMPEGStaticNatives.getAvVersion0(symbolAddr[2]));
246         versions[3] = FFMPEGStaticNatives.getAVVersion(FFMPEGStaticNatives.getAvVersion0(symbolAddr[3]));
247         versions[4] = FFMPEGStaticNatives.getAVVersion(FFMPEGStaticNatives.getAvVersion0(symbolAddr[4]));
248 
249         return res;
250     }
251 
252     static {
253         // native ffmpeg media player implementation is included in jogl_desktop and jogl_mobile
GLProfile.initSingleton()254         GLProfile.initSingleton();
255         boolean _ready = false;
256         /** util, format, codec, avresample, swresample */
257         final VersionNumber[] _versions = new VersionNumber[5];
258         try {
259             _ready = initSymbols(_versions);
260         } catch (final Throwable t) {
261             ExceptionUtils.dumpThrowable("", t);
262         }
263         libsUFCLoaded = libLoaded[LIB_IDX_UTI] && libLoaded[LIB_IDX_FMT] && libLoaded[LIB_IDX_COD];
264         avUtilVersion = _versions[0];
265         avFormatVersion = _versions[1];
266         avCodecVersion = _versions[2];
267         avResampleVersion = _versions[3];
268         swResampleVersion = _versions[4];
269         if(!libsUFCLoaded) {
270             System.err.println("LIB_AV Not Available: lavu, lavc, lavu");
271             natives = null;
272             ready = false;
273         } else if(!_ready) {
274             System.err.println("LIB_AV Not Matching");
275             natives = null;
276             ready = false;
277         } else {
278             final int avCodecMajor = avCodecVersion.getMajor();
279             final int avFormatMajor = avFormatVersion.getMajor();
280             final int avUtilMajor = avUtilVersion.getMajor();
281             if(        avCodecMajor == 53 && avFormatMajor == 53 && avUtilMajor == 51 ) {
282                 // lavc53.lavf53.lavu51
283                 natives = new FFMPEGv08Natives();
284             } else if( avCodecMajor == 54 && avFormatMajor == 54 && avUtilMajor == 52 ) {
285                 // lavc54.lavf54.lavu52.lavr01
286                 natives = new FFMPEGv09Natives();
287             } else if( avCodecMajor == 55 && avFormatMajor == 55 && ( avUtilMajor == 52 || avUtilMajor == 53 ) ) {
288                 // lavc55.lavf55.lavu52.lavr01 (ffmpeg) or lavc55.lavf55.lavu53.lavr01 (libav)
289                 natives = new FFMPEGv10Natives();
290             } else if( avCodecMajor == 56 && avFormatMajor == 56 && avUtilMajor == 54 ) {
291                 // lavc56.lavf56.lavu54.lavr02
292                 natives = new FFMPEGv11Natives();
293             } else {
294                 System.err.println("LIB_AV No Version/Native-Impl Match");
295                 natives = null;
296             }
297             if( null != natives && FFMPEGStaticNatives.initIDs0() ) {
298                 ready = natives.initSymbols0(symbolAddr, symbolCount);
299             } else {
300                 ready = false;
301             }
302         }
303     }
304 
libsLoaded()305     static boolean libsLoaded() { return libsUFCLoaded; }
avDeviceLoaded()306     static boolean avDeviceLoaded() { return libLoaded[LIB_IDX_DEV]; }
avResampleLoaded()307     static boolean avResampleLoaded() { return libLoaded[LIB_IDX_AVR]; }
swResampleLoaded()308     static boolean swResampleLoaded() { return libLoaded[LIB_IDX_SWR]; }
getNatives()309     static FFMPEGNatives getNatives() { return natives; }
initSingleton()310     static boolean initSingleton() { return ready; }
311 
FFMPEGDynamicLibraryBundleInfo()312     protected FFMPEGDynamicLibraryBundleInfo() {
313     }
314 
315     @Override
shallLinkGlobal()316     public final boolean shallLinkGlobal() { return true; }
317 
318     /**
319      * {@inheritDoc}
320      * <p>
321      * Returns <code>true</code>.
322      * </p>
323      */
324     @Override
shallLookupGlobal()325     public final boolean shallLookupGlobal() {
326         return true;
327     }
328 
329     @Override
getGlueLibNames()330     public final List<String> getGlueLibNames() {
331         return glueLibNames;
332     }
333 
334     @Override
getToolLibNames()335     public final List<List<String>> getToolLibNames() {
336         final List<List<String>> libsList = new ArrayList<List<String>>();
337 
338         // 6: util, format, codec, device, avresample, swresample
339 
340         final List<String> avutil = new ArrayList<String>();
341         avutil.add("avutil");        // default
342 
343         avutil.add("libavutil.so.55");     // dummy future proof
344         avutil.add("libavutil.so.54");     // ffmpeg 2.[4-x] / libav 11
345         avutil.add("libavutil.so.53");     // ffmpeg 2.[0-3] / libav 10
346         avutil.add("libavutil.so.52");     // ffmpeg 1.2 + 2.[0-3] / libav 9
347         avutil.add("libavutil.so.51");     // 0.8
348         avutil.add("libavutil.so.50");     // 0.7
349 
350         avutil.add("avutil-55");     // dummy future proof
351         avutil.add("avutil-54");     // ffmpeg 2.[4-x] / libav 11
352         avutil.add("avutil-53");     // ffmpeg 2.[0-3] / libav 10
353         avutil.add("avutil-52");     // ffmpeg 1.2 + 2.[0-3] / libav 9
354         avutil.add("avutil-51");     // 0.8
355         avutil.add("avutil-50");     // 0.7
356         libsList.add(avutil);
357 
358         final List<String> avformat = new ArrayList<String>();
359         avformat.add("avformat");    // default
360 
361         avformat.add("libavformat.so.57"); // dummy future proof
362         avformat.add("libavformat.so.56"); // ffmpeg 2.[4-x] / libav 11
363         avformat.add("libavformat.so.55"); // ffmpeg 2.[0-3] / libav 10
364         avformat.add("libavformat.so.54"); // ffmpeg 1.2 / libav 9
365         avformat.add("libavformat.so.53"); // 0.8
366         avformat.add("libavformat.so.52"); // 0.7
367 
368         avformat.add("avformat-57"); // dummy future proof
369         avformat.add("avformat-56"); // ffmpeg 2.[4-x] / libav 11
370         avformat.add("avformat-55"); // ffmpeg 2.[0-3] / libav 10
371         avformat.add("avformat-54"); // ffmpeg 1.2 / libav 9
372         avformat.add("avformat-53"); // 0.8
373         avformat.add("avformat-52"); // 0.7
374         libsList.add(avformat);
375 
376         final List<String> avcodec = new ArrayList<String>();
377         avcodec.add("avcodec");      // default
378 
379         avcodec.add("libavcodec.so.57");   // dummy future proof
380         avcodec.add("libavcodec.so.56");   // ffmpeg 2.[4-x] / libav 11
381         avcodec.add("libavcodec.so.55");   // ffmpeg 2.[0-3] / libav 10
382         avcodec.add("libavcodec.so.54");   // ffmpeg 1.2 / libav 9
383         avcodec.add("libavcodec.so.53");   // 0.8
384         avcodec.add("libavcodec.so.52");   // 0.7
385 
386         avcodec.add("avcodec-57");   // dummy future proof
387         avcodec.add("avcodec-56");   // ffmpeg 2.[4-x] / libav 11
388         avcodec.add("avcodec-55");   // ffmpeg 2.[0-3] / libav 10
389         avcodec.add("avcodec-54");   // ffmpeg 1.2 / libav 9
390         avcodec.add("avcodec-53");   // 0.8
391         avcodec.add("avcodec-52");   // 0.7
392         libsList.add(avcodec);
393 
394         final List<String> avdevice = new ArrayList<String>();
395         avdevice.add("avdevice");        // default
396 
397         avdevice.add("libavdevice.so.57");     // dummy future proof
398         avdevice.add("libavdevice.so.56");     // ffmpeg 2.[4-x]
399         avdevice.add("libavdevice.so.55");     // ffmpeg 2.[0-3] / libav 11
400         avdevice.add("libavdevice.so.54");     // ffmpeg 1.2 / libav 10
401         avdevice.add("libavdevice.so.53");     // 0.8 && libav 9
402 
403         avdevice.add("avdevice-57");     // dummy future proof
404         avdevice.add("avdevice-56");     // ffmpeg 2.[4-x]
405         avdevice.add("avdevice-55");     // ffmpeg 2.[0-3] / libav 11
406         avdevice.add("avdevice-54");     // ffmpeg 1.2 / libav 10
407         avdevice.add("avdevice-53");     // 0.8 && libav 9
408         libsList.add(avdevice);
409 
410         final List<String> avresample = new ArrayList<String>();
411         avresample.add("avresample");        // default
412 
413         avresample.add("libavresample.so.3");     // dummy future proof
414         avresample.add("libavresample.so.2");     // libav 11
415         avresample.add("libavresample.so.1");     // libav 9 + 10
416 
417         avresample.add("avresample-3");     // dummy future proof
418         avresample.add("avresample-2");     // libav 11
419         avresample.add("avresample-1");     // libav 9 + 10
420         libsList.add(avresample);
421 
422         final List<String> swresample = new ArrayList<String>();
423         swresample.add("swresample");        // default
424 
425         swresample.add("libswresample.so.2");     // dummy future proof
426         swresample.add("libswresample.so.1");     // ffmpeg 2.[4-x]
427         swresample.add("libswresample.so.0");     // ffmpeg 1.2 + 2.[0-3]
428 
429         swresample.add("swresample-2");     // dummy future proof
430         swresample.add("swresample-1");     // ffmpeg 2.[4-x]
431         swresample.add("swresample-0");     // ffmpeg 1.2 + 2.[0-3]
432         libsList.add(swresample);
433 
434         return libsList;
435     }
436 
437     @Override
getToolGetProcAddressFuncNameList()438     public final List<String> getToolGetProcAddressFuncNameList() {
439         return null;
440     }
441 
442     @Override
toolGetProcAddress(final long toolGetProcAddressHandle, final String funcName)443     public final long toolGetProcAddress(final long toolGetProcAddressHandle, final String funcName) {
444         return 0;
445     }
446 
447     @Override
useToolGetProcAdressFirst(final String funcName)448     public final boolean useToolGetProcAdressFirst(final String funcName) {
449         return false;
450     }
451 
452     @Override
getLibLoaderExecutor()453     public final RunnableExecutor getLibLoaderExecutor() {
454         return DynamicLibraryBundle.getDefaultRunnableExecutor();
455     }
456 }
457