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