source: trunk/FFusionCodec.c @ 1321

Revision 1321, 48.9 KB checked in by astrange, 4 years ago (diff)

Fix a possible null pointer dereference.

No idea how this would occur in practice - VP3 only?

Found with Build & Analyze.

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 library is free software; you can redistribute it and/or
14//modify it under the terms of the GNU Lesser General Public
15//License as published by the Free Software Foundation; either
16//version 2.1 of the License, or (at your option) any later version.
17//
18//This library 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 GNU
21//Lesser General Public License for more details.
22//
23//You should have received a copy of the GNU Lesser General Public
24//License along with this library; if not, write to the Free Software
25//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
26//
27//---------------------------------------------------------------------------
28// Source Code
29//---------------------------------------------------------------------------
30
31#include <Carbon/Carbon.h>
32#include <QuickTime/QuickTime.h>
33#include <Accelerate/Accelerate.h>
34#include <sys/sysctl.h>
35
36#include "PerianResourceIDs.h"
37#include "avcodec.h"
38#include "Codecprintf.h"
39#include "ColorConversions.h"
40#include "bitstream_info.h"
41#include "FrameBuffer.h"
42#include "CommonUtils.h"
43#include "pthread.h"
44#include "CodecIDs.h"
45
46//---------------------------------------------------------------------------
47// Types
48//---------------------------------------------------------------------------
49
50// 64 because that's 2 * ffmpeg's INTERNAL_BUFFER_SIZE and QT sometimes uses more than 32
51#define FFUSION_MAX_BUFFERS 64
52
53#define kNumPixelFormatsSupportedFFusion 1
54
55typedef struct
56{
57        AVFrame         *frame;
58        short           retainCount;
59        short           ffmpegUsing;
60        long            frameNumber;
61        AVFrame         returnedFrame;
62} FFusionBuffer;
63
64typedef enum
65{
66        PACKED_QUICKTIME_KNOWS_ORDER, /* This is for non-stupid container formats which actually know the difference between decode and display order */
67        PACKED_ALL_IN_FIRST_FRAME, /* This is for the divx hack which contains a P frame and all subsequent B frames in a single frame. Ex: I, PB, -, PB, -, I...*/
68        PACKED_DELAY_BY_ONE_FRAME /* This is for stupid containers where frames are soley writen in decode order.  Ex: I, P, B, P, B, P, I.... */
69} FFusionPacked;
70
71/* Why do these small structs?  It makes the usage of these variables clearer, no other reason */
72
73/* globs used by the BeginBand routine */
74struct begin_glob
75{
76        FFusionParserContext    *parser;
77        long                    lastFrame;
78        long                    lastIFrame;
79        int                             lastFrameType;
80        int                             futureType;
81        FrameData               *lastPFrameData;
82};
83
84/* globs used by the DecodeBand routine */
85struct decode_glob
86{
87        long                    lastFrame;
88        FFusionBuffer   *futureBuffer;
89};
90
91struct per_frame_decode_stats
92{
93        unsigned                begin_calls;
94        unsigned                decode_calls;
95        unsigned                draw_calls;
96        unsigned                end_calls;
97};
98
99struct decode_stats
100{
101        struct per_frame_decode_stats type[4];
102        int             max_frames_begun;
103        int             max_frames_decoded;
104};
105
106typedef struct
107{
108    ComponentInstance           self;
109    ComponentInstance           delegateComponent;
110    ComponentInstance           target;
111    ImageCodecMPDrawBandUPP     drawBandUPP;
112    Handle                      pixelTypes;
113    AVCodec                     *avCodec;
114    AVCodecContext      *avContext;
115    OSType                      componentType;
116        FILE                    *fileLog;
117        AVFrame                 lastDisplayedFrame;
118        FFusionPacked   packedType;
119        FFusionBuffer   buffers[FFUSION_MAX_BUFFERS];   // the buffers which the codec has retained
120        int                             lastAllocatedBuffer;            // the index of the buffer which was last allocated
121                                                                                                // by the codec (and is the latest in decode order)     
122        int                             shouldUseReturnedFrame;
123        struct begin_glob       begin;
124        FFusionData             data;
125        struct decode_glob      decode;
126        struct decode_stats stats;
127        ColorConversionFuncs colorConv;
128} FFusionGlobalsRecord, *FFusionGlobals;
129
130typedef struct
131{
132    long                        width;
133    long                        height;
134    long                        depth;
135    OSType                      pixelFormat;
136        int                             decoded;
137        long                    frameNumber;
138        long                    GOPStartFrameNumber;
139        long                    bufferSize;
140        FFusionBuffer   *buffer;
141        FrameData               *frameData;
142} FFusionDecompressRecord;
143
144
145//---------------------------------------------------------------------------
146// Prototypes of private subroutines
147//---------------------------------------------------------------------------
148
149static OSErr FFusionDecompress(FFusionGlobals glob, AVCodecContext *context, UInt8 *dataPtr, long width, long height, AVFrame *picture, long length);
150static int FFusionGetBuffer(AVCodecContext *s, AVFrame *pic);
151static void FFusionReleaseBuffer(AVCodecContext *s, AVFrame *pic);
152static void releaseBuffer(AVCodecContext *s, FFusionBuffer *buf);
153static FFusionBuffer *retainBuffer(FFusionGlobals glob, FFusionBuffer *buf);
154
155int GetPPUserPreference();
156void SetPPUserPreference(int value);
157pascal OSStatus HandlePPDialogWindowEvent(EventHandlerCallRef  nextHandler, EventRef theEvent, void* userData);
158pascal OSStatus HandlePPDialogControlEvent(EventHandlerCallRef  nextHandler, EventRef theEvent, void* userData);
159void ChangeHintText(int value, ControlRef staticTextField);
160
161extern void init_FFmpeg();
162extern CFMutableStringRef CopyHomeDirectory();
163
164#define FFusionDebugPrint(x...) if (glob->fileLog) Codecprintf(glob->fileLog, x);
165#define not(x) ((x) ? "" : "not ")
166
167//---------------------------------------------------------------------------
168// Component Dispatcher
169//---------------------------------------------------------------------------
170
171#define IMAGECODEC_BASENAME()           FFusionCodec
172#define IMAGECODEC_GLOBALS()            FFusionGlobals storage
173
174#define CALLCOMPONENT_BASENAME()        IMAGECODEC_BASENAME()
175#define CALLCOMPONENT_GLOBALS()         IMAGECODEC_GLOBALS()
176
177#define COMPONENT_UPP_PREFIX()          uppImageCodec
178#define COMPONENT_DISPATCH_FILE         "FFusionCodecDispatch.h"
179#define COMPONENT_SELECT_PREFIX()       kImageCodec
180
181#define GET_DELEGATE_COMPONENT()        (storage->delegateComponent)
182
183#include <QuickTime/ImageCodec.k.h>
184#include <QuickTime/ComponentDispatchHelper.c>
185
186static void *launchUpdateChecker(void *args)
187{
188        FSRef *ref = (FSRef *)args;
189    LSOpenFSRef(ref, NULL);
190        free(ref);
191        return NULL;
192}
193
194Boolean FFusionAlreadyRanUpdateCheck = 0;
195
196void FFusionRunUpdateCheck()
197{
198        if (FFusionAlreadyRanUpdateCheck) return;
199
200    CFDateRef lastRunDate = CopyPreferencesValueTyped(CFSTR("NextRunDate"), CFDateGetTypeID());
201    CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
202   
203        FFusionAlreadyRanUpdateCheck = 1;
204       
205        if (lastRunDate) {
206                Boolean exit = CFDateGetAbsoluteTime(lastRunDate) > now;
207                CFRelease(lastRunDate);
208                if (exit) return;
209        }
210   
211    //Two places to check, home dir and /
212   
213    CFMutableStringRef location = CopyHomeDirectory();
214    CFStringAppend(location, CFSTR("/Library/PreferencePanes/Perian.prefPane/Contents/Resources/PerianUpdateChecker.app"));
215   
216    char fileRep[1024];
217    FSRef *updateCheckRef = malloc(sizeof(FSRef));
218    Boolean doCheck = FALSE;
219   
220    if(CFStringGetFileSystemRepresentation(location, fileRep, 1024))
221        if(FSPathMakeRef((UInt8 *)fileRep, updateCheckRef, NULL) == noErr)
222            doCheck = TRUE;
223   
224    CFRelease(location);
225    if(doCheck == FALSE)
226    {
227        CFStringRef absLocation = CFSTR("/Library/PreferencePanes/Perian.prefPane/Contents/Resources/PerianUpdateChecker.app");
228        if(CFStringGetFileSystemRepresentation(absLocation, fileRep, 1024))
229            if(FSPathMakeRef((UInt8 *)fileRep, updateCheckRef, NULL) != noErr)
230                goto err;  //We have failed
231    }
232        pthread_t thread;
233        pthread_create(&thread, NULL, launchUpdateChecker, updateCheckRef);
234       
235        return;
236err:
237        free(updateCheckRef);
238}
239
240static void RecomputeMaxCounts(FFusionGlobals glob)
241{
242        int i;
243        unsigned begun = 0, decoded = 0, ended = 0, drawn = 0;
244       
245        for (i = 0; i < 4; i++) {
246                struct per_frame_decode_stats *f = &glob->stats.type[i];
247               
248                begun += f->begin_calls;
249                decoded += f->decode_calls;
250                drawn += f->draw_calls;
251                ended += f->end_calls;
252        }
253       
254        signed begin_diff = begun - ended, decode_diff = decoded - drawn;
255       
256        if (abs(begin_diff) > glob->stats.max_frames_begun) glob->stats.max_frames_begun = begin_diff;
257        if (abs(decode_diff) > glob->stats.max_frames_decoded) glob->stats.max_frames_decoded = decode_diff;
258}
259
260static void DumpFrameDropStats(FFusionGlobals glob)
261{
262        static const char types[4] = {'?', 'I', 'P', 'B'};
263        int i;
264       
265        if (!glob->fileLog || glob->decode.lastFrame == 0) return;
266       
267        Codecprintf(glob->fileLog, "%p frame drop stats\nType\t| BeginBand\t| DecodeBand\t| DrawBand\t| dropped before decode\t| dropped before draw\n", glob);
268       
269        for (i = 0; i < 4; i++) {
270                struct per_frame_decode_stats *f = &glob->stats.type[i];
271                               
272                Codecprintf(glob->fileLog, "%c\t| %d\t\t| %d\t\t| %d\t\t| %d/%f%%\t\t| %d/%f%%\n", types[i], f->begin_calls, f->decode_calls, f->draw_calls,
273                                        f->begin_calls - f->decode_calls,(f->begin_calls > f->decode_calls) ? ((float)(f->begin_calls - f->decode_calls)/(float)f->begin_calls) * 100. : 0.,
274                                        f->decode_calls - f->draw_calls,(f->decode_calls > f->draw_calls) ? ((float)(f->decode_calls - f->draw_calls)/(float)f->decode_calls) * 100. : 0.);
275        }
276}
277
278static enum PixelFormat FindPixFmtFromVideo(AVCodec *codec, AVCodecContext *avctx, Ptr data, int bufferSize)
279{
280    AVCodecContext tmpContext;
281    AVFrame tmpFrame;
282    int got_picture;
283    enum PixelFormat pix_fmt;
284   
285    avcodec_get_context_defaults2(&tmpContext, CODEC_TYPE_VIDEO);
286        avcodec_get_frame_defaults(&tmpFrame);
287    tmpContext.width = avctx->width;
288    tmpContext.height = avctx->height;
289        tmpContext.flags = avctx->flags;
290        tmpContext.bits_per_coded_sample = avctx->bits_per_coded_sample;
291    tmpContext.codec_tag = avctx->codec_tag;
292        tmpContext.codec_id  = avctx->codec_id;
293        tmpContext.extradata = avctx->extradata;
294        tmpContext.extradata_size = avctx->extradata_size;
295       
296    avcodec_open(&tmpContext, codec);
297        AVPacket pkt;
298        av_init_packet(&pkt);
299        pkt.data = (UInt8*)data;
300        pkt.size = bufferSize;
301    avcodec_decode_video2(&tmpContext, &tmpFrame, &got_picture, &pkt);
302    pix_fmt = tmpContext.pix_fmt;
303    avcodec_close(&tmpContext);
304   
305    return pix_fmt;
306}
307
308static void SetupMultithreadedDecoding(AVCodecContext *s, enum CodecID codecID)
309{
310        int nthreads = 1;
311        size_t len = 4;
312       
313    // multithreading is only effective for mpeg1/2 and h.264 with slices
314    if (codecID != CODEC_ID_MPEG1VIDEO && codecID != CODEC_ID_MPEG2VIDEO && codecID != CODEC_ID_H264) return;
315   
316        // two threads on multicore, otherwise 1
317        if (sysctlbyname("hw.activecpu", &nthreads, &len, NULL, 0) == -1) nthreads = 1;
318        else nthreads = FFMIN(nthreads, 2);
319       
320        avcodec_thread_init(s, nthreads);
321}
322
323static void SetSkipLoopFilter(FFusionGlobals glob, AVCodecContext *avctx)
324{
325        Boolean keyExists = FALSE;
326        int loopFilterSkip = CFPreferencesGetAppIntegerValue(CFSTR("SkipLoopFilter"), PERIAN_PREF_DOMAIN, &keyExists);
327        if(keyExists)
328        {
329                enum AVDiscard loopFilterValue = AVDISCARD_DEFAULT;
330                switch (loopFilterSkip) {
331                        case 1:
332                                loopFilterValue = AVDISCARD_NONREF;
333                                break;
334                        case 2:
335                                loopFilterValue = AVDISCARD_BIDIR;
336                                break;
337                        case 3:
338                                loopFilterValue = AVDISCARD_NONKEY;
339                                break;
340                        case 4:
341                                loopFilterValue = AVDISCARD_ALL;
342                                break;
343                        default:
344                                break;
345                }
346                avctx->skip_loop_filter = loopFilterValue;
347                FFusionDebugPrint("%p Preflight set skip loop filter to %d", glob, loopFilterValue);
348        }
349}
350
351// A list of codec types (mostly official Apple codecs) which always have DTS info.
352// This is the wrong way to do it, instead we should check for a ctts atom directly.
353// This way causes files to play frames out of order if we guess wrong. Doesn't seem
354// possible to do it right, though.
355FFusionPacked DefaultPackedTypeForCodec(OSType codec)
356{
357        switch (codec) {
358                case kMPEG1VisualCodecType:
359                case kMPEG2VisualCodecType:
360                case 'hdv1':
361                case kMPEG4VisualCodecType:
362                case kH264CodecType:
363                        return PACKED_QUICKTIME_KNOWS_ORDER;
364                default:
365                        return PACKED_ALL_IN_FIRST_FRAME;
366        }
367}
368
369static void swapFrame(AVFrame * *a, AVFrame * *b)
370{
371        AVFrame *t = *a;
372        *a = *b;
373        *b = t;
374}
375
376//---------------------------------------------------------------------------
377// Component Routines
378//---------------------------------------------------------------------------
379
380// -- This Image Decompressor Use the Base Image Decompressor Component --
381//      The base image decompressor is an Apple-supplied component
382//      that makes it easier for developers to create new decompressors.
383//      The base image decompressor does most of the housekeeping and
384//      interface functions required for a QuickTime decompressor component,
385//      including scheduling for asynchronous decompression.
386
387//-----------------------------------------------------------------
388// Component Open Request - Required
389//-----------------------------------------------------------------
390
391pascal ComponentResult FFusionCodecOpen(FFusionGlobals glob, ComponentInstance self)
392{
393    ComponentResult err;
394    ComponentDescription descout;
395   
396    GetComponentInfo((Component)self, &descout, 0, 0, 0);
397       
398  //  FourCCprintf("Opening component for type ", descout.componentSubType);
399    // Allocate memory for our globals, set them up and inform the component manager that we've done so
400       
401    glob = (FFusionGlobals)NewPtrClear(sizeof(FFusionGlobalsRecord));
402   
403    if (err = MemError())
404    {
405        Codecprintf(NULL, "Unable to allocate globals! Exiting.\n");           
406    }
407    else
408    {
409                CFStringRef pathToLogFile = CopyPreferencesValueTyped(CFSTR("DebugLogFile"), CFStringGetTypeID());
410                char path[PATH_MAX];
411        SetComponentInstanceStorage(self, (Handle)glob);
412
413        glob->self = self;
414        glob->target = self;
415        glob->drawBandUPP = NULL;
416        glob->pixelTypes = NewHandleClear((kNumPixelFormatsSupportedFFusion+1) * sizeof(OSType));
417        glob->avCodec = 0;
418        glob->componentType = descout.componentSubType;
419                glob->data.frames = NULL;
420                glob->begin.parser = NULL;
421                if (pathToLogFile) {
422                        CFStringGetCString(pathToLogFile, path, PATH_MAX, kCFStringEncodingUTF8);
423                        CFRelease(pathToLogFile);
424                        glob->fileLog = fopen(path, "a");
425                }
426                glob->shouldUseReturnedFrame = 0;
427               
428        // Open and target an instance of the base decompressor as we delegate
429        // most of our calls to the base decompressor instance
430       
431        err = OpenADefaultComponent(decompressorComponentType, kBaseCodecType, &glob->delegateComponent);
432        if (!err)
433        {
434            ComponentSetTarget(glob->delegateComponent, self);
435        }
436        else
437        {
438            Codecprintf(glob->fileLog, "Error opening the base image decompressor! Exiting.\n");
439        }
440               
441                // we allocate some space for copying the frame data since we need some padding at the end
442                // for ffmpeg's optimized bitstream readers. Size doesn't really matter, it'll grow if need be
443                FFusionDataSetup(&(glob->data), 256, 1024*1024);
444        FFusionRunUpdateCheck();
445    }
446   
447    FFusionDebugPrint("%p opened for '%s'\n", glob, FourCCString(glob->componentType));
448    return err;
449}
450
451//-----------------------------------------------------------------
452// Component Close Request - Required
453//-----------------------------------------------------------------
454
455pascal ComponentResult FFusionCodecClose(FFusionGlobals glob, ComponentInstance self)
456{
457    FFusionDebugPrint("%p closed.\n", glob);
458        DumpFrameDropStats(glob);
459
460    // Make sure to close the base component and deallocate our storage
461    if (glob) 
462    {
463        if (glob->delegateComponent) 
464        {
465            CloseComponent(glob->delegateComponent);
466        }
467       
468        if (glob->drawBandUPP) 
469        {
470            DisposeImageCodecMPDrawBandUPP(glob->drawBandUPP);
471        }
472                               
473        if (glob->avContext)
474        {
475                        if (glob->avContext->extradata)
476                                free(glob->avContext->extradata);
477                                               
478                        if (glob->avContext->codec) avcodec_close(glob->avContext);
479            av_free(glob->avContext);
480        }
481               
482                if (glob->begin.parser)
483                {
484                        ffusionParserFree(glob->begin.parser);
485                }
486               
487                if (glob->pixelTypes)
488                {
489                        DisposeHandle(glob->pixelTypes);
490                }
491               
492                FFusionDataFree(&(glob->data));
493       
494                if(glob->fileLog)
495                        fclose(glob->fileLog);
496               
497        memset(glob, 0, sizeof(FFusionGlobalsRecord));
498        DisposePtr((Ptr)glob);
499    }
500       
501    return noErr;
502}
503
504//-----------------------------------------------------------------
505// Component Version Request - Required
506//-----------------------------------------------------------------
507
508pascal ComponentResult FFusionCodecVersion(FFusionGlobals glob)
509{
510    return kFFusionCodecVersion;
511}
512
513//-----------------------------------------------------------------
514// Component Target Request
515//-----------------------------------------------------------------
516// Allows another component to "target" you i.e., you call
517// another component whenever you would call yourself (as a result
518// of your component being used by another component)
519//-----------------------------------------------------------------
520
521pascal ComponentResult FFusionCodecTarget(FFusionGlobals glob, ComponentInstance target)
522{
523    glob->target = target;
524       
525    return noErr;
526}
527
528//-----------------------------------------------------------------
529// Component GetMPWorkFunction Request
530//-----------------------------------------------------------------
531// Allows your image decompressor component to perform asynchronous
532// decompression in a single MP task by taking advantage of the
533// Base Decompressor. If you implement this selector, your DrawBand
534// function must be MP-safe. MP safety means not calling routines
535// that may move or purge memory and not calling any routines which
536// might cause 68K code to be executed. Ideally, your DrawBand
537// function should not make any API calls whatsoever. Obviously
538// don't implement this if you're building a 68k component.
539//-----------------------------------------------------------------
540
541pascal ComponentResult FFusionCodecGetMPWorkFunction(FFusionGlobals glob, ComponentMPWorkFunctionUPP *workFunction, void **refCon)
542{
543        if (glob->drawBandUPP == NULL)
544                glob->drawBandUPP = NewImageCodecMPDrawBandUPP((ImageCodecMPDrawBandProcPtr)FFusionCodecDrawBand);
545       
546        return ImageCodecGetBaseMPWorkFunction(glob->delegateComponent, workFunction, refCon, glob->drawBandUPP, glob);
547}
548
549//-----------------------------------------------------------------
550// ImageCodecInitialize
551//-----------------------------------------------------------------
552// The first function call that your image decompressor component
553// receives from the base image decompressor is always a call to
554// ImageCodecInitialize . In response to this call, your image
555// decompressor component returns an ImageSubCodecDecompressCapabilities
556// structure that specifies its capabilities.
557//-----------------------------------------------------------------
558
559pascal ComponentResult FFusionCodecInitialize(FFusionGlobals glob, ImageSubCodecDecompressCapabilities *cap)
560{
561        Boolean doExperimentalFlags = CFPreferencesGetAppBooleanValue(CFSTR("ExperimentalQTFlags"), PERIAN_PREF_DOMAIN, NULL);
562       
563    // Secifies the size of the ImageSubCodecDecompressRecord structure
564    // and say we can support asyncronous decompression
565    // With the help of the base image decompressor, any image decompressor
566    // that uses only interrupt-safe calls for decompression operations can
567    // support asynchronous decompression.
568       
569    cap->decompressRecordSize = sizeof(FFusionDecompressRecord) + 12;
570    cap->canAsync = true;
571       
572        // QT 7
573        if(cap->recordSize > offsetof(ImageSubCodecDecompressCapabilities, baseCodecShouldCallDecodeBandForAllFrames))
574        {
575                cap->subCodecIsMultiBufferAware = true;
576                cap->subCodecSupportsOutOfOrderDisplayTimes = true;
577                cap->baseCodecShouldCallDecodeBandForAllFrames = true;
578                cap->subCodecSupportsScheduledBackwardsPlaybackWithDifferenceFrames = !doExperimentalFlags;
579                cap->subCodecSupportsDrawInDecodeOrder = doExperimentalFlags; 
580                cap->subCodecSupportsDecodeSmoothing = true; 
581        }
582       
583    return noErr;
584}
585
586static inline int shouldDecode(FFusionGlobals glob, enum CodecID codecID)
587{
588        FFusionDecodeAbilities decode = FFUSION_PREFER_DECODE;
589        if (glob->componentType == 'avc1')
590                decode = ffusionIsParsedVideoDecodable(glob->begin.parser);
591        if(decode > FFUSION_CANNOT_DECODE && 
592           (codecID == CODEC_ID_H264 || codecID == CODEC_ID_MPEG4) && CFPreferencesGetAppBooleanValue(CFSTR("PreferAppleCodecs"), PERIAN_PREF_DOMAIN, NULL))
593                decode = FFUSION_PREFER_NOT_DECODE;
594        if(decode > FFUSION_CANNOT_DECODE)
595                if(IsForcedDecodeEnabled())
596                        decode = FFUSION_PREFER_DECODE;
597        return decode > FFUSION_PREFER_NOT_DECODE;
598}
599
600//-----------------------------------------------------------------
601// ImageCodecPreflight
602//-----------------------------------------------------------------
603// The base image decompressor gets additional information about the
604// capabilities of your image decompressor component by calling
605// ImageCodecPreflight. The base image decompressor uses this
606// information when responding to a call to the ImageCodecPredecompress
607// function, which the ICM makes before decompressing an image. You
608// are required only to provide values for the wantedDestinationPixelSize
609// and wantedDestinationPixelTypes fields and can also modify other
610// fields if necessary.
611//-----------------------------------------------------------------
612
613pascal ComponentResult FFusionCodecPreflight(FFusionGlobals glob, CodecDecompressParams *p)
614{
615    OSType *pos;
616    int index;
617    CodecCapabilities *capabilities = p->capabilities;
618        long count = 0;
619        Handle imgDescExt;
620        OSErr err = noErr;
621       
622    // We first open libavcodec library and the codec corresponding
623    // to the fourCC if it has not been done before
624   
625        FFusionDebugPrint("%p Preflight called.\n", glob);
626        FFusionDebugPrint("%p Frame dropping is %senabled\n", glob, not(IsFrameDroppingEnabled()));
627       
628    if (!glob->avCodec)
629    {
630                init_FFmpeg();
631                initFFusionParsers();
632
633                OSType componentType = glob->componentType;
634                enum CodecID codecID = getCodecID(componentType);
635               
636                glob->packedType = DefaultPackedTypeForCodec(componentType);
637               
638                if(componentType == 'VP30' || componentType == 'VP31')
639                        glob->shouldUseReturnedFrame = TRUE;
640
641                if(codecID == CODEC_ID_NONE)
642                {
643                        Codecprintf(glob->fileLog, "Warning! Unknown codec type! Using MPEG4 by default.\n");
644                        codecID = CODEC_ID_MPEG4;
645                }
646               
647                glob->avCodec = avcodec_find_decoder(codecID);
648//              if(glob->packedType != PACKED_QUICKTIME_KNOWS_ORDER)
649                        glob->begin.parser = ffusionParserInit(codecID);
650               
651                if ((codecID == CODEC_ID_MPEG4 || codecID == CODEC_ID_H264) && !glob->begin.parser)
652                        Codecprintf(glob->fileLog, "This is a parseable format, but we couldn't open a parser!\n");
653               
654        // we do the same for the AVCodecContext since all context values are
655        // correctly initialized when calling the alloc function
656       
657        glob->avContext = avcodec_alloc_context2(CODEC_TYPE_VIDEO);
658               
659                // Use low delay
660                glob->avContext->flags |= CODEC_FLAG_LOW_DELAY;
661               
662        // Image size is mandatory for DivX-like codecs
663       
664        glob->avContext->width = (**p->imageDescription).width;
665        glob->avContext->height = (**p->imageDescription).height;
666                glob->avContext->bits_per_coded_sample = (**p->imageDescription).depth;
667               
668        // We also pass the FourCC since it allows the H263 hybrid decoder
669        // to make the difference between the various flavours of DivX
670        glob->avContext->codec_tag = Endian32_Swap(glob->componentType);
671                glob->avContext->codec_id  = codecID;
672       
673                // avc1 requires the avcC extension
674                if (glob->componentType == 'avc1') {
675                        count = isImageDescriptionExtensionPresent(p->imageDescription, 'avcC');
676                       
677                        if (count >= 1) {
678                                imgDescExt = NewHandle(0);
679                                GetImageDescriptionExtension(p->imageDescription, &imgDescExt, 'avcC', 1);
680                               
681                                glob->avContext->extradata = calloc(1, GetHandleSize(imgDescExt) + FF_INPUT_BUFFER_PADDING_SIZE);
682                                memcpy(glob->avContext->extradata, *imgDescExt, GetHandleSize(imgDescExt));
683                                glob->avContext->extradata_size = GetHandleSize(imgDescExt);
684                               
685                                DisposeHandle(imgDescExt);
686                        } else {
687                                count = isImageDescriptionExtensionPresent(p->imageDescription, 'strf');
688                               
689                                // avc1 in AVI, need to reorder frames
690                                if (count >= 1)
691                                        glob->packedType = PACKED_ALL_IN_FIRST_FRAME;
692                        }
693                } else if (glob->componentType == 'mp4v') {
694                        count = isImageDescriptionExtensionPresent(p->imageDescription, 'esds');
695                       
696                        if (count >= 1) {
697                                imgDescExt = NewHandle(0);
698                                GetImageDescriptionExtension(p->imageDescription, &imgDescExt, 'esds', 1);
699                               
700                                ReadESDSDescExt(imgDescExt, &glob->avContext->extradata, &glob->avContext->extradata_size);
701                               
702                                DisposeHandle(imgDescExt);
703                        }
704                } else {
705                        count = isImageDescriptionExtensionPresent(p->imageDescription, 'strf');
706                       
707                        if (count >= 1) {
708                                imgDescExt = NewHandle(0);
709                                GetImageDescriptionExtension(p->imageDescription, &imgDescExt, 'strf', 1);
710                               
711                                if (GetHandleSize(imgDescExt) - 40 > 0) {
712                                        glob->avContext->extradata = calloc(1, GetHandleSize(imgDescExt) - 40 + FF_INPUT_BUFFER_PADDING_SIZE);
713                                        memcpy(glob->avContext->extradata, *imgDescExt + 40, GetHandleSize(imgDescExt) - 40);
714                                        glob->avContext->extradata_size = GetHandleSize(imgDescExt) - 40;
715                                }
716                                DisposeHandle(imgDescExt);
717                        }
718                }
719               
720                FFusionDebugPrint("%p preflighted for %dx%d '%s'. (%d bytes of extradata)\n", glob, (**p->imageDescription).width, (**p->imageDescription).height, FourCCString(glob->componentType), glob->avContext->extradata_size);
721       
722                if(glob->avContext->extradata_size != 0 && glob->begin.parser != NULL)
723                        ffusionParseExtraData(glob->begin.parser, glob->avContext->extradata, glob->avContext->extradata_size);
724               
725                if (glob->fileLog)
726                        ffusionLogDebugInfo(glob->begin.parser, glob->fileLog);
727               
728                if (!shouldDecode(glob, codecID))
729                        err = featureUnsupported;
730               
731                // some hooks into ffmpeg's buffer allocation to get frames in
732                // decode order without delay more easily
733                glob->avContext->opaque = glob;
734                glob->avContext->get_buffer = FFusionGetBuffer;
735                glob->avContext->release_buffer = FFusionReleaseBuffer;
736               
737                // multi-slice decoding
738                SetupMultithreadedDecoding(glob->avContext, codecID);
739               
740                // deblock skipping for h264
741                SetSkipLoopFilter(glob, glob->avContext);
742               
743                //fast flag
744                if (CFPreferencesGetAppBooleanValue(CFSTR("UseFastDecode"), PERIAN_PREF_DOMAIN, NULL))
745                        glob->avContext->flags2 |= CODEC_FLAG2_FAST;
746               
747        // Finally we open the avcodec
748       
749        if (avcodec_open(glob->avContext, glob->avCodec))
750        {
751            Codecprintf(glob->fileLog, "Error opening avcodec!\n");
752           
753                        err = paramErr;
754        }
755               
756        // codec was opened, but didn't give us its pixfmt
757                // we have to decode the first frame to find out one
758                else if (glob->avContext->pix_fmt == PIX_FMT_NONE && p->bufferSize && p->data)
759            glob->avContext->pix_fmt = FindPixFmtFromVideo(glob->avCodec, glob->avContext, p->data, p->bufferSize);
760    }
761   
762    // Specify the minimum image band height supported by the component
763    // bandInc specifies a common factor of supported image band heights -
764    // if your component supports only image bands that are an even
765    // multiple of some number of pixels high report this common factor in bandInc
766   
767    capabilities->bandMin = (**p->imageDescription).height;
768    capabilities->bandInc = capabilities->bandMin;
769       
770    // libavcodec 0.4.x is no longer stream based i.e. you cannot pass just
771    // an arbitrary amount of data to the library.
772    // Instead we have to tell QT to just pass the data corresponding
773    // to one frame
774       
775    capabilities->flags |= codecWantsSpecialScaling;
776   
777    p->requestedBufferWidth = (**p->imageDescription).width;
778    p->requestedBufferHeight = (**p->imageDescription).height;
779   
780    // Indicate the pixel depth the component can use with the specified image
781    // normally should be capabilities->wantedPixelSize = (**p->imageDescription).depth;
782    // but we don't care since some DivX are so bugged that the depth information
783    // is not correct
784   
785    capabilities->wantedPixelSize = 0;
786   
787    pos = *((OSType **)glob->pixelTypes);
788   
789    index = 0;
790       
791        if (!err) {
792                OSType qtPixFmt = ColorConversionDstForPixFmt(glob->avContext->pix_fmt);
793               
794                /*
795                 an error here means either
796                 1) a color converter for this format isn't implemented
797                 2) we know QT doesn't like this format and will give us argb 32bit instead
798                 
799                 in the case of 2 we have to special-case bail right here, since errors
800                 in BeginBand are ignored
801                 */
802                if (qtPixFmt)
803                        pos[index++] = qtPixFmt;
804                else
805                        err = featureUnsupported;
806        }
807       
808    p->wantedDestinationPixelTypes = (OSType **)glob->pixelTypes;
809   
810    // Specify the number of pixels the image must be extended in width and height if
811    // the component cannot accommodate the image at its given width and height
812    // It is not the case here
813   
814    capabilities->extendWidth = 0;
815    capabilities->extendHeight = 0;
816   
817        capabilities->flags |= codecCanAsync | codecCanAsyncWhen;
818       
819        FFusionDebugPrint("%p Preflight requesting colorspace '%s'. (error %d)\n", glob, FourCCString(pos[0]), err);
820       
821    return err;
822}
823
824static int qtTypeForFrameInfo(int original, int fftype, int skippable)
825{
826        if(fftype == FF_I_TYPE)
827        {
828                if(!skippable)
829                        return kCodecFrameTypeKey;
830        }
831        else if(skippable && IsFrameDroppingEnabled())
832                return kCodecFrameTypeDroppableDifference;
833        else if(fftype != 0)
834                return kCodecFrameTypeDifference;       
835        return original;
836}
837
838//-----------------------------------------------------------------
839// ImageCodecBeginBand
840//-----------------------------------------------------------------
841// The ImageCodecBeginBand function allows your image decompressor
842// component to save information about a band before decompressing
843// it. This function is never called at interrupt time. The base
844// image decompressor preserves any changes your component makes to
845// any of the fields in the ImageSubCodecDecompressRecord or
846// CodecDecompressParams structures. If your component supports
847// asynchronous scheduled decompression, it may receive more than
848// one ImageCodecBeginBand call before receiving an ImageCodecDrawBand
849// call.
850//-----------------------------------------------------------------
851
852pascal ComponentResult FFusionCodecBeginBand(FFusionGlobals glob, CodecDecompressParams *p, ImageSubCodecDecompressRecord *drp, long flags)
853{       
854    FFusionDecompressRecord *myDrp = (FFusionDecompressRecord *)drp->userDecompressRecord;
855        int redisplayFirstFrame = 0;
856        int type = 0;
857        int skippable = 0;
858       
859    //////
860  /*  IBNibRef          nibRef;
861    WindowRef           window;
862    OSStatus            err;
863    CFBundleRef         bundleRef;
864    EventHandlerUPP     handlerUPP, handlerUPP2;
865    EventTypeSpec       eventType;
866    ControlID           controlID;
867    ControlRef          theControl;
868    KeyMap              currentKeyMap;
869    int                 userPreference; */
870    ///////
871
872    myDrp->width = (**p->imageDescription).width;
873    myDrp->height = (**p->imageDescription).height;
874    myDrp->depth = (**p->imageDescription).depth;
875       
876    myDrp->pixelFormat = p->dstPixMap.pixelFormat;
877        myDrp->decoded = p->frameTime ? (0 != (p->frameTime->flags & icmFrameAlreadyDecoded)) : false;
878        myDrp->frameData = NULL;
879        myDrp->buffer = NULL;
880       
881        FFusionDebugPrint("%p BeginBand #%d. (%sdecoded, packed %d)\n", glob, p->frameNumber, not(myDrp->decoded), glob->packedType);
882       
883        if (!glob->avContext) {
884                Codecprintf(glob->fileLog, "Perian: QT tried to call BeginBand without preflighting!\n");
885                return internalComponentErr;
886        }
887       
888        if (p->frameNumber == 0 && myDrp->pixelFormat != ColorConversionDstForPixFmt(glob->avContext->pix_fmt)) {
889                Codecprintf(glob->fileLog, "QT gave us unwanted pixelFormat %s (%08x), this will not work\n", FourCCString(myDrp->pixelFormat), myDrp->pixelFormat);
890        }
891       
892        if(myDrp->decoded)
893        {
894                int i;
895                myDrp->frameNumber = p->frameNumber;
896                for (i = 0; i < FFUSION_MAX_BUFFERS; i++) {
897                        if (glob->buffers[i].retainCount && glob->buffers[i].frameNumber == myDrp->frameNumber) {
898                                myDrp->buffer = retainBuffer(glob, &glob->buffers[i]);
899                                break;
900                        }
901                }
902                return noErr;
903        }
904       
905        if(glob->packedType != PACKED_QUICKTIME_KNOWS_ORDER && p->frameNumber != glob->begin.lastFrame + 1)
906        {
907                if(glob->decode.lastFrame < p->frameNumber && p->frameNumber < glob->begin.lastFrame + 1)
908                {
909                        /* We already began this sucker, but haven't decoded it yet, find the data */
910                        FrameData *frameData = NULL;
911                        frameData = FFusionDataFind(&glob->data, p->frameNumber);
912                        if(frameData != NULL)
913                        {
914                                myDrp->frameData = frameData;
915                                drp->frameType = qtTypeForFrameInfo(drp->frameType, myDrp->frameData->type, myDrp->frameData->skippabble);
916                                myDrp->frameNumber = p->frameNumber;
917                                myDrp->GOPStartFrameNumber = glob->begin.lastIFrame;
918                                return noErr;
919                        }
920                }
921                else
922                {
923                        /* Reset context, safe marking in such a case */
924                        glob->begin.lastFrameType = FF_I_TYPE;
925                        FFusionDataReadUnparsed(&(glob->data));
926                        glob->begin.lastPFrameData = NULL;
927                        redisplayFirstFrame = 1;
928                }
929        }
930       
931        if(glob->begin.parser != NULL)
932        {
933                int parsedBufSize = 0;
934                uint8_t *buffer = (uint8_t *)drp->codecData;
935                int bufferSize = p->bufferSize;
936                int skipped = 0;
937               
938                if(glob->data.unparsedFrames.dataSize != 0)
939                {
940                        buffer = glob->data.unparsedFrames.buffer;
941                        bufferSize = glob->data.unparsedFrames.dataSize;
942                }
943               
944                if(ffusionParse(glob->begin.parser, buffer, bufferSize, &parsedBufSize, &type, &skippable, &skipped) == 0 && (!skipped || glob->begin.futureType))
945                {
946                        /* parse failed */
947                        myDrp->bufferSize = bufferSize;
948                        if(glob->begin.futureType != 0)
949                        {
950                                /* Assume our previously decoded P frame */
951                                type = glob->begin.futureType;
952                                glob->begin.futureType = 0;
953                                myDrp->frameData = glob->begin.lastPFrameData;
954                        }
955                        else
956                        {
957                                Codecprintf(glob->fileLog, "parse failed frame %d with size %d\n", p->frameNumber, bufferSize);
958                                if(glob->data.unparsedFrames.dataSize != 0)
959                                        Codecprintf(glob->fileLog, ", parser had extra data\n");                               
960                        }
961                }
962                else if(glob->packedType != PACKED_QUICKTIME_KNOWS_ORDER)
963                {
964                        if(type == FF_B_TYPE && glob->packedType == PACKED_ALL_IN_FIRST_FRAME && glob->begin.futureType == 0)
965                                /* Badly framed.  We hit a B frame after it was supposed to be displayed, switch to delaying by a frame */
966                                glob->packedType = PACKED_DELAY_BY_ONE_FRAME;
967                        else if(glob->packedType == PACKED_DELAY_BY_ONE_FRAME && parsedBufSize < bufferSize - 16)
968                                /* Seems to be switching back to packed in one frame; switch back*/
969                                glob->packedType = PACKED_ALL_IN_FIRST_FRAME;
970                       
971                        myDrp->frameData = FFusionDataAppend(&(glob->data), buffer, parsedBufSize, type);
972                        if(type != FF_I_TYPE)
973                                myDrp->frameData->prereqFrame = glob->begin.lastPFrameData;
974                        if(glob->packedType == PACKED_DELAY_BY_ONE_FRAME)
975                        {
976                                if(type != FF_B_TYPE)
977                                {
978                                        FrameData *nextPFrame = myDrp->frameData;
979                                        FrameData *lastPFrame = glob->begin.lastPFrameData;
980                                        if(lastPFrame != NULL)
981                                                /* Mark the next P or I frame, predictive decoding */
982                                                lastPFrame->nextFrame = nextPFrame;
983                                        myDrp->frameData = lastPFrame;
984                                        glob->begin.lastPFrameData = nextPFrame;
985
986                                        if(redisplayFirstFrame)
987                                                myDrp->frameData = nextPFrame;
988                                }
989                               
990                                int displayType = glob->begin.lastFrameType;
991                                glob->begin.lastFrameType = type;
992                                type = displayType;
993                        }
994                        else if(type != FF_B_TYPE)
995                                glob->begin.lastPFrameData = myDrp->frameData;
996
997                        if(type == FF_I_TYPE && glob->packedType == PACKED_ALL_IN_FIRST_FRAME)
998                                /* Wipe memory of past P frames */
999                                glob->begin.futureType = 0;
1000                       
1001                        if(parsedBufSize < p->bufferSize)
1002                        {
1003                                int oldType = type;
1004                               
1005                                buffer += parsedBufSize;
1006                                bufferSize -= parsedBufSize;
1007                                int success = ffusionParse(glob->begin.parser, buffer, bufferSize, &parsedBufSize, &type, &skippable, &skipped);
1008                                if(success && type == FF_B_TYPE)
1009                                {
1010                                        /* A B frame follows us, so setup the P frame for the future and set dependencies */
1011                                        glob->begin.futureType = oldType;
1012                                        if(glob->begin.lastPFrameData != NULL)
1013                                                /* Mark the next P or I frame, predictive decoding */
1014                                                glob->begin.lastPFrameData->nextFrame = myDrp->frameData;
1015                                        glob->begin.lastPFrameData = myDrp->frameData;
1016                                        myDrp->frameData = FFusionDataAppend(&(glob->data), buffer, parsedBufSize, type);
1017                                        myDrp->frameData->prereqFrame = glob->begin.lastPFrameData;
1018                                        buffer += parsedBufSize;
1019                                        bufferSize -= parsedBufSize;
1020                                }
1021                                if(bufferSize > 0)
1022                                        FFusionDataSetUnparsed(&(glob->data), buffer, bufferSize);
1023                                else
1024                                        FFusionDataReadUnparsed(&(glob->data));
1025                        }
1026                        else
1027                                FFusionDataReadUnparsed(&(glob->data));
1028                        myDrp->bufferSize = 0;
1029                }
1030                else
1031                {
1032                        myDrp->bufferSize = bufferSize;
1033                }
1034               
1035                drp->frameType = qtTypeForFrameInfo(drp->frameType, type, skippable);
1036                if(myDrp->frameData != NULL)
1037                {
1038                        myDrp->frameData->frameNumber = p->frameNumber;
1039                        myDrp->frameData->skippabble = skippable;
1040                }
1041        }
1042        else
1043                myDrp->bufferSize = p->bufferSize;
1044        glob->begin.lastFrame = p->frameNumber;
1045        if(drp->frameType == kCodecFrameTypeKey)
1046                glob->begin.lastIFrame = p->frameNumber;
1047        myDrp->frameNumber = p->frameNumber;
1048        myDrp->GOPStartFrameNumber = glob->begin.lastIFrame;
1049       
1050        glob->stats.type[drp->frameType].begin_calls++;
1051        RecomputeMaxCounts(glob);
1052        FFusionDebugPrint("%p BeginBand: frame #%d type %d. (%sskippable)\n", glob, myDrp->frameNumber, type, not(skippable));
1053       
1054    return noErr;
1055}
1056
1057static OSErr PrereqDecompress(FFusionGlobals glob, FrameData *prereq, AVCodecContext *context, long width, long height, AVFrame *picture)
1058{
1059        FFusionDebugPrint("%p prereq-decompressing frame #%d.\n", glob, prereq->frameNumber);
1060       
1061        FrameData *preprereq = FrameDataCheckPrereq(prereq);
1062        if(preprereq)
1063                PrereqDecompress(glob, preprereq, context, width, height, picture);
1064       
1065        unsigned char *dataPtr = (unsigned char *)prereq->buffer;
1066        int dataSize = prereq->dataSize;
1067       
1068        OSErr err = FFusionDecompress(glob, context, dataPtr, width, height, picture, dataSize);
1069       
1070        return err;
1071}
1072
1073pascal ComponentResult FFusionCodecDecodeBand(FFusionGlobals glob, ImageSubCodecDecompressRecord *drp, unsigned long flags)
1074{
1075        OSErr err = noErr;
1076        AVFrame tempFrame;
1077
1078    FFusionDecompressRecord *myDrp = (FFusionDecompressRecord *)drp->userDecompressRecord;
1079       
1080        glob->stats.type[drp->frameType].decode_calls++;
1081        RecomputeMaxCounts(glob);
1082        FFusionDebugPrint("%p DecodeBand #%d qtType %d. (packed %d)\n", glob, myDrp->frameNumber, drp->frameType, glob->packedType);
1083       
1084        // QuickTime will drop H.264 frames when necessary if a sample dependency table exists
1085        // we don't want to flush buffers in that case.
1086        if(glob->packedType != PACKED_QUICKTIME_KNOWS_ORDER && myDrp->frameNumber != glob->decode.lastFrame + 1)
1087        {
1088                /* Skipped some frames in here */
1089                FFusionDebugPrint("%p - frames skipped.\n", glob);
1090                if(drp->frameType == kCodecFrameTypeKey || myDrp->GOPStartFrameNumber > glob->decode.lastFrame || myDrp->frameNumber < glob->decode.lastFrame)
1091                {
1092                        /* If this is a key frame or the P frame before us is after the last frame (skip ahead), or we are before the last decoded frame (skip back) *
1093                         * then we are in a whole new GOP */
1094                        avcodec_flush_buffers(glob->avContext);
1095                }
1096        }
1097       
1098        if(myDrp->frameData && myDrp->frameData->decoded && glob->decode.futureBuffer != NULL)
1099        {
1100                myDrp->buffer = retainBuffer(glob, glob->decode.futureBuffer);
1101                myDrp->decoded = true;
1102#if 0   /* Need to make sure this frame's data is not eradicated during the decompress */
1103                FrameData *nextFrame = myDrp->frameData->nextFrame;
1104                if(nextFrame != NULL)
1105                {
1106                        FFusionDecompress(glob, glob->avContext, nextFrame->buffer, NULL, myDrp->width, myDrp->height, &tempFrame, nextFrame->dataSize);
1107                        if(tempFrame.data[0] != NULL)
1108                        {
1109                                glob->decode.futureBuffer = (FFusionBuffer *)tempFrame.opaque;
1110                                nextFrame->decoded = TRUE;
1111                        }
1112                }
1113                else
1114#endif
1115                        glob->decode.futureBuffer = NULL;
1116                FFusionDataMarkRead(myDrp->frameData);
1117                glob->decode.lastFrame = myDrp->frameNumber;
1118                return err;
1119        }
1120                       
1121        FrameData *frameData = NULL;
1122        unsigned char *dataPtr = NULL;
1123        unsigned int dataSize;
1124        if(glob->packedType != PACKED_QUICKTIME_KNOWS_ORDER && myDrp->frameData != NULL && myDrp->frameData->decoded == 0)
1125        {
1126                /* Pull from our buffer */
1127                frameData = myDrp->frameData;
1128                FrameData *prereq = FrameDataCheckPrereq(frameData);
1129               
1130                if(prereq)
1131                {
1132                        PrereqDecompress(glob, prereq, glob->avContext, myDrp->width, myDrp->height, &tempFrame);
1133                        if(tempFrame.data[0] != NULL)
1134                        {
1135                                glob->decode.futureBuffer = (FFusionBuffer *)tempFrame.opaque;
1136                                prereq->decoded = TRUE;
1137                        }
1138                }
1139                dataPtr = (unsigned char *)frameData->buffer;
1140                dataSize = frameData->dataSize;
1141                frameData->decoded = TRUE;
1142        }
1143        else
1144        {
1145                /* data is already set up properly for us */
1146                dataSize = myDrp->bufferSize;
1147                dataPtr = FFusionCreateEntireDataBuffer(&(glob->data), (uint8_t *)drp->codecData, dataSize);
1148        }
1149               
1150        err = FFusionDecompress(glob, glob->avContext, dataPtr, myDrp->width, myDrp->height, &tempFrame, dataSize);
1151                       
1152        if (glob->packedType == PACKED_QUICKTIME_KNOWS_ORDER) {
1153                myDrp->buffer = &glob->buffers[glob->lastAllocatedBuffer];
1154                myDrp->buffer->frameNumber = myDrp->frameNumber;
1155                retainBuffer(glob, myDrp->buffer);
1156                myDrp->buffer->returnedFrame = tempFrame;
1157                myDrp->decoded = true;
1158                glob->decode.lastFrame = myDrp->frameNumber;
1159                return err;
1160        }
1161        if(tempFrame.data[0] == NULL)
1162                myDrp->buffer = NULL;
1163        else
1164                myDrp->buffer = retainBuffer(glob, (FFusionBuffer *)tempFrame.opaque);
1165       
1166        if(tempFrame.pict_type == FF_I_TYPE)
1167                /* Wipe memory of past P frames */
1168                glob->decode.futureBuffer = NULL;
1169        glob->decode.lastFrame = myDrp->frameNumber;
1170        myDrp->decoded = true;
1171        if (myDrp->buffer) myDrp->buffer->returnedFrame = tempFrame;
1172       
1173        FFusionDataMarkRead(frameData);
1174       
1175        return err;
1176}
1177
1178//-----------------------------------------------------------------
1179// ImageCodecDrawBand
1180//-----------------------------------------------------------------
1181// The base image decompressor calls your image decompressor
1182// component's ImageCodecDrawBand function to decompress a band or
1183// frame. Your component must implement this function. If the
1184// ImageSubCodecDecompressRecord structure specifies a progress function
1185// or data-loading function, the base image decompressor will never call
1186// ImageCodecDrawBand at interrupt time. If the ImageSubCodecDecompressRecord
1187// structure specifies a progress function, the base image decompressor
1188// handles codecProgressOpen and codecProgressClose calls, and your image
1189// decompressor component must not implement these functions.
1190// If not, the base image decompressor may call the ImageCodecDrawBand
1191// function at interrupt time. When the base image decompressor calls your
1192// ImageCodecDrawBand function, your component must perform the decompression
1193// specified by the fields of the ImageSubCodecDecompressRecord structure.
1194// The structure includes any changes your component made to it when
1195// performing the ImageCodecBeginBand function. If your component supports
1196// asynchronous scheduled decompression, it may receive more than one
1197// ImageCodecBeginBand call before receiving an ImageCodecDrawBand call.
1198//-----------------------------------------------------------------
1199
1200pascal ComponentResult FFusionCodecDrawBand(FFusionGlobals glob, ImageSubCodecDecompressRecord *drp)
1201{
1202    OSErr err = noErr;
1203    FFusionDecompressRecord *myDrp = (FFusionDecompressRecord *)drp->userDecompressRecord;
1204        AVFrame *picture;
1205       
1206        glob->stats.type[drp->frameType].draw_calls++;
1207        RecomputeMaxCounts(glob);
1208        FFusionDebugPrint("%p DrawBand #%d. (%sdecoded)\n", glob, myDrp->frameNumber, not(myDrp->decoded));
1209       
1210        if(!myDrp->decoded) {
1211                err = FFusionCodecDecodeBand(glob, drp, 0);
1212
1213                if (err) goto err;
1214        }
1215       
1216        if (myDrp->buffer)
1217        {
1218                picture = myDrp->buffer->frame;
1219        }
1220        else
1221                picture = &glob->lastDisplayedFrame;
1222       
1223        if(!picture || picture->data[0] == 0)
1224        {
1225                if(glob->shouldUseReturnedFrame && myDrp->buffer &&
1226                   myDrp->buffer->returnedFrame.data[0])
1227                        //Some decoders (vp3) keep their internal buffers in an unusable state
1228                        picture = &myDrp->buffer->returnedFrame;
1229                else if(glob->lastDisplayedFrame.data[0] != NULL)
1230                        //Display last frame
1231                        picture = &glob->lastDisplayedFrame;
1232                else {
1233                        //Display black (no frame decoded yet)
1234
1235                        if (!glob->colorConv.clear) {
1236                                err = ColorConversionFindFor(&glob->colorConv, glob->avContext->pix_fmt, NULL, myDrp->pixelFormat);
1237                                if (err) goto err;
1238                        }
1239                       
1240                        glob->colorConv.clear((UInt8*)drp->baseAddr, drp->rowBytes, myDrp->width, myDrp->height);
1241                        return noErr;
1242                }
1243        }
1244        else
1245        {
1246                if (myDrp->buffer)
1247                        glob->lastDisplayedFrame = *picture;
1248        }
1249       
1250        if (!glob->colorConv.convert) {
1251                err = ColorConversionFindFor(&glob->colorConv, glob->avContext->pix_fmt, picture, myDrp->pixelFormat);
1252                if (err) goto err;
1253        }
1254       
1255        glob->colorConv.convert(picture, (UInt8*)drp->baseAddr, drp->rowBytes, myDrp->width, myDrp->height);
1256       
1257err:
1258    return err;
1259}
1260
1261//-----------------------------------------------------------------
1262// ImageCodecEndBand
1263//-----------------------------------------------------------------
1264// The ImageCodecEndBand function notifies your image decompressor
1265// component that decompression of a band has finished or
1266// that it was terminated by the Image Compression Manager. Your
1267// image decompressor component is not required to implement the
1268// ImageCodecEndBand function. The base image decompressor may call
1269// the ImageCodecEndBand function at interrupt time.
1270// After your image decompressor component handles an ImageCodecEndBand
1271// call, it can perform any tasks that are required when decompression
1272// is finished, such as disposing of data structures that are no longer
1273// needed. Because this function can be called at interrupt time, your
1274// component cannot use this function to dispose of data structures;
1275// this must occur after handling the function. The value of the result
1276// parameter should be set to noErr if the band or frame was
1277// drawn successfully.
1278// If it is any other value, the band or frame was not drawn.
1279//-----------------------------------------------------------------
1280
1281pascal ComponentResult FFusionCodecEndBand(FFusionGlobals glob, ImageSubCodecDecompressRecord *drp, OSErr result, long flags)
1282{
1283        FFusionDecompressRecord *myDrp = (FFusionDecompressRecord *)drp->userDecompressRecord;
1284        glob->stats.type[drp->frameType].end_calls++;
1285        FFusionBuffer *buf = myDrp->buffer;
1286        if(buf && buf->frame)
1287                releaseBuffer(glob->avContext, buf);
1288       
1289        FFusionDebugPrint("%p EndBand #%d.\n", glob, myDrp->frameNumber);
1290       
1291    return noErr;
1292}
1293
1294// Gamma curve value for FFusion video.
1295ComponentResult FFusionCodecGetSourceDataGammaLevel(FFusionGlobals glob, Fixed *sourceDataGammaLevel)
1296{
1297        enum AVColorTransferCharacteristic color_trc = AVCOL_TRC_UNSPECIFIED;
1298        float gamma;
1299       
1300        if (glob->avContext)
1301                color_trc = glob->avContext->color_trc;
1302       
1303        switch (color_trc) {
1304                case AVCOL_TRC_GAMMA28:
1305                        gamma = 2.8;
1306                        break;
1307                case AVCOL_TRC_GAMMA22:
1308                        gamma = 2.2;
1309                        break;
1310                default: // absolutely everything in the world will reach here
1311                        gamma = 1/.45; // and GAMMA22 is probably a typo for this anyway
1312        }
1313       
1314        *sourceDataGammaLevel = FloatToFixed(gamma);
1315        return noErr;
1316}
1317
1318//-----------------------------------------------------------------
1319// ImageCodecGetCodecInfo
1320//-----------------------------------------------------------------
1321// Your component receives the ImageCodecGetCodecInfo request whenever
1322// an application calls the Image Compression Manager's GetCodecInfo
1323// function.
1324// Your component should return a formatted compressor information
1325// structure defining its capabilities.
1326// Both compressors and decompressors may receive this request.
1327//-----------------------------------------------------------------
1328
1329pascal ComponentResult FFusionCodecGetCodecInfo(FFusionGlobals glob, CodecInfo *info)
1330{
1331        return getPerianCodecInfo(glob->self, glob->componentType, info);
1332}
1333
1334static int FFusionGetBuffer(AVCodecContext *s, AVFrame *pic)
1335{
1336        FFusionGlobals glob = s->opaque;
1337        int ret = avcodec_default_get_buffer(s, pic);
1338        int i;
1339       
1340        if (ret >= 0) {
1341                for (i = 0; i < FFUSION_MAX_BUFFERS; i++) {
1342                        if (!glob->buffers[i].retainCount) {
1343//                              FFusionDebugPrint("%p Starting Buffer %p.\n", glob, &glob->buffers[i]);
1344                                pic->opaque = &glob->buffers[i];
1345                                glob->buffers[i].frame = pic;
1346                                glob->buffers[i].retainCount = 1;
1347                                glob->buffers[i].ffmpegUsing = 1;
1348                                glob->lastAllocatedBuffer = i;
1349                                break;
1350                        }
1351                }
1352        }
1353       
1354        return ret;
1355}
1356
1357static void FFusionReleaseBuffer(AVCodecContext *s, AVFrame *pic)
1358{
1359//      FFusionGlobals glob = s->opaque;
1360        FFusionBuffer *buf = pic->opaque;
1361       
1362        if(buf->ffmpegUsing)
1363        {
1364                buf->ffmpegUsing = 0;
1365                releaseBuffer(s, buf);
1366        }
1367}
1368
1369static FFusionBuffer *retainBuffer(FFusionGlobals glob, FFusionBuffer *buf)
1370{
1371        buf->retainCount++;
1372//      FFusionDebugPrint("%p Retained Buffer %p #%d to %d.\n", glob, buf, buf->frameNumber, buf->retainCount);
1373        return buf;
1374}
1375
1376static void releaseBuffer(AVCodecContext *s, FFusionBuffer *buf)
1377{
1378        buf->retainCount--;
1379//      FFusionGlobals glob = (FFusionGlobals)s->opaque;
1380//      FFusionDebugPrint("%p Released Buffer %p #%d to %d.\n", glob, buf, buf->frameNumber, buf->retainCount);
1381        if(!buf->retainCount && !buf->ffmpegUsing)
1382        {
1383                buf->returnedFrame.data[0] = NULL;
1384                avcodec_default_release_buffer(s, buf->frame);
1385        }
1386}
1387
1388//-----------------------------------------------------------------
1389// FFusionDecompress
1390//-----------------------------------------------------------------
1391// This function calls libavcodec to decompress one frame.
1392//-----------------------------------------------------------------
1393
1394OSErr FFusionDecompress(FFusionGlobals glob, AVCodecContext *context, UInt8 *dataPtr, long width, long height, AVFrame *picture, long length)
1395{
1396    OSErr err = noErr;
1397    int got_picture = false;
1398    int len = 0;
1399       
1400        FFusionDebugPrint("%p Decompress %d bytes.\n", glob, length);
1401    avcodec_get_frame_defaults(picture);
1402       
1403        AVPacket pkt;
1404        av_init_packet(&pkt);
1405        pkt.data = dataPtr;
1406        pkt.size = length;
1407        len = avcodec_decode_video2(context, picture, &got_picture, &pkt);
1408       
1409        if (len < 0)
1410        {           
1411                Codecprintf(glob->fileLog, "Error while decoding frame\n");
1412        } 
1413       
1414        return err;
1415}
Note: See TracBrowser for help on using the repository browser.