]> git.sesse.net Git - vlc/blob - modules/access/eyetv.m
Add video and audio aliases to udev discovery
[vlc] / modules / access / eyetv.m
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 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_access.h>
35
36 #include <vlc_network.h>
37
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <sys/un.h>
41 #include <unistd.h>
42
43 #import <Foundation/Foundation.h>
44
45 #define MTU 65535
46
47 /* TODO:
48  * watch for PluginQuit or DeviceRemoved to stop output to VLC's core then */
49
50 /*****************************************************************************
51  * Module descriptior
52  *****************************************************************************/
53 static int  Open ( vlc_object_t * );
54 static void Close( vlc_object_t * );
55
56 #define CHANNEL_TEXT N_("Channel number")
57 #define CHANNEL_LONGTEXT N_( \
58     "EyeTV program number, or use 0 for last channel, " \
59     "-1 for S-Video input, -2 for Composite input" )
60
61 vlc_module_begin ()
62     set_shortname( "EyeTV" )
63     set_description( N_("EyeTV input") )
64     set_category( CAT_INPUT )
65     set_subcategory( SUBCAT_INPUT_ACCESS )
66
67     add_integer( "eyetv-channel", 0,
68                  CHANNEL_TEXT, CHANNEL_LONGTEXT, false )
69
70     set_capability( "access", 0 )
71     add_shortcut( "eyetv" )
72     set_callbacks( Open, Close )
73 vlc_module_end ()
74
75 /*****************************************************************************
76  * Access: local prototypes
77  *****************************************************************************/
78 struct access_sys_t
79 {
80     int eyetvSock;
81 };
82
83 static block_t *BlockRead( access_t *);
84 static int Control( access_t *, int, va_list );
85
86 static void selectChannel( vlc_object_t *p_this, int theChannelNum )
87 {
88     NSAppleScript *script;
89     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
90     switch( theChannelNum )
91     {
92         case -2: // Composite
93             script = [[NSAppleScript alloc] initWithSource:
94                         @"tell application \"EyeTV\"\n"
95                          "  input_change input source composite video input\n"
96                          "  volume_change level 0\n"
97                          "  show player_window\n"
98                          "  tell application \"System Events\" to set visible of process \"EyeTV\" to false\n"
99                          "end tell"];
100             break;
101         case -1: // S-Video
102             script = [[NSAppleScript alloc] initWithSource:
103                         @"tell application \"EyeTV\"\n"
104                          "  input_change input source S video input\n"
105                          "  volume_change level 0\n"
106                          "  show player_window\n"
107                          "  tell application \"System Events\" to set visible of process \"EyeTV\" to false\n"
108                          "end tell"];
109             break;
110         case 0: // Last
111             script = [[NSAppleScript alloc] initWithSource:
112                         @"tell application \"EyeTV\"\n"
113                          "  volume_change level 0\n"
114                          "  show player_window\n"
115                          "  tell application \"System Events\" to set visible of process \"EyeTV\" to false\n"
116                          "end tell"];
117             break;
118         default:
119             if( theChannelNum > 0 )
120             {
121                 NSString *channel_change = [NSString stringWithFormat:
122                     @"tell application \"EyeTV\"\n"
123                      "  channel_change channel number %d\n"
124                      "  volume_change level 0\n"
125                      "  show player_window\n"
126                      "  tell application \"System Events\" to set visible of process \"EyeTV\" to false\n"
127                      "end tell", theChannelNum];
128                 script = [[NSAppleScript alloc] initWithSource:channel_change];
129             }
130             else
131                 return;
132     }
133     NSDictionary *errorDict;
134     NSAppleEventDescriptor *descriptor = [script executeAndReturnError:&errorDict];
135     if( nil == descriptor ) 
136     {
137         NSString *errorString = [errorDict objectForKey:NSAppleScriptErrorMessage];
138         msg_Err( p_this, "EyeTV source change failed with error status '%s'", [errorString UTF8String] );
139     }
140     [script release];
141     [pool release];
142 }
143
144 /*****************************************************************************
145  * Open: sets up the module and its threads
146  *****************************************************************************/
147 static int Open( vlc_object_t *p_this )
148 {
149     access_t        *p_access = (access_t *)p_this;
150     access_sys_t    *p_sys;
151
152     struct sockaddr_un publicAddr, peerAddr;
153     int publicSock;
154  
155     /* Init p_access */
156     access_InitFields( p_access );
157     ACCESS_SET_CALLBACKS( NULL, BlockRead, Control, NULL );
158     p_sys = p_access->p_sys = calloc( 1, sizeof( access_sys_t ) );
159     if( !p_sys )
160         return VLC_ENOMEM;
161
162     msg_Dbg( p_access, "coming up" );
163     selectChannel( p_this, var_InheritInteger( p_access, "eyetv-channel" ) );
164
165     /* socket */
166     memset(&publicAddr, 0, sizeof(publicAddr));
167     publicAddr.sun_family = AF_UNIX;
168     strncpy(publicAddr.sun_path, "/tmp/.vlc-eyetv-bridge", sizeof(publicAddr.sun_path)-1);
169     /* remove previous public path if it wasn't cleanly removed */
170     if( (0 != unlink(publicAddr.sun_path)) && (ENOENT != errno) )
171     {
172         msg_Err( p_access, "local socket path is not usable (errno=%d)", errno );
173         free( p_sys );
174         return VLC_EGENERIC;
175     }
176
177     publicSock = socket(PF_UNIX, SOCK_STREAM, 0);
178     if( publicSock == -1 )
179     {
180         msg_Err( p_access, "create local socket failed (errno=%d)", errno );
181         free( p_sys );
182         return VLC_EGENERIC;
183     }
184
185     if( bind(publicSock, (struct sockaddr *)&publicAddr, sizeof(struct sockaddr_un)) == -1 )
186     {
187         msg_Err( p_access, "bind local socket failed (errno=%d)", errno );
188         close( publicSock );
189         free( p_sys );
190         return VLC_EGENERIC;
191     }
192
193     /* we are not expecting more than one connection */
194     if( listen(publicSock, 1) == -1 )
195     {
196         msg_Err( p_access, "cannot accept connection (errno=%d)", errno );
197         close( publicSock );
198         free( p_sys );
199         return VLC_EGENERIC;
200     }
201     else
202     {
203         socklen_t peerSockLen = sizeof(struct sockaddr_un);
204         int peerSock;
205
206         /* tell the EyeTV plugin to open start sending */
207         CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter (),
208                                               CFSTR("VLCAccessStartDataSending"),
209                                               CFSTR("VLCEyeTVSupport"),
210                                               /*userInfo*/ NULL,
211                                               TRUE );
212
213         msg_Dbg( p_access, "plugin notified" );
214
215         peerSock = accept(publicSock, (struct sockaddr *)&peerAddr, &peerSockLen);
216         if( peerSock == -1 )
217         {
218             msg_Err( p_access, "cannot wait for connection (errno=%d)", errno );
219             close( publicSock );
220             free( p_sys );
221             return VLC_EGENERIC;
222         }
223
224         msg_Dbg( p_access, "plugin connected" );
225
226         p_sys->eyetvSock = peerSock;
227
228         /* remove public access */
229         close(publicSock);
230         unlink(publicAddr.sun_path);
231     }
232     return VLC_SUCCESS;
233 }
234
235 /*****************************************************************************
236  * Close: closes msg-port, free resources
237  *****************************************************************************/
238 static void Close( vlc_object_t *p_this )
239 {
240     access_t     *p_access = (access_t *)p_this;
241     access_sys_t *p_sys = p_access->p_sys;
242  
243     msg_Dbg( p_access, "closing" );
244  
245     /* tell the EyeTV plugin to close its msg port and stop sending */
246     CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter (),
247                                           CFSTR("VLCAccessStopDataSending"),
248                                           CFSTR("VLCEyeTVSupport"),
249                                           /*userInfo*/ NULL,
250                                           TRUE );
251  
252     msg_Dbg( p_access, "plugin notified" );
253
254         close(p_sys->eyetvSock);
255  
256     msg_Dbg( p_access, "msg port closed and freed" );
257  
258     free( p_sys );
259 }
260
261 /*****************************************************************************
262 * BlockRead: forwarding data from EyeTV plugin which was received above
263 *****************************************************************************/
264 static block_t *BlockRead( access_t *p_access )
265 {
266     access_sys_t *p_sys = p_access->p_sys;
267     block_t      *p_block;
268     ssize_t len;
269
270     if( p_access->info.b_eof )
271         return NULL;
272
273     /* Read data */
274     p_block = block_New( p_access, MTU );
275     len = net_Read( p_access, p_sys->eyetvSock, NULL,
276                     p_block->p_buffer, MTU, false );
277
278     if( len < 0 )
279     {
280         block_Release( p_block );
281         return NULL;
282     }
283
284     return block_Realloc( p_block, 0, p_block->i_buffer = len );
285 }
286
287 /*****************************************************************************
288  * Control:
289  *****************************************************************************/
290 static int Control( access_t *p_access, int i_query, va_list args )
291 {
292     bool   *pb_bool;
293     int          *pi_int;
294     int64_t      *pi_64;
295     access_sys_t  *p_sys = (access_sys_t *) p_access->p_sys;
296  
297     switch( i_query )
298     {
299         case ACCESS_CAN_SEEK:
300         case ACCESS_CAN_FASTSEEK:
301             pb_bool = (bool*)va_arg( args, bool* );
302             *pb_bool = false;
303             break;
304         case ACCESS_CAN_PAUSE:
305             pb_bool = (bool*)va_arg( args, bool* );
306             *pb_bool = false;
307             break;
308         case ACCESS_CAN_CONTROL_PACE:
309             pb_bool = (bool*)va_arg( args, bool* );
310             *pb_bool = false;
311             break;
312
313         /* */
314         case ACCESS_GET_PTS_DELAY:
315             pi_64 = (int64_t*)va_arg( args, int64_t * );
316             *pi_64 =
317                 INT64_C(1000) * var_InheritInteger( p_access, "live-caching" );
318             break;
319         
320         case ACCESS_SET_PAUSE_STATE:
321         case ACCESS_GET_TITLE_INFO:
322         case ACCESS_SET_TITLE:
323         case ACCESS_SET_SEEKPOINT:
324         case ACCESS_SET_PRIVATE_ID_STATE:
325         case ACCESS_GET_CONTENT_TYPE:
326             return VLC_EGENERIC;
327  
328         default:
329             msg_Warn( p_access, "unimplemented query in control" );
330             return VLC_EGENERIC;
331  
332     }
333     return VLC_SUCCESS;
334 }