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