1/*
2 * Copyright (c) 2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
24 *  DVtest.c
25 *  IOFWDVComponents
26 *
27 *  Created by wgulland on Tue Oct 17 2000.
28 *  Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
29 *
30 */
31
32#include <Carbon/Carbon.h>
33
34#include <unistd.h>
35#include <stdio.h>
36#include <stdlib.h>
37
38#include <sys/types.h>
39#include <sys/stat.h>
40#include <fcntl.h>
41#include <IOKit/IOReturn.h>
42
43#include "DVFamily.h"
44
45typedef struct DVFuncsStruct {
46    UInt32 (*fDVCountDevices)( void );
47    OSErr (*fDVGetIndDevice)( DVDeviceID * pDVDevice, UInt32 index );
48    OSErr (*fDVSetDeviceName)( DVDeviceID deviceID, char * str );
49    OSErr (*fDVGetDeviceName)( DVDeviceID deviceID, char * str );
50
51    OSErr (*fDVOpenDriver)( DVDeviceID deviceID, DVDeviceRefNum *pRefNum );
52    OSErr (*fDVCloseDriver)( DVDeviceRefNum refNum );
53
54    OSErr (*fDVDoAVCTransaction)( DVDeviceRefNum refNum, AVCTransactionParamsPtr pParams );
55
56    OSErr (*fDVIsEnabled)( DVDeviceRefNum refNum, Boolean *isEnabled);
57    OSErr (*fDVGetDeviceStandard)( DVDeviceRefNum refNum, UInt32 * pStandard );
58
59    // DV Isoch Read
60    OSErr (*fDVEnableRead)( DVDeviceRefNum refNum );
61    OSErr (*fDVDisableRead)( DVDeviceRefNum refNum );
62    OSErr (*fDVReadFrame)( DVDeviceRefNum refNum, Ptr *ppReadBuffer, UInt32 * pSize );
63    OSErr (*fDVReleaseFrame)( DVDeviceRefNum refNum, Ptr pReadBuffer );
64
65    // DV Isoch Write
66    OSErr (*fDVEnableWrite)( DVDeviceRefNum refNum );
67    OSErr (*fDVDisableWrite)( DVDeviceRefNum refNum );
68    OSErr (*fDVGetEmptyFrame)( DVDeviceRefNum refNum, Ptr *ppEmptyFrameBuffer, UInt32 * pSize );
69    OSErr (*fDVWriteFrame)( DVDeviceRefNum refNum, Ptr pWriteBuffer );
70    OSErr (*fDVSetWriteSignalMode)( DVDeviceRefNum refNum, UInt8 mode);
71
72    // Notifications
73    OSErr (*fDVNewNotification)( DVDeviceRefNum refNum, DVNotifyProc notifyProc,
74						void *userData, DVNotificationID *pNotifyID );
75    OSErr (*fDVNotifyMeWhen)( DVDeviceRefNum refNum, DVNotificationID notifyID, UInt32 events);
76    OSErr (*fDVCancelNotification)( DVDeviceRefNum refNum, DVNotificationID notifyID );
77    OSErr (*fDVDisposeNotification)( DVDeviceRefNum refNum, DVNotificationID notifyID );
78
79} DVFuncs, *DVFuncsPtr;
80
81static DVFuncs sDVFuncs;
82static char *sFile = "/tmp/dump.dv";
83static int sWrite = 0;
84static int sLoop = 0;
85static int sSDL;
86static int sDVCPro;
87static int sNotifyTest;
88
89static CFBundleRef findMe()
90{
91    CFURLRef    bundleURL;
92    CFBundleRef myBundle;
93    Boolean didLoad = false;
94    // Make a CFURLRef from the CFString representation of the
95    // bundle's path. See the Core Foundation URL Services chapter
96    // for details.
97    bundleURL = CFURLCreateWithFileSystemPath(
98                    kCFAllocatorDefault,
99                    //CFSTR("/System/Library/Extensions/DVFamily.bundle"),
100                    CFSTR("DVFamily.bundle"),
101                    kCFURLPOSIXPathStyle,
102                    true );
103
104    // Make a bundle instance using the URLRef.
105    myBundle = CFBundleCreate( kCFAllocatorDefault, bundleURL );
106    if(!myBundle) {
107        bundleURL = CFURLCreateWithFileSystemPath(
108                        kCFAllocatorDefault,
109                        CFSTR("/System/Library/Extensions/DVFamily.bundle"),
110                        kCFURLPOSIXPathStyle,
111                        true );
112
113        // Make a bundle instance using the URLRef.
114        myBundle = CFBundleCreate( kCFAllocatorDefault, bundleURL );
115    }
116    printf("Bundle: %p\n", myBundle);
117
118    // Try to load the executable from my bundle.
119    didLoad = CFBundleLoadExecutable( myBundle );
120    printf("loaded? %d\n", didLoad);
121
122    sDVFuncs.fDVCountDevices = CFBundleGetFunctionPointerForName(
123                myBundle, CFSTR("DVCountDevices") );
124    sDVFuncs.fDVGetIndDevice = CFBundleGetFunctionPointerForName(
125                myBundle, CFSTR("DVGetIndDevice") );
126    sDVFuncs.fDVSetDeviceName = CFBundleGetFunctionPointerForName(
127                myBundle, CFSTR("DVSetDeviceName") );
128    sDVFuncs.fDVGetDeviceName = CFBundleGetFunctionPointerForName(
129                myBundle, CFSTR("DVGetDeviceName") );
130    sDVFuncs.fDVOpenDriver = CFBundleGetFunctionPointerForName(
131                myBundle, CFSTR("DVOpenDriver") );
132    sDVFuncs.fDVCloseDriver = CFBundleGetFunctionPointerForName(
133                myBundle, CFSTR("DVCloseDriver") );
134
135// AVC Stuff
136    sDVFuncs.fDVDoAVCTransaction = CFBundleGetFunctionPointerForName(
137                myBundle, CFSTR("DVDoAVCTransaction") );
138
139    sDVFuncs.fDVIsEnabled = CFBundleGetFunctionPointerForName(
140                myBundle, CFSTR("DVIsEnabled") );
141    sDVFuncs.fDVGetDeviceStandard = CFBundleGetFunctionPointerForName(
142                myBundle, CFSTR("DVGetDeviceStandard") );
143
144// Isoch I/O
145    sDVFuncs.fDVEnableRead = CFBundleGetFunctionPointerForName(
146                myBundle, CFSTR("DVEnableRead") );
147    sDVFuncs.fDVDisableRead = CFBundleGetFunctionPointerForName(
148                myBundle, CFSTR("DVDisableRead") );
149    sDVFuncs.fDVReadFrame = CFBundleGetFunctionPointerForName(
150                myBundle, CFSTR("DVReadFrame") );
151    sDVFuncs.fDVReleaseFrame = CFBundleGetFunctionPointerForName(
152                myBundle, CFSTR("DVReleaseFrame") );
153    sDVFuncs.fDVEnableWrite = CFBundleGetFunctionPointerForName(
154                myBundle, CFSTR("DVEnableWrite") );
155    sDVFuncs.fDVDisableWrite = CFBundleGetFunctionPointerForName(
156                myBundle, CFSTR("DVDisableWrite") );
157    sDVFuncs.fDVGetEmptyFrame = CFBundleGetFunctionPointerForName(
158                myBundle, CFSTR("DVGetEmptyFrame") );
159    sDVFuncs.fDVWriteFrame = CFBundleGetFunctionPointerForName(
160                myBundle, CFSTR("DVWriteFrame") );
161    sDVFuncs.fDVSetWriteSignalMode = CFBundleGetFunctionPointerForName(
162                myBundle, CFSTR("DVSetWriteSignalMode") );
163
164// Notifications
165    sDVFuncs.fDVNewNotification = CFBundleGetFunctionPointerForName(
166                myBundle, CFSTR("DVNewNotification") );
167    sDVFuncs.fDVNotifyMeWhen = CFBundleGetFunctionPointerForName(
168                myBundle, CFSTR("DVNotifyMeWhen") );
169    sDVFuncs.fDVCancelNotification = CFBundleGetFunctionPointerForName(
170                myBundle, CFSTR("DVCancelNotification") );
171    sDVFuncs.fDVDisposeNotification = CFBundleGetFunctionPointerForName(
172                myBundle, CFSTR("DVDisposeNotification") );
173
174    // Any CF objects returned from functions with "create" or
175    // "copy" in their names must be released by us!
176    CFRelease( bundleURL );
177    return myBundle;
178}
179
180static OSErr doControlTest(DVDeviceRefNum refNum, UInt8 op1, UInt8 op2)
181{
182        //Component control;
183    AVCTransactionParams avcParams;
184    char in[4], out[16];
185    OSErr err;
186
187    // fill up the avc frame
188    in[0]	= kAVCControlCommand;
189    in[1] 	= 0x20;	// for now
190    in[2] 	= op1;
191    in[3] 	= op2;
192
193    // fill up the transaction parameter block
194    avcParams.commandBufferPtr = in;
195    avcParams.commandLength = sizeof(in);
196    avcParams.responseBufferPtr = out;
197    avcParams.responseBufferSize = sizeof(out);
198    avcParams.responseHandler = NULL;
199
200    err = sDVFuncs.fDVDoAVCTransaction(refNum, &avcParams );
201    if(err)
202        printf("Error %d calling DVDoAVCTransaction(%ld)\n", err, refNum);
203
204    return err;
205}
206
207static void readFrames(DVDeviceRefNum refNum, int file, int numFrames)
208{
209    OSErr err, wait;
210    Ptr pReadBuffer;
211    UInt32 size;
212    int i;
213
214    err = sDVFuncs.fDVEnableRead(refNum);
215
216    for(i=0; i<numFrames; i++) {
217        wait = sDVFuncs.fDVReadFrame( refNum, &pReadBuffer, &size );
218        while(wait == -1) {
219            usleep(10000);	// 10 milliseconds
220            wait = sDVFuncs.fDVReadFrame( refNum, &pReadBuffer, &size );
221        }
222        if(file)
223            write(file, pReadBuffer, size);
224        err = sDVFuncs.fDVReleaseFrame( refNum, pReadBuffer );
225    }
226
227    err = sDVFuncs.fDVDisableRead( refNum );
228
229}
230
231static void destructoRead(DVDeviceRefNum refNum)
232{
233    OSErr err, wait;
234    Ptr pReadBuffer;
235    UInt32 size;
236    int i;
237
238    do {
239        err = sDVFuncs.fDVEnableRead(refNum);
240
241        for(i=0; i<100; i++) {
242            wait = sDVFuncs.fDVReadFrame( refNum, &pReadBuffer, &size );
243            while(wait == -1) {
244                //usleep(10000);	// 10 milliseconds
245                wait = sDVFuncs.fDVReadFrame( refNum, &pReadBuffer, &size );
246            }
247            err = sDVFuncs.fDVReleaseFrame( refNum, pReadBuffer );
248        }
249
250        err = sDVFuncs.fDVDisableRead( refNum );
251    } while(1);
252}
253
254static void writeFrames(DVDeviceRefNum refNum, int file)
255{
256    OSErr err, wait;
257    Ptr pReadBuffer;
258    UInt32 size, len;
259
260    err = sDVFuncs.fDVEnableWrite(refNum);
261    if(err)
262        return;
263
264    do {
265        wait = sDVFuncs.fDVGetEmptyFrame( refNum, &pReadBuffer, &size );
266        while(wait == -1) {
267            usleep(10000);	// 10 milliseconds
268            wait = sDVFuncs.fDVGetEmptyFrame( refNum, &pReadBuffer, &size );
269        }
270        if(wait)
271            break;
272        if(file) {
273            len = read(file, pReadBuffer, size);
274            if(len < size) {
275                if(sLoop) {
276                    int res;
277                    res = lseek(file, 0, SEEK_SET);
278                    printf("lseek res %d\n", res);
279                        //break;
280                }
281                else
282                    break;
283            }
284        }
285        err = sDVFuncs.fDVWriteFrame( refNum, pReadBuffer );
286    } while (1);
287
288    err = sDVFuncs.fDVDisableWrite( refNum );
289
290}
291
292static void destructoWrite(DVDeviceID device, DVDeviceRefNum refNum, int file)
293{
294    OSErr err, wait;
295    Ptr pReadBuffer;
296    UInt32 size, len;
297    int i;
298
299    do {
300        err = sDVFuncs.fDVEnableWrite(refNum);
301        if(err) {
302            sleep(1);
303            continue;
304        }
305        for(i=0; i<100; i++) {
306            wait = sDVFuncs.fDVGetEmptyFrame( refNum, &pReadBuffer, &size );
307            while(wait == -1) {
308                usleep(10000);	// 10 milliseconds
309                wait = sDVFuncs.fDVGetEmptyFrame( refNum, &pReadBuffer, &size );
310            }
311            if(file) {
312                len = read(file, pReadBuffer, size);
313                if(len < size) {
314                    int res;
315                    res = lseek(file, 0, SEEK_SET);
316                    printf("lseek res %d\n", res);
317                }
318            }
319            err = sDVFuncs.fDVWriteFrame( refNum, pReadBuffer );
320        };
321
322        err = sDVFuncs.fDVDisableWrite( refNum );
323#if 1
324        err = sDVFuncs.fDVCloseDriver(refNum );
325        err = sDVFuncs.fDVOpenDriver( device, &refNum );
326        if(err) {
327            if(err == (OSErr)kIOReturnExclusiveAccess) {
328                do {
329                    sleep(1);
330                	err = sDVFuncs.fDVOpenDriver( device, &refNum );
331                }
332                while (err == (OSErr)kIOReturnExclusiveAccess);
333            }
334            printf("Error %d calling DVOpenDriver(%ld)\n", err, device);
335        }
336#endif
337    } while (err == noErr);
338}
339
340static OSStatus myNotifyProc(DVEventRecordPtr event, void *userData )
341{
342    printf("event for device %d, event %d, userdata %p\n",
343        event->eventHeader.deviceID, event->eventHeader.theEvent, userData);
344    return noErr;
345}
346
347int main(int argc, char **argv)
348{
349    CFBundleRef myBundle;
350    UInt32 numDevs, i;
351    UInt32 standard;
352    Boolean isEnabled;
353    OSErr err;
354    DVDeviceID device;
355    DVNotificationID notifyID;
356    char name[256];
357    int file;
358    int pos = 1;
359    int destruct = 0;
360
361    while(argc > pos) {
362        if(strcmp(argv[pos], "-w") == 0)
363            sWrite = 1;
364        else if(strcmp(argv[pos], "-r") == 0)
365            sWrite = 0;
366        else if(strcmp(argv[pos], "-d") == 0)
367            destruct = 1;
368        else if(strcmp(argv[pos], "-l") == 0)
369            sLoop = 1;
370        else if(strcmp(argv[pos], "-sdl") == 0)
371            sSDL = 1;
372        else if(strcmp(argv[pos], "-DVCPro") == 0)
373            sDVCPro = 1;
374        else if(strcmp(argv[pos], "-n") == 0)
375            sNotifyTest = 1;
376        else if(strcmp(argv[pos], "-h") == 0) {
377            printf("%s [-w/-r] [-l(oop)] [-d(estructoTest)] [-sdl/-DVCPro] [-n(otify test] [filename]\n", argv[0]);
378            exit(0);
379        }
380        else
381            sFile = argv[pos];
382        pos++;
383    }
384    myBundle = findMe();
385    printf("sLoop = %d\n", sLoop);
386    numDevs = sDVFuncs.fDVCountDevices();
387    printf("Number of devices: %ld\n", numDevs);
388    if(numDevs == 0) {
389        err = sDVFuncs.fDVNewNotification( kEveryDVDeviceRefNum, myNotifyProc,
390						0x1234, &notifyID );
391        if(err)
392            printf("Error %d calling DVNewNotification(, %ld)\n", err, kEveryDVDeviceRefNum);
393        err = sDVFuncs.fDVNotifyMeWhen( kEveryDVDeviceRefNum, notifyID,
394            kDVDeviceAdded | kDVDeviceRemoved);
395        if(err)
396            printf("Error %d calling NotifyMeWhen(%ld, %ld)\n", err, kEveryDVDeviceRefNum, notifyID);
397
398        while (numDevs == 0 || sNotifyTest) {
399            printf("Waiting for devices: %ld\n", numDevs);
400            usleep(1000000);	// 1000 milliseconds
401            numDevs = sDVFuncs.fDVCountDevices();
402        }
403    }
404    for(i=1; i<=numDevs; i++) {
405        DVDeviceRefNum refNum;
406        err = sDVFuncs.fDVGetIndDevice(&device, i);
407        if(err)
408            printf("Error %d calling DVGetIndDevice(, %ld)\n", err, i);
409        err = sDVFuncs.fDVGetDeviceName(device, name);
410        if(err)
411            printf("Error %d calling DVGetDeviceName(%ld)\n", err, device);
412        else
413            printf("Device %ld name is %s\n", device, name);
414
415        err = sDVFuncs.fDVOpenDriver( device, &refNum );
416        if(err) {
417            if(err == (OSErr)kIOReturnExclusiveAccess) {
418                do {
419                    sleep(1);
420                	err = sDVFuncs.fDVOpenDriver( device, &refNum );
421                }
422                while (err == (OSErr)kIOReturnExclusiveAccess);
423            }
424            printf("Error %d calling DVOpenDriver(%ld)\n", err, device);
425        }
426        err = sDVFuncs.fDVGetDeviceStandard(refNum, &standard);
427        if(err)
428            printf("Error %d calling DVGetDeviceStandard(%ld)\n", err, device);
429        else if(standard == kNTSCStandard)
430            printf("Device %ld video standard is NTSC\n", device);
431        else if(standard == kPALStandard)
432            printf("Device %ld video standard is PAL\n", device);
433        else
434            printf("Device %ld, unknown video standard %ld\n", device, standard);
435
436        err = sDVFuncs.fDVIsEnabled(refNum, &isEnabled);
437        if(err)
438            printf("Error %d calling DVIsEnabled(%ld)\n", err, device);
439        else
440            printf("Device %ld isEnabled: %d\n", device, isEnabled);
441
442        if(sWrite) {
443            if(sSDL) {
444                err = sDVFuncs.fDVSetWriteSignalMode(refNum, kAVCSignalModeSDL525_60);
445            }
446            file = open(sFile, O_RDONLY, 0666);
447            printf("opened file, %d\n", file);
448            if(destruct)
449                destructoWrite(device, refNum, file);
450            else
451                writeFrames(refNum, file);
452        }
453        else {
454            file = open(sFile, O_CREAT | O_WRONLY | O_TRUNC, 0666);
455    // Start camera playing
456
457            err = doControlTest(refNum, kAVCPlayOpcode, kAVCPlayForward);
458
459            if(destruct)
460                destructoRead(refNum);
461            else
462                readFrames(refNum, file, 300);
463
464            //sleep(10);
465            err = doControlTest(refNum, kAVCWindOpcode, kAVCWindStop);
466
467            err = sDVFuncs.fDVCloseDriver( refNum );
468            if(err)
469                printf("Error %d calling DVCloseDriver(%ld)\n", err, device);
470            while(sLoop) {
471                readFrames(refNum, 0, 300);
472            }
473        }
474        err = sDVFuncs.fDVCloseDriver(refNum );
475
476    }
477
478    sleep(10);
479    // Unload the bundle's executable code.
480    // Don't, because there's no way to stop it!
481    //CFBundleUnloadExecutable( myBundle );
482    //CFRelease( myBundle );
483    return 0;
484}
485