]> git.sesse.net Git - vlc/blob - modules/access/vcdx/vcdplayer.c
Guard against dereferencing a NULL pointer.
[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.8 2003/12/11 05:31:37 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 /* Handles PBC navigation when reaching the end of a play item. */
140 vcdplayer_read_status_t
141 vcdplayer_pbc_nav ( input_thread_t * p_input )
142 {
143   thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
144
145   /* We are in playback control. */
146   vcdinfo_itemid_t itemid;
147
148   /* The end of an entry is really the end of the associated 
149      sequence (or track). */
150   
151   if ( (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) && 
152        (p_vcd->cur_lsn < p_vcd->end_lsn) ) {
153     /* Set up to just continue to the next entry */
154     p_vcd->play_item.num++;
155     dbg_print( (INPUT_DBG_LSN|INPUT_DBG_PBC), 
156                "continuing into next entry: %u", p_vcd->play_item.num);
157     VCDPlay( p_input, p_vcd->play_item );
158     /* p_vcd->update_title(); */
159     return READ_BLOCK;
160   }
161   
162   switch (p_vcd->pxd.descriptor_type) {
163   case PSD_TYPE_END_LIST:
164     return READ_END;
165     break;
166   case PSD_TYPE_PLAY_LIST: {
167     int wait_time = vcdinf_get_wait_time(p_vcd->pxd.pld);
168     
169     dbg_print(INPUT_DBG_PBC, "playlist wait_time: %d", wait_time);
170     
171     if (vcdplayer_inc_play_item(p_input))
172       return READ_BLOCK;
173
174     /* Handle any wait time given. */
175     if (p_vcd->in_still) {
176       vcdIntfStillTime( p_vcd->p_intf, wait_time );
177       return READ_STILL_FRAME;
178     }
179
180     vcdplayer_update_entry( p_input, 
181                             vcdinf_pld_get_next_offset(p_vcd->pxd.pld),
182                             &itemid.num, "next" );
183     itemid.type = VCDINFO_ITEM_TYPE_LID;
184     VCDPlay( p_input, itemid );
185     break;
186   }
187   case PSD_TYPE_SELECTION_LIST:     /* Selection List (+Ext. for SVCD) */
188   case PSD_TYPE_EXT_SELECTION_LIST: /* Extended Selection List (VCD2.0) */
189     {
190       int wait_time         = vcdinf_get_timeout_time(p_vcd->pxd.psd);
191       uint16_t timeout_offs = vcdinf_get_timeout_offset(p_vcd->pxd.psd);
192       uint16_t max_loop     = vcdinf_get_loop_count(p_vcd->pxd.psd);
193       vcdinfo_offset_t *offset_timeout_LID = 
194         vcdinfo_get_offset_t(p_vcd->vcd, timeout_offs);
195       
196       dbg_print(INPUT_DBG_PBC, "wait_time: %d, looped: %d, max_loop %d", 
197                 wait_time, p_vcd->loop_count, max_loop);
198       
199       /* Handle any wait time given */
200       if (p_vcd->in_still) {
201         vcdIntfStillTime( p_vcd->p_intf, wait_time );
202         return READ_STILL_FRAME;
203       } 
204       
205       /* Handle any looping given. */
206       if ( max_loop == 0 || p_vcd->loop_count < max_loop ) {
207         p_vcd->loop_count++;
208         if (p_vcd->loop_count == 0x7f) p_vcd->loop_count = 0;
209         VCDSeek( p_input, 0 );
210         /* if (p_vcd->in_still) p_vcd->force_redisplay();*/
211         return READ_BLOCK;
212       }
213       
214       /* Looping finished and wait finished. Move to timeout
215          entry or next entry, or handle still. */
216       
217       if (NULL != offset_timeout_LID) {
218         /* Handle timeout_LID */
219         itemid.num  = offset_timeout_LID->lid;
220         itemid.type = VCDINFO_ITEM_TYPE_LID;
221         dbg_print(INPUT_DBG_PBC, "timeout to: %d", itemid.num);
222         VCDPlay( p_input, itemid );
223         return READ_BLOCK;
224       } else {
225         int num_selections = vcdinf_get_num_selections(p_vcd->pxd.psd);
226         if (num_selections > 0) {
227           /* Pick a random selection. */
228           unsigned int bsn=vcdinf_get_bsn(p_vcd->pxd.psd);
229           int rand_selection=bsn +
230             (int) ((num_selections+0.0)*rand()/(RAND_MAX+1.0));
231           lid_t rand_lid=vcdinfo_selection_get_lid (p_vcd->vcd, 
232                                                     p_vcd->cur_lid, 
233                                                     rand_selection);
234           itemid.num = rand_lid;
235           itemid.type = VCDINFO_ITEM_TYPE_LID;
236           dbg_print(INPUT_DBG_PBC, "random selection %d, lid: %d", 
237                     rand_selection - bsn, rand_lid);
238           VCDPlay( p_input, itemid );
239           return READ_BLOCK;
240         } else if (p_vcd->in_still) {
241           /* Hack: Just go back and do still again */
242           sleep(1);
243           return READ_STILL_FRAME;
244         }
245       }
246       break;
247     }
248   case VCDINFO_ITEM_TYPE_NOTFOUND:  
249     LOG_ERR( "NOTFOUND in PBC -- not supposed to happen" );
250     break;
251   case VCDINFO_ITEM_TYPE_SPAREID2:  
252     LOG_ERR( "SPAREID2 in PBC -- not supposed to happen" );
253     break;
254   case VCDINFO_ITEM_TYPE_LID:  
255     LOG_ERR( "LID in PBC -- not supposed to happen" );
256     break;
257     
258   default:
259     ;
260   }
261   /* FIXME: Should handle autowait ...  */
262
263   return READ_ERROR;
264 }
265
266 /*!
267   Get the next play-item in the list given in the LIDs. Note play-item
268   here refers to list of play-items for a single LID It shouldn't be
269   confused with a user's list of favorite things to play or the 
270   "next" field of a LID which moves us to a different LID.
271  */
272 bool
273 vcdplayer_inc_play_item( input_thread_t *p_input )
274 {
275   thread_vcd_data_t *     p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
276
277   int noi;
278
279   dbg_print(INPUT_DBG_CALL, "called pli: %d", p_vcd->pdi);
280
281   if ( NULL == p_vcd || NULL == p_vcd->pxd.pld  ) return false;
282
283   noi = vcdinf_pld_get_noi(p_vcd->pxd.pld);
284   
285   if ( noi <= 0 ) return false;
286   
287   /* Handle delays like autowait or wait here? */
288
289   p_vcd->pdi++;
290
291   if ( p_vcd->pdi < 0 || p_vcd->pdi >= noi ) return false;
292
293   else {
294     uint16_t trans_itemid_num=vcdinf_pld_get_play_item(p_vcd->pxd.pld, 
295                                                        p_vcd->pdi);
296     vcdinfo_itemid_t trans_itemid;
297
298     if (VCDINFO_INVALID_ITEMID == trans_itemid_num) return false;
299     
300     vcdinfo_classify_itemid(trans_itemid_num, &trans_itemid);
301     dbg_print(INPUT_DBG_PBC, "  play-item[%d]: %s",
302               p_vcd->pdi, vcdinfo_pin2str (trans_itemid_num));
303     return VLC_SUCCESS == VCDPlay( p_input, trans_itemid );
304   }
305 }
306
307 /*!
308   Play item assocated with the "default" selection.
309
310   Return false if there was some problem.
311 */
312 bool
313 vcdplayer_play_default( input_thread_t * p_input )
314 {
315   thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
316
317   vcdinfo_itemid_t   itemid;
318
319   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
320              "current: %d" , p_vcd->play_item.num);
321
322   itemid.type = p_vcd->play_item.type;
323
324   if  (vcdplayer_pbc_is_on(p_vcd)) {
325
326     lid_t lid=vcdinfo_get_multi_default_lid(p_vcd->vcd, p_vcd->cur_lid,
327                                             itemid.num);
328
329     if (VCDINFO_INVALID_LID != lid) {
330       itemid.num  = lid;
331       itemid.type = VCDINFO_ITEM_TYPE_LID;
332       dbg_print(INPUT_DBG_PBC, "DEFAULT to %d\n", itemid.num);
333     } else {
334       dbg_print(INPUT_DBG_PBC, "no DEFAULT for LID %d\n", p_vcd->cur_lid);
335     }
336     
337
338   } else {
339
340     /* PBC is not on. "default" selection beginning of current 
341        selection . */
342   
343     p_vcd->play_item.num = p_vcd->play_item.num;
344     
345   }
346
347   /** ??? p_vcd->update_title(); ***/
348   return VLC_SUCCESS == VCDPlay( p_input, itemid );
349
350 }
351
352 /*!
353   Play item assocated with the "next" selection.
354
355   Return false if there was some problem.
356 */
357 bool
358 vcdplayer_play_next( input_thread_t * p_input )
359 {
360   thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
361
362   vcdinfo_obj_t     *obj;
363   vcdinfo_itemid_t   itemid;
364
365   if (!p_vcd) return false;
366
367   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
368              "current: %d" , p_vcd->play_item.num);
369
370   obj = p_vcd->vcd;
371
372   itemid.type = p_vcd->play_item.type;
373
374   if  (vcdplayer_pbc_is_on(p_vcd)) {
375
376     vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
377     
378     switch (p_vcd->pxd.descriptor_type) {
379     case PSD_TYPE_SELECTION_LIST:
380     case PSD_TYPE_EXT_SELECTION_LIST:
381       if (p_vcd->pxd.psd == NULL) return false;
382       vcdplayer_update_entry( p_input, 
383                               vcdinf_psd_get_next_offset(p_vcd->pxd.psd), 
384                               &itemid.num, "next");
385       itemid.type = VCDINFO_ITEM_TYPE_LID;
386       break;
387
388     case PSD_TYPE_PLAY_LIST: 
389       if (p_vcd->pxd.pld == NULL) return false;
390       vcdplayer_update_entry( p_input, 
391                               vcdinf_pld_get_next_offset(p_vcd->pxd.pld), 
392                               &itemid.num, "next");
393       itemid.type = VCDINFO_ITEM_TYPE_LID;
394       break;
395       
396     case PSD_TYPE_END_LIST:
397     case PSD_TYPE_COMMAND_LIST:
398       LOG_WARN( "There is no PBC 'next' selection here" );
399       return false;
400     }
401   } else {
402
403     /* PBC is not on. "Next" selection is play_item.num+1 if possible. */
404   
405     int max_entry = 0;
406
407     switch (p_vcd->play_item.type) {
408     case VCDINFO_ITEM_TYPE_ENTRY: 
409     case VCDINFO_ITEM_TYPE_SEGMENT: 
410     case VCDINFO_ITEM_TYPE_TRACK: 
411       
412       switch (p_vcd->play_item.type) {
413       case VCDINFO_ITEM_TYPE_ENTRY: 
414         max_entry = p_vcd->num_entries;
415         break;
416       case VCDINFO_ITEM_TYPE_SEGMENT: 
417         max_entry = p_vcd->num_segments;
418         break;
419       case VCDINFO_ITEM_TYPE_TRACK: 
420         max_entry = p_vcd->num_tracks;
421         break;
422       default: ; /* Handle exceptional cases below */
423       }
424       
425       if (p_vcd->play_item.num+1 < max_entry) {
426         itemid.num = p_vcd->play_item.num+1;
427       } else {
428         LOG_WARN( "At the end - non-PBC 'next' not possible here" );
429         return false;
430       }
431       
432       break;
433       
434     case VCDINFO_ITEM_TYPE_LID: 
435       {
436         /* Should have handled above. */
437         LOG_WARN( "Internal inconsistency - should not have gotten here." );
438         return false;
439       }
440     default: 
441       return false;
442     }
443   }
444
445   /** ??? p_vcd->update_title(); ***/
446   return VLC_SUCCESS == VCDPlay( p_input, itemid );
447
448 }
449
450 /*!
451   Play item assocated with the "prev" selection.
452
453   Return false if there was some problem.
454 */
455 bool
456 vcdplayer_play_prev( input_thread_t * p_input )
457 {
458   thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
459
460   vcdinfo_obj_t     *obj  = p_vcd->vcd;
461   vcdinfo_itemid_t   itemid;
462
463   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
464              "current: %d" , p_vcd->play_item.num);
465
466   itemid.type = p_vcd->play_item.type;
467
468   if  (vcdplayer_pbc_is_on(p_vcd)) {
469
470     vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
471     
472     switch (p_vcd->pxd.descriptor_type) {
473     case PSD_TYPE_SELECTION_LIST:
474     case PSD_TYPE_EXT_SELECTION_LIST:
475       if (p_vcd->pxd.psd == NULL) return false;
476       vcdplayer_update_entry( p_input, 
477                               vcdinf_psd_get_prev_offset(p_vcd->pxd.psd), 
478                               &itemid.num, "prev");
479       itemid.type = VCDINFO_ITEM_TYPE_LID;
480       break;
481
482     case PSD_TYPE_PLAY_LIST: 
483       if (p_vcd->pxd.pld == NULL) return false;
484       vcdplayer_update_entry( p_input, 
485                               vcdinf_pld_get_prev_offset(p_vcd->pxd.pld), 
486                               &itemid.num, "prev");
487       itemid.type = VCDINFO_ITEM_TYPE_LID;
488       break;
489       
490     case PSD_TYPE_END_LIST:
491     case PSD_TYPE_COMMAND_LIST:
492       LOG_WARN( "There is no PBC 'prev' selection here" );
493       return false;
494     }
495   } else {
496
497     /* PBC is not on. "Prev" selection is play_item.num-1 if possible. */
498   
499     int min_entry = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) 
500       ? 0 : 1;
501     
502     if (p_vcd->play_item.num > min_entry) {
503       itemid.num = p_vcd->play_item.num-1;
504     } else {
505       LOG_WARN( "At the beginning - non-PBC 'prev' not possible here" );
506       return false;
507     }
508       
509   }
510
511   /** ??? p_vcd->update_title(); ***/
512   return VLC_SUCCESS == VCDPlay( p_input, itemid );
513
514 }
515
516 /*!
517   Play item assocated with the "return" selection.
518
519   Return false if there was some problem.
520 */
521 bool
522 vcdplayer_play_return( input_thread_t * p_input )
523 {
524   thread_vcd_data_t *p_vcd= (thread_vcd_data_t *)p_input->p_access_data;
525
526   vcdinfo_obj_t     *obj  = p_vcd->vcd;
527   vcdinfo_itemid_t   itemid;
528
529   dbg_print( (INPUT_DBG_CALL|INPUT_DBG_PBC), 
530              "current: %d" , p_vcd->play_item.num);
531
532   itemid.type = p_vcd->play_item.type;
533
534   if  (vcdplayer_pbc_is_on(p_vcd)) {
535
536     vcdinfo_lid_get_pxd(obj, &(p_vcd->pxd), p_vcd->cur_lid);
537     
538     switch (p_vcd->pxd.descriptor_type) {
539     case PSD_TYPE_SELECTION_LIST:
540     case PSD_TYPE_EXT_SELECTION_LIST:
541       if (p_vcd->pxd.psd == NULL) return false;
542       vcdplayer_update_entry( p_input, 
543                               vcdinf_psd_get_return_offset(p_vcd->pxd.psd), 
544                               &itemid.num, "return");
545       itemid.type = VCDINFO_ITEM_TYPE_LID;
546       break;
547
548     case PSD_TYPE_PLAY_LIST: 
549       if (p_vcd->pxd.pld == NULL) return false;
550       vcdplayer_update_entry( p_input, 
551                               vcdinf_pld_get_return_offset(p_vcd->pxd.pld), 
552                               &itemid.num, "return");
553       itemid.type = VCDINFO_ITEM_TYPE_LID;
554       break;
555       
556     case PSD_TYPE_END_LIST:
557     case PSD_TYPE_COMMAND_LIST:
558       LOG_WARN( "There is no PBC 'return' selection here" );
559       return false;
560     }
561   } else {
562
563     /* PBC is not on. "Return" selection is min_entry if possible. */
564   
565     p_vcd->play_item.num = (VCDINFO_ITEM_TYPE_ENTRY == p_vcd->play_item.type) 
566       ? 0 : 1;
567     
568   }
569
570   /** ??? p_vcd->update_title(); ***/
571   return VLC_SUCCESS == VCDPlay( p_input, itemid );
572
573 }