/**
 * $Id$
 *
 * quicktime_export.c
 *
 * 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,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *
 * The Original Code is written by Rob Haarsma (phase)
 *
 * Contributor(s): Stefan Gartner (sgefant)
 *
 * ***** END GPL LICENSE BLOCK *****
 */

#ifdef WITH_QUICKTIME
#if defined(_WIN32) || defined(__APPLE__)
#ifndef USE_QTKIT

#include "DNA_scene_types.h"
#include "DNA_windowmanager_types.h"

#include "WM_api.h"
#include "WM_types.h"

#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_report.h"
#include "BKE_scene.h"

#include "BLI_blenlib.h"

#include "BLO_sys_types.h"

#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"

#include "MEM_guardedalloc.h"

#include "quicktime_import.h"
#include "quicktime_export.h"

#ifdef _WIN32
#include <QTML.h>
#include <Movies.h>
#include <QuickTimeComponents.h>
#include <TextUtils.h> 
#include <string.h>
#include <memory.h>

#endif /* _WIN32 */

#ifdef __APPLE__
/* evil */
#ifndef __AIFF__
#define __AIFF__
#endif
#include <QuickTime/Movies.h>
#include <QuickTime/QuickTimeComponents.h>
#include <fcntl.h> /* open() */
#include <unistd.h> /* close() */
#include <sys/stat.h> /* file permissions */
#endif /* __APPLE__ */

#define	kMyCreatorType	FOUR_CHAR_CODE('TVOD')
#define	kTrackStart		0
#define	kMediaStart		0

static void QT_StartAddVideoSamplesToMedia (const Rect *trackFrame, int rectx, int recty, struct ReportList *reports);
static void QT_DoAddVideoSamplesToMedia (int frame, int *pixels, int rectx, int recty, struct ReportList *reports);
static void QT_EndAddVideoSamplesToMedia (void);
static void QT_CreateMyVideoTrack (int rectx, int recty, struct ReportList *reports);
static void QT_EndCreateMyVideoTrack (struct ReportList *reports);
static void check_renderbutton_framerate(struct RenderData *rd, struct ReportList *reports);
static int get_qtcodec_settings(struct RenderData *rd, struct ReportList *reports);

typedef struct QuicktimeExport {

	FSSpec		theSpec;
	short		resRefNum;
	Str255		qtfilename;

	Media		theMedia;
	Movie		theMovie;
	Track		theTrack;

	GWorldPtr			theGWorld;
	PixMapHandle		thePixMap;
	ImageDescription	**anImageDescription;

	ImBuf		*ibuf;	//imagedata for Quicktime's Gworld
	ImBuf		*ibuf2;	//copy of renderdata, to be Y-flipped

} QuicktimeExport;

typedef struct QuicktimeComponentData {

	ComponentInstance	theComponent;
	SCTemporalSettings  gTemporalSettings;
	SCSpatialSettings   gSpatialSettings;
	SCDataRateSettings  aDataRateSetting;
	TimeValue			duration;
	long				kVideoTimeScale;

} QuicktimeComponentData;

static struct QuicktimeExport *qtexport;
static struct QuicktimeComponentData *qtdata;

static int	sframe;

/* RNA functions */

static QuicktimeCodecTypeDesc qtVideoCodecList[] = {
	{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}};

static int qtVideoCodecCount = 12;

int quicktime_get_num_videocodecs() {
	return qtVideoCodecCount;
}

QuicktimeCodecTypeDesc* quicktime_get_videocodecType_desc(int indexValue) {
	if ((indexValue>=0) && (indexValue < qtVideoCodecCount))
		return &qtVideoCodecList[indexValue];
	else
		return NULL;
}

int quicktime_rnatmpvalue_from_videocodectype(int codecType) {
	int i;
	for (i=0;i<qtVideoCodecCount;i++) {
		if (qtVideoCodecList[i].codecType == codecType)
			return qtVideoCodecList[i].rnatmpvalue;
	}
	
	return 0;
}

int quicktime_videocodecType_from_rnatmpvalue(int rnatmpvalue) {
	int i;
	for (i=0;i<qtVideoCodecCount;i++) {
		if (qtVideoCodecList[i].rnatmpvalue == rnatmpvalue)
			return qtVideoCodecList[i].codecType;
	}
	
	return 0;	
}



static void CheckError(OSErr err, char *msg, ReportList *reports)
{
	if(err != noErr) {
		BKE_reportf(reports, RPT_ERROR, "%s: %d", msg, err);
	}
}


static OSErr QT_SaveCodecSettingsToScene(RenderData *rd, ReportList *reports)
{	
	QTAtomContainer		myContainer = NULL;
	ComponentResult		myErr = noErr;
	Ptr					myPtr;
	long				mySize = 0;

	CodecInfo			ci;

	QuicktimeCodecData *qcd = rd->qtcodecdata;
	
	// check if current scene already has qtcodec settings, and clear them
	if (qcd) {
		free_qtcodecdata(qcd);
	} else {
		qcd = rd->qtcodecdata = MEM_callocN(sizeof(QuicktimeCodecData), "QuicktimeCodecData");
	}

	// obtain all current codec settings
	SCSetInfo(qtdata->theComponent, scTemporalSettingsType,	&qtdata->gTemporalSettings);
	SCSetInfo(qtdata->theComponent, scSpatialSettingsType,	&qtdata->gSpatialSettings);
	SCSetInfo(qtdata->theComponent, scDataRateSettingsType,	&qtdata->aDataRateSetting);

	// retreive codecdata from quicktime in a atomcontainer
	myErr = SCGetSettingsAsAtomContainer(qtdata->theComponent,  &myContainer);
	if (myErr != noErr) {
		BKE_reportf(reports, RPT_ERROR, "Quicktime: SCGetSettingsAsAtomContainer failed\n"); 
		goto bail;
	}

	// get the size of the atomcontainer
	mySize = GetHandleSize((Handle)myContainer);

	// lock and convert the atomcontainer to a *valid* pointer
	QTLockContainer(myContainer);
	myPtr = *(Handle)myContainer;

	// copy the Quicktime data into the blender qtcodecdata struct
	if (myPtr) {
		qcd->cdParms = MEM_mallocN(mySize, "qt.cdParms");
		memcpy(qcd->cdParms, myPtr, mySize);
		qcd->cdSize = mySize;

		GetCodecInfo (&ci, qtdata->gSpatialSettings.codecType, 0);
	} else {
		BKE_reportf(reports, RPT_ERROR, "Quicktime: QT_SaveCodecSettingsToScene failed\n"); 
	}

	QTUnlockContainer(myContainer);

bail:
	if (myContainer != NULL)
		QTDisposeAtomContainer(myContainer);
		
	return((OSErr)myErr);
}


static OSErr QT_GetCodecSettingsFromScene(RenderData *rd, ReportList *reports)
{	
	Handle				myHandle = NULL;
	ComponentResult		myErr = noErr;

	QuicktimeCodecData *qcd = rd->qtcodecdata;

	// if there is codecdata in the blendfile, convert it to a Quicktime handle 
	if (qcd) {
		myHandle = NewHandle(qcd->cdSize);
		PtrToHand( qcd->cdParms, &myHandle, qcd->cdSize);
	}
		
	// restore codecsettings to the quicktime component
	if(qcd->cdParms && qcd->cdSize) {
		myErr = SCSetSettingsFromAtomContainer((GraphicsExportComponent)qtdata->theComponent, (QTAtomContainer)myHandle);
		if (myErr != noErr) {
			BKE_reportf(reports, RPT_ERROR, "Quicktime: SCSetSettingsFromAtomContainer failed\n"); 
			goto bail;
		}

		// update runtime codecsettings for use with the codec dialog
		SCGetInfo(qtdata->theComponent, scDataRateSettingsType,	&qtdata->aDataRateSetting);
		SCGetInfo(qtdata->theComponent, scSpatialSettingsType,	&qtdata->gSpatialSettings);
		SCGetInfo(qtdata->theComponent, scTemporalSettingsType,	&qtdata->gTemporalSettings);


		//Fill the render QuicktimeCodecSettigns struct
		rd->qtcodecsettings.codecTemporalQuality = (qtdata->gTemporalSettings.temporalQuality * 100) / codecLosslessQuality;
		//Do not override scene frame rate (qtdata->gTemporalSettings.framerate)
		rd->qtcodecsettings.keyFrameRate = qtdata->gTemporalSettings.keyFrameRate;
		
		rd->qtcodecsettings.codecType = qtdata->gSpatialSettings.codecType;
		rd->qtcodecsettings.codec = (int)qtdata->gSpatialSettings.codec;
		rd->qtcodecsettings.colorDepth = qtdata->gSpatialSettings.depth;
		rd->qtcodecsettings.codecSpatialQuality = (qtdata->gSpatialSettings.spatialQuality * 100) / codecLosslessQuality;
		
		rd->qtcodecsettings.bitRate = qtdata->aDataRateSetting.dataRate;
		rd->qtcodecsettings.minSpatialQuality = (qtdata->aDataRateSetting.minSpatialQuality * 100) / codecLosslessQuality;
		rd->qtcodecsettings.minTemporalQuality = (qtdata->aDataRateSetting.minTemporalQuality * 100) / codecLosslessQuality;
		//Frame duration is already known (qtdata->aDataRateSetting.frameDuration)
		
	} else {
		BKE_reportf(reports, RPT_ERROR, "Quicktime: QT_GetCodecSettingsFromScene failed\n"); 
	}
bail:
	if (myHandle != NULL)
		DisposeHandle(myHandle);
		
	return((OSErr)myErr);
}


static OSErr QT_AddUserDataTextToMovie (Movie theMovie, char *theText, OSType theType)
{
	UserData					myUserData = NULL;
	Handle						myHandle = NULL;
	long						myLength = strlen(theText);
	OSErr						myErr = noErr;

	// get the movie's user data list
	myUserData = GetMovieUserData(theMovie);
	if (myUserData == NULL)
		return(paramErr);
	
	// copy the specified text into a new handle
	myHandle = NewHandleClear(myLength);
	if (myHandle == NULL)
		return(MemError());

	BlockMoveData(theText, *myHandle, myLength);

	// add the data to the movie's user data
	myErr = AddUserDataText(myUserData, myHandle, theType, 1, (short)GetScriptManagerVariable(smRegionCode));

	// clean up
	DisposeHandle(myHandle);
	return(myErr);
}


static void QT_CreateMyVideoTrack(int rectx, int recty, ReportList *reports)
{
	OSErr err = noErr;
	Rect trackFrame;
//	MatrixRecord myMatrix;

	trackFrame.top = 0;
	trackFrame.left = 0;
	trackFrame.bottom = recty;
	trackFrame.right = rectx;
	
	qtexport->theTrack = NewMovieTrack (qtexport->theMovie, 
							FixRatio(trackFrame.right,1),
							FixRatio(trackFrame.bottom,1), 
							0);
	CheckError( GetMoviesError(), "NewMovieTrack error", reports );

//	SetIdentityMatrix(&myMatrix);
//	ScaleMatrix(&myMatrix, fixed1, Long2Fix(-1), 0, 0);
//	TranslateMatrix(&myMatrix, 0, Long2Fix(trackFrame.bottom));
//	SetMovieMatrix(qtexport->theMovie, &myMatrix);

	qtexport->theMedia = NewTrackMedia (qtexport->theTrack,
							VideoMediaType,
							qtdata->kVideoTimeScale,
							nil,
							0);
	CheckError( GetMoviesError(), "NewTrackMedia error", reports );

	err = BeginMediaEdits (qtexport->theMedia);
	CheckError( err, "BeginMediaEdits error", reports );

	QT_StartAddVideoSamplesToMedia (&trackFrame, rectx, recty, reports);
} 


static void QT_EndCreateMyVideoTrack(ReportList *reports)
{
	OSErr err = noErr;

	QT_EndAddVideoSamplesToMedia ();

	err = EndMediaEdits (qtexport->theMedia);
	CheckError( err, "EndMediaEdits error", reports );

	err = InsertMediaIntoTrack (qtexport->theTrack,
								kTrackStart,/* track start time */
								kMediaStart,/* media start time */
								GetMediaDuration (qtexport->theMedia),
								fixed1);
	CheckError( err, "InsertMediaIntoTrack error", reports );
} 


static void QT_StartAddVideoSamplesToMedia (const Rect *trackFrame, int rectx, int recty, ReportList *reports)
{
	SCTemporalSettings gTemporalSettings;
	OSErr err = noErr;

	qtexport->ibuf = IMB_allocImBuf (rectx, recty, 32, IB_rect);
	qtexport->ibuf2 = IMB_allocImBuf (rectx, recty, 32, IB_rect);

	err = NewGWorldFromPtr( &qtexport->theGWorld,
							k32ARGBPixelFormat,
							trackFrame,
							NULL, NULL, 0,
							(Ptr)qtexport->ibuf->rect,
							rectx * 4 );
	CheckError (err, "NewGWorldFromPtr error", reports);

	qtexport->thePixMap = GetGWorldPixMap(qtexport->theGWorld);
	LockPixels(qtexport->thePixMap);

	SCDefaultPixMapSettings (qtdata->theComponent, qtexport->thePixMap, true);

	// workaround for crash with H.264, which requires an upgrade to
	// the new callback based api for proper encoding, but that's not
	// really compatible with rendering out frames sequentially
	gTemporalSettings = qtdata->gTemporalSettings;
	if(qtdata->gSpatialSettings.codecType == kH264CodecType) {
		if(gTemporalSettings.temporalQuality != codecMinQuality) {
			BKE_reportf(reports, RPT_WARNING, "Only minimum quality compression supported for QuickTime H.264.\n");
			gTemporalSettings.temporalQuality = codecMinQuality;
		}
	}

	SCSetInfo(qtdata->theComponent, scTemporalSettingsType,	&gTemporalSettings);
	SCSetInfo(qtdata->theComponent, scSpatialSettingsType,	&qtdata->gSpatialSettings);
	SCSetInfo(qtdata->theComponent, scDataRateSettingsType,	&qtdata->aDataRateSetting);

	err = SCCompressSequenceBegin(qtdata->theComponent, qtexport->thePixMap, NULL, &qtexport->anImageDescription); 
	CheckError (err, "SCCompressSequenceBegin error", reports );
}


static void QT_DoAddVideoSamplesToMedia (int frame, int *pixels, int rectx, int recty, ReportList *reports)
{
	OSErr	err = noErr;
	Rect	imageRect;

	int		index;
	int		boxsize;
	unsigned char *from, *to;

	short	syncFlag;
	long	dataSize;
	Handle	compressedData;
	Ptr		myPtr;


	//copy and flip renderdata
	memcpy(qtexport->ibuf2->rect, pixels, 4*rectx*recty);
	IMB_flipy(qtexport->ibuf2);

	//get pointers to parse bitmapdata
	myPtr = GetPixBaseAddr(qtexport->thePixMap);
	imageRect = (**qtexport->thePixMap).bounds;

	from = (unsigned char *) qtexport->ibuf2->rect;
	to = (unsigned char *) myPtr;

	//parse RGBA bitmap into Quicktime's ARGB GWorld
	boxsize = rectx * recty;
	for( index = 0; index < boxsize; index++) {
		to[0] = from[3];
		to[1] = from[0];
		to[2] = from[1];
		to[3] = from[2];
		to +=4, from += 4;
	}

	err = SCCompressSequenceFrame(qtdata->theComponent,
		qtexport->thePixMap,
		&imageRect,
		&compressedData,
		&dataSize,
		&syncFlag);
	CheckError(err, "SCCompressSequenceFrame error", reports);

	err = AddMediaSample(qtexport->theMedia,
		compressedData,
		0,
		dataSize,
		qtdata->duration,
		(SampleDescriptionHandle)qtexport->anImageDescription,
		1,
		syncFlag,
		NULL);
	CheckError(err, "AddMediaSample error", reports);
}


static void QT_EndAddVideoSamplesToMedia (void)
{
	SCCompressSequenceEnd(qtdata->theComponent);

	UnlockPixels(qtexport->thePixMap);
	if (qtexport->theGWorld)
		DisposeGWorld (qtexport->theGWorld);

	if (qtexport->ibuf)
		IMB_freeImBuf(qtexport->ibuf);

	if (qtexport->ibuf2)
		IMB_freeImBuf(qtexport->ibuf2);
} 


void filepath_qt(char *string, RenderData *rd) {
	char txt[64];

	if (string==0) return;

	strcpy(string, rd->pic);
	BLI_path_abs(string, G.main->name);

	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);
	}
}


int start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty, ReportList *reports) {
	OSErr err = noErr;

	char name[2048];
	char theFullPath[255];

#ifdef __APPLE__
	int		myFile;
	FSRef	myRef;
#else
	char	*qtname;
#endif
	int success= 1;

	if(qtexport == NULL) qtexport = MEM_callocN(sizeof(QuicktimeExport), "QuicktimeExport");

	if(qtdata) {
		if(qtdata->theComponent) CloseComponent(qtdata->theComponent);
		free_qtcomponentdata();
	}

	qtdata = MEM_callocN(sizeof(QuicktimeComponentData), "QuicktimeCodecDataExt");

	if(rd->qtcodecdata == NULL || rd->qtcodecdata->cdParms == NULL) {
		get_qtcodec_settings(rd, reports);
	} else {
		qtdata->theComponent = OpenDefaultComponent(StandardCompressionType, StandardCompressionSubType);

		QT_GetCodecSettingsFromScene(rd, reports);
		check_renderbutton_framerate(rd, reports);
	}
	
	sframe = (rd->sfra);

	filepath_qt(name, rd);

#ifdef __APPLE__
	EnterMoviesOnThread(0);
	sprintf(theFullPath, "%s", name);

	/* hack: create an empty file to make FSPathMakeRef() happy */
	myFile = open(theFullPath, O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRUSR|S_IWUSR);
	if (myFile < 0) {
		BKE_reportf(reports, RPT_ERROR, "error while creating movie file!\n");
		/* do something? */
	}
	close(myFile);
	err = FSPathMakeRef((const UInt8 *)theFullPath, &myRef, 0);
	CheckError(err, "FsPathMakeRef error", reports);
	err = FSGetCatalogInfo(&myRef, kFSCatInfoNone, NULL, NULL, &qtexport->theSpec, NULL);
	CheckError(err, "FsGetCatalogInfoRef error", reports);
#endif
#ifdef _WIN32
	qtname = get_valid_qtname(name);
	sprintf(theFullPath, "%s", qtname);
	strcpy(name, qtname);
	MEM_freeN(qtname);
	
	CopyCStringToPascal(theFullPath, qtexport->qtfilename);
	err = FSMakeFSSpec(0, 0L, qtexport->qtfilename, &qtexport->theSpec);
#endif

	err = CreateMovieFile (&qtexport->theSpec, 
						kMyCreatorType,
						smCurrentScript, 
						createMovieFileDeleteCurFile | createMovieFileDontCreateResFile,
						&qtexport->resRefNum, 
						&qtexport->theMovie );
	CheckError(err, "CreateMovieFile error", reports);

	if(err != noErr) {
		BKE_reportf(reports, RPT_ERROR, "Unable to create Quicktime movie: %s", name);
		success= 0;
#ifdef __APPLE__
		ExitMoviesOnThread();
#endif
	} else {
		//printf("Created QuickTime movie: %s\n", name);

		QT_CreateMyVideoTrack(rectx, recty, reports);
	}

	return success;
}


int append_qt(struct RenderData *rd, int frame, int *pixels, int rectx, int recty, ReportList *reports) {
	QT_DoAddVideoSamplesToMedia(frame, pixels, rectx, recty, reports);
	return 1;
}


void end_qt(void) {
	OSErr err = noErr;
	short resId = movieInDataForkResID;

	if(qtexport->theMovie) {
		QT_EndCreateMyVideoTrack(NULL);

		err = AddMovieResource (qtexport->theMovie, qtexport->resRefNum, &resId, qtexport->qtfilename);
		CheckError(err, "AddMovieResource error", NULL);

		err = QT_AddUserDataTextToMovie(qtexport->theMovie, "Made with Blender", kUserDataTextInformation);
		CheckError(err, "AddUserDataTextToMovie error", NULL);

		err = UpdateMovieResource(qtexport->theMovie, qtexport->resRefNum, resId, qtexport->qtfilename);
		CheckError(err, "UpdateMovieResource error", NULL);

		if(qtexport->resRefNum) CloseMovieFile(qtexport->resRefNum);

		DisposeMovie(qtexport->theMovie);

		//printf("Finished QuickTime movie.\n");
	}

#ifdef __APPLE__
		ExitMoviesOnThread();
#endif
	
	if(qtexport) {
		MEM_freeN(qtexport);
		qtexport = NULL;
	}
}


void free_qtcomponentdata(void) {
	if(qtdata) {
		if(qtdata->theComponent) CloseComponent(qtdata->theComponent);
		MEM_freeN(qtdata);
		qtdata = NULL;
	}
}


static void check_renderbutton_framerate(RenderData *rd, ReportList *reports) 
{
	// to keep float framerates consistent between the codec dialog and frs/sec button.
	OSErr	err;	

	err = SCGetInfo(qtdata->theComponent, scTemporalSettingsType,	&qtdata->gTemporalSettings);
	CheckError(err, "SCGetInfo fr error", reports);

	if( (rd->frs_sec == 24 || rd->frs_sec == 30 || rd->frs_sec == 60) &&
		(qtdata->gTemporalSettings.frameRate == 1571553 ||
		 qtdata->gTemporalSettings.frameRate == 1964113 ||
		 qtdata->gTemporalSettings.frameRate == 3928227)) {;} 
	else {
		if (rd->frs_sec_base > 0)
			qtdata->gTemporalSettings.frameRate = 
			((float)(rd->frs_sec << 16) / rd->frs_sec_base) ;
	}
	
	err = SCSetInfo(qtdata->theComponent, scTemporalSettingsType,	&qtdata->gTemporalSettings);
	CheckError( err, "SCSetInfo error", reports );

	if(qtdata->gTemporalSettings.frameRate == 1571553) {			// 23.98 fps
		qtdata->kVideoTimeScale = 24000;
		qtdata->duration = 1001;
	} else if (qtdata->gTemporalSettings.frameRate == 1964113) {	// 29.97 fps
		qtdata->kVideoTimeScale = 30000;
		qtdata->duration = 1001;
	} else if (qtdata->gTemporalSettings.frameRate == 3928227) {	// 59.94 fps
		qtdata->kVideoTimeScale = 60000;
		qtdata->duration = 1001;
	} else {
		qtdata->kVideoTimeScale = (qtdata->gTemporalSettings.frameRate >> 16) * 100;
		qtdata->duration = 100;
	}
}

void quicktime_verify_image_type(RenderData *rd)
{
	if (rd->imtype == R_QUICKTIME) {
		if ((rd->qtcodecsettings.codecType== 0) ||
			(rd->qtcodecsettings.codecSpatialQuality <0) ||
			(rd->qtcodecsettings.codecSpatialQuality > 100)) {
			
			rd->qtcodecsettings.codecType = kJPEGCodecType;
			rd->qtcodecsettings.codec = (int)anyCodec;
			rd->qtcodecsettings.codecSpatialQuality = (codecHighQuality*100)/codecLosslessQuality;
			rd->qtcodecsettings.codecTemporalQuality = (codecHighQuality*100)/codecLosslessQuality;
			rd->qtcodecsettings.keyFrameRate = 25;
			rd->qtcodecsettings.bitRate = 5000000; //5 Mbps
		}
	}
}

int get_qtcodec_settings(RenderData *rd, ReportList *reports) 
{
	OSErr err = noErr;
		// erase any existing codecsetting
	if(qtdata) {
		if(qtdata->theComponent) CloseComponent(qtdata->theComponent);
		free_qtcomponentdata();
	}

	// allocate new
	qtdata = MEM_callocN(sizeof(QuicktimeComponentData), "QuicktimeComponentData");
	qtdata->theComponent = OpenDefaultComponent(StandardCompressionType, StandardCompressionSubType);

	// get previous selected codecsetting, from qtatom or detailed settings
	if(rd->qtcodecdata && rd->qtcodecdata->cdParms) {
		QT_GetCodecSettingsFromScene(rd, reports);
	} else {
		SCGetInfo(qtdata->theComponent, scDataRateSettingsType,	&qtdata->aDataRateSetting);
		SCGetInfo(qtdata->theComponent, scSpatialSettingsType,	&qtdata->gSpatialSettings);
		SCGetInfo(qtdata->theComponent, scTemporalSettingsType,	&qtdata->gTemporalSettings);

		qtdata->gSpatialSettings.codecType = rd->qtcodecsettings.codecType;
		qtdata->gSpatialSettings.codec = (CodecComponent)rd->qtcodecsettings.codec;      
		qtdata->gSpatialSettings.spatialQuality = (rd->qtcodecsettings.codecSpatialQuality * codecLosslessQuality) /100;
		qtdata->gTemporalSettings.temporalQuality = (rd->qtcodecsettings.codecTemporalQuality * codecLosslessQuality) /100;
		qtdata->gTemporalSettings.keyFrameRate = rd->qtcodecsettings.keyFrameRate;   
		qtdata->aDataRateSetting.dataRate = rd->qtcodecsettings.bitRate;
		qtdata->gSpatialSettings.depth = rd->qtcodecsettings.colorDepth;
		qtdata->aDataRateSetting.minSpatialQuality = (rd->qtcodecsettings.minSpatialQuality * codecLosslessQuality) / 100;
		qtdata->aDataRateSetting.minTemporalQuality = (rd->qtcodecsettings.minTemporalQuality * codecLosslessQuality) / 100;
		
		qtdata->aDataRateSetting.frameDuration = rd->frs_sec;
		SetMovieTimeScale(qtexport->theMovie, rd->frs_sec_base*1000);
		
		
		err = SCSetInfo(qtdata->theComponent, scTemporalSettingsType,	&qtdata->gTemporalSettings);
		CheckError(err, "SCSetInfo1 error", reports);
		err = SCSetInfo(qtdata->theComponent, scSpatialSettingsType,	&qtdata->gSpatialSettings);
		CheckError(err, "SCSetInfo2 error", reports);
		err = SCSetInfo(qtdata->theComponent, scDataRateSettingsType,	&qtdata->aDataRateSetting);
		CheckError(err, "SCSetInfo3 error", reports);
	}

	check_renderbutton_framerate(rd, reports);
	
	return err;
}

static int request_qtcodec_settings(bContext *C, wmOperator *op)
{
	OSErr	err = noErr;
	Scene *scene = CTX_data_scene(C);
	RenderData *rd = &scene->r;

	// erase any existing codecsetting
	if(qtdata) {
		if(qtdata->theComponent) CloseComponent(qtdata->theComponent);
		free_qtcomponentdata();
	}
	
	// allocate new
	qtdata = MEM_callocN(sizeof(QuicktimeComponentData), "QuicktimeComponentData");
	qtdata->theComponent = OpenDefaultComponent(StandardCompressionType, StandardCompressionSubType);
	
	// get previous selected codecsetting, from qtatom or detailed settings
	if(rd->qtcodecdata && rd->qtcodecdata->cdParms) {
		QT_GetCodecSettingsFromScene(rd, op->reports);
	} else {
		SCGetInfo(qtdata->theComponent, scDataRateSettingsType,	&qtdata->aDataRateSetting);
		SCGetInfo(qtdata->theComponent, scSpatialSettingsType,	&qtdata->gSpatialSettings);
		SCGetInfo(qtdata->theComponent, scTemporalSettingsType,	&qtdata->gTemporalSettings);
		
		qtdata->gSpatialSettings.codecType = rd->qtcodecsettings.codecType;
		qtdata->gSpatialSettings.codec = (CodecComponent)rd->qtcodecsettings.codec;      
		qtdata->gSpatialSettings.spatialQuality = (rd->qtcodecsettings.codecSpatialQuality * codecLosslessQuality) /100;
		qtdata->gTemporalSettings.temporalQuality = (rd->qtcodecsettings.codecTemporalQuality * codecLosslessQuality) /100;
		qtdata->gTemporalSettings.keyFrameRate = rd->qtcodecsettings.keyFrameRate;
		qtdata->gTemporalSettings.frameRate = ((float)(rd->frs_sec << 16) / rd->frs_sec_base);
		qtdata->aDataRateSetting.dataRate = rd->qtcodecsettings.bitRate;
		qtdata->gSpatialSettings.depth = rd->qtcodecsettings.colorDepth;
		qtdata->aDataRateSetting.minSpatialQuality = (rd->qtcodecsettings.minSpatialQuality * codecLosslessQuality) / 100;
		qtdata->aDataRateSetting.minTemporalQuality = (rd->qtcodecsettings.minTemporalQuality * codecLosslessQuality) / 100;
		
		qtdata->aDataRateSetting.frameDuration = rd->frs_sec;		
		
		err = SCSetInfo(qtdata->theComponent, scTemporalSettingsType,	&qtdata->gTemporalSettings);
		CheckError(err, "SCSetInfo1 error", op->reports);
		err = SCSetInfo(qtdata->theComponent, scSpatialSettingsType,	&qtdata->gSpatialSettings);
		CheckError(err, "SCSetInfo2 error", op->reports);
		err = SCSetInfo(qtdata->theComponent, scDataRateSettingsType,	&qtdata->aDataRateSetting);
		CheckError(err, "SCSetInfo3 error", op->reports);
	}
		// put up the dialog box - it needs to be called from the main thread
	err = SCRequestSequenceSettings(qtdata->theComponent);
 
	if (err == scUserCancelled) {
		return OPERATOR_FINISHED;
	}

		// update runtime codecsettings for use with the codec dialog
	SCGetInfo(qtdata->theComponent, scDataRateSettingsType,	&qtdata->aDataRateSetting);
	SCGetInfo(qtdata->theComponent, scSpatialSettingsType,	&qtdata->gSpatialSettings);
	SCGetInfo(qtdata->theComponent, scTemporalSettingsType,	&qtdata->gTemporalSettings);
	
	
		//Fill the render QuicktimeCodecSettings struct
	rd->qtcodecsettings.codecTemporalQuality = (qtdata->gTemporalSettings.temporalQuality * 100) / codecLosslessQuality;
		//Do not override scene frame rate (qtdata->gTemporalSettings.framerate)
	rd->qtcodecsettings.keyFrameRate = qtdata->gTemporalSettings.keyFrameRate;
	
	rd->qtcodecsettings.codecType = qtdata->gSpatialSettings.codecType;
	rd->qtcodecsettings.codec = (int)qtdata->gSpatialSettings.codec;
	rd->qtcodecsettings.colorDepth = qtdata->gSpatialSettings.depth;
	rd->qtcodecsettings.codecSpatialQuality = (qtdata->gSpatialSettings.spatialQuality * 100) / codecLosslessQuality;
	
	rd->qtcodecsettings.bitRate = qtdata->aDataRateSetting.dataRate;
	rd->qtcodecsettings.minSpatialQuality = (qtdata->aDataRateSetting.minSpatialQuality * 100) / codecLosslessQuality;
	rd->qtcodecsettings.minTemporalQuality = (qtdata->aDataRateSetting.minTemporalQuality * 100) / codecLosslessQuality;
		//Frame duration is already known (qtdata->aDataRateSetting.frameDuration)
	
	QT_SaveCodecSettingsToScene(rd, op->reports);

	// framerate jugglin'
	if(qtdata->gTemporalSettings.frameRate == 1571553) {			// 23.98 fps
		qtdata->kVideoTimeScale = 24000;
		qtdata->duration = 1001;

		rd->frs_sec = 24;
		rd->frs_sec_base = 1.001;
	} else if (qtdata->gTemporalSettings.frameRate == 1964113) {	// 29.97 fps
		qtdata->kVideoTimeScale = 30000;
		qtdata->duration = 1001;

		rd->frs_sec = 30;
		rd->frs_sec_base = 1.001;
	} else if (qtdata->gTemporalSettings.frameRate == 3928227) {	// 59.94 fps
		qtdata->kVideoTimeScale = 60000;
		qtdata->duration = 1001;

		rd->frs_sec = 60;
		rd->frs_sec_base = 1.001;
	} else {
		double fps = qtdata->gTemporalSettings.frameRate;

		qtdata->kVideoTimeScale = 60000;
		qtdata->duration = qtdata->kVideoTimeScale / (qtdata->gTemporalSettings.frameRate / 65536);

		if ((qtdata->gTemporalSettings.frameRate & 0xffff) == 0) {
			rd->frs_sec = fps / 65536;
			rd->frs_sec_base = 1.0;
		} else {
			/* we do our very best... */
			rd->frs_sec = fps  / 65536;
			rd->frs_sec_base = 1.0;
		}
	}

	return OPERATOR_FINISHED;
}

static int ED_operator_setqtcodec(bContext *C)
{
	return G.have_quicktime != FALSE;
}

#if defined(__APPLE__) && defined(GHOST_COCOA)
//Need to set up a Cocoa NSAutoReleasePool to avoid memory leak
//And it must be done in an objC file, so use a GHOST_SystemCocoa.mm function for that
extern int cocoa_request_qtcodec_settings(bContext *C, wmOperator *op);

int fromcocoa_request_qtcodec_settings(bContext *C, wmOperator *op)
{
	return request_qtcodec_settings(C, op);
}
#endif


void SCENE_OT_render_data_set_quicktime_codec(wmOperatorType *ot)
{
	/* identifiers */
	ot->name= "Change codec";
	ot->description= "Change Quicktime codec Settings";
	ot->idname= "SCENE_OT_render_data_set_quicktime_codec";
	
	/* api callbacks */
#if defined(__APPLE__) && defined(GHOST_COCOA)
	ot->exec = cocoa_request_qtcodec_settings;
#else
	ot->exec= request_qtcodec_settings;
#endif
	ot->poll= ED_operator_setqtcodec;
	
	/* flags */
	ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
}

#endif /* USE_QTKIT */
#endif /* _WIN32 || __APPLE__ */
#endif /* WITH_QUICKTIME */

