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