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