forked from bartvdbraak/blender
QTKit (OSX 64bit): Add audio export to Quicktime
Supports default OSX codecs : Linear PCM, Apple Lossless and AAC Note that AAC codec doesn't support sample rates above 48kHz. If a python/rna guru knows how to easily enforce this limit, he is welcome! Enjoy making Quicktime movies now with audio!
This commit is contained in:
parent
c3ab6bc509
commit
c939331a6c
@ -362,14 +362,38 @@ class RENDER_PT_output(RenderButtonsPanel):
|
|||||||
|
|
||||||
elif rd.file_format == 'QUICKTIME_CARBON':
|
elif rd.file_format == 'QUICKTIME_CARBON':
|
||||||
split = layout.split()
|
split = layout.split()
|
||||||
split.operator("scene.render_set_quicktime_codec")
|
split.operator("scene.render_data_set_quicktime_codec")
|
||||||
|
|
||||||
elif rd.file_format == 'QUICKTIME_QTKIT':
|
elif rd.file_format == 'QUICKTIME_QTKIT':
|
||||||
split = layout.split()
|
split = layout.split()
|
||||||
col = split.column()
|
col = split.column()
|
||||||
col.prop(rd, "quicktime_codec_type")
|
col.prop(rd, "quicktime_codec_type", text="Video Codec")
|
||||||
col.prop(rd, "quicktime_codec_spatial_quality", text="Quality")
|
col.prop(rd, "quicktime_codec_spatial_quality", text="Quality")
|
||||||
|
|
||||||
|
#Audio
|
||||||
|
col.prop(rd,"quicktime_audiocodec_type", text="Audio Codec")
|
||||||
|
if rd.quicktime_audiocodec_type != 'No audio':
|
||||||
|
split = layout.split()
|
||||||
|
col = split.column()
|
||||||
|
if rd.quicktime_audiocodec_type == 'LPCM':
|
||||||
|
col.prop(rd,"quicktime_audio_bitdepth", text="")
|
||||||
|
if wide_ui:
|
||||||
|
col = split.column()
|
||||||
|
col.prop(rd,"quicktime_audio_samplerate", text="")
|
||||||
|
|
||||||
|
split = layout.split()
|
||||||
|
col = split.column()
|
||||||
|
if rd.quicktime_audiocodec_type == 'AAC':
|
||||||
|
col.prop(rd,"quicktime_audio_bitrate")
|
||||||
|
if wide_ui:
|
||||||
|
subsplit = split.split()
|
||||||
|
col = subsplit.column()
|
||||||
|
if rd.quicktime_audiocodec_type == 'AAC':
|
||||||
|
col.prop(rd,"quicktime_audio_codec_isvbr")
|
||||||
|
if wide_ui:
|
||||||
|
col = subsplit.column()
|
||||||
|
col.prop(rd,"quicktime_audio_resampling_hq")
|
||||||
|
|
||||||
|
|
||||||
class RENDER_PT_encoding(RenderButtonsPanel):
|
class RENDER_PT_encoding(RenderButtonsPanel):
|
||||||
bl_label = "Encoding"
|
bl_label = "Encoding"
|
||||||
|
@ -104,6 +104,15 @@ typedef struct QuicktimeCodecSettings {
|
|||||||
int minTemporalQuality; /* in 0-100 scale, to be translated in 0-1024 for qt use */
|
int minTemporalQuality; /* in 0-100 scale, to be translated in 0-1024 for qt use */
|
||||||
int keyFrameRate;
|
int keyFrameRate;
|
||||||
int bitRate; /* bitrate in bps */
|
int bitRate; /* bitrate in bps */
|
||||||
|
|
||||||
|
/* Audio Codec settings */
|
||||||
|
int audiocodecType;
|
||||||
|
int audioSampleRate;
|
||||||
|
short audioBitDepth;
|
||||||
|
short audioChannels;
|
||||||
|
int audioCodecFlags;
|
||||||
|
int audioBitRate;
|
||||||
|
int pad1;
|
||||||
} QuicktimeCodecSettings;
|
} QuicktimeCodecSettings;
|
||||||
|
|
||||||
typedef struct FFMpegCodecData {
|
typedef struct FFMpegCodecData {
|
||||||
|
@ -6,7 +6,7 @@ objs = []
|
|||||||
o = SConscript('intern/SConscript')
|
o = SConscript('intern/SConscript')
|
||||||
objs += o
|
objs += o
|
||||||
|
|
||||||
incs = '#/intern/guardedalloc ../blenkernel ../blenlib ../makesdna intern .'
|
incs = '#/intern/guardedalloc #/intern/audaspace/intern ../blenkernel ../blenlib ../makesdna intern .'
|
||||||
incs += ' ../windowmanager ../editors/include ../gpu ../imbuf ../ikplugin'
|
incs += ' ../windowmanager ../editors/include ../gpu ../imbuf ../ikplugin'
|
||||||
incs += ' ../render/extern/include'
|
incs += ' ../render/extern/include'
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ SET(SRC
|
|||||||
../../../../intern/guardedalloc/intern/mallocn.c
|
../../../../intern/guardedalloc/intern/mallocn.c
|
||||||
../../../../intern/guardedalloc/intern/mmap_win.c)
|
../../../../intern/guardedalloc/intern/mmap_win.c)
|
||||||
|
|
||||||
INCLUDE_DIRECTORIES(../../../../intern/guardedalloc .. ../../makesdna ../../blenkernel ../../blenlib ../../ikplugin ../../windowmanager ../../editors/include ../../gpu ../../imbuf ../../render/extern/include .)
|
INCLUDE_DIRECTORIES(../../../../intern/audaspace/intern ../../../../intern/guardedalloc .. ../../makesdna ../../blenkernel ../../blenlib ../../ikplugin ../../windowmanager ../../editors/include ../../gpu ../../imbuf ../../render/extern/include .)
|
||||||
FILE(GLOB INC_FILES ../*.h ../../makesdna/*.h)
|
FILE(GLOB INC_FILES ../*.h ../../makesdna/*.h)
|
||||||
|
|
||||||
IF(NOT WITH_PYTHON)
|
IF(NOT WITH_PYTHON)
|
||||||
|
@ -46,6 +46,7 @@ endif
|
|||||||
CFLAGS += $(LEVEL_1_C_WARNINGS)
|
CFLAGS += $(LEVEL_1_C_WARNINGS)
|
||||||
|
|
||||||
CPPFLAGS += -I$(NAN_GUARDEDALLOC)/include
|
CPPFLAGS += -I$(NAN_GUARDEDALLOC)/include
|
||||||
|
CPPFLAGS += -I../../../../intern/audaspace/intern
|
||||||
CPPFLAGS += -I../../blenlib
|
CPPFLAGS += -I../../blenlib
|
||||||
CPPFLAGS += -I../../blenkernel
|
CPPFLAGS += -I../../blenkernel
|
||||||
CPPFLAGS += -I../../imbuf
|
CPPFLAGS += -I../../imbuf
|
||||||
|
@ -33,6 +33,7 @@ incs = '#/intern/guardedalloc ../../blenlib ../../blenkernel'
|
|||||||
incs += ' ../../imbuf ../../makesdna ../../makesrna ../../ikplugin'
|
incs += ' ../../imbuf ../../makesdna ../../makesrna ../../ikplugin'
|
||||||
incs += ' ../../windowmanager ../../editors/include'
|
incs += ' ../../windowmanager ../../editors/include'
|
||||||
incs += ' ../../render/extern/include'
|
incs += ' ../../render/extern/include'
|
||||||
|
incs += ' #/intern/audaspace/intern'
|
||||||
|
|
||||||
if env['WITH_BF_OPENEXR']:
|
if env['WITH_BF_OPENEXR']:
|
||||||
defs.append('WITH_OPENEXR')
|
defs.append('WITH_OPENEXR')
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
|
|
||||||
#ifdef WITH_QUICKTIME
|
#ifdef WITH_QUICKTIME
|
||||||
#include "quicktime_export.h"
|
#include "quicktime_export.h"
|
||||||
|
#include "AUD_C-API.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_FFMPEG
|
#ifdef WITH_FFMPEG
|
||||||
@ -523,14 +524,14 @@ static int rna_RenderSettings_qtcodecsettings_codecType_get(PointerRNA *ptr)
|
|||||||
{
|
{
|
||||||
RenderData *rd= (RenderData*)ptr->data;
|
RenderData *rd= (RenderData*)ptr->data;
|
||||||
|
|
||||||
return quicktime_rnatmpvalue_from_codectype(rd->qtcodecsettings.codecType);
|
return quicktime_rnatmpvalue_from_videocodectype(rd->qtcodecsettings.codecType);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rna_RenderSettings_qtcodecsettings_codecType_set(PointerRNA *ptr, int value)
|
static void rna_RenderSettings_qtcodecsettings_codecType_set(PointerRNA *ptr, int value)
|
||||||
{
|
{
|
||||||
RenderData *rd= (RenderData*)ptr->data;
|
RenderData *rd= (RenderData*)ptr->data;
|
||||||
|
|
||||||
rd->qtcodecsettings.codecType = quicktime_codecType_from_rnatmpvalue(value);
|
rd->qtcodecsettings.codecType = quicktime_videocodecType_from_rnatmpvalue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static EnumPropertyItem *rna_RenderSettings_qtcodecsettings_codecType_itemf(bContext *C, PointerRNA *ptr, int *free)
|
static EnumPropertyItem *rna_RenderSettings_qtcodecsettings_codecType_itemf(bContext *C, PointerRNA *ptr, int *free)
|
||||||
@ -541,8 +542,8 @@ static EnumPropertyItem *rna_RenderSettings_qtcodecsettings_codecType_itemf(bCon
|
|||||||
int i=1, totitem= 0;
|
int i=1, totitem= 0;
|
||||||
char id[5];
|
char id[5];
|
||||||
|
|
||||||
for(i=0;i<quicktime_get_num_codecs();i++) {
|
for(i=0;i<quicktime_get_num_videocodecs();i++) {
|
||||||
codecTypeDesc = quicktime_get_codecType_desc(i);
|
codecTypeDesc = quicktime_get_videocodecType_desc(i);
|
||||||
if (!codecTypeDesc) break;
|
if (!codecTypeDesc) break;
|
||||||
|
|
||||||
tmp.value= codecTypeDesc->rnatmpvalue;
|
tmp.value= codecTypeDesc->rnatmpvalue;
|
||||||
@ -558,6 +559,45 @@ static EnumPropertyItem *rna_RenderSettings_qtcodecsettings_codecType_itemf(bCon
|
|||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_QTKIT
|
||||||
|
static int rna_RenderSettings_qtcodecsettings_audiocodecType_get(PointerRNA *ptr)
|
||||||
|
{
|
||||||
|
RenderData *rd= (RenderData*)ptr->data;
|
||||||
|
|
||||||
|
return quicktime_rnatmpvalue_from_audiocodectype(rd->qtcodecsettings.audiocodecType);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rna_RenderSettings_qtcodecsettings_audiocodecType_set(PointerRNA *ptr, int value)
|
||||||
|
{
|
||||||
|
RenderData *rd= (RenderData*)ptr->data;
|
||||||
|
|
||||||
|
rd->qtcodecsettings.audiocodecType = quicktime_audiocodecType_from_rnatmpvalue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static EnumPropertyItem *rna_RenderSettings_qtcodecsettings_audiocodecType_itemf(bContext *C, PointerRNA *ptr, int *free)
|
||||||
|
{
|
||||||
|
EnumPropertyItem *item= NULL;
|
||||||
|
EnumPropertyItem tmp = {0, "", 0, "", ""};
|
||||||
|
QuicktimeCodecTypeDesc *codecTypeDesc;
|
||||||
|
int i=1, totitem= 0;
|
||||||
|
|
||||||
|
for(i=0;i<quicktime_get_num_audiocodecs();i++) {
|
||||||
|
codecTypeDesc = quicktime_get_audiocodecType_desc(i);
|
||||||
|
if (!codecTypeDesc) break;
|
||||||
|
|
||||||
|
tmp.value= codecTypeDesc->rnatmpvalue;
|
||||||
|
tmp.identifier= codecTypeDesc->codecName;
|
||||||
|
tmp.name= codecTypeDesc->codecName;
|
||||||
|
RNA_enum_item_add(&item, &totitem, &tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
RNA_enum_item_end(&item, &totitem);
|
||||||
|
*free= 1;
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int rna_RenderSettings_active_layer_index_get(PointerRNA *ptr)
|
static int rna_RenderSettings_active_layer_index_get(PointerRNA *ptr)
|
||||||
@ -1824,6 +1864,35 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
|
|||||||
static EnumPropertyItem quicktime_codec_type_items[] = {
|
static EnumPropertyItem quicktime_codec_type_items[] = {
|
||||||
{0, "codec", 0, "codec", ""},
|
{0, "codec", 0, "codec", ""},
|
||||||
{0, NULL, 0, NULL, NULL}};
|
{0, NULL, 0, NULL, NULL}};
|
||||||
|
|
||||||
|
#ifdef USE_QTKIT
|
||||||
|
static EnumPropertyItem quicktime_audio_samplerate_items[] = {
|
||||||
|
{22050, "22050", 0, "22kHz", ""},
|
||||||
|
{44100, "44100", 0, "44.1kHz", ""},
|
||||||
|
{48000, "48000", 0, "48kHz", ""},
|
||||||
|
{88200, "88200", 0, "88.2kHz", ""},
|
||||||
|
{96000, "96000", 0, "96kHz", ""},
|
||||||
|
{192000, "192000", 0, "192kHz", ""},
|
||||||
|
{0, NULL, 0, NULL, NULL}};
|
||||||
|
|
||||||
|
static EnumPropertyItem quicktime_audio_bitdepth_items[] = {
|
||||||
|
{AUD_FORMAT_U8, "8BIT", 0, "8bit", ""},
|
||||||
|
{AUD_FORMAT_S16, "16BIT", 0, "16bit", ""},
|
||||||
|
{AUD_FORMAT_S24, "24BIT", 0, "24bit", ""},
|
||||||
|
{AUD_FORMAT_S32, "32BIT", 0, "32bit", ""},
|
||||||
|
{AUD_FORMAT_FLOAT32, "FLOAT32", 0, "float32", ""},
|
||||||
|
{AUD_FORMAT_FLOAT64, "FLOAT64", 0, "float64", ""},
|
||||||
|
{0, NULL, 0, NULL, NULL}};
|
||||||
|
|
||||||
|
static EnumPropertyItem quicktime_audio_bitrate_items[] = {
|
||||||
|
{64000, "64000", 0, "64kbps", ""},
|
||||||
|
{112000, "112000", 0, "112kpbs", ""},
|
||||||
|
{128000, "128000", 0, "128kbps", ""},
|
||||||
|
{192000, "192000", 0, "192kbps", ""},
|
||||||
|
{256000, "256000", 0, "256kbps", ""},
|
||||||
|
{320000, "320000", 0, "320kbps", ""},
|
||||||
|
{0, NULL, 0, NULL, NULL}};
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_FFMPEG
|
#ifdef WITH_FFMPEG
|
||||||
@ -2032,6 +2101,45 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
|
|||||||
RNA_def_property_range(prop, 0, 100);
|
RNA_def_property_range(prop, 0, 100);
|
||||||
RNA_def_property_ui_text(prop, "Spatial quality", "Intra-frame spatial quality level");
|
RNA_def_property_ui_text(prop, "Spatial quality", "Intra-frame spatial quality level");
|
||||||
RNA_def_property_update(prop, NC_SCENE|ND_RENDER_OPTIONS, NULL);
|
RNA_def_property_update(prop, NC_SCENE|ND_RENDER_OPTIONS, NULL);
|
||||||
|
|
||||||
|
#ifdef USE_QTKIT
|
||||||
|
prop= RNA_def_property(srna, "quicktime_audiocodec_type", PROP_ENUM, PROP_NONE);
|
||||||
|
RNA_def_property_enum_bitflag_sdna(prop, NULL, "qtcodecsettings.audiocodecType");
|
||||||
|
RNA_def_property_enum_items(prop, quicktime_codec_type_items);
|
||||||
|
RNA_def_property_enum_funcs(prop, "rna_RenderSettings_qtcodecsettings_audiocodecType_get",
|
||||||
|
"rna_RenderSettings_qtcodecsettings_audiocodecType_set",
|
||||||
|
"rna_RenderSettings_qtcodecsettings_audiocodecType_itemf");
|
||||||
|
RNA_def_property_ui_text(prop, "Audio Codec", "QuickTime audio codec type");
|
||||||
|
RNA_def_property_update(prop, NC_SCENE|ND_RENDER_OPTIONS, NULL);
|
||||||
|
|
||||||
|
prop= RNA_def_property(srna, "quicktime_audio_samplerate", PROP_ENUM, PROP_NONE);
|
||||||
|
RNA_def_property_enum_bitflag_sdna(prop, NULL, "qtcodecsettings.audioSampleRate");
|
||||||
|
RNA_def_property_enum_items(prop, quicktime_audio_samplerate_items);
|
||||||
|
RNA_def_property_ui_text(prop, "Smp Rate", "Sample Rate");
|
||||||
|
RNA_def_property_update(prop, NC_SCENE|ND_RENDER_OPTIONS, NULL);
|
||||||
|
|
||||||
|
prop= RNA_def_property(srna, "quicktime_audio_bitdepth", PROP_ENUM, PROP_NONE);
|
||||||
|
RNA_def_property_enum_bitflag_sdna(prop, NULL, "qtcodecsettings.audioBitDepth");
|
||||||
|
RNA_def_property_enum_items(prop, quicktime_audio_bitdepth_items);
|
||||||
|
RNA_def_property_ui_text(prop, "Bit Depth", "Bit Depth");
|
||||||
|
RNA_def_property_update(prop, NC_SCENE|ND_RENDER_OPTIONS, NULL);
|
||||||
|
|
||||||
|
prop= RNA_def_property(srna, "quicktime_audio_resampling_hq", PROP_BOOLEAN, PROP_NONE);
|
||||||
|
RNA_def_property_boolean_negative_sdna(prop, NULL, "qtcodecsettings.audioCodecFlags", QTAUDIO_FLAG_RESAMPLE_NOHQ);
|
||||||
|
RNA_def_property_ui_text(prop, "HQ", "Use High Quality resampling algorithm");
|
||||||
|
RNA_def_property_update(prop, NC_SCENE|ND_RENDER_OPTIONS, NULL);
|
||||||
|
|
||||||
|
prop= RNA_def_property(srna, "quicktime_audio_codec_isvbr", PROP_BOOLEAN, PROP_NONE);
|
||||||
|
RNA_def_property_boolean_negative_sdna(prop, NULL, "qtcodecsettings.audioCodecFlags", QTAUDIO_FLAG_CODEC_ISCBR);
|
||||||
|
RNA_def_property_ui_text(prop, "VBR", "Use Variable Bit Rate compression (improves quality at same bitrate)");
|
||||||
|
RNA_def_property_update(prop, NC_SCENE|ND_RENDER_OPTIONS, NULL);
|
||||||
|
|
||||||
|
prop= RNA_def_property(srna, "quicktime_audio_bitrate", PROP_ENUM, PROP_NONE);
|
||||||
|
RNA_def_property_enum_bitflag_sdna(prop, NULL, "qtcodecsettings.audioBitRate");
|
||||||
|
RNA_def_property_enum_items(prop, quicktime_audio_bitrate_items);
|
||||||
|
RNA_def_property_ui_text(prop, "Bitrate", "Compressed audio bitrate");
|
||||||
|
RNA_def_property_update(prop, NC_SCENE|ND_RENDER_OPTIONS, NULL);
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_FFMPEG
|
#ifdef WITH_FFMPEG
|
||||||
|
@ -45,6 +45,7 @@ SET(INC
|
|||||||
../render/extern/include
|
../render/extern/include
|
||||||
../include
|
../include
|
||||||
../windowmanager
|
../windowmanager
|
||||||
|
../../../intern/audaspace/intern
|
||||||
)
|
)
|
||||||
|
|
||||||
SET(INC ${INC} ${QUICKTIME_INC})
|
SET(INC ${INC} ${QUICKTIME_INC})
|
||||||
|
@ -23,7 +23,8 @@ incs = ['.',
|
|||||||
'../imbuf/intern',
|
'../imbuf/intern',
|
||||||
'../blenloader',
|
'../blenloader',
|
||||||
'../render/extern/include',
|
'../render/extern/include',
|
||||||
'../editors/include']
|
'../editors/include',
|
||||||
|
'#/intern/audaspace/intern']
|
||||||
|
|
||||||
incs.append(env['BF_QUICKTIME_INC'])
|
incs.append(env['BF_QUICKTIME_INC'])
|
||||||
|
|
||||||
|
@ -59,4 +59,5 @@ CPPFLAGS += -I..
|
|||||||
CPPFLAGS += -I../../blenloader -I../../imbuf/intern -I../../imbuf
|
CPPFLAGS += -I../../blenloader -I../../imbuf/intern -I../../imbuf
|
||||||
CPPFLAGS += -I../../blenlib -I../../makesdna -I../../editors/include -I../../avi
|
CPPFLAGS += -I../../blenlib -I../../makesdna -I../../editors/include -I../../avi
|
||||||
CPPFLAGS += -I../../blenkernel -I../../render/extern/include -I../../windowmanager -I../../makesrna
|
CPPFLAGS += -I../../blenkernel -I../../render/extern/include -I../../windowmanager -I../../makesrna
|
||||||
|
CPPFLAGS += -I../../../intern/audaspace/intern
|
||||||
|
|
||||||
|
@ -36,6 +36,9 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "DNA_scene_types.h"
|
#include "DNA_scene_types.h"
|
||||||
|
#include "DNA_userdef_types.h"
|
||||||
|
|
||||||
|
#include "AUD_C-API.h"
|
||||||
|
|
||||||
#include "BKE_global.h"
|
#include "BKE_global.h"
|
||||||
#include "BKE_scene.h"
|
#include "BKE_scene.h"
|
||||||
@ -57,6 +60,7 @@
|
|||||||
#endif
|
#endif
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import <QTKit/QTKit.h>
|
#import <QTKit/QTKit.h>
|
||||||
|
#include <AudioToolbox/AudioToolbox.h>
|
||||||
|
|
||||||
#if (MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4) || !__LP64__
|
#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
|
#error 64 bit build & OSX 10.5 minimum are needed for QTKit
|
||||||
@ -74,14 +78,34 @@ typedef struct QuicktimeExport {
|
|||||||
|
|
||||||
QTTime frameDuration;
|
QTTime frameDuration;
|
||||||
NSDictionary *frameAttributes;
|
NSDictionary *frameAttributes;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
} QuicktimeExport;
|
} QuicktimeExport;
|
||||||
|
|
||||||
static struct QuicktimeExport *qtexport;
|
static struct QuicktimeExport *qtexport;
|
||||||
|
|
||||||
|
#define AUDIOOUTPUTBUFFERSIZE 65536
|
||||||
|
|
||||||
#pragma mark rna helper functions
|
#pragma mark rna helper functions
|
||||||
|
|
||||||
|
/* Video codec */
|
||||||
static QuicktimeCodecTypeDesc qtCodecList[] = {
|
static QuicktimeCodecTypeDesc qtVideoCodecList[] = {
|
||||||
{kRawCodecType, 1, "Uncompressed"},
|
{kRawCodecType, 1, "Uncompressed"},
|
||||||
{kJPEGCodecType, 2, "JPEG"},
|
{kJPEGCodecType, 2, "JPEG"},
|
||||||
{kMotionJPEGACodecType, 3, "M-JPEG A"},
|
{kMotionJPEGACodecType, 3, "M-JPEG A"},
|
||||||
@ -96,34 +120,75 @@ static QuicktimeCodecTypeDesc qtCodecList[] = {
|
|||||||
{kH264CodecType, 12, "H.264"},
|
{kH264CodecType, 12, "H.264"},
|
||||||
{0,0,NULL}};
|
{0,0,NULL}};
|
||||||
|
|
||||||
static int qtCodecCount = 12;
|
static int qtVideoCodecCount = 12;
|
||||||
|
|
||||||
int quicktime_get_num_codecs() {
|
int quicktime_get_num_videocodecs() {
|
||||||
return qtCodecCount;
|
return qtVideoCodecCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
QuicktimeCodecTypeDesc* quicktime_get_codecType_desc(int indexValue) {
|
QuicktimeCodecTypeDesc* quicktime_get_videocodecType_desc(int indexValue) {
|
||||||
if ((indexValue>=0) && (indexValue < qtCodecCount))
|
if ((indexValue>=0) && (indexValue < qtVideoCodecCount))
|
||||||
return &qtCodecList[indexValue];
|
return &qtVideoCodecList[indexValue];
|
||||||
else
|
else
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int quicktime_rnatmpvalue_from_codectype(int codecType) {
|
int quicktime_rnatmpvalue_from_videocodectype(int codecType) {
|
||||||
int i;
|
int i;
|
||||||
for (i=0;i<qtCodecCount;i++) {
|
for (i=0;i<qtVideoCodecCount;i++) {
|
||||||
if (qtCodecList[i].codecType == codecType)
|
if (qtVideoCodecList[i].codecType == codecType)
|
||||||
return qtCodecList[i].rnatmpvalue;
|
return qtVideoCodecList[i].rnatmpvalue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int quicktime_codecType_from_rnatmpvalue(int rnatmpvalue) {
|
int quicktime_videocodecType_from_rnatmpvalue(int rnatmpvalue) {
|
||||||
int i;
|
int i;
|
||||||
for (i=0;i<qtCodecCount;i++) {
|
for (i=0;i<qtVideoCodecCount;i++) {
|
||||||
if (qtCodecList[i].rnatmpvalue == rnatmpvalue)
|
if (qtVideoCodecList[i].rnatmpvalue == rnatmpvalue)
|
||||||
return qtCodecList[i].codecType;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -172,14 +237,68 @@ void filepath_qt(char *string, RenderData *rd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#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)
|
||||||
|
*ioNumberDataPackets += qtexport->audioLastFrame - qtexport->audioTotalExportedFrames;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark export functions
|
#pragma mark export functions
|
||||||
|
|
||||||
int start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty, ReportList *reports)
|
int start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty, ReportList *reports)
|
||||||
{
|
{
|
||||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||||
NSError *error;
|
NSError *error;
|
||||||
char name[2048];
|
char name[1024];
|
||||||
int success= 1;
|
int success= 1;
|
||||||
|
OSStatus err=noErr;
|
||||||
|
|
||||||
if(qtexport == NULL) qtexport = MEM_callocN(sizeof(QuicktimeExport), "QuicktimeExport");
|
if(qtexport == NULL) qtexport = MEM_callocN(sizeof(QuicktimeExport), "QuicktimeExport");
|
||||||
|
|
||||||
@ -192,14 +311,223 @@ int start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty, R
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
makeqtstring(rd, name);
|
makeqtstring(rd, name);
|
||||||
qtexport->filename = [NSString stringWithCString:name
|
qtexport->filename = [[NSString alloc] initWithCString:name
|
||||||
encoding:[NSString defaultCStringEncoding]];
|
encoding:[NSString defaultCStringEncoding]];
|
||||||
|
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;
|
||||||
|
qtexport->audioInputFormat.mFramesPerPacket = 1;
|
||||||
|
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;
|
||||||
|
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];
|
qtexport->movie = [[QTMovie alloc] initToWritableFile:qtexport->filename error:&error];
|
||||||
|
|
||||||
if(qtexport->movie == nil) {
|
if(qtexport->movie == nil) {
|
||||||
BKE_report(reports, RPT_ERROR, "Unable to create quicktime movie.");
|
BKE_report(reports, RPT_ERROR, "Unable to create quicktime movie.");
|
||||||
success= 0;
|
success= 0;
|
||||||
NSLog(@"Unable to create quicktime movie : %@",[error localizedDescription]);
|
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;
|
||||||
[QTMovie exitQTKitOnThread];
|
[QTMovie exitQTKitOnThread];
|
||||||
} else {
|
} else {
|
||||||
[qtexport->movie retain];
|
[qtexport->movie retain];
|
||||||
@ -226,6 +554,23 @@ int start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty, R
|
|||||||
nil];
|
nil];
|
||||||
}
|
}
|
||||||
[qtexport->frameAttributes retain];
|
[qtexport->frameAttributes retain];
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,6 +621,41 @@ int append_qt(struct RenderData *rd, int frame, int *pixels, int rectx, int rect
|
|||||||
|
|
||||||
[blBitmapFormatImage release];
|
[blBitmapFormatImage release];
|
||||||
[frameImage release];
|
[frameImage release];
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
[pool drain];
|
[pool drain];
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@ -284,14 +664,93 @@ int append_qt(struct RenderData *rd, int frame, int *pixels, int rectx, int rect
|
|||||||
|
|
||||||
void end_qt(void)
|
void end_qt(void)
|
||||||
{
|
{
|
||||||
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||||
if (qtexport->movie) {
|
if (qtexport->movie) {
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
else {
|
||||||
/* Flush update of the movie file */
|
/* Flush update of the movie file */
|
||||||
[qtexport->movie updateMovieFile];
|
[qtexport->movie updateMovieFile];
|
||||||
|
|
||||||
[qtexport->movie invalidate];
|
[qtexport->movie invalidate];
|
||||||
|
}
|
||||||
|
|
||||||
/* Clean up movie structure */
|
/* Clean up movie structure */
|
||||||
[qtexport->filename release];
|
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;
|
||||||
[qtexport->frameAttributes release];
|
[qtexport->frameAttributes release];
|
||||||
[qtexport->movie release];
|
[qtexport->movie release];
|
||||||
}
|
}
|
||||||
@ -302,6 +761,7 @@ void end_qt(void)
|
|||||||
MEM_freeN(qtexport);
|
MEM_freeN(qtexport);
|
||||||
qtexport = NULL;
|
qtexport = NULL;
|
||||||
}
|
}
|
||||||
|
[pool drain];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -318,6 +778,15 @@ void quicktime_verify_image_type(RenderData *rd)
|
|||||||
rd->qtcodecsettings.codecType = kJPEGCodecType;
|
rd->qtcodecsettings.codecType = kJPEGCodecType;
|
||||||
rd->qtcodecsettings.codecSpatialQuality = (codecHighQuality*100)/codecLosslessQuality;
|
rd->qtcodecsettings.codecSpatialQuality = (codecHighQuality*100)/codecLosslessQuality;
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ static int sframe;
|
|||||||
|
|
||||||
/* RNA functions */
|
/* RNA functions */
|
||||||
|
|
||||||
static QuicktimeCodecTypeDesc qtCodecList[] = {
|
static QuicktimeCodecTypeDesc qtVideoCodecList[] = {
|
||||||
{kRawCodecType, 1, "Uncompressed"},
|
{kRawCodecType, 1, "Uncompressed"},
|
||||||
{kJPEGCodecType, 2, "JPEG"},
|
{kJPEGCodecType, 2, "JPEG"},
|
||||||
{kMotionJPEGACodecType, 3, "M-JPEG A"},
|
{kMotionJPEGACodecType, 3, "M-JPEG A"},
|
||||||
@ -138,34 +138,34 @@ static QuicktimeCodecTypeDesc qtCodecList[] = {
|
|||||||
{kH264CodecType, 12, "H.264"},
|
{kH264CodecType, 12, "H.264"},
|
||||||
{0,0,NULL}};
|
{0,0,NULL}};
|
||||||
|
|
||||||
static int qtCodecCount = 12;
|
static int qtVideoCodecCount = 12;
|
||||||
|
|
||||||
int quicktime_get_num_codecs() {
|
int quicktime_get_num_videocodecs() {
|
||||||
return qtCodecCount;
|
return qtVideoCodecCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
QuicktimeCodecTypeDesc* quicktime_get_codecType_desc(int indexValue) {
|
QuicktimeCodecTypeDesc* quicktime_get_videocodecType_desc(int indexValue) {
|
||||||
if ((indexValue>=0) && (indexValue < qtCodecCount))
|
if ((indexValue>=0) && (indexValue < qtVideoCodecCount))
|
||||||
return &qtCodecList[indexValue];
|
return &qtVideoCodecList[indexValue];
|
||||||
else
|
else
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int quicktime_rnatmpvalue_from_codectype(int codecType) {
|
int quicktime_rnatmpvalue_from_videocodectype(int codecType) {
|
||||||
int i;
|
int i;
|
||||||
for (i=0;i<qtCodecCount;i++) {
|
for (i=0;i<qtVideoCodecCount;i++) {
|
||||||
if (qtCodecList[i].codecType == codecType)
|
if (qtVideoCodecList[i].codecType == codecType)
|
||||||
return qtCodecList[i].rnatmpvalue;
|
return qtVideoCodecList[i].rnatmpvalue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int quicktime_codecType_from_rnatmpvalue(int rnatmpvalue) {
|
int quicktime_videocodecType_from_rnatmpvalue(int rnatmpvalue) {
|
||||||
int i;
|
int i;
|
||||||
for (i=0;i<qtCodecCount;i++) {
|
for (i=0;i<qtVideoCodecCount;i++) {
|
||||||
if (qtCodecList[i].rnatmpvalue == rnatmpvalue)
|
if (qtVideoCodecList[i].rnatmpvalue == rnatmpvalue)
|
||||||
return qtCodecList[i].codecType;
|
return qtVideoCodecList[i].codecType;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -34,6 +34,10 @@
|
|||||||
#define __AIFF__
|
#define __AIFF__
|
||||||
|
|
||||||
|
|
||||||
|
#define QTAUDIO_FLAG_RESAMPLE_NOHQ 1
|
||||||
|
#define QTAUDIO_FLAG_CODEC_ISCBR 2
|
||||||
|
|
||||||
|
|
||||||
/*Codec list*/
|
/*Codec list*/
|
||||||
typedef struct QuicktimeCodecTypeDesc {
|
typedef struct QuicktimeCodecTypeDesc {
|
||||||
int codecType;
|
int codecType;
|
||||||
@ -54,10 +58,19 @@ void filepath_qt(char *string, struct RenderData *rd);
|
|||||||
|
|
||||||
/*RNA helper functions */
|
/*RNA helper functions */
|
||||||
void quicktime_verify_image_type(struct RenderData *rd); //used by RNA for defaults values init, if needed
|
void quicktime_verify_image_type(struct RenderData *rd); //used by RNA for defaults values init, if needed
|
||||||
int quicktime_get_num_codecs();
|
/*Video codec type*/
|
||||||
QuicktimeCodecTypeDesc* quicktime_get_codecType_desc(int indexValue);
|
int quicktime_get_num_videocodecs();
|
||||||
int quicktime_rnatmpvalue_from_codectype(int codecType);
|
QuicktimeCodecTypeDesc* quicktime_get_videocodecType_desc(int indexValue);
|
||||||
int quicktime_codecType_from_rnatmpvalue(int rnatmpvalue);
|
int quicktime_rnatmpvalue_from_videocodectype(int codecType);
|
||||||
|
int quicktime_videocodecType_from_rnatmpvalue(int rnatmpvalue);
|
||||||
|
|
||||||
|
#ifdef USE_QTKIT
|
||||||
|
/*Audio codec type*/
|
||||||
|
int quicktime_get_num_audiocodecs();
|
||||||
|
QuicktimeCodecTypeDesc* quicktime_get_audiocodecType_desc(int indexValue);
|
||||||
|
int quicktime_rnatmpvalue_from_audiocodectype(int codecType);
|
||||||
|
int quicktime_audiocodecType_from_rnatmpvalue(int rnatmpvalue);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef USE_QTKIT
|
#ifndef USE_QTKIT
|
||||||
void SCENE_OT_render_data_set_quicktime_codec(struct wmOperatorType *ot); //Operator to raise quicktime standard dialog to request codec settings
|
void SCENE_OT_render_data_set_quicktime_codec(struct wmOperatorType *ot); //Operator to raise quicktime standard dialog to request codec settings
|
||||||
|
Loading…
Reference in New Issue
Block a user