source: trunk/FFusionCodec.c @ 1

Revision 1, 46.7 KB checked in by durin42, 9 years ago (diff)

Initial import of what was my revision 16. Good to have this in a reliable
SVN repository.

Line 
1//---------------------------------------------------------------------------
2//FFusion
3//Alternative DivX Codec for MacOS X
4//version 2.2 (build 72)
5//by Jerome Cornet
6//Copyright 2002-2003 Jerome Cornet
7//parts by Dan Christiansen
8//Copyright 2003 Dan Christiansen
9//from DivOSX by Jamby
10//Copyright 2001-2002 Jamby
11//uses libavcodec from ffmpeg 0.4.6
12//
13// This program is free software; you can redistribute it and/or modify
14// it under the terms of the GNU General Public License as published by
15// the Free Software Foundation; either version 2 of the License, or
16// (at your option) any later version.
17//
18// This program is distributed in the hope that it will be useful,
19// but WITHOUT ANY WARRANTY; without even the implied warranty of
20// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21// GNU General Public License for more details.
22//
23// You should have received a copy of the GNU General Public License
24// along with this program; if not, write to the Free Software
25// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26//
27//---------------------------------------------------------------------------
28// Source Code
29//---------------------------------------------------------------------------
30
31#include <Carbon/Carbon.h>
32#include <QuickTime/QuickTime.h>
33
34#include "FFusionCodec.h"
35#include "EI_Image.h"
36#include "avcodec.h"
37#include "postprocess.h"
38
39//---------------------------------------------------------------------------
40// Types
41//---------------------------------------------------------------------------
42
43typedef struct
44{
45    pp_mode_t                   *mode[PP_QUALITY_MAX+1];
46    pp_context_t                *context;
47    short                       goodness;
48    short                       level;
49} PostProcParamRecord;
50
51typedef struct
52{
53    ComponentInstance           self;
54    ComponentInstance           delegateComponent;
55    ComponentInstance           target;
56    ImageCodecMPDrawBandUPP     drawBandUPP;
57    Handle                      pixelTypes;
58    AVCodec                     *avCodec;
59    AVCodecContext              *avContext;
60    AVFrame                     *picture;
61    OSType                      componentType;
62    char                        hasy420;
63    char                        firstFrame;
64    char                        alreadyDonePPPref;
65    PostProcParamRecord         postProcParams;
66} FFusionGlobalsRecord, *FFusionGlobals;
67
68typedef struct
69{
70    long                        width;
71    long                        height;
72    long                        depth;
73    OSType                      pixelFormat;
74    long                        bufferSize;
75} FFusionDecompressRecord;
76
77
78//---------------------------------------------------------------------------
79// Prototypes of private subroutines
80//---------------------------------------------------------------------------
81
82static OSErr FFusionDecompress(AVCodecContext *context, UInt8 *dataPtr, ICMDataProcRecordPtr dataProc, UInt8 *baseAddr, long rowBump, long width, long height, AVFrame *picture, long length, char firstFrame);
83
84static OSErr FFusionSlowDecompress(AVCodecContext *context, UInt8 *dataPtr, ICMDataProcRecordPtr dataProc, UInt8 *baseAddr, long rowBump, long width, long height, AVFrame *picture, long length, char firstFrame);
85
86#ifdef DEBUG_BUILD
87int Codecprintf(const char *format, ...);
88void FourCCprintf(char *string, unsigned long a);
89#else
90#define Codecprintf(fmt, ...) /**/
91#define FourCCprintf(string,a) /**/
92#endif
93void FourCCcopy(OSType *dest, OSType *src);
94int FourCCcompare(OSType *a, OSType *b);
95
96int GetPPUserPreference();
97void SetPPUserPreference(int value);
98pascal OSStatus HandlePPDialogWindowEvent(EventHandlerCallRef  nextHandler, EventRef theEvent, void* userData);
99pascal OSStatus HandlePPDialogControlEvent(EventHandlerCallRef  nextHandler, EventRef theEvent, void* userData);
100void ChangeHintText(int value, ControlRef staticTextField);
101
102//---------------------------------------------------------------------------
103// Component Dispatcher
104//---------------------------------------------------------------------------
105
106#define IMAGECODEC_BASENAME()           FFusionCodec
107#define IMAGECODEC_GLOBALS()            FFusionGlobals storage
108
109#define CALLCOMPONENT_BASENAME()        IMAGECODEC_BASENAME()
110#define CALLCOMPONENT_GLOBALS()         IMAGECODEC_GLOBALS()
111
112#define COMPONENT_UPP_PREFIX()          uppImageCodec
113#define COMPONENT_DISPATCH_FILE         "FFusionCodecDispatch.h"
114#define COMPONENT_SELECT_PREFIX()       kImageCodec
115
116#define GET_DELEGATE_COMPONENT()        (storage->delegateComponent)
117
118#include <QuickTime/ImageCodec.k.h>
119#include <QuickTime/ComponentDispatchHelper.c>
120
121//---------------------------------------------------------------------------
122// Component Routines
123//---------------------------------------------------------------------------
124
125// -- This Image Decompressor Use the Base Image Decompressor Component --
126//      The base image decompressor is an Apple-supplied component
127//      that makes it easier for developers to create new decompressors.
128//      The base image decompressor does most of the housekeeping and
129//      interface functions required for a QuickTime decompressor component,
130//      including scheduling for asynchronous decompression.
131
132//-----------------------------------------------------------------
133// Component Open Request - Required
134//-----------------------------------------------------------------
135
136pascal ComponentResult FFusionCodecOpen(FFusionGlobals glob, ComponentInstance self)
137{
138    ComponentResult err;
139    ComponentDescription descout;
140    ComponentDescription cd;
141    Component c = 0;
142    long bitfield;
143
144    cd.componentType = 'imdc';
145    cd.componentSubType = 'y420';
146    cd.componentManufacturer = 0;
147    cd.componentFlags = 0;
148    cd.componentFlagsMask = 0;
149   
150    GetComponentInfo((Component)self, &descout, 0, 0, 0);
151
152    Codecprintf("Opening component for type");
153    FourCCprintf(" ", descout.componentSubType);
154
155    // Allocate memory for our globals, set them up and inform the component manager that we've done so
156       
157    glob = (FFusionGlobals)NewPtrClear(sizeof(FFusionGlobalsRecord));
158   
159    if (err = MemError())
160    {
161        Codecprintf("Unable to allocate globals! Exiting.\n");           
162    }
163    else
164    {
165        SetComponentInstanceStorage(self, (Handle)glob);
166       
167        glob->self = self;
168        glob->target = self;
169        glob->drawBandUPP = NULL;
170        glob->pixelTypes = NewHandle(10 * sizeof(OSType));
171        glob->avCodec = 0;
172        glob->hasy420 = 0;
173        glob->firstFrame = 0;
174        glob->alreadyDonePPPref = 0;
175        glob->componentType = descout.componentSubType;
176
177        c = FindNextComponent(c, &cd);
178
179        if (c != 0)
180        {           
181            Gestalt(gestaltSystemVersion, &bitfield);
182           
183            if (bitfield >= 0x1010)
184            {
185                Codecprintf("Use speedy y420 component");
186                printf(" 0x%lx\n", bitfield);
187               
188                glob->hasy420 = 1;
189            }
190        }
191        else
192        {
193            Codecprintf("Use slow y420 component\n");
194        }
195
196        // Open and target an instance of the base decompressor as we delegate
197        // most of our calls to the base decompressor instance
198       
199        err = OpenADefaultComponent(decompressorComponentType, kBaseCodecType, &glob->delegateComponent);
200        printf("Perian codec: glob->delegateComponent:0x%x \n",glob->delegateComponent);
201        if (!err)
202        {
203            ComponentSetTarget(glob->delegateComponent, self);
204        }
205        else
206        {
207            Codecprintf("Error opening the base image decompressor! Exiting.\n");
208        }
209    }
210   
211    return err;
212}
213
214//-----------------------------------------------------------------
215// Component Close Request - Required
216//-----------------------------------------------------------------
217
218pascal ComponentResult FFusionCodecClose(FFusionGlobals glob, ComponentInstance self)
219{
220    // Make sure to close the base component and dealocate our storage
221    int i;
222   
223    if (glob) 
224    {
225        if (glob->delegateComponent) 
226        {
227            CloseComponent(glob->delegateComponent);
228        }
229       
230        if (glob->drawBandUPP) 
231        {
232            DisposeImageCodecMPDrawBandUPP(glob->drawBandUPP);
233        }
234       
235        if (glob->avCodec)
236        {
237            avcodec_close(glob->avContext);
238        }
239       
240        if (glob->picture)
241        {
242            av_free(glob->picture);
243        }
244       
245        if (glob->avContext)
246        {
247            av_free(glob->avContext);
248        }
249       
250        for (i=0; i<=PP_QUALITY_MAX; i++)
251        {
252            if (glob->postProcParams.mode[i])
253                pp_free_mode(glob->postProcParams.mode[i]);
254        }
255
256        if (glob->postProcParams.context)
257            pp_free_context(glob->postProcParams.context);
258             
259        DisposePtr((Ptr)glob);
260    }
261
262    return noErr;
263}
264
265//-----------------------------------------------------------------
266// Component Version Request - Required
267//-----------------------------------------------------------------
268
269pascal ComponentResult FFusionCodecVersion(FFusionGlobals glob)
270{
271    return kFFusionCodecVersion;
272}
273
274//-----------------------------------------------------------------
275// Component Target Request
276//-----------------------------------------------------------------
277// Allows another component to "target" you i.e., you call
278// another component whenever you would call yourself (as a result
279// of your component being used by another component)
280//-----------------------------------------------------------------
281
282pascal ComponentResult FFusionCodecTarget(FFusionGlobals glob, ComponentInstance target)
283{
284    glob->target = target;
285       
286    return noErr;
287}
288
289//-----------------------------------------------------------------
290// Component GetMPWorkFunction Request
291//-----------------------------------------------------------------
292// Allows your image decompressor component to perform asynchronous
293// decompression in a single MP task by taking advantage of the
294// Base Decompressor. If you implement this selector, your DrawBand
295// function must be MP-safe. MP safety means not calling routines
296// that may move or purge memory and not calling any routines which
297// might cause 68K code to be executed. Ideally, your DrawBand
298// function should not make any API calls whatsoever. Obviously
299// don't implement this if you're building a 68k component.
300//-----------------------------------------------------------------
301
302pascal ComponentResult FFusionCodecGetMPWorkFunction(FFusionGlobals glob, ComponentMPWorkFunctionUPP *workFunction, void **refCon)
303{
304        if (glob->drawBandUPP == NULL)
305            glob->drawBandUPP = NewImageCodecMPDrawBandUPP((ImageCodecMPDrawBandProcPtr)FFusionCodecDrawBand);
306               
307        return ImageCodecGetBaseMPWorkFunction(glob->delegateComponent, workFunction, refCon, glob->drawBandUPP, glob);
308}
309
310//-----------------------------------------------------------------
311// ImageCodecInitialize
312//-----------------------------------------------------------------
313// The first function call that your image decompressor component
314// receives from the base image decompressor is always a call to
315// ImageCodecInitialize . In response to this call, your image
316// decompressor component returns an ImageSubCodecDecompressCapabilities
317// structure that specifies its capabilities.
318//-----------------------------------------------------------------
319
320pascal ComponentResult FFusionCodecInitialize(FFusionGlobals glob, ImageSubCodecDecompressCapabilities *cap)
321{
322
323    // Secifies the size of the ImageSubCodecDecompressRecord structure
324    // and say we can support asyncronous decompression
325    // With the help of the base image decompressor, any image decompressor
326    // that uses only interrupt-safe calls for decompression operations can
327    // support asynchronous decompression.
328
329    cap->decompressRecordSize = sizeof(FFusionDecompressRecord);
330    cap->canAsync = true;
331
332    return noErr;
333}
334
335//-----------------------------------------------------------------
336// ImageCodecPreflight
337//-----------------------------------------------------------------
338// The base image decompressor gets additional information about the
339// capabilities of your image decompressor component by calling
340// ImageCodecPreflight. The base image decompressor uses this
341// information when responding to a call to the ImageCodecPredecompress
342// function, which the ICM makes before decompressing an image. You
343// are required only to provide values for the wantedDestinationPixelSize
344// and wantedDestinationPixelTypes fields and can also modify other
345// fields if necessary.
346//-----------------------------------------------------------------
347
348pascal ComponentResult FFusionCodecPreflight(FFusionGlobals glob, CodecDecompressParams *p)
349{
350    OSType *pos;
351    int index, i;
352    CodecCapabilities *capabilities = p->capabilities;
353    long bitfield;
354    char altivec = 0;
355    Byte* myptr;
356
357    // We first open libavcodec library and the codec corresponding
358    // to the fourCC if it has not been done before
359   
360    if (!glob->avCodec)
361    {
362        avcodec_init();
363        avcodec_register_all();
364
365        switch (glob->componentType)
366        {
367            case 'MPG4':        // MS-MPEG4 v1
368            case 'mpg4':
369            case 'DIV1':
370            case 'div1':
371                glob->avCodec = avcodec_find_decoder(CODEC_ID_MSMPEG4V1);
372            break;
373
374            case 'MP42':        // MS-MPEG4 v2
375            case 'mp42':
376            case 'DIV2':
377            case 'div2':
378                glob->avCodec = avcodec_find_decoder(CODEC_ID_MSMPEG4V2);
379            break;
380
381            case 'div6':        // DivX 3
382            case 'DIV6':
383            case 'div5':
384            case 'DIV5':
385            case 'div4':
386            case 'DIV4':
387            case 'div3':
388            case 'DIV3':
389            case 'MP43':
390            case 'mp43':
391            case 'MPG3':
392            case 'mpg3':
393            case 'AP41':
394            case 'COL0':
395            case 'col0':
396            case 'COL1':
397            case 'col1':
398            case '3IVD':        // 3ivx
399            case '3ivd':
400                glob->avCodec = avcodec_find_decoder(CODEC_ID_MSMPEG4V3);
401            break;
402
403            case 'divx':        // DivX 4
404            case 'DIVX':
405            case 'mp4s':
406            case 'MP4S':
407            case 'm4s2':
408            case 'M4S2':
409            case 0x04000000:
410            case 'UMP4':
411            case 'DX50':        // DivX 5
412            case 'XVID':        // XVID
413            case 'xvid':
414            case 'XviD':
415            case 'XVIX':
416            case 'BLZ0':
417            case '3IV2':        // 3ivx
418            case '3iv2':
419                glob->avCodec = avcodec_find_decoder(CODEC_ID_MPEG4);
420            break;
421           
422            default:
423                Codecprintf("Warning! Unknown codec type! Using MPEG4 by default.\n");
424               
425                glob->avCodec = avcodec_find_decoder(CODEC_ID_MPEG4);
426        }
427       
428        // libavcodec version 0.4.6 and higher uses AVFrame instead of AVPicture
429        // It is better to handle the frame with a pointer and to use built-in
430        // allocation function
431       
432        glob->picture = avcodec_alloc_frame();
433       
434        // we do the same for the AVCodecContext since all context values are
435        // correctly initialized when calling the alloc function
436       
437        glob->avContext = avcodec_alloc_context();
438       
439        // Image size is mandatory for DivX-like codecs
440       
441        glob->avContext->width = (**p->imageDescription).width;
442        glob->avContext->height = (**p->imageDescription).height;
443       
444        // We also pass the FourCC since it allows the H263 hybrid decoder
445        // to make the difference between the various flavours of DivX
446       
447        myptr = (unsigned char *)&(glob->componentType);
448        glob->avContext->codec_tag = (myptr[3] << 24) + (myptr[2] << 16) + (myptr[1] << 8) + myptr[0];
449       
450        // Let's look for a CPU with AltiVec-like SMID unit
451       
452        Gestalt(gestaltPowerPCProcessorFeatures, &bitfield);
453        altivec = (bitfield & (1 << gestaltPowerPCHasVectorInstructions)) != 0;
454       
455        if (altivec)
456        {
457            Codecprintf("Altivec Acceleration enabled!\n");
458               
459            glob->avContext->idct_algo = FF_IDCT_ALTIVEC;
460        }
461   
462        // Finally we open the avcodec
463       
464        if (avcodec_open(glob->avContext, glob->avCodec))
465        {
466            Codecprintf("Error opening avcodec!\n");
467           
468            return -2;
469        }
470    }
471   
472    // Specify the minimum image band height supported by the component
473    // bandInc specifies a common factor of supported image band heights -
474    // if your component supports only image bands that are an even
475    // multiple of some number of pixels high report this common factor in bandInc
476   
477    capabilities->bandMin = (**p->imageDescription).height;
478    capabilities->bandInc = capabilities->bandMin;
479
480    // libavcodec 0.4.x is no longer stream based i.e. you cannot pass just
481    // an arbitrary amount of data to the library.
482    // Instead we have to tell QT to just pass the data corresponding
483    // to one frame
484     
485    capabilities->flags |= codecWantsSpecialScaling;
486   
487    p->requestedBufferWidth = (**p->imageDescription).width;
488    p->requestedBufferHeight = (**p->imageDescription).height;
489   
490    // Indicate the pixel depth the component can use with the specified image
491    // normally should be capabilities->wantedPixelSize = (**p->imageDescription).depth;
492    // but we don't care since some DivX are so bugged that the depth information
493    // is not correct
494   
495    capabilities->wantedPixelSize = 0;
496   
497    // Type of pixels used in output
498    // If QuickTime got the y420 component it is cool
499    // since libavcodec ouputs y420
500    // If not we'll do some king of conversion to 2vuy
501   
502    HLock(glob->pixelTypes);
503    pos = *((OSType **)glob->pixelTypes);
504   
505    index = 0;
506
507    if (glob->hasy420)
508    {
509        pos[index++] = 'y420';
510    }
511    else
512    {
513        pos[index++] = k2vuyPixelFormat;       
514    }
515   
516    pos[index++] = 0;
517    HUnlock(glob->pixelTypes);
518
519    p->wantedDestinationPixelTypes = (OSType **)glob->pixelTypes;
520   
521    // Specify the number of pixels the image must be extended in width and height if
522    // the component cannot accommodate the image at its given width and height
523    // It is not the case here
524   
525    capabilities->extendWidth = 0;
526    capabilities->extendHeight = 0;
527   
528    // Post-processing
529   
530    glob->postProcParams.context = pp_get_context((**p->imageDescription).width, (**p->imageDescription).height, PP_FORMAT_420);
531   
532    for (i=0; i<=PP_QUALITY_MAX; i++) 
533    {
534       
535        if (i <= 2) 
536        {
537            glob->postProcParams.mode[i] = pp_get_mode_by_name_and_quality("h1,v1,dr"/*"al:f"*/, i);
538        } 
539        else if (i <= 4) 
540        {
541            glob->postProcParams.mode[i] = pp_get_mode_by_name_and_quality("hb,vb,dr,"/*"al:f"*/, i);
542        } 
543        else 
544        {
545            glob->postProcParams.mode[i] = pp_get_mode_by_name_and_quality("hb,vb,dr,""hb:c,vb:c,dr:c,"/*"al:f"*/, i);
546        }
547
548        if (glob->postProcParams.mode[i] == NULL) 
549        {
550            printf("Error getting PP filter %d!\n", i);
551           
552            return -1;
553        }
554
555        glob->postProcParams.goodness = 0;
556        glob->postProcParams.level = 0;//GetPPUserPreference();
557    }
558
559   
560    return noErr;
561}
562
563//-----------------------------------------------------------------
564// ImageCodecBeginBand
565//-----------------------------------------------------------------
566// The ImageCodecBeginBand function allows your image decompressor
567// component to save information about a band before decompressing
568// it. This function is never called at interrupt time. The base
569// image decompressor preserves any changes your component makes to
570// any of the fields in the ImageSubCodecDecompressRecord or
571// CodecDecompressParams structures. If your component supports
572// asynchronous scheduled decompression, it may receive more than
573// one ImageCodecBeginBand call before receiving an ImageCodecDrawBand
574// call.
575//-----------------------------------------------------------------
576
577pascal ComponentResult FFusionCodecBeginBand(FFusionGlobals glob, CodecDecompressParams *p, ImageSubCodecDecompressRecord *drp, long flags)
578{       
579    long offsetH, offsetV;
580    FFusionDecompressRecord *myDrp = (FFusionDecompressRecord *)drp->userDecompressRecord;
581       
582    //////
583    IBNibRef            nibRef;
584    WindowRef           window;
585    OSStatus            err;
586    CFBundleRef         bundleRef;
587    EventHandlerUPP     handlerUPP, handlerUPP2;
588    EventTypeSpec       eventType;
589    ControlID           controlID;
590    ControlRef          theControl;
591    KeyMap              currentKeyMap;
592    int                 userPreference;
593    ///////
594
595    offsetH = (long)(p->dstRect.left - p->dstPixMap.bounds.left) * (long)(p->dstPixMap.pixelSize >> 3);
596    offsetV = (long)(p->dstRect.top - p->dstPixMap.bounds.top) * (long)drp->rowBytes;
597   
598    myDrp->width = (**p->imageDescription).width;
599    myDrp->height = (**p->imageDescription).height;
600    myDrp->depth = (**p->imageDescription).depth;
601    myDrp->bufferSize = p->bufferSize;                  // bufferSize is the data size of the current frame
602
603    myDrp->pixelFormat = p->dstPixMap.pixelFormat;
604   
605    if (p->conditionFlags & codecConditionFirstFrame)
606    {
607        glob->firstFrame = 1;
608       
609        GetKeys(currentKeyMap);
610       
611/*        if ((currentKeyMap[1] & kOptionKeyModifier) && !glob->alreadyDonePPPref)
612        {
613            glob->alreadyDonePPPref = 1;
614   
615            bundleRef = CFBundleGetBundleWithIdentifier(CFSTR("net.aldorande.component.FFusion"));
616       
617            if (bundleRef == NULL)
618                printf("Cannot get main bundle reference\n");
619               
620            err = CreateNibReferenceWithCFBundle(bundleRef, CFSTR("main"), &nibRef);
621           
622            if (err != noErr)
623                printf("cannot create nib reference! %d\n", (int)err);
624           
625            if (nibRef != NULL)
626            {
627                err = CreateWindowFromNib(nibRef, CFSTR("PostProcessing"), &window);
628               
629                if (err != noErr)
630                    printf("cannot create window!\n");
631               
632                DisposeNibReference(nibRef);
633           
634                if (window != NULL)
635                {
636                    controlID.signature = 'post';
637                    controlID.id = 128;
638                   
639                    err = GetControlByID(window, &controlID, &theControl);
640                   
641                    if (err != noErr)
642                    {
643                        printf("Cannot get slider hint text control!\n");
644                    }
645
646                    userPreference = GetPPUserPreference();
647                    ChangeHintText(userPreference, theControl);
648                   
649                    controlID.signature = 'post';
650                    controlID.id = 129;
651                   
652                    err = GetControlByID(window, &controlID, &theControl);
653                   
654                    if (err != noErr)
655                    {
656                        printf("Cannot get slider control!\n");
657                    }
658
659                    SetControl32BitValue(theControl, userPreference);
660
661                    ShowWindow(window);
662                   
663                    handlerUPP = NewEventHandlerUPP(HandlePPDialogWindowEvent);
664                   
665                    eventType.eventClass = kEventClassCommand;
666                    eventType.eventKind  = kEventCommandProcess;
667                   
668                    InstallWindowEventHandler(window, handlerUPP, 1, &eventType, window, NULL);
669                   
670                    handlerUPP2 = NewEventHandlerUPP(HandlePPDialogControlEvent);
671                   
672                    eventType.eventClass = kEventClassControl;
673                    eventType.eventKind = kEventControlTrack;
674                   
675                    InstallControlEventHandler(theControl, handlerUPP2, 1, &eventType, window, NULL);
676                }
677            }
678        }*/
679    }
680   
681    /*if ((glob->postProcParams.level >= 0) && (GetPPUserPreference() > 0))
682    {
683        if (p->conditionFlags & codecConditionCatchUpDiff)
684        {
685            if (glob->postProcParams.level > 0)
686            {
687                glob->postProcParams.level--;
688               
689                printf("PP=%i -\n", glob->postProcParams.level);
690            }
691           
692            glob->postProcParams.goodness = 0;
693        }
694        else
695        {
696            if (++glob->postProcParams.goodness > 5)
697            {
698                if (glob->postProcParams.level < PP_QUALITY_MAX + 1)
699                {
700                    glob->postProcParams.level++;
701                   
702                    printf("PP=%i +\n", glob->postProcParams.level);
703                }
704               
705                glob->postProcParams.goodness = 0;
706            }
707        }
708    }*/
709
710    return noErr;
711}
712
713//-----------------------------------------------------------------
714// ImageCodecDrawBand
715//-----------------------------------------------------------------
716// The base image decompressor calls your image decompressor
717// component's ImageCodecDrawBand function to decompress a band or
718// frame. Your component must implement this function. If the
719// ImageSubCodecDecompressRecord structure specifies a progress function
720// or data-loading function, the base image decompressor will never call
721// ImageCodecDrawBand at interrupt time. If the ImageSubCodecDecompressRecord
722// structure specifies a progress function, the base image decompressor
723// handles codecProgressOpen and codecProgressClose calls, and your image
724// decompressor component must not implement these functions.
725// If not, the base image decompressor may call the ImageCodecDrawBand
726// function at interrupt time. When the base image decompressor calls your
727// ImageCodecDrawBand function, your component must perform the decompression
728// specified by the fields of the ImageSubCodecDecompressRecord structure.
729// The structure includes any changes your component made to it when
730// performing the ImageCodecBeginBand function. If your component supports
731// asynchronous scheduled decompression, it may receive more than one
732// ImageCodecBeginBand call before receiving an ImageCodecDrawBand call.
733//-----------------------------------------------------------------
734
735pascal ComponentResult FFusionCodecDrawBand(FFusionGlobals glob, ImageSubCodecDecompressRecord *drp)
736{
737    OSErr err = noErr;
738   
739    FFusionDecompressRecord *myDrp = (FFusionDecompressRecord *)drp->userDecompressRecord;
740    unsigned char *dataPtr = (unsigned char *)drp->codecData;
741   
742    ICMDataProcRecordPtr dataProc = drp->dataProcRecord.dataProc ? &drp->dataProcRecord : NULL;
743   
744    unsigned char *ppPage[3];
745    int ppStride[3];
746
747   
748    if (myDrp->pixelFormat == 'y420')
749    {
750        err = FFusionDecompress(glob->avContext, dataPtr, dataProc, (UInt8 *)drp->baseAddr, drp->rowBytes, myDrp->width, myDrp->height, glob->picture, myDrp->bufferSize, glob->firstFrame);
751    }
752    else
753    {
754        err = FFusionSlowDecompress(glob->avContext, dataPtr, dataProc, (UInt8 *)drp->baseAddr, drp->rowBytes, myDrp->width, myDrp->height, glob->picture, myDrp->bufferSize, glob->firstFrame);
755    }
756
757    if (glob->firstFrame)
758        glob->firstFrame = 0;
759   
760    if ((err == noErr) && (glob->postProcParams.level > 0))
761    {
762        ppPage[0] = glob->picture->data[0];
763        ppPage[1] = glob->picture->data[1];
764        ppPage[2] = glob->picture->data[2];
765        ppStride[0] = glob->picture->linesize[0];
766        ppStride[1] = glob->picture->linesize[1];
767        ppStride[2] = glob->picture->linesize[2];
768
769        pp_postprocess(ppPage, ppStride,
770                       ppPage, ppStride,
771                       myDrp->width, myDrp->height,
772                       glob->picture->qscale_table,
773                       glob->picture->qstride,
774                       glob->postProcParams.mode[glob->postProcParams.level],
775                       glob->postProcParams.context,
776                       glob->picture->pict_type);
777    }
778   
779    return err;
780}
781
782//-----------------------------------------------------------------
783// ImageCodecEndBand
784//-----------------------------------------------------------------
785// The ImageCodecEndBand function notifies your image decompressor
786// component that decompression of a band has finished or
787// that it was terminated by the Image Compression Manager. Your
788// image decompressor component is not required to implement the
789// ImageCodecEndBand function. The base image decompressor may call
790// the ImageCodecEndBand function at interrupt time.
791// After your image decompressor component handles an ImageCodecEndBand
792// call, it can perform any tasks that are required when decompression
793// is finished, such as disposing of data structures that are no longer
794// needed. Because this function can be called at interrupt time, your
795// component cannot use this function to dispose of data structures;
796// this must occur after handling the function. The value of the result
797// parameter should be set to noErr if the band or frame was
798// drawn successfully.
799// If it is any other value, the band or frame was not drawn.
800//-----------------------------------------------------------------
801
802pascal ComponentResult FFusionCodecEndBand(FFusionGlobals glob, ImageSubCodecDecompressRecord *drp, OSErr result, long flags)
803{
804    return noErr;
805}
806
807//-----------------------------------------------------------------
808// ImageCodecQueueStarting
809//-----------------------------------------------------------------
810// If your component supports asynchronous scheduled decompression,
811// the base image decompressor calls your image decompressor
812// component's ImageCodecQueueStarting function before decompressing
813// the frames in the queue. Your component is not required to implement
814// this function. It can implement the function if it needs to perform
815// any tasks at this time, such as locking data structures.
816// The base image decompressor never calls the ImageCodecQueueStarting
817// function at interrupt time.
818//-----------------------------------------------------------------
819
820pascal ComponentResult FFusionCodecQueueStarting(FFusionGlobals glob)
821{       
822    return noErr;
823}
824
825//-----------------------------------------------------------------
826// ImageCodecQueueStopping
827//-----------------------------------------------------------------
828// If your image decompressor component supports asynchronous scheduled
829// decompression, the ImageCodecQueueStopping function notifies your
830// component that the frames in the queue have been decompressed.
831// Your component is not required to implement this function.
832// After your image decompressor component handles an ImageCodecQueueStopping
833// call, it can perform any tasks that are required when decompression
834// of the frames is finished, such as disposing of data structures that
835// are no longer needed.
836// The base image decompressor never calls the ImageCodecQueueStopping
837// function at interrupt time.
838//-----------------------------------------------------------------
839
840pascal ComponentResult FFusionCodecQueueStopping(FFusionGlobals glob)
841{       
842    return noErr;
843}
844
845//-----------------------------------------------------------------
846// ImageCodecGetCompressedImageSize
847//-----------------------------------------------------------------
848// Your component receives the ImageCodecGetCompressedImageSize request
849// whenever an application calls the ICM's GetCompressedImageSize function.
850// You can use the ImageCodecGetCompressedImageSize function when you
851// are extracting a single image from a sequence; therefore, you don't have
852// an image description structure and don't know the exact size of one frame.
853// In this case, the Image Compression Manager calls the component to determine
854// the size of the data. Your component should return a long integer indicating
855// the number of bytes of data in the compressed image. You may want to store
856// the image size somewhere in the image description structure, so that you can
857// respond to this request quickly. Only decompressors receive this request.
858//-----------------------------------------------------------------
859
860pascal ComponentResult FFusionCodecGetCompressedImageSize(FFusionGlobals glob, ImageDescriptionHandle desc, Ptr data, long dataSize, ICMDataProcRecordPtr dataProc, long *size)
861{
862    ImageFramePtr framePtr = (ImageFramePtr)data;
863
864    if (size == NULL) 
865            return paramErr;
866
867    *size = EndianU32_BtoN(framePtr->frameSize) + sizeof(ImageFrame);
868
869    return noErr;
870}
871
872//-----------------------------------------------------------------
873// ImageCodecGetCodecInfo
874//-----------------------------------------------------------------
875// Your component receives the ImageCodecGetCodecInfo request whenever
876// an application calls the Image Compression Manager's GetCodecInfo
877// function.
878// Your component should return a formatted compressor information
879// structure defining its capabilities.
880// Both compressors and decompressors may receive this request.
881//-----------------------------------------------------------------
882
883pascal ComponentResult FFusionCodecGetCodecInfo(FFusionGlobals glob, CodecInfo *info)
884{
885    OSErr err = noErr;
886
887    if (info == NULL) 
888    {
889        err = paramErr;
890    }
891    else 
892    {
893        CodecInfo **tempCodecInfo;
894
895        switch (glob->componentType)
896        {
897            case 'MPG4':        // MS-MPEG4 v1
898            case 'mpg4':
899            case 'DIV1':
900            case 'div1':
901                err = GetComponentResource((Component)glob->self, codecInfoResourceType, kDivX1CodecInfoResID, (Handle *)&tempCodecInfo);
902                break;
903               
904            case 'MP42':        // MS-MPEG4 v2
905            case 'mp42':
906            case 'DIV2':
907            case 'div2':
908                err = GetComponentResource((Component)glob->self, codecInfoResourceType, kDivX2CodecInfoResID, (Handle *)&tempCodecInfo);
909                break;
910               
911            case 'div6':        // DivX 3
912            case 'DIV6':
913            case 'div5':
914            case 'DIV5':
915            case 'div4':
916            case 'DIV4':
917            case 'div3':
918            case 'DIV3':
919            case 'MP43':
920            case 'mp43':
921            case 'MPG3':
922            case 'mpg3':
923            case 'AP41':
924            case 'COL0':
925            case 'col0':
926            case 'COL1':
927            case 'col1':
928                err = GetComponentResource((Component)glob->self, codecInfoResourceType, kDivX3CodecInfoResID, (Handle *)&tempCodecInfo);
929                break;
930
931            case 'divx':        // DivX 4
932            case 'DIVX':
933            case 'mp4s':
934            case 'MP4S':
935            case 'm4s2':
936            case 'M4S2':
937            case 0x04000000:
938            case 'UMP4':
939                err = GetComponentResource((Component)glob->self, codecInfoResourceType, kDivX4CodecInfoResID, (Handle *)&tempCodecInfo);
940                break;
941               
942            case 'DX50':
943                err = GetComponentResource((Component)glob->self, codecInfoResourceType, kDivX5CodecInfoResID, (Handle *)&tempCodecInfo);
944                break;
945               
946            case 'XVID':        // XVID
947            case 'xvid':
948            case 'XviD':
949            case 'XVIX':
950            case 'BLZ0':
951                err = GetComponentResource((Component)glob->self, codecInfoResourceType, kXVIDCodecInfoResID, (Handle *)&tempCodecInfo);
952                break;
953               
954            case '3IVD':        // 3ivx
955            case '3ivd':
956            case '3IV2':
957            case '3iv2':
958                err = GetComponentResource((Component)glob->self, codecInfoResourceType, k3ivxCodecInfoResID, (Handle *)&tempCodecInfo);
959                break;
960               
961            default:    // should never happen but we have to handle the case
962                err = GetComponentResource((Component)glob->self, codecInfoResourceType, kDivX4CodecInfoResID, (Handle *)&tempCodecInfo);
963
964        }
965       
966        if (err == noErr) 
967        {
968            *info = **tempCodecInfo;
969           
970            DisposeHandle((Handle)tempCodecInfo);
971        }
972    }
973
974    return err;
975}
976
977#define kSpoolChunkSize (16384)
978#define kInfiniteDataSize (0x7fffffff)
979
980//---------------------------------------------------------------------------
981// Private SubRoutines
982//---------------------------------------------------------------------------
983
984//-----------------------------------------------------------------
985// Codecprintf
986//-----------------------------------------------------------------
987// Little function to print correctly messages in the console
988//-----------------------------------------------------------------
989
990#ifdef DEBUG_BUILD
991int Codecprintf(const char *format, ...)
992{
993    printf(CODEC_HEADER);
994   
995    return printf(format);
996}
997#endif
998
999//-----------------------------------------------------------------
1000// FourCCprintf
1001//-----------------------------------------------------------------
1002// Little function to print correctly AVI's FourCC Code
1003//-----------------------------------------------------------------
1004#ifdef DEBUG_BUILD
1005void FourCCprintf (char *string, unsigned long a)
1006{
1007    if (a < 64)
1008    {
1009        printf("%s: %ld\n", string, a);
1010    }
1011    else
1012    {
1013        printf("%s: %c%c%c%c\n", string, (unsigned char)((a >> 24) & 0xff), 
1014                                        (unsigned char)((a >> 16) & 0xff), 
1015                                        (unsigned char)((a >> 8) & 0xff), 
1016                                        (unsigned char)(a & 0xff));
1017    }
1018}
1019#endif
1020
1021//-----------------------------------------------------------------
1022// FourCCcopy
1023//-----------------------------------------------------------------
1024// Copy src FourCC in dest FourCC
1025//-----------------------------------------------------------------
1026
1027void FourCCcopy(OSType *dest, OSType *src)
1028{
1029    int i;
1030   
1031    for (i=0; i<3; i++)
1032        dest[i] = src[i];
1033}
1034
1035//-----------------------------------------------------------------
1036// FourCCprintf
1037//-----------------------------------------------------------------
1038// Compare two FourCC (case sensitive)
1039// Returns 1 if equal, 0 if not equal
1040//-----------------------------------------------------------------
1041
1042int FourCCcompare(OSType *a, OSType *b)
1043{
1044    return ((a[0] == b[0]) && (a[1] == b[1]) && (a[2] == b[2]) && (a[3] == b[3]));
1045}
1046
1047//-----------------------------------------------------------------
1048// FFusionDecompress
1049//-----------------------------------------------------------------
1050// This function calls libavcodec to decompress one frame.
1051// It returns y420 data directly to QuickTime which then converts
1052// in RGB for display
1053//-----------------------------------------------------------------
1054
1055OSErr FFusionDecompress(AVCodecContext *context, UInt8 *dataPtr, ICMDataProcRecordPtr dataProc, UInt8 *baseAddr, long rowBump, long width, long height, AVFrame *picture, long length, char firstFrame)
1056{
1057    OSErr err = noErr;
1058    int got_picture = false;
1059    int len = 0;
1060    UInt8 *endOfScanLine;
1061    PlanarPixmapInfoYUV420 *planar;
1062    long availableData = dataProc ? codecMinimumDataSize : kInfiniteDataSize;
1063
1064    endOfScanLine = baseAddr + (width * 4);
1065    context->width = width;
1066    context->height = height;
1067    picture->data[0] = 0;
1068   
1069    while (!got_picture) 
1070    {
1071        if (availableData < kSpoolChunkSize) 
1072        {
1073            // get some more source data
1074           
1075            err = InvokeICMDataUPP((Ptr *)&dataPtr, length, dataProc->dataRefCon, dataProc->dataProc);
1076               
1077            if (err == eofErr) err = noErr;
1078            if (err) return err;
1079
1080            availableData = codecMinimumDataSize;
1081        }
1082               
1083        len = avcodec_decode_video(context, picture, &got_picture, dataPtr, length);
1084       
1085        if (len < 0)
1086        {           
1087            got_picture = 0;
1088            len = 1;
1089            Codecprintf("Error while decoding frame\n");
1090           
1091            if (firstFrame)
1092            {
1093                return codecErr;
1094            }
1095            else
1096            {
1097                return noErr;
1098            }
1099        }
1100       
1101        availableData -= len;
1102        dataPtr += len;
1103    }
1104
1105    planar = (PlanarPixmapInfoYUV420 *) baseAddr;
1106   
1107    // if ya can't set da poiners, set da offsets
1108    planar->componentInfoY.offset = picture->data[0] - baseAddr;
1109    planar->componentInfoCb.offset =  picture->data[1] - baseAddr;
1110    planar->componentInfoCr.offset =  picture->data[2] - baseAddr;
1111   
1112    // for the 16/32 add look at EDGE in mpegvideo.c
1113    planar->componentInfoY.rowBytes = picture->linesize[0];
1114    planar->componentInfoCb.rowBytes = picture->linesize[1];
1115    planar->componentInfoCr.rowBytes = picture->linesize[2];
1116   
1117    return err;
1118}
1119
1120//-----------------------------------------------------------------
1121// FFusionSlowDecompress
1122//-----------------------------------------------------------------
1123// This function calls libavcodec to decompress one frame.
1124// In this case we have to return 2yuv values because
1125// QT version has no built-in y420 component.
1126// Since we do the conversion ourselves it is not really optimized....
1127// The function should never be called since many people now
1128// have a decent OS/QT version.
1129//-----------------------------------------------------------------
1130
1131OSErr FFusionSlowDecompress(AVCodecContext *context, UInt8 *dataPtr, ICMDataProcRecordPtr dataProc, UInt8 *baseAddr, long rowBump, long width, long height, AVFrame *picture, long length, char firstFrame)
1132{
1133    OSErr err = noErr;
1134    int got_picture = false;
1135    int len = 0;
1136    unsigned int i, j;
1137    UInt8 *endOfScanLine;
1138    char *yuvPtr;
1139    char *py,*pu,*pv;
1140    long availableData = dataProc ? codecMinimumDataSize : kInfiniteDataSize;
1141
1142    endOfScanLine = baseAddr + (width * 4);
1143    context->width = width;
1144    context->height = height;
1145    picture->data[0] = 0;
1146   
1147    while (!got_picture)
1148    {
1149        if (availableData < kSpoolChunkSize) 
1150        {
1151            // get some more source data
1152           
1153            err = InvokeICMDataUPP((Ptr *)&dataPtr, length, dataProc->dataRefCon, dataProc->dataProc);
1154               
1155            if (err == eofErr) err = noErr;
1156            if (err) return err;
1157   
1158            availableData = codecMinimumDataSize;
1159        }
1160       
1161        len = avcodec_decode_video(context, picture, &got_picture, dataPtr, length);
1162       
1163        if (len < 0)
1164        {
1165            got_picture = 0;
1166            len = 1;
1167           
1168            Codecprintf("Error while decoding frame\n");
1169
1170            if (firstFrame)
1171            {
1172                return codecErr;
1173            }
1174            else
1175            {
1176                return noErr;
1177            }
1178        }
1179       
1180        availableData -= len;
1181        dataPtr += len;
1182    }
1183   
1184    // now let's do some yuv420/vuy2 conversion
1185   
1186    yuvPtr = baseAddr;
1187    py = picture->data[0];
1188    pu = picture->data[1];
1189    pv = picture->data[2];
1190
1191    for(i = 0 ;  i < height; i++)
1192    {
1193        for(j = 0; j < width; j+= 2)
1194        {
1195            yuvPtr[2*j] = pu[j>>1];
1196            yuvPtr[2*j+1] = py[j];
1197            yuvPtr[2*j+2] = pv[j>>1];
1198            yuvPtr[2*j+3] = py[j+1];
1199        }
1200       
1201        yuvPtr += rowBump;
1202        py += picture->linesize[0];
1203       
1204        if (i & 1)
1205        {
1206            pu += picture->linesize[1];
1207            pv += picture->linesize[2];
1208        }
1209    }
1210
1211    return err;
1212}
1213
1214int GetPPUserPreference()
1215{
1216    CFIndex     userPref;
1217    Boolean     keyExists;
1218    int         ppUserValue = 0;
1219   
1220    userPref = CFPreferencesGetAppIntegerValue(CFSTR("postProcessingLevel"), CFSTR("net.aldorande.component.FFusion"), &keyExists);
1221   
1222    if (keyExists)
1223    {
1224        ppUserValue = (int)userPref;
1225    }
1226    else
1227    {
1228        Codecprintf("Key does not exists\n");
1229       
1230        ppUserValue = 0;
1231    }
1232   
1233    return ppUserValue;
1234}
1235
1236void SetPPUserPreference(int value)
1237{
1238    int     sameValue = value;
1239    Boolean syncOK;
1240       
1241    CFPreferencesSetAppValue(CFSTR("postProcessingLevel"), CFNumberCreate(NULL, kCFNumberIntType, &sameValue), CFSTR("net.aldorande.component.FFusion"));
1242   
1243    syncOK = CFPreferencesAppSynchronize(CFSTR("net.aldorande.component.FFusion"));
1244   
1245    if (!syncOK)
1246    {
1247        Codecprintf("Error writing user preference!\n");
1248    }
1249}
1250
1251pascal OSStatus HandlePPDialogWindowEvent(EventHandlerCallRef  nextHandler, EventRef theEvent, void* userData)
1252{
1253    UInt32      whatHappened;
1254    HICommand   commandStruct;
1255    UInt32      theCommandID;
1256    OSErr       theErr = eventNotHandledErr;
1257    OSErr       resErr;
1258    WindowRef   window = (WindowRef)userData;
1259    SInt32      value = 0;
1260    ControlRef  slider;
1261    ControlID   controlID;
1262   
1263    whatHappened = GetEventKind(theEvent);
1264
1265    switch (whatHappened)
1266    {
1267        case kEventCommandProcess:
1268            GetEventParameter(theEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &commandStruct);
1269       
1270            theCommandID = commandStruct.commandID;
1271           
1272            if (theCommandID == kHICommandOK)
1273            {
1274                controlID.id = 129;
1275                controlID.signature = 'post';
1276                resErr = GetControlByID(window, &controlID, &slider);
1277               
1278                if (resErr == noErr)
1279                {
1280                    value = GetControl32BitValue(slider);
1281                }
1282                else
1283                {
1284                    Codecprintf("Unable to get post-processing control value!\n");
1285                }
1286               
1287                SetPPUserPreference(value);
1288           
1289                DisposeWindow(window);
1290               
1291                theErr = noErr;
1292            }
1293           
1294            if (theCommandID == kHICommandCancel)
1295            {               
1296                DisposeWindow(window);
1297               
1298                theErr = noErr;
1299            }
1300
1301            break;
1302    }
1303   
1304    return theErr;
1305}
1306
1307pascal OSStatus HandlePPDialogControlEvent(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
1308{
1309    WindowRef   window = (WindowRef)userData;
1310    ControlID   controlID;
1311    ControlRef  slider, staticText;
1312    OSErr       resErr;
1313    SInt32      value;
1314    OSErr       result;
1315   
1316    result = CallNextEventHandler(nextHandler, theEvent);
1317   
1318    if (result == noErr)
1319    {       
1320        controlID.id = 128;
1321        controlID.signature = 'post';
1322        resErr = GetControlByID(window, &controlID, &staticText);
1323       
1324        controlID.id = 129;
1325        controlID.signature = 'post';
1326        resErr = GetControlByID(window, &controlID, &slider);
1327       
1328        value = GetControl32BitValue(slider);
1329       
1330        ChangeHintText(value, staticText);
1331    }
1332   
1333    return result;
1334}
1335
1336void ChangeHintText(int value, ControlRef staticTextField)
1337{
1338    CFStringRef myCFSTR, myIndexCFSTR;
1339    OSErr       resErr;
1340    CFBundleRef bundleRef;
1341   
1342    bundleRef = CFBundleGetBundleWithIdentifier(CFSTR("net.aldorande.component.FFusion"));
1343   
1344    if (bundleRef == NULL)
1345    {
1346        Codecprintf("Error getting current bundle!\n");
1347    }
1348   
1349    if (myIndexCFSTR = CFStringCreateWithFormatAndArguments(NULL, NULL, CFSTR("%d"), (va_list)&value))
1350    {
1351        myCFSTR = CFBundleCopyLocalizedString(bundleRef, myIndexCFSTR, NULL, CFSTR("PostProcessing"));
1352    }
1353    else
1354    {
1355        myCFSTR = CFBundleCopyLocalizedString(bundleRef, CFSTR("0"), NULL, CFSTR("PostProcessing"));
1356    }
1357           
1358    //resErr = SetControlData(staticTextField, kControlEntireControl, kControlStaticTextTextTag, CFStringGetLength(myCFSTR), CFStringGetCStringPtr(myCFSTR, CFStringGetSystemEncoding()));
1359   
1360    //this works only under 10.2
1361    resErr = SetControlData(staticTextField, kControlEntireControl, kControlStaticTextCFStringTag, CFStringGetLength(myCFSTR), &myCFSTR);
1362               
1363    if (resErr != noErr)
1364        Codecprintf("Could not change control title! (%d) \n", (int)resErr);
1365   
1366    Draw1Control(staticTextField);
1367}
Note: See TracBrowser for help on using the repository browser.