sync mga_vid.h to revision 265 from the mga_vid repo
[mplayer/glamo.git] / stream / stream_dvdnav.c
blob633ff65b454c5cf8b921d8f233cb7ebdf6c8b410
1 #include "config.h"
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <unistd.h>
6 #include <string.h>
7 #include <errno.h>
8 #include "mp_msg.h"
9 #include "osdep/timer.h"
10 #include "input/input.h"
11 #include "stream.h"
12 #include "libmpdemux/demuxer.h"
13 #include <dvdnav/dvdnav.h>
14 #include "stream_dvdnav.h"
15 #include "libvo/video_out.h"
16 #include "libavutil/common.h"
17 #include "spudec.h"
18 #include "m_option.h"
19 #include "m_struct.h"
20 #include "help_mp.h"
21 #include "stream_dvd_common.h"
23 /* state flags */
24 typedef enum {
25 NAV_FLAG_EOF = 1 << 0, /* end of stream has been reached */
26 NAV_FLAG_WAIT = 1 << 1, /* wait event */
27 NAV_FLAG_WAIT_SKIP = 1 << 2, /* wait skip disable */
28 NAV_FLAG_CELL_CHANGED = 1 << 3, /* cell change event */
29 NAV_FLAG_WAIT_READ_AUTO = 1 << 4, /* wait read auto mode */
30 NAV_FLAG_WAIT_READ = 1 << 5, /* suspend read from stream */
31 NAV_FLAG_VTS_DOMAIN = 1 << 6, /* vts domain */
32 NAV_FLAG_SPU_SET = 1 << 7, /* spu_clut is valid */
33 NAV_FLAG_STREAM_CHANGE = 1 << 8, /* title, chapter, audio or SPU */
34 } dvdnav_state_t;
36 typedef struct {
37 dvdnav_t * dvdnav; /* handle to libdvdnav stuff */
38 char * filename; /* path */
39 unsigned int duration; /* in milliseconds */
40 int mousex, mousey;
41 int title;
42 unsigned int spu_clut[16];
43 dvdnav_highlight_event_t hlev;
44 int still_length; /* still frame duration */
45 unsigned int state;
46 } dvdnav_priv_t;
48 extern char *dvd_device;
49 extern char *audio_lang, *dvdsub_lang;
50 extern char *dvd_audio_stream_channels[6], *dvd_audio_stream_types[8];
52 static struct stream_priv_s {
53 int track;
54 char* device;
55 } stream_priv_dflts = {
57 NULL
60 #define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f)
61 /// URL definition
62 static const m_option_t stream_opts_fields[] = {
63 {"filename", ST_OFF(device), CONF_TYPE_STRING, 0, 0, 0, NULL },
64 {"hostname", ST_OFF(track), CONF_TYPE_INT, 0, 0, 0, NULL},
65 { NULL, NULL, 0, 0, 0, 0, NULL }
67 static const struct m_struct_st stream_opts = {
68 "dvd",
69 sizeof(struct stream_priv_s),
70 &stream_priv_dflts,
71 stream_opts_fields
74 static int seek(stream_t *s, off_t newpos);
76 static dvdnav_priv_t * new_dvdnav_stream(char * filename) {
77 const char * title_str;
78 dvdnav_priv_t *priv;
80 if (!filename)
81 return NULL;
83 if (!(priv=calloc(1,sizeof(dvdnav_priv_t))))
84 return NULL;
86 if (!(priv->filename=strdup(filename))) {
87 free(priv);
88 return NULL;
91 if(dvdnav_open(&(priv->dvdnav),priv->filename)!=DVDNAV_STATUS_OK)
93 free(priv->filename);
94 free(priv);
95 return NULL;
98 if (!priv->dvdnav) {
99 free(priv);
100 return NULL;
103 if(1) //from vlc: if not used dvdnav from cvs will fail
105 int len, event;
106 char buf[2048];
108 dvdnav_get_next_block(priv->dvdnav,buf,&event,&len);
109 dvdnav_sector_search(priv->dvdnav, 0, SEEK_SET);
112 /* turn off dvdnav caching */
113 dvdnav_set_readahead_flag(priv->dvdnav, 0);
114 if(dvdnav_set_PGC_positioning_flag(priv->dvdnav, 1) != DVDNAV_STATUS_OK)
115 mp_msg(MSGT_OPEN,MSGL_ERR,"stream_dvdnav, failed to set PGC positioning\n");
116 #if 1
117 /* report the title?! */
118 if (dvdnav_get_title_string(priv->dvdnav,&title_str)==DVDNAV_STATUS_OK) {
119 mp_msg(MSGT_IDENTIFY, MSGL_INFO,"Title: '%s'\n",title_str);
121 #endif
123 //dvdnav_event_clear(priv);
125 return priv;
128 static void dvdnav_get_highlight (dvdnav_priv_t *priv, int display_mode) {
129 pci_t *pnavpci = NULL;
130 dvdnav_highlight_event_t *hlev = &(priv->hlev);
131 int btnum;
133 if (!priv || !priv->dvdnav)
134 return;
136 pnavpci = dvdnav_get_current_nav_pci (priv->dvdnav);
137 if (!pnavpci)
138 return;
140 dvdnav_get_current_highlight (priv->dvdnav, &(hlev->buttonN));
141 hlev->display = display_mode; /* show */
143 if (hlev->buttonN > 0 && pnavpci->hli.hl_gi.btn_ns > 0 && hlev->display) {
144 for (btnum = 0; btnum < pnavpci->hli.hl_gi.btn_ns; btnum++) {
145 btni_t *btni = &(pnavpci->hli.btnit[btnum]);
147 if (hlev->buttonN == btnum + 1) {
148 hlev->sx = FFMIN (btni->x_start, btni->x_end);
149 hlev->ex = FFMAX (btni->x_start, btni->x_end);
150 hlev->sy = FFMIN (btni->y_start, btni->y_end);
151 hlev->ey = FFMAX (btni->y_start, btni->y_end);
153 hlev->palette = (btni->btn_coln == 0) ? 0 :
154 pnavpci->hli.btn_colit.btn_coli[btni->btn_coln - 1][0];
155 break;
158 } else { /* hide button or no button */
159 hlev->sx = hlev->ex = 0;
160 hlev->sy = hlev->ey = 0;
161 hlev->palette = hlev->buttonN = 0;
165 static inline int dvdnav_get_duration (int length) {
166 return (length == 255) ? 0 : length * 1000;
169 static int dvdnav_stream_read(dvdnav_priv_t * priv, unsigned char *buf, int *len) {
170 int event = DVDNAV_NOP;
172 *len=-1;
173 if (dvdnav_get_next_block(priv->dvdnav,buf,&event,len)!=DVDNAV_STATUS_OK) {
174 mp_msg(MSGT_OPEN,MSGL_V, "Error getting next block from DVD %d (%s)\n",event, dvdnav_err_to_string(priv->dvdnav) );
175 *len=-1;
177 else if (event!=DVDNAV_BLOCK_OK) {
178 // need to handle certain events internally (like skipping stills)
179 switch (event) {
180 case DVDNAV_NAV_PACKET:
181 return event;
182 case DVDNAV_STILL_FRAME: {
183 dvdnav_still_event_t *still_event = (dvdnav_still_event_t *) buf;
184 priv->still_length = still_event->length;
185 /* set still frame duration */
186 priv->duration = dvdnav_get_duration (priv->still_length);
187 if (priv->still_length <= 1) {
188 pci_t *pnavpci = dvdnav_get_current_nav_pci (priv->dvdnav);
189 priv->duration = mp_dvdtimetomsec (&pnavpci->pci_gi.e_eltm);
191 break;
193 case DVDNAV_HIGHLIGHT: {
194 dvdnav_get_highlight (priv, 1);
195 break;
197 case DVDNAV_CELL_CHANGE: {
198 dvdnav_cell_change_event_t *ev = (dvdnav_cell_change_event_t*)buf;
199 uint32_t nextstill;
201 priv->state &= ~NAV_FLAG_WAIT_SKIP;
202 priv->state |= NAV_FLAG_STREAM_CHANGE;
203 if(ev->pgc_length)
204 priv->duration = ev->pgc_length/90;
206 if (dvdnav_is_domain_vts(priv->dvdnav))
207 priv->state &= ~NAV_FLAG_VTS_DOMAIN;
208 else
209 priv->state |= NAV_FLAG_VTS_DOMAIN;
211 nextstill = dvdnav_get_next_still_flag (priv->dvdnav);
212 if (nextstill) {
213 priv->duration = dvdnav_get_duration (nextstill);
214 priv->still_length = nextstill;
215 if (priv->still_length <= 1) {
216 pci_t *pnavpci = dvdnav_get_current_nav_pci (priv->dvdnav);
217 priv->duration = mp_dvdtimetomsec (&pnavpci->pci_gi.e_eltm);
221 break;
223 case DVDNAV_SPU_CLUT_CHANGE: {
224 memcpy(priv->spu_clut, buf, 16*sizeof(unsigned int));
225 priv->state |= NAV_FLAG_SPU_SET;
226 break;
228 case DVDNAV_WAIT: {
229 if ((priv->state & NAV_FLAG_WAIT_SKIP) &&
230 !(priv->state & NAV_FLAG_WAIT))
231 dvdnav_wait_skip (priv->dvdnav);
232 else
233 priv->state |= NAV_FLAG_WAIT;
234 break;
236 case DVDNAV_VTS_CHANGE: {
237 priv->state &= ~NAV_FLAG_WAIT_SKIP;
238 priv->state |= NAV_FLAG_STREAM_CHANGE;
239 break;
241 case DVDNAV_SPU_STREAM_CHANGE: {
242 priv->state |= NAV_FLAG_STREAM_CHANGE;
243 break;
247 *len=0;
249 return event;
252 static void update_title_len(stream_t *stream) {
253 dvdnav_priv_t *priv = stream->priv;
254 dvdnav_status_t status;
255 uint32_t pos = 0, len = 0;
257 status = dvdnav_get_position(priv->dvdnav, &pos, &len);
258 if(status == DVDNAV_STATUS_OK && len) {
259 stream->end_pos = (off_t) len * 2048;
260 stream->seek = seek;
261 } else {
262 stream->seek = NULL;
263 stream->end_pos = 0;
268 static int seek(stream_t *s, off_t newpos) {
269 uint32_t sector = 0;
270 dvdnav_priv_t *priv = s->priv;
272 if(s->end_pos && newpos > s->end_pos)
273 newpos = s->end_pos;
274 sector = newpos / 2048ULL;
275 if(dvdnav_sector_search(priv->dvdnav, (uint64_t) sector, SEEK_SET) != DVDNAV_STATUS_OK)
276 goto fail;
278 s->pos = newpos;
280 return 1;
282 fail:
283 mp_msg(MSGT_STREAM,MSGL_INFO,"dvdnav_stream, seeking to %"PRIu64" failed: %s\n", newpos, dvdnav_err_to_string(priv->dvdnav));
285 return 1;
288 static void stream_dvdnav_close(stream_t *s) {
289 dvdnav_priv_t *priv = s->priv;
290 dvdnav_close(priv->dvdnav);
291 priv->dvdnav = NULL;
292 free(priv);
296 static int fill_buffer(stream_t *s, char *but, int len)
298 int event;
300 dvdnav_priv_t* priv=s->priv;
301 if (priv->state & NAV_FLAG_WAIT_READ) /* read is suspended */
302 return -1;
303 len=0;
304 if(!s->end_pos)
305 update_title_len(s);
306 while(!len) /* grab all event until DVDNAV_BLOCK_OK (len=2048), DVDNAV_STOP or DVDNAV_STILL_FRAME */
308 event=dvdnav_stream_read(priv, s->buffer, &len);
309 if(event==-1 || len==-1)
311 mp_msg(MSGT_CPLAYER,MSGL_ERR, "DVDNAV stream read error!\n");
312 return 0;
314 if (event != DVDNAV_BLOCK_OK)
315 dvdnav_get_highlight (priv, 1);
316 switch (event) {
317 case DVDNAV_STOP: {
318 priv->state |= NAV_FLAG_EOF;
319 return len;
321 case DVDNAV_BLOCK_OK:
322 case DVDNAV_NAV_PACKET:
323 case DVDNAV_STILL_FRAME:
324 return len;
325 case DVDNAV_WAIT: {
326 if (priv->state & NAV_FLAG_WAIT)
327 return len;
328 break;
330 case DVDNAV_VTS_CHANGE: {
331 int tit = 0, part = 0;
332 dvdnav_vts_change_event_t *vts_event = (dvdnav_vts_change_event_t *)s->buffer;
333 mp_msg(MSGT_CPLAYER,MSGL_INFO, "DVDNAV, switched to title: %d\r\n", vts_event->new_vtsN);
334 priv->state |= NAV_FLAG_CELL_CHANGED;
335 priv->state &= ~NAV_FLAG_WAIT_SKIP;
336 priv->state &= ~NAV_FLAG_WAIT;
337 s->end_pos = 0;
338 update_title_len(s);
339 if (priv->state & NAV_FLAG_WAIT_READ_AUTO)
340 priv->state |= NAV_FLAG_WAIT_READ;
341 if(dvdnav_current_title_info(priv->dvdnav, &tit, &part) == DVDNAV_STATUS_OK) {
342 mp_msg(MSGT_CPLAYER,MSGL_V, "\r\nDVDNAV, NEW TITLE %d\r\n", tit);
343 dvdnav_get_highlight (priv, 0);
344 if(priv->title > 0 && tit != priv->title)
345 return 0;
347 break;
349 case DVDNAV_CELL_CHANGE: {
350 priv->state |= NAV_FLAG_CELL_CHANGED;
351 priv->state &= ~NAV_FLAG_WAIT_SKIP;
352 priv->state &= ~NAV_FLAG_WAIT;
353 if (priv->state & NAV_FLAG_WAIT_READ_AUTO)
354 priv->state |= NAV_FLAG_WAIT_READ;
355 if(priv->title > 0 && dvd_last_chapter > 0) {
356 int tit=0, part=0;
357 if(dvdnav_current_title_info(priv->dvdnav, &tit, &part) == DVDNAV_STATUS_OK && part > dvd_last_chapter)
358 return 0;
360 dvdnav_get_highlight (priv, 1);
362 break;
365 mp_msg(MSGT_STREAM,MSGL_DBG2,"DVDNAV fill_buffer len: %d\n",len);
366 return len;
369 static int control(stream_t *stream, int cmd, void* arg) {
370 dvdnav_priv_t* priv=stream->priv;
371 int tit, part;
373 switch(cmd)
375 case STREAM_CTRL_SEEK_TO_CHAPTER:
377 int chap = *((unsigned int *)arg)+1;
379 if(chap < 1 || dvdnav_current_title_info(priv->dvdnav, &tit, &part) != DVDNAV_STATUS_OK)
380 break;
381 if(dvdnav_part_play(priv->dvdnav, tit, chap) != DVDNAV_STATUS_OK)
382 break;
383 return 1;
385 case STREAM_CTRL_GET_NUM_CHAPTERS:
387 if(dvdnav_current_title_info(priv->dvdnav, &tit, &part) != DVDNAV_STATUS_OK)
388 break;
389 if(dvdnav_get_number_of_parts(priv->dvdnav, tit, &part) != DVDNAV_STATUS_OK)
390 break;
391 if(!part)
392 break;
393 *((unsigned int *)arg) = part;
394 return 1;
396 case STREAM_CTRL_GET_CURRENT_CHAPTER:
398 if(dvdnav_current_title_info(priv->dvdnav, &tit, &part) != DVDNAV_STATUS_OK)
399 break;
400 *((unsigned int *)arg) = part - 1;
401 return 1;
403 case STREAM_CTRL_GET_TIME_LENGTH:
405 if(priv->duration || priv->still_length)
407 *((double *)arg) = (double)priv->duration / 1000.0;
408 return 1;
410 break;
412 case STREAM_CTRL_GET_ASPECT_RATIO:
414 uint8_t ar = dvdnav_get_video_aspect(priv->dvdnav);
415 *((double *)arg) = !ar ? 4.0/3.0 : 16.0/9.0;
416 return 1;
418 case STREAM_CTRL_GET_CURRENT_TIME:
420 double tm;
421 tm = dvdnav_get_current_time(priv->dvdnav)/90000.0f;
422 if(tm != -1)
424 *((double *)arg) = tm;
425 return 1;
427 break;
429 case STREAM_CTRL_SEEK_TO_TIME:
431 uint64_t tm = (uint64_t) (*((double*)arg) * 90000);
432 if(dvdnav_time_search(priv->dvdnav, tm) == DVDNAV_STATUS_OK)
433 return 1;
434 break;
436 case STREAM_CTRL_GET_NUM_ANGLES:
438 uint32_t curr, angles;
439 if(dvdnav_get_angle_info(priv->dvdnav, &curr, &angles) != DVDNAV_STATUS_OK)
440 break;
441 *((int *)arg) = angles;
442 return 1;
444 case STREAM_CTRL_GET_ANGLE:
446 uint32_t curr, angles;
447 if(dvdnav_get_angle_info(priv->dvdnav, &curr, &angles) != DVDNAV_STATUS_OK)
448 break;
449 *((int *)arg) = curr;
450 return 1;
452 case STREAM_CTRL_SET_ANGLE:
454 uint32_t curr, angles;
455 int new_angle = *((int *)arg);
456 if(dvdnav_get_angle_info(priv->dvdnav, &curr, &angles) != DVDNAV_STATUS_OK)
457 break;
458 if(new_angle>angles || new_angle<1)
459 break;
460 if(dvdnav_angle_change(priv->dvdnav, new_angle) != DVDNAV_STATUS_OK)
461 return 1;
465 return STREAM_UNSUPPORTED;
468 static void identify_chapters(dvdnav_t *nav, uint32_t title)
470 uint64_t *parts=NULL, duration=0;
471 uint32_t n, i, t;
472 n = dvdnav_describe_title_chapters(nav, title, &parts, &duration);
473 if(parts) {
474 t = duration / 90;
475 mp_msg(MSGT_IDENTIFY, MSGL_V, "ID_DVD_TITLE_%d_LENGTH=%d.%03d\n", title, t / 1000, t % 1000);
476 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "TITLE %u, CHAPTERS: ", title);
477 for(i=0; i<n; i++) {
478 t = parts[i] / 90000;
479 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "%02d:%02d:%02d,", t/3600, (t/60)%60, t%60);
481 free(parts);
482 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "\n");
486 static void identify(dvdnav_priv_t *priv, struct stream_priv_s *p)
488 uint32_t titles=0, i;
489 if(p->track <= 0) {
490 dvdnav_get_number_of_titles(priv->dvdnav, &titles);
491 for(i=0; i<titles; i++)
492 identify_chapters(priv->dvdnav, i);
494 else
495 identify_chapters(priv->dvdnav, p->track);
498 static void show_audio_subs_languages(dvdnav_t *nav)
500 uint8_t lg;
501 uint16_t i, lang, format, id, channels;
502 int base[7] = {128, 0, 0, 0, 160, 136, 0};
503 char tmp[3];
504 for(i=0; i<8; i++)
506 lg = dvdnav_get_audio_logical_stream(nav, i);
507 if(lg == 0xff) continue;
508 channels = dvdnav_audio_stream_channels(nav, lg);
509 if(channels == 0xFFFF)
510 channels = 2; //unknown
511 else
512 channels--;
513 lang = dvdnav_audio_stream_to_lang(nav, lg);
514 if(lang == 0xFFFF)
515 tmp[0] = tmp[1] = '?';
516 else
518 tmp[0] = lang >> 8;
519 tmp[1] = lang & 0xFF;
521 tmp[2] = 0;
522 format = dvdnav_audio_stream_format(nav, lg);
523 if(format == 0xFFFF || format > 6)
524 format = 1; //unknown
525 id = i + base[format];
526 mp_msg(MSGT_OPEN,MSGL_STATUS,MSGTR_DVDaudioStreamInfo, i,
527 dvd_audio_stream_types[format], dvd_audio_stream_channels[channels], tmp, id);
530 for(i=0; i<32; i++)
532 lg = dvdnav_get_spu_logical_stream(nav, i);
533 if(lg == 0xff) continue;
534 lang = dvdnav_spu_stream_to_lang(nav, lg);
535 if(lang == 0xFFFF)
536 tmp[0] = tmp[1] = '?';
537 else
539 tmp[0] = lang >> 8;
540 tmp[1] = lang & 0xFF;
542 tmp[2] = 0;
543 mp_msg(MSGT_OPEN,MSGL_STATUS,MSGTR_DVDsubtitleLanguage, i+0x20, tmp);
547 static int open_s(stream_t *stream,int mode, void* opts, int* file_format) {
548 struct stream_priv_s* p = (struct stream_priv_s*)opts;
549 char *filename;
550 dvdnav_priv_t *priv;
552 if(p->device) filename = p->device;
553 else if(dvd_device) filename= dvd_device;
554 else filename = DEFAULT_DVD_DEVICE;
555 if(!(priv=new_dvdnav_stream(filename))) {
556 mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_CantOpenDVD,filename, strerror(errno));
557 return STREAM_UNSUPPORTED;
560 if(p->track > 0) {
561 priv->title = p->track;
562 if(dvdnav_title_play(priv->dvdnav, p->track) != DVDNAV_STATUS_OK) {
563 mp_msg(MSGT_OPEN,MSGL_FATAL,"dvdnav_stream, couldn't select title %d, error '%s'\n", p->track, dvdnav_err_to_string(priv->dvdnav));
564 return STREAM_UNSUPPORTED;
566 } else if (p->track == 0) {
567 if(dvdnav_menu_call(priv->dvdnav, DVD_MENU_Root) != DVDNAV_STATUS_OK)
568 dvdnav_menu_call(priv->dvdnav, DVD_MENU_Title);
570 if(mp_msg_test(MSGT_IDENTIFY, MSGL_INFO))
571 identify(priv, p);
572 if(p->track > 0)
573 show_audio_subs_languages(priv->dvdnav);
574 if(dvd_angle > 1)
575 dvdnav_angle_change(priv->dvdnav, dvd_angle);
577 stream->sector_size = 2048;
578 stream->flags = STREAM_READ | STREAM_SEEK;
579 stream->fill_buffer = fill_buffer;
580 stream->seek = seek;
581 stream->control = control;
582 stream->close = stream_dvdnav_close;
583 stream->type = STREAMTYPE_DVDNAV;
584 stream->priv=(void*)priv;
585 *file_format = DEMUXER_TYPE_MPEG_PS;
587 update_title_len(stream);
588 if(!stream->pos && p->track > 0)
589 mp_msg(MSGT_OPEN,MSGL_ERR, "INIT ERROR: couldn't get init pos %s\r\n", dvdnav_err_to_string(priv->dvdnav));
591 mp_msg(MSGT_OPEN,MSGL_INFO, "Remember to disable MPlayer's cache when playing dvdnav:// streams (adding -nocache to your command line)\r\n");
593 return STREAM_OK;
597 void mp_dvdnav_handle_input(stream_t *stream, int cmd, int *button) {
598 dvdnav_priv_t * priv = stream->priv;
599 dvdnav_t *nav = priv->dvdnav;
600 dvdnav_status_t status=DVDNAV_STATUS_ERR;
601 pci_t *pci = dvdnav_get_current_nav_pci(nav);
603 if(cmd != MP_CMD_DVDNAV_SELECT && !pci)
604 return;
606 switch(cmd) {
607 case MP_CMD_DVDNAV_UP:
608 status = dvdnav_upper_button_select(nav, pci);
609 break;
610 case MP_CMD_DVDNAV_DOWN:
611 status = dvdnav_lower_button_select(nav, pci);
612 break;
613 case MP_CMD_DVDNAV_LEFT:
614 status = dvdnav_left_button_select(nav, pci);
615 break;
616 case MP_CMD_DVDNAV_RIGHT:
617 status = dvdnav_right_button_select(nav, pci);
618 break;
619 case MP_CMD_DVDNAV_MENU:
620 status = dvdnav_menu_call(nav,DVD_MENU_Root);
621 break;
622 case MP_CMD_DVDNAV_PREVMENU: {
623 int title=0, part=0;
625 dvdnav_current_title_info(nav, &title, &part);
626 if(title) {
627 if((status=dvdnav_menu_call(nav, DVD_MENU_Part)) == DVDNAV_STATUS_OK)
628 break;
630 if((status=dvdnav_menu_call(nav, DVD_MENU_Title)) == DVDNAV_STATUS_OK)
631 break;
632 status=dvdnav_menu_call(nav, DVD_MENU_Root);
634 break;
635 case MP_CMD_DVDNAV_SELECT:
636 status = dvdnav_button_activate(nav, pci);
637 break;
638 case MP_CMD_DVDNAV_MOUSECLICK:
640 this is a workaround: in theory the simple dvdnav_lower_button_select()+dvdnav_button_activate()
641 should be enough (and generally it is), but there are cases when the calls to dvdnav_lower_button_select()
642 and friends fail! Hence we have to call dvdnav_mouse_activate(priv->mousex, priv->mousey) with
643 the coodinates saved by mp_dvdnav_update_mouse_pos().
644 This last call always works well
646 status = dvdnav_mouse_activate(nav, pci, priv->mousex, priv->mousey);
647 break;
648 default:
649 mp_msg(MSGT_CPLAYER, MSGL_V, "Unknown DVDNAV cmd %d\n", cmd);
650 break;
653 if(status == DVDNAV_STATUS_OK)
654 dvdnav_get_current_highlight(nav, button);
657 void mp_dvdnav_update_mouse_pos(stream_t *stream, int32_t x, int32_t y, int* button) {
658 dvdnav_priv_t * priv = stream->priv;
659 dvdnav_t *nav = priv->dvdnav;
660 dvdnav_status_t status;
661 pci_t *pci = dvdnav_get_current_nav_pci(nav);
663 if(!pci) return;
665 status = dvdnav_mouse_select(nav, pci, x, y);
666 if(status == DVDNAV_STATUS_OK) dvdnav_get_current_highlight(nav, button);
667 else *button = -1;
668 priv->mousex = x;
669 priv->mousey = y;
673 * \brief dvdnav_aid_from_lang() returns the audio id corresponding to the language code 'lang'
674 * \param stream: - stream pointer
675 * \param lang: 2-characters language code[s], eventually separated by spaces of commas
676 * \return -1 on error, current subtitle id if successful
678 int dvdnav_aid_from_lang(stream_t *stream, unsigned char *language) {
679 dvdnav_priv_t * priv = stream->priv;
680 int k;
681 uint8_t format, lg;
682 uint16_t lang, lcode;;
684 while(language && strlen(language)>=2) {
685 lcode = (language[0] << 8) | (language[1]);
686 for(k=0; k<32; k++) {
687 lg = dvdnav_get_audio_logical_stream(priv->dvdnav, k);
688 if(lg == 0xff) continue;
689 lang = dvdnav_audio_stream_to_lang(priv->dvdnav, lg);
690 if(lang != 0xFFFF && lang == lcode) {
691 format = dvdnav_audio_stream_format(priv->dvdnav, lg);
692 switch(format) {
693 case DVDNAV_FORMAT_AC3:
694 return k+128;
695 case DVDNAV_FORMAT_DTS:
696 return k+136;
697 case DVDNAV_FORMAT_LPCM:
698 return k+160;
699 case DVDNAV_FORMAT_MPEGAUDIO:
700 return k;
701 default:
702 return -1;
706 language += 2;
707 while(language[0]==',' || language[0]==' ') ++language;
709 return -1;
713 * \brief dvdnav_lang_from_aid() assigns to buf the language corresponding to audio id 'aid'
714 * \param stream: - stream pointer
715 * \param sid: physical subtitle id
716 * \param buf: buffer to contain the 2-chars language string
717 * \return 0 on error, 1 if successful
719 int dvdnav_lang_from_aid(stream_t *stream, int aid, unsigned char *buf) {
720 uint8_t lg;
721 uint16_t lang;
722 dvdnav_priv_t * priv = stream->priv;
724 if(aid < 0)
725 return 0;
726 lg = dvdnav_get_audio_logical_stream(priv->dvdnav, aid & 0x7);
727 if(lg == 0xff) return 0;
728 lang = dvdnav_audio_stream_to_lang(priv->dvdnav, lg);
729 if(lang == 0xffff) return 0;
730 buf[0] = lang >> 8;
731 buf[1] = lang & 0xFF;
732 buf[2] = 0;
733 return 1;
738 * \brief dvdnav_sid_from_lang() returns the subtitle id corresponding to the language code 'lang'
739 * \param stream: - stream pointer
740 * \param lang: 2-characters language code[s], eventually separated by spaces of commas
741 * \return -1 on error, current subtitle id if successful
743 int dvdnav_sid_from_lang(stream_t *stream, unsigned char *language) {
744 dvdnav_priv_t * priv = stream->priv;
745 uint8_t lg, k;
746 uint16_t lang, lcode;
748 while(language && strlen(language)>=2) {
749 lcode = (language[0] << 8) | (language[1]);
750 for(k=0; k<32; k++) {
751 lg = dvdnav_get_spu_logical_stream(priv->dvdnav, k);
752 if(lg == 0xff) continue;
753 lang = dvdnav_spu_stream_to_lang(priv->dvdnav, lg);
754 if(lang != 0xFFFF && lang == lcode) {
755 return k;
758 language += 2;
759 while(language[0]==',' || language[0]==' ') ++language;
761 return -1;
765 * \brief dvdnav_lang_from_sid() assigns to buf the language corresponding to subtitle id 'sid'
766 * \param stream: - stream pointer
767 * \param sid: physical subtitle id
768 * \param buf: buffer to contain the 2-chars language string
769 * \return 0 on error, 1 if successful
771 int dvdnav_lang_from_sid(stream_t *stream, int sid, unsigned char *buf) {
772 uint8_t lg;
773 uint16_t lang;
774 dvdnav_priv_t *priv = stream->priv;
775 if(sid < 0) return 0;
776 lg = dvdnav_get_spu_logical_stream(priv->dvdnav, sid);
777 lang = dvdnav_spu_stream_to_lang(priv->dvdnav, lg);
778 if(lang == 0xffff) return 0;
779 buf[0] = lang >> 8;
780 buf[1] = lang & 0xFF;
781 buf[2] = 0;
782 return 1;
786 * \brief dvdnav_number_of_subs() returns the count of available subtitles
787 * \param stream: - stream pointer
788 * \return 0 on error, something meaningful otherwise
790 int dvdnav_number_of_subs(stream_t *stream) {
791 dvdnav_priv_t * priv = stream->priv;
792 uint8_t lg, k, n=0;
794 if (priv->state & NAV_FLAG_VTS_DOMAIN) return 0;
795 for(k=0; k<32; k++) {
796 lg = dvdnav_get_spu_logical_stream(priv->dvdnav, k);
797 if(lg == 0xff) continue;
798 n++;
800 return n;
804 * \brief mp_dvdnav_get_spu_clut() returns the spu clut
805 * \param stream: - stream pointer
806 * \return spu clut pointer
808 unsigned int *mp_dvdnav_get_spu_clut(stream_t *stream) {
809 dvdnav_priv_t *priv = stream->priv;
810 return (priv->state & NAV_FLAG_SPU_SET) ? priv->spu_clut : NULL;
814 * \brief mp_dvdnav_get_highlight() get dvdnav highlight struct
815 * \param stream: - stream pointer
816 * \param hl : - highlight struct pointer
818 void mp_dvdnav_get_highlight (stream_t *stream, nav_highlight_t *hl) {
819 dvdnav_priv_t *priv = stream->priv;
820 dvdnav_highlight_event_t hlev = priv->hlev;
822 hl->sx = hlev.sx;
823 hl->sy = hlev.sy;
824 hl->ex = hlev.ex;
825 hl->ey = hlev.ey;
828 void mp_dvdnav_switch_title (stream_t *stream, int title) {
829 dvdnav_priv_t *priv = stream->priv;
830 uint32_t titles;
832 dvdnav_get_number_of_titles (priv->dvdnav, &titles);
833 if (title > 0 && title <= titles)
834 dvdnav_title_play (priv->dvdnav, title);
838 * \brief Check if end of stream has been reached
839 * \param stream: - stream pointer
840 * \return 1 on really eof
842 int mp_dvdnav_is_eof (stream_t *stream) {
843 return ((dvdnav_priv_t *) stream->priv)->state & NAV_FLAG_EOF;
847 * \brief Skip still frame
848 * \param stream: - stream pointer
849 * \return 0 on success
851 int mp_dvdnav_skip_still (stream_t *stream) {
852 dvdnav_priv_t *priv = stream->priv;
853 if (priv->still_length == 0xff)
854 return 1;
855 dvdnav_still_skip(priv->dvdnav);
856 return 0;
860 * \brief Skip wait event
861 * \param stream: - stream pointer
862 * \return 0 on success
864 int mp_dvdnav_skip_wait (stream_t *stream) {
865 dvdnav_priv_t *priv = stream->priv;
866 if (!(priv->state & NAV_FLAG_WAIT))
867 return 1;
868 priv->state &= ~NAV_FLAG_WAIT;
869 dvdnav_wait_skip(priv->dvdnav);
870 return 0;
874 * \brief Set wait mode
875 * \param stream : - stream pointer
876 * \param mode : - if true, then suspend block read
877 * \param automode: - if true, then VTS or cell change set wait mode
879 void mp_dvdnav_read_wait (stream_t *stream, int mode, int automode) {
880 dvdnav_priv_t *priv = stream->priv;
881 if (mode == 0)
882 priv->state &= ~NAV_FLAG_WAIT_READ;
883 if (mode > 0)
884 priv->state |= NAV_FLAG_WAIT_READ;
885 if (automode == 0)
886 priv->state &= ~NAV_FLAG_WAIT_READ_AUTO;
887 if (automode > 0)
888 priv->state |= NAV_FLAG_WAIT_READ_AUTO;
892 * \brief Check if cell has changed
893 * \param stream: - stream pointer
894 * \param clear : - if true, then clear cell change flag
895 * \return 1 if cell has changed
897 int mp_dvdnav_cell_has_changed (stream_t *stream, int clear) {
898 dvdnav_priv_t *priv = stream->priv;
899 if (!(priv->state & NAV_FLAG_CELL_CHANGED))
900 return 0;
901 if (clear) {
902 priv->state &= ~NAV_FLAG_CELL_CHANGED;
903 priv->state |= NAV_FLAG_STREAM_CHANGE;
905 return 1;
908 /* Notify if something has changed in stream
909 * Can be related to title, chapter, audio or SPU
911 int mp_dvdnav_stream_has_changed (stream_t *stream) {
912 dvdnav_priv_t *priv = stream->priv;
914 if (!(priv->state & NAV_FLAG_STREAM_CHANGE))
915 return 0;
917 priv->state &= ~NAV_FLAG_STREAM_CHANGE;
918 return 1;
921 const stream_info_t stream_info_dvdnav = {
922 "DVDNAV stream",
923 "null",
926 open_s,
927 { "dvdnav", NULL },
928 &stream_opts,
929 1 // Urls are an option string