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