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