001package net.filebot.mediainfo; 002 003import java.io.File; 004import java.io.IOException; 005import java.nio.ByteBuffer; 006import java.nio.channels.FileChannel; 007import java.nio.file.StandardOpenOption; 008import java.util.LinkedHashMap; 009import java.util.Map; 010 011import com.sun.jna.Memory; 012import com.sun.jna.Platform; 013import com.sun.jna.Pointer; 014import com.sun.jna.WString; 015 016public class MediaInfo implements MediaInfoProperties { 017 018 private final Pointer handle; 019 020 public MediaInfo() { 021 try { 022 // initialize native handle 023 handle = MediaInfoLibrary.INSTANCE.New(); 024 025 // initialize default options 026 option("Language", "raw"); 027 option("Complete", "1"); 028 } catch (LinkageError e) { 029 throw new MediaInfoException(e); 030 } 031 } 032 033 public synchronized int open(File file) { 034 return MediaInfoLibrary.INSTANCE.Open(handle, new WString(file.getPath())); 035 } 036 037 public synchronized long read(File file, int chunk) throws IOException { 038 try (FileChannel channel = FileChannel.open(file.toPath(), StandardOpenOption.READ)) { 039 long size = channel.size(); 040 041 // NOTE: We must not use Memory.close() since it was only added recently and is thus not yet commonly available on Linux platforms 042 Memory memory = new Memory(chunk); 043 ByteBuffer buffer = memory.getByteBuffer(0, memory.size()); 044 045 // record total number of bytes read 046 long total = 0; 047 048 MediaInfoLibrary.INSTANCE.Open_Buffer_Init(handle, size, 0); 049 int read = -1; 050 051 // read file chunk by chunk 052 while ((read = channel.read(buffer)) >= 0) { 053 total += read; 054 055 if ((MediaInfoLibrary.INSTANCE.Open_Buffer_Continue(handle, memory, read) & 0x08) == 0x08) { 056 break; 057 } 058 059 long seek = MediaInfoLibrary.INSTANCE.Open_Buffer_Continue_GoTo_Get(handle); 060 if (seek >= 0) { 061 channel.position(seek); 062 MediaInfoLibrary.INSTANCE.Open_Buffer_Init(handle, size, seek); 063 } 064 065 buffer.rewind(); 066 } 067 068 MediaInfoLibrary.INSTANCE.Open_Buffer_Finalize(handle); 069 return total; 070 } 071 } 072 073 public synchronized String option(String option) { 074 return option(option, ""); 075 } 076 077 public synchronized String option(String option, String value) { 078 return MediaInfoLibrary.INSTANCE.Option(handle, new WString(option), new WString(value)).toString(); 079 } 080 081 @Override 082 public synchronized String get(StreamKind streamKind, int streamNumber, String parameter) { 083 return MediaInfoLibrary.INSTANCE.Get(handle, streamKind.ordinal(), streamNumber, new WString(parameter), InfoKind.Text.ordinal(), InfoKind.Name.ordinal()).toString(); 084 } 085 086 public synchronized String get(StreamKind streamKind, int streamNumber, int parameterIndex, InfoKind infoKind) { 087 return MediaInfoLibrary.INSTANCE.GetI(handle, streamKind.ordinal(), streamNumber, parameterIndex, infoKind.ordinal()).toString(); 088 } 089 090 @Override 091 public synchronized int streamCount(StreamKind streamKind) { 092 // HACK: Count_Get() in MediaInfo.dll does not work correctly on Windows with newer versions of JNA for unknown reasons (see https://github.com/MediaArea/MediaInfo/issues/521) 093 if (Platform.isWindows()) { 094 String count = get(streamKind, 0, "StreamCount"); 095 return count.isEmpty() ? 0 : Integer.parseInt(count); 096 } 097 return MediaInfoLibrary.INSTANCE.Count_Get(handle, streamKind.ordinal(), -1); 098 } 099 100 public synchronized int parameterCount(StreamKind streamKind, int streamNumber) { 101 return MediaInfoLibrary.INSTANCE.Count_Get(handle, streamKind.ordinal(), streamNumber); 102 } 103 104 @Override 105 public synchronized Map<String, String> map(StreamKind streamKind, int streamNumber) { 106 int parameterCount = parameterCount(streamKind, streamNumber); 107 Map<String, String> streamInfo = new LinkedHashMap<String, String>(parameterCount); 108 109 for (int i = 0; i < parameterCount; i++) { 110 String value = get(streamKind, streamNumber, i, InfoKind.Text); 111 if (value.isEmpty()) { 112 continue; 113 } 114 115 String name = get(streamKind, streamNumber, i, InfoKind.Name); 116 streamInfo.put(name, value); 117 } 118 119 return streamInfo; 120 } 121 122 public synchronized String raw() throws IOException { 123 return MediaInfoLibrary.INSTANCE.Inform(handle).toString(); 124 } 125 126 @Override 127 public synchronized void close() { 128 MediaInfoLibrary.INSTANCE.Close(handle); 129 MediaInfoLibrary.INSTANCE.Delete(handle); 130 } 131 132 public enum InfoKind { 133 /** 134 * Unique name of parameter. 135 */ 136 Name, 137 138 /** 139 * Value of parameter. 140 */ 141 Text, 142 143 /** 144 * Unique name of measure unit of parameter. 145 */ 146 Measure, 147 148 /** 149 * See infooptions_t. 150 */ 151 Options, 152 153 /** 154 * Translated name of parameter. 155 */ 156 Name_Text, 157 158 /** 159 * Translated name of measure unit. 160 */ 161 Measure_Text, 162 163 /** 164 * More information about the parameter. 165 */ 166 Info, 167 168 /** 169 * How this parameter is supported, could be N (No), B (Beta), R (Read only), W (Read/Write). 170 */ 171 HowTo, 172 173 /** 174 * Domain of this piece of information. 175 */ 176 Domain; 177 } 178 179 public static String version() { 180 return staticOption("Info_Version"); 181 } 182 183 public static String parameters() { 184 return staticOption("Info_Parameters"); 185 } 186 187 public static String codecs() { 188 return staticOption("Info_Codecs"); 189 } 190 191 public static String capacities() { 192 return staticOption("Info_Capacities"); 193 } 194 195 public static String staticOption(String option) { 196 return staticOption(option, ""); 197 } 198 199 public static String staticOption(String option, String value) { 200 return MediaInfoLibrary.INSTANCE.Option(null, new WString(option), new WString(value)).toString(); 201 } 202 203}