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