]> git.sesse.net Git - vlc/blob - modules/access/cdda/cdda.c
Move libcdio CD-DA plugin into its own directory before the big split up.
[vlc] / modules / access / cdda / cdda.c
1 /*****************************************************************************
2  * cddax.c : CD digital audio input module for vlc using libcdio
3  *****************************************************************************
4  * Copyright (C) 2000,2003 VideoLAN
5  * $Id: cdda.c,v 1.1 2003/11/26 01:32:52 rocky Exp $
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *          Gildas Bazin <gbazin@netcourrier.com>
9  *          Rocky Bernstein <rocky@panix.com> 
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <stdio.h>
30 #include <stdlib.h>
31
32 #include <vlc/vlc.h>
33 #include <vlc/intf.h>
34 #include <vlc/input.h>
35 #include <sys/types.h>
36 #include <cdio/cdio.h>
37 #include <cdio/cd_types.h>
38
39 #include "codecs.h"
40 #include "vlc_keys.h"
41
42 #ifdef HAVE_UNISTD_H
43 #   include <unistd.h>
44 #endif
45
46 #include <string.h>
47
48 #include "../vcdx/cdrom.h"
49
50 /* how many blocks E_(Open) will read in each loop */
51 #define CDDA_BLOCKS_ONCE 1
52 #define CDDA_DATA_ONCE   (CDDA_BLOCKS_ONCE * CDIO_CD_FRAMESIZE_RAW)
53
54 /*****************************************************************************
55  * cdda_data_t: CD audio information
56  *****************************************************************************/
57 typedef struct cdda_data_s
58 {
59     cddev_t     *p_cddev;                           /* CD device descriptor */
60     int         i_nb_tracks;                        /* Nb of tracks (titles) */
61     int         i_track;                                    /* Current track */
62     lsn_t       i_sector;                                  /* Current Sector */
63     lsn_t *     p_sectors;                                  /* Track sectors */
64     vlc_bool_t  b_end_of_track;           /* If the end of track was reached */
65     int         i_debug;                  /* Debugging mask */
66     intf_thread_t *p_intf;
67
68 } cdda_data_t;
69
70 /*****************************************************************************
71  * Debugging 
72  *****************************************************************************/
73 #define INPUT_DBG_MRL         1 
74 #define INPUT_DBG_EVENT       2 /* Trace keyboard events */
75 #define INPUT_DBG_EXT         4 /* Calls from external routines */
76 #define INPUT_DBG_CALL        8 /* all calls */
77 #define INPUT_DBG_LSN        16 /* LSN changes */
78 #define INPUT_DBG_CDIO       32 /* Debugging from CDIO */
79 #define INPUT_DBG_SEEK       64 /* Seeks to set location */
80
81 #define DEBUG_TEXT N_("set debug mask for additional debugging.")
82 #define DEBUG_LONGTEXT N_( \
83     "This integer when viewed in binary is a debugging mask\n" \
84     "MRL             1\n" \
85     "events          2\n" \
86     "external call   4\n" \
87     "all calls       8\n" \
88     "LSN      (10)  16\n" \
89     "libcdio  (20)  32\n" \
90     "seeks    (40)  64\n" )
91
92 #define DEV_TEXT N_("CD-ROM device name")
93 #define DEV_LONGTEXT N_( \
94     "Specify the name of the CD-ROM device that will be used by default. " \
95     "If you don't specify anything, we'll scan for a suitable CD-ROM device.")
96
97 #define INPUT_DEBUG 1
98 #if INPUT_DEBUG
99 #define dbg_print(mask, s, args...) \
100    if (p_cdda->i_debug & mask) \
101      msg_Dbg(p_input, "%s: "s, __func__ , ##args)
102 #else
103 #define dbg_print(mask, s, args...) 
104 #endif
105
106 /*****************************************************************************
107  * intf_sys_t: description and status of interface
108  *****************************************************************************/
109 struct intf_sys_t
110 {
111     input_thread_t    * p_input;
112     cdda_data_t       * p_cdda;
113     vlc_bool_t          b_click, b_move, b_key_pressed;
114 };
115
116 /* FIXME: This variable is a hack. Would be nice to eliminate. */
117 static input_thread_t *p_cdda_input = NULL;
118
119 /*****************************************************************************
120  * Local prototypes
121  *****************************************************************************/
122 static int  E_(Open)         ( vlc_object_t * );
123 static void E_(Close)        ( vlc_object_t * );
124 static int  E_(OpenIntf)     ( vlc_object_t * );
125 static void E_(CloseIntf)    ( vlc_object_t * );
126
127 static int  CDDARead         ( input_thread_t *, byte_t *, size_t );
128 static void CDDASeek         ( input_thread_t *, off_t );
129 static int  CDDASetArea      ( input_thread_t *, input_area_t * );
130 static int  CDDAPlay         ( input_thread_t *, int );
131 static int  CDDASetProgram   ( input_thread_t *, pgrm_descriptor_t * );
132
133 static int  InitThread     ( intf_thread_t *p_intf );
134 static int  KeyEvent       ( vlc_object_t *, char const *,
135                              vlc_value_t, vlc_value_t, void * );
136
137 static void RunIntf          ( intf_thread_t *p_intf );
138
139 static int  E_(DebugCallback) ( vlc_object_t *p_this, const char *psz_name,
140                                 vlc_value_t oldval, vlc_value_t val, 
141                                 void *p_data );
142
143 /*****************************************************************************
144  * Module descriptor
145  *****************************************************************************/
146
147 static int  DemuxOpen  ( vlc_object_t * );
148 static void DemuxClose ( vlc_object_t * );
149
150 #define CACHING_TEXT N_("Caching value in ms")
151 #define CACHING_LONGTEXT N_( \
152     "Allows you to modify the default caching value for cdda streams. This " \
153     "value should be set in miliseconds units." )
154
155 vlc_module_begin();
156     add_usage_hint( N_("cddax://[device-or-file][@num]") );
157     set_description( _("Compact Disc Digital Audio (CD-DA) input") );
158     set_capability( "access", 75 /* slightly higher than cdda */ );
159     add_integer( "cddax-caching", DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
160     set_callbacks( E_(Open), E_(Close) );
161     add_shortcut( "cdda" );
162     add_shortcut( "cddax" );
163
164     /* Configuration options */
165     add_category_hint( N_("CDX"), NULL, VLC_TRUE );
166     add_integer ( MODULE_STRING "-debug", 0, E_(DebugCallback), DEBUG_TEXT, 
167                   DEBUG_LONGTEXT, VLC_TRUE );
168     add_string( MODULE_STRING "-device", "", NULL, DEV_TEXT, 
169                 DEV_LONGTEXT, VLC_TRUE );
170
171     add_submodule();
172         set_description( _("CD Audio demux") );
173         set_capability( "demux", 0 );
174         set_callbacks( DemuxOpen, DemuxClose );
175         add_shortcut( "cdda" );
176
177     add_submodule();
178         set_capability( "interface", 0 );
179         set_callbacks( E_(OpenIntf), E_(CloseIntf) );
180
181 vlc_module_end();
182
183 /****************************************************************************
184  * Private functions
185  ****************************************************************************/
186
187 static int
188 E_(DebugCallback)   ( vlc_object_t *p_this, const char *psz_name,
189                       vlc_value_t oldval, vlc_value_t val, void *p_data )
190 {
191   cdda_data_t *p_cdda;
192
193   if (NULL == p_cdda_input) return VLC_EGENERIC;
194   
195   p_cdda = (cdda_data_t *)p_cdda_input->p_access_data;
196
197   if (p_cdda->i_debug & (INPUT_DBG_CALL|INPUT_DBG_EXT)) {
198     msg_Dbg( p_cdda_input, "Old debug (x%0x) %d, new debug (x%0x) %d", 
199              p_cdda->i_debug, p_cdda->i_debug, val.i_int, val.i_int);
200   }
201   p_cdda->i_debug = val.i_int;
202   return VLC_SUCCESS;
203 }
204
205 /* process messages that originate from libcdio. */
206 static void
207 cdio_log_handler (cdio_log_level_t level, const char message[])
208 {
209   cdda_data_t *p_cdda = (cdda_data_t *)p_cdda_input->p_access_data;
210   switch (level) {
211   case CDIO_LOG_DEBUG:
212   case CDIO_LOG_INFO:
213     if (p_cdda->i_debug & INPUT_DBG_CDIO) 
214       msg_Dbg( p_cdda_input, message);
215     break;
216   case CDIO_LOG_WARN:
217     msg_Warn( p_cdda_input, message);
218     break;
219   case CDIO_LOG_ERROR:
220   case CDIO_LOG_ASSERT:
221     msg_Err( p_cdda_input, message);
222     break;
223   default:
224     msg_Warn( p_cdda_input, message,
225             _("The above message had unknown vcdimager log level"), 
226             level);
227   }
228   return;
229 }
230
231
232 /*****************************************************************************
233  * E_(Open): open cdda
234  *****************************************************************************/
235 static int E_(Open)( vlc_object_t *p_this )
236 {
237     input_thread_t *        p_input = (input_thread_t *)p_this;
238     char *                  psz_orig;
239     char *                  psz_parser;
240     char *                  psz_source;
241     cdda_data_t *           p_cdda;
242     int                     i;
243     int                     i_title = 1;
244     cddev_t                 *p_cddev;
245
246     /* Set where to log errors messages from libcdio. */
247     p_cdda_input = (input_thread_t *)p_this;
248
249     /* parse the options passed in command line : */
250     psz_orig = psz_parser = psz_source = strdup( p_input->psz_name );
251
252     if( !psz_orig )
253     {
254         return( -1 );
255     }
256
257     while( *psz_parser && *psz_parser != '@' )
258     {
259         psz_parser++;
260     }
261
262     if( *psz_parser == '@' )
263     {
264         /* Found options */
265         *psz_parser = '\0';
266         ++psz_parser;
267
268         if ('T' == *psz_parser || 't' == *psz_parser ) 
269             ++psz_parser;
270           
271         i_title = (int)strtol( psz_parser, NULL, 10 );
272         i_title = i_title ? i_title : 1;
273     }
274
275     if( !*psz_source ) {
276       /* No source specified, so figure it out. */
277       if( !p_input->psz_access ) {
278         free( psz_orig );
279         return -1;
280       }
281       psz_source = config_GetPsz( p_input, MODULE_STRING "-device" );
282       
283       if( !psz_source || 0==strlen(psz_source) ) {
284         /* Scan for a CD-ROM drive with a CD-DA in it. */
285         char **cd_drives = 
286           cdio_get_devices_with_cap(NULL,  CDIO_FS_AUDIO, false);
287         if (NULL == cd_drives) return -1;
288         if (cd_drives[0] == NULL) {
289           cdio_free_device_list(cd_drives);
290           return -1;
291         }
292         psz_source = strdup(cd_drives[0]);
293         cdio_free_device_list(cd_drives);
294       }
295     }
296
297     /* Open CDDA */
298     cdio_log_set_handler ( cdio_log_handler );
299
300     if( !(p_cddev = ioctl_Open( p_this, psz_source )) )
301     {
302         msg_Warn( p_input, "could not open %s", psz_source );
303         free( psz_source );
304         return -1;
305     }
306     free( psz_source );
307
308     p_cdda = malloc( sizeof(cdda_data_t) );
309     if( p_cdda == NULL )
310     {
311         msg_Err( p_input, "out of memory" );
312         free( psz_source );
313         return -1;
314     }
315
316     p_cdda->p_cddev        = p_cddev;
317     p_cdda->i_debug        = config_GetInt( p_this, MODULE_STRING "-debug" );
318     p_input->p_access_data = (void *)p_cdda;
319
320     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "%s", psz_source );
321
322     p_input->i_mtu = CDDA_DATA_ONCE;
323
324     /* We read the Table Of Content information */
325     p_cdda->i_nb_tracks = ioctl_GetTracksMap( VLC_OBJECT(p_input),
326                               p_cdda->p_cddev->cdio, &p_cdda->p_sectors );
327     if( p_cdda->i_nb_tracks < 0 )
328         msg_Err( p_input, "unable to count tracks" );
329     else if( p_cdda->i_nb_tracks <= 0 )
330         msg_Err( p_input, "no audio tracks found" );
331
332     if( p_cdda->i_nb_tracks <= 1)
333     {
334         ioctl_Close( p_cdda->p_cddev );
335         free( p_cdda );
336         return -1;
337     }
338
339     if( i_title >= p_cdda->i_nb_tracks || i_title < 1 )
340         i_title = 1;
341
342     /* Set stream and area data */
343     vlc_mutex_lock( &p_input->stream.stream_lock );
344
345     /* Initialize ES structures */
346     input_InitStream( p_input, 0 );
347
348     /* cdda input method */
349     p_input->stream.i_method = INPUT_METHOD_CDDA;
350
351     p_input->stream.b_pace_control = 1;
352     p_input->stream.b_seekable = 1;
353     p_input->stream.i_mux_rate = 44100 * 4 / 50;
354
355 #define area p_input->stream.pp_areas
356     for( i = 1 ; i <= p_cdda->i_nb_tracks ; i++ )
357     {
358         input_AddArea( p_input, i, 1 );
359
360         /* Absolute start offset and size */
361         area[i]->i_start =
362             (off_t)p_cdda->p_sectors[i-1] * (off_t)CDIO_CD_FRAMESIZE_RAW;
363         area[i]->i_size =
364             (off_t)(p_cdda->p_sectors[i] - p_cdda->p_sectors[i-1])
365             * (off_t)CDIO_CD_FRAMESIZE_RAW;
366     }
367 #undef area
368
369     CDDAPlay( p_input, i_title);
370
371     vlc_mutex_unlock( &p_input->stream.stream_lock );
372
373     if( !p_input->psz_demux || !*p_input->psz_demux )
374     {
375         p_input->psz_demux = "cdda";
376     }
377
378     p_input->pf_read = CDDARead;
379     p_input->pf_seek = CDDASeek;
380     p_input->pf_set_area = CDDASetArea;
381     p_input->pf_set_program = CDDASetProgram;
382
383     /* Update default_pts to a suitable value for cdda access */
384     p_input->i_pts_delay = config_GetInt( p_input, 
385                                           MODULE_STRING "-caching" ) * 1000;
386
387     p_cdda->p_intf = intf_Create( p_input, "cddax" );
388     intf_RunThread( p_cdda->p_intf );
389
390     return 0;
391 }
392
393 /*****************************************************************************
394  * CDDAPlay: Arrange things play track
395  *****************************************************************************/
396 static vlc_bool_t
397 CDDAPlay( input_thread_t *p_input, int i_track )
398 {
399   cdda_data_t *p_cdda = (cdda_data_t *) p_input->p_access_data;
400
401   if( i_track >= p_cdda->i_nb_tracks || i_track < 1 )
402     return VLC_FALSE;
403
404   CDDASetArea( p_input, p_input->stream.pp_areas[i_track] );
405   return VLC_TRUE;
406 }
407
408 /*****************************************************************************
409  * E_(Close): closes cdda
410  *****************************************************************************/
411 static void 
412 E_(Close)( vlc_object_t *p_this )
413 {
414     input_thread_t *   p_input = (input_thread_t *)p_this;
415     cdda_data_t *p_cdda = (cdda_data_t *)p_input->p_access_data;
416
417     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "" );
418     ioctl_Close( p_cdda->p_cddev );
419     free( p_cdda );
420     p_cdda_input = NULL;
421 }
422
423 /*****************************************************************************
424  * CDDARead: reads from the CDDA into PES packets.
425  *****************************************************************************
426  * Returns -1 in case of error, 0 in case of EOF, otherwise the number of
427  * bytes.
428  *****************************************************************************/
429 static int CDDARead( input_thread_t * p_input, byte_t * p_buffer,
430                      size_t i_len )
431 {
432     cdda_data_t *           p_cdda;
433     int                     i_blocks;
434     int                     i_index;
435     int                     i_read;
436
437     p_cdda = (cdda_data_t *)p_input->p_access_data;
438
439     i_read = 0;
440
441     /* Compute the number of blocks we have to read */
442
443     i_blocks = i_len / CDIO_CD_FRAMESIZE_RAW;
444
445     for ( i_index = 0; i_index < i_blocks; i_index++ )
446     {
447
448       if (cdio_read_audio_sector(p_cdda->p_cddev->cdio, p_buffer, 
449                                  p_cdda->i_sector) != 0)
450         {
451           msg_Err( p_input, "could not read sector %d", p_cdda->i_sector );
452           return -1;
453         }
454
455         p_cdda->i_sector ++;
456         if ( p_cdda->i_sector == p_cdda->p_sectors[p_cdda->i_track + 1] )
457         {
458             input_area_t *p_area;
459
460             dbg_print( (INPUT_DBG_LSN|INPUT_DBG_CALL), 
461                        "end of track, cur: %u", p_cdda->i_sector );
462
463             if ( p_cdda->i_track >= p_cdda->i_nb_tracks - 1 )
464                 return 0; /* EOF */
465
466             vlc_mutex_lock( &p_input->stream.stream_lock );
467             p_area = p_input->stream.pp_areas[
468                     p_input->stream.p_selected_area->i_id + 1 ];
469
470             p_area->i_part = 1;
471             CDDASetArea( p_input, p_area );
472             vlc_mutex_unlock( &p_input->stream.stream_lock );
473         }
474         i_read += CDIO_CD_FRAMESIZE_RAW;
475     }
476
477     if ( i_len % CDIO_CD_FRAMESIZE_RAW ) /* this should not happen */
478     {
479         msg_Err( p_input, "must read full sectors" );
480     }
481
482     return i_read;
483 }
484
485 /*****************************************************************************
486  * CDDASetProgram: Does nothing since a CDDA is mono_program
487  *****************************************************************************/
488 static int CDDASetProgram( input_thread_t * p_input,
489                            pgrm_descriptor_t * p_program)
490 {
491     cdda_data_t * p_cdda= (cdda_data_t *) p_input->p_access_data;
492     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "" );
493     return 0;
494 }
495
496 /*****************************************************************************
497  * CDDASetArea: initialize input data for title x.
498  * It should be called for each user navigation request.
499  ****************************************************************************/
500 static int CDDASetArea( input_thread_t * p_input, input_area_t * p_area )
501 {
502     cdda_data_t *p_cdda = (cdda_data_t*) p_input->p_access_data;
503     vlc_value_t val;
504
505     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT), "");
506
507     /* we can't use the interface slider until initilization is complete */
508     p_input->stream.b_seekable = 0;
509
510     if( p_area != p_input->stream.p_selected_area )
511     {
512         /* Change the default area */
513         p_input->stream.p_selected_area = p_area;
514
515         /* Change the current track */
516         p_cdda->i_track = p_area->i_id - 1;
517         p_cdda->i_sector = p_cdda->p_sectors[p_cdda->i_track];
518
519         /* Update the navigation variables without triggering a callback */
520         val.i_int = p_area->i_id;
521         var_Change( p_input, "title", VLC_VAR_SETVALUE, &val, NULL );
522     }
523
524     p_cdda->i_sector = p_cdda->p_sectors[p_cdda->i_track];
525
526     p_input->stream.p_selected_area->i_tell =
527         (off_t)p_cdda->i_sector * (off_t)CDIO_CD_FRAMESIZE_RAW
528          - p_input->stream.p_selected_area->i_start;
529
530     /* warn interface that something has changed */
531     p_input->stream.b_seekable = 1;
532     p_input->stream.b_changed = 1;
533
534     return 0;
535 }
536
537 /****************************************************************************
538  * CDDASeek
539  ****************************************************************************/
540 static void CDDASeek( input_thread_t * p_input, off_t i_off )
541 {
542     cdda_data_t * p_cdda;
543
544     p_cdda = (cdda_data_t *) p_input->p_access_data;
545
546     p_cdda->i_sector = p_cdda->p_sectors[p_cdda->i_track]
547                        + i_off / (off_t)CDIO_CD_FRAMESIZE_RAW;
548
549     vlc_mutex_lock( &p_input->stream.stream_lock );
550     p_input->stream.p_selected_area->i_tell =
551         (off_t)p_cdda->i_sector * (off_t)CDIO_CD_FRAMESIZE_RAW
552          - p_input->stream.p_selected_area->i_start;
553
554     vlc_mutex_unlock( &p_input->stream.stream_lock );
555
556     dbg_print( (INPUT_DBG_CALL|INPUT_DBG_EXT|INPUT_DBG_SEEK),
557     "sector %ud, offset: %lld, i_tell: %lld",  p_cdda->i_sector, i_off, 
558                p_input->stream.p_selected_area->i_tell );
559
560 }
561
562 /*****************************************************************************
563  * Demux: local prototypes
564  *****************************************************************************/
565 struct demux_sys_t
566 {
567     es_out_id_t *p_es;
568     mtime_t     i_pts;
569 };
570
571 static int  Demux     ( input_thread_t * p_input );
572
573 /****************************************************************************
574  * DemuxOpen:
575  ****************************************************************************/
576 static int  DemuxOpen    ( vlc_object_t * p_this)
577 {
578     input_thread_t *p_input = (input_thread_t *)p_this;
579     demux_sys_t    *p_sys;
580
581     es_format_t    fmt;
582
583     if( p_input->stream.i_method != INPUT_METHOD_CDDA )
584     {
585         return VLC_EGENERIC;
586     }
587
588     p_input->pf_demux  = Demux;
589     p_input->pf_rewind = NULL;
590     p_input->pf_demux_control = demux_vaControlDefault;
591     p_input->p_demux_data = p_sys = malloc( sizeof( es_descriptor_t ) );
592     p_sys->i_pts = 0;
593
594     vlc_mutex_lock( &p_input->stream.stream_lock );
595     if( input_InitStream( p_input, 0 ) == -1)
596     {
597         vlc_mutex_unlock( &p_input->stream.stream_lock );
598         msg_Err( p_input, "cannot init stream" );
599         free( p_sys );
600         return VLC_EGENERIC;
601     }
602     p_input->stream.i_mux_rate = 4 * 44100 / 50;
603     vlc_mutex_unlock( &p_input->stream.stream_lock );
604
605     es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( 'a', 'r', 'a', 'w' ) );
606     fmt.audio.i_channels = 2;
607     fmt.audio.i_rate = 44100;
608     fmt.audio.i_bitspersample = 16;
609     fmt.audio.i_blockalign = 4;
610     fmt.i_bitrate = 4 * 44100 * 8;
611
612     p_sys->p_es =  es_out_Add( p_input->p_es_out, &fmt );
613
614     return VLC_SUCCESS;
615 }
616
617 /****************************************************************************
618  * DemuxClose:
619  ****************************************************************************/
620 static void DemuxClose( vlc_object_t * p_this)
621 {
622     input_thread_t *p_input = (input_thread_t*)p_this;
623     demux_sys_t    *p_sys = (demux_sys_t*)p_input->p_demux_data;
624
625     free( p_sys );
626     return;
627 }
628
629 /****************************************************************************
630  * Demux:
631  ****************************************************************************/
632 static int  Demux( input_thread_t * p_input )
633 {
634     demux_sys_t    *p_sys = (demux_sys_t*)p_input->p_demux_data;
635     block_t        *p_block;
636
637
638     input_ClockManageRef( p_input,
639                           p_input->stream.p_selected_program,
640                           p_sys->i_pts );
641
642     if( ( p_block = stream_Block( p_input->s, CDIO_CD_FRAMESIZE_RAW ) ) == NULL )
643     {
644         /* eof */
645         return 0;
646     }
647     p_block->i_dts =
648     p_block->i_pts = input_ClockGetTS( p_input,
649                                        p_input->stream.p_selected_program,
650                                        p_sys->i_pts );
651     p_block->i_length = (mtime_t)90000 * (mtime_t)p_block->i_buffer/44100/4;
652
653     p_sys->i_pts += p_block->i_length;
654
655     es_out_Send( p_input->p_es_out, p_sys->p_es, p_block );
656
657     return 1;
658 }
659
660 /*****************************************************************************
661  * OpenIntf: initialize dummy interface
662  *****************************************************************************/
663 int E_(OpenIntf) ( vlc_object_t *p_this )
664 {
665     intf_thread_t *p_intf = (intf_thread_t *)p_this;
666
667     /* Allocate instance and initialize some members */
668     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
669     if( p_intf->p_sys == NULL )
670     {
671         return( 1 );
672     };
673
674     p_intf->pf_run = RunIntf;
675
676     var_AddCallback( p_intf->p_vlc, "key-pressed", KeyEvent, p_intf );
677
678     return( 0 );
679 }
680
681 /*****************************************************************************
682  * CloseIntf: destroy dummy interface
683  *****************************************************************************/
684 void E_(CloseIntf) ( vlc_object_t *p_this )
685 {
686     intf_thread_t *p_intf = (intf_thread_t *)p_this;
687
688     /* Destroy structure */
689     free( p_intf->p_sys );
690 }
691
692
693 /*****************************************************************************
694  * RunIntf: main loop
695  *****************************************************************************/
696 static void RunIntf( intf_thread_t *p_intf )
697 {
698     vlc_object_t      * p_vout = NULL;
699     cdda_data_t       * p_cdda;
700     input_thread_t    * p_input;
701     
702     /* What you add to the last input number entry. It accumulates all of
703        the 10_ADD keypresses */
704     int number_addend = 0; 
705     
706     if( InitThread( p_intf ) < 0 )
707     {
708         msg_Err( p_intf, "can't initialize intf" );
709         return;
710     }
711
712     p_input = p_intf->p_sys->p_input;
713     p_cdda   = p_intf->p_sys->p_cdda = 
714       (cdda_data_t *) p_input->p_access_data;
715
716     dbg_print( INPUT_DBG_CALL, "intf initialized" );
717
718     /* Main loop */
719     while( !p_intf->b_die )
720     {
721       vlc_mutex_lock( &p_intf->change_lock );
722
723       /*
724        * keyboard event
725        */
726       if( p_vout && p_intf->p_sys->b_key_pressed )
727         {
728           vlc_value_t val;
729           int i, i_action = -1;
730           struct hotkey *p_hotkeys = p_intf->p_vlc->p_hotkeys;
731
732           p_intf->p_sys->b_key_pressed = VLC_FALSE;
733           
734           /* Find action triggered by hotkey (if any) */
735           var_Get( p_intf->p_vlc, "key-pressed", &val );
736
737           dbg_print( INPUT_DBG_EVENT, "Key pressed %d", val.i_int );
738
739           for( i = 0; p_hotkeys[i].psz_action != NULL; i++ )
740             {
741               if( p_hotkeys[i].i_key == val.i_int )
742                 {
743                   i_action = p_hotkeys[i].i_action;
744                 }
745             }
746           
747           if( i_action != -1) {
748             switch (i_action) {
749               
750             case ACTIONID_NAV_LEFT: 
751               dbg_print( INPUT_DBG_EVENT, "ACTIONID_NAV_LEFT (%d)", 
752                          number_addend );
753               do {
754                 if ( CDDAPlay( p_input, p_cdda->i_track-1 ) ) {
755                   p_cdda->i_track--;
756                 } else {
757                   break;
758                 }
759               } while (number_addend-- > 0);
760               break;
761
762             case ACTIONID_NAV_RIGHT:
763               dbg_print( INPUT_DBG_EVENT, "ACTIONID_NAV_RIGHT (%d)",
764                          number_addend );
765               do {
766                 if ( CDDAPlay( p_input, p_cdda->i_track+1 ) ) {
767                   p_cdda->i_track++;
768                 } else {
769                   break;
770                 }
771               } while (number_addend-- > 0);
772               break;
773
774             case ACTIONID_NAV_UP:
775               dbg_print( INPUT_DBG_EVENT, "ACTIONID_NAV_UP" );
776               do {
777                 ;
778               } while (number_addend-- > 0);
779               break;
780
781             case ACTIONID_NAV_DOWN:
782               dbg_print( INPUT_DBG_EVENT, "ACTIONID_NAV_DOWN"  );
783               break;
784
785             case ACTIONID_NAV_ACTIVATE: 
786               {
787                 dbg_print( INPUT_DBG_EVENT, "ACTIONID_NAV_ACTIVATE" );
788                 if ( CDDAPlay( p_input, number_addend ) ) {
789                   p_cdda->i_track = number_addend;
790                 } else {
791                   break;
792                 }
793                 break;
794               }
795             }
796             number_addend = 0;
797           } else {
798             unsigned int digit_entered=0;
799
800             switch (val.i_int) {
801             case '9':
802               digit_entered++;
803             case '8':
804               digit_entered++;
805             case '7':
806               digit_entered++;
807             case '6':
808               digit_entered++;
809             case '5':
810               digit_entered++;
811             case '4':
812               digit_entered++;
813             case '3':
814               digit_entered++;
815             case '2':
816               digit_entered++;
817             case '1':
818               digit_entered++;
819             case '0':
820               {
821                 number_addend *= 10;
822                 number_addend += digit_entered;
823                 dbg_print( INPUT_DBG_EVENT, 
824                            "Added %d. Number is now: %d\n", 
825                            digit_entered, number_addend);
826                 break;
827               }
828             }
829           }
830         }
831
832       
833       vlc_mutex_unlock( &p_intf->change_lock );
834       
835       if( p_vout == NULL )
836         {
837           p_vout = vlc_object_find( p_intf->p_sys->p_input,
838                                     VLC_OBJECT_VOUT, FIND_ANYWHERE );
839           if( p_vout )
840             {
841               var_AddCallback( p_vout, "key-pressed", KeyEvent, p_intf );
842             }
843         }
844       
845       
846       /* Wait a bit */
847       msleep( INTF_IDLE_SLEEP );
848     }
849
850     if( p_vout )
851     {
852         var_DelCallback( p_vout, "key-pressed", KeyEvent, p_intf );
853         vlc_object_release( p_vout );
854     }
855
856     vlc_object_release( p_intf->p_sys->p_input );
857 }
858
859 /*****************************************************************************
860  * InitThread:
861  *****************************************************************************/
862 static int InitThread( intf_thread_t * p_intf )
863 {
864     /* We might need some locking here */
865     if( !p_intf->b_die )
866     {
867         input_thread_t * p_input;
868
869         p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_PARENT );
870
871         /* Maybe the input just died */
872         if( p_input == NULL )
873         {
874             return VLC_EGENERIC;
875         }
876
877         vlc_mutex_lock( &p_intf->change_lock );
878
879         p_intf->p_sys->p_input = p_input;
880
881         p_intf->p_sys->b_move = VLC_FALSE;
882         p_intf->p_sys->b_click = VLC_FALSE;
883         p_intf->p_sys->b_key_pressed = VLC_FALSE;
884
885         vlc_mutex_unlock( &p_intf->change_lock );
886
887         return VLC_SUCCESS;
888     }
889     else
890     {
891         return VLC_EGENERIC;
892     }
893 }
894
895 /*****************************************************************************
896  * KeyEvent: callback for keyboard events
897  *****************************************************************************/
898 static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
899                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
900 {
901     intf_thread_t *p_intf = (intf_thread_t *)p_data;
902     vlc_mutex_lock( &p_intf->change_lock );
903
904     p_intf->p_sys->b_key_pressed = VLC_TRUE;
905
906     vlc_mutex_unlock( &p_intf->change_lock );
907
908     return VLC_SUCCESS;
909 }