]> git.sesse.net Git - vlc/blob - modules/access/vcdx/vcdplayer.c
Minor debug output change.
[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.5 2003/11/24 03:30:36 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: LID %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       itemid.type = VCDINFO_ITEM_TYPE_LID;
393       break;
394
395     case PSD_TYPE_PLAY_LIST: 
396       if (p_vcd->pxd.pld == NULL) return false;
397       vcdplayer_update_entry( p_input, 
398                               vcdinf_pld_get_next_offset(p_vcd->pxd.pld), 
399                               &itemid.num, "next");
400       itemid.type = VCDINFO_ITEM_TYPE_LID;
401       break;
402       
403     case PSD_TYPE_END_LIST:
404     case PSD_TYPE_COMMAND_LIST:
405       LOG_WARN( "There is no PBC 'next' selection here" );
406       return false;
407     }
408   } else {
409
410     /* PBC is not on. "Next" selection is play_item.num+1 if possible. */
411   
412     int max_entry = 0;
413
414     switch (p_vcd->play_item.type) {
415     case VCDINFO_ITEM_TYPE_ENTRY: 
416     case VCDINFO_ITEM_TYPE_SEGMENT: 
417     case VCDINFO_ITEM_TYPE_TRACK: 
418       
419       switch (p_vcd->play_item.type) {
420       case VCDINFO_ITEM_TYPE_ENTRY: 
421         max_entry = p_vcd->num_entries;
422         break;
423       case VCDINFO_ITEM_TYPE_SEGMENT: 
424         max_entry = p_vcd->num_segments;
425         break;
426       case VCDINFO_ITEM_TYPE_TRACK: 
427         max_entry = p_vcd->num_tracks;
428         break;
429       default: ; /* Handle exceptional cases below */
430       }
431       
432       if (p_vcd->play_item.num+1 < max_entry) {
433         itemid.num = p_vcd->play_item.num+1;
434       } else {
435         LOG_WARN( "At the end - non-PBC 'next' not possible here" );
436         return false;
437       }
438       
439       break;
440       
441     case VCDINFO_ITEM_TYPE_LID: 
442       {
443         /* Should have handled above. */
444         LOG_WARN( "Internal inconsistency - should not have gotten here." );
445         return false;
446       }
447     default: 
448       return false;
449     }
450   }
451
452   /** ??? p_vcd->update_title(); ***/
453   return VLC_SUCCESS == VCDPlay( p_input, itemid );
454
455 }
456
457 /*!
458   Play item assocated with the "prev" selection.
459
460   Return false if there was some problem.
461 */
462 bool
463 vcdplayer_play_prev( input_thread_t * p_input )
464 {
465   thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
466
467   vcdinfo_obj_t     *obj  = p_vcd->vcd;
468   vcdinfo_itemid_t   itemid;
469
470   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
471              "current: %d" , p_vcd->play_item.num);
472
473   itemid.type = p_vcd->play_item.type;
474
475   if  (vcdplayer_pbc_is_on(p_vcd)) {
476
477     vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
478     
479     switch (p_vcd->pxd.descriptor_type) {
480     case PSD_TYPE_SELECTION_LIST:
481     case PSD_TYPE_EXT_SELECTION_LIST:
482       if (p_vcd->pxd.psd == NULL) return false;
483       vcdplayer_update_entry( p_input, 
484                               vcdinf_psd_get_prev_offset(p_vcd->pxd.psd), 
485                               &itemid.num, "prev");
486       itemid.type = VCDINFO_ITEM_TYPE_LID;
487       break;
488
489     case PSD_TYPE_PLAY_LIST: 
490       if (p_vcd->pxd.pld == NULL) return false;
491       vcdplayer_update_entry( p_input, 
492                               vcdinf_pld_get_prev_offset(p_vcd->pxd.pld), 
493                               &itemid.num, "prev");
494       itemid.type = VCDINFO_ITEM_TYPE_LID;
495       break;
496       
497     case PSD_TYPE_END_LIST:
498     case PSD_TYPE_COMMAND_LIST:
499       LOG_WARN( "There is no PBC 'prev' selection here" );
500       return false;
501     }
502   } else {
503
504     /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */
505   
506     int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) 
507       ? 0 : 1;
508     
509     if (p_vcd->play_item.num > min_entry) {
510       itemid.num = p_vcd->play_item.num-1;
511     } else {
512       LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" );
513       return false;
514     }
515       
516   }
517
518   /** ??? p_vcd->update_title(); ***/
519   return VLC_SUCCESS == VCDPlay( p_input, itemid );
520
521 }
522
523 /*!
524   Play item assocated with the "return" selection.
525
526   Return false if there was some problem.
527 */
528 bool
529 vcdplayer_play_return( input_thread_t * p_input )
530 {
531   thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
532
533   vcdinfo_obj_t     *obj  = p_vcd->vcd;
534   vcdinfo_itemid_t   itemid;
535
536   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
537              "current: %d" , p_vcd->play_item.num);
538
539   itemid.type = p_vcd->play_item.type;
540
541   if  (vcdplayer_pbc_is_on(p_vcd)) {
542
543     vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
544     
545     switch (p_vcd->pxd.descriptor_type) {
546     case PSD_TYPE_SELECTION_LIST:
547     case PSD_TYPE_EXT_SELECTION_LIST:
548       if (p_vcd->pxd.psd == NULL) return false;
549       vcdplayer_update_entry( p_input, 
550                               vcdinf_psd_get_return_offset(p_vcd->pxd.psd), 
551                               &itemid.num, "return");
552       itemid.type = VCDINFO_ITEM_TYPE_LID;
553       break;
554
555     case PSD_TYPE_PLAY_LIST: 
556       if (p_vcd->pxd.pld == NULL) return false;
557       vcdplayer_update_entry( p_input, 
558                               vcdinf_pld_get_return_offset(p_vcd->pxd.pld), 
559                               &itemid.num, "return");
560       itemid.type = VCDINFO_ITEM_TYPE_LID;
561       break;
562       
563     case PSD_TYPE_END_LIST:
564     case PSD_TYPE_COMMAND_LIST:
565       LOG_WARN( "There is no PBC 'return' selection here" );
566       return false;
567     }
568   } else {
569
570     /* PBC is not on. "Return" selection is min_entry if possible. */
571   
572     p_vcd->play_item.num = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) 
573       ? 0 : 1;
574     
575   }
576
577   /** ??? p_vcd->update_title(); ***/
578   return VLC_SUCCESS == VCDPlay( p_input, itemid );
579
580 }