]> git.sesse.net Git - vlc/blob - modules/access/eyetv.c
Fix line breaks
[vlc] / modules / access / eyetv.c
1 /*****************************************************************************
2  * eyetv.c : Access module to connect to our plugin running within EyeTV
3  *****************************************************************************
4  * Copyright (C) 2006-2007 the VideoLAN team
5  * $Id$
6  *
7  * Author: Felix Kühne <fkuehne at videolan dot org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>
28
29 #include <vlc/vlc.h>
30 #include <vlc_access.h>
31
32 #include <CoreFoundation/CoreFoundation.h>
33
34 /* TODO:
35  * watch for PluginQuit or DeviceRemoved to stop output to VLC's core then */
36
37 /*****************************************************************************
38  * Module descriptior
39  *****************************************************************************/
40 static int  Open ( vlc_object_t * );
41 static void Close( vlc_object_t * );
42
43 vlc_module_begin();
44     set_shortname( "EyeTV" );
45     set_description( _("EyeTV access module") );
46     set_category( CAT_INPUT );
47     set_subcategory( SUBCAT_INPUT_ACCESS );
48
49     set_capability( "access2", 0 );
50     add_shortcut( "eyetv" );
51     set_callbacks( Open, Close );
52 vlc_module_end();
53
54 /*****************************************************************************
55  * Access: local prototypes
56  *****************************************************************************/
57 typedef struct
58 {
59     VLC_COMMON_MEMBERS
60     
61     vlc_mutex_t     lock;
62     vlc_cond_t      wait;
63     
64     CFMessagePortRef    inputMessagePortFromEyeTV;
65 } eyetv_thread_t;
66
67 struct access_sys_t
68 {
69     eyetv_thread_t      *p_thread;
70 };
71
72 CFDataRef dataFromEyetv;
73 int lastPacketId;
74 int lastForwardedPacketId;
75
76 static int Read( access_t *, uint8_t *, int );
77 static int Control( access_t *, int, va_list );
78 static void Thread( vlc_object_t * );
79 CFDataRef msgPortCallback( CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info );
80
81 /*****************************************************************************
82  * Open: sets up the module and its threads
83  *****************************************************************************/
84 static int Open( vlc_object_t *p_this )
85 {
86     access_t        *p_access = (access_t *)p_this;
87     access_sys_t    *p_sys;
88     eyetv_thread_t  *p_thread;
89     CFMessagePortContext context;
90     memset(&context, 0, sizeof(context)); 
91     
92     /* Init p_access */
93     access_InitFields( p_access ); \
94     ACCESS_SET_CALLBACKS( Read, NULL, Control, NULL ); \
95     MALLOC_ERR( p_access->p_sys, access_sys_t ); \
96     p_sys = p_access->p_sys; memset( p_sys, 0, sizeof( access_sys_t ) );
97
98     msg_Dbg( p_access, "coming up" );
99
100     /* create receiving thread which will keep the message port alive without blocking */
101     p_sys->p_thread = p_thread = vlc_object_create( p_access, sizeof( eyetv_thread_t ) );
102     vlc_object_attach( p_thread, p_this );
103     vlc_mutex_init( p_access, &p_thread->lock );
104     vlc_cond_init( p_access, &p_thread->wait );
105     msg_Dbg( p_access, "thread created, msg port following now" );
106     
107     /* set up our own msg port
108     * we may give the msgport such a generic name, because EyeTV may only run
109     * once per entire machine, so we can't interfere with other instances.
110     * we just trust the user no to launch multiple VLC instances trying to 
111     * access EyeTV at the same time. If this happens, the latest launched
112     * instance will win. */
113     p_sys->p_thread->inputMessagePortFromEyeTV =  CFMessagePortCreateLocal( kCFAllocatorDefault,
114                                                                   CFSTR("VLCEyeTVMsgPort"),
115                                                                   &msgPortCallback,
116                                                                   &context,
117                                                                   /* no info to free */ NULL );
118     if( p_sys->p_thread->inputMessagePortFromEyeTV == NULL )
119     {
120         msg_Err( p_access, "opening local msg port failed" );
121         free( p_sys->p_thread->inputMessagePortFromEyeTV );
122         vlc_mutex_destroy( &p_thread->lock );
123         vlc_cond_destroy( &p_thread->wait );
124         vlc_object_detach( p_thread );
125         vlc_object_destroy( p_thread );
126         free( p_sys );
127         return VLC_EGENERIC;
128     }
129     else
130         msg_Dbg( p_access, "remote msg port opened" );
131     
132     /* let the thread run */
133     if( vlc_thread_create( p_thread, "EyeTV Receiver Thread", Thread,
134                            VLC_THREAD_PRIORITY_HIGHEST, VLC_FALSE ) )
135     {
136         msg_Err( p_access, "couldn't launch eyetv receiver thread" );
137         vlc_mutex_destroy( &p_thread->lock );
138         vlc_cond_destroy( &p_thread->wait );
139         vlc_object_detach( p_thread );
140         vlc_object_destroy( p_thread );
141         free( p_sys );
142         return VLC_EGENERIC;
143     }
144
145     msg_Dbg( p_access, "receiver thread created and launched" );
146     
147     /* tell the EyeTV plugin to open up its msg port and start sending */
148     CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter (),
149                                           CFSTR("VLCAccessStartDataSending"), 
150                                           CFSTR("VLCEyeTVSupport"), 
151                                           /*userInfo*/ NULL, 
152                                           TRUE );
153     
154     msg_Dbg( p_access, "plugin notified" );
155     
156     /* we don't need such a high priority */
157     //vlc_thread_set_priority( p_access, VLC_THREAD_PRIORITY_LOW );
158     
159     return VLC_SUCCESS;
160 }
161
162 /*****************************************************************************
163  * Close: closes msg-port, free resources
164  *****************************************************************************/
165 static void Close( vlc_object_t *p_this )
166 {
167     access_t     *p_access = (access_t *)p_this;
168     access_sys_t *p_sys = p_access->p_sys;
169     
170     msg_Dbg( p_access, "closing" );
171     
172     /* tell the EyeTV plugin to close its msg port and stop sending */
173     CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter (),
174                                           CFSTR("VLCAccessStopDataSending"), 
175                                           CFSTR("VLCEyeTVSupport"), 
176                                           /*userInfo*/ NULL, 
177                                           TRUE );
178     
179     msg_Dbg( p_access, "plugin notified" );
180     
181     /* stop receiver thread */
182     p_sys->p_thread->b_die = VLC_TRUE;
183     vlc_mutex_lock( &p_sys->p_thread->lock );
184     vlc_cond_signal( &p_sys->p_thread->wait );
185     vlc_mutex_unlock( &p_sys->p_thread->lock );
186     vlc_thread_join( p_sys->p_thread );
187     
188     /* close msg port */
189     CFMessagePortInvalidate( p_sys->p_thread->inputMessagePortFromEyeTV );
190     free( p_sys->p_thread->inputMessagePortFromEyeTV );
191     msg_Dbg( p_access, "msg port closed and freed" );
192     
193     /* free thread */
194     vlc_mutex_destroy( &p_sys->p_thread->lock );
195     vlc_cond_destroy( &p_sys->p_thread->wait );
196     vlc_object_detach( p_sys->p_thread );
197     vlc_object_destroy( p_sys->p_thread );
198     
199     free( p_sys );
200 }
201
202 static void Thread( vlc_object_t *p_this )
203 {
204     eyetv_thread_t *p_thread= (eyetv_thread_t*)p_this;
205     CFRunLoopSourceRef runLoopSource;
206     
207     /* create our run loop source for the port and attach it to our current loop */
208     runLoopSource = CFMessagePortCreateRunLoopSource( kCFAllocatorDefault,
209                                                       p_thread->inputMessagePortFromEyeTV,
210                                                       0 );
211     CFRunLoopAddSource( CFRunLoopGetCurrent(),
212                         runLoopSource,
213                         kCFRunLoopDefaultMode );
214     
215     CFRunLoopRun();
216 }
217
218
219 /*****************************************************************************
220 * msgPortCallback: receives data from the EyeTV plugin
221 *****************************************************************************/
222 CFDataRef msgPortCallback( CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info )
223 {
224     extern CFDataRef dataFromEyetv;
225     extern int lastPacketId;
226     
227     /* copy callback data to module data */
228     dataFromEyetv = CFDataCreateCopy( kCFAllocatorDefault, data );
229 #if 0
230     printf( "packet %i contained %i bytes, forwarding %i bytes\n",
231             (int)msgid,
232             (int)CFDataGetLength( data ),
233             (int)CFDataGetLength( dataFromEyetv ) );
234 #endif
235
236     lastPacketId = msgid;
237     
238     return NULL; /* we've got nothing to return */
239 }
240
241 /*****************************************************************************
242 * Read: forwarding data from EyeTV plugin which was received above
243 *****************************************************************************/
244 static int Read( access_t *p_access, uint8_t *p_buffer, int i_len )
245 {
246     access_sys_t *p_sys = p_access->p_sys;
247     extern CFDataRef dataFromEyetv;
248     extern int lastPacketId;
249     extern int lastForwardedPacketId;
250     
251     /* wait for a new buffer before forwarding */
252     while( lastPacketId == lastForwardedPacketId && !p_access->b_die )
253     {
254         msleep( INPUT_ERROR_SLEEP );
255     }
256     
257     /* read data here, copy it to p_buffer, fill i_len with respective length
258      * and return info with i_read; i_read = 0 == EOF */
259     if( dataFromEyetv )
260     {
261         CFDataGetBytes( dataFromEyetv,
262                         CFRangeMake( 0, CFDataGetLength( dataFromEyetv ) ),
263                         (uint8_t *)p_buffer );
264         i_len = (int)CFDataGetLength( dataFromEyetv );
265 #if 0
266         msg_Dbg( p_access, "%i bytes with id %i received in read function, pushing to core",
267              (int)CFDataGetLength( dataFromEyetv ), lastPacketId );
268 #endif
269         lastForwardedPacketId = lastPacketId;
270         if( i_len == 0)
271         {
272             msg_Err( p_access, "you looosed!" );
273             return 0;
274         }
275     }
276     
277     if( p_access->b_die )
278         return 0;
279     
280     return i_len;
281 }
282
283 /*****************************************************************************
284  * Control:
285  *****************************************************************************/
286 static int Control( access_t *p_access, int i_query, va_list args )
287 {/*
288     vlc_bool_t   *pb_bool;
289     int          *pi_int;
290     int64_t      *pi_64;
291     
292     switch( i_query )
293     {
294         * *
295         case ACCESS_SET_PAUSE_STATE:
296             * Nothing to do *
297             break;
298
299         case ACCESS_CAN_SEEK:
300         case ACCESS_CAN_FASTSEEK:
301         case ACCESS_CAN_PAUSE:
302         case ACCESS_CAN_CONTROL_PACE:
303         case ACCESS_GET_MTU:
304         case ACCESS_GET_PTS_DELAY:            
305         case ACCESS_GET_TITLE_INFO:
306         case ACCESS_SET_TITLE:
307         case ACCESS_SET_SEEKPOINT:
308         case ACCESS_SET_PRIVATE_ID_STATE:
309             return VLC_EGENERIC;
310             
311         default:
312             msg_Warn( p_access, "unimplemented query in control" );
313             return VLC_EGENERIC;
314             
315     }
316     return VLC_SUCCESS;*/
317     return VLC_EGENERIC;
318 }