1 /*****************************************************************************
2 * eyetv.c : Access module to connect to our plugin running within EyeTV
3 *****************************************************************************
4 * Copyright (C) 2006-2007 the VideoLAN team
7 * Author: Felix Kühne <fkuehne at videolan dot org>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
30 #include <vlc_access.h>
32 #include <CoreFoundation/CoreFoundation.h>
35 * watch for PluginQuit or DeviceRemoved to stop output to VLC's core then */
37 /*****************************************************************************
39 *****************************************************************************/
40 static int Open ( vlc_object_t * );
41 static void Close( vlc_object_t * );
44 set_shortname( "EyeTV" );
45 set_description( _("EyeTV access module") );
46 set_category( CAT_INPUT );
47 set_subcategory( SUBCAT_INPUT_ACCESS );
49 set_capability( "access2", 0 );
50 add_shortcut( "eyetv" );
51 set_callbacks( Open, Close );
54 /*****************************************************************************
55 * Access: local prototypes
56 *****************************************************************************/
64 CFMessagePortRef inputMessagePortFromEyeTV;
69 eyetv_thread_t *p_thread;
72 CFDataRef dataFromEyetv;
74 int lastForwardedPacketId;
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 );
81 /*****************************************************************************
82 * Open: sets up the module and its threads
83 *****************************************************************************/
84 static int Open( vlc_object_t *p_this )
86 access_t *p_access = (access_t *)p_this;
88 eyetv_thread_t *p_thread;
89 CFMessagePortContext context;
90 memset(&context, 0, sizeof(context));
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 ) );
98 msg_Dbg( p_access, "coming up" );
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" );
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"),
117 /* no info to free */ NULL );
118 if( p_sys->p_thread->inputMessagePortFromEyeTV == NULL )
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 );
130 msg_Dbg( p_access, "remote msg port opened" );
132 /* let the thread run */
133 if( vlc_thread_create( p_thread, "EyeTV Receiver Thread", Thread,
134 VLC_THREAD_PRIORITY_HIGHEST, VLC_FALSE ) )
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 );
145 msg_Dbg( p_access, "receiver thread created and launched" );
147 /* tell the EyeTV plugin to open up its msg port and start sending */
148 CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter (),
149 CFSTR("VLCAccessStartDataSending"),
150 CFSTR("VLCEyeTVSupport"),
154 msg_Dbg( p_access, "plugin notified" );
156 /* we don't need such a high priority */
157 //vlc_thread_set_priority( p_access, VLC_THREAD_PRIORITY_LOW );
162 /*****************************************************************************
163 * Close: closes msg-port, free resources
164 *****************************************************************************/
165 static void Close( vlc_object_t *p_this )
167 access_t *p_access = (access_t *)p_this;
168 access_sys_t *p_sys = p_access->p_sys;
170 msg_Dbg( p_access, "closing" );
172 /* tell the EyeTV plugin to close its msg port and stop sending */
173 CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter (),
174 CFSTR("VLCAccessStopDataSending"),
175 CFSTR("VLCEyeTVSupport"),
179 msg_Dbg( p_access, "plugin notified" );
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 );
189 CFMessagePortInvalidate( p_sys->p_thread->inputMessagePortFromEyeTV );
190 free( p_sys->p_thread->inputMessagePortFromEyeTV );
191 msg_Dbg( p_access, "msg port closed and freed" );
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 );
202 static void Thread( vlc_object_t *p_this )
204 eyetv_thread_t *p_thread= (eyetv_thread_t*)p_this;
205 CFRunLoopSourceRef runLoopSource;
207 /* create our run loop source for the port and attach it to our current loop */
208 runLoopSource = CFMessagePortCreateRunLoopSource( kCFAllocatorDefault,
209 p_thread->inputMessagePortFromEyeTV,
211 CFRunLoopAddSource( CFRunLoopGetCurrent(),
213 kCFRunLoopDefaultMode );
219 /*****************************************************************************
220 * msgPortCallback: receives data from the EyeTV plugin
221 *****************************************************************************/
222 CFDataRef msgPortCallback( CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info )
224 extern CFDataRef dataFromEyetv;
225 extern int lastPacketId;
227 /* copy callback data to module data */
228 dataFromEyetv = CFDataCreateCopy( kCFAllocatorDefault, data );
230 printf( "packet %i contained %i bytes, forwarding %i bytes\n",
232 (int)CFDataGetLength( data ),
233 (int)CFDataGetLength( dataFromEyetv ) );
236 lastPacketId = msgid;
238 return NULL; /* we've got nothing to return */
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 )
246 access_sys_t *p_sys = p_access->p_sys;
247 extern CFDataRef dataFromEyetv;
248 extern int lastPacketId;
249 extern int lastForwardedPacketId;
251 /* wait for a new buffer before forwarding */
252 while( lastPacketId == lastForwardedPacketId && !p_access->b_die )
254 msleep( INPUT_ERROR_SLEEP );
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 */
261 CFDataGetBytes( dataFromEyetv,
262 CFRangeMake( 0, CFDataGetLength( dataFromEyetv ) ),
263 (uint8_t *)p_buffer );
264 i_len = (int)CFDataGetLength( dataFromEyetv );
266 msg_Dbg( p_access, "%i bytes with id %i received in read function, pushing to core",
267 (int)CFDataGetLength( dataFromEyetv ), lastPacketId );
269 lastForwardedPacketId = lastPacketId;
272 msg_Err( p_access, "you looosed!" );
277 if( p_access->b_die )
283 /*****************************************************************************
285 *****************************************************************************/
286 static int Control( access_t *p_access, int i_query, va_list args )
295 case ACCESS_SET_PAUSE_STATE:
299 case ACCESS_CAN_SEEK:
300 case ACCESS_CAN_FASTSEEK:
301 case ACCESS_CAN_PAUSE:
302 case ACCESS_CAN_CONTROL_PACE:
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:
312 msg_Warn( p_access, "unimplemented query in control" );
316 return VLC_SUCCESS;*/