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