#include "DVLib.h"
#include <pthread.h>
#include <mach/mach_port.h>
#include <mach/thread_act.h>
#include <mach/vm_map.h>
#include <syslog.h> // Debug messages
#include <IOKit/IOMessage.h>
#include <IOKit/firewire/IOFireWireLibIsoch.h>
#include <IOKit/avc/IOFireWireAVCConsts.h>
#include "IsochronousDataHandler.h"
#define kDVRequestID 0
#define ALT_TIMING 1
#define PAGE_SIZE 4096;
#define kNoPlug 0xdeadbeef
#define kWriteChannel 62
#define kReadChannel 63
#define kAVCSignalModeDVCPro50_525_60 0x74
#define kAVCSignalModeDVCPro50_625_50 0xF4
#define kAVCSignalModeDVCPro100_525_60 0x70
#define kAVCSignalModeDVCPro100_625_50 0xF0
#define kAVCSignalModeMask_DVCPro50 0x74
#define kAVCSignalModeMask_DVCPro100 0x70
#define kAVCSignalModeMask_HD 0x08
enum {
kDVRunning = 0,
kDVStopped = 1,
kDVWriteOverrun = 2,
kDVReadOverrun = 3,
kDVStopping = 4
};
enum {
kPALBit = 0x800000, kSTypeMask = 0x7c0000, kSTypeSD = 0x00000,
kSTypeSDL = 0x40000,
kSTypeHD = 0x80000
};
enum {
kNumPacketsPerInputBuffer = 100,
kDVSDPayloadPacketSize = 480,
kDVSDLPayloadPacketSize = 240,
kDVHDPayloadPacketSize = 960,
kDVCPro50PayloadPacketSize = 960,
kDVCPro100PayloadPacketSize = 1920,
kNumPingPongs = 8, kNumPacketsPerPingPong = 100,
kNumDCLsPerPingPongPacket = 1,
kRecordNumDCLs =
kNumPingPongs * (kNumPacketsPerPingPong * kNumDCLsPerPingPongPacket+3)+6,
kMaxDCLSize = 32,
kRecordDCLProgramSize = kMaxDCLSize * kRecordNumDCLs,
};
enum {
kNTSCFrameRateNumerator = 2997,
kNTSCFrameRateDenominator = 100,
kNTSCPlayFramePeriodNumerator = kNTSCFrameRateDenominator,
kNTSCPlayFramePeriodDenominator = kNTSCFrameRateNumerator,
kNTSCNumDataPacketsPerDVFrame = 250,
kNTSCNumDataPacketsPerGroup = 125,
kPALFrameRateNumerator = 25,
kPALFrameRateDenominator = 1,
kPALPlayFramePeriodNumerator = kPALFrameRateDenominator,
kPALPlayFramePeriodDenominator = kPALFrameRateNumerator,
kPALNumDataPacketsPerDVFrame = 300,
kPALNumDataPacketsPerGroup = 150,
kDVPacketAlignSlop = 8, kDVPacketCIPSize = 8,
kPlaySYTDelay = 3, };
enum {
kFrameSize_SD525_60 = 120000,
kFrameSize_DVCPro525_60 = 120000,
kFrameSize_SD625_50 = 144000,
kFrameSize_DVCPro625_50 = 144000,
kFrameSize_SDL525_60 = 60000,
kFrameSize_SDL625_50 = 72000,
kFrameSize_DVCPro50_525_60 = 240000,
kFrameSize_HD1125_60 = 240000,
kFrameSize_DVCPro50_625_50 = 288000,
kFrameSize_HD1250_50 = 288000,
kFrameSize_DVCPro100_525_60 = 480000,
kFrameSize_DVCPro100_625_50 = 576000
};
typedef struct {
UInt32 fRequest;
void * fParams;
} DVReq;
typedef struct {
mach_msg_header_t msgHdr;
DVReq dvRequest;
} SendMsg;
typedef struct {
mach_msg_header_t msgHdr;
DVReq dvRequest;
mach_msg_trailer_t trailer;
} ReceiveMsg;
struct DVLocalOutStruct;
typedef struct DVLocalOutStruct DVLocalOut, *DVLocalOutPtr;
typedef struct _DVStreamStruct {
IOFireWireLibDeviceRef pFWDevice;
IOFireWireAVCLibProtocolInterface **fAVCProtoInterface;
IOFireWireLibLocalIsochPortRef pFWLocalIsochPort;
IOFireWireLibRemoteIsochPortRef pFWRemoteIsochPort;
IOFireWireLibIsochChannelRef fIsochChannelRef;
UInt32 fPlug; DVThread * fThread;
DVFrameVars fFrames;
DCLCommandPtr pDCLList; UInt8 * fDCLBuffers; UInt32 fDCLBufferSize; UInt8 fSignalMode; UInt8 fIsocChannel; UInt8 fMaxSpeed; DVDevice *pDVDevice;
UInt32 fDVFrameSize; } DVStream;
struct DVLocalOutStruct
{
DVLocalOutPtr pNextLocalData;
DVLocalOutPtr pPrevLocalData;
DVGlobalOutPtr pGlobalData;
DCLJumpPtr pEndOfBufferGroupDCLJump;
DCLLabelPtr pEndOfBufferGroupDCLLabel;
DCLJumpPtr pBufferGroupSkipEmptyPacketDCLJump;
DCLLabelPtr pBufferGroupSkipEmptyPacketDCLLabel;
DCLLabelPtr pBufferGroupDontSkipEmptyPacketDCLLabel;
UInt32 * pBufferGroupTimeStampPtr;
DCLCommandPtr pFirstBufferGroupDCLCommand;
DCLCommandPtr pLastBufferGroupDCLCommand;
DCLCommandPtr timeStampUpdateDCLList;
DCLCommandPtr * bufferGroupUpdateDCLList;
UInt32 updateListSize;
UInt32 numPackets;
UInt32 fBlockNum;
bool skippingEmptyPacket;
};
struct DVGlobalOutStruct {
DVStream fStreamVars;
UInt8 * fDCLCommandPool;
UInt32 fTotalPool;
UInt32 fAllocatedPool;
DVSharedVars fSharedDCLVars;
DVLocalOut fLocalDataArray[kNumPlayBufferGroups]; UInt8 * pEmptyTransmitBuffers; DCLLabelPtr pUnderrunDCLLabel; UInt32 totalPackets; UInt32 activePackets; UInt32 nominalFrameCycleTime; UInt32 nextSYT; UInt32 nextDBC; UInt32 numDataPacketsPerFrame; UInt32 numDataPacketsPerGroup; UInt32 playFramePeriodNumerator;
UInt32 playFramePeriodDenominator;
UInt32 playFrameRateNumerator, playFrameRateDenominator;
UInt32 fDataPacketSize; UInt32 fDataQuadSize; UInt32 fAlignQuadSize; UInt32 fHeader0; UInt32 fHeader1; UInt32 nextDataPacketNum; UInt32 * pImageBuffer; bool fUpdateBuffers; };
typedef struct DVLocalInStruct
{
DVGlobalInPtr pGlobalData;
DCLCommandPtr fFirstCmd;
DCLLabelPtr fStateLabel;
DCLJumpPtr fStateJmp;
UInt32 fBlockNum;
UInt32 * fTimeStampPtr;
} DVLocalIn, *DVLocalInPtr;
struct DVGlobalInStruct
{
DVStream fStreamVars;
DCLCommandPtr *ppUpdateDCLList;
UInt32 packetCount;
UInt32 fLastFrameTime;
DVLocalIn fLocalDataArray[kNumPingPongs];
DCLLabelPtr fTerminal;
UInt8 *pImageBuffer;
UInt8 lastSequenceCount;
UInt8 fState; UInt8 fSynced;
UInt8 fRestarted;
};
static IOReturn buildWriteProgram(DVGlobalOutPtr pGlobalData);
static IOReturn allocateBuffers(DVGlobalOutPtr pGlobalData);
static void DVWritePoll(DVGlobalOutPtr globs);
static void DVReadPoll(DVGlobalInPtr globs);
static void closeStream(DVStream *stream);
UInt32 AddFWCycleTimeToFWCycleTime( UInt32 cycleTime1, UInt32 cycleTime2 )
{
UInt32 secondCount,
cycleCount,
cycleOffset;
UInt32 cycleTime;
cycleOffset = (cycleTime1 & 0x0FFF) + (cycleTime2 & 0x0FFF);
cycleCount = (cycleTime1 & 0x01FFF000) + (cycleTime2 & 0x01FFF000);
if (cycleOffset > 3071)
{
cycleCount += 0x1000;
cycleOffset -= 3072;
}
secondCount = (cycleTime1 & 0xFE000000) + (cycleTime2 & 0xFE000000);
if (cycleCount > (7999 << 12))
{
secondCount += 0x02000000;
cycleCount -= (8000 << 12);
}
cycleTime = secondCount | cycleCount | cycleOffset;
return (cycleTime);
}
UInt32 SubtractFWCycleTimeFromFWCycleTime( UInt32 cycleTime1, UInt32 cycleTime2)
{
SInt32 secondCount,
cycleCount,
cycleOffset;
UInt32 cycleTime;
cycleOffset = (cycleTime1 & 0x0FFF) - (cycleTime2 & 0x0FFF);
cycleCount = (cycleTime1 & 0x01FFF000) - (cycleTime2 & 0x01FFF000);
if (cycleOffset < 0)
{
cycleCount -= 0x1000;
cycleOffset += 3072;
}
secondCount = (cycleTime1 & 0xFE000000) - (cycleTime2 & 0xFE000000);
if (cycleCount < 0)
{
secondCount -= 0x02000000;
cycleCount += (8000 << 12);
}
cycleTime = secondCount | cycleCount | cycleOffset;
return (cycleTime);
}
static UInt32 ConvertFractionalSecondsToFWCycleTime( UInt32 secondsNumerator, UInt32 secondsDenominator )
{
UInt32 iSecondsCount2;
UInt32 iCycleCount2;
UInt32 iCycleOffset2;
UInt32 mSecondCount;
UInt32 mCycleCount;
UInt32 secondsCycleTime2;
iSecondsCount2 = secondsNumerator / secondsDenominator;
mSecondCount = secondsNumerator % secondsDenominator;
iCycleCount2 = (mSecondCount * 8000) / secondsDenominator;
mCycleCount = (mSecondCount * 8000) % secondsDenominator;
iCycleOffset2 = (mCycleCount * 3072) / secondsDenominator;
secondsCycleTime2 = (iSecondsCount2 << 25) | (iCycleCount2 << 12) | iCycleOffset2;
return (secondsCycleTime2);
}
static IOReturn writePlug(IOFireWireAVCLibProtocolInterface **interface, UInt32 plug, UInt32 val)
{
return (*interface)->updateOutputPlug(interface, plug,
(*interface)->readOutputPlug(interface, plug), val);
}
static void handlePCRLock(void *refcon, UInt32 generation, UInt16 nodeID, UInt32 plug,
UInt32 oldVal, UInt32 newVal)
{
}
static IOReturn writeDeviceOutputMCR(IOFireWireLibDeviceRef interface, UInt32 mask, UInt32 val)
{
UInt32 oldVal, newVal;
IOReturn err;
FWAddress addr;
io_object_t obj;
addr.nodeID = 0;
addr.addressHi = 0xffff;
addr.addressLo = 0xf0000900;
obj = (*interface)->GetDevice(interface);
err = (*interface)->ReadQuadlet(interface, obj, &addr, &oldVal, false, 0);
if(err == kIOReturnSuccess) {
if( (oldVal & mask) != val) {
newVal = (oldVal & ~mask) | val;
err = (*interface)->CompareSwap(interface, obj, &addr, oldVal, newVal, false, 0);
}
}
return err;
}
static IOReturn writeDeviceOutputPlug(IOFireWireLibDeviceRef interface, UInt32 plugNo, UInt32 mask, UInt32 val)
{
UInt32 oldVal, newVal;
IOReturn err;
FWAddress addr;
io_object_t obj;
int i;
for( i = 0; i < 4; i++ )
{
addr.nodeID = 0;
addr.addressHi = 0xffff;
addr.addressLo = 0xf0000904+plugNo*4;
obj = (*interface)->GetDevice(interface);
err = (*interface)->ReadQuadlet(interface, obj, &addr, &oldVal, false, 0);
if(err == kIOReturnSuccess) {
if( (oldVal & mask) != val) {
newVal = (oldVal & ~mask) | val;
err = (*interface)->CompareSwap(interface, obj, &addr, oldVal, newVal, false, 0);
}
}
if( err == kIOReturnSuccess )
{
break;
}
}
return err;
}
static IOReturn writeDeviceInputPlug(IOFireWireLibDeviceRef interface, UInt32 plugNo, UInt32 mask, UInt32 val, int p2pInc)
{
UInt32 oldVal, newVal;
IOReturn err;
FWAddress addr;
io_object_t obj;
int i;
UInt32 p2p;
for( i = 0; i < 4; i++ )
{
addr.nodeID = 0;
addr.addressHi = 0xffff;
addr.addressLo = 0xf0000984+plugNo*4;
obj = (*interface)->GetDevice(interface);
err = (*interface)->ReadQuadlet(interface, obj, &addr, &oldVal, false, 0);
if(err == kIOReturnSuccess) {
newVal = (oldVal & ~mask) | val;
p2p = newVal & kIOFWPCRP2PCount;
if(p2pInc > 0)
p2p += 1 << kIOFWPCRP2PCountPhase;
else if (p2pInc < 0)
p2p -= 1 << kIOFWPCRP2PCountPhase;
newVal = (newVal & ~kIOFWPCRP2PCount) | (p2p &kIOFWPCRP2PCount);
if(newVal != oldVal)
err = (*interface)->CompareSwap(interface, obj, &addr, oldVal, newVal, false, 0);
}
if( err == kIOReturnSuccess )
{
break;
}
}
return err;
}
static IOReturn MakeP2PConnectionForWrite(DVDevice *pDVDevice,UInt32 plug, UInt32 chan)
{
IOReturn err;
err = writeDeviceInputPlug( pDVDevice->fDevInterface,
plug,
(kIOFWPCRChannel | kIOFWPCRBroadcast),
chan<<kIOFWPCRChannelPhase, 1);
if (err == kIOReturnSuccess)
{
pDVDevice->p2pConnected = true;
pDVDevice->p2pPlug = plug;
pDVDevice->p2pChan = chan;
}
return err;
}
static IOReturn BreakP2PConnectionForWrite(DVDevice *pDVDevice,UInt32 plug, UInt32 chan)
{
IOReturn err;
err = writeDeviceInputPlug( pDVDevice->fDevInterface,
plug,
(kIOFWPCRChannel | kIOFWPCRBroadcast),
chan<<kIOFWPCRChannelPhase, -1);
pDVDevice->p2pConnected = false;
return err;
}
static IOReturn remakeP2PConnectionForWrite(DVDevice *pDVDevice)
{
if (pDVDevice->p2pConnected) {
MakeP2PConnectionForWrite(pDVDevice,pDVDevice->p2pPlug,pDVDevice->p2pChan);
}
return kIOReturnSuccess;
}
void AVCUnitMessageCallback(void * refCon, UInt32 type, void * arg )
{
DVDevice *pDVDevice = (DVDevice*) refCon;
if (type == kIOMessageServiceIsResumed)
{
DVRequest(pDVDevice->fThread, remakeP2PConnectionForWrite, pDVDevice, 0);
}
if (pDVDevice->fThread->fDeviceMessage != NULL)
pDVDevice->fThread->fDeviceMessage((void*)pDVDevice->deviceIndex,type,arg);
return;
}
static IOReturn getSignalMode(IOFireWireAVCLibUnitInterface **avc, UInt8 *mode)
{
UInt32 size;
UInt8 cmd[4],response[4];
IOReturn res;
cmd[0] = kAVCStatusInquiryCommand;
cmd[1] = IOAVCAddress(kAVCTapeRecorder, 0);
cmd[2] = kAVCOutputSignalModeOpcode;
cmd[3] = kAVCSignalModeDummyOperand;
size = 4;
res = (*avc)->AVCCommand(avc, cmd, 4, response, &size);
if(res == kIOReturnSuccess) {
*mode = response[3];
}
return res;
}
static bool isDVCPro(IOFireWireAVCLibUnitInterface **avc, UInt8 *pMode)
{
UInt32 size;
UInt8 cmd[10],response[10];
IOReturn res;
cmd[0] = kAVCStatusInquiryCommand;
cmd[1] = kAVCUnitAddress;
cmd[2] = kAVCVendorDependentOpcode;
cmd[3] = 0;
cmd[4] = 0x80;
cmd[5] = 0x45;
cmd[6] = 0x82;
cmd[7] = 0x48;
cmd[8] = 0xff;
cmd[9] = 0xff;
size = 10;
res = (*avc)->AVCCommand(avc, cmd, 10, response, &size);
if ((res == kIOReturnSuccess) && (response[0] == kAVCImplementedStatus))
{
cmd[0] = kAVCStatusInquiryCommand;
cmd[1] = kAVCUnitAddress;
cmd[2] = kAVCOutputPlugSignalFormatOpcode;
cmd[3] = 0;
cmd[4] = 0xFF;
cmd[5] = 0xFF;
cmd[6] = 0xFF;
cmd[7] = 0xFF;
size = 8;
res = (*avc)->AVCCommand(avc, cmd, 8, response, &size);
if (res == kIOReturnSuccess && response[0] == kAVCImplementedStatus)
*pMode = response[5];
else
*pMode = 0x00;
return true;
}
else
return false;
}
static bool isSDL(IOFireWireAVCLibUnitInterface **avc, UInt8 signalMode)
{
IOReturn res;
bool hasSDL;
UInt32 size;
UInt8 cmd[4],response[4];
cmd[0] = kAVCControlCommand;
cmd[1] = IOAVCAddress(kAVCTapeRecorder, 0);
cmd[2] = kAVCInputSignalModeOpcode;
cmd[3] = (signalMode & ~kAVCSignalModeMask_STYPE) | kAVCSignalModeMask_SDL;
size = 4;
res = (*avc)->AVCCommand(avc, cmd, 4, response, &size);
if(res != kIOReturnSuccess || response[0] != kAVCAcceptedStatus)
return false;
cmd[0] = kAVCStatusInquiryCommand;
cmd[1] = IOAVCAddress(kAVCTapeRecorder, 0);
cmd[2] = kAVCInputSignalModeOpcode;
cmd[3] = kAVCSignalModeDummyOperand;
size = 4;
res = (*avc)->AVCCommand(avc, cmd, 4, response, &size);
hasSDL = (response[3] & kAVCSignalModeMask_STYPE) == kAVCSignalModeMask_SDL;
cmd[0] = kAVCControlCommand;
cmd[1] = IOAVCAddress(kAVCTapeRecorder, 0);
cmd[2] = kAVCInputSignalModeOpcode;
cmd[3] = signalMode;
size = 4;
res = (*avc)->AVCCommand(avc, cmd, 4, response, &size);
return hasSDL;
}
static void deviceArrived(void *refcon, io_iterator_t iterator )
{
io_object_t obj;
DVThread * dvThread = (DVThread *)refcon;
UInt8 dvcProMode;
while(obj = IOIteratorNext(iterator)) {
CFMutableDictionaryRef properties;
CFNumberRef dataDesc;
CFStringRef strDesc;
kern_return_t err;
UInt64 GUID;
int refound = 0;
int device;
DVDevice *dev = NULL;
err = IORegistryEntryCreateCFProperties(obj, &properties, kCFAllocatorDefault, kNilOptions);
dataDesc = (CFNumberRef)CFDictionaryGetValue(properties, CFSTR("GUID"));
CFNumberGetValue(dataDesc, kCFNumberSInt64Type, &GUID);
for(device=0; device<dvThread->fNumDevices; device++) {
if(GUID == dvThread->fDevices[device].fGUID) {
refound = 1;
dev = &dvThread->fDevices[device];
break;
}
}
if(!refound) {
CFBooleanRef hasFCP;
device = dvThread->fNumDevices;
dvThread->fNumDevices++;
dev = &dvThread->fDevices[device];
strDesc = (CFStringRef)CFDictionaryGetValue(properties, CFSTR("FireWire Product Name"));
if(strDesc) {
dev->fName[0] = 0;
CFStringGetCString(strDesc, dev->fName, sizeof(dev->fName), kCFStringEncodingMacRoman);
}
hasFCP = (CFBooleanRef)CFDictionaryGetValue(properties, CFSTR("supportsFCP"));
dev->fSupportsFCP = true;
if(hasFCP)
dev->fSupportsFCP = CFBooleanGetValue(hasFCP);
dev->fGUID = GUID;
dev->fMaxSpeed = kFWSpeed100MBit;
dev->fWriteChan = kWriteChannel;
dev->fReadChan = kReadChannel;
}
CFRelease(properties);
dev->fObject = obj;
dev->fThread = dvThread;
err = openAVCUnit(dev->fObject, &dev->fAVCInterface, dvThread);
if(err == kIOReturnSuccess)
{
UInt8 mode, stype;
dev->deviceIndex = device+1;
(*dev->fAVCInterface)->setMessageCallback(dev->fAVCInterface, (void *) dev, AVCUnitMessageCallback);
if(dev->fSupportsFCP)
{
err = getSignalMode(dev->fAVCInterface, &mode);
if(err == kIOReturnSuccess)
{
if(mode & kAVCSignalModeMask_50)
dev->standard = palIn;
else
dev->standard = ntscIn;
stype = mode & kAVCSignalModeMask_STYPE;
if(stype == kAVCSignalModeMask_DVCPro25)
{
dev->fDVFormats |= 1 << kIDHDVCPro_25;
}
else if(stype == kAVCSignalModeMask_DVCPro50)
{
dev->fDVFormats |= 1 << kIDHDVCPro_50;
dev->fMaxSpeed = kFWSpeed400MBit; }
else
{
if(isDVCPro(dev->fAVCInterface,&dvcProMode))
{
if((dvcProMode & kAVCSignalModeMask_STYPE) == kAVCSignalModeMask_DVCPro50)
{
dev->fDVFormats |= 1 << kIDHDVCPro_50;
dev->fMaxSpeed = kFWSpeed400MBit; }
else
dev->fDVFormats |= 1 << kIDHDVCPro_25;
}
}
if(stype == kAVCSignalModeMask_SDL)
dev->fDVFormats |= 1 << kIDHDV_SDL;
else
{
if(isSDL(dev->fAVCInterface, mode))
dev->fDVFormats |= 1 << kIDHDV_SDL;
}
}
else
{
dev->fDVFormats = 1 << kIDHDV_SD; dev->standard = ntscIn;
}
}
else
{
dev->fDVFormats = 1 << kIDHDV_SD; dev->standard = ntscIn; }
(dvThread->fAddedFunc)(dvThread->fAddedRefCon, dev, device+1, refound);
}
}
}
static OSStatus DVthreadExit(DVThread *dvThread, UInt32 params)
{
if(dvThread->fNotifySource)
CFRunLoopSourceInvalidate(dvThread->fNotifySource);
CFRunLoopStop(dvThread->fWorkLoop) ;
dvThread->fTimerFunc = NULL;
return noErr;
}
static void *DVRTThreadStart(DVThread *dvThread)
{
ReceiveMsg msg;
kern_return_t err;
int delay;
int run = true;
int i;
deviceArrived(dvThread, dvThread->fMatchEnumer);
DVSignalSync(&dvThread->fRequestSyncer, &dvThread->fSyncRequest, 1);
delay = 12; while(run) {
int nextTick;
#if TIMING
CFAbsoluteTime start;
start = CFAbsoluteTimeGetCurrent();
#endif
err = mach_msg(&msg.msgHdr, MACH_RCV_MSG | MACH_RCV_TIMEOUT,
0, sizeof(msg), dvThread->fRequestMachPort, delay, MACH_PORT_NULL);
#if TIMING
DVLog(dvThread, 'mmsg', start, CFAbsoluteTimeGetCurrent());
#endif
if(err == MACH_MSG_SUCCESS) {
switch (msg.msgHdr.msgh_id) {
case kDVRequestID:
dvThread->fRequestResult = (dvThread->fRequestFunc)(dvThread->fRequestArg, dvThread->fRequestParam);
if(dvThread->fRequestFunc == DVthreadExit)
run = false;
DVSignalSync(&dvThread->fRequestSyncer, &dvThread->fSyncRequest, (UInt32)dvThread->fRequestFunc);
}
}
for(i=0; i<kDVMaxStreamsActive; i++) {
if(dvThread->fInStreams[i])
DVReadPoll(dvThread->fInStreams[i]);
if(dvThread->fOutStreams[i])
DVWritePoll(dvThread->fOutStreams[i]);
}
if(dvThread->fTimerFunc) {
dvThread->fTimerFunc(NULL, dvThread->fTimerRefCon);
delay = 12; nextTick = (int)((dvThread->requestTimeoutTime-CFAbsoluteTimeGetCurrent())*1000.0);
if(nextTick <= 0)
nextTick = 1;
if(nextTick < delay)
delay = nextTick;
}
}
return NULL;
}
static void *DVRLThreadStart(DVThread *thread)
{
CFRunLoopRef loop;
loop = CFRunLoopGetCurrent();
if(thread->fNotifySource)
CFRunLoopAddSource(loop, thread->fNotifySource, kCFRunLoopDefaultMode);
CFRetain(loop);
thread->fWorkLoop = loop;
DVSignalSync(&thread->fRequestSyncer, &thread->fSyncRequest, 1);
CFRunLoopRun();
return NULL;
}
DVThread * DVCreateThread(DVDeviceArrivedFunc deviceAdded, void * addedRefCon,
CFRunLoopTimerCallBack timerTick, void *timerRefCon, IOFWAVCMessageCallback deviceMessage)
{
UInt32 i;
IOReturn err;
mach_port_t masterDevicePort;
DVThread *dvThread;
const UInt8 num = kAVCTapeRecorder;
CFMutableDictionaryRef dict = 0;
CFNumberRef tape;
dict = CFDictionaryCreateMutable( kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if(!dict)
return nil;
tape = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt8Type, &num);
if(!tape)
return nil;
CFDictionarySetValue( dict, CFSTR(kIOProviderClassKey), CFSTR("IOFireWireAVCSubUnit") );
CFDictionarySetValue( dict, CFSTR("SubUnit_Type"), tape);
CFRelease(tape);
if ((err = IOMasterPort(bootstrap_port, &masterDevicePort)) != KERN_SUCCESS) {
return NULL;
}
dvThread = malloc(sizeof(DVThread));
bzero(dvThread, sizeof(DVThread));
for(i = 0 ; i < kDVMaxDevicesActive ; i++){
dvThread->fDevices[i].fOutPlug = kNoPlug;
}
pthread_mutex_init(&dvThread->fRequestSyncer.fMutex, NULL);
pthread_cond_init(&dvThread->fRequestSyncer.fSyncCond, NULL);
pthread_mutex_init(&dvThread->fRequestMutex, NULL);
dvThread->fNotifyPort = IONotificationPortCreate(masterDevicePort);
dvThread->fNotifySource = IONotificationPortGetRunLoopSource(dvThread->fNotifyPort);
err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &dvThread->fRequestMachPort);
err = mach_port_insert_right(mach_task_self(), dvThread->fRequestMachPort, dvThread->fRequestMachPort,
MACH_MSG_TYPE_MAKE_SEND);
if(timerTick) {
dvThread->fTimerFunc = timerTick;
dvThread->fTimerRefCon = timerRefCon;
}
dvThread->fAddedRefCon = addedRefCon;
dvThread->fAddedFunc = deviceAdded;
dvThread->fDeviceMessage = deviceMessage;
err = IOServiceAddMatchingNotification( dvThread->fNotifyPort,
kIOMatchedNotification, dict,
deviceArrived, dvThread, &dvThread->fMatchEnumer );
return dvThread;
}
static void setThreadPriority(pthread_t thread)
{
double mult;
unsigned int delta;
unsigned int abs_to_ns_num;
unsigned int abs_to_ns_denom;
unsigned int proc_to_abs_num;
unsigned int proc_to_abs_denom;
thread_time_constraint_policy_data_t constraints;
kern_return_t result;
#if 0
{
mach_msg_type_number_t count;
boolean_t get_default = TRUE;
count = THREAD_TIME_CONSTRAINT_POLICY_COUNT;
thread_policy_get(pthread_mach_thread_np(thread), THREAD_TIME_CONSTRAINT_POLICY,
(thread_policy_t)&constraints, &count, &get_default);
syslog(LOG_INFO, "default period %d computation %d constraint %d preemptible %d\n",
constraints.period, constraints.computation, constraints.constraint, constraints.preemptible);
}
#endif
(void)MKGetTimeBaseInfo (&delta, &abs_to_ns_num, &abs_to_ns_denom,
&proc_to_abs_num, &proc_to_abs_denom);
mult = ((double)abs_to_ns_denom / (double)abs_to_ns_num) * 1000000;
constraints.period = 12*mult;
constraints.computation = 2*mult;
constraints.constraint = 24*mult;
constraints.preemptible = TRUE;
result = thread_policy_set(pthread_mach_thread_np(thread), THREAD_TIME_CONSTRAINT_POLICY,
(thread_policy_t)&constraints, THREAD_TIME_CONSTRAINT_POLICY_COUNT);
}
void DVSetTimeoutTime(DVThread * dvThread, CFAbsoluteTime fireDate)
{
dvThread->setTimeoutTime = CFAbsoluteTimeGetCurrent();
dvThread->requestTimeoutTime = fireDate;
}
void DVRunThread(DVThread * dvThread)
{
pthread_attr_t threadAttr; pthread_t thread;
dvThread->fSyncRequest = 0;
pthread_attr_init(&threadAttr);
pthread_create(&thread, &threadAttr, DVRLThreadStart, dvThread);
dvThread->fRLThread = thread;
DVWaitSync(&dvThread->fRequestSyncer, &dvThread->fSyncRequest);
dvThread->fSyncRequest = 0;
pthread_create(&thread, &threadAttr, DVRTThreadStart, dvThread);
dvThread->fRTThread = thread;
setThreadPriority(thread);
DVWaitSync(&dvThread->fRequestSyncer, &dvThread->fSyncRequest);
}
void DVFreeThread(DVThread * dvThread)
{
DVRequest(dvThread, DVthreadExit, dvThread, 0);
pthread_join(dvThread->fRTThread, NULL);
pthread_join(dvThread->fRLThread, NULL);
CFRelease(dvThread->fWorkLoop);
if(dvThread->fMatchEnumer)
IOObjectRelease(dvThread->fMatchEnumer);
if(dvThread->fNotifyPort) {
CFMachPortRef hack;
CFMachPortContext context;
Boolean shouldFreeInfo;
context.version = 1;
context.info = (void *) dvThread->fNotifyPort;
context.retain = NULL;
context.release = NULL;
context.copyDescription = NULL;
hack = CFMachPortCreateWithPort(NULL, IONotificationPortGetMachPort(dvThread->fNotifyPort),
NULL, &context, &shouldFreeInfo);
CFMachPortInvalidate(hack);
IONotificationPortDestroy(dvThread->fNotifyPort);
CFRelease(hack);
}
mach_port_destroy(mach_task_self(), dvThread->fRequestMachPort);
pthread_mutex_destroy(&dvThread->fRequestSyncer.fMutex);
pthread_cond_destroy(&dvThread->fRequestSyncer.fSyncCond);
pthread_mutex_destroy(&dvThread->fRequestMutex);
memset(dvThread, 0xde, sizeof(DVThread));
free(dvThread);
}
void DVSignalSync(ThreadSyncer *sync, UInt32 *var, UInt32 val)
{
pthread_mutex_lock(&sync->fMutex);
*var = val;
pthread_mutex_unlock(&sync->fMutex);
pthread_cond_broadcast(&sync->fSyncCond);
}
void DVWaitSync(ThreadSyncer *sync, UInt32 *var)
{
{
pthread_mutex_lock(&sync->fMutex);
while(!*var) {
pthread_cond_wait(&sync->fSyncCond, &sync->fMutex);
}
pthread_mutex_unlock(&sync->fMutex);
}
}
void DVLock(ThreadSyncer *sync)
{
pthread_mutex_lock(&sync->fMutex);
}
void DVUnlock(ThreadSyncer *sync)
{
pthread_mutex_unlock(&sync->fMutex);
}
static IOReturn isochPortGetSupported(
IOFireWireLibIsochPortRef interface,
IOFWSpeed* outMaxSpeed,
UInt64* outChanSupported)
{
DVStream *stream;
stream = (DVStream *)((*interface)->GetRefCon(interface));
if(*outMaxSpeed > stream->fMaxSpeed)
*outMaxSpeed = stream->fMaxSpeed;
*outChanSupported = ((UInt64)1) << (63-stream->fIsocChannel);
return kIOReturnSuccess;
}
IOReturn openFireWireUnit(IOFireWireAVCLibUnitInterface **avcInterface, IOFireWireSessionRef session, IOFireWireLibDeviceRef *retInterface, DVThread *thread)
{
IOFireWireLibDeviceRef resultInterface;
IOReturn err = kIOReturnNoMemory;
int opened = false;
do {
resultInterface = (*avcInterface)->getAncestorInterface(avcInterface, "IOFireWireUnit",
CFUUIDGetUUIDBytes(kIOFireWireLibTypeID), CFUUIDGetUUIDBytes(kIOFireWireUnitInterfaceID_v3));
if(!resultInterface)
break;
if(session)
err = (*resultInterface)->OpenWithSessionRef(resultInterface, session);
else
err = (*resultInterface)->Open(resultInterface);
if(err)
break;
opened = true;
err = (*resultInterface)->AddIsochCallbackDispatcherToRunLoop(resultInterface, thread->fWorkLoop);
} while (false);
if(!err)
*retInterface = resultInterface;
else {
if(opened)
(*resultInterface)->Close(resultInterface);
if(resultInterface)
(*resultInterface)->Release(resultInterface);
}
return err;
}
IOReturn openAVCUnit(io_object_t obj, IOFireWireAVCLibUnitInterface ***retInterface, DVThread *thread)
{
IOCFPlugInInterface** theCFPlugInInterface;
IOFireWireAVCLibUnitInterface **resultInterface = 0 ;
SInt32 theScore ;
IOReturn err;
err = IOCreatePlugInInterfaceForService(
obj,
kIOFireWireAVCLibUnitTypeID,
kIOCFPlugInInterfaceID, & theCFPlugInInterface,
& theScore);
if (!err) {
HRESULT comErr;
comErr = (*theCFPlugInInterface)->QueryInterface(
theCFPlugInInterface,
CFUUIDGetUUIDBytes(kIOFireWireAVCLibUnitInterfaceID),
(void**) & resultInterface);
if (comErr == S_OK) {
err = (*resultInterface)->addCallbackDispatcherToRunLoop(resultInterface, thread->fWorkLoop );
}
else
err = comErr;
(*theCFPlugInInterface)->Release(theCFPlugInInterface); }
if(!err)
*retInterface = resultInterface;
return err;
}
IOReturn openAVCProto(IOFireWireAVCLibUnitInterface **avcInterface, IOFireWireAVCLibProtocolInterface ***retInterface, DVThread *thread)
{
IOFireWireAVCLibProtocolInterface **resultInterface;
IOReturn err = noErr;
do {
resultInterface = (*avcInterface)->getProtocolInterface(avcInterface,
CFUUIDGetUUIDBytes(kIOFireWireAVCLibProtocolTypeID),
CFUUIDGetUUIDBytes(kIOFireWireAVCLibProtocolInterfaceID));
if(!resultInterface)
break;
err = (*resultInterface)->addCallbackDispatcherToRunLoop(resultInterface, thread->fWorkLoop);
} while (false);
if(!err)
*retInterface = resultInterface;
else {
if(resultInterface)
(*resultInterface)->Release(resultInterface);
}
return err;
}
void DVDeviceTerminate(DVDevice *dev)
{
DVDeviceClose(dev);
if(dev->fAVCInterface) {
(*dev->fAVCInterface)->Release(dev->fAVCInterface);
dev->fAVCInterface = NULL;
}
if(dev->fObject) {
IOObjectRelease(dev->fObject);
dev->fObject = NULL;
}
}
IOReturn DVDeviceOpen(DVThread *dvThread, DVDevice *device)
{
IOReturn err = noErr;
if(!device->fAVCInterface)
return kIOReturnNoMemory;
do {
err = (*device->fAVCInterface)->open(device->fAVCInterface);
if(err != kIOReturnSuccess) break;
err = openFireWireUnit(device->fAVCInterface, (*device->fAVCInterface)->getSessionRef(device->fAVCInterface),
&device->fDevInterface, dvThread);
if(err != kIOReturnSuccess) break;
err = openAVCProto(device->fAVCInterface, &device->fAVCProtoInterface, dvThread);
if(err != kIOReturnSuccess) break;
err = (*device->fAVCProtoInterface)->allocateOutputPlug(device->fAVCProtoInterface,
device, handlePCRLock, &device->fOutPlug);
if(err != kIOReturnSuccess) break;
err = writePlug(device->fAVCProtoInterface, device->fOutPlug, 122 << kIOFWPCROutputPayloadPhase);
if(err != kIOReturnSuccess) break;
} while (0);
if(err != kIOReturnSuccess)
DVDeviceClose(device);
return err;
}
static IOReturn doDVDeviceClose(DVDevice *dev)
{
if(dev->fDevInterface) {
UInt32 ref;
(*dev->fDevInterface)->Close(dev->fDevInterface);
ref = (*dev->fDevInterface)->Release(dev->fDevInterface);
dev->fDevInterface = NULL;
}
if(dev->fAVCProtoInterface) {
UInt32 ref;
if(dev->fOutPlug != kNoPlug) {
(*dev->fAVCProtoInterface)->freeOutputPlug(dev->fAVCProtoInterface, dev->fOutPlug);
dev->fOutPlug = kNoPlug;
}
ref = (*dev->fAVCProtoInterface)->Release(dev->fAVCProtoInterface);
dev->fAVCProtoInterface = NULL;
}
if(dev->fAVCInterface) {
(*dev->fAVCInterface)->close(dev->fAVCInterface);
}
return kIOReturnSuccess;
}
void DVDeviceClose(DVDevice *dev)
{
DVRequest(dev->fThread, doDVDeviceClose, dev, 0);
}
IOReturn DVRequest(DVThread *thread, IOReturn (*func)(void *arg, UInt32 param), void *arg, UInt32 param)
{
IOReturn result;
if(thread->fRTThread != pthread_self()) {
pthread_mutex_lock(&thread->fRequestMutex);
thread->fSyncRequest = 0;
thread->fRequestFunc = func;
thread->fRequestArg = arg;
thread->fRequestParam = param;
{
SendMsg msg;
bzero( &msg, sizeof(msg));
msg.msgHdr.msgh_remote_port = thread->fRequestMachPort;
msg.msgHdr.msgh_bits = MACH_MSGH_BITS(
MACH_MSG_TYPE_COPY_SEND,
MACH_MSG_TYPE_COPY_SEND );
msg.msgHdr.msgh_size = sizeof(msg);
msg.msgHdr.msgh_id = kDVRequestID;
mach_msg(&msg.msgHdr, MACH_SEND_MSG,
msg.msgHdr.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
}
DVWaitSync(&thread->fRequestSyncer, &thread->fSyncRequest);
result = thread->fRequestResult;
pthread_mutex_unlock(&thread->fRequestMutex);
}
else
result = (*func)(arg, param);
return result;
}
static void initStream(DVStream *stream, DVDevice *device, UInt32 plug, UInt32 channel, DVThread *thread)
{
stream->pFWDevice = device->fDevInterface;
stream->pDVDevice = device;
stream->fAVCProtoInterface = device->fAVCProtoInterface;
stream->fPlug = plug;
stream->fIsocChannel = channel;
stream->fMaxSpeed = device->fMaxSpeed;
stream->fThread = thread;
}
static IOReturn openStream(DVStream *stream, bool forWrite, UInt32 packetSize)
{
IOReturn err;
IOFireWireLibIsochPortRef talker, listener;
IOVirtualRange bufRange;
do {
stream->fIsochChannelRef = (*stream->pFWDevice)->CreateIsochChannel(stream->pFWDevice, forWrite, packetSize,
stream->fMaxSpeed, CFUUIDGetUUIDBytes(kIOFireWireIsochChannelInterfaceID));
if (NULL == stream->fIsochChannelRef) {
err = memFullErr;
break;
}
bufRange.address = (IOVirtualAddress)stream->fDCLBuffers;
bufRange.length = stream->fDCLBufferSize;
if(forWrite) {
stream->pFWLocalIsochPort = (*stream->pFWDevice)->CreateLocalIsochPort(stream->pFWDevice, 1 ,
stream->pDCLList, kFWDCLCycleEvent, 0, 0x0000f000, nil, 0, &bufRange, 1,
CFUUIDGetUUIDBytes(kIOFireWireLocalIsochPortInterfaceID));
}
else
stream->pFWLocalIsochPort = (*stream->pFWDevice)->CreateLocalIsochPort(stream->pFWDevice, 0 ,
stream->pDCLList, 0, 0, 0, nil, 0, &bufRange, 1,
CFUUIDGetUUIDBytes(kIOFireWireLocalIsochPortInterfaceID));
if (!stream->pFWLocalIsochPort) {
err = memFullErr;
break;
}
stream->pFWRemoteIsochPort = (*stream->pFWDevice)->CreateRemoteIsochPort(stream->pFWDevice, 0, CFUUIDGetUUIDBytes(kIOFireWireRemoteIsochPortInterfaceID) );
(*stream->pFWRemoteIsochPort)->SetRefCon( stream->pFWRemoteIsochPort, stream);
(*stream->pFWRemoteIsochPort)->SetGetSupportedHandler( stream->pFWRemoteIsochPort, &isochPortGetSupported);
if(forWrite) {
talker = (IOFireWireLibIsochPortRef) stream->pFWLocalIsochPort;
listener = (IOFireWireLibIsochPortRef)stream->pFWRemoteIsochPort;
}
else {
listener = (IOFireWireLibIsochPortRef) stream->pFWLocalIsochPort;
talker = (IOFireWireLibIsochPortRef)stream->pFWRemoteIsochPort;
}
err = (*stream->fIsochChannelRef)->SetTalker( stream->fIsochChannelRef, talker);
if(err)
break;
err = (*stream->fIsochChannelRef)->AddListener( stream->fIsochChannelRef, listener);
if(err)
break;
(*stream->fIsochChannelRef)->TurnOnNotification(stream->fIsochChannelRef);
err = (*stream->fIsochChannelRef)->AllocateChannel(stream->fIsochChannelRef);
if(err)
break;
err = (*stream->fIsochChannelRef)->Start(stream->fIsochChannelRef);
if(err)
break;
stream->fFrames.fStatus = kDVRunning;
} while (false);
if(err)
closeStream(stream);
return err;
}
static void closeStream(DVStream *stream)
{
IOReturn err;
stream->fFrames.fStatus = kDVStopped;
if(stream->fIsochChannelRef) {
(*stream->fIsochChannelRef)->TurnOffNotification(stream->fIsochChannelRef);
err = (*stream->fIsochChannelRef)->Stop(stream->fIsochChannelRef);
err = (*stream->fIsochChannelRef)->ReleaseChannel(stream->fIsochChannelRef);
(*stream->fIsochChannelRef)->Release(stream->fIsochChannelRef);
stream->fIsochChannelRef = NULL;
}
if(stream->pFWLocalIsochPort) {
(*stream->pFWLocalIsochPort)->Release(stream->pFWLocalIsochPort);
stream->pFWLocalIsochPort = NULL;
}
if(stream->pFWRemoteIsochPort) {
(*stream->pFWRemoteIsochPort)->Release(stream->pFWRemoteIsochPort);
stream->pFWRemoteIsochPort = NULL;
}
}
static IOReturn DVAllocFrames(DVFrameVars *pFrameData, UInt32 numFrames, UInt32 frameSize,
DVFrameVars **frameVars, UInt8 **frames)
{
int i;
pFrameData->fNumFrames = numFrames;
pFrameData->fFrames = malloc(numFrames*frameSize);
pFrameData->fReader = 0;
pFrameData->fWriter = 0;
pFrameData->fDroppedFrames = 0;
pFrameData->fStatus = 0;
for(i=0; i<numFrames; i++) {
frames[i] = pFrameData->fFrames + i*frameSize;
}
*frameVars = pFrameData;
return kIOReturnSuccess;
}
static void DVFreeFrames(DVFrameVars *pFrameData)
{
if(!pFrameData->fFrames)
return;
free(pFrameData->fFrames);
pFrameData->fFrames = NULL;
}
static void DVGetNextFullOutputFrame(DVFrameVars *pFrameData, UInt8** ppFrame, UInt32 frameSize )
{
if(NULL == *ppFrame) {
*ppFrame = pFrameData->fFrames;
}
else {
if (pFrameData->fReader + 1 < pFrameData->fWriter) {
pFrameData->fReader++;
}
else {
pFrameData->fDroppedFrames++;
}
*ppFrame = pFrameData->fFrames +
frameSize*(pFrameData->fReader % pFrameData->fNumFrames);
}
}
void DVSetInputFrameSizeAndMode(DVFrameVars *pFrameData, UInt32 bytes, UInt8 mode, UInt32 frameTime )
{
int index = pFrameData->fWriter % pFrameData->fNumFrames;
int i;
pFrameData->fFrameSize[index] = bytes;
pFrameData->fFrameStandard[index] = mode;
pFrameData->fFrameTime[index] = frameTime;
pFrameData->fFrameStatus[index] = kReady;
for(i=pFrameData->fWriter + 1; i < pFrameData->fReader + pFrameData->fNumFrames; i++) {
if(pFrameData->fFrameStatus[i % pFrameData->fNumFrames] != kReading)
break;
}
if (i< pFrameData->fReader + pFrameData->fNumFrames)
pFrameData->fWriter = i;
else {
pFrameData->fDroppedFrames++;
}
}
void DVGetNextEmptyInputFrame(DVFrameVars *pFrameData, UInt8** ppFrame, UInt32 frameSize )
{
int index = pFrameData->fWriter % pFrameData->fNumFrames;
*ppFrame = pFrameData->fFrames + frameSize*index;
pFrameData->fFrameStatus[index] = kWriting;
}
static UInt32 getEmptyPacketsPerGroup(DVGlobalOutPtr pGlobalData, UInt32 numDataPacketsPerPlayBufferGroup)
{
UInt32 numEmptyPacketsPerPlayBufferGroup;
UInt32 A1, C1, d1, n1;
A1 = numDataPacketsPerPlayBufferGroup;
C1 = pGlobalData->numDataPacketsPerFrame;
n1 = pGlobalData->playFrameRateNumerator;
d1 = pGlobalData->playFrameRateDenominator;
#if ALT_TIMING
{
UInt32 d2 = C1*n1;
UInt32 n2 = (8000 * d1 * A1) - (d2 * A1) + d2;
numEmptyPacketsPerPlayBufferGroup = n2 / d2;
}
#else
numEmptyPacketsPerPlayBufferGroup = (8000 * d1 * A1 + (n1 * C1)/2) / (n1 * C1) - A1;
#endif
return numEmptyPacketsPerPlayBufferGroup;
}
static void FreeDCLCommandPool(DVGlobalOutPtr pGlobalData)
{
if( pGlobalData->fDCLCommandPool != NULL ) {
free(pGlobalData->fDCLCommandPool);
pGlobalData->fDCLCommandPool = NULL;
}
}
static IOReturn AllocateDCLCommandPool(DVGlobalOutPtr pGlobalData, UInt32 total )
{
UInt8 * pDCLCommandPool;
pDCLCommandPool = malloc(total);
if (pDCLCommandPool == NULL)
{
return kIOReturnNoMemory;
}
else
{
pGlobalData->fTotalPool = total;
pGlobalData->fAllocatedPool = 0;
pGlobalData->fDCLCommandPool = pDCLCommandPool;
}
return kIOReturnSuccess;
}
static DCLCommandPtr AllocateDCLCommand(DVGlobalOutPtr pGlobalData, UInt32 dclSize )
{
DCLCommandPtr pDCLCommand;
if(pGlobalData->fAllocatedPool + dclSize <= pGlobalData->fTotalPool) {
pDCLCommand = (DCLCommandPtr)(pGlobalData->fDCLCommandPool + pGlobalData->fAllocatedPool);
pGlobalData->fAllocatedPool += dclSize;
}
else {
syslog(LOG_INFO, "Trying to allocated DCL command size %d, no space left\n", dclSize);
pDCLCommand = NULL;
}
return (pDCLCommand);
}
static DVLocalOutPtr DVAllocatePlayBufferGroup(DVGlobalOutPtr pGlobalData, int num)
{
DVLocalOutPtr pLocalData,
pPrevLocalData,
pNextLocalData;
pLocalData = &pGlobalData->fLocalDataArray[num];
pLocalData->pGlobalData = pGlobalData;
pLocalData->fBlockNum = num;
if(num == 0) {
pPrevLocalData = &pGlobalData->fLocalDataArray[kNumPlayBufferGroups-1];
}
else
pPrevLocalData = &pGlobalData->fLocalDataArray[num-1];
if(num == kNumPlayBufferGroups-1)
pNextLocalData = &pGlobalData->fLocalDataArray[0];
else
pNextLocalData = &pGlobalData->fLocalDataArray[num+1];
pLocalData->pNextLocalData = pNextLocalData;
pLocalData->pPrevLocalData = pPrevLocalData;
return (pLocalData);
}
static void DVDeallocatePlayBufferGroup( DVLocalOutPtr pLocalData )
{
if ( pLocalData != NULL )
{
if ( pLocalData->bufferGroupUpdateDCLList != NULL )
free(pLocalData->bufferGroupUpdateDCLList);
}
}
static IOReturn DVCreatePlayBufferGroupUpdateList( DVLocalOutPtr pLocalData)
{
DCLCommandPtr pDCLCommand,
pLastDCLCommand;
DCLCommandPtr *updateDCLList,
*pUpdateDCLListEntry;
UInt32 opcode;
UInt32 updateListSize;
IOReturn error = 0;
pDCLCommand = pLocalData->pFirstBufferGroupDCLCommand;
pLastDCLCommand = pLocalData->pLastBufferGroupDCLCommand;
updateListSize = 0;
while (pDCLCommand != pLastDCLCommand)
{
opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask;
if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp))
updateListSize++;
pDCLCommand = pDCLCommand->pNextDCLCommand;
}
opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask;
if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp))
updateListSize++;
updateDCLList = (DCLCommandPtr *)malloc( updateListSize * sizeof (DCLCommandPtr) );
if (updateDCLList == NULL)
{
error = kIOReturnNoMemory;
}
else
{
bzero( updateDCLList, updateListSize * sizeof (DCLCommandPtr) );
}
if (error == 0)
{
pDCLCommand = pLocalData->pFirstBufferGroupDCLCommand;
pLastDCLCommand = pLocalData->pLastBufferGroupDCLCommand;
pUpdateDCLListEntry = updateDCLList;
while (pDCLCommand != pLastDCLCommand)
{
opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask;
if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp))
*pUpdateDCLListEntry++ = pDCLCommand;
pDCLCommand = pDCLCommand->pNextDCLCommand;
}
opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask;
if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp))
*pUpdateDCLListEntry++ = pDCLCommand;
}
if (error == 0)
{
pLocalData->bufferGroupUpdateDCLList = updateDCLList;
pLocalData->updateListSize = updateListSize;
}
else
{
pLocalData->bufferGroupUpdateDCLList = NULL;
pLocalData->updateListSize = 0;
}
return ( error );
}
static void ModifyDCLJump(IOFireWireLibLocalIsochPortRef port, DCLJumpPtr pDCLJump, DCLLabelPtr pDCLLabel)
{
if (port) {
(*port)->ModifyJumpDCL( port, pDCLJump, pDCLLabel);
}
}
void DVSilenceFrame(UInt8 mode, UInt8* frame)
{
UInt32 i,j,k,n;
UInt8 *tPtr;
UInt8 sType = ((mode & 0x7C) >> 2);
tPtr = frame;
if ((tPtr[3] &= 0x80) == 0)
n=10; else
n=12;
if (sType == 1)
n /= 2; else if (sType == 0x1D)
n *= 2;
for (i=0;i<n;i++)
{
for (j=0;j<9;j++)
{
tPtr = frame + (i * 12000) + ((j * 16 + 6) * 80) + 8;
for (k=0;k<72;k++)
*tPtr++ = 0x0;
}
}
}
static void DVUpdateOutputBuffers( DVLocalOutPtr pLocalData )
{
DCLCommandPtr pCurrentDCLCommand;
DCLTransferPacketPtr pDCLTransferPacket;
DVLocalOutPtr pPrevLocalData;
DVGlobalOutPtr pGlobalData;
UInt16 localNodeID;
UInt32 shiftedNodeID; UInt32 nominalFrameCycleTime;
UInt32 syt;
UInt32 *pBuffer, *pImageBuffer, *pLastImageBuffer;
UInt32 packetNum, dataPacketNum, numPackets;
UInt32 dbc;
UInt8 stype;
pGlobalData = pLocalData->pGlobalData;
pCurrentDCLCommand = pLocalData->pFirstBufferGroupDCLCommand;
nominalFrameCycleTime = pGlobalData->nominalFrameCycleTime;
pPrevLocalData = pLocalData->pPrevLocalData;
syt = pGlobalData->nextSYT;
dbc = pGlobalData->nextDBC;
dataPacketNum = pGlobalData->nextDataPacketNum;
(*pGlobalData->fStreamVars.pFWDevice)->GetLocalNodeID(pGlobalData->fStreamVars.pFWDevice, &localNodeID);
localNodeID &= 0x3f;
shiftedNodeID = (UInt32)localNodeID << 24;
while (pCurrentDCLCommand->opcode != kDCLSendPacketStartOp)
pCurrentDCLCommand = pCurrentDCLCommand->pNextDCLCommand;
pDCLTransferPacket = (DCLTransferPacketPtr) pCurrentDCLCommand;
numPackets = pLocalData->numPackets;
if(pGlobalData->fUpdateBuffers) {
if( pGlobalData->pImageBuffer == NULL ) {
DVGetNextFullOutputFrame(&pGlobalData->fStreamVars.fFrames,
(UInt8 **)&(pGlobalData->pImageBuffer),
pGlobalData->fStreamVars.fDVFrameSize);
}
}
pImageBuffer = ( pGlobalData->pImageBuffer + (pGlobalData->fDataQuadSize * dataPacketNum) );
for( packetNum = 0; packetNum < numPackets; packetNum++)
{
pBuffer = (UInt32 *) pDCLTransferPacket->buffer;
pBuffer[0] = pGlobalData->fHeader0 | (dbc & 0xFF) | shiftedNodeID;
pBuffer[1] = pGlobalData->fHeader1 | 0xFFFF;
if (pDCLTransferPacket->size > kDVPacketCIPSize)
{
if (dataPacketNum == 0)
{
pBuffer[1] = pGlobalData->fHeader1 | (syt & 0xFFFF);
syt = AddFWCycleTimeToFWCycleTime(syt, pGlobalData->nominalFrameCycleTime);
}
if(pGlobalData->fUpdateBuffers) {
bcopy(pImageBuffer, (void *)((UInt32)(pDCLTransferPacket->buffer) + kDVPacketCIPSize),
pGlobalData->fDataPacketSize);
pImageBuffer += pGlobalData->fDataQuadSize;
}
stype = pGlobalData->fStreamVars.fSignalMode & kAVCSignalModeMask_STYPE;
switch (stype)
{
case kAVCSignalModeMask_DVCPro50:
dbc += 2; break;
case kAVCSignalModeMask_DVCPro100:
dbc += 4; break;
case kAVCSignalModeMask_SDL:
case kAVCSignalModeMask_DVCPro25:
case kAVCSignalModeMask_HD:
default: dbc += 1; break;
};
dataPacketNum++;
if (dataPacketNum == pGlobalData->numDataPacketsPerFrame )
{
dataPacketNum = 0;
if(pGlobalData->fUpdateBuffers) {
pLastImageBuffer = pGlobalData->pImageBuffer;
DVGetNextFullOutputFrame(&pGlobalData->fStreamVars.fFrames,
(UInt8 **)&(pGlobalData->pImageBuffer),
pGlobalData->fStreamVars.fDVFrameSize);
pImageBuffer = pGlobalData->pImageBuffer;
if (pImageBuffer == pLastImageBuffer)
DVSilenceFrame(pGlobalData->fStreamVars.fSignalMode, (UInt8 *)pImageBuffer);
} } }
pCurrentDCLCommand = pCurrentDCLCommand->pNextDCLCommand;
while (pCurrentDCLCommand != NULL)
{
if (pCurrentDCLCommand->opcode != kDCLSendPacketStartOp)
pCurrentDCLCommand = pCurrentDCLCommand->pNextDCLCommand;
else
break;
}
pDCLTransferPacket = (DCLTransferPacketPtr) pCurrentDCLCommand;
}
pGlobalData->nextSYT = syt;
pGlobalData->nextDBC = dbc;
pGlobalData->nextDataPacketNum = dataPacketNum;
}
static void DVHandleOutput(DVLocalOutPtr pLocalData)
{
DVGlobalOutPtr pGlobalData;
DVLocalOutPtr pPrevLocalData;
UInt32 nominalFrameCycleTime;
UInt32 fractionalFrameCycleCount,
fractionalFrameCycleOffset;
SInt32 timeDrift;
UInt32 cycleDrift;
UInt32 projectedTimeStamp,
projectedSYT;
#if TIMING
CFAbsoluteTime cstart, cend;
cstart = CFAbsoluteTimeGetCurrent();
#endif
pPrevLocalData = pLocalData->pPrevLocalData;
pGlobalData = pLocalData->pGlobalData;
nominalFrameCycleTime = pGlobalData->nominalFrameCycleTime;
if (pLocalData->skippingEmptyPacket)
{
ModifyDCLJump(pGlobalData->fStreamVars.pFWLocalIsochPort,
pLocalData->pBufferGroupSkipEmptyPacketDCLJump, pLocalData->pBufferGroupDontSkipEmptyPacketDCLLabel);
pGlobalData->activePackets++;
pLocalData->skippingEmptyPacket = false;
}
projectedTimeStamp = *pLocalData->pBufferGroupTimeStampPtr; projectedTimeStamp = AddFWCycleTimeToFWCycleTime(projectedTimeStamp, 1 << 12);
projectedTimeStamp = AddFWCycleTimeToFWCycleTime(projectedTimeStamp, pGlobalData->activePackets << 12);
projectedTimeStamp = SubtractFWCycleTimeFromFWCycleTime(projectedTimeStamp, pLocalData->numPackets << 12);
projectedSYT = pGlobalData->nextSYT;
if(pGlobalData->nextDataPacketNum != 0) {
projectedSYT = SubtractFWCycleTimeFromFWCycleTime(projectedSYT, nominalFrameCycleTime);
fractionalFrameCycleOffset =
((nominalFrameCycleTime & 0x0FFF) * pGlobalData->nextDataPacketNum) /
pGlobalData->numDataPacketsPerFrame;
fractionalFrameCycleCount =
((nominalFrameCycleTime & 0x01FFF000) * pGlobalData->nextDataPacketNum) /
pGlobalData->numDataPacketsPerFrame;
fractionalFrameCycleCount =
(fractionalFrameCycleCount & 0x01FFF000) +
(((fractionalFrameCycleCount & 0x0FFF) * 3072) / 4096);
projectedSYT = AddFWCycleTimeToFWCycleTime (projectedSYT, fractionalFrameCycleOffset);
projectedSYT = AddFWCycleTimeToFWCycleTime (projectedSYT, fractionalFrameCycleCount);
}
cycleDrift = AddFWCycleTimeToFWCycleTime(projectedTimeStamp, kPlaySYTDelay << 12);
cycleDrift = SubtractFWCycleTimeFromFWCycleTime(cycleDrift, projectedSYT);
timeDrift = (cycleDrift >> 12) & 0x000F;
if ((timeDrift > 0) && (timeDrift < 0x0008))
{
ModifyDCLJump(pGlobalData->fStreamVars.pFWLocalIsochPort,
pLocalData->pBufferGroupSkipEmptyPacketDCLJump, pLocalData->pBufferGroupSkipEmptyPacketDCLLabel);
pGlobalData->activePackets--;
pLocalData->skippingEmptyPacket = true;
}
DVUpdateOutputBuffers( pLocalData );
ModifyDCLJump (pGlobalData->fStreamVars.pFWLocalIsochPort,
pLocalData->pEndOfBufferGroupDCLJump, pGlobalData->pUnderrunDCLLabel);
ModifyDCLJump (pGlobalData->fStreamVars.pFWLocalIsochPort,
pPrevLocalData->pEndOfBufferGroupDCLJump, pPrevLocalData->pEndOfBufferGroupDCLLabel);
pGlobalData->fSharedDCLVars.fDMAPos = pLocalData->fBlockNum;
#if TIMING
cend = CFAbsoluteTimeGetCurrent();
DVLog(pGlobalData->fStreamVars.fThread, 'isoc', cstart, cend);
#endif
}
static void DVWritePoll(DVGlobalOutPtr globs)
{
int i, pos;
pos = globs->fSharedDCLVars.fDMAPos;
for(i=pos; i<kNumPlayBufferGroups; i++)
if(*globs->fLocalDataArray[i].pBufferGroupTimeStampPtr != 0xffffffff) {
DVHandleOutput(&globs->fLocalDataArray[i]);
*globs->fLocalDataArray[i].pBufferGroupTimeStampPtr = 0xffffffff;
}
for(i=0; i<pos; i++)
if(*globs->fLocalDataArray[i].pBufferGroupTimeStampPtr != 0xffffffff) {
DVHandleOutput(&globs->fLocalDataArray[i]);
*globs->fLocalDataArray[i].pBufferGroupTimeStampPtr = 0xffffffff;
}
}
static void doDVHandleOutputUnderrun( DCLCommandPtr pDCLCommandPtr )
{
DVGlobalOutPtr pGlobalData;
IOReturn err;
pGlobalData = (DVGlobalOutPtr)((DCLCallProcPtr)pDCLCommandPtr)->procData;
syslog(LOG_INFO, "DVHandleOutputUnderrun: 0x%p\n", pGlobalData);
closeStream(&pGlobalData->fStreamVars);
FreeDCLCommandPool(pGlobalData);
err = buildWriteProgram(pGlobalData);
if(err != kIOReturnSuccess)
syslog(LOG_INFO, "DVHandleOutputUnderrun: buildWriteProgram returned %x\n", err);
err = DVWriteStart(pGlobalData);
}
static void DVHandleOutputUnderrun( DCLCommandPtr pDCLCommandPtr )
{
DVGlobalOutPtr pGlobalData;
pGlobalData = (DVGlobalOutPtr)((DCLCallProcPtr)pDCLCommandPtr)->procData;
DVRequest(pGlobalData->fStreamVars.fThread, doDVHandleOutputUnderrun, pDCLCommandPtr, 0);
}
static void DVDisposeDCLOutput( DVGlobalOutPtr pOutputData )
{
DVLocalOutPtr pLocalData, pNextLocalData;
UInt32 bufferGroupNum;
if( pOutputData != NULL )
{
pLocalData = &pOutputData->fLocalDataArray[0];
for (bufferGroupNum = 0; bufferGroupNum < kNumPlayBufferGroups; bufferGroupNum++)
{
if( pLocalData != NULL )
{
pNextLocalData = pLocalData->pNextLocalData;
DVDeallocatePlayBufferGroup (pLocalData);
pLocalData = pNextLocalData;
}
}
FreeDCLCommandPool(pOutputData);
if( pOutputData->fStreamVars.fDCLBuffers != NULL)
{
vm_deallocate(mach_task_self(), (vm_address_t)pOutputData->fStreamVars.fDCLBuffers,
pOutputData->fStreamVars.fDCLBufferSize);
}
}
}
static IOReturn allocateBuffers(DVGlobalOutPtr pGlobalData)
{
UInt32 numDataPacketsPerPage;
UInt32 numEmptyPackets;
UInt32 pageSize;
UInt32 emptySize; UInt32 transmitBuffersSize;
IOReturn res;
UInt8 stype = pGlobalData->fStreamVars.fSignalMode & kAVCSignalModeMask_STYPE;
switch (stype)
{
case kAVCSignalModeMask_SDL:
pGlobalData->fHeader0 = 0x003c0000;
pGlobalData->fHeader1 = 0x80040000;
pGlobalData->fDataPacketSize = kDVSDLPayloadPacketSize; break;
case kAVCSignalModeMask_DVCPro25:
pGlobalData->fHeader0 = 0x00780000;
pGlobalData->fHeader1 = 0x80780000;
pGlobalData->fDataPacketSize = kDVSDPayloadPacketSize; break;
case kAVCSignalModeMask_DVCPro50:
pGlobalData->fHeader0 = 0x00784000;
pGlobalData->fHeader1 = 0x80740000;
pGlobalData->fDataPacketSize = kDVCPro50PayloadPacketSize; break;
case kAVCSignalModeMask_DVCPro100:
pGlobalData->fHeader0 = 0x00788000;
pGlobalData->fHeader1 = 0x80700000;
pGlobalData->fDataPacketSize = kDVCPro50PayloadPacketSize; break;
case kAVCSignalModeMask_HD:
pGlobalData->fHeader0 = 0x00F00000;
pGlobalData->fHeader1 = 0x80080000;
pGlobalData->fDataPacketSize = kDVCPro50PayloadPacketSize; break;
default: pGlobalData->fHeader0 = 0x00780000;
pGlobalData->fHeader1 = 0x80000000;
pGlobalData->fDataPacketSize = kDVSDPayloadPacketSize; break;
};
pGlobalData->fDataQuadSize = pGlobalData->fDataPacketSize/4; pGlobalData->fAlignQuadSize = (pGlobalData->fDataPacketSize + kDVPacketCIPSize + 15)/16;
pGlobalData->fAlignQuadSize *= 4; pGlobalData->fSharedDCLVars.fAlignedPacketSize = 4*pGlobalData->fAlignQuadSize; pGlobalData->fSharedDCLVars.fPacketDataSize = pGlobalData->fDataPacketSize;
if( !(pGlobalData->fStreamVars.fSignalMode & kAVCSignalModeMask_50) )
{
pGlobalData->playFramePeriodNumerator = kNTSCPlayFramePeriodNumerator;
pGlobalData->playFramePeriodDenominator = kNTSCPlayFramePeriodDenominator;
pGlobalData->playFrameRateNumerator = kNTSCFrameRateNumerator;
pGlobalData->playFrameRateDenominator = kNTSCFrameRateDenominator;
pGlobalData->numDataPacketsPerFrame = kNTSCNumDataPacketsPerDVFrame;
pGlobalData->numDataPacketsPerGroup = kNTSCNumDataPacketsPerGroup;
}
else
{
pGlobalData->fHeader1 |= kPALBit;
pGlobalData->playFramePeriodNumerator = kPALPlayFramePeriodNumerator;
pGlobalData->playFramePeriodDenominator = kPALPlayFramePeriodDenominator;
pGlobalData->playFrameRateNumerator = kPALFrameRateNumerator;
pGlobalData->playFrameRateDenominator = kPALFrameRateDenominator;
pGlobalData->numDataPacketsPerFrame = kPALNumDataPacketsPerDVFrame;
pGlobalData->numDataPacketsPerGroup = kPALNumDataPacketsPerGroup;
}
pGlobalData->nominalFrameCycleTime = ConvertFractionalSecondsToFWCycleTime
(pGlobalData->playFramePeriodNumerator, pGlobalData->playFramePeriodDenominator);
pGlobalData->fSharedDCLVars.fNumGroups = kNumPlayBufferGroups;
pGlobalData->fSharedDCLVars.fGroupSize = pGlobalData->numDataPacketsPerGroup;
pageSize = PAGE_SIZE;
numDataPacketsPerPage = pageSize /
(pGlobalData->fDataPacketSize + kDVPacketCIPSize + kDVPacketAlignSlop);
#if ALT_TIMING
numEmptyPackets = getEmptyPacketsPerGroup(pGlobalData, pGlobalData->numDataPacketsPerGroup) * kNumPlayBufferGroups;
#else
numEmptyPackets =
getEmptyPacketsPerGroup(pGlobalData, pGlobalData->numDataPacketsPerGroup * kNumPlayBufferGroups) + kNumPlayBufferGroups/2;
#endif
transmitBuffersSize = pGlobalData->numDataPacketsPerGroup * kNumPlayBufferGroups * pageSize;
transmitBuffersSize /= numDataPacketsPerPage;
emptySize = numEmptyPackets * (kDVPacketCIPSize + kDVPacketAlignSlop);
pGlobalData->fStreamVars.fDCLBufferSize =
transmitBuffersSize + emptySize + sizeof(UInt32)*kNumPlayBufferGroups;
vm_allocate(mach_task_self(), (vm_address_t *)&pGlobalData->fStreamVars.fDCLBuffers,
pGlobalData->fStreamVars.fDCLBufferSize, VM_FLAGS_ANYWHERE);
if( pGlobalData->fStreamVars.fDCLBuffers == NULL ) {
res = kIOReturnNoMemory;
goto bail;
}
bzero( pGlobalData->fStreamVars.fDCLBuffers, pGlobalData->fStreamVars.fDCLBufferSize );
pGlobalData->pEmptyTransmitBuffers = pGlobalData->fStreamVars.fDCLBuffers + transmitBuffersSize;
pGlobalData->fSharedDCLVars.fTimeStampPtrs = (UInt32 *)(pGlobalData->pEmptyTransmitBuffers + emptySize);
return kIOReturnSuccess;
bail:
DVDisposeDCLOutput( pGlobalData );
return res;
}
static IOReturn buildWriteProgram(DVGlobalOutPtr pGlobalData)
{
UInt32 numEmptyPacketsInPlayBufferGroup;
DCLCommandPtr pDCLCommand;
DCLCommandPtr pFirstBufferGroupDCLCommand;
DCLLabelPtr pUnderrunDCLLabel,
pLoopDCLLabel,
pBufferGroupDCLLabel,
pDCLLabel;
DCLTransferPacketPtr pDCLTransferPacket;
DCLCallProcPtr pDCLCallProc;
DCLSetTagSyncBitsPtr pDCLSetTagSyncBits;
DCLJumpPtr pDCLJump,
pBufferGroupDCLJump;
DCLLabelPtr pBufferGroupSkipEmptyPacketDCLLabel;
DCLUpdateDCLListPtr pDCLUpdateDCLList;
DCLPtrTimeStampPtr pDCLTimeStamp;
DVLocalOutPtr pPlayBufferGroupData;
UInt32 * pTransmitBuffer;
UInt8 * pEmptyTransmitBuffer;
volatile UInt32 * pTimeStampPtr;
UInt32 bufferGroupNum;
UInt32 dataPacketNum;
UInt32 numPackets;
UInt32 emptyPacketNumerator;
UInt32 pageOffset;
IOReturn res;
UInt32 totalDCLSize;
UInt32 totalEmpty, emptySoFar;
#if ALT_TIMING
totalEmpty = getEmptyPacketsPerGroup(pGlobalData, pGlobalData->numDataPacketsPerGroup) * kNumPlayBufferGroups;
#else
totalEmpty = getEmptyPacketsPerGroup(pGlobalData, pGlobalData->numDataPacketsPerGroup * kNumPlayBufferGroups) + kNumPlayBufferGroups/2;
#endif
emptySoFar = 0;
pTransmitBuffer = (UInt32 *) pGlobalData->fStreamVars.fDCLBuffers;
pEmptyTransmitBuffer = pGlobalData->pEmptyTransmitBuffers;
pTimeStampPtr = pGlobalData->fSharedDCLVars.fTimeStampPtrs;
totalDCLSize = 2 * sizeof(DCLLabel) + sizeof(DCLSetTagSyncBits) + sizeof(DCLJump) + sizeof(DCLCallProc) +
kNumPlayBufferGroups * (pGlobalData->numDataPacketsPerGroup * sizeof(DCLTransferPacket) + 3*sizeof(DCLLabel) + 2*sizeof(DCLJump) + sizeof(DCLPtrTimeStamp) + 2*sizeof(DCLUpdateDCLList) + sizeof(DCLCallProc) + totalEmpty * sizeof(DCLTransferPacket));
res = AllocateDCLCommandPool(pGlobalData, totalDCLSize);
if (res)
goto bail;
pGlobalData->totalPackets = 0;
pGlobalData->activePackets = 0;
pLoopDCLLabel = (DCLLabelPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLLabel));
pDCLCommand = (DCLCommandPtr) pLoopDCLLabel;
pGlobalData->fStreamVars.pDCLList = pDCLCommand;
pLoopDCLLabel->opcode = kDCLLabelOp;
pDCLSetTagSyncBits = (DCLSetTagSyncBitsPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLSetTagSyncBits));
pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLSetTagSyncBits;
pDCLCommand = (DCLCommandPtr) pDCLSetTagSyncBits;
pDCLSetTagSyncBits->opcode = kDCLSetTagSyncBitsOp;
pDCLSetTagSyncBits->tagBits = 1;
pDCLSetTagSyncBits->syncBits = 0;
for (bufferGroupNum = 0; bufferGroupNum < kNumPlayBufferGroups; bufferGroupNum++)
{
pPlayBufferGroupData = DVAllocatePlayBufferGroup( pGlobalData, bufferGroupNum);
dataPacketNum = 0;
numPackets = 0;
emptyPacketNumerator = 0;
#if ALT_TIMING
numEmptyPacketsInPlayBufferGroup = totalEmpty/kNumPlayBufferGroups;
#else
numEmptyPacketsInPlayBufferGroup = (totalEmpty*(bufferGroupNum+1)+kNumPlayBufferGroups/2)/kNumPlayBufferGroups - emptySoFar;
#endif
emptySoFar += numEmptyPacketsInPlayBufferGroup;
pFirstBufferGroupDCLCommand = NULL;
pBufferGroupSkipEmptyPacketDCLLabel = NULL;
pGlobalData->fSharedDCLVars.fDataOffset[bufferGroupNum] =
(UInt8*)pTransmitBuffer - pGlobalData->fStreamVars.fDCLBuffers;
while (dataPacketNum < pGlobalData->numDataPacketsPerGroup)
{
pDCLTransferPacket = (DCLTransferPacketPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLTransferPacket));
pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLTransferPacket;
pDCLCommand = (DCLCommandPtr) pDCLTransferPacket;
pDCLTransferPacket->opcode = kDCLSendPacketStartOp;
pDCLTransferPacket->size = pGlobalData->fDataPacketSize + kDVPacketCIPSize;
pageOffset = (UInt32) (pTransmitBuffer + pGlobalData->fAlignQuadSize) & 0x0fff;
if (pageOffset < (4*pGlobalData->fAlignQuadSize) && pageOffset > 0)
{
pTransmitBuffer += pGlobalData->fAlignQuadSize;
pTransmitBuffer = (UInt32 *)((UInt32)pTransmitBuffer & 0xfffff000);
}
pDCLTransferPacket->buffer = (UInt8 *) pTransmitBuffer;
pTransmitBuffer += pGlobalData->fAlignQuadSize;
if (pFirstBufferGroupDCLCommand == NULL)
pFirstBufferGroupDCLCommand = (DCLCommandPtr) pDCLCommand;
dataPacketNum++;
numPackets++;
emptyPacketNumerator += numEmptyPacketsInPlayBufferGroup;
if (emptyPacketNumerator >= pGlobalData->numDataPacketsPerGroup)
{
if (pBufferGroupSkipEmptyPacketDCLLabel == NULL)
{
pDCLJump = (DCLJumpPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLJump));
pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLJump = pDCLJump;
pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLJump;
pDCLCommand = (DCLCommandPtr) pDCLJump;
pDCLJump->opcode = kDCLJumpOp | kFWDCLOpDynamicFlag;
pDCLLabel = (DCLLabelPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLLabel));
pPlayBufferGroupData->pBufferGroupDontSkipEmptyPacketDCLLabel = pDCLLabel;
pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLLabel;
pDCLCommand = (DCLCommandPtr) pDCLLabel;
pDCLLabel->opcode = kDCLLabelOp;
pDCLJump->pJumpDCLLabel = pDCLLabel;
}
pDCLTransferPacket = (DCLTransferPacketPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLTransferPacket));
pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLTransferPacket;
pDCLCommand = (DCLCommandPtr) pDCLTransferPacket;
pDCLTransferPacket->opcode = kDCLSendPacketStartOp;
pDCLTransferPacket->buffer = pEmptyTransmitBuffer;
pDCLTransferPacket->size = kDVPacketCIPSize;
pEmptyTransmitBuffer += kDVPacketCIPSize+kDVPacketAlignSlop;
numPackets++;
emptyPacketNumerator -= pGlobalData->numDataPacketsPerGroup;
if (pBufferGroupSkipEmptyPacketDCLLabel == NULL)
{
pDCLLabel = (DCLLabelPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLLabel));
pBufferGroupSkipEmptyPacketDCLLabel = pDCLLabel;
pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLLabel = pBufferGroupSkipEmptyPacketDCLLabel;
pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLLabel;
pDCLCommand = (DCLCommandPtr) pDCLLabel;
pDCLLabel->opcode = kDCLLabelOp;
}
}
}
pPlayBufferGroupData->numPackets = numPackets;
pPlayBufferGroupData->pFirstBufferGroupDCLCommand = pFirstBufferGroupDCLCommand;
pPlayBufferGroupData->pLastBufferGroupDCLCommand = (DCLCommandPtr) pDCLCommand;
DVCreatePlayBufferGroupUpdateList( pPlayBufferGroupData );
pGlobalData->totalPackets += numPackets;
pGlobalData->activePackets += numPackets;
#if ALT_TIMING
{
UInt32 nominalProgramCycleTime = 0;
UInt32 nominalActivePackets = 0;
SInt32 cycleDrift = 0;
nominalProgramCycleTime = ConvertFractionalSecondsToFWCycleTime( pGlobalData->playFramePeriodNumerator*(kNumPlayBufferGroups/2),
pGlobalData->playFramePeriodDenominator );
nominalActivePackets = ((nominalProgramCycleTime & 0x01FFF000) >> 12);
cycleDrift = pGlobalData->activePackets - ((nominalActivePackets*(bufferGroupNum+1)) / kNumPlayBufferGroups);
if(cycleDrift > 0) {
pGlobalData->activePackets--;
pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLJump->pJumpDCLLabel = pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLLabel;
pPlayBufferGroupData->skippingEmptyPacket = true;
}
else
pPlayBufferGroupData->skippingEmptyPacket = false;
}
#else
emptyError = getEmptyPacketsPerGroup(pGlobalData, pGlobalData->numDataPacketsPerGroup*(bufferGroupNum+1));
emptyError = pGlobalData->activePackets - pGlobalData->numDataPacketsPerGroup*(bufferGroupNum+1) - emptyError;
if(emptyError > 0) {
pGlobalData->activePackets--;
pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLJump->pJumpDCLLabel = pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLLabel;
pPlayBufferGroupData->skippingEmptyPacket = true;
}
else
pPlayBufferGroupData->skippingEmptyPacket = false;
#endif
pBufferGroupDCLJump = (DCLJumpPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLJump));
pPlayBufferGroupData->pEndOfBufferGroupDCLJump = pBufferGroupDCLJump;
pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pBufferGroupDCLJump;
pDCLCommand = (DCLCommandPtr) pBufferGroupDCLJump;
pBufferGroupDCLJump->opcode = kDCLJumpOp | kFWDCLOpDynamicFlag;
pBufferGroupDCLLabel = (DCLLabelPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLLabel));
pPlayBufferGroupData->pEndOfBufferGroupDCLLabel = pBufferGroupDCLLabel;
pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pBufferGroupDCLLabel;
pDCLCommand = (DCLCommandPtr) pBufferGroupDCLLabel;
pBufferGroupDCLLabel->opcode = kDCLLabelOp;
pBufferGroupDCLJump->pJumpDCLLabel = pBufferGroupDCLLabel;
pDCLTimeStamp = (DCLPtrTimeStampPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLPtrTimeStamp));
pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLTimeStamp;
pDCLCommand = (DCLCommandPtr) pDCLTimeStamp;
pDCLTimeStamp->opcode = kDCLPtrTimeStampOp;
*pTimeStampPtr = 0xffffffff; pDCLTimeStamp->timeStampPtr = pTimeStampPtr++;
pPlayBufferGroupData->pBufferGroupTimeStampPtr = pDCLTimeStamp->timeStampPtr;
pPlayBufferGroupData->timeStampUpdateDCLList = (DCLCommandPtr) pDCLTimeStamp;
pDCLUpdateDCLList = (DCLUpdateDCLListPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLUpdateDCLList));
pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList;
pDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList;
pDCLUpdateDCLList->opcode = kDCLUpdateDCLListOp;
pDCLUpdateDCLList->dclCommandList = &(pPlayBufferGroupData->timeStampUpdateDCLList);
pDCLUpdateDCLList->numDCLCommands = 1;
pDCLUpdateDCLList = (DCLUpdateDCLListPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLUpdateDCLList));
pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList;
pDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList;
pDCLUpdateDCLList->opcode = kDCLUpdateDCLListOp;
pDCLUpdateDCLList->dclCommandList = pPlayBufferGroupData->bufferGroupUpdateDCLList;
pDCLUpdateDCLList->numDCLCommands = pPlayBufferGroupData->updateListSize;
}
pDCLJump = (DCLJumpPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLJump));
pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLJump;
pDCLCommand = (DCLCommandPtr) pDCLJump;
pDCLJump->opcode = kDCLJumpOp | kFWDCLOpDynamicFlag;
pDCLJump->pJumpDCLLabel = pLoopDCLLabel;
pUnderrunDCLLabel = (DCLLabelPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLLabel));
pGlobalData->pUnderrunDCLLabel = pUnderrunDCLLabel;
pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pUnderrunDCLLabel;
pDCLCommand = (DCLCommandPtr) pUnderrunDCLLabel;
pUnderrunDCLLabel->opcode = kDCLLabelOp;
pBufferGroupDCLJump->pJumpDCLLabel = pUnderrunDCLLabel;
pDCLCallProc = (DCLCallProcPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLCallProc));
pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLCallProc;
pDCLCallProc->pNextDCLCommand = NULL;
pDCLCallProc->opcode = kDCLCallProcOp;
pDCLCallProc->proc = DVHandleOutputUnderrun;
pDCLCallProc->procData = (UInt32) pGlobalData;
return kIOReturnSuccess;
bail:
return res;
}
DVGlobalOutPtr DVAllocWrite(DVDevice *device, DVThread *thread)
{
DVGlobalOutPtr globs;
globs = malloc(sizeof(DVGlobalOut));
if(!globs)
return NULL;
bzero(globs, sizeof(DVGlobalOut));
initStream(&globs->fStreamVars, device, device->fOutPlug, device->fWriteChan, thread);
globs->fUpdateBuffers = 1;
return globs;
}
IOReturn DVWriteSetSignalMode(DVGlobalOutPtr globs, UInt8 mode)
{
globs->fStreamVars.fSignalMode = mode;
switch (mode)
{
case kAVCSignalModeSD525_60:
case kAVCSignalModeDVCPro525_60:
globs->fStreamVars.fDVFrameSize = kFrameSize_SD525_60;
break;
case kAVCSignalModeSD625_50:
case kAVCSignalModeDVCPro625_50:
globs->fStreamVars.fDVFrameSize = kFrameSize_SD625_50;
break;
case kAVCSignalModeSDL525_60:
globs->fStreamVars.fDVFrameSize = kFrameSize_SDL525_60;
break;
case kAVCSignalModeSDL625_50:
globs->fStreamVars.fDVFrameSize = kFrameSize_SDL625_50;
break;
case kAVCSignalModeDVCPro50_525_60:
case kAVCSignalModeHD1125_60:
globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro50_525_60;
break;
case kAVCSignalModeDVCPro50_625_50:
case kAVCSignalModeHD1250_50:
globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro50_625_50;
break;
case kAVCSignalModeDVCPro100_525_60:
globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro100_525_60;
break;
case kAVCSignalModeDVCPro100_625_50:
globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro100_625_50;
break;
default:
globs->fStreamVars.fDVFrameSize = kFrameSize_SD625_50;
break;
};
return kIOReturnSuccess;
}
IOReturn DVWriteAllocFrames(DVGlobalOutPtr pGlobalData, UInt32 numFrames,
DVFrameVars **frameVars, UInt8 **frames)
{
IOReturn err;
do {
err = DVAllocFrames(&pGlobalData->fStreamVars.fFrames,
numFrames,
pGlobalData->fStreamVars.fDVFrameSize,
frameVars,
frames);
if(err != kIOReturnSuccess)
break;
err = allocateBuffers(pGlobalData);
if(err != kIOReturnSuccess)
break;
err = buildWriteProgram(pGlobalData);
} while (0);
return err;
}
UInt8 * DVWriteGetDCLBuffer(DVGlobalOutPtr pGlobalData, DVSharedVars **varPtr)
{
pGlobalData->fUpdateBuffers = 0;
*varPtr = &pGlobalData->fSharedDCLVars;
return pGlobalData->fStreamVars.fDCLBuffers;
}
static IOReturn doDVWriteStart(DVGlobalOutPtr pGlobalData)
{
IOReturn err;
DVLocalOutPtr pPlayBufferGroupData;
UInt32 bufferGroupNum;
int i;
DVThread * dvThread = pGlobalData->fStreamVars.fThread;
do {
err = writePlug(pGlobalData->fStreamVars.fAVCProtoInterface, pGlobalData->fStreamVars.fPlug,
kIOFWPCROnline | kIOFWPCRBroadcast | (1 << kIOFWPCRP2PCountPhase) |
(pGlobalData->fStreamVars.fIsocChannel<<kIOFWPCRChannelPhase) |
(15 << kIOFWPCROutputOverheadPhase) | (122 << kIOFWPCROutputPayloadPhase));
if(err)
break;
#if 1
err = MakeP2PConnectionForWrite(pGlobalData->fStreamVars.pDVDevice,0,pGlobalData->fStreamVars.fIsocChannel);
#else
err = writeDeviceInputPlug(pGlobalData->fStreamVars.pFWDevice, 0,
kIOFWPCRChannel, pGlobalData->fStreamVars.fIsocChannel<<kIOFWPCRChannelPhase);
#endif
pGlobalData->nextSYT = kPlaySYTDelay<<12;
pGlobalData->nextDBC = 0;
pGlobalData->nextDataPacketNum = 0;
pGlobalData->pImageBuffer = NULL;
pGlobalData->fSharedDCLVars.fDMAPos = 0;
pPlayBufferGroupData = &pGlobalData->fLocalDataArray[0];
for (bufferGroupNum = 0; bufferGroupNum < kNumPlayBufferGroups; bufferGroupNum++)
{
DVUpdateOutputBuffers( pPlayBufferGroupData);
pPlayBufferGroupData = pPlayBufferGroupData->pNextLocalData;
}
err = openStream(&pGlobalData->fStreamVars, true, pGlobalData->fDataPacketSize + kDVPacketCIPSize);
for(i=0; i<kDVMaxStreamsActive; i++) {
if(dvThread->fOutStreams[i] == NULL) {
dvThread->fOutStreams[i] = pGlobalData;
break;
}
else if(dvThread->fOutStreams[i] == pGlobalData) {
break; }
}
} while (0);
return err;
}
IOReturn DVWriteStart(DVGlobalOutPtr pGlobalData)
{
return DVRequest(pGlobalData->fStreamVars.fThread, doDVWriteStart, pGlobalData, 0);
}
static void doDVWriteStop(DVGlobalOutPtr pGlobalData)
{
int i;
DVThread * dvThread = pGlobalData->fStreamVars.fThread;
for(i=0; i<kDVMaxStreamsActive; i++) {
if(dvThread->fOutStreams[i] == pGlobalData) {
dvThread->fOutStreams[i] = NULL;
break;
}
}
closeStream(&pGlobalData->fStreamVars);
#if 1
BreakP2PConnectionForWrite(pGlobalData->fStreamVars.pDVDevice,0,pGlobalData->fStreamVars.fIsocChannel);
#else
writePlug(pGlobalData->fStreamVars.fAVCProtoInterface, pGlobalData->fStreamVars.fPlug,
122 << kIOFWPCROutputPayloadPhase);
#endif
DVDisposeDCLOutput(pGlobalData);
}
void DVWriteStop(DVGlobalOutPtr pGlobalData)
{
DVRequest(pGlobalData->fStreamVars.fThread, doDVWriteStop, pGlobalData, 0);
}
void DVWriteFreeFrames(DVGlobalOutPtr globs)
{
DVFreeFrames(&globs->fStreamVars.fFrames);
}
void DVWriteFree(DVGlobalOutPtr globs)
{
free(globs);
}
DVGlobalInPtr DVAllocRead(DVDevice *device, DVThread *thread)
{
DVGlobalInPtr globs;
globs = malloc(sizeof(DVGlobalIn));
if(!globs)
return NULL;
bzero(globs, sizeof(DVGlobalIn));
initStream(&globs->fStreamVars, device, kNoPlug, device->fReadChan, thread);
if (device->standard == 0)
DVReadSetSignalMode(globs,0x00); else
DVReadSetSignalMode(globs,0x80);
return globs;
}
IOReturn DVReadSetSignalMode(DVGlobalInPtr globs, UInt8 mode)
{
globs->fStreamVars.fSignalMode = mode;
switch (mode)
{
case kAVCSignalModeSD525_60:
case kAVCSignalModeDVCPro525_60:
globs->fStreamVars.fDVFrameSize = kFrameSize_SD525_60;
break;
case kAVCSignalModeSD625_50:
case kAVCSignalModeDVCPro625_50:
globs->fStreamVars.fDVFrameSize = kFrameSize_SD625_50;
break;
case kAVCSignalModeSDL525_60:
globs->fStreamVars.fDVFrameSize = kFrameSize_SDL525_60;
break;
case kAVCSignalModeSDL625_50:
globs->fStreamVars.fDVFrameSize = kFrameSize_SDL625_50;
break;
case kAVCSignalModeDVCPro50_525_60:
case kAVCSignalModeHD1125_60:
globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro50_525_60;
break;
case kAVCSignalModeDVCPro50_625_50:
case kAVCSignalModeHD1250_50:
globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro50_625_50;
break;
case kAVCSignalModeDVCPro100_525_60:
globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro100_525_60;
break;
case kAVCSignalModeDVCPro100_625_50:
globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro100_625_50;
break;
default:
globs->fStreamVars.fDVFrameSize = kFrameSize_SD625_50;
break;
};
return kIOReturnSuccess;
}
IOReturn DVReadAllocFrames(DVGlobalInPtr globs, UInt32 numFrames,
DVFrameVars **frameVars, UInt8 **frames)
{
return DVAllocFrames(&globs->fStreamVars.fFrames,
numFrames,
globs->fStreamVars.fDVFrameSize,
frameVars,
frames);
}
static void doDVReadHandleInputUnderrun( DCLCommandPtr pDCLCommandPtr )
{
DVGlobalInPtr pGlobalData;
DVStream *stream;
UInt32 timeNow, lastFrameTime;
pGlobalData = (DVGlobalInPtr)((DCLCallProcPtr)pDCLCommandPtr)->procData;
stream = &pGlobalData->fStreamVars;
(*stream->pFWDevice)->
GetCycleTime(stream->pFWDevice, &timeNow);
syslog(LOG_INFO, "At %8.3f Req time %8.3f, now %8.3f\n",
stream->fThread->setTimeoutTime, stream->fThread->requestTimeoutTime, CFAbsoluteTimeGetCurrent());
syslog(LOG_INFO, "DVReadHandleInputUnderrun: 0x%p, last block = %d, status %d, writer %d reader %d timeNow %x\n",
pGlobalData, pGlobalData->fState, stream->fFrames.fStatus,
stream->fFrames.fWriter, stream->fFrames.fReader, timeNow);
DVReadStop(pGlobalData);
lastFrameTime = stream->fFrames.fFrameTime[(stream->fFrames.fWriter-1)%stream->fFrames.fNumFrames];
pGlobalData->fSynced = 0;
pGlobalData->fRestarted = true;
pGlobalData->fLastFrameTime = lastFrameTime;
DVReadStart(pGlobalData);
}
static void DVReadHandleInputUnderrun( DCLCommandPtr pDCLCommandPtr )
{
DVGlobalInPtr pGlobalData;
pGlobalData = (DVGlobalInPtr)((DCLCallProcPtr)pDCLCommandPtr)->procData;
DVRequest(pGlobalData->fStreamVars.fThread, doDVReadHandleInputUnderrun, pDCLCommandPtr, 0);
}
static void DVStorePackets(DVLocalInPtr pLocalData)
{
DVGlobalInPtr pGlobalData;
DCLCommandPtr pCurrentCmd;
DCLTransferPacketPtr pDCLTransferPacket;
UInt8 * pPacketBuffer;
UInt32 packetHeader, packetSize, packetNum, packetPerFrame;
bool vSyncDetected;
UInt8 currentSequenceCount;
int prevBlock;
UInt8 fn;
UInt8 stype;
#if TIMING
CFAbsoluteTime cstart, cend;
cstart = CFAbsoluteTimeGetCurrent();
#endif
pGlobalData = pLocalData->pGlobalData;
pCurrentCmd = pLocalData->fFirstCmd;
packetPerFrame = (pGlobalData->fStreamVars.fSignalMode & kAVCSignalModeMask_50) ?
kPALNumDataPacketsPerDVFrame : kNTSCNumDataPacketsPerDVFrame;
for ( packetNum = 0; packetNum < kNumPacketsPerInputBuffer; packetNum++ )
{
pDCLTransferPacket = (DCLTransferPacketPtr) pCurrentCmd;
pPacketBuffer = (UInt8*)pDCLTransferPacket->buffer;
packetHeader = *((UInt32*) pPacketBuffer);
pPacketBuffer += 4; packetSize = (packetHeader & kFWIsochDataLength) >> kFWIsochDataLengthPhase;
#if 1
fn = ((pPacketBuffer[2] & 0xC0) >> 6);
if (fn == 0)
fn = 1;
else
fn = 1 << fn;
if(packetSize < 8) {
syslog(LOG_INFO, "DVStorePackets: size %d header 0x%x\n", packetSize, packetHeader);
packetSize = 8;
}
else if(packetSize > 8 && packetSize != (pPacketBuffer[1]*4*fn) + 8) {
syslog(LOG_INFO, "DVStorePackets: size %d header 0x%x\n", packetSize, packetHeader);
packetSize = 8;
}
#endif
if( packetSize > 8 ) {
currentSequenceCount = pPacketBuffer[3];
stype = pGlobalData->fStreamVars.fSignalMode & kAVCSignalModeMask_STYPE;
switch (stype)
{
case kAVCSignalModeMask_DVCPro50:
pGlobalData->lastSequenceCount += 2;
break;
case kAVCSignalModeMask_DVCPro100:
pGlobalData->lastSequenceCount += 4;
break;
case kAVCSignalModeMask_SDL:
case kAVCSignalModeMask_DVCPro25:
case kAVCSignalModeMask_HD:
default: pGlobalData->lastSequenceCount += 1;
break;
};
packetSize -= 8;
vSyncDetected = ((*(short *)(pPacketBuffer + 8) & 0xE0F8 ) == 0x0000 );
if( vSyncDetected ) {
UInt32 frameEnd = SubtractFWCycleTimeFromFWCycleTime(*pLocalData->fTimeStampPtr, (kNumPacketsPerInputBuffer - packetNum) << 12);
UInt32 cip2 = *(UInt32 *)(pPacketBuffer+4);
pGlobalData->fStreamVars.fSignalMode = (cip2 >> 16) & 0xff;
packetPerFrame = (pGlobalData->fStreamVars.fSignalMode & kAVCSignalModeMask_50) ?
kPALNumDataPacketsPerDVFrame : kNTSCNumDataPacketsPerDVFrame;
if( pGlobalData->packetCount == packetPerFrame ) {
DVSetInputFrameSizeAndMode(&pGlobalData->fStreamVars.fFrames, packetPerFrame * packetSize,
pGlobalData->fStreamVars.fSignalMode, frameEnd);
}
else {
if(pGlobalData->fRestarted) {
UInt32 lastFrameTime;
SInt32 cycleDiff, secsDiff;
UInt32 dropped;
lastFrameTime = pGlobalData->fLastFrameTime;
cycleDiff = ((frameEnd & 0x01FFF000) - (lastFrameTime & 0x01FFF000));
if(cycleDiff < 0) {
cycleDiff += 8000 << 12;
frameEnd -= 0x02000000;
}
secsDiff = (frameEnd & 0x0e000000) - (lastFrameTime & 0x0e000000);
if(secsDiff < 0)
secsDiff += 0x10000000;
secsDiff >>= 25;
cycleDiff >>= 12;
cycleDiff += secsDiff * 8000;
if(pGlobalData->fStreamVars.fSignalMode & kAVCSignalModeMask_50)
dropped = (cycleDiff * kPALFrameRateNumerator + (4000*kPALFrameRateDenominator)) / (8000*kPALFrameRateDenominator);
else
dropped = (cycleDiff * kNTSCFrameRateNumerator + (4000*kNTSCFrameRateDenominator)) / (8000*kNTSCFrameRateDenominator);
pGlobalData->fStreamVars.fFrames.fDroppedFrames += dropped;
pGlobalData->fRestarted = false;
}
}
pGlobalData->packetCount = 0;
pGlobalData->lastSequenceCount = currentSequenceCount;
DVGetNextEmptyInputFrame(&pGlobalData->fStreamVars.fFrames,
&(pGlobalData->pImageBuffer),
pGlobalData->fStreamVars.fDVFrameSize);
pGlobalData->fSynced = true;
}
if(pGlobalData->fSynced) {
pPacketBuffer += 8;
if (currentSequenceCount == pGlobalData->lastSequenceCount && pGlobalData->packetCount < packetPerFrame) {
bcopy( pPacketBuffer, (void *)((UInt32) pGlobalData->pImageBuffer + (pGlobalData->packetCount * packetSize)), packetSize );
pGlobalData->packetCount++;
}
else {
pGlobalData->packetCount = 0;
pGlobalData->fSynced = false;
}
}
pGlobalData->lastSequenceCount = currentSequenceCount;
}
pCurrentCmd = pCurrentCmd->pNextDCLCommand;
}
pGlobalData->fState = pLocalData->fBlockNum;
if(pLocalData->fBlockNum == 0)
prevBlock = kNumPingPongs-1;
else
prevBlock = pLocalData->fBlockNum-1;
ModifyDCLJump(pGlobalData->fStreamVars.pFWLocalIsochPort,
pGlobalData->fLocalDataArray[prevBlock].fStateJmp, pLocalData->fStateLabel);
ModifyDCLJump(pGlobalData->fStreamVars.pFWLocalIsochPort, pLocalData->fStateJmp, pGlobalData->fTerminal);
#if TIMING
cend = CFAbsoluteTimeGetCurrent();
DVLog(pGlobalData->fStreamVars.fThread, 'isoc', cstart, cend);
#endif
}
void DVReadPoll(DVGlobalInPtr globs)
{
int i, pos;
pos = globs->fState;
for(i=pos; i<kNumPingPongs; i++)
if(*globs->fLocalDataArray[i].fTimeStampPtr != 0xffffffff) {
DVStorePackets(&globs->fLocalDataArray[i]);
*globs->fLocalDataArray[i].fTimeStampPtr = 0xffffffff;
}
for(i=0; i<pos; i++)
if(*globs->fLocalDataArray[i].fTimeStampPtr != 0xffffffff) {
DVStorePackets(&globs->fLocalDataArray[i]);
*globs->fLocalDataArray[i].fTimeStampPtr = 0xffffffff;
}
}
IOReturn DVReadStart(DVGlobalInPtr globs)
{
DCLCommandPtr opcodes;
UInt8 * pingPongBuffer = NULL;
UInt8 * pingPongPtr;
UInt8 * pDCLCommand;
DCLLabelPtr pStartDCLLabel;
DCLLabelPtr pBlockDCLLabel;
DCLLabelPtr pUnderrunDCLLabel;
DCLTransferPacketPtr pDCLTransferPacket;
DCLPtrTimeStampPtr pDCLTimeStamp;
DCLCallProcPtr pUnderrunDCLCallProc;
DCLJumpPtr pDCLPingPongLoop;
int pingPongNum, packetNum;
UInt32 updateListSize;
UInt32 bufferSize;
DCLUpdateDCLListPtr pDCLUpdateDCLList;
DCLCommandPtr *updateDCLList, *startUpdateDCLList;
DVLocalInPtr pLocalData;
IOReturn res;
UInt32 * timeStampPtr;
int i;
UInt32 packetBufferSize;
UInt32 alignedDVPacketSize;
UInt32 pingPongBufferSize;
pingPongBuffer = NULL;
globs->fStreamVars.pDCLList = NULL;
globs->ppUpdateDCLList = NULL;
globs->pImageBuffer = NULL;
globs->fStreamVars.fDCLBuffers = NULL;
globs->packetCount = 0;
globs->fState = 0;
switch (globs->fStreamVars.fSignalMode)
{
case kAVCSignalModeSDL525_60:
case kAVCSignalModeSDL625_50:
packetBufferSize = 252;
alignedDVPacketSize = 512;
break;
case kAVCSignalModeDVCPro50_525_60:
case kAVCSignalModeHD1125_60:
case kAVCSignalModeDVCPro50_625_50:
case kAVCSignalModeHD1250_50:
packetBufferSize = 972;
alignedDVPacketSize = 1024;
break;
case kAVCSignalModeDVCPro100_525_60:
case kAVCSignalModeDVCPro100_625_50:
packetBufferSize = 1932;
alignedDVPacketSize = 2048;
break;
case kAVCSignalModeSD525_60:
case kAVCSignalModeDVCPro525_60:
case kAVCSignalModeSD625_50:
case kAVCSignalModeDVCPro625_50:
default:
packetBufferSize = 492;
alignedDVPacketSize = 512;
break;
};
pingPongBufferSize = kNumPingPongs * kNumPacketsPerPingPong * alignedDVPacketSize;
bufferSize = pingPongBufferSize + alignedDVPacketSize + kNumPingPongs * sizeof(UInt32);
vm_allocate(mach_task_self(), (vm_address_t *)&pingPongBuffer,
bufferSize, VM_FLAGS_ANYWHERE);
if (pingPongBuffer == NULL)
{
res = kIOReturnNoMemory;
goto bail;
}
timeStampPtr = (UInt32 *)(pingPongBuffer + pingPongBufferSize + alignedDVPacketSize);
globs->fStreamVars.fDCLBuffers = pingPongBuffer;
globs->fStreamVars.fDCLBufferSize = bufferSize;
bzero( pingPongBuffer, bufferSize );
opcodes = (DCLCommandPtr)malloc(kRecordDCLProgramSize);
globs->fStreamVars.pDCLList = opcodes;
if (opcodes == NULL)
{
res = kIOReturnNoMemory;
goto bail;
}
bzero( opcodes, kRecordDCLProgramSize );
pDCLCommand = (UInt8 *)opcodes;
updateDCLList = (DCLCommandPtr *)malloc(kRecordNumDCLs * sizeof(DCLCommandPtr));
globs->ppUpdateDCLList = updateDCLList;
if (updateDCLList == NULL)
{
res = kIOReturnNoMemory;
goto bail;
}
bzero( updateDCLList, kRecordNumDCLs * sizeof(DCLCommandPtr));
pStartDCLLabel = (DCLLabelPtr) pDCLCommand;
pDCLCommand += sizeof (DCLLabel);
pStartDCLLabel->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
pStartDCLLabel->opcode = kDCLLabelOp;
pingPongPtr = pingPongBuffer;
for (pingPongNum = 0; pingPongNum < kNumPingPongs; pingPongNum++)
{
pLocalData = &globs->fLocalDataArray[pingPongNum];
pLocalData->pGlobalData = globs;
pLocalData->fBlockNum = pingPongNum;
pLocalData->fTimeStampPtr = timeStampPtr;
*timeStampPtr = 0xffffffff;
startUpdateDCLList = updateDCLList;
updateListSize = 0;
pBlockDCLLabel = (DCLLabelPtr) pDCLCommand;
pDCLCommand += sizeof (DCLLabel);
pBlockDCLLabel->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
pBlockDCLLabel->opcode = kDCLLabelOp;
pLocalData->fStateLabel = pBlockDCLLabel;
pLocalData->fFirstCmd = (DCLCommandPtr) pDCLCommand;
for (packetNum = 0; packetNum < kNumPacketsPerPingPong; packetNum++)
{
pDCLTransferPacket = (DCLTransferPacketPtr) pDCLCommand;
pDCLCommand += sizeof (DCLTransferPacket);
pDCLTransferPacket->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
pDCLTransferPacket->opcode = kDCLReceivePacketStartOp;
pDCLTransferPacket->buffer = pingPongPtr;
pDCLTransferPacket->size = packetBufferSize;
*updateDCLList++ = (DCLCommandPtr) pDCLTransferPacket;
updateListSize++;
pingPongPtr += alignedDVPacketSize;
}
pDCLTimeStamp = (DCLPtrTimeStampPtr) pDCLCommand;
pDCLCommand += sizeof (DCLPtrTimeStamp);
pDCLTimeStamp->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
pDCLTimeStamp->opcode = kDCLPtrTimeStampOp;
pDCLTimeStamp->timeStampPtr = timeStampPtr++;
*updateDCLList++ = (DCLCommandPtr) pDCLTimeStamp;
updateListSize++;
pDCLUpdateDCLList = (DCLUpdateDCLListPtr) pDCLCommand;
pDCLCommand += sizeof (DCLUpdateDCLList);
pDCLUpdateDCLList->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
pDCLUpdateDCLList->opcode = kDCLUpdateDCLListOp;
pDCLUpdateDCLList->dclCommandList = startUpdateDCLList;
pDCLUpdateDCLList->numDCLCommands = updateListSize;
pDCLPingPongLoop = (DCLJumpPtr) pDCLCommand;
pDCLCommand += sizeof (DCLJump);
pDCLPingPongLoop->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
pDCLPingPongLoop->opcode = kDCLJumpOp | kFWDCLOpDynamicFlag;
pDCLPingPongLoop->pJumpDCLLabel = (DCLLabelPtr)pDCLCommand;
pLocalData->fStateJmp = pDCLPingPongLoop;
}
pUnderrunDCLLabel = (DCLLabelPtr) pDCLCommand;
pDCLCommand += sizeof (DCLLabel);
pUnderrunDCLLabel->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
pUnderrunDCLLabel->opcode = kDCLLabelOp;
globs->fTerminal = pUnderrunDCLLabel;
pDCLTransferPacket = (DCLTransferPacketPtr) pDCLCommand;
pDCLCommand += sizeof (DCLTransferPacket);
pDCLTransferPacket->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
pDCLTransferPacket->opcode = kDCLReceivePacketStartOp;
pDCLTransferPacket->buffer = pingPongPtr;
pDCLTransferPacket->size = packetBufferSize;
pUnderrunDCLCallProc = (DCLCallProcPtr) pDCLCommand;
pDCLCommand += sizeof (DCLCallProc);
pUnderrunDCLCallProc->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
pUnderrunDCLCallProc->opcode = kDCLCallProcOp;
pUnderrunDCLCallProc->proc = DVReadHandleInputUnderrun;
pUnderrunDCLCallProc->procData = (UInt32)globs;
pUnderrunDCLLabel = (DCLLabelPtr) pDCLCommand;
pDCLCommand += sizeof (DCLLabel);
pUnderrunDCLLabel->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
pUnderrunDCLLabel->opcode = kDCLLabelOp;
pDCLTransferPacket = (DCLTransferPacketPtr) pDCLCommand;
pDCLCommand += sizeof (DCLTransferPacket);
pDCLTransferPacket->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
pDCLTransferPacket->opcode = kDCLReceivePacketStartOp;
pDCLTransferPacket->buffer = pingPongPtr;
pDCLTransferPacket->size = packetBufferSize;
pDCLPingPongLoop = (DCLJumpPtr) pDCLCommand;
pDCLPingPongLoop->pNextDCLCommand = NULL;
pDCLPingPongLoop->opcode = kDCLJumpOp;
pDCLPingPongLoop->pJumpDCLLabel = pUnderrunDCLLabel;
res = openStream(&globs->fStreamVars, false, kDVSDPayloadPacketSize + kDVPacketCIPSize);
if(res != kIOReturnSuccess)
goto bail;
for(i=0; i<kDVMaxStreamsActive; i++) {
if(globs->fStreamVars.fThread->fInStreams[i] == NULL) {
globs->fStreamVars.fThread->fInStreams[i] = globs;
break;
}
}
return kIOReturnSuccess;
bail:
syslog(LOG_INFO, "DVRead::Start() failed: 0x%x\n", res);
return res;
}
static IOReturn doDVReadStop(DVGlobalInPtr pGlobalData)
{
int i;
for(i=0; i<kDVMaxStreamsActive; i++) {
if(pGlobalData->fStreamVars.fThread->fInStreams[i] == pGlobalData) {
pGlobalData->fStreamVars.fThread->fInStreams[i] = NULL;
break;
}
}
closeStream(&pGlobalData->fStreamVars);
if ( pGlobalData->ppUpdateDCLList) {
free( pGlobalData->ppUpdateDCLList); pGlobalData->ppUpdateDCLList = NULL;
}
if ( pGlobalData->fStreamVars.pDCLList) {
free( pGlobalData->fStreamVars.pDCLList); pGlobalData->fStreamVars.pDCLList = NULL;
}
if ( pGlobalData->fStreamVars.fDCLBuffers) {
vm_deallocate(mach_task_self(), (vm_address_t)pGlobalData->fStreamVars.fDCLBuffers,
pGlobalData->fStreamVars.fDCLBufferSize);
pGlobalData->fStreamVars.fDCLBuffers = NULL;
}
return kIOReturnSuccess;
}
void DVReadStop(DVGlobalInPtr pGlobalData)
{
DVRequest(pGlobalData->fStreamVars.fThread, doDVReadStop, pGlobalData, 0);
}
void DVReadFreeFrames(DVGlobalInPtr globs)
{
DVFreeFrames(&globs->fStreamVars.fFrames);
}
void DVReadFree(DVGlobalInPtr globs)
{
free(globs);
}
void DVLog(DVThread *thread, UInt32 tag, CFAbsoluteTime start, CFAbsoluteTime end)
{
#if TIMING
Log * log;
log = &thread->fLog[thread->fLogPos];
log->tag = tag;
log->start = start;
log->end = end;
thread->fLogPos++;
if(thread->fLogPos >= kLogSize)
thread->fLogPos = 0;
#endif
}
void DVDumpLog(DVThread *thread)
{
#if TIMING
Log * log;
UInt32 tag;
int i;
for(i=thread->fLogPos; i<kLogSize; i++) {
log = &thread->fLog[i];
tag = log->tag;
if(tag) {
syslog(LOG_INFO, "%d %c%c%c%c %8.3f to %8.3f\n", i, tag>>24, tag>>16, tag>>8, tag, log->start, log->end);
}
else
syslog(LOG_INFO, "%d %x %8.3f to %8.3f\n", i, tag, log->start, log->end);
}
for(i=0; i< thread->fLogPos; i++) {
log = &thread->fLog[i];
tag = log->tag;
if(tag) {
syslog(LOG_INFO, "%d %c%c%c%c %8.3f to %8.3f\n", i, tag>>24, tag>>16, tag>>8, tag, log->start, log->end);
}
else
syslog(LOG_INFO, "%d %x %8.3f to %8.3f\n", i, tag, log->start, log->end);
}
#endif
}