#include <mach/message.h>
#include <mach/mach_error.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <syslog.h> // Debug messages
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#include <IOKit/IOMessage.h>
#include <IOKit/firewire/IOFireWireLib.h>
#include <IOKit/avc/IOFireWireAVCConsts.h>
#include "DVLib.h"
#include "DVFamily.h"
#define kNTSCCompressedBufferSize 120000
#define kPALCompressedBufferSize 144000
#define kMaxNotifications 64
typedef struct device_info_struct {
DVDevice *fDevice;
UInt32 fNumOutputFrames;
UInt32 fOpens;
UInt32 frameSize;
UInt8* bufMem[kDVMaxFrames];
DVFrameVars *fReadSharedVars; DVFrameVars *fWriteSharedVars; DVGlobalOutPtr fWrite;
DVGlobalInPtr fRead;
UInt8 fOutputMode; } device_info;
typedef struct DVNotificationEntryStruct {
UInt32 wantedEvents;
DVNotifyProc notifyProc;
void *userRefCon;
DVDeviceID device;
} DVNotificationEntry, *DVNotificationEntryPtr;
static DVThread *sThread;
static UInt32 fNumDevices, fNumAlive;
static device_info devices[kDVMaxDevicesActive];
static DVNotificationEntry sNotifications[kMaxNotifications];
static int inited = 0;
static void postEvent( DVEventHeaderPtr event )
{
DVNotificationEntryPtr note;
int i;
for(i=0; i<kMaxNotifications; i++) {
note = &sNotifications[i];
if(note->notifyProc != NULL && (note->wantedEvents & event->theEvent) &&
(note->device == kEveryDVDeviceID || note->device == event->deviceID)) {
event->notifID = (DVNotificationID)(i+1);
note->notifyProc((DVEventRecordPtr)event, note->userRefCon);
}
}
}
static void deviceMessage(void * refcon, UInt32 messageType, void *messageArgument)
{
DVDeviceRefNum refNum = (DVDeviceRefNum)refcon;
device_info *dev = &devices[refNum];
switch(messageType) {
case kIOMessageServiceIsTerminated:
break;
case kIOFWMessageServiceIsRequestingClose:
if(dev->fOpens > 0) {
if(dev->fWrite)
DVDisableWrite(refNum);
if(dev->fRead)
DVDisableRead(refNum);
dev->fOpens = 1;
DVCloseDriver(refNum);
}
break;
case kIOMessageServiceWasClosed:
break;
default:
break;
}
}
static void deviceArrived(void *refcon, DVDevice *device, UInt32 index, UInt32 refound)
{
fNumAlive++;
devices[index].fDevice = device;
if(!refound)
fNumDevices++;
{
DVConnectionEvent theEvent;
theEvent.eventHeader.deviceID = (DVDeviceID) index;
theEvent.eventHeader.theEvent = kDVDeviceAdded;
postEvent( &theEvent.eventHeader );
}
}
static SInt32 init(void)
{
fNumDevices = 0;
fNumAlive = 0;
sThread = DVCreateThread(deviceArrived, (void *)12345, nil, nil, deviceMessage);
DVRunThread(sThread);
inited = 1;
return noErr;
}
OSErr DVNewNotification( DVDeviceRefNum refNum, DVNotifyProc notifyProc,
void *userData, DVNotificationID *pNotifyID )
{
int i;
for(i=0; i<kMaxNotifications; i++) {
if(sNotifications[i].notifyProc == NULL) {
sNotifications[i].wantedEvents = 0;
sNotifications[i].notifyProc = notifyProc;
sNotifications[i].userRefCon = userData;
sNotifications[i].device = (DVDeviceID)refNum;
*pNotifyID = (DVNotificationID)(i+1);
return noErr;
}
}
return kDVNoNotificationsErr;
}
OSErr DVNotifyMeWhen( DVDeviceRefNum refNum, DVNotificationID notifyID, UInt32 events)
{
int id = ((int)notifyID)-1;
if(sNotifications[id].notifyProc != NULL) {
sNotifications[id].wantedEvents = events;
return noErr;
}
else
return paramErr;
}
OSErr DVCancelNotification( DVDeviceRefNum refNum, DVNotificationID notifyID )
{
int id = ((int)notifyID)-1;
if(sNotifications[id].notifyProc != NULL) {
sNotifications[id].wantedEvents = 0;
return noErr;
}
else
return paramErr;
}
OSErr DVDisposeNotification( DVDeviceRefNum refNum, DVNotificationID notifyID )
{
int id = ((int)notifyID)-1;
if(sNotifications[id].notifyProc != NULL) {
sNotifications[id].wantedEvents = 0;
sNotifications[id].notifyProc = NULL;
sNotifications[id].userRefCon = NULL;
return noErr;
}
else
return paramErr;
}
OSErr DVDoAVCTransaction( DVDeviceRefNum refNum, AVCTransactionParamsPtr pParams )
{
IOReturn err;
device_info *dev = &devices[refNum];
if(!dev->fDevice->fSupportsFCP) {
return( -4162 ); }
err = (*dev->fDevice->fAVCInterface)->AVCCommand(dev->fDevice->fAVCInterface,
pParams->commandBufferPtr, pParams->commandLength,
pParams->responseBufferPtr, &pParams->responseBufferSize);
return err;
}
UInt32 DVCountDevices( void )
{
if(!inited)
init();
return fNumDevices;
}
OSErr DVGetIndDevice( DVDeviceID * pDVDevice, UInt32 index )
{
*pDVDevice = index;
return noErr;
}
OSErr DVSetDeviceName( DVDeviceID deviceID, char * str )
{
return noErr; }
OSErr DVGetDeviceName( DVDeviceID deviceID, char * str )
{
strcpy(str, devices[deviceID].fDevice->fName);
return noErr;
}
OSErr DVUnregisterClientApp( DVClientID dvClientID )
{
return noErr; }
OSErr DVRegisterClientApp( DVClientID *pDVClientID, UInt32 clientContextData )
{
*pDVClientID = (DVClientID)1;
return noErr; }
OSStatus DVGetNextClientEvent( DVClientID dvClientID )
{
return noErr; }
OSErr DVOpenDriver( DVDeviceID deviceID, DVDeviceRefNum *pRefNum )
{
IOReturn err = kIOReturnSuccess;
device_info *dev = &devices[deviceID];
*pRefNum = deviceID;
if(dev->fOpens > 0) {
dev->fOpens++;
return noErr;
}
do {
if(dev->fDevice->fObject == 0) {
err = kDVDisconnectedErr;
break;
}
dev->fNumOutputFrames = 5;
dev->fOpens++;
} while (0);
if(err != kIOReturnSuccess) {
syslog(LOG_INFO, "error opening DV device: %x", err);
dev->fOpens = 1;
DVCloseDriver(deviceID);
}
return err; }
OSErr DVCloseDriver( DVDeviceRefNum refNum )
{
device_info *dev = &devices[refNum];
if(dev->fOpens > 0) {
dev->fOpens--;
if(dev->fOpens == 0) {
}
}
return noErr;
}
OSErr DVGetDeviceStandard(DVDeviceRefNum refNum, UInt32 * pStandard )
{
AVCCTSFrameStruct avcFrame;
AVCTransactionParams transactionParams;
UInt8 responseBuffer[ 16 ];
OSStatus theErr = noErr;
UInt32 currentSignal, AVCStatus;
device_info * dev = &devices[refNum];
if(!devices[refNum].fDevice->fSupportsFCP) {
*pStandard = kNTSCStandard;
devices[refNum].frameSize = kNTSCCompressedBufferSize;
devices[refNum].fOutputMode = kAVCSignalModeSD525_60;
return( theErr );
}
avcFrame.cmdType_respCode = kAVCStatusInquiryCommand;
avcFrame.headerAddress = 0x20; avcFrame.opcode = kAVCOutputSignalModeOpcode;
avcFrame.operand[ 0 ] = kAVCSignalModeDummyOperand;
transactionParams.commandBufferPtr = (Ptr) &avcFrame;
transactionParams.commandLength = 4;
transactionParams.responseBufferPtr = (Ptr) responseBuffer;
transactionParams.responseBufferSize = 4;
transactionParams.responseHandler = nil;
theErr = (*dev->fDevice->fAVCInterface)->AVCCommand(dev->fDevice->fAVCInterface,
transactionParams.commandBufferPtr, transactionParams.commandLength,
transactionParams.responseBufferPtr, &transactionParams.responseBufferSize);
if(theErr) {
if(theErr == kIOReturnTimeout) {
*pStandard = kNTSCStandard;
return noErr;
}
return theErr;
}
currentSignal = ((responseBuffer[ 2 ] << 8) | responseBuffer[ 3 ]);
AVCStatus = responseBuffer[ 0 ];
*pStandard = kUnknownStandard;
switch (currentSignal & 0x000000ff)
{
case kAVCSignalModeSD525_60:
case kAVCSignalModeSDL525_60:
case kAVCSignalModeHD1125_60:
devices[refNum].frameSize = kNTSCCompressedBufferSize;
devices[refNum].fOutputMode = kAVCSignalModeSD525_60;
*pStandard = kNTSCStandard;
return( theErr );
case kAVCSignalModeSD625_50:
case kAVCSignalModeSDL625_50:
case kAVCSignalModeHD1250_50:
devices[refNum].frameSize = kPALCompressedBufferSize;
devices[refNum].fOutputMode = kAVCSignalModeSD625_50;
*pStandard = kPALStandard;
return( theErr );
default:
syslog(LOG_INFO, "DVGetDeviceStandard(), err 0x%x\n", kUnknownStandardErr);
return( kUnknownStandardErr ); }
}
OSErr DVIsEnabled( DVDeviceRefNum refNum, Boolean *isEnabled)
{
*isEnabled = devices[refNum].fRead != 0;
return noErr; }
OSErr DVEnableRead( DVDeviceRefNum refNum )
{
OSErr err;
device_info *dev = &devices[refNum];
if(dev->fRead) {
return noErr;
}
if(dev->fWrite) {
return kAlreadyEnabledErr;
}
do {
err = DVDeviceOpen(sThread, dev->fDevice);
if(err != kIOReturnSuccess) break;
dev->fRead = DVAllocRead(dev->fDevice, sThread);
err = DVReadAllocFrames(dev->fRead, dev->fNumOutputFrames,
&dev->fReadSharedVars, dev->bufMem);
if(err != kIOReturnSuccess) break;
err = DVReadStart(dev->fRead);
} while (0);
if(err) {
syslog(LOG_INFO, "DVEnableRead(), err 0x%x\n", err);
DVDisableRead(refNum);
}
return err;
}
OSErr DVDisableRead( DVDeviceRefNum refNum )
{
device_info *dev = &devices[refNum];
if(dev->fRead) {
DVReadStop(dev->fRead);
DVReadFreeFrames(dev->fRead);
DVReadFree(dev->fRead);
dev->fRead = NULL;
DVDeviceClose(dev->fDevice);
}
return noErr;
}
OSErr DVReadFrame( DVDeviceRefNum refNum, Ptr *ppReadBuffer, UInt32 * pSize )
{
device_info *dev = &devices[refNum];
int index=0, i;
for(i=dev->fReadSharedVars->fReader; i<dev->fReadSharedVars->fWriter; i++) {
index = i % dev->fNumOutputFrames;
if(dev->fReadSharedVars->fFrameStatus[index] == kReady)
break;
}
if (i >= dev->fReadSharedVars->fWriter)
return -1;
*ppReadBuffer = dev->bufMem[index];
*pSize = dev->fReadSharedVars->fFrameSize[index];
dev->fReadSharedVars->fFrameStatus[index] = kReading;
dev->fReadSharedVars->fReader = i;
return noErr; }
OSErr DVReleaseFrame( DVDeviceRefNum refNum, Ptr pReadBuffer )
{
int i;
device_info *dev = &devices[refNum];
for(i=0; i<dev->fNumOutputFrames; i++) {
if(pReadBuffer == dev->bufMem[i])
break;
}
dev->fReadSharedVars->fFrameStatus[i] = kEmpty;
return noErr; }
OSErr DVEnableWrite( DVDeviceRefNum refNum )
{
IOReturn err;
device_info *dev = &devices[refNum];
if(dev->fWrite) {
return noErr;
}
if(dev->fRead) {
return kAlreadyEnabledErr;
}
do {
err = DVDeviceOpen(sThread, dev->fDevice);
if(err != kIOReturnSuccess) break;
dev->fWrite = DVAllocWrite(dev->fDevice, sThread);
err = DVWriteSetSignalMode(dev->fWrite, dev->fOutputMode);
if(err)
break;
err = DVWriteAllocFrames(dev->fWrite, dev->fNumOutputFrames,
&dev->fWriteSharedVars, dev->bufMem);
if(err)
break;
err = DVWriteStart(dev->fWrite);
} while (0);
if(err) {
syslog(LOG_INFO, "DVEnableWrite(), err 0x%x\n", err);
DVDisableWrite(refNum);
}
return err;
}
OSErr DVDisableWrite( DVDeviceRefNum refNum )
{
device_info *dev = &devices[refNum];
if(dev->fWrite) {
DVWriteStop(dev->fWrite);
DVWriteFreeFrames(dev->fWrite);
DVWriteFree(dev->fWrite);
dev->fWrite = NULL;
DVDeviceClose(dev->fDevice);
}
return noErr;
}
OSErr DVGetEmptyFrame( DVDeviceRefNum refNum, Ptr *ppEmptyFrameBuffer, UInt32 * pSize )
{
if(!devices[refNum].fWrite) {
syslog(LOG_INFO, "DVGetEmptyFrame, not writing!!\n");
return kNotEnabledErr;
}
if (devices[refNum].fWriteSharedVars->fStatus == 2)
{
syslog(LOG_INFO, "DVGetEmptyFrame, status %d\n", devices[refNum].fWriteSharedVars->fStatus);
return -2;
}
if (devices[refNum].fWriteSharedVars->fWriter + 1 >=
devices[refNum].fWriteSharedVars->fReader + devices[refNum].fNumOutputFrames)
{
return -1;
}
*ppEmptyFrameBuffer = devices[refNum].bufMem[devices[refNum].fWriteSharedVars->fWriter % devices[refNum].fNumOutputFrames];
*pSize = devices[refNum].frameSize;
return noErr; }
OSErr DVWriteFrame( DVDeviceRefNum refNum, Ptr pWriteBuffer )
{
device_info *dev = &devices[refNum];
if(!dev->fWrite) {
syslog(LOG_INFO, "DVWriteFrame, not writing!!\n");
return kNotEnabledErr;
}
dev->fWriteSharedVars->fWriter += 1;
return noErr; }
OSErr DVSetWriteSignalMode( DVDeviceRefNum refNum, UInt8 mode)
{
devices[refNum].fOutputMode = mode;
return noErr;
}
UInt32 getNumDroppedFrames(DVDeviceRefNum refNum)
{
return devices[refNum].fWriteSharedVars->fDroppedFrames;
}