Blender V2.61 - r43446
|
00001 /* 00002 * Code to create QuickTime Movies with Blender 00003 * 00004 * ***** BEGIN GPL LICENSE BLOCK ***** 00005 * This program is free software; you can redistribute it and/or 00006 * modify it under the terms of the GNU General Public License 00007 * as published by the Free Software Foundation; either version 2 00008 * of the License, or (at your option) any later version. 00009 * 00010 * This program is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 * GNU General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU General Public License 00016 * along with this program; if not, write to the Free Software Foundation, 00017 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00018 * 00019 * 00020 * The Original Code is written by Rob Haarsma (phase) 00021 * 00022 * Contributor(s): Stefan Gartner (sgefant) 00023 * Damien Plisson 11/2009 00024 * 00025 * ***** END GPL LICENSE BLOCK ***** 00026 */ 00027 00028 #ifdef WITH_QUICKTIME 00029 #if defined(_WIN32) || defined(__APPLE__) 00030 00031 #include <stdio.h> 00032 #include <string.h> 00033 00034 #include "DNA_scene_types.h" 00035 #include "DNA_userdef_types.h" 00036 00037 #ifdef WITH_AUDASPACE 00038 # include "AUD_C-API.h" 00039 #endif 00040 00041 #include "BKE_global.h" 00042 #include "BKE_main.h" 00043 #include "BKE_scene.h" 00044 #include "BKE_report.h" 00045 00046 #include "BLI_blenlib.h" 00047 00048 #include "BLO_sys_types.h" 00049 00050 #include "IMB_imbuf.h" 00051 #include "IMB_imbuf_types.h" 00052 00053 #include "MEM_guardedalloc.h" 00054 00055 #ifdef __APPLE__ 00056 /* evil */ 00057 #ifndef __AIFF__ 00058 #define __AIFF__ 00059 #endif 00060 #import <Cocoa/Cocoa.h> 00061 #import <QTKit/QTKit.h> 00062 #include <AudioToolbox/AudioToolbox.h> 00063 00064 #if (MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4) || !__LP64__ 00065 #error 64 bit build & OSX 10.5 minimum are needed for QTKit 00066 #endif 00067 00068 #include "quicktime_import.h" 00069 #include "quicktime_export.h" 00070 00071 #endif /* __APPLE__ */ 00072 00073 typedef struct QuicktimeExport { 00074 QTMovie *movie; 00075 00076 NSString *filename; 00077 00078 QTTime frameDuration; 00079 NSDictionary *frameAttributes; 00080 00081 NSString *videoTempFileName; 00082 /* Audio section */ 00083 AUD_Device *audioInputDevice; 00084 AudioFileID audioFile; 00085 NSString *audioFileName; 00086 AudioConverterRef audioConverter; 00087 AudioBufferList audioBufferList; 00088 AudioStreamBasicDescription audioInputFormat, audioOutputFormat; 00089 AudioStreamPacketDescription *audioOutputPktDesc; 00090 SInt64 audioFilePos; 00091 char* audioInputBuffer; 00092 char* audioOutputBuffer; 00093 UInt32 audioCodecMaxOutputPacketSize; 00094 UInt64 audioTotalExportedFrames, audioTotalSavedFrames; 00095 UInt64 audioLastFrame; 00096 SInt64 audioOutputPktPos; 00097 00098 } QuicktimeExport; 00099 00100 static struct QuicktimeExport *qtexport; 00101 00102 #define AUDIOOUTPUTBUFFERSIZE 65536 00103 00104 #pragma mark rna helper functions 00105 00106 /* Video codec */ 00107 static QuicktimeCodecTypeDesc qtVideoCodecList[] = { 00108 {kRawCodecType, 1, "Uncompressed"}, 00109 {k422YpCbCr8CodecType, 2, "Uncompressed 8-bit 4:2:2"}, 00110 {k422YpCbCr10CodecType, 3, "Uncompressed 10-bit 4:2:2"}, 00111 {kComponentVideoCodecType, 4, "Component Video"}, 00112 {kPixletCodecType, 5, "Pixlet"}, 00113 {kPNGCodecType, 6, "PNG"}, 00114 {kJPEGCodecType, 7, "JPEG"}, 00115 {kMotionJPEGACodecType, 8, "M-JPEG A"}, 00116 {kMotionJPEGBCodecType, 9, "M-JPEG B"}, 00117 {kDVCPALCodecType, 10, "DV PAL"}, 00118 {kDVCNTSCCodecType, 11, "DV/DVCPRO NTSC"}, 00119 {kDVCPROHD720pCodecType, 12, "DVCPRO HD 720p"}, 00120 {kDVCPROHD1080i50CodecType, 13, "DVCPRO HD 1080i50"}, 00121 {kDVCPROHD1080i60CodecType, 14, "DVCPRO HD 1080i60"}, 00122 {kMPEG4VisualCodecType, 15, "MPEG4"}, 00123 {kH263CodecType, 16, "H.263"}, 00124 {kH264CodecType, 17, "H.264"}, 00125 {kAnimationCodecType, 18, "Animation"}, 00126 {0,0,NULL}}; 00127 00128 static int qtVideoCodecCount = 18; 00129 00130 int quicktime_get_num_videocodecs() 00131 { 00132 return qtVideoCodecCount; 00133 } 00134 00135 QuicktimeCodecTypeDesc* quicktime_get_videocodecType_desc(int indexValue) { 00136 if ((indexValue>=0) && (indexValue < qtVideoCodecCount)) 00137 return &qtVideoCodecList[indexValue]; 00138 else 00139 return NULL; 00140 } 00141 00142 int quicktime_rnatmpvalue_from_videocodectype(int codecType) 00143 { 00144 int i; 00145 for (i=0;i<qtVideoCodecCount;i++) { 00146 if (qtVideoCodecList[i].codecType == codecType) 00147 return qtVideoCodecList[i].rnatmpvalue; 00148 } 00149 00150 return 0; 00151 } 00152 00153 int quicktime_videocodecType_from_rnatmpvalue(int rnatmpvalue) 00154 { 00155 int i; 00156 for (i=0;i<qtVideoCodecCount;i++) { 00157 if (qtVideoCodecList[i].rnatmpvalue == rnatmpvalue) 00158 return qtVideoCodecList[i].codecType; 00159 } 00160 00161 return 0; 00162 } 00163 00164 /* Audio codec */ 00165 static QuicktimeCodecTypeDesc qtAudioCodecList[] = { 00166 {0, 0, "No audio"}, 00167 {kAudioFormatLinearPCM, 1, "LPCM"}, 00168 {kAudioFormatAppleLossless, 2, "Apple Lossless"}, 00169 {kAudioFormatMPEG4AAC, 3, "AAC"}, 00170 {0,0,NULL}}; 00171 00172 static int qtAudioCodecCount = 4; 00173 00174 int quicktime_get_num_audiocodecs() 00175 { 00176 return qtAudioCodecCount; 00177 } 00178 00179 QuicktimeCodecTypeDesc* quicktime_get_audiocodecType_desc(int indexValue) { 00180 if ((indexValue>=0) && (indexValue < qtAudioCodecCount)) 00181 return &qtAudioCodecList[indexValue]; 00182 else 00183 return NULL; 00184 } 00185 00186 int quicktime_rnatmpvalue_from_audiocodectype(int codecType) 00187 { 00188 int i; 00189 for (i=0;i<qtAudioCodecCount;i++) { 00190 if (qtAudioCodecList[i].codecType == codecType) 00191 return qtAudioCodecList[i].rnatmpvalue; 00192 } 00193 00194 return 0; 00195 } 00196 00197 int quicktime_audiocodecType_from_rnatmpvalue(int rnatmpvalue) 00198 { 00199 int i; 00200 for (i=0;i<qtAudioCodecCount;i++) { 00201 if (qtAudioCodecList[i].rnatmpvalue == rnatmpvalue) 00202 return qtAudioCodecList[i].codecType; 00203 } 00204 00205 return 0; 00206 } 00207 00208 00209 static NSString *stringWithCodecType(int codecType) 00210 { 00211 char str[5]; 00212 00213 *((int*)str) = EndianU32_NtoB(codecType); 00214 str[4] = 0; 00215 00216 return [NSString stringWithCString:str encoding:NSASCIIStringEncoding]; 00217 } 00218 00219 void makeqtstring (RenderData *rd, char *string) 00220 { 00221 char txt[64]; 00222 00223 strcpy(string, rd->pic); 00224 BLI_path_abs(string, G.main->name); 00225 00226 BLI_make_existing_file(string); 00227 00228 if (BLI_strcasecmp(string + strlen(string) - 4, ".mov")) { 00229 sprintf(txt, "%04d-%04d.mov", (rd->sfra) , (rd->efra) ); 00230 strcat(string, txt); 00231 } 00232 } 00233 00234 void filepath_qt(char *string, RenderData *rd) 00235 { 00236 if (string==NULL) return; 00237 00238 strcpy(string, rd->pic); 00239 BLI_path_abs(string, G.main->name); 00240 00241 BLI_make_existing_file(string); 00242 00243 if (!BLI_testextensie(string, ".mov")) { 00244 /* if we dont have any #'s to insert numbers into, use 4 numbers by default */ 00245 if (strchr(string, '#')==NULL) 00246 strcat(string, "####"); /* 4 numbers */ 00247 00248 BLI_path_frame_range(string, rd->sfra, rd->efra, 4); 00249 strcat(string, ".mov"); 00250 } 00251 } 00252 00253 00254 #pragma mark audio export functions 00255 00256 static OSStatus write_cookie(AudioConverterRef converter, AudioFileID outfile) 00257 { 00258 // grab the cookie from the converter and write it to the file 00259 UInt32 cookieSize = 0; 00260 OSStatus err = AudioConverterGetPropertyInfo(converter, kAudioConverterCompressionMagicCookie, &cookieSize, NULL); 00261 // if there is an error here, then the format doesn't have a cookie, so on we go 00262 if (!err && cookieSize) { 00263 char* cookie = malloc(cookieSize); 00264 00265 err = AudioConverterGetProperty(converter, kAudioConverterCompressionMagicCookie, &cookieSize, cookie); 00266 00267 if (!err) 00268 err = AudioFileSetProperty (outfile, kAudioFilePropertyMagicCookieData, cookieSize, cookie); 00269 // even though some formats have cookies, some files don't take them 00270 00271 free(cookie); 00272 } 00273 return err; 00274 } 00275 00276 /* AudioConverter input stream callback */ 00277 static OSStatus AudioConverterInputCallback(AudioConverterRef inAudioConverter, 00278 UInt32* ioNumberDataPackets, 00279 AudioBufferList* ioData, 00280 AudioStreamPacketDescription** outDataPacketDescription, 00281 void* inUserData) 00282 { 00283 if (qtexport->audioTotalExportedFrames >= qtexport->audioLastFrame) { /* EOF */ 00284 *ioNumberDataPackets = 0; 00285 return noErr; 00286 } 00287 00288 if (qtexport->audioInputFormat.mBytesPerPacket * *ioNumberDataPackets > AUDIOOUTPUTBUFFERSIZE) 00289 *ioNumberDataPackets = AUDIOOUTPUTBUFFERSIZE / qtexport->audioInputFormat.mBytesPerPacket; 00290 00291 if ((qtexport->audioTotalExportedFrames + *ioNumberDataPackets) > qtexport->audioLastFrame) 00292 *ioNumberDataPackets = (qtexport->audioLastFrame - qtexport->audioTotalExportedFrames) / qtexport->audioInputFormat.mFramesPerPacket; 00293 00294 qtexport->audioTotalExportedFrames += *ioNumberDataPackets; 00295 00296 AUD_readDevice(qtexport->audioInputDevice, (UInt8*)qtexport->audioInputBuffer, 00297 qtexport->audioInputFormat.mFramesPerPacket * *ioNumberDataPackets); 00298 00299 ioData->mBuffers[0].mDataByteSize = qtexport->audioInputFormat.mBytesPerPacket * *ioNumberDataPackets; 00300 ioData->mBuffers[0].mData = qtexport->audioInputBuffer; 00301 ioData->mBuffers[0].mNumberChannels = qtexport->audioInputFormat.mChannelsPerFrame; 00302 00303 return noErr; 00304 } 00305 00306 00307 #pragma mark export functions 00308 00309 int start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty, ReportList *reports) 00310 { 00311 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 00312 NSError *error; 00313 char name[1024]; 00314 int success= 1; 00315 OSStatus err=noErr; 00316 00317 if(qtexport == NULL) qtexport = MEM_callocN(sizeof(QuicktimeExport), "QuicktimeExport"); 00318 00319 [QTMovie enterQTKitOnThread]; 00320 00321 /* Check first if the QuickTime 7.2.1 initToWritableFile: method is available */ 00322 if ([[[[QTMovie alloc] init] autorelease] respondsToSelector:@selector(initToWritableFile:error:)] != YES) { 00323 BKE_report(reports, RPT_ERROR, "\nUnable to create quicktime movie, need Quicktime rev 7.2.1 or later"); 00324 success= 0; 00325 } 00326 else { 00327 makeqtstring(rd, name); 00328 qtexport->filename = [[NSString alloc] initWithCString:name 00329 encoding:[NSString defaultCStringEncoding]]; 00330 qtexport->movie = nil; 00331 qtexport->audioFile = NULL; 00332 00333 if (rd->qtcodecsettings.audiocodecType) { 00334 // generate a name for our video & audio files 00335 /* Init audio file */ 00336 CFURLRef outputFileURL; 00337 char extension[32]; 00338 AudioFileTypeID audioFileType; 00339 00340 switch (rd->qtcodecsettings.audiocodecType) { 00341 case kAudioFormatLinearPCM: 00342 audioFileType = kAudioFileWAVEType; 00343 strcpy(extension,".wav"); 00344 break; 00345 case kAudioFormatMPEG4AAC: 00346 case kAudioFormatAppleLossless: 00347 audioFileType = kAudioFileM4AType; 00348 strcpy(extension, ".m4a"); 00349 break; 00350 default: 00351 audioFileType = kAudioFileAIFFType; 00352 strcpy(extension,".aiff"); 00353 break; 00354 } 00355 00356 tmpnam(name); 00357 strcat(name, extension); 00358 outputFileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,(UInt8*) name, strlen(name), false); 00359 00360 if (outputFileURL) { 00361 00362 qtexport->audioFileName = [[NSString alloc] initWithCString:name 00363 encoding:[NSString defaultCStringEncoding]]; 00364 00365 qtexport->audioInputFormat.mSampleRate = U.audiorate; 00366 qtexport->audioInputFormat.mFormatID = kAudioFormatLinearPCM; 00367 qtexport->audioInputFormat.mChannelsPerFrame = U.audiochannels; 00368 switch (U.audioformat) { 00369 case AUD_FORMAT_U8: 00370 qtexport->audioInputFormat.mBitsPerChannel = 8; 00371 qtexport->audioInputFormat.mFormatFlags = 0; 00372 break; 00373 case AUD_FORMAT_S24: 00374 qtexport->audioInputFormat.mBitsPerChannel = 24; 00375 qtexport->audioInputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; 00376 break; 00377 case AUD_FORMAT_S32: 00378 qtexport->audioInputFormat.mBitsPerChannel = 32; 00379 qtexport->audioInputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; 00380 break; 00381 case AUD_FORMAT_FLOAT32: 00382 qtexport->audioInputFormat.mBitsPerChannel = 32; 00383 qtexport->audioInputFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat; 00384 break; 00385 case AUD_FORMAT_FLOAT64: 00386 qtexport->audioInputFormat.mBitsPerChannel = 64; 00387 qtexport->audioInputFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat; 00388 break; 00389 case AUD_FORMAT_S16: 00390 default: 00391 qtexport->audioInputFormat.mBitsPerChannel = 16; 00392 qtexport->audioInputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; 00393 break; 00394 } 00395 qtexport->audioInputFormat.mBytesPerFrame = qtexport->audioInputFormat.mChannelsPerFrame * qtexport->audioInputFormat.mBitsPerChannel / 8; 00396 qtexport->audioInputFormat.mFramesPerPacket = 1; /*If not ==1, then need to check input callback for "rounding" issues"*/ 00397 qtexport->audioInputFormat.mBytesPerPacket = qtexport->audioInputFormat.mBytesPerFrame; 00398 qtexport->audioInputFormat.mFormatFlags |= kLinearPCMFormatFlagIsPacked; 00399 00400 00401 /*Ouput format*/ 00402 qtexport->audioOutputFormat.mFormatID = rd->qtcodecsettings.audiocodecType; 00403 //TODO: set audio channels 00404 qtexport->audioOutputFormat.mChannelsPerFrame = 2; 00405 qtexport->audioOutputFormat.mSampleRate = rd->qtcodecsettings.audioSampleRate; 00406 00407 /* Default value for compressed formats, overriden after if not the case */ 00408 qtexport->audioOutputFormat.mFramesPerPacket = 0; 00409 qtexport->audioOutputFormat.mBytesPerFrame = 0; 00410 qtexport->audioOutputFormat.mBytesPerPacket = 0; 00411 qtexport->audioOutputFormat.mBitsPerChannel = 0; 00412 00413 switch (rd->qtcodecsettings.audiocodecType) { 00414 case kAudioFormatMPEG4AAC: 00415 qtexport->audioOutputFormat.mFormatFlags = kMPEG4Object_AAC_Main; 00416 /* AAC codec does not handle sample rates above 48kHz, force this limit instead of getting an error afterwards */ 00417 if (qtexport->audioOutputFormat.mSampleRate > 48000) qtexport->audioOutputFormat.mSampleRate = 48000; 00418 break; 00419 case kAudioFormatAppleLossless: 00420 switch (U.audioformat) { 00421 case AUD_FORMAT_S16: 00422 qtexport->audioOutputFormat.mFormatFlags = kAppleLosslessFormatFlag_16BitSourceData; 00423 break; 00424 case AUD_FORMAT_S24: 00425 qtexport->audioOutputFormat.mFormatFlags = kAppleLosslessFormatFlag_24BitSourceData; 00426 break; 00427 case AUD_FORMAT_S32: 00428 qtexport->audioOutputFormat.mFormatFlags = kAppleLosslessFormatFlag_32BitSourceData; 00429 break; 00430 case AUD_FORMAT_U8: 00431 case AUD_FORMAT_FLOAT32: 00432 case AUD_FORMAT_FLOAT64: 00433 default: 00434 break; 00435 } 00436 break; 00437 case kAudioFormatLinearPCM: 00438 default: 00439 switch (rd->qtcodecsettings.audioBitDepth) { 00440 case AUD_FORMAT_U8: 00441 qtexport->audioOutputFormat.mBitsPerChannel = 8; 00442 qtexport->audioOutputFormat.mFormatFlags = 0; 00443 break; 00444 case AUD_FORMAT_S24: 00445 qtexport->audioOutputFormat.mBitsPerChannel = 24; 00446 qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; 00447 break; 00448 case AUD_FORMAT_S32: 00449 qtexport->audioOutputFormat.mBitsPerChannel = 32; 00450 qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; 00451 break; 00452 case AUD_FORMAT_FLOAT32: 00453 qtexport->audioOutputFormat.mBitsPerChannel = 32; 00454 qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat; 00455 break; 00456 case AUD_FORMAT_FLOAT64: 00457 qtexport->audioOutputFormat.mBitsPerChannel = 64; 00458 qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat; 00459 break; 00460 case AUD_FORMAT_S16: 00461 default: 00462 qtexport->audioOutputFormat.mBitsPerChannel = 16; 00463 qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; 00464 break; 00465 } 00466 qtexport->audioOutputFormat.mFormatFlags |= kLinearPCMFormatFlagIsPacked; 00467 qtexport->audioOutputFormat.mBytesPerPacket = qtexport->audioOutputFormat.mChannelsPerFrame * (qtexport->audioOutputFormat.mBitsPerChannel / 8); 00468 qtexport->audioOutputFormat.mFramesPerPacket = 1; 00469 qtexport->audioOutputFormat.mBytesPerFrame = qtexport->audioOutputFormat.mBytesPerPacket; 00470 break; 00471 } 00472 00473 err = AudioFileCreateWithURL(outputFileURL, audioFileType, &qtexport->audioOutputFormat, kAudioFileFlags_EraseFile, &qtexport->audioFile); 00474 CFRelease(outputFileURL); 00475 00476 if(err) 00477 BKE_report(reports, RPT_ERROR, "\nQuicktime: unable to create temporary audio file. Format error ?"); 00478 else { 00479 err = AudioConverterNew(&qtexport->audioInputFormat, &qtexport->audioOutputFormat, &qtexport->audioConverter); 00480 if (err) { 00481 BKE_report(reports, RPT_ERROR, "\nQuicktime: unable to initialize audio codec converter. Format error ?"); 00482 AudioFileClose(qtexport->audioFile); 00483 qtexport->audioFile = NULL; 00484 [qtexport->audioFileName release]; 00485 qtexport->audioFileName = nil; 00486 } else { 00487 UInt32 prop,propSize; 00488 /* Set up codec properties */ 00489 if (rd->qtcodecsettings.audiocodecType == kAudioFormatMPEG4AAC) { /*Lossy compressed format*/ 00490 prop = rd->qtcodecsettings.audioBitRate; 00491 AudioConverterSetProperty(qtexport->audioConverter, kAudioConverterEncodeBitRate, 00492 sizeof(prop), &prop); 00493 00494 if (rd->qtcodecsettings.audioCodecFlags & QTAUDIO_FLAG_CODEC_ISCBR) 00495 prop = kAudioCodecBitRateControlMode_Constant; 00496 else 00497 prop = kAudioCodecBitRateControlMode_LongTermAverage; 00498 AudioConverterSetProperty(qtexport->audioConverter, kAudioCodecPropertyBitRateControlMode, 00499 sizeof(prop), &prop); 00500 } 00501 /* Conversion quality : if performance impact then offer degraded option */ 00502 if ((rd->qtcodecsettings.audioCodecFlags & QTAUDIO_FLAG_RESAMPLE_NOHQ) == 0) { 00503 prop = kAudioConverterSampleRateConverterComplexity_Mastering; 00504 AudioConverterSetProperty(qtexport->audioConverter, kAudioConverterSampleRateConverterComplexity, 00505 sizeof(prop), &prop); 00506 00507 prop = kAudioConverterQuality_Max; 00508 AudioConverterSetProperty(qtexport->audioConverter, kAudioConverterSampleRateConverterQuality, 00509 sizeof(prop), &prop); 00510 } 00511 00512 write_cookie(qtexport->audioConverter, qtexport->audioFile); 00513 00514 /* Allocate output buffer */ 00515 if (qtexport->audioOutputFormat.mBytesPerPacket ==0) /* VBR */ 00516 AudioConverterGetProperty(qtexport->audioConverter, kAudioConverterPropertyMaximumOutputPacketSize, 00517 &propSize, &qtexport->audioCodecMaxOutputPacketSize); 00518 else 00519 qtexport->audioCodecMaxOutputPacketSize = qtexport->audioOutputFormat.mBytesPerPacket; 00520 00521 qtexport->audioInputBuffer = MEM_mallocN(AUDIOOUTPUTBUFFERSIZE, "qt_audio_inputPacket"); 00522 qtexport->audioOutputBuffer = MEM_mallocN(AUDIOOUTPUTBUFFERSIZE, "qt_audio_outputPacket"); 00523 qtexport->audioOutputPktDesc = MEM_mallocN(sizeof(AudioStreamPacketDescription)*AUDIOOUTPUTBUFFERSIZE/qtexport->audioCodecMaxOutputPacketSize, 00524 "qt_audio_pktdesc"); 00525 } 00526 } 00527 } 00528 00529 if (err == noErr) { 00530 qtexport->videoTempFileName = [[NSString alloc] initWithCString:tmpnam(nil) 00531 encoding:[NSString defaultCStringEncoding]]; 00532 if (qtexport->videoTempFileName) 00533 qtexport->movie = [[QTMovie alloc] initToWritableFile:qtexport->videoTempFileName error:&error]; 00534 00535 } 00536 } else 00537 qtexport->movie = [[QTMovie alloc] initToWritableFile:qtexport->filename error:&error]; 00538 00539 if(qtexport->movie == nil) { 00540 BKE_report(reports, RPT_ERROR, "Unable to create quicktime movie."); 00541 success= 0; 00542 if (qtexport->filename) [qtexport->filename release]; 00543 qtexport->filename = nil; 00544 if (qtexport->audioFileName) [qtexport->audioFileName release]; 00545 qtexport->audioFileName = nil; 00546 if (qtexport->videoTempFileName) [qtexport->videoTempFileName release]; 00547 qtexport->videoTempFileName = nil; 00548 [QTMovie exitQTKitOnThread]; 00549 } else { 00550 [qtexport->movie retain]; 00551 [qtexport->movie setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieEditableAttribute]; 00552 [qtexport->movie setAttribute:@"Made with Blender" forKey:QTMovieCopyrightAttribute]; 00553 00554 qtexport->frameDuration = QTMakeTime(rd->frs_sec_base*1000, rd->frs_sec*1000); 00555 00556 /* specifying the codec attributes : try to retrieve them from render data first*/ 00557 if (rd->qtcodecsettings.codecType) { 00558 qtexport->frameAttributes = [NSDictionary dictionaryWithObjectsAndKeys: 00559 stringWithCodecType(rd->qtcodecsettings.codecType), 00560 QTAddImageCodecType, 00561 [NSNumber numberWithLong:((rd->qtcodecsettings.codecSpatialQuality)*codecLosslessQuality)/100], 00562 QTAddImageCodecQuality, 00563 nil]; 00564 } 00565 else { 00566 qtexport->frameAttributes = [NSDictionary dictionaryWithObjectsAndKeys:@"jpeg", 00567 QTAddImageCodecType, 00568 [NSNumber numberWithLong:codecHighQuality], 00569 QTAddImageCodecQuality, 00570 nil]; 00571 } 00572 [qtexport->frameAttributes retain]; 00573 00574 if (qtexport->audioFile) { 00575 /* Init audio input stream */ 00576 AUD_DeviceSpecs specs; 00577 00578 specs.channels = U.audiochannels; 00579 specs.format = U.audioformat; 00580 specs.rate = U.audiorate; 00581 qtexport->audioInputDevice = AUD_openReadDevice(specs); 00582 AUD_playDevice(qtexport->audioInputDevice, scene->sound_scene, rd->sfra * rd->frs_sec_base / rd->frs_sec); 00583 00584 qtexport->audioOutputPktPos = 0; 00585 qtexport->audioTotalExportedFrames = 0; 00586 qtexport->audioTotalSavedFrames = 0; 00587 00588 qtexport->audioLastFrame = (rd->efra - rd->sfra) * qtexport->audioInputFormat.mSampleRate * rd->frs_sec_base / rd->frs_sec; 00589 } 00590 } 00591 } 00592 00593 [pool drain]; 00594 00595 return success; 00596 } 00597 00598 00599 int append_qt(struct RenderData *rd, int frame, int *pixels, int rectx, int recty, ReportList *reports) 00600 { 00601 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 00602 NSBitmapImageRep *blBitmapFormatImage; 00603 NSImage *frameImage; 00604 OSStatus err = noErr; 00605 unsigned char *from_Ptr,*to_Ptr; 00606 int y,from_i,to_i; 00607 BOOL alpha = (rd->im_format.planes == R_IMF_PLANES_RGBA)? YES: NO; 00608 00609 00610 /* Create bitmap image rep in blender format (32bit RGBA) */ 00611 blBitmapFormatImage = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL 00612 pixelsWide:rectx 00613 pixelsHigh:recty 00614 bitsPerSample:8 samplesPerPixel:4 hasAlpha:alpha isPlanar:NO 00615 colorSpaceName:NSCalibratedRGBColorSpace 00616 bitmapFormat:NSAlphaNonpremultipliedBitmapFormat 00617 bytesPerRow:rectx*4 00618 bitsPerPixel:32]; 00619 if (!blBitmapFormatImage) { 00620 [pool drain]; 00621 return 0; 00622 } 00623 00624 from_Ptr = (unsigned char*)pixels; 00625 to_Ptr = (unsigned char*)[blBitmapFormatImage bitmapData]; 00626 for (y = 0; y < recty; y++) { 00627 to_i = (recty-y-1)*rectx; 00628 from_i = y*rectx; 00629 memcpy(to_Ptr+4*to_i, from_Ptr+4*from_i, 4*rectx); 00630 } 00631 00632 frameImage = [[NSImage alloc] initWithSize:NSMakeSize(rectx, recty)]; 00633 [frameImage addRepresentation:blBitmapFormatImage]; 00634 00635 /* Add the image to the movie clip */ 00636 [qtexport->movie addImage:frameImage 00637 forDuration:qtexport->frameDuration 00638 withAttributes:qtexport->frameAttributes]; 00639 00640 [blBitmapFormatImage release]; 00641 [frameImage release]; 00642 00643 00644 if (qtexport->audioFile) { 00645 UInt32 audioPacketsConverted; 00646 /* Append audio */ 00647 while (qtexport->audioTotalExportedFrames < qtexport->audioLastFrame) { 00648 00649 qtexport->audioBufferList.mNumberBuffers = 1; 00650 qtexport->audioBufferList.mBuffers[0].mNumberChannels = qtexport->audioOutputFormat.mChannelsPerFrame; 00651 qtexport->audioBufferList.mBuffers[0].mDataByteSize = AUDIOOUTPUTBUFFERSIZE; 00652 qtexport->audioBufferList.mBuffers[0].mData = qtexport->audioOutputBuffer; 00653 audioPacketsConverted = AUDIOOUTPUTBUFFERSIZE / qtexport->audioCodecMaxOutputPacketSize; 00654 00655 err = AudioConverterFillComplexBuffer(qtexport->audioConverter, AudioConverterInputCallback, 00656 NULL, &audioPacketsConverted, &qtexport->audioBufferList, qtexport->audioOutputPktDesc); 00657 if (audioPacketsConverted) { 00658 AudioFileWritePackets(qtexport->audioFile, false, qtexport->audioBufferList.mBuffers[0].mDataByteSize, 00659 qtexport->audioOutputPktDesc, qtexport->audioOutputPktPos, &audioPacketsConverted, qtexport->audioOutputBuffer); 00660 qtexport->audioOutputPktPos += audioPacketsConverted; 00661 00662 if (qtexport->audioOutputFormat.mFramesPerPacket) { 00663 // this is the common case: format has constant frames per packet 00664 qtexport->audioTotalSavedFrames += (audioPacketsConverted * qtexport->audioOutputFormat.mFramesPerPacket); 00665 } else { 00666 unsigned int i; 00667 // if there are variable frames per packet, then we have to do this for each packeet 00668 for (i = 0; i < audioPacketsConverted; ++i) 00669 qtexport->audioTotalSavedFrames += qtexport->audioOutputPktDesc[i].mVariableFramesInPacket; 00670 } 00671 00672 00673 } 00674 else { 00675 //Error getting audio packets 00676 BKE_reportf(reports, RPT_ERROR, "Unable to get further audio packets from frame %i, error = 0x%x",(int)qtexport->audioTotalExportedFrames,err); 00677 break; 00678 } 00679 00680 } 00681 } 00682 [pool drain]; 00683 00684 return 1; 00685 } 00686 00687 00688 void end_qt(void) 00689 { 00690 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 00691 if (qtexport->movie) { 00692 00693 if (qtexport->audioFile) 00694 { 00695 NSDictionary *dict = nil; 00696 QTMovie *audioTmpMovie = nil; 00697 NSError *error; 00698 NSFileManager *fileManager; 00699 00700 /* Mux video and audio then save file */ 00701 00702 /* Write last frames for VBR files */ 00703 if (qtexport->audioOutputFormat.mBitsPerChannel == 0) { 00704 OSStatus err = noErr; 00705 AudioConverterPrimeInfo primeInfo; 00706 UInt32 primeSize = sizeof(primeInfo); 00707 00708 err = AudioConverterGetProperty(qtexport->audioConverter, kAudioConverterPrimeInfo, &primeSize, &primeInfo); 00709 if (err == noErr) { 00710 // there's priming to write out to the file 00711 AudioFilePacketTableInfo pti; 00712 pti.mPrimingFrames = primeInfo.leadingFrames; 00713 pti.mRemainderFrames = primeInfo.trailingFrames; 00714 pti.mNumberValidFrames = qtexport->audioTotalSavedFrames - pti.mPrimingFrames - pti.mRemainderFrames; 00715 AudioFileSetProperty(qtexport->audioFile, kAudioFilePropertyPacketTableInfo, sizeof(pti), &pti); 00716 } 00717 00718 } 00719 00720 write_cookie(qtexport->audioConverter, qtexport->audioFile); 00721 AudioConverterDispose(qtexport->audioConverter); 00722 AudioFileClose(qtexport->audioFile); 00723 AUD_closeReadDevice(qtexport->audioInputDevice); 00724 qtexport->audioFile = NULL; 00725 qtexport->audioInputDevice = NULL; 00726 MEM_freeN(qtexport->audioInputBuffer); 00727 MEM_freeN(qtexport->audioOutputBuffer); 00728 MEM_freeN(qtexport->audioOutputPktDesc); 00729 00730 /* Reopen audio file and merge it */ 00731 audioTmpMovie = [QTMovie movieWithFile:qtexport->audioFileName error:&error]; 00732 if (audioTmpMovie) { 00733 NSArray *audioTracks = [audioTmpMovie tracksOfMediaType:QTMediaTypeSound]; 00734 QTTrack *audioTrack = nil; 00735 if( [audioTracks count] > 0 ) 00736 { 00737 audioTrack = [audioTracks objectAtIndex:0]; 00738 } 00739 00740 if( audioTrack ) 00741 { 00742 QTTimeRange totalRange; 00743 totalRange.time = QTZeroTime; 00744 totalRange.duration = [[audioTmpMovie attributeForKey:QTMovieDurationAttribute] QTTimeValue]; 00745 00746 [qtexport->movie insertSegmentOfTrack:audioTrack timeRange:totalRange atTime:QTZeroTime]; 00747 } 00748 } 00749 00750 /* Save file */ 00751 dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] 00752 forKey:QTMovieFlatten]; 00753 00754 if (dict) { 00755 [qtexport->movie writeToFile:qtexport->filename withAttributes:dict]; 00756 } 00757 00758 /* Delete temp files */ 00759 fileManager = [[NSFileManager alloc] init]; 00760 [fileManager removeItemAtPath:qtexport->audioFileName error:&error]; 00761 [fileManager removeItemAtPath:qtexport->videoTempFileName error:&error]; 00762 [fileManager release]; 00763 } 00764 else { 00765 /* Flush update of the movie file */ 00766 [qtexport->movie updateMovieFile]; 00767 00768 [qtexport->movie invalidate]; 00769 } 00770 00771 /* Clean up movie structure */ 00772 if (qtexport->filename) [qtexport->filename release]; 00773 qtexport->filename = nil; 00774 if (qtexport->audioFileName) [qtexport->audioFileName release]; 00775 qtexport->audioFileName = nil; 00776 if (qtexport->videoTempFileName) [qtexport->videoTempFileName release]; 00777 qtexport->videoTempFileName = nil; 00778 [qtexport->frameAttributes release]; 00779 [qtexport->movie release]; 00780 } 00781 00782 [QTMovie exitQTKitOnThread]; 00783 00784 if(qtexport) { 00785 MEM_freeN(qtexport); 00786 qtexport = NULL; 00787 } 00788 [pool drain]; 00789 } 00790 00791 00792 void free_qtcomponentdata(void) 00793 { 00794 } 00795 00796 void quicktime_verify_image_type(RenderData *rd, ImageFormatData *imf) 00797 { 00798 if (imf->imtype == R_IMF_IMTYPE_QUICKTIME) { 00799 if ((rd->qtcodecsettings.codecType<= 0) || 00800 (rd->qtcodecsettings.codecSpatialQuality <0) || 00801 (rd->qtcodecsettings.codecSpatialQuality > 100)) { 00802 00803 rd->qtcodecsettings.codecType = kJPEGCodecType; 00804 rd->qtcodecsettings.codecSpatialQuality = (codecHighQuality*100)/codecLosslessQuality; 00805 } 00806 if ((rd->qtcodecsettings.audioSampleRate < 21000) || 00807 (rd->qtcodecsettings.audioSampleRate > 193000)) 00808 rd->qtcodecsettings.audioSampleRate = 48000; 00809 00810 if (rd->qtcodecsettings.audioBitDepth == 0) 00811 rd->qtcodecsettings.audioBitDepth = AUD_FORMAT_S16; 00812 00813 if (rd->qtcodecsettings.audioBitRate == 0) 00814 rd->qtcodecsettings.audioBitRate = 256000; 00815 } 00816 } 00817 00818 #endif /* _WIN32 || __APPLE__ */ 00819 #endif /* WITH_QUICKTIME */ 00820