Blender V2.61 - r43446

qtkit_export.m

Go to the documentation of this file.
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