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