diff options
author | Rich J Wareham <richwareham@users.sourceforge.net> | 2002-08-08 17:49:20 +0000 |
---|---|---|
committer | Rich J Wareham <richwareham@users.sourceforge.net> | 2002-08-08 17:49:20 +0000 |
commit | 892715d418f93897aa941bf8aaee69e74628099f (patch) | |
tree | b0954b9c584b9a79a83fcb186d40bb947e5af430 /src/input/libdvdnav | |
parent | 2f9f3c0ec0e38ca2c6cbfea9d6a4f8fe5fde78b9 (diff) | |
download | xine-lib-892715d418f93897aa941bf8aaee69e74628099f.tar.gz xine-lib-892715d418f93897aa941bf8aaee69e74628099f.tar.bz2 |
First stage of DVD plugin -> dvdnav conversion
CVS patchset: 2404
CVS date: 2002/08/08 17:49:20
Diffstat (limited to 'src/input/libdvdnav')
-rw-r--r-- | src/input/libdvdnav/Makefile.am | 30 | ||||
-rw-r--r-- | src/input/libdvdnav/decoder.c | 768 | ||||
-rw-r--r-- | src/input/libdvdnav/decoder.h | 107 | ||||
-rw-r--r-- | src/input/libdvdnav/dvd_types.h | 300 | ||||
-rw-r--r-- | src/input/libdvdnav/dvdnav.c | 1136 | ||||
-rw-r--r-- | src/input/libdvdnav/dvdnav.h | 879 | ||||
-rw-r--r-- | src/input/libdvdnav/dvdnav_events.h | 124 | ||||
-rw-r--r-- | src/input/libdvdnav/dvdnav_internal.h | 168 | ||||
-rw-r--r-- | src/input/libdvdnav/highlight.c | 344 | ||||
-rw-r--r-- | src/input/libdvdnav/navigation.c | 164 | ||||
-rw-r--r-- | src/input/libdvdnav/read_cache.c | 543 | ||||
-rw-r--r-- | src/input/libdvdnav/read_cache.h | 51 | ||||
-rw-r--r-- | src/input/libdvdnav/searching.c | 443 | ||||
-rw-r--r-- | src/input/libdvdnav/settings.c | 120 | ||||
-rw-r--r-- | src/input/libdvdnav/vm.c | 1905 | ||||
-rw-r--r-- | src/input/libdvdnav/vm.h | 154 | ||||
-rw-r--r-- | src/input/libdvdnav/vmcmd.c | 488 | ||||
-rw-r--r-- | src/input/libdvdnav/vmcmd.h | 35 |
18 files changed, 7759 insertions, 0 deletions
diff --git a/src/input/libdvdnav/Makefile.am b/src/input/libdvdnav/Makefile.am new file mode 100644 index 000000000..cf30e12e4 --- /dev/null +++ b/src/input/libdvdnav/Makefile.am @@ -0,0 +1,30 @@ +DVD_CFLAGS = -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE \ + -Ilibdvdnav/ \ + -Ilibdvdread/ + +CFLAGS = @CFLAGS@ $(DVD_CFLAGS) + +noinst_LTLIBRARIES = libdvdnav.la + +libdvdnav_la_SOURCES = decoder.c dvdnav.c vm.c vmcmd.c \ + read_cache.c navigation.c highlight.c \ + searching.c settings.c +libdvdnav_la_LDFLAGS = $(THREAD_LIBS) \ + -avoid-version -module + +noinst_HEADERS = decoder.h dvdnav.h dvdnav_events.h \ + dvdnav_internal.h vm.h vmcmd.h read_cache.h dvd_types.h + +debug: + @$(MAKE) CFLAGS="$(DEBUG_CFLAGS) $(OGG_CFLAGS) $(VORBIS_CFLAGS)" + +install-debug: debug + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +mostlyclean-generic: + -rm -f *~ \#* .*~ .\#* + +maintainer-clean-generic: + -@echo "This command is intended for maintainers to use;" + -@echo "it deletes files that may require special tools to rebuild." + -rm -f Makefile.in diff --git a/src/input/libdvdnav/decoder.c b/src/input/libdvdnav/decoder.c new file mode 100644 index 000000000..8e177b49d --- /dev/null +++ b/src/input/libdvdnav/decoder.c @@ -0,0 +1,768 @@ +/* + * Copyright (C) 2000, 2001 Martin Norbäck, Håkan Hjort + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: decoder.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $ + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <inttypes.h> +#include <string.h> /* For memset */ +#include <dvdread/ifo_types.h> /* vm_cmd_t */ +#include <assert.h> +#include "vmcmd.h" +#include "decoder.h" + +uint32_t vm_getbits(command_t *command, int start, int count) { + uint64_t result = 0; + uint64_t bit_mask=0xffffffffffffffff; /* I could put -1 instead */ + uint64_t examining = 0; + int32_t bits; + if (count == 0) return 0; + + if ( ((count+start) > 64) || + (count > 32) || + (start > 63) || + (count < 0) || + (start < 0) ){ + fprintf(stderr, "Bad call to vm_getbits. Parameter out of range\n"); + assert(0); + } + bit_mask >>= start; + bits = 64-count-start; + examining = ((bit_mask >> bits) << bits ); + command->examined |= examining; + result = (command->instruction & bit_mask) >> bits; + return (uint32_t) result; +} + +static uint16_t get_GPRM(registers_t* registers, uint8_t reg) { + if (registers->GPRM_mode[reg] & 0x01) { + struct timeval current_time, time_offset; + uint16_t result; + /* Counter mode */ + /* fprintf(stderr, "Getting counter %d\n",reg);*/ + gettimeofday(¤t_time, NULL); + time_offset.tv_sec = current_time.tv_sec - registers->GPRM_time[reg].tv_sec; + time_offset.tv_usec = current_time.tv_usec - registers->GPRM_time[reg].tv_usec; + if (time_offset.tv_usec < 0) { + time_offset.tv_sec--; + time_offset.tv_usec += 1000000; + } + result = (uint16_t) (time_offset.tv_sec & 0xffff); + registers->GPRM[reg]=result; + return result; + + } else { + /* Register mode */ + return registers->GPRM[reg]; + } + +} + +static void set_GPRM(registers_t* registers, uint8_t reg, uint16_t value) { + if (registers->GPRM_mode[reg] & 0x01) { + struct timeval current_time; + /* Counter mode */ + /* fprintf(stderr, "Setting counter %d\n",reg); */ + gettimeofday(¤t_time, NULL); + registers->GPRM_time[reg] = current_time; + registers->GPRM_time[reg].tv_sec -= value; + } + registers->GPRM[reg] = value; +} + +/* Eval register code, can either be system or general register. + SXXX_XXXX, where S is 1 if it is system register. */ +static uint16_t eval_reg(command_t* command, uint8_t reg) { + if(reg & 0x80) { + if ((reg & 0x1f) == 20) { + fprintf(stderr, "Suspected RCE Region Protection!!!"); + } + return command->registers->SPRM[reg & 0x1f]; /* FIXME max 24 not 32 */ + } else { + return get_GPRM(command->registers, reg & 0x0f) ; + } +} + +/* Eval register or immediate data. + AAAA_AAAA BBBB_BBBB, if immediate use all 16 bits for data else use + lower eight bits for the system or general purpose register. */ +static uint16_t eval_reg_or_data(command_t* command, int32_t imm, int32_t byte) { + if(imm) { /* immediate */ + return vm_getbits(command, (byte*8), 16); + } else { + return eval_reg(command, vm_getbits(command, ((byte + 1)*8), 8)); + } +} + +/* Eval register or immediate data. + xBBB_BBBB, if immediate use all 7 bits for data else use + lower four bits for the general purpose register number. */ +/* Evaluates gprm or data depending on bit, data is in byte n */ +uint16_t eval_reg_or_data_2(command_t* command, int32_t imm, int32_t byte) { + if(imm) /* immediate */ + return vm_getbits(command, ((byte*8)+1), 7); + else + return get_GPRM(command->registers, (vm_getbits(command, ((byte*8)+4), 4)) ); +} + + +/* Compare data using operation, return result from comparison. + Helper function for the different if functions. */ +static int32_t eval_compare(uint8_t operation, uint16_t data1, uint16_t data2) { + switch(operation) { + case 1: + return data1 & data2; + case 2: + return data1 == data2; + case 3: + return data1 != data2; + case 4: + return data1 >= data2; + case 5: + return data1 > data2; + case 6: + return data1 <= data2; + case 7: + return data1 < data2; + } + fprintf(stderr,"eval_compare: Invalid comparison code\n"); + return 0; +} + + +/* Evaluate if version 1. + Has comparison data in byte 3 and 4-5 (immediate or register) */ +static int32_t eval_if_version_1(command_t* command) { + uint8_t op = vm_getbits(command, 9, 3); + if(op) { + return eval_compare(op, eval_reg(command, vm_getbits(command, 24, 8)), + eval_reg_or_data(command, vm_getbits(command, 8, 1), 4)); + } + return 1; +} + +/* Evaluate if version 2. + This version only compares register which are in byte 6 and 7 */ +static int32_t eval_if_version_2(command_t* command) { + uint8_t op = vm_getbits(command, 9, 3); + if(op) { + return eval_compare(op, eval_reg(command, vm_getbits(command, 48, 8)), + eval_reg(command, vm_getbits(command, 56, 8))); + } + return 1; +} + +/* Evaluate if version 3. + Has comparison data in byte 2 and 6-7 (immediate or register) */ +static int32_t eval_if_version_3(command_t* command) { + uint8_t op = vm_getbits(command, 9, 3); + if(op) { + return eval_compare(op, eval_reg(command, vm_getbits(command, 16, 8)), + eval_reg_or_data(command, vm_getbits(command, 8, 1), 6)); + } + return 1; +} + +/* Evaluate if version 4. + Has comparison data in byte 1 and 4-5 (immediate or register) + The register in byte 1 is only the lowe nibble (4 bits) */ +static int32_t eval_if_version_4(command_t* command) { + uint8_t op = vm_getbits(command, 9, 3); + if(op) { + return eval_compare(op, eval_reg(command, vm_getbits(command, 12, 4)), + eval_reg_or_data(command, vm_getbits(command, 8, 1), 4)); + } + return 1; +} + +/* Evaluate special instruction.... returns the new row/line number, + 0 if no new row and 256 if Break. */ +static int32_t eval_special_instruction(command_t* command, int32_t cond) { + int32_t line, level; + + switch(vm_getbits(command, 12, 4)) { + case 0: /* NOP */ + line = 0; + return cond ? line : 0; + case 1: /* Goto line */ + line = vm_getbits(command, 56, 8); + return cond ? line : 0; + case 2: /* Break */ + /* max number of rows < 256, so we will end this set */ + line = 256; + return cond ? 256 : 0; + case 3: /* Set temporary parental level and goto */ + line = vm_getbits(command, 56, 8); + level = vm_getbits(command, 52, 4); + if(cond) { + /* This always succeeds now, if we want real parental protection */ + /* we need to ask the user and have passwords and stuff. */ + command->registers->SPRM[13] = level; + } + return cond ? line : 0; + } + return 0; +} + +/* Evaluate link by subinstruction. + Return 1 if link, or 0 if no link + Actual link instruction is in return_values parameter */ +static int32_t eval_link_subins(command_t* command, int32_t cond, link_t *return_values) { + uint16_t button = vm_getbits(command, 48, 6); + uint8_t linkop = vm_getbits(command, 59, 5); + + if(linkop > 0x10) + return 0; /* Unknown Link by Sub-Instruction command */ + + /* Assumes that the link_cmd_t enum has the same values as the LinkSIns codes */ + return_values->command = linkop; + return_values->data1 = button; + return cond; +} + + +/* Evaluate link instruction. + Return 1 if link, or 0 if no link + Actual link instruction is in return_values parameter */ +static int32_t eval_link_instruction(command_t* command, int32_t cond, link_t *return_values) { + uint8_t op = vm_getbits(command, 12, 4); + + switch(op) { + case 1: + return eval_link_subins(command, cond, return_values); + case 4: + return_values->command = LinkPGCN; + return_values->data1 = vm_getbits(command, 49, 15); + return cond; + case 5: + return_values->command = LinkPTTN; + return_values->data1 = vm_getbits(command, 54, 10); + return_values->data2 = vm_getbits(command, 48, 6); + return cond; + case 6: + return_values->command = LinkPGN; + return_values->data1 = vm_getbits(command, 57, 7); + return_values->data2 = vm_getbits(command, 48, 6); + return cond; + case 7: + return_values->command = LinkCN; + return_values->data1 = vm_getbits(command, 56, 8); + return_values->data2 = vm_getbits(command, 48, 6); + return cond; + } + return 0; +} + + +/* Evaluate a jump instruction. + returns 1 if jump or 0 if no jump + actual jump instruction is in return_values parameter */ +static int32_t eval_jump_instruction(command_t* command, int32_t cond, link_t *return_values) { + + switch(vm_getbits(command, 12, 4)) { + case 1: + return_values->command = Exit; + return cond; + case 2: + return_values->command = JumpTT; + return_values->data1 = vm_getbits(command, 41, 7); + return cond; + case 3: + return_values->command = JumpVTS_TT; + return_values->data1 = vm_getbits(command, 41, 7); + return cond; + case 5: + return_values->command = JumpVTS_PTT; + return_values->data1 = vm_getbits(command, 41, 7); + return_values->data2 = vm_getbits(command, 22, 10); + return cond; + case 6: + switch(vm_getbits(command, 40, 2)) { + case 0: + return_values->command = JumpSS_FP; + return cond; + case 1: + return_values->command = JumpSS_VMGM_MENU; + return_values->data1 = vm_getbits(command, 44, 4); + return cond; + case 2: + return_values->command = JumpSS_VTSM; + return_values->data1 = vm_getbits(command, 32, 8); + return_values->data2 = vm_getbits(command, 24, 8); + return_values->data3 = vm_getbits(command, 44, 4); + return cond; + case 3: + return_values->command = JumpSS_VMGM_PGC; + return_values->data1 = vm_getbits(command, 17, 15); + return cond; + } + break; + case 8: + switch(vm_getbits(command, 40, 2)) { + case 0: + return_values->command = CallSS_FP; + return_values->data1 = vm_getbits(command, 32, 8); + return cond; + case 1: + return_values->command = CallSS_VMGM_MENU; + return_values->data1 = vm_getbits(command, 44, 4); + return_values->data2 = vm_getbits(command, 32, 8); + return cond; + case 2: + return_values->command = CallSS_VTSM; + return_values->data1 = vm_getbits(command, 44, 4); + return_values->data2 = vm_getbits(command, 32, 8); + return cond; + case 3: + return_values->command = CallSS_VMGM_PGC; + return_values->data1 = vm_getbits(command, 17, 15); + return_values->data2 = vm_getbits(command, 32, 8); + return cond; + } + break; + } + return 0; +} + +/* Evaluate a set sytem register instruction + May contain a link so return the same as eval_link */ +static int32_t eval_system_set(command_t* command, int32_t cond, link_t *return_values) { + int32_t i; + uint16_t data, data2; + + switch(vm_getbits(command, 4, 4)) { + case 1: /* Set system reg 1 &| 2 &| 3 (Audio, Subp. Angle) */ + for(i = 1; i <= 3; i++) { + if(vm_getbits(command, ((2 + i)*8), 1)) { + data = eval_reg_or_data_2(command, vm_getbits(command, 3, 1), 2 + i); + if(cond) { + command->registers->SPRM[i] = data; + } + } + } + break; + case 2: /* Set system reg 9 & 10 (Navigation timer, Title PGC number) */ + data = eval_reg_or_data(command, vm_getbits(command, 3, 1), 2); + data2 = vm_getbits(command, 40, 8); /* ?? size */ + if(cond) { + command->registers->SPRM[9] = data; /* time */ + command->registers->SPRM[10] = data2; /* pgcN */ + } + break; + case 3: /* Mode: Counter / Register + Set */ + data = eval_reg_or_data(command, vm_getbits(command, 3, 1), 2); + data2 = vm_getbits(command, 44, 4); + if(vm_getbits(command, 40, 1)) { + command->registers->GPRM_mode[data2] |= 1; /* Set bit 0 */ + } else { + command->registers->GPRM_mode[data2] &= ~ 0x01; /* Reset bit 0 */ + } + if(cond) { + set_GPRM(command->registers, data2, data); + } + break; + case 6: /* Set system reg 8 (Highlighted button) */ + data = eval_reg_or_data(command, vm_getbits(command, 3, 1), 4); /* Not system reg!! */ + if(cond) { + command->registers->SPRM[8] = data; + } + break; + } + if(vm_getbits(command, 12, 4)) { + return eval_link_instruction(command, cond, return_values); + } + return 0; +} + + +/* Evaluate set operation + Sets the register given to the value indicated by op and data. + For the swap case the contents of reg is stored in reg2. +*/ +static void eval_set_op(command_t* command, int32_t op, int32_t reg, int32_t reg2, int32_t data) { + const int32_t shortmax = 0xffff; + int32_t tmp; + switch(op) { + case 1: + set_GPRM(command->registers, reg, data); + break; + case 2: /* SPECIAL CASE - SWAP! */ + set_GPRM(command->registers, reg2, get_GPRM(command->registers, reg)); + set_GPRM(command->registers, reg, data); + break; + case 3: + tmp = get_GPRM(command->registers, reg) + data; + if(tmp > shortmax) tmp = shortmax; + set_GPRM(command->registers, reg, (uint16_t)tmp); + break; + case 4: + tmp = get_GPRM(command->registers, reg) - data; + if(tmp < 0) tmp = 0; + set_GPRM(command->registers, reg, (uint16_t)tmp); + break; + case 5: + tmp = get_GPRM(command->registers, reg) * data; + if(tmp >= shortmax) tmp = shortmax; + set_GPRM(command->registers, reg, (uint16_t)tmp); + break; + case 6: + if (data != 0) { + set_GPRM(command->registers, reg, (get_GPRM(command->registers, reg) / data) ); + } else { + set_GPRM(command->registers, reg, 0); /* Avoid that divide by zero! */ + } + break; + case 7: + set_GPRM(command->registers, reg, (get_GPRM(command->registers, reg) % data) ); + break; + case 8: /* SPECIAL CASE - RND! */ + set_GPRM(command->registers, reg, ((uint16_t) ((float) data * rand()/(RAND_MAX+1.0))) ); + break; + case 9: + set_GPRM(command->registers, reg, (get_GPRM(command->registers, reg) & data) ); + break; + case 10: + set_GPRM(command->registers, reg, (get_GPRM(command->registers, reg) | data) ); + break; + case 11: + set_GPRM(command->registers, reg, (get_GPRM(command->registers, reg) ^ data) ); + break; + } +} + +/* Evaluate set instruction, combined with either Link or Compare. */ +static void eval_set_version_1(command_t* command, int32_t cond) { + uint8_t op = vm_getbits(command, 4, 4); + uint8_t reg = vm_getbits(command, 28, 4); /* FIXME: This is different from vmcmd.c!!! */ + uint8_t reg2 = vm_getbits(command, 44, 4); + uint16_t data = eval_reg_or_data(command, vm_getbits(command, 3, 1), 4); + + if(cond) { + eval_set_op(command, op, reg, reg2, data); + } +} + + +/* Evaluate set instruction, combined with both Link and Compare. */ +static void eval_set_version_2(command_t* command, int32_t cond) { + uint8_t op = vm_getbits(command, 4, 4); + uint8_t reg = vm_getbits(command, 12, 4); + uint8_t reg2 = vm_getbits(command, 28, 4); /* FIXME: This is different from vmcmd.c!!! */ + uint16_t data = eval_reg_or_data(command, vm_getbits(command, 3, 1), 2); + + if(cond) { + eval_set_op(command, op, reg, reg2, data); + } +} + + +/* Evaluate a command + returns row number of goto, 0 if no goto, -1 if link. + Link command in return_values */ +static int32_t eval_command(uint8_t *bytes, registers_t* registers, link_t *return_values) { + int32_t cond, res = 0; + command_t command; + command.instruction =( (uint64_t) bytes[0] << 56 ) | + ( (uint64_t) bytes[1] << 48 ) | + ( (uint64_t) bytes[2] << 40 ) | + ( (uint64_t) bytes[3] << 32 ) | + ( (uint64_t) bytes[4] << 24 ) | + ( (uint64_t) bytes[5] << 16 ) | + ( (uint64_t) bytes[6] << 8 ) | + (uint64_t) bytes[7] ; + command.examined = 0; + command.registers = registers; + memset(return_values, 0, sizeof(link_t)); + + switch(vm_getbits(&command, 0, 3)) { /* three first old_bits */ + case 0: /* Special instructions */ + cond = eval_if_version_1(&command); + res = eval_special_instruction(&command, cond); + if(res == -1) { + fprintf(stderr, "Unknown Instruction!\n"); + assert(0); + } + break; + case 1: /* Link/jump instructions */ + if(vm_getbits(&command, 3, 1)) { + cond = eval_if_version_2(&command); + res = eval_jump_instruction(&command, cond, return_values); + } else { + cond = eval_if_version_1(&command); + res = eval_link_instruction(&command, cond, return_values); + } + if(res) + res = -1; + break; + case 2: /* System set instructions */ + cond = eval_if_version_2(&command); + res = eval_system_set(&command, cond, return_values); + if(res) + res = -1; + break; + case 3: /* Set instructions, either Compare or Link may be used */ + cond = eval_if_version_3(&command); + eval_set_version_1(&command, cond); + if(vm_getbits(&command, 12, 4)) { + res = eval_link_instruction(&command, cond, return_values); + } + if(res) + res = -1; + break; + case 4: /* Set, Compare -> Link Sub-Instruction */ + eval_set_version_2(&command, /*True*/ 1); + cond = eval_if_version_4(&command); + res = eval_link_subins(&command, cond, return_values); + if(res) + res = -1; + break; + case 5: /* Compare -> (Set and Link Sub-Instruction) */ + cond = eval_if_version_4(&command); + eval_set_version_2(&command, cond); + res = eval_link_subins(&command, cond, return_values); + if(res) + res = -1; + break; + case 6: /* Compare -> Set, allways Link Sub-Instruction */ + cond = eval_if_version_4(&command); + eval_set_version_2(&command, cond); + res = eval_link_subins(&command, /*True*/ 1, return_values); + if(res) + res = -1; + break; + default: /* Unknown command */ + fprintf(stderr, "WARNING: Unknown Command=%x\n", vm_getbits(&command, 0, 3)); + assert(0); + } + /* Check if there are bits not yet examined */ + + if(command.instruction & ~ command.examined) { + fprintf(stderr, " libdvdnav: decoder.c: [WARNING, unknown bits:"); + fprintf(stderr, " %08llx", (command.instruction & ~ command.examined) ); + fprintf(stderr, "]"); + } + + return res; +} + +/* Evaluate a set of commands in the given register set (which is + * modified */ +int32_t vmEval_CMD(vm_cmd_t commands[], int32_t num_commands, + registers_t *registers, link_t *return_values) { + int32_t i = 0; + int32_t total = 0; + +#ifdef TRACE + /* DEBUG */ + fprintf(stderr, "libdvdnav: Registers before transaction\n"); + vmPrint_registers( registers ); + if(1) { + int32_t i; + fprintf(stderr, "libdvdnav: Full list of commands to execute\n"); + for(i = 0; i < num_commands; i++) + vmPrint_CMD(i, &commands[i]); + fprintf(stderr, "--------------------------------------------\n"); + } /* end DEBUG */ + if (1) { + fprintf(stderr, "libdvdnav: Single stepping commands\n"); + } +#endif + + while(i < num_commands && total < 100000) { + int32_t line; + +#ifdef TRACE + if(1) vmPrint_CMD(i, &commands[i]); +#endif + line = eval_command(&commands[i].bytes[0], registers, return_values); + + if (line < 0) { /* Link command */ +#ifdef TRACE + fprintf(stderr, "libdvdnav: Registers after transaction\n"); + vmPrint_registers( registers ); + fprintf(stderr, "eval: Doing Link/Jump/Call\n"); +#endif + return 1; + } + + if (line > 0) /* Goto command */ + i = line - 1; + else /* Just continue on the next line */ + i++; + + total++; + } + + memset(return_values, 0, sizeof(link_t)); +#ifdef TRACE + fprintf(stderr, "libdvdnav: Registers after transaction\n"); + vmPrint_registers( registers ); +#endif + return 0; +} + +static char *linkcmd2str(link_cmd_t cmd) { + switch(cmd) { + case LinkNoLink: + return "LinkNoLink"; + case LinkTopC: + return "LinkTopC"; + case LinkNextC: + return "LinkNextC"; + case LinkPrevC: + return "LinkPrevC"; + case LinkTopPG: + return "LinkTopPG"; + case LinkNextPG: + return "LinkNextPG"; + case LinkPrevPG: + return "LinkPrevPG"; + case LinkTopPGC: + return "LinkTopPGC"; + case LinkNextPGC: + return "LinkNextPGC"; + case LinkPrevPGC: + return "LinkPrevPGC"; + case LinkGoUpPGC: + return "LinkGoUpPGC"; + case LinkTailPGC: + return "LinkTailPGC"; + case LinkRSM: + return "LinkRSM"; + case LinkPGCN: + return "LinkPGCN"; + case LinkPTTN: + return "LinkPTTN"; + case LinkPGN: + return "LinkPGN"; + case LinkCN: + return "LinkCN"; + case Exit: + return "Exit"; + case JumpTT: + return "JumpTT"; + case JumpVTS_TT: + return "JumpVTS_TT"; + case JumpVTS_PTT: + return "JumpVTS_PTT"; + case JumpSS_FP: + return "JumpSS_FP"; + case JumpSS_VMGM_MENU: + return "JumpSS_VMGM_MENU"; + case JumpSS_VTSM: + return "JumpSS_VTSM"; + case JumpSS_VMGM_PGC: + return "JumpSS_VMGM_PGC"; + case CallSS_FP: + return "CallSS_FP"; + case CallSS_VMGM_MENU: + return "CallSS_VMGM_MENU"; + case CallSS_VTSM: + return "CallSS_VTSM"; + case CallSS_VMGM_PGC: + return "CallSS_VMGM_PGC"; + case PlayThis: + return "PlayThis"; + } + return "*** (bug)"; +} + +void vmPrint_LINK(link_t value) { + char *cmd = linkcmd2str(value.command); + + switch(value.command) { + case LinkNoLink: + case LinkTopC: + case LinkNextC: + case LinkPrevC: + case LinkTopPG: + case LinkNextPG: + case LinkPrevPG: + case LinkTopPGC: + case LinkNextPGC: + case LinkPrevPGC: + case LinkGoUpPGC: + case LinkTailPGC: + case LinkRSM: + fprintf(stderr, "%s (button %d)\n", cmd, value.data1); + break; + case LinkPGCN: + case JumpTT: + case JumpVTS_TT: + case JumpSS_VMGM_MENU: /* == 2 -> Title Menu */ + case JumpSS_VMGM_PGC: + fprintf(stderr, "%s %d\n", cmd, value.data1); + break; + case LinkPTTN: + case LinkPGN: + case LinkCN: + fprintf(stderr, "%s %d (button %d)\n", cmd, value.data1, value.data2); + break; + case Exit: + case JumpSS_FP: + case PlayThis: /* Humm.. should we have this at all.. */ + fprintf(stderr, "%s\n", cmd); + break; + case JumpVTS_PTT: + fprintf(stderr, "%s %d:%d\n", cmd, value.data1, value.data2); + break; + case JumpSS_VTSM: + fprintf(stderr, "%s vts %d title %d menu %d\n", + cmd, value.data1, value.data2, value.data3); + break; + case CallSS_FP: + fprintf(stderr, "%s resume cell %d\n", cmd, value.data1); + break; + case CallSS_VMGM_MENU: /* == 2 -> Title Menu */ + case CallSS_VTSM: + fprintf(stderr, "%s %d resume cell %d\n", cmd, value.data1, value.data2); + break; + case CallSS_VMGM_PGC: + fprintf(stderr, "%s %d resume cell %d\n", cmd, value.data1, value.data2); + break; + } + } + +void vmPrint_registers( registers_t *registers ) { + int32_t i; + fprintf(stderr, " # "); + for(i = 0; i < 24; i++) + fprintf(stderr, " %2d |", i); + fprintf(stderr, "\nSRPMS: "); + for(i = 0; i < 24; i++) + fprintf(stderr, "%04x|", registers->SPRM[i]); + fprintf(stderr, "\nGRPMS: "); + for(i = 0; i < 16; i++) + fprintf(stderr, "%04x|", get_GPRM(registers, i) ); + fprintf(stderr, "\nGmode: "); + for(i = 0; i < 16; i++) + fprintf(stderr, "%04x|", registers->GPRM_mode[i]); + fprintf(stderr, "\nGtime: "); + for(i = 0; i < 16; i++) + fprintf(stderr, "%04lx|", registers->GPRM_time[i].tv_sec & 0xffff); + fprintf(stderr, "\n"); +} diff --git a/src/input/libdvdnav/decoder.h b/src/input/libdvdnav/decoder.h new file mode 100644 index 000000000..e977ac90f --- /dev/null +++ b/src/input/libdvdnav/decoder.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2000, 2001 Martin Norbäck, Håkan Hjort + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: decoder.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $ + * + */ + +#ifndef DECODER_H_INCLUDED +#define DECODER_H_INCLUDED + +#include <inttypes.h> +#include <sys/time.h> + +#include <dvdread/ifo_types.h> /* vm_cmd_t */ + +/* Uncomment for tracing */ +/* #define TRACE */ + +typedef enum { + LinkNoLink = 0, + + LinkTopC = 1, + LinkNextC = 2, + LinkPrevC = 3, + + LinkTopPG = 5, + LinkNextPG = 6, + LinkPrevPG = 7, + + LinkTopPGC = 9, + LinkNextPGC = 10, + LinkPrevPGC = 11, + LinkGoUpPGC = 12, + LinkTailPGC = 13, + + LinkRSM = 16, + + LinkPGCN, + LinkPTTN, + LinkPGN, + LinkCN, + + Exit, + + JumpTT, /* 22 */ + JumpVTS_TT, + JumpVTS_PTT, + + JumpSS_FP, + JumpSS_VMGM_MENU, + JumpSS_VTSM, + JumpSS_VMGM_PGC, + + CallSS_FP, /* 29 */ + CallSS_VMGM_MENU, + CallSS_VTSM, + CallSS_VMGM_PGC, + + PlayThis +} link_cmd_t; + +typedef struct { + link_cmd_t command; + uint16_t data1; + uint16_t data2; + uint16_t data3; +} link_t; + +typedef struct { + uint16_t SPRM[24]; + uint16_t GPRM[16]; + uint8_t GPRM_mode[16]; /* Need to have some thing to indicate normal/counter mode for every GPRM */ + struct timeval GPRM_time[16]; /* For counter mode */ +} registers_t; + +typedef struct +{ + uint64_t instruction; + uint64_t examined; + registers_t *registers; +} command_t; + +int vmEval_CMD(vm_cmd_t commands[], int num_commands, + registers_t *registers, link_t *return_values); + +void vmPrint_LINK(link_t value); +void vmPrint_registers( registers_t *registers ); +uint32_t vm_getbits(command_t* command, int start, int count); + +#endif /* DECODER_H_INCLUDED */ diff --git a/src/input/libdvdnav/dvd_types.h b/src/input/libdvdnav/dvd_types.h new file mode 100644 index 000000000..d19bce84a --- /dev/null +++ b/src/input/libdvdnav/dvd_types.h @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2000, 2001 Björn Englund, Håkan Hjort + * + * This file is part of libdvdnav, a DVD navigation library. It is a modified + * file originally part of the Ogle DVD player project. + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: dvd_types.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $ + * + */ + +#ifndef DVD_H_INCLUDED +#define DVD_H_INCLUDED + +#include <inttypes.h> + +/** + * \file dvd_types.h + * + * Various useful structs and enums for DVDs. + */ + +/** + * DVD Domain + */ +typedef enum { + DVD_DOMAIN_FirstPlay, /**< First Play Domain */ + DVD_DOMAIN_VMG, /**< Video Manager Domain */ + DVD_DOMAIN_VTSMenu, /**< Video Title Set Menu Domain */ + DVD_DOMAIN_VTSTitle, /**< Video Title Set Domain */ + DVD_DOMAIN_Stop /**< Stop Domain */ +} DVDDomain_t; + +/** + * DVD Menu + */ +typedef enum { + DVD_MENU_Title = 2, /**< TBD */ + DVD_MENU_Root = 3, /**< TBD */ + DVD_MENU_Subpicture = 4, /**< TBD */ + DVD_MENU_Audio = 5, /**< TBD */ + DVD_MENU_Angle = 6, /**< TBD */ + DVD_MENU_Part = 7 /**< TBD */ +} DVDMenuID_t; + +/** + * User operations + */ +typedef enum { + UOP_FLAG_TitleOrTimePlay = 0x00000001, + UOP_FLAG_ChapterSearchOrPlay = 0x00000002, + UOP_FLAG_TitlePlay = 0x00000004, + UOP_FLAG_Stop = 0x00000008, + UOP_FLAG_GoUp = 0x00000010, + UOP_FLAG_TimeOrChapterSearch = 0x00000020, + UOP_FLAG_PrevOrTopPGSearch = 0x00000040, + UOP_FLAG_NextPGSearch = 0x00000080, + UOP_FLAG_ForwardScan = 0x00000100, + UOP_FLAG_BackwardScan = 0x00000200, + UOP_FLAG_TitleMenuCall = 0x00000400, + UOP_FLAG_RootMenuCall = 0x00000800, + UOP_FLAG_SubPicMenuCall = 0x00001000, + UOP_FLAG_AudioMenuCall = 0x00002000, + UOP_FLAG_AngleMenuCall = 0x00004000, + UOP_FLAG_ChapterMenuCall = 0x00008000, + UOP_FLAG_Resume = 0x00010000, + UOP_FLAG_ButtonSelectOrActivate = 0x00020000, + UOP_FLAG_StillOff = 0x00040000, + UOP_FLAG_PauseOn = 0x00080000, + UOP_FLAG_AudioStreamChange = 0x00100000, + UOP_FLAG_SubPicStreamChange = 0x00200000, + UOP_FLAG_AngleChange = 0x00400000, + UOP_FLAG_KaraokeAudioPresModeChange = 0x00800000, + UOP_FLAG_VideoPresModeChange = 0x01000000 +} DVDUOP_t; + + +/** + * Parental Level + */ +typedef enum { + DVD_PARENTAL_LEVEL_1 = 1, + DVD_PARENTAL_LEVEL_2 = 2, + DVD_PARENTAL_LEVEL_3 = 3, + DVD_PARENTAL_LEVEL_4 = 4, + DVD_PARENTAL_LEVEL_5 = 5, + DVD_PARENTAL_LEVEL_6 = 6, + DVD_PARENTAL_LEVEL_7 = 7, + DVD_PARENTAL_LEVEL_8 = 8, + DVD_PARENTAL_LEVEL_None = 15 +} DVDParentalLevel_t; + +/** + * Language ID (ISO-639 language code) + */ +typedef uint16_t DVDLangID_t; + +/** + * Country ID (ISO-3166 country code) + */ +typedef uint16_t DVDCountryID_t; + +/** + * Register + */ +typedef uint16_t DVDRegister_t; + +typedef enum { + DVDFalse = 0, + DVDTrue = 1 +} DVDBool_t; + +typedef DVDRegister_t DVDGPRMArray_t[16]; +typedef DVDRegister_t DVDSPRMArray_t[24]; + +typedef int DVDStream_t; + +/** + * Angle number (1-9 or default?) + */ +typedef int DVDAngle_t; + +typedef int DVDPTT_t; +typedef int DVDTitle_t; +typedef struct { + uint8_t Hours; + uint8_t Minutes; + uint8_t Seconds; + uint8_t Frames; +} DVDTimecode_t; + +/** + * Subpicture stream number (0-31,62,63) + */ +typedef int DVDSubpictureStream_t; + +/** + * Audio stream number (0-7, 15(none)) + */ +typedef int DVDAudioStream_t; + + +/** + * The audio application mode + */ +typedef enum { + DVD_AUDIO_APP_MODE_None = 0, /**< app mode none */ + DVD_AUDIO_APP_MODE_Karaoke = 1, /**< app mode karaoke */ + DVD_AUDIO_APP_MODE_Surround = 2, /**< app mode surround */ + DVD_AUDIO_APP_MODE_Other = 3 /**< app mode other */ +} DVDAudioAppMode_t; + +/** + * The audio format + */ +typedef enum { + DVD_AUDIO_FORMAT_AC3 = 0, /**< Dolby AC-3 */ + DVD_AUDIO_FORMAT_MPEG1 = 1, /**< MPEG-1 */ + DVD_AUDIO_FORMAT_MPEG1_DRC = 2, /**< MPEG-1 with dynamic range control */ + DVD_AUDIO_FORMAT_MPEG2 = 3, /**< MPEG-2 */ + DVD_AUDIO_FORMAT_MPEG2_DRC = 4, /**< MPEG-2 with dynamic range control */ + DVD_AUDIO_FORMAT_LPCM = 5, /**< Linear Pulse Code Modulation */ + DVD_AUDIO_FORMAT_DTS = 6, /**< Digital Theater Systems */ + DVD_AUDIO_FORMAT_SDDS = 7, /**< Sony Dynamic Digital Sound */ + DVD_AUDIO_FORMAT_Other = 8 /**< Other format*/ +} DVDAudioFormat_t; + +/** + * Audio language extension + */ +typedef enum { + DVD_AUDIO_LANG_EXT_NotSpecified = 0, /**< TBD */ + DVD_AUDIO_LANG_EXT_NormalCaptions = 1, /**< TBD */ + DVD_AUDIO_LANG_EXT_VisuallyImpaired = 2, /**< TBD */ + DVD_AUDIO_LANG_EXT_DirectorsComments1 = 3, /**< TBD */ + DVD_AUDIO_LANG_EXT_DirectorsComments2 = 4 /**< TBD */ +} DVDAudioLangExt_t; + +/** + * Subpicture language extension + */ +typedef enum { + DVD_SUBPICTURE_LANG_EXT_NotSpecified = 0, + DVD_SUBPICTURE_LANG_EXT_NormalCaptions = 1, + DVD_SUBPICTURE_LANG_EXT_BigCaptions = 2, + DVD_SUBPICTURE_LANG_EXT_ChildrensCaptions = 3, + DVD_SUBPICTURE_LANG_EXT_NormalCC = 5, + DVD_SUBPICTURE_LANG_EXT_BigCC = 6, + DVD_SUBPICTURE_LANG_EXT_ChildrensCC = 7, + DVD_SUBPICTURE_LANG_EXT_Forced = 9, + DVD_SUBPICTURE_LANG_EXT_NormalDirectorsComments = 13, + DVD_SUBPICTURE_LANG_EXT_BigDirectorsComments = 14, + DVD_SUBPICTURE_LANG_EXT_ChildrensDirectorsComments = 15, +} DVDSubpictureLangExt_t; + +/** + * Karaoke Downmix mode + */ +typedef enum { + DVD_KARAOKE_DOWNMIX_0to0 = 0x0001, + DVD_KARAOKE_DOWNMIX_1to0 = 0x0002, + DVD_KARAOKE_DOWNMIX_2to0 = 0x0004, + DVD_KARAOKE_DOWNMIX_3to0 = 0x0008, + DVD_KARAOKE_DOWNMIX_4to0 = 0x0010, + DVD_KARAOKE_DOWNMIX_Lto0 = 0x0020, + DVD_KARAOKE_DOWNMIX_Rto0 = 0x0040, + DVD_KARAOKE_DOWNMIX_0to1 = 0x0100, + DVD_KARAOKE_DOWNMIX_1to1 = 0x0200, + DVD_KARAOKE_DOWNMIX_2to1 = 0x0400, + DVD_KARAOKE_DOWNMIX_3to1 = 0x0800, + DVD_KARAOKE_DOWNMIX_4to1 = 0x1000, + DVD_KARAOKE_DOWNMIX_Lto1 = 0x2000, + DVD_KARAOKE_DOWNMIX_Rto1 = 0x4000 +} DVDKaraokeDownmix_t; + +typedef int DVDKaraokeDownmixMask_t; + +typedef enum { + DVD_DISPLAY_MODE_ContentDefault = 0, + DVD_DISPLAY_MODE_16x9 = 1, + DVD_DISPLAY_MODE_4x3PanScan = 2, + DVD_DISPLAY_MODE_4x3Letterboxed = 3 +} DVDDisplayMode_t; + +typedef int DVDAudioSampleFreq_t; /**< TBD */ +typedef int DVDAudioSampleQuant_t; /**< TBD */ +typedef int DVDChannelNumber_t; /**< TBD */ + + +typedef struct { + DVDAudioAppMode_t AppMode; + DVDAudioFormat_t AudioFormat; + DVDLangID_t Language; + DVDAudioLangExt_t LanguageExtension; + DVDBool_t HasMultichannelInfo; + DVDAudioSampleFreq_t SampleFrequency; + DVDAudioSampleQuant_t SampleQuantization; + DVDChannelNumber_t NumberOfChannels; +} DVDAudioAttributes_t; + +typedef enum { + DVD_SUBPICTURE_TYPE_NotSpecified = 0, + DVD_SUBPICTURE_TYPE_Language = 1, + DVD_SUBPICTURE_TYPE_Other = 2 +} DVDSubpictureType_t; + +typedef enum { + DVD_SUBPICTURE_CODING_RunLength = 0, + DVD_SUBPICTURE_CODING_Extended = 1, + DVD_SUBPICTURE_CODING_Other = 2 +} DVDSubpictureCoding_t; + +typedef struct { + DVDSubpictureType_t Type; + DVDSubpictureCoding_t CodingMode; + DVDLangID_t Language; + DVDSubpictureLangExt_t LanguageExtension; +} DVDSubpictureAttributes_t; + +typedef int DVDVideoCompression_t; /**< TBD */ + +typedef struct { + DVDBool_t PanscanPermitted; + DVDBool_t LetterboxPermitted; + int AspectX; + int AspectY; + int FrameRate; + int FrameHeight; + DVDVideoCompression_t Compression; + DVDBool_t Line21Field1InGop; + DVDBool_t Line21Field2InGop; + int more_to_come; +} DVDVideoAttributes_t; + +/** + * Atructure containing info on highlight areas. + */ +typedef struct { + uint32_t palette; /*!< The CLUT entries for the highlight palette + (4-bits per entry -> 4 entries) */ + uint16_t sx,sy,ex,ey; /*!< The start/end x,y positions */ + uint32_t pts; /*!< Highlight PTS to match with SPU */ + uint32_t buttonN; /*!< Button number for the SPU decoder. */ +} dvdnav_highlight_area_t; + + +#endif /* DVD_H_INCLUDED */ diff --git a/src/input/libdvdnav/dvdnav.c b/src/input/libdvdnav/dvdnav.c new file mode 100644 index 000000000..63282db69 --- /dev/null +++ b/src/input/libdvdnav/dvdnav.c @@ -0,0 +1,1136 @@ +/* + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: dvdnav.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $ + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* +#define LOG_DEBUG +*/ + +#include <pthread.h> +#include <dvdnav.h> +#include "dvdnav_internal.h" +#include "read_cache.h" + +#include <dvdread/nav_read.h> + +#include <stdlib.h> +#include <stdio.h> + +/* + * NOTE: + * All NLCK_*() function are not mutex locked, this made them reusable in + * a locked context. Take care. + * + */ + +/* Current domain (backend to dvdnav_is_domain_() funcs) */ +static int8_t NLCK_dvdnav_is_domain(dvdnav_t *this, domain_t domain) { + dvd_state_t *state; + + if((!this) || (!this->started) || (!this->vm)) + return -1; + + state = &(this->vm->state); + + if(!state) + return -1; + + return (state->domain == domain) ? 1 : 0; +} + +static int8_t _dvdnav_is_domain(dvdnav_t *this, domain_t domain) { + int8_t retval; + + pthread_mutex_lock(&this->vm_lock); + retval = NLCK_dvdnav_is_domain(this, domain); + pthread_mutex_unlock(&this->vm_lock); + + return retval; +} + +static int8_t NCLK_dvdnav_get_audio_logical_stream(dvdnav_t *this, uint8_t audio_num) { + dvd_state_t *state; + int8_t logical = -1; + + if(!NLCK_dvdnav_is_domain(this, VTS_DOMAIN)) + audio_num = 0; + + state = &(this->vm->state); + + if(audio_num < 8) { + if(state->pgc->audio_control[audio_num] & (1 << 15)) { + logical = (state->pgc->audio_control[audio_num] >> 8) & 0x07; + } + } + + return logical; +} + +static int8_t NCLK_dvdnav_get_spu_logical_stream(dvdnav_t *this, uint8_t subp_num) { + dvd_state_t *state; + ifo_handle_t *vtsi; + + if(!this) + return -1; + + state = &(this->vm->state); + vtsi = this->vm->vtsi; + + if(subp_num >= vtsi->vtsi_mat->nr_of_vts_subp_streams) + return -1; + + return vm_get_subp_stream(this->vm, subp_num, 0); +} + +static int8_t NLCK_dvdnav_get_active_spu_stream(dvdnav_t *this) { + dvd_state_t *state; + int8_t subp_num; + int stream_num; + + state = &(this->vm->state); + subp_num = state->SPST_REG & ~0x40; + stream_num = NCLK_dvdnav_get_spu_logical_stream(this, subp_num); + + if(stream_num == -1) + for(subp_num = 0; subp_num < 32; subp_num++) + if(state->pgc->subp_control[subp_num] & (1 << 31)) { + stream_num = NCLK_dvdnav_get_spu_logical_stream(this, subp_num); + break; + } + + return stream_num; +} + +uint8_t dvdnav_get_video_aspect(dvdnav_t *this) { + uint8_t retval; + + pthread_mutex_lock(&this->vm_lock); + retval = (uint8_t) vm_get_video_aspect(this->vm); + pthread_mutex_unlock(&this->vm_lock); + + return retval; +} + +uint8_t dvdnav_get_video_scale_permission(dvdnav_t *this) { + uint8_t retval; + + pthread_mutex_lock(&this->vm_lock); + retval = (uint8_t) vm_get_video_scale_permission(this->vm); + pthread_mutex_unlock(&this->vm_lock); + + return retval; +} + +dvdnav_status_t dvdnav_clear(dvdnav_t * this) { + if (!this) { + printerr("Passed a NULL pointer"); + return S_ERR; + } + /* clear everything except path, file, vm, mutex, readahead */ + + /* path */ + if (this->file) DVDCloseFile(this->file); + this->file = NULL; + this->open_vtsN = -1; + this->open_domain = -1; + + memset(&this->pci,0,sizeof(this->pci)); + memset(&this->dsi,0,sizeof(this->dsi)); + + /* Set initial values of flags */ + this->position_current.still = 0; + this->skip_still = 0; + this->stop = 0; + this->spu_clut_changed = 0; + this->started=0; + /* this->use_read_ahead */ + + dvdnav_read_cache_clear(this->cache); + + return S_OK; +} + +dvdnav_status_t dvdnav_open(dvdnav_t** dest, char *path) { + dvdnav_t *this; + + /* Create a new structure */ + (*dest) = NULL; + this = (dvdnav_t*)malloc(sizeof(dvdnav_t)); + if(!this) + return S_ERR; + memset(this, 0, (sizeof(dvdnav_t) ) ); /* Make sure this structure is clean */ + + pthread_mutex_init(&this->vm_lock, NULL); + /* Initialise the error string */ + printerr(""); + + /* Initialise the VM */ + this->vm = vm_new_vm(); + if(!this->vm) { + printerr("Error initialising the DVD VM"); + return S_ERR; + } + if(vm_reset(this->vm, path) == -1) { + printerr("Error starting the VM / opening the DVD device"); + return S_ERR; + } + + /* Set the path. FIXME: Is a deep copy 'right' */ + strncpy(this->path, path, MAX_PATH_LEN); + + dvdnav_clear(this); + + /* 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); + if (this->file) DVDCloseFile(this->file); + this->file = NULL; + + //if(!this->started) { + // /* Start the VM */ + // vm_start(this->vm); + // this->started = 1; + //} + + /* Start the read-ahead cache. */ + this->cache = dvdnav_read_cache_new(this); + + (*dest) = this; + return S_OK; +} + +dvdnav_status_t dvdnav_close(dvdnav_t *this) { + if(!this) { + printerr("Passed a NULL pointer"); + return S_ERR; + } +#ifdef LOG_DEBUG + fprintf(stderr,"dvdnav:close:called\n"); +#endif + + if (this->file) { + DVDCloseFile(this->file); +#ifdef LOG_DEBUG + fprintf(stderr,"dvdnav:close:file closing\n"); +#endif + this->file = NULL; + } + + /* Free the VM */ + if(this->vm) { + vm_free_vm(this->vm); + } + if (this->file) { + DVDCloseFile(this->file); +#ifdef LOG_DEBUG + fprintf(stderr,"dvdnav:close2:file closing\n"); +#endif + this->file = NULL; + } + pthread_mutex_destroy(&this->vm_lock); + + /* 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 S_OK; +} + +dvdnav_status_t dvdnav_reset(dvdnav_t *this) { + dvdnav_status_t result; + +#ifdef LOG_DEBUG + printf("dvdnav:reset:called\n"); +#endif + if(!this) { + printerr("Passed a NULL pointer"); + return S_ERR; + } +#ifdef LOG_DEBUG + printf("getting lock\n"); +#endif + pthread_mutex_lock(&this->vm_lock); +#ifdef LOG_DEBUG + printf("reseting vm\n"); +#endif + if(vm_reset(this->vm, NULL) == -1) { + printerr("Error restarting the VM"); + pthread_mutex_unlock(&this->vm_lock); + return S_ERR; + } +#ifdef LOG_DEBUG + printf("clearing dvdnav\n"); +#endif + result=dvdnav_clear(this); +#ifdef LOG_DEBUG + printf("starting vm\n"); +#endif +// if(!this->started) { +// /* Start the VM */ +// vm_start(this->vm); +// this->started = 1; +// } +#ifdef LOG_DEBUG + printf("unlocking\n"); +#endif + pthread_mutex_unlock(&this->vm_lock); + return result; +} + +dvdnav_status_t dvdnav_path(dvdnav_t *this, char** path) { + if(!this || !path || !(*path)) { + return S_ERR; + } + + /* FIXME: Is shallow copy 'right'? */ + (*path) = this->path; + + return S_OK; +} + +char* dvdnav_err_to_string(dvdnav_t *this) { + if(!this) { + /* Shold this be "passed a NULL pointer?" */ + return "Hey! You gave me a NULL pointer you naughty person!"; + } + + return this->err_str; +} + +/** + * Returns 1 if block contains NAV packet, 0 otherwise. + * Precesses 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. + */ +int dvdnav_decode_packet(dvdnav_t *this, uint8_t *p, dsi_t* nav_dsi, pci_t* nav_pci) { + int bMpeg1=0; + uint32_t nHeaderLen; + uint32_t nPacketLen; + uint32_t nStreamID; +/* uint8_t *p_start=p; */ + + + if (p==NULL) { + fprintf(stderr,"Passed a NULL pointer.\n"); + return 0; + } + + /* dprint("Checking packet...\n"); */ + + if (p[3] == 0xBA) { /* program stream pack header */ + + int nStuffingBytes; + + /* xprintf (VERBOSE|DEMUX, "program stream pack header\n"); */ + + 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 */ + int nHeaderLen; + + 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)) { + fprintf(stderr,"demux error! %02x %02x %02x (should be 0x000001) \n",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 */ +/* + * int i; + * printf("dvdnav:nav packet=%u\n",p-p_start-6); + * for(i=0;i<80;i++) { + * printf("%02x ",p[i-6]); + * } + * printf("\n"); + */ + 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; + /* dprint("NAV DSI packet\n"); */ + 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 + */ +int dvdnav_get_vobu(dvdnav_t *self, dsi_t* nav_dsi, pci_t* nav_pci, dvdnav_vobu_t* vobu) { + uint32_t next; + int 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 + */ + + /* Relative offset from vobu_start */ + vobu->vobu_next = ( nav_dsi->vobu_sri.next_vobu & 0x3fffffff ); + + /* Old code -- may still be sueful 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; + } */ + + dvdnav_get_angle_info(self, &angle, &num_angle); +#if 0 + /* FIMXE: The angle reset doesn't work for some reason for the moment */ + + if((num_angle < angle) && (angle != 1)) { + printf("OOOOOOO angle ends!\n"); + + /* This is to switch back to angle one when we + * finish with angles. */ + dvdnav_angle_change(self, 1); + } +#endif + + if(num_angle != 0) { + next = nav_pci->nsml_agli.nsml_agl_dsta[angle-1]; + + if(next != 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( nav_dsi->sml_agli.data[angle-1].address != 0 ) { + next = nav_dsi->sml_agli.data[angle-1].address; + 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; +} + +/* This is the main get_next_block function which actually gets the media stream video and audio etc. + * The use of this function is optional, with the application programmer + * free to implement their own version of this function + * FIXME: Make the function calls from here public API calls. + */ + +dvdnav_status_t dvdnav_get_next_block(dvdnav_t *this, unsigned char *buf, + int *event, int *len) { + unsigned char *block; + dvdnav_status_t status; + + block = buf; + status = dvdnav_get_next_cache_block(this, &block, event, len); + if (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; +} + +dvdnav_status_t dvdnav_get_next_cache_block(dvdnav_t *this, unsigned char **buf, + int *event, int *len) { + dvd_state_t *state; + int result; + if(!this || !event || !len || !buf || !*buf) { + printerr("Passed a NULL pointer"); + return S_ERR; + } + pthread_mutex_lock(&this->vm_lock); + + if(!this->started) { + /* Start the VM */ + vm_start(this->vm); + this->started = 1; + } + + state = &(this->vm->state); + (*event) = DVDNAV_NOP; + (*len) = 0; + + /* Check the STOP flag */ + if(this->stop) { + (*event) = DVDNAV_STOP; + pthread_mutex_unlock(&this->vm_lock); + return S_OK; + } + + /* Check the STILLFRAME flag */ + /* FIXME: Still cell, not still frame */ + if(this->position_current.still != 0) { + dvdnav_still_event_t still_event; + + still_event.length = this->position_current.still; + + (*event) = DVDNAV_STILL_FRAME; + (*len) = sizeof(dvdnav_still_event_t); + memcpy(*buf, &(still_event), sizeof(dvdnav_still_event_t)); + + pthread_mutex_unlock(&this->vm_lock); + return S_OK; + } + + vm_position_get(this->vm,&this->position_next); + /********** + fprintf(stderr, "POS-NEXT "); + vm_position_print(this->vm, &this->position_next); + fprintf(stderr, "POS-CUR "); + vm_position_print(this->vm, &this->position_current); + **********/ + + if(this->position_current.hop_channel != this->position_next.hop_channel) { + this->position_current.hop_channel = this->position_next.hop_channel; + (*event) = DVDNAV_HOP_CHANNEL; + (*len) = 0; + pthread_mutex_unlock(&this->vm_lock); + return S_OK; + } + + + + if(this->spu_clut_changed) { + (*event) = DVDNAV_SPU_CLUT_CHANGE; +#ifdef LOG_DEBUG + fprintf(stderr,"libdvdnav:SPU_CLUT_CHANGE\n"); +#endif + (*len) = sizeof(dvdnav_still_event_t); + memcpy(*buf, &(state->pgc->palette), 16 * sizeof(uint32_t)); + this->spu_clut_changed = 0; +#ifdef LOG_DEBUG + fprintf(stderr,"libdvdnav:SPU_CLUT_CHANGE returning S_OK\n"); +#endif + pthread_mutex_unlock(&this->vm_lock); + return S_OK; + } + + if(this->position_current.spu_channel != this->position_next.spu_channel) { + dvdnav_spu_stream_change_event_t stream_change; + (*event) = DVDNAV_SPU_STREAM_CHANGE; +#ifdef LOG_DEBUG + fprintf(stderr,"libdvdnav:SPU_STREAM_CHANGE\n"); +#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); + memcpy(*buf, &(stream_change), sizeof( dvdnav_spu_stream_change_event_t)); + this->position_current.spu_channel = this->position_next.spu_channel; +#ifdef LOG_DEBUG + fprintf(stderr,"libdvdnav:SPU_STREAM_CHANGE stream_id_wide=%d\n",stream_change.physical_wide); + fprintf(stderr,"libdvdnav:SPU_STREAM_CHANGE stream_id_letterbox=%d\n",stream_change.physical_letterbox); + fprintf(stderr,"libdvdnav:SPU_STREAM_CHANGE stream_id_pan_scan=%d\n",stream_change.physical_pan_scan); +#endif + pthread_mutex_unlock(&this->vm_lock); + if (stream_change.physical_wide != -1 && + stream_change.physical_letterbox != -1 && + stream_change.physical_pan_scan != -1) { +#ifdef LOG_DEBUG + fprintf(stderr,"libdvdnav:SPU_STREAM_CHANGE returning S_OK\n"); +#endif + return S_OK; + } + } + + if(this->position_current.audio_channel != this->position_next.audio_channel) { + dvdnav_audio_stream_change_event_t stream_change; + (*event) = DVDNAV_AUDIO_STREAM_CHANGE; +#ifdef LOG_DEBUG + fprintf(stderr,"libdvdnav:AUDIO_STREAM_CHANGE\n"); +#endif + (*len) = sizeof(dvdnav_audio_stream_change_event_t); + stream_change.physical= vm_get_audio_active_stream( this->vm ); + memcpy(*buf, &(stream_change), sizeof( dvdnav_audio_stream_change_event_t)); + this->position_current.audio_channel = this->position_next.audio_channel; +#ifdef LOG_DEBUG + fprintf(stderr,"libdvdnav:AUDIO_STREAM_CHANGE stream_id=%d returning S_OK\n",stream_change.physical); +#endif + pthread_mutex_unlock(&this->vm_lock); + return S_OK; + } + + /* Check the HIGHLIGHT flag */ + /* FIXME: Use BUTTON instead of HIGHLIGHT. */ + if(this->position_current.button != this->position_next.button) { + dvdnav_highlight_event_t hevent; + + hevent.display = 1; + hevent.buttonN = this->position_next.button; + + this->position_current.button = this->position_next.button; + + (*event) = DVDNAV_HIGHLIGHT; + (*len) = sizeof(hevent); + memcpy(*buf, &(hevent), sizeof(hevent)); + pthread_mutex_unlock(&this->vm_lock); + return S_OK; + } + + /* Check to see if we need to change the currently opened VOB */ + if((this->position_current.vts != this->position_next.vts) || + (this->position_current.domain != this->position_next.domain)) { + dvd_read_domain_t domain; + int vtsN; + dvdnav_vts_change_event_t vts_event; + + if(this->file) { + dvdnav_read_cache_clear(this->cache); + DVDCloseFile(this->file); + this->file = NULL; + } + + vts_event.old_vtsN = this->open_vtsN; + vts_event.old_domain = this->open_domain; + + /* Use the current DOMAIN to find whether to open menu or title VOBs */ + switch(this->position_next.domain) { + case FP_DOMAIN: + case VMGM_DOMAIN: + domain = DVD_READ_MENU_VOBS; + vtsN = 0; + break; + case VTSM_DOMAIN: + domain = DVD_READ_MENU_VOBS; + vtsN = this->position_next.vts; + break; + case VTS_DOMAIN: + 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 S_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 S_ERR; + } + + /* File opened successfully so return a VTS change event */ + (*event) = DVDNAV_VTS_CHANGE; + memcpy(*buf, &(vts_event), sizeof(vts_event)); + (*len) = sizeof(vts_event); + + /* On a VTS change, we want to disable any highlights which + * may have been shown (FIXME: is this valid?) */ + 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 S_OK; + } + /* FIXME: Don't really need "cell", we only need vobu_start */ + if( (this->position_current.cell != this->position_next.cell) || + (this->position_current.cell_restart != this->position_next.cell_restart) || + (this->position_current.vobu_start != this->position_next.vobu_start) || + (this->position_current.vobu_next != this->position_next.vobu_next) ) { + this->position_current.cell = this->position_next.cell; + this->position_current.cell_restart = this->position_next.cell_restart; + /* vobu_start changes when PGC or PG changes. */ + this->position_current.vobu_start = this->position_next.vobu_start; + this->position_current.vobu_next = this->position_next.vobu_next; + /* FIXME: Need to set vobu_start, vobu_next */ + this->vobu.vobu_start = this->position_next.vobu_start; + /* vobu_next is use for mid cell resumes */ + this->vobu.vobu_next = this->position_next.vobu_next; + this->vobu.vobu_length = 0; + this->vobu.blockN = this->vobu.vobu_length + 1; + /* Make blockN > vobu_lenght to do expected_nav */ + (*event) = DVDNAV_CELL_CHANGE; + (*len) = 0; + pthread_mutex_unlock(&this->vm_lock); + return S_OK; + } + + + if (this->vobu.blockN > this->vobu.vobu_length) { + /* End of VOBU */ + + if(this->vobu.vobu_next == SRI_END_OF_CELL) { + /* End of Cell from NAV DSI info */ +#ifdef LOG_DEBUG + fprintf(stderr, "Still set to %x\n", this->position_next.still); +#endif + this->position_current.still = this->position_next.still; + + if( this->position_current.still == 0 || this->skip_still ) { + vm_get_next_cell(this->vm); + vm_position_get(this->vm,&this->position_next); + /* FIXME: Need to set vobu_start, vobu_next */ + this->position_current.still = 0; /* still gets activated at end of cell */ + this->skip_still = 0; + this->position_current.cell = this->position_next.cell; + this->position_current.vobu_start = this->position_next.vobu_start; + this->position_current.vobu_next = this->position_next.vobu_next; + this->vobu.vobu_start = this->position_next.vobu_start; + /* vobu_next is use for mid cell resumes */ + this->vobu.vobu_next = this->position_next.vobu_next; + this->vobu.vobu_length = 0; + this->vobu.blockN = this->vobu.vobu_length + 1; + /* Make blockN > vobu_next to do expected_nav */ + (*event) = DVDNAV_CELL_CHANGE; + (*len) = 0; + pthread_mutex_unlock(&this->vm_lock); + return S_OK; + } else { + dvdnav_still_event_t still_event; + still_event.length = this->position_current.still; + (*event) = DVDNAV_STILL_FRAME; + (*len) = sizeof(dvdnav_still_event_t); + memcpy(*buf, &(still_event), sizeof(dvdnav_still_event_t)); + pthread_mutex_unlock(&this->vm_lock); + return S_OK; + } + + /* Only set still after whole VOBU has been output. */ + /* + if(this->position_next.still != 0) { + this->position_current.still = this->position_next.still; + } + */ + + } + /* Perform the jump if necessary (this is always a + * VOBU boundary). */ + + //result = DVDReadBlocks(this->file, this->vobu.vobu_start + this->vobu.vobu_next, 1, buf); + 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 S_ERR; + } + /* Decode nav into pci and dsi. */ + /* Then get next VOBU info. */ + if(dvdnav_decode_packet(this, *buf, &this->dsi, &this->pci) == 0) { + printerr("Expected NAV packet but none found."); + pthread_mutex_unlock(&this->vm_lock); + return S_ERR; + } + dvdnav_get_vobu(this, &this->dsi,&this->pci, &this->vobu); + this->vobu.blockN=1; + /* FIXME: 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 should be implemented with a new Public API call. + */ + /* We cache one past the end of the VOBU, + * in the hope it might catch the next NAV packet as well. + * This reduces the amount of read commands sent to the DVD device. + * A cache miss will only happen for 3 reasons. + * 1) Seeking + * 2) Menu change + * 3) The next VOBU does not immeadiately follow the current one. E.g. Multi Angles, ILVU. + */ + dvdnav_pre_cache_blocks(this->cache, this->vobu.vobu_start+1, this->vobu.vobu_length+1); + + /* Successfully got a NAV packet */ + (*event) = DVDNAV_NAV_PACKET; + (*len) = 2048; + pthread_mutex_unlock(&this->vm_lock); + return S_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 S_ERR; + } + + 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 S_ERR; + } + this->vobu.blockN++; + (*len) = 2048; + (*event) = DVDNAV_BLOCK_OK; + + pthread_mutex_unlock(&this->vm_lock); + return S_OK; +} + +uint16_t dvdnav_audio_stream_to_lang(dvdnav_t *this, uint8_t stream) { + audio_attr_t attr; + + if(!this) + 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; +} + +int8_t dvdnav_get_audio_logical_stream(dvdnav_t *this, uint8_t audio_num) { + int8_t retval; + + if(!this) + return -1; + + pthread_mutex_lock(&this->vm_lock); + retval = NCLK_dvdnav_get_audio_logical_stream(this, audio_num); + pthread_mutex_unlock(&this->vm_lock); + + return retval; +} + +uint16_t dvdnav_spu_stream_to_lang(dvdnav_t *this, uint8_t stream) { + subp_attr_t attr; + + if(!this) + 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_spu_logical_stream(dvdnav_t *this, uint8_t subp_num) { + int8_t retval; + + if(!this) + return -1; + + pthread_mutex_lock(&this->vm_lock); + retval = NCLK_dvdnav_get_spu_logical_stream(this, subp_num); + pthread_mutex_unlock(&this->vm_lock); + + return retval; +} + +int8_t dvdnav_get_active_spu_stream(dvdnav_t *this) { + int8_t retval; + + if(!this) + return -1; + + pthread_mutex_lock(&this->vm_lock); + retval = NLCK_dvdnav_get_active_spu_stream(this); + 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, FP_DOMAIN); +} +/* Video management Menu domain. (Menu) */ +int8_t dvdnav_is_domain_vmgm(dvdnav_t *this) { + return _dvdnav_is_domain(this, VMGM_DOMAIN); +} +/* Video Title Menu domain (Menu) */ +int8_t dvdnav_is_domain_vtsm(dvdnav_t *this) { + return _dvdnav_is_domain(this, VTSM_DOMAIN); +} +/* Video Title domain (playing movie). */ +int8_t dvdnav_is_domain_vts(dvdnav_t *this) { + return _dvdnav_is_domain(this, VTS_DOMAIN); +} + +/* Generally delegate angle information handling to + * VM */ +dvdnav_status_t dvdnav_angle_change(dvdnav_t *this, int angle) { + int num, current; + + if(!this) { + return S_ERR; + } + + if(dvdnav_get_angle_info(this, ¤t, &num) != S_OK) { + printerr("Error getting angle info"); + return S_ERR; + } + + /* Set angle SPRM if valid */ + if((angle > 0) && (angle <= num)) { + this->vm->state.AGL_REG = angle; + } else { + printerr("Passed an invalid angle number"); + return S_ERR; + } + + return S_OK; +} +/* FIXME: change order of current_angle, number_of_angles */ +dvdnav_status_t dvdnav_get_angle_info(dvdnav_t *this, int* current_angle, + int *number_of_angles) { + if(!this || !this->vm) { + return S_ERR; + } + + if(!current_angle || !number_of_angles) { + printerr("Passed a NULL pointer"); + return S_ERR; + } + + vm_get_angle_info(this->vm, number_of_angles, current_angle); + + return S_OK; +} + +dvdnav_status_t dvdnav_get_cell_info(dvdnav_t *this, int* current_angle, + int *number_of_angles) { + if(!this || !this->vm) { + return S_ERR; + } + *current_angle=this->position_next.cell; + return S_OK; +} + +pci_t* dvdnav_get_current_nav_pci(dvdnav_t *this) { + if(!this || !this->vm) return 0; + return &this->pci; +} + +dsi_t* dvdnav_get_current_nav_dsi(dvdnav_t *this) { + if(!this || !this->vm) return 0; + return &this->dsi; +} + +uint32_t dvdnav_get_next_still_flag(dvdnav_t *this) { + if(!this || !this->vm) { + return S_ERR; + } + return this->position_next.still; +} + +/* + * $Log: dvdnav.c,v $ + * Revision 1.1 2002/08/08 17:49:21 richwareham + * First stage of DVD plugin -> dvdnav conversion + * + * Revision 1.29 2002/07/25 14:51:40 richwareham + * Moved get_current_nav_pci into dvdnac.c, changed example to use it instead of 'home-rolled' + * check_packet. + * + * Revision 1.28 2002/07/25 14:42:33 richwareham + * Patch from aschultz@cs.uni-magdeburg.de to allow for still-frame 'peek-ahead' + * + * Revision 1.27 2002/07/12 15:46:44 mroi + * use new memcopy'less read ahead cache + * + * Revision 1.26 2002/07/06 16:24:54 mroi + * * fix debug messages + * * send spu stream change event only, when there are new streams + * (should fix problems with Terminator disk 2) + * + * Revision 1.25 2002/07/05 14:18:54 mroi + * report all spu types (widescreen, letterbox and pan&scan), not widescreen + * only and report the stream's scale permissions to detect pan&scan material + * + * Revision 1.24 2002/07/05 01:42:30 jcdutton + * Add more debug info for Menu language selection. + * Only do vm_start when we have to. + * + * Revision 1.23 2002/07/02 22:57:09 jcdutton + * Rename some of the functions in vm.c to help readability. + * Hopefully fix __FUNCTION__ problem. Use __func_ as recommended in C99. + * Fix bug where libdvdnav would not immeadiately replay the same cell due to menu buttons. + * + * Revision 1.22 2002/06/25 13:37:11 jcdutton + * Revert back to old read_cache method. + * Some new optimizations added to the old read_cache method, thus reducing the amount of calls to read blocks from the DVD device. + * + * Revision 1.21 2002/06/06 15:03:09 richwareham + * Biiiiiiig change to let doxygen generate some docs for the library. Note that I'm in no way sure that the autoconf stuff plays nice. + * + * Revision 1.20 2002/06/04 13:35:16 richwareham + * Removed more C++ style comments + * + * Revision 1.19 2002/05/30 23:15:14 richwareham + * First draft of removing HAVE_DVDREAD9 + * + * Revision 1.18 2002/05/30 09:52:29 richwareham + * 'Objectified' the read-ahead cache in preparation to implement a 'proper' threaded cache a-la that recommended in the DVD Demystified book. + * + * Revision 1.17 2002/05/09 11:57:24 richwareham + * Angles now work (still a few wrinkles though -- e.g. angle does not reset to '1' when returning to menus) + * + * Revision 1.16 2002/04/24 21:15:25 jcdutton + * Quiet please!!! + * + * Revision 1.15 2002/04/24 00:47:46 jcdutton + * Some more cleanups. + * Improve button passing. + * + * Revision 1.14 2002/04/23 13:26:08 jcdutton + * Add some comments, FIXMEs. + * The main point being that dvdnav_get_next_block is almost in a state where it can be optional whether the application programmer uses it, or implements their own version of the function. That is been the main reason for the re-write of this function recently. + * + * Revision 1.13 2002/04/23 12:55:40 jcdutton + * Removed un-needed variables. + * General Clean up. + * + * Revision 1.12 2002/04/23 12:34:39 f1rmb + * Why rewrite vm function, use it instead (this remark is for me, of course ;-) ). + * Comment unused var, shut compiler warnings. + * + * Revision 1.11 2002/04/23 02:12:27 jcdutton + * Re-implemented seeking. + * + * Revision 1.10 2002/04/23 00:07:16 jcdutton + * Name stills work better. + * + * Revision 1.9 2002/04/22 22:00:48 jcdutton + * Start of rewrite of libdvdnav. Still need to re-implement seeking. + * + * Revision 1.8 2002/04/22 20:57:14 f1rmb + * Change/fix SPU active stream id. Same for audio. Few new functions, largely + * inspired from libogle ;-). + * + * Revision 1.7 2002/04/10 16:45:57 jcdutton + * Actually fix the const this time! + * + * Revision 1.6 2002/04/07 14:10:11 richwareham + * Stop C++ bitching about some things and extend the menus example + * + * Revision 1.5 2002/04/06 18:42:05 jcdutton + * Slight correction to handle quicker menu transitions. + * + * Revision 1.4 2002/04/06 18:31:50 jcdutton + * Some cleaning up. + * changed exit(1) to assert(0) so they actually get seen by the user so that it helps developers more. + * + * Revision 1.3 2002/04/02 18:22:27 richwareham + * Added reset patch from Kees Cook <kees@outflux.net> + * + * Revision 1.2 2002/04/01 18:56:28 richwareham + * Added initial example programs directory and make sure all debug/error output goes to stderr. + * + * Revision 1.1.1.1 2002/03/12 19:45:57 richwareham + * Initial import + * + * Revision 1.28 2002/02/02 23:26:20 richwareham + * Restored title selection + * + * Revision 1.27 2002/02/01 15:48:10 richwareham + * Re-implemented angle selection and title/chapter display + * + * Revision 1.26 2002/01/31 16:53:49 richwareham + * Big patch from Daniel Caujolle-Bert to (re)implement SPU/Audio language display + * + * Revision 1.25 2002/01/24 20:53:50 richwareham + * Added option to _not_ use DVD read-ahead to options + * + * Revision 1.24 2002/01/20 15:54:59 jcdutton + * Implement seeking. + * It is still a bit buggy, but works sometimes. + * I need to find out how to make the jump clean. + * At the moment, some corruption of the mpeg2 stream occurs, + * which causes libmpeg2 to crash. + * + * Revision 1.23 2002/01/18 00:23:52 jcdutton + * Support Ejecting of DVD. + * It will first un-mount the DVD, then eject it. + * + * Revision 1.22 2002/01/17 14:50:32 jcdutton + * Fix corruption of stream during menu transitions. + * Menu transitions are now clean. + * + * Revision 1.21 2002/01/15 00:37:03 jcdutton + * Just a few cleanups, and a assert fix. (memset fixed it) + * + * Revision 1.20 2002/01/13 22:17:57 jcdutton + * Change logging. + * + * + */ diff --git a/src/input/libdvdnav/dvdnav.h b/src/input/libdvdnav/dvdnav.h new file mode 100644 index 000000000..4d03341cc --- /dev/null +++ b/src/input/libdvdnav/dvdnav.h @@ -0,0 +1,879 @@ +/* + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: dvdnav.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $ + * + */ + +/** + * \file dvdnav.h + * The main file you should include if you want to access dvdnav + * functionality. + */ + +#ifndef DVDNAV_H_INCLUDED +#define DVDNAV_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +/* Defines the various events and dvdnav_event_t */ +#include "dvdnav_events.h" + +/* Various useful types */ +#include "dvd_types.h" + +#include <dvdread/dvd_reader.h> + +/** + * Opaque data-type can be viewed as a 'DVD handle'. You should get + * a pointer to a dvdnav_t from the dvdnav_open() function. + * \sa dvdnav_open() + */ +typedef struct dvdnav_s dvdnav_t; + +/* Status */ +typedef int dvdnav_status_t; + +#define DVDNAV_STATUS_ERR 0 +#define DVDNAV_STATUS_OK 1 + +/** + * NOTE: */ + +/** + * \defgroup init Initialisation & housekeeping functions + * These functions allow you to open a DVD device and associate it + * with a dvdnav_t. + * + * 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(). + * + * A minimal <tt>libdvdnav</tt> program should always call dvdnav_open() + * and dvdnav_close(). + * + * \par Example: + * \include minimal.c + * + * @{ + */ + +/** + * Attempts to open the DVD drive at the specifiec path and pre-cache + * any CSS-keys that your hacked libdvdread may use. + * + * \param dest Pointer to a dvdnav_t pointer to fill in. + * \param path Any libdvdread acceptable path + */ +dvdnav_status_t dvdnav_open(dvdnav_t** dest, char *path); + +/** + * Closes a dvdnav_t previously opened with dvdnav_open(), freeing any + * memory associated with it. + * + * \param self dvdnav_t to close. + */ +dvdnav_status_t dvdnav_close(dvdnav_t *self); + +/** + * Resets the virtual machine and buffers in a previously opened dvdnav + * + * \param self dvdnav_t to reset. + */ +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 it NULL + * on error. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param path Pointer to char* to fill in. + */ +dvdnav_status_t dvdnav_path(dvdnav_t *self, char** path); + +/** + * Returns a human-readable string describing the last error. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \returns A pointer to said string. + */ +char* dvdnav_err_to_string(dvdnav_t *self); + +/** \@} */ + +/** + * \defgroup char Changing and Reading DVD Player characteristics + * + * These functions allow you to manipulate the various global characteristics + * of the DVD playback engine. + * + * @{ + */ + +/** + * Returns 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. + * + * \par Note: + * This has <b>nothing</b> to do with the region setting of the DVD drive. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param region Pointer to an int which will receive the region code mask. + */ +dvdnav_status_t dvdnav_get_region_mask(dvdnav_t *self, int *region); + +/** + * Sets the region mask of the virtual machine. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param mask 0x00..0xff -- the desired region mask. + * + * \sa dvdnav_get_region_mask() + */ +dvdnav_status_t dvdnav_set_region_mask(dvdnav_t *self, int mask); + +/** + * Specify whether read-ahead caching should be used. You may not want this if your + * decoding engine does its own buffering or if you don't like the fact that this is + * implemented in a multithreaded manner. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param use_readahead 0 - no, 1 - yes + */ +dvdnav_status_t dvdnav_set_readahead_flag(dvdnav_t *self, int use_readahead); + +/** + * Query whether readahead caching/buffering will be used. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param flag Pointer to int to recieve flag value. + * + * \sa dvdnav_get_readahead_flag() + */ +dvdnav_status_t dvdnav_get_readahead_flag(dvdnav_t *self, int* flag); + +/** + * @} + */ + +/** + * \defgroup data Reading Data + * + * These functions are used to poll the playback enginge 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. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param buf Buffer (at least 2048 octets) to fill with next block/event structure. + * \param event Pointer to int to get event type. + * \param len Pointer to int to get the number of octets written into buf. + */ +dvdnav_status_t dvdnav_get_next_block(dvdnav_t *self, unsigned char *buf, + int *event, int *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. + * I 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 pointer must _never_ be freed but instead returned to the library via + + dvdnav_free_cache_block. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param buf Buffer (at least 2048 octets) to fill with next block/event structure. + * A different buffer might be returned, if the block was found in the internal cache. + * \param event Pointer to int to get event type. + * \param len Pointer to int to get the number of octets written into buf. + */ +dvdnav_status_t dvdnav_get_next_cache_block(dvdnav_t *self, unsigned char **buf, + int *event, int *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. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param buf Buffer received from internal cache. + */ +dvdnav_status_t dvdnav_free_cache_block(dvdnav_t *self, unsigned char *buf); + +/** + * Get video aspect code. + * + * \param self Pointer to dvdnav_t associated with this operation. + * + * \returns Video aspect ratio code, 0 -- 4:3, 2 -- 16:9 + */ +uint8_t dvdnav_get_video_aspect(dvdnav_t *self); + +/** + * Get video scaling permissions. + * + * \param self Pointer to dvdnav_t associated with this operation. + * + * \returns Video scaling permissions, bit0 - deny letterboxing, bit1 - deny pan&scan + */ +uint8_t dvdnav_get_video_scale_permission(dvdnav_t *self); + +/** + * @} + */ + +/** + * \defgroup nav Navigation Commands + * + * @{ + */ + +/** + * Returns the number of titles on the disk. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param titles Pointer to int to receive number of titles. + */ +dvdnav_status_t dvdnav_get_number_of_titles(dvdnav_t *self, int *titles); + +/** + * Returns the number of programs within the current title. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param programs Pointer to int to receive number of programs. + */ +dvdnav_status_t dvdnav_get_number_of_programs(dvdnav_t *self, int *programs); + +/** + * If we are currently in a still-frame this function skips it (or attempts to). + * This might fail if this still-frame is of infinite duration as most DVD + * authors wouldn't expect you to be able to do this <tt>:)</tt> + * + * \param self Pointer to dvdnav_t associated with this operation. + */ +dvdnav_status_t dvdnav_still_skip(dvdnav_t *self); + +/** + * Plays a specified title of the DVD. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param title 1..99 -- Title number to play. + */ +dvdnav_status_t dvdnav_title_play(dvdnav_t *self, int title); + +/** + * Plays the specifiec title, starting from the specified + * part (chapter). + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param title 1..99 -- Title number to play. + * \param part 1..999 -- Part to start from. + */ +dvdnav_status_t dvdnav_part_play(dvdnav_t *self, int title, int part); + +/** + * Play the specified amount of parts of the specified title of + * the DVD then STOP. + * + * \par Note: + * Currently unimplemented! + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param title 1..99 -- Title number to play. + * \param part 1..999 -- Part to start from. + * \param parts_to_play 1..999 -- Number of parts to play. + */ +dvdnav_status_t dvdnav_part_play_auto_stop(dvdnav_t *self, int title, + int part, int parts_to_play); + +/** + * Play the specified title starting from the specified time + * + * \par Note: + * Currently unimplemented! + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param title 1..99 -- Title number to play. + * \param time Timecode to start from (hours, minutes, seconds + frames). + */ +dvdnav_status_t dvdnav_time_play(dvdnav_t *self, int title, + unsigned long int time); + +/** + * Stops playing the current title (causes a STOP action in + * dvdnav_get_next_block()). + * + * \param self Pointer to dvdnav_t associated with this operation. + * + * \sa dvdnav_get_next_block() + */ +dvdnav_status_t dvdnav_stop(dvdnav_t *self); + +/** + * Stop playing current title and play the "GoUp"-program chain + * (which generally leads to the title menu or a higer-level menu). + * + * \param self Pointer to dvdnav_t associated with this operation. + */ +dvdnav_status_t dvdnav_go_up(dvdnav_t *self); + +/** + * @} + */ + +/** + * \defgroup search Searching + * + * @{ + */ + +/** + * Stop playing the current title and start playback of the title + * from the specified timecode. + * + * \par Note: + * Currently unimplemented! + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param time Timecode to start from. + */ +dvdnav_status_t dvdnav_time_search(dvdnav_t *self, + unsigned long int time); + +/** + * Stop playing the current title and start playback of the title + * from the specified sector offset. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param offset Sector offset to start from. + * \param origin Start from here, start or end. + */ +dvdnav_status_t dvdnav_sector_search(dvdnav_t *self, + unsigned long int offset, int origin); + +/** + * Stop playing the current title and start playback of the title + * from the specified part (chapter). + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param part 1..999 -- Part to start from. + */ +dvdnav_status_t dvdnav_part_search(dvdnav_t *self, int part); + +/** + * Stop playing the current title and start playback of the title + * from the previous program (if it exists). + * + * \param self Pointer to dvdnav_t associated with this operation. + */ +dvdnav_status_t dvdnav_prev_pg_search(dvdnav_t *self); + +/** + * Stop playing the current title and start playback of the title + * from the first program. + * + * \param self Pointer to dvdnav_t associated with this operation. + */ +dvdnav_status_t dvdnav_top_pg_search(dvdnav_t *self); + +/** + * Stop playing the current title and start playback of the title + * from the next program (if it exists). + * + * \param self Pointer to dvdnav_t associated with this operation. + */ +dvdnav_status_t dvdnav_next_pg_search(dvdnav_t *self); + +/** + * Stop playing the current title and jump to the specified menu. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param menu Which menu to call (see DVDMenuID_t). + * + * \sa DVDMenuID_t (from <tt>libdvdread</tt>) + */ +dvdnav_status_t dvdnav_menu_call(dvdnav_t *self, DVDMenuID_t menu); + +/** + * Return the title number and chapter currently being played or + * -1 if in a menu. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param title Pointer to into which will receive the current title number. + * \param part Pointer to into which will receive the current part number. + */ +dvdnav_status_t dvdnav_current_title_info(dvdnav_t *self, int *title, + int *part); + +/** + * Return a string describing the title. This is an ID string encoded on the + * disc byt the author. In many cases this is a descriptive string such as + * `<tt>THE_MATRIX</tt>' but sometimes is sigularly uninformative such as + * `<tt>PDVD-011421</tt>'. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param title_str Pointer to C-style string to receive a string describing the title. + */ +dvdnav_status_t dvdnav_get_title_string(dvdnav_t *self, char **title_str); + +/** + * Return the current position (in blocks) within the current + * part and the length (in blocks) of said part. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param pos Pointer to unsigned int to get the current position. + * \param len Pointer to unsinged int to hold the length of the current part. + */ +dvdnav_status_t dvdnav_get_position(dvdnav_t *self, unsigned int* pos, + unsigned int *len); + +/** + * Return the current position (in blocks) within the current + * title and the length (in blocks) of said title. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param pos Pointer to unsigned int to get the current position. + * \param len Pointer to unsinged int to hold the length of the current title. + */ +dvdnav_status_t dvdnav_get_position_in_title(dvdnav_t *self, + unsigned int* pos, + unsigned int *len); + +/** + * @} + */ + +/** + * \defgroup highlight Highlights + * + * @{ + */ + +/** + * Get the currently highlighted button + * number (1..36) or 0 if no button is highlighed. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param button Pointer to the value to fill in. + */ +dvdnav_status_t dvdnav_get_current_highlight(dvdnav_t *self, int* button); + +/** + * Returns the Presentation Control Information (PCI) structure associated + * with the current position. + * + * \param self Pointer to dvdnav_t associated with this operation. + * + * \sa pci_t (in <tt>libdvdread</tt>) + */ +pci_t* dvdnav_get_current_nav_pci(dvdnav_t *self); + +/** + * Returns the DSI (data seach information) structure associated + * with the current position. + * + * \param self Pointer to dvdnav_t associated with this operation. + * + * \sa pci_t (in <tt>libdvdread</tt>) + */ +dsi_t* dvdnav_get_current_nav_dsi(dvdnav_t *self); + +/** + * Get the area associated with a certain button. + * + * \param nav_pci Pointer to the PCI structure you may have got via + * dvdnav_get_current_nav_pci(). + * + * \param button Button number to query. + * \param mode 0..3 -- Button mode to query. + * \param highlight Pointer to dvdnav_highlight_area_t to fill in. + * + * \sa dvdnav_highlight_area_t + */ +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). + * + * \param self Pointer to dvdnav_t associated with this operation. + */ +dvdnav_status_t dvdnav_upper_button_select(dvdnav_t *self); +/** + * Move button highlight around as suggested by function name (e.g. with arrow keys). + * + * \param self Pointer to dvdnav_t associated with this operation. + */ +dvdnav_status_t dvdnav_lower_button_select(dvdnav_t *self); +/** + * Move button highlight around as suggested by function name (e.g. with arrow keys). + * + * \param self Pointer to dvdnav_t associated with this operation. + */ +dvdnav_status_t dvdnav_right_button_select(dvdnav_t *self); +/** + * Move button highlight around as suggested by function name (e.g. with arrow keys). + * + * \param self Pointer to dvdnav_t associated with this operation. + */ +dvdnav_status_t dvdnav_left_button_select(dvdnav_t *self); + +/** + * Activate (press) the currently highlighted button. + * \param self Pointer to dvdnav_t associated with this operation. + */ +dvdnav_status_t dvdnav_button_activate(dvdnav_t *self); + +/** + * Highlight a specific button. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param button 1..39 -- Button number to activate. + */ +dvdnav_status_t dvdnav_button_select(dvdnav_t *self, int button); + +/** + * Activate (press) specified button. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param button 1..39 -- Button number to activate. + */ +dvdnav_status_t dvdnav_button_select_and_activate(dvdnav_t *self, int button); + +/** + * Select button at specified (image) co-ordinates. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param x X co-ordinate in image. + * \param y Y xo-ordinate in image. + */ +dvdnav_status_t dvdnav_mouse_select(dvdnav_t *self, int x, int y); + +/** + * Activate (press) button at specified co-ordinates. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param x X co-ordinate in image. + * \param y Y xo-ordinate in image. + */ +dvdnav_status_t dvdnav_mouse_activate(dvdnav_t *self, int x, int y); + +/** + * @} + */ + +/** + * \defgroup languages Languages + * + * @{ + */ + +/** + * Set which menu language we should use. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param code 2 char ISO639 Language code in a C-style string. + */ +dvdnav_status_t dvdnav_menu_language_select(dvdnav_t *self, + char *code); + +/** + * Set which audio language we should use. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param code 2 char ISO639 Language code in a C-style string. + */ +dvdnav_status_t dvdnav_audio_language_select(dvdnav_t *self, + char *code); + +/** + * Set which spu language we should use. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param code 2 char ISO639 Language code in a C-style string. + */ +dvdnav_status_t dvdnav_spu_language_select(dvdnav_t *self, + char *code); + +/** + * @} + */ + +/** + * \defgroup streams Sub-Picture Unit (Subtitles) and Audio Streams + * + * All these commands manipulate the audio/subtitle stream numbers that the + * player engine thinks is playing. Note that all the streams are still returned + * multiplexed by dvdnav_get_next_block(). You should try to make sure that the + * MPEG demuxer and the player engine both have the same thoughts on what stream the + * user is currently listening to. + * + * \sa dvdnav_get_next_block() + * + * @{ + */ + +/** + * Set a specific PHYSICAL MPEG stream. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param audio 0..7 -- Stream number. + */ +dvdnav_status_t dvdnav_physical_audio_stream_change(dvdnav_t *self, + int audio); + +/** + * Set a specific logical audio stream. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param audio 0..7 -- Stream number. + */ +dvdnav_status_t dvdnav_logical_audio_stream_change(dvdnav_t *self, + int audio); + +/** + * Set the int pointed to to the current PHYSICAL audio + * stream. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param audio Pointer to int which will receive value. + */ +dvdnav_status_t dvdnav_get_physical_audio_stream(dvdnav_t *self, int* audio); + +/** + * Set the int pointed to to the current LOGICAL audio + * stream. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param audio Pointer to int which will receive value. + */ +dvdnav_status_t dvdnav_get_logical_audio_stream(dvdnav_t *self, int* audio); + +/** + * Set a specific PHYSICAL MPEG SPU stream and whether it should be + * displayed. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param stream 0..31 or 63 (dummy) -- Stram number. + * \param display: 0..1 -- Is this actually being displayed? + */ +dvdnav_status_t dvdnav_physical_spu_stream_change(dvdnav_t *self, + int stream, int display); + +/** + * Set a specific LOGICAL SPU stream and whether it should be + * displayed. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param stream 0..31 or 63 (dummy) -- Stram number. + * \param display: 0..1 -- Is this actually being displayed? + */ +dvdnav_status_t dvdnav_logical_spu_stream_change(dvdnav_t *self, + int stream, int display); + +/** + * Set the ints pointed to to the current PHYSICAL SPU + * stream & display flag. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param stream Pointer which will receive value. + * \param display Pointer which will receive value. + */ +dvdnav_status_t dvdnav_get_physical_spu_stream(dvdnav_t *self, + int* stream, int* display); + +/** + * Set the ints pointed to to the current LOGICAL SPU + * stream & display flag. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param stream Pointer which will receive value. + * \param display Pointer which will receive value. + */ +dvdnav_status_t dvdnav_get_logical_spu_stream(dvdnav_t *self, + int* stream, int* disply); + +/** + * Converts a *logical* audio stream id into country code + * (returns <tt>0xffff</tt> if no such stream). + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param stream Stream number + */ +uint16_t dvdnav_audio_stream_to_lang(dvdnav_t *self, uint8_t stream); + +/** + * Converts a *logical* subpicture stream id into country code + * (returns <tt>0xffff</tt> if no such stream). + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param stream Stream number + */ +uint16_t dvdnav_spu_stream_to_lang(dvdnav_t *self, uint8_t stream); + +/** + * Converts a *physical* audio stream id into a logical stream number + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param audio_num Stream number + */ +int8_t dvdnav_get_audio_logical_stream(dvdnav_t *self, uint8_t audio_num); + +/** + * Converts a *physical* subpicture stream id into a logical stream number + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param subp_num Stream number + */ +int8_t dvdnav_get_spu_logical_stream(dvdnav_t *self, uint8_t subp_num); + +/** + * Get active spu stream. + * + * \param self Pointer to dvdnav_t associated with this operation. + */ +int8_t dvdnav_get_active_spu_stream(dvdnav_t *self); + +/** + * @} + */ + +/** + * \defgroup angles Multiple angles + * + * The <tt>libdvdnav</tt> 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. + * + * \par 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 existant angle + * the call fails. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param angle 1..9 -- Currentl angle to follow. + */ +dvdnav_status_t dvdnav_angle_change(dvdnav_t *self, int angle); + +/** + * Returns the current angle and number of angles present. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param current_angle Pointer to int which will get the current angle. + * \param number_of_angles Pointer to int which will get the number of angles. + */ +dvdnav_status_t dvdnav_get_angle_info(dvdnav_t *self, int* current_angle, + int *number_of_angles); + +/** + * FIXME: WTF does this do? -- High qulaity documentation huh? + */ +dvdnav_status_t dvdnav_get_cell_info(dvdnav_t *self, int* current_angle, + int *number_of_angles); + +/** + * Returns the still time status from the next cell + * + * \param self Pointer to dvdnav_t associated with this operation. + */ +uint32_t dvdnav_get_next_still_flag(dvdnav_t *self); + +/** + * @} + */ + +/** + * \defgroup domain Domain Queries + * + * The following functions can be used to query whether we are in + * particular domains or not. + * + * @{ + */ + +/** + * Are we in the First Play domain. (Menu) + * \param self Pointer to dvdnav_t associated with this operation. + * \returns -1 on failure, 1 if condition is true, 0 if condition is false + */ +int8_t dvdnav_is_domain_fp(dvdnav_t *self); +/** + * Are we in the Video management Menu domain. (Menu) + * \param self Pointer to dvdnav_t associated with this operation. + * \returns -1 on failure, 1 if condition is true, 0 if condition is false + */ +int8_t dvdnav_is_domain_vmgm(dvdnav_t *self); +/** + * Are we in the Video Title Menu domain (Menu) + * \param self Pointer to dvdnav_t associated with this operation. + * \returns -1 on failure, 1 if condition is true, 0 if condition is false + */ +int8_t dvdnav_is_domain_vtsm(dvdnav_t *self); +/** + * Are we in the Video Title domain (playing movie)? + * \param self Pointer to dvdnav_t associated with this operation. + * \returns -1 on failure, 1 if condition is true, 0 if condition is false + */ +int8_t dvdnav_is_domain_vts(dvdnav_t *self); + +/** + * @} + */ + +/* + * The following info appears on the front page of the reference manual. It is + * included here to keep it with the main dvdnav source files. The copyright notice + * refers to the comments _only_. All machine-readable source code is covered by + * the copyright notice at the top of this file. + * + * Oh, and the official language of the documentation is English -- + * English English (as in the English spoken in England) not US English. + */ + +#ifdef __cplusplus +} +#endif + +#endif /* DVDNAV_H_INCLUDED */ diff --git a/src/input/libdvdnav/dvdnav_events.h b/src/input/libdvdnav/dvdnav_events.h new file mode 100644 index 000000000..427c7896c --- /dev/null +++ b/src/input/libdvdnav/dvdnav_events.h @@ -0,0 +1,124 @@ +/* + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: dvdnav_events.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $ + * + */ + +#ifndef DVDNAV_EVENTS_H_INCLUDED +#define DVDNAV_EVENTS_H_INCLUDED + +#include <dvdread/ifo_types.h> +#include <dvdread/nav_types.h> +#include <dvdread/dvd_reader.h> + +/** + * \file dvdnav_events.h + * This header defines events and event types + */ + +/*** EVENTS ***/ + +#define DVDNAV_BLOCK_OK 0 /*!< The next black was returned */ +#define DVDNAV_NOP 1 /*!< No action should be taken */ +#define DVDNAV_STILL_FRAME 2 /*!< The preceeding block was the last in a + still frame. */ +#define DVDNAV_SPU_STREAM_CHANGE 3 /*!< The SPU stream was changed */ +#define DVDNAV_AUDIO_STREAM_CHANGE 4 /*!< The Audio stream was changed */ +#define DVDNAV_VTS_CHANGE 5 /*!< We have changed VTS */ +#define DVDNAV_CELL_CHANGE 6 /*!< We have jumped to a new cell */ +#define DVDNAV_NAV_PACKET 7 /*!< The packet just passed was the NAV packet */ +#define DVDNAV_STOP 8 /*!< The last block was final, no more are coming */ +#define DVDNAV_HIGHLIGHT 9 /*!< Change highlight region */ +#define DVDNAV_SPU_CLUT_CHANGE 10 /*!< SPU CLUT */ +#define DVDNAV_SEEK_DONE 11 /*!< Seek done, subtitles should be reset */ +#define DVDNAV_HOP_CHANNEL 12 /*!< Sent when non-seemless stream change has happed + E.g. Menu button pressed causing change in menu */ + +/*** EVENT TYPES ***/ + +/** + * Structure providing information on DVDNAV_STILL_FRAME events. + */ +typedef struct { + int length; /*!< + The length (in seconds) the still frame + should be displayed for, or 0xff if + indefinate. */ +} dvdnav_still_event_t; + +/** + * Structure providing information on DVDNAV_SPU_STREAM_CHANGE events. + */ +typedef struct { + int physical_wide; /*!< The physical (MPEG) stream number for widescreen display. */ + int physical_letterbox; /*!< The physical (MPEG) stream number for letterboxed display. */ + int physical_pan_scan; /*!< The physical (MPEG) stream number for pan&scan display. */ + int logical; /*!< The logical (DVD) stream number. */ +} dvdnav_spu_stream_change_event_t; + +/** + * Structure providing information on DVDNAV_AUDIO_STREAM_CHANGE events. + */ +typedef struct { + int physical; /*!< The physical (MPEG) stream number. */ + int logical; /*!< The logical (DVD) stream number. */ +} dvdnav_audio_stream_change_event_t; + +/** + * Structure providing information on DVDNAV_VTS_CHANGE events. + */ +typedef struct { + int old_vtsN; /*!< The old VTS number */ + dvd_read_domain_t old_domain; /*!< The old domain */ + int new_vtsN; /*!< The new VTS number */ + dvd_read_domain_t new_domain; /*!< The new domain */ +} dvdnav_vts_change_event_t; + +/** + * Structure providing information on DVDNAV_CELL_CHANGE events. + */ +typedef struct { + cell_playback_t *old_cell; /*!< The old cell (or NULL if this is + the first cell) */ + cell_playback_t *new_cell; /*!< The cell_playback_t for the new cell */ +} dvdnav_cell_change_event_t; + +/** + * Structure providing information on DVDNAV_NAV_PACKET events. + */ +typedef struct { + pci_t *pci; + dsi_t *dsi; +} dvdnav_nav_packet_event_t; + +/** + * Structure providing information on DVDNAV_HIGHLIGHT events. + */ +typedef struct { + int display; /*!< 0 - hide, 1 - show, entries below only guaranteed useful + if this is '1' */ + uint32_t palette; /*!< The CLUT entries for the highlight palette + (4-bits per entry -> 4 entries) */ + uint16_t sx,sy,ex,ey; /*!< The start/end x,y positions */ + uint32_t pts; /*!< Highlight PTS to match with SPU */ + uint32_t buttonN; /*!< Button number for the SPU decoder. */ +} dvdnav_highlight_event_t; + +#endif /* DVDNAV_EVENTS_H_INCLUDED */ diff --git a/src/input/libdvdnav/dvdnav_internal.h b/src/input/libdvdnav/dvdnav_internal.h new file mode 100644 index 000000000..8b589af77 --- /dev/null +++ b/src/input/libdvdnav/dvdnav_internal.h @@ -0,0 +1,168 @@ +/* !! DO NO EDIT THIS FILE, it is automatically generated */ +/* + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: dvdnav_internal.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $ + * + */ + +#ifndef DVDNAV_INTERNAL_H_INCLUDED +#define DVDNAV_INTERNAL_H_INCLUDED + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "dvdnav.h" +#include "vm.h" + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <limits.h> +#include <string.h> +#include <pthread.h> + +#undef WORDS_BIGENDIAN + +#include <dvdread/dvd_reader.h> +#include <dvdread/ifo_read.h> +#include <dvdread/ifo_types.h> + + +/* Maximum length of an error string */ +#define MAX_ERR_LEN 255 + +/* Use the POSIX PATH_MAX if available */ +#ifdef PATH_MAX +#define MAX_PATH_LEN PATH_MAX +#else +#define MAX_PATH_LEN 255 /* Arbitary */ +#endif + +#ifndef DVD_VIDEO_LB_LEN +#define DVD_VIDEO_LB_LEN 2048 +#endif + +typedef struct read_cache_s read_cache_t; + +/* + * These are defined here because they are + * not in ifo_types.h, they maybe one day + */ + +#ifndef audio_status_t +typedef struct { +#ifdef WORDS_BIGENDIAN + unsigned int available : 1; + unsigned int zero1 : 4; + unsigned int stream_number : 3; + uint8_t zero2; +#else + uint8_t zero2; + unsigned int stream_number : 3; + unsigned int zero1 : 4; + unsigned int available : 1; +#endif +} ATTRIBUTE_PACKED audio_status_t; +#endif + +#ifndef spu_status_t +typedef struct { +#ifdef WORDS_BIGENDIAN + unsigned int available : 1; + unsigned int zero1 : 2; + unsigned int stream_number_4_3 : 5; + unsigned int zero2 : 3; + unsigned int stream_number_wide : 5; + unsigned int zero3 : 3; + unsigned int stream_number_letterbox : 5; + unsigned int zero4 : 3; + unsigned int stream_number_pan_scan : 5; +#else + unsigned int stream_number_pan_scan : 5; + unsigned int zero4 : 3; + unsigned int stream_number_letterbox : 5; + unsigned int zero3 : 3; + unsigned int stream_number_wide : 5; + unsigned int zero2 : 3; + unsigned int stream_number_4_3 : 5; + unsigned int zero1 : 2; + unsigned int available : 1; +#endif +} ATTRIBUTE_PACKED spu_status_t; +#endif + +typedef struct dvdnav_vobu_s { + int32_t vobu_start; /* Logical Absolute. MAX needed is 0x300000 */ + int32_t vobu_length; /* Relative offset */ + int32_t blockN; /* Relative offset */ + int32_t vobu_next; /* Relative offset */ +} dvdnav_vobu_t; + +/* The main DVDNAV type */ + +struct dvdnav_s { + /* General data */ + char path[MAX_PATH_LEN]; /* Path to DVD device/dir */ + dvd_file_t *file; /* Currently opened file */ + int open_vtsN; /* The domain and number of the... */ + int open_domain; /* ..currently opened VOB */ + + /* Position data */ + vm_position_t position_next; + vm_position_t position_current; + dvdnav_vobu_t vobu; + + /* NAV data */ + pci_t pci; + dsi_t dsi; + + /* Flags */ + int skip_still; /* Set when skipping a still */ + int stop; /* Are we stopped? (note not paused, actually stopped) */ + int spu_clut_changed; /* The SPU CLUT changed */ + int started; /* vm_start has been called? */ + int use_read_ahead; /* 1 - use read-ahead cache, 0 - don't */ + /* VM */ + vm_t* vm; + pthread_mutex_t vm_lock; + + /* Highlight */ + int hli_state; /* State of highlight: 0 - disabled, + 1 - selected, + 2 - activated */ + + /* Read-ahead cache */ + read_cache_t *cache; + + /* Errors */ + char err_str[MAX_ERR_LEN]; +}; + +/** USEFUL MACROS **/ + +#define printerrf(format, args...) snprintf(this->err_str, MAX_ERR_LEN, format, ## args); +#define printerr(str) strncpy(this->err_str, str, MAX_ERR_LEN); +/* Save my typing */ + +#define S_ERR DVDNAV_STATUS_ERR +#define S_OK DVDNAV_STATUS_OK + +#endif /* DVDNAV_INTERNAL_H_INCLUDED */ diff --git a/src/input/libdvdnav/highlight.c b/src/input/libdvdnav/highlight.c new file mode 100644 index 000000000..dc4f3877a --- /dev/null +++ b/src/input/libdvdnav/highlight.c @@ -0,0 +1,344 @@ +/* + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: highlight.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $ + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* +#define BUTTON_TESTING +*/ +#include <assert.h> + +#include <dvdnav.h> +#include "dvdnav_internal.h" + +#include "vm.h" +# +#include <dvdread/nav_types.h> + +#ifdef BUTTON_TESTING +#include <dvdread/nav_print.h> +#endif + +/* Highlighting API calls */ + +dvdnav_status_t dvdnav_get_current_highlight(dvdnav_t *this, int* button) { + if(!this) + return S_ERR; + + /* Simply return the appropriate value based on the SPRM */ + (*button) = (this->vm->state.HL_BTNN_REG) >> 10; + + return S_OK; +} + +btni_t *__get_current_button(dvdnav_t *this) { + int button = 0; + + if(dvdnav_get_current_highlight(this, &button) != S_OK) { + printerrf("Unable to get information on current highlight."); + return NULL; + } +#ifdef BUTTON_TESTING + navPrint_PCI(&(this->pci)); +#endif + + return &(this->pci.hli.btnit[button-1]); +} + +dvdnav_status_t dvdnav_button_auto_action(dvdnav_t *this) { + btni_t *button_ptr; + + if(!this) + return S_ERR; + + if((button_ptr = __get_current_button(this)) == NULL) { + return S_ERR; + } + if (button_ptr->auto_action_mode == 1) { + return S_OK; + } + return S_ERR; +} + + +dvdnav_status_t dvdnav_upper_button_select(dvdnav_t *this) { + btni_t *button_ptr; + + if(!this) + return S_ERR; + + if((button_ptr = __get_current_button(this)) == NULL) { + return S_ERR; + } + + dvdnav_button_select(this, button_ptr->up); + if (dvdnav_button_auto_action(this) ) { + dvdnav_button_activate(this); + } + + return S_OK; +} + +dvdnav_status_t dvdnav_lower_button_select(dvdnav_t *this) { + btni_t *button_ptr; + + if(!this) + return S_ERR; + + if((button_ptr = __get_current_button(this)) == NULL) { + return S_ERR; + } + + dvdnav_button_select(this, button_ptr->down); + if (dvdnav_button_auto_action(this) ) { + dvdnav_button_activate(this); + } + + return S_OK; +} + +dvdnav_status_t dvdnav_right_button_select(dvdnav_t *this) { + btni_t *button_ptr; + + if(!this) + return S_ERR; + + if((button_ptr = __get_current_button(this)) == NULL) { + printerr("Error fetching information on current button."); + return S_ERR; + } + + dvdnav_button_select(this, button_ptr->right); + if (dvdnav_button_auto_action(this) ) { + dvdnav_button_activate(this); + } + + return S_OK; +} + +dvdnav_status_t dvdnav_left_button_select(dvdnav_t *this) { + btni_t *button_ptr; + + if(!this) + return S_ERR; + + if((button_ptr = __get_current_button(this)) == NULL) { + return S_ERR; + } + + dvdnav_button_select(this, button_ptr->left); + if (dvdnav_button_auto_action(this) ) { + dvdnav_button_activate(this); + } + + return S_OK; +} + +dvdnav_status_t dvdnav_get_highlight_area(pci_t* nav_pci , int32_t button, int32_t mode, + dvdnav_highlight_area_t* highlight) { + btni_t *button_ptr; +#ifdef BUTTON_TESTING + fprintf(stderr,"Button get_highlight_area %i\n", button); +#endif + + /* Set the highlight SPRM if the passed button was valid*/ + if((button <= 0) || (button > nav_pci->hli.hl_gi.btn_ns)) { + fprintf(stderr,"Unable to select button number %i as it doesn't exist\n", + button); + return S_ERR; + } + button_ptr = &nav_pci->hli.btnit[button-1]; + + highlight->sx = button_ptr->x_start; + highlight->sy = button_ptr->y_start; + highlight->ex = button_ptr->x_end; + highlight->ey = button_ptr->y_end; + if(button_ptr->btn_coln != 0) { + highlight->palette = nav_pci->hli.btn_colit.btn_coli[button_ptr->btn_coln-1][mode]; + } else { + highlight->palette = 0; + } + highlight->pts = nav_pci->hli.hl_gi.hli_s_ptm; + highlight->buttonN = button; +#ifdef BUTTON_TESTING + fprintf(stderr,"highlight.c:Highlight area is (%u,%u)-(%u,%u), display = %i, button = %u\n", + button_ptr->x_start, button_ptr->y_start, + button_ptr->x_end, button_ptr->y_end, + 1, + button); +#endif + + return S_OK; +} + +dvdnav_status_t dvdnav_button_activate(dvdnav_t *this) { + int button; + btni_t *button_ptr = NULL; + + if(!this) + return S_ERR; + pthread_mutex_lock(&this->vm_lock); + + /* Precisely the same as selecting a button except we want + * a different palette */ + if(dvdnav_get_current_highlight(this, &button) != S_OK) { + pthread_mutex_unlock(&this->vm_lock); + return S_ERR; + } +/* FIXME: dvdnav_button_select should really return a + * special case for explicit NO-BUTTONS. + */ + if(dvdnav_button_select(this, button) != S_OK) { + /* Special code to handle still menus with no buttons. + * the navigation is expected to report to the appicatino that a STILL is + * underway. In turn, the application is supposed to report to the user + * that the playback is pause. The user is then expected to undo the pause. + * ie: hit play. At that point, the navigation should release the still and + * go to the next Cell. + * Explanation by Mathieu Lavage <mathieu_lacage@realmagic.fr> + * Code added by jcdutton. + */ + if (this->position_current.still != 0) { + /* In still, but no buttons. */ + vm_get_next_cell(this->vm); + this->position_current.still = 0; + pthread_mutex_unlock(&this->vm_lock); + return S_OK; + } + pthread_mutex_unlock(&this->vm_lock); + return S_ERR; + } + /* FIXME: The button command should really be passed in the API instead. */ + button_ptr = __get_current_button(this); + /* Finally, make the VM execute the appropriate code and + * scedule a jump */ +#ifdef BUTTON_TESTING + fprintf(stderr, "libdvdnav: Evaluating Button Activation commands.\n"); +#endif + if(vm_eval_cmd(this->vm, &(button_ptr->cmd)) == 1) { + /* Command caused a jump */ + this->vm->hop_channel++; + this->position_current.still = 0; + } + pthread_mutex_unlock(&this->vm_lock); + return S_OK; +} + +dvdnav_status_t dvdnav_button_select(dvdnav_t *this, int button) { + + if(!this) { + printerrf("Unable to select button number %i as this state bad", + button); + return S_ERR; + } + +#ifdef BUTTON_TESTING + fprintf(stderr,"libdvdnav: Button select %i\n", button); +#endif + + /* Set the highlight SPRM if the passed button was valid*/ + /* FIXME: this->pci should be provided by the application. */ + if((button <= 0) || (button > this->pci.hli.hl_gi.btn_ns)) { + printerrf("Unable to select button number %i as it doesn't exist", + button); + return S_ERR; + } + this->vm->state.HL_BTNN_REG = (button << 10); + + this->hli_state = 1; /* Selected */ + + this->position_current.button = -1; /* Force Highligh change */ + + return S_OK; +} + +dvdnav_status_t dvdnav_button_select_and_activate(dvdnav_t *this, + int button) { + /* A trivial function */ + if(dvdnav_button_select(this, button) != S_ERR) { + return dvdnav_button_activate(this); + } + + /* Should never get here without an error */ + return S_ERR; +} + +dvdnav_status_t dvdnav_mouse_select(dvdnav_t *this, int x, int y) { + int button, cur_button; + uint32_t best,dist; + int mx,my,dx,dy,d; + + /* FIXME: At the moment, the case of no button matchin (x,y) is + * silently ignored, is this OK? */ + if(!this) + return S_ERR; + + if(dvdnav_get_current_highlight(this, &cur_button) != S_OK) { + return S_ERR; + } + + best = 0; + dist = 0x08000000; /* >> than (720*720)+(567*567); */ + + /* Loop through each button */ + for(button=1; button <= this->pci.hli.hl_gi.btn_ns; button++) { + btni_t *button_ptr = NULL; + button_ptr = &(this->pci.hli.btnit[button-1]); + if((x >= button_ptr->x_start) && (x <= button_ptr->x_end) && + (y >= button_ptr->y_start) && (y <= button_ptr->y_end)) { + mx = (button_ptr->x_start + button_ptr->x_end)/2; + my = (button_ptr->y_start + button_ptr->y_end)/2; + dx = mx - x; + dy = my - y; + d = (dx*dx) + (dy*dy); + /* If the mouse is within the button and the mouse is closer + * to the center of this button then it is the best choice. */ + if(d < dist) { + dist = d; best=button; + } + } + } + + if (best!=0) { + /* As an efficiency measure, only re-select the button + * if it is different to the previously selected one. */ + if(best != cur_button) { + dvdnav_button_select(this, best); + } + } + + return S_OK; +} + +dvdnav_status_t dvdnav_mouse_activate(dvdnav_t *this, int x, int y) { + /* A trivial function */ + if(dvdnav_mouse_select(this, x,y) != S_ERR) { + return dvdnav_button_activate(this); + } + + /* Should never get here without an error */ + return S_ERR; +} + diff --git a/src/input/libdvdnav/navigation.c b/src/input/libdvdnav/navigation.c new file mode 100644 index 000000000..ba04b88c4 --- /dev/null +++ b/src/input/libdvdnav/navigation.c @@ -0,0 +1,164 @@ +/* + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: navigation.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $ + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <dvdnav.h> +#include "dvdnav_internal.h" + +#include "vm.h" + +/* Navigation API calls */ + +dvdnav_status_t dvdnav_still_skip(dvdnav_t *this) { + if(!this) + return S_ERR; + + this->position_current.still = 0; + this->skip_still = 1; + + return S_OK; +} + +dvdnav_status_t dvdnav_get_number_of_titles(dvdnav_t *this, int *titles) { + if(!this) + return S_ERR; + + if(!titles) { + printerr("Passed a NULL pointer"); + return S_ERR; + } + + (*titles) = vm_get_vmgi(this->vm)->tt_srpt->nr_of_srpts; + + return S_OK; +} + +dvdnav_status_t dvdnav_get_number_of_programs(dvdnav_t *this, int *programs) { + if(!this) + return S_ERR; + + if(!programs) { + printerr("Passed a NULL pointer"); + return S_ERR; + } + + (*programs) = this->vm->state.pgc->nr_of_programs; + + return S_OK; +} + +dvdnav_status_t dvdnav_title_play(dvdnav_t *this, int title) { + int num_titles; + + if(!this) { + return S_ERR; + } + + /* Check number of titles */ + dvdnav_get_number_of_titles(this, &num_titles); + if((title > num_titles) || (title <= 0)) { + printerrf("Invalid title passed (%i, maximum %i)", title, + num_titles); + return S_ERR; + } + + vm_start_title(this->vm, title); + + return S_OK; +} + +dvdnav_status_t dvdnav_part_play(dvdnav_t *this, int title, int part) { + int num_titles, num_progs; + + if(!this) { + return S_ERR; + } + + /* Check number of titles */ + dvdnav_get_number_of_titles(this, &num_titles); + if((title > num_titles) || (title <= 0)) { + printerrf("Invalid title passed (%i, maximum %i)", title, + num_titles); + return S_ERR; + } + + vm_start_title(this->vm, title); + + + /* Check number of parts */ + num_progs = this->vm->state.pgc->nr_of_programs; + if((part > num_progs) || (part <= 0)) { + printerrf("Invalid program passed (%i, maximum %i)", part, + num_progs); + return S_ERR; + } + + vm_jump_prog(this->vm, part); + + return S_OK; +} + +dvdnav_status_t dvdnav_part_play_auto_stop(dvdnav_t *this, int title, + int part, int parts_to_play) { + /* Perform jump as per usual */ + + return dvdnav_part_play(this, title, part); + + /* FIXME: Impement auto-stop */ + + /* return S_OK;*/ +} + +dvdnav_status_t dvdnav_time_play(dvdnav_t *this, int title, + unsigned long int time) { + /* FIXME: Implement */ + + return S_OK; +} + +dvdnav_status_t dvdnav_stop(dvdnav_t *this) { + if(!this) + return S_ERR; + + /* Set the STOP flag */ + + this->stop = 1; + + return S_OK; +} + +dvdnav_status_t dvdnav_go_up(dvdnav_t *this) { + if(!this) + return S_ERR; + + /* A nice easy function... delegate to the VM */ + vm_go_up(this->vm); + + return S_OK; +} + + + diff --git a/src/input/libdvdnav/read_cache.c b/src/input/libdvdnav/read_cache.c new file mode 100644 index 000000000..1d6e68727 --- /dev/null +++ b/src/input/libdvdnav/read_cache.c @@ -0,0 +1,543 @@ +/* + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: read_cache.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $ + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "dvdnav.h" +#include "read_cache.h" +#include <pthread.h> +#include <sys/time.h> +#include <time.h> + +/* +#define DVDNAV_PROFILE +*/ + +/* Read-ahead cache structure. */ +#if _MULTITHREAD_ + +/* For the multithreaded cache, the cache is a ring buffer + writing + * thread that continuously reads data into the buffer until it is + * full or the 'upper-bound' has been reached. + */ + +#define CACHE_BUFFER_SIZE 2048 /* Cache this number of blocks at a time */ + +struct read_cache_s { + pthread_mutex_t cache_lock; + pthread_t read_thread; + + /* Buffer */ + uint8_t *buffer; + + /* Size of buffer */ + int32_t size; + /* block offset from sector start of buffer 'head' */ + uint32_t pos; + /* block offset from sector start of read point */ + uint32_t read_point; + /* block offset from buffer start to ring-boundary */ + uint32_t start; + + /* Bit of strange cross-linking going on here :) -- Gotta love C :) */ + dvdnav_t *dvd_self; +}; + +#else + +#define READ_CACHE_CHUNKS 10 + +typedef struct read_cache_chunk_s { + uint8_t *cache_buffer; + int32_t cache_start_sector; /* -1 means cache invalid */ + size_t cache_block_count; + size_t cache_malloc_size; + int cache_valid; + int usage_count; /* counts how many buffers where issued from this chunk */ +} read_cache_chunk_t; + +struct read_cache_s { + read_cache_chunk_t chunk[READ_CACHE_CHUNKS]; + int current; + int freeing; /* is set to one when we are about to dispose the cache */ + pthread_mutex_t lock; + + /* Bit of strange cross-linking going on here :) -- Gotta love C :) */ + dvdnav_t *dvd_self; +}; +#endif + +#define _MT_TRACE 0 + +#if _MT_TRACE +#define dprintf(fmt, args...) fprintf(stderr, "%s: "fmt, __func__ , ## args); +#else +#define dprintf(fmt, args...) /* Nowt */ +#endif + +#if _MULTITHREAD_ + +void * read_cache_read_thread (void * this_gen) { + int cont = 1; + int32_t diff, start; + uint32_t pos, size, startp, endp; + uint32_t s,c; + uint8_t *at; + read_cache_t *self = (read_cache_t*)this_gen; + + while(cont) { + + pthread_mutex_lock(&self->cache_lock); + + if(self->size >= 0) { + diff = self->read_point - self->pos; + if(diff >= self->size/2) { + dprintf("(II) Read thread -- "); + + startp = (self->start) % CACHE_BUFFER_SIZE; + endp = abs((self->start + diff - 1) % CACHE_BUFFER_SIZE); + dprintf("startp = %i, endp = %i -- ",startp, endp); + + pos = self->pos + diff; + size = self->size - diff; + start = (self->start + diff) % CACHE_BUFFER_SIZE; + + /* Fill remainder of buffer */ + + if(startp > endp) { + s = pos + size; c = CACHE_BUFFER_SIZE - startp; + at = self->buffer + (startp * DVD_VIDEO_LB_LEN); + if(c > 0) { + dprintf("(1) Reading from %i to %i to %i ", s, s+c-1, startp); + pthread_mutex_unlock(&self->cache_lock); + DVDReadBlocks(self->dvd_self->file, s,c, at); + pthread_mutex_lock(&self->cache_lock); + } + + s = pos + size + c; c = CACHE_BUFFER_SIZE - size - c; + at = self->buffer; + if(c > 0) { + dprintf("(2) Reading from %i to %i to %i ", s, s+c-1, 0); + pthread_mutex_unlock(&self->cache_lock); + DVDReadBlocks(self->dvd_self->file, s,c, at); + pthread_mutex_lock(&self->cache_lock); + } + } else { + s = pos + size; c = CACHE_BUFFER_SIZE - size; + at = self->buffer + (startp * DVD_VIDEO_LB_LEN); + if(c > 0) { + dprintf("(3) Reading from %i to %i to %i ", s, s+c-1, startp); + pthread_mutex_unlock(&self->cache_lock); + DVDReadBlocks(self->dvd_self->file, s,c, at); + pthread_mutex_lock(&self->cache_lock); + } + } + + dprintf("\n"); + + self->pos = pos; + self->start = start; self->size = CACHE_BUFFER_SIZE; + } + } + + pthread_mutex_unlock(&self->cache_lock); + cont = (self->buffer != NULL); + usleep(100); + } + + return NULL; +} + +read_cache_t *dvdnav_read_cache_new(dvdnav_t* dvd_self) { + read_cache_t *me; + + me = (read_cache_t*)malloc(sizeof(struct read_cache_s)); + + if(me) { + int err; + + me->dvd_self = dvd_self; + me->buffer = (uint8_t*)malloc(CACHE_BUFFER_SIZE * DVD_VIDEO_LB_LEN); + me->start = 0; + me->pos = 0; + me->read_point = 0; + me->size = -1; + + /* Initialise the mutex */ + pthread_mutex_init(&me->cache_lock, NULL); + + if ((err = pthread_create (&me->read_thread, + NULL, read_cache_read_thread, me)) != 0) { + dprintf("read_cache: can't create new thread (%s)\n",strerror(err)); + } + } + + return me; +} + +void dvdnav_read_cache_free(read_cache_t* self) { + dvdnav_t *tmp; + + pthread_mutex_lock(&self->cache_lock); + + if(self->buffer) { + free(self->buffer); + self->buffer = NULL; + self->size = -2; + } + + pthread_mutex_unlock(&self->cache_lock); + + pthread_join(self->read_thread, NULL); + + pthread_mutex_destroy(&self->cache_lock); + + tmp = self->dvd_self; + free(self); + + /* We free the main structure, too, because we have no buffers out there. */ + free(tmp); +} + +/* This function MUST be called whenever self->file changes. */ +void dvdnav_read_cache_clear(read_cache_t *self) { + if(!self) + return; + + pthread_mutex_lock(&self->cache_lock); + self->size = -1; + self->start = 0; + self->pos = 0; + self->read_point = 0; + pthread_mutex_unlock(&self->cache_lock); +} + +/* This function is called just after reading the NAV packet. */ +void dvdnav_pre_cache_blocks(read_cache_t *self, int sector, size_t block_count) { + if(!self) + return; + + if(!self->dvd_self->use_read_ahead) { + return; + } + + pthread_mutex_lock(&self->cache_lock); + dprintf("Requested pre-cache (%i -> +%i) : current state pos=%i, size=%i.\n", + sector, block_count, self->pos, self->size); + + /* Are the contents of the buffer in any way relevant? */ + if((self->size > 0) && (sector >= self->pos) && (sector <= self->pos+self->size)) { + dprintf("Contents relevant ... adjusting\n"); + self->read_point = sector; + } else { + /* Flush the cache as its not much use */ + dprintf("Contents irrelevent... flushing\n"); + self->size = 0; + self->start = 0; + self->pos = sector; + self->read_point = sector; + } + + pthread_mutex_unlock(&self->cache_lock); +} + +/* This function will do the cache read once implemented */ +int dvdnav_read_cache_block( read_cache_t *self, int sector, size_t block_count, uint8_t **buf) { + int result, diff; + + if(!self) + return 0; + + pthread_mutex_lock(&self->cache_lock); + dprintf("Read from %i -> +%i (buffer pos=%i, read_point=%i, size=%i)... ", sector, block_count, + self->pos, self->read_point, self->size); + if((self->size > 0) && (sector >= self->read_point) && + (sector + block_count <= self->pos + self->size)) { + /* Hit */ + + /* Drop any skipped blocks */ + diff = sector - self->read_point; + if(diff > 0) + self->read_point += diff; + + diff = self->read_point - self->pos; + + if(((self->start + diff) % CACHE_BUFFER_SIZE) + block_count <= CACHE_BUFFER_SIZE) { + dprintf("************** Single read\n"); + memcpy(*buf, self->buffer + (((self->start + diff) % CACHE_BUFFER_SIZE) * DVD_VIDEO_LB_LEN), + block_count * DVD_VIDEO_LB_LEN); + self->read_point += block_count; + pthread_mutex_unlock(&self->cache_lock); + + return (int)block_count; + } else { + int32_t boundary = CACHE_BUFFER_SIZE - self->start; + + dprintf("************** Multiple read\n"); + memcpy(*buf, self->buffer + (((self->start + diff) % CACHE_BUFFER_SIZE) * DVD_VIDEO_LB_LEN), + boundary * DVD_VIDEO_LB_LEN); + memcpy(*buf + (boundary * DVD_VIDEO_LB_LEN), self->buffer, + (block_count-boundary) * DVD_VIDEO_LB_LEN); + self->read_point += block_count; + pthread_mutex_unlock(&self->cache_lock); + + return (int)block_count; + } + } else { + /* Miss */ + + fprintf(stderr, "DVD read cache miss! (not bad but a performance hit) sector=%d\n", sector); + result = DVDReadBlocks( self->dvd_self->file, sector, block_count, *buf); + self->read_point = sector+block_count; + if(self->read_point > self->pos + self->size) { + /* Flush the cache as its not much use */ + dprintf("Contents irrelevent... flushing\n"); + self->size = 0; + self->start = 0; + self->pos = sector+block_count; + } + pthread_mutex_unlock(&self->cache_lock); + usleep(300); + return result; + } + + /* Should never get here */ + return 0; +} + +dvdnav_status_t dvdnav_free_cache_block(dvdnav_t *self, unsigned char *buf) { + return DVDNAV_STATUS_OK; +} + +#else + +read_cache_t *dvdnav_read_cache_new(dvdnav_t* dvd_self) { + read_cache_t *self; + int i; + + self = (read_cache_t *)malloc(sizeof(read_cache_t)); + + if(self) { + self->current = 0; + self->freeing = 0; + self->dvd_self = dvd_self; + pthread_mutex_init(&self->lock, NULL); + dvdnav_read_cache_clear(self); + for (i = 0; i < READ_CACHE_CHUNKS; i++) { + self->chunk[i].cache_buffer = NULL; + self->chunk[i].usage_count = 0; + } + } + + return self; +} + +void dvdnav_read_cache_free(read_cache_t* self) { + dvdnav_t *tmp; + int i; + + pthread_mutex_lock(&self->lock); + self->freeing = 1; + for (i = 0; i < READ_CACHE_CHUNKS; i++) + if (self->chunk[i].cache_buffer && self->chunk[i].usage_count == 0) { + free(self->chunk[i].cache_buffer); + self->chunk[i].cache_buffer = NULL; + } + pthread_mutex_unlock(&self->lock); + + for (i = 0; i < READ_CACHE_CHUNKS; i++) + if (self->chunk[i].cache_buffer) return; + + /* all buffers returned, free everything */ + tmp = self->dvd_self; + pthread_mutex_destroy(&self->lock); + free(self); + free(tmp); +} + +/* This function MUST be called whenever self->file changes. */ +void dvdnav_read_cache_clear(read_cache_t *self) { + int i; + + if(!self) + return; + + pthread_mutex_lock(&self->lock); + for (i = 0; i < READ_CACHE_CHUNKS; i++) + self->chunk[i].cache_valid = 0; + pthread_mutex_unlock(&self->lock); +} + +#ifdef DVDNAV_PROFILE +//#ifdef ARCH_X86 +__inline__ unsigned long long int dvdnav_rdtsc() +{ + unsigned long long int x; + __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); + return x; +} +//#endif +#endif + +/* This function is called just after reading the NAV packet. */ +void dvdnav_pre_cache_blocks(read_cache_t *self, int sector, size_t block_count) { + int i, use, result; +#ifdef DVDNAV_PROFILE + struct timeval tv1, tv2, tv3; + unsigned long long p1, p2, p3; +#endif + + if(!self) + return; + + if(!self->dvd_self->use_read_ahead) + return; + + pthread_mutex_lock(&self->lock); + + /* find a free cache chunk that best fits the required size */ + use = -1; + for (i = 0; i < READ_CACHE_CHUNKS; i++) + if (self->chunk[i].usage_count == 0 && self->chunk[i].cache_buffer && + self->chunk[i].cache_malloc_size >= block_count && + (use == -1 || self->chunk[use].cache_malloc_size > self->chunk[i].cache_malloc_size)) + use = i; + + if (use == -1) { + /* we haven't found a cache chunk, so we try to reallocate an existing one */ + for (i = 0; i < READ_CACHE_CHUNKS; i++) + if (self->chunk[i].usage_count == 0 && self->chunk[i].cache_buffer && + (use == -1 || self->chunk[use].cache_malloc_size < self->chunk[i].cache_malloc_size)) + use = i; + if (use >= 0) { + self->chunk[use].cache_buffer = realloc(self->chunk[use].cache_buffer, + block_count * DVD_VIDEO_LB_LEN); + dprintf("pre_cache DVD read realloc happened\n"); + self->chunk[use].cache_malloc_size = block_count; + } else { + /* we still haven't found a cache chunk, let's allocate a new one */ + for (i = 0; i < READ_CACHE_CHUNKS; i++) + if (!self->chunk[i].cache_buffer) { + use = i; + break; + } + if (use >= 0) { + /* We start with a sensible figure for the first malloc of 500 blocks. + * Some DVDs I have seen venture to 450 blocks. + * This is so that fewer realloc's happen if at all. + */ + self->chunk[i].cache_buffer = malloc((block_count > 500 ? block_count : 500) * DVD_VIDEO_LB_LEN); + self->chunk[i].cache_malloc_size = block_count > 500 ? block_count : 500; + dprintf("pre_cache DVD read malloc %d blocks\n", + (block_count > 500 ? block_count : 500 )); + } + } + } + + if (use >= 0) { + self->chunk[use].cache_start_sector = sector; + self->chunk[use].cache_block_count = block_count; + self->current = use; +#ifdef DVDNAV_PROFILE + gettimeofday(&tv1, NULL); + p1 = dvdnav_rdtsc(); +#endif + result = DVDReadBlocks (self->dvd_self->file, sector, block_count, self->chunk[use].cache_buffer); +#ifdef DVDNAV_PROFILE + p2 = dvdnav_rdtsc(); + gettimeofday(&tv2, NULL); + timersub(&tv2, &tv1, &tv3); + dprintf("pre_cache DVD read %ld us, profile = %lld, block_count = %d\n", + tv3.tv_usec, p2-p1, block_count); +#endif + self->chunk[use].cache_valid = 1; + } else + dprintf("pre_caching was impossible, no cache chunk available\n"); + + pthread_mutex_unlock(&self->lock); +} + +int dvdnav_read_cache_block(read_cache_t *self, int sector, size_t block_count, uint8_t **buf) { + int i, use; + + if(!self) + return 0; + + pthread_mutex_lock(&self->lock); + + use = -1; + if(self->dvd_self->use_read_ahead) { + /* first check, if sector is in current chunk */ + read_cache_chunk_t cur = self->chunk[self->current]; + if (cur.cache_valid && sector >= cur.cache_start_sector && + sector + block_count <= cur.cache_start_sector + cur.cache_block_count) + use = self->current; + else + for (i = 0; i < READ_CACHE_CHUNKS; i++) + if (self->chunk[i].cache_valid && sector >= self->chunk[i].cache_start_sector && + sector + block_count <= self->chunk[i].cache_start_sector + self->chunk[i].cache_block_count) + use = i; + } + + if (use >= 0) { + self->chunk[use].usage_count++; + *buf = &self->chunk[use].cache_buffer[(sector - self->chunk[use].cache_start_sector) * + DVD_VIDEO_LB_LEN * block_count]; + pthread_mutex_unlock(&self->lock); + return DVD_VIDEO_LB_LEN * block_count; + } else { + if (self->dvd_self->use_read_ahead) + dprintf("cache miss on sector %d\n", sector); + pthread_mutex_unlock(&self->lock); + return DVDReadBlocks(self->dvd_self->file, sector, block_count, *buf); + } +} + +dvdnav_status_t dvdnav_free_cache_block(dvdnav_t *self, unsigned char *buf) { + read_cache_t *cache; + int i; + + if (!self) + return DVDNAV_STATUS_ERR; + + cache = self->cache; + if (!cache) + return DVDNAV_STATUS_ERR; + + pthread_mutex_lock(&cache->lock); + for (i = 0; i < READ_CACHE_CHUNKS; i++) + if (cache->chunk[i].cache_buffer && buf >= cache->chunk[i].cache_buffer && + buf < cache->chunk[i].cache_buffer + cache->chunk[i].cache_malloc_size * DVD_VIDEO_LB_LEN) + cache->chunk[i].usage_count--; + pthread_mutex_unlock(&cache->lock); + + if (cache->freeing) + /* when we want to dispose the cache, try freeing it now */ + dvdnav_read_cache_free(cache); + + return DVDNAV_STATUS_OK; +} + +#endif diff --git a/src/input/libdvdnav/read_cache.h b/src/input/libdvdnav/read_cache.h new file mode 100644 index 000000000..23c58c58e --- /dev/null +++ b/src/input/libdvdnav/read_cache.h @@ -0,0 +1,51 @@ +/* + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: read_cache.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $ + * + */ + +#ifndef __DVDNAV_READ_CACHE_H +#define __DVDNAV_READ_CACHE_H + +#include "dvdnav_internal.h" + +/* Opaque cache type -- defined in dvdnav_internal.h */ +/* typedef struct read_cache_s read_cache_t; */ + +/* EXPERIMENTAL: Setting the following to 1 will use an experimental multi-threaded + * read-ahead cache. + */ +#define _MULTITHREAD_ 0 + +/* Constructor/destructors */ +read_cache_t *dvdnav_read_cache_new(dvdnav_t* dvd_self); +void dvdnav_read_cache_free(read_cache_t* self); + +/* This function MUST be called whenever self->file changes. */ +void dvdnav_read_cache_clear(read_cache_t *self); +/* This function is called just after reading the NAV packet. */ +void dvdnav_pre_cache_blocks(read_cache_t *self, int sector, size_t block_count); +/* This function will do the cache read. + * The buffer handed in must be malloced to take one dvd block. + * On a cache hit, a different buffer will be returned though. + * Those buffers must _never_ be freed. */ +int dvdnav_read_cache_block(read_cache_t *self, int sector, size_t block_count, uint8_t **buf); + +#endif /* __DVDNAV_READ_CACHE_H */ diff --git a/src/input/libdvdnav/searching.c b/src/input/libdvdnav/searching.c new file mode 100644 index 000000000..43ac770f4 --- /dev/null +++ b/src/input/libdvdnav/searching.c @@ -0,0 +1,443 @@ +/* + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: searching.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $ + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <dvdnav.h> +#include "dvdnav_internal.h" + +#include "vm.h" +#include <dvdread/nav_types.h> + +/* Searching API calls */ + +dvdnav_status_t dvdnav_time_search(dvdnav_t *this, + unsigned long int time) { +/* Time search the current PGC based on the xxx table */ + return S_OK; +} + +/* Scan the ADMAP for a particular block number. */ +/* Return placed in vobu. */ +/* Returns error status */ + +dvdnav_status_t dvdnav_scan_admap(dvdnav_t *this, int32_t domain, int32_t seekto_block, int32_t *vobu) { + /* FIXME:Need to handle seeking outside current cell. */ + vobu_admap_t *admap = NULL; + *vobu = -1; + fprintf(stderr,"Seeking to target %u ...\n", + seekto_block); + + /* Search through the VOBU_ADMAP for the nearest VOBU + * to the target block */ + switch(domain) { + case FP_DOMAIN: + case VMGM_DOMAIN: + admap = this->vm->vmgi->menu_vobu_admap; + break; + case VTSM_DOMAIN: + admap = this->vm->vtsi->menu_vobu_admap; + break; + case VTS_DOMAIN: + admap = this->vm->vtsi->vts_vobu_admap; + break; + default: + fprintf(stderr,"Error: Unknown domain for seeking seek.\n"); + } + if(admap) { + int32_t address = 0; + int32_t vobu_start, next_vobu; + int found = 0; + + /* Search through ADMAP for best sector */ + vobu_start = 0x3fffffff; + /* FIXME: Implement a faster search algorithm */ + while((!found) && ((address<<2) < admap->last_byte)) { + next_vobu = admap->vobu_start_sectors[address]; + + /* printf("Found block %u\n", next_vobu); */ + + if(vobu_start <= seekto_block && + next_vobu > seekto_block) { + found = 1; + } else { + vobu_start = next_vobu; + } + + address ++; + } + if(found) { + *vobu = vobu_start; + return S_OK; + } else { + fprintf(stderr,"Could not locate block\n"); + return S_ERR; + } + } + fprintf(stderr,"admap not located\n"); + return S_ERR; +} + +dvdnav_status_t dvdnav_sector_search(dvdnav_t *this, + unsigned long int offset, int origin) { +/* FIXME: Implement */ + + uint32_t target = 0; + uint32_t length = 0; + uint32_t first_cell_nr, last_cell_nr, cell_nr, fnd_cell_nr; + int found; + cell_playback_t *cell, *fnd_cell; + dvd_state_t *state; + dvdnav_status_t result; + + if((!this) || (!this->vm) ) + return -1; + + state = &(this->vm->state); + if((!state) || (!state->pgc) ) + return -1; + + if(offset == 0) + return -1; + + if(this->position_current.still != 0) + /* Cannot do seeking in a still frame. */ + return -1; + + pthread_mutex_lock(&this->vm_lock); + result = dvdnav_get_position(this, &target, &length); + fprintf(stderr,"FIXME: seeking to offset=%lu pos=%u length=%u\n", offset, target, length); + fprintf(stderr,"FIXME: Before cellN=%u blockN=%u\n" , + state->cellN, + state->blockN); + if(!result) { + pthread_mutex_unlock(&this->vm_lock); + return -1; + } + + switch(origin) { + case SEEK_SET: + if(offset > length) { + pthread_mutex_unlock(&this->vm_lock); + return -1; + } + target = offset; + break; + case SEEK_CUR: + if(target + offset > length) { + pthread_mutex_unlock(&this->vm_lock); + return -1; + } + target += offset; + break; + case SEEK_END: + if(length - offset < 0) { + pthread_mutex_unlock(&this->vm_lock); + return -1; + } + target = length - offset; + default: + /* Error occured */ + pthread_mutex_unlock(&this->vm_lock); + return -1; + } + + /* First find closest cell number in program */ + first_cell_nr = state->pgc->program_map[state->pgN-1]; + 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; + } + + found = 0; target += state->pgc->cell_playback[first_cell_nr-1].first_sector; + fnd_cell_nr = last_cell_nr + 1; + for(cell_nr = first_cell_nr; (cell_nr <= last_cell_nr) && !found; cell_nr ++) { + cell = &(state->pgc->cell_playback[cell_nr-1]); + if((cell->first_sector <= target) && (cell->last_sector >= target)) { + state->cellN = cell_nr; + state->blockN = 0; + found = 1; + fnd_cell_nr = cell_nr; + fnd_cell = cell; + } + } + + if(fnd_cell_nr <= last_cell_nr) { + int32_t vobu, start; + dvdnav_status_t status; + fprintf(stderr,"Seeking to cell %i from choice of %i to %i\n", + fnd_cell_nr, first_cell_nr, last_cell_nr); + status = dvdnav_scan_admap(this, state->domain, target, &vobu); + /* + * Clut does not actually change, + * but as the decoders have been closed then opened, + * A new clut has to be sent. + */ + start =(state->pgc->cell_playback[state->cellN - 1].first_sector); + fprintf(stderr,"FIXME: After cellN=%u blockN=%u target=%x vobu=%x start=%x\n" , + state->cellN, + state->blockN, + target, + vobu, + start); + state->blockN = vobu - start; + fprintf(stderr,"FIXME: After vobu=%x start=%x blockN=%x\n" , + vobu, + start, + state->blockN); + pthread_mutex_unlock(&this->vm_lock); + return target; + } else { + fprintf(stderr, "Error when seeking, asked to seek outside program\n"); + } + + fprintf(stderr,"FIXME: Implement seeking to location %u\n", target); + + pthread_mutex_unlock(&this->vm_lock); + return -1; +} + +dvdnav_status_t dvdnav_part_search(dvdnav_t *this, int part) { + + if((!this) || (!this->vm) ) + return S_ERR; + + return S_OK; +} + +dvdnav_status_t dvdnav_prev_pg_search(dvdnav_t *this) { + dvd_state_t *state; + + if((!this) || (!this->vm) ) + return S_ERR; + + state = &(this->vm->state); + if((!state) || (!state->pgc) ) + return S_ERR; + + /* Make sure this is not the first chapter */ + if(state->pgN <= 1 ) { + fprintf(stderr,"dvdnav: at first chapter. prev chapter failed.\n"); + return S_ERR; + } + fprintf(stderr,"dvdnav: previous chapter\n"); + vm_jump_prog(this->vm, state->pgN - 1); + this->vm->hop_channel++; + fprintf(stderr,"dvdnav: previous chapter done\n"); + + return S_OK; +} + +dvdnav_status_t dvdnav_top_pg_search(dvdnav_t *this) { + + if((!this) || (!this->vm) ) + return S_ERR; + + fprintf(stderr,"dvdnav: top chapter. NOP.\n"); + + return S_OK; +} + +dvdnav_status_t dvdnav_next_pg_search(dvdnav_t *this) { + dvd_state_t *state; + + if((!this) || (!this->vm) ) + return S_ERR; + + state = &(this->vm->state); + if((!state) || (!state->pgc) ) + return S_ERR; + + /* Make sure this is not the last chapter */ + if(state->pgN >= state->pgc->nr_of_programs) { + fprintf(stderr,"dvdnav: at last chapter. next chapter failed.\n"); + return S_ERR; + } + fprintf(stderr,"dvdnav: next chapter\n"); + vm_jump_prog(this->vm, state->pgN + 1); + this->vm->hop_channel++; + fprintf(stderr,"dvdnav: next chapter done\n"); + + return S_OK; +} + +dvdnav_status_t dvdnav_menu_call(dvdnav_t *this, DVDMenuID_t menu) { + dvd_state_t *state; + + if((!this) || (!this->vm) ) + return S_ERR; + + pthread_mutex_lock(&this->vm_lock); + state = &(this->vm->state); + if (vm_menu_call(this->vm, menu, 0)) + this->vm->hop_channel++; + pthread_mutex_unlock(&this->vm_lock); + return S_OK; +} + +dvdnav_status_t dvdnav_current_title_info(dvdnav_t *this, int *tt, int *pr) { +int vts_ttn = 0; + int vts, i; + domain_t domain; + tt_srpt_t* srpt; + + if(!this) + return S_ERR; + + if(!tt || !pr) { + printerr("Passed a NULL pointer"); + } + + if(tt) + *tt = -1; + if(*pr) + *pr = -1; + + domain = this->vm->state.domain; + if((domain == FP_DOMAIN) || (domain == VMGM_DOMAIN)) { + /* Not in a title */ + return S_OK; + } + + vts_ttn = this->vm->state.VTS_TTN_REG; + vts = this->vm->state.vtsN; + + if(pr) { + *pr = this->vm->state.pgN; + } + + /* Search TT_SRPT for title */ + if(!(vm_get_vmgi(this->vm))) { + printerr("Oh poo, no SRPT"); + return S_ERR; + } + + srpt = vm_get_vmgi(this->vm)->tt_srpt; + for(i=0; i<srpt->nr_of_srpts; i++) { + title_info_t* info = &(srpt->title[i]); + if((info->title_set_nr == vts) && (info->vts_ttn == vts_ttn)) { + if(tt) + *tt = i+1; + } + } + + return S_OK; +} + +static char __title_str[] = "DVDNAV"; + +dvdnav_status_t dvdnav_get_title_string(dvdnav_t *this, char **title_str) { + if(!this) + return S_ERR; + + if(!title_str) { + printerr("Passed a NULL pointer"); + return S_ERR; + } + + (*title_str) = __title_str; + + return S_OK; +} + +dvdnav_status_t dvdnav_get_position(dvdnav_t *this, unsigned int* pos, + unsigned int *len) { + uint32_t cur_sector; + uint32_t first_cell_nr; + uint32_t last_cell_nr; + cell_playback_t *first_cell; + cell_playback_t *last_cell; + dvd_state_t *state; + if((!this) || (!this->vm) ) + return 0; + + state = &(this->vm->state); + if((!state) || (!state->pgc) ) + return 0; + + /* Sanity check */ + if(state->pgN > state->pgc->nr_of_programs) { + return 0; + } + + /* Get current sector */ + cur_sector = this->vobu.vobu_start + this->vobu.blockN; + + /* Find start cell of program. */ + first_cell_nr = state->pgc->program_map[state->pgN-1]; + first_cell = &(state->pgc->cell_playback[first_cell_nr-1]); + 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; + } + last_cell = &(state->pgc->cell_playback[last_cell_nr-1]); + + *pos= cur_sector - first_cell->first_sector; + *len= last_cell->last_sector - first_cell->first_sector; + /* printf("dvdnav:searching:current pos=%u length=%u\n",*pos,*len); */ + + + return S_OK; +} + +dvdnav_status_t dvdnav_get_position_in_title(dvdnav_t *this, + unsigned int *pos, + unsigned int *len) { + uint32_t cur_sector; + uint32_t first_cell_nr; + uint32_t last_cell_nr; + cell_playback_t *first_cell; + cell_playback_t *last_cell; + dvd_state_t *state; + if((!this) || (!this->vm) ) + return S_ERR; + + state = &(this->vm->state); + if((!state) || (!state->pgc) ) + return S_ERR; + + /* Sanity check */ + if(state->pgN > state->pgc->nr_of_programs) { + return S_ERR; + } + + /* Get current sector */ + cur_sector = this->vobu.vobu_start + this->vobu.blockN; + + /* Now find first and last cells in title. */ + first_cell_nr = state->pgc->program_map[0]; + first_cell = &(state->pgc->cell_playback[first_cell_nr-1]); + last_cell_nr = state->pgc->nr_of_cells; + last_cell = &(state->pgc->cell_playback[last_cell_nr-1]); + + *pos = cur_sector - first_cell->first_sector; + *len = last_cell->last_sector - first_cell->first_sector; + + return S_OK; +} + + diff --git a/src/input/libdvdnav/settings.c b/src/input/libdvdnav/settings.c new file mode 100644 index 000000000..a9a5df45e --- /dev/null +++ b/src/input/libdvdnav/settings.c @@ -0,0 +1,120 @@ +/* + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: settings.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $ + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <dvdnav.h> +#include "dvdnav_internal.h" + +#include "vm.h" + +/* Characteristics/setting API calls */ + +dvdnav_status_t dvdnav_get_region_mask(dvdnav_t *this, int *region) { + if(!this) + return S_ERR; + + if(!region) { + printerr("Passed a NULL pointer"); + return S_ERR; + } + + if(!this->vm) { + printerr("VM not yet initialised"); + return S_ERR; + } + + (*region) = this->vm->state.registers.SPRM[20]; + + return S_OK; +} + +dvdnav_status_t dvdnav_set_region_mask(dvdnav_t *this, int mask) { + if(!this) + return S_ERR; + + if(!this->vm) { + printerr("VM not yet initialised"); + return S_ERR; + } + + this->vm->state.registers.SPRM[20] = (mask & 0xff); + + return S_OK; +} + +dvdnav_status_t dvdnav_set_readahead_flag(dvdnav_t *this, int use_readahead) { + if(!this) + return S_ERR; + + this->use_read_ahead = use_readahead; + + return S_OK; +} + +dvdnav_status_t dvdnav_get_readahead_flag(dvdnav_t *this, int* flag) { + if(!this) + return S_ERR; + + if(!flag) { + printerr("Passed a NULL pointer"); + return S_ERR; + } + + (*flag) = this->use_read_ahead; + return S_OK; +} + +static dvdnav_status_t set_language_register(dvdnav_t *this, char *code, int reg) { + if(!this) + return S_ERR; + + if(!code[0] || !code[1]) { + printerr("Passed illegal language code"); + return S_ERR; + } + + if(!this->vm) { + printerr("VM not yet initialised"); + return S_ERR; + } + + pthread_mutex_lock(&this->vm_lock); + this->vm->state.registers.SPRM[reg] = (code[0] << 8) | code[1]; + pthread_mutex_unlock(&this->vm_lock); + return S_OK; +} + +dvdnav_status_t dvdnav_menu_language_select(dvdnav_t *this, char *code) { + return set_language_register(this, code, 0); +} + +dvdnav_status_t dvdnav_audio_language_select(dvdnav_t *this, char *code) { + return set_language_register(this, code, 16); +} + +dvdnav_status_t dvdnav_spu_language_select(dvdnav_t *this, char *code) { + return set_language_register(this, code, 18); +} diff --git a/src/input/libdvdnav/vm.c b/src/input/libdvdnav/vm.c new file mode 100644 index 000000000..ec4ac75fe --- /dev/null +++ b/src/input/libdvdnav/vm.c @@ -0,0 +1,1905 @@ +/* + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: vm.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $ + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <inttypes.h> +#include <assert.h> + +#include <dvdread/ifo_types.h> +#include <dvdread/ifo_read.h> + +#include "decoder.h" +#include "vmcmd.h" +#include "vm.h" +#include "dvdnav_internal.h" + +/* Local prototypes */ + +static void saveRSMinfo(vm_t *vm,int cellN, int blockN); +static link_t play_PGC(vm_t *vm); +static link_t play_PGC_PG(vm_t *vm, int pgN); +static link_t play_PGC_post(vm_t *vm); +static link_t play_PG(vm_t *vm); +static link_t play_Cell(vm_t *vm); +static link_t play_Cell_post(vm_t *vm); +static link_t process_command(vm_t *vm,link_t link_values); + +static void ifoOpenNewVTSI(vm_t *vm,dvd_reader_t *dvd, int vtsN); +static pgcit_t* get_PGCIT(vm_t *vm); + +/* get_XYZ returns a value. + * ser_XYZ sets state using passed parameters. + * returns success/failure. + */ + +/* Can only be called when in VTS_DOMAIN */ +static int set_FP_PGC(vm_t *vm); /* FP */ +static int set_TT(vm_t *vm,int tt); +static int set_VTS_TT(vm_t *vm,int vtsN, int vts_ttn); +static int set_VTS_PTT(vm_t *vm,int vtsN, int vts_ttn, int part); + +static int set_MENU(vm_t *vm,int menu); /* VTSM & VMGM */ + +/* Called in any domain */ +static int get_TT(vm_t *vm, int vtsN, int vts_ttn); +static int get_ID(vm_t *vm,int id); +static int get_PGCN(vm_t *vm); +static int set_PGN(vm_t *vm); /* Set PGN based on (vm->state).CellN */ +static int set_PGC(vm_t *vm,int pgcN); + +/* Initialisation */ + +vm_t* vm_new_vm() { + vm_t *vm = (vm_t*)calloc(sizeof(vm_t), sizeof(char)); + + return vm; +} + +static void vm_print_current_domain_state(vm_t *vm) { + switch((vm->state).domain) { + case VTS_DOMAIN: + fprintf(stderr, "Video Title Domain: -\n"); + break; + + case VTSM_DOMAIN: + fprintf(stderr, "Video Title Menu Domain: -\n"); + break; + + case VMGM_DOMAIN: + fprintf(stderr, "Video Manager Menu Domain: -\n"); + break; + + case FP_DOMAIN: + fprintf(stderr, "First Play Domain: -\n"); + break; + + default: + fprintf(stderr, "Unknown Domain: -\n"); + break; + } + fprintf(stderr, "VTS:%d PG:%u CELL:%u BLOCK:%u VTS_TTN:%u TTN:%u TT_PGCN:%u\n", + (vm->state).vtsN, + (vm->state).pgN, + (vm->state).cellN, + (vm->state).blockN, + (vm->state).VTS_TTN_REG, + (vm->state).TTN_REG, + (vm->state).TT_PGCN_REG); +} + +void vm_stop(vm_t *vm) { + if(!vm) + return; + + if(vm->vmgi) { + ifoClose(vm->vmgi); + vm->vmgi=NULL; + } + + if(vm->vtsi) { + ifoClose(vm->vtsi); + vm->vmgi=NULL; + } + + if(vm->dvd) { + DVDClose(vm->dvd); + vm->dvd=NULL; + } +} + +void vm_free_vm(vm_t *vm) { + if(vm) { + vm_stop(vm); + free(vm); + } +} + +/* IFO Access */ + +ifo_handle_t *vm_get_vmgi(vm_t *vm) { + if(!vm) + return NULL; + + return vm->vmgi; +} + +ifo_handle_t *vm_get_vtsi(vm_t *vm) { + if(!vm) + return NULL; + + return vm->vtsi; +} + +/* Reader Access */ + +dvd_reader_t *vm_get_dvd_reader(vm_t *vm) { + if(!vm) + return NULL; + + return vm->dvd; +} + +int vm_reset(vm_t *vm, char *dvdroot) /* , register_t regs) */ { + /* Setup State */ + memset((vm->state).registers.SPRM, 0, sizeof(uint16_t)*24); + 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 */ + /* Player Regional Code Mask. + * bit0 = Region 1 + * bit1 = Region 2 + */ + (vm->state).registers.SPRM[20] = 0x1; /* Player Regional Code Mask. Region free! */ + (vm->state).registers.SPRM[14] = 0x100; /* Try Pan&Scan */ + + (vm->state).pgN = 0; + (vm->state).cellN = 0; + (vm->state).cell_restart = 0; + + (vm->state).domain = FP_DOMAIN; + (vm->state).rsm_vtsN = 0; + (vm->state).rsm_cellN = 0; + (vm->state).rsm_blockN = 0; + + (vm->state).vtsN = -1; + + if (vm->dvd && dvdroot) { + /* a new dvd device has been requested */ + vm_stop(vm); + } + if (!vm->dvd) { + vm->dvd = DVDOpen(dvdroot); + if(!vm->dvd) { + fprintf(stderr, "vm: faild to open/read the DVD\n"); + return -1; + } + + vm->vmgi = ifoOpenVMGI(vm->dvd); + if(!vm->vmgi) { + fprintf(stderr, "vm: faild to read VIDEO_TS.IFO\n"); + return -1; + } + if(!ifoRead_FP_PGC(vm->vmgi)) { + fprintf(stderr, "vm: ifoRead_FP_PGC failed\n"); + return -1; + } + if(!ifoRead_TT_SRPT(vm->vmgi)) { + fprintf(stderr, "vm: ifoRead_TT_SRPT failed\n"); + return -1; + } + if(!ifoRead_PGCI_UT(vm->vmgi)) { + fprintf(stderr, "vm: ifoRead_PGCI_UT failed\n"); + return -1; + } + if(!ifoRead_PTL_MAIT(vm->vmgi)) { + fprintf(stderr, "vm: ifoRead_PTL_MAIT failed\n"); + ; /* return -1; Not really used for now.. */ + } + if(!ifoRead_VTS_ATRT(vm->vmgi)) { + fprintf(stderr, "vm: ifoRead_VTS_ATRT failed\n"); + ; /* return -1; Not really used for now.. */ + } + if(!ifoRead_VOBU_ADMAP(vm->vmgi)) { + fprintf(stderr, "vm: ifoRead_VOBU_ADMAP vgmi failed\n"); + ; /* return -1; Not really used for now.. */ + } + /* ifoRead_TXTDT_MGI(vmgi); Not implemented yet */ + } + else fprintf(stderr, "vm: reset\n"); + if (vm->vmgi) { + fprintf(stderr, "DVD disk reports itself with Region mask 0x%08x. Maybe region %u.\n", + vm->vmgi->vmgi_mat->vmg_category, + (((vm->vmgi->vmgi_mat->vmg_category >> 16) ^ 0xff) & 0xff) ); + } + return 0; +} + +/* FIXME TODO XXX $$$ Handle error condition too... */ +int vm_start(vm_t *vm) +{ + link_t link_values; + + /* Set pgc to FP(First Play) pgc */ + set_FP_PGC(vm); + link_values = play_PGC(vm); + link_values = process_command(vm,link_values); + assert(link_values.command == PlayThis); + (vm->state).blockN = link_values.data1; +#ifdef TRACE + fprintf(stderr, "vm_start: blockN set to 0x%x\n", (vm->state).blockN); +#endif + assert( (vm->state).blockN == 0 ); + + + return 0; /* ?? */ +} + +int vm_position_get(vm_t *vm, vm_position_t *position) { + position->button = (vm->state).HL_BTNN_REG >> 10; + 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->vts = (vm->state).vtsN; + position->domain = (vm->state).domain; + position->cell = (vm->state).cellN; + position->cell_restart = (vm->state).cell_restart; + position->still = (vm->state).pgc->cell_playback[(vm->state).cellN - 1].still_time; + position->vobu_start = (vm->state).pgc->cell_playback[(vm->state).cellN - 1].first_sector; + position->vobu_next = (vm->state).blockN; + /* position->vobu_next = 0; Just for now */ + return 1; +} + +int vm_position_print(vm_t *vm, vm_position_t *position) { + fprintf(stderr, "But=%x Spu=%x Aud=%x Ang=%x Hop=%x vts=%x dom=%x cell=%x cell_restart=%x still=%x start=%x next=%x\n", + position->button, + position->spu_channel, + position->audio_channel, + position->angle_channel, + position->hop_channel, + position->vts, + position->domain, + position->cell, + position->cell_restart, + position->still, + position->vobu_start, + position->vobu_next); + return 1; +} + + +int vm_start_title(vm_t *vm, int tt) { + link_t link_values; + + set_TT(vm, tt); + link_values = play_PGC(vm); + link_values = process_command(vm, link_values); + assert(link_values.command == PlayThis); + (vm->state).blockN = link_values.data1; +#ifdef TRACE + fprintf(stderr, "vm_start_title: blockN set to 0x%x\n", (vm->state).blockN); +#endif + assert( (vm->state).blockN == 0 ); + + return 0; /* ?? */ +} + +int vm_jump_prog(vm_t *vm, int pr) { + link_t link_values; + + + set_PGC(vm, get_PGCN(vm)); + (vm->state).pgN = pr; /* ?? set_PGC() clobbers pgN */ + link_values = play_PG(vm); + link_values = process_command(vm, link_values); + assert(link_values.command == PlayThis); + (vm->state).blockN = link_values.data1; +#ifdef TRACE + fprintf(stderr, "vm_jump_prog: blockN set to 0x%x\n", (vm->state).blockN); +#endif + assert( (vm->state).blockN == 0 ); + + return 0; /* ?? */ +} + +int vm_eval_cmd(vm_t *vm, vm_cmd_t *cmd) +{ + link_t link_values; + + if(vmEval_CMD(cmd, 1, &(vm->state).registers, &link_values)) { + link_values = process_command(vm, link_values); + assert(link_values.command == PlayThis); + (vm->state).blockN = link_values.data1; +#ifdef TRACE + fprintf(stderr, "vm_eval_cmd: blockN set to 0x%x\n", (vm->state).blockN); +#endif + assert( (vm->state).blockN == 0 ); + return 1; /* Something changed, Jump */ + } else { + return 0; /* It updated some state thats all... */ + } +} + +int vm_get_next_cell(vm_t *vm) +{ + link_t link_values; + link_values = play_Cell_post(vm); + link_values = process_command(vm,link_values); + assert(link_values.command == PlayThis); + (vm->state).blockN = link_values.data1; +#ifdef TRACE + fprintf(stderr, "vm_get_next_cell: blockN set to 0x%x\n", (vm->state).blockN); +#endif + assert( (vm->state).blockN == 0 ); + + return 0; /* ?? */ +} + +int vm_top_pg(vm_t *vm) +{ + link_t link_values; + link_values = play_PG(vm); + link_values = process_command(vm,link_values); + assert(link_values.command == PlayThis); + (vm->state).blockN = link_values.data1; +#ifdef TRACE + fprintf(stderr, "vm_top_pg: blockN set to 0x%x\n", (vm->state).blockN); +#endif + assert( (vm->state).blockN == 0 ); + + return 1; /* Jump */ +} + +int vm_go_up(vm_t *vm) +{ + link_t link_values; + + if(set_PGC(vm, (vm->state).pgc->goup_pgc_nr)) + assert(0); + + link_values = play_PGC(vm); + link_values = process_command(vm,link_values); + assert(link_values.command == PlayThis); + (vm->state).blockN = link_values.data1; +#ifdef TRACE + fprintf(stderr, "vm_go_up: blockN set to 0x%x\n", (vm->state).blockN); +#endif + assert( (vm->state).blockN == 0 ); + + return 1; /* Jump */ +} + +int vm_next_pg(vm_t *vm) +{ + /* Do we need to get a updated pgN value first? */ + (vm->state).pgN += 1; + return vm_top_pg(vm); +} + +int vm_prev_pg(vm_t *vm) +{ + /* Do we need to get a updated pgN value first? */ + (vm->state).pgN -= 1; + if((vm->state).pgN == 0) { + /* Check for previous PGCN ? */ + (vm->state).pgN = 1; + /* return 0; */ + } + return vm_top_pg(vm); +} + + +static domain_t menuid2domain(DVDMenuID_t menuid) +{ + domain_t result = VTSM_DOMAIN; /* Really shouldn't have to.. */ + + switch(menuid) { + case DVD_MENU_Title: + result = VMGM_DOMAIN; + break; + case DVD_MENU_Root: + case DVD_MENU_Subpicture: + case DVD_MENU_Audio: + case DVD_MENU_Angle: + case DVD_MENU_Part: + result = VTSM_DOMAIN; + break; + } + + return result; +} + +int vm_menu_call(vm_t *vm, DVDMenuID_t menuid, int block) +{ + domain_t old_domain; + link_t link_values; + + /* Should check if we are allowed/can acces this menu */ + + + /* FIXME XXX $$$ How much state needs to be restored + * when we fail to find a menu? */ + + old_domain = (vm->state).domain; + + switch((vm->state).domain) { + case VTS_DOMAIN: + saveRSMinfo(vm, 0, block); + /* FALL THROUGH */ + case VTSM_DOMAIN: + case VMGM_DOMAIN: + (vm->state).domain = menuid2domain(menuid); + if(get_PGCIT(vm) != NULL && set_MENU(vm, menuid) != -1) { + link_values = play_PGC(vm); + link_values = process_command(vm, link_values); + assert(link_values.command == PlayThis); + (vm->state).blockN = link_values.data1; +#ifdef TRACE + fprintf(stderr, "vm_menu_call: blockN set to 0x%x\n", (vm->state).blockN); +#endif + assert( (vm->state).blockN == 0 ); + return 1; /* Jump */ + } else { + (vm->state).domain = old_domain; + } + break; + case FP_DOMAIN: /* FIXME XXX $$$ What should we do here? */ + break; + } + + return 0; +} + + +int vm_resume(vm_t *vm) +{ + int i; + link_t link_values; + + /* Check and see if there is any rsm info!! */ + if((vm->state).rsm_vtsN == 0) { + return 0; + } + + (vm->state).domain = VTS_DOMAIN; + ifoOpenNewVTSI(vm, vm->dvd, (vm->state).rsm_vtsN); + set_PGC(vm, (vm->state).rsm_pgcN); + + /* These should never be set in SystemSpace and/or MenuSpace */ + /* (vm->state).TTN_REG = (vm->state).rsm_tt; */ + /* (vm->state).TT_PGCN_REG = (vm->state).rsm_pgcN; */ + /* (vm->state).HL_BTNN_REG = (vm->state).rsm_btnn; */ + for(i = 0; i < 5; i++) { + (vm->state).registers.SPRM[4 + i] = (vm->state).rsm_regs[i]; + } + + if((vm->state).rsm_cellN == 0) { + assert((vm->state).cellN); /* Checking if this ever happens */ + (vm->state).pgN = 1; + link_values = play_PG(vm); + link_values = process_command(vm, link_values); + assert(link_values.command == PlayThis); + (vm->state).blockN = link_values.data1; +#ifdef TRACE + fprintf(stderr, "vm_resume1: blockN set to 0x%x\n", (vm->state).blockN); +#endif + assert( (vm->state).blockN == 0 ); + } else { + (vm->state).cellN = (vm->state).rsm_cellN; + (vm->state).blockN = (vm->state).rsm_blockN; +#ifdef TRACE + fprintf(stderr, "vm_resume2: blockN set to 0x%x\n", (vm->state).blockN); +#endif + /* (vm->state).pgN = ?? does this gets the righ value in play_Cell, no! */ + if(set_PGN(vm)) { + /* Were at or past the end of the PGC, should not happen for a RSM */ + assert(0); + play_PGC_post(vm); + } + } + + return 1; /* Jump */ +} + +/** + * Return the substream id for 'logical' audio stream audioN. + * 0 <= audioN < 8 + */ +int vm_get_audio_stream(vm_t *vm, int audioN) +{ + int streamN = -1; +#ifdef TRACE + fprintf(stderr,"dvdnav:vm.c:get_audio_stream audioN=%d\n",audioN); +#endif + if((vm->state).domain == VTSM_DOMAIN + || (vm->state).domain == VMGM_DOMAIN + || (vm->state).domain == FP_DOMAIN) { + audioN = 0; + } + + if(audioN < 8) { + /* Is there any contol info for this logical stream */ + if((vm->state).pgc->audio_control[audioN] & (1<<15)) { + streamN = ((vm->state).pgc->audio_control[audioN] >> 8) & 0x07; + } + } + + if((vm->state).domain == VTSM_DOMAIN + || (vm->state).domain == VMGM_DOMAIN + || (vm->state).domain == FP_DOMAIN) { + if(streamN == -1) + streamN = 0; + } + + /* Should also check in vtsi/vmgi status that what kind of stream + * it is (ac3/lpcm/dts/sdds...) to find the right (sub)stream id */ + return streamN; +} + +/** + * Return the substream id for 'logical' subpicture stream subpN and given mode. + * 0 <= subpN < 32 + * mode == 0 - widescreen + * mode == 1 - letterbox + * mode == 2 - pan&scan + */ +int vm_get_subp_stream(vm_t *vm, int subpN, int mode) +{ + int streamN = -1; + int source_aspect = vm_get_video_aspect(vm); + + if((vm->state).domain == VTSM_DOMAIN + || (vm->state).domain == VMGM_DOMAIN + || (vm->state).domain == FP_DOMAIN) { + subpN = 0; + } + + if(subpN < 32) { /* a valid logical stream */ + /* Is this logical stream present */ + if((vm->state).pgc->subp_control[subpN] & (1<<31)) { + if(source_aspect == 0) /* 4:3 */ + streamN = ((vm->state).pgc->subp_control[subpN] >> 24) & 0x1f; + if(source_aspect == 3) /* 16:9 */ + switch (mode) { + case 0: + streamN = ((vm->state).pgc->subp_control[subpN] >> 16) & 0x1f; + break; + case 1: + streamN = ((vm->state).pgc->subp_control[subpN] >> 8) & 0x1f; + break; + case 2: + streamN = (vm->state).pgc->subp_control[subpN] & 0x1f; + } + } + } + + /* Paranoia.. if no stream select 0 anyway */ +/* I am not paranoid */ +/* if((vm->state).domain == VTSM_DOMAIN + || (vm->state).domain == VMGM_DOMAIN + || (vm->state).domain == FP_DOMAIN) { + if(streamN == -1) + streamN = 0; + } +*/ + /* Should also check in vtsi/vmgi status that what kind of stream it is. */ + return streamN; +} + +int vm_get_subp_active_stream(vm_t *vm, int mode) +{ + int subpN; + int streamN; + subpN = (vm->state).SPST_REG & ~0x40; + streamN = vm_get_subp_stream(vm, subpN, mode); + + /* If no such stream, then select the first one that exists. */ + if(streamN == -1) { + for(subpN = 0; subpN < 32; subpN++) { + if((vm->state).pgc->subp_control[subpN] & (1<<31)) { + + streamN = vm_get_subp_stream(vm, subpN, mode); + break; + } + } + } + + /* We should instead send the on/off status to the spudecoder / mixer */ + /* If we are in the title domain see if the spu mixing is on */ + if((vm->state).domain == VTS_DOMAIN && !((vm->state).SPST_REG & 0x40)) { + /* Bit 7 set means hide, and only let Forced display show */ + return (streamN | 0x80); + } else { + return streamN; + } +} + +int vm_get_audio_active_stream(vm_t *vm) +{ + int audioN; + int streamN; + audioN = (vm->state).AST_REG ; + streamN = vm_get_audio_stream(vm, audioN); + + /* If no such stream, then select the first one that exists. */ + if(streamN == -1) { + for(audioN = 0; audioN < 8; audioN++) { + if((vm->state).pgc->audio_control[audioN] & (1<<15)) { + streamN = vm_get_audio_stream(vm, audioN); + break; + } + } + } + + return streamN; +} + + +void vm_get_angle_info(vm_t *vm, int *num_avail, int *current) +{ + *num_avail = 1; + *current = 1; + + if((vm->state).domain == VTS_DOMAIN) { + /* TTN_REG does not allways point to the correct title.. */ + title_info_t *title; + if((vm->state).TTN_REG > vm->vmgi->tt_srpt->nr_of_srpts) + return; + title = &vm->vmgi->tt_srpt->title[(vm->state).TTN_REG - 1]; + if(title->title_set_nr != (vm->state).vtsN || + title->vts_ttn != (vm->state).VTS_TTN_REG) + return; + *num_avail = title->nr_of_angles; + *current = (vm->state).AGL_REG; + if(*current > *num_avail) /* Is this really a good idea? */ + *current = *num_avail; + } +} + + +void vm_get_audio_info(vm_t *vm, int *num_avail, int *current) +{ + if((vm->state).domain == VTS_DOMAIN) { + *num_avail = vm->vtsi->vtsi_mat->nr_of_vts_audio_streams; + *current = (vm->state).AST_REG; + } else if((vm->state).domain == VTSM_DOMAIN) { + *num_avail = vm->vtsi->vtsi_mat->nr_of_vtsm_audio_streams; /* 1 */ + *current = 1; + } else if((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN) { + *num_avail = vm->vmgi->vmgi_mat->nr_of_vmgm_audio_streams; /* 1 */ + *current = 1; + } +} + +void vm_get_subp_info(vm_t *vm, int *num_avail, int *current) +{ + if((vm->state).domain == VTS_DOMAIN) { + *num_avail = vm->vtsi->vtsi_mat->nr_of_vts_subp_streams; + *current = (vm->state).SPST_REG; + } else if((vm->state).domain == VTSM_DOMAIN) { + *num_avail = vm->vtsi->vtsi_mat->nr_of_vtsm_subp_streams; /* 1 */ + *current = 0x41; + } else if((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN) { + *num_avail = vm->vmgi->vmgi_mat->nr_of_vmgm_subp_streams; /* 1 */ + *current = 0x41; + } +} + +subp_attr_t vm_get_subp_attr(vm_t *vm, int streamN) +{ + subp_attr_t attr; + + if((vm->state).domain == VTS_DOMAIN) { + attr = vm->vtsi->vtsi_mat->vts_subp_attr[streamN]; + } else if((vm->state).domain == VTSM_DOMAIN) { + attr = vm->vtsi->vtsi_mat->vtsm_subp_attr; + } else if((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN) { + attr = vm->vmgi->vmgi_mat->vmgm_subp_attr; + } + return attr; +} + +audio_attr_t vm_get_audio_attr(vm_t *vm, int streamN) +{ + audio_attr_t attr; + + if((vm->state).domain == VTS_DOMAIN) { + attr = vm->vtsi->vtsi_mat->vts_audio_attr[streamN]; + } else if((vm->state).domain == VTSM_DOMAIN) { + attr = vm->vtsi->vtsi_mat->vtsm_audio_attr; + } else if((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN) { + attr = vm->vmgi->vmgi_mat->vmgm_audio_attr; + } + return attr; +} + +video_attr_t vm_get_video_attr(vm_t *vm) +{ + video_attr_t attr; + + if((vm->state).domain == VTS_DOMAIN) { + attr = vm->vtsi->vtsi_mat->vts_video_attr; + } else if((vm->state).domain == VTSM_DOMAIN) { + attr = vm->vtsi->vtsi_mat->vtsm_video_attr; + } else if((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN) { + attr = vm->vmgi->vmgi_mat->vmgm_video_attr; + } + return attr; +} + +void vm_get_video_res(vm_t *vm, int *width, int *height) +{ + video_attr_t attr; + + attr = vm_get_video_attr(vm); + + if(attr.video_format != 0) + *height = 576; + else + *height = 480; + switch(attr.picture_size) { + case 0: + *width = 720; + break; + case 1: + *width = 704; + break; + case 2: + *width = 352; + break; + case 3: + *width = 352; + *height /= 2; + break; + } +} + +/* Must be called before domain is changed (get_PGCN()) */ +static void saveRSMinfo(vm_t *vm, int cellN, int blockN) +{ + int i; + + if(cellN != 0) { + (vm->state).rsm_cellN = cellN; + (vm->state).rsm_blockN = 0; + } else { + (vm->state).rsm_cellN = (vm->state).cellN; + (vm->state).rsm_blockN = blockN; + } + (vm->state).rsm_vtsN = (vm->state).vtsN; + (vm->state).rsm_pgcN = get_PGCN(vm); + + /* assert((vm->state).rsm_pgcN == (vm->state).TT_PGCN_REG); for VTS_DOMAIN */ + + for(i = 0; i < 5; i++) { + (vm->state).rsm_regs[i] = (vm->state).registers.SPRM[4 + i]; + } +} + + + +/* Figure out the correct pgN from the cell and update (vm->state). */ +static int set_PGN(vm_t *vm) { + int new_pgN = 0; + + while(new_pgN < (vm->state).pgc->nr_of_programs + && (vm->state).cellN >= (vm->state).pgc->program_map[new_pgN]) + new_pgN++; + + if(new_pgN == (vm->state).pgc->nr_of_programs) /* We are at the last program */ + if((vm->state).cellN > (vm->state).pgc->nr_of_cells) + return 1; /* We are past the last cell */ + + (vm->state).pgN = new_pgN; + + if((vm->state).domain == VTS_DOMAIN) { + playback_type_t *pb_ty; + if((vm->state).TTN_REG > vm->vmgi->tt_srpt->nr_of_srpts) + return 0; /* ?? */ + pb_ty = &vm->vmgi->tt_srpt->title[(vm->state).TTN_REG - 1].pb_ty; + if(pb_ty->multi_or_random_pgc_title == /* One_Sequential_PGC_Title */ 0) { +#if 0 /* TTN_REG can't be trusted to have a correct value here... */ + vts_ptt_srpt_t *ptt_srpt = vtsi->vts_ptt_srpt; + assert((vm->state).VTS_TTN_REG <= ptt_srpt->nr_of_srpts); + assert(get_PGCN() == ptt_srpt->title[(vm->state).VTS_TTN_REG - 1].ptt[0].pgcn); + assert(1 == ptt_srpt->title[(vm->state).VTS_TTN_REG - 1].ptt[0].pgn); +#endif + (vm->state).PTTN_REG = (vm->state).pgN; + } + } + + return 0; +} + +static link_t play_PGC(vm_t *vm) +{ + link_t link_values; + +#ifdef TRACE + fprintf(stderr, "vm: play_PGC:"); + if((vm->state).domain != FP_DOMAIN) { + fprintf(stderr, " (vm->state).pgcN (%i)\n", get_PGCN(vm)); + } else { + fprintf(stderr, " first_play_pgc\n"); + } +#endif + + /* This must be set before the pre-commands are executed because they */ + /* might contain a CallSS that will save resume state */ + + /* FIXME: This may be only a temporary fix for something... */ + (vm->state).pgN = 1; + (vm->state).cellN = 0; + + /* eval -> updates the state and returns either + - some kind of jump (Jump(TT/SS/VTS_TTN/CallSS/link C/PG/PGC/PTTN) + - just play video i.e first PG + (This is what happens if you fall of the end of the pre_cmds) + - or a error (are there more cases?) */ + if((vm->state).pgc->command_tbl && (vm->state).pgc->command_tbl->nr_of_pre) { + if(vmEval_CMD((vm->state).pgc->command_tbl->pre_cmds, + (vm->state).pgc->command_tbl->nr_of_pre, + &(vm->state).registers, &link_values)) { + /* link_values contains the 'jump' return value */ + return link_values; + } else { +#ifdef TRACE + fprintf(stderr, "PGC pre commands didn't do a Jump, Link or Call\n"); +#endif + } + } + return play_PG(vm); +} + +static link_t play_PGC_PG(vm_t *vm, int pgN) +{ + link_t link_values; + +#ifdef TRACE + fprintf(stderr, "vm: play_PGC:"); + if((vm->state).domain != FP_DOMAIN) { + fprintf(stderr, " (vm->state).pgcN (%i)\n", get_PGCN(vm)); + } else { + fprintf(stderr, " first_play_pgc\n"); + } +#endif + + /* This must be set before the pre-commands are executed because they */ + /* might contain a CallSS that will save resume state */ + + /* FIXME: This may be only a temporary fix for something... */ + (vm->state).pgN = pgN; + (vm->state).cellN = 0; + + /* eval -> updates the state and returns either + - some kind of jump (Jump(TT/SS/VTS_TTN/CallSS/link C/PG/PGC/PTTN) + - just play video i.e first PG + (This is what happens if you fall of the end of the pre_cmds) + - or a error (are there more cases?) */ + if((vm->state).pgc->command_tbl && (vm->state).pgc->command_tbl->nr_of_pre) { + if(vmEval_CMD((vm->state).pgc->command_tbl->pre_cmds, + (vm->state).pgc->command_tbl->nr_of_pre, + &(vm->state).registers, &link_values)) { + /* link_values contains the 'jump' return value */ + return link_values; + } else { +#ifdef TRACE + fprintf(stderr, "PGC pre commands didn't do a Jump, Link or Call\n"); +#endif + } + } + return play_PG(vm); +} + +static link_t play_PG(vm_t *vm) +{ +#ifdef TRACE + fprintf(stderr, "play_PG: (vm->state).pgN (%i)\n", (vm->state).pgN); +#endif + + assert((vm->state).pgN > 0); + if((vm->state).pgN > (vm->state).pgc->nr_of_programs) { +#ifdef TRACE + fprintf(stderr, "(vm->state).pgN (%i) == pgc->nr_of_programs + 1 (%i)\n", + (vm->state).pgN, (vm->state).pgc->nr_of_programs + 1); +#endif + /*assert((vm->state).pgN == (vm->state).pgc->nr_of_programs + 1);*/ + return play_PGC_post(vm); + } + + (vm->state).cellN = (vm->state).pgc->program_map[(vm->state).pgN - 1]; + + return play_Cell(vm); +} + + +static link_t play_Cell(vm_t *vm) +{ +#ifdef TRACE + fprintf(stderr, "play_Cell: (vm->state).cellN (%i)\n", (vm->state).cellN); +#endif + + assert((vm->state).cellN > 0); + if((vm->state).cellN > (vm->state).pgc->nr_of_cells) { +#ifdef TRACE + fprintf(stderr, "(vm->state).cellN (%i) == pgc->nr_of_cells + 1 (%i)\n", + (vm->state).cellN, (vm->state).pgc->nr_of_cells + 1); +#endif + assert((vm->state).cellN == (vm->state).pgc->nr_of_cells + 1); + return play_PGC_post(vm); + } + + + /* Multi angle/Interleaved */ + switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode) { + case 0: /* Normal */ + assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 0); + break; + case 1: /* The first cell in the block */ + switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type) { + case 0: /* Not part of a block */ + assert(0); + case 1: /* Angle block */ + /* Loop and check each cell instead? So we don't get outsid the block. */ + (vm->state).cellN += (vm->state).AGL_REG - 1; + assert((vm->state).cellN <= (vm->state).pgc->nr_of_cells); + assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode != 0); + assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 1); + break; + case 2: /* ?? */ + case 3: /* ?? */ + default: + fprintf(stderr, "Invalid? Cell block_mode (%d), block_type (%d)\n", + (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode, + (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type); + } + break; + case 2: /* Cell in the block */ + case 3: /* Last cell in the block */ + /* These might perhaps happen for RSM or LinkC commands? */ + default: + fprintf(stderr, "Cell is in block but did not enter at first cell!\n"); + } + + /* Updates (vm->state).pgN and PTTN_REG */ + if(set_PGN(vm)) { + /* Should not happen */ + link_t tmp = {LinkTailPGC, /* No Button */ 0, 0, 0}; + assert(0); + return tmp; + } + (vm->state).cell_restart++; + fprintf(stderr, "libdvdnav: Cell should restart here\n"); + { + link_t tmp = {PlayThis, /* Block in Cell */ 0, 0, 0}; + return tmp; + } + +} + +static link_t play_Cell_post(vm_t *vm) +{ + cell_playback_t *cell; + +#ifdef TRACE + fprintf(stderr, "play_Cell_post: (vm->state).cellN (%i)\n", (vm->state).cellN); +#endif + + cell = &(vm->state).pgc->cell_playback[(vm->state).cellN - 1]; + + /* Still time is already taken care of before we get called. */ + + /* Deal with a Cell command, if any */ + if(cell->cell_cmd_nr != 0) { + link_t link_values; + + assert((vm->state).pgc->command_tbl != NULL); + assert((vm->state).pgc->command_tbl->nr_of_cell >= cell->cell_cmd_nr); +#ifdef TRACE + fprintf(stderr, "Cell command pressent, executing\n"); +#endif + if(vmEval_CMD(&(vm->state).pgc->command_tbl->cell_cmds[cell->cell_cmd_nr - 1], 1, + &(vm->state).registers, &link_values)) { + return link_values; + } else { + fprintf(stderr, "Cell command didn't do a Jump, Link or Call\n"); + /* Error ?? goto tail? goto next PG? or what? just continue? */ + } + } + + + /* Where to continue after playing the cell... */ + /* Multi angle/Interleaved */ + switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode) { + case 0: /* Normal */ + assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 0); + (vm->state).cellN++; + break; + case 1: /* The first cell in the block */ + case 2: /* A cell in the block */ + case 3: /* The last cell in the block */ + default: + switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type) { + case 0: /* Not part of a block */ + assert(0); + case 1: /* Angle block */ + /* Skip the 'other' angles */ + (vm->state).cellN++; + while((vm->state).cellN <= (vm->state).pgc->nr_of_cells + && (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode >= 2) { + (vm->state).cellN++; + } + break; + case 2: /* ?? */ + case 3: /* ?? */ + default: + fprintf(stderr, "Invalid? Cell block_mode (%d), block_type (%d)\n", + (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode, + (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type); + } + break; + } + + + /* Figure out the correct pgN for the new cell */ + if(set_PGN(vm)) { +#ifdef TRACE + fprintf(stderr, "last cell in this PGC\n"); +#endif + return play_PGC_post(vm); + } + + return play_Cell(vm); +} + + +static link_t play_PGC_post(vm_t *vm) +{ + link_t link_values; + +#ifdef TRACE + fprintf(stderr, "play_PGC_post:\n"); +#endif + + assert((vm->state).pgc->still_time == 0); /* FIXME $$$ */ + + /* eval -> updates the state and returns either + - some kind of jump (Jump(TT/SS/VTS_TTN/CallSS/link C/PG/PGC/PTTN) + - or a error (are there more cases?) + - if you got to the end of the post_cmds, then what ?? */ + if((vm->state).pgc->command_tbl && + vmEval_CMD((vm->state).pgc->command_tbl->post_cmds, + (vm->state).pgc->command_tbl->nr_of_post, + &(vm->state).registers, &link_values)) { + return link_values; + } + + /* Or perhaps handle it here? */ + { + link_t link_next_pgc = {LinkNextPGC, 0, 0, 0}; + fprintf(stderr, "** Fell of the end of the pgc, continuing in NextPGC\n"); + assert((vm->state).pgc->next_pgc_nr != 0); + /* Should end up in the STOP_DOMAIN if next_pgc is 0. */ + return link_next_pgc; + } +} + + +static link_t process_command(vm_t *vm, link_t link_values) +{ + /* FIXME $$$ Move this to a separate function? */ + vm->badness_counter++; + if (vm->badness_counter > 1) fprintf(stderr, "**** process_command re-entered %d*****\n",vm->badness_counter); + while(link_values.command != PlayThis) { + +#ifdef TRACE + vmPrint_LINK(link_values); + + fprintf(stderr, "Link values %i %i %i %i\n", link_values.command, + link_values.data1, link_values.data2, link_values.data3); + + fprintf(stderr, "Before:"); + vm_print_current_domain_state(vm); +#endif + + switch(link_values.command) { + case LinkNoLink: + /* No Link */ + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + (vm->state).HL_BTNN_REG = link_values.data1 << 10; + fprintf(stderr, "libdvdnav: FIXME: in trouble...LinkNoLink - CRASHING!!!\n"); + assert(0); + + case LinkTopC: + /* Restart playing from the beginning of the current Cell. */ + /* BUTTON number:data1 */ + fprintf(stderr, "libdvdnav: FIXME: LinkTopC. Replay current Cell\n"); + 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; /* if cellN becomes > nr_of_cells? it is handled in play_Cell() */ + 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; + (vm->state).cellN -= 1; /* If cellN becomes < 1? it is handled in play_Cell() */ + link_values = play_Cell(vm); + break; + + case LinkTopPG: + /* Link to Top Program */ + /* BUTTON number:data1 */ + fprintf(stderr, "libdvdnav: FIXME: LinkTopPG. This should start the current PG again.\n"); + if(link_values.data1 != 0) + (vm->state).HL_BTNN_REG = link_values.data1 << 10; + /* Does pgN always contain the current value? */ + 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; + /* Does pgN always contain the current value? */ + (vm->state).pgN += 1; /* FIXME: What if pgN becomes > pgc.nr_of_programs? */ + 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; + /* Does pgN always contain the current value? */ + assert((vm->state).pgN > 1); + (vm->state).pgN -= 1; /* FIXME: What if pgN becomes < 1? */ + link_values = play_PG(vm); + break; + + case LinkTopPGC: + /* Restart playing from beginning of current Program Chain */ + /* BUTTON number:data1 */ + fprintf(stderr, "libdvdnav: FIXME: LinkTopPGC. Restart from beginning of current Program Chain\n"); + 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_PGC(vm, (vm->state).pgc->next_pgc_nr)) + assert(0); + link_values = play_PGC(vm); + 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_PGC(vm, (vm->state).pgc->prev_pgc_nr)) + assert(0); + link_values = play_PGC(vm); + 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_PGC(vm, (vm->state).pgc->goup_pgc_nr)) + assert(0); + link_values = play_PGC(vm); + break; + case LinkTailPGC: + /* Link to Tail??? Program Chain */ + /* BUTTON number:data1 */ + /* fprintf(stderr, "libdvdnav: FIXME: LinkTailPGC. What is LinkTailPGC?\n"); */ + if(link_values.data1 != 0) + (vm->state).HL_BTNN_REG = link_values.data1 << 10; + link_values = play_PGC_post(vm); + break; + + case LinkRSM: + { + /* Link to Resume */ + int i; + /* Check and see if there is any rsm info!! */ + (vm->state).domain = VTS_DOMAIN; + ifoOpenNewVTSI(vm, vm->dvd, (vm->state).rsm_vtsN); + set_PGC(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; ?? */ + 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 */ + /* assert( time/block/vobu is 0 ); */ + (vm->state).pgN = 1; + link_values = play_PG(vm); + } else { + /* assert( time/block/vobu is _not_ 0 ); */ + /* play_Cell_at_time */ + /* (vm->state).pgN = ?? this gets the righ value in play_Cell */ + (vm->state).cellN = (vm->state).rsm_cellN; + link_values.command = PlayThis; + link_values.data1 = (vm->state).rsm_blockN; + 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_PGC(vm, link_values.data1)) + assert(0); + link_values = play_PGC(vm); + break; + case LinkPTTN: + /* Link to Part of this Title Number:data1 */ + /* BUTTON number:data2 */ + assert((vm->state).domain == VTS_DOMAIN); + 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) == -1) + assert(0); + 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: + fprintf(stderr, "libdvdnav: FIXME:in trouble...Link Exit - CRASHING!!!\n"); + assert(0); /* What should we do here?? */ + + case JumpTT: + /* Jump to VTS Title Domain */ + /* Only allowed from the First Play domain(PGC) */ + /* or the Video Manager domain (VMG) */ + assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN); /* ?? */ + if(set_TT(vm,link_values.data1) == -1) + assert(0); + link_values = play_PGC(vm); + 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) */ + assert((vm->state).domain == VTSM_DOMAIN || (vm->state).domain == VTS_DOMAIN); /* ?? */ + fprintf(stderr, "libdvdnav: FIXME: Should be able to use get_VTS_PTT here.\n"); + if(set_VTS_TT(vm,(vm->state).vtsN, link_values.data1) == -1) + assert(0); + 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) */ + assert((vm->state).domain == VTSM_DOMAIN || (vm->state).domain == VTS_DOMAIN); /* ?? */ + if(set_VTS_PTT(vm,(vm->state).vtsN, link_values.data1, link_values.data2) == -1) + assert(0); + link_values = play_PGC_PG(vm, link_values.data2); + break; + + case JumpSS_FP: + /* Jump to First Play Domain */ + /* Only allowed from the VTS Menu Domain(VTSM) */ + /* or the Video Manager domain (VMG) */ + assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == VTSM_DOMAIN); /* ?? */ + set_FP_PGC(vm); + link_values = play_PGC(vm); + break; + case JumpSS_VMGM_MENU: + /* Jump to Video Manger domain - Title Menu:data1 or any PGC in VMG */ + /* Allowed from anywhere except the VTS Title domain */ + assert((vm->state).domain == VMGM_DOMAIN || + (vm->state).domain == VTSM_DOMAIN || + (vm->state).domain == FP_DOMAIN); /* ?? */ + (vm->state).domain = VMGM_DOMAIN; + if(set_MENU(vm,link_values.data1) == -1) + 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 */ + /* FIXME: This goes badly wrong for some DVDs. */ + /* FIXME: Keep in touch with ogle people regarding what to do here */ + /* ifoOpenNewVTSI:data1 */ + /* VTS_TTN_REG:data2 */ + /* get_MENU:data3 */ +#ifdef TRACE + fprintf(stderr, "dvdnav: BUG TRACKING *******************************************************************\n"); + fprintf(stderr, " data1=%u data2=%u data3=%u\n", + link_values.data1, + link_values.data2, + link_values.data3); + fprintf(stderr, "dvdnav: *******************************************************************\n"); +#endif + + if(link_values.data1 !=0) { + assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN); /* ?? */ + (vm->state).domain = VTSM_DOMAIN; + ifoOpenNewVTSI(vm, vm->dvd, link_values.data1); /* Also sets (vm->state).vtsN */ + } else { + /* This happens on 'The Fifth Element' region 2. */ + assert((vm->state).domain == VTSM_DOMAIN); + } + /* 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; + if(set_MENU(vm, link_values.data3) == -1) + assert(0); + link_values = play_PGC(vm); + break; + case JumpSS_VMGM_PGC: + /* get_PGC:data1 */ + assert((vm->state).domain == VMGM_DOMAIN || + (vm->state).domain == VTSM_DOMAIN || + (vm->state).domain == FP_DOMAIN); /* ?? */ + (vm->state).domain = VMGM_DOMAIN; + if(set_PGC(vm,link_values.data1) == -1) + assert(0); + link_values = play_PGC(vm); + break; + + case CallSS_FP: + /* saveRSMinfo:data1 */ + assert((vm->state).domain == VTS_DOMAIN); /* ?? */ + /* Must be called before domain is changed */ + saveRSMinfo(vm, link_values.data1, /* We dont have block info */ 0); + set_FP_PGC(vm); + link_values = play_PGC(vm); + break; + case CallSS_VMGM_MENU: + /* get_MENU:data1 */ + /* saveRSMinfo:data2 */ + assert((vm->state).domain == VTS_DOMAIN); /* ?? */ + /* Must be called before domain is changed */ + saveRSMinfo(vm,link_values.data2, /* We dont have block info */ 0); + (vm->state).domain = VMGM_DOMAIN; + if(set_MENU(vm,link_values.data1) == -1) + assert(0); + link_values = play_PGC(vm); + break; + case CallSS_VTSM: + /* get_MENU:data1 */ + /* saveRSMinfo:data2 */ + assert((vm->state).domain == VTS_DOMAIN); /* ?? */ + /* Must be called before domain is changed */ + saveRSMinfo(vm,link_values.data2, /* We dont have block info */ 0); + (vm->state).domain = VTSM_DOMAIN; + if(set_MENU(vm,link_values.data1) == -1) + assert(0); + link_values = play_PGC(vm); + break; + case CallSS_VMGM_PGC: + /* get_PGC:data1 */ + /* saveRSMinfo:data2 */ + assert((vm->state).domain == VTS_DOMAIN); /* ?? */ + /* Must be called before domain is changed */ + saveRSMinfo(vm,link_values.data2, /* We dont have block info */ 0); + (vm->state).domain = VMGM_DOMAIN; + if(set_PGC(vm,link_values.data1) == -1) + assert(0); + link_values = play_PGC(vm); + break; + case PlayThis: + /* Should never happen. */ + assert(0); + break; + } + +#ifdef TRACE + fprintf(stderr, "After:"); + vm_print_current_domain_state(vm); +#endif + + } + vm->badness_counter--; + return link_values; +} + +/* Searches the TT tables, to find the current TT. + * returns the current TT. + * returns 0 if not found. + */ +static int get_TT(vm_t *vm, int vtsN, int vts_ttn) { + int i; + int tt=0; + for(i = 1; i <= vm->vmgi->tt_srpt->nr_of_srpts; i++) { + if( vm->vmgi->tt_srpt->title[i - 1].title_set_nr == vtsN && + vm->vmgi->tt_srpt->title[i - 1].vts_ttn == vts_ttn) { + tt=i; + break; + } + } + return tt; +} + +static int set_TT(vm_t *vm, int tt) +{ + assert(tt <= vm->vmgi->tt_srpt->nr_of_srpts); + + (vm->state).TTN_REG = tt; + + return set_VTS_TT(vm, vm->vmgi->tt_srpt->title[tt - 1].title_set_nr, + vm->vmgi->tt_srpt->title[tt - 1].vts_ttn); +} + + +static int set_VTS_TT(vm_t *vm, int vtsN, int vts_ttn) +{ + fprintf(stderr, "get_VTS_TT called, testing!!! vtsN=%d, vts_ttn=%d\n", vtsN, vts_ttn); + return set_VTS_PTT(vm, vtsN, vts_ttn, 1); + /* pgcN = get_ID(vm, vts_ttn); This might return -1 */ + /* + assert(pgcN != -1); + + (vm->state).TTN_REG = get_TT(*vm, vtsN, vts_ttn); + (vm->state).VTS_TTN_REG = vts_ttn; + (vm->state).vtsN = + */ + /* Any other registers? */ + + /* return set_PGC(vm, pgcN); */ +} + + +static int set_VTS_PTT(vm_t *vm, int vtsN, int /* is this really */ vts_ttn, int part) +{ + int pgcN, pgN, res; + + (vm->state).domain = VTS_DOMAIN; + if(vtsN != (vm->state).vtsN) + ifoOpenNewVTSI(vm, vm->dvd, vtsN); /* Also sets (vm->state).vtsN */ + + assert(vts_ttn <= vm->vtsi->vts_ptt_srpt->nr_of_srpts); + assert(part <= vm->vtsi->vts_ptt_srpt->title[vts_ttn - 1].nr_of_ptts); + + pgcN = vm->vtsi->vts_ptt_srpt->title[vts_ttn - 1].ptt[part - 1].pgcn; + pgN = vm->vtsi->vts_ptt_srpt->title[vts_ttn - 1].ptt[part - 1].pgn; + + (vm->state).TT_PGCN_REG = pgcN; + (vm->state).PTTN_REG = pgN; + + (vm->state).TTN_REG = get_TT(vm, vtsN, vts_ttn); + assert( (vm->state.TTN_REG) != 0 ); + (vm->state).VTS_TTN_REG = vts_ttn; + (vm->state).vtsN = vtsN; /* Not sure about this one. We can get to it easily from TTN_REG */ + /* Any other registers? */ + + res = set_PGC(vm, pgcN); // This clobber's state.pgN (sets it to 1), but we don't want clobbering here. + (vm->state).pgN = pgN; /* Part?? */ + return res; +} + + + +static int set_FP_PGC(vm_t *vm) +{ + (vm->state).domain = FP_DOMAIN; + + (vm->state).pgc = vm->vmgi->first_play_pgc; + + return 0; +} + + +static int set_MENU(vm_t *vm, int menu) +{ + assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == VTSM_DOMAIN); + return set_PGC(vm, get_ID(vm, menu)); +} + +/* Search for entry_id match of the PGC Category in the current VTS PGCIT table. + * Return pgcN based on entry_id match. + */ +static int get_ID(vm_t *vm, int id) +{ + int pgcN, i; + pgcit_t *pgcit; + + /* Relies on state to get the correct pgcit. */ + pgcit = get_PGCIT(vm); + assert(pgcit != NULL); + fprintf(stderr, "** Searching for menu (0x%x) entry PGC\n", id); + + /* Get menu/title */ + for(i = 0; i < pgcit->nr_of_pgci_srp; i++) { + if((pgcit->pgci_srp[i].entry_id & 0x7f) == id) { + assert((pgcit->pgci_srp[i].entry_id & 0x80) == 0x80); + pgcN = i + 1; + return pgcN; + } + } + fprintf(stderr, "** No such id/menu (%d) entry PGC\n", id); + for(i = 0; i < pgcit->nr_of_pgci_srp; i++) { + fprintf(stderr, "Available menus: 0x%x\n", + pgcit->pgci_srp[i].entry_id); + } + assert(0); /* Use assert for now, until the error is handled. */ + return -1; /* error */ +} + +/* Set the vm->state to pgcN. + * Returns success/failure. + */ +static int set_PGC(vm_t *vm, int pgcN) +{ + /* FIXME: Keep this up to date with the ogle people */ + pgcit_t *pgcit; + + pgcit = get_PGCIT(vm); + + assert(pgcit != NULL); /* ?? Make this return -1 instead */ + if(pgcN < 1 || pgcN > pgcit->nr_of_pgci_srp) { + fprintf(stderr, "** No such pgcN = %d\n", pgcN); + assert(0); + return -1; /* error */ + } + + /* (vm->state).pgcN = pgcN; */ + (vm->state).pgc = pgcit->pgci_srp[pgcN - 1].pgc; + (vm->state).pgN = 1; + + if((vm->state).domain == VTS_DOMAIN) + (vm->state).TT_PGCN_REG = pgcN; + + return 0; +} + +static int get_PGCN(vm_t *vm) +{ + pgcit_t *pgcit; + int pgcN = 1; + + pgcit = get_PGCIT(vm); + + assert(pgcit != NULL); + + while(pgcN <= pgcit->nr_of_pgci_srp) { + if(pgcit->pgci_srp[pgcN - 1].pgc == (vm->state).pgc) + return pgcN; + pgcN++; + } + fprintf(stderr, "libdvdnav: get_PGCN failed. Trying to find pgcN in domain %d \n", + (vm->state).domain); + assert(0); + return -1; /* error */ +} + +int vm_get_video_aspect(vm_t *vm) +{ + int aspect = 0; + + switch ((vm->state).domain) { + case VTS_DOMAIN: + aspect = vm->vtsi->vtsi_mat->vts_video_attr.display_aspect_ratio; + break; + case VTSM_DOMAIN: + aspect = vm->vtsi->vtsi_mat->vtsm_video_attr.display_aspect_ratio; + break; + case VMGM_DOMAIN: + aspect = vm->vmgi->vmgi_mat->vmgm_video_attr.display_aspect_ratio; + break; + default: + fprintf(stderr, "libdvdnav: vm_get_video_aspect failed. Unknown domain %d\n", + (vm->state).domain); + assert(0); + break; + } +#ifdef TRACE + fprintf(stderr, "dvdnav:get_video_aspect:aspect=%d\n",aspect); +#endif + assert(aspect == 0 || aspect == 3); + (vm->state).registers.SPRM[14] &= ~(0x3 << 10); + (vm->state).registers.SPRM[14] |= aspect << 10; + + return aspect; +} + +int vm_get_video_scale_permission(vm_t *vm) +{ + int permission = 0; + + if((vm->state).domain == VTS_DOMAIN) { + permission = vm->vtsi->vtsi_mat->vts_video_attr.permitted_df; + } else if((vm->state).domain == VTSM_DOMAIN) { + permission = vm->vtsi->vtsi_mat->vtsm_video_attr.permitted_df; + } else if((vm->state).domain == VMGM_DOMAIN) { + permission = vm->vmgi->vmgi_mat->vmgm_video_attr.permitted_df; + } +#ifdef TRACE + fprintf(stderr, "dvdnav:get_video_scale_permission:permission=%d\n",permission); +#endif + + return permission; +} + +static void ifoOpenNewVTSI(vm_t *vm, dvd_reader_t *dvd, int vtsN) +{ + if((vm->state).vtsN == vtsN) { + return; /* We alread have it */ + } + + if(vm->vtsi != NULL) + ifoClose(vm->vtsi); + + vm->vtsi = ifoOpenVTSI(dvd, vtsN); + if(vm->vtsi == NULL) { + fprintf(stderr, "ifoOpenVTSI failed - CRASHING!!!\n"); + assert(0); + } + if(!ifoRead_VTS_PTT_SRPT(vm->vtsi)) { + fprintf(stderr, "ifoRead_VTS_PTT_SRPT failed - CRASHING!!!\n"); + assert(0); + } + if(!ifoRead_PGCIT(vm->vtsi)) { + fprintf(stderr, "ifoRead_PGCIT failed - CRASHING!!!\n"); + assert(0); + } + if(!ifoRead_PGCI_UT(vm->vtsi)) { + fprintf(stderr, "ifoRead_PGCI_UT failed - CRASHING!!!\n"); + assert(0); + } + if(!ifoRead_VOBU_ADMAP(vm->vtsi)) { + fprintf(stderr, "ifoRead_VOBU_ADMAP vtsi failed - CRASHING\n"); + assert(0); + } + if(!ifoRead_TITLE_VOBU_ADMAP(vm->vtsi)) { + fprintf(stderr, "ifoRead_TITLE_VOBU_ADMAP vtsi failed - CRASHING\n"); + assert(0); + } + (vm->state).vtsN = vtsN; +} + +static pgcit_t* get_MENU_PGCIT(vm_t *vm, ifo_handle_t *h, uint16_t lang) +{ + int i; + + if(h == NULL || h->pgci_ut == NULL) { + fprintf(stderr, "*** pgci_ut handle is NULL ***\n"); + return NULL; /* error? */ + } + + i = 0; + while(i < h->pgci_ut->nr_of_lus + && h->pgci_ut->lu[i].lang_code != lang) + i++; + if(i == h->pgci_ut->nr_of_lus) { + fprintf(stderr, "Language '%c%c' not found, using '%c%c' instead\n", + (char)(lang >> 8), (char)(lang & 0xff), + (char)(h->pgci_ut->lu[0].lang_code >> 8), + (char)(h->pgci_ut->lu[0].lang_code & 0xff)); + fprintf(stderr, "Menu Languages available: "); + for(i=0;i< h->pgci_ut->nr_of_lus;i++) { + fprintf(stderr, "%c%c ", + (char)(h->pgci_ut->lu[0].lang_code >> 8), + (char)(h->pgci_ut->lu[0].lang_code & 0xff)); + } + fprintf(stderr, "\n"); + + i = 0; /* error? */ + } + + return h->pgci_ut->lu[i].pgcit; +} + +/* Uses state to decide what to return */ +static pgcit_t* get_PGCIT(vm_t *vm) { + pgcit_t *pgcit; + + switch ((vm->state).domain) { + case VTS_DOMAIN: + pgcit = vm->vtsi->vts_pgcit; + break; + case VTSM_DOMAIN: + pgcit = get_MENU_PGCIT(vm, vm->vtsi, (vm->state).registers.SPRM[0]); + break; + case VMGM_DOMAIN: + pgcit = get_MENU_PGCIT(vm, vm->vmgi, (vm->state).registers.SPRM[0]); + break; + default: + pgcit = NULL; /* Should never hapen */ + fprintf(stderr, "libdvdnav: get_PGCIT: Unknown domain:%d\n", + (vm->state).domain); + assert(0); + break; + } + + return pgcit; +} + +/* + * $Log: vm.c,v $ + * Revision 1.1 2002/08/08 17:49:21 richwareham + * First stage of DVD plugin -> dvdnav conversion + * + * Revision 1.24 2002/07/05 14:18:55 mroi + * report all spu types (widescreen, letterbox and pan&scan), not widescreen + * only and report the stream's scale permissions to detect pan&scan material + * + * Revision 1.23 2002/07/05 01:42:30 jcdutton + * Add more debug info for Menu language selection. + * Only do vm_start when we have to. + * + * Revision 1.22 2002/07/04 00:38:51 jcdutton + * Add some menu language printf's. + * + * Revision 1.21 2002/07/03 02:41:31 jcdutton + * Fix another long standing bug. + * Now changing PGC will force a start at the first PG of the PGC. + * + * Revision 1.20 2002/07/02 22:57:10 jcdutton + * Rename some of the functions in vm.c to help readability. + * Hopefully fix __FUNCTION__ problem. Use __func_ as recommended in C99. + * Fix bug where libdvdnav would not immeadiately replay the same cell due to menu buttons. + * + * Revision 1.19 2002/06/04 13:35:16 richwareham + * Removed more C++ style comments + * + * Revision 1.18 2002/05/30 19:25:08 richwareham + * Another small fix + * + * Revision 1.17 2002/05/30 15:56:41 richwareham + * Fixed (what appears to be) an error in JumpVTS_PTT implementation, it didn't call play_PGC after jumping. + * + * Revision 1.16 2002/04/24 21:15:25 jcdutton + * Quiet please!!! + * + * Revision 1.15 2002/04/23 13:18:31 jcdutton + * Insert some assert commands to hopefully catch a DVD which will give us information on what to do if these values are != 0. + * + * Revision 1.14 2002/04/23 12:34:39 f1rmb + * Why rewrite vm function, use it instead (this remark is for me, of course ;-) ). + * Comment unused var, shut compiler warnings. + * + * Revision 1.13 2002/04/23 02:12:27 jcdutton + * Re-implemented seeking. + * + * Revision 1.12 2002/04/22 22:00:48 jcdutton + * Start of rewrite of libdvdnav. Still need to re-implement seeking. + * + * Revision 1.11 2002/04/12 20:06:41 jcdutton + * Implement General Register Counters or GPRM counters. + * Navigation timers are not supported yet. SPRM[9] and SPRM[10]. + * + * Revision 1.10 2002/04/12 12:43:36 jcdutton + * Display DVD disk region setting. + * Display possible RCE region protection message. + * + * Revision 1.9 2002/04/10 16:45:57 jcdutton + * Actually fix the const this time! + * + * Revision 1.8 2002/04/10 16:40:52 jcdutton + * Fix a const problem. + * + * Revision 1.7 2002/04/10 13:09:40 jcdutton + * Some improvements to decoder.c + * Registers should be updated correctly now, but still needs checking. + * + * Revision 1.6 2002/04/09 15:19:07 jcdutton + * Added some debug info, to hopefully help in tracking bugs in libdvdnav. + * + * Revision 1.5 2002/04/07 19:35:54 jcdutton + * Added some comments into the code. + * + * Revision 1.4 2002/04/06 18:31:50 jcdutton + * Some cleaning up. + * changed exit(1) to assert(0) so they actually get seen by the user so that it helps developers more. + * + * Revision 1.3 2002/04/02 18:22:27 richwareham + * Added reset patch from Kees Cook <kees@outflux.net> + * + * Revision 1.2 2002/04/01 18:56:28 richwareham + * Added initial example programs directory and make sure all debug/error output goes to stderr. + * + * Revision 1.1.1.1 2002/03/12 19:45:55 richwareham + * Initial import + * + * Revision 1.18 2002/01/22 16:56:49 jcdutton + * Fix clut after seeking. + * Add a few virtual machine debug messages, to help diagnose problems with "Deep Purple - Total Abandon" DVD as I don't have the DVD itvm. + * Fix a few debug messages, so they do not say FIXME. + * Move the FIXME debug messages to comments in the code. + * + * Revision 1.17 2002/01/21 01:16:30 jcdutton + * Added some debug messages, to hopefully get info from users. + * + * Revision 1.16 2002/01/20 21:40:46 jcdutton + * Start to fix some assert failures. + * + * Revision 1.15 2002/01/19 20:24:38 jcdutton + * Just some FIXME notes added. + * + * Revision 1.14 2002/01/13 22:17:57 jcdutton + * Change logging. + * + * + */ diff --git a/src/input/libdvdnav/vm.h b/src/input/libdvdnav/vm.h new file mode 100644 index 000000000..a4ba8fb83 --- /dev/null +++ b/src/input/libdvdnav/vm.h @@ -0,0 +1,154 @@ +/* + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: vm.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $ + * + */ + +#ifndef VM_H_INCLUDED +#define VM_H_INCLUDED + +#include "decoder.h" +#include <dvd_types.h> + +/* DOMAIN enum */ + +typedef enum { + FP_DOMAIN = 1, + VTS_DOMAIN = 2, + VMGM_DOMAIN = 4, + VTSM_DOMAIN = 8 +} domain_t; + +/** + * State: SPRM, GPRM, Domain, pgc, pgN, cellN, ? + */ +typedef struct { + registers_t registers; + + pgc_t *pgc; /* either this or *pgc is enough? */ + + domain_t domain; + int vtsN; /* 0 is vmgm? */ + /* int pgcN; // either this or *pgc is enough. Which to use? */ + 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 */ + uint32_t clut; /* CLUT to use, not needed in this struct */ + int32_t vts; /* vts number to use */ + int32_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 */ + int32_t title; /* title number */ + int32_t chapter; /* chapter number */ + int32_t cell; /* cell number */ + int32_t cell_restart; /* get cell to restart */ + int32_t still; /* is cell still */ + int32_t vobu_start; /* block number of start of current VOBU in use */ + int32_t vobu_next; /* block number within VOBU in use */ +} vm_position_t; + +typedef struct { + dvd_reader_t *dvd; + ifo_handle_t *vmgi; + ifo_handle_t *vtsi; + dvd_state_t state; + int badness_counter; + int32_t hop_channel; +} vm_t; + + +/* 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 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); + +/* Jumping */ +int vm_start_title(vm_t *vm, int tt); +int vm_jump_prog(vm_t *vm, int pr); + +/* Other calls */ +int vm_reset(vm_t *vm, char *dvdroot); /* , register_t regs); */ +int vm_start(vm_t *vm); +int vm_position_get(vm_t *vm, vm_position_t *position); +int vm_position_print(vm_t *vm, vm_position_t *position); +int vm_eval_cmd(vm_t *vm, vm_cmd_t *cmd); +int vm_get_next_cell(vm_t *vm); +int vm_menu_call(vm_t *vm, DVDMenuID_t menuid, int block); +int vm_resume(vm_t *vm); +int vm_go_up(vm_t *vm); +int vm_top_pg(vm_t *vm); +int vm_next_pg(vm_t *vm); +int vm_prev_pg(vm_t *vm); +int vm_get_audio_stream(vm_t *vm, int audioN); +int vm_get_audio_active_stream(vm_t *vm); +int vm_get_subp_stream(vm_t *vm, int subpN, int mode); +int vm_get_subp_active_stream(vm_t *vm, int mode); +void vm_get_angle_info(vm_t *vm, int *num_avail, int *current); +void vm_get_audio_info(vm_t *vm, int *num_avail, int *current); +void vm_get_subp_info(vm_t *vm, int *num_avail, int *current); +subp_attr_t vm_get_subp_attr(vm_t *vm, int streamN); +audio_attr_t vm_get_audio_attr(vm_t *vm, int streamN); +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); + +#endif /* VM_HV_INCLUDED */ + diff --git a/src/input/libdvdnav/vmcmd.c b/src/input/libdvdnav/vmcmd.c new file mode 100644 index 000000000..4ae8f92ba --- /dev/null +++ b/src/input/libdvdnav/vmcmd.c @@ -0,0 +1,488 @@ +/* + * Copyright (C) 2000, 2001 Martin Norbäck, Håkan Hjort + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: vmcmd.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $ + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <ctype.h> +#include <inttypes.h> +#include <assert.h> + +#include "vmcmd.h" + + +/* freebsd compatibility */ +#ifndef PRIu8 +#define PRIu8 "d" +#endif + +/* freebsd compatibility */ +#ifndef PRIu16 +#define PRIu16 "d" +#endif + +static const char *cmp_op_table[] = { + NULL, "&", "==", "!=", ">=", ">", "<=", "<" +}; +static const char *set_op_table[] = { + NULL, "=", "<->", "+=", "-=", "*=", "/=", "%=", "rnd", "&=", "|=", "^=" +}; + +static const char *link_table[] = { + "LinkNoLink", "LinkTopC", "LinkNextC", "LinkPrevC", + NULL, "LinkTopPG", "LinkNextPG", "LinkPrevPG", + NULL, "LinkTopPGC", "LinkNextPGC", "LinkPrevPGC", + "LinkGoUpPGC", "LinkTailPGC", NULL, NULL, + "RSM" +}; + +static const char *system_reg_table[] = { + "Menu Description Language Code", + "Audio Stream Number", + "Sub-picture Stream Number", + "Angle Number", + "Title Track Number", + "VTS Title Track Number", + "VTS PGC Number", + "PTT Number for One_Sequential_PGC_Title", + "Highlighted Button Number", + "Navigation Timer", + "Title PGC Number for Navigation Timer", + "Audio Mixing Mode for Karaoke", + "Country Code for Parental Management", + "Parental Level", + "Player Configurations for Video", + "Player Configurations for Audio", + "Initial Language Code for Audio", + "Initial Language Code Extension for Audio", + "Initial Language Code for Sub-picture", + "Initial Language Code Extension for Sub-picture", + "Player Regional Code", + "Reserved 21", + "Reserved 22", + "Reserved 23" +}; + +static const char *system_reg_abbr_table[] = { + NULL, + "ASTN", + "SPSTN", + "AGLN", + "TTN", + "VTS_TTN", + "TT_PGCN", + "PTTN", + "HL_BTNN", + "NVTMR", + "NV_PGCN", + NULL, + "CC_PLT", + "PLT", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +static void print_system_reg(uint16_t reg) { + if(reg < sizeof(system_reg_abbr_table) / sizeof(char *)) + fprintf(stderr, "%s (SRPM:%d)", system_reg_table[reg], reg); + else + fprintf(stderr, " WARNING: Unknown system register ( reg=%d ) ", reg); +} + +static void print_reg(uint8_t reg) { + if(reg & 0x80) + print_system_reg(reg & 0x7f); + else + if(reg < 16) + fprintf(stderr, "g[%" PRIu8 "]", reg); + else + fprintf(stderr, " WARNING: Unknown general register "); +} + +static void print_cmp_op(uint8_t op) { + if(op < sizeof(cmp_op_table) / sizeof(char *) && cmp_op_table[op] != NULL) + fprintf(stderr, " %s ", cmp_op_table[op]); + else + fprintf(stderr, " WARNING: Unknown compare op "); +} + +static void print_set_op(uint8_t op) { + if(op < sizeof(set_op_table) / sizeof(char *) && set_op_table[op] != NULL) + fprintf(stderr, " %s ", set_op_table[op]); + else + fprintf(stderr, " WARNING: Unknown set op "); +} + +static void print_reg_or_data(command_t* command, int immediate, int byte) { + if(immediate) { + int i = vm_getbits(command, (byte*8), 16); + + fprintf(stderr, "0x%x", i); + if(isprint(i & 0xff) && isprint((i>>8) & 0xff)) + fprintf(stderr, " (\"%c%c\")", (char)((i>>8) & 0xff), (char)(i & 0xff)); + } else { + print_reg(vm_getbits(command, ((byte + 1)*8), 8)); + } +} + +static void print_reg_or_data_2(command_t* command, int immediate, int byte) { + if(immediate) + fprintf(stderr, "0x%x", vm_getbits(command, ((byte*8)+1), 7)); + else + fprintf(stderr, "g[%" PRIu8 "]", vm_getbits(command, ((byte*8)+4), 4)); +} + +static void print_if_version_1(command_t* command) { + uint8_t op = vm_getbits(command, 9, 3); + + if(op) { + fprintf(stderr, "if ("); + print_reg(vm_getbits(command,24,8)); + print_cmp_op(op); + print_reg_or_data(command, vm_getbits(command, 8,1), 4); + fprintf(stderr, ") "); + } +} + +static void print_if_version_2(command_t* command) { + uint8_t op = vm_getbits(command, 9, 3); + + if(op) { + fprintf(stderr, "if ("); + print_reg(vm_getbits(command, 48, 8)); + print_cmp_op(op); + print_reg(vm_getbits(command, 56, 8)); + fprintf(stderr, ") "); + } +} + +static void print_if_version_3(command_t* command) { + uint8_t op = vm_getbits(command, 9, 3); + + if(op) { + fprintf(stderr, "if ("); + print_reg(vm_getbits(command, 20, 4)); + print_cmp_op(op); + print_reg_or_data(command, vm_getbits(command, 8, 1), 6); + fprintf(stderr, ") "); + } +} + +static void print_if_version_4(command_t* command) { + uint8_t op = vm_getbits(command, 9, 3); + + if(op) { + fprintf(stderr, "if ("); + print_reg(vm_getbits(command, 12, 4)); + print_cmp_op(op); + print_reg_or_data(command, vm_getbits(command, 8, 1), 4); + fprintf(stderr, ") "); + } +} + +static void print_special_instruction(command_t* command) { + uint8_t op = vm_getbits(command, 12, 4); + + switch(op) { + case 0: /* NOP */ + fprintf(stderr, "Nop"); + break; + case 1: /* Goto line */ + fprintf(stderr, "Goto %" PRIu8, vm_getbits(command, 56, 8)); + break; + case 2: /* Break */ + fprintf(stderr, "Break"); + break; + case 3: /* Parental level */ + fprintf(stderr, "SetTmpPML %" PRIu8 ", Goto %" PRIu8, + vm_getbits(command, 52, 4), vm_getbits(command, 56, 8)); + break; + default: + fprintf(stderr, "WARNING: Unknown special instruction (%i)", + vm_getbits(command, 12, 4)); + } +} + +static void print_linksub_instruction(command_t* command) { + int linkop = vm_getbits(command, 59, 5); + int button = vm_getbits(command, 48, 6); + + if(linkop < sizeof(link_table)/sizeof(char *) && link_table[linkop] != NULL) + fprintf(stderr, "%s (button %" PRIu8 ")", link_table[linkop], button); + else + fprintf(stderr, "WARNING: Unknown linksub instruction (%i)", linkop); +} + +static void print_link_instruction(command_t* command, int optional) { + uint8_t op = vm_getbits(command, 12, 4); + + if(optional && op) + fprintf(stderr, ", "); + + switch(op) { + case 0: + if(!optional) + fprintf(stderr, "WARNING: NOP (link)!"); + break; + case 1: + print_linksub_instruction(command); + break; + case 4: + fprintf(stderr, "LinkPGCN %" PRIu16, vm_getbits(command, 49, 15)); + break; + case 5: + fprintf(stderr, "LinkPTT %" PRIu16 " (button %" PRIu8 ")", + vm_getbits(command, 54, 10), vm_getbits(command, 48, 6)); + break; + case 6: + fprintf(stderr, "LinkPGN %" PRIu8 " (button %" PRIu8 ")", + vm_getbits(command, 57, 7), vm_getbits(command, 48, 6)); + break; + case 7: + fprintf(stderr, "LinkCN %" PRIu8 " (button %" PRIu8 ")", + vm_getbits(command, 56, 8), vm_getbits(command, 48, 6)); + break; + default: + fprintf(stderr, "WARNING: Unknown link instruction"); + } +} + +static void print_jump_instruction(command_t* command) { + switch(vm_getbits(command, 12, 4)) { + case 1: + fprintf(stderr, "Exit"); + break; + case 2: + fprintf(stderr, "JumpTT %" PRIu8, vm_getbits(command, 41, 7)); + break; + case 3: + fprintf(stderr, "JumpVTS_TT %" PRIu8, vm_getbits(command, 41, 7)); + break; + case 5: + fprintf(stderr, "JumpVTS_PTT %" PRIu8 ":%" PRIu16, + vm_getbits(command, 41, 7), vm_getbits(command, 22, 10)); + break; + case 6: + switch(vm_getbits(command, 40, 2)) { + case 0: + fprintf(stderr, "JumpSS FP"); + break; + case 1: + fprintf(stderr, "JumpSS VMGM (menu %" PRIu8 ")", vm_getbits(command, 44, 4)); + break; + case 2: + fprintf(stderr, "JumpSS VTSM (vts %" PRIu8 ", title %" PRIu8 + ", menu %" PRIu8 ")", vm_getbits(command, 32, 8), vm_getbits(command, 24, 8), vm_getbits(command, 44, 4)); + break; + case 3: + fprintf(stderr, "JumpSS VMGM (pgc %" PRIu8 ")", vm_getbits(command, 17, 15)); + break; + } + break; + case 8: + switch(vm_getbits(command, 40, 2)) { + case 0: + fprintf(stderr, "CallSS FP (rsm_cell %" PRIu8 ")", + vm_getbits(command, 32, 8)); + break; + case 1: + fprintf(stderr, "CallSS VMGM (menu %" PRIu8 + ", rsm_cell %" PRIu8 ")", vm_getbits(command, 44, 4), vm_getbits(command, 32, 8)); + break; + case 2: + fprintf(stderr, "CallSS VTSM (menu %" PRIu8 + ", rsm_cell %" PRIu8 ")", vm_getbits(command, 44, 4), vm_getbits(command, 32, 8)); + break; + case 3: + fprintf(stderr, "CallSS VMGM (pgc %" PRIu8 ", rsm_cell %" PRIu8 ")", + vm_getbits(command, 17, 15), vm_getbits(command, 32, 8)); + break; + } + break; + default: + fprintf(stderr, "WARNING: Unknown Jump/Call instruction"); + } +} + +static void print_system_set(command_t* command) { + int i; + + switch(vm_getbits(command, 4, 4)) { + case 1: /* Set system reg 1 &| 2 &| 3 (Audio, Subp. Angle) */ + for(i = 1; i <= 3; i++) { + if(vm_getbits(command, ((2+i)*8), 1)) { + print_system_reg(i); + fprintf(stderr, " = "); + print_reg_or_data_2(command, vm_getbits(command, 3, 1), 2 + i); + fprintf(stderr, " "); + } + } + break; + case 2: /* Set system reg 9 & 10 (Navigation timer, Title PGC number) */ + print_system_reg(9); + fprintf(stderr, " = "); + print_reg_or_data(command, vm_getbits(command, 3, 1), 2); + fprintf(stderr, " "); + print_system_reg(10); + fprintf(stderr, " = %" PRIu8, vm_getbits(command, 40, 8)); /* ?? */ + break; + case 3: /* Mode: Counter / Register + Set */ + fprintf(stderr, "SetMode "); + if(vm_getbits(command, 40, 1)) + fprintf(stderr, "Counter "); + else + fprintf(stderr, "Register "); + print_reg(vm_getbits(command, 44, 4)); + print_set_op(0x1); /* '=' */ + print_reg_or_data(command, vm_getbits(command, 3, 1), 2); + break; + case 6: /* Set system reg 8 (Highlighted button) */ + print_system_reg(8); + if(vm_getbits(command, 3, 1)) /* immediate */ + fprintf(stderr, " = 0x%x (button no %d)", vm_getbits(command, 32, 16), vm_getbits(command, 32, 6)); + else + fprintf(stderr, " = g[%" PRIu8 "]", vm_getbits(command, 44, 4)); + break; + default: + fprintf(stderr, "WARNING: Unknown system set instruction (%i)", + vm_getbits(command, 4, 4)); + } +} + +static void print_set_version_1(command_t* command) { + uint8_t set_op = vm_getbits(command, 4, 4); + + if(set_op) { + print_reg(vm_getbits(command, 24, 8)); /* FIXME: This is different from decoder.c!!! */ + print_set_op(set_op); + print_reg_or_data(command, vm_getbits(command, 3, 1), 4); + } else { + fprintf(stderr, "NOP"); + } +} + +static void print_set_version_2(command_t* command) { + uint8_t set_op = vm_getbits(command, 4, 4); + + if(set_op) { + print_reg(vm_getbits(command, 12, 4)); + print_set_op(set_op); + print_reg_or_data(command, vm_getbits(command, 3, 1), 2); + } else { + fprintf(stderr, "NOP"); + } +} + +void vmPrint_mnemonic(vm_cmd_t *vm_command) { + command_t command; + command.instruction =( (uint64_t) vm_command->bytes[0] << 56 ) | + ( (uint64_t) vm_command->bytes[1] << 48 ) | + ( (uint64_t) vm_command->bytes[2] << 40 ) | + ( (uint64_t) vm_command->bytes[3] << 32 ) | + ( (uint64_t) vm_command->bytes[4] << 24 ) | + ( (uint64_t) vm_command->bytes[5] << 16 ) | + ( (uint64_t) vm_command->bytes[6] << 8 ) | + (uint64_t) vm_command->bytes[7] ; + command.examined = 0; + + switch(vm_getbits(&command,0,3)) { /* three first bits */ + case 0: /* Special instructions */ + print_if_version_1(&command); + print_special_instruction(&command); + break; + case 1: /* Jump/Call or Link instructions */ + if(vm_getbits(&command,3,1)) { + print_if_version_2(&command); + print_jump_instruction(&command); + } else { + print_if_version_1(&command); + print_link_instruction(&command, 0); /* must be pressent */ + } + break; + case 2: /* Set System Parameters instructions */ + print_if_version_2(&command); + print_system_set(&command); + print_link_instruction(&command, 1); /* either 'if' or 'link' */ + break; + case 3: /* Set General Parameters instructions */ + print_if_version_3(&command); + print_set_version_1(&command); + print_link_instruction(&command, 1); /* either 'if' or 'link' */ + break; + case 4: /* Set, Compare -> LinkSub instructions */ + print_set_version_2(&command); + fprintf(stderr, ", "); + print_if_version_4(&command); + print_linksub_instruction(&command); + break; + case 5: /* Compare -> (Set and LinkSub) instructions */ + print_if_version_4(&command); + fprintf(stderr, "{ "); + print_set_version_2(&command); + fprintf(stderr, ", "); + print_linksub_instruction(&command); + fprintf(stderr, " }"); + break; + case 6: /* Compare -> Set, always LinkSub instructions */ + print_if_version_4(&command); + fprintf(stderr, "{ "); + print_set_version_2(&command); + fprintf(stderr, " } "); + print_linksub_instruction(&command); + break; + default: + fprintf(stderr, "WARNING: Unknown instruction type (%i)", vm_getbits(&command, 0, 3)); + } + /* Check if there still are bits set that were not examined */ + + if(command.instruction & ~ command.examined) { + fprintf(stderr, " libdvdnav: vmcmd.c: [WARNING, unknown bits:"); + fprintf(stderr, " %08llx", (command.instruction & ~ command.examined) ); + fprintf(stderr, "]"); + } +} + +void vmPrint_CMD(int row, vm_cmd_t *vm_command) { + int i; + + fprintf(stderr, "(%03d) ", row + 1); + for(i = 0; i < 8; i++) + fprintf(stderr, "%02x ", vm_command->bytes[i]); + fprintf(stderr, "| "); + + vmPrint_mnemonic(vm_command); + fprintf(stderr, "\n"); +} + + diff --git a/src/input/libdvdnav/vmcmd.h b/src/input/libdvdnav/vmcmd.h new file mode 100644 index 000000000..c4079f245 --- /dev/null +++ b/src/input/libdvdnav/vmcmd.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2000, 2001 Martin Norbäck, Håkan Hjort + * + * 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: vmcmd.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $ + * + */ + +#ifndef VMCMD_H_INCLUDED +#define VMCMD_H_INCLUDED + +#include <inttypes.h> +#include <dvdread/ifo_types.h> /* Only for vm_cmd_t */ +#include "decoder.h" + +void vmPrint_mnemonic(vm_cmd_t *command); +void vmPrint_CMD(int row, vm_cmd_t *command); + +#endif /* VMCMD_H_INCLUDED */ |