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