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#define __PRINT__ // Print headers don't compile for some reason
24#include <Carbon/Carbon.h>
25#include <QuickTime/QuickTime.h>
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <pthread.h>
30
31#include <sys/types.h>
32#include <sys/stat.h>
33#include <fcntl.h>
34#include <iokit/IOKitLib.h>
35
36#include "IsochronousDataHandler.h"
37#include "DeviceControl.h"
38
39#define WRITEBUFF 1
40
41#if WRITEBUFF
42#define SYNC 0
43static Ptr myBuffer1, myBuffer2;
44#endif
45
46static int issued = 0;
47static int completed = 0;
48pthread_mutex_t		globalsMutex;			// lock this before updating globals
49pthread_cond_t		syncCond;				// To synchronize threads.
50int	finished=0;
51static int file = 0;
52static     QTAtomSpec videoConfig;
53static int frameSize = 120000;	// NTSC 144000 PAL
54static char *sFile;
55static int sSDL;
56static int sDVCPro;
57static int sDVCPro50;
58static int sFormat = 0;	// DV
59static int sLoop;
60static UInt64 sGUID = 0;
61
62static void printP(const char *s)
63{
64    int len = *s++;
65    while(len--)
66        printf("%c", *s++);
67}
68
69static void print4(const char *s, UInt32 val)
70{
71    printf("%s'%c%c%c%c'(0x%x)", s, val>>24, val>>16, val>>8, val, val);
72}
73
74// called when a new isoch write is done
75static OSStatus DVIsochComponentWriteCallback( IDHGenericEvent *eventRecord, void *userData)
76{
77    struct timeval tod;
78        OSErr 					result = noErr;
79        IDHParameterBlock		*pb = (IDHParameterBlock *) eventRecord;
80
81        ComponentInstance	theInst = userData;
82        if(file) {
83            int len;
84            len = read(file, pb->buffer, frameSize);
85            //len = read(file, myBuffer, frameSize);
86            if(len < frameSize) {
87                pthread_mutex_lock(&globalsMutex);
88                finished = 1;
89                pthread_mutex_unlock(&globalsMutex);
90                pthread_cond_broadcast(&syncCond);
91                printf("Completed %d\n", completed++);
92                return result;
93            }
94        }
95#if WRITEBUFF
96#else
97        pb->buffer = nil;
98#endif
99        // fill out structure
100        pb->requestedCount	= frameSize;
101        pb->actualCount 	= 0;
102        pb->completionProc 	= DVIsochComponentWriteCallback;
103        // do another write
104        //gettimeofday(&tod, NULL);
105        //printf("Completed %d, issuing write %d @ %d:%d\n", completed++, issued, tod.tv_sec, tod.tv_usec);
106        result = IDHWrite( theInst, pb);
107        if( result != noErr) {
108            printf("IDHWrite error %d\n", result);
109        }
110        issued++;
111        return result;
112}
113
114static OSErr doWriteTest(ComponentInstance theInst)
115{
116    struct timeval tod;
117    TimeRecord time;
118    IDHParameterBlock isochParamBlock1, isochParamBlock2;
119    ComponentResult err;
120    int len;
121
122    finished = 0;
123    err = pthread_mutex_init(&globalsMutex, NULL);
124    err = pthread_cond_init(&syncCond, NULL);
125
126    file = open(sFile, O_RDONLY, 0666);
127    printf("open file: %d\n", file);
128#if WRITEBUFF
129    if(!myBuffer1)
130        myBuffer1 = NewPtr(frameSize);
131    if(!myBuffer2)
132        myBuffer2 = NewPtr(frameSize);
133    read(file, myBuffer1, frameSize);
134    isochParamBlock1.buffer 		= myBuffer1;
135#else
136    isochParamBlock1.buffer 		= nil;
137#endif
138    isochParamBlock1.requestedCount	= frameSize;	// NTSC buffer size
139    isochParamBlock1.actualCount 	= 0;
140    isochParamBlock1.refCon		= (void *)theInst;
141
142#if SYNC
143    isochParamBlock1.completionProc 	= nil;
144#else
145    isochParamBlock1.completionProc 	= DVIsochComponentWriteCallback;
146#endif
147
148    isochParamBlock2 = isochParamBlock1;
149
150    // open the DV device for writing
151    err = IDHOpenDevice( theInst, kIDHOpenForWriteTransactions);
152    if( err != noErr)
153            goto error;
154
155    printf("Opened device\n");
156
157    //err = IDHGetDeviceTime(theInst, &time);
158    //if( err != noErr)
159    //        goto error;
160    //gettimeofday(&tod, NULL);
161    //printf("issuing write %d @ %d:%d =  0x%x:0x%x\n",
162    //issued, tod.tv_sec, tod.tv_usec, time.value.hi, time.value.lo);
163    err = IDHWrite( theInst, &isochParamBlock1);
164    if( err != noErr)
165            goto error;
166    err = IDHGetDeviceTime(theInst, &time);
167    if( err != noErr)
168            goto error;
169    gettimeofday(&tod, NULL);
170    printf("issued write %d @ %d:%d =  0x%x:0x%x\n",
171    issued, tod.tv_sec, tod.tv_usec, time.value.hi, time.value.lo);
172    issued++;
173#if SEND2
174#if WRITEBUFF
175    read(file, myBuffer2, frameSize);
176    isochParamBlock2.buffer 		= myBuffer2;
177#endif
178    err = IDHWrite( theInst, &isochParamBlock2);
179    if( err != noErr)
180            goto error;
181    err = IDHGetDeviceTime(theInst, &time);
182    if( err != noErr)
183            goto error;
184    gettimeofday(&tod, NULL);
185    printf("issued write %d @ %d:%d =  0x%x:0x%x\n",
186    issued, tod.tv_sec, tod.tv_usec, time.value.hi, time.value.lo);
187    issued++;
188#endif
189
190#if SYNC
191    do {
192        len = read(file, myBuffer1, frameSize);
193        if(len != frameSize) {
194            finished = 1;
195            break;
196        }
197        err = IDHWrite( theInst, &isochParamBlock1);
198        if( err != noErr)
199                goto error;
200        err = IDHGetDeviceTime(theInst, &time);
201        if( err != noErr)
202                goto error;
203        gettimeofday(&tod, NULL);
204        printf("issued write %d @ %d:%d =  0x%x:0x%x\n",
205        issued, tod.tv_sec, tod.tv_usec, time.value.hi, time.value.lo);
206        issued++;
207    } while (true);
208#endif
209
210    // Wait for work thread to finish initializing globals
211    err = pthread_mutex_lock(&globalsMutex);
212    while(!finished) {
213        err = pthread_cond_wait(&syncCond, &globalsMutex);
214    }
215    err = pthread_mutex_unlock(&globalsMutex);
216
217    // close the DV device
218    err = IDHCloseDevice( theInst);
219    if( err != noErr)
220            goto error;
221    printf("Did %d frames\n", issued);
222
223    printf("Closed device\n");
224
225    close(sFile);
226
227error:
228    if(err)
229        printf("Error %d(0x%x) in doWriteTest\n", err, err);
230    return err;
231}
232
233
234static void OpenDV()
235{
236    ComponentInstance theInst;
237    ComponentResult version;
238    QTAtomContainer deviceList = NULL;
239    short nDVDevices, i, j;
240    QTAtom deviceAtom;
241    UInt32 cmpFlag;
242    UInt32 isoversion;
243    long size;
244    OSErr err;
245
246    theInst = OpenDefaultComponent('ihlr', 'dv  ');
247    printf("Instance is 0x%x\n", theInst);
248        if(theInst == NULL)
249                return;
250
251    version = CallComponentVersion(theInst);
252    printf("Version is 0x%x\n", version);
253
254    do {
255        err = IDHGetDeviceList( theInst, &deviceList);
256        if( err != noErr)
257                goto error;
258
259        nDVDevices = QTCountChildrenOfType( deviceList, kParentAtomIsContainer, kIDHDeviceAtomType);
260        if(nDVDevices > 0)
261            break;
262        printf("Waiting for a camera...\n");
263        sleep(1);
264    } while(true);
265
266
267    QTLockContainer( deviceList);
268    // find the cmp atom
269    deviceAtom = QTFindChildByIndex( deviceList, kParentAtomIsContainer, kIDHUseCMPAtomType, 1, nil);
270    if( deviceAtom == nil)
271            goto error;
272
273    // get the value of the cmp atom
274    QTCopyAtomDataToPtr( deviceList, deviceAtom, true, sizeof( cmpFlag), &cmpFlag, &size);
275
276    // find the version atom
277    deviceAtom = QTFindChildByIndex( deviceList, kParentAtomIsContainer, kIDHIsochVersionAtomType, 1, nil);
278    if( deviceAtom == nil)
279            goto error;
280
281    // get the value of the version atom
282    QTCopyAtomDataToPtr( deviceList, deviceAtom, true, sizeof( isoversion), &isoversion, &size);
283
284    printf("Version 0x%x. %d DV devices, use CMP flag is %d\n", isoversion, nDVDevices, cmpFlag);
285
286    for( i=0; i<nDVDevices; ++i)
287    {
288            QTAtom isochAtom, dataAtom;
289            UInt64 test;
290            int nConfigs;
291            char cameraName[256];
292            IDHDeviceID deviceID;
293            IDHDeviceStatus deviceStatus;
294
295            // get the atom to this device
296            deviceAtom = QTFindChildByIndex( deviceList, kParentAtomIsContainer, kIDHDeviceAtomType, i + 1, nil);
297            if( deviceAtom == nil)
298                    goto error;
299
300            printf("device %d ", i);
301
302            dataAtom = QTFindChildByIndex( deviceList, deviceAtom, kIDHUniqueIDType, 1, nil);
303            if( dataAtom == nil)
304                    goto error;
305            QTCopyAtomDataToPtr( deviceList, dataAtom, true, sizeof( test), &test, &size);
306            printf("guid 0x%016llx ", test);
307            dataAtom = QTFindChildByIndex( deviceList, deviceAtom, kIDHNameAtomType, 1, nil);
308            if( dataAtom == nil)
309                    goto error;
310            QTCopyAtomDataToPtr( deviceList, dataAtom, true, 255, cameraName, &size);
311            cameraName[size] = 0;
312            printf("%s ", cameraName+1);
313
314            dataAtom = QTFindChildByIndex( deviceList, deviceAtom, kIDHDeviceIDType, 1, nil);
315            if( dataAtom == nil)
316                    goto error;
317            QTCopyAtomDataToPtr( deviceList, dataAtom, true, sizeof( deviceID), &deviceID, &size);
318            printf("deviceID 0x%x ", deviceID);
319
320            dataAtom = QTFindChildByIndex( deviceList, deviceAtom, 'ddin', 1, nil);
321            if( dataAtom == nil)
322                    goto error;
323            QTCopyAtomDataToPtr( deviceList, dataAtom, true, sizeof( deviceStatus), &deviceStatus, &size);
324            printf("\ndevice status:\n");
325            printf("version %d\n", deviceStatus.version);
326            printf("physicallyConnected %d\n", deviceStatus.physicallyConnected);
327            printf("readEnabled %d ", deviceStatus.readEnabled);
328            printf("writeEnabled %d ", deviceStatus.writeEnabled);
329            printf("exclusiveAccess %d\n", deviceStatus.exclusiveAccess);
330            printf("currentBandwidth %d ", deviceStatus.currentBandwidth);
331            printf("currentChannel %d ", deviceStatus.currentChannel);
332            printf("inputStandard %d ", deviceStatus.inputStandard);
333            printf("deviceActive %d\n", deviceStatus.deviceActive);
334            printf("supported DV types %x\n", deviceStatus.outputFormats);
335
336            // find the isoch characteristics for this device
337            isochAtom = QTFindChildByIndex( deviceList, deviceAtom, kIDHIsochServiceAtomType, 1, nil);
338            if( isochAtom == nil)
339                    goto error;
340
341            // how many configs exist for this device
342            nConfigs = QTCountChildrenOfType( deviceList, isochAtom, kIDHIsochModeAtomType);
343            printf("\n%d configs:\n", nConfigs);
344
345            videoConfig.atom = nil;	// start with no selected config
346
347            // process each config
348            for( j=0; j<nConfigs; ++j)
349            {
350                    OSType mediaType;
351                    QTAtom configAtom, mediaAtom, nameAtom;
352
353                    // get this configs atom
354                    configAtom = QTFindChildByIndex( deviceList, isochAtom, kIDHIsochModeAtomType, j + 1, nil);
355                    if( configAtom == nil)
356                            goto error;
357
358                    printf("Config %d",j);
359                    // find the media type atom
360                    mediaAtom = QTFindChildByIndex( deviceList, configAtom, kIDHIsochMediaType, 1, nil);
361                    if( mediaAtom == nil)
362                            goto error;
363
364                    // get the value of the mediaType atom
365                    QTCopyAtomDataToPtr( deviceList, mediaAtom, true, sizeof( mediaType), &mediaType, &size);
366                    print4(" Media type:", mediaType);
367                    nameAtom = QTFindChildByIndex( deviceList, configAtom, kIDHNameAtomType, 1, nil);
368                    if( nameAtom != nil) {
369                        QTCopyAtomDataToPtr( deviceList, nameAtom, true, 255, cameraName, &size);
370                        cameraName[size] = 0;
371                        printf(" name '%s' ", cameraName+1);
372                    }
373                    // is this config an video config?
374                    if( mediaType == kIDHVideoMediaAtomType)	// found video device
375                    {
376                        QTAtom frameSizeAtom;
377                        frameSizeAtom = QTFindChildByIndex( deviceList, configAtom,
378                            kIDHDataBufferSizeAtomType, 1, nil);
379                        // ignore DV_SDL config
380                        if(strcmp(cameraName+1, "DV-SDL")) {
381                            if(frameSizeAtom) {
382                                QTCopyAtomDataToPtr( deviceList, frameSizeAtom, true, sizeof( frameSize), &frameSize, &size);
383                                if(sSDL)
384                                    frameSize /= 2;
385								if (sDVCPro50)
386									frameSize *= 2;
387                                printf("Config buffer size %d\n", frameSize);
388                            }
389                            videoConfig.container = deviceList;	// save this config
390                            videoConfig.atom = configAtom;
391                        }
392                    }
393                    printf("\n");
394            }
395            printf("-----\n");
396            if(sGUID == test)
397                break;
398    }
399
400    if( videoConfig.atom == nil)	// no good configs found
401            goto error;
402
403    printf("setting config\n");
404    // set isoch to use this config
405    err = IDHSetDeviceConfiguration( theInst, &videoConfig);
406    if( err != noErr)
407            goto error;
408
409    IDHSetFormat( theInst, sFormat);
410
411    err = doWriteTest(theInst);
412
413    if( err != noErr)
414            goto error;
415
416error:
417    if( err != noErr)
418        printf("error %d(0x%x)\n", err, err);
419    if(deviceList) {
420        QTUnlockContainer( deviceList);
421        QTDisposeAtomContainer(deviceList);
422    }
423
424    CloseComponent(theInst);
425
426}
427
428
429int main(int argc, char **argv)
430{
431	UInt32 seed = GetComponentListModSeed();
432	UInt32 num;
433	Handle aName;
434	ComponentDescription desc, aDesc;
435	Component aComponent;
436    ComponentInstance theInst;
437    ComponentResult version;
438
439    int pos = 1;
440
441    sFile = "/tmp/dump.dv";
442    sSDL = 0;
443	sDVCPro = 0;
444	sDVCPro50 = 0;
445
446    while(argc > pos) {
447        if(strcmp(argv[pos], "-sdl") == 0)
448            sSDL = 1;
449        else if(strcmp(argv[pos], "-DVCPro") == 0)
450            sDVCPro = 1;
451        else if(strcmp(argv[pos], "-DVCPro50") == 0)
452            sDVCPro50 = 1;
453        else if(strcmp(argv[pos], "-l") == 0)
454            sLoop = 1;
455        else if(strcmp(argv[pos], "-guid") == 0 && argc > pos + 1) {
456            pos++;
457            sGUID = strtoq(argv[pos], NULL, 0);
458        }
459        else
460            sFile = argv[pos];
461        pos++;
462    }
463
464    if(sSDL) {
465        frameSize /= 2;
466        sFormat = kIDHDV_SDL;
467    }
468    else if(sDVCPro) {
469        sFormat = kIDHDVCPro_25;
470    }
471    else if(sDVCPro50) {
472		frameSize *= 2;
473        sFormat = kIDHDVCPro_50;
474    }
475
476
477    printf("Reading from %s\n", sFile);
478	printf("Component seed is %d\n", seed);
479    desc.componentType = 'ihlr';				/* A unique 4-byte code indentifying the command set */
480	desc.componentSubType = 0;			/* Particular flavor of this instance */
481	desc.componentManufacturer = 0;		/* Vendor indentification */
482	desc.componentFlags = 0;				/* 8 each for Component,Type,SubType,Manuf/revision */
483	desc.componentFlagsMask = 0;			/* Mask for specifying which flags to consider in search, zero during registration */
484
485	num = CountComponents(&desc);
486	printf("%d components match\n", num);
487
488	aComponent = 0;
489	aName = NewHandleClear(200);
490	while (aComponent = FindNextComponent(aComponent, &desc)) {
491		OSErr oops;
492		printf("Found component 0x%x:", aComponent);
493		oops = GetComponentInfo(aComponent, &aDesc, aName,
494                                         NULL, NULL);
495        if(oops)
496        	printf("GetComponentInfo() returned error %d\n", oops);
497        else {
498        	if(GetHandleSize(aName))
499        		printP(*aName);
500        	else
501        		printf("Unnamed");
502                print4(", Type ", aDesc.componentType);
503
504                print4(", SubType ", aDesc.componentSubType);
505                print4(", Manufacturer ", aDesc.componentManufacturer);
506                printf("\n");
507	}
508        }
509
510    do {
511        OpenDV();
512    } while (sLoop);
513	return 0;
514}
515
516