comparison stream/stream_dvdnav.c @ 25824:eb338d06c638

Add support for dvdnav still frames playback. Based on various patches from Otvos Attila and MPlayer'ized by me. N.B. Always use -vc ffmpeg12 with dvdnav://
author ben
date Sat, 26 Jan 2008 11:51:34 +0000
parents b43eaaa94dd3
children 4c17a62383a4
comparison
equal deleted inserted replaced
25823:60fda6c0c7dc 25824:eb338d06c638
16 #include "libavutil/common.h" 16 #include "libavutil/common.h"
17 #include "spudec.h" 17 #include "spudec.h"
18 #include "m_option.h" 18 #include "m_option.h"
19 #include "m_struct.h" 19 #include "m_struct.h"
20 #include "help_mp.h" 20 #include "help_mp.h"
21 #include "stream_dvd_common.h"
22
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 } dvdnav_state_t;
21 33
22 typedef struct { 34 typedef struct {
23 dvdnav_t * dvdnav; /* handle to libdvdnav stuff */ 35 dvdnav_t * dvdnav; /* handle to libdvdnav stuff */
24 char * filename; /* path */ 36 char * filename; /* path */
25 unsigned int duration; /* in milliseconds */ 37 unsigned int duration; /* in milliseconds */
26 int mousex, mousey; 38 int mousex, mousey;
27 int title; 39 int title;
28 unsigned int spu_clut[16], spu_set; 40 unsigned int spu_clut[16], spu_set;
29 dvdnav_highlight_event_t hlev; 41 dvdnav_highlight_event_t hlev;
42 int still_length; /* still frame duration */
43 unsigned int state;
30 } dvdnav_priv_t; 44 } dvdnav_priv_t;
31 45
32 extern char *dvd_device; 46 extern char *dvd_device;
33 extern char *audio_lang, *dvdsub_lang; 47 extern char *audio_lang, *dvdsub_lang;
34 extern char *dvd_audio_stream_channels[6], *dvd_audio_stream_types[8]; 48 extern char *dvd_audio_stream_channels[6], *dvd_audio_stream_types[8];
144 hlev->sy = hlev->ey = 0; 158 hlev->sy = hlev->ey = 0;
145 hlev->palette = hlev->buttonN = 0; 159 hlev->palette = hlev->buttonN = 0;
146 } 160 }
147 } 161 }
148 162
163 static inline int dvdnav_get_duration (int length) {
164 return (length == 255) ? 0 : length * 1000;
165 }
166
149 static int dvdnav_stream_read(dvdnav_priv_t * priv, unsigned char *buf, int *len) { 167 static int dvdnav_stream_read(dvdnav_priv_t * priv, unsigned char *buf, int *len) {
150 int event = DVDNAV_NOP; 168 int event = DVDNAV_NOP;
151 169
152 *len=-1; 170 *len=-1;
153 if (dvdnav_get_next_block(priv->dvdnav,buf,&event,len)!=DVDNAV_STATUS_OK) { 171 if (dvdnav_get_next_block(priv->dvdnav,buf,&event,len)!=DVDNAV_STATUS_OK) {
158 // need to handle certain events internally (like skipping stills) 176 // need to handle certain events internally (like skipping stills)
159 switch (event) { 177 switch (event) {
160 case DVDNAV_NAV_PACKET: 178 case DVDNAV_NAV_PACKET:
161 return event; 179 return event;
162 case DVDNAV_STILL_FRAME: { 180 case DVDNAV_STILL_FRAME: {
163 dvdnav_still_skip(priv->dvdnav); // don't let dvdnav stall on this image 181 dvdnav_still_event_t *still_event = (dvdnav_still_event_t *) buf;
182 priv->still_length = still_event->length;
183 /* set still frame duration */
184 priv->duration = dvdnav_get_duration (priv->still_length);
185 if (priv->still_length <= 1) {
186 pci_t *pnavpci = dvdnav_get_current_nav_pci (priv->dvdnav);
187 priv->duration = mp_dvdtimetomsec (&pnavpci->pci_gi.e_eltm);
188 }
164 break; 189 break;
165 } 190 }
166 case DVDNAV_HIGHLIGHT: { 191 case DVDNAV_HIGHLIGHT: {
167 dvdnav_get_highlight (priv, 1); 192 dvdnav_get_highlight (priv, 1);
168 break; 193 break;
169 } 194 }
170 case DVDNAV_CELL_CHANGE: { 195 case DVDNAV_CELL_CHANGE: {
171 dvdnav_cell_change_event_t *ev = (dvdnav_cell_change_event_t*)buf; 196 dvdnav_cell_change_event_t *ev = (dvdnav_cell_change_event_t*)buf;
197 uint32_t nextstill;
198
199 priv->state &= ~NAV_FLAG_WAIT_SKIP;
172 if(ev->pgc_length) 200 if(ev->pgc_length)
173 priv->duration = ev->pgc_length/90; 201 priv->duration = ev->pgc_length/90;
202
203 if (dvdnav_is_domain_vts(priv->dvdnav))
204 priv->state &= ~NAV_FLAG_VTS_DOMAIN;
205 else
206 priv->state |= NAV_FLAG_VTS_DOMAIN;
207
208 nextstill = dvdnav_get_next_still_flag (priv->dvdnav);
209 if (nextstill) {
210 priv->duration = dvdnav_get_duration (nextstill);
211 priv->still_length = nextstill;
212 if (priv->still_length <= 1) {
213 pci_t *pnavpci = dvdnav_get_current_nav_pci (priv->dvdnav);
214 priv->duration = mp_dvdtimetomsec (&pnavpci->pci_gi.e_eltm);
215 }
216 }
217
174 break; 218 break;
175 } 219 }
176 case DVDNAV_SPU_CLUT_CHANGE: { 220 case DVDNAV_SPU_CLUT_CHANGE: {
177 memcpy(priv->spu_clut, buf, 16*sizeof(unsigned int)); 221 memcpy(priv->spu_clut, buf, 16*sizeof(unsigned int));
178 priv->spu_set = 1; 222 priv->spu_set = 1;
179 break; 223 break;
180 } 224 }
181 case DVDNAV_WAIT: 225 case DVDNAV_WAIT: {
182 dvdnav_wait_skip(priv->dvdnav); 226 if ((priv->state & NAV_FLAG_WAIT_SKIP) &&
183 break; 227 !(priv->state & NAV_FLAG_WAIT))
228 dvdnav_wait_skip (priv->dvdnav);
229 else
230 priv->state |= NAV_FLAG_WAIT;
231 break;
232 }
233 case DVDNAV_VTS_CHANGE: {
234 priv->state &= ~NAV_FLAG_WAIT_SKIP;
235 break;
236 }
184 } 237 }
185 238
186 *len=0; 239 *len=0;
187 } 240 }
188 return event; 241 return event;
235 static int fill_buffer(stream_t *s, char *but, int len) 288 static int fill_buffer(stream_t *s, char *but, int len)
236 { 289 {
237 int event; 290 int event;
238 291
239 dvdnav_priv_t* priv=s->priv; 292 dvdnav_priv_t* priv=s->priv;
293 if (priv->state & NAV_FLAG_WAIT_READ) /* read is suspended */
294 return -1;
240 len=0; 295 len=0;
241 if(!s->end_pos) 296 if(!s->end_pos)
242 update_title_len(s); 297 update_title_len(s);
243 while(!len) /* grab all event until DVDNAV_BLOCK_OK (len=2048), DVDNAV_STOP or DVDNAV_STILL_FRAME */ 298 while(!len) /* grab all event until DVDNAV_BLOCK_OK (len=2048), DVDNAV_STOP or DVDNAV_STILL_FRAME */
244 { 299 {
247 { 302 {
248 mp_msg(MSGT_CPLAYER,MSGL_ERR, "DVDNAV stream read error!\n"); 303 mp_msg(MSGT_CPLAYER,MSGL_ERR, "DVDNAV stream read error!\n");
249 return 0; 304 return 0;
250 } 305 }
251 switch (event) { 306 switch (event) {
252 case DVDNAV_STOP: 307 case DVDNAV_STOP: {
308 priv->state |= NAV_FLAG_EOF;
309 return len;
310 }
253 case DVDNAV_BLOCK_OK: 311 case DVDNAV_BLOCK_OK:
254 case DVDNAV_NAV_PACKET: 312 case DVDNAV_NAV_PACKET:
313 case DVDNAV_STILL_FRAME:
255 return len; 314 return len;
315 case DVDNAV_WAIT: {
316 if (priv->state & NAV_FLAG_WAIT)
317 return len;
318 break;
319 }
256 case DVDNAV_VTS_CHANGE: { 320 case DVDNAV_VTS_CHANGE: {
257 int tit = 0, part = 0; 321 int tit = 0, part = 0;
258 dvdnav_vts_change_event_t *vts_event = (dvdnav_vts_change_event_t *)s->buffer; 322 dvdnav_vts_change_event_t *vts_event = (dvdnav_vts_change_event_t *)s->buffer;
259 mp_msg(MSGT_CPLAYER,MSGL_INFO, "DVDNAV, switched to title: %d\r\n", vts_event->new_vtsN); 323 mp_msg(MSGT_CPLAYER,MSGL_INFO, "DVDNAV, switched to title: %d\r\n", vts_event->new_vtsN);
324 priv->state |= NAV_FLAG_CELL_CHANGED;
325 priv->state &= ~NAV_FLAG_WAIT_SKIP;
326 priv->state &= ~NAV_FLAG_WAIT;
260 s->end_pos = 0; 327 s->end_pos = 0;
261 update_title_len(s); 328 update_title_len(s);
329 if (priv->state & NAV_FLAG_WAIT_READ_AUTO)
330 priv->state |= NAV_FLAG_WAIT_READ;
262 if(dvdnav_current_title_info(priv->dvdnav, &tit, &part) == DVDNAV_STATUS_OK) { 331 if(dvdnav_current_title_info(priv->dvdnav, &tit, &part) == DVDNAV_STATUS_OK) {
263 mp_msg(MSGT_CPLAYER,MSGL_V, "\r\nDVDNAV, NEW TITLE %d\r\n", tit); 332 mp_msg(MSGT_CPLAYER,MSGL_V, "\r\nDVDNAV, NEW TITLE %d\r\n", tit);
264 dvdnav_get_highlight (priv, 0); 333 dvdnav_get_highlight (priv, 0);
265 if(priv->title > 0 && tit != priv->title) 334 if(priv->title > 0 && tit != priv->title)
266 return 0; 335 return 0;
267 } 336 }
268 break; 337 break;
269 } 338 }
270 case DVDNAV_CELL_CHANGE: { 339 case DVDNAV_CELL_CHANGE: {
340 priv->state |= NAV_FLAG_CELL_CHANGED;
341 priv->state &= ~NAV_FLAG_WAIT_SKIP;
342 priv->state &= ~NAV_FLAG_WAIT;
343 if (priv->state & NAV_FLAG_WAIT_READ_AUTO)
344 priv->state |= NAV_FLAG_WAIT_READ;
271 if(priv->title > 0 && dvd_last_chapter > 0) { 345 if(priv->title > 0 && dvd_last_chapter > 0) {
272 int tit=0, part=0; 346 int tit=0, part=0;
273 if(dvdnav_current_title_info(priv->dvdnav, &tit, &part) == DVDNAV_STATUS_OK && part > dvd_last_chapter) 347 if(dvdnav_current_title_info(priv->dvdnav, &tit, &part) == DVDNAV_STATUS_OK && part > dvd_last_chapter)
274 return 0; 348 return 0;
275 } 349 }
315 *((unsigned int *)arg) = part - 1; 389 *((unsigned int *)arg) = part - 1;
316 return 1; 390 return 1;
317 } 391 }
318 case STREAM_CTRL_GET_TIME_LENGTH: 392 case STREAM_CTRL_GET_TIME_LENGTH:
319 { 393 {
320 if(priv->duration) 394 if(priv->duration || priv->still_length)
321 { 395 {
322 *((double *)arg) = (double)priv->duration / 1000.0; 396 *((double *)arg) = (double)priv->duration / 1000.0;
323 return 1; 397 return 1;
324 } 398 }
325 break; 399 break;
482 mp_msg(MSGT_OPEN,MSGL_FATAL,"dvdnav_stream, couldn't select title %d, error '%s'\n", p->track, dvdnav_err_to_string(priv->dvdnav)); 556 mp_msg(MSGT_OPEN,MSGL_FATAL,"dvdnav_stream, couldn't select title %d, error '%s'\n", p->track, dvdnav_err_to_string(priv->dvdnav));
483 return STREAM_UNSUPPORTED; 557 return STREAM_UNSUPPORTED;
484 } 558 }
485 if(dvd_chapter > 0) 559 if(dvd_chapter > 0)
486 dvdnav_part_play(priv->dvdnav, p->track, dvd_chapter); 560 dvdnav_part_play(priv->dvdnav, p->track, dvd_chapter);
487 } else if(p->track == -1) 561 } else if (p->track == 0) {
488 dvdnav_menu_call(priv->dvdnav, DVD_MENU_Root); 562 if(dvdnav_menu_call(priv->dvdnav, DVD_MENU_Root) != DVDNAV_STATUS_OK)
489 else { 563 dvdnav_menu_call(priv->dvdnav, DVD_MENU_Title);
490 mp_msg(MSGT_OPEN,MSGL_INFO,"dvdnav_stream, you didn't specify a track number (as in dvdnav://1), playing whole disc\n");
491 dvdnav_menu_call(priv->dvdnav, DVD_MENU_Title);
492 } 564 }
493 if(mp_msg_test(MSGT_IDENTIFY, MSGL_INFO)) 565 if(mp_msg_test(MSGT_IDENTIFY, MSGL_INFO))
494 identify(priv, p); 566 identify(priv, p);
495 if(p->track > 0) 567 if(p->track > 0)
496 show_audio_subs_languages(priv->dvdnav); 568 show_audio_subs_languages(priv->dvdnav);
506 stream->type = STREAMTYPE_DVDNAV; 578 stream->type = STREAMTYPE_DVDNAV;
507 stream->priv=(void*)priv; 579 stream->priv=(void*)priv;
508 *file_format = DEMUXER_TYPE_MPEG_PS; 580 *file_format = DEMUXER_TYPE_MPEG_PS;
509 581
510 update_title_len(stream); 582 update_title_len(stream);
511 if(!stream->pos) 583 if(!stream->pos && p->track > 0)
512 mp_msg(MSGT_OPEN,MSGL_ERR, "INIT ERROR: couldn't get init pos %s\r\n", dvdnav_err_to_string(priv->dvdnav)); 584 mp_msg(MSGT_OPEN,MSGL_ERR, "INIT ERROR: couldn't get init pos %s\r\n", dvdnav_err_to_string(priv->dvdnav));
513 585
514 mp_msg(MSGT_OPEN,MSGL_INFO, "Remember to disable MPlayer's cache when playing dvdnav:// streams (adding -nocache to your command line)\r\n"); 586 mp_msg(MSGT_OPEN,MSGL_INFO, "Remember to disable MPlayer's cache when playing dvdnav:// streams (adding -nocache to your command line)\r\n");
515 587
516 return STREAM_OK; 588 return STREAM_OK;
517 } 589 }
518 590
519 591
520 int mp_dvdnav_handle_input(stream_t *stream, int cmd, int *button) { 592 void mp_dvdnav_handle_input(stream_t *stream, int cmd, int *button) {
521 dvdnav_priv_t * priv=(dvdnav_priv_t*)stream->priv; 593 dvdnav_priv_t * priv=(dvdnav_priv_t*)stream->priv;
522 dvdnav_t *nav = priv->dvdnav; 594 dvdnav_t *nav = priv->dvdnav;
523 dvdnav_status_t status=DVDNAV_STATUS_ERR; 595 dvdnav_status_t status=DVDNAV_STATUS_ERR;
524 pci_t *pci = dvdnav_get_current_nav_pci(nav); 596 pci_t *pci = dvdnav_get_current_nav_pci(nav);
525 int reset = 0;
526 597
527 if(cmd != MP_CMD_DVDNAV_SELECT && !pci) 598 if(cmd != MP_CMD_DVDNAV_SELECT && !pci)
528 return 0; 599 return;
529 600
530 switch(cmd) { 601 switch(cmd) {
531 case MP_CMD_DVDNAV_UP: 602 case MP_CMD_DVDNAV_UP:
532 status = dvdnav_upper_button_select(nav, pci); 603 status = dvdnav_upper_button_select(nav, pci);
533 break; 604 break;
540 case MP_CMD_DVDNAV_RIGHT: 611 case MP_CMD_DVDNAV_RIGHT:
541 status = dvdnav_right_button_select(nav, pci); 612 status = dvdnav_right_button_select(nav, pci);
542 break; 613 break;
543 case MP_CMD_DVDNAV_MENU: 614 case MP_CMD_DVDNAV_MENU:
544 status = dvdnav_menu_call(nav,DVD_MENU_Root); 615 status = dvdnav_menu_call(nav,DVD_MENU_Root);
545 reset = 1;
546 break; 616 break;
547 case MP_CMD_DVDNAV_PREVMENU: { 617 case MP_CMD_DVDNAV_PREVMENU: {
548 int title=0, part=0; 618 int title=0, part=0;
549 619
550 dvdnav_current_title_info(nav, &title, &part); 620 dvdnav_current_title_info(nav, &title, &part);
551 if(title) { 621 if(title) {
552 if(dvdnav_menu_call(nav, DVD_MENU_Part) == DVDNAV_STATUS_OK 622 if((status=dvdnav_menu_call(nav, DVD_MENU_Part)) == DVDNAV_STATUS_OK)
553 || dvdnav_menu_call(nav, DVD_MENU_Title) == DVDNAV_STATUS_OK) {
554 reset = 1;
555 break; 623 break;
556 } 624 }
557 } 625 if((status=dvdnav_menu_call(nav, DVD_MENU_Title)) == DVDNAV_STATUS_OK)
558 if(dvdnav_menu_call(nav, DVD_MENU_Root) == DVDNAV_STATUS_OK) 626 break;
559 reset = 1; 627 status=dvdnav_menu_call(nav, DVD_MENU_Root);
560 } 628 }
561 break; 629 break;
562 case MP_CMD_DVDNAV_SELECT: 630 case MP_CMD_DVDNAV_SELECT:
563 status = dvdnav_button_activate(nav, pci); 631 status = dvdnav_button_activate(nav, pci);
564 if(status == DVDNAV_STATUS_OK) reset = 1;
565 break; 632 break;
566 case MP_CMD_DVDNAV_MOUSECLICK: 633 case MP_CMD_DVDNAV_MOUSECLICK:
567 /* 634 /*
568 this is a workaround: in theory the simple dvdnav_lower_button_select()+dvdnav_button_activate() 635 this is a workaround: in theory the simple dvdnav_lower_button_select()+dvdnav_button_activate()
569 should be enough (and generally it is), but there are cases when the calls to dvdnav_lower_button_select() 636 should be enough (and generally it is), but there are cases when the calls to dvdnav_lower_button_select()
570 and friends fail! Hence we have to call dvdnav_mouse_activate(priv->mousex, priv->mousey) with 637 and friends fail! Hence we have to call dvdnav_mouse_activate(priv->mousex, priv->mousey) with
571 the coodinates saved by mp_dvdnav_update_mouse_pos(). 638 the coodinates saved by mp_dvdnav_update_mouse_pos().
572 This last call always works well 639 This last call always works well
573 */ 640 */
574 status = dvdnav_mouse_activate(nav, pci, priv->mousex, priv->mousey); 641 status = dvdnav_mouse_activate(nav, pci, priv->mousex, priv->mousey);
575 if(status == DVDNAV_STATUS_OK) reset = 1;
576 break; 642 break;
577 default: 643 default:
578 mp_msg(MSGT_CPLAYER, MSGL_V, "Unknown DVDNAV cmd %d\n", cmd); 644 mp_msg(MSGT_CPLAYER, MSGL_V, "Unknown DVDNAV cmd %d\n", cmd);
579 break; 645 break;
580 } 646 }
581 647
582 if(status == DVDNAV_STATUS_OK) 648 if(status == DVDNAV_STATUS_OK)
583 dvdnav_get_current_highlight(nav, button); 649 dvdnav_get_current_highlight(nav, button);
584
585 return reset;
586 } 650 }
587 651
588 void mp_dvdnav_update_mouse_pos(stream_t *stream, int32_t x, int32_t y, int* button) { 652 void mp_dvdnav_update_mouse_pos(stream_t *stream, int32_t x, int32_t y, int* button) {
589 dvdnav_priv_t * priv=(dvdnav_priv_t*)stream->priv; 653 dvdnav_priv_t * priv=(dvdnav_priv_t*)stream->priv;
590 dvdnav_t *nav = priv->dvdnav; 654 dvdnav_t *nav = priv->dvdnav;
720 */ 784 */
721 int dvdnav_number_of_subs(stream_t *stream) { 785 int dvdnav_number_of_subs(stream_t *stream) {
722 dvdnav_priv_t * priv=(dvdnav_priv_t*)stream->priv; 786 dvdnav_priv_t * priv=(dvdnav_priv_t*)stream->priv;
723 uint8_t lg, k, n=0; 787 uint8_t lg, k, n=0;
724 788
789 if (priv->state & NAV_FLAG_VTS_DOMAIN) return 0;
725 for(k=0; k<32; k++) { 790 for(k=0; k<32; k++) {
726 lg = dvdnav_get_spu_logical_stream(priv->dvdnav, k); 791 lg = dvdnav_get_spu_logical_stream(priv->dvdnav, k);
727 if(lg == 0xff) continue; 792 if(lg == 0xff) continue;
728 n++; 793 n++;
729 } 794 }
762 827
763 dvdnav_get_number_of_titles (priv->dvdnav, &titles); 828 dvdnav_get_number_of_titles (priv->dvdnav, &titles);
764 if (title > 0 && title <= titles) 829 if (title > 0 && title <= titles)
765 dvdnav_title_play (priv->dvdnav, title); 830 dvdnav_title_play (priv->dvdnav, title);
766 } 831 }
832
833 /**
834 * \brief Check if end of stream has been reached
835 * \param stream: - stream pointer
836 * \return 1 on really eof
837 */
838 int mp_dvdnav_is_eof (stream_t *stream) {
839 return ((dvdnav_priv_t *) stream->priv)->state & NAV_FLAG_EOF;
840 }
841
842 /**
843 * \brief Skip still frame
844 * \param stream: - stream pointer
845 * \return 0 on success
846 */
847 int mp_dvdnav_skip_still (stream_t *stream) {
848 dvdnav_priv_t *priv = stream->priv;
849 if (priv->still_length == 0xff)
850 return 1;
851 dvdnav_still_skip(priv->dvdnav);
852 return 0;
853 }
854
855 /**
856 * \brief Skip wait event
857 * \param stream: - stream pointer
858 * \return 0 on success
859 */
860 int mp_dvdnav_skip_wait (stream_t *stream) {
861 dvdnav_priv_t *priv = stream->priv;
862 if (!(priv->state & NAV_FLAG_WAIT))
863 return 1;
864 priv->state &= ~NAV_FLAG_WAIT;
865 dvdnav_wait_skip(priv->dvdnav);
866 return 0;
867 }
868
869 /**
870 * \brief Set wait mode
871 * \param stream : - stream pointer
872 * \param mode : - if true, then suspend block read
873 * \param automode: - if true, then VTS or cell change set wait mode
874 */
875 void mp_dvdnav_read_wait (stream_t *stream, int mode, int automode) {
876 dvdnav_priv_t *priv = stream->priv;
877 if (mode == 0)
878 priv->state &= ~NAV_FLAG_WAIT_READ;
879 if (mode > 0)
880 priv->state |= NAV_FLAG_WAIT_READ;
881 if (automode == 0)
882 priv->state &= ~NAV_FLAG_WAIT_READ_AUTO;
883 if (automode > 0)
884 priv->state |= NAV_FLAG_WAIT_READ_AUTO;
885 }
886
887 /**
888 * \brief Check if cell has changed
889 * \param stream: - stream pointer
890 * \param clear : - if true, then clear cell change flag
891 * \return 1 if cell has changed
892 */
893 int mp_dvdnav_cell_has_changed (stream_t *stream, int clear) {
894 dvdnav_priv_t *priv = stream->priv;
895 if (!(priv->state & NAV_FLAG_CELL_CHANGED))
896 return 0;
897 if (clear)
898 priv->state &= ~NAV_FLAG_CELL_CHANGED;
899 return 1;
900 }
901
767 902
768 const stream_info_t stream_info_dvdnav = { 903 const stream_info_t stream_info_dvdnav = {
769 "DVDNAV stream", 904 "DVDNAV stream",
770 "null", 905 "null",
771 "", 906 "",