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.
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 */
41 #include "vm/decoder.h"
43 #include "dvdnav_internal.h"
44 #include "read_cache.h"
45 #include <dvdread/nav_read.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
);
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;
63 this->sync_wait_skip
= 0;
64 this->spu_clut_changed
= 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
) {
78 /* Create a new structure */
79 fprintf(MSG_OUT
, "libdvdnav: Using dvdnav version %s\n", VERSION
);
82 this = (dvdnav_t
*)malloc(sizeof(dvdnav_t
));
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 */
91 /* Initialise the VM */
92 this->vm
= vm_new_vm();
94 printerr("Error initialising the DVD VM.");
95 pthread_mutex_destroy(&this->vm_lock
);
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
);
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
);
125 return DVDNAV_STATUS_OK
;
128 dvdnav_status_t
dvdnav_close(dvdnav_t
*this) {
131 fprintf(MSG_OUT
, "libdvdnav: close:called\n");
135 pthread_mutex_lock(&this->vm_lock
);
136 DVDCloseFile(this->file
);
138 fprintf(MSG_OUT
, "libdvdnav: close:file closing\n");
141 pthread_mutex_unlock(&this->vm_lock
);
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. */
154 dvdnav_read_cache_free(this->cache
);
158 return DVDNAV_STATUS_OK
;
161 dvdnav_status_t
dvdnav_reset(dvdnav_t
*this) {
162 dvdnav_status_t result
;
165 fprintf(MSG_OUT
, "libdvdnav: reset:called\n");
168 pthread_mutex_lock(&this->vm_lock
);
171 fprintf(MSG_OUT
, "libdvdnav: reseting vm\n");
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
;
179 fprintf(MSG_OUT
, "libdvdnav: clearing dvdnav\n");
181 result
= dvdnav_clear(this);
183 pthread_mutex_unlock(&this->vm_lock
);
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) {
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
) {
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;
219 result
+= frames
* 3600;
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
) {
237 if (p
[3] == 0xBA) { /* program stream pack header */
238 int32_t nStuffingBytes
;
240 bMpeg1
= (p
[4] & 0x40) == 0;
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];
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]);
261 nPacketLen
= p
[4] << 8 | p
[5];
267 if (nStreamID
== 0xbf) { /* Private stream 2 */
270 fprintf(MSG_OUT
, "libdvdnav: nav packet=%u\n",p
-p_start
-6);
272 fprintf(MSG_OUT
, "%02x ",p
[i
-6]);
273 fprintf(MSG_OUT
, "\n");
277 navRead_PCI(nav_pci
, p
+1);
282 /* We should now have a DSI packet. */
284 nPacketLen
= p
[4] << 8 | p
[5];
286 navRead_DSI(nav_dsi
, p
+1);
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
) {
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
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
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 );
320 vobu
->vobu_next
= vobu
->vobu_length
;
323 /* Relative offset from vobu_start */
324 vobu
->vobu_next
= ( nav_dsi
->vobu_sri
.next_vobu
& 0x3fffffff );
327 vm_get_angle_info(this->vm
, &angle
, &num_angle
);
329 /* FIMXE: The angle reset doesn't work for some reason for the moment */
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);
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);
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);
355 vobu
->vobu_next
= + (int32_t)(next
& 0x3fffffff);
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
;
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
);
388 int64_t dvdnav_get_current_time(dvdnav_t
*this) {
391 dvd_state_t
*state
= &this->vm
->state
;
393 for(i
=0; i
<state
->cellN
-1; i
++) {
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
;
405 dvdnav_status_t
dvdnav_get_next_cache_block(dvdnav_t
*this, uint8_t **buf
,
406 int32_t *event
, int32_t *len
) {
410 pthread_mutex_lock(&this->vm_lock
);
414 if (!vm_start(this->vm
)) {
415 printerr("Encrypted or faulty DVD");
416 pthread_mutex_unlock(&this->vm_lock
);
417 return DVDNAV_STATUS_ERR
;
422 state
= &(this->vm
->state
);
423 (*event
) = DVDNAV_NOP
;
426 /* Check the STOP flag */
427 if(this->vm
->stopped
) {
429 (*event
) = DVDNAV_STOP
;
431 pthread_mutex_unlock(&this->vm_lock
);
432 return DVDNAV_STATUS_OK
;
435 vm_position_get(this->vm
, &this->position_next
);
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
);
445 if(this->position_current
.hop_channel
!= this->position_next
.hop_channel
) {
446 (*event
) = DVDNAV_HOP_CHANNEL
;
448 fprintf(MSG_OUT
, "libdvdnav: HOP_CHANNEL\n");
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
, ¤t
, &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
);
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;
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
;
500 fprintf(MSG_OUT
, "libdvdnav: HIGHLIGHT\n");
502 (*len
) = sizeof(dvdnav_highlight_event_t
);
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
;
514 fprintf(MSG_OUT
, "libdvdnav: WAIT\n");
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
;
526 dvdnav_vts_change_event_t
*vts_event
= (dvdnav_vts_change_event_t
*)*buf
;
529 DVDCloseFile(this->file
);
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
) {
540 domain
= DVD_READ_MENU_VOBS
;
544 domain
= DVD_READ_MENU_VOBS
;
545 vtsN
= this->position_next
.vts
;
548 domain
= DVD_READ_TITLE_VOBS
;
549 vtsN
= this->position_next
.vts
;
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
;
574 fprintf(MSG_OUT
, "libdvdnav: VTS_CHANGE\n");
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
;
598 fprintf(MSG_OUT
, "libdvdnav: CELL_CHANGE\n");
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;
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
;
655 fprintf(MSG_OUT
, "libdvdnav: SPU_CLUT_CHANGE\n");
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
;
670 fprintf(MSG_OUT
, "libdvdnav: SPU_STREAM_CHANGE\n");
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
;
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");
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
;
693 fprintf(MSG_OUT
, "libdvdnav: AUDIO_STREAM_CHANGE\n");
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
;
700 fprintf(MSG_OUT
, "libdvdnav: AUDIO_STREAM_CHANGE stream_id=%d returning DVDNAV_STATUS_OK\n",stream_change
->physical
);
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
;
712 fprintf(MSG_OUT
, "libdvdnav: STILL_FRAME\n");
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 */
727 fprintf(MSG_OUT
, "libdvdnav: Still set to %x\n", this->position_next
.still
);
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
) {
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
;
748 pthread_mutex_unlock(&this->vm_lock
);
749 return DVDNAV_STATUS_OK
;
752 /* Perform remapping jump if necessary (this is always a
755 this->vobu
.vobu_next
= remap_block( this->vm
->map
,
756 this->vm
->state
.domain
, this->vm
->state
.TTN_REG
,
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
);
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
;
794 fprintf(MSG_OUT
, "libdvdnav: NAV_PACKET\n");
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. */
804 printerr("Attempting to read without opening file.");
805 pthread_mutex_unlock(&this->vm_lock
);
806 return DVDNAV_STATUS_ERR
;
810 result
= dvdnav_read_cache_block(this->cache
, this->vobu
.vobu_start
+ this->vobu
.blockN
, 1, buf
);
812 printerr("Error reading from DVD.");
813 pthread_mutex_unlock(&this->vm_lock
);
814 return DVDNAV_STATUS_ERR
;
816 (*event
) = DVDNAV_BLOCK_OK
;
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) {
837 printerr("Virtual DVD machine not started.");
841 pthread_mutex_lock(&this->vm_lock
);
842 retval
= (uint8_t)vm_get_video_aspect(this->vm
);
843 pthread_mutex_unlock(&this->vm_lock
);
848 uint8_t dvdnav_get_video_scale_permission(dvdnav_t
*this) {
852 printerr("Virtual DVD machine not started.");
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
);
863 uint16_t dvdnav_audio_stream_to_lang(dvdnav_t
*this, uint8_t stream
) {
867 printerr("Virtual DVD machine not started.");
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)
878 return attr
.lang_code
;
881 uint16_t dvdnav_audio_stream_format(dvdnav_t
*this, uint8_t stream
) {
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
) {
896 format
= DVDNAV_FORMAT_AC3
;
898 case 2: /* MPEG-1 or MPEG-2 without extension bitstream. */
899 case 3: /* MPEG-2 with extension bitstream. */
900 format
= DVDNAV_FORMAT_MPEGAUDIO
;
903 format
= DVDNAV_FORMAT_LPCM
;
906 format
= DVDNAV_FORMAT_DTS
;
909 format
= DVDNAV_FORMAT_SDDS
;
919 uint16_t dvdnav_audio_stream_channels(dvdnav_t
*this, uint8_t stream
) {
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
) {
938 printerr("Virtual DVD machine not started.");
942 pthread_mutex_lock(&this->vm_lock
);
943 attr
= vm_get_subp_attr(this->vm
, stream
);
944 pthread_mutex_unlock(&this->vm_lock
);
949 return attr
.lang_code
;
952 int8_t dvdnav_get_audio_logical_stream(dvdnav_t
*this, uint8_t audio_num
) {
956 printerr("Virtual DVD machine not started.");
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
);
966 retval
= vm_get_audio_stream(this->vm
, audio_num
);
967 pthread_mutex_unlock(&this->vm_lock
);
972 dvdnav_status_t
dvdnav_get_audio_attr(dvdnav_t
*this, uint8_t audio_num
, audio_attr_t
*audio_attr
) {
974 printerr("Virtual DVD machine not started.");
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
);
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
) {
993 printerr("Virtual DVD machine not started.");
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
);
1003 retval
= vm_get_subp_stream(this->vm
, subp_num
, 0);
1004 pthread_mutex_unlock(&this->vm_lock
);
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.");
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
);
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) {
1028 if(!this->started
) {
1029 printerr("Virtual DVD machine not started.");
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
);
1039 retval
= vm_get_audio_active_stream(this->vm
);
1040 pthread_mutex_unlock(&this->vm_lock
);
1045 int8_t dvdnav_get_active_spu_stream(dvdnav_t
*this) {
1048 if(!this->started
) {
1049 printerr("Virtual DVD machine not started.");
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
);
1059 retval
= vm_get_subp_active_stream(this->vm
, 0);
1060 pthread_mutex_unlock(&this->vm_lock
);
1065 static int8_t dvdnav_is_domain(dvdnav_t
*this, domain_t domain
) {
1068 if(!this->started
) {
1069 printerr("Virtual DVD machine not started.");
1073 pthread_mutex_lock(&this->vm_lock
);
1074 retval
= (this->vm
->state
.domain
== domain
);
1075 pthread_mutex_unlock(&this->vm_lock
);
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
, ¤t
, &num
);
1103 /* Set angle SPRM if valid */
1104 if((angle
> 0) && (angle
<= num
)) {
1105 this->vm
->state
.AGL_REG
= angle
;
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) {
1130 dsi_t
* dvdnav_get_current_nav_dsi(dvdnav_t
*this) {
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.
1147 user_ops_t ops_struct
;
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
;