]> git.sesse.net Git - vlc/blob - modules/access/vcdx/vcdplayer.c
Cleanups towards getting this to a usable state.
[vlc] / modules / access / vcdx / vcdplayer.c
1 /*****************************************************************************
2  * vcdplayer.c : VCD input module for vlc
3  *               using libcdio, libvcd and libvcdinfo
4  *****************************************************************************
5  * Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com>
6  * $Id$
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
21  *****************************************************************************/
22
23 /*
24    This contains more of the vlc-independent parts that might be used
25    in any VCD input module for a media player. However at present there
26    are vlc-specific structures. See also vcdplayer.c of the xine plugin.
27  */
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31
32 #include <vlc/vlc.h>
33 #include <vlc/input.h>
34 #include <vlc/intf.h>
35
36 #include "vcd.h"
37 #include "vcdplayer.h"
38 #include "intf.h"
39
40 #include <string.h>
41
42 #include <cdio/cdio.h>
43 #include <cdio/util.h>
44 #include <libvcd/info.h>
45
46 #ifdef WIN32
47 #define sleep(A) Sleep((A)*1000)
48 #endif
49
50 /*!
51   Return VLC_TRUE if playback control (PBC) is on
52 */
53 vlc_bool_t 
54 vcdplayer_pbc_is_on( const access_vcd_data_t *p_vcd ) 
55 {
56   return VCDINFO_INVALID_ENTRY != p_vcd->i_lid; 
57 }
58
59 static void 
60 vcdplayer_update_entry( access_t * p_access, uint16_t ofs, 
61                         uint16_t *entry, const char *label)
62 {
63   access_vcd_data_t *     p_vcd= (access_vcd_data_t *)p_access->p_sys;
64
65   if ( ofs == VCDINFO_INVALID_OFFSET ) {
66     *entry = VCDINFO_INVALID_ENTRY;
67   } else {
68     vcdinfo_offset_t *off = vcdinfo_get_offset_t(p_vcd->vcd, ofs);
69     if (off != NULL) {
70       *entry = off->lid;
71       dbg_print(INPUT_DBG_PBC, "%s: LID %d\n", label, off->lid);
72     } else
73       *entry = VCDINFO_INVALID_ENTRY;
74   }
75 }
76
77 /* Handles navigation when NOT in PBC reaching the end of a play item. 
78
79    The navigations rules here may be sort of made up, but the intent 
80    is to do something that's probably right or helpful.
81
82    return VLC_TRUE if the caller should return.
83 */
84 vcdplayer_read_status_t 
85 vcdplayer_non_pbc_nav ( access_t * p_access )
86 {
87   access_vcd_data_t *     p_vcd= (access_vcd_data_t *)p_access->p_sys;
88
89   /* Not in playback control. Do we advance automatically or stop? */
90   switch (p_vcd->play_item.type) {
91   case VCDINFO_ITEM_TYPE_TRACK:
92   case VCDINFO_ITEM_TYPE_ENTRY: {
93
94     dbg_print( INPUT_DBG_LSN, "new track %d, lsn %d", p_vcd->i_track+1, 
95                vcdinfo_get_track_lsn(p_vcd->vcd, p_vcd->i_track+1) );
96     return READ_END;
97     break;
98   }
99   case VCDINFO_ITEM_TYPE_SPAREID2:  
100     dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN), 
101                "SPAREID2" );
102     /* FIXME */
103     if (p_vcd->in_still)
104     {
105       return READ_STILL_FRAME ;
106     }
107     return READ_END;
108   case VCDINFO_ITEM_TYPE_NOTFOUND:  
109     LOG_ERR ("NOTFOUND outside PBC -- not supposed to happen");
110     return READ_ERROR;
111   case VCDINFO_ITEM_TYPE_LID:  
112     LOG_ERR ("LID outside PBC -- not supposed to happen");
113     return READ_ERROR;
114   case VCDINFO_ITEM_TYPE_SEGMENT:
115       /* Hack: Just go back and do still again */
116     /* FIXME */
117     if (p_vcd->in_still) 
118     {
119       dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN), 
120                  "End of Segment - looping" );
121       return READ_STILL_FRAME;
122     }
123     return READ_END;
124   }
125   return READ_BLOCK;
126 }
127
128 /* Handles PBC navigation when reaching the end of a play item. */
129 vcdplayer_read_status_t
130 vcdplayer_pbc_nav ( access_t * p_access )
131 {
132   access_vcd_data_t *     p_vcd= (access_vcd_data_t *)p_access->p_sys;
133
134   /* We are in playback control. */
135   vcdinfo_itemid_t itemid;
136
137   /* The end of an entry is really the end of the associated 
138      sequence (or track). */
139   
140   if ( (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) && 
141        (p_vcd->i_lsn < p_vcd->end_lsn) ) {
142     /* Set up to just continue to the next entry */
143     p_vcd->play_item.num++;
144     dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), 
145                "continuing into next entry: %u", p_vcd->play_item.num);
146     VCDPlay( p_access, p_vcd->play_item );
147     /* p_vcd->update_title(); */
148     return READ_BLOCK;
149   }
150   
151   switch (p_vcd->pxd.descriptor_type) {
152   case PSD_TYPE_END_LIST:
153     return READ_END;
154     break;
155   case PSD_TYPE_PLAY_LIST: {
156     int wait_time = vcdinf_get_wait_time(p_vcd->pxd.pld);
157     
158     dbg_print(INPUT_DBG_PBC, "playlist wait_time: %d", wait_time);
159     
160     if (vcdplayer_inc_play_item(p_access))
161       return READ_BLOCK;
162
163     /* Handle any wait time given. */
164 #if FIXED
165     if (p_vcd->in_still) {
166       vcdIntfStillTime( p_vcd->p_intf, wait_time );
167       return READ_STILL_FRAME;
168     }
169 #endif
170
171     vcdplayer_update_entry( p_access, 
172                             vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
173                             &itemid.num, "next" );
174     itemid.type = VCDINFO_ITEM_TYPE_LID;
175     VCDPlay( p_access, itemid );
176     break;
177   }
178   case PSD_TYPE_SELECTION_LIST:     /* Selection List (+Ext. for SVCD) */
179   case PSD_TYPE_EXT_SELECTION_LIST: /* Extended Selection List (VCD2.0) */
180     {
181       int wait_time         = vcdinf_get_timeout_time(p_vcd->pxd.psd);
182       uint16_t timeout_offs = vcdinf_get_timeout_offset(p_vcd->pxd.psd);
183       uint16_t max_loop     = vcdinf_get_loop_count(p_vcd->pxd.psd);
184       vcdinfo_offset_t *offset_timeout_LID = 
185         vcdinfo_get_offset_t(p_vcd->vcd, timeout_offs);
186       
187       dbg_print(INPUT_DBG_PBC, "wait_time: %d, looped: %d, max_loop %d", 
188                 wait_time, p_vcd->loop_count, max_loop);
189       
190       /* Handle any wait time given */
191 #if FIXED
192       if (p_vcd->in_still) {
193         vcdIntfStillTime( p_vcd->p_intf, wait_time );
194         return READ_STILL_FRAME;
195       } 
196 #endif
197       
198       /* Handle any looping given. */
199       if ( max_loop == 0 || p_vcd->loop_count < max_loop ) {
200         p_vcd->loop_count++;
201         if (p_vcd->loop_count == 0x7f) p_vcd->loop_count = 0;
202         VCDSeek( p_access, 0 );
203         /* if (p_vcd->in_still) p_vcd->force_redisplay();*/
204         return READ_BLOCK;
205       }
206       
207       /* Looping finished and wait finished. Move to timeout
208          entry or next entry, or handle still. */
209       
210       if (NULL != offset_timeout_LID) {
211         /* Handle timeout_LID */
212         itemid.num  = offset_timeout_LID->lid;
213         itemid.type = VCDINFO_ITEM_TYPE_LID;
214         dbg_print(INPUT_DBG_PBC, "timeout to: %d", itemid.num);
215         VCDPlay( p_access, itemid );
216         return READ_BLOCK;
217       } else {
218         int num_selections = vcdinf_get_num_selections(p_vcd->pxd.psd);
219         if (num_selections > 0) {
220           /* Pick a random selection. */
221           unsigned int bsn=vcdinf_get_bsn(p_vcd->pxd.psd);
222           int rand_selection=bsn +
223             (int) ((num_selections+0.0)*rand()/(RAND_MAX+1.0));
224           lid_t rand_lid=vcdinfo_selection_get_lid (p_vcd->vcd, 
225                                                     p_vcd->i_lid, 
226                                                     rand_selection);
227           itemid.num = rand_lid;
228           itemid.type = VCDINFO_ITEM_TYPE_LID;
229           dbg_print(INPUT_DBG_PBC, "random selection %d, lid: %d", 
230                     rand_selection - bsn, rand_lid);
231           VCDPlay( p_access, itemid );
232           return READ_BLOCK;
233         } else if (p_vcd->in_still) {
234           /* Hack: Just go back and do still again */
235           sleep(1);
236           return READ_STILL_FRAME;
237         }
238       }
239       break;
240     }
241   case VCDINFO_ITEM_TYPE_NOTFOUND:  
242     LOG_ERR( "NOTFOUND in PBC -- not supposed to happen" );
243     break;
244   case VCDINFO_ITEM_TYPE_SPAREID2:  
245     LOG_ERR( "SPAREID2 in PBC -- not supposed to happen" );
246     break;
247   case VCDINFO_ITEM_TYPE_LID:  
248     LOG_ERR( "LID in PBC -- not supposed to happen" );
249     break;
250     
251   default:
252     ;
253   }
254   /* FIXME: Should handle autowait ...  */
255
256   return READ_ERROR;
257 }
258
259 /*!
260   Get the next play-item in the list given in the LIDs. Note play-item
261   here refers to list of play-items for a single LID It shouldn't be
262   confused with a user's list of favorite things to play or the 
263   "next" field of a LID which moves us to a different LID.
264  */
265 vlc_bool_t 
266 vcdplayer_inc_play_item( access_t *p_access )
267 {
268   access_vcd_data_t *     p_vcd= (access_vcd_data_t *)p_access->p_sys;
269
270   int noi;
271
272   dbg_print(INPUT_DBG_CALL, "called pli: %d", p_vcd->pdi);
273
274   if ( NULL == p_vcd || NULL == p_vcd->pxd.pld  ) return VLC_FALSE;
275
276   noi = vcdinf_pld_get_noi(p_vcd->pxd.pld);
277   
278   if ( noi <= 0 ) return VLC_FALSE;
279   
280   /* Handle delays like autowait or wait here? */
281
282   p_vcd->pdi++;
283
284   if ( p_vcd->pdi < 0 || p_vcd->pdi >= noi ) return VLC_FALSE;
285
286   else {
287     uint16_t trans_itemid_num=vcdinf_pld_get_play_item(p_vcd->pxd.pld, 
288                                                        p_vcd->pdi);
289     vcdinfo_itemid_t trans_itemid;
290
291     if (VCDINFO_INVALID_ITEMID == trans_itemid_num) return VLC_FALSE;
292     
293     vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
294     dbg_print(INPUT_DBG_PBC, "  play-item[%d]: %s",
295               p_vcd->pdi, vcdinfo_pin2str (trans_itemid_num));
296     return VLC_SUCCESS == VCDPlay( p_access, trans_itemid );
297   }
298 }
299
300 /*!
301   Play item assocated with the "default" selection.
302
303   Return VLC_FALSE if there was some problem.
304 */
305 vlc_bool_t 
306 vcdplayer_play_default( access_t * p_access )
307 {
308   access_vcd_data_t *p_vcd= (access_vcd_data_t *)p_access->p_sys;
309
310   vcdinfo_itemid_t   itemid;
311
312   if (!p_vcd) {
313     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
314                "null p_vcd" );
315     return VLC_EGENERIC;
316   }
317   
318
319   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
320              "current: %d" , p_vcd->play_item.num);
321
322   itemid.type = p_vcd->play_item.type;
323
324   if  (vcdplayer_pbc_is_on(p_vcd)) {
325
326 #if defined(LIBVCD_VERSION)
327     lid_t lid=vcdinfo_get_multi_default_lid(p_vcd->vcd, p_vcd->i_lid,
328                                             p_vcd->i_lsn);
329
330     if (VCDINFO_INVALID_LID != lid) {
331       itemid.num  = lid;
332       itemid.type = VCDINFO_ITEM_TYPE_LID;
333       dbg_print(INPUT_DBG_PBC, "DEFAULT to %d\n", itemid.num);
334     } else {
335       dbg_print(INPUT_DBG_PBC, "no DEFAULT for LID %d\n", p_vcd->i_lid);
336     }
337
338 #else 
339     vcdinfo_lid_get_pxd(p_vcd->vcd, &(p_vcd->pxd), p_vcd->i_lid);
340     
341     switch (p_vcd->pxd.descriptor_type) {
342     case PSD_TYPE_SELECTION_LIST:
343     case PSD_TYPE_EXT_SELECTION_LIST:
344       if (p_vcd->pxd.psd == NULL) return VLC_FALSE;
345       vcdplayer_update_entry( p_access, 
346                               vcdinfo_get_default_offset(p_vcd->vcd, 
347                                                          p_vcd->i_lid), 
348                               &itemid.num, "default");
349       break;
350
351     case PSD_TYPE_PLAY_LIST: 
352     case PSD_TYPE_END_LIST:
353     case PSD_TYPE_COMMAND_LIST:
354       LOG_WARN( "There is no PBC 'default' selection here" );
355       return VLC_FALSE;
356     }
357 #endif /* LIBVCD_VERSION (< 0.7.21) */
358     
359
360   } else {
361
362     /* PBC is not on. "default" selection beginning of current 
363        selection . */
364   
365     p_vcd->play_item.num = p_vcd->play_item.num;
366     
367   }
368
369   /** ??? p_vcd->update_title(); ***/
370   return VLC_SUCCESS == VCDPlay( p_access, itemid );
371
372 }
373
374 /*!
375   Play item assocated with the "next" selection.
376
377   Return VLC_FALSE if there was some problem.
378 */
379 vlc_bool_t 
380 vcdplayer_play_next( access_t * p_access )
381 {
382   access_vcd_data_t *p_vcd= (access_vcd_data_t *)p_access->p_sys;
383
384   vcdinfo_obj_t     *obj;
385   vcdinfo_itemid_t   itemid;
386
387   if (!p_vcd) return VLC_FALSE;
388
389   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
390              "current: %d" , p_vcd->play_item.num);
391
392   obj = p_vcd->vcd;
393
394   itemid.type = p_vcd->play_item.type;
395
396   if  (vcdplayer_pbc_is_on(p_vcd)) {
397
398     vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->i_lid);
399     
400     switch (p_vcd->pxd.descriptor_type) {
401     case PSD_TYPE_SELECTION_LIST:
402     case PSD_TYPE_EXT_SELECTION_LIST:
403       if (p_vcd->pxd.psd == NULL) return VLC_FALSE;
404       vcdplayer_update_entry( p_access, 
405                               vcdinf_psd_get_next_offset(p_vcd->pxd.psd), 
406                               &itemid.num, "next");
407       itemid.type = VCDINFO_ITEM_TYPE_LID;
408       break;
409
410     case PSD_TYPE_PLAY_LIST: 
411       if (p_vcd->pxd.pld == NULL) return VLC_FALSE;
412       vcdplayer_update_entry( p_access, 
413                               vcdinf_pld_get_next_offset(p_vcd->pxd.pld), 
414                               &itemid.num, "next");
415       itemid.type = VCDINFO_ITEM_TYPE_LID;
416       break;
417       
418     case PSD_TYPE_END_LIST:
419     case PSD_TYPE_COMMAND_LIST:
420       LOG_WARN( "There is no PBC 'next' selection here" );
421       return VLC_FALSE;
422     }
423   } else {
424
425     /* PBC is not on. "Next" selection is play_item.num+1 if possible. */
426   
427     int max_entry = 0;
428
429     switch (p_vcd->play_item.type) {
430     case VCDINFO_ITEM_TYPE_ENTRY: 
431     case VCDINFO_ITEM_TYPE_SEGMENT: 
432     case VCDINFO_ITEM_TYPE_TRACK: 
433       
434       switch (p_vcd->play_item.type) {
435       case VCDINFO_ITEM_TYPE_ENTRY: 
436         max_entry = p_vcd->i_entries;
437         break;
438       case VCDINFO_ITEM_TYPE_SEGMENT: 
439         max_entry = p_vcd->i_segments;
440         break;
441       case VCDINFO_ITEM_TYPE_TRACK: 
442         max_entry = p_vcd->i_tracks;
443         break;
444       default: ; /* Handle exceptional cases below */
445       }
446       
447       if (p_vcd->play_item.num+1 < max_entry) {
448         itemid.num = p_vcd->play_item.num+1;
449       } else {
450         LOG_WARN( "At the end - non-PBC 'next' not possible here" );
451         return VLC_FALSE;
452       }
453       
454       break;
455       
456     case VCDINFO_ITEM_TYPE_LID: 
457       {
458         /* Should have handled above. */
459         LOG_WARN( "Internal inconsistency - should not have gotten here." );
460         return VLC_FALSE;
461       }
462     default: 
463       return VLC_FALSE;
464     }
465   }
466
467   /** ??? p_vcd->update_title(); ***/
468   return VLC_SUCCESS == VCDPlay( p_access, itemid );
469
470 }
471
472 /*!
473   Play item assocated with the "prev" selection.
474
475   Return VLC_FALSE if there was some problem.
476 */
477 vlc_bool_t 
478 vcdplayer_play_prev( access_t * p_access )
479 {
480   access_vcd_data_t *p_vcd= (access_vcd_data_t *)p_access->p_sys;
481
482   vcdinfo_obj_t     *obj  = p_vcd->vcd;
483   vcdinfo_itemid_t   itemid;
484
485   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
486              "current: %d" , p_vcd->play_item.num);
487
488   itemid.type = p_vcd->play_item.type;
489
490   if  (vcdplayer_pbc_is_on(p_vcd)) {
491
492     vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->i_lid);
493     
494     switch (p_vcd->pxd.descriptor_type) {
495     case PSD_TYPE_SELECTION_LIST:
496     case PSD_TYPE_EXT_SELECTION_LIST:
497       if (p_vcd->pxd.psd == NULL) return VLC_FALSE;
498       vcdplayer_update_entry( p_access, 
499                               vcdinf_psd_get_prev_offset(p_vcd->pxd.psd), 
500                               &itemid.num, "prev");
501       itemid.type = VCDINFO_ITEM_TYPE_LID;
502       break;
503
504     case PSD_TYPE_PLAY_LIST: 
505       if (p_vcd->pxd.pld == NULL) return VLC_FALSE;
506       vcdplayer_update_entry( p_access, 
507                               vcdinf_pld_get_prev_offset(p_vcd->pxd.pld), 
508                               &itemid.num, "prev");
509       itemid.type = VCDINFO_ITEM_TYPE_LID;
510       break;
511       
512     case PSD_TYPE_END_LIST:
513     case PSD_TYPE_COMMAND_LIST:
514       LOG_WARN( "There is no PBC 'prev' selection here" );
515       return VLC_FALSE;
516     }
517   } else {
518
519     /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */
520   
521     int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) 
522       ? 0 : 1;
523     
524     if (p_vcd->play_item.num > min_entry) {
525       itemid.num = p_vcd->play_item.num-1;
526     } else {
527       LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" );
528       return VLC_FALSE;
529     }
530       
531   }
532
533   /** ??? p_vcd->update_title(); ***/
534   return VLC_SUCCESS == VCDPlay( p_access, itemid );
535
536 }
537
538 /*!
539   Play item assocated with the "return" selection.
540
541   Return VLC_FALSE if there was some problem.
542 */
543 vlc_bool_t 
544 vcdplayer_play_return( access_t * p_access )
545 {
546   access_vcd_data_t *p_vcd= (access_vcd_data_t *)p_access->p_sys;
547
548   vcdinfo_obj_t     *obj  = p_vcd->vcd;
549   vcdinfo_itemid_t   itemid;
550
551   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
552              "current: %d" , p_vcd->play_item.num);
553
554   itemid.type = p_vcd->play_item.type;
555
556   if  (vcdplayer_pbc_is_on(p_vcd)) {
557
558     vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->i_lid);
559     
560     switch (p_vcd->pxd.descriptor_type) {
561     case PSD_TYPE_SELECTION_LIST:
562     case PSD_TYPE_EXT_SELECTION_LIST:
563       if (p_vcd->pxd.psd == NULL) return VLC_FALSE;
564       vcdplayer_update_entry( p_access, 
565                               vcdinf_psd_get_return_offset(p_vcd->pxd.psd), 
566                               &itemid.num, "return");
567       itemid.type = VCDINFO_ITEM_TYPE_LID;
568       break;
569
570     case PSD_TYPE_PLAY_LIST: 
571       if (p_vcd->pxd.pld == NULL) return VLC_FALSE;
572       vcdplayer_update_entry( p_access, 
573                               vcdinf_pld_get_return_offset(p_vcd->pxd.pld), 
574                               &itemid.num, "return");
575       itemid.type = VCDINFO_ITEM_TYPE_LID;
576       break;
577       
578     case PSD_TYPE_END_LIST:
579     case PSD_TYPE_COMMAND_LIST:
580       LOG_WARN( "There is no PBC 'return' selection here" );
581       return VLC_FALSE;
582     }
583   } else {
584
585     /* PBC is not on. "Return" selection is min_entry if possible. */
586   
587     p_vcd->play_item.num = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) 
588       ? 0 : 1;
589     
590   }
591
592   /** ??? p_vcd->update_title(); ***/
593   return VLC_SUCCESS == VCDPlay( p_access, itemid );
594
595 }