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