1 /*****************************************************************************
2 * eyetvplugin.c: Plug-In for the EyeTV software to connect to VLC
3 *****************************************************************************
4 * Copyright (C) 2006-2007 the VideoLAN team
7 * Authors: 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 #include "eyetvplugin.h"
26 #include <sys/types.h>
27 #include <sys/socket.h>
33 #define MAX_ACTIVE_PIDS 256
34 #define MAX_DEVICES 16
35 #define VLC_NOTIFICATION_OBJECT "VLCEyeTVSupport"
40 /* Structure for TS-Packets */
43 uint32_t sync_byte : 8,
44 transport_error_indicator : 1,
45 payload_unit_start_indicator : 1,
46 transport_priority : 1,
48 transport_scrambling_control : 2,
49 adaptation_field_control : 2,
50 continuity_counter : 4;
53 } TransportStreamPacket;
58 /* Structure to hold global data to communicate with EyeTV */
61 EyeTVPluginCallbackProc callback;
62 /* Structure to hold current active service */
63 EyeTVPluginDeviceID activeDeviceID;
65 EyeTVPluginPIDInfo activePIDs[MAX_ACTIVE_PIDS];
66 } VLCEyeTVPluginGlobals_t;
68 /* following globals limits us to one VLC instance using EyeTV */
69 static int i_deviceCount;
74 /* initialise the plug-in */
75 static long VLCEyeTVPluginInitialize(VLCEyeTVPluginGlobals_t** globals, long apiVersion, EyeTVPluginCallbackProc callback)
77 printf("VLC media player Plug-In: Initialize\n");
80 /* init our own storage */
84 /* notify a potential VLC instance about our initialisation */
85 CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter(),
87 CFSTR(VLC_NOTIFICATION_OBJECT),
91 /* init our notification support */
92 CFNotificationCenterAddObserver( CFNotificationCenterGetDistributedCenter(),
94 /* callBack */ VLCEyeTVPluginGlobalNotificationReceived,
95 /* name, NULL==all */ NULL,
96 CFSTR(VLC_NOTIFICATION_OBJECT),
97 CFNotificationSuspensionBehaviorDeliverImmediately );
99 *globals = (VLCEyeTVPluginGlobals_t *) calloc(1, sizeof( VLCEyeTVPluginGlobals_t ) );
100 ( *globals )->callback = callback;
105 /* we will be terminated soon, clean up */
106 static long VLCEyeTVPluginTerminate(VLCEyeTVPluginGlobals_t *globals)
110 printf("VLC media player Plug-In: Terminate\n");
112 /* notify a potential VLC instance about our termination */
113 CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter (),
115 CFSTR(VLC_NOTIFICATION_OBJECT),
119 /* remove us from the global notification centre */
120 CFNotificationCenterRemoveEveryObserver( CFNotificationCenterGetDistributedCenter(),
121 (void *)VLCEyeTVPluginGlobalNotificationReceived );
123 /* close data connection */
124 if( i_vlcSock != -1 )
134 /* called when EyeTV asks various stuff about us */
135 static long VLCEyeTVPluginGetInformation(VLCEyeTVPluginGlobals_t *globals, long* outAPIVersion, char* outName, char *outDescription)
137 printf("VLC media player Plug-In: GetInfo\n");
144 *outAPIVersion = EYETV_PLUGIN_API_VERSION;
149 strcpy( outName, "VLC media player Plug-In");
154 strcpy( outDescription, "This Plug-In connects EyeTV to the VLC media player for streaming purposes.");
161 /* called if we received a global notification */
162 void VLCEyeTVPluginGlobalNotificationReceived( CFNotificationCenterRef center,
166 CFDictionaryRef userInfo )
168 /* when VLC launches after us, we need to inform it about our existance and the current state of available devices */
169 if( CFStringCompare( name, CFSTR( "VLCOSXGUIInit" ), 0) == kCFCompareEqualTo )
172 CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter (),
174 CFSTR(VLC_NOTIFICATION_OBJECT),
177 if( i_deviceCount > 0 )
179 /* at least one device is apparently connected */
180 CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter (),
181 CFSTR("DeviceAdded"),
182 CFSTR(VLC_NOTIFICATION_OBJECT),
188 /* VLC wants us to start sending data */
189 if( CFStringCompare( name, CFSTR( "VLCAccessStartDataSending" ), 0) == kCFCompareEqualTo )
191 if( i_vlcSock == -1 )
195 /* set-up data socket */
196 peerSock = socket(AF_UNIX, SOCK_STREAM, 0);
199 struct sockaddr_un peerAddr;
200 /* set-up connection address */
201 memset(&peerAddr, 0, sizeof(peerAddr));
202 peerAddr.sun_family = AF_UNIX;
203 strncpy(peerAddr.sun_path, "/tmp/.vlc-eyetv-bridge", sizeof(peerAddr.sun_path)-1);
206 printf("data connect in progess...\n");
207 if( connect(peerSock, (struct sockaddr *)&peerAddr, sizeof(struct sockaddr_un)) != -1 )
209 printf("data sending switched on\n");
211 i_vlcSock = peerSock;
214 printf("connect data socket failed (errno=%d)\n", errno );
217 printf("create data socket failed (errno=%d)\n", errno );
221 /* VLC wants us to stop sending data */
222 if( CFStringCompare( name, CFSTR( "VLCAccessStopDataSending" ), 0) == kCFCompareEqualTo )
224 if( i_vlcSock != -1 )
228 printf( "data sending switched off\n" );
233 /* called if a device is added */
234 static long VLCEyeTVPluginDeviceAdded(VLCEyeTVPluginGlobals_t *globals, EyeTVPluginDeviceID deviceID, EyeTVPluginDeviceType deviceType)
236 printf("VLC media player Plug-In: Device with type %i and ID %i added\n", (int)deviceType, (int)deviceID);
243 if( 1 == i_deviceCount )
245 /* notify a potential VLC instance about the addition */
246 CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter(),
247 CFSTR("DeviceAdded"),
248 CFSTR(VLC_NOTIFICATION_OBJECT),
256 /* called if a device is removed */
257 static long VLCEyeTVPluginDeviceRemoved(VLCEyeTVPluginGlobals_t *globals, EyeTVPluginDeviceID deviceID)
259 printf("VLC media player Plug-In: DeviceRemoved\n");
264 if( 0 == i_deviceCount )
266 /* notify a potential VLC instance about the removal */
267 CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter(),
268 CFSTR("DeviceRemoved"),
269 CFSTR(VLC_NOTIFICATION_OBJECT),
273 if( (i_vlcSock != -1) && (deviceID == globals->activeDeviceID) )
277 printf( "data sending switched off\n" );
283 /* This function is called, whenever packets are received by EyeTV. For reasons of performance,
284 * the data is the original data, not a copy. That means, EyeTV waits until this method is
285 * finished. Therefore all in this method should be as fast as possible. */
286 static long VLCEyeTVPluginPacketsArrived(VLCEyeTVPluginGlobals_t *globals, EyeTVPluginDeviceID deviceID, long **packets, long packetsCount)
290 /* check if data connection is active */
291 if( i_vlcSock != -1 )
293 if( deviceID == globals->activeDeviceID )
295 long pidCount = globals->activePIDsCount;
298 uint8_t packetBuffer[sizeof(TransportStreamPacket)*20];
299 int packetBufferSize = 0;
300 while( packetsCount )
302 /* apply PID filtering, only PIDs in active service for device are sent through */
303 long pid = ntohl(**packets)>>8 & 0x1FFFL;
304 /* ignore NULL packets */
308 for( i=0; i<pidCount; ++i )
310 if( globals->activePIDs[i].pid == pid )
312 if( packetBufferSize <= (sizeof(packetBuffer)-sizeof(TransportStreamPacket)) )
314 /* copy packet in our buffer */
315 memcpy(packetBuffer+packetBufferSize, *packets, sizeof(TransportStreamPacket));
316 packetBufferSize += sizeof(TransportStreamPacket);
320 /* flush buffer to VLC */
321 ssize_t sent = write(i_vlcSock, packetBuffer, packetBufferSize);
322 if( sent != packetBufferSize )
325 printf("data sending failed (errno=%d)\n", errno);
327 printf("data sending incomplete (sent=%d)\n", sent);
332 packetBufferSize = 0;
336 /* if we assume that consecutive packets would have the same PID in most cases,
337 it would therefore speed up filtering to reorder activePIDs list based on pid
339 EyeTVPluginPIDInfo swap = globals->activePIDs[i];
342 register int c = i--;
343 globals->activePIDs[c] = globals->activePIDs[i];
346 globals->activePIDs[i] = swap;
349 if( pid && globals->activePIDs[0].pidType != kEyeTVPIDType_PMT )
351 /* to save on CPU, prevent EyeTV from mirroring that program by blocking video & audio packets
352 by changing all packets but PAT and PMT to NULL PID */
353 #if defined(WORDS_BIGENDIAN)
354 **packets |= 0x001FFF00L;
356 **packets |= 0x00FFF800L;
359 /* done filtering on this packet, move on to next packet */
367 if( packetBufferSize )
369 /* flush buffer to VLC */
370 ssize_t sent = write(i_vlcSock, packetBuffer, packetBufferSize);
371 if( sent != packetBufferSize )
374 printf("data sending failed (errno=%d)\n", errno);
376 printf("data sending incomplete (sent=%d)\n", sent);
389 /* VLCEyeTVPluginServiceChanged,
391 * - *globals : The plug-in Globals
392 * - deviceID : Identifies the active Device
393 * - headendID : The HeadendID, for e300 it's the orbital position of the satelite in
395 * - transponderID : The Frequency in kHz
396 * - serviceID : original ServiceID from the DVB-Stream (e300, e400)
397 * - pidList : List of active PIDs
399 * Whenever a service changes, this function is called. Service-related plug-in data should be updated here.
401 static long VLCEyeTVPluginServiceChanged(VLCEyeTVPluginGlobals_t *globals,
402 EyeTVPluginDeviceID deviceID,
406 EyeTVPluginPIDInfo *pidList,
412 printf("\nVLC media player Plug-In: ServiceChanged:\n");
413 printf( "=====================================\n");
417 printf("DeviceID: %ld, ", deviceID);
418 printf("HeadendID: %ld, ", headendID);
419 printf("TransponderID: %ld, ", transponderID);
420 printf("ServiceID: %ld\n\n", serviceID);
422 globals->activeDeviceID = deviceID;
423 globals->activePIDsCount = pidsCount;
425 /* need active PIDs for packet filtering */
426 for( i = 0; i < pidsCount; i++ )
428 globals->activePIDs[i] = pidList[i];
429 printf("Active PID: %ld, type: %ld\n", pidList[i].pid, pidList[i].pidType);
432 printf( "=====================================\n");
434 /* notify a potential VLC instance about the service change */
435 CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter(),
436 CFSTR("ServiceChanged"),
437 CFSTR(VLC_NOTIFICATION_OBJECT),
446 /* EyeTVPluginDispatcher,
448 * - selector : See 'EyeTVPluginDefs.h'
449 * - *refCon : The RefCon to the plug-in-related Data
450 * - deviceID : Identifies the Device
451 * - params : Parameters for functioncall
453 * This function is a part of the interface for the communication with EyeTV. If something happens,
454 * EyeTV thinks, we should know of, it calls this function with the corresponding selector. */
458 long EyeTVPluginDispatcher( EyeTVPluginParams* params )
462 switch( params->selector )
464 case kEyeTVPluginSelector_Initialize:
465 result = VLCEyeTVPluginInitialize((VLCEyeTVPluginGlobals_t**)params->refCon,
466 params->initialize.apiVersion, params->initialize.callback);
469 case kEyeTVPluginSelector_Terminate:
470 result = VLCEyeTVPluginTerminate((VLCEyeTVPluginGlobals_t*)params->refCon);
473 case kEyeTVPluginSelector_GetInfo:
474 result = VLCEyeTVPluginGetInformation((VLCEyeTVPluginGlobals_t*)params->refCon,
475 params->info.pluginAPIVersion, params->info.pluginName, params->info.description);
478 case kEyeTVPluginSelector_DeviceAdded:
479 result = VLCEyeTVPluginDeviceAdded((VLCEyeTVPluginGlobals_t*)params->refCon,
480 params->deviceID, params->deviceAdded.deviceType);
483 case kEyeTVPluginSelector_DeviceRemoved:
484 result = VLCEyeTVPluginDeviceRemoved((VLCEyeTVPluginGlobals_t*)params->refCon, params->deviceID);
487 case kEyeTVPluginSelector_PacketsArrived:
488 result = VLCEyeTVPluginPacketsArrived((VLCEyeTVPluginGlobals_t*)params->refCon, params->deviceID,
489 params->packetsArrived.packets, params->packetsArrived.packetCount);
492 case kEyeTVPluginSelector_ServiceChanged:
493 result = VLCEyeTVPluginServiceChanged((VLCEyeTVPluginGlobals_t*)params->refCon,
494 params->deviceID, params->serviceChanged.headendID,
495 params->serviceChanged.transponderID, params->serviceChanged.serviceID,
496 params->serviceChanged.pidList, params->serviceChanged.pidCount);