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