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