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