2 * Code to create QuickTime Movies with Blender
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.
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.
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.
20 * The Original Code is written by Rob Haarsma (phase)
22 * Contributor(s): Stefan Gartner (sgefant)
23 * Damien Plisson 11/2009
25 * ***** END GPL LICENSE BLOCK *****
29 #if defined(_WIN32) || defined(__APPLE__)
34 #include "DNA_scene_types.h"
35 #include "DNA_userdef_types.h"
38 # include "AUD_C-API.h"
41 #include "BKE_global.h"
43 #include "BKE_scene.h"
44 #include "BKE_report.h"
46 #include "BLI_blenlib.h"
48 #include "BLO_sys_types.h"
50 #include "IMB_imbuf.h"
51 #include "IMB_imbuf_types.h"
53 #include "MEM_guardedalloc.h"
60 #import <Cocoa/Cocoa.h>
61 #import <QTKit/QTKit.h>
62 #include <AudioToolbox/AudioToolbox.h>
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
68 #include "quicktime_import.h"
69 #include "quicktime_export.h"
71 #endif /* __APPLE__ */
73 typedef struct QuicktimeExport {
79 NSDictionary *frameAttributes;
81 NSString *videoTempFileName;
83 AUD_Device *audioInputDevice;
84 AudioFileID audioFile;
85 NSString *audioFileName;
86 AudioConverterRef audioConverter;
87 AudioBufferList audioBufferList;
88 AudioStreamBasicDescription audioInputFormat, audioOutputFormat;
89 AudioStreamPacketDescription *audioOutputPktDesc;
91 char* audioInputBuffer;
92 char* audioOutputBuffer;
93 UInt32 audioCodecMaxOutputPacketSize;
94 UInt64 audioTotalExportedFrames, audioTotalSavedFrames;
95 UInt64 audioLastFrame;
96 SInt64 audioOutputPktPos;
100 static struct QuicktimeExport *qtexport;
102 #define AUDIOOUTPUTBUFFERSIZE 65536
104 #pragma mark rna helper functions
107 static QuicktimeCodecTypeDesc qtVideoCodecList[] = {
108 {kRawCodecType, 1, "Uncompressed"},
109 {k422YpCbCr8CodecType, 2, "Uncompressed 8-bit 4:2:2"},
110 {k422YpCbCr10CodecType, 3, "Uncompressed 10-bit 4:2:2"},
111 {kComponentVideoCodecType, 4, "Component Video"},
112 {kPixletCodecType, 5, "Pixlet"},
113 {kPNGCodecType, 6, "PNG"},
114 {kJPEGCodecType, 7, "JPEG"},
115 {kMotionJPEGACodecType, 8, "M-JPEG A"},
116 {kMotionJPEGBCodecType, 9, "M-JPEG B"},
117 {kDVCPALCodecType, 10, "DV PAL"},
118 {kDVCNTSCCodecType, 11, "DV/DVCPRO NTSC"},
119 {kDVCPROHD720pCodecType, 12, "DVCPRO HD 720p"},
120 {kDVCPROHD1080i50CodecType, 13, "DVCPRO HD 1080i50"},
121 {kDVCPROHD1080i60CodecType, 14, "DVCPRO HD 1080i60"},
122 {kMPEG4VisualCodecType, 15, "MPEG4"},
123 {kH263CodecType, 16, "H.263"},
124 {kH264CodecType, 17, "H.264"},
125 {kAnimationCodecType, 18, "Animation"},
128 static int qtVideoCodecCount = 18;
130 int quicktime_get_num_videocodecs()
132 return qtVideoCodecCount;
135 QuicktimeCodecTypeDesc* quicktime_get_videocodecType_desc(int indexValue) {
136 if ((indexValue>=0) && (indexValue < qtVideoCodecCount))
137 return &qtVideoCodecList[indexValue];
142 int quicktime_rnatmpvalue_from_videocodectype(int codecType)
145 for (i=0;i<qtVideoCodecCount;i++) {
146 if (qtVideoCodecList[i].codecType == codecType)
147 return qtVideoCodecList[i].rnatmpvalue;
153 int quicktime_videocodecType_from_rnatmpvalue(int rnatmpvalue)
156 for (i=0;i<qtVideoCodecCount;i++) {
157 if (qtVideoCodecList[i].rnatmpvalue == rnatmpvalue)
158 return qtVideoCodecList[i].codecType;
165 static QuicktimeCodecTypeDesc qtAudioCodecList[] = {
167 {kAudioFormatLinearPCM, 1, "LPCM"},
168 {kAudioFormatAppleLossless, 2, "Apple Lossless"},
169 {kAudioFormatMPEG4AAC, 3, "AAC"},
172 static int qtAudioCodecCount = 4;
174 int quicktime_get_num_audiocodecs()
176 return qtAudioCodecCount;
179 QuicktimeCodecTypeDesc* quicktime_get_audiocodecType_desc(int indexValue) {
180 if ((indexValue>=0) && (indexValue < qtAudioCodecCount))
181 return &qtAudioCodecList[indexValue];
186 int quicktime_rnatmpvalue_from_audiocodectype(int codecType)
189 for (i=0;i<qtAudioCodecCount;i++) {
190 if (qtAudioCodecList[i].codecType == codecType)
191 return qtAudioCodecList[i].rnatmpvalue;
197 int quicktime_audiocodecType_from_rnatmpvalue(int rnatmpvalue)
200 for (i=0;i<qtAudioCodecCount;i++) {
201 if (qtAudioCodecList[i].rnatmpvalue == rnatmpvalue)
202 return qtAudioCodecList[i].codecType;
209 static NSString *stringWithCodecType(int codecType)
213 *((int*)str) = EndianU32_NtoB(codecType);
216 return [NSString stringWithCString:str encoding:NSASCIIStringEncoding];
219 void makeqtstring (RenderData *rd, char *string)
223 strcpy(string, rd->pic);
224 BLI_path_abs(string, G.main->name);
226 BLI_make_existing_file(string);
228 if (BLI_strcasecmp(string + strlen(string) - 4, ".mov")) {
229 sprintf(txt, "%04d-%04d.mov", (rd->sfra) , (rd->efra) );
234 void filepath_qt(char *string, RenderData *rd)
236 if (string==NULL) return;
238 strcpy(string, rd->pic);
239 BLI_path_abs(string, G.main->name);
241 BLI_make_existing_file(string);
243 if (!BLI_testextensie(string, ".mov")) {
244 /* if we dont have any #'s to insert numbers into, use 4 numbers by default */
245 if (strchr(string, '#')==NULL)
246 strcat(string, "####"); /* 4 numbers */
248 BLI_path_frame_range(string, rd->sfra, rd->efra, 4);
249 strcat(string, ".mov");
254 #pragma mark audio export functions
256 static OSStatus write_cookie(AudioConverterRef converter, AudioFileID outfile)
258 // grab the cookie from the converter and write it to the file
259 UInt32 cookieSize = 0;
260 OSStatus err = AudioConverterGetPropertyInfo(converter, kAudioConverterCompressionMagicCookie, &cookieSize, NULL);
261 // if there is an error here, then the format doesn't have a cookie, so on we go
262 if (!err && cookieSize) {
263 char* cookie = malloc(cookieSize);
265 err = AudioConverterGetProperty(converter, kAudioConverterCompressionMagicCookie, &cookieSize, cookie);
268 err = AudioFileSetProperty (outfile, kAudioFilePropertyMagicCookieData, cookieSize, cookie);
269 // even though some formats have cookies, some files don't take them
276 /* AudioConverter input stream callback */
277 static OSStatus AudioConverterInputCallback(AudioConverterRef inAudioConverter,
278 UInt32* ioNumberDataPackets,
279 AudioBufferList* ioData,
280 AudioStreamPacketDescription** outDataPacketDescription,
283 if (qtexport->audioTotalExportedFrames >= qtexport->audioLastFrame) { /* EOF */
284 *ioNumberDataPackets = 0;
288 if (qtexport->audioInputFormat.mBytesPerPacket * *ioNumberDataPackets > AUDIOOUTPUTBUFFERSIZE)
289 *ioNumberDataPackets = AUDIOOUTPUTBUFFERSIZE / qtexport->audioInputFormat.mBytesPerPacket;
291 if ((qtexport->audioTotalExportedFrames + *ioNumberDataPackets) > qtexport->audioLastFrame)
292 *ioNumberDataPackets = (qtexport->audioLastFrame - qtexport->audioTotalExportedFrames) / qtexport->audioInputFormat.mFramesPerPacket;
294 qtexport->audioTotalExportedFrames += *ioNumberDataPackets;
296 AUD_readDevice(qtexport->audioInputDevice, (UInt8*)qtexport->audioInputBuffer,
297 qtexport->audioInputFormat.mFramesPerPacket * *ioNumberDataPackets);
299 ioData->mBuffers[0].mDataByteSize = qtexport->audioInputFormat.mBytesPerPacket * *ioNumberDataPackets;
300 ioData->mBuffers[0].mData = qtexport->audioInputBuffer;
301 ioData->mBuffers[0].mNumberChannels = qtexport->audioInputFormat.mChannelsPerFrame;
307 #pragma mark export functions
309 int start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty, ReportList *reports)
311 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
317 if(qtexport == NULL) qtexport = MEM_callocN(sizeof(QuicktimeExport), "QuicktimeExport");
319 [QTMovie enterQTKitOnThread];
321 /* Check first if the QuickTime 7.2.1 initToWritableFile: method is available */
322 if ([[[[QTMovie alloc] init] autorelease] respondsToSelector:@selector(initToWritableFile:error:)] != YES) {
323 BKE_report(reports, RPT_ERROR, "\nUnable to create quicktime movie, need Quicktime rev 7.2.1 or later");
327 makeqtstring(rd, name);
328 qtexport->filename = [[NSString alloc] initWithCString:name
329 encoding:[NSString defaultCStringEncoding]];
330 qtexport->movie = nil;
331 qtexport->audioFile = NULL;
333 if (rd->qtcodecsettings.audiocodecType) {
334 // generate a name for our video & audio files
335 /* Init audio file */
336 CFURLRef outputFileURL;
338 AudioFileTypeID audioFileType;
340 switch (rd->qtcodecsettings.audiocodecType) {
341 case kAudioFormatLinearPCM:
342 audioFileType = kAudioFileWAVEType;
343 strcpy(extension,".wav");
345 case kAudioFormatMPEG4AAC:
346 case kAudioFormatAppleLossless:
347 audioFileType = kAudioFileM4AType;
348 strcpy(extension, ".m4a");
351 audioFileType = kAudioFileAIFFType;
352 strcpy(extension,".aiff");
357 strcat(name, extension);
358 outputFileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,(UInt8*) name, strlen(name), false);
362 qtexport->audioFileName = [[NSString alloc] initWithCString:name
363 encoding:[NSString defaultCStringEncoding]];
365 qtexport->audioInputFormat.mSampleRate = U.audiorate;
366 qtexport->audioInputFormat.mFormatID = kAudioFormatLinearPCM;
367 qtexport->audioInputFormat.mChannelsPerFrame = U.audiochannels;
368 switch (U.audioformat) {
370 qtexport->audioInputFormat.mBitsPerChannel = 8;
371 qtexport->audioInputFormat.mFormatFlags = 0;
374 qtexport->audioInputFormat.mBitsPerChannel = 24;
375 qtexport->audioInputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
378 qtexport->audioInputFormat.mBitsPerChannel = 32;
379 qtexport->audioInputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
381 case AUD_FORMAT_FLOAT32:
382 qtexport->audioInputFormat.mBitsPerChannel = 32;
383 qtexport->audioInputFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
385 case AUD_FORMAT_FLOAT64:
386 qtexport->audioInputFormat.mBitsPerChannel = 64;
387 qtexport->audioInputFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
391 qtexport->audioInputFormat.mBitsPerChannel = 16;
392 qtexport->audioInputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
395 qtexport->audioInputFormat.mBytesPerFrame = qtexport->audioInputFormat.mChannelsPerFrame * qtexport->audioInputFormat.mBitsPerChannel / 8;
396 qtexport->audioInputFormat.mFramesPerPacket = 1; /*If not ==1, then need to check input callback for "rounding" issues"*/
397 qtexport->audioInputFormat.mBytesPerPacket = qtexport->audioInputFormat.mBytesPerFrame;
398 qtexport->audioInputFormat.mFormatFlags |= kLinearPCMFormatFlagIsPacked;
402 qtexport->audioOutputFormat.mFormatID = rd->qtcodecsettings.audiocodecType;
403 //TODO: set audio channels
404 qtexport->audioOutputFormat.mChannelsPerFrame = 2;
405 qtexport->audioOutputFormat.mSampleRate = rd->qtcodecsettings.audioSampleRate;
407 /* Default value for compressed formats, overriden after if not the case */
408 qtexport->audioOutputFormat.mFramesPerPacket = 0;
409 qtexport->audioOutputFormat.mBytesPerFrame = 0;
410 qtexport->audioOutputFormat.mBytesPerPacket = 0;
411 qtexport->audioOutputFormat.mBitsPerChannel = 0;
413 switch (rd->qtcodecsettings.audiocodecType) {
414 case kAudioFormatMPEG4AAC:
415 qtexport->audioOutputFormat.mFormatFlags = kMPEG4Object_AAC_Main;
416 /* AAC codec does not handle sample rates above 48kHz, force this limit instead of getting an error afterwards */
417 if (qtexport->audioOutputFormat.mSampleRate > 48000) qtexport->audioOutputFormat.mSampleRate = 48000;
419 case kAudioFormatAppleLossless:
420 switch (U.audioformat) {
422 qtexport->audioOutputFormat.mFormatFlags = kAppleLosslessFormatFlag_16BitSourceData;
425 qtexport->audioOutputFormat.mFormatFlags = kAppleLosslessFormatFlag_24BitSourceData;
428 qtexport->audioOutputFormat.mFormatFlags = kAppleLosslessFormatFlag_32BitSourceData;
431 case AUD_FORMAT_FLOAT32:
432 case AUD_FORMAT_FLOAT64:
437 case kAudioFormatLinearPCM:
439 switch (rd->qtcodecsettings.audioBitDepth) {
441 qtexport->audioOutputFormat.mBitsPerChannel = 8;
442 qtexport->audioOutputFormat.mFormatFlags = 0;
445 qtexport->audioOutputFormat.mBitsPerChannel = 24;
446 qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
449 qtexport->audioOutputFormat.mBitsPerChannel = 32;
450 qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
452 case AUD_FORMAT_FLOAT32:
453 qtexport->audioOutputFormat.mBitsPerChannel = 32;
454 qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
456 case AUD_FORMAT_FLOAT64:
457 qtexport->audioOutputFormat.mBitsPerChannel = 64;
458 qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
462 qtexport->audioOutputFormat.mBitsPerChannel = 16;
463 qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
466 qtexport->audioOutputFormat.mFormatFlags |= kLinearPCMFormatFlagIsPacked;
467 qtexport->audioOutputFormat.mBytesPerPacket = qtexport->audioOutputFormat.mChannelsPerFrame * (qtexport->audioOutputFormat.mBitsPerChannel / 8);
468 qtexport->audioOutputFormat.mFramesPerPacket = 1;
469 qtexport->audioOutputFormat.mBytesPerFrame = qtexport->audioOutputFormat.mBytesPerPacket;
473 err = AudioFileCreateWithURL(outputFileURL, audioFileType, &qtexport->audioOutputFormat, kAudioFileFlags_EraseFile, &qtexport->audioFile);
474 CFRelease(outputFileURL);
477 BKE_report(reports, RPT_ERROR, "\nQuicktime: unable to create temporary audio file. Format error ?");
479 err = AudioConverterNew(&qtexport->audioInputFormat, &qtexport->audioOutputFormat, &qtexport->audioConverter);
481 BKE_report(reports, RPT_ERROR, "\nQuicktime: unable to initialize audio codec converter. Format error ?");
482 AudioFileClose(qtexport->audioFile);
483 qtexport->audioFile = NULL;
484 [qtexport->audioFileName release];
485 qtexport->audioFileName = nil;
487 UInt32 prop,propSize;
488 /* Set up codec properties */
489 if (rd->qtcodecsettings.audiocodecType == kAudioFormatMPEG4AAC) { /*Lossy compressed format*/
490 prop = rd->qtcodecsettings.audioBitRate;
491 AudioConverterSetProperty(qtexport->audioConverter, kAudioConverterEncodeBitRate,
492 sizeof(prop), &prop);
494 if (rd->qtcodecsettings.audioCodecFlags & QTAUDIO_FLAG_CODEC_ISCBR)
495 prop = kAudioCodecBitRateControlMode_Constant;
497 prop = kAudioCodecBitRateControlMode_LongTermAverage;
498 AudioConverterSetProperty(qtexport->audioConverter, kAudioCodecPropertyBitRateControlMode,
499 sizeof(prop), &prop);
501 /* Conversion quality : if performance impact then offer degraded option */
502 if ((rd->qtcodecsettings.audioCodecFlags & QTAUDIO_FLAG_RESAMPLE_NOHQ) == 0) {
503 prop = kAudioConverterSampleRateConverterComplexity_Mastering;
504 AudioConverterSetProperty(qtexport->audioConverter, kAudioConverterSampleRateConverterComplexity,
505 sizeof(prop), &prop);
507 prop = kAudioConverterQuality_Max;
508 AudioConverterSetProperty(qtexport->audioConverter, kAudioConverterSampleRateConverterQuality,
509 sizeof(prop), &prop);
512 write_cookie(qtexport->audioConverter, qtexport->audioFile);
514 /* Allocate output buffer */
515 if (qtexport->audioOutputFormat.mBytesPerPacket ==0) /* VBR */
516 AudioConverterGetProperty(qtexport->audioConverter, kAudioConverterPropertyMaximumOutputPacketSize,
517 &propSize, &qtexport->audioCodecMaxOutputPacketSize);
519 qtexport->audioCodecMaxOutputPacketSize = qtexport->audioOutputFormat.mBytesPerPacket;
521 qtexport->audioInputBuffer = MEM_mallocN(AUDIOOUTPUTBUFFERSIZE, "qt_audio_inputPacket");
522 qtexport->audioOutputBuffer = MEM_mallocN(AUDIOOUTPUTBUFFERSIZE, "qt_audio_outputPacket");
523 qtexport->audioOutputPktDesc = MEM_mallocN(sizeof(AudioStreamPacketDescription)*AUDIOOUTPUTBUFFERSIZE/qtexport->audioCodecMaxOutputPacketSize,
530 qtexport->videoTempFileName = [[NSString alloc] initWithCString:tmpnam(nil)
531 encoding:[NSString defaultCStringEncoding]];
532 if (qtexport->videoTempFileName)
533 qtexport->movie = [[QTMovie alloc] initToWritableFile:qtexport->videoTempFileName error:&error];
537 qtexport->movie = [[QTMovie alloc] initToWritableFile:qtexport->filename error:&error];
539 if(qtexport->movie == nil) {
540 BKE_report(reports, RPT_ERROR, "Unable to create quicktime movie.");
542 if (qtexport->filename) [qtexport->filename release];
543 qtexport->filename = nil;
544 if (qtexport->audioFileName) [qtexport->audioFileName release];
545 qtexport->audioFileName = nil;
546 if (qtexport->videoTempFileName) [qtexport->videoTempFileName release];
547 qtexport->videoTempFileName = nil;
548 [QTMovie exitQTKitOnThread];
550 [qtexport->movie retain];
551 [qtexport->movie setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieEditableAttribute];
552 [qtexport->movie setAttribute:@"Made with Blender" forKey:QTMovieCopyrightAttribute];
554 qtexport->frameDuration = QTMakeTime(rd->frs_sec_base*1000, rd->frs_sec*1000);
556 /* specifying the codec attributes : try to retrieve them from render data first*/
557 if (rd->qtcodecsettings.codecType) {
558 qtexport->frameAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
559 stringWithCodecType(rd->qtcodecsettings.codecType),
561 [NSNumber numberWithLong:((rd->qtcodecsettings.codecSpatialQuality)*codecLosslessQuality)/100],
562 QTAddImageCodecQuality,
566 qtexport->frameAttributes = [NSDictionary dictionaryWithObjectsAndKeys:@"jpeg",
568 [NSNumber numberWithLong:codecHighQuality],
569 QTAddImageCodecQuality,
572 [qtexport->frameAttributes retain];
574 if (qtexport->audioFile) {
575 /* Init audio input stream */
576 AUD_DeviceSpecs specs;
578 specs.channels = U.audiochannels;
579 specs.format = U.audioformat;
580 specs.rate = U.audiorate;
581 qtexport->audioInputDevice = AUD_openReadDevice(specs);
582 AUD_playDevice(qtexport->audioInputDevice, scene->sound_scene, rd->sfra * rd->frs_sec_base / rd->frs_sec);
584 qtexport->audioOutputPktPos = 0;
585 qtexport->audioTotalExportedFrames = 0;
586 qtexport->audioTotalSavedFrames = 0;
588 qtexport->audioLastFrame = (rd->efra - rd->sfra) * qtexport->audioInputFormat.mSampleRate * rd->frs_sec_base / rd->frs_sec;
599 int append_qt(struct RenderData *rd, int frame, int *pixels, int rectx, int recty, ReportList *reports)
601 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
602 NSBitmapImageRep *blBitmapFormatImage;
604 OSStatus err = noErr;
605 unsigned char *from_Ptr,*to_Ptr;
607 BOOL alpha = (rd->im_format.planes == R_IMF_PLANES_RGBA)? YES: NO;
610 /* Create bitmap image rep in blender format (32bit RGBA) */
611 blBitmapFormatImage = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
614 bitsPerSample:8 samplesPerPixel:4 hasAlpha:alpha isPlanar:NO
615 colorSpaceName:NSCalibratedRGBColorSpace
616 bitmapFormat:NSAlphaNonpremultipliedBitmapFormat
619 if (!blBitmapFormatImage) {
624 from_Ptr = (unsigned char*)pixels;
625 to_Ptr = (unsigned char*)[blBitmapFormatImage bitmapData];
626 for (y = 0; y < recty; y++) {
627 to_i = (recty-y-1)*rectx;
629 memcpy(to_Ptr+4*to_i, from_Ptr+4*from_i, 4*rectx);
632 frameImage = [[NSImage alloc] initWithSize:NSMakeSize(rectx, recty)];
633 [frameImage addRepresentation:blBitmapFormatImage];
635 /* Add the image to the movie clip */
636 [qtexport->movie addImage:frameImage
637 forDuration:qtexport->frameDuration
638 withAttributes:qtexport->frameAttributes];
640 [blBitmapFormatImage release];
641 [frameImage release];
644 if (qtexport->audioFile) {
645 UInt32 audioPacketsConverted;
647 while (qtexport->audioTotalExportedFrames < qtexport->audioLastFrame) {
649 qtexport->audioBufferList.mNumberBuffers = 1;
650 qtexport->audioBufferList.mBuffers[0].mNumberChannels = qtexport->audioOutputFormat.mChannelsPerFrame;
651 qtexport->audioBufferList.mBuffers[0].mDataByteSize = AUDIOOUTPUTBUFFERSIZE;
652 qtexport->audioBufferList.mBuffers[0].mData = qtexport->audioOutputBuffer;
653 audioPacketsConverted = AUDIOOUTPUTBUFFERSIZE / qtexport->audioCodecMaxOutputPacketSize;
655 err = AudioConverterFillComplexBuffer(qtexport->audioConverter, AudioConverterInputCallback,
656 NULL, &audioPacketsConverted, &qtexport->audioBufferList, qtexport->audioOutputPktDesc);
657 if (audioPacketsConverted) {
658 AudioFileWritePackets(qtexport->audioFile, false, qtexport->audioBufferList.mBuffers[0].mDataByteSize,
659 qtexport->audioOutputPktDesc, qtexport->audioOutputPktPos, &audioPacketsConverted, qtexport->audioOutputBuffer);
660 qtexport->audioOutputPktPos += audioPacketsConverted;
662 if (qtexport->audioOutputFormat.mFramesPerPacket) {
663 // this is the common case: format has constant frames per packet
664 qtexport->audioTotalSavedFrames += (audioPacketsConverted * qtexport->audioOutputFormat.mFramesPerPacket);
667 // if there are variable frames per packet, then we have to do this for each packeet
668 for (i = 0; i < audioPacketsConverted; ++i)
669 qtexport->audioTotalSavedFrames += qtexport->audioOutputPktDesc[i].mVariableFramesInPacket;
675 //Error getting audio packets
676 BKE_reportf(reports, RPT_ERROR, "Unable to get further audio packets from frame %i, error = 0x%x",(int)qtexport->audioTotalExportedFrames,err);
690 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
691 if (qtexport->movie) {
693 if (qtexport->audioFile)
695 NSDictionary *dict = nil;
696 QTMovie *audioTmpMovie = nil;
698 NSFileManager *fileManager;
700 /* Mux video and audio then save file */
702 /* Write last frames for VBR files */
703 if (qtexport->audioOutputFormat.mBitsPerChannel == 0) {
704 OSStatus err = noErr;
705 AudioConverterPrimeInfo primeInfo;
706 UInt32 primeSize = sizeof(primeInfo);
708 err = AudioConverterGetProperty(qtexport->audioConverter, kAudioConverterPrimeInfo, &primeSize, &primeInfo);
710 // there's priming to write out to the file
711 AudioFilePacketTableInfo pti;
712 pti.mPrimingFrames = primeInfo.leadingFrames;
713 pti.mRemainderFrames = primeInfo.trailingFrames;
714 pti.mNumberValidFrames = qtexport->audioTotalSavedFrames - pti.mPrimingFrames - pti.mRemainderFrames;
715 AudioFileSetProperty(qtexport->audioFile, kAudioFilePropertyPacketTableInfo, sizeof(pti), &pti);
720 write_cookie(qtexport->audioConverter, qtexport->audioFile);
721 AudioConverterDispose(qtexport->audioConverter);
722 AudioFileClose(qtexport->audioFile);
723 AUD_closeReadDevice(qtexport->audioInputDevice);
724 qtexport->audioFile = NULL;
725 qtexport->audioInputDevice = NULL;
726 MEM_freeN(qtexport->audioInputBuffer);
727 MEM_freeN(qtexport->audioOutputBuffer);
728 MEM_freeN(qtexport->audioOutputPktDesc);
730 /* Reopen audio file and merge it */
731 audioTmpMovie = [QTMovie movieWithFile:qtexport->audioFileName error:&error];
733 NSArray *audioTracks = [audioTmpMovie tracksOfMediaType:QTMediaTypeSound];
734 QTTrack *audioTrack = nil;
735 if( [audioTracks count] > 0 )
737 audioTrack = [audioTracks objectAtIndex:0];
742 QTTimeRange totalRange;
743 totalRange.time = QTZeroTime;
744 totalRange.duration = [[audioTmpMovie attributeForKey:QTMovieDurationAttribute] QTTimeValue];
746 [qtexport->movie insertSegmentOfTrack:audioTrack timeRange:totalRange atTime:QTZeroTime];
751 dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]
752 forKey:QTMovieFlatten];
755 [qtexport->movie writeToFile:qtexport->filename withAttributes:dict];
758 /* Delete temp files */
759 fileManager = [[NSFileManager alloc] init];
760 [fileManager removeItemAtPath:qtexport->audioFileName error:&error];
761 [fileManager removeItemAtPath:qtexport->videoTempFileName error:&error];
762 [fileManager release];
765 /* Flush update of the movie file */
766 [qtexport->movie updateMovieFile];
768 [qtexport->movie invalidate];
771 /* Clean up movie structure */
772 if (qtexport->filename) [qtexport->filename release];
773 qtexport->filename = nil;
774 if (qtexport->audioFileName) [qtexport->audioFileName release];
775 qtexport->audioFileName = nil;
776 if (qtexport->videoTempFileName) [qtexport->videoTempFileName release];
777 qtexport->videoTempFileName = nil;
778 [qtexport->frameAttributes release];
779 [qtexport->movie release];
782 [QTMovie exitQTKitOnThread];
792 void free_qtcomponentdata(void)
796 void quicktime_verify_image_type(RenderData *rd, ImageFormatData *imf)
798 if (imf->imtype == R_IMF_IMTYPE_QUICKTIME) {
799 if ((rd->qtcodecsettings.codecType<= 0) ||
800 (rd->qtcodecsettings.codecSpatialQuality <0) ||
801 (rd->qtcodecsettings.codecSpatialQuality > 100)) {
803 rd->qtcodecsettings.codecType = kJPEGCodecType;
804 rd->qtcodecsettings.codecSpatialQuality = (codecHighQuality*100)/codecLosslessQuality;
806 if ((rd->qtcodecsettings.audioSampleRate < 21000) ||
807 (rd->qtcodecsettings.audioSampleRate > 193000))
808 rd->qtcodecsettings.audioSampleRate = 48000;
810 if (rd->qtcodecsettings.audioBitDepth == 0)
811 rd->qtcodecsettings.audioBitDepth = AUD_FORMAT_S16;
813 if (rd->qtcodecsettings.audioBitRate == 0)
814 rd->qtcodecsettings.audioBitRate = 256000;
818 #endif /* _WIN32 || __APPLE__ */
819 #endif /* WITH_QUICKTIME */