/** * Copyright 2012 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: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ package jogamp.opengl.util.av.impl; 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; import com.jogamp.common.os.DynamicLibraryBundle; import com.jogamp.common.os.DynamicLibraryBundleInfo; import com.jogamp.common.util.RunnableExecutor; /** * FIXME: We need native structure access methods to deal with API changes * in the libav headers, which break binary compatibility! * Currently we are binary compatible w/ [0.6 ?, ] 0.7 and 0.8 but not w/ trunk. * * ChangeList for trunk: * Thu Jan 12 11:21:02 2012 a17479dfce67fbea2d0a1bf303010dce1e79059f major 53 -> 54 * Mon Feb 27 22:40:11 2012 ee42df8a35c2b795f524c856834d0823dbd4e75d reorder AVStream and AVFormatContext * Tue Feb 28 12:07:53 2012 322537478b63c6bc01e640643550ff539864d790 minor 1 -> 2 */ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { private static List<String> glueLibNames = new ArrayList<String>(); // none private static final int symbolCount = 32; private static String[] symbolNames = { "avcodec_version", "avformat_version", /* 3 */ "avutil_version", // libavcodec "avcodec_close", "avcodec_string", "avcodec_find_decoder", "avcodec_open2", // 53.6.0 (opt) "avcodec_open", "avcodec_alloc_frame", "avcodec_default_get_buffer", "avcodec_default_release_buffer", "av_free_packet", "avcodec_decode_audio4", // 53.25.0 (opt) "avcodec_decode_audio3", // 52.23.0 /* 15 */ "avcodec_decode_video2", // 52.23.0 // libavutil "av_pix_fmt_descriptors", "av_free", "av_get_bits_per_pixel", /* 19 */ "av_samples_get_buffer_size", // libavformat "avformat_alloc_context", "avformat_free_context", // 52.96.0 (opt) "avformat_close_input", // 53.17.0 (opt) "av_close_input_file", "av_register_all", "avformat_open_input", "av_dump_format", "av_read_frame", "av_seek_frame", "avformat_network_init", // 53.13.0 (opt) "avformat_network_deinit", // 53.13.0 (opt) "avformat_find_stream_info", // 53.3.0 (opt) /* 32 */ "av_find_stream_info", }; // alternate symbol names private static String[][] altSymbolNames = { { "avcodec_open", "avcodec_open2" }, // old, 53.6.0 { "avcodec_decode_audio3", "avcodec_decode_audio4" }, // old, 53.25.0 { "av_close_input_file", "avformat_close_input" }, // old, 53.17.0 { "av_find_stream_info", "avformat_find_stream_info" }, // old, 53.3.0 }; // optional symbol names private static String[] optionalSymbolNames = { "avformat_free_context", // 52.96.0 (opt) "avformat_network_init", // 53.13.0 (opt) "avformat_network_deinit", // 53.13.0 (opt) }; private static long[] symbolAddr; private static final boolean ready; static { // native ffmpeg media player implementation is included in jogl_desktop and jogl_mobile GLProfile.initSingleton(); boolean _ready = false; try { _ready = initSymbols(); } catch (Throwable t) { t.printStackTrace(); } ready = _ready; if(!ready) { System.err.println("FFMPEG: Not Available"); } } static boolean initSingleton() { return ready; } private static boolean initSymbols() { 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+"]"); } if(!dl.isToolLibComplete()) { throw new RuntimeException("FFMPEG Tool libraries incomplete"); } if(symbolNames.length != symbolCount) { throw new InternalError("XXX0 "+symbolNames.length+" != "+symbolCount); } symbolAddr = new long[symbolCount]; // optional symbol name set 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 for(int i = 0; i<symbolCount; i++) { symbolAddr[i] = dl.dynamicLookupFunction(symbolNames[i]); } // validate results for(int i = 0; i<symbolCount; i++) { if( 0 == symbolAddr[i] ) { // 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 && (true || DEBUG )) { // keep it verbose per default for now .. 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."); return false; } } else if(true || DEBUG ) { // keep it verbose per default for now .. System.err.println("OK: Unresolved optional symbol <"+symbolNames[i]+">"); } } } return initSymbols0(symbolAddr, symbolCount); } protected FFMPEGDynamicLibraryBundleInfo() { } @Override public boolean shallLinkGlobal() { return true; } @Override public boolean shallLookupGlobal() { return true; } @Override public final List<String> getGlueLibNames() { return glueLibNames; } @Override public List<List<String>> getToolLibNames() { List<List<String>> libsList = new ArrayList<List<String>>(); final List<String> avutil = new ArrayList<String>(); avutil.add("avutil"); // default avutil.add("libavutil.so.52"); // dummy future proof avutil.add("libavutil.so.51"); // 0.8 avutil.add("libavutil.so.50"); // 0.7 avutil.add("avutil-52"); // dummy future proof avutil.add("avutil-51"); // 0.8 avutil.add("avutil-50"); // 0.7 libsList.add(avutil); final List<String> avformat = new ArrayList<String>(); avformat.add("avformat"); // default avformat.add("libavformat.so.55"); // dummy future proof avformat.add("libavformat.so.54"); // 0.? 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"); // 0.? avformat.add("avformat-53"); // 0.8 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"); // 0.? 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"); // 0.? avcodec.add("avcodec-53"); // 0.8 avcodec.add("avcodec-52"); // 0.7 libsList.add(avcodec); return libsList; } @Override public final List<String> getToolGetProcAddressFuncNameList() { return null; } @Override public final long toolGetProcAddress(long toolGetProcAddressHandle, String funcName) { return 0; } @Override public boolean useToolGetProcAdressFirst(String funcName) { return false; } @Override public RunnableExecutor getLibLoaderExecutor() { return DynamicLibraryBundle.getDefaultRunnableExecutor(); } private static native boolean initSymbols0(long[] symbols, int count); }