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