Fix vf_tcdump's compilation
[mplayer/kovensky.git] / libdvdnav / dvdnav.c
blob24d9cab418eaeb81fe87731b87fa97c090af1b65
1 /*
2 * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
4 * This file is part of libdvdnav, a DVD navigation library.
6 * libdvdnav is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * libdvdnav is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with libdvdnav; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
26 #define LOG_DEBUG
29 #include <inttypes.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <unistd.h>
33 #include <limits.h>
34 #include <string.h>
35 #include <sys/time.h>
36 #include "dvdnav/dvdnav.h"
37 #include <dvdread/dvd_reader.h>
38 #include <dvdread/nav_types.h>
39 #include <dvdread/ifo_types.h> /* For vm_cmd_t */
40 #include "remap.h"
41 #include "vm/decoder.h"
42 #include "vm/vm.h"
43 #include "dvdnav_internal.h"
44 #include "read_cache.h"
45 #include <dvdread/nav_read.h>
46 #include "remap.h"
48 static dvdnav_status_t dvdnav_clear(dvdnav_t * this) {
49 /* clear everything except file, vm, mutex, readahead */
51 pthread_mutex_lock(&this->vm_lock);
52 if (this->file) DVDCloseFile(this->file);
53 this->file = NULL;
55 memset(&this->pci,0,sizeof(this->pci));
56 memset(&this->dsi,0,sizeof(this->dsi));
57 this->last_cmd_nav_lbn = SRI_END_OF_CELL;
59 /* Set initial values of flags */
60 this->position_current.still = 0;
61 this->skip_still = 0;
62 this->sync_wait = 0;
63 this->sync_wait_skip = 0;
64 this->spu_clut_changed = 0;
65 this->started = 0;
66 this->cur_cell_time = 0;
68 dvdnav_read_cache_clear(this->cache);
69 pthread_mutex_unlock(&this->vm_lock);
71 return DVDNAV_STATUS_OK;
74 dvdnav_status_t dvdnav_open(dvdnav_t** dest, const char *path) {
75 dvdnav_t *this;
76 struct timeval time;
78 /* Create a new structure */
79 fprintf(MSG_OUT, "libdvdnav: Using dvdnav version %s\n", VERSION);
81 (*dest) = NULL;
82 this = (dvdnav_t*)malloc(sizeof(dvdnav_t));
83 if(!this)
84 return DVDNAV_STATUS_ERR;
85 memset(this, 0, (sizeof(dvdnav_t) ) ); /* Make sure this structure is clean */
87 pthread_mutex_init(&this->vm_lock, NULL);
88 /* Initialise the error string */
89 printerr("");
91 /* Initialise the VM */
92 this->vm = vm_new_vm();
93 if(!this->vm) {
94 printerr("Error initialising the DVD VM.");
95 pthread_mutex_destroy(&this->vm_lock);
96 free(this);
97 return DVDNAV_STATUS_ERR;
99 if(!vm_reset(this->vm, path)) {
100 printerr("Error starting the VM / opening the DVD device.");
101 pthread_mutex_destroy(&this->vm_lock);
102 vm_free_vm(this->vm);
103 free(this);
104 return DVDNAV_STATUS_ERR;
107 /* Set the path. FIXME: Is a deep copy 'right' */
108 strncpy(this->path, path, MAX_PATH_LEN - 1);
109 this->path[MAX_PATH_LEN - 1] = '\0';
111 /* Pre-open and close a file so that the CSS-keys are cached. */
112 this->file = DVDOpenFile(vm_get_dvd_reader(this->vm), 0, DVD_READ_MENU_VOBS);
114 /* Start the read-ahead cache. */
115 this->cache = dvdnav_read_cache_new(this);
117 /* Seed the random numbers. So that the DVD VM Command rand()
118 * gives a different start value each time a DVD is played. */
119 gettimeofday(&time, NULL);
120 srand(time.tv_usec);
122 dvdnav_clear(this);
124 (*dest) = this;
125 return DVDNAV_STATUS_OK;
128 dvdnav_status_t dvdnav_close(dvdnav_t *this) {
130 #ifdef LOG_DEBUG
131 fprintf(MSG_OUT, "libdvdnav: close:called\n");
132 #endif
134 if (this->file) {
135 pthread_mutex_lock(&this->vm_lock);
136 DVDCloseFile(this->file);
137 #ifdef LOG_DEBUG
138 fprintf(MSG_OUT, "libdvdnav: close:file closing\n");
139 #endif
140 this->file = NULL;
141 pthread_mutex_unlock(&this->vm_lock);
144 /* Free the VM */
145 if(this->vm)
146 vm_free_vm(this->vm);
148 pthread_mutex_destroy(&this->vm_lock);
150 /* We leave the final freeing of the entire structure to the cache,
151 * because we don't know, if there are still buffers out in the wild,
152 * that must return first. */
153 if(this->cache)
154 dvdnav_read_cache_free(this->cache);
155 else
156 free(this);
158 return DVDNAV_STATUS_OK;
161 dvdnav_status_t dvdnav_reset(dvdnav_t *this) {
162 dvdnav_status_t result;
164 #ifdef LOG_DEBUG
165 fprintf(MSG_OUT, "libdvdnav: reset:called\n");
166 #endif
168 pthread_mutex_lock(&this->vm_lock);
170 #ifdef LOG_DEBUG
171 fprintf(MSG_OUT, "libdvdnav: reseting vm\n");
172 #endif
173 if(!vm_reset(this->vm, NULL)) {
174 printerr("Error restarting the VM.");
175 pthread_mutex_unlock(&this->vm_lock);
176 return DVDNAV_STATUS_ERR;
178 #ifdef LOG_DEBUG
179 fprintf(MSG_OUT, "libdvdnav: clearing dvdnav\n");
180 #endif
181 result = dvdnav_clear(this);
183 pthread_mutex_unlock(&this->vm_lock);
184 return result;
187 dvdnav_status_t dvdnav_path(dvdnav_t *this, const char** path) {
188 (*path) = this->path;
190 return DVDNAV_STATUS_OK;
193 const char* dvdnav_err_to_string(dvdnav_t *this) {
195 if(!this)
196 return "Hey! You gave me a NULL pointer you naughty person!";
198 return this->err_str;
201 /* converts a dvd_time_t to PTS ticks */
202 int64_t dvdnav_convert_time(dvd_time_t *time) {
203 int64_t result;
204 int64_t frames;
206 result = (time->hour >> 4 ) * 10 * 60 * 60 * 90000;
207 result += (time->hour & 0x0f) * 60 * 60 * 90000;
208 result += (time->minute >> 4 ) * 10 * 60 * 90000;
209 result += (time->minute & 0x0f) * 60 * 90000;
210 result += (time->second >> 4 ) * 10 * 90000;
211 result += (time->second & 0x0f) * 90000;
213 frames = ((time->frame_u & 0x30) >> 4) * 10;
214 frames += ((time->frame_u & 0x0f) ) ;
216 if (time->frame_u & 0x80)
217 result += frames * 3000;
218 else
219 result += frames * 3600;
221 return result;
225 * Returns 1 if block contains NAV packet, 0 otherwise.
226 * Processes said NAV packet if present.
228 * Most of the code in here is copied from xine's MPEG demuxer
229 * so any bugs which are found in that should be corrected here also.
231 static int32_t dvdnav_decode_packet(dvdnav_t *this, uint8_t *p, dsi_t *nav_dsi, pci_t *nav_pci) {
232 int32_t bMpeg1 = 0;
233 uint32_t nHeaderLen;
234 uint32_t nPacketLen;
235 uint32_t nStreamID;
237 if (p[3] == 0xBA) { /* program stream pack header */
238 int32_t nStuffingBytes;
240 bMpeg1 = (p[4] & 0x40) == 0;
242 if (bMpeg1) {
243 p += 12;
244 } else { /* mpeg2 */
245 nStuffingBytes = p[0xD] & 0x07;
246 p += 14 + nStuffingBytes;
250 if (p[3] == 0xbb) { /* program stream system header */
251 nHeaderLen = (p[4] << 8) | p[5];
252 p += 6 + nHeaderLen;
255 /* we should now have a PES packet here */
256 if (p[0] || p[1] || (p[2] != 1)) {
257 fprintf(MSG_OUT, "libdvdnav: demux error! %02x %02x %02x (should be 0x000001) \n",p[0],p[1],p[2]);
258 return 0;
261 nPacketLen = p[4] << 8 | p[5];
262 nStreamID = p[3];
264 nHeaderLen = 6;
265 p += nHeaderLen;
267 if (nStreamID == 0xbf) { /* Private stream 2 */
268 #if 0
269 int32_t i;
270 fprintf(MSG_OUT, "libdvdnav: nav packet=%u\n",p-p_start-6);
271 for(i=0;i<80;i++)
272 fprintf(MSG_OUT, "%02x ",p[i-6]);
273 fprintf(MSG_OUT, "\n");
274 #endif
276 if(p[0] == 0x00) {
277 navRead_PCI(nav_pci, p+1);
280 p += nPacketLen;
282 /* We should now have a DSI packet. */
283 if(p[6] == 0x01) {
284 nPacketLen = p[4] << 8 | p[5];
285 p += 6;
286 navRead_DSI(nav_dsi, p+1);
288 return 1;
290 return 0;
293 /* DSI is used for most angle stuff.
294 * PCI is used for only non-seemless angle stuff
296 static int32_t dvdnav_get_vobu(dvdnav_t *this, dsi_t *nav_dsi, pci_t *nav_pci, dvdnav_vobu_t *vobu) {
297 uint32_t next;
298 int32_t angle, num_angle;
300 vobu->vobu_start = nav_dsi->dsi_gi.nv_pck_lbn; /* Absolute offset from start of disk */
301 vobu->vobu_length = nav_dsi->dsi_gi.vobu_ea; /* Relative offset from vobu_start */
304 * If we're not at the end of this cell, we can determine the next
305 * VOBU to display using the VOBU_SRI information section of the
306 * DSI. Using this value correctly follows the current angle,
307 * avoiding the doubled scenes in The Matrix, and makes our life
308 * really happy.
310 * vobu_next is an offset value, 0x3fffffff = SRI_END_OF_CELL
311 * DVDs are about 6 Gigs, which is only up to 0x300000 blocks
312 * Should really assert if bit 31 != 1
315 #if 0
316 /* Old code -- may still be useful one day */
317 if(nav_dsi->vobu_sri.next_vobu != SRI_END_OF_CELL ) {
318 vobu->vobu_next = ( nav_dsi->vobu_sri.next_vobu & 0x3fffffff );
319 } else {
320 vobu->vobu_next = vobu->vobu_length;
322 #else
323 /* Relative offset from vobu_start */
324 vobu->vobu_next = ( nav_dsi->vobu_sri.next_vobu & 0x3fffffff );
325 #endif
327 vm_get_angle_info(this->vm, &angle, &num_angle);
329 /* FIMXE: The angle reset doesn't work for some reason for the moment */
330 #if 0
331 if((num_angle < angle) && (angle != 1)) {
332 fprintf(MSG_OUT, "libdvdnav: angle ends!\n");
334 /* This is to switch back to angle one when we
335 * finish with angles. */
336 dvdnav_angle_change(this, 1);
338 #endif
340 if(num_angle != 0) {
342 if((next = nav_pci->nsml_agli.nsml_agl_dsta[angle-1]) != 0) {
343 if((next & 0x3fffffff) != 0) {
344 if(next & 0x80000000)
345 vobu->vobu_next = - (int32_t)(next & 0x3fffffff);
346 else
347 vobu->vobu_next = + (int32_t)(next & 0x3fffffff);
349 } else if((next = nav_dsi->sml_agli.data[angle-1].address) != 0) {
350 vobu->vobu_length = nav_dsi->sml_pbi.ilvu_ea;
352 if((next & 0x80000000) && (next != 0x7fffffff))
353 vobu->vobu_next = - (int32_t)(next & 0x3fffffff);
354 else
355 vobu->vobu_next = + (int32_t)(next & 0x3fffffff);
359 return 1;
363 * These are the main get_next_block function which actually get the media stream video and audio etc.
365 * There are two versions: The second one is using the zero-copy read ahead cache and therefore
366 * hands out pointers targetting directly into the cache.
367 * The first one uses a memcopy to fill this cache block into the application provided memory.
368 * The benefit of this first one is that no special memory management is needed. The application is
369 * the only one responsible of allocating and freeing the memory associated with the pointer.
370 * The drawback is the additional memcopy.
373 dvdnav_status_t dvdnav_get_next_block(dvdnav_t *this, uint8_t *buf,
374 int32_t *event, int32_t *len) {
375 unsigned char *block;
376 dvdnav_status_t status;
378 block = buf;
379 status = dvdnav_get_next_cache_block(this, &block, event, len);
380 if (status == DVDNAV_STATUS_OK && block != buf) {
381 /* we received a block from the cache, copy it, so we can give it back */
382 memcpy(buf, block, DVD_VIDEO_LB_LEN);
383 dvdnav_free_cache_block(this, block);
385 return status;
388 int64_t dvdnav_get_current_time(dvdnav_t *this) {
389 int i;
390 int64_t tm=0;
391 dvd_state_t *state = &this->vm->state;
393 for(i=0; i<state->cellN-1; i++) {
394 if(!
395 (state->pgc->cell_playback[i].block_type == BLOCK_TYPE_ANGLE_BLOCK &&
396 state->pgc->cell_playback[i].block_mode != BLOCK_MODE_FIRST_CELL)
398 tm += dvdnav_convert_time(&state->pgc->cell_playback[i].playback_time);
400 tm += this->cur_cell_time;
402 return tm;
405 dvdnav_status_t dvdnav_get_next_cache_block(dvdnav_t *this, uint8_t **buf,
406 int32_t *event, int32_t *len) {
407 dvd_state_t *state;
408 int32_t result;
410 pthread_mutex_lock(&this->vm_lock);
412 if(!this->started) {
413 /* Start the VM */
414 if (!vm_start(this->vm)) {
415 printerr("Encrypted or faulty DVD");
416 pthread_mutex_unlock(&this->vm_lock);
417 return DVDNAV_STATUS_ERR;
419 this->started = 1;
422 state = &(this->vm->state);
423 (*event) = DVDNAV_NOP;
424 (*len) = 0;
426 /* Check the STOP flag */
427 if(this->vm->stopped) {
428 vm_stop(this->vm);
429 (*event) = DVDNAV_STOP;
430 this->started = 0;
431 pthread_mutex_unlock(&this->vm_lock);
432 return DVDNAV_STATUS_OK;
435 vm_position_get(this->vm, &this->position_next);
437 #ifdef LOG_DEBUG
438 fprintf(MSG_OUT, "libdvdnav: POS-NEXT ");
439 vm_position_print(this->vm, &this->position_next);
440 fprintf(MSG_OUT, "libdvdnav: POS-CUR ");
441 vm_position_print(this->vm, &this->position_current);
442 #endif
444 /* did we hop? */
445 if(this->position_current.hop_channel != this->position_next.hop_channel) {
446 (*event) = DVDNAV_HOP_CHANNEL;
447 #ifdef LOG_DEBUG
448 fprintf(MSG_OUT, "libdvdnav: HOP_CHANNEL\n");
449 #endif
450 if (this->position_next.hop_channel - this->position_current.hop_channel >= HOP_SEEK) {
451 int32_t num_angles = 0, current;
453 /* we seeked -> check for multiple angles */
454 vm_get_angle_info(this->vm, &current, &num_angles);
455 if (num_angles > 1) {
456 int32_t result, block;
457 /* we have to skip the first VOBU when seeking in a multiangle feature,
458 * because it might belong to the wrong angle */
459 block = this->position_next.cell_start + this->position_next.block;
460 result = dvdnav_read_cache_block(this->cache, block, 1, buf);
461 if(result <= 0) {
462 printerr("Error reading NAV packet.");
463 pthread_mutex_unlock(&this->vm_lock);
464 return DVDNAV_STATUS_ERR;
466 /* Decode nav into pci and dsi. Then get next VOBU info. */
467 if(!dvdnav_decode_packet(this, *buf, &this->dsi, &this->pci)) {
468 printerr("Expected NAV packet but none found.");
469 pthread_mutex_unlock(&this->vm_lock);
470 return DVDNAV_STATUS_ERR;
472 dvdnav_get_vobu(this, &this->dsi, &this->pci, &this->vobu);
473 /* skip to next, if there is a next */
474 if (this->vobu.vobu_next != SRI_END_OF_CELL) {
475 this->vobu.vobu_start += this->vobu.vobu_next;
476 this->vobu.vobu_next = 0;
478 /* update VM state */
479 this->vm->state.blockN = this->vobu.vobu_start - this->position_next.cell_start;
482 this->position_current.hop_channel = this->position_next.hop_channel;
483 /* update VOBU info */
484 this->vobu.vobu_start = this->position_next.cell_start + this->position_next.block;
485 this->vobu.vobu_next = 0;
486 /* Make blockN == vobu_length to do expected_nav */
487 this->vobu.vobu_length = 0;
488 this->vobu.blockN = 0;
489 this->sync_wait = 0;
490 pthread_mutex_unlock(&this->vm_lock);
491 return DVDNAV_STATUS_OK;
494 /* Check the HIGHLIGHT flag */
495 if(this->position_current.button != this->position_next.button) {
496 dvdnav_highlight_event_t *hevent = (dvdnav_highlight_event_t *)*buf;
498 (*event) = DVDNAV_HIGHLIGHT;
499 #ifdef LOG_DEBUG
500 fprintf(MSG_OUT, "libdvdnav: HIGHLIGHT\n");
501 #endif
502 (*len) = sizeof(dvdnav_highlight_event_t);
503 hevent->display = 1;
504 hevent->buttonN = this->position_next.button;
505 this->position_current.button = this->position_next.button;
506 pthread_mutex_unlock(&this->vm_lock);
507 return DVDNAV_STATUS_OK;
510 /* Check the WAIT flag */
511 if(this->sync_wait) {
512 (*event) = DVDNAV_WAIT;
513 #ifdef LOG_DEBUG
514 fprintf(MSG_OUT, "libdvdnav: WAIT\n");
515 #endif
516 (*len) = 0;
517 pthread_mutex_unlock(&this->vm_lock);
518 return DVDNAV_STATUS_OK;
521 /* Check to see if we need to change the currently opened VOB */
522 if((this->position_current.vts != this->position_next.vts) ||
523 (this->position_current.domain != this->position_next.domain)) {
524 dvd_read_domain_t domain;
525 int32_t vtsN;
526 dvdnav_vts_change_event_t *vts_event = (dvdnav_vts_change_event_t *)*buf;
528 if(this->file) {
529 DVDCloseFile(this->file);
530 this->file = NULL;
533 vts_event->old_vtsN = this->position_current.vts;
534 vts_event->old_domain = this->position_current.domain;
536 /* Use the DOMAIN to find whether to open menu or title VOBs */
537 switch(this->position_next.domain) {
538 case FP_DOMAIN:
539 case VMGM_DOMAIN:
540 domain = DVD_READ_MENU_VOBS;
541 vtsN = 0;
542 break;
543 case VTSM_DOMAIN:
544 domain = DVD_READ_MENU_VOBS;
545 vtsN = this->position_next.vts;
546 break;
547 case VTS_DOMAIN:
548 domain = DVD_READ_TITLE_VOBS;
549 vtsN = this->position_next.vts;
550 break;
551 default:
552 printerr("Unknown domain when changing VTS.");
553 pthread_mutex_unlock(&this->vm_lock);
554 return DVDNAV_STATUS_ERR;
557 this->position_current.vts = this->position_next.vts;
558 this->position_current.domain = this->position_next.domain;
559 dvdnav_read_cache_clear(this->cache);
560 this->file = DVDOpenFile(vm_get_dvd_reader(this->vm), vtsN, domain);
561 vts_event->new_vtsN = this->position_next.vts;
562 vts_event->new_domain = this->position_next.domain;
564 /* If couldn't open the file for some reason, moan */
565 if(this->file == NULL) {
566 printerrf("Error opening vtsN=%i, domain=%i.", vtsN, domain);
567 pthread_mutex_unlock(&this->vm_lock);
568 return DVDNAV_STATUS_ERR;
571 /* File opened successfully so return a VTS change event */
572 (*event) = DVDNAV_VTS_CHANGE;
573 #ifdef LOG_DEBUG
574 fprintf(MSG_OUT, "libdvdnav: VTS_CHANGE\n");
575 #endif
576 (*len) = sizeof(dvdnav_vts_change_event_t);
578 this->spu_clut_changed = 1;
579 this->position_current.cell = -1; /* Force an update */
580 this->position_current.spu_channel = -1; /* Force an update */
581 this->position_current.audio_channel = -1; /* Force an update */;
583 pthread_mutex_unlock(&this->vm_lock);
584 return DVDNAV_STATUS_OK;
587 /* Check if the cell changed */
588 if( (this->position_current.cell != this->position_next.cell) ||
589 (this->position_current.cell_restart != this->position_next.cell_restart) ||
590 (this->position_current.cell_start != this->position_next.cell_start) ) {
591 dvdnav_cell_change_event_t *cell_event = (dvdnav_cell_change_event_t *)*buf;
592 int32_t first_cell_nr, last_cell_nr, i;
593 dvd_state_t *state = &this->vm->state;
595 this->cur_cell_time = 0;
596 (*event) = DVDNAV_CELL_CHANGE;
597 #ifdef LOG_DEBUG
598 fprintf(MSG_OUT, "libdvdnav: CELL_CHANGE\n");
599 #endif
600 (*len) = sizeof(dvdnav_cell_change_event_t);
602 cell_event->cellN = state->cellN;
603 cell_event->pgN = state->pgN;
604 cell_event->cell_length =
605 dvdnav_convert_time(&state->pgc->cell_playback[state->cellN-1].playback_time);
607 cell_event->pg_length = 0;
608 /* Find start cell of program. */
609 first_cell_nr = state->pgc->program_map[state->pgN-1];
610 /* Find end cell of program */
611 if(state->pgN < state->pgc->nr_of_programs)
612 last_cell_nr = state->pgc->program_map[state->pgN] - 1;
613 else
614 last_cell_nr = state->pgc->nr_of_cells;
615 for (i = first_cell_nr; i <= last_cell_nr; i++)
616 cell_event->pg_length +=
617 dvdnav_convert_time(&state->pgc->cell_playback[i - 1].playback_time);
618 cell_event->pgc_length = dvdnav_convert_time(&state->pgc->playback_time);
620 cell_event->cell_start = 0;
621 for (i = 1; i < state->cellN; i++)
622 cell_event->cell_start +=
623 dvdnav_convert_time(&state->pgc->cell_playback[i - 1].playback_time);
625 cell_event->pg_start = 0;
626 for (i = 1; i < state->pgc->program_map[state->pgN-1]; i++)
627 cell_event->pg_start +=
628 dvdnav_convert_time(&state->pgc->cell_playback[i - 1].playback_time);
630 this->position_current.cell = this->position_next.cell;
631 this->position_current.cell_restart = this->position_next.cell_restart;
632 this->position_current.cell_start = this->position_next.cell_start;
633 this->position_current.block = this->position_next.block;
635 /* vobu info is used for mid cell resumes */
636 this->vobu.vobu_start = this->position_next.cell_start + this->position_next.block;
637 this->vobu.vobu_next = 0;
638 /* Make blockN == vobu_length to do expected_nav */
639 this->vobu.vobu_length = 0;
640 this->vobu.blockN = 0;
642 /* update the spu palette at least on PGC changes */
643 this->spu_clut_changed = 1;
644 this->position_current.spu_channel = -1; /* Force an update */
645 this->position_current.audio_channel = -1; /* Force an update */
647 pthread_mutex_unlock(&this->vm_lock);
648 return DVDNAV_STATUS_OK;
651 /* has the CLUT changed? */
652 if(this->spu_clut_changed) {
653 (*event) = DVDNAV_SPU_CLUT_CHANGE;
654 #ifdef LOG_DEBUG
655 fprintf(MSG_OUT, "libdvdnav: SPU_CLUT_CHANGE\n");
656 #endif
657 (*len) = 16 * sizeof(uint32_t);
658 memcpy(*buf, &(state->pgc->palette), 16 * sizeof(uint32_t));
659 this->spu_clut_changed = 0;
660 pthread_mutex_unlock(&this->vm_lock);
661 return DVDNAV_STATUS_OK;
664 /* has the SPU channel changed? */
665 if(this->position_current.spu_channel != this->position_next.spu_channel) {
666 dvdnav_spu_stream_change_event_t *stream_change = (dvdnav_spu_stream_change_event_t *)*buf;
668 (*event) = DVDNAV_SPU_STREAM_CHANGE;
669 #ifdef LOG_DEBUG
670 fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE\n");
671 #endif
672 (*len) = sizeof(dvdnav_spu_stream_change_event_t);
673 stream_change->physical_wide = vm_get_subp_active_stream(this->vm, 0);
674 stream_change->physical_letterbox = vm_get_subp_active_stream(this->vm, 1);
675 stream_change->physical_pan_scan = vm_get_subp_active_stream(this->vm, 2);
676 this->position_current.spu_channel = this->position_next.spu_channel;
677 #ifdef LOG_DEBUG
678 fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE stream_id_wide=%d\n",stream_change->physical_wide);
679 fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE stream_id_letterbox=%d\n",stream_change->physical_letterbox);
680 fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE stream_id_pan_scan=%d\n",stream_change->physical_pan_scan);
681 fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE returning DVDNAV_STATUS_OK\n");
682 #endif
683 pthread_mutex_unlock(&this->vm_lock);
684 return DVDNAV_STATUS_OK;
687 /* has the audio channel changed? */
688 if(this->position_current.audio_channel != this->position_next.audio_channel) {
689 dvdnav_audio_stream_change_event_t *stream_change = (dvdnav_audio_stream_change_event_t *)*buf;
691 (*event) = DVDNAV_AUDIO_STREAM_CHANGE;
692 #ifdef LOG_DEBUG
693 fprintf(MSG_OUT, "libdvdnav: AUDIO_STREAM_CHANGE\n");
694 #endif
695 (*len) = sizeof(dvdnav_audio_stream_change_event_t);
696 stream_change->physical = vm_get_audio_active_stream( this->vm );
697 stream_change->logical = this->position_next.audio_channel;
698 this->position_current.audio_channel = this->position_next.audio_channel;
699 #ifdef LOG_DEBUG
700 fprintf(MSG_OUT, "libdvdnav: AUDIO_STREAM_CHANGE stream_id=%d returning DVDNAV_STATUS_OK\n",stream_change->physical);
701 #endif
702 pthread_mutex_unlock(&this->vm_lock);
703 return DVDNAV_STATUS_OK;
706 /* Check the STILLFRAME flag */
707 if(this->position_current.still != 0) {
708 dvdnav_still_event_t *still_event = (dvdnav_still_event_t *)*buf;
710 (*event) = DVDNAV_STILL_FRAME;
711 #ifdef LOG_DEBUG
712 fprintf(MSG_OUT, "libdvdnav: STILL_FRAME\n");
713 #endif
714 (*len) = sizeof(dvdnav_still_event_t);
715 still_event->length = this->position_current.still;
716 pthread_mutex_unlock(&this->vm_lock);
717 return DVDNAV_STATUS_OK;
720 /* Have we reached the end of a VOBU? */
721 if (this->vobu.blockN >= this->vobu.vobu_length) {
723 /* Have we reached the end of a cell? */
724 if(this->vobu.vobu_next == SRI_END_OF_CELL) {
725 /* End of Cell from NAV DSI info */
726 #ifdef LOG_DEBUG
727 fprintf(MSG_OUT, "libdvdnav: Still set to %x\n", this->position_next.still);
728 #endif
729 this->position_current.still = this->position_next.still;
731 /* we are about to leave a cell, so a lot of state changes could occur;
732 * under certain conditions, the application should get in sync with us before this,
733 * otherwise it might show stills or menus too shortly */
734 if ((this->position_current.still || this->pci.hli.hl_gi.hli_ss) && !this->sync_wait_skip) {
735 this->sync_wait = 1;
736 } else {
737 if( this->position_current.still == 0 || this->skip_still ) {
738 /* no active cell still -> get us to the next cell */
739 vm_get_next_cell(this->vm);
740 this->position_current.still = 0; /* still gets activated at end of cell */
741 this->skip_still = 0;
742 this->sync_wait_skip = 0;
745 /* handle related state changes in next iteration */
746 (*event) = DVDNAV_NOP;
747 (*len) = 0;
748 pthread_mutex_unlock(&this->vm_lock);
749 return DVDNAV_STATUS_OK;
752 /* Perform remapping jump if necessary (this is always a
753 * VOBU boundary). */
754 if (this->vm->map) {
755 this->vobu.vobu_next = remap_block( this->vm->map,
756 this->vm->state.domain, this->vm->state.TTN_REG,
757 this->vm->state.pgN,
758 this->vobu.vobu_start, this->vobu.vobu_next);
761 /* at the start of the next VOBU -> expecting NAV packet */
762 result = dvdnav_read_cache_block(this->cache, this->vobu.vobu_start + this->vobu.vobu_next, 1, buf);
764 if(result <= 0) {
765 printerr("Error reading NAV packet.");
766 pthread_mutex_unlock(&this->vm_lock);
767 return DVDNAV_STATUS_ERR;
769 /* Decode nav into pci and dsi. Then get next VOBU info. */
770 if(!dvdnav_decode_packet(this, *buf, &this->dsi, &this->pci)) {
771 printerr("Expected NAV packet but none found.");
772 pthread_mutex_unlock(&this->vm_lock);
773 return DVDNAV_STATUS_ERR;
775 /* We need to update the vm state->blockN with which VOBU we are in.
776 * This is so RSM resumes to the VOBU level and not just the CELL level.
778 this->vm->state.blockN = this->vobu.vobu_start - this->position_current.cell_start;
780 dvdnav_get_vobu(this, &this->dsi, &this->pci, &this->vobu);
781 this->vobu.blockN = 0;
782 /* Give the cache a hint about the size of next VOBU.
783 * This improves pre-caching, because the VOBU will almost certainly be read entirely.
785 dvdnav_pre_cache_blocks(this->cache, this->vobu.vobu_start+1, this->vobu.vobu_length+1);
787 /* release NAV menu filter, when we reach the same NAV packet again */
788 if (this->last_cmd_nav_lbn == this->pci.pci_gi.nv_pck_lbn)
789 this->last_cmd_nav_lbn = SRI_END_OF_CELL;
791 /* Successfully got a NAV packet */
792 (*event) = DVDNAV_NAV_PACKET;
793 #ifdef LOG_DEBUG
794 fprintf(MSG_OUT, "libdvdnav: NAV_PACKET\n");
795 #endif
796 (*len) = 2048;
797 this->cur_cell_time = dvdnav_convert_time(&this->dsi.dsi_gi.c_eltm);
798 pthread_mutex_unlock(&this->vm_lock);
799 return DVDNAV_STATUS_OK;
802 /* If we've got here, it must just be a normal block. */
803 if(!this->file) {
804 printerr("Attempting to read without opening file.");
805 pthread_mutex_unlock(&this->vm_lock);
806 return DVDNAV_STATUS_ERR;
809 this->vobu.blockN++;
810 result = dvdnav_read_cache_block(this->cache, this->vobu.vobu_start + this->vobu.blockN, 1, buf);
811 if(result <= 0) {
812 printerr("Error reading from DVD.");
813 pthread_mutex_unlock(&this->vm_lock);
814 return DVDNAV_STATUS_ERR;
816 (*event) = DVDNAV_BLOCK_OK;
817 (*len) = 2048;
819 pthread_mutex_unlock(&this->vm_lock);
820 return DVDNAV_STATUS_OK;
823 dvdnav_status_t dvdnav_get_title_string(dvdnav_t *this, const char **title_str) {
824 (*title_str) = this->vm->dvd_name;
825 return DVDNAV_STATUS_OK;
828 dvdnav_status_t dvdnav_get_serial_string(dvdnav_t *this, const char **serial_str) {
829 (*serial_str) = this->vm->dvd_serial;
830 return DVDNAV_STATUS_OK;
833 uint8_t dvdnav_get_video_aspect(dvdnav_t *this) {
834 uint8_t retval;
836 if(!this->started) {
837 printerr("Virtual DVD machine not started.");
838 return -1;
841 pthread_mutex_lock(&this->vm_lock);
842 retval = (uint8_t)vm_get_video_aspect(this->vm);
843 pthread_mutex_unlock(&this->vm_lock);
845 return retval;
848 uint8_t dvdnav_get_video_scale_permission(dvdnav_t *this) {
849 uint8_t retval;
851 if(!this->started) {
852 printerr("Virtual DVD machine not started.");
853 return -1;
856 pthread_mutex_lock(&this->vm_lock);
857 retval = (uint8_t)vm_get_video_scale_permission(this->vm);
858 pthread_mutex_unlock(&this->vm_lock);
860 return retval;
863 uint16_t dvdnav_audio_stream_to_lang(dvdnav_t *this, uint8_t stream) {
864 audio_attr_t attr;
866 if(!this->started) {
867 printerr("Virtual DVD machine not started.");
868 return -1;
871 pthread_mutex_lock(&this->vm_lock);
872 attr = vm_get_audio_attr(this->vm, stream);
873 pthread_mutex_unlock(&this->vm_lock);
875 if(attr.lang_type != 1)
876 return 0xffff;
878 return attr.lang_code;
881 uint16_t dvdnav_audio_stream_format(dvdnav_t *this, uint8_t stream) {
882 audio_attr_t attr;
883 uint16_t format;
885 if(!this->started) {
886 printerr("Virtual DVD machine not started.");
887 return -1; /* 0xffff */
890 pthread_mutex_lock(&this->vm_lock);
891 attr = vm_get_audio_attr(this->vm, stream);
892 pthread_mutex_unlock(&this->vm_lock);
894 switch(attr.audio_format) {
895 case 0:
896 format = DVDNAV_FORMAT_AC3;
897 break;
898 case 2: /* MPEG-1 or MPEG-2 without extension bitstream. */
899 case 3: /* MPEG-2 with extension bitstream. */
900 format = DVDNAV_FORMAT_MPEGAUDIO;
901 break;
902 case 4:
903 format = DVDNAV_FORMAT_LPCM;
904 break;
905 case 6:
906 format = DVDNAV_FORMAT_DTS;
907 break;
908 case 7:
909 format = DVDNAV_FORMAT_SDDS;
910 break;
911 default:
912 format = 0xffff;
913 break;
916 return format;
919 uint16_t dvdnav_audio_stream_channels(dvdnav_t *this, uint8_t stream) {
920 audio_attr_t attr;
922 if(!this->started) {
923 printerr("Virtual DVD machine not started.");
924 return -1; /* 0xffff */
927 pthread_mutex_lock(&this->vm_lock);
928 attr = vm_get_audio_attr(this->vm, stream);
929 pthread_mutex_unlock(&this->vm_lock);
931 return attr.channels + 1;
934 uint16_t dvdnav_spu_stream_to_lang(dvdnav_t *this, uint8_t stream) {
935 subp_attr_t attr;
937 if(!this->started) {
938 printerr("Virtual DVD machine not started.");
939 return -1;
942 pthread_mutex_lock(&this->vm_lock);
943 attr = vm_get_subp_attr(this->vm, stream);
944 pthread_mutex_unlock(&this->vm_lock);
946 if(attr.type != 1)
947 return 0xffff;
949 return attr.lang_code;
952 int8_t dvdnav_get_audio_logical_stream(dvdnav_t *this, uint8_t audio_num) {
953 int8_t retval;
955 if(!this->started) {
956 printerr("Virtual DVD machine not started.");
957 return -1;
960 pthread_mutex_lock(&this->vm_lock);
961 if (!this->vm->state.pgc) {
962 printerr("No current PGC.");
963 pthread_mutex_unlock(&this->vm_lock);
964 return -1;
966 retval = vm_get_audio_stream(this->vm, audio_num);
967 pthread_mutex_unlock(&this->vm_lock);
969 return retval;
972 dvdnav_status_t dvdnav_get_audio_attr(dvdnav_t *this, uint8_t audio_num, audio_attr_t *audio_attr) {
973 if(!this->started) {
974 printerr("Virtual DVD machine not started.");
975 return -1;
977 pthread_mutex_lock(&this->vm_lock);
978 if (!this->vm->state.pgc) {
979 printerr("No current PGC.");
980 pthread_mutex_unlock(&this->vm_lock);
981 return -1;
983 *audio_attr=vm_get_audio_attr(this->vm, audio_num);
984 pthread_mutex_unlock(&this->vm_lock);
986 return DVDNAV_STATUS_OK;
989 int8_t dvdnav_get_spu_logical_stream(dvdnav_t *this, uint8_t subp_num) {
990 int8_t retval;
992 if(!this->started) {
993 printerr("Virtual DVD machine not started.");
994 return -1;
997 pthread_mutex_lock(&this->vm_lock);
998 if (!this->vm->state.pgc) {
999 printerr("No current PGC.");
1000 pthread_mutex_unlock(&this->vm_lock);
1001 return -1;
1003 retval = vm_get_subp_stream(this->vm, subp_num, 0);
1004 pthread_mutex_unlock(&this->vm_lock);
1006 return retval;
1009 dvdnav_status_t dvdnav_get_spu_attr(dvdnav_t *this, uint8_t audio_num, subp_attr_t *subp_attr) {
1010 if(!this->started) {
1011 printerr("Virtual DVD machine not started.");
1012 return -1;
1014 pthread_mutex_lock(&this->vm_lock);
1015 if (!this->vm->state.pgc) {
1016 printerr("No current PGC.");
1017 pthread_mutex_unlock(&this->vm_lock);
1018 return -1;
1020 *subp_attr=vm_get_subp_attr(this->vm, audio_num);
1021 pthread_mutex_unlock(&this->vm_lock);
1022 return DVDNAV_STATUS_OK;
1025 int8_t dvdnav_get_active_audio_stream(dvdnav_t *this) {
1026 int8_t retval;
1028 if(!this->started) {
1029 printerr("Virtual DVD machine not started.");
1030 return -1;
1033 pthread_mutex_lock(&this->vm_lock);
1034 if (!this->vm->state.pgc) {
1035 printerr("No current PGC.");
1036 pthread_mutex_unlock(&this->vm_lock);
1037 return -1;
1039 retval = vm_get_audio_active_stream(this->vm);
1040 pthread_mutex_unlock(&this->vm_lock);
1042 return retval;
1045 int8_t dvdnav_get_active_spu_stream(dvdnav_t *this) {
1046 int8_t retval;
1048 if(!this->started) {
1049 printerr("Virtual DVD machine not started.");
1050 return -1;
1053 pthread_mutex_lock(&this->vm_lock);
1054 if (!this->vm->state.pgc) {
1055 printerr("No current PGC.");
1056 pthread_mutex_unlock(&this->vm_lock);
1057 return -1;
1059 retval = vm_get_subp_active_stream(this->vm, 0);
1060 pthread_mutex_unlock(&this->vm_lock);
1062 return retval;
1065 static int8_t dvdnav_is_domain(dvdnav_t *this, domain_t domain) {
1066 int8_t retval;
1068 if(!this->started) {
1069 printerr("Virtual DVD machine not started.");
1070 return -1;
1073 pthread_mutex_lock(&this->vm_lock);
1074 retval = (this->vm->state.domain == domain);
1075 pthread_mutex_unlock(&this->vm_lock);
1077 return retval;
1080 /* First Play domain. (Menu) */
1081 int8_t dvdnav_is_domain_fp(dvdnav_t *this) {
1082 return dvdnav_is_domain(this, FP_DOMAIN);
1084 /* Video management Menu domain. (Menu) */
1085 int8_t dvdnav_is_domain_vmgm(dvdnav_t *this) {
1086 return dvdnav_is_domain(this, VMGM_DOMAIN);
1088 /* Video Title Menu domain (Menu) */
1089 int8_t dvdnav_is_domain_vtsm(dvdnav_t *this) {
1090 return dvdnav_is_domain(this, VTSM_DOMAIN);
1092 /* Video Title domain (playing movie). */
1093 int8_t dvdnav_is_domain_vts(dvdnav_t *this) {
1094 return dvdnav_is_domain(this, VTS_DOMAIN);
1097 /* Generally delegate angle information handling to VM */
1098 dvdnav_status_t dvdnav_angle_change(dvdnav_t *this, int32_t angle) {
1099 int32_t num, current;
1101 pthread_mutex_lock(&this->vm_lock);
1102 vm_get_angle_info(this->vm, &current, &num);
1103 /* Set angle SPRM if valid */
1104 if((angle > 0) && (angle <= num)) {
1105 this->vm->state.AGL_REG = angle;
1106 } else {
1107 printerr("Passed an invalid angle number.");
1108 pthread_mutex_unlock(&this->vm_lock);
1109 return DVDNAV_STATUS_ERR;
1111 pthread_mutex_unlock(&this->vm_lock);
1113 return DVDNAV_STATUS_OK;
1116 dvdnav_status_t dvdnav_get_angle_info(dvdnav_t *this, int32_t *current_angle,
1117 int32_t *number_of_angles) {
1118 pthread_mutex_lock(&this->vm_lock);
1119 vm_get_angle_info(this->vm, current_angle, number_of_angles);
1120 pthread_mutex_unlock(&this->vm_lock);
1122 return DVDNAV_STATUS_OK;
1125 pci_t* dvdnav_get_current_nav_pci(dvdnav_t *this) {
1126 if(!this) return 0;
1127 return &this->pci;
1130 dsi_t* dvdnav_get_current_nav_dsi(dvdnav_t *this) {
1131 if(!this) return 0;
1132 return &this->dsi;
1135 uint32_t dvdnav_get_next_still_flag(dvdnav_t *this) {
1136 if(!this) return -1;
1137 return this->position_next.still;
1140 user_ops_t dvdnav_get_restrictions(dvdnav_t* this) {
1142 * user_ops_t is a structure of 32 bits. We want to compute
1143 * the union of two of those bitfields so to make this quicker
1144 * than performing 32 ORs, we will access them as 32bits words.
1146 union {
1147 user_ops_t ops_struct;
1148 uint32_t ops_int;
1149 } ops;
1151 ops.ops_int = 0;
1153 if(!this->started) {
1154 printerr("Virtual DVD machine not started.");
1155 return ops.ops_struct;
1158 pthread_mutex_lock(&this->vm_lock);
1159 ops.ops_int |= *(uint32_t*)&this->pci.pci_gi.vobu_uop_ctl;
1161 if(this->vm && this->vm->state.pgc)
1162 ops.ops_int |= *(uint32_t*)&this->vm->state.pgc->prohibited_ops;
1163 pthread_mutex_unlock(&this->vm_lock);
1165 return ops.ops_struct;