Blender  V2.59
qtkit_export.m
Go to the documentation of this file.
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