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