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}