Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:smarty12:multimedia
libdvdnav
libdvdnav.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File libdvdnav.patch of Package libdvdnav
diff -durN a/src/dvdnav/dvdnav.h b/src/dvdnav/dvdnav.h --- a/src/dvdnav/dvdnav.h 2020-05-03 15:01:16.798469411 +0200 +++ b/src/dvdnav/dvdnav.h 2020-05-03 15:01:26.138448826 +0200 @@ -30,6 +30,7 @@ extern "C" { #endif +#include <stdbool.h> #include "version.h" #include <dvdnav/dvd_types.h> #include <dvdread/dvd_reader.h> @@ -410,6 +411,14 @@ int64_t dvdnav_get_current_time(dvdnav_t *self); /* + * Find the nearest vobu and jump to it + * + * Alternative to dvdnav_time_search + */ +dvdnav_status_t dvdnav_jump_to_sector_by_time(dvdnav_t *self, + uint64_t time_in_pts_ticks, int32_t mode); + +/* * Stop playing the current position and start playback of the title * from the specified timecode. * @@ -727,6 +736,18 @@ */ int8_t dvdnav_is_domain_vts(dvdnav_t *self); +int dvdnav_get_button_info(dvdnav_t *self, int alpha[2][4], int color[2][4]); +uint32_t dvdnav_kodi_get_vmg_category(dvdnav_t *self); +dvdnav_status_t dvdnav_kodi_set_active_audio_stream(dvdnav_t *self, int streamId); +dvdnav_status_t dvdnav_kodi_set_active_subtitle_stream(dvdnav_t *self, int streamId); +int dvdnav_kodi_get_subtitle_count(dvdnav_t *self); +int dvdnav_kodi_get_audio_stream_count(dvdnav_t *self); +void dvdnav_kodi_enable_subtitle_stream(dvdnav_t *self, bool enable); +bool dvdnav_kodi_get_subtitle_stream_state(dvdnav_t *self); +int dvdnav_kodi_convert_audio_stream_id_to_xbmc(dvdnav_t *self, int id); +int dvdnav_kodi_convert_audio_stream_id_from_xbmc(dvdnav_t *self, int id); +int dvdnav_kodi_convert_subtitle_stream_id_to_xbmc(dvdnav_t *self, int id); +int dvdnav_kodi_convert_subtitle_stream_id_from_xbmc(dvdnav_t *self, int id); #ifdef __cplusplus } diff -durN a/src/dvdnav/dvdnav.h.orig b/src/dvdnav/dvdnav.h.orig --- a/src/dvdnav/dvdnav.h.orig 1970-01-01 01:00:00.000000000 +0100 +++ b/src/dvdnav/dvdnav.h.orig 2020-05-03 15:01:15.766471686 +0200 @@ -0,0 +1,735 @@ +/* + * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net> + * + * This file is part of libdvdnav, a DVD navigation library. + * + * libdvdnav is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * libdvdnav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with libdvdnav; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * This is the main header file applications should include if they want + * to access dvdnav functionality. + */ + +#ifndef LIBDVDNAV_DVDNAV_H +#define LIBDVDNAV_DVDNAV_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "version.h" +#include <dvdnav/dvd_types.h> +#include <dvdread/dvd_reader.h> +#include <dvdread/nav_types.h> +#include <dvdread/ifo_types.h> /* For vm_cmd_t */ +#include <dvdnav/dvdnav_events.h> + +#include <stdarg.h> + +/********************************************************************* + * dvdnav data types * + *********************************************************************/ + +/* + * Opaque data-type can be viewed as a 'DVD handle'. You should get + * a pointer to a dvdnav_t from the dvdnav_open() function. + * Never call free() on the pointer, you have to give it back with + * dvdnav_close(). + */ +typedef struct dvdnav_s dvdnav_t; + +/* Status as reported by most of libdvdnav's functions */ +typedef int32_t dvdnav_status_t; + +typedef dvd_reader_stream_cb dvdnav_stream_cb; + +/* + * Unless otherwise stated, all functions return DVDNAV_STATUS_OK if + * they succeeded, otherwise DVDNAV_STATUS_ERR is returned and the error may + * be obtained by calling dvdnav_err_to_string(). + */ +#define DVDNAV_STATUS_ERR 0 +#define DVDNAV_STATUS_OK 1 + +/********************************************************************* + * initialisation & housekeeping functions * + *********************************************************************/ + +/* + * Logger callback definition + */ +typedef enum +{ + DVDNAV_LOGGER_LEVEL_INFO, + DVDNAV_LOGGER_LEVEL_ERROR, + DVDNAV_LOGGER_LEVEL_WARN, + DVDNAV_LOGGER_LEVEL_DEBUG, +} dvdnav_logger_level_t; + +typedef struct +{ + void ( *pf_log ) ( void *, dvdnav_logger_level_t, const char *, va_list ); +} dvdnav_logger_cb; + +/* + * These functions allow you to open a DVD device and associate it + * with a dvdnav_t. + */ + +/* + * Attempts to open the DVD drive at the specified path or using external + * seek/read functions (dvdnav_open_stream) and pre-cache the CSS-keys. + * libdvdread is used to access the DVD, so any source supported by libdvdread + * can be given with "path" or "stream_cb". Currently, using dvdnav_open, + * libdvdread can access : DVD drives, DVD image files, DVD file-by-file + * copies. Using dvdnav_open_stream, libdvdread can access any kind of DVD + * storage via custom implementation of seek/read functions. + * + * The resulting dvdnav_t handle will be written to *dest. + */ +dvdnav_status_t dvdnav_open(dvdnav_t **dest, const char *path); +dvdnav_status_t +dvdnav_open_stream(dvdnav_t **dest, void *priv, dvdnav_stream_cb *stream_cb); + +dvdnav_status_t dvdnav_open2(dvdnav_t **dest, + void *, const dvdnav_logger_cb *, + const char *path); +dvdnav_status_t dvdnav_open_stream2(dvdnav_t **dest, + void *priv, const dvdnav_logger_cb *, + dvdnav_stream_cb *stream_cb); + +dvdnav_status_t dvdnav_dup(dvdnav_t **dest, dvdnav_t *src); +dvdnav_status_t dvdnav_free_dup(dvdnav_t * _this); + +/* + * Closes a dvdnav_t previously opened with dvdnav_open(), freeing any + * memory associated with it. + */ +dvdnav_status_t dvdnav_close(dvdnav_t *self); + +/* + * Resets the DVD virtual machine and cache buffers. + */ +dvdnav_status_t dvdnav_reset(dvdnav_t *self); + +/* + * Fills a pointer with a value pointing to a string describing + * the path associated with an open dvdnav_t. It assigns *path to NULL + * on error. + */ +dvdnav_status_t dvdnav_path(dvdnav_t *self, const char **path); + +/* + * Returns a human-readable string describing the last error. + */ +const char* dvdnav_err_to_string(dvdnav_t *self); + +const char* dvdnav_version(void); + +/********************************************************************* + * changing and reading DVD player characteristics * + *********************************************************************/ + +/* + * These functions allow you to manipulate the various global characteristics + * of the DVD playback engine. + */ + +/* + * Sets the region mask (bit 0 set implies region 1, bit 1 set implies + * region 2, etc) of the virtual machine. Generally you will only need to set + * this if you are playing RCE discs which query the virtual machine as to its + * region setting. + * + * This has _nothing_ to do with the region setting of the DVD drive. + */ +dvdnav_status_t dvdnav_set_region_mask(dvdnav_t *self, int32_t region_mask); + +/* + * Returns the region mask (bit 0 set implies region 1, bit 1 set implies + * region 2, etc) of the virtual machine. + * + * This has _nothing_ to do with the region setting of the DVD drive. + */ +dvdnav_status_t dvdnav_get_region_mask(dvdnav_t *self, int32_t *region_mask); + +/* + * Specify whether read-ahead caching should be used. You may not want this if your + * decoding engine does its own buffering. + * + * The default read-ahead cache does not use an additional thread for the reading + * (see read_cache.c for a threaded cache, but note that this code is currently + * unmaintained). It prebuffers on VOBU level by reading ahead several buffers + * on every read request. The speed of this prebuffering has been optimized to + * also work on slow DVD drives. + * + * If in addition you want to prevent memcpy's to improve performance, have a look + * at dvdnav_get_next_cache_block(). + */ +dvdnav_status_t dvdnav_set_readahead_flag(dvdnav_t *self, int32_t read_ahead_flag); + +/* + * Query whether read-ahead caching/buffering will be used. + */ +dvdnav_status_t dvdnav_get_readahead_flag(dvdnav_t *self, int32_t *read_ahead_flag); + +/* + * Specify whether the positioning works PGC or PG based. + * Programs (PGs) on DVDs are similar to Chapters and a program chain (PGC) + * usually covers a whole feature. This affects the behaviour of the + * functions dvdnav_get_position() and dvdnav_sector_search(). See there. + * Default is PG based positioning. + */ +dvdnav_status_t dvdnav_set_PGC_positioning_flag(dvdnav_t *self, int32_t pgc_based_flag); + +/* + * Query whether positioning is PG or PGC based. + */ +dvdnav_status_t dvdnav_get_PGC_positioning_flag(dvdnav_t *self, int32_t *pgc_based_flag); + + +/********************************************************************* + * reading data * + *********************************************************************/ + +/* + * These functions are used to poll the playback engine and actually get data + * off the DVD. + */ + +/* + * Attempts to get the next block off the DVD and copies it into the buffer 'buf'. + * If there is any special actions that may need to be performed, the value + * pointed to by 'event' gets set accordingly. + * + * If 'event' is DVDNAV_BLOCK_OK then 'buf' is filled with the next block + * (note that means it has to be at /least/ 2048 bytes big). 'len' is + * then set to 2048. + * + * Otherwise, buf is filled with an appropriate event structure and + * len is set to the length of that structure. + * + * See the dvdnav_events.h header for information on the various events. + */ +dvdnav_status_t dvdnav_get_next_block(dvdnav_t *self, uint8_t *buf, + int32_t *event, int32_t *len); + +/* + * This basically does the same as dvdnav_get_next_block. The only difference is + * that it avoids a memcopy, when the requested block was found in the cache. + * In such a case (cache hit) this function will return a different pointer than + * the one handed in, pointing directly into the relevant block in the cache. + * Those pointers must _never_ be freed but instead returned to the library via + * dvdnav_free_cache_block(). + */ +dvdnav_status_t dvdnav_get_next_cache_block(dvdnav_t *self, uint8_t **buf, + int32_t *event, int32_t *len); + +/* + * All buffers which came from the internal cache (when dvdnav_get_next_cache_block() + * returned a buffer different from the one handed in) have to be freed with this + * function. Although handing in other buffers not from the cache doesn't cause any harm. + */ +dvdnav_status_t dvdnav_free_cache_block(dvdnav_t *self, unsigned char *buf); + +/* + * If we are currently in a still-frame this function skips it. + * + * See also the DVDNAV_STILL_FRAME event. + */ +dvdnav_status_t dvdnav_still_skip(dvdnav_t *self); + +/* + * If we are currently in WAIT state, that is: the application is required to + * wait for its fifos to become empty, calling this signals libdvdnav that this + * is achieved and that it can continue. + * + * See also the DVDNAV_WAIT event. + */ +dvdnav_status_t dvdnav_wait_skip(dvdnav_t *self); + +/* + * Returns the still time from the currently playing cell. + * The still time is given in seconds with 0xff meaning an indefinite still. + * + * This function can be used to detect still frames before they are reached. + * Some players might need this to prepare for a frame to be shown for a + * longer time than usual. + */ +uint32_t dvdnav_get_next_still_flag(dvdnav_t *self); + +/* + * Stops playback. The next event obtained with one of the get_next_block + * functions will be a DVDNAV_STOP event. + * + * It is not required to call this before dvdnav_close(). + */ +dvdnav_status_t dvdnav_stop(dvdnav_t *self); + + +/********************************************************************* + * title/part navigation * + *********************************************************************/ + +/* + * Returns the number of titles on the disk. + */ +dvdnav_status_t dvdnav_get_number_of_titles(dvdnav_t *self, int32_t *titles); + +/* + * Returns the number of parts within the given title. + */ +dvdnav_status_t dvdnav_get_number_of_parts(dvdnav_t *self, int32_t title, int32_t *parts); + +/* + * Returns the number of angles for the given title. + */ +dvdnav_status_t dvdnav_get_number_of_angles(dvdnav_t *self, int32_t title, int32_t *angles); + +/* + * Plays the specified title of the DVD from its beginning (that is: part 1). + */ +dvdnav_status_t dvdnav_title_play(dvdnav_t *self, int32_t title); + +/* + * Plays the specified title, starting from the specified part. + */ +dvdnav_status_t dvdnav_part_play(dvdnav_t *self, int32_t title, int32_t part); + +/* + * Plays the specified title, starting from the specified program + */ +dvdnav_status_t dvdnav_program_play(dvdnav_t *self, int32_t title, int32_t pgcn, int32_t pgn); + +/* + * Stores in *times an array (that the application *must* free) of + * dvdtimes corresponding to the chapter times for the chosen title. + * *duration will have the duration of the title + * The number of entries in *times is the result of the function. + * On error *times is NULL and the output is 0 + */ +uint32_t dvdnav_describe_title_chapters(dvdnav_t *self, int32_t title, uint64_t **times, uint64_t *duration); + +/* + * Play the specified amount of parts of the specified title of + * the DVD then STOP. + * + * Currently unimplemented! + */ +dvdnav_status_t dvdnav_part_play_auto_stop(dvdnav_t *self, int32_t title, + int32_t part, int32_t parts_to_play); + +/* + * Play the specified title starting from the specified time. + * + * Currently unimplemented! + */ +dvdnav_status_t dvdnav_time_play(dvdnav_t *self, int32_t title, + uint64_t time); + +/* + * Stop playing the current position and jump to the specified menu. + * + * See also DVDMenuID_t from libdvdread + */ +dvdnav_status_t dvdnav_menu_call(dvdnav_t *self, DVDMenuID_t menu); + +/* + * Return the title number and part currently being played. + * A title of 0 indicates we are in a menu. In this case, part + * is set to the current menu's ID. + */ +dvdnav_status_t dvdnav_current_title_info(dvdnav_t *self, int32_t *title, + int32_t *part); + +/* + * Return the title number, pgcn and pgn currently being played. + * A title of 0 indicates, we are in a menu. + */ +dvdnav_status_t dvdnav_current_title_program(dvdnav_t *self, int32_t *title, + int32_t *pgcn, int32_t *pgn); + +/* + * Return the current position (in blocks) within the current + * title and the length (in blocks) of said title. + * + * Current implementation is wrong and likely to behave unpredictably! + * Use is discouraged! + */ +dvdnav_status_t dvdnav_get_position_in_title(dvdnav_t *self, + uint32_t *pos, + uint32_t *len); + +/* + * This function is only available for compatibility reasons. + * + * Stop playing the current position and start playback of the current title + * from the specified part. + */ +dvdnav_status_t dvdnav_part_search(dvdnav_t *self, int32_t part); + + +/********************************************************************* + * program chain/program navigation * + *********************************************************************/ + +/* + * Stop playing the current position and start playback from the last + * VOBU boundary before the given sector. The sector number is not + * meant to be an absolute physical DVD sector, but a relative sector + * in the current program. This function cannot leave the current + * program and will fail if asked to do so. + * + * If program chain based positioning is enabled + * (see dvdnav_set_PGC_positioning_flag()), this will seek to the relative + * sector inside the current program chain. + * + * 'origin' can be one of SEEK_SET, SEEK_CUR, SEEK_END as defined in + * fcntl.h. + */ +dvdnav_status_t dvdnav_sector_search(dvdnav_t *self, + int64_t offset, int32_t origin); + +/* + returns the current stream time in PTS ticks as reported by the IFO structures + divide it by 90000 to get the current play time in seconds + */ +int64_t dvdnav_get_current_time(dvdnav_t *self); + +/* + * Stop playing the current position and start playback of the title + * from the specified timecode. + * + * Currently implemented using interpolation. That interpolation is slightly + * inaccurate. + */ +dvdnav_status_t dvdnav_time_search(dvdnav_t *self, + uint64_t time); + +/* + * Stop playing current position and play the "GoUp"-program chain. + * (which generally leads to the title menu or a higher-level menu). + */ +dvdnav_status_t dvdnav_go_up(dvdnav_t *self); + +/* + * Stop playing the current position and start playback at the + * previous program (if it exists). + */ +dvdnav_status_t dvdnav_prev_pg_search(dvdnav_t *self); + +/* + * Stop playing the current position and start playback at the + * first program. + */ +dvdnav_status_t dvdnav_top_pg_search(dvdnav_t *self); + +/* + * Stop playing the current position and start playback at the + * next program (if it exists). + */ +dvdnav_status_t dvdnav_next_pg_search(dvdnav_t *self); + +/* + * Return the current position (in blocks) within the current + * program and the length (in blocks) of current program. + * + * If program chain based positioning is enabled + * (see dvdnav_set_PGC_positioning_flag()), this will return the + * relative position in and the length of the current program chain. + */ +dvdnav_status_t dvdnav_get_position(dvdnav_t *self, uint32_t *pos, + uint32_t *len); + + +/********************************************************************* + * menu highlights * + *********************************************************************/ + +/* + * Most functions related to highlights take a NAV PCI packet as a parameter. + * While you can get such a packet from libdvdnav, this will result in + * errors for players with internal FIFOs because due to the FIFO length, + * libdvdnav will be ahead in the stream compared to what the user is + * seeing on screen. Therefore, player applications who have a NAV + * packet available, which is better in sync with the actual playback, + * should always pass this one to these functions. + */ + +/* + * Get the currently highlighted button + * number (1..36) or 0 if no button is highlighted. + */ +dvdnav_status_t dvdnav_get_current_highlight(dvdnav_t *self, int32_t *button); + +/* + * Returns the Presentation Control Information (PCI) structure associated + * with the current position. + * + * Read the general notes above. + * See also libdvdreads nav_types.h for definition of pci_t. + */ +pci_t* dvdnav_get_current_nav_pci(dvdnav_t *self); + +/* + * Returns the DSI (data search information) structure associated + * with the current position. + * + * Read the general notes above. + * See also libdvdreads nav_types.h for definition of dsi_t. + */ +dsi_t* dvdnav_get_current_nav_dsi(dvdnav_t *self); + +/* + * Get the area associated with a certain button. + */ +dvdnav_status_t dvdnav_get_highlight_area(pci_t *nav_pci , int32_t button, int32_t mode, + dvdnav_highlight_area_t *highlight); + +/* + * Move button highlight around as suggested by function name (e.g. with arrow keys). + */ +dvdnav_status_t dvdnav_upper_button_select(dvdnav_t *self, pci_t *pci); +dvdnav_status_t dvdnav_lower_button_select(dvdnav_t *self, pci_t *pci); +dvdnav_status_t dvdnav_right_button_select(dvdnav_t *self, pci_t *pci); +dvdnav_status_t dvdnav_left_button_select(dvdnav_t *self, pci_t *pci); + +/* + * Activate ("press") the currently highlighted button. + */ +dvdnav_status_t dvdnav_button_activate(dvdnav_t *self, pci_t *pci); + +/* + * Highlight a specific button. + */ +dvdnav_status_t dvdnav_button_select(dvdnav_t *self, pci_t *pci, int32_t button); + +/* + * Activate ("press") specified button. + */ +dvdnav_status_t dvdnav_button_select_and_activate(dvdnav_t *self, pci_t *pci, int32_t button); + +/* + * Activate ("press") a button and execute specified command. + */ +dvdnav_status_t dvdnav_button_activate_cmd(dvdnav_t *self, int32_t button, vm_cmd_t *cmd); + +/* + * Select button at specified video frame coordinates. + */ +dvdnav_status_t dvdnav_mouse_select(dvdnav_t *self, pci_t *pci, int32_t x, int32_t y); + +/* + * Activate ("press") button at specified video frame coordinates. + */ +dvdnav_status_t dvdnav_mouse_activate(dvdnav_t *self, pci_t *pci, int32_t x, int32_t y); + + +/********************************************************************* + * languages * + *********************************************************************/ + +/* + * The language codes expected by these functions are two character + * codes as defined in ISO639. + */ + +/* + * Set which menu language we should use per default. + */ +dvdnav_status_t dvdnav_menu_language_select(dvdnav_t *self, + char *code); + +/* + * Set which audio language we should use per default. + */ +dvdnav_status_t dvdnav_audio_language_select(dvdnav_t *self, + char *code); + +/* + * Set which spu language we should use per default. + */ +dvdnav_status_t dvdnav_spu_language_select(dvdnav_t *self, + char *code); + + +/********************************************************************* + * obtaining stream attributes * + *********************************************************************/ + +/* + * Return a string describing the title of the DVD. + * This is an ID string encoded on the disc by the author. In many cases + * this is a descriptive string such as `THE_MATRIX' but sometimes is singularly + * uninformative such as `PDVD-011421'. Some DVD authors even forget to set this, + * so you may also read the default of the authoring software they used, like + * `DVDVolume'. + */ +dvdnav_status_t dvdnav_get_title_string(dvdnav_t *self, const char **title_str); + +/* + * Returns a string containing the serial number of the DVD. + * This has a max of 15 characters and should be more unique than the + * title string. + */ +dvdnav_status_t dvdnav_get_serial_string(dvdnav_t *self, const char **serial_str); + +/* + * Get video aspect code. + * The aspect code does only change on VTS boundaries. + * See the DVDNAV_VTS_CHANGE event. + * + * 0 -- 4:3, 2 -- 16:9 + */ +uint8_t dvdnav_get_video_aspect(dvdnav_t *self); + +/* + * Get video resolution. + */ +dvdnav_status_t dvdnav_get_video_resolution(dvdnav_t *self, uint32_t *width, uint32_t *height); + +/* + * Get video scaling permissions. + * The scaling permission does only change on VTS boundaries. + * See the DVDNAV_VTS_CHANGE event. + * + * bit0 set = deny letterboxing, bit1 set = deny pan&scan + */ +uint8_t dvdnav_get_video_scale_permission(dvdnav_t *self); + +/* + * Converts a *logical* audio stream id into language code + * (returns 0xffff if no such stream). + */ +uint16_t dvdnav_audio_stream_to_lang(dvdnav_t *self, uint8_t stream); + +/* + * Returns the format of *logical* audio stream 'stream' + * (returns 0xffff if no such stream). + */ +uint16_t dvdnav_audio_stream_format(dvdnav_t *self, uint8_t stream); + +/* + * Returns number of channels in *logical* audio stream 'stream' + * (returns 0xffff if no such stream). + */ +uint16_t dvdnav_audio_stream_channels(dvdnav_t *self, uint8_t stream); + +/* + * Converts a *logical* subpicture stream id into country code + * (returns 0xffff if no such stream). + */ +uint16_t dvdnav_spu_stream_to_lang(dvdnav_t *self, uint8_t stream); + +/* + * Converts a *physical* (MPEG) audio stream id into a logical stream number. + */ +int8_t dvdnav_get_audio_logical_stream(dvdnav_t *self, uint8_t audio_num); + +#define HAVE_GET_AUDIO_ATTR +/* + * Get audio attr + */ +dvdnav_status_t dvdnav_get_audio_attr(dvdnav_t *self, uint8_t audio_mum, audio_attr_t *audio_attr); + +/* + * Converts a *physical* (MPEG) subpicture stream id into a logical stream number. + */ +int8_t dvdnav_get_spu_logical_stream(dvdnav_t *self, uint8_t subp_num); + +#define HAVE_GET_SPU_ATTR +/* + * Get spu attr + */ +dvdnav_status_t dvdnav_get_spu_attr(dvdnav_t *self, uint8_t audio_mum, subp_attr_t *subp_attr); + +/* + * Get active audio stream. + */ +int8_t dvdnav_get_active_audio_stream(dvdnav_t *self); + +/* + * Get active spu stream. + */ +int8_t dvdnav_get_active_spu_stream(dvdnav_t *self); + +/* + * Get the set of user operations that are currently prohibited. + * There are potentially new restrictions right after + * DVDNAV_CHANNEL_HOP and DVDNAV_NAV_PACKET. + */ +user_ops_t dvdnav_get_restrictions(dvdnav_t *self); + + +/********************************************************************* + * multiple angles * + *********************************************************************/ + +/* + * The libdvdnav library abstracts away the difference between seamless and + * non-seamless angles. From the point of view of the programmer you just set the + * angle number and all is well in the world. You will always see only the + * selected angle coming from the get_next_block functions. + * + * Note: + * It is quite possible that some tremendously strange DVD feature might change the + * angle number from under you. Generally you should always view the results from + * dvdnav_get_angle_info() as definitive only up to the next time you call + * dvdnav_get_next_block(). + */ + +/* + * Sets the current angle. If you try to follow a non existent angle + * the call fails. + */ +dvdnav_status_t dvdnav_angle_change(dvdnav_t *self, int32_t angle); + +/* + * Returns the current angle and number of angles present. + */ +dvdnav_status_t dvdnav_get_angle_info(dvdnav_t *self, int32_t *current_angle, + int32_t *number_of_angles); + +/********************************************************************* + * domain queries * + *********************************************************************/ + +/* + * Are we in the First Play domain? + */ +int8_t dvdnav_is_domain_fp(dvdnav_t *self); + +/* + * Are we in the Video management Menu domain? + */ +int8_t dvdnav_is_domain_vmgm(dvdnav_t *self); + +/* + * Are we in the Video Title Menu domain? + */ +int8_t dvdnav_is_domain_vtsm(dvdnav_t *self); + +/* + * Are we in the Video Title Set domain? + */ +int8_t dvdnav_is_domain_vts(dvdnav_t *self); + + +#ifdef __cplusplus +} +#endif + +#endif /* LIBDVDNAV_DVDNAV_H */ diff -durN a/src/dvdnav.c b/src/dvdnav.c --- a/src/dvdnav.c 2020-05-03 15:01:16.798469411 +0200 +++ b/src/dvdnav.c 2020-05-03 15:01:26.138448826 +0200 @@ -1310,3 +1310,166 @@ const char* dvdnav_version(void) { return VERSION; } + +/* return the alpha and color for the current active button + * color, alpha [0][] = selection + * color, alpha = color + * + * argsize = [2][4] + */ +int dvdnav_get_button_info(dvdnav_t *this, int alpha[2][4], int color[2][4]) +{ + int current_button, current_button_color, i; + pci_t* pci; + + if (!this) return -1; + + pci = dvdnav_get_current_nav_pci(this); + if (!pci) return -1; + + dvdnav_get_current_highlight(this, ¤t_button); + current_button_color = pci->hli.btnit[current_button - 1].btn_coln; + + for (i = 0; i < 2; i++) + { + alpha[i][0] = pci->hli.btn_colit.btn_coli[current_button_color - 1][i] >> 0 & 0xf; + alpha[i][1] = pci->hli.btn_colit.btn_coli[current_button_color - 1][i] >> 4 & 0xf; + alpha[i][2] = pci->hli.btn_colit.btn_coli[current_button_color - 1][i] >> 8 & 0xf; + alpha[i][3] = pci->hli.btn_colit.btn_coli[current_button_color - 1][i] >> 12 & 0xf; + + color[i][0] = pci->hli.btn_colit.btn_coli[current_button_color - 1][i] >> 16 & 0xf; + color[i][1] = pci->hli.btn_colit.btn_coli[current_button_color - 1][i] >> 20 & 0xf; + color[i][2] = pci->hli.btn_colit.btn_coli[current_button_color - 1][i] >> 24 & 0xf; + color[i][3] = pci->hli.btn_colit.btn_coli[current_button_color - 1][i] >> 28 & 0xf; + } + + return 0; +} + +/* libdvdread ifoOpen could be used */ +uint32_t dvdnav_kodi_get_vmg_category(dvdnav_t *this) +{ + uint32_t ret = 0; + + pthread_mutex_lock(&this->vm_lock); + if(this->vm) + ret = vm_kodi_get_vmg_category(this->vm); + pthread_mutex_unlock(&this->vm_lock); + return ret; +} + +dvdnav_status_t dvdnav_kodi_set_active_audio_stream(dvdnav_t *this, int streamId) +{ + dvdnav_status_t ret = DVDNAV_STATUS_ERR; + + pthread_mutex_lock(&this->vm_lock); + if(this->vm) + { + int r = vm_kodi_set_active_audio_stream(this->vm, streamId); + + if(r) + ret = DVDNAV_STATUS_OK; + } + pthread_mutex_unlock(&this->vm_lock); + return ret; +} + +dvdnav_status_t dvdnav_kodi_set_active_subtitle_stream(dvdnav_t *this, int streamId) +{ + dvdnav_status_t ret = DVDNAV_STATUS_ERR; + + pthread_mutex_lock(&this->vm_lock); + if(this->vm) + { + int r = vm_kodi_set_active_subtitle_stream(this->vm, streamId); + + if(r) + ret = DVDNAV_STATUS_OK; + } + pthread_mutex_unlock(&this->vm_lock); + return ret; +} + +int dvdnav_kodi_get_subtitle_count(dvdnav_t *this) +{ + int ret = 0; + + pthread_mutex_lock(&this->vm_lock); + if(this->vm) + ret = vm_kodi_get_subtitle_count(this->vm); + pthread_mutex_unlock(&this->vm_lock); + return ret; +} + +int dvdnav_kodi_get_audio_stream_count(dvdnav_t *this) +{ + int ret = 0; + + pthread_mutex_lock(&this->vm_lock); + if(this->vm) + ret = vm_kodi_get_audio_stream_count(this->vm); + pthread_mutex_unlock(&this->vm_lock); + return ret; +} + +void dvdnav_kodi_enable_subtitle_stream(dvdnav_t *this, bool enable) +{ + pthread_mutex_lock(&this->vm_lock); + if(this->vm) + vm_kodi_enable_subtitle_stream(this->vm, enable); + pthread_mutex_unlock(&this->vm_lock); +} + +bool dvdnav_kodi_get_subtitle_stream_state(dvdnav_t *this) +{ + bool ret = false; + pthread_mutex_lock(&this->vm_lock); + if(this->vm) + ret = vm_kodi_get_subtitle_stream_state(this->vm); + pthread_mutex_unlock(&this->vm_lock); + return ret; +} + +int dvdnav_kodi_convert_audio_stream_id_to_xbmc(dvdnav_t *this, int id) +{ + int ret = -1; + + pthread_mutex_lock(&this->vm_lock); + if(this->vm) + ret = vm_kodi_convert_audio_stream_id_to_xbmc(this->vm, id); + pthread_mutex_unlock(&this->vm_lock); + return ret; +} + +int dvdnav_kodi_convert_audio_stream_id_from_xbmc(dvdnav_t *this, int id) +{ + int ret = -1; + + pthread_mutex_lock(&this->vm_lock); + if(this->vm) + ret = vm_kodi_convert_audio_stream_id_from_xbmc(this->vm, id); + pthread_mutex_unlock(&this->vm_lock); + return ret; +} + +int dvdnav_kodi_convert_subtitle_stream_id_to_xbmc(dvdnav_t *this, int id) +{ + int ret = -1; + + pthread_mutex_lock(&this->vm_lock); + if(this->vm) + ret = vm_kodi_convert_subtitle_stream_id_to_xbmc(this->vm, id); + pthread_mutex_unlock(&this->vm_lock); + return ret; +} + +int dvdnav_kodi_convert_subtitle_stream_id_from_xbmc(dvdnav_t *this, int id) +{ + int ret = -1; + + pthread_mutex_lock(&this->vm_lock); + if(this->vm) + ret = vm_kodi_convert_subtitle_stream_id_from_xbmc(this->vm, id); + pthread_mutex_unlock(&this->vm_lock); + return ret; +} diff -durN a/src/dvdnav.c.orig b/src/dvdnav.c.orig --- a/src/dvdnav.c.orig 1970-01-01 01:00:00.000000000 +0100 +++ b/src/dvdnav.c.orig 2020-05-03 15:01:15.766471686 +0200 @@ -0,0 +1,1312 @@ +/* + * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net> + * + * This file is part of libdvdnav, a DVD navigation library. + * + * libdvdnav is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * libdvdnav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with libdvdnav; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* +#define LOG_DEBUG +*/ + +#include <inttypes.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <limits.h> +#include <string.h> +#include <sys/time.h> +#include "dvdnav/dvdnav.h" +#include <dvdread/dvd_reader.h> +#include <dvdread/nav_types.h> +#include <dvdread/ifo_types.h> /* For vm_cmd_t */ +#include "vm/decoder.h" +#include "vm/vm.h" +#include "vm/getset.h" +#include "dvdnav_internal.h" +#include "logger.h" +#include "read_cache.h" +#include <dvdread/nav_read.h> + +static dvdnav_status_t dvdnav_clear(dvdnav_t * this) { + /* clear everything except file, vm, mutex, readahead */ + + pthread_mutex_lock(&this->vm_lock); + if (this->file) DVDCloseFile(this->file); + this->file = NULL; + + memset(&this->position_current,0,sizeof(this->position_current)); + memset(&this->pci,0,sizeof(this->pci)); + memset(&this->dsi,0,sizeof(this->dsi)); + this->last_cmd_nav_lbn = SRI_END_OF_CELL; + + /* Set initial values of flags */ + this->skip_still = 0; + this->sync_wait = 0; + this->sync_wait_skip = 0; + this->spu_clut_changed = 0; + this->started = 0; + this->cur_cell_time = 0; + + dvdnav_read_cache_clear(this->cache); + pthread_mutex_unlock(&this->vm_lock); + + return DVDNAV_STATUS_OK; +} + +dvdnav_status_t dvdnav_dup(dvdnav_t **dest, dvdnav_t *src) { + dvdnav_t *this; + + (*dest) = NULL; + this = (dvdnav_t*)malloc(sizeof(dvdnav_t)); + if (!this) + return DVDNAV_STATUS_ERR; + + memcpy(this, src, sizeof(dvdnav_t)); + this->file = NULL; + this->vm = NULL; + this->path = NULL; + this->cache = NULL; + + pthread_mutex_init(&this->vm_lock, NULL); + + this->vm = vm_new_copy(src->vm); + if (!this->vm) + goto fail; + + this->path = strdup(src->path); + if (!this->path) + goto fail; + + /* Start the read-ahead cache. */ + this->cache = dvdnav_read_cache_new(this); + if (!this->cache) + goto fail; + + (*dest) = this; + return DVDNAV_STATUS_OK; + +fail: + printerr("Error initialising the DVD VM."); + pthread_mutex_destroy(&this->vm_lock); + vm_free_vm(this->vm); + free(this->path); + free(this); + return DVDNAV_STATUS_ERR; +} + +dvdnav_status_t dvdnav_free_dup(dvdnav_t *this) { + +#ifdef LOG_DEBUG + Log3(this, "free_dup:called"); +#endif + + if (this->file) { + pthread_mutex_lock(&this->vm_lock); + DVDCloseFile(this->file); +#ifdef LOG_DEBUG + Log3(this, "close:file closing"); +#endif + this->file = NULL; + pthread_mutex_unlock(&this->vm_lock); + } + + /* Free the VM */ + if(this->vm) + vm_free_copy(this->vm); + + pthread_mutex_destroy(&this->vm_lock); + + free(this->path); + + /* We leave the final freeing of the entire structure to the cache, + * because we don't know, if there are still buffers out in the wild, + * that must return first. */ + if(this->cache) + dvdnav_read_cache_free(this->cache); + else + free(this); + + return DVDNAV_STATUS_OK; +} + +static dvdnav_status_t dvdnav_open_common(dvdnav_t** dest, + void *priv, const dvdnav_logger_cb *logcb, + const char *path, + dvdnav_stream_cb *stream_cb) { + dvdnav_t *this; + struct timeval time; + + /* Create a new structure */ + (*dest) = NULL; + this = (dvdnav_t*)calloc(1, sizeof(dvdnav_t)); + if(!this) + return DVDNAV_STATUS_ERR; + + this->priv = priv; + if(logcb) + this->logcb = *logcb; + + pthread_mutex_init(&this->vm_lock, NULL); + /* Initialise the error string */ + printerr(""); + + /* Initialise the VM */ + this->vm = vm_new_vm(priv, logcb); + if(!this->vm) { + goto fail; + } + if(!vm_reset(this->vm, path, priv, stream_cb)) { + goto fail; + } + + /* Set the path. */ + if(path != NULL) + { + this->path = strdup(path); + if(!this->path) + goto fail; + } + + /* Pre-open and close a file so that the CSS-keys are cached. */ + this->file = DVDOpenFile(vm_get_dvd_reader(this->vm), 0, DVD_READ_MENU_VOBS); + + /* Start the read-ahead cache. */ + this->cache = dvdnav_read_cache_new(this); + if(!this->cache) + goto fail; + + /* Seed the random numbers. So that the DVD VM Command rand() + * gives a different start value each time a DVD is played. */ + gettimeofday(&time, NULL); + srand(time.tv_usec); + + dvdnav_clear(this); + + (*dest) = this; + return DVDNAV_STATUS_OK; + +fail: + pthread_mutex_destroy(&this->vm_lock); + vm_free_vm(this->vm); + free(this->path); + free(this); + return DVDNAV_STATUS_ERR; +} + +dvdnav_status_t dvdnav_open(dvdnav_t** dest, const char *path) { + return dvdnav_open_common(dest, NULL, NULL, path, NULL); +} + +dvdnav_status_t dvdnav_open2(dvdnav_t** dest, + void *priv,const dvdnav_logger_cb *logcb, + const char *path) { + return dvdnav_open_common(dest, priv, logcb, path, NULL); +} + +dvdnav_status_t dvdnav_open_stream(dvdnav_t** dest, + void *priv, dvdnav_stream_cb *stream_cb) { + return dvdnav_open_common(dest, priv, NULL, NULL, stream_cb); +} + +dvdnav_status_t dvdnav_open_stream2(dvdnav_t** dest, + void *priv,const dvdnav_logger_cb *logcb, + dvdnav_stream_cb *stream_cb) { + return dvdnav_open_common(dest, priv, logcb, NULL, stream_cb); +} + +dvdnav_status_t dvdnav_close(dvdnav_t *this) { + +#ifdef LOG_DEBUG + Log3(this, "close:called"); +#endif + + if (this->file) { + pthread_mutex_lock(&this->vm_lock); + DVDCloseFile(this->file); +#ifdef LOG_DEBUG + Log3(this, "close:file closing"); +#endif + this->file = NULL; + pthread_mutex_unlock(&this->vm_lock); + } + + /* Free the VM */ + if(this->vm) + vm_free_vm(this->vm); + + pthread_mutex_destroy(&this->vm_lock); + + free(this->path); + + /* We leave the final freeing of the entire structure to the cache, + * because we don't know, if there are still buffers out in the wild, + * that must return first. */ + if(this->cache) + dvdnav_read_cache_free(this->cache); + else + free(this); + + return DVDNAV_STATUS_OK; +} + +dvdnav_status_t dvdnav_reset(dvdnav_t *this) { + dvdnav_status_t result; + +#ifdef LOG_DEBUG + Log3(this, "reset:called"); +#endif + + pthread_mutex_lock(&this->vm_lock); + +#ifdef LOG_DEBUG + Log3(this, "reseting vm"); +#endif + if(!vm_reset(this->vm, NULL, NULL, NULL)) { + printerr("Error restarting the VM."); + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_ERR; + } +#ifdef LOG_DEBUG + Log3(this, "clearing dvdnav"); +#endif + pthread_mutex_unlock(&this->vm_lock); + result = dvdnav_clear(this); + + return result; +} + +dvdnav_status_t dvdnav_path(dvdnav_t *this, const char** path) { + (*path) = this->path; + + return DVDNAV_STATUS_OK; +} + +const char* dvdnav_err_to_string(dvdnav_t *this) { + + if(!this) + return "Hey! You gave me a NULL pointer you naughty person!"; + + return this->err_str; +} + +/* converts a dvd_time_t to PTS ticks */ +int64_t dvdnav_convert_time(dvd_time_t *time) { + int64_t result; + int64_t frames; + + result = ((int64_t)(time->hour >> 4 )) * 10 * 60 * 60 * 90000; + result += ((int64_t)(time->hour & 0x0f)) * 60 * 60 * 90000; + result += ((int64_t)(time->minute >> 4 )) * 10 * 60 * 90000; + result += ((int64_t)(time->minute & 0x0f)) * 60 * 90000; + result += ((int64_t)(time->second >> 4 )) * 10 * 90000; + result += ((int64_t)(time->second & 0x0f)) * 90000; + + frames = ((time->frame_u & 0x30) >> 4) * 10; + frames += ((time->frame_u & 0x0f) ) ; + + if (time->frame_u & 0x80) + result += frames * 3000; + else + result += frames * 3600; + + return result; +} + +/* + * Returns 1 if block contains NAV packet, 0 otherwise. + * Processes said NAV packet if present. + * + * Most of the code in here is copied from xine's MPEG demuxer + * so any bugs which are found in that should be corrected here also. + */ +static int32_t dvdnav_decode_packet(dvdnav_t *this, uint8_t *p, + dsi_t *nav_dsi, pci_t *nav_pci) { + int32_t bMpeg1 = 0; + uint32_t nHeaderLen; + uint32_t nPacketLen; + uint32_t nStreamID; + + if (p[3] == 0xBA) { /* program stream pack header */ + int32_t nStuffingBytes; + + bMpeg1 = (p[4] & 0x40) == 0; + + if (bMpeg1) { + p += 12; + } else { /* mpeg2 */ + nStuffingBytes = p[0xD] & 0x07; + p += 14 + nStuffingBytes; + } + } + + if (p[3] == 0xbb) { /* program stream system header */ + nHeaderLen = (p[4] << 8) | p[5]; + p += 6 + nHeaderLen; + } + + /* we should now have a PES packet here */ + if (p[0] || p[1] || (p[2] != 1)) { + Log1(this, "demux error! %02x %02x %02x (should be 0x000001)",p[0],p[1],p[2]); + return 0; + } + + nPacketLen = p[4] << 8 | p[5]; + nStreamID = p[3]; + + nHeaderLen = 6; + p += nHeaderLen; + + if (nStreamID == 0xbf) { /* Private stream 2 */ +#if 0 + char buffer[80 * 3 + 1]; + int32_t i; + for(i=0;i<80;i++) + sprintf(&buffer[i*3], "%02x ",p[i-6]); + Log3(this, "nav packet=%u %s",p-p_start-6, buffer); +#endif + + if(p[0] == 0x00) { + navRead_PCI(nav_pci, p+1); + } + + p += nPacketLen; + + /* We should now have a DSI packet. */ + if(p[6] == 0x01) { + nPacketLen = p[4] << 8 | p[5]; + p += 6; + navRead_DSI(nav_dsi, p+1); + } + return 1; + } + return 0; +} + +/* DSI is used for most angle stuff. + * PCI is used for only non-seemless angle stuff + */ +static int32_t dvdnav_get_vobu(dvdnav_t *this, dsi_t *nav_dsi, pci_t *nav_pci, dvdnav_vobu_t *vobu) { + uint32_t next; + int32_t angle, num_angle; + + vobu->vobu_start = nav_dsi->dsi_gi.nv_pck_lbn; /* Absolute offset from start of disk */ + vobu->vobu_length = nav_dsi->dsi_gi.vobu_ea; /* Relative offset from vobu_start */ + + /* + * If we're not at the end of this cell, we can determine the next + * VOBU to display using the VOBU_SRI information section of the + * DSI. Using this value correctly follows the current angle, + * avoiding the doubled scenes in The Matrix, and makes our life + * really happy. + * + * vobu_next is an offset value, 0x3fffffff = SRI_END_OF_CELL + * DVDs are about 6 Gigs, which is only up to 0x300000 blocks + * Should really assert if bit 31 != 1 + */ + +#if 0 + /* Old code -- may still be useful one day */ + if(nav_dsi->vobu_sri.next_vobu != SRI_END_OF_CELL ) { + vobu->vobu_next = ( nav_dsi->vobu_sri.next_vobu & 0x3fffffff ); + } else { + vobu->vobu_next = vobu->vobu_length; + } +#else + /* Relative offset from vobu_start */ + vobu->vobu_next = ( nav_dsi->vobu_sri.next_vobu & 0x3fffffff ); +#endif + + vm_get_angle_info(this->vm, &angle, &num_angle); + + /* FIMXE: The angle reset doesn't work for some reason for the moment */ +#if 0 + if((num_angle < angle) && (angle != 1)) { + Log3(this, "angle ends!"); + + /* This is to switch back to angle one when we + * finish with angles. */ + dvdnav_angle_change(this, 1); + } +#endif + /* only use ILVU information if we are at the last vobunit in ILVU */ + /* otherwise we will miss nav packets from vobunits inbetween */ + if(num_angle != 0 && (nav_dsi->sml_pbi.category & DSI_ILVU_MASK) == (DSI_ILVU_BLOCK | DSI_ILVU_LAST)) { + + if((next = nav_pci->nsml_agli.nsml_agl_dsta[angle-1]) != 0) { + if((next & 0x3fffffff) != 0) { + if(next & 0x80000000) + vobu->vobu_next = - (int32_t)(next & 0x3fffffff); + else + vobu->vobu_next = + (int32_t)(next & 0x3fffffff); + } + } else if((next = nav_dsi->sml_agli.data[angle-1].address) != 0) { + vobu->vobu_length = nav_dsi->sml_pbi.ilvu_ea; + + if((next & 0x80000000) && (next != 0x7fffffff)) + vobu->vobu_next = - (int32_t)(next & 0x3fffffff); + else + vobu->vobu_next = + (int32_t)(next & 0x3fffffff); + } + } + + return 1; +} + +/* + * These are the main get_next_block function which actually get the media stream video and audio etc. + * + * There are two versions: The second one is using the zero-copy read ahead cache and therefore + * hands out pointers targetting directly into the cache. + * The first one uses a memcopy to fill this cache block into the application provided memory. + * The benefit of this first one is that no special memory management is needed. The application is + * the only one responsible of allocating and freeing the memory associated with the pointer. + * The drawback is the additional memcopy. + */ + +dvdnav_status_t dvdnav_get_next_block(dvdnav_t *this, uint8_t *buf, + int32_t *event, int32_t *len) { + unsigned char *block; + dvdnav_status_t status; + + block = buf; + status = dvdnav_get_next_cache_block(this, &block, event, len); + if (status == DVDNAV_STATUS_OK && block != buf) { + /* we received a block from the cache, copy it, so we can give it back */ + memcpy(buf, block, DVD_VIDEO_LB_LEN); + dvdnav_free_cache_block(this, block); + } + return status; +} + +int64_t dvdnav_get_current_time(dvdnav_t *this) { + int i; + int64_t tm=0; + dvd_state_t *state = &this->vm->state; + + if(! state->pgc) + return 0; + + for(i=0; i<state->cellN-1; i++) { + if(! + (state->pgc->cell_playback[i].block_type == BLOCK_TYPE_ANGLE_BLOCK && + state->pgc->cell_playback[i].block_mode != BLOCK_MODE_FIRST_CELL) + ) + tm += dvdnav_convert_time(&state->pgc->cell_playback[i].playback_time); + } + tm += this->cur_cell_time; + + return tm; +} + +dvdnav_status_t dvdnav_get_next_cache_block(dvdnav_t *this, uint8_t **buf, + int32_t *event, int32_t *len) { + dvd_state_t *state; + int32_t result; + + if(!this) + return DVDNAV_STATUS_ERR; + + pthread_mutex_lock(&this->vm_lock); + + if(!this->started) { + /* Start the VM */ + if (!vm_start(this->vm)) { + printerr("Encrypted or faulty DVD"); + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_ERR; + } + this->started = 1; + } + + if (!this->vm->state.pgc) { + printerr("No current PGC."); + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_ERR; + } + + state = &(this->vm->state); + (*event) = DVDNAV_NOP; + (*len) = 0; + + /* Check the STOP flag */ + if(this->vm->stopped) { + vm_stop(this->vm); + (*event) = DVDNAV_STOP; + this->started = 0; + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_OK; + } + + vm_position_get(this->vm, &this->position_next); + +#ifdef LOG_DEBUG + Log3(vm, "POS-NEXT "); + vm_position_print(this->vm, &this->position_next); + Log3(vm, "POS-CUR "); + vm_position_print(this->vm, &this->position_current); +#endif + + /* did we hop? */ + if(this->position_current.hop_channel != this->position_next.hop_channel) { + (*event) = DVDNAV_HOP_CHANNEL; +#ifdef LOG_DEBUG + Log3(vm, "HOP_CHANNEL"); +#endif + if (this->position_next.hop_channel - this->position_current.hop_channel >= HOP_SEEK) { + int32_t num_angles = 0, current; + + /* we seeked -> check for multiple angles */ + vm_get_angle_info(this->vm, ¤t, &num_angles); + if (num_angles > 1) { + int32_t result, block; + /* we have to skip the first VOBU when seeking in a multiangle feature, + * because it might belong to the wrong angle */ + block = this->position_next.cell_start + this->position_next.block; + result = dvdnav_read_cache_block(this->cache, block, 1, buf); + if(result <= 0) { + printerr("Error reading NAV packet."); + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_ERR; + } + /* Decode nav into pci and dsi. Then get next VOBU info. */ + if(!dvdnav_decode_packet(this, *buf, &this->dsi, &this->pci)) { + printerr("Expected NAV packet but none found."); + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_ERR; + } + dvdnav_get_vobu(this, &this->dsi, &this->pci, &this->vobu); + /* skip to next, if there is a next */ + if (this->vobu.vobu_next != SRI_END_OF_CELL) { + this->vobu.vobu_start += this->vobu.vobu_next; + this->vobu.vobu_next = 0; + } + /* update VM state */ + this->vm->state.blockN = this->vobu.vobu_start - this->position_next.cell_start; + } + } + this->position_current.hop_channel = this->position_next.hop_channel; + /* update VOBU info */ + this->vobu.vobu_start = this->position_next.cell_start + this->position_next.block; + this->vobu.vobu_next = 0; + /* Make blockN == vobu_length to do expected_nav */ + this->vobu.vobu_length = 0; + this->vobu.blockN = 0; + this->sync_wait = 0; + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_OK; + } + + /* Check the HIGHLIGHT flag */ + if(this->position_current.button != this->position_next.button) { + dvdnav_highlight_event_t *hevent = (dvdnav_highlight_event_t *)*buf; + + (*event) = DVDNAV_HIGHLIGHT; +#ifdef LOG_DEBUG + Log3(this, "HIGHLIGHT"); +#endif + (*len) = sizeof(dvdnav_highlight_event_t); + hevent->display = 1; + hevent->buttonN = this->position_next.button; + this->position_current.button = this->position_next.button; + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_OK; + } + + /* Check the WAIT flag */ + if(this->sync_wait) { + (*event) = DVDNAV_WAIT; +#ifdef LOG_DEBUG + Log3(this, "WAIT"); +#endif + (*len) = 0; + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_OK; + } + + /* Check to see if we need to change the currently opened VOB or open + * a new one because we don't currently have an opened VOB. */ + if((this->file == NULL) || + (this->position_current.vts != this->position_next.vts) || + (this->position_current.domain != this->position_next.domain)) { + dvd_read_domain_t domain; + int32_t vtsN; + dvdnav_vts_change_event_t *vts_event = (dvdnav_vts_change_event_t *)*buf; + + if(this->file) { + DVDCloseFile(this->file); + this->file = NULL; + } + + vts_event->old_vtsN = this->position_current.vts; + vts_event->old_domain = this->position_current.domain; + + /* Use the DOMAIN to find whether to open menu or title VOBs */ + switch(this->position_next.domain) { + case DVD_DOMAIN_FirstPlay: + case DVD_DOMAIN_VMGM: + domain = DVD_READ_MENU_VOBS; + vtsN = 0; + break; + case DVD_DOMAIN_VTSMenu: + domain = DVD_READ_MENU_VOBS; + vtsN = this->position_next.vts; + break; + case DVD_DOMAIN_VTSTitle: + domain = DVD_READ_TITLE_VOBS; + vtsN = this->position_next.vts; + break; + default: + printerr("Unknown domain when changing VTS."); + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_ERR; + } + + this->position_current.vts = this->position_next.vts; + this->position_current.domain = this->position_next.domain; + dvdnav_read_cache_clear(this->cache); + this->file = DVDOpenFile(vm_get_dvd_reader(this->vm), vtsN, domain); + vts_event->new_vtsN = this->position_next.vts; + vts_event->new_domain = this->position_next.domain; + + /* If couldn't open the file for some reason, moan */ + if(this->file == NULL) { + printerrf("Error opening vtsN=%i, domain=%i.", vtsN, domain); + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_ERR; + } + + /* File opened successfully so return a VTS change event */ + (*event) = DVDNAV_VTS_CHANGE; +#ifdef LOG_DEBUG + Log3(this, "VTS_CHANGE"); +#endif + (*len) = sizeof(dvdnav_vts_change_event_t); + + this->spu_clut_changed = 1; + this->position_current.cell = -1; /* Force an update */ + this->position_current.spu_channel = -1; /* Force an update */ + this->position_current.audio_channel = -1; /* Force an update */; + + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_OK; + } + + /* Check if the cell changed */ + if( (this->position_current.cell != this->position_next.cell) || + (this->position_current.cell_restart != this->position_next.cell_restart) || + (this->position_current.cell_start != this->position_next.cell_start) ) { + dvdnav_cell_change_event_t *cell_event = (dvdnav_cell_change_event_t *)*buf; + int32_t first_cell_nr, last_cell_nr, i; + dvd_state_t *state = &this->vm->state; + + this->cur_cell_time = 0; + (*event) = DVDNAV_CELL_CHANGE; +#ifdef LOG_DEBUG + Log3(this, "CELL_CHANGE"); +#endif + (*len) = sizeof(dvdnav_cell_change_event_t); + + cell_event->cellN = state->cellN; + cell_event->pgN = state->pgN; + cell_event->cell_length = + (state->pgc->cell_playback[state->cellN - 1].last_sector + 1); + + cell_event->pg_length = 0; + /* Find start cell of program. */ + first_cell_nr = state->pgc->program_map[state->pgN-1]; + /* Find end cell of program */ + if(state->pgN < state->pgc->nr_of_programs) + last_cell_nr = state->pgc->program_map[state->pgN] - 1; + else + last_cell_nr = state->pgc->nr_of_cells; + for (i = first_cell_nr; i <= last_cell_nr; i++) + cell_event->pg_length += + (state->pgc->cell_playback[i - 1].last_sector + 1); + + cell_event->pgc_length = dvdnav_convert_time(&state->pgc->playback_time); + + cell_event->cell_start = 0; + for (i = 1; i < state->cellN; i++) + cell_event->cell_start += + (state->pgc->cell_playback[i - 1].last_sector + 1); + + cell_event->pg_start = 0; + for (i = 1; i < state->pgc->program_map[state->pgN-1]; i++) + cell_event->pg_start += + (state->pgc->cell_playback[i - 1].last_sector + 1); + + this->position_current.cell = this->position_next.cell; + this->position_current.cell_restart = this->position_next.cell_restart; + this->position_current.cell_start = this->position_next.cell_start; + this->position_current.block = this->position_next.block; + + /* vobu info is used for mid cell resumes */ + this->vobu.vobu_start = this->position_next.cell_start + this->position_next.block; + this->vobu.vobu_next = 0; + /* Make blockN == vobu_length to do expected_nav */ + this->vobu.vobu_length = 0; + this->vobu.blockN = 0; + + /* update the spu palette at least on PGC changes */ + this->spu_clut_changed = 1; + this->position_current.spu_channel = -1; /* Force an update */ + this->position_current.audio_channel = -1; /* Force an update */ + + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_OK; + } + + /* has the CLUT changed? */ + if(this->spu_clut_changed) { + (*event) = DVDNAV_SPU_CLUT_CHANGE; +#ifdef LOG_DEBUG + Log3(this, "SPU_CLUT_CHANGE"); +#endif + (*len) = 16 * sizeof(uint32_t); + memcpy(*buf, state->pgc->palette, sizeof(state->pgc->palette)); + this->spu_clut_changed = 0; + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_OK; + } + + /* has the SPU channel changed? */ + if(this->position_current.spu_channel != this->position_next.spu_channel) { + dvdnav_spu_stream_change_event_t *stream_change = (dvdnav_spu_stream_change_event_t *)*buf; + + (*event) = DVDNAV_SPU_STREAM_CHANGE; +#ifdef LOG_DEBUG + Log3(this, "SPU_STREAM_CHANGE"); +#endif + (*len) = sizeof(dvdnav_spu_stream_change_event_t); + stream_change->physical_wide = vm_get_subp_active_stream(this->vm, 0); + stream_change->physical_letterbox = vm_get_subp_active_stream(this->vm, 1); + stream_change->physical_pan_scan = vm_get_subp_active_stream(this->vm, 2); + this->position_current.spu_channel = this->position_next.spu_channel; +#ifdef LOG_DEBUG + Log3(this, "SPU_STREAM_CHANGE stream_id_wide=%d",stream_change->physical_wide); + Log3(this, "SPU_STREAM_CHANGE stream_id_letterbox=%d",stream_change->physical_letterbox); + Log3(this, "SPU_STREAM_CHANGE stream_id_pan_scan=%d",stream_change->physical_pan_scan); + Log3(this, "SPU_STREAM_CHANGE returning DVDNAV_STATUS_OK"); +#endif + /* This is not realy the right place to do this. FOSL_BTNN should set the register + * at HLI_S_PTM rather than when we enter the SPU. As well we should activate FOAC_BTNN + * at HLI_E_PTM + */ + if (this->pci.hli.hl_gi.fosl_btnn != 0) { + set_HL_BTN(this->vm, this->pci.hli.hl_gi.fosl_btnn); + } + + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_OK; + } + + /* has the audio channel changed? */ + if(this->position_current.audio_channel != this->position_next.audio_channel) { + dvdnav_audio_stream_change_event_t *stream_change = (dvdnav_audio_stream_change_event_t *)*buf; + + (*event) = DVDNAV_AUDIO_STREAM_CHANGE; +#ifdef LOG_DEBUG + Log3(this, "AUDIO_STREAM_CHANGE"); +#endif + (*len) = sizeof(dvdnav_audio_stream_change_event_t); + stream_change->physical = vm_get_audio_active_stream( this->vm ); + stream_change->logical = this->position_next.audio_channel; + this->position_current.audio_channel = this->position_next.audio_channel; +#ifdef LOG_DEBUG + Log3(this, "AUDIO_STREAM_CHANGE stream_id=%d returning DVDNAV_STATUS_OK",stream_change->physical); +#endif + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_OK; + } + + /* Check the STILLFRAME flag */ + if(this->position_current.still != 0) { + dvdnav_still_event_t *still_event = (dvdnav_still_event_t *)*buf; + + (*event) = DVDNAV_STILL_FRAME; +#ifdef LOG_DEBUG + Log3(this, "STILL_FRAME"); +#endif + (*len) = sizeof(dvdnav_still_event_t); + still_event->length = this->position_current.still; + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_OK; + } + + /* Have we reached the end of a VOBU? */ + if (this->vobu.blockN >= this->vobu.vobu_length) { + + /* Have we reached the end of a cell? */ + if(this->vobu.vobu_next == SRI_END_OF_CELL) { + /* End of Cell from NAV DSI info */ +#ifdef LOG_DEBUG + Log3(this, "Still set to %x", this->position_next.still); +#endif + this->position_current.still = this->position_next.still; + + /* we are about to leave a cell, so a lot of state changes could occur; + * under certain conditions, the application should get in sync with us before this, + * otherwise it might show stills or menus too shortly */ + if ((this->position_current.still || this->pci.hli.hl_gi.hli_ss) && !this->sync_wait_skip) + this->sync_wait = 1; + + if(!this->position_current.still || this->skip_still ) { + /* no active cell still -> get us to the next cell */ + vm_get_next_cell(this->vm); + this->position_current.still = 0; /* still gets activated at end of cell */ + this->skip_still = 0; + this->sync_wait_skip = 0; + } + /* handle related state changes in next iteration */ + (*event) = DVDNAV_NOP; + (*len) = 0; + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_OK; + } + + /* at the start of the next VOBU -> expecting NAV packet */ + result = dvdnav_read_cache_block(this->cache, this->vobu.vobu_start + this->vobu.vobu_next, 1, buf); + + if(result <= 0) { + printerr("Error reading NAV packet."); + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_ERR; + } + /* Decode nav into pci and dsi. Then get next VOBU info. */ + if(!dvdnav_decode_packet(this, *buf, &this->dsi, &this->pci)) { + printerr("Expected NAV packet but none found."); + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_ERR; + } + /* We need to update the vm state->blockN with which VOBU we are in. + * This is so RSM resumes to the VOBU level and not just the CELL level. + */ + this->vm->state.blockN = this->vobu.vobu_start - this->position_current.cell_start; + + dvdnav_get_vobu(this, &this->dsi, &this->pci, &this->vobu); + this->vobu.blockN = 0; + /* Give the cache a hint about the size of next VOBU. + * This improves pre-caching, because the VOBU will almost certainly be read entirely. + */ + dvdnav_pre_cache_blocks(this->cache, this->vobu.vobu_start+1, this->vobu.vobu_length+1); + + /* release NAV menu filter, when we reach the same NAV packet again */ + if (this->last_cmd_nav_lbn == this->pci.pci_gi.nv_pck_lbn) + this->last_cmd_nav_lbn = SRI_END_OF_CELL; + + /* Successfully got a NAV packet */ + (*event) = DVDNAV_NAV_PACKET; +#ifdef LOG_DEBUG + Log3(this, "NAV_PACKET"); +#endif + (*len) = 2048; + this->cur_cell_time = dvdnav_convert_time(&this->dsi.dsi_gi.c_eltm); + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_OK; + } + + /* If we've got here, it must just be a normal block. */ + if(!this->file) { + printerr("Attempting to read without opening file."); + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_ERR; + } + + this->vobu.blockN++; + result = dvdnav_read_cache_block(this->cache, this->vobu.vobu_start + this->vobu.blockN, 1, buf); + if(result <= 0) { + printerr("Error reading from DVD."); + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_ERR; + } + (*event) = DVDNAV_BLOCK_OK; + (*len) = 2048; + + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_OK; +} + +dvdnav_status_t dvdnav_get_title_string(dvdnav_t *this, const char **title_str) { + (*title_str) = this->vm->dvd_name; + return DVDNAV_STATUS_OK; +} + +dvdnav_status_t dvdnav_get_serial_string(dvdnav_t *this, const char **serial_str) { + (*serial_str) = this->vm->dvd_serial; + return DVDNAV_STATUS_OK; +} + +uint8_t dvdnav_get_video_aspect(dvdnav_t *this) { + uint8_t retval; + + if(!this->started) { + printerr("Virtual DVD machine not started."); + return -1; + } + + pthread_mutex_lock(&this->vm_lock); + retval = (uint8_t)vm_get_video_aspect(this->vm); + pthread_mutex_unlock(&this->vm_lock); + + return retval; +} +dvdnav_status_t dvdnav_get_video_resolution(dvdnav_t *this, uint32_t *width, uint32_t *height) { + int w, h; + + if(!this->started) { + printerr("Virtual DVD machine not started."); + return DVDNAV_STATUS_ERR; + } + + pthread_mutex_lock(&this->vm_lock); + vm_get_video_res(this->vm, &w, &h); + pthread_mutex_unlock(&this->vm_lock); + + *width = w; + *height = h; + return DVDNAV_STATUS_OK; +} + +uint8_t dvdnav_get_video_scale_permission(dvdnav_t *this) { + uint8_t retval; + + if(!this->started) { + printerr("Virtual DVD machine not started."); + return -1; + } + + pthread_mutex_lock(&this->vm_lock); + retval = (uint8_t)vm_get_video_scale_permission(this->vm); + pthread_mutex_unlock(&this->vm_lock); + + return retval; +} + +uint16_t dvdnav_audio_stream_to_lang(dvdnav_t *this, uint8_t stream) { + audio_attr_t attr; + + if(!this->started) { + printerr("Virtual DVD machine not started."); + return -1; + } + + pthread_mutex_lock(&this->vm_lock); + attr = vm_get_audio_attr(this->vm, stream); + pthread_mutex_unlock(&this->vm_lock); + + if(attr.lang_type != 1) + return 0xffff; + + return attr.lang_code; +} + +uint16_t dvdnav_audio_stream_format(dvdnav_t *this, uint8_t stream) { + audio_attr_t attr; + uint16_t format; + + if(!this->started) { + printerr("Virtual DVD machine not started."); + return -1; /* 0xffff */ + } + + pthread_mutex_lock(&this->vm_lock); + attr = vm_get_audio_attr(this->vm, stream); + pthread_mutex_unlock(&this->vm_lock); + + switch(attr.audio_format) { + case 0: + format = DVD_AUDIO_FORMAT_AC3; + break; + case 2: /* MPEG-1 or MPEG-2 without extension bitstream. */ + case 3: /* MPEG-2 with extension bitstream. */ + format = DVD_AUDIO_FORMAT_MPEG2_EXT; + break; + case 4: + format = DVD_AUDIO_FORMAT_LPCM; + break; + case 6: + format = DVD_AUDIO_FORMAT_DTS; + break; + case 7: + format = DVD_AUDIO_FORMAT_SDDS; + break; + default: + format = 0xffff; + break; + } + + return format; +} + +uint16_t dvdnav_audio_stream_channels(dvdnav_t *this, uint8_t stream) { + audio_attr_t attr; + + if(!this->started) { + printerr("Virtual DVD machine not started."); + return -1; /* 0xffff */ + } + + pthread_mutex_lock(&this->vm_lock); + attr = vm_get_audio_attr(this->vm, stream); + pthread_mutex_unlock(&this->vm_lock); + + return attr.channels + 1; +} + +uint16_t dvdnav_spu_stream_to_lang(dvdnav_t *this, uint8_t stream) { + subp_attr_t attr; + + if(!this->started) { + printerr("Virtual DVD machine not started."); + return -1; + } + + pthread_mutex_lock(&this->vm_lock); + attr = vm_get_subp_attr(this->vm, stream); + pthread_mutex_unlock(&this->vm_lock); + + if(attr.type != 1) + return 0xffff; + + return attr.lang_code; +} + +int8_t dvdnav_get_audio_logical_stream(dvdnav_t *this, uint8_t audio_num) { + int8_t retval; + + if(!this->started) { + printerr("Virtual DVD machine not started."); + return -1; + } + + pthread_mutex_lock(&this->vm_lock); + if (!this->vm->state.pgc) { + printerr("No current PGC."); + pthread_mutex_unlock(&this->vm_lock); + return -1; + } + retval = vm_get_audio_stream(this->vm, audio_num); + pthread_mutex_unlock(&this->vm_lock); + + return retval; +} + +dvdnav_status_t dvdnav_get_audio_attr(dvdnav_t *this, uint8_t audio_num, audio_attr_t *audio_attr) { + if(!this->started) { + printerr("Virtual DVD machine not started."); + return DVDNAV_STATUS_ERR; + } + pthread_mutex_lock(&this->vm_lock); + if (!this->vm->state.pgc) { + printerr("No current PGC."); + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_ERR; + } + *audio_attr=vm_get_audio_attr(this->vm, audio_num); + pthread_mutex_unlock(&this->vm_lock); + + return DVDNAV_STATUS_OK; +} + +int8_t dvdnav_get_spu_logical_stream(dvdnav_t *this, uint8_t subp_num) { + int8_t retval; + + if(!this->started) { + printerr("Virtual DVD machine not started."); + return -1; + } + + pthread_mutex_lock(&this->vm_lock); + if (!this->vm->state.pgc) { + printerr("No current PGC."); + pthread_mutex_unlock(&this->vm_lock); + return -1; + } + retval = vm_get_subp_stream(this->vm, subp_num, 0); + pthread_mutex_unlock(&this->vm_lock); + + return retval; +} + +dvdnav_status_t dvdnav_get_spu_attr(dvdnav_t *this, uint8_t audio_num, subp_attr_t *subp_attr) { + if(!this->started) { + printerr("Virtual DVD machine not started."); + return DVDNAV_STATUS_ERR; + } + pthread_mutex_lock(&this->vm_lock); + if (!this->vm->state.pgc) { + printerr("No current PGC."); + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_ERR; + } + *subp_attr=vm_get_subp_attr(this->vm, audio_num); + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_OK; +} + +int8_t dvdnav_get_active_audio_stream(dvdnav_t *this) { + int8_t retval; + + if(!this->started) { + printerr("Virtual DVD machine not started."); + return -1; + } + + pthread_mutex_lock(&this->vm_lock); + if (!this->vm->state.pgc) { + printerr("No current PGC."); + pthread_mutex_unlock(&this->vm_lock); + return -1; + } + retval = vm_get_audio_active_stream(this->vm); + pthread_mutex_unlock(&this->vm_lock); + + return retval; +} + +int8_t dvdnav_get_active_spu_stream(dvdnav_t *this) { + int8_t retval; + + if(!this->started) { + printerr("Virtual DVD machine not started."); + return -1; + } + + pthread_mutex_lock(&this->vm_lock); + if (!this->vm->state.pgc) { + printerr("No current PGC."); + pthread_mutex_unlock(&this->vm_lock); + return -1; + } + retval = vm_get_subp_active_stream(this->vm, 0); + pthread_mutex_unlock(&this->vm_lock); + + return retval; +} + +static int8_t dvdnav_is_domain(dvdnav_t *this, DVDDomain_t domain) { + int8_t retval; + + if(!this->started) { + printerr("Virtual DVD machine not started."); + return -1; + } + + pthread_mutex_lock(&this->vm_lock); + retval = (this->vm->state.domain == domain); + pthread_mutex_unlock(&this->vm_lock); + + return retval; +} + +/* First Play domain. (Menu) */ +int8_t dvdnav_is_domain_fp(dvdnav_t *this) { + return dvdnav_is_domain(this, DVD_DOMAIN_FirstPlay); +} +/* Video management Menu domain. (Menu) */ +int8_t dvdnav_is_domain_vmgm(dvdnav_t *this) { + return dvdnav_is_domain(this, DVD_DOMAIN_VMGM); +} +/* Video Title Menu domain (Menu) */ +int8_t dvdnav_is_domain_vtsm(dvdnav_t *this) { + return dvdnav_is_domain(this, DVD_DOMAIN_VTSMenu); +} +/* Video Title domain (playing movie). */ +int8_t dvdnav_is_domain_vts(dvdnav_t *this) { + return dvdnav_is_domain(this, DVD_DOMAIN_VTSTitle); +} + +/* Generally delegate angle information handling to VM */ +dvdnav_status_t dvdnav_angle_change(dvdnav_t *this, int32_t angle) { + int32_t num, current; + + pthread_mutex_lock(&this->vm_lock); + vm_get_angle_info(this->vm, ¤t, &num); + /* Set angle SPRM if valid */ + if((angle > 0) && (angle <= num)) { + this->vm->state.AGL_REG = angle; + } else { + printerr("Passed an invalid angle number."); + pthread_mutex_unlock(&this->vm_lock); + return DVDNAV_STATUS_ERR; + } + pthread_mutex_unlock(&this->vm_lock); + + return DVDNAV_STATUS_OK; +} + +dvdnav_status_t dvdnav_get_angle_info(dvdnav_t *this, int32_t *current_angle, + int32_t *number_of_angles) { + pthread_mutex_lock(&this->vm_lock); + vm_get_angle_info(this->vm, current_angle, number_of_angles); + pthread_mutex_unlock(&this->vm_lock); + + return DVDNAV_STATUS_OK; +} + +pci_t* dvdnav_get_current_nav_pci(dvdnav_t *this) { + if(!this) return 0; + return &this->pci; +} + +dsi_t* dvdnav_get_current_nav_dsi(dvdnav_t *this) { + if(!this) return 0; + return &this->dsi; +} + +uint32_t dvdnav_get_next_still_flag(dvdnav_t *this) { + if(!this) return -1; + return this->position_next.still; +} + +user_ops_t dvdnav_get_restrictions(dvdnav_t* this) { + /* + * user_ops_t is a structure of 32 bits. We want to compute + * the union of two of those bitfields so to make this quicker + * than performing 32 ORs, we will access them as 32bits words. + */ + union { + user_ops_t ops_struct; + uint32_t ops_int; + } ops, tmp; + + ops.ops_int = 0; + + if(!this->started) { + printerr("Virtual DVD machine not started."); + return ops.ops_struct; + } + + pthread_mutex_lock(&this->vm_lock); + ops.ops_struct = this->pci.pci_gi.vobu_uop_ctl; + + if(this->vm && this->vm->state.pgc) { + tmp.ops_struct = this->vm->state.pgc->prohibited_ops; + ops.ops_int |= tmp.ops_int; + } + pthread_mutex_unlock(&this->vm_lock); + + return ops.ops_struct; +} + +const char* dvdnav_version(void) { + return VERSION; +} diff -durN a/src/vm/vm.c b/src/vm/vm.c --- a/src/vm/vm.c 2020-05-03 15:01:16.798469411 +0200 +++ b/src/vm/vm.c 2020-05-03 15:01:26.138448826 +0200 @@ -1176,3 +1176,191 @@ { ifoClose(ifo); } + +uint32_t vm_kodi_get_vmg_category(vm_t *vm) +{ + uint32_t ret = 0; + + if(vm && vm->vmgi && vm->vmgi->vmgi_mat) + ret = vm->vmgi->vmgi_mat->vmg_category; + return ret; +} + +int vm_kodi_set_active_audio_stream(vm_t *vm, int streamId) +{ + uint32_t ret = 0; + + if(vm && vm->state.pgc && (vm->state.pgc->audio_control[streamId] & (1<<15))) + { + if(!(vm->state.domain != DVD_DOMAIN_VTSTitle && streamId != 0)) + { + vm->state.AST_REG = streamId; + ret = 1; + } + } + return ret; +} + +int vm_kodi_set_active_subtitle_stream(vm_t *vm, int streamId) +{ + uint32_t ret = 0; + + if(vm && vm->state.pgc && (vm->state.pgc->subp_control[streamId] & (1<<31))) + { + if(!(vm->state.domain != DVD_DOMAIN_VTSTitle && streamId != 0)) + { + vm->state.SPST_REG = streamId | (vm->state.SPST_REG & 0x40); + ret = 1; + } + } + return ret; +} + +uint32_t vm_kodi_get_subtitle_count(vm_t *vm) +{ + int ret = 1; + + if(vm && vm->state.pgc && vm->state.domain == DVD_DOMAIN_VTSTitle) + { + int i; + + ret = 0; + for(i = 0; i < 32; i++) + { + if (vm->state.pgc->subp_control[i] & (1<<31)) + ret++; + } + } + return ret; +} + +uint32_t vm_kodi_get_audio_stream_count(vm_t *vm) +{ + int ret = 1; + + if(vm && vm->state.pgc && vm->state.domain == DVD_DOMAIN_VTSTitle) + { + int i; + + ret = 0; + for(i = 0; i < 8; i++) + { + if (vm->state.pgc->audio_control[i] & (1<<15)) + ret++; + } + } + return ret; +} + +void vm_kodi_enable_subtitle_stream(vm_t *vm, bool enable) +{ + if(enable) + vm->state.SPST_REG |= 0x40; + else + vm->state.SPST_REG &= ~0x40; +} + +bool vm_kodi_get_subtitle_stream_state(vm_t *vm) +{ + return !!(vm->state.SPST_REG & 0x40); +} + +int vm_kodi_convert_audio_stream_id_to_xbmc(vm_t *vm, int id) +{ + int ret = -1; + + if(vm->state.domain == DVD_DOMAIN_VTSTitle) + { + if(id >= 0 && id < 8 && vm->state.pgc && (vm->state.pgc->audio_control[id] & (1<<15))) + { + int i, stream = -1; + + for(i = 0; i < id; i++) + { + if (vm->state.pgc->audio_control[i] & (1<<15)) + stream++; + } + ret = stream; + } + } else if(id == 0) + ret = 0; + + return ret; +} + +int vm_kodi_convert_audio_stream_id_from_xbmc(vm_t *vm, int id) +{ + int ret = -1; + + if(vm->state.domain == DVD_DOMAIN_VTSTitle) + { + if(id >= 0 && id < 8 && vm->state.pgc) + { + int i, stream = -1; + + for(i = 0; i < 8; i++) + { + if (vm->state.pgc->audio_control[i] & (1<<15)) + stream++; + if (stream == id) + { + ret = i; + break; + } + } + } + } else if(id == 0) + ret = 0; + + return ret; +} + +int vm_kodi_convert_subtitle_stream_id_to_xbmc(vm_t *vm, int id) +{ + int ret = -1; + + if(vm->state.domain == DVD_DOMAIN_VTSTitle) + { + if(id >= 0 && id < 32 && vm->state.pgc && (vm->state.pgc->subp_control[id] & (1<<31))) + { + int i, stream = -1; + + for(i = 0; i < id; i++) + { + if (vm->state.pgc->subp_control[i] & (1<<31)) + stream++; + } + ret = stream; + } + } else if(id == 0) + ret = 0; + + return ret; +} + +int vm_kodi_convert_subtitle_stream_id_from_xbmc(vm_t *vm, int id) +{ + int ret = -1; + + if(vm->state.domain == DVD_DOMAIN_VTSTitle) + { + if(id >= 0 && id < 32 && vm->state.pgc) + { + int i, stream = -1; + + for(i = 0; i < 32; i++) + { + if (vm->state.pgc->subp_control[i] & (1<<31)) + stream++; + if (stream == id) + { + ret = i; + break; + } + } + } + } else if(id == 0) + ret = 0; + + return ret; +} diff -durN a/src/vm/vm.c.orig b/src/vm/vm.c.orig --- a/src/vm/vm.c.orig 1970-01-01 01:00:00.000000000 +0100 +++ b/src/vm/vm.c.orig 2020-05-03 15:01:15.766471686 +0200 @@ -0,0 +1,1178 @@ +/* + * Copyright (C) 2000, 2001 Håkan Hjort + * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net> + * 2002-2004 the dvdnav project + * + * This file is part of libdvdnav, a DVD navigation library. It is modified + * from a file originally part of the Ogle DVD player. + * + * libdvdnav is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * libdvdnav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with libdvdnav; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <inttypes.h> +#include <limits.h> +#include <assert.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <fcntl.h> +#include <ctype.h> + +#include <dvdread/nav_types.h> +#include <dvdread/ifo_types.h> +#include <dvdread/ifo_read.h> +#include "dvdnav/dvdnav.h" + +#include "decoder.h" +#include "vm.h" +#include "play.h" +#include "getset.h" +#include "dvdnav_internal.h" +#include "logger.h" + +#ifdef _MSC_VER +#include <io.h> /* read() */ +#endif /* _MSC_VER */ + +#ifdef __OS2__ +#define INCL_DOS +#include <os2safe.h> +#include <os2.h> +#include <io.h> /* setmode() */ +#include <fcntl.h> /* O_BINARY */ +#endif + +#if DVDREAD_VERSION >= DVDREAD_VERSION_CODE(6,1,0) +static void dvd_reader_logger_handler( void *priv, dvd_logger_level_t level, + const char *fmt, va_list list ) +{ + vm_t *vm = priv; + if(vm->logcb.pf_log) + vm->logcb.pf_log(vm->priv, (dvdnav_logger_level_t) level, fmt, list ); +} +#endif + +static int dvd_reader_seek_handler(void *priv, uint64_t pos) +{ + vm_t *vm = priv; + if(vm->streamcb.pf_seek) + return vm->streamcb.pf_seek(vm->priv, pos); + return 1; +} + +static int dvd_reader_read_handler(void *priv, void *buffer, int count) +{ + vm_t *vm = priv; + if(vm->streamcb.pf_read) + return vm->streamcb.pf_read(vm->priv, buffer, count); + return 1; +} + +static int dvd_reader_readv_handler(void *priv, void *iovec, int count) +{ + vm_t *vm = priv; + if(vm->streamcb.pf_readv) + return vm->streamcb.pf_readv(vm->priv, iovec, count); + return 1; +} + +/* +#define DVDNAV_STRICT +*/ + +/* Local prototypes */ + +/* Process link - returns 1 if a hop has been performed */ +static int process_command(vm_t *vm,link_t link_values); + +/* Helper functions */ +static void vm_close(vm_t *vm); + +/* Debug functions */ + +#ifdef TRACE +void vm_position_print(vm_t *vm, vm_position_t *position) { + Log3(vm, "But=%x Spu=%x Aud=%x Ang=%x Hop=%x vts=%x dom=%x cell=%x cell_restart=%x cell_start=%x still=%x block=%x", + position->button, + position->spu_channel, + position->audio_channel, + position->angle_channel, + position->hop_channel, + position->vts, + position->domain, + position->cell, + position->cell_restart, + position->cell_start, + position->still, + position->block); +} + +static void vm_print_current_domain_state(vm_t *vm) { + const char *domain; + + switch(vm->state.domain) { + case DVD_DOMAIN_VTSTitle: domain = "Video Title"; break; + case DVD_DOMAIN_VTSMenu: domain = "Video Title Menu"; break; + case DVD_DOMAIN_VMGM: domain = "Video Manager Menu"; break; + case DVD_DOMAIN_FirstPlay: domain = "First Play"; break; + default: domain = "Unknown"; break; + } + Log3(vm, "%s Domain: VTS:%d PGC:%d PG:%u CELL:%u BLOCK:%u VTS_TTN:%u TTN:%u TT_PGCN:%u", + domain, + vm->state.vtsN, + get_PGCN(vm), + vm->state.pgN, + vm->state.cellN, + vm->state.blockN, + vm->state.VTS_TTN_REG, + vm->state.TTN_REG, + vm->state.TT_PGCN_REG); +} +#endif + +#ifdef __OS2__ +#define open os2_open + +static int os2_open(const char *name, int oflag) +{ + HFILE hfile; + ULONG ulAction; + ULONG rc; + + rc = DosOpenL(name, &hfile, &ulAction, 0, FILE_NORMAL, + OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW, + OPEN_ACCESS_READONLY | OPEN_SHARE_DENYNONE | OPEN_FLAGS_DASD, + NULL); + + if(rc) + return -1; + + setmode(hfile, O_BINARY); + + return (int)hfile; +} +#endif + +static void escaped_strcpy(char *dst, const char *src, size_t len) +{ + for(size_t i=0; i < len; i++ ) + { + if(src[i] == 0) + { + dst[i] = 0; + break; + } + else if(isprint(src[i])) + { + dst[i] = src[i]; + } else { + dst[i] = ' '; + } + } +} + +static int dvd_read_name(const vm_t *vm, char *name, char *serial, const char *device) { + /* Because we are compiling with _FILE_OFFSET_BITS=64 + * all off_t are 64bit. + */ + off_t off; + ssize_t read_size = 0; + int fd = -1; + uint8_t data[DVD_VIDEO_LB_LEN]; + + /* Read DVD name */ + if (device == NULL) { + Log0(vm, "Device name string NULL"); + goto fail; + } + if ((fd = open(device, O_RDONLY)) == -1) { + Log0(vm, "Unable to open device file %s.", device); + goto fail; + } + + if ((off = lseek( fd, 32 * (off_t) DVD_VIDEO_LB_LEN, SEEK_SET )) == (off_t) - 1) { + Log0(vm, "Unable to seek to the title block %u.", 32); + goto fail; + } + + if( off != ( 32 * (off_t) DVD_VIDEO_LB_LEN ) ) { + Log0(vm, "Can't seek to block %u", 32 ); + goto fail; + } + + if ((read_size = read( fd, data, DVD_VIDEO_LB_LEN )) == -1) { + Log0(vm, "Can't read name block. Probably not a DVD-ROM device."); + goto fail; + } + + close(fd); + fd = -1; + if (read_size != DVD_VIDEO_LB_LEN) { + Log0(vm, "Can't read name block. Probably not a DVD-ROM device."); + goto fail; + } + + char buffer[49] = {0}; + strncpy(name, (const char*) &data[25], 48); + name[48] = 0; + escaped_strcpy(buffer, name, 48); + Log2(vm, "DVD Title: %s", buffer); + + strncpy(serial, (const char*) &data[73], 14); + serial[14] = 0; + escaped_strcpy(buffer, serial, 14); + Log2(vm, "DVD Serial Number: %s", buffer); + + escaped_strcpy(buffer, (const char *) &data[89], 128 - 89); + Log2(vm, "DVD Title (Alternative): %s", buffer); + + return 1; + +fail: + if (fd >= 0) + close(fd); + + return 0; +} + +int ifoOpenNewVTSI(vm_t *vm, dvd_reader_t *dvd, int vtsN) { + if(vm->state.vtsN == vtsN) { + return 1; /* We alread have it */ + } + + if(vm->vtsi != NULL) + ifoClose(vm->vtsi); + + vm->vtsi = ifoOpenVTSI(dvd, vtsN); + if(vm->vtsi == NULL) { + Log0(vm, "ifoOpenVTSI failed"); + return 0; + } + if(!ifoRead_VTS_PTT_SRPT(vm->vtsi)) { + Log0(vm, "ifoRead_VTS_PTT_SRPT failed"); + return 0; + } + if(!ifoRead_PGCIT(vm->vtsi)) { + Log0(vm, "ifoRead_PGCIT failed"); + return 0; + } + if(!ifoRead_PGCI_UT(vm->vtsi)) { + Log0(vm, "ifoRead_PGCI_UT failed"); + return 0; + } + if(!ifoRead_VOBU_ADMAP(vm->vtsi)) { + Log0(vm, "ifoRead_VOBU_ADMAP vtsi failed"); + return 0; + } + if(!ifoRead_TITLE_VOBU_ADMAP(vm->vtsi)) { + Log0(vm, "ifoRead_TITLE_VOBU_ADMAP vtsi failed"); + return 0; + } + vm->state.vtsN = vtsN; + + return 1; +} + + +/* Initialisation & Destruction */ + +vm_t* vm_new_vm(void *priv, const dvdnav_logger_cb *logcb) { + vm_t *vm = calloc(1, sizeof(vm_t)); + if(vm) + { + vm->priv = priv; + if(logcb) + vm->logcb = *logcb; + } + return vm; +} + +void vm_free_vm(vm_t *vm) { + vm_close(vm); + free(vm); +} + + +/* IFO Access */ + +ifo_handle_t *vm_get_vmgi(vm_t *vm) { + return vm->vmgi; +} + +ifo_handle_t *vm_get_vtsi(vm_t *vm) { + return vm->vtsi; +} + + +/* Reader Access */ + +dvd_reader_t *vm_get_dvd_reader(vm_t *vm) { + return vm->dvd; +} + + +/* Basic Handling */ + +int vm_start(vm_t *vm) { + if (vm->stopped) { + if (!vm_reset(vm, NULL, NULL, NULL)) + return 0; + + vm->stopped = 0; + } + /* Set pgc to FP (First Play) pgc */ + set_FP_PGC(vm); + process_command(vm, play_PGC(vm)); + return !vm->stopped; +} + +void vm_stop(vm_t *vm) { + vm->stopped = 1; +} + +static void vm_close(vm_t *vm) { + if(!vm) + return; + if(vm->vmgi) { + ifoClose(vm->vmgi); + vm->vmgi=NULL; + } + if(vm->vtsi) { + ifoClose(vm->vtsi); + vm->vtsi=NULL; + } + if(vm->dvd) { + DVDClose(vm->dvd); + vm->dvd=NULL; + } + vm->stopped = 1; +} + +int vm_reset(vm_t *vm, const char *dvdroot, + void *priv, dvdnav_stream_cb *stream_cb) { + /* Setup State */ + memset(vm->state.registers.SPRM, 0, sizeof(vm->state.registers.SPRM)); + memset(vm->state.registers.GPRM, 0, sizeof(vm->state.registers.GPRM)); + memset(vm->state.registers.GPRM_mode, 0, sizeof(vm->state.registers.GPRM_mode)); + memset(vm->state.registers.GPRM_mode, 0, sizeof(vm->state.registers.GPRM_mode)); + memset(vm->state.registers.GPRM_time, 0, sizeof(vm->state.registers.GPRM_time)); + vm->state.registers.SPRM[0] = ('e'<<8)|'n'; /* Player Menu Languange code */ + vm->state.AST_REG = 15; /* 15 why? */ + vm->state.SPST_REG = 62; /* 62 why? */ + vm->state.AGL_REG = 1; + vm->state.TTN_REG = 1; + vm->state.VTS_TTN_REG = 1; + /* vm->state.TT_PGCN_REG = 0 */ + vm->state.PTTN_REG = 1; + vm->state.HL_BTNN_REG = 1 << 10; + vm->state.PTL_REG = 15; /* Parental Level */ + vm->state.registers.SPRM[12] = ('U'<<8)|'S'; /* Parental Management Country Code */ + vm->state.registers.SPRM[16] = ('e'<<8)|'n'; /* Initial Language Code for Audio */ + vm->state.registers.SPRM[18] = ('e'<<8)|'n'; /* Initial Language Code for Spu */ + vm->state.registers.SPRM[20] = 0x1; /* Player Regional Code Mask. Region free! */ + vm->state.registers.SPRM[14] = 0x100; /* Try Pan&Scan */ + vm->state.registers.SPRM[15] = 0x7CFC; /* Audio capabilities - All defined audio types */ + + vm->state.pgN = 0; + vm->state.cellN = 0; + vm->state.cell_restart = 0; + + vm->state.domain = DVD_DOMAIN_FirstPlay; + vm->state.rsm_vtsN = 0; + vm->state.rsm_cellN = 0; + vm->state.rsm_blockN = 0; + + vm->state.vtsN = -1; + + vm->hop_channel = 0; + + /* save target callbacks */ + if(stream_cb) + vm->streamcb = *stream_cb; + else + vm->streamcb = (dvdnav_stream_cb) { NULL, NULL, NULL }; + + /* bind local callbacks */ + vm->dvdstreamcb.pf_seek = vm->streamcb.pf_seek ? dvd_reader_seek_handler : NULL; + vm->dvdstreamcb.pf_read = vm->streamcb.pf_read ? dvd_reader_read_handler : NULL; + vm->dvdstreamcb.pf_readv = vm->streamcb.pf_readv ? dvd_reader_readv_handler : NULL; + + if (vm->dvd && (dvdroot || (priv && stream_cb))) { + /* a new dvd device has been requested */ + vm_close(vm); + } + if (!vm->dvd) { + /* dvdread stream callback handlers for redirection */ +#if DVDREAD_VERSION >= DVDREAD_VERSION_CODE(6,1,0) + dvd_logger_cb dvdread_logcb = { .pf_log = dvd_reader_logger_handler }; + /* Only install log handler if we have one ourself */ + dvd_logger_cb *p_dvdread_logcb = vm->logcb.pf_log ? &dvdread_logcb : NULL; + if(dvdroot) + vm->dvd = DVDOpen2(vm, p_dvdread_logcb, dvdroot); + else if(vm->priv && vm->dvdstreamcb.pf_read) + vm->dvd = DVDOpenStream2(vm, p_dvdread_logcb, &vm->dvdstreamcb); +#else + if(dvdroot) + vm->dvd = DVDOpen(dvdroot); + else if(vm->priv && vm->dvdstreamcb.pf_read) + vm->dvd = DVDOpenStream(vm, &vm->dvdstreamcb); +#endif + if(!vm->dvd) { + Log0(vm, "vm: failed to open/read the DVD"); + return 0; + } + vm->vmgi = ifoOpenVMGI(vm->dvd); + if(!vm->vmgi) { + Log0(vm, "vm: vm: failed to read VIDEO_TS.IFO"); + return 0; + } + if(!ifoRead_FP_PGC(vm->vmgi)) { + Log0(vm, "vm: vm: ifoRead_FP_PGC failed"); + return 0; + } + if(!ifoRead_TT_SRPT(vm->vmgi)) { + Log0(vm, "vm: vm: ifoRead_TT_SRPT failed"); + return 0; + } + if(!ifoRead_PGCI_UT(vm->vmgi)) { + Log0(vm, "vm: vm: ifoRead_PGCI_UT failed"); + return 0; + } + if(!ifoRead_PTL_MAIT(vm->vmgi)) { + Log0(vm, "vm: ifoRead_PTL_MAIT failed"); + /* return 0; Not really used for now.. */ + } + if(!ifoRead_VTS_ATRT(vm->vmgi)) { + Log0(vm, "vm: ifoRead_VTS_ATRT failed"); + /* return 0; Not really used for now.. */ + } + if(!ifoRead_VOBU_ADMAP(vm->vmgi)) { + Log0(vm, "vm: ifoRead_VOBU_ADMAP vgmi failed"); + /* return 0; Not really used for now.. */ + } + /* ifoRead_TXTDT_MGI(vmgi); Not implemented yet */ + if(dvd_read_name(vm, vm->dvd_name, vm->dvd_serial, dvdroot) != 1) { + Log1(vm, "vm: dvd_read_name failed"); + } + } + if (vm->vmgi) { + int i, mask; + char buffer[8 * 3 + 1]; + char *p = buffer; + for (i = 1, mask = 1; i <= 8; i++, mask <<= 1) + { + if (((vm->vmgi->vmgi_mat->vmg_category >> 16) & mask) == 0) + { + sprintf(p, " %02d", i); + p +=3; + } + } + *p = 0; + Log2(vm, "DVD disk reports itself with Region mask 0x%08x. Regions:%s", + vm->vmgi->vmgi_mat->vmg_category, buffer); + } + return 1; +} + + +/* copying and merging */ + +vm_t *vm_new_copy(vm_t *source) { + vm_t *target = vm_new_vm(source->priv, &source->logcb); + int vtsN; + int pgcN = get_PGCN(source); + int pgN = (source->state).pgN; + + if (target == NULL || pgcN == 0) + goto fail; + + memcpy(target, source, sizeof(vm_t)); + + /* open a new vtsi handle, because the copy might switch to another VTS */ + target->vtsi = NULL; + vtsN = (target->state).vtsN; + if (vtsN > 0) { + (target->state).vtsN = 0; + if (!ifoOpenNewVTSI(target, target->dvd, vtsN)) + goto fail; + + /* restore pgc pointer into the new vtsi */ + if (!set_PGCN(target, pgcN)) + goto fail; + + (target->state).pgN = pgN; + } + + return target; + +fail: + if (target != NULL) + vm_free_vm(target); + + return NULL; +} + +void vm_merge(vm_t *target, vm_t *source) { + if(target->vtsi) + ifoClose(target->vtsi); + memcpy(target, source, sizeof(vm_t)); + memset(source, 0, sizeof(vm_t)); +} + +void vm_free_copy(vm_t *vm) { + if(vm->vtsi) + ifoClose(vm->vtsi); + free(vm); +} + + +/* regular playback */ + +void vm_position_get(vm_t *vm, vm_position_t *position) { + position->button = vm->state.HL_BTNN_REG >> 10; + position->vts = vm->state.vtsN; + position->domain = vm->state.domain; + position->spu_channel = vm->state.SPST_REG; + position->audio_channel = vm->state.AST_REG; + position->angle_channel = vm->state.AGL_REG; + position->hop_channel = vm->hop_channel; /* Increases by one on each hop */ + position->cell = vm->state.cellN; + position->cell_restart = vm->state.cell_restart; + position->cell_start = vm->state.pgc->cell_playback[vm->state.cellN - 1].first_sector; + position->still = vm->state.pgc->cell_playback[vm->state.cellN - 1].still_time; + position->block = vm->state.blockN; + + /* handle PGC stills at PGC end */ + if (vm->state.cellN == vm->state.pgc->nr_of_cells) + position->still += vm->state.pgc->still_time; + /* still already determined */ + if (position->still) + return; + /* This is a rough fix for some strange still situations on some strange DVDs. + * There are discs (like the German "Back to the Future" RC2) where the only + * indication of a still is a cell playback time higher than the time the frames + * in this cell actually take to play (like 1 frame with 1 minute playback time). + * On the said BTTF disc, for these cells last_sector and last_vobu_start_sector + * are equal and the cells are very short, so we abuse these conditions to + * detect such discs. I consider these discs broken, so the fix is somewhat + * broken, too. */ + if ((vm->state.pgc->cell_playback[vm->state.cellN - 1].last_sector == + vm->state.pgc->cell_playback[vm->state.cellN - 1].last_vobu_start_sector) && + (vm->state.pgc->cell_playback[vm->state.cellN - 1].last_sector - + vm->state.pgc->cell_playback[vm->state.cellN - 1].first_sector < 1024)) { + int time; + int size = vm->state.pgc->cell_playback[vm->state.cellN - 1].last_sector - + vm->state.pgc->cell_playback[vm->state.cellN - 1].first_sector; + time = (vm->state.pgc->cell_playback[vm->state.cellN - 1].playback_time.hour >> 4 ) * 36000; + time += (vm->state.pgc->cell_playback[vm->state.cellN - 1].playback_time.hour & 0x0f) * 3600; + time += (vm->state.pgc->cell_playback[vm->state.cellN - 1].playback_time.minute >> 4 ) * 600; + time += (vm->state.pgc->cell_playback[vm->state.cellN - 1].playback_time.minute & 0x0f) * 60; + time += (vm->state.pgc->cell_playback[vm->state.cellN - 1].playback_time.second >> 4 ) * 10; + time += (vm->state.pgc->cell_playback[vm->state.cellN - 1].playback_time.second & 0x0f) * 1; + if (!time || size / time > 30) + /* datarate is too high, it might be a very short, but regular cell */ + return; + if (time > 0xff) time = 0xff; + position->still = time; + } +} + +void vm_get_next_cell(vm_t *vm) { + process_command(vm, play_Cell_post(vm)); +} + + +/* Jumping */ + +int vm_jump_pg(vm_t *vm, int pg) { + vm->state.pgN = pg; + process_command(vm, play_PG(vm)); + return 1; +} + +int vm_jump_cell_block(vm_t *vm, int cell, int block) { + vm->state.cellN = cell; + process_command(vm, play_Cell(vm)); + /* play_Cell can jump to a different cell in case of angles */ + if (vm->state.cellN == cell) + vm->state.blockN = block; + return 1; +} + +int vm_jump_title_program(vm_t *vm, int title, int pgcn, int pgn) { + link_t link; + + if(!set_PROG(vm, title, pgcn, pgn)) + return 0; + /* Some DVDs do not want us to jump directly into a title and have + * PGC pre commands taking us back to some menu. Since we do not like that, + * we do not execute PGC pre commands that would do a jump. */ + /* process_command(vm, play_PGC_PG(vm, vm->state.pgN)); */ + link = play_PGC_PG(vm, vm->state.pgN); + if (link.command != PlayThis) + /* jump occured -> ignore it and play the PG anyway */ + process_command(vm, play_PG(vm)); + else + process_command(vm, link); + return 1; +} + +int vm_jump_title_part(vm_t *vm, int title, int part) { + link_t link; + + if(!set_PTT(vm, title, part)) + return 0; + /* Some DVDs do not want us to jump directly into a title and have + * PGC pre commands taking us back to some menu. Since we do not like that, + * we do not execute PGC pre commands that would do a jump. */ + /* process_command(vm, play_PGC_PG(vm, vm->state.pgN)); */ + link = play_PGC_PG(vm, vm->state.pgN); + if (link.command != PlayThis) + /* jump occured -> ignore it and play the PG anyway */ + process_command(vm, play_PG(vm)); + else + process_command(vm, link); + return 1; +} + +int vm_jump_top_pg(vm_t *vm) { + process_command(vm, play_PG(vm)); + return 1; +} + +int vm_jump_next_pg(vm_t *vm) { + if(vm->state.pgN >= vm->state.pgc->nr_of_programs) { + /* last program -> move to TailPGC */ + process_command(vm, play_PGC_post(vm)); + return 1; + } else { + vm_jump_pg(vm, vm->state.pgN + 1); + return 1; + } +} + +int vm_jump_prev_pg(vm_t *vm) { + if (vm->state.pgN <= 1) { + /* first program -> move to last program of previous PGC */ + if (vm->state.pgc->prev_pgc_nr && set_PGCN(vm, vm->state.pgc->prev_pgc_nr)) { + process_command(vm, play_PGC(vm)); + vm_jump_pg(vm, vm->state.pgc->nr_of_programs); + return 1; + } + return 0; + } else { + vm_jump_pg(vm, vm->state.pgN - 1); + return 1; + } +} + +int vm_jump_up(vm_t *vm) { + if(vm->state.pgc->goup_pgc_nr && set_PGCN(vm, vm->state.pgc->goup_pgc_nr)) { + process_command(vm, play_PGC(vm)); + return 1; + } + return 0; +} + +int vm_jump_menu(vm_t *vm, DVDMenuID_t menuid) { + DVDDomain_t old_domain = vm->state.domain; + + switch (vm->state.domain) { + case DVD_DOMAIN_FirstPlay: /* FIXME XXX $$$ What should we do here? */ + break; + case DVD_DOMAIN_VTSTitle: + set_RSMinfo(vm, 0, vm->state.blockN); + /* FALL THROUGH */ + case DVD_DOMAIN_VTSMenu: + case DVD_DOMAIN_VMGM: + switch(menuid) { + case DVD_MENU_Title: + case DVD_MENU_Escape: + if(vm->vmgi == NULL || vm->vmgi->pgci_ut == NULL) + return 0; + vm->state.domain = DVD_DOMAIN_VMGM; + break; + case DVD_MENU_Root: + case DVD_MENU_Subpicture: + case DVD_MENU_Audio: + case DVD_MENU_Angle: + case DVD_MENU_Part: + if(vm->vtsi == NULL || vm->vtsi->pgci_ut == NULL) + return 0; + vm->state.domain = DVD_DOMAIN_VTSMenu; + break; + } + if(get_PGCIT(vm) && set_MENU(vm, menuid)) { + process_command(vm, play_PGC(vm)); + return 1; /* Jump */ + } else + vm->state.domain = old_domain; + break; + } + + return 0; +} + +int vm_jump_resume(vm_t *vm) { + link_t link_values = { LinkRSM, 0, 0, 0 }; + + if (!vm->state.rsm_vtsN) /* Do we have resume info? */ + return 0; + return !!process_command(vm, link_values); +} + +int vm_exec_cmd(vm_t *vm, vm_cmd_t *cmd) { + link_t link_values; + + if(vmEval_CMD(cmd, 1, &vm->state.registers, &link_values)) + return process_command(vm, link_values); + else + return 0; /* It updated some state thats all... */ +} + +/* link processing */ + +static int process_command(vm_t *vm, link_t link_values) { + + while(link_values.command != PlayThis) { + +#ifdef TRACE + Log3(vm, "Before printout starts:"); + vm_print_link(link_values); + Log3(vm, "Link values %i %i %i %i", link_values.command, + link_values.data1, link_values.data2, link_values.data3); + vm_print_current_domain_state(vm); + Log3(vm, "Before printout ends."); +#endif + + switch(link_values.command) { + + case LinkNoLink: + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + vm->state.HL_BTNN_REG = link_values.data1 << 10; + return 0; /* no actual jump */ + + case LinkTopC: + /* Restart playing from the beginning of the current Cell. */ + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + vm->state.HL_BTNN_REG = link_values.data1 << 10; + link_values = play_Cell(vm); + break; + + case LinkNextC: + /* Link to Next Cell */ + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + vm->state.HL_BTNN_REG = link_values.data1 << 10; + vm->state.cellN += 1; + link_values = play_Cell(vm); + break; + + case LinkPrevC: + /* Link to Previous Cell */ + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + vm->state.HL_BTNN_REG = link_values.data1 << 10; + assert(vm->state.cellN > 1); + vm->state.cellN -= 1; + link_values = play_Cell(vm); + break; + + case LinkTopPG: + /* Link to Top of current Program */ + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + vm->state.HL_BTNN_REG = link_values.data1 << 10; + link_values = play_PG(vm); + break; + + case LinkNextPG: + /* Link to Next Program */ + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + vm->state.HL_BTNN_REG = link_values.data1 << 10; + vm->state.pgN += 1; + link_values = play_PG(vm); + break; + + case LinkPrevPG: + /* Link to Previous Program */ + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + vm->state.HL_BTNN_REG = link_values.data1 << 10; + assert(vm->state.pgN > 1); + vm->state.pgN -= 1; + link_values = play_PG(vm); + break; + + case LinkTopPGC: + /* Restart playing from beginning of current Program Chain */ + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + vm->state.HL_BTNN_REG = link_values.data1 << 10; + link_values = play_PGC(vm); + break; + + case LinkNextPGC: + /* Link to Next Program Chain */ + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + vm->state.HL_BTNN_REG = link_values.data1 << 10; + assert(vm->state.pgc->next_pgc_nr != 0); + if(set_PGCN(vm, vm->state.pgc->next_pgc_nr)) + link_values = play_PGC(vm); + else + link_values.command = Exit; + break; + + case LinkPrevPGC: + /* Link to Previous Program Chain */ + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + vm->state.HL_BTNN_REG = link_values.data1 << 10; + assert(vm->state.pgc->prev_pgc_nr != 0); + if(set_PGCN(vm, vm->state.pgc->prev_pgc_nr)) + link_values = play_PGC(vm); + else + link_values.command = Exit; + break; + + case LinkGoUpPGC: + /* Link to GoUp Program Chain */ + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + vm->state.HL_BTNN_REG = link_values.data1 << 10; + assert(vm->state.pgc->goup_pgc_nr != 0); + if(set_PGCN(vm, vm->state.pgc->goup_pgc_nr)) + link_values = play_PGC(vm); + else + link_values.command = Exit; + break; + + case LinkTailPGC: + /* Link to Tail of Program Chain */ + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + vm->state.HL_BTNN_REG = link_values.data1 << 10; + link_values = play_PGC_post(vm); + break; + + case LinkRSM: + /* Check and see if there is any rsm info!! */ + if (!vm->state.rsm_vtsN) { + Log2(vm, "trying to resume without any resume info set"); + link_values.command = Exit; + break; + } + + vm->state.domain = DVD_DOMAIN_VTSTitle; + if (!ifoOpenNewVTSI(vm, vm->dvd, vm->state.rsm_vtsN)) + assert(0); + set_PGCN(vm, vm->state.rsm_pgcN); + + /* These should never be set in SystemSpace and/or MenuSpace */ + /* vm->state.TTN_REG = rsm_tt; ?? */ + /* vm->state.TT_PGCN_REG = vm->state.rsm_pgcN; ?? */ + int i; + for(i = 0; i < 5; i++) { + vm->state.registers.SPRM[4 + i] = vm->state.rsm_regs[i]; + } + + if(link_values.data1 != 0) + vm->state.HL_BTNN_REG = link_values.data1 << 10; + + if(vm->state.rsm_cellN == 0) { + assert(vm->state.cellN); /* Checking if this ever happens */ + vm->state.pgN = 1; + link_values = play_PG(vm); + } else { + /* vm->state.pgN = ?? this gets the right value in set_PGN() below */ + vm->state.cellN = vm->state.rsm_cellN; + link_values.command = PlayThis; + link_values.data1 = vm->state.rsm_blockN & 0xffff; + link_values.data2 = vm->state.rsm_blockN >> 16; + if(!set_PGN(vm)) { + /* Were at the end of the PGC, should not happen for a RSM */ + assert(0); + link_values.command = LinkTailPGC; + link_values.data1 = 0; /* No button */ + } + } + break; + + case LinkPGCN: + /* Link to Program Chain Number:data1 */ + if(!set_PGCN(vm, link_values.data1)) + assert(0); + link_values = play_PGC(vm); + break; + + case LinkPTTN: + /* Link to Part of current Title Number:data1 */ + /* BUTTON number:data2 */ + /* PGC Pre-Commands are not executed */ + assert(vm->state.domain == DVD_DOMAIN_VTSTitle); + if(link_values.data2 != 0) + vm->state.HL_BTNN_REG = link_values.data2 << 10; + if(!set_VTS_PTT(vm, vm->state.vtsN, vm->state.VTS_TTN_REG, link_values.data1)) + link_values.command = Exit; + else + link_values = play_PG(vm); + break; + + case LinkPGN: + /* Link to Program Number:data1 */ + /* BUTTON number:data2 */ + if(link_values.data2 != 0) + vm->state.HL_BTNN_REG = link_values.data2 << 10; + /* Update any other state, PTTN perhaps? */ + vm->state.pgN = link_values.data1; + link_values = play_PG(vm); + break; + + case LinkCN: + /* Link to Cell Number:data1 */ + /* BUTTON number:data2 */ + if(link_values.data2 != 0) + vm->state.HL_BTNN_REG = link_values.data2 << 10; + /* Update any other state, pgN, PTTN perhaps? */ + vm->state.cellN = link_values.data1; + link_values = play_Cell(vm); + break; + + case Exit: + vm->stopped = 1; + return 0; + + case JumpTT: + /* Jump to VTS Title Domain */ + /* Only allowed from the First Play domain(PGC) */ + /* or the Video Manager domain (VMG) */ + /* Stop SPRM9 Timer */ + /* Set SPRM1 and SPRM2 */ + assert(vm->state.domain == DVD_DOMAIN_VMGM || vm->state.domain == DVD_DOMAIN_FirstPlay); /* ?? */ + if(set_TT(vm, link_values.data1)) + link_values = play_PGC(vm); + else + link_values.command = Exit; + break; + + case JumpVTS_TT: + /* Jump to Title:data1 in same VTS Title Domain */ + /* Only allowed from the VTS Menu Domain(VTSM) */ + /* or the Video Title Set Domain(VTS) */ + /* Stop SPRM9 Timer */ + /* Set SPRM1 and SPRM2 */ + assert(vm->state.domain == DVD_DOMAIN_VTSMenu || vm->state.domain == DVD_DOMAIN_VTSTitle); /* ?? */ + if(!set_VTS_TT(vm, vm->state.vtsN, link_values.data1)) + link_values.command = Exit; + else + link_values = play_PGC(vm); + break; + + case JumpVTS_PTT: + /* Jump to Part:data2 of Title:data1 in same VTS Title Domain */ + /* Only allowed from the VTS Menu Domain(VTSM) */ + /* or the Video Title Set Domain(VTS) */ + /* Stop SPRM9 Timer */ + /* Set SPRM1 and SPRM2 */ + assert(vm->state.domain == DVD_DOMAIN_VTSMenu || vm->state.domain == DVD_DOMAIN_VTSTitle); /* ?? */ + if(!set_VTS_PTT(vm, vm->state.vtsN, link_values.data1, link_values.data2)) + link_values.command = Exit; + else + link_values = play_PGC_PG(vm, vm->state.pgN); + break; + + case JumpSS_FP: + /* Jump to First Play Domain */ + /* Only allowed from the VTS Menu Domain(VTSM) */ + /* or the Video Manager domain (VMG) */ + /* Stop SPRM9 Timer and any GPRM counters */ + assert(vm->state.domain == DVD_DOMAIN_VMGM || vm->state.domain == DVD_DOMAIN_VTSMenu); /* ?? */ + if (!set_FP_PGC(vm)) + assert(0); + link_values = play_PGC(vm); + break; + + case JumpSS_VMGM_MENU: + /* Jump to Video Manager domain - Title Menu:data1 or any PGC in VMG */ + /* Allowed from anywhere except the VTS Title domain */ + /* Stop SPRM9 Timer and any GPRM counters */ + assert(vm->state.domain != DVD_DOMAIN_VTSTitle); /* ?? */ + if(vm->vmgi == NULL || vm->vmgi->pgci_ut == NULL) { + link_values.command = Exit; + break; + } + vm->state.domain = DVD_DOMAIN_VMGM; + if(!set_MENU(vm, link_values.data1)) + assert(0); + link_values = play_PGC(vm); + break; + + case JumpSS_VTSM: + /* Jump to a menu in Video Title domain, */ + /* or to a Menu is the current VTS */ + /* Stop SPRM9 Timer and any GPRM counters */ + /* ifoOpenNewVTSI:data1 */ + /* VTS_TTN_REG:data2 */ + /* get_MENU:data3 */ + if(link_values.data1 != 0) { + assert(vm->state.domain == DVD_DOMAIN_VTSMenu || + vm->state.domain == DVD_DOMAIN_VMGM || vm->state.domain == DVD_DOMAIN_FirstPlay); /* ?? */ + if (link_values.data1 != vm->state.vtsN) { + /* the normal case */ + assert(vm->state.domain != DVD_DOMAIN_VTSMenu); + if (!ifoOpenNewVTSI(vm, vm->dvd, link_values.data1)) /* Also sets vm->state.vtsN */ + vm->vtsi = NULL; + } else { + /* This happens on some discs like "Captain Scarlet & the Mysterons" or + * the German RC2 of "Anatomie" in VTSM. */ + } + + if(vm->vtsi == NULL || vm->vtsi->pgci_ut == NULL) { + link_values.command = Exit; + break; + } + vm->state.domain = DVD_DOMAIN_VTSMenu; + } else { + /* This happens on 'The Fifth Element' region 2. */ + assert(vm->state.domain == DVD_DOMAIN_VTSMenu); + } + /* I don't know what title is supposed to be used for. */ + /* Alien or Aliens has this != 1, I think. */ + /* assert(link_values.data2 == 1); */ + vm->state.VTS_TTN_REG = link_values.data2; + /* TTN_REG (SPRM4), VTS_TTN_REG (SPRM5), TT_PGCN_REG (SPRM6) are linked, */ + /* so if one changes, the others must change to match it. */ + vm->state.TTN_REG = get_TT(vm, vm->state.vtsN, vm->state.VTS_TTN_REG); + if(!set_MENU(vm, link_values.data3)) + assert(0); + link_values = play_PGC(vm); + break; + + case JumpSS_VMGM_PGC: + /* set_PGCN:data1 */ + /* Stop SPRM9 Timer and any GPRM counters */ + assert(vm->state.domain != DVD_DOMAIN_VTSTitle); /* ?? */ + if(vm->vmgi == NULL || vm->vmgi->pgci_ut == NULL) { + link_values.command = Exit; + break; + } + vm->state.domain = DVD_DOMAIN_VMGM; + if(!set_PGCN(vm, link_values.data1)) + assert(0); + link_values = play_PGC(vm); + break; + + case CallSS_FP: + /* set_RSMinfo:data1 */ + assert(vm->state.domain == DVD_DOMAIN_VTSTitle); /* ?? */ + /* Must be called before domain is changed */ + set_RSMinfo(vm, link_values.data1, /* We dont have block info */ 0); + set_FP_PGC(vm); + link_values = play_PGC(vm); + break; + + case CallSS_VMGM_MENU: + /* set_MENU:data1 */ + /* set_RSMinfo:data2 */ + assert(vm->state.domain == DVD_DOMAIN_VTSTitle); /* ?? */ + /* Must be called before domain is changed */ + if(vm->vmgi == NULL || vm->vmgi->pgci_ut == NULL) { + link_values.command = Exit; + break; + } + set_RSMinfo(vm, link_values.data2, /* We dont have block info */ 0); + vm->state.domain = DVD_DOMAIN_VMGM; + if(!set_MENU(vm, link_values.data1)) + assert(0); + link_values = play_PGC(vm); + break; + + case CallSS_VTSM: + /* set_MENU:data1 */ + /* set_RSMinfo:data2 */ + assert(vm->state.domain == DVD_DOMAIN_VTSTitle); /* ?? */ + /* Must be called before domain is changed */ + if(vm->vtsi == NULL || vm->vtsi->pgci_ut == NULL) { + link_values.command = Exit; + break; + } + set_RSMinfo(vm, link_values.data2, /* We dont have block info */ 0); + vm->state.domain = DVD_DOMAIN_VTSMenu; + if(!set_MENU(vm, link_values.data1)) + assert(0); + link_values = play_PGC(vm); + break; + + case CallSS_VMGM_PGC: + /* set_PGC:data1 */ + /* set_RSMinfo:data2 */ + assert(vm->state.domain == DVD_DOMAIN_VTSTitle); /* ?? */ + /* Must be called before domain is changed */ + if(vm->vmgi == NULL || vm->vmgi->pgci_ut == NULL) { + link_values.command = Exit; + break; + } + set_RSMinfo(vm, link_values.data2, /* We dont have block info */ 0); + vm->state.domain = DVD_DOMAIN_VMGM; + if(!set_PGCN(vm, link_values.data1)) + assert(0); + link_values = play_PGC(vm); + break; + + case PlayThis: + /* Should never happen. */ + assert(0); + break; + } + +#ifdef TRACE + Log3(vm, "After printout starts:"); + vm_print_current_domain_state(vm); + Log3(vm, "After printout ends."); +#endif + + } + + vm->state.blockN = link_values.data1 | (link_values.data2 << 16); + return 1; +} + +//return the ifo_handle_t describing required title, used to +//identify chapters +ifo_handle_t *vm_get_title_ifo(vm_t *vm, uint32_t title) +{ + uint8_t titleset_nr; + if((title < 1) || (title > vm->vmgi->tt_srpt->nr_of_srpts)) + return NULL; + titleset_nr = vm->vmgi->tt_srpt->title[title-1].title_set_nr; + return ifoOpen(vm->dvd, titleset_nr); +} + +void vm_ifo_close(ifo_handle_t *ifo) +{ + ifoClose(ifo); +} diff -durN a/src/vm/vm.h b/src/vm/vm.h --- a/src/vm/vm.h 2020-05-03 15:01:16.798469411 +0200 +++ b/src/vm/vm.h 2020-05-03 15:01:26.138448826 +0200 @@ -174,5 +174,17 @@ int ifoOpenNewVTSI(vm_t *vm, dvd_reader_t *dvd, int vtsN); +uint32_t vm_kodi_get_vmg_category(vm_t *vm); +int vm_kodi_set_active_audio_stream(vm_t *vm, int streamId); +int vm_kodi_set_active_subtitle_stream(vm_t *vm, int streamId); +uint32_t vm_kodi_get_subtitle_count(vm_t *vm); +uint32_t vm_kodi_get_audio_stream_count(vm_t *vm); +void vm_kodi_enable_subtitle_stream(vm_t *vm, bool enable); +bool vm_kodi_get_subtitle_stream_state(vm_t *vm); +int vm_kodi_convert_audio_stream_id_to_xbmc(vm_t *vm, int id); +int vm_kodi_convert_audio_stream_id_from_xbmc(vm_t *vm, int id); +int vm_kodi_convert_subtitle_stream_id_to_xbmc(vm_t *vm, int id); +int vm_kodi_convert_subtitle_stream_id_from_xbmc(vm_t *vm, int id); + #endif /* LIBDVDNAV_VM_H */ diff -durN a/src/vm/vm.h.orig b/src/vm/vm.h.orig --- a/src/vm/vm.h.orig 1970-01-01 01:00:00.000000000 +0100 +++ b/src/vm/vm.h.orig 2020-05-03 15:01:15.766471686 +0200 @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2000, 2001 Håkan Hjort + * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net> + * + * This file is part of libdvdnav, a DVD navigation library. It is modified + * from a file originally part of the Ogle DVD player. + * + * libdvdnav is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * libdvdnav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with libdvdnav; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef LIBDVDNAV_VM_H +#define LIBDVDNAV_VM_H + +/** + * State: SPRM, GPRM, Domain, pgc, pgN, cellN, ? + */ +typedef struct { + registers_t registers; + + DVDDomain_t domain; + int vtsN; /* 0 is vmgm? */ + pgc_t *pgc; /* either this or 'int pgcN' is enough? */ + int pgcN; /* but provide pgcN for quick lookup */ + int pgN; /* is this needed? can allways fid pgN from cellN? */ + int cellN; + int32_t cell_restart; /* get cell to restart */ + int blockN; + + /* Resume info */ + int rsm_vtsN; + int rsm_blockN; /* of nav_packet */ + uint16_t rsm_regs[5]; /* system registers 4-8 */ + int rsm_pgcN; + int rsm_cellN; +} dvd_state_t; + +typedef struct vm_position_s { + int16_t button; /* Button highlighted */ + int32_t vts; /* vts number to use */ + DVDDomain_t domain; /* domain to use */ + int32_t spu_channel; /* spu channel to use */ + int32_t angle_channel; /* angle channel to use */ + int32_t audio_channel; /* audio channel to use */ + int32_t hop_channel; /* channel hopping. E.g menu button pressed */ +#if 0 + /* currently unused */ + int32_t title; /* title number */ + int32_t chapter; /* chapter number */ +#endif + int32_t cell; /* cell number */ + int32_t cell_restart; /* get cell to restart */ + int32_t cell_start; /* sector number of start of current cell in use */ + int32_t still; /* is cell still */ + int32_t block; /* block number within cell in use */ +} vm_position_t; + +typedef struct { + void *priv; + dvdnav_logger_cb logcb; + dvdnav_stream_cb streamcb; + dvd_reader_t *dvd; + dvd_reader_stream_cb dvdstreamcb; + ifo_handle_t *vmgi; + ifo_handle_t *vtsi; + dvd_state_t state; + int32_t hop_channel; + char dvd_name[50]; + char dvd_serial[15]; + int stopped; +} vm_t; + +/* magic number for seeking hops */ +#define HOP_SEEK 0x1000 + + +/* Audio stream number */ +#define AST_REG registers.SPRM[1] +/* Subpicture stream number */ +#define SPST_REG registers.SPRM[2] +/* Angle number */ +#define AGL_REG registers.SPRM[3] +/* Title Track Number */ +#define TTN_REG registers.SPRM[4] +/* VTS Title Track Number */ +#define VTS_TTN_REG registers.SPRM[5] +/* PGC Number for this Title Track */ +#define TT_PGCN_REG registers.SPRM[6] +/* Current Part of Title (PTT) number for (One_Sequential_PGC_Title) */ +#define PTTN_REG registers.SPRM[7] +/* Highlighted Button Number (btn nr 1 == value 1024) */ +#define HL_BTNN_REG registers.SPRM[8] +/* Parental Level */ +#define PTL_REG registers.SPRM[13] + +/* Initialisation & destruction */ +vm_t *vm_new_vm(void *, const dvdnav_logger_cb *); +void vm_free_vm(vm_t *vm); + +/* IFO access */ +ifo_handle_t *vm_get_vmgi(vm_t *vm); +ifo_handle_t *vm_get_vtsi(vm_t *vm); + +/* Reader Access */ +dvd_reader_t *vm_get_dvd_reader(vm_t *vm); + +/* Basic Handling */ +int vm_start(vm_t *vm); +void vm_stop(vm_t *vm); +int vm_reset(vm_t *vm, const char *dvdroot, void *priv, + dvdnav_stream_cb *stream_cb); + +/* copying and merging - useful for try-running an operation */ +vm_t *vm_new_copy(vm_t *vm); +void vm_merge(vm_t *target, vm_t *source); +void vm_free_copy(vm_t *vm); + +/* regular playback */ +void vm_position_get(vm_t *vm, vm_position_t *position); +void vm_get_next_cell(vm_t *vm); + +/* Jumping - all these return 1, if a hop has been performed */ +int vm_jump_pg(vm_t *vm, int pg); +int vm_jump_cell_block(vm_t *vm, int cell, int block); +int vm_jump_title_part(vm_t *vm, int title, int part); +int vm_jump_title_program(vm_t *vm, int title, int pgcn, int pgn); +int vm_jump_top_pg(vm_t *vm); +int vm_jump_next_pg(vm_t *vm); +int vm_jump_prev_pg(vm_t *vm); +int vm_jump_up(vm_t *vm); +int vm_jump_menu(vm_t *vm, DVDMenuID_t menuid); +int vm_jump_resume(vm_t *vm); +int vm_exec_cmd(vm_t *vm, vm_cmd_t *cmd); + +/* getting information */ +int vm_get_current_menu(vm_t *vm, int *menuid); +int vm_get_current_title_part(vm_t *vm, int *title_result, int *part_result); +int vm_get_audio_stream(vm_t *vm, int audioN); +int vm_get_subp_stream(vm_t *vm, int subpN, int mode); +int vm_get_audio_active_stream(vm_t *vm); +int vm_get_subp_active_stream(vm_t *vm, int mode); +void vm_get_angle_info(vm_t *vm, int *current, int *num_avail); +#if 0 +/* currently unused */ +void vm_get_audio_info(vm_t *vm, int *current, int *num_avail); +void vm_get_subp_info(vm_t *vm, int *current, int *num_avail); +#endif +void vm_get_video_res(vm_t *vm, int *width, int *height); +int vm_get_video_aspect(vm_t *vm); +int vm_get_video_scale_permission(vm_t *vm); +video_attr_t vm_get_video_attr(vm_t *vm); +audio_attr_t vm_get_audio_attr(vm_t *vm, int streamN); +subp_attr_t vm_get_subp_attr(vm_t *vm, int streamN); +ifo_handle_t *vm_get_title_ifo(vm_t *vm, uint32_t title); +void vm_ifo_close(ifo_handle_t *ifo); + +/* Uncomment for VM command tracing */ +/* #define TRACE */ +#ifdef TRACE +/* Debug */ +void vm_position_print(vm_t *vm, vm_position_t *position); +#endif + +int ifoOpenNewVTSI(vm_t *vm, dvd_reader_t *dvd, int vtsN); + + +#endif /* LIBDVDNAV_VM_H */
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor