Commit 36868ee9 authored by damienf's avatar damienf

eyetv: latest version of EyeTV capture plugin, needs lotta testing

parent 5266ede1
/*****************************************************************************
* eyetvplugin.c: Plug-In for the EyeTV software to connect to VLC
*****************************************************************************
* Copyright (C) 2006-2007 the VideoLAN team
* $Id$
*
* Authors: Felix Kühne <fkuehne at videolan dot org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "eyetvplugin.h"
#define MAX_PIDS 256
#define MAX_ACTIVE_PIDS 256
#define MAX_DEVICES 16
#define VLC_NOTIFICATION_OBJECT "VLCEyeTVSupport"
#pragma push
#pragma pack(1)
/* Structure for TS-Packets */
typedef struct
{
unsigned long sync_byte : 8,
transport_error_indicator : 1,
payload_unit_start_indicator : 1,
transport_priority : 1,
PID : 13,
transport_scrambling_control : 2,
adaptation_field_control : 2,
continuity_counter : 4;
unsigned char data[188-4];
} TransportStreamPacket;
#pragma pop
/* Structure to hold Information on devices */
typedef struct
{
EyeTVPluginDeviceID deviceID;
EyeTVPluginDeviceType deviceType;
long headendID;
long transponderID;
long serviceID;
long pidsCount;
long pids[MAX_PIDS];
EyeTVPluginPIDInfo activePIDs[MAX_ACTIVE_PIDS];
long activePIDsCount;
} DeviceInfo;
/* Structure to hold global data to communicate with EyeTV */
typedef struct
{
EyeTVPluginCallbackProc callback;
long deviceCount;
DeviceInfo devices[MAX_DEVICES];
long long packetCount;
} VLCEyeTVPluginGlobals_t;
/* 2nd structure to store our own global data which isn't shared with EyeTV
* a bit empty at the moment, but it will get larger as development progresses */
typedef struct
{
int i_deviceCount;
CFMessagePortRef messagePortToVLC;
bool b_msgPortOpen;
} VLCEyeTVPluginOwnGlobals_t;
VLCEyeTVPluginOwnGlobals_t *nativeGlobals;
/* return the DeviceInfo with ID deviceID */
static DeviceInfo *GetDeviceInfo(VLCEyeTVPluginGlobals_t *globals, EyeTVPluginDeviceID deviceID)
{
int i;
if( globals )
{
for( i=0; i<globals->deviceCount; i++)
{
if( globals->devices[i].deviceID == deviceID )
{
return &globals->devices[i];
}
}
}
return NULL;
}
#pragma mark -
/* initialise the plug-in */
static long VLCEyeTVPluginInitialize(VLCEyeTVPluginGlobals_t** globals, long apiVersion, EyeTVPluginCallbackProc callback)
{
printf("VLC media player Plug-In: Initialize\n");
long result = 0;
/* init our own storage */
extern VLCEyeTVPluginOwnGlobals_t *nativeGlobals;
nativeGlobals = malloc( sizeof( VLCEyeTVPluginOwnGlobals_t ) );
/* notify a potential VLC instance about our initialisation */
CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter(),
CFSTR("PluginInit"),
CFSTR(VLC_NOTIFICATION_OBJECT),
/*userInfo*/ NULL,
TRUE );
/* init our notification support */
CFNotificationCenterAddObserver( CFNotificationCenterGetDistributedCenter(),
/* observer */ NULL,
/* callBack */ VLCEyeTVPluginGlobalNotificationReceived,
/* name, NULL==all */ NULL,
CFSTR(VLC_NOTIFICATION_OBJECT),
CFNotificationSuspensionBehaviorDeliverImmediately );
*globals = (VLCEyeTVPluginGlobals_t *) calloc(1, sizeof( VLCEyeTVPluginGlobals_t ) );
( *globals )->callback = callback;
return result;
}
/* we will be terminated soon, clean up */
static long VLCEyeTVPluginTerminate(VLCEyeTVPluginGlobals_t *globals)
{
extern VLCEyeTVPluginOwnGlobals_t *nativeGlobals;
printf("VLC media player Plug-In: Terminate\n");
long result = 0;
/* notify a potential VLC instance about our termination */
CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter (),
CFSTR("PluginQuit"),
CFSTR(VLC_NOTIFICATION_OBJECT),
/*userInfo*/ NULL,
TRUE );
/* remove us from the global notification centre */
CFNotificationCenterRemoveEveryObserver( CFNotificationCenterGetDistributedCenter(),
(void *)VLCEyeTVPluginGlobalNotificationReceived );
/* invalidate and free msg port */
if( nativeGlobals->messagePortToVLC )
{
CFMessagePortInvalidate( nativeGlobals->messagePortToVLC );
free( nativeGlobals->messagePortToVLC );
printf( "msgport invalidated and freed\n" );
}
else
printf( "no msgport to free\n" );
if( globals )
{
free( globals );
}
if( nativeGlobals )
free( nativeGlobals );
return result;
}
/* called when EyeTV asks various stuff about us */
static long VLCEyeTVPluginGetInformation(VLCEyeTVPluginGlobals_t *globals, long* outAPIVersion, char* outName, char *outDescription)
{
printf("VLC media player Plug-In: GetInfo\n");
long result = 0;
if( globals )
{
if( outAPIVersion )
{
*outAPIVersion = EYETV_PLUGIN_API_VERSION;
}
if( outName )
{
char* name = "VLC media player Plug-In";
strcpy( &outName[0], name);
}
if( outDescription )
{
char* desc = "This Plug-In connects EyeTV to the VLC media player for streaming purposes.";
strcpy( &outDescription[0], desc);
}
}
return result;
}
/* called if we received a global notification */
void VLCEyeTVPluginGlobalNotificationReceived( CFNotificationCenterRef center,
void *observer,
CFStringRef name,
const void *object,
CFDictionaryRef userInfo )
{
CFIndex maxlen;
char *theName, *theObject;
extern VLCEyeTVPluginOwnGlobals_t *nativeGlobals;
maxlen = CFStringGetMaximumSizeForEncoding( CFStringGetLength( name ),
kCFStringEncodingUTF8) + 1;
theName = malloc(maxlen);
CFStringGetCString( name,
theName,
maxlen,
kCFStringEncodingUTF8);
maxlen = CFStringGetMaximumSizeForEncoding( CFStringGetLength( name ),
kCFStringEncodingUTF8) + 1;
theObject = malloc(maxlen);
CFStringGetCString( object,
theObject,
maxlen,
kCFStringEncodingUTF8);
printf( "notication received with name: %s and object: %s\n", theName, theObject );
/* when VLC launches after us, we need to inform it about our existance and the current state of available devices */
if( CFStringCompare( name, CFSTR( "VLCOSXGUIInit" ), 0) == kCFCompareEqualTo )
{
/* we're here */
CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter (),
CFSTR("PluginInit"),
CFSTR(VLC_NOTIFICATION_OBJECT),
/*userInfo*/ NULL,
TRUE );
if( nativeGlobals && ( nativeGlobals->i_deviceCount > 0 ) )
{
/* at least one device is apparently connected */
CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter (),
CFSTR("DeviceAdded"),
CFSTR(VLC_NOTIFICATION_OBJECT),
/*userInfo*/ NULL,
TRUE );
}
}
/* VLC wants us to start sending data */
if( CFStringCompare( name, CFSTR( "VLCAccessStartDataSending" ), 0) == kCFCompareEqualTo )
{
nativeGlobals->messagePortToVLC = CFMessagePortCreateRemote( kCFAllocatorDefault,
CFSTR("VLCEyeTVMsgPort") );
if( nativeGlobals->messagePortToVLC == NULL )
printf( "getting messagePortToVLC failed!\n" );
else
{
nativeGlobals->b_msgPortOpen = TRUE;
printf( "msg port opened / data sending switched on\n" );
}
}
/* VLC wants us to stop sending data */
if( CFStringCompare( name, CFSTR( "VLCAccessStopDataSending" ), 0) == kCFCompareEqualTo )
{
nativeGlobals->b_msgPortOpen = FALSE;
printf( "data sending switched off\n" );
}
}
/* called if a device is added */
static long VLCEyeTVPluginDeviceAdded(VLCEyeTVPluginGlobals_t *globals, EyeTVPluginDeviceID deviceID, EyeTVPluginDeviceType deviceType)
{
printf("VLC media player Plug-In: Device with type %i and ID %i added\n", (int)deviceType, (int)deviceID);
long result = 0;
DeviceInfo *deviceInfo;
extern VLCEyeTVPluginOwnGlobals_t *nativeGlobals;
if( globals )
{
if( globals->deviceCount < MAX_DEVICES )
{
deviceInfo = &( globals->devices[globals->deviceCount] );
memset(deviceInfo, 0, sizeof(DeviceInfo));
deviceInfo->deviceID = deviceID;
deviceInfo->deviceType = deviceType;
globals->deviceCount++;
if( nativeGlobals )
nativeGlobals->i_deviceCount = globals->deviceCount;
/* notify a potential VLC instance about the addition */
CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter(),
CFSTR("DeviceAdded"),
CFSTR(VLC_NOTIFICATION_OBJECT),
/*userInfo*/ NULL,
TRUE );
}
}
return result;
}
/* called if a device is removed */
static long VLCEyeTVPluginDeviceRemoved(VLCEyeTVPluginGlobals_t *globals, EyeTVPluginDeviceID deviceID)
{
printf("VLC media player Plug-In: DeviceRemoved\n");
extern VLCEyeTVPluginOwnGlobals_t *nativeGlobals;
long result = 0;
int i;
if( globals )
{
for( i = 0; i < globals->deviceCount; i++ )
{
if ( globals->devices[i].deviceID == deviceID )
{
globals->deviceCount--;
if( i<globals->deviceCount )
{
globals->devices[i] = globals->devices[globals->deviceCount];
}
if( nativeGlobals )
nativeGlobals->i_deviceCount = globals->deviceCount;
/* notify a potential VLC instance about the removal */
CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter(),
CFSTR("DeviceRemoved"),
CFSTR(VLC_NOTIFICATION_OBJECT),
/*userInfo*/ NULL,
TRUE );
}
}
}
return result;
}
/* This function is called, whenever packets are received by EyeTV. For reasons of performance,
* the data is the original data, not a copy. That means, EyeTV waits until this method is
* finished. Therefore all in this method should be as fast as possible. */
int i=0;
static long VLCEyeTVPluginPacketsArrived(VLCEyeTVPluginGlobals_t *globals, EyeTVPluginDeviceID deviceID, long **packets, long packetsCount)
{
long result = 0;
int i, j, isNewPID;
TransportStreamPacket *packet;
extern VLCEyeTVPluginOwnGlobals_t *nativeGlobals;
SInt32 i_returnValue;
CFMutableDataRef theMutableRef;
uint8_t *p_bufferForSending = malloc(4);
bool b_nonSendData;
int i_lastSentPacket;
if( globals && nativeGlobals )
{
DeviceInfo *deviceInfo = GetDeviceInfo(globals, deviceID);
if( deviceInfo )
{
/* alloc the buffer if wanted */
if( nativeGlobals->b_msgPortOpen == TRUE )
theMutableRef = CFDataCreateMutable( kCFAllocatorDefault, (188) );
for( i = 0; i < packetsCount; i++ )
{
packet = ( TransportStreamPacket* )packets[i];
isNewPID = 1;
/* search for PID */
for( j = 0; j < deviceInfo->pidsCount; j++ )
{
if( packet->PID == deviceInfo->pids[j] )
{
isNewPID = 0;
break;
}
}
/* add new PIDs to the DeviceInfo */
if( isNewPID )
{
printf ("VLC media player Plug-In: SamplePacketsArrived, newPID = %6d\n", packet->PID);
if( deviceInfo->pidsCount < MAX_PIDS )
{
deviceInfo->pids[deviceInfo->pidsCount++] = packet->PID;
}
}
else
{
/* forward data to VLC if wanted */
/* FIXME: we only receive ARD for now */
if( nativeGlobals->b_msgPortOpen == TRUE && (
packet->PID == 1401 ||
packet->PID == 1402 ||
packet->PID == 1400 ||
packet->PID == 1404 ||
packet->PID == 3070 ||
packet->PID == 3072 ||
packet->PID == 3074 ||
packet->PID == 5074 ||
packet->PID == 0 ||
packet->PID == 17 ||
packet->PID == 19 ||
packet->PID == 20 ) )
{
/* in a good world, this wouldn't be necessary */
if( theMutableRef == NULL )
theMutableRef = CFDataCreateMutable( kCFAllocatorDefault, (188) );
/* collect data to send larger packets */
/* enlarge buffer if necessary */
if( i > 0 )
CFDataIncreaseLength( theMutableRef, 188 );
/* add missing header */
memcpy( p_bufferForSending, packet, 4 );
CFDataAppendBytes( theMutableRef, p_bufferForSending, sizeof(p_bufferForSending) );
free( p_bufferForSending );
p_bufferForSending = malloc(4);
/* add payload */
CFDataAppendBytes( theMutableRef, packet->data, sizeof(packet->data) );
b_nonSendData = TRUE;
}
}
globals->packetCount++;
if( globals->packetCount%10000 == 0 )
printf("-> %lld Packets received so far...\n",globals->packetCount);
}
if( nativeGlobals->b_msgPortOpen == TRUE )
{
printf( "sending %i bytes of data\n", CFDataGetLength( theMutableRef ) );
i_returnValue = CFMessagePortSendRequest( nativeGlobals->messagePortToVLC,
/* arbitrary int val */ globals->packetCount,
/* the data */ theMutableRef,
/* no timeout for sending */ 0,
/* no timeout for resp */ 0,
/* no resp. wanted */ NULL,
NULL );
b_nonSendData = FALSE;
i_lastSentPacket = globals->packetCount;
if( i_returnValue == kCFMessagePortSendTimeout )
printf( "time out while sending\n" );
else if( i_returnValue == kCFMessagePortReceiveTimeout )
printf( "time out while waiting for resp\n" );
else if( i_returnValue == kCFMessagePortIsInvalid )
{
/* suppress any further attemps */
printf( "message port is invalid!\n" );
nativeGlobals->b_msgPortOpen = FALSE;
}
else if( i_returnValue == kCFMessagePortTransportError )
printf( "transport error while sending!\n" );
else
{
//printf( "success, freeing resources\n" );
free( theMutableRef );
theMutableRef = CFDataCreateMutable( kCFAllocatorDefault, (188) );
}
}
}
}
else
printf( "warning: either globals or nativeGlobals are NIL in VLCEyeTVPluginPacketsArrived" );
/* clean up before leaving function */
//if( nativeGlobals->b_msgPortOpen == TRUE )
// free( theMutableRef );
free( p_bufferForSending );
return result;
}
/* VLCEyeTVPluginServiceChanged,
*
* - *globals : The plug-in Globals
* - deviceID : Identifies the active Device
* - headendID : The HeadendID, for e300 it's the orbital position of the satelite in
* tenth degrees east
* - transponderID : The Frequency in kHz
* - serviceID : original ServiceID from the DVB-Stream (e300, e400)
* - pidList : List of active PIDs
*
* Whenever a service changes, this function is called. Service-related plug-in data should be updated here.
*/
static long VLCEyeTVPluginServiceChanged(VLCEyeTVPluginGlobals_t *globals,
EyeTVPluginDeviceID deviceID,
long headendID,
long transponderID,
long serviceID,
EyeTVPluginPIDInfo *pidList,
long pidsCount)
{
long result = 0;
int i;
printf("\nVLC media player Plug-In: ServiceChanged:\n");
printf( "=====================================\n");
if( globals )
{
DeviceInfo *deviceInfo = GetDeviceInfo( globals, deviceID );
if( deviceInfo )
{
deviceInfo->headendID = headendID;
printf("HeadendID: %ld, ", headendID);
deviceInfo->transponderID = transponderID;
printf("TransponderID: %ld, ", transponderID);
deviceInfo->serviceID = serviceID;
printf("ServiceID: %ld\n\n", serviceID);
deviceInfo->activePIDsCount = pidsCount;
for( i = 0; i < pidsCount; i++ )
{
deviceInfo->activePIDs[i] = pidList[i];
printf("Active PID: %ld, type: %ld\n", pidList[i].pid, pidList[i].pidType);
}
deviceInfo->pidsCount = 0;
}
}
printf( "=====================================\n");
/* notify a potential VLC instance about the service change */
CFNotificationCenterPostNotification( CFNotificationCenterGetDistributedCenter(),
CFSTR("ServiceChanged"),
CFSTR(VLC_NOTIFICATION_OBJECT),
/*userInfo*/ NULL,
TRUE );
return result;
}
#pragma mark -
/* EyeTVPluginDispatcher,
*
* - selector : See 'EyeTVPluginDefs.h'
* - *refCon : The RefCon to the plug-in-related Data
* - deviceID : Identifies the Device
* - params : Parameters for functioncall
*
* This function is a part of the interface for the communication with EyeTV. If something happens,
* EyeTV thinks, we should know of, it calls this function with the corresponding selector. */
#pragma export on
long EyeTVPluginDispatcher( EyeTVPluginParams* params )
{
long result = 0;
switch( params->selector )
{
case kEyeTVPluginSelector_Initialize:
result = VLCEyeTVPluginInitialize((VLCEyeTVPluginGlobals_t**)params->refCon,
params->initialize.apiVersion, params->initialize.callback);
break;
case kEyeTVPluginSelector_Terminate:
result = VLCEyeTVPluginTerminate((VLCEyeTVPluginGlobals_t*)params->refCon);
break;
case kEyeTVPluginSelector_GetInfo:
result = VLCEyeTVPluginGetInformation((VLCEyeTVPluginGlobals_t*)params->refCon,
params->info.pluginAPIVersion, params->info.pluginName, params->info.description);
break;
case kEyeTVPluginSelector_DeviceAdded:
result = VLCEyeTVPluginDeviceAdded((VLCEyeTVPluginGlobals_t*)params->refCon,
params->deviceID, params->deviceAdded.deviceType);
break;
case kEyeTVPluginSelector_DeviceRemoved:
result = VLCEyeTVPluginDeviceRemoved((VLCEyeTVPluginGlobals_t*)params->refCon, params->deviceID);
break;
case kEyeTVPluginSelector_PacketsArrived:
result = VLCEyeTVPluginPacketsArrived((