75c7d890ddd34e0075c63af768658c12e7c31a22
[blender.git] / source / blender / quicktime / apple / qtkit_export.m
1 /*
2  * Code to create QuickTime Movies with Blender
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  *
20  * The Original Code is written by Rob Haarsma (phase)
21  *
22  * Contributor(s): Stefan Gartner (sgefant)
23  *                                 Damien Plisson 11/2009
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 #ifdef WITH_QUICKTIME
29 #if defined(_WIN32) || defined(__APPLE__)
30
31 #include <stdio.h>
32 #include <string.h>
33
34 #include "DNA_scene_types.h"
35 #include "DNA_userdef_types.h"
36
37 #ifdef WITH_AUDASPACE
38 #  include "AUD_C-API.h"
39 #endif
40
41 #include "BKE_global.h"
42 #include "BKE_main.h"
43 #include "BKE_scene.h"
44 #include "BKE_report.h"
45
46 #include "BLI_blenlib.h"
47
48 #include "BLO_sys_types.h"
49
50 #include "IMB_imbuf.h"
51 #include "IMB_imbuf_types.h"
52
53 #include "MEM_guardedalloc.h"
54
55 #ifdef __APPLE__
56 /* evil */
57 #ifndef __AIFF__
58 #define __AIFF__
59 #endif
60 #import <Cocoa/Cocoa.h>
61 #import <QTKit/QTKit.h>
62 #include <AudioToolbox/AudioToolbox.h>
63
64 #if (MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4) || !__LP64__
65 #error 64 bit build & OSX 10.5 minimum are needed for QTKit
66 #endif
67
68 #include "quicktime_import.h"
69 #include "quicktime_export.h"
70
71 #endif /* __APPLE__ */
72
73 typedef struct QuicktimeExport {
74         QTMovie *movie;
75         
76         NSString *filename;
77
78         QTTime frameDuration;
79         NSDictionary *frameAttributes;
80         
81         NSString *videoTempFileName;
82         /* Audio section */
83         AUD_Device *audioInputDevice;
84         AudioFileID audioFile;
85         NSString *audioFileName;
86         AudioConverterRef audioConverter;
87         AudioBufferList audioBufferList;
88         AudioStreamBasicDescription audioInputFormat, audioOutputFormat;
89         AudioStreamPacketDescription *audioOutputPktDesc;
90         SInt64 audioFilePos;
91         char* audioInputBuffer;
92         char* audioOutputBuffer;
93         UInt32 audioCodecMaxOutputPacketSize;
94         UInt64 audioTotalExportedFrames, audioTotalSavedFrames;
95         UInt64 audioLastFrame;
96         SInt64 audioOutputPktPos;
97         
98 } QuicktimeExport;
99
100 static struct QuicktimeExport *qtexport;
101
102 #define AUDIOOUTPUTBUFFERSIZE 65536
103
104 #pragma mark rna helper functions
105
106 /* Video codec */
107 static QuicktimeCodecTypeDesc qtVideoCodecList[] = {
108         {kRawCodecType, 1, "Uncompressed"},
109         {kJPEGCodecType, 2, "JPEG"},
110         {kMotionJPEGACodecType, 3, "M-JPEG A"},
111         {kMotionJPEGBCodecType, 4, "M-JPEG B"},
112         {kDVCPALCodecType, 5, "DV PAL"},
113         {kDVCNTSCCodecType, 6, "DV/DVCPRO NTSC"},
114         {kDVCPROHD720pCodecType, 7, "DVCPRO HD 720p"},
115         {kDVCPROHD1080i50CodecType, 8, "DVCPRO HD 1080i50"},
116         {kDVCPROHD1080i60CodecType, 9, "DVCPRO HD 1080i60"},
117         {kMPEG4VisualCodecType, 10, "MPEG4"},
118         {kH263CodecType, 11, "H.263"},
119         {kH264CodecType, 12, "H.264"},
120         {kAnimationCodecType, 13, "Animation"},
121         {0,0,NULL}};
122
123 static int qtVideoCodecCount = 13;
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.main->name);
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.main->name);
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) / qtexport->audioInputFormat.mFramesPerPacket;
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; /*If not ==1, then need to check input callback for "rounding" issues"*/
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                                                 /* AAC codec does not handle sample rates above 48kHz, force this limit instead of getting an error afterwards */
403                                                 if (qtexport->audioOutputFormat.mSampleRate > 48000) qtexport->audioOutputFormat.mSampleRate = 48000;
404                                                 break;
405                                         case kAudioFormatAppleLossless:
406                                                 switch (U.audioformat) {
407                                                         case AUD_FORMAT_S16:
408                                                                 qtexport->audioOutputFormat.mFormatFlags = kAppleLosslessFormatFlag_16BitSourceData;
409                                                                 break;
410                                                         case AUD_FORMAT_S24:
411                                                                 qtexport->audioOutputFormat.mFormatFlags = kAppleLosslessFormatFlag_24BitSourceData;
412                                                                 break;
413                                                         case AUD_FORMAT_S32:
414                                                                 qtexport->audioOutputFormat.mFormatFlags = kAppleLosslessFormatFlag_32BitSourceData;
415                                                                 break;
416                                                         case AUD_FORMAT_U8:
417                                                         case AUD_FORMAT_FLOAT32:
418                                                         case AUD_FORMAT_FLOAT64:
419                                                         default:
420                                                                 break;
421                                                 }
422                                                 break;
423                                         case kAudioFormatLinearPCM:
424                                         default:
425                                                 switch (rd->qtcodecsettings.audioBitDepth) {
426                                                         case AUD_FORMAT_U8:
427                                                                 qtexport->audioOutputFormat.mBitsPerChannel = 8;
428                                                                 qtexport->audioOutputFormat.mFormatFlags = 0;
429                                                                 break;
430                                                         case AUD_FORMAT_S24:
431                                                                 qtexport->audioOutputFormat.mBitsPerChannel = 24;
432                                                                 qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
433                                                                 break;
434                                                         case AUD_FORMAT_S32:
435                                                                 qtexport->audioOutputFormat.mBitsPerChannel = 32;
436                                                                 qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
437                                                                 break;
438                                                         case AUD_FORMAT_FLOAT32:
439                                                                 qtexport->audioOutputFormat.mBitsPerChannel = 32;
440                                                                 qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
441                                                                 break;
442                                                         case AUD_FORMAT_FLOAT64:
443                                                                 qtexport->audioOutputFormat.mBitsPerChannel = 64;
444                                                                 qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
445                                                                 break;
446                                                         case AUD_FORMAT_S16:
447                                                         default:
448                                                                 qtexport->audioOutputFormat.mBitsPerChannel = 16;
449                                                                 qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
450                                                                 break;
451                                                 }
452                                                 qtexport->audioOutputFormat.mFormatFlags |= kLinearPCMFormatFlagIsPacked;
453                                                 qtexport->audioOutputFormat.mBytesPerPacket = qtexport->audioOutputFormat.mChannelsPerFrame * (qtexport->audioOutputFormat.mBitsPerChannel / 8);
454                                                 qtexport->audioOutputFormat.mFramesPerPacket = 1;
455                                                 qtexport->audioOutputFormat.mBytesPerFrame = qtexport->audioOutputFormat.mBytesPerPacket;
456                                                 break;
457                                 }
458                                                                                                 
459                                 err = AudioFileCreateWithURL(outputFileURL, audioFileType, &qtexport->audioOutputFormat, kAudioFileFlags_EraseFile, &qtexport->audioFile);
460                                 CFRelease(outputFileURL);
461                                 
462                                 if(err)
463                                         BKE_report(reports, RPT_ERROR, "\nQuicktime: unable to create temporary audio file. Format error ?");
464                                 else {
465                                         err = AudioConverterNew(&qtexport->audioInputFormat, &qtexport->audioOutputFormat, &qtexport->audioConverter);
466                                         if (err) {
467                                                 BKE_report(reports, RPT_ERROR, "\nQuicktime: unable to initialize audio codec converter. Format error ?");
468                                                 AudioFileClose(qtexport->audioFile);
469                                                 qtexport->audioFile = NULL;
470                                                 [qtexport->audioFileName release];
471                                                 qtexport->audioFileName = nil;
472                                         } else {
473                                                 UInt32 prop,propSize;
474                                                 /* Set up codec properties */
475                                                 if (rd->qtcodecsettings.audiocodecType == kAudioFormatMPEG4AAC) { /*Lossy compressed format*/
476                                                         prop = rd->qtcodecsettings.audioBitRate;
477                                                         AudioConverterSetProperty(qtexport->audioConverter, kAudioConverterEncodeBitRate,
478                                                                                                           sizeof(prop), &prop);
479                                                         
480                                                         if (rd->qtcodecsettings.audioCodecFlags & QTAUDIO_FLAG_CODEC_ISCBR)
481                                                                 prop = kAudioCodecBitRateControlMode_Constant;
482                                                         else
483                                                                 prop = kAudioCodecBitRateControlMode_LongTermAverage;
484                                                         AudioConverterSetProperty(qtexport->audioConverter, kAudioCodecPropertyBitRateControlMode,
485                                                                                                                         sizeof(prop), &prop);
486                                                 }
487                                                 /* Conversion quality : if performance impact then offer degraded option */
488                                                 if ((rd->qtcodecsettings.audioCodecFlags & QTAUDIO_FLAG_RESAMPLE_NOHQ) == 0) {                                                  
489                                                         prop = kAudioConverterSampleRateConverterComplexity_Mastering;
490                                                         AudioConverterSetProperty(qtexport->audioConverter, kAudioConverterSampleRateConverterComplexity,
491                                                                                                           sizeof(prop), &prop);
492                                                         
493                                                         prop = kAudioConverterQuality_Max;
494                                                         AudioConverterSetProperty(qtexport->audioConverter, kAudioConverterSampleRateConverterQuality,
495                                                                                                           sizeof(prop), &prop);
496                                                 }
497                                                 
498                                                 write_cookie(qtexport->audioConverter, qtexport->audioFile);
499                                                 
500                                                 /* Allocate output buffer */
501                                                 if (qtexport->audioOutputFormat.mBytesPerPacket ==0) /* VBR */
502                                                         AudioConverterGetProperty(qtexport->audioConverter, kAudioConverterPropertyMaximumOutputPacketSize,
503                                                                                                   &propSize, &qtexport->audioCodecMaxOutputPacketSize);
504                                                 else
505                                                         qtexport->audioCodecMaxOutputPacketSize = qtexport->audioOutputFormat.mBytesPerPacket;
506                                                 
507                                                 qtexport->audioInputBuffer = MEM_mallocN(AUDIOOUTPUTBUFFERSIZE, "qt_audio_inputPacket");
508                                                 qtexport->audioOutputBuffer = MEM_mallocN(AUDIOOUTPUTBUFFERSIZE, "qt_audio_outputPacket");
509                                                 qtexport->audioOutputPktDesc = MEM_mallocN(sizeof(AudioStreamPacketDescription)*AUDIOOUTPUTBUFFERSIZE/qtexport->audioCodecMaxOutputPacketSize,
510                                                                                                                                    "qt_audio_pktdesc");
511                                         }
512                                 }
513                         }
514                         
515                         if (err == noErr) {
516                                 qtexport->videoTempFileName = [[NSString alloc] initWithCString:tmpnam(nil) 
517                                                                                                                          encoding:[NSString defaultCStringEncoding]];                   
518                                 if (qtexport->videoTempFileName)
519                                         qtexport->movie = [[QTMovie alloc] initToWritableFile:qtexport->videoTempFileName error:&error];
520
521                         }
522                 } else
523                         qtexport->movie = [[QTMovie alloc] initToWritableFile:qtexport->filename error:&error];
524                         
525                 if(qtexport->movie == nil) {
526                         BKE_report(reports, RPT_ERROR, "Unable to create quicktime movie.");
527                         success= 0;
528                         if (qtexport->filename) [qtexport->filename release];
529                         qtexport->filename = nil;
530                         if (qtexport->audioFileName) [qtexport->audioFileName release];
531                         qtexport->audioFileName = nil;
532                         if (qtexport->videoTempFileName) [qtexport->videoTempFileName release];
533                         qtexport->videoTempFileName = nil;
534                         [QTMovie exitQTKitOnThread];
535                 } else {
536                         [qtexport->movie retain];
537                         [qtexport->movie setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieEditableAttribute];
538                         [qtexport->movie setAttribute:@"Made with Blender" forKey:QTMovieCopyrightAttribute];
539                         
540                         qtexport->frameDuration = QTMakeTime(rd->frs_sec_base*1000, rd->frs_sec*1000);
541                         
542                         /* specifying the codec attributes : try to retrieve them from render data first*/
543                         if (rd->qtcodecsettings.codecType) {
544                                 qtexport->frameAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
545                                                                                          stringWithCodecType(rd->qtcodecsettings.codecType),
546                                                                                          QTAddImageCodecType,
547                                                                                          [NSNumber numberWithLong:((rd->qtcodecsettings.codecSpatialQuality)*codecLosslessQuality)/100],
548                                                                                          QTAddImageCodecQuality,
549                                                                                          nil];
550                         }
551                         else {
552                                 qtexport->frameAttributes = [NSDictionary dictionaryWithObjectsAndKeys:@"jpeg",
553                                                                                          QTAddImageCodecType,
554                                                                                          [NSNumber numberWithLong:codecHighQuality],
555                                                                                          QTAddImageCodecQuality,
556                                                                                          nil];
557                         }
558                         [qtexport->frameAttributes retain];
559                         
560                         if (qtexport->audioFile) {
561                                 /* Init audio input stream */
562                                 AUD_DeviceSpecs specs;
563
564                                 specs.channels = U.audiochannels;
565                                 specs.format = U.audioformat;
566                                 specs.rate = U.audiorate;
567                                 qtexport->audioInputDevice = AUD_openReadDevice(specs);
568                                 AUD_playDevice(qtexport->audioInputDevice, scene->sound_scene, rd->sfra * rd->frs_sec_base / rd->frs_sec);
569                                                                 
570                                 qtexport->audioOutputPktPos = 0;
571                                 qtexport->audioTotalExportedFrames = 0;
572                                 qtexport->audioTotalSavedFrames = 0;
573                                 
574                                 qtexport->audioLastFrame = (rd->efra - rd->sfra) * qtexport->audioInputFormat.mSampleRate * rd->frs_sec_base / rd->frs_sec;
575                         }
576                 }
577         }
578         
579         [pool drain];
580
581         return success;
582 }
583
584
585 int append_qt(struct RenderData *rd, int frame, int *pixels, int rectx, int recty, ReportList *reports)
586 {
587         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
588         NSBitmapImageRep *blBitmapFormatImage;
589         NSImage *frameImage;
590         OSStatus err = noErr;
591         unsigned char *from_Ptr,*to_Ptr;
592         int y,from_i,to_i;
593         
594         
595         /* Create bitmap image rep in blender format (32bit RGBA) */
596         blBitmapFormatImage = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
597                                                                                                                                   pixelsWide:rectx 
598                                                                                                                                   pixelsHigh:recty
599                                                                                                                            bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
600                                                                                                                           colorSpaceName:NSCalibratedRGBColorSpace 
601                                                                                                                                 bitmapFormat:NSAlphaNonpremultipliedBitmapFormat
602                                                                                                                                  bytesPerRow:rectx*4
603                                                                                                                                 bitsPerPixel:32];
604         if (!blBitmapFormatImage) {
605                 [pool drain];
606                 return 0;
607         }
608         
609         from_Ptr = (unsigned char*)pixels;
610         to_Ptr = (unsigned char*)[blBitmapFormatImage bitmapData];
611         for (y = 0; y < recty; y++) {
612                 to_i = (recty-y-1)*rectx;
613                 from_i = y*rectx;
614                 memcpy(to_Ptr+4*to_i, from_Ptr+4*from_i, 4*rectx);
615         }
616         
617         frameImage = [[NSImage alloc] initWithSize:NSMakeSize(rectx, recty)];
618         [frameImage addRepresentation:blBitmapFormatImage];
619         
620         /* Add the image to the movie clip */
621         [qtexport->movie addImage:frameImage
622                                   forDuration:qtexport->frameDuration
623                            withAttributes:qtexport->frameAttributes];
624
625         [blBitmapFormatImage release];
626         [frameImage release];
627         
628         
629         if (qtexport->audioFile) {
630                 UInt32 audioPacketsConverted;
631                 /* Append audio */
632                 while (qtexport->audioTotalExportedFrames < qtexport->audioLastFrame) { 
633
634                         qtexport->audioBufferList.mNumberBuffers = 1;
635                         qtexport->audioBufferList.mBuffers[0].mNumberChannels = qtexport->audioOutputFormat.mChannelsPerFrame;
636                         qtexport->audioBufferList.mBuffers[0].mDataByteSize = AUDIOOUTPUTBUFFERSIZE;
637                         qtexport->audioBufferList.mBuffers[0].mData = qtexport->audioOutputBuffer;
638                         audioPacketsConverted = AUDIOOUTPUTBUFFERSIZE / qtexport->audioCodecMaxOutputPacketSize;
639                         
640                         err = AudioConverterFillComplexBuffer(qtexport->audioConverter, AudioConverterInputCallback,
641                                                                                         NULL, &audioPacketsConverted, &qtexport->audioBufferList, qtexport->audioOutputPktDesc);
642                         if (audioPacketsConverted) {
643                                 AudioFileWritePackets(qtexport->audioFile, false, qtexport->audioBufferList.mBuffers[0].mDataByteSize,
644                                                                           qtexport->audioOutputPktDesc, qtexport->audioOutputPktPos, &audioPacketsConverted, qtexport->audioOutputBuffer);
645                                 qtexport->audioOutputPktPos += audioPacketsConverted;
646                                 
647                                 if (qtexport->audioOutputFormat.mFramesPerPacket) { 
648                                         // this is the common case: format has constant frames per packet
649                                         qtexport->audioTotalSavedFrames += (audioPacketsConverted * qtexport->audioOutputFormat.mFramesPerPacket);
650                                 } else {
651                                         unsigned int i;
652                                         // if there are variable frames per packet, then we have to do this for each packeet
653                                         for (i = 0; i < audioPacketsConverted; ++i)
654                                                 qtexport->audioTotalSavedFrames += qtexport->audioOutputPktDesc[i].mVariableFramesInPacket;
655                                 }
656                                 
657                                 
658                         }
659                         else {
660                                 //Error getting audio packets
661                                 BKE_reportf(reports, RPT_ERROR, "Unable to get further audio packets from frame %i, error = 0x%x",(int)qtexport->audioTotalExportedFrames,err);
662                                 break;
663                         }
664
665                 }
666         }
667         [pool drain];   
668
669         return 1;
670 }
671
672
673 void end_qt(void)
674 {
675         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
676         if (qtexport->movie) {
677                 
678                 if (qtexport->audioFile)
679                 {
680                         NSDictionary *dict = nil;
681                         QTMovie *audioTmpMovie = nil;
682                         NSError *error;
683                         NSFileManager *fileManager;
684                         
685                         /* Mux video and audio then save file */
686                         
687                         /* Write last frames for VBR files */
688                         if (qtexport->audioOutputFormat.mBitsPerChannel == 0) {
689                                 OSStatus err = noErr;
690                                 AudioConverterPrimeInfo primeInfo;
691                                 UInt32 primeSize = sizeof(primeInfo);
692                                 
693                                 err = AudioConverterGetProperty(qtexport->audioConverter, kAudioConverterPrimeInfo, &primeSize, &primeInfo);
694                                 if (err == noErr) {
695                                         // there's priming to write out to the file
696                                         AudioFilePacketTableInfo pti;
697                                         pti.mPrimingFrames = primeInfo.leadingFrames;
698                                         pti.mRemainderFrames = primeInfo.trailingFrames;
699                                         pti.mNumberValidFrames = qtexport->audioTotalSavedFrames - pti.mPrimingFrames - pti.mRemainderFrames;
700                                         AudioFileSetProperty(qtexport->audioFile, kAudioFilePropertyPacketTableInfo, sizeof(pti), &pti);
701                                 }
702                                 
703                         }
704                         
705                         write_cookie(qtexport->audioConverter, qtexport->audioFile);
706                         AudioConverterDispose(qtexport->audioConverter);
707                         AudioFileClose(qtexport->audioFile);
708                         AUD_closeReadDevice(qtexport->audioInputDevice);
709                         qtexport->audioFile = NULL;
710                         qtexport->audioInputDevice = NULL;
711                         MEM_freeN(qtexport->audioInputBuffer);
712                         MEM_freeN(qtexport->audioOutputBuffer);
713                         MEM_freeN(qtexport->audioOutputPktDesc);
714                         
715                         /* Reopen audio file and merge it */
716                         audioTmpMovie = [QTMovie movieWithFile:qtexport->audioFileName error:&error];
717                         if (audioTmpMovie) {
718                                 NSArray *audioTracks = [audioTmpMovie tracksOfMediaType:QTMediaTypeSound];
719                                 QTTrack *audioTrack = nil;
720                                 if( [audioTracks count] > 0 )
721                                 {
722                                         audioTrack = [audioTracks objectAtIndex:0];
723                                 }
724                         
725                                 if( audioTrack )
726                                 {
727                                         QTTimeRange totalRange;
728                                         totalRange.time = QTZeroTime;
729                                         totalRange.duration = [[audioTmpMovie attributeForKey:QTMovieDurationAttribute] QTTimeValue];
730                                         
731                                         [qtexport->movie insertSegmentOfTrack:audioTrack timeRange:totalRange atTime:QTZeroTime];
732                                 }
733                         }
734                         
735                         /* Save file */
736                         dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] 
737                                                                                            forKey:QTMovieFlatten];
738
739                         if (dict) {
740                                 [qtexport->movie writeToFile:qtexport->filename withAttributes:dict];
741                         }
742                         
743                         /* Delete temp files */
744                         fileManager = [[NSFileManager alloc] init];
745                         [fileManager removeItemAtPath:qtexport->audioFileName error:&error];
746                         [fileManager removeItemAtPath:qtexport->videoTempFileName error:&error];
747                         [fileManager release];
748                 }
749                 else {
750                         /* Flush update of the movie file */
751                         [qtexport->movie updateMovieFile];
752                         
753                         [qtexport->movie invalidate];
754                 }
755                 
756                 /* Clean up movie structure */
757                 if (qtexport->filename) [qtexport->filename release];
758                 qtexport->filename = nil;
759                 if (qtexport->audioFileName) [qtexport->audioFileName release];
760                 qtexport->audioFileName = nil;
761                 if (qtexport->videoTempFileName) [qtexport->videoTempFileName release];
762                 qtexport->videoTempFileName = nil;
763                 [qtexport->frameAttributes release];
764                 [qtexport->movie release];
765         }
766         
767         [QTMovie exitQTKitOnThread];
768
769         if(qtexport) {
770                 MEM_freeN(qtexport);
771                 qtexport = NULL;
772         }
773         [pool drain];
774 }
775
776
777 void free_qtcomponentdata(void) {
778 }
779
780 void quicktime_verify_image_type(RenderData *rd)
781 {
782         if (rd->im_format.imtype == R_IMF_IMTYPE_QUICKTIME) {
783                 if ((rd->qtcodecsettings.codecType<= 0) ||
784                         (rd->qtcodecsettings.codecSpatialQuality <0) ||
785                         (rd->qtcodecsettings.codecSpatialQuality > 100)) {
786                         
787                         rd->qtcodecsettings.codecType = kJPEGCodecType;
788                         rd->qtcodecsettings.codecSpatialQuality = (codecHighQuality*100)/codecLosslessQuality;
789                 }
790                 if ((rd->qtcodecsettings.audioSampleRate < 21000) ||
791                         (rd->qtcodecsettings.audioSampleRate > 193000)) 
792                         rd->qtcodecsettings.audioSampleRate = 48000;
793                 
794                 if (rd->qtcodecsettings.audioBitDepth == 0)
795                         rd->qtcodecsettings.audioBitDepth = AUD_FORMAT_S16;
796                 
797                 if (rd->qtcodecsettings.audioBitRate == 0)
798                         rd->qtcodecsettings.audioBitRate = 256000;
799         }
800 }
801
802 #endif /* _WIN32 || __APPLE__ */
803 #endif /* WITH_QUICKTIME */
804