=bmesh= merge from trunk at r36153
[blender.git] / source / blender / quicktime / apple / qtkit_export.m
1 /**
2  * $Id$
3  *
4  * qtkit_export.m
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  *                                 Damien Plisson 11/2009
28  *
29  * ***** END GPL LICENSE BLOCK *****
30  */
31
32 #ifdef WITH_QUICKTIME
33 #if defined(_WIN32) || defined(__APPLE__)
34
35 #include <stdio.h>
36 #include <string.h>
37
38 #include "DNA_scene_types.h"
39 #include "DNA_userdef_types.h"
40
41 #include "AUD_C-API.h"
42
43 #include "BKE_global.h"
44 #include "BKE_main.h"
45 #include "BKE_scene.h"
46 #include "BKE_report.h"
47
48 #include "BLI_blenlib.h"
49
50 #include "BLO_sys_types.h"
51
52 #include "IMB_imbuf.h"
53 #include "IMB_imbuf_types.h"
54
55 #include "MEM_guardedalloc.h"
56
57 #ifdef __APPLE__
58 /* evil */
59 #ifndef __AIFF__
60 #define __AIFF__
61 #endif
62 #import <Cocoa/Cocoa.h>
63 #import <QTKit/QTKit.h>
64 #include <AudioToolbox/AudioToolbox.h>
65
66 #if (MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4) || !__LP64__
67 #error 64 bit build & OSX 10.5 minimum are needed for QTKit
68 #endif
69
70 #include "quicktime_import.h"
71 #include "quicktime_export.h"
72
73 #endif /* __APPLE__ */
74
75 typedef struct QuicktimeExport {
76         QTMovie *movie;
77         
78         NSString *filename;
79
80         QTTime frameDuration;
81         NSDictionary *frameAttributes;
82         
83         NSString *videoTempFileName;
84         /* Audio section */
85         AUD_Device *audioInputDevice;
86         AudioFileID audioFile;
87         NSString *audioFileName;
88         AudioConverterRef audioConverter;
89         AudioBufferList audioBufferList;
90         AudioStreamBasicDescription audioInputFormat, audioOutputFormat;
91         AudioStreamPacketDescription *audioOutputPktDesc;
92         SInt64 audioFilePos;
93         char* audioInputBuffer;
94         char* audioOutputBuffer;
95         UInt32 audioCodecMaxOutputPacketSize;
96         UInt64 audioTotalExportedFrames, audioTotalSavedFrames;
97         UInt64 audioLastFrame;
98         SInt64 audioOutputPktPos;
99         
100 } QuicktimeExport;
101
102 static struct QuicktimeExport *qtexport;
103
104 #define AUDIOOUTPUTBUFFERSIZE 65536
105
106 #pragma mark rna helper functions
107
108 /* Video codec */
109 static QuicktimeCodecTypeDesc qtVideoCodecList[] = {
110         {kRawCodecType, 1, "Uncompressed"},
111         {kJPEGCodecType, 2, "JPEG"},
112         {kMotionJPEGACodecType, 3, "M-JPEG A"},
113         {kMotionJPEGBCodecType, 4, "M-JPEG B"},
114         {kDVCPALCodecType, 5, "DV PAL"},
115         {kDVCNTSCCodecType, 6, "DV/DVCPRO NTSC"},
116         {kDVCPROHD720pCodecType, 7, "DVCPRO HD 720p"},
117         {kDVCPROHD1080i50CodecType, 8, "DVCPRO HD 1080i50"},
118         {kDVCPROHD1080i60CodecType, 9, "DVCPRO HD 1080i60"},
119         {kMPEG4VisualCodecType, 10, "MPEG4"},
120         {kH263CodecType, 11, "H.263"},
121         {kH264CodecType, 12, "H.264"},
122         {kAnimationCodecType, 13, "Animation"},
123         {0,0,NULL}};
124
125 static int qtVideoCodecCount = 13;
126
127 int quicktime_get_num_videocodecs() {
128         return qtVideoCodecCount;
129 }
130
131 QuicktimeCodecTypeDesc* quicktime_get_videocodecType_desc(int indexValue) {
132         if ((indexValue>=0) && (indexValue < qtVideoCodecCount))
133                 return &qtVideoCodecList[indexValue];
134         else
135                 return NULL;
136 }
137
138 int quicktime_rnatmpvalue_from_videocodectype(int codecType) {
139         int i;
140         for (i=0;i<qtVideoCodecCount;i++) {
141                 if (qtVideoCodecList[i].codecType == codecType)
142                         return qtVideoCodecList[i].rnatmpvalue;
143         }
144
145         return 0;
146 }
147
148 int quicktime_videocodecType_from_rnatmpvalue(int rnatmpvalue) {
149         int i;
150         for (i=0;i<qtVideoCodecCount;i++) {
151                 if (qtVideoCodecList[i].rnatmpvalue == rnatmpvalue)
152                         return qtVideoCodecList[i].codecType;
153         }
154         
155         return 0;       
156 }
157
158 /* Audio codec */
159 static QuicktimeCodecTypeDesc qtAudioCodecList[] = {
160         {0, 0, "No audio"},
161         {kAudioFormatLinearPCM, 1, "LPCM"},
162         {kAudioFormatAppleLossless, 2, "Apple Lossless"},
163         {kAudioFormatMPEG4AAC, 3, "AAC"},
164         {0,0,NULL}};
165
166 static int qtAudioCodecCount = 4;
167
168 int quicktime_get_num_audiocodecs() {
169         return qtAudioCodecCount;
170 }
171
172 QuicktimeCodecTypeDesc* quicktime_get_audiocodecType_desc(int indexValue) {
173         if ((indexValue>=0) && (indexValue < qtAudioCodecCount))
174                 return &qtAudioCodecList[indexValue];
175         else
176                 return NULL;
177 }
178
179 int quicktime_rnatmpvalue_from_audiocodectype(int codecType) {
180         int i;
181         for (i=0;i<qtAudioCodecCount;i++) {
182                 if (qtAudioCodecList[i].codecType == codecType)
183                         return qtAudioCodecList[i].rnatmpvalue;
184         }
185         
186         return 0;
187 }
188
189 int quicktime_audiocodecType_from_rnatmpvalue(int rnatmpvalue) {
190         int i;
191         for (i=0;i<qtAudioCodecCount;i++) {
192                 if (qtAudioCodecList[i].rnatmpvalue == rnatmpvalue)
193                         return qtAudioCodecList[i].codecType;
194         }
195         
196         return 0;       
197 }
198
199
200 static NSString *stringWithCodecType(int codecType) {
201         char str[5];
202         
203         *((int*)str) = EndianU32_NtoB(codecType);
204         str[4] = 0;
205         
206         return [NSString stringWithCString:str encoding:NSASCIIStringEncoding];
207 }
208
209 void makeqtstring (RenderData *rd, char *string) {
210         char txt[64];
211
212         strcpy(string, rd->pic);
213         BLI_path_abs(string, G.main->name);
214
215         BLI_make_existing_file(string);
216
217         if (BLI_strcasecmp(string + strlen(string) - 4, ".mov")) {
218                 sprintf(txt, "%04d_%04d.mov", (rd->sfra) , (rd->efra) );
219                 strcat(string, txt);
220         }
221 }
222
223 void filepath_qt(char *string, RenderData *rd) {
224         if (string==NULL) return;
225         
226         strcpy(string, rd->pic);
227         BLI_path_abs(string, G.main->name);
228         
229         BLI_make_existing_file(string);
230         
231         if (!BLI_testextensie(string, ".mov")) {
232                 /* if we dont have any #'s to insert numbers into, use 4 numbers by default */
233                 if (strchr(string, '#')==NULL)
234                         strcat(string, "####"); /* 4 numbers */
235
236                 BLI_path_frame_range(string, rd->sfra, rd->efra, 4);
237                 strcat(string, ".mov");
238         }
239 }
240
241
242 #pragma mark audio export functions
243
244 static OSStatus write_cookie(AudioConverterRef converter, AudioFileID outfile)
245 {
246         // grab the cookie from the converter and write it to the file
247         UInt32 cookieSize = 0;
248         OSStatus err = AudioConverterGetPropertyInfo(converter, kAudioConverterCompressionMagicCookie, &cookieSize, NULL);
249         // if there is an error here, then the format doesn't have a cookie, so on we go
250         if (!err && cookieSize) {
251                 char* cookie = malloc(cookieSize);
252                 
253                 err = AudioConverterGetProperty(converter, kAudioConverterCompressionMagicCookie, &cookieSize, cookie);
254                 
255                 if (!err)
256                         err = AudioFileSetProperty (outfile, kAudioFilePropertyMagicCookieData, cookieSize, cookie);
257                         // even though some formats have cookies, some files don't take them
258                 
259                 free(cookie);
260         }
261         return err;
262 }
263
264 /* AudioConverter input stream callback */
265 static OSStatus AudioConverterInputCallback(AudioConverterRef inAudioConverter, 
266                                                  UInt32* ioNumberDataPackets,
267                                                  AudioBufferList* ioData,
268                                                  AudioStreamPacketDescription** outDataPacketDescription,
269                                                  void* inUserData)
270 {       
271         if (qtexport->audioTotalExportedFrames >= qtexport->audioLastFrame) { /* EOF */
272                 *ioNumberDataPackets = 0;
273                 return noErr;
274         }
275
276         if (qtexport->audioInputFormat.mBytesPerPacket * *ioNumberDataPackets > AUDIOOUTPUTBUFFERSIZE)
277                 *ioNumberDataPackets = AUDIOOUTPUTBUFFERSIZE / qtexport->audioInputFormat.mBytesPerPacket;
278         
279         if ((qtexport->audioTotalExportedFrames + *ioNumberDataPackets) > qtexport->audioLastFrame)
280                 *ioNumberDataPackets = (qtexport->audioLastFrame - qtexport->audioTotalExportedFrames) / qtexport->audioInputFormat.mFramesPerPacket;
281         
282         qtexport->audioTotalExportedFrames += *ioNumberDataPackets;
283         
284         AUD_readDevice(qtexport->audioInputDevice, (UInt8*)qtexport->audioInputBuffer, 
285                                    qtexport->audioInputFormat.mFramesPerPacket * *ioNumberDataPackets);
286         
287         ioData->mBuffers[0].mDataByteSize = qtexport->audioInputFormat.mBytesPerPacket * *ioNumberDataPackets;
288         ioData->mBuffers[0].mData = qtexport->audioInputBuffer;
289         ioData->mBuffers[0].mNumberChannels = qtexport->audioInputFormat.mChannelsPerFrame;
290         
291         return noErr;
292 }       
293
294
295 #pragma mark export functions
296
297 int start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty, ReportList *reports)
298 {
299         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
300         NSError *error;
301         char name[1024];
302         int success= 1;
303         OSStatus err=noErr;
304
305         if(qtexport == NULL) qtexport = MEM_callocN(sizeof(QuicktimeExport), "QuicktimeExport");
306         
307         [QTMovie enterQTKitOnThread];           
308         
309         /* Check first if the QuickTime 7.2.1 initToWritableFile: method is available */
310         if ([[[[QTMovie alloc] init] autorelease] respondsToSelector:@selector(initToWritableFile:error:)] != YES) {
311                 BKE_report(reports, RPT_ERROR, "\nUnable to create quicktime movie, need Quicktime rev 7.2.1 or later");
312                 success= 0;
313         }
314         else {
315                 makeqtstring(rd, name);
316                 qtexport->filename = [[NSString alloc] initWithCString:name
317                                                                   encoding:[NSString defaultCStringEncoding]];
318                 qtexport->movie = nil;
319                 qtexport->audioFile = NULL;
320
321                 if (rd->qtcodecsettings.audiocodecType) {
322                         // generate a name for our video & audio files
323                         /* Init audio file */
324                         CFURLRef outputFileURL;
325                         char extension[32];
326                         AudioFileTypeID audioFileType;
327                         
328                         switch (rd->qtcodecsettings.audiocodecType) {
329                                 case kAudioFormatLinearPCM:
330                                         audioFileType = kAudioFileWAVEType;
331                                         strcpy(extension,".wav");
332                                         break;
333                                 case kAudioFormatMPEG4AAC:
334                                 case kAudioFormatAppleLossless:
335                                         audioFileType = kAudioFileM4AType;
336                                         strcpy(extension, ".m4a");
337                                         break;
338                                 default:
339                                         audioFileType = kAudioFileAIFFType;
340                                         strcpy(extension,".aiff");
341                                         break;
342                         }
343                                         
344                         tmpnam(name);
345                         strcat(name, extension);
346                         outputFileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,(UInt8*) name, strlen(name), false);
347                         
348                         if (outputFileURL) {
349                                 
350                                 qtexport->audioFileName = [[NSString alloc] initWithCString:name
351                                                                                                                          encoding:[NSString defaultCStringEncoding]];
352                                 
353                                 qtexport->audioInputFormat.mSampleRate = U.audiorate;
354                                 qtexport->audioInputFormat.mFormatID = kAudioFormatLinearPCM;
355                                 qtexport->audioInputFormat.mChannelsPerFrame = U.audiochannels;
356                                 switch (U.audioformat) {
357                                         case AUD_FORMAT_U8:
358                                                 qtexport->audioInputFormat.mBitsPerChannel = 8;
359                                                 qtexport->audioInputFormat.mFormatFlags = 0;
360                                                 break;
361                                         case AUD_FORMAT_S24:
362                                                 qtexport->audioInputFormat.mBitsPerChannel = 24;
363                                                 qtexport->audioInputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
364                                                 break;
365                                         case AUD_FORMAT_S32:
366                                                 qtexport->audioInputFormat.mBitsPerChannel = 32;
367                                                 qtexport->audioInputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
368                                                 break;
369                                         case AUD_FORMAT_FLOAT32:
370                                                 qtexport->audioInputFormat.mBitsPerChannel = 32;
371                                                 qtexport->audioInputFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
372                                                 break;
373                                         case AUD_FORMAT_FLOAT64:
374                                                 qtexport->audioInputFormat.mBitsPerChannel = 64;
375                                                 qtexport->audioInputFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
376                                                 break;
377                                         case AUD_FORMAT_S16:
378                                         default:
379                                                 qtexport->audioInputFormat.mBitsPerChannel = 16;
380                                                 qtexport->audioInputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
381                                                 break;
382                                 }
383                                 qtexport->audioInputFormat.mBytesPerFrame = qtexport->audioInputFormat.mChannelsPerFrame * qtexport->audioInputFormat.mBitsPerChannel / 8;
384                                 qtexport->audioInputFormat.mFramesPerPacket = 1; /*If not ==1, then need to check input callback for "rounding" issues"*/
385                                 qtexport->audioInputFormat.mBytesPerPacket = qtexport->audioInputFormat.mBytesPerFrame;
386                                 qtexport->audioInputFormat.mFormatFlags |= kLinearPCMFormatFlagIsPacked;
387                                 
388                                 
389                                 /*Ouput format*/
390                                 qtexport->audioOutputFormat.mFormatID = rd->qtcodecsettings.audiocodecType;
391                                 //TODO: set audio channels
392                                 qtexport->audioOutputFormat.mChannelsPerFrame = 2;
393                                 qtexport->audioOutputFormat.mSampleRate = rd->qtcodecsettings.audioSampleRate;
394                                 
395                                 /* Default value for compressed formats, overriden after if not the case */
396                                 qtexport->audioOutputFormat.mFramesPerPacket = 0;
397                                 qtexport->audioOutputFormat.mBytesPerFrame = 0;
398                                 qtexport->audioOutputFormat.mBytesPerPacket = 0;
399                                 qtexport->audioOutputFormat.mBitsPerChannel = 0;
400
401                                 switch (rd->qtcodecsettings.audiocodecType) {
402                                         case kAudioFormatMPEG4AAC:
403                                                 qtexport->audioOutputFormat.mFormatFlags = kMPEG4Object_AAC_Main;
404                                                 /* AAC codec does not handle sample rates above 48kHz, force this limit instead of getting an error afterwards */
405                                                 if (qtexport->audioOutputFormat.mSampleRate > 48000) qtexport->audioOutputFormat.mSampleRate = 48000;
406                                                 break;
407                                         case kAudioFormatAppleLossless:
408                                                 switch (U.audioformat) {
409                                                         case AUD_FORMAT_S16:
410                                                                 qtexport->audioOutputFormat.mFormatFlags = kAppleLosslessFormatFlag_16BitSourceData;
411                                                                 break;
412                                                         case AUD_FORMAT_S24:
413                                                                 qtexport->audioOutputFormat.mFormatFlags = kAppleLosslessFormatFlag_24BitSourceData;
414                                                                 break;
415                                                         case AUD_FORMAT_S32:
416                                                                 qtexport->audioOutputFormat.mFormatFlags = kAppleLosslessFormatFlag_32BitSourceData;
417                                                                 break;
418                                                         case AUD_FORMAT_U8:
419                                                         case AUD_FORMAT_FLOAT32:
420                                                         case AUD_FORMAT_FLOAT64:
421                                                         default:
422                                                                 break;
423                                                 }
424                                                 break;
425                                         case kAudioFormatLinearPCM:
426                                         default:
427                                                 switch (rd->qtcodecsettings.audioBitDepth) {
428                                                         case AUD_FORMAT_U8:
429                                                                 qtexport->audioOutputFormat.mBitsPerChannel = 8;
430                                                                 qtexport->audioOutputFormat.mFormatFlags = 0;
431                                                                 break;
432                                                         case AUD_FORMAT_S24:
433                                                                 qtexport->audioOutputFormat.mBitsPerChannel = 24;
434                                                                 qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
435                                                                 break;
436                                                         case AUD_FORMAT_S32:
437                                                                 qtexport->audioOutputFormat.mBitsPerChannel = 32;
438                                                                 qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
439                                                                 break;
440                                                         case AUD_FORMAT_FLOAT32:
441                                                                 qtexport->audioOutputFormat.mBitsPerChannel = 32;
442                                                                 qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
443                                                                 break;
444                                                         case AUD_FORMAT_FLOAT64:
445                                                                 qtexport->audioOutputFormat.mBitsPerChannel = 64;
446                                                                 qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
447                                                                 break;
448                                                         case AUD_FORMAT_S16:
449                                                         default:
450                                                                 qtexport->audioOutputFormat.mBitsPerChannel = 16;
451                                                                 qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
452                                                                 break;
453                                                 }
454                                                 qtexport->audioOutputFormat.mFormatFlags |= kLinearPCMFormatFlagIsPacked;
455                                                 qtexport->audioOutputFormat.mBytesPerPacket = qtexport->audioOutputFormat.mChannelsPerFrame * (qtexport->audioOutputFormat.mBitsPerChannel / 8);
456                                                 qtexport->audioOutputFormat.mFramesPerPacket = 1;
457                                                 qtexport->audioOutputFormat.mBytesPerFrame = qtexport->audioOutputFormat.mBytesPerPacket;
458                                                 break;
459                                 }
460                                                                                                 
461                                 err = AudioFileCreateWithURL(outputFileURL, audioFileType, &qtexport->audioOutputFormat, kAudioFileFlags_EraseFile, &qtexport->audioFile);
462                                 CFRelease(outputFileURL);
463                                 
464                                 if(err)
465                                         BKE_report(reports, RPT_ERROR, "\nQuicktime: unable to create temporary audio file. Format error ?");
466                                 else {
467                                         err = AudioConverterNew(&qtexport->audioInputFormat, &qtexport->audioOutputFormat, &qtexport->audioConverter);
468                                         if (err) {
469                                                 BKE_report(reports, RPT_ERROR, "\nQuicktime: unable to initialize audio codec converter. Format error ?");
470                                                 AudioFileClose(qtexport->audioFile);
471                                                 qtexport->audioFile = NULL;
472                                                 [qtexport->audioFileName release];
473                                                 qtexport->audioFileName = nil;
474                                         } else {
475                                                 UInt32 prop,propSize;
476                                                 /* Set up codec properties */
477                                                 if (rd->qtcodecsettings.audiocodecType == kAudioFormatMPEG4AAC) { /*Lossy compressed format*/
478                                                         prop = rd->qtcodecsettings.audioBitRate;
479                                                         AudioConverterSetProperty(qtexport->audioConverter, kAudioConverterEncodeBitRate,
480                                                                                                           sizeof(prop), &prop);
481                                                         
482                                                         if (rd->qtcodecsettings.audioCodecFlags & QTAUDIO_FLAG_CODEC_ISCBR)
483                                                                 prop = kAudioCodecBitRateControlMode_Constant;
484                                                         else
485                                                                 prop = kAudioCodecBitRateControlMode_LongTermAverage;
486                                                         AudioConverterSetProperty(qtexport->audioConverter, kAudioCodecPropertyBitRateControlMode,
487                                                                                                                         sizeof(prop), &prop);
488                                                 }
489                                                 /* Conversion quality : if performance impact then offer degraded option */
490                                                 if ((rd->qtcodecsettings.audioCodecFlags & QTAUDIO_FLAG_RESAMPLE_NOHQ) == 0) {                                                  
491                                                         prop = kAudioConverterSampleRateConverterComplexity_Mastering;
492                                                         AudioConverterSetProperty(qtexport->audioConverter, kAudioConverterSampleRateConverterComplexity,
493                                                                                                           sizeof(prop), &prop);
494                                                         
495                                                         prop = kAudioConverterQuality_Max;
496                                                         AudioConverterSetProperty(qtexport->audioConverter, kAudioConverterSampleRateConverterQuality,
497                                                                                                           sizeof(prop), &prop);
498                                                 }
499                                                 
500                                                 write_cookie(qtexport->audioConverter, qtexport->audioFile);
501                                                 
502                                                 /* Allocate output buffer */
503                                                 if (qtexport->audioOutputFormat.mBytesPerPacket ==0) /* VBR */
504                                                         AudioConverterGetProperty(qtexport->audioConverter, kAudioConverterPropertyMaximumOutputPacketSize,
505                                                                                                   &propSize, &qtexport->audioCodecMaxOutputPacketSize);
506                                                 else
507                                                         qtexport->audioCodecMaxOutputPacketSize = qtexport->audioOutputFormat.mBytesPerPacket;
508                                                 
509                                                 qtexport->audioInputBuffer = MEM_mallocN(AUDIOOUTPUTBUFFERSIZE, "qt_audio_inputPacket");
510                                                 qtexport->audioOutputBuffer = MEM_mallocN(AUDIOOUTPUTBUFFERSIZE, "qt_audio_outputPacket");
511                                                 qtexport->audioOutputPktDesc = MEM_mallocN(sizeof(AudioStreamPacketDescription)*AUDIOOUTPUTBUFFERSIZE/qtexport->audioCodecMaxOutputPacketSize,
512                                                                                                                                    "qt_audio_pktdesc");
513                                         }
514                                 }
515                         }
516                         
517                         if (err == noErr) {
518                                 qtexport->videoTempFileName = [[NSString alloc] initWithCString:tmpnam(nil) 
519                                                                                                                          encoding:[NSString defaultCStringEncoding]];                   
520                                 if (qtexport->videoTempFileName)
521                                         qtexport->movie = [[QTMovie alloc] initToWritableFile:qtexport->videoTempFileName error:&error];
522
523                         }
524                 } else
525                         qtexport->movie = [[QTMovie alloc] initToWritableFile:qtexport->filename error:&error];
526                         
527                 if(qtexport->movie == nil) {
528                         BKE_report(reports, RPT_ERROR, "Unable to create quicktime movie.");
529                         success= 0;
530                         if (qtexport->filename) [qtexport->filename release];
531                         qtexport->filename = nil;
532                         if (qtexport->audioFileName) [qtexport->audioFileName release];
533                         qtexport->audioFileName = nil;
534                         if (qtexport->videoTempFileName) [qtexport->videoTempFileName release];
535                         qtexport->videoTempFileName = nil;
536                         [QTMovie exitQTKitOnThread];
537                 } else {
538                         [qtexport->movie retain];
539                         [qtexport->movie setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieEditableAttribute];
540                         [qtexport->movie setAttribute:@"Made with Blender" forKey:QTMovieCopyrightAttribute];
541                         
542                         qtexport->frameDuration = QTMakeTime(rd->frs_sec_base*1000, rd->frs_sec*1000);
543                         
544                         /* specifying the codec attributes : try to retrieve them from render data first*/
545                         if (rd->qtcodecsettings.codecType) {
546                                 qtexport->frameAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
547                                                                                          stringWithCodecType(rd->qtcodecsettings.codecType),
548                                                                                          QTAddImageCodecType,
549                                                                                          [NSNumber numberWithLong:((rd->qtcodecsettings.codecSpatialQuality)*codecLosslessQuality)/100],
550                                                                                          QTAddImageCodecQuality,
551                                                                                          nil];
552                         }
553                         else {
554                                 qtexport->frameAttributes = [NSDictionary dictionaryWithObjectsAndKeys:@"jpeg",
555                                                                                          QTAddImageCodecType,
556                                                                                          [NSNumber numberWithLong:codecHighQuality],
557                                                                                          QTAddImageCodecQuality,
558                                                                                          nil];
559                         }
560                         [qtexport->frameAttributes retain];
561                         
562                         if (qtexport->audioFile) {
563                                 /* Init audio input stream */
564                                 AUD_DeviceSpecs specs;
565
566                                 specs.channels = U.audiochannels;
567                                 specs.format = U.audioformat;
568                                 specs.rate = U.audiorate;
569                                 qtexport->audioInputDevice = AUD_openReadDevice(specs);
570                                 AUD_playDevice(qtexport->audioInputDevice, scene->sound_scene, rd->sfra * rd->frs_sec_base / rd->frs_sec);
571                                                                 
572                                 qtexport->audioOutputPktPos = 0;
573                                 qtexport->audioTotalExportedFrames = 0;
574                                 qtexport->audioTotalSavedFrames = 0;
575                                 
576                                 qtexport->audioLastFrame = (rd->efra - rd->sfra) * qtexport->audioInputFormat.mSampleRate * rd->frs_sec_base / rd->frs_sec;
577                         }
578                 }
579         }
580         
581         [pool drain];
582
583         return success;
584 }
585
586
587 int append_qt(struct RenderData *rd, int frame, int *pixels, int rectx, int recty, ReportList *reports)
588 {
589         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
590         NSBitmapImageRep *blBitmapFormatImage;
591         NSImage *frameImage;
592         OSStatus err = noErr;
593         unsigned char *from_Ptr,*to_Ptr;
594         int y,from_i,to_i;
595         
596         
597         /* Create bitmap image rep in blender format (32bit RGBA) */
598         blBitmapFormatImage = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
599                                                                                                                                   pixelsWide:rectx 
600                                                                                                                                   pixelsHigh:recty
601                                                                                                                            bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
602                                                                                                                           colorSpaceName:NSCalibratedRGBColorSpace 
603                                                                                                                                 bitmapFormat:NSAlphaNonpremultipliedBitmapFormat
604                                                                                                                                  bytesPerRow:rectx*4
605                                                                                                                                 bitsPerPixel:32];
606         if (!blBitmapFormatImage) {
607                 [pool drain];
608                 return 0;
609         }
610         
611         from_Ptr = (unsigned char*)pixels;
612         to_Ptr = (unsigned char*)[blBitmapFormatImage bitmapData];
613         for (y = 0; y < recty; y++) {
614                 to_i = (recty-y-1)*rectx;
615                 from_i = y*rectx;
616                 memcpy(to_Ptr+4*to_i, from_Ptr+4*from_i, 4*rectx);
617         }
618         
619         frameImage = [[NSImage alloc] initWithSize:NSMakeSize(rectx, recty)];
620         [frameImage addRepresentation:blBitmapFormatImage];
621         
622         /* Add the image to the movie clip */
623         [qtexport->movie addImage:frameImage
624                                   forDuration:qtexport->frameDuration
625                            withAttributes:qtexport->frameAttributes];
626
627         [blBitmapFormatImage release];
628         [frameImage release];
629         
630         
631         if (qtexport->audioFile) {
632                 UInt32 audioPacketsConverted;
633                 /* Append audio */
634                 while (qtexport->audioTotalExportedFrames < qtexport->audioLastFrame) { 
635
636                         qtexport->audioBufferList.mNumberBuffers = 1;
637                         qtexport->audioBufferList.mBuffers[0].mNumberChannels = qtexport->audioOutputFormat.mChannelsPerFrame;
638                         qtexport->audioBufferList.mBuffers[0].mDataByteSize = AUDIOOUTPUTBUFFERSIZE;
639                         qtexport->audioBufferList.mBuffers[0].mData = qtexport->audioOutputBuffer;
640                         audioPacketsConverted = AUDIOOUTPUTBUFFERSIZE / qtexport->audioCodecMaxOutputPacketSize;
641                         
642                         err = AudioConverterFillComplexBuffer(qtexport->audioConverter, AudioConverterInputCallback,
643                                                                                         NULL, &audioPacketsConverted, &qtexport->audioBufferList, qtexport->audioOutputPktDesc);
644                         if (audioPacketsConverted) {
645                                 AudioFileWritePackets(qtexport->audioFile, false, qtexport->audioBufferList.mBuffers[0].mDataByteSize,
646                                                                           qtexport->audioOutputPktDesc, qtexport->audioOutputPktPos, &audioPacketsConverted, qtexport->audioOutputBuffer);
647                                 qtexport->audioOutputPktPos += audioPacketsConverted;
648                                 
649                                 if (qtexport->audioOutputFormat.mFramesPerPacket) { 
650                                         // this is the common case: format has constant frames per packet
651                                         qtexport->audioTotalSavedFrames += (audioPacketsConverted * qtexport->audioOutputFormat.mFramesPerPacket);
652                                 } else {
653                                         unsigned int i;
654                                         // if there are variable frames per packet, then we have to do this for each packeet
655                                         for (i = 0; i < audioPacketsConverted; ++i)
656                                                 qtexport->audioTotalSavedFrames += qtexport->audioOutputPktDesc[i].mVariableFramesInPacket;
657                                 }
658                                 
659                                 
660                         }
661                         else {
662                                 //Error getting audio packets
663                                 BKE_reportf(reports, RPT_ERROR, "Unable to get further audio packets from frame %i, error = 0x%x",qtexport->audioTotalExportedFrames,err);
664                                 break;
665                         }
666
667                 }
668         }
669         [pool drain];   
670
671         return 1;
672 }
673
674
675 void end_qt(void)
676 {
677         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
678         if (qtexport->movie) {
679                 
680                 if (qtexport->audioFile)
681                 {
682                         NSDictionary *dict = nil;
683                         QTMovie *audioTmpMovie = nil;
684                         NSError *error;
685                         NSFileManager *fileManager;
686                         
687                         /* Mux video and audio then save file */
688                         
689                         /* Write last frames for VBR files */
690                         if (qtexport->audioOutputFormat.mBitsPerChannel == 0) {
691                                 OSStatus err = noErr;
692                                 AudioConverterPrimeInfo primeInfo;
693                                 UInt32 primeSize = sizeof(primeInfo);
694                                 
695                                 err = AudioConverterGetProperty(qtexport->audioConverter, kAudioConverterPrimeInfo, &primeSize, &primeInfo);
696                                 if (err == noErr) {
697                                         // there's priming to write out to the file
698                                         AudioFilePacketTableInfo pti;
699                                         pti.mPrimingFrames = primeInfo.leadingFrames;
700                                         pti.mRemainderFrames = primeInfo.trailingFrames;
701                                         pti.mNumberValidFrames = qtexport->audioTotalSavedFrames - pti.mPrimingFrames - pti.mRemainderFrames;
702                                         AudioFileSetProperty(qtexport->audioFile, kAudioFilePropertyPacketTableInfo, sizeof(pti), &pti);
703                                 }
704                                 
705                         }
706                         
707                         write_cookie(qtexport->audioConverter, qtexport->audioFile);
708                         AudioConverterDispose(qtexport->audioConverter);
709                         AudioFileClose(qtexport->audioFile);
710                         AUD_closeReadDevice(qtexport->audioInputDevice);
711                         qtexport->audioFile = NULL;
712                         qtexport->audioInputDevice = NULL;
713                         MEM_freeN(qtexport->audioInputBuffer);
714                         MEM_freeN(qtexport->audioOutputBuffer);
715                         MEM_freeN(qtexport->audioOutputPktDesc);
716                         
717                         /* Reopen audio file and merge it */
718                         audioTmpMovie = [QTMovie movieWithFile:qtexport->audioFileName error:&error];
719                         if (audioTmpMovie) {
720                                 NSArray *audioTracks = [audioTmpMovie tracksOfMediaType:QTMediaTypeSound];
721                                 QTTrack *audioTrack = nil;
722                                 if( [audioTracks count] > 0 )
723                                 {
724                                         audioTrack = [audioTracks objectAtIndex:0];
725                                 }
726                         
727                                 if( audioTrack )
728                                 {
729                                         QTTimeRange totalRange;
730                                         totalRange.time = QTZeroTime;
731                                         totalRange.duration = [[audioTmpMovie attributeForKey:QTMovieDurationAttribute] QTTimeValue];
732                                         
733                                         [qtexport->movie insertSegmentOfTrack:audioTrack timeRange:totalRange atTime:QTZeroTime];
734                                 }
735                         }
736                         
737                         /* Save file */
738                         dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] 
739                                                                                            forKey:QTMovieFlatten];
740
741                         if (dict) {
742                                 [qtexport->movie writeToFile:qtexport->filename withAttributes:dict];
743                         }
744                         
745                         /* Delete temp files */
746                         fileManager = [[NSFileManager alloc] init];
747                         [fileManager removeItemAtPath:qtexport->audioFileName error:&error];
748                         [fileManager removeItemAtPath:qtexport->videoTempFileName error:&error];
749                         [fileManager release];
750                 }
751                 else {
752                         /* Flush update of the movie file */
753                         [qtexport->movie updateMovieFile];
754                         
755                         [qtexport->movie invalidate];
756                 }
757                 
758                 /* Clean up movie structure */
759                 if (qtexport->filename) [qtexport->filename release];
760                 qtexport->filename = nil;
761                 if (qtexport->audioFileName) [qtexport->audioFileName release];
762                 qtexport->audioFileName = nil;
763                 if (qtexport->videoTempFileName) [qtexport->videoTempFileName release];
764                 qtexport->videoTempFileName = nil;
765                 [qtexport->frameAttributes release];
766                 [qtexport->movie release];
767         }
768         
769         [QTMovie exitQTKitOnThread];
770
771         if(qtexport) {
772                 MEM_freeN(qtexport);
773                 qtexport = NULL;
774         }
775         [pool drain];
776 }
777
778
779 void free_qtcomponentdata(void) {
780 }
781
782 void quicktime_verify_image_type(RenderData *rd)
783 {
784         if (rd->imtype == R_QUICKTIME) {
785                 if ((rd->qtcodecsettings.codecType<= 0) ||
786                         (rd->qtcodecsettings.codecSpatialQuality <0) ||
787                         (rd->qtcodecsettings.codecSpatialQuality > 100)) {
788                         
789                         rd->qtcodecsettings.codecType = kJPEGCodecType;
790                         rd->qtcodecsettings.codecSpatialQuality = (codecHighQuality*100)/codecLosslessQuality;
791                 }
792                 if ((rd->qtcodecsettings.audioSampleRate < 21000) ||
793                         (rd->qtcodecsettings.audioSampleRate > 193000)) 
794                         rd->qtcodecsettings.audioSampleRate = 48000;
795                 
796                 if (rd->qtcodecsettings.audioBitDepth == 0)
797                         rd->qtcodecsettings.audioBitDepth = AUD_FORMAT_S16;
798                 
799                 if (rd->qtcodecsettings.audioBitRate == 0)
800                         rd->qtcodecsettings.audioBitRate = 256000;
801         }
802 }
803
804 #endif /* _WIN32 || __APPLE__ */
805 #endif /* WITH_QUICKTIME */
806