2009-11-18 09:49:42 +00:00
|
|
|
/**
|
2009-11-18 18:45:38 +00:00
|
|
|
* $Id$
|
2009-11-18 09:49:42 +00:00
|
|
|
*
|
|
|
|
* qtkit_export.m
|
|
|
|
*
|
|
|
|
* Code to create QuickTime Movies with Blender
|
|
|
|
*
|
|
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software Foundation,
|
2010-02-12 13:34:04 +00:00
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
2009-11-18 09:49:42 +00:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* The Original Code is written by Rob Haarsma (phase)
|
|
|
|
*
|
|
|
|
* Contributor(s): Stefan Gartner (sgefant)
|
|
|
|
* Damien Plisson 11/2009
|
|
|
|
*
|
|
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef WITH_QUICKTIME
|
|
|
|
#if defined(_WIN32) || defined(__APPLE__)
|
|
|
|
|
2009-11-18 15:01:59 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
2009-11-18 09:49:42 +00:00
|
|
|
#include "DNA_scene_types.h"
|
2010-04-10 09:31:41 +00:00
|
|
|
#include "DNA_userdef_types.h"
|
|
|
|
|
|
|
|
#include "AUD_C-API.h"
|
2009-11-18 09:49:42 +00:00
|
|
|
|
|
|
|
#include "BKE_global.h"
|
|
|
|
#include "BKE_scene.h"
|
2009-12-22 15:20:31 +00:00
|
|
|
#include "BKE_report.h"
|
2009-11-18 09:49:42 +00:00
|
|
|
|
|
|
|
#include "BLI_blenlib.h"
|
|
|
|
|
|
|
|
#include "BLO_sys_types.h"
|
|
|
|
|
|
|
|
#include "IMB_imbuf.h"
|
|
|
|
#include "IMB_imbuf_types.h"
|
|
|
|
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
|
|
#ifdef __APPLE__
|
|
|
|
/* evil */
|
|
|
|
#ifndef __AIFF__
|
|
|
|
#define __AIFF__
|
|
|
|
#endif
|
2009-11-18 15:01:59 +00:00
|
|
|
#import <Cocoa/Cocoa.h>
|
|
|
|
#import <QTKit/QTKit.h>
|
2010-04-10 09:31:41 +00:00
|
|
|
#include <AudioToolbox/AudioToolbox.h>
|
2009-11-28 18:16:27 +00:00
|
|
|
|
2009-12-03 09:59:52 +00:00
|
|
|
#if (MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4) || !__LP64__
|
|
|
|
#error 64 bit build & OSX 10.5 minimum are needed for QTKit
|
2009-11-28 18:16:27 +00:00
|
|
|
#endif
|
|
|
|
|
2009-12-02 11:23:11 +00:00
|
|
|
#include "quicktime_import.h"
|
|
|
|
#include "quicktime_export.h"
|
|
|
|
|
2009-11-18 09:49:42 +00:00
|
|
|
#endif /* __APPLE__ */
|
|
|
|
|
|
|
|
typedef struct QuicktimeExport {
|
2009-11-18 15:01:59 +00:00
|
|
|
QTMovie *movie;
|
|
|
|
|
|
|
|
NSString *filename;
|
2009-11-18 09:49:42 +00:00
|
|
|
|
2009-11-18 15:01:59 +00:00
|
|
|
QTTime frameDuration;
|
|
|
|
NSDictionary *frameAttributes;
|
2010-04-10 09:31:41 +00:00
|
|
|
|
|
|
|
NSString *videoTempFileName;
|
|
|
|
/* Audio section */
|
|
|
|
AUD_Device *audioInputDevice;
|
|
|
|
AudioFileID audioFile;
|
|
|
|
NSString *audioFileName;
|
|
|
|
AudioConverterRef audioConverter;
|
|
|
|
AudioBufferList audioBufferList;
|
|
|
|
AudioStreamBasicDescription audioInputFormat, audioOutputFormat;
|
|
|
|
AudioStreamPacketDescription *audioOutputPktDesc;
|
|
|
|
SInt64 audioFilePos;
|
|
|
|
char* audioInputBuffer;
|
|
|
|
char* audioOutputBuffer;
|
|
|
|
UInt32 audioCodecMaxOutputPacketSize;
|
|
|
|
UInt64 audioTotalExportedFrames, audioTotalSavedFrames;
|
|
|
|
UInt64 audioLastFrame;
|
|
|
|
SInt64 audioOutputPktPos;
|
|
|
|
|
2009-11-18 09:49:42 +00:00
|
|
|
} QuicktimeExport;
|
|
|
|
|
|
|
|
static struct QuicktimeExport *qtexport;
|
|
|
|
|
2010-04-10 09:31:41 +00:00
|
|
|
#define AUDIOOUTPUTBUFFERSIZE 65536
|
2009-11-18 09:49:42 +00:00
|
|
|
|
2010-04-10 09:31:41 +00:00
|
|
|
#pragma mark rna helper functions
|
2009-12-02 11:23:11 +00:00
|
|
|
|
2010-04-10 09:31:41 +00:00
|
|
|
/* Video codec */
|
|
|
|
static QuicktimeCodecTypeDesc qtVideoCodecList[] = {
|
2009-12-02 11:23:11 +00:00
|
|
|
{kRawCodecType, 1, "Uncompressed"},
|
|
|
|
{kJPEGCodecType, 2, "JPEG"},
|
|
|
|
{kMotionJPEGACodecType, 3, "M-JPEG A"},
|
|
|
|
{kMotionJPEGBCodecType, 4, "M-JPEG B"},
|
|
|
|
{kDVCPALCodecType, 5, "DV PAL"},
|
|
|
|
{kDVCNTSCCodecType, 6, "DV/DVCPRO NTSC"},
|
|
|
|
{kDVCPROHD720pCodecType, 7, "DVCPRO HD 720p"},
|
|
|
|
{kDVCPROHD1080i50CodecType, 8, "DVCPRO HD 1080i50"},
|
|
|
|
{kDVCPROHD1080i60CodecType, 9, "DVCPRO HD 1080i60"},
|
|
|
|
{kMPEG4VisualCodecType, 10, "MPEG4"},
|
|
|
|
{kH263CodecType, 11, "H.263"},
|
|
|
|
{kH264CodecType, 12, "H.264"},
|
|
|
|
{0,0,NULL}};
|
|
|
|
|
2010-04-10 09:31:41 +00:00
|
|
|
static int qtVideoCodecCount = 12;
|
2009-12-02 11:23:11 +00:00
|
|
|
|
2010-04-10 09:31:41 +00:00
|
|
|
int quicktime_get_num_videocodecs() {
|
|
|
|
return qtVideoCodecCount;
|
2009-12-02 11:23:11 +00:00
|
|
|
}
|
|
|
|
|
2010-04-10 09:31:41 +00:00
|
|
|
QuicktimeCodecTypeDesc* quicktime_get_videocodecType_desc(int indexValue) {
|
|
|
|
if ((indexValue>=0) && (indexValue < qtVideoCodecCount))
|
|
|
|
return &qtVideoCodecList[indexValue];
|
2009-12-02 11:23:11 +00:00
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-04-10 09:31:41 +00:00
|
|
|
int quicktime_rnatmpvalue_from_videocodectype(int codecType) {
|
2009-12-02 11:23:11 +00:00
|
|
|
int i;
|
2010-04-10 09:31:41 +00:00
|
|
|
for (i=0;i<qtVideoCodecCount;i++) {
|
|
|
|
if (qtVideoCodecList[i].codecType == codecType)
|
|
|
|
return qtVideoCodecList[i].rnatmpvalue;
|
2009-11-20 10:37:50 +00:00
|
|
|
}
|
2009-12-02 11:23:11 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-04-10 09:31:41 +00:00
|
|
|
int quicktime_videocodecType_from_rnatmpvalue(int rnatmpvalue) {
|
2009-12-02 11:23:11 +00:00
|
|
|
int i;
|
2010-04-10 09:31:41 +00:00
|
|
|
for (i=0;i<qtVideoCodecCount;i++) {
|
|
|
|
if (qtVideoCodecList[i].rnatmpvalue == rnatmpvalue)
|
|
|
|
return qtVideoCodecList[i].codecType;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Audio codec */
|
|
|
|
static QuicktimeCodecTypeDesc qtAudioCodecList[] = {
|
|
|
|
{0, 0, "No audio"},
|
|
|
|
{kAudioFormatLinearPCM, 1, "LPCM"},
|
|
|
|
{kAudioFormatAppleLossless, 2, "Apple Lossless"},
|
|
|
|
{kAudioFormatMPEG4AAC, 3, "AAC"},
|
|
|
|
{0,0,NULL}};
|
|
|
|
|
|
|
|
static int qtAudioCodecCount = 4;
|
|
|
|
|
|
|
|
int quicktime_get_num_audiocodecs() {
|
|
|
|
return qtAudioCodecCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
QuicktimeCodecTypeDesc* quicktime_get_audiocodecType_desc(int indexValue) {
|
|
|
|
if ((indexValue>=0) && (indexValue < qtAudioCodecCount))
|
|
|
|
return &qtAudioCodecList[indexValue];
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int quicktime_rnatmpvalue_from_audiocodectype(int codecType) {
|
|
|
|
int i;
|
|
|
|
for (i=0;i<qtAudioCodecCount;i++) {
|
|
|
|
if (qtAudioCodecList[i].codecType == codecType)
|
|
|
|
return qtAudioCodecList[i].rnatmpvalue;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int quicktime_audiocodecType_from_rnatmpvalue(int rnatmpvalue) {
|
|
|
|
int i;
|
|
|
|
for (i=0;i<qtAudioCodecCount;i++) {
|
|
|
|
if (qtAudioCodecList[i].rnatmpvalue == rnatmpvalue)
|
|
|
|
return qtAudioCodecList[i].codecType;
|
2009-12-02 11:23:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static NSString *stringWithCodecType(int codecType) {
|
|
|
|
char str[5];
|
|
|
|
|
|
|
|
*((int*)str) = EndianU32_NtoB(codecType);
|
|
|
|
str[4] = 0;
|
|
|
|
|
|
|
|
return [NSString stringWithCString:str encoding:NSASCIIStringEncoding];
|
2009-11-20 10:37:50 +00:00
|
|
|
}
|
|
|
|
|
2009-11-18 09:49:42 +00:00
|
|
|
void makeqtstring (RenderData *rd, char *string) {
|
|
|
|
char txt[64];
|
|
|
|
|
|
|
|
strcpy(string, rd->pic);
|
2010-03-09 19:04:05 +00:00
|
|
|
BLI_path_abs(string, G.sce);
|
2009-11-18 09:49:42 +00:00
|
|
|
|
|
|
|
BLI_make_existing_file(string);
|
|
|
|
|
|
|
|
if (BLI_strcasecmp(string + strlen(string) - 4, ".mov")) {
|
|
|
|
sprintf(txt, "%04d_%04d.mov", (rd->sfra) , (rd->efra) );
|
|
|
|
strcat(string, txt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-08 14:42:52 +00:00
|
|
|
void filepath_qt(char *string, RenderData *rd) {
|
2010-01-26 21:41:38 +00:00
|
|
|
if (string==NULL) return;
|
2010-01-08 14:42:52 +00:00
|
|
|
|
|
|
|
strcpy(string, rd->pic);
|
2010-03-09 19:04:05 +00:00
|
|
|
BLI_path_abs(string, G.sce);
|
2010-01-08 14:42:52 +00:00
|
|
|
|
|
|
|
BLI_make_existing_file(string);
|
|
|
|
|
2010-01-26 21:41:38 +00:00
|
|
|
if (!BLI_testextensie(string, ".mov")) {
|
|
|
|
/* if we dont have any #'s to insert numbers into, use 4 numbers by default */
|
|
|
|
if (strchr(string, '#')==NULL)
|
|
|
|
strcat(string, "####"); /* 4 numbers */
|
|
|
|
|
2010-03-09 17:36:23 +00:00
|
|
|
BLI_path_frame_range(string, rd->sfra, rd->efra, 4);
|
2010-01-26 21:41:38 +00:00
|
|
|
strcat(string, ".mov");
|
2010-01-08 14:42:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-10 09:31:41 +00:00
|
|
|
#pragma mark audio export functions
|
|
|
|
|
|
|
|
static OSStatus write_cookie(AudioConverterRef converter, AudioFileID outfile)
|
|
|
|
{
|
|
|
|
// grab the cookie from the converter and write it to the file
|
|
|
|
UInt32 cookieSize = 0;
|
|
|
|
OSStatus err = AudioConverterGetPropertyInfo(converter, kAudioConverterCompressionMagicCookie, &cookieSize, NULL);
|
|
|
|
// if there is an error here, then the format doesn't have a cookie, so on we go
|
|
|
|
if (!err && cookieSize) {
|
|
|
|
char* cookie = malloc(cookieSize);
|
|
|
|
|
|
|
|
err = AudioConverterGetProperty(converter, kAudioConverterCompressionMagicCookie, &cookieSize, cookie);
|
|
|
|
|
|
|
|
if (!err)
|
|
|
|
err = AudioFileSetProperty (outfile, kAudioFilePropertyMagicCookieData, cookieSize, cookie);
|
|
|
|
// even though some formats have cookies, some files don't take them
|
|
|
|
|
|
|
|
free(cookie);
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* AudioConverter input stream callback */
|
|
|
|
static OSStatus AudioConverterInputCallback(AudioConverterRef inAudioConverter,
|
|
|
|
UInt32* ioNumberDataPackets,
|
|
|
|
AudioBufferList* ioData,
|
|
|
|
AudioStreamPacketDescription** outDataPacketDescription,
|
|
|
|
void* inUserData)
|
|
|
|
{
|
|
|
|
if (qtexport->audioTotalExportedFrames >= qtexport->audioLastFrame) { /* EOF */
|
|
|
|
*ioNumberDataPackets = 0;
|
|
|
|
return noErr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (qtexport->audioInputFormat.mBytesPerPacket * *ioNumberDataPackets > AUDIOOUTPUTBUFFERSIZE)
|
|
|
|
*ioNumberDataPackets = AUDIOOUTPUTBUFFERSIZE / qtexport->audioInputFormat.mBytesPerPacket;
|
|
|
|
|
|
|
|
if ((qtexport->audioTotalExportedFrames + *ioNumberDataPackets) > qtexport->audioLastFrame)
|
2010-04-17 08:33:42 +00:00
|
|
|
*ioNumberDataPackets = (qtexport->audioLastFrame - qtexport->audioTotalExportedFrames) / qtexport->audioInputFormat.mFramesPerPacket;
|
2010-04-10 09:31:41 +00:00
|
|
|
|
|
|
|
qtexport->audioTotalExportedFrames += *ioNumberDataPackets;
|
|
|
|
|
|
|
|
AUD_readDevice(qtexport->audioInputDevice, (UInt8*)qtexport->audioInputBuffer,
|
|
|
|
qtexport->audioInputFormat.mFramesPerPacket * *ioNumberDataPackets);
|
|
|
|
|
|
|
|
ioData->mBuffers[0].mDataByteSize = qtexport->audioInputFormat.mBytesPerPacket * *ioNumberDataPackets;
|
|
|
|
ioData->mBuffers[0].mData = qtexport->audioInputBuffer;
|
|
|
|
ioData->mBuffers[0].mNumberChannels = qtexport->audioInputFormat.mChannelsPerFrame;
|
|
|
|
|
|
|
|
return noErr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-02 11:23:11 +00:00
|
|
|
#pragma mark export functions
|
2009-11-18 09:49:42 +00:00
|
|
|
|
2009-12-22 12:01:32 +00:00
|
|
|
int start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty, ReportList *reports)
|
2009-11-18 15:01:59 +00:00
|
|
|
{
|
|
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
NSError *error;
|
2010-04-10 09:31:41 +00:00
|
|
|
char name[1024];
|
2009-12-22 12:01:32 +00:00
|
|
|
int success= 1;
|
2010-04-10 09:31:41 +00:00
|
|
|
OSStatus err=noErr;
|
2009-11-18 09:49:42 +00:00
|
|
|
|
2009-12-22 12:01:32 +00:00
|
|
|
if(qtexport == NULL) qtexport = MEM_callocN(sizeof(QuicktimeExport), "QuicktimeExport");
|
2009-11-18 15:01:59 +00:00
|
|
|
|
2009-12-22 12:01:32 +00:00
|
|
|
[QTMovie enterQTKitOnThread];
|
|
|
|
|
|
|
|
/* Check first if the QuickTime 7.2.1 initToWritableFile: method is available */
|
|
|
|
if ([[[[QTMovie alloc] init] autorelease] respondsToSelector:@selector(initToWritableFile:error:)] != YES) {
|
2009-12-22 15:20:31 +00:00
|
|
|
BKE_report(reports, RPT_ERROR, "\nUnable to create quicktime movie, need Quicktime rev 7.2.1 or later");
|
2009-12-22 12:01:32 +00:00
|
|
|
success= 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
makeqtstring(rd, name);
|
2010-04-10 09:31:41 +00:00
|
|
|
qtexport->filename = [[NSString alloc] initWithCString:name
|
2009-12-22 12:01:32 +00:00
|
|
|
encoding:[NSString defaultCStringEncoding]];
|
2010-04-10 09:31:41 +00:00
|
|
|
qtexport->movie = nil;
|
|
|
|
qtexport->audioFile = NULL;
|
|
|
|
|
|
|
|
if (rd->qtcodecsettings.audiocodecType) {
|
|
|
|
// generate a name for our video & audio files
|
|
|
|
/* Init audio file */
|
|
|
|
CFURLRef outputFileURL;
|
|
|
|
char extension[32];
|
|
|
|
AudioFileTypeID audioFileType;
|
|
|
|
|
|
|
|
switch (rd->qtcodecsettings.audiocodecType) {
|
|
|
|
case kAudioFormatLinearPCM:
|
|
|
|
audioFileType = kAudioFileWAVEType;
|
|
|
|
strcpy(extension,".wav");
|
|
|
|
break;
|
|
|
|
case kAudioFormatMPEG4AAC:
|
|
|
|
case kAudioFormatAppleLossless:
|
|
|
|
audioFileType = kAudioFileM4AType;
|
|
|
|
strcpy(extension, ".m4a");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
audioFileType = kAudioFileAIFFType;
|
|
|
|
strcpy(extension,".aiff");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmpnam(name);
|
|
|
|
strcat(name, extension);
|
|
|
|
outputFileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,(UInt8*) name, strlen(name), false);
|
|
|
|
|
|
|
|
if (outputFileURL) {
|
|
|
|
|
|
|
|
qtexport->audioFileName = [[NSString alloc] initWithCString:name
|
|
|
|
encoding:[NSString defaultCStringEncoding]];
|
|
|
|
|
|
|
|
qtexport->audioInputFormat.mSampleRate = U.audiorate;
|
|
|
|
qtexport->audioInputFormat.mFormatID = kAudioFormatLinearPCM;
|
|
|
|
qtexport->audioInputFormat.mChannelsPerFrame = U.audiochannels;
|
|
|
|
switch (U.audioformat) {
|
|
|
|
case AUD_FORMAT_U8:
|
|
|
|
qtexport->audioInputFormat.mBitsPerChannel = 8;
|
|
|
|
qtexport->audioInputFormat.mFormatFlags = 0;
|
|
|
|
break;
|
|
|
|
case AUD_FORMAT_S24:
|
|
|
|
qtexport->audioInputFormat.mBitsPerChannel = 24;
|
|
|
|
qtexport->audioInputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
|
|
|
|
break;
|
|
|
|
case AUD_FORMAT_S32:
|
|
|
|
qtexport->audioInputFormat.mBitsPerChannel = 32;
|
|
|
|
qtexport->audioInputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
|
|
|
|
break;
|
|
|
|
case AUD_FORMAT_FLOAT32:
|
|
|
|
qtexport->audioInputFormat.mBitsPerChannel = 32;
|
|
|
|
qtexport->audioInputFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
|
|
|
|
break;
|
|
|
|
case AUD_FORMAT_FLOAT64:
|
|
|
|
qtexport->audioInputFormat.mBitsPerChannel = 64;
|
|
|
|
qtexport->audioInputFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
|
|
|
|
break;
|
|
|
|
case AUD_FORMAT_S16:
|
|
|
|
default:
|
|
|
|
qtexport->audioInputFormat.mBitsPerChannel = 16;
|
|
|
|
qtexport->audioInputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
qtexport->audioInputFormat.mBytesPerFrame = qtexport->audioInputFormat.mChannelsPerFrame * qtexport->audioInputFormat.mBitsPerChannel / 8;
|
2010-04-17 08:33:42 +00:00
|
|
|
qtexport->audioInputFormat.mFramesPerPacket = 1; /*If not ==1, then need to check input callback for "rounding" issues"*/
|
2010-04-10 09:31:41 +00:00
|
|
|
qtexport->audioInputFormat.mBytesPerPacket = qtexport->audioInputFormat.mBytesPerFrame;
|
|
|
|
qtexport->audioInputFormat.mFormatFlags |= kLinearPCMFormatFlagIsPacked;
|
|
|
|
|
|
|
|
|
|
|
|
/*Ouput format*/
|
|
|
|
qtexport->audioOutputFormat.mFormatID = rd->qtcodecsettings.audiocodecType;
|
|
|
|
//TODO: set audio channels
|
|
|
|
qtexport->audioOutputFormat.mChannelsPerFrame = 2;
|
|
|
|
qtexport->audioOutputFormat.mSampleRate = rd->qtcodecsettings.audioSampleRate;
|
|
|
|
|
|
|
|
/* Default value for compressed formats, overriden after if not the case */
|
|
|
|
qtexport->audioOutputFormat.mFramesPerPacket = 0;
|
|
|
|
qtexport->audioOutputFormat.mBytesPerFrame = 0;
|
|
|
|
qtexport->audioOutputFormat.mBytesPerPacket = 0;
|
|
|
|
qtexport->audioOutputFormat.mBitsPerChannel = 0;
|
|
|
|
|
|
|
|
switch (rd->qtcodecsettings.audiocodecType) {
|
|
|
|
case kAudioFormatMPEG4AAC:
|
|
|
|
qtexport->audioOutputFormat.mFormatFlags = kMPEG4Object_AAC_Main;
|
2010-04-17 08:33:42 +00:00
|
|
|
/* AAC codec does not handle sample rates above 48kHz, force this limit instead of getting an error afterwards */
|
|
|
|
if (qtexport->audioOutputFormat.mSampleRate > 48000) qtexport->audioOutputFormat.mSampleRate = 48000;
|
|
|
|
break;
|
2010-04-10 09:31:41 +00:00
|
|
|
case kAudioFormatAppleLossless:
|
|
|
|
switch (U.audioformat) {
|
|
|
|
case AUD_FORMAT_S16:
|
|
|
|
qtexport->audioOutputFormat.mFormatFlags = kAppleLosslessFormatFlag_16BitSourceData;
|
|
|
|
break;
|
|
|
|
case AUD_FORMAT_S24:
|
|
|
|
qtexport->audioOutputFormat.mFormatFlags = kAppleLosslessFormatFlag_24BitSourceData;
|
|
|
|
break;
|
|
|
|
case AUD_FORMAT_S32:
|
|
|
|
qtexport->audioOutputFormat.mFormatFlags = kAppleLosslessFormatFlag_32BitSourceData;
|
|
|
|
break;
|
|
|
|
case AUD_FORMAT_U8:
|
|
|
|
case AUD_FORMAT_FLOAT32:
|
|
|
|
case AUD_FORMAT_FLOAT64:
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kAudioFormatLinearPCM:
|
|
|
|
default:
|
|
|
|
switch (rd->qtcodecsettings.audioBitDepth) {
|
|
|
|
case AUD_FORMAT_U8:
|
|
|
|
qtexport->audioOutputFormat.mBitsPerChannel = 8;
|
|
|
|
qtexport->audioOutputFormat.mFormatFlags = 0;
|
|
|
|
break;
|
|
|
|
case AUD_FORMAT_S24:
|
|
|
|
qtexport->audioOutputFormat.mBitsPerChannel = 24;
|
|
|
|
qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
|
|
|
|
break;
|
|
|
|
case AUD_FORMAT_S32:
|
|
|
|
qtexport->audioOutputFormat.mBitsPerChannel = 32;
|
|
|
|
qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
|
|
|
|
break;
|
|
|
|
case AUD_FORMAT_FLOAT32:
|
|
|
|
qtexport->audioOutputFormat.mBitsPerChannel = 32;
|
|
|
|
qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
|
|
|
|
break;
|
|
|
|
case AUD_FORMAT_FLOAT64:
|
|
|
|
qtexport->audioOutputFormat.mBitsPerChannel = 64;
|
|
|
|
qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
|
|
|
|
break;
|
|
|
|
case AUD_FORMAT_S16:
|
|
|
|
default:
|
|
|
|
qtexport->audioOutputFormat.mBitsPerChannel = 16;
|
|
|
|
qtexport->audioOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
qtexport->audioOutputFormat.mFormatFlags |= kLinearPCMFormatFlagIsPacked;
|
|
|
|
qtexport->audioOutputFormat.mBytesPerPacket = qtexport->audioOutputFormat.mChannelsPerFrame * (qtexport->audioOutputFormat.mBitsPerChannel / 8);
|
|
|
|
qtexport->audioOutputFormat.mFramesPerPacket = 1;
|
|
|
|
qtexport->audioOutputFormat.mBytesPerFrame = qtexport->audioOutputFormat.mBytesPerPacket;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = AudioFileCreateWithURL(outputFileURL, audioFileType, &qtexport->audioOutputFormat, kAudioFileFlags_EraseFile, &qtexport->audioFile);
|
|
|
|
CFRelease(outputFileURL);
|
|
|
|
|
|
|
|
if(err)
|
|
|
|
BKE_report(reports, RPT_ERROR, "\nQuicktime: unable to create temporary audio file. Format error ?");
|
|
|
|
else {
|
|
|
|
err = AudioConverterNew(&qtexport->audioInputFormat, &qtexport->audioOutputFormat, &qtexport->audioConverter);
|
|
|
|
if (err) {
|
|
|
|
BKE_report(reports, RPT_ERROR, "\nQuicktime: unable to initialize audio codec converter. Format error ?");
|
|
|
|
AudioFileClose(qtexport->audioFile);
|
|
|
|
qtexport->audioFile = NULL;
|
|
|
|
[qtexport->audioFileName release];
|
|
|
|
qtexport->audioFileName = nil;
|
|
|
|
} else {
|
|
|
|
UInt32 prop,propSize;
|
|
|
|
/* Set up codec properties */
|
|
|
|
if (rd->qtcodecsettings.audiocodecType == kAudioFormatMPEG4AAC) { /*Lossy compressed format*/
|
|
|
|
prop = rd->qtcodecsettings.audioBitRate;
|
|
|
|
AudioConverterSetProperty(qtexport->audioConverter, kAudioConverterEncodeBitRate,
|
|
|
|
sizeof(prop), &prop);
|
|
|
|
|
|
|
|
if (rd->qtcodecsettings.audioCodecFlags & QTAUDIO_FLAG_CODEC_ISCBR)
|
|
|
|
prop = kAudioCodecBitRateControlMode_Constant;
|
|
|
|
else
|
|
|
|
prop = kAudioCodecBitRateControlMode_LongTermAverage;
|
|
|
|
AudioConverterSetProperty(qtexport->audioConverter, kAudioCodecPropertyBitRateControlMode,
|
|
|
|
sizeof(prop), &prop);
|
|
|
|
}
|
|
|
|
/* Conversion quality : if performance impact then offer degraded option */
|
|
|
|
if ((rd->qtcodecsettings.audioCodecFlags & QTAUDIO_FLAG_RESAMPLE_NOHQ) == 0) {
|
|
|
|
prop = kAudioConverterSampleRateConverterComplexity_Mastering;
|
|
|
|
AudioConverterSetProperty(qtexport->audioConverter, kAudioConverterSampleRateConverterComplexity,
|
|
|
|
sizeof(prop), &prop);
|
|
|
|
|
|
|
|
prop = kAudioConverterQuality_Max;
|
|
|
|
AudioConverterSetProperty(qtexport->audioConverter, kAudioConverterSampleRateConverterQuality,
|
|
|
|
sizeof(prop), &prop);
|
|
|
|
}
|
|
|
|
|
|
|
|
write_cookie(qtexport->audioConverter, qtexport->audioFile);
|
|
|
|
|
|
|
|
/* Allocate output buffer */
|
|
|
|
if (qtexport->audioOutputFormat.mBytesPerPacket ==0) /* VBR */
|
|
|
|
AudioConverterGetProperty(qtexport->audioConverter, kAudioConverterPropertyMaximumOutputPacketSize,
|
|
|
|
&propSize, &qtexport->audioCodecMaxOutputPacketSize);
|
|
|
|
else
|
|
|
|
qtexport->audioCodecMaxOutputPacketSize = qtexport->audioOutputFormat.mBytesPerPacket;
|
|
|
|
|
|
|
|
qtexport->audioInputBuffer = MEM_mallocN(AUDIOOUTPUTBUFFERSIZE, "qt_audio_inputPacket");
|
|
|
|
qtexport->audioOutputBuffer = MEM_mallocN(AUDIOOUTPUTBUFFERSIZE, "qt_audio_outputPacket");
|
|
|
|
qtexport->audioOutputPktDesc = MEM_mallocN(sizeof(AudioStreamPacketDescription)*AUDIOOUTPUTBUFFERSIZE/qtexport->audioCodecMaxOutputPacketSize,
|
|
|
|
"qt_audio_pktdesc");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err == noErr) {
|
|
|
|
qtexport->videoTempFileName = [[NSString alloc] initWithCString:tmpnam(nil)
|
|
|
|
encoding:[NSString defaultCStringEncoding]];
|
|
|
|
if (qtexport->videoTempFileName)
|
|
|
|
qtexport->movie = [[QTMovie alloc] initToWritableFile:qtexport->videoTempFileName error:&error];
|
|
|
|
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
qtexport->movie = [[QTMovie alloc] initToWritableFile:qtexport->filename error:&error];
|
2009-12-22 12:01:32 +00:00
|
|
|
|
|
|
|
if(qtexport->movie == nil) {
|
|
|
|
BKE_report(reports, RPT_ERROR, "Unable to create quicktime movie.");
|
|
|
|
success= 0;
|
2010-04-10 09:31:41 +00:00
|
|
|
if (qtexport->filename) [qtexport->filename release];
|
|
|
|
qtexport->filename = nil;
|
|
|
|
if (qtexport->audioFileName) [qtexport->audioFileName release];
|
|
|
|
qtexport->audioFileName = nil;
|
|
|
|
if (qtexport->videoTempFileName) [qtexport->videoTempFileName release];
|
|
|
|
qtexport->videoTempFileName = nil;
|
2009-12-22 12:01:32 +00:00
|
|
|
[QTMovie exitQTKitOnThread];
|
|
|
|
} else {
|
|
|
|
[qtexport->movie retain];
|
|
|
|
[qtexport->movie setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieEditableAttribute];
|
|
|
|
[qtexport->movie setAttribute:@"Made with Blender" forKey:QTMovieCopyrightAttribute];
|
|
|
|
|
|
|
|
qtexport->frameDuration = QTMakeTime(rd->frs_sec_base*1000, rd->frs_sec*1000);
|
|
|
|
|
|
|
|
/* specifying the codec attributes : try to retrieve them from render data first*/
|
|
|
|
if (rd->qtcodecsettings.codecType) {
|
|
|
|
qtexport->frameAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
stringWithCodecType(rd->qtcodecsettings.codecType),
|
|
|
|
QTAddImageCodecType,
|
|
|
|
[NSNumber numberWithLong:((rd->qtcodecsettings.codecSpatialQuality)*codecLosslessQuality)/100],
|
|
|
|
QTAddImageCodecQuality,
|
|
|
|
nil];
|
2009-11-18 15:01:59 +00:00
|
|
|
}
|
2009-12-22 12:01:32 +00:00
|
|
|
else {
|
|
|
|
qtexport->frameAttributes = [NSDictionary dictionaryWithObjectsAndKeys:@"jpeg",
|
|
|
|
QTAddImageCodecType,
|
|
|
|
[NSNumber numberWithLong:codecHighQuality],
|
|
|
|
QTAddImageCodecQuality,
|
|
|
|
nil];
|
|
|
|
}
|
|
|
|
[qtexport->frameAttributes retain];
|
2010-04-10 09:31:41 +00:00
|
|
|
|
|
|
|
if (qtexport->audioFile) {
|
|
|
|
/* Init audio input stream */
|
|
|
|
AUD_DeviceSpecs specs;
|
|
|
|
|
|
|
|
specs.channels = U.audiochannels;
|
|
|
|
specs.format = U.audioformat;
|
|
|
|
specs.rate = U.audiorate;
|
|
|
|
qtexport->audioInputDevice = AUD_openReadDevice(specs);
|
|
|
|
AUD_playDevice(qtexport->audioInputDevice, scene->sound_scene, rd->sfra * rd->frs_sec_base / rd->frs_sec);
|
|
|
|
|
|
|
|
qtexport->audioOutputPktPos = 0;
|
|
|
|
qtexport->audioTotalExportedFrames = 0;
|
|
|
|
qtexport->audioTotalSavedFrames = 0;
|
|
|
|
|
|
|
|
qtexport->audioLastFrame = (rd->efra - rd->sfra) * qtexport->audioInputFormat.mSampleRate * rd->frs_sec_base / rd->frs_sec;
|
|
|
|
}
|
2009-11-18 09:49:42 +00:00
|
|
|
}
|
|
|
|
}
|
2009-11-18 15:01:59 +00:00
|
|
|
|
|
|
|
[pool drain];
|
2009-12-22 12:01:32 +00:00
|
|
|
|
|
|
|
return success;
|
2009-11-18 09:49:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-22 12:01:32 +00:00
|
|
|
int append_qt(struct RenderData *rd, int frame, int *pixels, int rectx, int recty, ReportList *reports)
|
2009-11-18 15:01:59 +00:00
|
|
|
{
|
|
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
NSBitmapImageRep *blBitmapFormatImage;
|
|
|
|
NSImage *frameImage;
|
|
|
|
unsigned char *from_Ptr,*to_Ptr;
|
|
|
|
int y,from_i,to_i;
|
|
|
|
|
|
|
|
|
|
|
|
/* Create bitmap image rep in blender format (32bit RGBA) */
|
|
|
|
blBitmapFormatImage = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
|
|
|
|
pixelsWide:rectx
|
|
|
|
pixelsHigh:recty
|
|
|
|
bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
|
|
|
|
colorSpaceName:NSCalibratedRGBColorSpace
|
|
|
|
bitmapFormat:NSAlphaNonpremultipliedBitmapFormat
|
|
|
|
bytesPerRow:rectx*4
|
|
|
|
bitsPerPixel:32];
|
|
|
|
if (!blBitmapFormatImage) {
|
|
|
|
[pool drain];
|
2009-12-22 12:01:32 +00:00
|
|
|
return 0;
|
2009-11-18 15:01:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
from_Ptr = (unsigned char*)pixels;
|
|
|
|
to_Ptr = (unsigned char*)[blBitmapFormatImage bitmapData];
|
|
|
|
for (y = 0; y < recty; y++) {
|
|
|
|
to_i = (recty-y-1)*rectx;
|
|
|
|
from_i = y*rectx;
|
|
|
|
memcpy(to_Ptr+4*to_i, from_Ptr+4*from_i, 4*rectx);
|
|
|
|
}
|
|
|
|
|
|
|
|
frameImage = [[NSImage alloc] initWithSize:NSMakeSize(rectx, recty)];
|
|
|
|
[frameImage addRepresentation:blBitmapFormatImage];
|
|
|
|
|
|
|
|
/* Add the image to the movie clip */
|
|
|
|
[qtexport->movie addImage:frameImage
|
|
|
|
forDuration:qtexport->frameDuration
|
|
|
|
withAttributes:qtexport->frameAttributes];
|
|
|
|
|
|
|
|
[blBitmapFormatImage release];
|
|
|
|
[frameImage release];
|
2010-04-10 09:31:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
if (qtexport->audioFile) {
|
|
|
|
UInt32 audioPacketsConverted;
|
|
|
|
/* Append audio */
|
|
|
|
while (((double)qtexport->audioTotalExportedFrames / (double) qtexport->audioInputFormat.mSampleRate)
|
|
|
|
< ((double)(frame - rd->sfra)) / (((double)rd->frs_sec) / rd->frs_sec_base)) {
|
|
|
|
|
|
|
|
qtexport->audioBufferList.mNumberBuffers = 1;
|
|
|
|
qtexport->audioBufferList.mBuffers[0].mNumberChannels = qtexport->audioOutputFormat.mChannelsPerFrame;
|
|
|
|
qtexport->audioBufferList.mBuffers[0].mDataByteSize = AUDIOOUTPUTBUFFERSIZE;
|
|
|
|
qtexport->audioBufferList.mBuffers[0].mData = qtexport->audioOutputBuffer;
|
|
|
|
audioPacketsConverted = AUDIOOUTPUTBUFFERSIZE / qtexport->audioCodecMaxOutputPacketSize;
|
|
|
|
|
|
|
|
AudioConverterFillComplexBuffer(qtexport->audioConverter, AudioConverterInputCallback,
|
|
|
|
NULL, &audioPacketsConverted, &qtexport->audioBufferList, qtexport->audioOutputPktDesc);
|
|
|
|
if (audioPacketsConverted) {
|
|
|
|
AudioFileWritePackets(qtexport->audioFile, false, qtexport->audioBufferList.mBuffers[0].mDataByteSize,
|
|
|
|
qtexport->audioOutputPktDesc, qtexport->audioOutputPktPos, &audioPacketsConverted, qtexport->audioOutputBuffer);
|
|
|
|
qtexport->audioOutputPktPos += audioPacketsConverted;
|
|
|
|
|
|
|
|
if (qtexport->audioOutputFormat.mFramesPerPacket) {
|
|
|
|
// this is the common case: format has constant frames per packet
|
|
|
|
qtexport->audioTotalSavedFrames += (audioPacketsConverted * qtexport->audioOutputFormat.mFramesPerPacket);
|
|
|
|
} else {
|
|
|
|
unsigned int i;
|
|
|
|
// if there are variable frames per packet, then we have to do this for each packeet
|
|
|
|
for (i = 0; i < audioPacketsConverted; ++i)
|
|
|
|
qtexport->audioTotalSavedFrames += qtexport->audioOutputPktDesc[i].mVariableFramesInPacket;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-11-18 15:01:59 +00:00
|
|
|
[pool drain];
|
2009-12-22 12:01:32 +00:00
|
|
|
|
|
|
|
return 1;
|
2009-11-18 09:49:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-18 15:01:59 +00:00
|
|
|
void end_qt(void)
|
|
|
|
{
|
2010-04-10 09:31:41 +00:00
|
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
2009-11-18 15:01:59 +00:00
|
|
|
if (qtexport->movie) {
|
2009-11-18 19:35:03 +00:00
|
|
|
|
2010-04-10 09:31:41 +00:00
|
|
|
if (qtexport->audioFile)
|
|
|
|
{
|
|
|
|
NSDictionary *dict = nil;
|
|
|
|
QTMovie *audioTmpMovie = nil;
|
|
|
|
NSError *error;
|
|
|
|
NSFileManager *fileManager;
|
|
|
|
|
|
|
|
/* Mux video and audio then save file */
|
|
|
|
|
|
|
|
/* Write last frames for VBR files */
|
|
|
|
if (qtexport->audioOutputFormat.mBitsPerChannel == 0) {
|
|
|
|
OSStatus err = noErr;
|
|
|
|
AudioConverterPrimeInfo primeInfo;
|
|
|
|
UInt32 primeSize = sizeof(primeInfo);
|
|
|
|
|
|
|
|
err = AudioConverterGetProperty(qtexport->audioConverter, kAudioConverterPrimeInfo, &primeSize, &primeInfo);
|
|
|
|
if (err == noErr) {
|
|
|
|
// there's priming to write out to the file
|
|
|
|
AudioFilePacketTableInfo pti;
|
|
|
|
pti.mPrimingFrames = primeInfo.leadingFrames;
|
|
|
|
pti.mRemainderFrames = primeInfo.trailingFrames;
|
|
|
|
pti.mNumberValidFrames = qtexport->audioTotalSavedFrames - pti.mPrimingFrames - pti.mRemainderFrames;
|
|
|
|
AudioFileSetProperty(qtexport->audioFile, kAudioFilePropertyPacketTableInfo, sizeof(pti), &pti);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
write_cookie(qtexport->audioConverter, qtexport->audioFile);
|
|
|
|
AudioConverterDispose(qtexport->audioConverter);
|
|
|
|
AudioFileClose(qtexport->audioFile);
|
|
|
|
AUD_closeReadDevice(qtexport->audioInputDevice);
|
|
|
|
qtexport->audioFile = NULL;
|
|
|
|
qtexport->audioInputDevice = NULL;
|
|
|
|
MEM_freeN(qtexport->audioInputBuffer);
|
|
|
|
MEM_freeN(qtexport->audioOutputBuffer);
|
|
|
|
MEM_freeN(qtexport->audioOutputPktDesc);
|
|
|
|
|
|
|
|
/* Reopen audio file and merge it */
|
|
|
|
audioTmpMovie = [QTMovie movieWithFile:qtexport->audioFileName error:&error];
|
|
|
|
if (audioTmpMovie) {
|
|
|
|
NSArray *audioTracks = [audioTmpMovie tracksOfMediaType:QTMediaTypeSound];
|
|
|
|
QTTrack *audioTrack = nil;
|
|
|
|
if( [audioTracks count] > 0 )
|
|
|
|
{
|
|
|
|
audioTrack = [audioTracks objectAtIndex:0];
|
|
|
|
}
|
|
|
|
|
|
|
|
if( audioTrack )
|
|
|
|
{
|
|
|
|
QTTimeRange totalRange;
|
|
|
|
totalRange.time = QTZeroTime;
|
|
|
|
totalRange.duration = [[audioTmpMovie attributeForKey:QTMovieDurationAttribute] QTTimeValue];
|
|
|
|
|
|
|
|
[qtexport->movie insertSegmentOfTrack:audioTrack timeRange:totalRange atTime:QTZeroTime];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Save file */
|
|
|
|
dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]
|
|
|
|
forKey:QTMovieFlatten];
|
|
|
|
|
|
|
|
if (dict) {
|
|
|
|
[qtexport->movie writeToFile:qtexport->filename withAttributes:dict];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Delete temp files */
|
|
|
|
fileManager = [[NSFileManager alloc] init];
|
|
|
|
[fileManager removeItemAtPath:qtexport->audioFileName error:&error];
|
|
|
|
[fileManager removeItemAtPath:qtexport->videoTempFileName error:&error];
|
2010-04-17 08:33:42 +00:00
|
|
|
[fileManager release];
|
2010-04-10 09:31:41 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Flush update of the movie file */
|
|
|
|
[qtexport->movie updateMovieFile];
|
|
|
|
|
|
|
|
[qtexport->movie invalidate];
|
|
|
|
}
|
2009-11-18 19:35:03 +00:00
|
|
|
|
2009-11-18 15:01:59 +00:00
|
|
|
/* Clean up movie structure */
|
2010-04-10 09:31:41 +00:00
|
|
|
if (qtexport->filename) [qtexport->filename release];
|
|
|
|
qtexport->filename = nil;
|
|
|
|
if (qtexport->audioFileName) [qtexport->audioFileName release];
|
|
|
|
qtexport->audioFileName = nil;
|
|
|
|
if (qtexport->videoTempFileName) [qtexport->videoTempFileName release];
|
|
|
|
qtexport->videoTempFileName = nil;
|
2009-11-18 15:01:59 +00:00
|
|
|
[qtexport->frameAttributes release];
|
|
|
|
[qtexport->movie release];
|
2010-04-10 09:31:41 +00:00
|
|
|
}
|
2009-11-28 18:16:27 +00:00
|
|
|
|
|
|
|
[QTMovie exitQTKitOnThread];
|
2009-11-18 09:49:42 +00:00
|
|
|
|
|
|
|
if(qtexport) {
|
|
|
|
MEM_freeN(qtexport);
|
|
|
|
qtexport = NULL;
|
2009-11-18 15:01:59 +00:00
|
|
|
}
|
2010-04-10 09:31:41 +00:00
|
|
|
[pool drain];
|
2009-11-18 09:49:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void free_qtcomponentdata(void) {
|
|
|
|
}
|
|
|
|
|
2009-11-20 10:37:50 +00:00
|
|
|
void quicktime_verify_image_type(RenderData *rd)
|
2009-11-18 09:49:42 +00:00
|
|
|
{
|
2009-11-20 10:37:50 +00:00
|
|
|
if (rd->imtype == R_QUICKTIME) {
|
|
|
|
if ((rd->qtcodecsettings.codecType<= 0) ||
|
|
|
|
(rd->qtcodecsettings.codecSpatialQuality <0) ||
|
|
|
|
(rd->qtcodecsettings.codecSpatialQuality > 100)) {
|
2009-11-18 15:01:59 +00:00
|
|
|
|
2009-12-02 11:23:11 +00:00
|
|
|
rd->qtcodecsettings.codecType = kJPEGCodecType;
|
2009-11-20 10:37:50 +00:00
|
|
|
rd->qtcodecsettings.codecSpatialQuality = (codecHighQuality*100)/codecLosslessQuality;
|
2009-11-18 09:49:42 +00:00
|
|
|
}
|
2010-04-10 09:31:41 +00:00
|
|
|
if ((rd->qtcodecsettings.audioSampleRate < 21000) ||
|
|
|
|
(rd->qtcodecsettings.audioSampleRate > 193000))
|
|
|
|
rd->qtcodecsettings.audioSampleRate = 48000;
|
|
|
|
|
|
|
|
if (rd->qtcodecsettings.audioBitDepth == 0)
|
|
|
|
rd->qtcodecsettings.audioBitDepth = AUD_FORMAT_S16;
|
|
|
|
|
|
|
|
if (rd->qtcodecsettings.audioBitRate == 0)
|
|
|
|
rd->qtcodecsettings.audioBitRate = 256000;
|
2009-11-18 09:49:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* _WIN32 || __APPLE__ */
|
|
|
|
#endif /* WITH_QUICKTIME */
|
|
|
|
|