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