001package videolan.libdvdnav; 002 003import java.io.IOException; 004import java.io.UnsupportedEncodingException; 005import java.util.Objects; 006import lombok.Data; 007import lombok.Synchronized; 008import lombok.ToString; 009import org.fusesource.hawtjni.runtime.JniArg; 010import org.fusesource.hawtjni.runtime.JniClass; 011import org.fusesource.hawtjni.runtime.JniField; 012import org.fusesource.hawtjni.runtime.JniMethod; 013import org.fusesource.hawtjni.runtime.Library; 014 015import static java.nio.charset.StandardCharsets.UTF_8; 016import static org.fusesource.hawtjni.runtime.ArgFlag.CRITICAL; 017import static org.fusesource.hawtjni.runtime.ArgFlag.NO_IN; 018import static org.fusesource.hawtjni.runtime.FieldFlag.CONSTANT; 019import static org.fusesource.hawtjni.runtime.MethodFlag.CONSTANT_INITIALIZER; 020 021/** 022 * {@link DVDNav} is the Java entry-point to 023 * {@link.uri https://www.videolan.org/developers/libdvdnav.html libdvdnav}. 024 * 025 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball} 026 * @version $Revision: 5985 $ 027 */ 028@JniClass 029@ToString 030public class DVDNav implements AutoCloseable { 031 private static final Library LIBRARY = 032 new Library("videolan-libdvdnav", DVDNav.class); 033 034 @JniMethod(flags = { CONSTANT_INITIALIZER }) 035 private static final native void init(); 036 037 static { LIBRARY.load(); init(); } 038 039 /** 040 * {@code <dvdnav/dvdnav.h>} 041 */ 042 @JniField(flags = { CONSTANT }) 043 private static int 044 DVDNAV_STATUS_ERR, 045 DVDNAV_STATUS_OK; 046 047 /** 048 * {@code <dvdnav/dvdnav_events.h>} 049 */ 050 @JniField(flags = { CONSTANT }) 051 private static int 052 DVDNAV_BLOCK_OK, 053 DVDNAV_NOP, 054 DVDNAV_STILL_FRAME, 055 DVDNAV_SPU_STREAM_CHANGE, 056 DVDNAV_AUDIO_STREAM_CHANGE, 057 DVDNAV_VTS_CHANGE, 058 DVDNAV_CELL_CHANGE, 059 DVDNAV_NAV_PACKET, 060 DVDNAV_STOP, 061 DVDNAV_HIGHLIGHT, 062 DVDNAV_SPU_CLUT_CHANGE, 063 DVDNAV_HOP_CHANNEL, 064 DVDNAV_WAIT; 065 066 /** 067 * {@code <dvdread/dvd_reader.h>} 068 */ 069 @JniField(flags = { CONSTANT }) 070 private static int 071 DVD_VIDEO_LB_LEN, 072 MAX_UDF_FILE_NAME_LEN; 073 074 /** 075 * {@code <fcntl.h>} 076 */ 077 @JniField(flags = { CONSTANT }) 078 private static int 079 SEEK_SET, 080 SEEK_CUR, 081 SEEK_END; 082 083 private final String path; 084 private final long self; 085 private boolean finished = false; 086 private boolean closed = false; 087 private String title = null; 088 private String serial = null; 089 090 /** 091 * Sole constructor. 092 * 093 * @param path The path ({@link String}) to the DVD 094 * device. 095 * 096 * @throws IOException If the device cannot be opened. 097 */ 098 public DVDNav(String path) throws IOException { 099 this.path = Objects.requireNonNull(path); 100 101 long selfp[] = new long[] { 0L }; 102 103 if (dvdnav_open(selfp, path) == DVDNAV_STATUS_OK) { 104 self = selfp[0]; 105 } else { 106 throw new IOException("Cannot open " + path); 107 } 108 109 if (set_readahead_flag((byte) 0) != DVDNAV_STATUS_OK) { 110 throw new IOException(err_to_string()); 111 } 112 } 113 114 protected int set_readahead_flag(byte flag) { 115 return dvdnav_set_readahead_flag(self, flag); 116 } 117 118 protected String err_to_string() { return dvdnav_err_to_string(self); } 119 120 @Synchronized 121 @Override 122 public void close() { 123 if (! closed) { 124 if (self != 0L) { 125 dvdnav_close(self); 126 } 127 } 128 } 129 130 public void setLanguage(String language) throws IOException { 131 if (menu_language_select(language) != DVDNAV_STATUS_OK 132 || audio_language_select(language) != DVDNAV_STATUS_OK 133 || spu_language_select(language) != DVDNAV_STATUS_OK) { 134 throw new IOException(err_to_string()); 135 } 136 } 137 138 protected int menu_language_select(String language) { 139 return dvdnav_menu_language_select(self, language); 140 } 141 142 protected int audio_language_select(String language) { 143 return dvdnav_audio_language_select(self, language); 144 } 145 146 protected int spu_language_select(String language) { 147 return dvdnav_spu_language_select(self, language); 148 } 149 150 public String getTitle() { 151 if (title == null) { 152 long[] titlep = new long[] { 0L }; 153 154 if (dvdnav_get_title_string(self, titlep) == DVDNAV_STATUS_OK) { 155 title = strdup(titlep[0]); 156 } 157 } 158 159 return title; 160 } 161 162 public String getSerial() { 163 if (serial == null) { 164 long[] serialp = new long[] { 0L }; 165 166 if (dvdnav_get_serial_string(self, serialp) == DVDNAV_STATUS_OK) { 167 serial = strdup(serialp[0]); 168 } 169 } 170 171 return serial; 172 } 173 174 @Synchronized 175 public Block getNextBlock() throws IOException { 176 Block block = null; 177 178 if (! finished) { 179 byte[] buf = new byte[DVD_VIDEO_LB_LEN]; 180 int[] eventp = new int[] { 0 }; 181 int[] lenp = new int[] { 0 }; 182 183 if (get_next_block(buf, eventp, lenp) == DVDNAV_STATUS_OK) { 184 block = new Block(buf, eventp[0], lenp[0]); 185 finished |= (block.getEvent() == DVDNAV_STOP); 186 } else { 187 throw new IOException(err_to_string()); 188 } 189 } 190 191 return block; 192 } 193 194 protected int get_next_block(byte[] buf, int[] eventp, int[] lenp) { 195 return dvdnav_get_next_block(self, buf, eventp, lenp); 196 } 197 198 @Data 199 public class Block { 200 private final byte[] buf; 201 private final int event; 202 private final int len; 203 } 204 205 private static native int dvdnav_open(@JniArg(cast = "dvdnav_t **") long[] selfp, 206 @JniArg(cast = "const char *") String path); 207 208 private static native int dvdnav_dup(@JniArg(cast = "dvdnav_t **") long[] destp, 209 @JniArg(cast = "dvdnav_t *") long src); 210 211 private static native int dvdnav_free_dup(@JniArg(cast = "dvdnav_t *") long self); 212 213 private static native int dvdnav_close(@JniArg(cast = "dvdnav_t *") long self); 214 215 private static native int dvdnav_reset(@JniArg(cast = "dvdnav_t *") long self); 216 /* 217 * dvdnav_status_t dvdnav_path(dvdnav_t *self, const char **path); 218 * 219 * private static native int dvdnav_path(@JniArg(cast = "dvdnav_t *") long self, const char **path); 220 */ 221 private static native String dvdnav_err_to_string(@JniArg(cast = "dvdnav_t *") long self); 222 223 private static native String dvdnav_version(); 224 225 private static native int dvdnav_set_region_mask(@JniArg(cast = "dvdnav_t *") long self, 226 int mask); 227 228 private static native int dvdnav_get_region_mask(@JniArg(cast = "dvdnav_t *") long self, 229 int[] maskp); 230 231 private static native int dvdnav_set_readahead_flag(@JniArg(cast = "dvdnav_t *") long self, 232 int flag); 233 234 private static native int dvdnav_get_readahead_flag(@JniArg(cast = "dvdnav_t *") long self, 235 int[] flagp); 236 237 private static native int dvdnav_set_PGC_positioning_flag(@JniArg(cast = "dvdnav_t *") long self, 238 int flag); 239 240 private static native int dvdnav_get_PGC_positioning_flag(@JniArg(cast = "dvdnav_t *") long self, 241 int[] flagp); 242 243 private static native int dvdnav_get_next_block(@JniArg(cast = "dvdnav_t *") long self, 244 byte[] buf, int[] eventp, int[] lenp); 245 /* 246 * dvdnav_status_t 247 * dvdnav_get_next_cache_block(dvdnav_t *self, uint8_t **buf, 248 * int32_t *event, int32_t *len); 249 * 250 * private static native int dvdnav_get_next_cache_block(@JniArg(cast = "dvdnav_t *") long self, uint8_t **buf, int[] eventp, int[] lenp); 251 */ 252 /* 253 * dvdnav_status_t dvdnav_free_cache_block(dvdnav_t *self, 254 * unsigned char *buf); 255 * 256 * private static native int dvdnav_free_cache_block(@JniArg(cast = "dvdnav_t *") long self, unsigned char *buf); 257 */ 258 private static native int dvdnav_still_skip(@JniArg(cast = "dvdnav_t *") long self); 259 260 private static native int dvdnav_wait_skip(@JniArg(cast = "dvdnav_t *") long self); 261 262 private static native int dvdnav_get_next_still_flag(@JniArg(cast = "dvdnav_t *") long self); 263 264 private static native int dvdnav_stop(@JniArg(cast = "dvdnav_t *") long self); 265 266 private static native int dvdnav_get_number_of_titles(@JniArg(cast = "dvdnav_t *") long self, 267 int[] titlesp); 268 269 private static native int dvdnav_get_number_of_parts(@JniArg(cast = "dvdnav_t *") long self, 270 int title, int[] partsp); 271 272 private static native int dvdnav_title_play(@JniArg(cast = "dvdnav_t *") long self, 273 int title); 274 /* 275 * Currently unimplemented! 276 * dvdnav_status_t 277 * dvdnav_part_play(dvdnav_t *self, int32_t title, int32_t part); 278 */ 279 private static native int dvdnav_program_play(@JniArg(cast = "dvdnav_t *") long self, 280 int title, int pgcn, int pgn); 281 /* 282 * uint32_t 283 * dvdnav_describe_title_chapters(dvdnav_t *self, int32_t title, 284 * uint64_t **times, uint64_t *duration); 285 * 286 * private static native int dvdnav_describe_title_chapters(@JniArg(cast = "dvdnav_t *") long self, int title, uint64_t **times, long[] durationp); 287 */ 288 /* 289 * Currently unimplemented! 290 * dvdnav_status_t dvdnav_part_play_auto_stop(dvdnav_t *self, int32_t title, int32_t part, int32_t parts_to_play); 291 */ 292 /* 293 * Currently unimplemented! 294 * dvdnav_status_t dvdnav_time_play(dvdnav_t *self, int32_t title, uint64_t time); 295 */ 296 /* 297 * dvdnav_status_t dvdnav_menu_call(dvdnav_t *self, DVDMenuID_t menu); 298 * 299 * private static native int dvdnav_menu_call(@JniArg(cast = "dvdnav_t *") long self, DVDMenuID_t menu); 300 */ 301 private static native int dvdnav_current_title_info(@JniArg(cast = "dvdnav_t *") long self, 302 int[] titlep, int[] partp); 303 304 private static native int dvdnav_current_title_program(@JniArg(cast = "dvdnav_t *") long self, 305 int[] titlep, int[] pgcnp, int[] pgnp); 306 /* 307 * Current implementation is wrong and likely to behave unpredictably! 308 * Use is discouraged! 309 * 310 * dvdnav_status_t 311 * dvdnav_get_position_in_title(dvdnav_t *self, 312 * uint32_t *pos, uint32_t *len); 313 */ 314 private static native int dvdnav_part_search(@JniArg(cast = "dvdnav_t *") long self, 315 int part); 316 317 private static native int dvdnav_sector_search(@JniArg(cast = "dvdnav_t *") long self, 318 long offset, int origin); 319 320 private static native long dvdnav_get_current_time(@JniArg(cast = "dvdnav_t *") long self); 321 322 private static native int dvdnav_time_search(@JniArg(cast = "dvdnav_t *") long self, 323 long time); 324 325 private static native int dvdnav_go_up(@JniArg(cast = "dvdnav_t *") long self); 326 327 private static native int dvdnav_prev_pg_search(@JniArg(cast = "dvdnav_t *") long self); 328 329 private static native int dvdnav_top_pg_search(@JniArg(cast = "dvdnav_t *") long self); 330 331 private static native int dvdnav_next_pg_search(@JniArg(cast = "dvdnav_t *") long self); 332 333 private static native int dvdnav_get_position(@JniArg(cast = "dvdnav_t *") long self, 334 int[] posp, int[] lenp); 335 336 private static native int dvdnav_get_current_highlight(@JniArg(cast = "dvdnav_t *") long self, 337 int[] buttonp); 338 339 private static native long dvdnav_get_current_nav_pci(@JniArg(cast = "dvdnav_t *") long self); 340 341 private static native long dvdnav_get_current_nav_dsi(@JniArg(cast = "dvdnav_t *") long self); 342 /* 343 * dvdnav_status_t 344 * dvdnav_get_highlight_area(pci_t *nav_pci, int32_t button, int32_t mode, 345 * dvdnav_highlight_area_t *highlight); 346 * 347 * private static native int dvdnav_get_highlight_area(@JniArg(cast = "pci_t *") long nav_pci, int button, int mode, dvdnav_highlight_area_t *highlight); 348 */ 349 private static native int dvdnav_upper_button_select(@JniArg(cast = "dvdnav_t *") long self, 350 @JniArg(cast = "pci_t *") long pci); 351 352 private static native int dvdnav_lower_button_select(@JniArg(cast = "dvdnav_t *") long self, 353 @JniArg(cast = "pci_t *") long pci); 354 355 private static native int dvdnav_right_button_select(@JniArg(cast = "dvdnav_t *") long self, 356 @JniArg(cast = "pci_t *") long pci); 357 358 private static native int dvdnav_left_button_select(@JniArg(cast = "dvdnav_t *") long self, 359 @JniArg(cast = "pci_t *") long pci); 360 361 private static native int dvdnav_button_activate(@JniArg(cast = "dvdnav_t *") long self, 362 @JniArg(cast = "pci_t *") long pci); 363 364 private static native int dvdnav_button_select(@JniArg(cast = "dvdnav_t *") long self, 365 @JniArg(cast = "pci_t *") long pci, int button); 366 367 private static native int dvdnav_button_select_and_activate(@JniArg(cast = "dvdnav_t *") long self, 368 @JniArg(cast = "pci_t *") long pci, int button); 369 /* 370 * dvdnav_status_t 371 * dvdnav_button_activate_cmd(dvdnav_t *self, 372 * int32_t button, vm_cmd_t *cmd); 373 * 374 * private static native int dvdnav_button_activate_cmd(@JniArg(cast = "dvdnav_t *") long self, int button, vm_cmd_t *cmd); 375 */ 376 private static native int dvdnav_mouse_select(@JniArg(cast = "dvdnav_t *") long self, 377 @JniArg(cast = "pci_t *") long pci, int x, int y); 378 379 private static native int dvdnav_mouse_activate(@JniArg(cast = "dvdnav_t *") long self, 380 @JniArg(cast = "pci_t *") long pci, int x, int y); 381 382 private static native int dvdnav_menu_language_select(@JniArg(cast = "dvdnav_t *") long self, 383 String code); 384 385 private static native int dvdnav_audio_language_select(@JniArg(cast = "dvdnav_t *") long self, 386 String code); 387 388 private static native int dvdnav_spu_language_select(@JniArg(cast = "dvdnav_t *") long self, 389 String code); 390 391 private static native int dvdnav_get_title_string(@JniArg(cast = "dvdnav_t *") long self, 392 @JniArg(cast = "const char **") long[] titlep); 393 394 private static native int dvdnav_get_serial_string(@JniArg(cast = "dvdnav_t *") long self, 395 @JniArg(cast = "const char **") long[] serialp); 396 397 private static native byte dvdnav_get_video_aspect(@JniArg(cast = "dvdnav_t *") long self); 398 399 private static native int dvdnav_get_video_resolution(@JniArg(cast = "dvdnav_t *") long self, 400 int[] widthp, int[] heightp); 401 402 private static native byte dvdnav_get_video_scale_permission(@JniArg(cast = "dvdnav_t *") long self); 403 404 private static native char dvdnav_audio_stream_to_lang(@JniArg(cast = "dvdnav_t *") long self, 405 byte stream); 406 407 private static native char dvdnav_audio_stream_format(@JniArg(cast = "dvdnav_t *") long self, 408 byte stream); 409 410 private static native char dvdnav_audio_stream_channels(@JniArg(cast = "dvdnav_t *") long self, 411 byte stream); 412 413 private static native char dvdnav_spu_stream_to_lang(@JniArg(cast = "dvdnav_t *") long self, 414 byte stream); 415 416 private static native byte dvdnav_get_audio_logical_stream(@JniArg(cast = "dvdnav_t *") long self, 417 byte audio_num); 418 /* 419 * dvdnav_status_t 420 * dvdnav_get_audio_attr(dvdnav_t *self, uint8_t audio_mum, audio_attr_t *audio_attr); 421 * 422 * private static native int dvdnav_get_audio_attr(@JniArg(cast = "dvdnav_t *") long self, byte audio_mum, audio_attr_t *audio_attr); 423 */ 424 private static native byte dvdnav_get_spu_logical_stream(@JniArg(cast = "dvdnav_t *") long self, 425 byte subp_num); 426 /* 427 * dvdnav_status_t 428 * dvdnav_get_spu_attr(dvdnav_t *self, uint8_t audio_mum, subp_attr_t *subp_attr); 429 * 430 * private static native int dvdnav_get_spu_attr(@JniArg(cast = "dvdnav_t *") long self, byte audio_mum, subp_attr_t *subp_attr); 431 */ 432 private static native byte dvdnav_get_active_audio_stream(@JniArg(cast = "dvdnav_t *") long self); 433 434 private static native byte dvdnav_get_active_spu_stream(@JniArg(cast = "dvdnav_t *") long self); 435 436 private static native int dvdnav_angle_change(@JniArg(cast = "dvdnav_t *") long self, 437 int angle); 438 439 private static native int dvdnav_get_angle_info(@JniArg(cast = "dvdnav_t *") long self, 440 int[] current_anglep, 441 int[] number_of_anglesp); 442 443 private static native byte dvdnav_is_domain_fp(@JniArg(cast = "dvdnav_t *") long self); 444 445 private static native byte dvdnav_is_domain_vmgm(@JniArg(cast = "dvdnav_t *") long self); 446 447 private static native byte dvdnav_is_domain_vtsm(@JniArg(cast = "dvdnav_t *") long self); 448 449 private static native byte dvdnav_is_domain_vts(@JniArg(cast = "dvdnav_t *") long self); 450 451 private String strdup(long pointer) { 452 String string = null; 453 454 if (pointer != 0L) { 455 int length = strlen(pointer); 456 byte[] bytes = new byte[length]; 457 458 memmove(bytes, pointer, bytes.length); 459 460 try { 461 string = new String(bytes, UTF_8.name()); 462 } catch (UnsupportedEncodingException exception) { 463 throw new IllegalStateException(exception); 464 } 465 } 466 467 return string; 468 } 469 470 private static native void memmove(@JniArg(cast = "void *", flags = { NO_IN, CRITICAL }) byte[] out, 471 @JniArg(cast = "const void *") long in, 472 @JniArg(cast = "size_t") long size); 473 private static native int strlen(@JniArg(cast = "char *") long pointer); 474}