2014-04-19 02:30:29 -04:00
package net.filebot.mediainfo ;
2009-03-27 11:22:08 -04:00
2016-07-27 18:24:13 -04:00
import static java.nio.charset.StandardCharsets.* ;
2017-02-25 04:31:49 -05:00
import static net.filebot.Logging.* ;
2016-07-27 18:24:13 -04:00
2009-03-27 11:22:08 -04:00
import java.io.Closeable ;
import java.io.File ;
2012-07-30 08:05:18 -04:00
import java.io.IOException ;
2015-05-24 18:53:47 -04:00
import java.io.RandomAccessFile ;
2009-03-27 11:22:08 -04:00
import java.util.ArrayList ;
import java.util.EnumMap ;
2009-04-06 16:34:33 -04:00
import java.util.LinkedHashMap ;
2009-03-27 11:22:08 -04:00
import java.util.List ;
import java.util.Map ;
2009-07-27 18:34:42 -04:00
import com.sun.jna.Platform ;
2009-03-27 11:22:08 -04:00
import com.sun.jna.Pointer ;
import com.sun.jna.WString ;
public class MediaInfo implements Closeable {
2014-04-30 22:14:33 -04:00
2009-07-27 18:34:42 -04:00
private Pointer handle ;
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
public MediaInfo ( ) {
2011-11-14 21:50:48 -05:00
try {
handle = MediaInfoLibrary . INSTANCE . New ( ) ;
} catch ( LinkageError e ) {
2015-06-19 12:27:29 -04:00
throw new MediaInfoException ( e ) ;
2011-11-14 21:50:48 -05:00
}
2009-03-27 11:22:08 -04:00
}
2014-04-30 22:14:33 -04:00
2016-03-12 05:38:07 -05:00
public synchronized MediaInfo open ( File file ) throws IOException , IllegalArgumentException {
2015-05-25 14:15:22 -04:00
if ( ! file . isFile ( ) | | file . length ( ) < 64 * 1024 ) {
2016-03-11 03:16:59 -05:00
throw new IllegalArgumentException ( " Invalid media file: " + file ) ;
2015-05-24 18:53:47 -04:00
}
2014-04-30 22:14:33 -04:00
2016-03-11 03:16:59 -05:00
String path = file . getCanonicalPath ( ) ;
2015-05-24 18:53:47 -04:00
// on Mac files that contain accents cannot be opened via JNA WString file paths due to encoding differences so we use the buffer interface instead for these files
2016-07-27 18:24:13 -04:00
if ( Platform . isMac ( ) & & ! US_ASCII . newEncoder ( ) . canEncode ( path ) ) {
2015-05-24 18:53:47 -04:00
try ( RandomAccessFile raf = new RandomAccessFile ( file , " r " ) ) {
2016-03-11 03:16:59 -05:00
if ( openViaBuffer ( raf ) ) {
2016-07-27 18:24:13 -04:00
return this ;
2016-03-11 03:16:59 -05:00
}
2016-07-27 18:24:13 -04:00
throw new IOException ( " Failed to initialize media info buffer: " + path ) ;
2015-05-24 18:53:47 -04:00
}
2013-06-24 05:23:14 -04:00
}
2015-05-24 18:53:47 -04:00
2016-07-27 18:24:13 -04:00
// open via file path
if ( 0 ! = MediaInfoLibrary . INSTANCE . Open ( handle , new WString ( path ) ) ) {
return this ;
2016-03-11 03:16:59 -05:00
}
2016-07-27 18:24:13 -04:00
throw new IOException ( " Failed to open media file: " + path ) ;
2009-03-27 11:22:08 -04:00
}
2014-04-30 22:14:33 -04:00
2015-05-24 18:53:47 -04:00
private boolean openViaBuffer ( RandomAccessFile f ) throws IOException {
2015-05-24 18:54:53 -04:00
byte [ ] buffer = new byte [ 4 * 1024 * 1024 ] ; // use large buffer to reduce JNA calls
2015-05-24 18:53:47 -04:00
int read = - 1 ;
2016-03-11 03:16:59 -05:00
if ( 0 = = MediaInfoLibrary . INSTANCE . Open_Buffer_Init ( handle , f . length ( ) , 0 ) ) {
2015-05-24 18:53:47 -04:00
return false ;
}
do {
read = f . read ( buffer ) ;
int result = MediaInfoLibrary . INSTANCE . Open_Buffer_Continue ( handle , buffer , read ) ;
if ( ( result & 8 ) = = 8 ) {
break ;
}
2015-05-24 18:54:53 -04:00
long gotoPos = MediaInfoLibrary . INSTANCE . Open_Buffer_Continue_GoTo_Get ( handle ) ;
if ( gotoPos > = 0 ) {
2015-05-24 18:53:47 -04:00
f . seek ( gotoPos ) ;
MediaInfoLibrary . INSTANCE . Open_Buffer_Init ( handle , f . length ( ) , gotoPos ) ;
}
} while ( read > 0 ) ;
MediaInfoLibrary . INSTANCE . Open_Buffer_Finalize ( handle ) ;
return true ;
}
2009-07-29 16:31:08 -04:00
public synchronized String inform ( ) {
2009-03-27 11:22:08 -04:00
return MediaInfoLibrary . INSTANCE . Inform ( handle ) . toString ( ) ;
}
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
public String option ( String option ) {
return option ( option , " " ) ;
}
2014-04-30 22:14:33 -04:00
2009-07-29 16:31:08 -04:00
public synchronized String option ( String option , String value ) {
2009-03-27 11:22:08 -04:00
return MediaInfoLibrary . INSTANCE . Option ( handle , new WString ( option ) , new WString ( value ) ) . toString ( ) ;
}
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
public String get ( StreamKind streamKind , int streamNumber , String parameter ) {
return get ( streamKind , streamNumber , parameter , InfoKind . Text , InfoKind . Name ) ;
}
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
public String get ( StreamKind streamKind , int streamNumber , String parameter , InfoKind infoKind ) {
return get ( streamKind , streamNumber , parameter , infoKind , InfoKind . Name ) ;
}
2014-04-30 22:14:33 -04:00
2009-07-29 16:31:08 -04:00
public synchronized String get ( StreamKind streamKind , int streamNumber , String parameter , InfoKind infoKind , InfoKind searchKind ) {
2009-03-27 11:22:08 -04:00
return MediaInfoLibrary . INSTANCE . Get ( handle , streamKind . ordinal ( ) , streamNumber , new WString ( parameter ) , infoKind . ordinal ( ) , searchKind . ordinal ( ) ) . toString ( ) ;
}
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
public String get ( StreamKind streamKind , int streamNumber , int parameterIndex ) {
return get ( streamKind , streamNumber , parameterIndex , InfoKind . Text ) ;
}
2014-04-30 22:14:33 -04:00
2009-07-29 16:31:08 -04:00
public synchronized String get ( StreamKind streamKind , int streamNumber , int parameterIndex , InfoKind infoKind ) {
2009-03-27 11:22:08 -04:00
return MediaInfoLibrary . INSTANCE . GetI ( handle , streamKind . ordinal ( ) , streamNumber , parameterIndex , infoKind . ordinal ( ) ) . toString ( ) ;
}
2014-04-30 22:14:33 -04:00
2009-07-29 16:31:08 -04:00
public synchronized int streamCount ( StreamKind streamKind ) {
2009-03-27 11:22:08 -04:00
return MediaInfoLibrary . INSTANCE . Count_Get ( handle , streamKind . ordinal ( ) , - 1 ) ;
}
2014-04-30 22:14:33 -04:00
2009-07-29 16:31:08 -04:00
public synchronized int parameterCount ( StreamKind streamKind , int streamNumber ) {
2009-03-27 11:22:08 -04:00
return MediaInfoLibrary . INSTANCE . Count_Get ( handle , streamKind . ordinal ( ) , streamNumber ) ;
}
2014-04-30 22:14:33 -04:00
2009-04-06 16:34:33 -04:00
public Map < StreamKind , List < Map < String , String > > > snapshot ( ) {
Map < StreamKind , List < Map < String , String > > > mediaInfo = new EnumMap < StreamKind , List < Map < String , String > > > ( StreamKind . class ) ;
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
for ( StreamKind streamKind : StreamKind . values ( ) ) {
int streamCount = streamCount ( streamKind ) ;
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
if ( streamCount > 0 ) {
2009-04-06 16:34:33 -04:00
List < Map < String , String > > streamInfoList = new ArrayList < Map < String , String > > ( streamCount ) ;
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
for ( int i = 0 ; i < streamCount ; i + + ) {
streamInfoList . add ( snapshot ( streamKind , i ) ) ;
}
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
mediaInfo . put ( streamKind , streamInfoList ) ;
}
}
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
return mediaInfo ;
}
2014-04-30 22:14:33 -04:00
2009-04-06 16:34:33 -04:00
public Map < String , String > snapshot ( StreamKind streamKind , int streamNumber ) {
Map < String , String > streamInfo = new LinkedHashMap < String , String > ( ) ;
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
for ( int i = 0 , count = parameterCount ( streamKind , streamNumber ) ; i < count ; i + + ) {
String value = get ( streamKind , streamNumber , i , InfoKind . Text ) ;
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
if ( value . length ( ) > 0 ) {
streamInfo . put ( get ( streamKind , streamNumber , i , InfoKind . Name ) , value ) ;
}
}
2014-04-30 22:14:33 -04:00
2017-02-24 15:05:07 -05:00
// MediaInfo does not support EXIF image metadata natively so we use the metadata-extractor library and implicitly merge that information in
if ( streamKind = = StreamKind . Image & & streamNumber = = 0 ) {
2017-02-25 04:31:49 -05:00
String path = get ( StreamKind . General , 0 , " CompleteName " ) ;
try {
2017-02-25 06:24:47 -05:00
streamInfo . putAll ( new ImageMetadata ( new File ( path ) ) ) ;
2017-02-25 04:31:49 -05:00
} catch ( Throwable e ) {
debug . warning ( format ( " %s: %s " , e , path ) ) ;
}
2017-02-24 15:05:07 -05:00
}
2009-03-27 11:22:08 -04:00
return streamInfo ;
}
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
@Override
2009-07-29 16:31:08 -04:00
public synchronized void close ( ) {
2009-03-27 11:22:08 -04:00
MediaInfoLibrary . INSTANCE . Close ( handle ) ;
}
2014-04-30 22:14:33 -04:00
2009-07-29 16:31:08 -04:00
public synchronized void dispose ( ) {
2016-03-02 10:55:06 -05:00
if ( handle = = null ) {
2009-07-29 16:31:08 -04:00
return ;
2016-03-02 10:55:06 -05:00
}
2015-11-14 13:24:31 -05:00
2009-07-29 16:31:08 -04:00
// delete handle
2009-03-27 11:22:08 -04:00
MediaInfoLibrary . INSTANCE . Delete ( handle ) ;
handle = null ;
}
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
@Override
2009-07-29 16:31:08 -04:00
protected void finalize ( ) {
dispose ( ) ;
2009-03-27 11:22:08 -04:00
}
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
public enum StreamKind {
2012-07-30 08:05:18 -04:00
General , Video , Audio , Text , Chapters , Image , Menu ;
2009-03-27 11:22:08 -04:00
}
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
public enum InfoKind {
/ * *
* Unique name of parameter .
* /
Name ,
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
/ * *
* Value of parameter .
* /
Text ,
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
/ * *
* Unique name of measure unit of parameter .
* /
Measure ,
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
Options ,
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
/ * *
* Translated name of parameter .
* /
Name_Text ,
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
/ * *
* Translated name of measure unit .
* /
Measure_Text ,
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
/ * *
* More information about the parameter .
* /
Info ,
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
/ * *
2012-07-30 08:05:18 -04:00
* How this parameter is supported , could be N ( No ) , B ( Beta ) , R ( Read only ) , W ( Read / Write ) .
2009-03-27 11:22:08 -04:00
* /
HowTo ,
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
/ * *
* Domain of this piece of information .
* /
Domain ;
}
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
public static String version ( ) {
return staticOption ( " Info_Version " ) ;
}
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
public static String parameters ( ) {
return staticOption ( " Info_Parameters " ) ;
}
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
public static String codecs ( ) {
return staticOption ( " Info_Codecs " ) ;
}
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
public static String capacities ( ) {
return staticOption ( " Info_Capacities " ) ;
}
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
public static String staticOption ( String option ) {
return staticOption ( option , " " ) ;
}
2014-04-30 22:14:33 -04:00
2009-03-27 11:22:08 -04:00
public static String staticOption ( String option , String value ) {
2011-11-14 21:50:48 -05:00
try {
return MediaInfoLibrary . INSTANCE . Option ( null , new WString ( option ) , new WString ( value ) ) . toString ( ) ;
} catch ( LinkageError e ) {
throw new MediaInfoException ( e ) ;
}
2009-03-27 11:22:08 -04:00
}
2014-04-30 22:14:33 -04:00
2012-07-30 08:05:18 -04:00
public static Map < StreamKind , List < Map < String , String > > > snapshot ( File file ) throws IOException {
2016-08-09 16:13:39 -04:00
try ( MediaInfo mi = new MediaInfo ( ) . open ( file ) ) {
return mi . snapshot ( ) ;
2012-07-30 08:05:18 -04:00
}
}
2016-03-11 03:16:59 -05:00
2009-03-27 11:22:08 -04:00
}