Quicktime for Cocoa : import part
[blender-staging.git] / source / blender / quicktime / apple / qtkit_export.m
1 /**
2  * $Id: qtkit_export.m 24424 2009-11-09 17:06:48Z damien78 $
3  *
4  * qtkit_export.m
5  *
6  * Code to create QuickTime Movies with Blender
7  *
8  * ***** BEGIN GPL LICENSE BLOCK *****
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  *
24  * The Original Code is written by Rob Haarsma (phase)
25  *
26  * Contributor(s): Stefan Gartner (sgefant)
27  *                                 Damien Plisson 11/2009
28  *
29  * ***** END GPL LICENSE BLOCK *****
30  */
31
32 #ifdef WITH_QUICKTIME
33 #if defined(_WIN32) || defined(__APPLE__)
34
35 #include "DNA_scene_types.h"
36
37 #include "BKE_global.h"
38 #include "BKE_scene.h"
39
40 #include "BLI_blenlib.h"
41
42 #include "BLO_sys_types.h"
43
44 #include "IMB_imbuf.h"
45 #include "IMB_imbuf_types.h"
46
47 #include "MEM_guardedalloc.h"
48
49 #include "quicktime_import.h"
50 #include "quicktime_export.h"
51
52
53 #ifdef __APPLE__
54 /* evil */
55 #ifndef __AIFF__
56 #define __AIFF__
57 #endif
58 #endif /* __APPLE__ */
59
60 #define kMyCreatorType  FOUR_CHAR_CODE('TVOD')
61 #define kTrackStart             0
62 #define kMediaStart             0
63
64 //static void QT_StartAddVideoSamplesToMedia (const Rect *trackFrame, int rectx, int recty);
65 static void QT_DoAddVideoSamplesToMedia (int frame, int *pixels, int rectx, int recty);
66 static void QT_EndAddVideoSamplesToMedia (void);
67 static void QT_CreateMyVideoTrack (int rectx, int recty);
68 static void QT_EndCreateMyVideoTrack (void);
69 static void check_renderbutton_framerate(struct RenderData *rd);
70
71 typedef struct QuicktimeExport {
72
73         /*FSSpec                theSpec;
74         short           resRefNum;
75         Str255          qtfilename;
76
77         Media           theMedia;
78         Movie           theMovie;
79         Track           theTrack;
80
81         GWorldPtr                       theGWorld;
82         PixMapHandle            thePixMap;
83         ImageDescription        **anImageDescription;*/
84
85         ImBuf           *ibuf;  //imagedata for Quicktime's Gworld
86         ImBuf           *ibuf2; //copy of renderdata, to be Y-flipped
87
88 } QuicktimeExport;
89
90 typedef struct QuicktimeComponentData {
91
92         /*ComponentInstance     theComponent;
93         SCTemporalSettings  gTemporalSettings;
94         SCSpatialSettings   gSpatialSettings;
95         SCDataRateSettings  aDataRateSetting;
96         TimeValue                       duration;
97         long                            kVideoTimeScale;*/
98
99 } QuicktimeComponentData;
100
101 static struct QuicktimeExport *qtexport;
102 static struct QuicktimeComponentData *qtdata;
103
104 static int      sframe;
105
106 #if 0
107
108 static OSErr QT_SaveCodecSettingsToScene(RenderData *rd)
109 {       
110         QTAtomContainer         myContainer = NULL;
111         ComponentResult         myErr = noErr;
112         Ptr                                     myPtr;
113         long                            mySize = 0;
114
115         CodecInfo                       ci;
116         char str[255];
117
118         QuicktimeCodecData *qcd = rd->qtcodecdata;
119
120         // check if current scene already has qtcodec settings, and clear them
121         if (qcd) {
122                 free_qtcodecdata(qcd);
123         } else {
124                 qcd = rd->qtcodecdata = MEM_callocN(sizeof(QuicktimeCodecData), "QuicktimeCodecData");
125         }
126
127         // obtain all current codec settings
128         SCSetInfo(qtdata->theComponent, scTemporalSettingsType, &qtdata->gTemporalSettings);
129         SCSetInfo(qtdata->theComponent, scSpatialSettingsType,  &qtdata->gSpatialSettings);
130         SCSetInfo(qtdata->theComponent, scDataRateSettingsType, &qtdata->aDataRateSetting);
131
132         // retreive codecdata from quicktime in a atomcontainer
133         myErr = SCGetSettingsAsAtomContainer(qtdata->theComponent,  &myContainer);
134         if (myErr != noErr) {
135                 printf("Quicktime: SCGetSettingsAsAtomContainer failed\n"); 
136                 goto bail;
137         }
138
139         // get the size of the atomcontainer
140         mySize = GetHandleSize((Handle)myContainer);
141
142         // lock and convert the atomcontainer to a *valid* pointer
143         QTLockContainer(myContainer);
144         myPtr = *(Handle)myContainer;
145
146         // copy the Quicktime data into the blender qtcodecdata struct
147         if (myPtr) {
148                 qcd->cdParms = MEM_mallocN(mySize, "qt.cdParms");
149                 memcpy(qcd->cdParms, myPtr, mySize);
150                 qcd->cdSize = mySize;
151
152                 GetCodecInfo (&ci, qtdata->gSpatialSettings.codecType, 0);
153                 CopyPascalStringToC(ci.typeName, str);
154                 sprintf(qcd->qtcodecname, "Codec: %s", str);
155         } else {
156                 printf("Quicktime: QT_SaveCodecSettingsToScene failed\n"); 
157         }
158
159         QTUnlockContainer(myContainer);
160
161 bail:
162         if (myContainer != NULL)
163                 QTDisposeAtomContainer(myContainer);
164                 
165         return((OSErr)myErr);
166 }
167
168
169 static OSErr QT_GetCodecSettingsFromScene(RenderData *rd)
170 {       
171         Handle                          myHandle = NULL;
172         ComponentResult         myErr = noErr;
173 //      CodecInfo ci;
174 //      char str[255];
175
176         QuicktimeCodecData *qcd = rd->qtcodecdata;
177
178         // if there is codecdata in the blendfile, convert it to a Quicktime handle 
179         if (qcd) {
180                 myHandle = NewHandle(qcd->cdSize);
181                 PtrToHand( qcd->cdParms, &myHandle, qcd->cdSize);
182         }
183                 
184         // restore codecsettings to the quicktime component
185         if(qcd->cdParms && qcd->cdSize) {
186                 myErr = SCSetSettingsFromAtomContainer((GraphicsExportComponent)qtdata->theComponent, (QTAtomContainer)myHandle);
187                 if (myErr != noErr) {
188                         printf("Quicktime: SCSetSettingsFromAtomContainer failed\n"); 
189                         goto bail;
190                 }
191
192                 // update runtime codecsettings for use with the codec dialog
193                 SCGetInfo(qtdata->theComponent, scDataRateSettingsType, &qtdata->aDataRateSetting);
194                 SCGetInfo(qtdata->theComponent, scSpatialSettingsType,  &qtdata->gSpatialSettings);
195                 SCGetInfo(qtdata->theComponent, scTemporalSettingsType, &qtdata->gTemporalSettings);
196
197 //              GetCodecInfo (&ci, qtdata->gSpatialSettings.codecType, 0);
198 //              CopyPascalStringToC(ci.typeName, str);
199 //              printf("restored Codec: %s\n", str);
200         } else {
201                 printf("Quicktime: QT_GetCodecSettingsFromScene failed\n"); 
202         }
203 bail:
204         if (myHandle != NULL)
205                 DisposeHandle(myHandle);
206                 
207         return((OSErr)myErr);
208 }
209
210
211 static OSErr QT_AddUserDataTextToMovie (Movie theMovie, char *theText, OSType theType)
212 {
213         UserData                                        myUserData = NULL;
214         Handle                                          myHandle = NULL;
215         long                                            myLength = strlen(theText);
216         OSErr                                           myErr = noErr;
217
218         // get the movie's user data list
219         myUserData = GetMovieUserData(theMovie);
220         if (myUserData == NULL)
221                 return(paramErr);
222         
223         // copy the specified text into a new handle
224         myHandle = NewHandleClear(myLength);
225         if (myHandle == NULL)
226                 return(MemError());
227
228         BlockMoveData(theText, *myHandle, myLength);
229
230         // add the data to the movie's user data
231         myErr = AddUserDataText(myUserData, myHandle, theType, 1, (short)GetScriptManagerVariable(smRegionCode));
232
233         // clean up
234         DisposeHandle(myHandle);
235         return(myErr);
236 }
237
238
239 static void QT_CreateMyVideoTrack(int rectx, int recty)
240 {
241         OSErr err = noErr;
242         Rect trackFrame;
243 //      MatrixRecord myMatrix;
244
245         trackFrame.top = 0;
246         trackFrame.left = 0;
247         trackFrame.bottom = recty;
248         trackFrame.right = rectx;
249         
250         qtexport->theTrack = NewMovieTrack (qtexport->theMovie, 
251                                                         FixRatio(trackFrame.right,1),
252                                                         FixRatio(trackFrame.bottom,1), 
253                                                         0);
254         CheckError( GetMoviesError(), "NewMovieTrack error" );
255
256 //      SetIdentityMatrix(&myMatrix);
257 //      ScaleMatrix(&myMatrix, fixed1, Long2Fix(-1), 0, 0);
258 //      TranslateMatrix(&myMatrix, 0, Long2Fix(trackFrame.bottom));
259 //      SetMovieMatrix(qtexport->theMovie, &myMatrix);
260
261         qtexport->theMedia = NewTrackMedia (qtexport->theTrack,
262                                                         VideoMediaType,
263                                                         qtdata->kVideoTimeScale,
264                                                         nil,
265                                                         0);
266         CheckError( GetMoviesError(), "NewTrackMedia error" );
267
268         err = BeginMediaEdits (qtexport->theMedia);
269         CheckError( err, "BeginMediaEdits error" );
270
271         QT_StartAddVideoSamplesToMedia (&trackFrame, rectx, recty);
272
273
274
275 static void QT_EndCreateMyVideoTrack(void)
276 {
277         OSErr err = noErr;
278
279         QT_EndAddVideoSamplesToMedia ();
280
281         err = EndMediaEdits (qtexport->theMedia);
282         CheckError( err, "EndMediaEdits error" );
283
284         err = InsertMediaIntoTrack (qtexport->theTrack,
285                                                                 kTrackStart,/* track start time */
286                                                                 kMediaStart,/* media start time */
287                                                                 GetMediaDuration (qtexport->theMedia),
288                                                                 fixed1);
289         CheckError( err, "InsertMediaIntoTrack error" );
290
291
292
293 static void QT_StartAddVideoSamplesToMedia (const Rect *trackFrame, int rectx, int recty)
294 {
295         SCTemporalSettings gTemporalSettings;
296         OSErr err = noErr;
297
298         qtexport->ibuf = IMB_allocImBuf (rectx, recty, 32, IB_rect, 0);
299         qtexport->ibuf2 = IMB_allocImBuf (rectx, recty, 32, IB_rect, 0);
300
301         err = NewGWorldFromPtr( &qtexport->theGWorld,
302                                                         k32ARGBPixelFormat,
303                                                         trackFrame,
304                                                         NULL, NULL, 0,
305                                                         (Ptr)qtexport->ibuf->rect,
306                                                         rectx * 4 );
307         CheckError (err, "NewGWorldFromPtr error");
308
309         qtexport->thePixMap = GetGWorldPixMap(qtexport->theGWorld);
310         LockPixels(qtexport->thePixMap);
311
312         SCDefaultPixMapSettings (qtdata->theComponent, qtexport->thePixMap, true);
313
314         // workaround for crash with H.264, which requires an upgrade to
315         // the new callback based api for proper encoding, but that's not
316         // really compatible with rendering out frames sequentially
317         gTemporalSettings = qtdata->gTemporalSettings;
318         if(qtdata->gSpatialSettings.codecType == kH264CodecType) {
319                 if(gTemporalSettings.temporalQuality != codecMinQuality) {
320                         fprintf(stderr, "Only minimum quality compression supported for QuickTime H.264.\n");
321                         gTemporalSettings.temporalQuality = codecMinQuality;
322                 }
323         }
324
325         SCSetInfo(qtdata->theComponent, scTemporalSettingsType, &gTemporalSettings);
326         SCSetInfo(qtdata->theComponent, scSpatialSettingsType,  &qtdata->gSpatialSettings);
327         SCSetInfo(qtdata->theComponent, scDataRateSettingsType, &qtdata->aDataRateSetting);
328
329         err = SCCompressSequenceBegin(qtdata->theComponent, qtexport->thePixMap, NULL, &qtexport->anImageDescription); 
330         CheckError (err, "SCCompressSequenceBegin error" );
331 }
332
333
334 static void QT_DoAddVideoSamplesToMedia (int frame, int *pixels, int rectx, int recty)
335 {
336         OSErr   err = noErr;
337         Rect    imageRect;
338
339         int             index;
340         int             boxsize;
341         unsigned char *from, *to;
342
343         short   syncFlag;
344         long    dataSize;
345         Handle  compressedData;
346         Ptr             myPtr;
347
348
349         //copy and flip renderdata
350         memcpy(qtexport->ibuf2->rect, pixels, 4*rectx*recty);
351         IMB_flipy(qtexport->ibuf2);
352
353         //get pointers to parse bitmapdata
354         myPtr = GetPixBaseAddr(qtexport->thePixMap);
355         imageRect = (**qtexport->thePixMap).bounds;
356
357         from = (unsigned char *) qtexport->ibuf2->rect;
358         to = (unsigned char *) myPtr;
359
360         //parse RGBA bitmap into Quicktime's ARGB GWorld
361         boxsize = rectx * recty;
362         for( index = 0; index < boxsize; index++) {
363                 to[0] = from[3];
364                 to[1] = from[0];
365                 to[2] = from[1];
366                 to[3] = from[2];
367                 to +=4, from += 4;
368         }
369
370         err = SCCompressSequenceFrame(qtdata->theComponent,
371                 qtexport->thePixMap,
372                 &imageRect,
373                 &compressedData,
374                 &dataSize,
375                 &syncFlag);
376         CheckError(err, "SCCompressSequenceFrame error");
377
378         err = AddMediaSample(qtexport->theMedia,
379                 compressedData,
380                 0,
381                 dataSize,
382                 qtdata->duration,
383                 (SampleDescriptionHandle)qtexport->anImageDescription,
384                 1,
385                 syncFlag,
386                 NULL);
387         CheckError(err, "AddMediaSample error");
388
389         printf ("added frame %3d (frame %3d in movie): ", frame, frame-sframe);
390 }
391
392
393 static void QT_EndAddVideoSamplesToMedia (void)
394 {
395         SCCompressSequenceEnd(qtdata->theComponent);
396
397         UnlockPixels(qtexport->thePixMap);
398         if (qtexport->theGWorld)
399                 DisposeGWorld (qtexport->theGWorld);
400
401         if (qtexport->ibuf)
402                 IMB_freeImBuf(qtexport->ibuf);
403
404         if (qtexport->ibuf2)
405                 IMB_freeImBuf(qtexport->ibuf2);
406
407 #endif //0
408
409 void makeqtstring (RenderData *rd, char *string) {
410         char txt[64];
411
412         if (string==0) return;
413
414         strcpy(string, rd->pic);
415         BLI_convertstringcode(string, G.sce);
416
417         BLI_make_existing_file(string);
418
419         if (BLI_strcasecmp(string + strlen(string) - 4, ".mov")) {
420                 sprintf(txt, "%04d_%04d.mov", (rd->sfra) , (rd->efra) );
421                 strcat(string, txt);
422         }
423 }
424
425
426 void start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty) {
427 #if 0
428         OSErr err = noErr;
429
430         char name[2048];
431         char theFullPath[255];
432
433
434         if(qtexport == NULL) qtexport = MEM_callocN(sizeof(QuicktimeExport), "QuicktimeExport");
435
436         if(qtdata) {
437                 if(qtdata->theComponent) CloseComponent(qtdata->theComponent);
438                 free_qtcomponentdata();
439         }
440
441         qtdata = MEM_callocN(sizeof(QuicktimeComponentData), "QuicktimeCodecDataExt");
442
443         if(rd->qtcodecdata == NULL || rd->qtcodecdata->cdParms == NULL) {
444                 get_qtcodec_settings(rd);
445         } else {
446                 qtdata->theComponent = OpenDefaultComponent(StandardCompressionType, StandardCompressionSubType);
447
448                 QT_GetCodecSettingsFromScene(rd);
449                 check_renderbutton_framerate(rd);
450         }
451         
452         if (G.afbreek != 1) {
453                 sframe = (rd->sfra);
454
455                 makeqtstring(rd, name);
456
457                 sprintf(theFullPath, "%s", name);
458
459                 /* hack: create an empty file to make FSPathMakeRef() happy */
460                 myFile = open(theFullPath, O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRUSR|S_IWUSR);
461                 if (myFile < 0) {
462                         printf("error while creating file!\n");
463                         /* do something? */
464                 }
465                 close(myFile);
466                 err = FSPathMakeRef((const UInt8 *)theFullPath, &myRef, 0);
467                 CheckError(err, "FsPathMakeRef error");
468                 err = FSGetCatalogInfo(&myRef, kFSCatInfoNone, NULL, NULL, &qtexport->theSpec, NULL);
469                 CheckError(err, "FsGetCatalogInfoRef error");
470
471                 err = CreateMovieFile (&qtexport->theSpec, 
472                                                         kMyCreatorType,
473                                                         smCurrentScript, 
474                                                         createMovieFileDeleteCurFile | createMovieFileDontCreateResFile,
475                                                         &qtexport->resRefNum, 
476                                                         &qtexport->theMovie );
477                 CheckError(err, "CreateMovieFile error");
478
479                 if(err != noErr) {
480                         G.afbreek = 1;
481 // XXX                  error("Unable to create Quicktime movie: %s", name);
482                 } else {
483                         printf("Created QuickTime movie: %s\n", name);
484
485                         QT_CreateMyVideoTrack(rectx, recty);
486                 }
487         }
488 #endif
489 }
490
491
492 void append_qt(struct RenderData *rd, int frame, int *pixels, int rectx, int recty) {
493         //QT_DoAddVideoSamplesToMedia(frame, pixels, rectx, recty);
494 }
495
496
497 void end_qt(void) {
498 /*      OSErr err = noErr;
499         short resId = movieInDataForkResID;
500
501         if(qtexport->theMovie) {
502                 QT_EndCreateMyVideoTrack();
503
504                 err = AddMovieResource (qtexport->theMovie, qtexport->resRefNum, &resId, qtexport->qtfilename);
505                 CheckError(err, "AddMovieResource error");
506
507                 err = QT_AddUserDataTextToMovie(qtexport->theMovie, "Made with Blender", kUserDataTextInformation);
508                 CheckError(err, "AddUserDataTextToMovie error");
509
510                 err = UpdateMovieResource(qtexport->theMovie, qtexport->resRefNum, resId, qtexport->qtfilename);
511                 CheckError(err, "UpdateMovieResource error");
512
513                 if(qtexport->resRefNum) CloseMovieFile(qtexport->resRefNum);
514
515                 DisposeMovie(qtexport->theMovie);
516
517                 printf("Finished QuickTime movie.\n");
518         }
519
520         if(qtexport) {
521                 MEM_freeN(qtexport);
522                 qtexport = NULL;
523         }*/
524 }
525
526
527 void free_qtcomponentdata(void) {
528         /*if(qtdata) {
529                 if(qtdata->theComponent) CloseComponent(qtdata->theComponent);
530                 MEM_freeN(qtdata);
531                 qtdata = NULL;
532         }*/
533 }
534
535
536 static void check_renderbutton_framerate(RenderData *rd) 
537 {
538         // to keep float framerates consistent between the codec dialog and frs/sec button.
539 /*      OSErr   err;    
540
541         //err = SCGetInfo(qtdata->theComponent, scTemporalSettingsType, &qtdata->gTemporalSettings);
542         CheckError(err, "SCGetInfo fr error");
543
544         if( (rd->frs_sec == 24 || rd->frs_sec == 30 || rd->frs_sec == 60) &&
545                 (qtdata->gTemporalSettings.frameRate == 1571553 ||
546                  qtdata->gTemporalSettings.frameRate == 1964113 ||
547                  qtdata->gTemporalSettings.frameRate == 3928227)) {;} 
548         else {
549                 if (rd->frs_sec_base > 0)
550                         qtdata->gTemporalSettings.frameRate = 
551                         (rd->frs_sec << 16) / rd->frs_sec_base ;
552         }
553         
554         //err = SCSetInfo(qtdata->theComponent, scTemporalSettingsType, &qtdata->gTemporalSettings);
555         CheckError( err, "SCSetInfo error" );
556
557         if(qtdata->gTemporalSettings.frameRate == 1571553) {                    // 23.98 fps
558                 qtdata->kVideoTimeScale = 24000;
559                 qtdata->duration = 1001;
560         } else if (qtdata->gTemporalSettings.frameRate == 1964113) {    // 29.97 fps
561                 qtdata->kVideoTimeScale = 30000;
562                 qtdata->duration = 1001;
563         } else if (qtdata->gTemporalSettings.frameRate == 3928227) {    // 59.94 fps
564                 qtdata->kVideoTimeScale = 60000;
565                 qtdata->duration = 1001;
566         } else {
567                 qtdata->kVideoTimeScale = (qtdata->gTemporalSettings.frameRate >> 16) * 100;
568                 qtdata->duration = 100;
569         }*/
570 }
571
572
573 int get_qtcodec_settings(RenderData *rd) 
574 {
575 /*      OSErr   err = noErr;
576
577         // erase any existing codecsetting
578         if(qtdata) {
579                 if(qtdata->theComponent) CloseComponent(qtdata->theComponent);
580                 free_qtcomponentdata();
581         }
582
583         // allocate new
584         qtdata = MEM_callocN(sizeof(QuicktimeComponentData), "QuicktimeComponentData");
585         qtdata->theComponent = OpenDefaultComponent(StandardCompressionType, StandardCompressionSubType);
586
587         // get previous selected codecsetting, if any 
588         if(rd->qtcodecdata && rd->qtcodecdata->cdParms) {
589                 QT_GetCodecSettingsFromScene(rd);
590                 check_renderbutton_framerate(rd);
591         } else {
592                 // configure the standard image compression dialog box
593                 // set some default settings
594                 qtdata->gSpatialSettings.codec = anyCodec;         
595                 qtdata->gSpatialSettings.spatialQuality = codecMaxQuality;
596                 qtdata->gTemporalSettings.temporalQuality = codecMaxQuality;
597                 qtdata->gTemporalSettings.keyFrameRate = 25;   
598                 qtdata->aDataRateSetting.dataRate = 90 * 1024;          
599
600                 err = SCSetInfo(qtdata->theComponent, scTemporalSettingsType,   &qtdata->gTemporalSettings);
601                 CheckError(err, "SCSetInfo1 error");
602                 err = SCSetInfo(qtdata->theComponent, scSpatialSettingsType,    &qtdata->gSpatialSettings);
603                 CheckError(err, "SCSetInfo2 error");
604                 err = SCSetInfo(qtdata->theComponent, scDataRateSettingsType,   &qtdata->aDataRateSetting);
605                 CheckError(err, "SCSetInfo3 error");
606         }
607
608         check_renderbutton_framerate(rd);
609
610         // put up the dialog box - it needs to be called from the main thread
611         err = SCRequestSequenceSettings(qtdata->theComponent);
612  
613         if (err == scUserCancelled) {
614                 G.afbreek = 1;
615                 return 0;
616         }
617
618         // get user selected data
619         SCGetInfo(qtdata->theComponent, scTemporalSettingsType, &qtdata->gTemporalSettings);
620         SCGetInfo(qtdata->theComponent, scSpatialSettingsType,  &qtdata->gSpatialSettings);
621         SCGetInfo(qtdata->theComponent, scDataRateSettingsType, &qtdata->aDataRateSetting);
622
623         QT_SaveCodecSettingsToScene(rd);
624
625         // framerate jugglin'
626         if(qtdata->gTemporalSettings.frameRate == 1571553) {                    // 23.98 fps
627                 qtdata->kVideoTimeScale = 24000;
628                 qtdata->duration = 1001;
629
630                 rd->frs_sec = 24;
631                 rd->frs_sec_base = 1.001;
632         } else if (qtdata->gTemporalSettings.frameRate == 1964113) {    // 29.97 fps
633                 qtdata->kVideoTimeScale = 30000;
634                 qtdata->duration = 1001;
635
636                 rd->frs_sec = 30;
637                 rd->frs_sec_base = 1.001;
638         } else if (qtdata->gTemporalSettings.frameRate == 3928227) {    // 59.94 fps
639                 qtdata->kVideoTimeScale = 60000;
640                 qtdata->duration = 1001;
641
642                 rd->frs_sec = 60;
643                 rd->frs_sec_base = 1.001;
644         } else {
645                 double fps = qtdata->gTemporalSettings.frameRate;
646
647                 qtdata->kVideoTimeScale = 60000;
648                 qtdata->duration = qtdata->kVideoTimeScale / (qtdata->gTemporalSettings.frameRate / 65536);
649
650                 if ((qtdata->gTemporalSettings.frameRate & 0xffff) == 0) {
651                         rd->frs_sec = fps / 65536;
652                         rd->frs_sec_base = 1;
653                 } else {
654                         // we do our very best... 
655                         rd->frs_sec = (fps * 10000 / 65536);
656                         rd->frs_sec_base = 10000;
657                 }
658         }
659 */
660         return 0;
661 }
662
663 #endif /* _WIN32 || __APPLE__ */
664 #endif /* WITH_QUICKTIME */
665