<commit_before>//---------------------------------------------------------------------------
//FFusion
//Alternative DivX Codec for MacOS X
//version 2.2 (build 72)
//by <NAME>
//Copyright 2002-2003 <NAME>
//parts by <NAME>
//Copyright 2003 <NAME>
//from DivOSX by Jamby
//Copyright 2001-2002 Jamby
//uses libavcodec from ffmpeg 0.4.6
//
//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 of the License, or (at your option) any later version.
//
//This library 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
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
//---------------------------------------------------------------------------
// Source Code
//---------------------------------------------------------------------------

#include <Carbon/Carbon.h>
#include <QuickTime/QuickTime.h>
#include <Accelerate/Accelerate.h>
#include <sys/sysctl.h>

#include "PerianResourceIDs.h"
#include "avcodec.h"
#include "Codecprintf.h"
#include "ColorConversions.h"
#include "bitstream_info.h"
#include "FrameBuffer.h"
#include "CommonUtils.h"
#include "pthread.h"
#include "CodecIDs.h"

//---------------------------------------------------------------------------
// Types
//---------------------------------------------------------------------------

// 64 because that's 2 * ffmpeg's INTERNAL_BUFFER_SIZE and QT sometimes uses more than 32
#define FFUSION_MAX_BUFFERS 64

#define kNumPixelFormatsSupportedFFusion 1

typedef struct
{
	AVFrame		*frame;
	short		retainCount;
	short		ffmpegUsing;
	long		frameNumber;
	AVFrame		returnedFrame;
} FFusionBuffer;

typedef enum
{
	PACKED_QUICKTIME_KNOWS_ORDER, /* This is for non-stupid container formats which actually know the difference between decode and display order */
	PACKED_ALL_IN_FIRST_FRAME, /* This is for the divx hack which contains a P frame and all subsequent B frames in a single frame. Ex: I, PB, -, PB, -, I...*/
	PACKED_DELAY_BY_ONE_FRAME /* This is for stupid containers where frames are soley writen in decode order.  Ex: I, P, B, P, B, P, I.... */
} FFusionPacked;

/* Why do these small structs?  It makes the usage of these variables clearer, no other reason */

/* globs used by the BeginBand routine */
struct begin_glob
{
	FFusionParserContext	*parser;
	long			lastFrame;
	long			lastIFrame;
	int				lastFrameType;
	int				futureType;
	FrameData		*lastPFrameData;
};

/* globs used by the DecodeBand routine */
struct decode_glob
{
	long			lastFrame;
	FFusionBuffer	*futureBuffer;
};

struct per_frame_decode_stats
{
	unsigned		begin_calls;
	unsigned		decode_calls;
	unsigned		draw_calls;
	unsigned		end_calls;
};

struct decode_stats
{
	struct per_frame_decode_stats type[4];
	int		max_frames_begun;
	int		max_frames_decoded;
};

typedef struct
{
    ComponentInstance		self;
    ComponentInstance		delegateComponent;
    ComponentInstance		target;
    ImageCodecMPDrawBandUPP 	drawBandUPP;
    Handle			pixelTypes;
    AVCodec			*avCodec;
    AVCodecContext	*avContext;
    OSType			componentType;
	FILE			*fileLog;
	AVFrame			lastDisplayedFrame;
	FFusionPacked	packedType;
	FFusionBuffer	buffers[FFUSION_MAX_BUFFERS];	// the buffers which the codec has retained
	int				lastAllocatedBuffer;		// the index of the buffer which was last allocated 
												// by the codec (and is the latest in decode order)	
	int				shouldUseReturnedFrame;
	struct begin_glob	begin;
	FFusionData		data;
	struct decode_glob	decode;
	struct decode_stats stats;
	ColorConversionFuncs colorConv;
} FFusionGlobalsRecord, *FFusionGlobals;

typedef struct
{
    long			width;
    long			height;
    long			depth;
    OSType			pixelFormat;
	int				decoded;
	long			frameNumber;
	long			GOPStartFrameNumber;
	long			bufferSize;
	FFusionBuffer	*buffer;
	FrameData		*frameData;
} FFusionDecompressRecord;


//---------------------------------------------------------------------------
// Prototypes of private subroutines
//---------------------------------------------------------------------------

static OSErr FFusionDecompress(FFusionGlobals glob, AVCodecContext *context, UInt8 *dataPtr, long width, long height, AVFrame *picture, long length);
static int FFusionGetBuffer(AVCodecContext *s, AVFrame *pic);
static void FFusionReleaseBuffer(AVCodecContext *s, AVFrame *pic);
static void releaseBuffer(AVCodecContext *s, FFusionBuffer *buf);
static FFusionBuffer *retainBuffer(FFusionGlobals glob, FFusionBuffer *buf);

int GetPPUserPreference();
void SetPPUserPreference(int value);
pascal OSStatus HandlePPDialogWindowEvent(EventHandlerCallRef  nextHandler, EventRef theEvent, void* userData);
pascal OSStatus HandlePPDialogControlEvent(EventHandlerCallRef  nextHandler, EventRef theEvent, void* userData);
void ChangeHintText(int value, ControlRef staticTextField);

extern void init_FFmpeg();
extern CFMutableStringRef CopyHomeDirectory();

#define FFusionDebugPrint(x...) if (glob->fileLog) Codecprintf(glob->fileLog, x);
#define not(x) ((x) ? "" : "not ")

//---------------------------------------------------------------------------
// Component Dispatcher
//---------------------------------------------------------------------------

#define IMAGECODEC_BASENAME() 		FFusionCodec
#define IMAGECODEC_GLOBALS() 		FFusionGlobals storage

#define CALLCOMPONENT_BASENAME()	IMAGECODEC_BASENAME()
#define	CALLCOMPONENT_GLOBALS()		IMAGECODEC_GLOBALS()

#define COMPONENT_UPP_PREFIX()		uppImageCodec
#define COMPONENT_DISPATCH_FILE		"FFusionCodecDispatch.h"
#define COMPONENT_SELECT_PREFIX()  	kImageCodec

#define	GET_DELEGATE_COMPONENT()	(storage->delegateComponent)

#include <QuickTime/ImageCodec.k.h>
#include <QuickTime/ComponentDispatchHelper.c>

static void *launchUpdateChecker(void *args)
{
	FSRef *ref = (FSRef *)args;
    LSOpenFSRef(ref, NULL);
	free(ref);
	return NULL;
}

Boolean FFusionAlreadyRanUpdateCheck = 0;

void FFusionRunUpdateCheck()
{
	if (FFusionAlreadyRanUpdateCheck) return;

    CFDateRef lastRunDate = CopyPreferencesValueTyped(CFSTR("NextRunDate"), CFDateGetTypeID());
    CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
    
	FFusionAlreadyRanUpdateCheck = 1;
	
	if (lastRunDate) {
		Boolean exit = CFDateGetAbsoluteTime(lastRunDate) > now;
		CFRelease(lastRunDate);
		if (exit) return;
	}
    
    //Two places to check, home dir and /
    
    CFMutableStringRef location = CopyHomeDirectory();
    CFStringAppend(location, CFSTR("/Library/PreferencePanes/Perian.prefPane/Contents/Resources/PerianUpdateChecker.app"));
    
    char fileRep[1024];
    FSRef *updateCheckRef = malloc(sizeof(FSRef));
    Boolean doCheck = FALSE;
    
    if(CFStringGetFileSystemRepresentation(location, fileRep, 1024))
        if(FSPathMakeRef((UInt8 *)fileRep, updateCheckRef, NULL) == noErr)
            doCheck = TRUE;
    
    CFRelease(location);
    if(doCheck == FALSE)
    {
        CFStringRef absLocation = CFSTR("/Library/PreferencePanes/Perian.prefPane/Contents/Resources/PerianUpdateChecker.app");
        if(CFStringGetFileSystemRepresentation(absLocation, fileRep, 1024))
            if(FSPathMakeRef((UInt8 *)fileRep, updateCheckRef, NULL) != noErr)
                goto err;  //We have failed
    }
	pthread_t thread;
	pthread_create(&thread, NULL, launchUpdateChecker, updateCheckRef);
	
	return;
err:
	free(updateCheckRef);
}

static void RecomputeMaxCounts(FFusionGlobals glob)
{
	int i;
	unsigned begun = 0, decoded = 0, ended = 0, drawn = 0;
	
	for (i = 0; i < 4; i++) {
		struct per_frame_decode_stats *f = &glob->stats.type[i];
		
		begun += f->begin_calls;
		decoded += f->decode_calls;
		drawn += f->draw_calls;
		ended += f->end_calls;
	}
	
	signed begin_diff = begun - ended, decode_diff = decoded - drawn;
	
	if (abs(begin_diff) > glob->stats.max_frames_begun) glob->stats.max_frames_begun = begin_diff;
	if (abs(decode_diff) > glob->stats.max_frames_decoded) glob->stats.max_frames_decoded = decode_diff;
}

static void DumpFrameDropStats(FFusionGlobals glob)
{
	static const char types[4] = {'?', 'I', 'P', 'B'};
	int i;
	
	if (!glob->fileLog || glob->decode.lastFrame == 0) return;
	
	Codecprintf(glob->fileLog, "%p frame drop stats\nType\t| BeginBand\t| DecodeBand\t| DrawBand\t| dropped before decode\t| dropped before draw\n", glob);
	
	for (i = 0; i < 4; i++) {
		struct per_frame_decode_stats *f = &glob->stats.type[i];
				
		Codecprintf(glob->fileLog, "%c\t| %d\t\t| %d\t\t| %d\t\t| %d/%f%%\t\t| %d/%f%%\n", types[i], f->begin_calls, f->decode_calls, f->draw_calls,
					f->begin_calls - f->decode_calls,(f->begin_calls > f->decode_calls) ? ((float)(f->begin_calls - f->decode_calls)/(float)f->begin_calls) * 100. : 0.,
					f->decode_calls - f->draw_calls,(f->decode_calls > f->draw_calls) ? ((float)(f->decode_calls - f->draw_calls)/(float)f->decode_calls) * 100. : 0.);
	}
}

static enum PixelFormat FindPixFmtFromVideo(AVCodec *codec, AVCodecContext *avctx, Ptr data, int bufferSize)
{
    AVCodecContext tmpContext;
    AVFrame tmpFrame;
    int got_picture;
    enum PixelFormat pix_fmt;
    
    avcodec_get_context_defaults2(&tmpContext, CODEC_TYPE_VIDEO);
	avcodec_get_frame_defaults(&tmpFrame);
    tmpContext.width = avctx->width;
    tmpContext.height = avctx->height;
	tmpContext.flags = avctx->flags;
	tmpContext.bits_per_coded_sample = avctx->bits_per_coded_sample;
    tmpContext.codec_tag = avctx->codec_tag;
	tmpContext.codec_id  = avctx->codec_id;
	tmpContext.extradata = avctx->extradata;
	tmpContext.extradata_size = avctx->extradata_size;
	
    avcodec_open(&tmpContext, codec);
	AVPacket pkt;
	av_init_packet(&pkt);
	pkt.data = (UInt8*)data;
	pkt.size = bufferSize;
    avcodec_decode_video2(&tmpContext, &tmpFrame, &got_picture, &pkt);
    pix_fmt = tmpContext.pix_fmt;
    avcodec_close(&tmpContext);
    
    return pix_fmt;
}

static void SetupMultithreadedDecoding(AVCodecContext *s, enum CodecID codecID)
{
	int nthreads = 1;
	size_t len = 4;
	
    // multithreading is only effective for mpeg1/2 and h.264 with slices
    if (codecID != CODEC_ID_MPEG1VIDEO && codecID != CODEC_ID_MPEG2VIDEO && codecID != CODEC_ID_H264) return;
    
	// two threads on multicore, otherwise 1
	if (sysctlbyname("hw.activecpu", &nthreads, &len, NULL, 0) == -1) nthreads = 1;
	else nthreads = FFMIN(nthreads, 2);
	
	avcodec_thread_init(s, nthreads);
}

static void SetSkipLoopFilter(FFusionGlobals glob, AVCodecContext *avctx)
{
	Boolean keyExists = FALSE;
	int loopFilterSkip = CFPreferencesGetAppIntegerValue(CFSTR("SkipLoopFilter"), PERIAN_PREF_DOMAIN, &keyExists);
	if(keyExists)
	{
		enum AVDiscard loopFilterValue = AVDISCARD_DEFAULT;
		switch (loopFilterSkip) {
			case 1:
				loopFilterValue = AVDISCARD_NONREF;
				break;
			case 2:
				loopFilterValue = AVDISCARD_BIDIR;
				break;
			case 3:
				loopFilterValue = AVDISCARD_NONKEY;
				break;
			case 4:
				loopFilterValue = AVDISCARD_ALL;
				break;
			default:
				break;
		}
		avctx->skip_loop_filter = loopFilterValue;
		FFusionDebugPrint("%p Preflight set skip loop filter to %d", glob, loopFilterValue);
	}
}

// A list of codec types (mostly official Apple codecs) which always have DTS info.
// This is the wrong way to do it, instead we should check for a ctts atom directly.
// This way causes files to play frames out of order if we guess wrong. Doesn't seem
// possible to do it right, though.
FFusionPacked DefaultPackedTypeForCodec(OSType codec)
{
	switch (codec) {
		case kMPEG1VisualCodecType:
		case kMPEG2VisualCodecType:
		case 'hdv1':
		case kMPEG4VisualCodecType:
		case kH264CodecType:
			return PACKED_QUICKTIME_KNOWS_ORDER;
		default:
			return PACKED_ALL_IN_FIRST_FRAME;
	}
}

static void swapFrame(AVFrame * *a, AVFrame * *b)
{
	AVFrame *t = *a;
	*a = *b;
	*b = t;
}

//---------------------------------------------------------------------------
// Component Routines
//---------------------------------------------------------------------------

// -- This Image Decompressor Use the Base Image Decompressor Component --
//	The base image decompressor is an Apple-supplied component
//	that makes it easier for developers to create new decompressors.
//	The base image decompressor does most of the housekeeping and
//	interface functions required for a QuickTime decompressor component,
//	including scheduling for asynchronous decompression.

//-----------------------------------------------------------------
// Component Open Request - Required
//-----------------------------------------------------------------

pascal ComponentResult FFusionCodecOpen(FFusionGlobals glob, ComponentInstance self)
{
    ComponentResult err;
    ComponentDescription descout;
    
    GetComponentInfo((Component)self, &descout, 0, 0, 0);
	
  //  FourCCprintf("Opening component for type ", descout.componentSubType);
    // Allocate memory for our globals, set them up and inform the component manager that we've done so
	
    glob = (FFusionGlobals)NewPtrClear(sizeof(FFusionGlobalsRecord));
    
    if (err = MemError())
    {
        Codecprintf(NULL, "Unable to allocate globals! Exiting.\n");            
    }
    else
    {
		CFStringRef pathToLogFile = CopyPreferencesValueTyped(CFSTR("DebugLogFile"), CFStringGetTypeID());
		char path[PATH_MAX];
        SetComponentInstanceStorage(self, (Handle)glob);

        glob->self = self;
        glob->target = self;
        glob->drawBandUPP = NULL;
        glob->pixelTypes = NewHandleClear((kNumPixelFormatsSupportedFFusion+1) * sizeof(OSType));
        glob->avCodec = 0;
        glob->componentType = descout.componentSubType;
		glob->data.frames = NULL;
		glob->begin.parser = NULL;
		if (pathToLogFile) {
			CFStringGetCString(pathToLogFile, path, PATH_MAX, kCFStringEncodingUTF8);
			CFRelease(pathToLogFile);
			glob->fileLog = fopen(path, "a");
		}
		glob->shouldUseReturnedFrame = 0;
		
        // Open and target an instance of the base decompressor as we delegate
        // most of our calls to the base decompressor instance
        
        err = OpenADefaultComponent(decompressorComponentType, kBaseCodecType, &glob->delegateComponent);
        if (!err)
        {
            ComponentSetTarget(glob->delegateComponent, self);
        }
        else
        {
            Codecprintf(glob->fileLog, "Error opening the base image decompressor! Exiting.\n");
        }
		
		// we allocate some space for copying the frame data since we need some padding at the end
		// for ffmpeg's optimized bitstream readers. Size doesn't really matter, it'll grow if need be
		FFusionDataSetup(&(glob->data), 256, 1024*1024);
        FFusionRunUpdateCheck();
    }
    
    FFusionDebugPrint("%p opened for '%s'\n", glob, FourCCString(glob->componentType));
    return err;
}

//-----------------------------------------------------------------
// Component Close Request - Required
//-----------------------------------------------------------------

pascal ComponentResult FFusionCodecClose(FFusionGlobals glob, ComponentInstance self)
{
    FFusionDebugPrint("%p closed.\n", glob);
	DumpFrameDropStats(glob);

    // Make sure to close the base component and deallocate our storage
    if (glob) 
    {
        if (glob->delegateComponent) 
        {
            CloseComponent(glob->delegateComponent);
        }
        
        if (glob->drawBandUPP) 
        {
            DisposeImageCodecMPDrawBandUPP(glob->drawBandUPP);
        }
				
        if (glob->avContext)
        {
			if (glob->avContext->extradata)
				free(glob->avContext->extradata);
						
			if (glob->avContext->codec) avcodec_close(glob->avContext);
            av_free(glob->avContext);
        }
		
		if (glob->begin.parser)
		{
			ffusionParserFree(glob->begin.parser);
		}
		
		if (glob->pixelTypes)
		{
			DisposeHandle(glob->pixelTypes);
		}
		
		FFusionDataFree(&(glob->data));
        
		if(glob->fileLog)
			fclose(glob->fileLog);
		
        memset(glob, 0, sizeof(FFusionGlobalsRecord));
        DisposePtr((Ptr)glob);
    }
	
    return noErr;
}

//-----------------------------------------------------------------
// Component Version Request - Required
//-----------------------------------------------------------------

pascal ComponentResult FFusionCodecVersion(FFusionGlobals glob)
{
    return kFFusionCodecVersion;
}

//-----------------------------------------------------------------
// Component Target Request
//-----------------------------------------------------------------
// Allows another component to "target" you i.e., you call 
// another component whenever you would call yourself (as a result 
// of your component being used by another component)
//-----------------------------------------------------------------

pascal ComponentResult FFusionCodecTarget(FFusionGlobals glob, ComponentInstance target)
{
    glob->target = target;
	
    return noErr;
}

//-----------------------------------------------------------------
// Component GetMPWorkFunction Request
//-----------------------------------------------------------------
// Allows your image decompressor component to perform asynchronous 
// decompression in a single MP task by taking advantage of the 
// Base Decompressor. If you implement this selector, your DrawBand 
// function must be MP-safe. MP safety means not calling routines 
// that may move or purge memory and not calling any routines which
// might cause 68K code to be executed. Ideally, your DrawBand 
// function should not make any API calls whatsoever. Obviously 
// don't implement this if you're building a 68k component.
//-----------------------------------------------------------------

pascal ComponentResult FFusionCodecGetMPWorkFunction(FFusionGlobals glob, ComponentMPWorkFunctionUPP *workFunction, void **refCon)
{
	if (glob->drawBandUPP == NULL)
		glob->drawBandUPP = NewImageCodecMPDrawBandUPP((ImageCodecMPDrawBandProcPtr)FFusionCodecDrawBand);
	
	return ImageCodecGetBaseMPWorkFunction(glob->delegateComponent, workFunction, refCon, glob->drawBandUPP, glob);
}

//-----------------------------------------------------------------
// ImageCodecInitialize
//-----------------------------------------------------------------
// The first function call that your image decompressor component 
// receives from the base image decompressor is always a call to 
// ImageCodecInitialize . In response to this call, your image 
// decompressor component returns an ImageSubCodecDecompressCapabilities 
// structure that specifies its capabilities.
//-----------------------------------------------------------------

pascal ComponentResult FFusionCodecInitialize(FFusionGlobals glob, ImageSubCodecDecompressCapabilities *cap)
{
	Boolean doExperimentalFlags = CFPreferencesGetAppBooleanValue(CFSTR("ExperimentalQTFlags"), PERIAN_PREF_DOMAIN, NULL);
	
    // Secifies the size of the ImageSubCodecDecompressRecord structure
    // and say we can support asyncronous decompression
    // With the help of the base image decompressor, any image decompressor
    // that uses only interrupt-safe calls for decompression operations can
    // support asynchronous decompression.
	
    cap->decompressRecordSize = sizeof(FFusionDecompressRecord) + 12;
    cap->canAsync = true;
	
	// QT 7
	if(cap->recordSize > offsetof(ImageSubCodecDecompressCapabilities, baseCodecShouldCallDecodeBandForAllFrames))
	{
		cap->subCodecIsMultiBufferAware = true;
		cap->subCodecSupportsOutOfOrderDisplayTimes = true;
		cap->baseCodecShouldCallDecodeBandForAllFrames = true;
		cap->subCodecSupportsScheduledBackwardsPlaybackWithDifferenceFrames = !doExperimentalFlags;
		cap->subCodecSupportsDrawInDecodeOrder = doExperimentalFlags; 
		cap->subCodecSupportsDecodeSmoothing = true; 
	}
	
    return noErr;
}

static inline int shouldDecode(FFusionGlobals glob, enum CodecID codecID)
{
	FFusionDecodeAbilities decode = FFUSION_PREFER_DECODE;
	if (glob->componentType == 'avc1')
		decode = ffusionIsParsedVideoDecodable(glob->begin.parser);
	if(decode > FFUSION_CANNOT_DECODE && 
	   (codecID == CODEC_ID_H264 || codecID == CODEC_ID_MPEG4) && CFPreferencesGetAppBooleanValue(CFSTR("PreferAppleCodecs"), PERIAN_PREF_DOMAIN, NULL))
		decode = FFUSION_PREFER_NOT_DECODE;
	if(decode > FFUSION_CANNOT_DECODE)
		if(IsForcedDecodeEnabled())
			decode = FFUSION_PREFER_DECODE;
	return decode > FFUSION_PREFER_NOT_DECODE;
}

//-----------------------------------------------------------------
// ImageCodecPreflight
//-----------------------------------------------------------------
// The base image decompressor gets additional information about the 
// capabilities of your image decompressor component by calling 
// ImageCodecPreflight. The base image decompressor uses this
// information when responding to a call to the ImageCodecPredecompress 
// function, which the ICM makes before decompressing an image. You 
// are required only to provide values for the wantedDestinationPixelSize 
// and wantedDestinationPixelTypes fields and can also modify other
// fields if necessary.
//-----------------------------------------------------------------

pascal ComponentResult FFusionCodecPreflight(FFusionGlobals glob, CodecDecompressParams *p)
{
    OSType *pos;
    int index;
    CodecCapabilities *capabilities = p->capabilities;
	long count = 0;
	Handle imgDescExt;
	OSErr err = noErr;
	
    // We first open libavcodec library and the codec corresponding
    // to the fourCC if it has not been done before
    
	FFusionDebugPrint("%p Preflight called.\n", glob);
	FFusionDebugPrint("%p Frame dropping is %senabled\n", glob, not(IsFrameDroppingEnabled()));
	
    if (!glob->avCodec)
    {
		init_FFmpeg();
		initFFusionParsers();

		OSType componentType = glob->componentType;
		enum CodecID codecID = getCodecID(componentType);
		
		glob->packedType = DefaultPackedTypeForCodec(componentType);
		
		if(componentType == 'VP30' || componentType == 'VP31')
			glob->shouldUseReturnedFrame = TRUE;

		if(codecID == CODEC_ID_NONE)
		{
			Codecprintf(glob->fileLog, "Warning! Unknown codec type! Using MPEG4 by default.\n");
			codecID = CODEC_ID_MPEG4;
		}
		
		glob->avCodec = avcodec_find_decoder(codecID);
//		if(glob->packedType != PACKED_QUICKTIME_KNOWS_ORDER)
			glob->begin.parser = ffusionParserInit(codecID);
                
		if ((codecID == CODEC_ID_MPEG4 || codecID == CODEC_ID_H264) && !glob->begin.parser)
			Codecprintf(glob->fileLog, "This is a parseable format, but we couldn't open a parser!\n");
		
        // we do the same for the AVCodecContext since all context values are
        // correctly initialized when calling the alloc function
        
        glob->avContext = avcodec_alloc_context2(CODEC_TYPE_VIDEO);
		
		// Use low delay
		glob->avContext->flags |= CODEC_FLAG_LOW_DELAY;
		
        // Image size is mandatory for DivX-like codecs
        
        glob->avContext->width = (**p->imageDescription).width;
        glob->avContext->height = (**p->imageDescription).height;
		glob->avContext->bits_per_coded_sample = (**p->imageDescription).depth;
		
        // We also pass the FourCC since it allows the H263 hybrid decoder
        // to make the difference between the various flavours of DivX
        glob->avContext->codec_tag = Endian32_Swap(glob->componentType);
		glob->avContext->codec_id  = codecID;
        
		// avc1 requires the avcC extension
		if (glob->componentType == 'avc1') {
			count = isImageDescriptionExtensionPresent(p->imageDescription, 'avcC');
			
			if (count >= 1) {
				imgDescExt = NewHandle(0);
				GetImageDescriptionExtension(p->imageDescription, &imgDescExt, 'avcC', 1);
				
				glob->avContext->extradata = calloc(1, GetHandleSize(imgDescExt) + FF_INPUT_BUFFER_PADDING_SIZE);
				memcpy(glob->avContext->extradata, *imgDescExt, GetHandleSize(imgDescExt));
				glob->avContext->extradata_size = GetHandleSize(imgDescExt);
				
				DisposeHandle(imgDescExt);
			} else {
				count = isImageDescriptionExtensionPresent(p->imageDescription, 'strf');
				
				// avc1 in AVI, need to reorder frames
				if (count >= 1)
					glob->packedType = PACKED_ALL_IN_FIRST_FRAME;
			}
		} else if (glob->componentType == 'mp4v') {
			count = isImageDescriptionExtensionPresent(p->imageDescription, 'esds');
			
			if (count >= 1) {
				imgDescExt = NewHandle(0);
				GetImageDescriptionExtension(p->imageDescription, &imgDescExt, 'esds', 1);
				
				ReadESDSDescExt(imgDescExt, &glob->avContext->extradata, &glob->avContext->extradata_size);
				
				DisposeHandle(imgDescExt);
			}
		} else {
			count = isImageDescriptionExtensionPresent(p->imageDescription, 'strf');
			
			if (count >= 1) {
				imgDescExt = NewHandle(0);
				GetImageDescriptionExtension(p->imageDescription, &imgDescExt, 'strf', 1);
				
				if (GetHandleSize(imgDescExt) - 40 > 0) {
					glob->avContext->extradata = calloc(1, GetHandleSize(imgDescExt) - 40 + FF_INPUT_BUFFER_PADDING_SIZE);
					memcpy(glob->avContext->extradata, *imgDescExt + 40, GetHandleSize(imgDescExt) - 40);
					glob->avContext->extradata_size = GetHandleSize(imgDescExt) - 40;
				}
				DisposeHandle(imgDescExt);
			}
		}
		
		FFusionDebugPrint("%p preflighted for %dx%d '%s'. (%d bytes of extradata)\n", glob, (**p->imageDescription).width, (**p->imageDescription).height, FourCCString(glob->componentType), glob->avContext->extradata_size);
        
		if(glob->avContext->extradata_size != 0 && glob->begin.parser != NULL)
			ffusionParseExtraData(glob->begin.parser, glob->avContext->extradata, glob->avContext->extradata_size);
		
		if (glob->fileLog)
			ffusionLogDebugInfo(glob->begin.parser, glob->fileLog);
		
		if (!shouldDecode(glob, codecID))
			err = featureUnsupported;
		
		// some hooks into ffmpeg's buffer allocation to get frames in 
		// decode order without delay more easily
		glob->avContext->opaque = glob;
		glob->avContext->get_buffer = FFusionGetBuffer;
		glob->avContext->release_buffer = FFusionReleaseBuffer;
		
		// multi-slice decoding
		SetupMultithreadedDecoding(glob->avContext, codecID);
		
		// deblock skipping for h264
		SetSkipLoopFilter(glob, glob->avContext);
		
		//fast flag
		if (CFPreferencesGetAppBooleanValue(CFSTR("UseFastDecode"), PERIAN_PREF_DOMAIN, NULL))
			glob->avContext->flags2 |= CODEC_FLAG2_FAST;
		
        // Finally we open the avcodec 
        
        if (avcodec_open(glob->avContext, glob->avCodec))
        {
            Codecprintf(glob->fileLog, "Error opening avcodec!\n");
            
			err = paramErr;
        }
		
        // this format doesn't have enough information in its headers
		// we have to decode the first frame
		if (glob->avContext->pix_fmt == PIX_FMT_NONE && p->bufferSize && p->data)
            glob->avContext->pix_fmt = FindPixFmtFromVideo(glob->avCodec, glob->avContext, p->data, p->bufferSize);
    }
    
    // Specify the minimum image band height supported by the component
    // bandInc specifies a common factor of supported image band heights - 
    // if your component supports only image bands that are an even
    // multiple of some number of pixels high report this common factor in bandInc
    
    capabilities->bandMin = (**p->imageDescription).height;
    capabilities->bandInc = capabilities->bandMin;
	
    // libavcodec 0.4.x is no longer stream based i.e. you cannot pass just
    // an arbitrary amount of data to the library.<commit_msg>FFusion: Don't crash if avcodec_open() fails<commit_after>//---------------------------------------------------------------------------
//FFusion
//Alternative DivX Codec for MacOS X
//version 2.2 (build 72)
//by <NAME>
//Copyright 2002-2003 <NAME>
//parts by <NAME>
//Copyright 2003 <NAME>
//from DivOSX by Jamby
//Copyright 2001-2002 Jamby
//uses libavcodec from ffmpeg 0.4.6
//
//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 of the License, or (at your option) any later version.
//
//This library 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
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
//---------------------------------------------------------------------------
// Source Code
//---------------------------------------------------------------------------

#include <Carbon/Carbon.h>
#include <QuickTime/QuickTime.h>
#include <Accelerate/Accelerate.h>
#include <sys/sysctl.h>

#include "PerianResourceIDs.h"
#include "avcodec.h"
#include "Codecprintf.h"
#include "ColorConversions.h"
#include "bitstream_info.h"
#include "FrameBuffer.h"
#include "CommonUtils.h"
#include "pthread.h"
#include "CodecIDs.h"

//---------------------------------------------------------------------------
// Types
//---------------------------------------------------------------------------

// 64 because that's 2 * ffmpeg's INTERNAL_BUFFER_SIZE and QT sometimes uses more than 32
#define FFUSION_MAX_BUFFERS 64

#define kNumPixelFormatsSupportedFFusion 1

typedef struct
{
	AVFrame		*frame;
	short		retainCount;
	short		ffmpegUsing;
	long		frameNumber;
	AVFrame		returnedFrame;
} FFusionBuffer;

typedef enum
{
	PACKED_QUICKTIME_KNOWS_ORDER, /* This is for non-stupid container formats which actually know the difference between decode and display order */
	PACKED_ALL_IN_FIRST_FRAME, /* This is for the divx hack which contains a P frame and all subsequent B frames in a single frame. Ex: I, PB, -, PB, -, I...*/
	PACKED_DELAY_BY_ONE_FRAME /* This is for stupid containers where frames are soley writen in decode order.  Ex: I, P, B, P, B, P, I.... */
} FFusionPacked;

/* Why do these small structs?  It makes the usage of these variables clearer, no other reason */

/* globs used by the BeginBand routine */
struct begin_glob
{
	FFusionParserContext	*parser;
	long			lastFrame;
	long			lastIFrame;
	int				lastFrameType;
	int				futureType;
	FrameData		*lastPFrameData;
};

/* globs used by the DecodeBand routine */
struct decode_glob
{
	long			lastFrame;
	FFusionBuffer	*futureBuffer;
};

struct per_frame_decode_stats
{
	unsigned		begin_calls;
	unsigned		decode_calls;
	unsigned		draw_calls;
	unsigned		end_calls;
};

struct decode_stats
{
	struct per_frame_decode_stats type[4];
	int		max_frames_begun;
	int		max_frames_decoded;
};

typedef struct
{
    ComponentInstance		self;
    ComponentInstance		delegateComponent;
    ComponentInstance		target;
    ImageCodecMPDrawBandUPP 	drawBandUPP;
    Handle			pixelTypes;
    AVCodec			*avCodec;
    AVCodecContext	*avContext;
    OSType			componentType;
	FILE			*fileLog;
	AVFrame			lastDisplayedFrame;
	FFusionPacked	packedType;
	FFusionBuffer	buffers[FFUSION_MAX_BUFFERS];	// the buffers which the codec has retained
	int				lastAllocatedBuffer;		// the index of the buffer which was last allocated 
												// by the codec (and is the latest in decode order)	
	int				shouldUseReturnedFrame;
	struct begin_glob	begin;
	FFusionData		data;
	struct decode_glob	decode;
	struct decode_stats stats;
	ColorConversionFuncs colorConv;
} FFusionGlobalsRecord, *FFusionGlobals;

typedef struct
{
    long			width;
    long			height;
    long			depth;
    OSType			pixelFormat;
	int				decoded;
	long			frameNumber;
	long			GOPStartFrameNumber;
	long			bufferSize;
	FFusionBuffer	*buffer;
	FrameData		*frameData;
} FFusionDecompressRecord;


//---------------------------------------------------------------------------
// Prototypes of private subroutines
//---------------------------------------------------------------------------

static OSErr FFusionDecompress(FFusionGlobals glob, AVCodecContext *context, UInt8 *dataPtr, long width, long height, AVFrame *picture, long length);
static int FFusionGetBuffer(AVCodecContext *s, AVFrame *pic);
static void FFusionReleaseBuffer(AVCodecContext *s, AVFrame *pic);
static void releaseBuffer(AVCodecContext *s, FFusionBuffer *buf);
static FFusionBuffer *retainBuffer(FFusionGlobals glob, FFusionBuffer *buf);

int GetPPUserPreference();
void SetPPUserPreference(int value);
pascal OSStatus HandlePPDialogWindowEvent(EventHandlerCallRef  nextHandler, EventRef theEvent, void* userData);
pascal OSStatus HandlePPDialogControlEvent(EventHandlerCallRef  nextHandler, EventRef theEvent, void* userData);
void ChangeHintText(int value, ControlRef staticTextField);

extern void init_FFmpeg();
extern CFMutableStringRef CopyHomeDirectory();

#define FFusionDebugPrint(x...) if (glob->fileLog) Codecprintf(glob->fileLog, x);
#define not(x) ((x) ? "" : "not ")

//---------------------------------------------------------------------------
// Component Dispatcher
//---------------------------------------------------------------------------

#define IMAGECODEC_BASENAME() 		FFusionCodec
#define IMAGECODEC_GLOBALS() 		FFusionGlobals storage

#define CALLCOMPONENT_BASENAME()	IMAGECODEC_BASENAME()
#define	CALLCOMPONENT_GLOBALS()		IMAGECODEC_GLOBALS()

#define COMPONENT_UPP_PREFIX()		uppImageCodec
#define COMPONENT_DISPATCH_FILE		"FFusionCodecDispatch.h"
#define COMPONENT_SELECT_PREFIX()  	kImageCodec

#define	GET_DELEGATE_COMPONENT()	(storage->delegateComponent)

#include <QuickTime/ImageCodec.k.h>
#include <QuickTime/ComponentDispatchHelper.c>

static void *launchUpdateChecker(void *args)
{
	FSRef *ref = (FSRef *)args;
    LSOpenFSRef(ref, NULL);
	free(ref);
	return NULL;
}

Boolean FFusionAlreadyRanUpdateCheck = 0;

void FFusionRunUpdateCheck()
{
	if (FFusionAlreadyRanUpdateCheck) return;

    CFDateRef lastRunDate = CopyPreferencesValueTyped(CFSTR("NextRunDate"), CFDateGetTypeID());
    CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
    
	FFusionAlreadyRanUpdateCheck = 1;
	
	if (lastRunDate) {
		Boolean exit = CFDateGetAbsoluteTime(lastRunDate) > now;
		CFRelease(lastRunDate);
		if (exit) return;
	}
    
    //Two places to check, home dir and /
    
    CFMutableStringRef location = CopyHomeDirectory();
    CFStringAppend(location, CFSTR("/Library/PreferencePanes/Perian.prefPane/Contents/Resources/PerianUpdateChecker.app"));
    
    char fileRep[1024];
    FSRef *updateCheckRef = malloc(sizeof(FSRef));
    Boolean doCheck = FALSE;
    
    if(CFStringGetFileSystemRepresentation(location, fileRep, 1024))
        if(FSPathMakeRef((UInt8 *)fileRep, updateCheckRef, NULL) == noErr)
            doCheck = TRUE;
    
    CFRelease(location);
    if(doCheck == FALSE)
    {
        CFStringRef absLocation = CFSTR("/Library/PreferencePanes/Perian.prefPane/Contents/Resources/PerianUpdateChecker.app");
        if(CFStringGetFileSystemRepresentation(absLocation, fileRep, 1024))
            if(FSPathMakeRef((UInt8 *)fileRep, updateCheckRef, NULL) != noErr)
                goto err;  //We have failed
    }
	pthread_t thread;
	pthread_create(&thread, NULL, launchUpdateChecker, updateCheckRef);
	
	return;
err:
	free(updateCheckRef);
}

static void RecomputeMaxCounts(FFusionGlobals glob)
{
	int i;
	unsigned begun = 0, decoded = 0, ended = 0, drawn = 0;
	
	for (i = 0; i < 4; i++) {
		struct per_frame_decode_stats *f = &glob->stats.type[i];
		
		begun += f->begin_calls;
		decoded += f->decode_calls;
		drawn += f->draw_calls;
		ended += f->end_calls;
	}
	
	signed begin_diff = begun - ended, decode_diff = decoded - drawn;
	
	if (abs(begin_diff) > glob->stats.max_frames_begun) glob->stats.max_frames_begun = begin_diff;
	if (abs(decode_diff) > glob->stats.max_frames_decoded) glob->stats.max_frames_decoded = decode_diff;
}

static void DumpFrameDropStats(FFusionGlobals glob)
{
	static const char types[4] = {'?', 'I', 'P', 'B'};
	int i;
	
	if (!glob->fileLog || glob->decode.lastFrame == 0) return;
	
	Codecprintf(glob->fileLog, "%p frame drop stats\nType\t| BeginBand\t| DecodeBand\t| DrawBand\t| dropped before decode\t| dropped before draw\n", glob);
	
	for (i = 0; i < 4; i++) {
		struct per_frame_decode_stats *f = &glob->stats.type[i];
				
		Codecprintf(glob->fileLog, "%c\t| %d\t\t| %d\t\t| %d\t\t| %d/%f%%\t\t| %d/%f%%\n", types[i], f->begin_calls, f->decode_calls, f->draw_calls,
					f->begin_calls - f->decode_calls,(f->begin_calls > f->decode_calls) ? ((float)(f->begin_calls - f->decode_calls)/(float)f->begin_calls) * 100. : 0.,
					f->decode_calls - f->draw_calls,(f->decode_calls > f->draw_calls) ? ((float)(f->decode_calls - f->draw_calls)/(float)f->decode_calls) * 100. : 0.);
	}
}

static enum PixelFormat FindPixFmtFromVideo(AVCodec *codec, AVCodecContext *avctx, Ptr data, int bufferSize)
{
    AVCodecContext tmpContext;
    AVFrame tmpFrame;
    int got_picture;
    enum PixelFormat pix_fmt;
    
    avcodec_get_context_defaults2(&tmpContext, CODEC_TYPE_VIDEO);
	avcodec_get_frame_defaults(&tmpFrame);
    tmpContext.width = avctx->width;
    tmpContext.height = avctx->height;
	tmpContext.flags = avctx->flags;
	tmpContext.bits_per_coded_sample = avctx->bits_per_coded_sample;
    tmpContext.codec_tag = avctx->codec_tag;
	tmpContext.codec_id  = avctx->codec_id;
	tmpContext.extradata = avctx->extradata;
	tmpContext.extradata_size = avctx->extradata_size;
	
    avcodec_open(&tmpContext, codec);
	AVPacket pkt;
	av_init_packet(&pkt);
	pkt.data = (UInt8*)data;
	pkt.size = bufferSize;
    avcodec_decode_video2(&tmpContext, &tmpFrame, &got_picture, &pkt);
    pix_fmt = tmpContext.pix_fmt;
    avcodec_close(&tmpContext);
    
    return pix_fmt;
}

static void SetupMultithreadedDecoding(AVCodecContext *s, enum CodecID codecID)
{
	int nthreads = 1;
	size_t len = 4;
	
    // multithreading is only effective for mpeg1/2 and h.264 with slices
    if (codecID != CODEC_ID_MPEG1VIDEO && codecID != CODEC_ID_MPEG2VIDEO && codecID != CODEC_ID_H264) return;
    
	// two threads on multicore, otherwise 1
	if (sysctlbyname("hw.activecpu", &nthreads, &len, NULL, 0) == -1) nthreads = 1;
	else nthreads = FFMIN(nthreads, 2);
	
	avcodec_thread_init(s, nthreads);
}

static void SetSkipLoopFilter(FFusionGlobals glob, AVCodecContext *avctx)
{
	Boolean keyExists = FALSE;
	int loopFilterSkip = CFPreferencesGetAppIntegerValue(CFSTR("SkipLoopFilter"), PERIAN_PREF_DOMAIN, &keyExists);
	if(keyExists)
	{
		enum AVDiscard loopFilterValue = AVDISCARD_DEFAULT;
		switch (loopFilterSkip) {
			case 1:
				loopFilterValue = AVDISCARD_NONREF;
				break;
			case 2:
				loopFilterValue = AVDISCARD_BIDIR;
				break;
			case 3:
				loopFilterValue = AVDISCARD_NONKEY;
				break;
			case 4:
				loopFilterValue = AVDISCARD_ALL;
				break;
			default:
				break;
		}
		avctx->skip_loop_filter = loopFilterValue;
		FFusionDebugPrint("%p Preflight set skip loop filter to %d", glob, loopFilterValue);
	}
}

// A list of codec types (mostly official Apple codecs) which always have DTS info.
// This is the wrong way to do it, instead we should check for a ctts atom directly.
// This way causes files to play frames out of order if we guess wrong. Doesn't seem
// possible to do it right, though.
FFusionPacked DefaultPackedTypeForCodec(OSType codec)
{
	switch (codec) {
		case kMPEG1VisualCodecType:
		case kMPEG2VisualCodecType:
		case 'hdv1':
		case kMPEG4VisualCodecType:
		case kH264CodecType:
			return PACKED_QUICKTIME_KNOWS_ORDER;
		default:
			return PACKED_ALL_IN_FIRST_FRAME;
	}
}

static void swapFrame(AVFrame * *a, AVFrame * *b)
{
	AVFrame *t = *a;
	*a = *b;
	*b = t;
}

//---------------------------------------------------------------------------
// Component Routines
//---------------------------------------------------------------------------

// -- This Image Decompressor Use the Base Image Decompressor Component --
//	The base image decompressor is an Apple-supplied component
//	that makes it easier for developers to create new decompressors.
//	The base image decompressor does most of the housekeeping and
//	interface functions required for a QuickTime decompressor component,
//	including scheduling for asynchronous decompression.

//-----------------------------------------------------------------
// Component Open Request - Required
//-----------------------------------------------------------------

pascal ComponentResult FFusionCodecOpen(FFusionGlobals glob, ComponentInstance self)
{
    ComponentResult err;
    ComponentDescription descout;
    
    GetComponentInfo((Component)self, &descout, 0, 0, 0);
	
  //  FourCCprintf("Opening component for type ", descout.componentSubType);
    // Allocate memory for our globals, set them up and inform the component manager that we've done so
	
    glob = (FFusionGlobals)NewPtrClear(sizeof(FFusionGlobalsRecord));
    
    if (err = MemError())
    {
        Codecprintf(NULL, "Unable to allocate globals! Exiting.\n");            
    }
    else
    {
		CFStringRef pathToLogFile = CopyPreferencesValueTyped(CFSTR("DebugLogFile"), CFStringGetTypeID());
		char path[PATH_MAX];
        SetComponentInstanceStorage(self, (Handle)glob);

        glob->self = self;
        glob->target = self;
        glob->drawBandUPP = NULL;
        glob->pixelTypes = NewHandleClear((kNumPixelFormatsSupportedFFusion+1) * sizeof(OSType));
        glob->avCodec = 0;
        glob->componentType = descout.componentSubType;
		glob->data.frames = NULL;
		glob->begin.parser = NULL;
		if (pathToLogFile) {
			CFStringGetCString(pathToLogFile, path, PATH_MAX, kCFStringEncodingUTF8);
			CFRelease(pathToLogFile);
			glob->fileLog = fopen(path, "a");
		}
		glob->shouldUseReturnedFrame = 0;
		
        // Open and target an instance of the base decompressor as we delegate
        // most of our calls to the base decompressor instance
        
        err = OpenADefaultComponent(decompressorComponentType, kBaseCodecType, &glob->delegateComponent);
        if (!err)
        {
            ComponentSetTarget(glob->delegateComponent, self);
        }
        else
        {
            Codecprintf(glob->fileLog, "Error opening the base image decompressor! Exiting.\n");
        }
		
		// we allocate some space for copying the frame data since we need some padding at the end
		// for ffmpeg's optimized bitstream readers. Size doesn't really matter, it'll grow if need be
		FFusionDataSetup(&(glob->data), 256, 1024*1024);
        FFusionRunUpdateCheck();
    }
    
    FFusionDebugPrint("%p opened for '%s'\n", glob, FourCCString(glob->componentType));
    return err;
}

//-----------------------------------------------------------------
// Component Close Request - Required
//-----------------------------------------------------------------

pascal ComponentResult FFusionCodecClose(FFusionGlobals glob, ComponentInstance self)
{
    FFusionDebugPrint("%p closed.\n", glob);
	DumpFrameDropStats(glob);

    // Make sure to close the base component and deallocate our storage
    if (glob) 
    {
        if (glob->delegateComponent) 
        {
            CloseComponent(glob->delegateComponent);
        }
        
        if (glob->drawBandUPP) 
        {
            DisposeImageCodecMPDrawBandUPP(glob->drawBandUPP);
        }
				
        if (glob->avContext)
        {
			if (glob->avContext->extradata)
				free(glob->avContext->extradata);
						
			if (glob->avContext->codec) avcodec_close(glob->avContext);
            av_free(glob->avContext);
        }
		
		if (glob->begin.parser)
		{
			ffusionParserFree(glob->begin.parser);
		}
		
		if (glob->pixelTypes)
		{
			DisposeHandle(glob->pixelTypes);
		}
		
		FFusionDataFree(&(glob->data));
        
		if(glob->fileLog)
			fclose(glob->fileLog);
		
        memset(glob, 0, sizeof(FFusionGlobalsRecord));
        DisposePtr((Ptr)glob);
    }
	
    return noErr;
}

//-----------------------------------------------------------------
// Component Version Request - Required
//-----------------------------------------------------------------

pascal ComponentResult FFusionCodecVersion(FFusionGlobals glob)
{
    return kFFusionCodecVersion;
}

//-----------------------------------------------------------------
// Component Target Request
//-----------------------------------------------------------------
// Allows another component to "target" you i.e., you call 
// another component whenever you would call yourself (as a result 
// of your component being used by another component)
//-----------------------------------------------------------------

pascal ComponentResult FFusionCodecTarget(FFusionGlobals glob, ComponentInstance target)
{
    glob->target = target;
	
    return noErr;
}

//-----------------------------------------------------------------
// Component GetMPWorkFunction Request
//-----------------------------------------------------------------
// Allows your image decompressor component to perform asynchronous 
// decompression in a single MP task by taking advantage of the 
// Base Decompressor. If you implement this selector, your DrawBand 
// function must be MP-safe. MP safety means not calling routines 
// that may move or purge memory and not calling any routines which
// might cause 68K code to be executed. Ideally, your DrawBand 
// function should not make any API calls whatsoever. Obviously 
// don't implement this if you're building a 68k component.
//-----------------------------------------------------------------

pascal ComponentResult FFusionCodecGetMPWorkFunction(FFusionGlobals glob, ComponentMPWorkFunctionUPP *workFunction, void **refCon)
{
	if (glob->drawBandUPP == NULL)
		glob->drawBandUPP = NewImageCodecMPDrawBandUPP((ImageCodecMPDrawBandProcPtr)FFusionCodecDrawBand);
	
	return ImageCodecGetBaseMPWorkFunction(glob->delegateComponent, workFunction, refCon, glob->drawBandUPP, glob);
}

//-----------------------------------------------------------------
// ImageCodecInitialize
//-----------------------------------------------------------------
// The first function call that your image decompressor component 
// receives from the base image decompressor is always a call to 
// ImageCodecInitialize . In response to this call, your image 
// decompressor component returns an ImageSubCodecDecompressCapabilities 
// structure that specifies its capabilities.
//-----------------------------------------------------------------

pascal ComponentResult FFusionCodecInitialize(FFusionGlobals glob, ImageSubCodecDecompressCapabilities *cap)
{
	Boolean doExperimentalFlags = CFPreferencesGetAppBooleanValue(CFSTR("ExperimentalQTFlags"), PERIAN_PREF_DOMAIN, NULL);
	
    // Secifies the size of the ImageSubCodecDecompressRecord structure
    // and say we can support asyncronous decompression
    // With the help of the base image decompressor, any image decompressor
    // that uses only interrupt-safe calls for decompression operations can
    // support asynchronous decompression.
	
    cap->decompressRecordSize = sizeof(FFusionDecompressRecord) + 12;
    cap->canAsync = true;
	
	// QT 7
	if(cap->recordSize > offsetof(ImageSubCodecDecompressCapabilities, baseCodecShouldCallDecodeBandForAllFrames))
	{
		cap->subCodecIsMultiBufferAware = true;
		cap->subCodecSupportsOutOfOrderDisplayTimes = true;
		cap->baseCodecShouldCallDecodeBandForAllFrames = true;
		cap->subCodecSupportsScheduledBackwardsPlaybackWithDifferenceFrames = !doExperimentalFlags;
		cap->subCodecSupportsDrawInDecodeOrder = doExperimentalFlags; 
		cap->subCodecSupportsDecodeSmoothing = true; 
	}
	
    return noErr;
}

static inline int shouldDecode(FFusionGlobals glob, enum CodecID codecID)
{
	FFusionDecodeAbilities decode = FFUSION_PREFER_DECODE;
	if (glob->componentType == 'avc1')
		decode = ffusionIsParsedVideoDecodable(glob->begin.parser);
	if(decode > FFUSION_CANNOT_DECODE && 
	   (codecID == CODEC_ID_H264 || codecID == CODEC_ID_MPEG4) && CFPreferencesGetAppBooleanValue(CFSTR("PreferAppleCodecs"), PERIAN_PREF_DOMAIN, NULL))
		decode = FFUSION_PREFER_NOT_DECODE;
	if(decode > FFUSION_CANNOT_DECODE)
		if(IsForcedDecodeEnabled())
			decode = FFUSION_PREFER_DECODE;
	return decode > FFUSION_PREFER_NOT_DECODE;
}

//-----------------------------------------------------------------
// ImageCodecPreflight
//-----------------------------------------------------------------
// The base image decompressor gets additional information about the 
// capabilities of your image decompressor component by calling 
// ImageCodecPreflight. The base image decompressor uses this
// information when responding to a call to the ImageCodecPredecompress 
// function, which the ICM makes before decompressing an image. You 
// are required only to provide values for the wantedDestinationPixelSize 
// and wantedDestinationPixelTypes fields and can also modify other
// fields if necessary.
//-----------------------------------------------------------------

pascal ComponentResult FFusionCodecPreflight(FFusionGlobals glob, CodecDecompressParams *p)
{
    OSType *pos;
    int index;
    CodecCapabilities *capabilities = p->capabilities;
	long count = 0;
	Handle imgDescExt;
	OSErr err = noErr;
	
    // We first open libavcodec library and the codec corresponding
    // to the fourCC if it has not been done before
    
	FFusionDebugPrint("%p Preflight called.\n", glob);
	FFusionDebugPrint("%p Frame dropping is %senabled\n", glob, not(IsFrameDroppingEnabled()));
	
    if (!glob->avCodec)
    {
		init_FFmpeg();
		initFFusionParsers();

		OSType componentType = glob->componentType;
		enum CodecID codecID = getCodecID(componentType);
		
		glob->packedType = DefaultPackedTypeForCodec(componentType);
		
		if(componentType == 'VP30' || componentType == 'VP31')
			glob->shouldUseReturnedFrame = TRUE;

		if(codecID == CODEC_ID_NONE)
		{
			Codecprintf(glob->fileLog, "Warning! Unknown codec type! Using MPEG4 by default.\n");
			codecID = CODEC_ID_MPEG4;
		}
		
		glob->avCodec = avcodec_find_decoder(codecID);
//		if(glob->packedType != PACKED_QUICKTIME_KNOWS_ORDER)
			glob->begin.parser = ffusionParserInit(codecID);
                
		if ((codecID == CODEC_ID_MPEG4 || codecID == CODEC_ID_H264) && !glob->begin.parser)
			Codecprintf(glob->fileLog, "This is a parseable format, but we couldn't open a parser!\n");
		
        // we do the same for the AVCodecContext since all context values are
        // correctly initialized when calling the alloc function
        
        glob->avContext = avcodec_alloc_context2(CODEC_TYPE_VIDEO);
		
		// Use low delay
		glob->avContext->flags |= CODEC_FLAG_LOW_DELAY;
		
        // Image size is mandatory for DivX-like codecs
        
        glob->avContext->width = (**p->imageDescription).width;
        glob->avContext->height = (**p->imageDescription).height;
		glob->avContext->bits_per_coded_sample = (**p->imageDescription).depth;
		
        // We also pass the FourCC since it allows the H263 hybrid decoder
        // to make the difference between the various flavours of DivX
        glob->avContext->codec_tag = Endian32_Swap(glob->componentType);
		glob->avContext->codec_id  = codecID;
        
		// avc1 requires the avcC extension
		if (glob->componentType == 'avc1') {
			count = isImageDescriptionExtensionPresent(p->imageDescription, 'avcC');
			
			if (count >= 1) {
				imgDescExt = NewHandle(0);
				GetImageDescriptionExtension(p->imageDescription, &imgDescExt, 'avcC', 1);
				
				glob->avContext->extradata = calloc(1, GetHandleSize(imgDescExt) + FF_INPUT_BUFFER_PADDING_SIZE);
				memcpy(glob->avContext->extradata, *imgDescExt, GetHandleSize(imgDescExt));
				glob->avContext->extradata_size = GetHandleSize(imgDescExt);
				
				DisposeHandle(imgDescExt);
			} else {
				count = isImageDescriptionExtensionPresent(p->imageDescription, 'strf');
				
				// avc1 in AVI, need to reorder frames
				if (count >= 1)
					glob->packedType = PACKED_ALL_IN_FIRST_FRAME;
			}
		} else if (glob->componentType == 'mp4v') {
			count = isImageDescriptionExtensionPresent(p->imageDescription, 'esds');
			
			if (count >= 1) {
				imgDescExt = NewHandle(0);
				GetImageDescriptionExtension(p->imageDescription, &imgDescExt, 'esds', 1);
				
				ReadESDSDescExt(imgDescExt, &glob->avContext->extradata, &glob->avContext->extradata_size);
				
				DisposeHandle(imgDescExt);
			}
		} else {
			count = isImageDescriptionExtensionPresent(p->imageDescription, 'strf');
			
			if (count >= 1) {
				imgDescExt = NewHandle(0);
				GetImageDescriptionExtension(p->imageDescription, &imgDescExt, 'strf', 1);
				
				if (GetHandleSize(imgDescExt) - 40 > 0) {
					glob->avContext->extradata = calloc(1, GetHandleSize(imgDescExt) - 40 + FF_INPUT_BUFFER_PADDING_SIZE);
					memcpy(glob->avContext->extradata, *imgDescExt + 40, GetHandleSize(imgDescExt) - 40);
					glob->avContext->extradata_size = GetHandleSize(imgDescExt) - 40;
				}
				DisposeHandle(imgDescExt);
			}
		}
		
		FFusionDebugPrint("%p preflighted for %dx%d '%s'. (%d bytes of extradata)\n", glob, (**p->imageDescription).width, (**p->imageDescription).height, FourCCString(glob->componentType), glob->avContext->extradata_size);
        
		if(glob->avContext->extradata_size != 0 && glob->begin.parser != NULL)
			ffusionParseExtraData(glob->begin.parser, glob->avContext->extradata, glob->avContext->extradata_size);
		
		if (glob->fileLog)
			ffusionLogDebugInfo(glob->begin.parser, glob->fileLog);
		
		if (!shouldDecode(glob, codecID))
			err = featureUnsupported;
		
		// some hooks into ffmpeg's buffer allocation to get frames in 
		// decode order without delay more easily
		glob->avContext->opaque = glob;
		glob->avContext->get_buffer = FFusionGetBuffer;
		glob->avContext->release_buffer = FFusionReleaseBuffer;
		
		// multi-slice decoding
		SetupMultithreadedDecoding(glob->avContext, codecID);
		
		// deblock skipping for h264
		SetSkipLoopFilter(glob, glob->avContext);
		
		//fast flag
		if (CFPreferencesGetAppBooleanValue(CFSTR("UseFastDecode"), PERIAN_PREF_DOMAIN, NULL))
			glob->avContext->flags2 |= CODEC_FLAG2_FAST;
		
        // Finally we open the avcodec 
        
        if (avcodec_open(glob->avContext, glob->avCodec))
        {
            Codecprintf(glob->fileLog, "Error opening avcodec!\n");
            
			err = paramErr;
        }
		
        // codec was opened, but didn't give us its pixfmt
		// we have to decode the first frame to find out one
		else if (glob->avContext->pix_fmt == PIX_FMT_NONE && p->bufferSize && p->data)
            glob->avContext->pix_fmt = FindPixFmtFromVideo(glob->avCodec, glob->avContext, p->data, p->bufferSize);
    }
    
    // Specify the minimum image band height supported by the component
    // bandInc specifies a common factor of supported image band heights - 
    // if your component supports only image bands that are an even
    // multiple of some number of pixels high report this common factor in bandInc
    
    capabilities->bandMin = (**p->imageDescription).height;
    capabilities->bandInc = capabilities->bandMin;
	
    // libavcodec 0.4.x is no longer stream based i.e. you cannot pass just
    // an arbitrary amount of data to the library.
{'fraction_non_alphanumeric': 0.16699004478938112, 'fraction_numerical': 0.00718080765952817, 'mean_word_length': 2.8422629415453216, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 433, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4460758', 'n_tokens_mistral': 17536, 'n_tokens_neox': 16224, 'n_words': 4233}