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