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 *****************************************************************************/
29 #include <vlc_access.h>
31 #include <CoreFoundation/CoreFoundation.h>
34 * watch for PluginQuit or DeviceRemoved to stop output to VLC's core then */
36 /*****************************************************************************
38 *****************************************************************************/
39 static int Open ( vlc_object_t * );
40 static void Close( vlc_object_t * );
43 set_shortname( "EyeTV" );
44 set_description( _("EyeTV access module") );
45 set_category( CAT_INPUT );
46 set_subcategory( SUBCAT_INPUT_ACCESS );
48 set_capability( "access2", 0 );
49 add_shortcut( "eyetv" );
50 set_callbacks( Open, Close );
53 /*****************************************************************************
54 * Access: local prototypes
55 *****************************************************************************/
63 CFMessagePortRef inputMessagePortFromEyeTV;
68 eyetv_thread_t *p_thread;
71 CFDataRef dataFromEyetv;
73 int lastForwardedPacketId;
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 );
80 /*****************************************************************************
81 * Open: sets up the module and its threads
82 *****************************************************************************/
83 static int Open( vlc_object_t *p_this )
85 access_t *p_access = (access_t *)p_this;
87 eyetv_thread_t *p_thread;
88 CFMessagePortContext context;
89 memset(&context, 0, sizeof(context));
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 ) );
97 msg_Dbg( p_access, "coming up" );
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" );
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"),
116 /* no info to free */ NULL );
117 if( p_sys->p_thread->inputMessagePortFromEyeTV == NULL )
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 );
129 msg_Dbg( p_access, "remote msg port opened" );
131 /* let the thread run */
132 if( vlc_thread_create( p_thread, "EyeTV Receiver Thread", Thread,
133 VLC_THREAD_PRIORITY_HIGHEST, VLC_FALSE ) )
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 );
144 msg_Dbg( p_access, "receiver thread created and launched" );
146 /* tell the EyeTV plugin to open up its msg port and start sending */
147 CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter (),
148 CFSTR("VLCAccessStartDataSending"),
149 CFSTR("VLCEyeTVSupport"),
153 msg_Dbg( p_access, "plugin notified" );
155 /* we don't need such a high priority */
156 //vlc_thread_set_priority( p_access, VLC_THREAD_PRIORITY_LOW );
161 /*****************************************************************************
162 * Close: closes msg-port, free resources
163 *****************************************************************************/
164 static void Close( vlc_object_t *p_this )
166 access_t *p_access = (access_t *)p_this;
167 access_sys_t *p_sys = p_access->p_sys;
169 msg_Dbg( p_access, "closing" );
171 /* tell the EyeTV plugin to close its msg port and stop sending */
172 CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter (),
173 CFSTR("VLCAccessStopDataSending"),
174 CFSTR("VLCEyeTVSupport"),
178 msg_Dbg( p_access, "plugin notified" );
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 );
188 CFMessagePortInvalidate( p_sys->p_thread->inputMessagePortFromEyeTV );
189 free( p_sys->p_thread->inputMessagePortFromEyeTV );
190 msg_Dbg( p_access, "msg port closed and freed" );
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 );
201 static void Thread( vlc_object_t *p_this )
203 eyetv_thread_t *p_thread= (eyetv_thread_t*)p_this;
204 CFRunLoopSourceRef runLoopSource;
206 /* create our run loop source for the port and attach it to our current loop */
207 runLoopSource = CFMessagePortCreateRunLoopSource( kCFAllocatorDefault,
208 p_thread->inputMessagePortFromEyeTV,
210 CFRunLoopAddSource( CFRunLoopGetCurrent(),
212 kCFRunLoopDefaultMode );
218 /*****************************************************************************
219 * msgPortCallback: receives data from the EyeTV plugin
220 *****************************************************************************/
221 CFDataRef msgPortCallback( CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info )
223 extern CFDataRef dataFromEyetv;
224 extern int lastPacketId;
226 /* copy callback data to module data */
227 dataFromEyetv = CFDataCreateCopy( kCFAllocatorDefault, data );
229 printf( "packet %i contained %i bytes, forwarding %i bytes\n",
231 (int)CFDataGetLength( data ),
232 (int)CFDataGetLength( dataFromEyetv ) );
235 lastPacketId = msgid;
237 return NULL; /* we've got nothing to return */
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 )
245 access_sys_t *p_sys = p_access->p_sys;
246 extern CFDataRef dataFromEyetv;
247 extern int lastPacketId;
248 extern int lastForwardedPacketId;
250 /* wait for a new buffer before forwarding */
251 while( lastPacketId == lastForwardedPacketId && !p_access->b_die )
253 msleep( INPUT_ERROR_SLEEP );
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 */
260 CFDataGetBytes( dataFromEyetv,
261 CFRangeMake( 0, CFDataGetLength( dataFromEyetv ) ),
262 (uint8_t *)p_buffer );
263 i_len = (int)CFDataGetLength( dataFromEyetv );
265 msg_Dbg( p_access, "%i bytes with id %i received in read function, pushing to core",
266 (int)CFDataGetLength( dataFromEyetv ), lastPacketId );
268 lastForwardedPacketId = lastPacketId;
271 msg_Err( p_access, "you looosed!" );
276 if( p_access->b_die )
282 /*****************************************************************************
284 *****************************************************************************/
285 static int Control( access_t *p_access, int i_query, va_list args )
294 case ACCESS_SET_PAUSE_STATE:
298 case ACCESS_CAN_SEEK:
299 case ACCESS_CAN_FASTSEEK:
300 case ACCESS_CAN_PAUSE:
301 case ACCESS_CAN_CONTROL_PACE:
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:
311 msg_Warn( p_access, "unimplemented query in control" );
315 return VLC_SUCCESS;*/