source: trunk/FFusionCodec.c @ 1472

Revision 1472, 48.8 KB checked in by astrange, 2 years ago (diff)

Xcode project cleanup and Lion SDK updates

  • Update ACPublic/PublicUtility to Lion version.

API changed in various ways. Rather than copy the files
I've just included them from the Developer directory,
which may turn out to be a bad idea.
Removed use of ACSimpleCodec along the way.

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