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
7 * Author: Felix Kühne <fkuehne at videolan dot org>
\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
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
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
24 /*****************************************************************************
\r
26 *****************************************************************************/
\r
29 #include <vlc/vlc.h>
\r
30 #include <vlc_access.h>
\r
32 #include <CoreFoundation/CoreFoundation.h>
\r
35 * watch for PluginQuit or DeviceRemoved to stop output to VLC's core then */
\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
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
49 set_capability( "access2", 0 );
\r
50 add_shortcut( "eyetv" );
\r
51 set_callbacks( Open, Close );
\r
54 /*****************************************************************************
\r
55 * Access: local prototypes
\r
56 *****************************************************************************/
\r
64 CFMessagePortRef inputMessagePortFromEyeTV;
\r
69 eyetv_thread_t *p_thread;
\r
72 CFDataRef dataFromEyetv;
\r
74 int lastForwardedPacketId;
\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
81 /*****************************************************************************
\r
82 * Open: sets up the module and its threads
\r
83 *****************************************************************************/
\r
84 static int Open( vlc_object_t *p_this )
\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
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
98 msg_Dbg( p_access, "coming up" );
\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
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
117 /* no info to free */ NULL );
\r
118 if( p_sys->p_thread->inputMessagePortFromEyeTV == NULL )
\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
127 return VLC_EGENERIC;
\r
130 msg_Dbg( p_access, "remote msg port opened" );
\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
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
142 return VLC_EGENERIC;
\r
145 msg_Dbg( p_access, "receiver thread created and launched" );
\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
154 msg_Dbg( p_access, "plugin notified" );
\r
156 /* we don't need such a high priority */
\r
157 //vlc_thread_set_priority( p_access, VLC_THREAD_PRIORITY_LOW );
\r
159 return VLC_SUCCESS;
\r
162 /*****************************************************************************
\r
163 * Close: closes msg-port, free resources
\r
164 *****************************************************************************/
\r
165 static void Close( vlc_object_t *p_this )
\r
167 access_t *p_access = (access_t *)p_this;
\r
168 access_sys_t *p_sys = p_access->p_sys;
\r
170 msg_Dbg( p_access, "closing" );
\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
179 msg_Dbg( p_access, "plugin notified" );
\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
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
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
202 static void Thread( vlc_object_t *p_this )
\r
204 eyetv_thread_t *p_thread= (eyetv_thread_t*)p_this;
\r
205 CFRunLoopSourceRef runLoopSource;
\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
211 CFRunLoopAddSource( CFRunLoopGetCurrent(),
\r
213 kCFRunLoopDefaultMode );
\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
224 extern CFDataRef dataFromEyetv;
\r
225 extern int lastPacketId;
\r
227 /* copy callback data to module data */
\r
228 dataFromEyetv = CFDataCreateCopy( kCFAllocatorDefault, data );
\r
230 printf( "packet %i contained %i bytes, forwarding %i bytes\n",
\r
232 (int)CFDataGetLength( data ),
\r
233 (int)CFDataGetLength( dataFromEyetv ) );
\r
236 lastPacketId = msgid;
\r
238 return NULL; /* we've got nothing to return */
\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
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
251 /* wait for a new buffer before forwarding */
\r
252 while( lastPacketId == lastForwardedPacketId && !p_access->b_die )
\r
254 msleep( INPUT_ERROR_SLEEP );
\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
261 CFDataGetBytes( dataFromEyetv,
\r
262 CFRangeMake( 0, CFDataGetLength( dataFromEyetv ) ),
\r
263 (uint8_t *)p_buffer );
\r
264 i_len = (int)CFDataGetLength( dataFromEyetv );
\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
269 lastForwardedPacketId = lastPacketId;
\r
272 msg_Err( p_access, "you looosed!" );
\r
277 if( p_access->b_die )
\r
283 /*****************************************************************************
\r
285 *****************************************************************************/
\r
286 static int Control( access_t *p_access, int i_query, va_list args )
\r
288 vlc_bool_t *pb_bool;
\r
295 case ACCESS_SET_PAUSE_STATE:
\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
312 msg_Warn( p_access, "unimplemented query in control" );
\r
313 return VLC_EGENERIC;
\r
316 return VLC_SUCCESS;*/
\r
317 return VLC_EGENERIC;
\r