comparison dvdnav.c @ 0:3ddf0eaece51 src

Initial revision
author richwareham
date Tue, 12 Mar 2002 19:45:53 +0000
parents
children 328eadb3f37e
comparison
equal deleted inserted replaced
-1:000000000000 0:3ddf0eaece51
1 /*
2 * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
3 *
4 * This file is part of libdvdnav, a DVD navigation library.
5 *
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.
10 *
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.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 *
20 * $Id$
21 *
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <pthread.h>
29 #include <dvdnav.h>
30 #include "dvdnav_internal.h"
31 #include "read_cache.h"
32
33 #include <dvdread/nav_read.h>
34
35 #include <stdlib.h>
36 #include <stdio.h>
37
38 dvdnav_status_t dvdnav_open(dvdnav_t** dest, char *path) {
39 dvdnav_t *self;
40
41 /* Create a new structure */
42 (*dest) = NULL;
43 self = (dvdnav_t*)malloc(sizeof(dvdnav_t));
44 if(!self)
45 return S_ERR;
46 memset(self, 0, (sizeof(dvdnav_t) ) ); /* Make sure self structure is clean */
47
48 pthread_mutex_init(&self->vm_lock, NULL);
49 /* Initialise the error string */
50 printerr("");
51
52 /* Initialise the VM */
53 self->vm = vm_new_vm();
54 if(!self->vm) {
55 printerr("Error initialising the DVD VM");
56 return S_ERR;
57 }
58 if(vm_reset(self->vm, path) == -1) {
59 printerr("Error starting the VM / opening the DVD device");
60 return S_ERR;
61 }
62
63 /* Set the path. FIXME: Is a deep copy 'right' */
64 strncpy(self->path, path, MAX_PATH_LEN);
65
66 /* Set initial values of flags */
67 self->expecting_nav_packet = 1;
68 self->started = 0;
69
70 self->open_vtsN = -1;
71 self->open_domain = -1;
72 self->file = NULL;
73 self->cell = NULL;
74 self->at_soc = 1;
75 self->jumping = 0;
76 self->seeking = 0;
77 self->still_frame = -1;
78 self->cache_buffer = NULL;
79 self->cache_start_sector = -1;
80 self->cache_block_count = 0;
81 self->cache_valid = 0;
82 self->use_read_ahead = 1;
83 self->stop = 0;
84 self->highlight_changed = 0;
85 self->spu_clut_changed = 0;
86
87 self->vobu_start = self->vobu_length = 0;
88
89 /* Pre-open and close a file so that the CSS-keys are cached. */
90 self->file = DVDOpenFile(vm_get_dvd_reader(self->vm), 0, DVD_READ_MENU_VOBS);
91 if (self->file) DVDCloseFile(self->file);
92 self->file = NULL;
93
94 if(!self->started) {
95 /* Start the VM */
96 vm_start(self->vm);
97 self->started = 1;
98 }
99
100 (*dest) = self;
101 return S_OK;
102 }
103
104 dvdnav_status_t dvdnav_close(dvdnav_t *self) {
105 if(!self) {
106 printerr("Passed a NULL pointer");
107 return S_ERR;
108 }
109 printf("dvdnav:close:called\n");
110 if (self->file) {
111 DVDCloseFile(self->file);
112 printf("dvdnav:close:file closing\n");
113 self->file = NULL;
114 }
115
116 /* Free the VM */
117 if(self->vm) {
118 vm_free_vm(self->vm);
119 }
120 if (self->file) {
121 DVDCloseFile(self->file);
122 printf("dvdnav:close2:file closing\n");
123 self->file = NULL;
124 }
125 pthread_mutex_destroy(&self->vm_lock);
126 /* Finally free the entire structure */
127 free(self);
128
129 return S_OK;
130 }
131
132 dvdnav_status_t dvdnav_path(dvdnav_t *self, char** path) {
133 if(!self || !path || !(*path)) {
134 return S_ERR;
135 }
136
137 /* FIXME: Is shallow copy 'right'? */
138 (*path) = self->path;
139
140 return S_OK;
141 }
142
143 char* dvdnav_err_to_string(dvdnav_t *self) {
144 if(!self) {
145 /* Shold this be "passed a NULL pointer?" */
146 return NULL;
147 }
148
149 return self->err_str;
150 }
151
152 /**
153 * Returns 1 if block contains NAV packet, 0 otherwise.
154 * Precesses said NAV packet if present.
155 *
156 * Most of the code in here is copied from xine's MPEG demuxer
157 * so any bugs which are found in that should be corrected here also.
158 */
159 int dvdnav_check_packet(dvdnav_t *self, uint8_t *p) {
160 int bMpeg1=0;
161 uint32_t nHeaderLen;
162 uint32_t nPacketLen;
163 uint32_t nStreamID;
164 /* uint8_t *p_start=p; */
165
166
167 if (p==NULL) {
168 printf("Passed a NULL pointer.\n");
169 return 0;
170 }
171
172 /* dprint("Checking packet...\n"); */
173
174 if (p[3] == 0xBA) { /* program stream pack header */
175
176 int nStuffingBytes;
177
178 /* xprintf (VERBOSE|DEMUX, "program stream pack header\n"); */
179
180 bMpeg1 = (p[4] & 0x40) == 0;
181
182 if (bMpeg1) {
183 p += 12;
184 } else { /* mpeg2 */
185 nStuffingBytes = p[0xD] & 0x07;
186 p += 14 + nStuffingBytes;
187 }
188 }
189
190
191 if (p[3] == 0xbb) { /* program stream system header */
192 int nHeaderLen;
193
194 nHeaderLen = (p[4] << 8) | p[5];
195 p += 6 + nHeaderLen;
196 }
197
198 /* we should now have a PES packet here */
199
200 if (p[0] || p[1] || (p[2] != 1)) {
201 printf("demux error! %02x %02x %02x (should be 0x000001) \n",p[0],p[1],p[2]);
202 return 0;
203 }
204
205 nPacketLen = p[4] << 8 | p[5];
206 nStreamID = p[3];
207
208 nHeaderLen = 6;
209 p += nHeaderLen;
210
211 if (nStreamID == 0xbf) { /* Private stream 2 */
212 /*
213 * int i;
214 * printf("dvdnav:nav packet=%u\n",p-p_start-6);
215 * for(i=0;i<80;i++) {
216 * printf("%02x ",p[i-6]);
217 * }
218 * printf("\n");
219 */
220 if(p[0] == 0x00) {
221 #ifdef HAVE_DVDREAD9
222 navRead_PCI(&(self->pci), p+1);
223 #else
224 navRead_PCI(&(self->pci), p+1, nPacketLen - 1);
225 #endif
226 }
227
228 p += nPacketLen;
229
230 /* We should now have a DSI packet. */
231 if(p[6] == 0x01) {
232 int num=0, current=0;
233
234 nPacketLen = p[4] << 8 | p[5];
235 p += 6;
236 /* dprint("NAV DSI packet\n"); */
237 #ifdef HAVE_DVDREAD9
238 navRead_DSI(&(self->dsi), p+1);
239 #else
240 navRead_DSI(&(self->dsi), p+1, sizeof(dsi_t));
241 #endif
242
243 self->vobu_start = self->dsi.dsi_gi.nv_pck_lbn;
244 self->vobu_length = self->dsi.dsi_gi.vobu_ea;
245
246 /**
247 * If we're not at the end of this cell, we can determine the next
248 * VOBU to display using the VOBU_SRI information section of the
249 * DSI. Using this value correctly follows the current angle,
250 * avoiding the doubled scenes in The Matrix, and makes our life
251 * really happy.
252 *
253 * Otherwise, we set our next address past the end of this cell to
254 * force the code above to go to the next cell in the program.
255 */
256 if( self->dsi.vobu_sri.next_vobu != SRI_END_OF_CELL ) {
257 self->next_vobu = self->dsi.dsi_gi.nv_pck_lbn
258 + ( self->dsi.vobu_sri.next_vobu & 0x7fffffff );
259 } else {
260 self->next_vobu = self->vobu_start + self->vobu_length;
261 }
262
263 dvdnav_get_angle_info(self, &current, &num);
264 if(num == 1) {
265 /* This is to switch back to angle one when we
266 * finish */
267 dvdnav_angle_change(self, 1);
268 }
269
270 if(num != 0) {
271 uint32_t next = self->pci.nsml_agli.nsml_agl_dsta[current-1];
272
273 if(next != 0) {
274 int dir = 0;
275 if(next & 0x80000000) {
276 dir = -1;
277 next = next & 0x3fffffff;
278 } else {
279 dir = 1;
280 }
281
282 if(next != 0) {
283 self->next_vobu = self->vobu_start + dir * next;
284 }
285 } else if( self->dsi.sml_agli.data[current-1].address != 0 ) {
286 next = self->dsi.sml_agli.data[current-1].address;
287 self->vobu_length = self->dsi.sml_pbi.ilvu_ea;
288
289 if((next & 0x80000000) && (next != 0x7fffffff)) {
290 self->next_vobu = self->dsi.dsi_gi.nv_pck_lbn - (next & 0x7fffffff);
291 } else {
292 self->next_vobu = self->dsi.dsi_gi.nv_pck_lbn + next;
293 }
294 }
295 }
296 }
297 return 1;
298 }
299
300 return 0;
301 }
302
303 dvdnav_status_t dvdnav_get_next_block(dvdnav_t *self, unsigned char *buf,
304 int *event, int *len) {
305 dvd_state_t *state;
306 int result;
307 if(!self || !event || !len || !buf) {
308 printerr("Passed a NULL pointer");
309 return S_ERR;
310 }
311 pthread_mutex_lock(&self->vm_lock);
312
313 if(!self->started) {
314 /* Start the VM */
315 vm_start(self->vm);
316 self->started = 1;
317 }
318
319 state = &(self->vm->state);
320 (*event) = DVDNAV_NOP;
321 (*len) = 0;
322
323 /* Check the STOP flag */
324 if(self->stop) {
325 (*event) = DVDNAV_STOP;
326 pthread_mutex_unlock(&self->vm_lock);
327 return S_OK;
328 }
329
330 if(self->spu_clut_changed) {
331 (*event) = DVDNAV_SPU_CLUT_CHANGE;
332 printf("libdvdnav:SPU_CLUT_CHANGE\n");
333 (*len) = sizeof(dvdnav_still_event_t);
334 memcpy(buf, &(state->pgc->palette), 16 * sizeof(uint32_t));
335 self->spu_clut_changed = 0;
336 printf("libdvdnav:SPU_CLUT_CHANGE returning S_OK\n");
337 pthread_mutex_unlock(&self->vm_lock);
338 return S_OK;
339 }
340
341 if(self->spu_stream_changed) {
342 dvdnav_stream_change_event_t stream_change;
343 (*event) = DVDNAV_SPU_STREAM_CHANGE;
344 printf("libdvdnav:SPU_STREAM_CHANGE\n");
345 (*len) = sizeof(dvdnav_stream_change_event_t);
346 stream_change.physical= vm_get_subp_active_stream( self->vm );
347 memcpy(buf, &(stream_change), sizeof( dvdnav_stream_change_event_t));
348 self->spu_stream_changed = 0;
349 printf("libdvdnav:SPU_STREAM_CHANGE stream_id=%d returning S_OK\n",stream_change.physical);
350 pthread_mutex_unlock(&self->vm_lock);
351 return S_OK;
352 }
353
354 if(self->audio_stream_changed) {
355 dvdnav_stream_change_event_t stream_change;
356 (*event) = DVDNAV_AUDIO_STREAM_CHANGE;
357 printf("libdvdnav:AUDIO_STREAM_CHANGE\n");
358 (*len) = sizeof(dvdnav_stream_change_event_t);
359 stream_change.physical= vm_get_audio_active_stream( self->vm );
360 memcpy(buf, &(stream_change), sizeof( dvdnav_stream_change_event_t));
361 self->audio_stream_changed = 0;
362 printf("libdvdnav:AUDIO_STREAM_CHANGE stream_id=%d returning S_OK\n",stream_change.physical);
363 pthread_mutex_unlock(&self->vm_lock);
364 return S_OK;
365 }
366
367 /* Check the HIGHLIGHT flag */
368 if(self->highlight_changed) {
369 dvdnav_highlight_event_t hevent;
370
371 /* Fill in highlight struct with appropriate values */
372 if(self->hli_state != 0) {
373 hevent.display = 1;
374
375 /* Copy current button bounding box. */
376 hevent.sx = self->hli_bbox[0];
377 hevent.sy = self->hli_bbox[1];
378 hevent.ex = self->hli_bbox[2];
379 hevent.ey = self->hli_bbox[3];
380
381 hevent.palette = self->hli_clut;
382 hevent.pts = self->hli_pts;
383 hevent.buttonN = self->hli_buttonN;
384
385 } else {
386 hevent.display = 0;
387 }
388
389 (*event) = DVDNAV_HIGHLIGHT;
390 memcpy(buf, &(hevent), sizeof(hevent));
391 (*len) = sizeof(hevent);
392
393 self->highlight_changed = 0;
394
395 pthread_mutex_unlock(&self->vm_lock);
396 return S_OK;
397 }
398
399 /* Check to see if we need to change the curently opened VOB */
400 if((self->open_vtsN != state->vtsN) ||
401 (self->open_domain != state->domain)) {
402 dvd_read_domain_t domain;
403 int vtsN;
404 dvdnav_vts_change_event_t vts_event;
405
406 if(self->file) {
407 dvdnav_read_cache_clear(self);
408 DVDCloseFile(self->file);
409 self->file = NULL;
410 }
411
412 vts_event.old_vtsN = self->open_vtsN;
413 vts_event.old_domain = self->open_domain;
414
415 /* Use the current DOMAIN to find whether to open menu or title VOBs */
416 switch(state->domain) {
417 case FP_DOMAIN:
418 case VMGM_DOMAIN:
419 domain = DVD_READ_MENU_VOBS;
420 vtsN = 0;
421 break;
422 case VTSM_DOMAIN:
423 domain = DVD_READ_MENU_VOBS;
424 vtsN = state->vtsN;
425 break;
426 case VTS_DOMAIN:
427 domain = DVD_READ_TITLE_VOBS;
428 vtsN = state->vtsN;
429 break;
430 default:
431 printerr("Unknown domain when changing VTS.");
432 pthread_mutex_unlock(&self->vm_lock);
433 return S_ERR;
434 }
435
436 self->open_domain = state->domain;
437 self->open_vtsN = state->vtsN;
438 dvdnav_read_cache_clear(self);
439 self->file = DVDOpenFile(vm_get_dvd_reader(self->vm), vtsN, domain);
440 vts_event.new_vtsN = self->open_vtsN;
441 vts_event.new_domain = self->open_domain;
442
443 /* If couldn't open the file for some reason, moan */
444 if(self->file == NULL) {
445 printerrf("Error opening vtsN=%i, domain=%i.", vtsN, domain);
446 pthread_mutex_unlock(&self->vm_lock);
447 return S_ERR;
448 }
449
450 /* File opened successfully so return a VTS change event */
451 (*event) = DVDNAV_VTS_CHANGE;
452 memcpy(buf, &(vts_event), sizeof(vts_event));
453 (*len) = sizeof(vts_event);
454
455 /* On a VTS change, we want to disable any highlights which
456 * may have been shown (FIXME: is this valid?) */
457 self->highlight_changed = 1;
458 self->spu_clut_changed = 1;
459 self->spu_stream_changed = 1;
460 self->audio_stream_changed = 1;
461 self->hli_state = 0; /* Hide */
462 self->expecting_nav_packet = 1;
463
464 pthread_mutex_unlock(&self->vm_lock);
465 return S_OK;
466 }
467
468 /* Check the STILLFRAME flag */
469 if(self->still_frame != -1) {
470 dvdnav_still_event_t still_event;
471
472 still_event.length = self->still_frame;
473
474 (*event) = DVDNAV_STILL_FRAME;
475 (*len) = sizeof(dvdnav_still_event_t);
476 memcpy(buf, &(still_event), sizeof(dvdnav_still_event_t));
477
478 pthread_mutex_unlock(&self->vm_lock);
479 return S_OK;
480 }
481
482 if(self->at_soc) {
483 dvdnav_cell_change_event_t cell_event;
484 cell_playback_t *cell = &(state->pgc->cell_playback[state->cellN - 1]);
485
486 cell_event.old_cell = self->cell;
487 self->vobu_start = cell->first_sector;
488 self->cell = cell;
489 cell_event.new_cell = self->cell;
490
491 self->at_soc = 0;
492
493 (*event) = DVDNAV_CELL_CHANGE;
494 (*len) = sizeof(dvdnav_cell_change_event_t);
495 memcpy(buf, &(cell_event), sizeof(dvdnav_cell_change_event_t));
496
497 pthread_mutex_unlock(&self->vm_lock);
498 return S_OK;
499 }
500
501 if(self->expecting_nav_packet) {
502 dvdnav_nav_packet_event_t nav_event;
503
504 /* Perform the jump if necessary (this is always a
505 * VOBU boundary). */
506
507 if(self->seeking) {
508 /* FIXME:Need to handle seeking outside current cell. */
509 vobu_admap_t *admap = NULL;
510
511 printf("Seeking to target %u ...\n",
512 self->seekto_block);
513
514 /* Search through the VOBU_ADMAP for the nearest VOBU
515 * to the target block */
516 switch(state->domain) {
517 case FP_DOMAIN:
518 case VMGM_DOMAIN:
519 //ifo = vm_get_vmgi();
520 //ifoRead_VOBU_ADMAP(ifo);
521 admap = self->vm->vmgi->menu_vobu_admap;
522 break;
523 case VTSM_DOMAIN:
524 //ifo = vm_get_vtsi();
525 //ifoRead_VOBU_ADMAP(ifo);
526 admap = self->vm->vtsi->menu_vobu_admap;
527 break;
528 case VTS_DOMAIN:
529 //ifo = vm_get_vtsi();
530 //ifoRead_TITLE_VOBU_ADMAP(ifo);
531 admap = self->vm->vtsi->vts_vobu_admap;
532 break;
533 default:
534 printf("Error: Unknown domain for seeking seek.\n");
535 }
536
537 if(admap) {
538 uint32_t address = 0;
539 uint32_t vobu_start, next_vobu;
540 int found = 0;
541
542 /* Search through ADMAP for best sector */
543 vobu_start = 0x3fffffff;
544
545 while((!found) && ((address<<2) < admap->last_byte)) {
546 next_vobu = admap->vobu_start_sectors[address];
547
548 /* printf("Found block %u\n", next_vobu); */
549
550 if(vobu_start <= self->seekto_block &&
551 next_vobu > self->seekto_block) {
552 found = 1;
553 } else {
554 vobu_start = next_vobu;
555 }
556
557 address ++;
558 }
559 if(found) {
560 self->vobu_start = vobu_start;
561 self->blockN = 0;
562 self->seeking = 0;
563 //self->at_soc = 1;
564 (*event) = DVDNAV_SEEK_DONE;
565 (*len) = 0;
566 pthread_mutex_unlock(&self->vm_lock);
567 return S_OK;
568 } else {
569 printf("Could not locate block\n");
570 return -1;
571 }
572 }
573 }
574 if(self->jumping) {
575 printf("doing jumping\n");
576 self->vobu_start = self->jmp_vobu_start;
577 self->blockN = self->jmp_blockN;
578 self->jumping = 0;
579 self->at_soc = 1;
580 }
581
582 result = DVDReadBlocks(self->file, self->vobu_start + self->blockN, 1, buf);
583
584 if(result <= 0) {
585 printerr("Error reading NAV packet.");
586 pthread_mutex_unlock(&self->vm_lock);
587 return S_ERR;
588 }
589
590 if(dvdnav_check_packet(self, buf) == 0) {
591 printerr("Expected NAV packet but none found.");
592 pthread_mutex_unlock(&self->vm_lock);
593 return S_ERR;
594 }
595
596 self->blockN++;
597 self->expecting_nav_packet = 0;
598
599 dvdnav_pre_cache_blocks(self, self->vobu_start, self->vobu_length+1);
600
601 /* Successfully got a NAV packet */
602 nav_event.pci = &(self->pci);
603 nav_event.dsi = &(self->dsi);
604
605 (*event) = DVDNAV_NAV_PACKET;
606 //memcpy(buf, &(nav_event), sizeof(dvdnav_nav_packet_event_t));
607 //(*len) = sizeof(dvdnav_nav_packet_event_t);
608 (*len) = 2048;
609 pthread_mutex_unlock(&self->vm_lock);
610 return S_OK;
611 }
612
613 /* If we've got here, it must just be a normal block. */
614 if(!self->file) {
615 printerr("Attempting to read without opening file");
616 pthread_mutex_unlock(&self->vm_lock);
617 return S_ERR;
618 }
619
620 result = dvdnav_read_cache_block(self, self->vobu_start + self->blockN, 1, buf);
621 if(result <= 0) {
622 printerr("Error reading from DVD.");
623 pthread_mutex_unlock(&self->vm_lock);
624 return S_ERR;
625 }
626 self->blockN++;
627 (*len) = 2048;
628 (*event) = DVDNAV_BLOCK_OK;
629
630 if(self->blockN > self->vobu_length) {
631 self->vobu_start = self->next_vobu;
632 self->blockN = 0;
633 self->expecting_nav_packet = 1;
634
635 if(self->dsi.vobu_sri.next_vobu == SRI_END_OF_CELL) {
636 cell_playback_t *cell = &(state->pgc->cell_playback[state->cellN - 1]);
637
638 if(cell->still_time != 0xff) {
639 vm_get_next_cell(self->vm);
640 }
641
642 if(cell->still_time != 0) {
643 self->still_frame = cell->still_time;
644 }
645
646 self->at_soc = 1;
647 }
648 }
649
650 pthread_mutex_unlock(&self->vm_lock);
651 return S_OK;
652 }
653
654 uint16_t dvdnav_audio_stream_to_lang(dvdnav_t *self, uint8_t stream) {
655 ifo_handle_t *vtsi;
656 dvd_state_t *state;
657
658 if(!self)
659 return -1;
660
661 pthread_mutex_lock(&self->vm_lock);
662
663 vtsi = self->vm->vtsi;
664 state = &(self->vm->state);
665
666 if((vtsi == NULL) || (state == NULL) || (state->domain != VTS_DOMAIN))
667 goto __failed;
668
669 if(stream >= vtsi->vtsi_mat->nr_of_vts_audio_streams)
670 goto __failed;
671
672 if(vtsi->vtsi_mat->vts_audio_attr[stream].lang_type != 1)
673 goto __failed;
674
675 pthread_mutex_unlock(&self->vm_lock);
676 return vtsi->vtsi_mat->vts_audio_attr[stream].lang_code;
677
678 __failed:
679 pthread_mutex_unlock(&self->vm_lock);
680 return 0xffff;
681 }
682
683 int8_t dvdnav_audio_logical_to_physical(dvdnav_t *self, uint8_t logical) {
684 audio_status_t *audio_status;
685 dvd_state_t *state;
686 int i = 0;
687
688 if(!self)
689 return -1;
690
691 pthread_mutex_lock(&self->vm_lock);
692
693 state = &(self->vm->state);
694
695 if((!state) || (!state->pgc) || (!state->pgc->audio_control))
696 goto __failed;
697
698 if(logical > 7) {
699 fprintf(stderr, "Invalid logical audio channel: %i\n", logical);
700 goto __failed;
701 }
702
703 while (i < 8) {
704 audio_status = (audio_status_t*) &(state->pgc->audio_control[i]);
705
706 if(!audio_status)
707 goto __failed;
708
709 if(audio_status->available)
710 break;
711
712 i++;
713 }
714
715 if (i > 7)
716 goto __failed;
717
718 if ((logical+i) > 7)
719 goto __failed;
720
721 audio_status = (audio_status_t*) &(state->pgc->audio_control[logical+i]);
722
723 if(!audio_status)
724 goto __failed;
725
726 if(audio_status->available) {
727 /* Stream is available */
728 pthread_mutex_unlock(&self->vm_lock);
729 return audio_status->stream_number;
730 }
731
732 __failed:
733 pthread_mutex_unlock(&self->vm_lock);
734 return -1;
735 }
736
737 int8_t dvdnav_audio_physical_to_logical(dvdnav_t *self, uint8_t physical) {
738 int8_t logical = -1;
739 int i = 0;
740
741 if((!self) || (physical > 7))
742 return -1;
743
744 do {
745 if(dvdnav_audio_logical_to_physical(self, i) == physical)
746 logical = i;
747
748 i++;
749 } while((i<8) && (logical == -1));
750
751 return logical;
752 }
753
754 uint16_t dvdnav_spu_stream_to_lang(dvdnav_t *self, uint8_t stream) {
755 ifo_handle_t *vtsi;
756 dvd_state_t *state;
757
758 if(!self)
759 return -1;
760
761 pthread_mutex_lock(&self->vm_lock);
762
763 vtsi = self->vm->vtsi;
764 state = &(self->vm->state);
765
766 if((vtsi == NULL) || (state == NULL) || (state->domain != VTS_DOMAIN)) {
767 goto __failed;
768 }
769
770 if(stream >= vtsi->vtsi_mat->nr_of_vts_subp_streams) {
771 goto __failed;
772 }
773
774 if(vtsi->vtsi_mat->vts_subp_attr[stream].type != 1) {
775 goto __failed;
776 }
777
778 pthread_mutex_unlock(&self->vm_lock);
779 return vtsi->vtsi_mat->vts_subp_attr[stream].lang_code;
780
781 __failed:
782 pthread_mutex_unlock(&self->vm_lock);
783 return 0xffff;
784 }
785
786 int8_t dvdnav_spu_logical_to_physical(dvdnav_t *self, uint8_t logical) {
787 spu_status_t *spu_status;
788 dvd_state_t *state;
789 ifo_handle_t *vtsi;
790
791 if(!self)
792 return -1;
793
794 pthread_mutex_lock(&self->vm_lock);
795
796 vtsi = self->vm->vtsi;
797 state = &(self->vm->state);
798
799 if((!state) || (!vtsi))
800 goto __failed;
801
802 if(logical > 31) {
803 fprintf(stderr, "Invalid logical spu channel: %i\n", logical);
804 goto __failed;
805 }
806
807 spu_status = (spu_status_t*) &(state->pgc->subp_control[logical]);
808
809 if(!spu_status)
810 goto __failed;
811
812 if(spu_status->available) {
813 int8_t logical = -1;
814 video_attr_t *attr;
815
816 attr = &(vtsi->vtsi_mat->vtsm_video_attr);
817
818 if(!attr)
819 goto __failed;
820
821 /* Stream is available */
822 switch(attr->display_aspect_ratio) {
823 case 0: /* 4:3 */
824 logical = spu_status->stream_number_4_3;
825 break;
826 case 3: /* 16:9 */
827 logical = spu_status->stream_number_letterbox;
828 break;
829 }
830 pthread_mutex_unlock(&self->vm_lock);
831 return logical;
832 }
833
834 __failed:
835 pthread_mutex_unlock(&self->vm_lock);
836 return -1;
837 }
838
839 int8_t dvdnav_spu_physical_to_logical(dvdnav_t *self, uint8_t physical) {
840 int8_t logical = -1;
841 int i = 0;
842
843 if(physical > 31)
844 return -1;
845
846 do {
847 if(dvdnav_spu_logical_to_physical(self, i) == physical)
848 logical = i;
849
850 i++;
851 } while((i<32) && (logical == -1));
852
853 return logical;
854 }
855
856 /* Current domain (backend to dvdnav_is_domain_() funcs) */
857 static int8_t _dvdnav_is_domain(dvdnav_t *self, domain_t domain) {
858 dvd_state_t *state;
859
860 if((!self) || (!self->started) || (!self->vm))
861 return -1;
862
863 pthread_mutex_lock(&self->vm_lock);
864 state = &(self->vm->state);
865 pthread_mutex_unlock(&self->vm_lock);
866
867 if(!state)
868 return -1;
869
870 return (state->domain == domain) ? 1 : 0;
871 }
872
873 /* First Play domain. (Menu) */
874 int8_t dvdnav_is_domain_fp(dvdnav_t *self) {
875 return _dvdnav_is_domain(self, FP_DOMAIN);
876 }
877 /* Video management Menu domain. (Menu) */
878 int8_t dvdnav_is_domain_vmgm(dvdnav_t *self) {
879 return _dvdnav_is_domain(self, VMGM_DOMAIN);
880 }
881 /* Video Title Menu domain (Menu) */
882 int8_t dvdnav_is_domain_vtsm(dvdnav_t *self) {
883 return _dvdnav_is_domain(self, VTSM_DOMAIN);
884 }
885 /* Video Title domain (playing movie). */
886 int8_t dvdnav_is_domain_vts(dvdnav_t *self) {
887 return _dvdnav_is_domain(self, VTS_DOMAIN);
888 }
889
890 /* Generally delegate angle information handling to
891 * VM */
892 dvdnav_status_t dvdnav_angle_change(dvdnav_t *self, int angle) {
893 int num, current;
894
895 if(!self) {
896 return S_ERR;
897 }
898
899 if(dvdnav_get_angle_info(self, &current, &num) != S_OK) {
900 printerr("Error getting angle info");
901 return S_ERR;
902 }
903
904 /* Set angle SPRM if valid */
905 if((angle > 0) && (angle <= num)) {
906 self->vm->state.AGL_REG = angle;
907 } else {
908 printerr("Passed an invalid angle number");
909 return S_ERR;
910 }
911
912 return S_OK;
913 }
914
915 dvdnav_status_t dvdnav_get_angle_info(dvdnav_t *self, int* current_angle,
916 int *number_of_angles) {
917 if(!self || !self->vm) {
918 return S_ERR;
919 }
920
921 if(!current_angle || !number_of_angles) {
922 printerr("Passed a NULL pointer");
923 return S_ERR;
924 }
925
926 vm_get_angle_info(self->vm, number_of_angles, current_angle);
927
928 return S_OK;
929 }
930
931
932 /*
933 * $Log$
934 * Revision 1.1 2002/03/12 19:45:57 richwareham
935 * Initial revision
936 *
937 * Revision 1.28 2002/02/02 23:26:20 richwareham
938 * Restored title selection
939 *
940 * Revision 1.27 2002/02/01 15:48:10 richwareham
941 * Re-implemented angle selection and title/chapter display
942 *
943 * Revision 1.26 2002/01/31 16:53:49 richwareham
944 * Big patch from Daniel Caujolle-Bert to (re)implement SPU/Audio language display
945 *
946 * Revision 1.25 2002/01/24 20:53:50 richwareham
947 * Added option to _not_ use DVD read-ahead to options
948 *
949 * Revision 1.24 2002/01/20 15:54:59 jcdutton
950 * Implement seeking.
951 * It is still a bit buggy, but works sometimes.
952 * I need to find out how to make the jump clean.
953 * At the moment, some corruption of the mpeg2 stream occurs,
954 * which causes libmpeg2 to crash.
955 *
956 * Revision 1.23 2002/01/18 00:23:52 jcdutton
957 * Support Ejecting of DVD.
958 * It will first un-mount the DVD, then eject it.
959 *
960 * Revision 1.22 2002/01/17 14:50:32 jcdutton
961 * Fix corruption of stream during menu transitions.
962 * Menu transitions are now clean.
963 *
964 * Revision 1.21 2002/01/15 00:37:03 jcdutton
965 * Just a few cleanups, and a assert fix. (memset fixed it)
966 *
967 * Revision 1.20 2002/01/13 22:17:57 jcdutton
968 * Change logging.
969 *
970 *
971 */