doxygen: prevent GPL license block from being parsed as doxygen comment.
[blender.git] / source / blender / quicktime / apple / quicktime_export.c
1 /*
2  * $Id$
3  *
4  * quicktime_export.c
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22  *
23  *
24  * The Original Code is written by Rob Haarsma (phase)
25  *
26  * Contributor(s): Stefan Gartner (sgefant)
27  *
28  * ***** END GPL LICENSE BLOCK *****
29  */
30
31 #ifdef WITH_QUICKTIME
32 #if defined(_WIN32) || defined(__APPLE__)
33 #ifndef USE_QTKIT
34
35 #include "DNA_scene_types.h"
36 #include "DNA_windowmanager_types.h"
37
38 #include "WM_api.h"
39 #include "WM_types.h"
40
41 #include "BKE_context.h"
42 #include "BKE_global.h"
43 #include "BKE_main.h"
44 #include "BKE_report.h"
45 #include "BKE_scene.h"
46
47 #include "BLI_blenlib.h"
48
49 #include "BLO_sys_types.h"
50
51 #include "IMB_imbuf.h"
52 #include "IMB_imbuf_types.h"
53
54 #include "MEM_guardedalloc.h"
55
56 #include "quicktime_import.h"
57 #include "quicktime_export.h"
58
59 #ifdef _WIN32
60 #include <QTML.h>
61 #include <Movies.h>
62 #include <QuickTimeComponents.h>
63 #include <TextUtils.h> 
64 #include <string.h>
65 #include <memory.h>
66
67 #endif /* _WIN32 */
68
69 #ifdef __APPLE__
70 /* evil */
71 #ifndef __AIFF__
72 #define __AIFF__
73 #endif
74 #include <QuickTime/Movies.h>
75 #include <QuickTime/QuickTimeComponents.h>
76 #include <fcntl.h> /* open() */
77 #include <unistd.h> /* close() */
78 #include <sys/stat.h> /* file permissions */
79 #endif /* __APPLE__ */
80
81 #define kMyCreatorType  FOUR_CHAR_CODE('TVOD')
82 #define kTrackStart             0
83 #define kMediaStart             0
84
85 static void QT_StartAddVideoSamplesToMedia (const Rect *trackFrame, int rectx, int recty, struct ReportList *reports);
86 static void QT_DoAddVideoSamplesToMedia (int frame, int *pixels, int rectx, int recty, struct ReportList *reports);
87 static void QT_EndAddVideoSamplesToMedia (void);
88 static void QT_CreateMyVideoTrack (int rectx, int recty, struct ReportList *reports);
89 static void QT_EndCreateMyVideoTrack (struct ReportList *reports);
90 static void check_renderbutton_framerate(struct RenderData *rd, struct ReportList *reports);
91 static int get_qtcodec_settings(struct RenderData *rd, struct ReportList *reports);
92
93 typedef struct QuicktimeExport {
94
95         FSSpec          theSpec;
96         short           resRefNum;
97         Str255          qtfilename;
98
99         Media           theMedia;
100         Movie           theMovie;
101         Track           theTrack;
102
103         GWorldPtr                       theGWorld;
104         PixMapHandle            thePixMap;
105         ImageDescription        **anImageDescription;
106
107         ImBuf           *ibuf;  //imagedata for Quicktime's Gworld
108         ImBuf           *ibuf2; //copy of renderdata, to be Y-flipped
109
110 } QuicktimeExport;
111
112 typedef struct QuicktimeComponentData {
113
114         ComponentInstance       theComponent;
115         SCTemporalSettings  gTemporalSettings;
116         SCSpatialSettings   gSpatialSettings;
117         SCDataRateSettings  aDataRateSetting;
118         TimeValue                       duration;
119         long                            kVideoTimeScale;
120
121 } QuicktimeComponentData;
122
123 static struct QuicktimeExport *qtexport;
124 static struct QuicktimeComponentData *qtdata;
125
126 static int      sframe;
127
128 /* RNA functions */
129
130 static QuicktimeCodecTypeDesc qtVideoCodecList[] = {
131         {kRawCodecType, 1, "Uncompressed"},
132         {kJPEGCodecType, 2, "JPEG"},
133         {kMotionJPEGACodecType, 3, "M-JPEG A"},
134         {kMotionJPEGBCodecType, 4, "M-JPEG B"},
135         {kDVCPALCodecType, 5, "DV PAL"},
136         {kDVCNTSCCodecType, 6, "DV/DVCPRO NTSC"},
137         {kDVCPROHD720pCodecType, 7, "DVCPRO HD 720p"},
138         {kDVCPROHD1080i50CodecType, 8, "DVCPRO HD 1080i50"},
139         {kDVCPROHD1080i60CodecType, 9, "DVCPRO HD 1080i60"},
140         {kMPEG4VisualCodecType, 10, "MPEG4"},
141         {kH263CodecType, 11, "H.263"},
142         {kH264CodecType, 12, "H.264"},
143         {0,0,NULL}};
144
145 static int qtVideoCodecCount = 12;
146
147 int quicktime_get_num_videocodecs() {
148         return qtVideoCodecCount;
149 }
150
151 QuicktimeCodecTypeDesc* quicktime_get_videocodecType_desc(int indexValue) {
152         if ((indexValue>=0) && (indexValue < qtVideoCodecCount))
153                 return &qtVideoCodecList[indexValue];
154         else
155                 return NULL;
156 }
157
158 int quicktime_rnatmpvalue_from_videocodectype(int codecType) {
159         int i;
160         for (i=0;i<qtVideoCodecCount;i++) {
161                 if (qtVideoCodecList[i].codecType == codecType)
162                         return qtVideoCodecList[i].rnatmpvalue;
163         }
164         
165         return 0;
166 }
167
168 int quicktime_videocodecType_from_rnatmpvalue(int rnatmpvalue) {
169         int i;
170         for (i=0;i<qtVideoCodecCount;i++) {
171                 if (qtVideoCodecList[i].rnatmpvalue == rnatmpvalue)
172                         return qtVideoCodecList[i].codecType;
173         }
174         
175         return 0;       
176 }
177
178
179
180 static void CheckError(OSErr err, char *msg, ReportList *reports)
181 {
182         if(err != noErr) {
183                 BKE_reportf(reports, RPT_ERROR, "%s: %d", msg, err);
184         }
185 }
186
187
188 static OSErr QT_SaveCodecSettingsToScene(RenderData *rd, ReportList *reports)
189 {       
190         QTAtomContainer         myContainer = NULL;
191         ComponentResult         myErr = noErr;
192         Ptr                                     myPtr;
193         long                            mySize = 0;
194
195         CodecInfo                       ci;
196
197         QuicktimeCodecData *qcd = rd->qtcodecdata;
198         
199         // check if current scene already has qtcodec settings, and clear them
200         if (qcd) {
201                 free_qtcodecdata(qcd);
202         } else {
203                 qcd = rd->qtcodecdata = MEM_callocN(sizeof(QuicktimeCodecData), "QuicktimeCodecData");
204         }
205
206         // obtain all current codec settings
207         SCSetInfo(qtdata->theComponent, scTemporalSettingsType, &qtdata->gTemporalSettings);
208         SCSetInfo(qtdata->theComponent, scSpatialSettingsType,  &qtdata->gSpatialSettings);
209         SCSetInfo(qtdata->theComponent, scDataRateSettingsType, &qtdata->aDataRateSetting);
210
211         // retreive codecdata from quicktime in a atomcontainer
212         myErr = SCGetSettingsAsAtomContainer(qtdata->theComponent,  &myContainer);
213         if (myErr != noErr) {
214                 BKE_reportf(reports, RPT_ERROR, "Quicktime: SCGetSettingsAsAtomContainer failed\n"); 
215                 goto bail;
216         }
217
218         // get the size of the atomcontainer
219         mySize = GetHandleSize((Handle)myContainer);
220
221         // lock and convert the atomcontainer to a *valid* pointer
222         QTLockContainer(myContainer);
223         myPtr = *(Handle)myContainer;
224
225         // copy the Quicktime data into the blender qtcodecdata struct
226         if (myPtr) {
227                 qcd->cdParms = MEM_mallocN(mySize, "qt.cdParms");
228                 memcpy(qcd->cdParms, myPtr, mySize);
229                 qcd->cdSize = mySize;
230
231                 GetCodecInfo (&ci, qtdata->gSpatialSettings.codecType, 0);
232         } else {
233                 BKE_reportf(reports, RPT_ERROR, "Quicktime: QT_SaveCodecSettingsToScene failed\n"); 
234         }
235
236         QTUnlockContainer(myContainer);
237
238 bail:
239         if (myContainer != NULL)
240                 QTDisposeAtomContainer(myContainer);
241                 
242         return((OSErr)myErr);
243 }
244
245
246 static OSErr QT_GetCodecSettingsFromScene(RenderData *rd, ReportList *reports)
247 {       
248         Handle                          myHandle = NULL;
249         ComponentResult         myErr = noErr;
250
251         QuicktimeCodecData *qcd = rd->qtcodecdata;
252
253         // if there is codecdata in the blendfile, convert it to a Quicktime handle 
254         if (qcd) {
255                 myHandle = NewHandle(qcd->cdSize);
256                 PtrToHand( qcd->cdParms, &myHandle, qcd->cdSize);
257         }
258                 
259         // restore codecsettings to the quicktime component
260         if(qcd->cdParms && qcd->cdSize) {
261                 myErr = SCSetSettingsFromAtomContainer((GraphicsExportComponent)qtdata->theComponent, (QTAtomContainer)myHandle);
262                 if (myErr != noErr) {
263                         BKE_reportf(reports, RPT_ERROR, "Quicktime: SCSetSettingsFromAtomContainer failed\n"); 
264                         goto bail;
265                 }
266
267                 // update runtime codecsettings for use with the codec dialog
268                 SCGetInfo(qtdata->theComponent, scDataRateSettingsType, &qtdata->aDataRateSetting);
269                 SCGetInfo(qtdata->theComponent, scSpatialSettingsType,  &qtdata->gSpatialSettings);
270                 SCGetInfo(qtdata->theComponent, scTemporalSettingsType, &qtdata->gTemporalSettings);
271
272
273                 //Fill the render QuicktimeCodecSettigns struct
274                 rd->qtcodecsettings.codecTemporalQuality = (qtdata->gTemporalSettings.temporalQuality * 100) / codecLosslessQuality;
275                 //Do not override scene frame rate (qtdata->gTemporalSettings.framerate)
276                 rd->qtcodecsettings.keyFrameRate = qtdata->gTemporalSettings.keyFrameRate;
277                 
278                 rd->qtcodecsettings.codecType = qtdata->gSpatialSettings.codecType;
279                 rd->qtcodecsettings.codec = (int)qtdata->gSpatialSettings.codec;
280                 rd->qtcodecsettings.colorDepth = qtdata->gSpatialSettings.depth;
281                 rd->qtcodecsettings.codecSpatialQuality = (qtdata->gSpatialSettings.spatialQuality * 100) / codecLosslessQuality;
282                 
283                 rd->qtcodecsettings.bitRate = qtdata->aDataRateSetting.dataRate;
284                 rd->qtcodecsettings.minSpatialQuality = (qtdata->aDataRateSetting.minSpatialQuality * 100) / codecLosslessQuality;
285                 rd->qtcodecsettings.minTemporalQuality = (qtdata->aDataRateSetting.minTemporalQuality * 100) / codecLosslessQuality;
286                 //Frame duration is already known (qtdata->aDataRateSetting.frameDuration)
287                 
288         } else {
289                 BKE_reportf(reports, RPT_ERROR, "Quicktime: QT_GetCodecSettingsFromScene failed\n"); 
290         }
291 bail:
292         if (myHandle != NULL)
293                 DisposeHandle(myHandle);
294                 
295         return((OSErr)myErr);
296 }
297
298
299 static OSErr QT_AddUserDataTextToMovie (Movie theMovie, char *theText, OSType theType)
300 {
301         UserData                                        myUserData = NULL;
302         Handle                                          myHandle = NULL;
303         long                                            myLength = strlen(theText);
304         OSErr                                           myErr = noErr;
305
306         // get the movie's user data list
307         myUserData = GetMovieUserData(theMovie);
308         if (myUserData == NULL)
309                 return(paramErr);
310         
311         // copy the specified text into a new handle
312         myHandle = NewHandleClear(myLength);
313         if (myHandle == NULL)
314                 return(MemError());
315
316         BlockMoveData(theText, *myHandle, myLength);
317
318         // add the data to the movie's user data
319         myErr = AddUserDataText(myUserData, myHandle, theType, 1, (short)GetScriptManagerVariable(smRegionCode));
320
321         // clean up
322         DisposeHandle(myHandle);
323         return(myErr);
324 }
325
326
327 static void QT_CreateMyVideoTrack(int rectx, int recty, ReportList *reports)
328 {
329         OSErr err = noErr;
330         Rect trackFrame;
331 //      MatrixRecord myMatrix;
332
333         trackFrame.top = 0;
334         trackFrame.left = 0;
335         trackFrame.bottom = recty;
336         trackFrame.right = rectx;
337         
338         qtexport->theTrack = NewMovieTrack (qtexport->theMovie, 
339                                                         FixRatio(trackFrame.right,1),
340                                                         FixRatio(trackFrame.bottom,1), 
341                                                         0);
342         CheckError( GetMoviesError(), "NewMovieTrack error", reports );
343
344 //      SetIdentityMatrix(&myMatrix);
345 //      ScaleMatrix(&myMatrix, fixed1, Long2Fix(-1), 0, 0);
346 //      TranslateMatrix(&myMatrix, 0, Long2Fix(trackFrame.bottom));
347 //      SetMovieMatrix(qtexport->theMovie, &myMatrix);
348
349         qtexport->theMedia = NewTrackMedia (qtexport->theTrack,
350                                                         VideoMediaType,
351                                                         qtdata->kVideoTimeScale,
352                                                         nil,
353                                                         0);
354         CheckError( GetMoviesError(), "NewTrackMedia error", reports );
355
356         err = BeginMediaEdits (qtexport->theMedia);
357         CheckError( err, "BeginMediaEdits error", reports );
358
359         QT_StartAddVideoSamplesToMedia (&trackFrame, rectx, recty, reports);
360
361
362
363 static void QT_EndCreateMyVideoTrack(ReportList *reports)
364 {
365         OSErr err = noErr;
366
367         QT_EndAddVideoSamplesToMedia ();
368
369         err = EndMediaEdits (qtexport->theMedia);
370         CheckError( err, "EndMediaEdits error", reports );
371
372         err = InsertMediaIntoTrack (qtexport->theTrack,
373                                                                 kTrackStart,/* track start time */
374                                                                 kMediaStart,/* media start time */
375                                                                 GetMediaDuration (qtexport->theMedia),
376                                                                 fixed1);
377         CheckError( err, "InsertMediaIntoTrack error", reports );
378
379
380
381 static void QT_StartAddVideoSamplesToMedia (const Rect *trackFrame, int rectx, int recty, ReportList *reports)
382 {
383         SCTemporalSettings gTemporalSettings;
384         OSErr err = noErr;
385
386         qtexport->ibuf = IMB_allocImBuf (rectx, recty, 32, IB_rect);
387         qtexport->ibuf2 = IMB_allocImBuf (rectx, recty, 32, IB_rect);
388
389         err = NewGWorldFromPtr( &qtexport->theGWorld,
390                                                         k32ARGBPixelFormat,
391                                                         trackFrame,
392                                                         NULL, NULL, 0,
393                                                         (Ptr)qtexport->ibuf->rect,
394                                                         rectx * 4 );
395         CheckError (err, "NewGWorldFromPtr error", reports);
396
397         qtexport->thePixMap = GetGWorldPixMap(qtexport->theGWorld);
398         LockPixels(qtexport->thePixMap);
399
400         SCDefaultPixMapSettings (qtdata->theComponent, qtexport->thePixMap, true);
401
402         // workaround for crash with H.264, which requires an upgrade to
403         // the new callback based api for proper encoding, but that's not
404         // really compatible with rendering out frames sequentially
405         gTemporalSettings = qtdata->gTemporalSettings;
406         if(qtdata->gSpatialSettings.codecType == kH264CodecType) {
407                 if(gTemporalSettings.temporalQuality != codecMinQuality) {
408                         BKE_reportf(reports, RPT_WARNING, "Only minimum quality compression supported for QuickTime H.264.\n");
409                         gTemporalSettings.temporalQuality = codecMinQuality;
410                 }
411         }
412
413         SCSetInfo(qtdata->theComponent, scTemporalSettingsType, &gTemporalSettings);
414         SCSetInfo(qtdata->theComponent, scSpatialSettingsType,  &qtdata->gSpatialSettings);
415         SCSetInfo(qtdata->theComponent, scDataRateSettingsType, &qtdata->aDataRateSetting);
416
417         err = SCCompressSequenceBegin(qtdata->theComponent, qtexport->thePixMap, NULL, &qtexport->anImageDescription); 
418         CheckError (err, "SCCompressSequenceBegin error", reports );
419 }
420
421
422 static void QT_DoAddVideoSamplesToMedia (int frame, int *pixels, int rectx, int recty, ReportList *reports)
423 {
424         OSErr   err = noErr;
425         Rect    imageRect;
426
427         int             index;
428         int             boxsize;
429         unsigned char *from, *to;
430
431         short   syncFlag;
432         long    dataSize;
433         Handle  compressedData;
434         Ptr             myPtr;
435
436
437         //copy and flip renderdata
438         memcpy(qtexport->ibuf2->rect, pixels, 4*rectx*recty);
439         IMB_flipy(qtexport->ibuf2);
440
441         //get pointers to parse bitmapdata
442         myPtr = GetPixBaseAddr(qtexport->thePixMap);
443         imageRect = (**qtexport->thePixMap).bounds;
444
445         from = (unsigned char *) qtexport->ibuf2->rect;
446         to = (unsigned char *) myPtr;
447
448         //parse RGBA bitmap into Quicktime's ARGB GWorld
449         boxsize = rectx * recty;
450         for( index = 0; index < boxsize; index++) {
451                 to[0] = from[3];
452                 to[1] = from[0];
453                 to[2] = from[1];
454                 to[3] = from[2];
455                 to +=4, from += 4;
456         }
457
458         err = SCCompressSequenceFrame(qtdata->theComponent,
459                 qtexport->thePixMap,
460                 &imageRect,
461                 &compressedData,
462                 &dataSize,
463                 &syncFlag);
464         CheckError(err, "SCCompressSequenceFrame error", reports);
465
466         err = AddMediaSample(qtexport->theMedia,
467                 compressedData,
468                 0,
469                 dataSize,
470                 qtdata->duration,
471                 (SampleDescriptionHandle)qtexport->anImageDescription,
472                 1,
473                 syncFlag,
474                 NULL);
475         CheckError(err, "AddMediaSample error", reports);
476 }
477
478
479 static void QT_EndAddVideoSamplesToMedia (void)
480 {
481         SCCompressSequenceEnd(qtdata->theComponent);
482
483         UnlockPixels(qtexport->thePixMap);
484         if (qtexport->theGWorld)
485                 DisposeGWorld (qtexport->theGWorld);
486
487         if (qtexport->ibuf)
488                 IMB_freeImBuf(qtexport->ibuf);
489
490         if (qtexport->ibuf2)
491                 IMB_freeImBuf(qtexport->ibuf2);
492
493
494
495 void filepath_qt(char *string, RenderData *rd) {
496         char txt[64];
497
498         if (string==0) return;
499
500         strcpy(string, rd->pic);
501         BLI_path_abs(string, G.main->name);
502
503         BLI_make_existing_file(string);
504
505         if (BLI_strcasecmp(string + strlen(string) - 4, ".mov")) {
506                 sprintf(txt, "%04d_%04d.mov", (rd->sfra) , (rd->efra) );
507                 strcat(string, txt);
508         }
509 }
510
511
512 int start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty, ReportList *reports) {
513         OSErr err = noErr;
514
515         char name[2048];
516         char theFullPath[255];
517
518 #ifdef __APPLE__
519         int             myFile;
520         FSRef   myRef;
521 #else
522         char    *qtname;
523 #endif
524         int success= 1;
525
526         if(qtexport == NULL) qtexport = MEM_callocN(sizeof(QuicktimeExport), "QuicktimeExport");
527
528         if(qtdata) {
529                 if(qtdata->theComponent) CloseComponent(qtdata->theComponent);
530                 free_qtcomponentdata();
531         }
532
533         qtdata = MEM_callocN(sizeof(QuicktimeComponentData), "QuicktimeCodecDataExt");
534
535         if(rd->qtcodecdata == NULL || rd->qtcodecdata->cdParms == NULL) {
536                 get_qtcodec_settings(rd, reports);
537         } else {
538                 qtdata->theComponent = OpenDefaultComponent(StandardCompressionType, StandardCompressionSubType);
539
540                 QT_GetCodecSettingsFromScene(rd, reports);
541                 check_renderbutton_framerate(rd, reports);
542         }
543         
544         sframe = (rd->sfra);
545
546         filepath_qt(name, rd);
547
548 #ifdef __APPLE__
549         EnterMoviesOnThread(0);
550         sprintf(theFullPath, "%s", name);
551
552         /* hack: create an empty file to make FSPathMakeRef() happy */
553         myFile = open(theFullPath, O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRUSR|S_IWUSR);
554         if (myFile < 0) {
555                 BKE_reportf(reports, RPT_ERROR, "error while creating movie file!\n");
556                 /* do something? */
557         }
558         close(myFile);
559         err = FSPathMakeRef((const UInt8 *)theFullPath, &myRef, 0);
560         CheckError(err, "FsPathMakeRef error", reports);
561         err = FSGetCatalogInfo(&myRef, kFSCatInfoNone, NULL, NULL, &qtexport->theSpec, NULL);
562         CheckError(err, "FsGetCatalogInfoRef error", reports);
563 #endif
564 #ifdef _WIN32
565         qtname = get_valid_qtname(name);
566         sprintf(theFullPath, "%s", qtname);
567         strcpy(name, qtname);
568         MEM_freeN(qtname);
569         
570         CopyCStringToPascal(theFullPath, qtexport->qtfilename);
571         err = FSMakeFSSpec(0, 0L, qtexport->qtfilename, &qtexport->theSpec);
572 #endif
573
574         err = CreateMovieFile (&qtexport->theSpec, 
575                                                 kMyCreatorType,
576                                                 smCurrentScript, 
577                                                 createMovieFileDeleteCurFile | createMovieFileDontCreateResFile,
578                                                 &qtexport->resRefNum, 
579                                                 &qtexport->theMovie );
580         CheckError(err, "CreateMovieFile error", reports);
581
582         if(err != noErr) {
583                 BKE_reportf(reports, RPT_ERROR, "Unable to create Quicktime movie: %s", name);
584                 success= 0;
585 #ifdef __APPLE__
586                 ExitMoviesOnThread();
587 #endif
588         } else {
589                 //printf("Created QuickTime movie: %s\n", name);
590
591                 QT_CreateMyVideoTrack(rectx, recty, reports);
592         }
593
594         return success;
595 }
596
597
598 int append_qt(struct RenderData *rd, int frame, int *pixels, int rectx, int recty, ReportList *reports) {
599         QT_DoAddVideoSamplesToMedia(frame, pixels, rectx, recty, reports);
600         return 1;
601 }
602
603
604 void end_qt(void) {
605         OSErr err = noErr;
606         short resId = movieInDataForkResID;
607
608         if(qtexport->theMovie) {
609                 QT_EndCreateMyVideoTrack(NULL);
610
611                 err = AddMovieResource (qtexport->theMovie, qtexport->resRefNum, &resId, qtexport->qtfilename);
612                 CheckError(err, "AddMovieResource error", NULL);
613
614                 err = QT_AddUserDataTextToMovie(qtexport->theMovie, "Made with Blender", kUserDataTextInformation);
615                 CheckError(err, "AddUserDataTextToMovie error", NULL);
616
617                 err = UpdateMovieResource(qtexport->theMovie, qtexport->resRefNum, resId, qtexport->qtfilename);
618                 CheckError(err, "UpdateMovieResource error", NULL);
619
620                 if(qtexport->resRefNum) CloseMovieFile(qtexport->resRefNum);
621
622                 DisposeMovie(qtexport->theMovie);
623
624                 //printf("Finished QuickTime movie.\n");
625         }
626
627 #ifdef __APPLE__
628                 ExitMoviesOnThread();
629 #endif
630         
631         if(qtexport) {
632                 MEM_freeN(qtexport);
633                 qtexport = NULL;
634         }
635 }
636
637
638 void free_qtcomponentdata(void) {
639         if(qtdata) {
640                 if(qtdata->theComponent) CloseComponent(qtdata->theComponent);
641                 MEM_freeN(qtdata);
642                 qtdata = NULL;
643         }
644 }
645
646
647 static void check_renderbutton_framerate(RenderData *rd, ReportList *reports) 
648 {
649         // to keep float framerates consistent between the codec dialog and frs/sec button.
650         OSErr   err;    
651
652         err = SCGetInfo(qtdata->theComponent, scTemporalSettingsType,   &qtdata->gTemporalSettings);
653         CheckError(err, "SCGetInfo fr error", reports);
654
655         if( (rd->frs_sec == 24 || rd->frs_sec == 30 || rd->frs_sec == 60) &&
656                 (qtdata->gTemporalSettings.frameRate == 1571553 ||
657                  qtdata->gTemporalSettings.frameRate == 1964113 ||
658                  qtdata->gTemporalSettings.frameRate == 3928227)) {;} 
659         else {
660                 if (rd->frs_sec_base > 0)
661                         qtdata->gTemporalSettings.frameRate = 
662                         ((float)(rd->frs_sec << 16) / rd->frs_sec_base) ;
663         }
664         
665         err = SCSetInfo(qtdata->theComponent, scTemporalSettingsType,   &qtdata->gTemporalSettings);
666         CheckError( err, "SCSetInfo error", reports );
667
668         if(qtdata->gTemporalSettings.frameRate == 1571553) {                    // 23.98 fps
669                 qtdata->kVideoTimeScale = 24000;
670                 qtdata->duration = 1001;
671         } else if (qtdata->gTemporalSettings.frameRate == 1964113) {    // 29.97 fps
672                 qtdata->kVideoTimeScale = 30000;
673                 qtdata->duration = 1001;
674         } else if (qtdata->gTemporalSettings.frameRate == 3928227) {    // 59.94 fps
675                 qtdata->kVideoTimeScale = 60000;
676                 qtdata->duration = 1001;
677         } else {
678                 qtdata->kVideoTimeScale = (qtdata->gTemporalSettings.frameRate >> 16) * 100;
679                 qtdata->duration = 100;
680         }
681 }
682
683 void quicktime_verify_image_type(RenderData *rd)
684 {
685         if (rd->imtype == R_QUICKTIME) {
686                 if ((rd->qtcodecsettings.codecType== 0) ||
687                         (rd->qtcodecsettings.codecSpatialQuality <0) ||
688                         (rd->qtcodecsettings.codecSpatialQuality > 100)) {
689                         
690                         rd->qtcodecsettings.codecType = kJPEGCodecType;
691                         rd->qtcodecsettings.codec = (int)anyCodec;
692                         rd->qtcodecsettings.codecSpatialQuality = (codecHighQuality*100)/codecLosslessQuality;
693                         rd->qtcodecsettings.codecTemporalQuality = (codecHighQuality*100)/codecLosslessQuality;
694                         rd->qtcodecsettings.keyFrameRate = 25;
695                         rd->qtcodecsettings.bitRate = 5000000; //5 Mbps
696                 }
697         }
698 }
699
700 int get_qtcodec_settings(RenderData *rd, ReportList *reports) 
701 {
702         OSErr err = noErr;
703                 // erase any existing codecsetting
704         if(qtdata) {
705                 if(qtdata->theComponent) CloseComponent(qtdata->theComponent);
706                 free_qtcomponentdata();
707         }
708
709         // allocate new
710         qtdata = MEM_callocN(sizeof(QuicktimeComponentData), "QuicktimeComponentData");
711         qtdata->theComponent = OpenDefaultComponent(StandardCompressionType, StandardCompressionSubType);
712
713         // get previous selected codecsetting, from qtatom or detailed settings
714         if(rd->qtcodecdata && rd->qtcodecdata->cdParms) {
715                 QT_GetCodecSettingsFromScene(rd, reports);
716         } else {
717                 SCGetInfo(qtdata->theComponent, scDataRateSettingsType, &qtdata->aDataRateSetting);
718                 SCGetInfo(qtdata->theComponent, scSpatialSettingsType,  &qtdata->gSpatialSettings);
719                 SCGetInfo(qtdata->theComponent, scTemporalSettingsType, &qtdata->gTemporalSettings);
720
721                 qtdata->gSpatialSettings.codecType = rd->qtcodecsettings.codecType;
722                 qtdata->gSpatialSettings.codec = (CodecComponent)rd->qtcodecsettings.codec;      
723                 qtdata->gSpatialSettings.spatialQuality = (rd->qtcodecsettings.codecSpatialQuality * codecLosslessQuality) /100;
724                 qtdata->gTemporalSettings.temporalQuality = (rd->qtcodecsettings.codecTemporalQuality * codecLosslessQuality) /100;
725                 qtdata->gTemporalSettings.keyFrameRate = rd->qtcodecsettings.keyFrameRate;   
726                 qtdata->aDataRateSetting.dataRate = rd->qtcodecsettings.bitRate;
727                 qtdata->gSpatialSettings.depth = rd->qtcodecsettings.colorDepth;
728                 qtdata->aDataRateSetting.minSpatialQuality = (rd->qtcodecsettings.minSpatialQuality * codecLosslessQuality) / 100;
729                 qtdata->aDataRateSetting.minTemporalQuality = (rd->qtcodecsettings.minTemporalQuality * codecLosslessQuality) / 100;
730                 
731                 qtdata->aDataRateSetting.frameDuration = rd->frs_sec;
732                 SetMovieTimeScale(qtexport->theMovie, rd->frs_sec_base*1000);
733                 
734                 
735                 err = SCSetInfo(qtdata->theComponent, scTemporalSettingsType,   &qtdata->gTemporalSettings);
736                 CheckError(err, "SCSetInfo1 error", reports);
737                 err = SCSetInfo(qtdata->theComponent, scSpatialSettingsType,    &qtdata->gSpatialSettings);
738                 CheckError(err, "SCSetInfo2 error", reports);
739                 err = SCSetInfo(qtdata->theComponent, scDataRateSettingsType,   &qtdata->aDataRateSetting);
740                 CheckError(err, "SCSetInfo3 error", reports);
741         }
742
743         check_renderbutton_framerate(rd, reports);
744         
745         return err;
746 }
747
748 static int request_qtcodec_settings(bContext *C, wmOperator *op)
749 {
750         OSErr   err = noErr;
751         Scene *scene = CTX_data_scene(C);
752         RenderData *rd = &scene->r;
753
754         // erase any existing codecsetting
755         if(qtdata) {
756                 if(qtdata->theComponent) CloseComponent(qtdata->theComponent);
757                 free_qtcomponentdata();
758         }
759         
760         // allocate new
761         qtdata = MEM_callocN(sizeof(QuicktimeComponentData), "QuicktimeComponentData");
762         qtdata->theComponent = OpenDefaultComponent(StandardCompressionType, StandardCompressionSubType);
763         
764         // get previous selected codecsetting, from qtatom or detailed settings
765         if(rd->qtcodecdata && rd->qtcodecdata->cdParms) {
766                 QT_GetCodecSettingsFromScene(rd, op->reports);
767         } else {
768                 SCGetInfo(qtdata->theComponent, scDataRateSettingsType, &qtdata->aDataRateSetting);
769                 SCGetInfo(qtdata->theComponent, scSpatialSettingsType,  &qtdata->gSpatialSettings);
770                 SCGetInfo(qtdata->theComponent, scTemporalSettingsType, &qtdata->gTemporalSettings);
771                 
772                 qtdata->gSpatialSettings.codecType = rd->qtcodecsettings.codecType;
773                 qtdata->gSpatialSettings.codec = (CodecComponent)rd->qtcodecsettings.codec;      
774                 qtdata->gSpatialSettings.spatialQuality = (rd->qtcodecsettings.codecSpatialQuality * codecLosslessQuality) /100;
775                 qtdata->gTemporalSettings.temporalQuality = (rd->qtcodecsettings.codecTemporalQuality * codecLosslessQuality) /100;
776                 qtdata->gTemporalSettings.keyFrameRate = rd->qtcodecsettings.keyFrameRate;
777                 qtdata->gTemporalSettings.frameRate = ((float)(rd->frs_sec << 16) / rd->frs_sec_base);
778                 qtdata->aDataRateSetting.dataRate = rd->qtcodecsettings.bitRate;
779                 qtdata->gSpatialSettings.depth = rd->qtcodecsettings.colorDepth;
780                 qtdata->aDataRateSetting.minSpatialQuality = (rd->qtcodecsettings.minSpatialQuality * codecLosslessQuality) / 100;
781                 qtdata->aDataRateSetting.minTemporalQuality = (rd->qtcodecsettings.minTemporalQuality * codecLosslessQuality) / 100;
782                 
783                 qtdata->aDataRateSetting.frameDuration = rd->frs_sec;           
784                 
785                 err = SCSetInfo(qtdata->theComponent, scTemporalSettingsType,   &qtdata->gTemporalSettings);
786                 CheckError(err, "SCSetInfo1 error", op->reports);
787                 err = SCSetInfo(qtdata->theComponent, scSpatialSettingsType,    &qtdata->gSpatialSettings);
788                 CheckError(err, "SCSetInfo2 error", op->reports);
789                 err = SCSetInfo(qtdata->theComponent, scDataRateSettingsType,   &qtdata->aDataRateSetting);
790                 CheckError(err, "SCSetInfo3 error", op->reports);
791         }
792                 // put up the dialog box - it needs to be called from the main thread
793         err = SCRequestSequenceSettings(qtdata->theComponent);
794  
795         if (err == scUserCancelled) {
796                 return OPERATOR_FINISHED;
797         }
798
799                 // update runtime codecsettings for use with the codec dialog
800         SCGetInfo(qtdata->theComponent, scDataRateSettingsType, &qtdata->aDataRateSetting);
801         SCGetInfo(qtdata->theComponent, scSpatialSettingsType,  &qtdata->gSpatialSettings);
802         SCGetInfo(qtdata->theComponent, scTemporalSettingsType, &qtdata->gTemporalSettings);
803         
804         
805                 //Fill the render QuicktimeCodecSettings struct
806         rd->qtcodecsettings.codecTemporalQuality = (qtdata->gTemporalSettings.temporalQuality * 100) / codecLosslessQuality;
807                 //Do not override scene frame rate (qtdata->gTemporalSettings.framerate)
808         rd->qtcodecsettings.keyFrameRate = qtdata->gTemporalSettings.keyFrameRate;
809         
810         rd->qtcodecsettings.codecType = qtdata->gSpatialSettings.codecType;
811         rd->qtcodecsettings.codec = (int)qtdata->gSpatialSettings.codec;
812         rd->qtcodecsettings.colorDepth = qtdata->gSpatialSettings.depth;
813         rd->qtcodecsettings.codecSpatialQuality = (qtdata->gSpatialSettings.spatialQuality * 100) / codecLosslessQuality;
814         
815         rd->qtcodecsettings.bitRate = qtdata->aDataRateSetting.dataRate;
816         rd->qtcodecsettings.minSpatialQuality = (qtdata->aDataRateSetting.minSpatialQuality * 100) / codecLosslessQuality;
817         rd->qtcodecsettings.minTemporalQuality = (qtdata->aDataRateSetting.minTemporalQuality * 100) / codecLosslessQuality;
818                 //Frame duration is already known (qtdata->aDataRateSetting.frameDuration)
819         
820         QT_SaveCodecSettingsToScene(rd, op->reports);
821
822         // framerate jugglin'
823         if(qtdata->gTemporalSettings.frameRate == 1571553) {                    // 23.98 fps
824                 qtdata->kVideoTimeScale = 24000;
825                 qtdata->duration = 1001;
826
827                 rd->frs_sec = 24;
828                 rd->frs_sec_base = 1.001;
829         } else if (qtdata->gTemporalSettings.frameRate == 1964113) {    // 29.97 fps
830                 qtdata->kVideoTimeScale = 30000;
831                 qtdata->duration = 1001;
832
833                 rd->frs_sec = 30;
834                 rd->frs_sec_base = 1.001;
835         } else if (qtdata->gTemporalSettings.frameRate == 3928227) {    // 59.94 fps
836                 qtdata->kVideoTimeScale = 60000;
837                 qtdata->duration = 1001;
838
839                 rd->frs_sec = 60;
840                 rd->frs_sec_base = 1.001;
841         } else {
842                 double fps = qtdata->gTemporalSettings.frameRate;
843
844                 qtdata->kVideoTimeScale = 60000;
845                 qtdata->duration = qtdata->kVideoTimeScale / (qtdata->gTemporalSettings.frameRate / 65536);
846
847                 if ((qtdata->gTemporalSettings.frameRate & 0xffff) == 0) {
848                         rd->frs_sec = fps / 65536;
849                         rd->frs_sec_base = 1.0;
850                 } else {
851                         /* we do our very best... */
852                         rd->frs_sec = fps  / 65536;
853                         rd->frs_sec_base = 1.0;
854                 }
855         }
856
857         return OPERATOR_FINISHED;
858 }
859
860 static int ED_operator_setqtcodec(bContext *C)
861 {
862         return G.have_quicktime != FALSE;
863 }
864
865 #if defined(__APPLE__) && defined(GHOST_COCOA)
866 //Need to set up a Cocoa NSAutoReleasePool to avoid memory leak
867 //And it must be done in an objC file, so use a GHOST_SystemCocoa.mm function for that
868 extern int cocoa_request_qtcodec_settings(bContext *C, wmOperator *op);
869
870 int fromcocoa_request_qtcodec_settings(bContext *C, wmOperator *op)
871 {
872         return request_qtcodec_settings(C, op);
873 }
874 #endif
875
876
877 void SCENE_OT_render_data_set_quicktime_codec(wmOperatorType *ot)
878 {
879         /* identifiers */
880         ot->name= "Change codec";
881         ot->description= "Change Quicktime codec Settings";
882         ot->idname= "SCENE_OT_render_data_set_quicktime_codec";
883         
884         /* api callbacks */
885 #if defined(__APPLE__) && defined(GHOST_COCOA)
886         ot->exec = cocoa_request_qtcodec_settings;
887 #else
888         ot->exec= request_qtcodec_settings;
889 #endif
890         ot->poll= ED_operator_setqtcodec;
891         
892         /* flags */
893         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
894 }
895
896 #endif /* USE_QTKIT */
897 #endif /* _WIN32 || __APPLE__ */
898 #endif /* WITH_QUICKTIME */
899