www.pudn.com > FFusion.rar > FFusionCodec.c


//---------------------------------------------------------------------------
//FFusion
//Alternative DivX Codec for MacOS X
//version 2.2 (build 72)
//by Jerome Cornet
//Copyright 2002-2003 Jerome Cornet
//parts by Dan Christiansen
//Copyright 2003 Dan Christiansen
//from DivOSX by Jamby
//Copyright 2001-2002 Jamby
//uses libavcodec from ffmpeg 0.4.6
//
// 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//---------------------------------------------------------------------------
// Source Code
//---------------------------------------------------------------------------

#include 
#include 

#include "FFusionCodec.h"
#include "EI_Image.h"
#include "avcodec.h"
#include "postprocess.h"

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

typedef struct
{
    pp_mode_t			*mode[PP_QUALITY_MAX+1];
    pp_context_t		*context;
    short			goodness;
    short			level;
} PostProcParamRecord;

typedef struct
{
    ComponentInstance		self;
    ComponentInstance		delegateComponent;
    ComponentInstance		target;
    ImageCodecMPDrawBandUPP 	drawBandUPP;
    Handle			pixelTypes;
    AVCodec			*avCodec;
    AVCodecContext		*avContext;
    AVFrame			*picture;
    OSType			componentType;
    char			hasy420;
    char			firstFrame;
    char			alreadyDonePPPref;
    PostProcParamRecord		postProcParams;
} FFusionGlobalsRecord, *FFusionGlobals;

typedef struct
{
    long			width;
    long			height;
    long			depth;
    OSType			pixelFormat;
    long			bufferSize;
} FFusionDecompressRecord;


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

static OSErr FFusionDecompress(AVCodecContext *context, UInt8 *dataPtr, ICMDataProcRecordPtr dataProc, UInt8 *baseAddr, long rowBump, long width, long height, AVFrame *picture, long length, char firstFrame);

static OSErr FFusionSlowDecompress(AVCodecContext *context, UInt8 *dataPtr, ICMDataProcRecordPtr dataProc, UInt8 *baseAddr, long rowBump, long width, long height, AVFrame *picture, long length, char firstFrame);

int Codecprintf(const char *format, ...);
void FourCCprintf(char *string, unsigned long a);
void FourCCcopy(OSType *dest, OSType *src);
int FourCCcompare(OSType *a, OSType *b);

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

//---------------------------------------------------------------------------
// 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 
#include 

//---------------------------------------------------------------------------
// 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;
    ComponentDescription cd;
    Component c = 0;
    long bitfield;

    cd.componentType = 'imdc';
    cd.componentSubType = 'y420';
    cd.componentManufacturer = 0;
    cd.componentFlags = 0;
    cd.componentFlagsMask = 0;
    
    GetComponentInfo((Component)self, &descout, 0, 0, 0);

    Codecprintf("Opening component for type");
    FourCCprintf(" ", 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("Unable to allocate globals! Exiting.\n");            
    }
    else
    {
        SetComponentInstanceStorage(self, (Handle)glob);
        
        glob->self = self;
        glob->target = self;
        glob->drawBandUPP = NULL;
        glob->pixelTypes = NewHandle(10 * sizeof(OSType));
        glob->avCodec = 0;
        glob->hasy420 = 0;
        glob->firstFrame = 0;
        glob->alreadyDonePPPref = 0;
        glob->componentType = descout.componentSubType;

        c = FindNextComponent(c, &cd);

        if (c != 0)
        {            
            Gestalt(gestaltSystemVersion, &bitfield);
            
            if (bitfield >= 0x1010)
            {
                Codecprintf("Use speedy y420 component");
                printf(" 0x%lx\n", bitfield);
                
                glob->hasy420 = 1;
            }
        }
        else
        {
            Codecprintf("Use slow y420 component\n");
        }

        // 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("Error opening the base image decompressor! Exiting.\n");
        }
    }
    
    return err;
}

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

pascal ComponentResult FFusionCodecClose(FFusionGlobals glob, ComponentInstance self)
{
    // Make sure to close the base component and dealocate our storage
    int i;
    
    if (glob) 
    {
        if (glob->delegateComponent) 
        {
            CloseComponent(glob->delegateComponent);
        }
        
        if (glob->drawBandUPP) 
        {
            DisposeImageCodecMPDrawBandUPP(glob->drawBandUPP);
        }
        
        if (glob->avCodec)
        {
            avcodec_close(glob->avContext);
        }
        
        if (glob->picture)
        {
            av_free(glob->picture);
        }
        
        if (glob->avContext)
        {
            av_free(glob->avContext);
        }
        
        for (i=0; i<=PP_QUALITY_MAX; i++)
        {
            if (glob->postProcParams.mode[i])
                pp_free_mode(glob->postProcParams.mode[i]);
        }

        if (glob->postProcParams.context)
            pp_free_context(glob->postProcParams.context);
             
        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)
{

    // 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);
    cap->canAsync = true;

    return noErr;
}

//-----------------------------------------------------------------
// 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, i;
    CodecCapabilities *capabilities = p->capabilities;
    long bitfield;
    char altivec = 0;
    Byte* myptr;

    // We first open libavcodec librairie and the codec corresponding
    // to the fourCC if it has not been done before
    
    if (!glob->avCodec)
    {
        avcodec_init();
        avcodec_register_all();

        switch (glob->componentType)
        {
            case 'MPG4':	// MS-MPEG4 v1
            case 'mpg4':
            case 'DIV1':
            case 'div1':
                glob->avCodec = avcodec_find_decoder(CODEC_ID_MSMPEG4V1);
            break;

            case 'MP42':	// MS-MPEG4 v2
            case 'mp42':
            case 'DIV2':
            case 'div2':
                glob->avCodec = avcodec_find_decoder(CODEC_ID_MSMPEG4V2);
            break;

            case 'div6':	// DivX 3
            case 'DIV6':
            case 'div5':
            case 'DIV5':
            case 'div4':
            case 'DIV4':
            case 'div3':
            case 'DIV3':
            case 'MP43':
            case 'mp43':
            case 'MPG3':
            case 'mpg3':
            case 'AP41':
            case 'COL0':
            case 'col0':
            case 'COL1':
            case 'col1':
            case '3IVD':	// 3ivx
            case '3ivd':
                glob->avCodec = avcodec_find_decoder(CODEC_ID_MSMPEG4V3);
            break;

            case 'divx':	// DivX 4
            case 'DIVX':
            case 'mp4s':
            case 'MP4S':
            case 'm4s2':
            case 'M4S2':
            case 0x04000000:
            case 'UMP4':
            case 'DX50':	// DivX 5
            case 'XVID':	// XVID
            case 'xvid':
            case 'XviD':
            case 'XVIX':
            case 'BLZ0':
            case '3IV2':	// 3ivx
            case '3iv2':
                glob->avCodec = avcodec_find_decoder(CODEC_ID_MPEG4);
            break;
            
            default:
                Codecprintf("Warning! Unknown codec type! Using MPEG4 by default.\n");
                
                glob->avCodec = avcodec_find_decoder(CODEC_ID_MPEG4);
        }
        
        // libavcodec version 0.4.6 and higher uses AVFrame instead of AVPicture
        // It is better to handle the frame with a pointer and to use built-in
        // allocation function
        
        glob->picture = avcodec_alloc_frame();
        
        // we do the same for the AVCodecContext since all context values are
        // correctly initialized when calling the alloc function
        
        glob->avContext = avcodec_alloc_context();
        
        // Image size is mandatory for DivX-like codecs
        
        glob->avContext->width = (**p->imageDescription).width;
        glob->avContext->height = (**p->imageDescription).height;
        
        // We also pass the FourCC since it allows the H263 hybrid decoder
        // to make the difference between the various flavours of DivX
        
        myptr = (unsigned char *)&(glob->componentType);
        glob->avContext->codec_tag = (myptr[3] << 24) + (myptr[2] << 16) + (myptr[1] << 8) + myptr[0];
        
        // Let's look for a CPU with AltiVec-like SMID unit
        
        Gestalt(gestaltPowerPCProcessorFeatures, &bitfield);
        altivec = (bitfield & (1 << gestaltPowerPCHasVectorInstructions)) != 0;
        
        if (altivec)
        {
            Codecprintf("Altivec Acceleration enabled!\n");
                
            glob->avContext->idct_algo = FF_IDCT_ALTIVEC;
        }
    
        // Finally we open the avcodec 
        
        if (avcodec_open(glob->avContext, glob->avCodec))
        {
            Codecprintf("Error opening avcodec!\n");
            
            return -2;
        }
    }
    
    // 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.
    // Instead we have to tell QT to just pass the data corresponding 
    // to one frame
     
    capabilities->flags |= codecWantsSpecialScaling;
    
    p->requestedBufferWidth = (**p->imageDescription).width;
    p->requestedBufferHeight = (**p->imageDescription).height;
    
    // Indicate the pixel depth the component can use with the specified image
    // normally should be capabilities->wantedPixelSize = (**p->imageDescription).depth;
    // but we don't care since some DivX are so bugged that the depth information
    // is not correct
    
    capabilities->wantedPixelSize = 0;
    
    // Type of pixels used in output
    // If QuickTime got the y420 component it is cool
    // since libavcodec ouputs y420
    // If not we'll do some king of conversion to 2vuy
    
    HLock(glob->pixelTypes);
    pos = *((OSType **)glob->pixelTypes);
    
    index = 0;

    if (glob->hasy420)
    {
        pos[index++] = 'y420';
    }
    else
    {
        pos[index++] = k2vuyPixelFormat;        
    }
    
    pos[index++] = 0;
    HUnlock(glob->pixelTypes);

    p->wantedDestinationPixelTypes = (OSType **)glob->pixelTypes;
    
    // Specify the number of pixels the image must be extended in width and height if
    // the component cannot accommodate the image at its given width and height
    // It is not the case here
    
    capabilities->extendWidth = 0;
    capabilities->extendHeight = 0;
    
    // Post-processing
    
    glob->postProcParams.context = pp_get_context((**p->imageDescription).width, (**p->imageDescription).height, PP_FORMAT_420);
    
    //glob->ppMode = pp_get_mode_by_name_and_quality("hb,vb,dr,""hb:c,vb:c,dr:c,""al:f", GetPPUserPreference());
    //glob->ppMode = pp_get_mode_by_name_and_quality("de", GetPPUserPreference());
    
    for (i=0; i<=PP_QUALITY_MAX; i++) 
    {
        //glob->postProcParams.mode[i] = pp_get_mode_by_name_and_quality("hb,vb,dr,""hb:c,vb:c,dr:c,""al:f", i);
        //glob->postProcParams.mode[i] = pp_get_mode_by_name_and_quality("de", i);
        
        if (i <= 2) 
        {
            glob->postProcParams.mode[i] = pp_get_mode_by_name_and_quality("h1,v1,dr"/*"al:f"*/, i);
        } 
        else if (i <= 4) 
        {
            glob->postProcParams.mode[i] = pp_get_mode_by_name_and_quality("hb,vb,dr,"/*"al:f"*/, i);
        } 
        else 
        {
            glob->postProcParams.mode[i] = pp_get_mode_by_name_and_quality("hb,vb,dr,""hb:c,vb:c,dr:c,"/*"al:f"*/, i);
        }

        if (glob->postProcParams.mode[i] == NULL) 
        {
            printf("Error getting PP filter %d!\n", i);
            
            return -1;
        }

        glob->postProcParams.goodness = 0;
        glob->postProcParams.level = GetPPUserPreference();
    }

    
    return noErr;
}

//-----------------------------------------------------------------
// ImageCodecBeginBand
//-----------------------------------------------------------------
// The ImageCodecBeginBand function allows your image decompressor 
// component to save information about a band before decompressing 
// it. This function is never called at interrupt time. The base 
// image decompressor preserves any changes your component makes to 
// any of the fields in the ImageSubCodecDecompressRecord or 
// CodecDecompressParams structures. If your component supports 
// asynchronous scheduled decompression, it may receive more than
// one ImageCodecBeginBand call before receiving an ImageCodecDrawBand 
// call.
//-----------------------------------------------------------------

pascal ComponentResult FFusionCodecBeginBand(FFusionGlobals glob, CodecDecompressParams *p, ImageSubCodecDecompressRecord *drp, long flags)
{	
    long offsetH, offsetV;
    FFusionDecompressRecord *myDrp = (FFusionDecompressRecord *)drp->userDecompressRecord;
        
    //////
    IBNibRef 		nibRef;
    WindowRef 		window;
    OSStatus		err;
    CFBundleRef		bundleRef;
    EventHandlerUPP  	handlerUPP, handlerUPP2;
    EventTypeSpec    	eventType;
    ControlID		controlID;
    ControlRef		theControl;
    KeyMap		currentKeyMap;
    int			userPreference;
    ///////

    offsetH = (long)(p->dstRect.left - p->dstPixMap.bounds.left) * (long)(p->dstPixMap.pixelSize >> 3);
    offsetV = (long)(p->dstRect.top - p->dstPixMap.bounds.top) * (long)drp->rowBytes;
    
    myDrp->width = (**p->imageDescription).width;
    myDrp->height = (**p->imageDescription).height;
    myDrp->depth = (**p->imageDescription).depth;
    myDrp->bufferSize = p->bufferSize;			// bufferSize is the data size of the current frame

    myDrp->pixelFormat = p->dstPixMap.pixelFormat;
    
    if (p->conditionFlags & codecConditionFirstFrame)
    {
        glob->firstFrame = 1;
        
        GetKeys(currentKeyMap);
        
        if ((currentKeyMap[1] & kOptionKeyModifier) && !glob->alreadyDonePPPref)
        {
            glob->alreadyDonePPPref = 1;
    
            bundleRef = CFBundleGetBundleWithIdentifier(CFSTR("net.aldorande.component.FFusion"));
        
            if (bundleRef == NULL)
                printf("Cannot get main bundle reference\n");
                
            err = CreateNibReferenceWithCFBundle(bundleRef, CFSTR("main"), &nibRef);
            
            if (err != noErr)
                printf("cannot create nib reference! %d\n", (int)err);
            
            if (nibRef != NULL)
            {
                err = CreateWindowFromNib(nibRef, CFSTR("PostProcessing"), &window);
                
                if (err != noErr)
                    printf("cannot create window!\n");
                
                DisposeNibReference(nibRef);
            
                if (window != NULL)
                {
                    controlID.signature = 'post';
                    controlID.id = 128;
                    
                    err = GetControlByID(window, &controlID, &theControl);
                    
                    if (err != noErr)
                    {
                        printf("Cannot get slider hint text control!\n");
                    }

                    userPreference = GetPPUserPreference();
                    ChangeHintText(userPreference, theControl);
                    
                    controlID.signature = 'post';
                    controlID.id = 129;
                    
                    err = GetControlByID(window, &controlID, &theControl);
                    
                    if (err != noErr)
                    {
                        printf("Cannot get slider control!\n");
                    }

                    SetControl32BitValue(theControl, userPreference);

                    ShowWindow(window);
                    
                    handlerUPP = NewEventHandlerUPP(HandlePPDialogWindowEvent);
                    
                    eventType.eventClass = kEventClassCommand;
                    eventType.eventKind  = kEventCommandProcess;
                    
                    InstallWindowEventHandler(window, handlerUPP, 1, &eventType, window, NULL);
                    
                    handlerUPP2 = NewEventHandlerUPP(HandlePPDialogControlEvent);
                    
                    eventType.eventClass = kEventClassControl;
                    eventType.eventKind = kEventControlTrack;
                    
                    InstallControlEventHandler(theControl, handlerUPP2, 1, &eventType, window, NULL);
                }
            }
        }
    }
    
    /*if ((glob->postProcParams.level >= 0) && (GetPPUserPreference() > 0)) 
    {
        if (p->conditionFlags & codecConditionCatchUpDiff) 
        {
            if (glob->postProcParams.level > 0) 
            {
                glob->postProcParams.level--;
                
                printf("PP=%i -\n", glob->postProcParams.level);
            }
            
            glob->postProcParams.goodness = 0;
        } 
        else 
        {
            if (++glob->postProcParams.goodness > 5) 
            {
                if (glob->postProcParams.level < PP_QUALITY_MAX + 1) 
                {
                    glob->postProcParams.level++;
                    
                    printf("PP=%i +\n", glob->postProcParams.level);
                }
                
                glob->postProcParams.goodness = 0;
            }
        }
    }*/

    return noErr;
}

//-----------------------------------------------------------------
// ImageCodecDrawBand
//-----------------------------------------------------------------
// The base image decompressor calls your image decompressor 
// component's ImageCodecDrawBand function to decompress a band or 
// frame. Your component must implement this function. If the 
// ImageSubCodecDecompressRecord structure specifies a progress function 
// or data-loading function, the base image decompressor will never call 
// ImageCodecDrawBand at interrupt time. If the ImageSubCodecDecompressRecord 
// structure specifies a progress function, the base image decompressor
// handles codecProgressOpen and codecProgressClose calls, and your image 
// decompressor component must not implement these functions.
// If not, the base image decompressor may call the ImageCodecDrawBand 
// function at interrupt time. When the base image decompressor calls your 
// ImageCodecDrawBand function, your component must perform the decompression 
// specified by the fields of the ImageSubCodecDecompressRecord structure. 
// The structure includes any changes your component made to it when
// performing the ImageCodecBeginBand function. If your component supports 
// asynchronous scheduled decompression, it may receive more than one 
// ImageCodecBeginBand call before receiving an ImageCodecDrawBand call.
//-----------------------------------------------------------------

pascal ComponentResult FFusionCodecDrawBand(FFusionGlobals glob, ImageSubCodecDecompressRecord *drp)
{
    OSErr err = noErr;
    
    FFusionDecompressRecord *myDrp = (FFusionDecompressRecord *)drp->userDecompressRecord;
    unsigned char *dataPtr = (unsigned char *)drp->codecData;
    
    ICMDataProcRecordPtr dataProc = drp->dataProcRecord.dataProc ? &drp->dataProcRecord : NULL;
    
    unsigned char *ppPage[3];
    int ppStride[3];

    
    if (myDrp->pixelFormat == 'y420')
    {
        err = FFusionDecompress(glob->avContext, dataPtr, dataProc, (UInt8 *)drp->baseAddr, drp->rowBytes, myDrp->width, myDrp->height, glob->picture, myDrp->bufferSize, glob->firstFrame);
    }
    else
    {
        err = FFusionSlowDecompress(glob->avContext, dataPtr, dataProc, (UInt8 *)drp->baseAddr, drp->rowBytes, myDrp->width, myDrp->height, glob->picture, myDrp->bufferSize, glob->firstFrame);
    }

    if (glob->firstFrame)
        glob->firstFrame = 0;
    
    if ((err == noErr) && (glob->postProcParams.level > 0))
    {
        ppPage[0] = glob->picture->data[0];
        ppPage[1] = glob->picture->data[1];
        ppPage[2] = glob->picture->data[2];
        ppStride[0] = glob->picture->linesize[0];
        ppStride[1] = glob->picture->linesize[1];
        ppStride[2] = glob->picture->linesize[2];

        pp_postprocess(ppPage, ppStride,
                       ppPage, ppStride,
                       myDrp->width, myDrp->height,
                       glob->picture->qscale_table,
                       glob->picture->qstride,
                       glob->postProcParams.mode[glob->postProcParams.level],
                       glob->postProcParams.context,
                       glob->picture->pict_type);
    }
    
    return err;
}

//-----------------------------------------------------------------
// ImageCodecEndBand
//-----------------------------------------------------------------
// The ImageCodecEndBand function notifies your image decompressor 
// component that decompression of a band has finished or
// that it was terminated by the Image Compression Manager. Your 
// image decompressor component is not required to implement the 
// ImageCodecEndBand function. The base image decompressor may call 
// the ImageCodecEndBand function at interrupt time.
// After your image decompressor component handles an ImageCodecEndBand 
// call, it can perform any tasks that are required when decompression
// is finished, such as disposing of data structures that are no longer 
// needed. Because this function can be called at interrupt time, your 
// component cannot use this function to dispose of data structures; 
// this must occur after handling the function. The value of the result
// parameter should be set to noErr if the band or frame was
// drawn successfully.
// If it is any other value, the band or frame was not drawn.
//-----------------------------------------------------------------

pascal ComponentResult FFusionCodecEndBand(FFusionGlobals glob, ImageSubCodecDecompressRecord *drp, OSErr result, long flags)
{
    return noErr;
}

//-----------------------------------------------------------------
// ImageCodecQueueStarting
//-----------------------------------------------------------------
// If your component supports asynchronous scheduled decompression,
// the base image decompressor calls your image decompressor 
// component's ImageCodecQueueStarting function before decompressing 
// the frames in the queue. Your component is not required to implement 
// this function. It can implement the function if it needs to perform 
// any tasks at this time, such as locking data structures.
// The base image decompressor never calls the ImageCodecQueueStarting 
// function at interrupt time.
//-----------------------------------------------------------------

pascal ComponentResult FFusionCodecQueueStarting(FFusionGlobals glob)
{	
    return noErr;
}

//-----------------------------------------------------------------
// ImageCodecQueueStopping
//-----------------------------------------------------------------
// If your image decompressor component supports asynchronous scheduled 
// decompression, the ImageCodecQueueStopping function notifies your 
// component that the frames in the queue have been decompressed. 
// Your component is not required to implement this function.
// After your image decompressor component handles an ImageCodecQueueStopping 
// call, it can perform any tasks that are required when decompression
// of the frames is finished, such as disposing of data structures that 
// are no longer needed. 
// The base image decompressor never calls the ImageCodecQueueStopping 
// function at interrupt time.
//-----------------------------------------------------------------

pascal ComponentResult FFusionCodecQueueStopping(FFusionGlobals glob)
{	
    return noErr;
}

//-----------------------------------------------------------------
// ImageCodecGetCompressedImageSize
//-----------------------------------------------------------------
// Your component receives the ImageCodecGetCompressedImageSize request 
// whenever an application calls the ICM's GetCompressedImageSize function.
// You can use the ImageCodecGetCompressedImageSize function when you 
// are extracting a single image from a sequence; therefore, you don't have 
// an image description structure and don't know the exact size of one frame. 
// In this case, the Image Compression Manager calls the component to determine
// the size of the data. Your component should return a long integer indicating 
// the number of bytes of data in the compressed image. You may want to store
// the image size somewhere in the image description structure, so that you can 
// respond to this request quickly. Only decompressors receive this request.
//-----------------------------------------------------------------

pascal ComponentResult FFusionCodecGetCompressedImageSize(FFusionGlobals glob, ImageDescriptionHandle desc, Ptr data, long dataSize, ICMDataProcRecordPtr dataProc, long *size)
{
    ImageFramePtr framePtr = (ImageFramePtr)data;

    if (size == NULL) 
            return paramErr;

    *size = EndianU32_BtoN(framePtr->frameSize) + sizeof(ImageFrame);

    return noErr;
}

//-----------------------------------------------------------------
// ImageCodecGetCodecInfo
//-----------------------------------------------------------------
// Your component receives the ImageCodecGetCodecInfo request whenever 
// an application calls the Image Compression Manager's GetCodecInfo 
// function.
// Your component should return a formatted compressor information 
// structure defining its capabilities.
// Both compressors and decompressors may receive this request.
//-----------------------------------------------------------------

pascal ComponentResult FFusionCodecGetCodecInfo(FFusionGlobals glob, CodecInfo *info)
{
    OSErr err = noErr;

    if (info == NULL) 
    {
        err = paramErr;
    }
    else 
    {
        CodecInfo **tempCodecInfo;

        switch (glob->componentType)
        {
            case 'MPG4':	// MS-MPEG4 v1
            case 'mpg4':
            case 'DIV1':
            case 'div1':
                err = GetComponentResource((Component)glob->self, codecInfoResourceType, kDivX1CodecInfoResID, (Handle *)&tempCodecInfo);
                break;
                
            case 'MP42':	// MS-MPEG4 v2
            case 'mp42':
            case 'DIV2':
            case 'div2':
                err = GetComponentResource((Component)glob->self, codecInfoResourceType, kDivX2CodecInfoResID, (Handle *)&tempCodecInfo);
                break;
                
            case 'div6':	// DivX 3
            case 'DIV6':
            case 'div5':
            case 'DIV5':
            case 'div4':
            case 'DIV4':
            case 'div3':
            case 'DIV3':
            case 'MP43':
            case 'mp43':
            case 'MPG3':
            case 'mpg3':
            case 'AP41':
            case 'COL0':
            case 'col0':
            case 'COL1':
            case 'col1':
                err = GetComponentResource((Component)glob->self, codecInfoResourceType, kDivX3CodecInfoResID, (Handle *)&tempCodecInfo);
                break;

            case 'divx':	// DivX 4
            case 'DIVX':
            case 'mp4s':
            case 'MP4S':
            case 'm4s2':
            case 'M4S2':
            case 0x04000000:
            case 'UMP4':
                err = GetComponentResource((Component)glob->self, codecInfoResourceType, kDivX4CodecInfoResID, (Handle *)&tempCodecInfo);
                break;
                
            case 'DX50':
                err = GetComponentResource((Component)glob->self, codecInfoResourceType, kDivX5CodecInfoResID, (Handle *)&tempCodecInfo);
                break;
                
            case 'XVID':	// XVID
            case 'xvid':
            case 'XviD':
            case 'XVIX':
            case 'BLZ0':
                err = GetComponentResource((Component)glob->self, codecInfoResourceType, kXVIDCodecInfoResID, (Handle *)&tempCodecInfo);
                break;
                
            case '3IVD':	// 3ivx
            case '3ivd':
            case '3IV2':
            case '3iv2':
                err = GetComponentResource((Component)glob->self, codecInfoResourceType, k3ivxCodecInfoResID, (Handle *)&tempCodecInfo);
                break;
                
            default:	// should never happen but we have to handle the case
                err = GetComponentResource((Component)glob->self, codecInfoResourceType, kDivX4CodecInfoResID, (Handle *)&tempCodecInfo);

        }
        
        if (err == noErr) 
        {
            *info = **tempCodecInfo;
            
            DisposeHandle((Handle)tempCodecInfo);
        }
    }

    return err;
}

#define kSpoolChunkSize (16384)
#define kInfiniteDataSize (0x7fffffff)

//---------------------------------------------------------------------------
// Private SubRoutines
//---------------------------------------------------------------------------

//-----------------------------------------------------------------
// Codecprintf
//-----------------------------------------------------------------
// Little function to print correctly messages in the console
//-----------------------------------------------------------------

int Codecprintf(const char *format, ...)
{
    printf(CODEC_HEADER);
    
    return printf(format);
}

//-----------------------------------------------------------------
// FourCCprintf
//-----------------------------------------------------------------
// Little function to print correctly AVI's FourCC Code
//-----------------------------------------------------------------

void FourCCprintf (char *string, unsigned long a)
{
    if (a < 64)
    {
        printf("%s: %ld\n", string, a);
    }
    else
    {
        printf("%s: %c%c%c%c\n", string, (unsigned char)((a >> 24) & 0xff), 
                                        (unsigned char)((a >> 16) & 0xff), 
                                        (unsigned char)((a >> 8) & 0xff), 
                                        (unsigned char)(a & 0xff));
    }
}

//-----------------------------------------------------------------
// FourCCcopy
//-----------------------------------------------------------------
// Copy src FourCC in dest FourCC
//-----------------------------------------------------------------

void FourCCcopy(OSType *dest, OSType *src)
{
    int i;
    
    for (i=0; i<3; i++)
        dest[i] = src[i];
}

//-----------------------------------------------------------------
// FourCCprintf
//-----------------------------------------------------------------
// Compare two FourCC (case sensitive)
// Returns 1 if equal, 0 if not equal
//-----------------------------------------------------------------

int FourCCcompare(OSType *a, OSType *b)
{
    return ((a[0] == b[0]) && (a[1] == b[1]) && (a[2] == b[2]) && (a[3] == b[3]));
}

//-----------------------------------------------------------------
// FFusionDecompress
//-----------------------------------------------------------------
// This function calls libavcodec to decompress one frame.
// It returns y420 data directly to QuickTime which then converts
// in RGB for display
//-----------------------------------------------------------------

OSErr FFusionDecompress(AVCodecContext *context, UInt8 *dataPtr, ICMDataProcRecordPtr dataProc, UInt8 *baseAddr, long rowBump, long width, long height, AVFrame *picture, long length, char firstFrame)
{
    OSErr err = noErr;
    int got_picture = false;
    int len = 0;
    UInt8 *endOfScanLine;
    PlanarPixmapInfoYUV420 *planar;
    long availableData = dataProc ? codecMinimumDataSize : kInfiniteDataSize;

    endOfScanLine = baseAddr + (width * 4);
    context->width = width;
    context->height = height;
    picture->data[0] = 0;
    
    while (!got_picture) 
    {
        if (availableData < kSpoolChunkSize) 
        {
            // get some more source data
            
            err = InvokeICMDataUPP((Ptr *)&dataPtr, length, dataProc->dataRefCon, dataProc->dataProc);
                
            if (err == eofErr) err = noErr;
            if (err) return err;

            availableData = codecMinimumDataSize;
        }
                
        len = avcodec_decode_video(context, picture, &got_picture, dataPtr, length);
        
        if (len < 0)
        {            
            got_picture = 0;
            len = 1;
            Codecprintf("Error while decoding frame\n");
            
            if (firstFrame)
            {
                return codecErr;
            }
            else
            {
                return noErr;
            }
        }
        
        availableData -= len;
        dataPtr += len;
    }

    planar = (PlanarPixmapInfoYUV420 *) baseAddr;
    
    // if ya can't set da poiners, set da offsets
    planar->componentInfoY.offset = picture->data[0] - baseAddr;
    planar->componentInfoCb.offset =  picture->data[1] - baseAddr;
    planar->componentInfoCr.offset =  picture->data[2] - baseAddr;
    
    // for the 16/32 add look at EDGE in mpegvideo.c
    planar->componentInfoY.rowBytes = picture->linesize[0];
    planar->componentInfoCb.rowBytes = picture->linesize[1];
    planar->componentInfoCr.rowBytes = picture->linesize[2];
    
    return err;
}

//-----------------------------------------------------------------
// FFusionSlowDecompress
//-----------------------------------------------------------------
// This function calls libavcodec to decompress one frame.
// In this case we have to return 2yuv values because
// QT version has no built-in y420 component.
// Since we do the conversion ourselves it is not really optimized....
// The function should never be called since many people now
// have a decent OS/QT version.
//-----------------------------------------------------------------

OSErr FFusionSlowDecompress(AVCodecContext *context, UInt8 *dataPtr, ICMDataProcRecordPtr dataProc, UInt8 *baseAddr, long rowBump, long width, long height, AVFrame *picture, long length, char firstFrame)
{
    OSErr err = noErr;
    int got_picture = false;
    int len = 0;
    unsigned int i, j;
    UInt8 *endOfScanLine;
    char *yuvPtr;
    char *py,*pu,*pv;
    long availableData = dataProc ? codecMinimumDataSize : kInfiniteDataSize;

    endOfScanLine = baseAddr + (width * 4);
    context->width = width;
    context->height = height;
    picture->data[0] = 0;
    
    while (!got_picture)
    {
        if (availableData < kSpoolChunkSize) 
        {
            // get some more source data
            
            err = InvokeICMDataUPP((Ptr *)&dataPtr, length, dataProc->dataRefCon, dataProc->dataProc);
                
            if (err == eofErr) err = noErr;
            if (err) return err;
    
            availableData = codecMinimumDataSize;
        }
        
        len = avcodec_decode_video(context, picture, &got_picture, dataPtr, length);
        
        if (len < 0)
        {
            got_picture = 0;
            len = 1;
            
            Codecprintf("Error while decoding frame\n");

            if (firstFrame)
            {
                return codecErr;
            }
            else
            {
                return noErr;
            }
        }
        
        availableData -= len;
        dataPtr += len;
    }
    
    // now let's do some yuv420/vuy2 conversion
    
    yuvPtr = baseAddr;
    py = picture->data[0];
    pu = picture->data[1];
    pv = picture->data[2];

    for(i = 0 ;  i < height; i++)
    {
        for(j = 0; j < width; j+= 2)
        {
            yuvPtr[2*j] = pu[j>>1];
            yuvPtr[2*j+1] = py[j];
            yuvPtr[2*j+2] = pv[j>>1];
            yuvPtr[2*j+3] = py[j+1];
        }
        
        yuvPtr += rowBump;
        py += picture->linesize[0];
        
        if (i & 1)
        {
            pu += picture->linesize[1];
            pv += picture->linesize[2];
        }
    }

    return err;
}

int GetPPUserPreference()
{
    CFIndex 	userPref;
    Boolean 	keyExists;
    int		ppUserValue = 0;
    
    userPref = CFPreferencesGetAppIntegerValue(CFSTR("postProcessingLevel"), CFSTR("net.aldorande.component.FFusion"), &keyExists);
    
    if (keyExists)
    {
        ppUserValue = (int)userPref;
    }
    else
    {
        Codecprintf("Key does not exists\n");
        
        ppUserValue = 0;
    }
    
    return ppUserValue;
}

void SetPPUserPreference(int value)
{
    int	    sameValue = value;
    Boolean syncOK;
        
    CFPreferencesSetAppValue(CFSTR("postProcessingLevel"), CFNumberCreate(NULL, kCFNumberIntType, &sameValue), CFSTR("net.aldorande.component.FFusion"));
    
    syncOK = CFPreferencesAppSynchronize(CFSTR("net.aldorande.component.FFusion"));
    
    if (!syncOK)
    {
        Codecprintf("Error writing user preference!\n");
    }
}

pascal OSStatus HandlePPDialogWindowEvent(EventHandlerCallRef  nextHandler, EventRef theEvent, void* userData)
{
    UInt32	whatHappened;
    HICommand 	commandStruct;
    UInt32 	theCommandID;
    OSErr	theErr = eventNotHandledErr;
    OSErr	resErr;
    WindowRef	window = (WindowRef)userData;
    SInt32	value = 0;
    ControlRef	slider;
    ControlID	controlID;
    
    whatHappened = GetEventKind(theEvent);

    switch (whatHappened)
    {
        case kEventCommandProcess:
            GetEventParameter(theEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &commandStruct);
        
            theCommandID = commandStruct.commandID;
            
            if (theCommandID == kHICommandOK)
            {
                controlID.id = 129;
                controlID.signature = 'post';
                resErr = GetControlByID(window, &controlID, &slider);
                
                if (resErr == noErr)
                {
                    value = GetControl32BitValue(slider);
                }
                else
                {
                    Codecprintf("Unable to get post-processing control value!\n");
                }
                
                SetPPUserPreference(value);
            
                DisposeWindow(window);
                
                theErr = noErr;
            }
            
            if (theCommandID == kHICommandCancel)
            {                
                DisposeWindow(window);
                
                theErr = noErr;
            }

            break;
    }
    
    return theErr;
}

pascal OSStatus HandlePPDialogControlEvent(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
{
    WindowRef	window = (WindowRef)userData;
    ControlID	controlID;
    ControlRef	slider, staticText;
    OSErr	resErr;
    SInt32	value;
    OSErr	result;
    
    result = CallNextEventHandler(nextHandler, theEvent);
    
    if (result == noErr)
    {        
        controlID.id = 128;
        controlID.signature = 'post';
        resErr = GetControlByID(window, &controlID, &staticText);
        
        controlID.id = 129;
        controlID.signature = 'post';
        resErr = GetControlByID(window, &controlID, &slider);
        
        value = GetControl32BitValue(slider);
        
        ChangeHintText(value, staticText);
    }
    
    return result;
}

void ChangeHintText(int value, ControlRef staticTextField)
{
    CFStringRef myCFSTR, myIndexCFSTR;
    OSErr	resErr;
    CFBundleRef	bundleRef;
    
    bundleRef = CFBundleGetBundleWithIdentifier(CFSTR("net.aldorande.component.FFusion"));
    
    if (bundleRef == NULL)
    {
        Codecprintf("Error getting current bundle!\n");
    }
    
    if (myIndexCFSTR = CFStringCreateWithFormatAndArguments(NULL, NULL, CFSTR("%d"), (va_list)&value))
    {
        myCFSTR = CFBundleCopyLocalizedString(bundleRef, myIndexCFSTR, NULL, CFSTR("PostProcessing"));
    }
    else
    {
        myCFSTR = CFBundleCopyLocalizedString(bundleRef, CFSTR("0"), NULL, CFSTR("PostProcessing"));
    }
            
    //resErr = SetControlData(staticTextField, kControlEntireControl, kControlStaticTextTextTag, CFStringGetLength(myCFSTR), CFStringGetCStringPtr(myCFSTR, CFStringGetSystemEncoding()));
    
    //this works only under 10.2
    resErr = SetControlData(staticTextField, kControlEntireControl, kControlStaticTextCFStringTag, CFStringGetLength(myCFSTR), &myCFSTR);
                
    if (resErr != noErr)
        Codecprintf("Could not change control title! (%d) \n", (int)resErr);
    
    Draw1Control(staticTextField);
}