1 /*****************************************************************************
\r
2 * eyetvplugin.c: Plug-In for the EyeTV software to connect to VLC
\r
3 *****************************************************************************
\r
4 * Copyright (C) 2006-2007 the VideoLAN team
\r
7 * Authors: 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 #include "eyetvplugin.h"
\r
26 #define MAX_PIDS 256
\r
27 #define MAX_ACTIVE_PIDS 256
\r
28 #define MAX_DEVICES 16
\r
29 #define VLC_NOTIFICATION_OBJECT "VLCEyeTVSupport"
\r
35 /* Structure for TS-Packets */
\r
38 unsigned long sync_byte : 8,
\r
39 transport_error_indicator : 1,
\r
40 payload_unit_start_indicator : 1,
\r
41 transport_priority : 1,
\r
43 transport_scrambling_control : 2,
\r
44 adaptation_field_control : 2,
\r
45 continuity_counter : 4;
\r
47 unsigned char data[188-4];
\r
49 } TransportStreamPacket;
\r
54 /* Structure to hold Information on devices */
\r
57 EyeTVPluginDeviceID deviceID;
\r
58 EyeTVPluginDeviceType deviceType;
\r
65 long pids[MAX_PIDS];
\r
67 EyeTVPluginPIDInfo activePIDs[MAX_ACTIVE_PIDS];
\r
68 long activePIDsCount;
\r
73 /* Structure to hold global data to communicate with EyeTV */
\r
76 EyeTVPluginCallbackProc callback;
\r
78 DeviceInfo devices[MAX_DEVICES];
\r
79 long long packetCount;
\r
81 } VLCEyeTVPluginGlobals_t;
\r
83 /* 2nd structure to store our own global data which isn't shared with EyeTV
\r
84 * a bit empty at the moment, but it will get larger as development progresses */
\r
88 CFMessagePortRef messagePortToVLC;
\r
90 } VLCEyeTVPluginOwnGlobals_t;
\r
92 VLCEyeTVPluginOwnGlobals_t *nativeGlobals;
\r
95 /* return the DeviceInfo with ID deviceID */
\r
96 static DeviceInfo *GetDeviceInfo(VLCEyeTVPluginGlobals_t *globals, EyeTVPluginDeviceID deviceID)
\r
102 for( i=0; i<globals->deviceCount; i++)
\r
104 if( globals->devices[i].deviceID == deviceID )
\r
106 return &globals->devices[i];
\r
116 /* initialise the plug-in */
\r
117 static long VLCEyeTVPluginInitialize(VLCEyeTVPluginGlobals_t** globals, long apiVersion, EyeTVPluginCallbackProc callback)
\r
119 printf("VLC media player Plug-In: Initialize\n");
\r
122 /* init our own storage */
\r
123 extern VLCEyeTVPluginOwnGlobals_t *nativeGlobals;
\r
124 nativeGlobals = malloc( sizeof( VLCEyeTVPluginOwnGlobals_t ) );
\r
126 /* notify a potential VLC instance about our initialisation */
\r
127 CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter(),
\r
128 CFSTR("PluginInit"),
\r
129 CFSTR(VLC_NOTIFICATION_OBJECT),
\r
130 /*userInfo*/ NULL,
\r
133 /* init our notification support */
\r
134 CFNotificationCenterAddObserver( CFNotificationCenterGetDistributedCenter(),
\r
135 /* observer */ NULL,
\r
136 /* callBack */ VLCEyeTVPluginGlobalNotificationReceived,
\r
137 /* name, NULL==all */ NULL,
\r
138 CFSTR(VLC_NOTIFICATION_OBJECT),
\r
139 CFNotificationSuspensionBehaviorDeliverImmediately );
\r
141 *globals = (VLCEyeTVPluginGlobals_t *) calloc(1, sizeof( VLCEyeTVPluginGlobals_t ) );
\r
142 ( *globals )->callback = callback;
\r
147 /* we will be terminated soon, clean up */
\r
148 static long VLCEyeTVPluginTerminate(VLCEyeTVPluginGlobals_t *globals)
\r
150 extern VLCEyeTVPluginOwnGlobals_t *nativeGlobals;
\r
152 printf("VLC media player Plug-In: Terminate\n");
\r
156 /* notify a potential VLC instance about our termination */
\r
157 CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter (),
\r
158 CFSTR("PluginQuit"),
\r
159 CFSTR(VLC_NOTIFICATION_OBJECT),
\r
160 /*userInfo*/ NULL,
\r
163 /* remove us from the global notification centre */
\r
164 CFNotificationCenterRemoveEveryObserver( CFNotificationCenterGetDistributedCenter(),
\r
165 (void *)VLCEyeTVPluginGlobalNotificationReceived );
\r
167 /* invalidate and free msg port */
\r
168 if( nativeGlobals->messagePortToVLC )
\r
170 CFMessagePortInvalidate( nativeGlobals->messagePortToVLC );
\r
171 free( nativeGlobals->messagePortToVLC );
\r
172 printf( "msgport invalidated and freed\n" );
\r
175 printf( "no msgport to free\n" );
\r
182 if( nativeGlobals )
\r
183 free( nativeGlobals );
\r
188 /* called when EyeTV asks various stuff about us */
\r
189 static long VLCEyeTVPluginGetInformation(VLCEyeTVPluginGlobals_t *globals, long* outAPIVersion, char* outName, char *outDescription)
\r
191 printf("VLC media player Plug-In: GetInfo\n");
\r
196 if( outAPIVersion )
\r
198 *outAPIVersion = EYETV_PLUGIN_API_VERSION;
\r
203 char* name = "VLC media player Plug-In";
\r
204 strcpy( &outName[0], name);
\r
207 if( outDescription )
\r
209 char* desc = "This Plug-In connects EyeTV to the VLC media player for streaming purposes.";
\r
210 strcpy( &outDescription[0], desc);
\r
217 /* called if we received a global notification */
\r
218 void VLCEyeTVPluginGlobalNotificationReceived( CFNotificationCenterRef center,
\r
221 const void *object,
\r
222 CFDictionaryRef userInfo )
\r
225 char *theName, *theObject;
\r
226 extern VLCEyeTVPluginOwnGlobals_t *nativeGlobals;
\r
228 maxlen = CFStringGetMaximumSizeForEncoding( CFStringGetLength( name ),
\r
229 kCFStringEncodingUTF8) + 1;
\r
230 theName = malloc(maxlen);
\r
231 CFStringGetCString( name,
\r
234 kCFStringEncodingUTF8);
\r
236 maxlen = CFStringGetMaximumSizeForEncoding( CFStringGetLength( name ),
\r
237 kCFStringEncodingUTF8) + 1;
\r
238 theObject = malloc(maxlen);
\r
239 CFStringGetCString( object,
\r
242 kCFStringEncodingUTF8);
\r
243 printf( "notication received with name: %s and object: %s\n", theName, theObject );
\r
245 /* when VLC launches after us, we need to inform it about our existance and the current state of available devices */
\r
246 if( CFStringCompare( name, CFSTR( "VLCOSXGUIInit" ), 0) == kCFCompareEqualTo )
\r
249 CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter (),
\r
250 CFSTR("PluginInit"),
\r
251 CFSTR(VLC_NOTIFICATION_OBJECT),
\r
252 /*userInfo*/ NULL,
\r
254 if( nativeGlobals && ( nativeGlobals->i_deviceCount > 0 ) )
\r
256 /* at least one device is apparently connected */
\r
257 CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter (),
\r
258 CFSTR("DeviceAdded"),
\r
259 CFSTR(VLC_NOTIFICATION_OBJECT),
\r
260 /*userInfo*/ NULL,
\r
265 /* VLC wants us to start sending data */
\r
266 if( CFStringCompare( name, CFSTR( "VLCAccessStartDataSending" ), 0) == kCFCompareEqualTo )
\r
268 nativeGlobals->messagePortToVLC = CFMessagePortCreateRemote( kCFAllocatorDefault,
\r
269 CFSTR("VLCEyeTVMsgPort") );
\r
270 if( nativeGlobals->messagePortToVLC == NULL )
\r
271 printf( "getting messagePortToVLC failed!\n" );
\r
274 nativeGlobals->b_msgPortOpen = TRUE;
\r
275 printf( "msg port opened / data sending switched on\n" );
\r
279 /* VLC wants us to stop sending data */
\r
280 if( CFStringCompare( name, CFSTR( "VLCAccessStopDataSending" ), 0) == kCFCompareEqualTo )
\r
282 nativeGlobals->b_msgPortOpen = FALSE;
\r
283 printf( "data sending switched off\n" );
\r
287 /* called if a device is added */
\r
288 static long VLCEyeTVPluginDeviceAdded(VLCEyeTVPluginGlobals_t *globals, EyeTVPluginDeviceID deviceID, EyeTVPluginDeviceType deviceType)
\r
290 printf("VLC media player Plug-In: Device with type %i and ID %i added\n", (int)deviceType, (int)deviceID);
\r
293 DeviceInfo *deviceInfo;
\r
294 extern VLCEyeTVPluginOwnGlobals_t *nativeGlobals;
\r
299 if( globals->deviceCount < MAX_DEVICES )
\r
301 deviceInfo = &( globals->devices[globals->deviceCount] );
\r
302 memset(deviceInfo, 0, sizeof(DeviceInfo));
\r
304 deviceInfo->deviceID = deviceID;
\r
305 deviceInfo->deviceType = deviceType;
\r
307 globals->deviceCount++;
\r
309 if( nativeGlobals )
\r
310 nativeGlobals->i_deviceCount = globals->deviceCount;
\r
312 /* notify a potential VLC instance about the addition */
\r
313 CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter(),
\r
314 CFSTR("DeviceAdded"),
\r
315 CFSTR(VLC_NOTIFICATION_OBJECT),
\r
316 /*userInfo*/ NULL,
\r
324 /* called if a device is removed */
\r
325 static long VLCEyeTVPluginDeviceRemoved(VLCEyeTVPluginGlobals_t *globals, EyeTVPluginDeviceID deviceID)
\r
327 printf("VLC media player Plug-In: DeviceRemoved\n");
\r
329 extern VLCEyeTVPluginOwnGlobals_t *nativeGlobals;
\r
335 for( i = 0; i < globals->deviceCount; i++ )
\r
337 if ( globals->devices[i].deviceID == deviceID )
\r
339 globals->deviceCount--;
\r
341 if( i<globals->deviceCount )
\r
343 globals->devices[i] = globals->devices[globals->deviceCount];
\r
346 if( nativeGlobals )
\r
347 nativeGlobals->i_deviceCount = globals->deviceCount;
\r
349 /* notify a potential VLC instance about the removal */
\r
350 CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter(),
\r
351 CFSTR("DeviceRemoved"),
\r
352 CFSTR(VLC_NOTIFICATION_OBJECT),
\r
353 /*userInfo*/ NULL,
\r
362 /* This function is called, whenever packets are received by EyeTV. For reasons of performance,
\r
363 * the data is the original data, not a copy. That means, EyeTV waits until this method is
\r
364 * finished. Therefore all in this method should be as fast as possible. */
\r
366 static long VLCEyeTVPluginPacketsArrived(VLCEyeTVPluginGlobals_t *globals, EyeTVPluginDeviceID deviceID, long **packets, long packetsCount)
\r
369 int i, j, isNewPID;
\r
370 TransportStreamPacket *packet;
\r
371 extern VLCEyeTVPluginOwnGlobals_t *nativeGlobals;
\r
372 SInt32 i_returnValue;
\r
373 CFMutableDataRef theMutableRef;
\r
374 uint8_t *p_bufferForSending = malloc(4);
\r
375 bool b_nonSendData;
\r
376 int i_lastSentPacket;
\r
378 if( globals && nativeGlobals )
\r
380 DeviceInfo *deviceInfo = GetDeviceInfo(globals, deviceID);
\r
384 /* alloc the buffer if wanted */
\r
385 if( nativeGlobals->b_msgPortOpen == TRUE )
\r
386 theMutableRef = CFDataCreateMutable( kCFAllocatorDefault, (188) );
\r
388 for( i = 0; i < packetsCount; i++ )
\r
390 packet = ( TransportStreamPacket* )packets[i];
\r
393 /* search for PID */
\r
394 for( j = 0; j < deviceInfo->pidsCount; j++ )
\r
396 if( packet->PID == deviceInfo->pids[j] )
\r
403 /* add new PIDs to the DeviceInfo */
\r
406 printf ("VLC media player Plug-In: SamplePacketsArrived, newPID = %6d\n", packet->PID);
\r
408 if( deviceInfo->pidsCount < MAX_PIDS )
\r
410 deviceInfo->pids[deviceInfo->pidsCount++] = packet->PID;
\r
415 /* forward data to VLC if wanted */
\r
416 /* FIXME: we only receive ARD for now */
\r
417 if( nativeGlobals->b_msgPortOpen == TRUE && (
\r
418 packet->PID == 1401 ||
\r
419 packet->PID == 1402 ||
\r
420 packet->PID == 1400 ||
\r
421 packet->PID == 1404 ||
\r
422 packet->PID == 3070 ||
\r
423 packet->PID == 3072 ||
\r
424 packet->PID == 3074 ||
\r
425 packet->PID == 5074 ||
\r
426 packet->PID == 0 ||
\r
427 packet->PID == 17 ||
\r
428 packet->PID == 19 ||
\r
429 packet->PID == 20 ) )
\r
431 /* in a good world, this wouldn't be necessary */
\r
432 if( theMutableRef == NULL )
\r
433 theMutableRef = CFDataCreateMutable( kCFAllocatorDefault, (188) );
\r
435 /* collect data to send larger packets */
\r
437 /* enlarge buffer if necessary */
\r
439 CFDataIncreaseLength( theMutableRef, 188 );
\r
441 /* add missing header */
\r
442 memcpy( p_bufferForSending, packet, 4 );
\r
443 CFDataAppendBytes( theMutableRef, p_bufferForSending, sizeof(p_bufferForSending) );
\r
445 free( p_bufferForSending );
\r
446 p_bufferForSending = malloc(4);
\r
449 CFDataAppendBytes( theMutableRef, packet->data, sizeof(packet->data) );
\r
451 b_nonSendData = TRUE;
\r
456 globals->packetCount++;
\r
458 if( globals->packetCount%10000 == 0 )
\r
459 printf("-> %lld Packets received so far...\n",globals->packetCount);
\r
462 if( nativeGlobals->b_msgPortOpen == TRUE )
\r
464 printf( "sending %i bytes of data\n", CFDataGetLength( theMutableRef ) );
\r
465 i_returnValue = CFMessagePortSendRequest( nativeGlobals->messagePortToVLC,
\r
466 /* arbitrary int val */ globals->packetCount,
\r
467 /* the data */ theMutableRef,
\r
468 /* no timeout for sending */ 0,
\r
469 /* no timeout for resp */ 0,
\r
470 /* no resp. wanted */ NULL,
\r
472 b_nonSendData = FALSE;
\r
473 i_lastSentPacket = globals->packetCount;
\r
474 if( i_returnValue == kCFMessagePortSendTimeout )
\r
475 printf( "time out while sending\n" );
\r
476 else if( i_returnValue == kCFMessagePortReceiveTimeout )
\r
477 printf( "time out while waiting for resp\n" );
\r
478 else if( i_returnValue == kCFMessagePortIsInvalid )
\r
480 /* suppress any further attemps */
\r
481 printf( "message port is invalid!\n" );
\r
482 nativeGlobals->b_msgPortOpen = FALSE;
\r
484 else if( i_returnValue == kCFMessagePortTransportError )
\r
485 printf( "transport error while sending!\n" );
\r
488 //printf( "success, freeing resources\n" );
\r
489 free( theMutableRef );
\r
490 theMutableRef = CFDataCreateMutable( kCFAllocatorDefault, (188) );
\r
497 printf( "warning: either globals or nativeGlobals are NIL in VLCEyeTVPluginPacketsArrived" );
\r
499 /* clean up before leaving function */
\r
500 //if( nativeGlobals->b_msgPortOpen == TRUE )
\r
501 // free( theMutableRef );
\r
503 free( p_bufferForSending );
\r
508 /* VLCEyeTVPluginServiceChanged,
\r
510 * - *globals : The plug-in Globals
\r
511 * - deviceID : Identifies the active Device
\r
512 * - headendID : The HeadendID, for e300 it's the orbital position of the satelite in
\r
513 * tenth degrees east
\r
514 * - transponderID : The Frequency in kHz
\r
515 * - serviceID : original ServiceID from the DVB-Stream (e300, e400)
\r
516 * - pidList : List of active PIDs
\r
518 * Whenever a service changes, this function is called. Service-related plug-in data should be updated here.
\r
520 static long VLCEyeTVPluginServiceChanged(VLCEyeTVPluginGlobals_t *globals,
\r
521 EyeTVPluginDeviceID deviceID,
\r
523 long transponderID,
\r
525 EyeTVPluginPIDInfo *pidList,
\r
531 printf("\nVLC media player Plug-In: ServiceChanged:\n");
\r
532 printf( "=====================================\n");
\r
536 DeviceInfo *deviceInfo = GetDeviceInfo( globals, deviceID );
\r
539 deviceInfo->headendID = headendID;
\r
540 printf("HeadendID: %ld, ", headendID);
\r
542 deviceInfo->transponderID = transponderID;
\r
543 printf("TransponderID: %ld, ", transponderID);
\r
545 deviceInfo->serviceID = serviceID;
\r
546 printf("ServiceID: %ld\n\n", serviceID);
\r
548 deviceInfo->activePIDsCount = pidsCount;
\r
550 for( i = 0; i < pidsCount; i++ )
\r
552 deviceInfo->activePIDs[i] = pidList[i];
\r
553 printf("Active PID: %ld, type: %ld\n", pidList[i].pid, pidList[i].pidType);
\r
556 deviceInfo->pidsCount = 0;
\r
560 printf( "=====================================\n");
\r
562 /* notify a potential VLC instance about the service change */
\r
563 CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter(),
\r
564 CFSTR("ServiceChanged"),
\r
565 CFSTR(VLC_NOTIFICATION_OBJECT),
\r
566 /*userInfo*/ NULL,
\r
574 /* EyeTVPluginDispatcher,
\r
576 * - selector : See 'EyeTVPluginDefs.h'
\r
577 * - *refCon : The RefCon to the plug-in-related Data
\r
578 * - deviceID : Identifies the Device
\r
579 * - params : Parameters for functioncall
\r
581 * This function is a part of the interface for the communication with EyeTV. If something happens,
\r
582 * EyeTV thinks, we should know of, it calls this function with the corresponding selector. */
\r
586 long EyeTVPluginDispatcher( EyeTVPluginParams* params )
\r
590 switch( params->selector )
\r
592 case kEyeTVPluginSelector_Initialize:
\r
593 result = VLCEyeTVPluginInitialize((VLCEyeTVPluginGlobals_t**)params->refCon,
\r
594 params->initialize.apiVersion, params->initialize.callback);
\r
597 case kEyeTVPluginSelector_Terminate:
\r
598 result = VLCEyeTVPluginTerminate((VLCEyeTVPluginGlobals_t*)params->refCon);
\r
601 case kEyeTVPluginSelector_GetInfo:
\r
602 result = VLCEyeTVPluginGetInformation((VLCEyeTVPluginGlobals_t*)params->refCon,
\r
603 params->info.pluginAPIVersion, params->info.pluginName, params->info.description);
\r
606 case kEyeTVPluginSelector_DeviceAdded:
\r
607 result = VLCEyeTVPluginDeviceAdded((VLCEyeTVPluginGlobals_t*)params->refCon,
\r
608 params->deviceID, params->deviceAdded.deviceType);
\r
611 case kEyeTVPluginSelector_DeviceRemoved:
\r
612 result = VLCEyeTVPluginDeviceRemoved((VLCEyeTVPluginGlobals_t*)params->refCon, params->deviceID);
\r
615 case kEyeTVPluginSelector_PacketsArrived:
\r
616 result = VLCEyeTVPluginPacketsArrived((VLCEyeTVPluginGlobals_t*)params->refCon, params->deviceID,
\r
617 params->packetsArrived.packets, params->packetsArrived.packetCount);
\r
620 case kEyeTVPluginSelector_ServiceChanged:
\r
621 result = VLCEyeTVPluginServiceChanged((VLCEyeTVPluginGlobals_t*)params->refCon,
\r
622 params->deviceID, params->serviceChanged.headendID,
\r
623 params->serviceChanged.transponderID, params->serviceChanged.serviceID,
\r
624 params->serviceChanged.pidList, params->serviceChanged.pidCount);
\r