]> git.sesse.net Git - vlc/blob - modules/access/vcdx/vcdplayer.c
more WIN32 changes.
[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   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
311              "current: %d" , p_vcd->play_item.num);
312
313   itemid.type = p_vcd->play_item.type;
314
315   if  (vcdplayer_pbc_is_on(p_vcd)) {
316
317     lid_t lid=vcdinfo_get_multi_default_lid(p_vcd->vcd, p_vcd->cur_lid,
318                                             itemid.num);
319
320     if (VCDINFO_INVALID_LID != lid) {
321       itemid.num  = lid;
322       itemid.type = VCDINFO_ITEM_TYPE_LID;
323       dbg_print(INPUT_DBG_PBC, "DEFAULT to %d\n", itemid.num);
324     } else {
325       dbg_print(INPUT_DBG_PBC, "no DEFAULT for LID %d\n", p_vcd->cur_lid);
326     }
327     
328
329   } else {
330
331     /* PBC is not on. "default" selection beginning of current 
332        selection . */
333   
334     p_vcd->play_item.num = p_vcd->play_item.num;
335     
336   }
337
338   /** ??? p_vcd->update_title(); ***/
339   return VLC_SUCCESS == VCDPlay( p_input, itemid );
340
341 }
342
343 /*!
344   Play item assocated with the "next" selection.
345
346   Return false if there was some problem.
347 */
348 bool
349 vcdplayer_play_next( input_thread_t * p_input )
350 {
351   thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
352
353   vcdinfo_obj_t     *obj;
354   vcdinfo_itemid_t   itemid;
355
356   if (!p_vcd) return false;
357
358   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
359              "current: %d" , p_vcd->play_item.num);
360
361   obj = p_vcd->vcd;
362
363   itemid.type = p_vcd->play_item.type;
364
365   if  (vcdplayer_pbc_is_on(p_vcd)) {
366
367     vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
368     
369     switch (p_vcd->pxd.descriptor_type) {
370     case PSD_TYPE_SELECTION_LIST:
371     case PSD_TYPE_EXT_SELECTION_LIST:
372       if (p_vcd->pxd.psd == NULL) return false;
373       vcdplayer_update_entry( p_input, 
374                               vcdinf_psd_get_next_offset(p_vcd->pxd.psd), 
375                               &itemid.num, "next");
376       itemid.type = VCDINFO_ITEM_TYPE_LID;
377       break;
378
379     case PSD_TYPE_PLAY_LIST: 
380       if (p_vcd->pxd.pld == NULL) return false;
381       vcdplayer_update_entry( p_input, 
382                               vcdinf_pld_get_next_offset(p_vcd->pxd.pld), 
383                               &itemid.num, "next");
384       itemid.type = VCDINFO_ITEM_TYPE_LID;
385       break;
386       
387     case PSD_TYPE_END_LIST:
388     case PSD_TYPE_COMMAND_LIST:
389       LOG_WARN( "There is no PBC 'next' selection here" );
390       return false;
391     }
392   } else {
393
394     /* PBC is not on. "Next" selection is play_item.num+1 if possible. */
395   
396     int max_entry = 0;
397
398     switch (p_vcd->play_item.type) {
399     case VCDINFO_ITEM_TYPE_ENTRY: 
400     case VCDINFO_ITEM_TYPE_SEGMENT: 
401     case VCDINFO_ITEM_TYPE_TRACK: 
402       
403       switch (p_vcd->play_item.type) {
404       case VCDINFO_ITEM_TYPE_ENTRY: 
405         max_entry = p_vcd->num_entries;
406         break;
407       case VCDINFO_ITEM_TYPE_SEGMENT: 
408         max_entry = p_vcd->num_segments;
409         break;
410       case VCDINFO_ITEM_TYPE_TRACK: 
411         max_entry = p_vcd->num_tracks;
412         break;
413       default: ; /* Handle exceptional cases below */
414       }
415       
416       if (p_vcd->play_item.num+1 < max_entry) {
417         itemid.num = p_vcd->play_item.num+1;
418       } else {
419         LOG_WARN( "At the end - non-PBC 'next' not possible here" );
420         return false;
421       }
422       
423       break;
424       
425     case VCDINFO_ITEM_TYPE_LID: 
426       {
427         /* Should have handled above. */
428         LOG_WARN( "Internal inconsistency - should not have gotten here." );
429         return false;
430       }
431     default: 
432       return false;
433     }
434   }
435
436   /** ??? p_vcd->update_title(); ***/
437   return VLC_SUCCESS == VCDPlay( p_input, itemid );
438
439 }
440
441 /*!
442   Play item assocated with the "prev" selection.
443
444   Return false if there was some problem.
445 */
446 bool
447 vcdplayer_play_prev( input_thread_t * p_input )
448 {
449   thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
450
451   vcdinfo_obj_t     *obj  = p_vcd->vcd;
452   vcdinfo_itemid_t   itemid;
453
454   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
455              "current: %d" , p_vcd->play_item.num);
456
457   itemid.type = p_vcd->play_item.type;
458
459   if  (vcdplayer_pbc_is_on(p_vcd)) {
460
461     vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
462     
463     switch (p_vcd->pxd.descriptor_type) {
464     case PSD_TYPE_SELECTION_LIST:
465     case PSD_TYPE_EXT_SELECTION_LIST:
466       if (p_vcd->pxd.psd == NULL) return false;
467       vcdplayer_update_entry( p_input, 
468                               vcdinf_psd_get_prev_offset(p_vcd->pxd.psd), 
469                               &itemid.num, "prev");
470       itemid.type = VCDINFO_ITEM_TYPE_LID;
471       break;
472
473     case PSD_TYPE_PLAY_LIST: 
474       if (p_vcd->pxd.pld == NULL) return false;
475       vcdplayer_update_entry( p_input, 
476                               vcdinf_pld_get_prev_offset(p_vcd->pxd.pld), 
477                               &itemid.num, "prev");
478       itemid.type = VCDINFO_ITEM_TYPE_LID;
479       break;
480       
481     case PSD_TYPE_END_LIST:
482     case PSD_TYPE_COMMAND_LIST:
483       LOG_WARN( "There is no PBC 'prev' selection here" );
484       return false;
485     }
486   } else {
487
488     /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */
489   
490     int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) 
491       ? 0 : 1;
492     
493     if (p_vcd->play_item.num > min_entry) {
494       itemid.num = p_vcd->play_item.num-1;
495     } else {
496       LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" );
497       return false;
498     }
499       
500   }
501
502   /** ??? p_vcd->update_title(); ***/
503   return VLC_SUCCESS == VCDPlay( p_input, itemid );
504
505 }
506
507 /*!
508   Play item assocated with the "return" selection.
509
510   Return false if there was some problem.
511 */
512 bool
513 vcdplayer_play_return( input_thread_t * p_input )
514 {
515   thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
516
517   vcdinfo_obj_t     *obj  = p_vcd->vcd;
518   vcdinfo_itemid_t   itemid;
519
520   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
521              "current: %d" , p_vcd->play_item.num);
522
523   itemid.type = p_vcd->play_item.type;
524
525   if  (vcdplayer_pbc_is_on(p_vcd)) {
526
527     vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
528     
529     switch (p_vcd->pxd.descriptor_type) {
530     case PSD_TYPE_SELECTION_LIST:
531     case PSD_TYPE_EXT_SELECTION_LIST:
532       if (p_vcd->pxd.psd == NULL) return false;
533       vcdplayer_update_entry( p_input, 
534                               vcdinf_psd_get_return_offset(p_vcd->pxd.psd), 
535                               &itemid.num, "return");
536       itemid.type = VCDINFO_ITEM_TYPE_LID;
537       break;
538
539     case PSD_TYPE_PLAY_LIST: 
540       if (p_vcd->pxd.pld == NULL) return false;
541       vcdplayer_update_entry( p_input, 
542                               vcdinf_pld_get_return_offset(p_vcd->pxd.pld), 
543                               &itemid.num, "return");
544       itemid.type = VCDINFO_ITEM_TYPE_LID;
545       break;
546       
547     case PSD_TYPE_END_LIST:
548     case PSD_TYPE_COMMAND_LIST:
549       LOG_WARN( "There is no PBC 'return' selection here" );
550       return false;
551     }
552   } else {
553
554     /* PBC is not on. "Return" selection is min_entry if possible. */
555   
556     p_vcd->play_item.num = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) 
557       ? 0 : 1;
558     
559   }
560
561   /** ??? p_vcd->update_title(); ***/
562   return VLC_SUCCESS == VCDPlay( p_input, itemid );
563
564 }