]> git.sesse.net Git - vlc/blob - modules/access/vcdx/vcdplayer.c
Slightly better handling of stills and toggling pause by using the
[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.6 2003/12/05 04:24:47 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 #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_t = vcdinfo_get_offset_t(p_vcd->vcd, ofs);
65     if (off_t != NULL) {
66       *entry = off_t->lid;
67       dbg_print(INPUT_DBG_PBC, "%s: LID %d\n", label, off_t->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     input_area_t *p_area;
90
91     dbg_print( INPUT_DBG_LSN, "new track %d, lsn %d", p_vcd->cur_track, 
92                p_vcd->p_sectors[p_vcd->cur_track+1] );
93     
94     if ( p_vcd->cur_track >= p_vcd->num_tracks - 1 )
95       return READ_END; /* EOF */
96         
97     p_vcd->play_item.num = p_vcd->cur_track++;
98     
99     vlc_mutex_lock( &p_input->stream.stream_lock );
100     p_area = p_input->stream.pp_areas[p_vcd->cur_track];
101     
102     p_area->i_part = 1;
103     VCDSetArea( p_input, p_area );
104     vlc_mutex_unlock( &p_input->stream.stream_lock );
105     return READ_BLOCK;
106     break;
107   }
108   case VCDINFO_ITEM_TYPE_SPAREID2:  
109     dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN), 
110                "SPAREID2" );
111     /* FIXME */
112     p_input->stream.b_seekable = 0;
113     if (p_vcd->in_still)
114     {
115       return READ_STILL_FRAME ;
116     }
117     return READ_END;
118   case VCDINFO_ITEM_TYPE_NOTFOUND:  
119     LOG_ERR ("NOTFOUND outside PBC -- not supposed to happen");
120     return READ_ERROR;
121   case VCDINFO_ITEM_TYPE_LID:  
122     LOG_ERR ("LID outside PBC -- not supposed to happen");
123     return READ_ERROR;
124   case VCDINFO_ITEM_TYPE_SEGMENT:
125       /* Hack: Just go back and do still again */
126     /* FIXME */
127     p_input->stream.b_seekable = 0;
128     if (p_vcd->in_still) 
129     {
130       dbg_print( (INPUT_DBG_STILL|INPUT_DBG_LSN), 
131                  "End of Segment - looping" );
132       return READ_STILL_FRAME;
133     }
134     return READ_END;
135   }
136   return READ_BLOCK;
137 }
138
139 /* FIXME: Will do whatever the right thing is later. */
140 #define SLEEP_1_SEC_AND_HANDLE_EVENTS sleep(1)
141
142 /* Handles PBC navigation when reaching the end of a play item. */
143 vcdplayer_read_status_t
144 vcdplayer_pbc_nav ( input_thread_t * p_input )
145 {
146   thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
147
148   /* We are in playback control. */
149   vcdinfo_itemid_t itemid;
150
151   if (0 != p_vcd->in_still && p_vcd->in_still != -5) {
152       SLEEP_1_SEC_AND_HANDLE_EVENTS;
153       if (p_vcd->in_still > 0) p_vcd->in_still--;
154       return READ_STILL_FRAME;
155   }
156
157   /* The end of an entry is really the end of the associated 
158      sequence (or track). */
159   
160   if ( (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) && 
161        (p_vcd->cur_lsn < p_vcd->end_lsn) ) {
162     /* Set up to just continue to the next entry */
163     p_vcd->play_item.num++;
164     dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), 
165                "continuing into next entry: %u", p_vcd->play_item.num);
166     VCDPlay( p_input, p_vcd->play_item );
167     /* p_vcd->update_title(); */
168     return READ_BLOCK;
169   }
170   
171   switch (p_vcd->pxd.descriptor_type) {
172   case PSD_TYPE_END_LIST:
173     return READ_END;
174     break;
175   case PSD_TYPE_PLAY_LIST: {
176     int wait_time = vcdinf_get_wait_time(p_vcd->pxd.pld);
177     
178     dbg_print(INPUT_DBG_PBC, "playlist wait_time: %d", wait_time);
179     
180     if (vcdplayer_inc_play_item(p_input))
181       return READ_BLOCK;
182
183     /* Handle any wait time given. */
184     if (-5 == p_vcd->in_still) {
185       if (wait_time != 0) {
186         /* FIXME */
187         p_vcd->in_still = wait_time - 1;
188         p_vcd->p_intf->p_sys->m_still_time = (wait_time - 1) * 1000000;
189         return READ_STILL_FRAME;
190       } else {
191         p_vcd->p_intf->p_sys->m_still_time = 0;
192         p_vcd->p_intf->p_sys->b_inf_still  = 1;
193       }
194
195     }
196     vcdplayer_update_entry( p_input, 
197                             vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
198                             &itemid.num, "next" );
199     itemid.type = VCDINFO_ITEM_TYPE_LID;
200     VCDPlay( p_input, itemid );
201     break;
202   }
203   case PSD_TYPE_SELECTION_LIST:     /* Selection List (+Ext. for SVCD) */
204   case PSD_TYPE_EXT_SELECTION_LIST: /* Extended Selection List (VCD2.0) */
205     {
206       int wait_time         = vcdinf_get_timeout_time(p_vcd->pxd.psd);
207       uint16_t timeout_offs = vcdinf_get_timeout_offset(p_vcd->pxd.psd);
208       uint16_t max_loop     = vcdinf_get_loop_count(p_vcd->pxd.psd);
209       vcdinfo_offset_t *offset_timeout_LID = 
210         vcdinfo_get_offset_t(p_vcd->vcd, timeout_offs);
211       
212       dbg_print(INPUT_DBG_PBC, "wait_time: %d, looped: %d, max_loop %d", 
213                 wait_time, p_vcd->loop_count, max_loop);
214       
215       /* Handle any wait time given */
216       if (-5 == p_vcd->in_still) {
217         if (wait_time != 0) {
218           /* FIXME */
219           p_vcd->in_still = wait_time - 1;
220           p_vcd->p_intf->p_sys->m_still_time = (wait_time - 1) * 1000000;
221           return READ_STILL_FRAME;
222         } else {
223           p_vcd->p_intf->p_sys->m_still_time = 0;
224           p_vcd->p_intf->p_sys->b_inf_still  = 1;
225         }
226         return READ_STILL_FRAME;
227       }
228       
229       /* Handle any looping given. */
230       if ( max_loop == 0 || p_vcd->loop_count < max_loop ) {
231         p_vcd->loop_count++;
232         if (p_vcd->loop_count == 0x7f) p_vcd->loop_count = 0;
233         VCDSeek( p_input, 0 );
234         /* if (p_vcd->in_still) p_vcd->force_redisplay();*/
235         return READ_BLOCK;
236       }
237       
238       /* Looping finished and wait finished. Move to timeout
239          entry or next entry, or handle still. */
240       
241       if (NULL != offset_timeout_LID) {
242         /* Handle timeout_LID */
243         itemid.num  = offset_timeout_LID->lid;
244         itemid.type = VCDINFO_ITEM_TYPE_LID;
245         dbg_print(INPUT_DBG_PBC, "timeout to: %d", itemid.num);
246         VCDPlay( p_input, itemid );
247         return READ_BLOCK;
248       } else {
249         int num_selections = vcdinf_get_num_selections(p_vcd->pxd.psd);
250         if (num_selections > 0) {
251           /* Pick a random selection. */
252           unsigned int bsn=vcdinf_get_bsn(p_vcd->pxd.psd);
253           int rand_selection=bsn +
254             (int) ((num_selections+0.0)*rand()/(RAND_MAX+1.0));
255           lid_t rand_lid=vcdinfo_selection_get_lid (p_vcd->vcd, 
256                                                     p_vcd->cur_lid, 
257                                                     rand_selection);
258           itemid.num = rand_lid;
259           itemid.type = VCDINFO_ITEM_TYPE_LID;
260           dbg_print(INPUT_DBG_PBC, "random selection %d, lid: %d", 
261                     rand_selection - bsn, rand_lid);
262           VCDPlay( p_input, itemid );
263           return READ_BLOCK;
264         } else if (p_vcd->in_still) {
265           /* Hack: Just go back and do still again */
266           SLEEP_1_SEC_AND_HANDLE_EVENTS ;
267           return READ_STILL_FRAME;
268         }
269       }
270       break;
271     }
272   case VCDINFO_ITEM_TYPE_NOTFOUND:  
273     LOG_ERR( "NOTFOUND in PBC -- not supposed to happen" );
274     break;
275   case VCDINFO_ITEM_TYPE_SPAREID2:  
276     LOG_ERR( "SPAREID2 in PBC -- not supposed to happen" );
277     break;
278   case VCDINFO_ITEM_TYPE_LID:  
279     LOG_ERR( "LID in PBC -- not supposed to happen" );
280     break;
281     
282   default:
283     ;
284   }
285   /* FIXME: Should handle autowait ...  */
286
287   return READ_ERROR;
288 }
289
290 /*!
291   Get the next play-item in the list given in the LIDs. Note play-item
292   here refers to list of play-items for a single LID It shouldn't be
293   confused with a user's list of favorite things to play or the 
294   "next" field of a LID which moves us to a different LID.
295  */
296 bool
297 vcdplayer_inc_play_item( input_thread_t *p_input )
298 {
299   thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
300
301   int noi;
302
303   dbg_print(INPUT_DBG_CALL, "called pli: %d", p_vcd->pdi);
304
305   if ( NULL == p_vcd || NULL == p_vcd->pxd.pld  ) return false;
306
307   noi = vcdinf_pld_get_noi(p_vcd->pxd.pld);
308   
309   if ( noi <= 0 ) return false;
310   
311   /* Handle delays like autowait or wait here? */
312
313   p_vcd->pdi++;
314
315   if ( p_vcd->pdi < 0 || p_vcd->pdi >= noi ) return false;
316
317   else {
318     uint16_t trans_itemid_num=vcdinf_pld_get_play_item(p_vcd->pxd.pld, 
319                                                        p_vcd->pdi);
320     vcdinfo_itemid_t trans_itemid;
321
322     if (VCDINFO_INVALID_ITEMID == trans_itemid_num) return false;
323     
324     vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
325     dbg_print(INPUT_DBG_PBC, "  play-item[%d]: %s",
326               p_vcd->pdi, vcdinfo_pin2str (trans_itemid_num));
327     return VLC_SUCCESS == VCDPlay( p_input, trans_itemid );
328   }
329 }
330
331 /*!
332   Play item assocated with the "default" selection.
333
334   Return false if there was some problem.
335 */
336 bool
337 vcdplayer_play_default( input_thread_t * p_input )
338 {
339   thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
340
341   vcdinfo_itemid_t   itemid;
342
343   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
344              "current: %d" , p_vcd->play_item.num);
345
346   itemid.type = p_vcd->play_item.type;
347
348   if  (vcdplayer_pbc_is_on(p_vcd)) {
349
350     lid_t lid=vcdinfo_get_multi_default_lid(p_vcd->vcd, p_vcd->cur_lid,
351                                             itemid.num);
352
353     if (VCDINFO_INVALID_LID != lid) {
354       itemid.num  = lid;
355       itemid.type = VCDINFO_ITEM_TYPE_LID;
356       dbg_print(INPUT_DBG_PBC, "DEFAULT to %d\n", itemid.num);
357     } else {
358       dbg_print(INPUT_DBG_PBC, "no DEFAULT for LID %d\n", p_vcd->cur_lid);
359     }
360     
361
362   } else {
363
364     /* PBC is not on. "default" selection beginning of current 
365        selection . */
366   
367     p_vcd->play_item.num = p_vcd->play_item.num;
368     
369   }
370
371   /** ??? p_vcd->update_title(); ***/
372   return VLC_SUCCESS == VCDPlay( p_input, itemid );
373
374 }
375
376 /*!
377   Play item assocated with the "next" selection.
378
379   Return false if there was some problem.
380 */
381 bool
382 vcdplayer_play_next( input_thread_t * p_input )
383 {
384   thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
385
386   vcdinfo_obj_t     *obj  = p_vcd->vcd;
387   vcdinfo_itemid_t   itemid;
388
389   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
390              "current: %d" , p_vcd->play_item.num);
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 }