source: trunk/ff_MovieImport.c @ 1242

Revision 1242, 18.8 KB checked in by astrange, 5 years ago (diff)

Apply gbooker's patch fixing broken-index detection in the importer.

This allows incomplete AVI files to be fully imported again.

Closes #460

Line 
1/*****************************************************************************
2*
3*  Avi Import Component QuickTime Component Interface
4*
5*  Copyright(C) 2006 Christoph Naegeli <chn1@mac.com>
6*
7*  This library is free software; you can redistribute it and/or
8*  modify it under the terms of the GNU Lesser General Public
9*  License as published by the Free Software Foundation; either
10*  version 2.1 of the License, or (at your option) any later version.
11
12*  This library is distributed in the hope that it will be useful,
13*  but WITHOUT ANY WARRANTY; without even the implied warranty of
14*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15*  Lesser General Public License for more details.
16
17*  You should have received a copy of the GNU Lesser General Public
18*  License along with this library; if not, write to the Free Software
19*  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20*
21****************************************************************************/
22
23#include "ff_MovieImportVersion.h"
24#include "ff_private.h"
25#include "Codecprintf.h"
26#include "riff.h"
27#include "SubImport.h"
28#include "CommonUtils.h"
29
30/* This one is a little big in ffmpeg and private anyway */
31#define PROBE_BUF_SIZE 64
32
33#include <CoreServices/CoreServices.h>
34#include <QuickTime/QuickTime.h>
35#include <pthread.h>
36
37#define MOVIEIMPORT_BASENAME()          FFAvi_MovieImport
38#define MOVIEIMPORT_GLOBALS()           ff_global_ptr storage
39
40#define CALLCOMPONENT_BASENAME()        MOVIEIMPORT_BASENAME()
41#define CALLCOMPONENT_GLOBALS()         MOVIEIMPORT_GLOBALS()
42
43#define COMPONENT_DISPATCH_FILE         "ff_MovieImportDispatch.h"
44#define COMPONENT_UPP_SELECT_ROOT()     MovieImport
45
46#include <CoreServices/Components.k.h>
47#include <QuickTime/QuickTimeComponents.k.h>
48#include <QuickTime/ComponentDispatchHelper.c>
49
50#pragma mark -
51
52static int PerianLockMgrCallback(void **mutex, enum AVLockOp op)
53{
54        pthread_mutex_t **m = (pthread_mutex_t **)mutex;
55        int ret = 0;
56       
57        switch (op) {
58                case AV_LOCK_CREATE:
59                        *m = malloc(sizeof(pthread_mutex_t));
60                        ret = pthread_mutex_init(*m, NULL);
61                        break;
62                case AV_LOCK_OBTAIN:
63                        ret = pthread_mutex_lock(*m);
64                        break;
65                case AV_LOCK_RELEASE:
66                        ret = pthread_mutex_unlock(*m);
67                        break;
68                case AV_LOCK_DESTROY:
69                        ret = pthread_mutex_destroy(*m);
70                        free(*m);
71        }
72       
73        return ret;
74}
75
76#define REGISTER_MUXER(x) { \
77        extern AVOutputFormat x##_muxer; \
78                av_register_output_format(&x##_muxer); }
79#define REGISTER_DEMUXER(x) { \
80        extern AVInputFormat x##_demuxer; \
81                av_register_input_format(&x##_demuxer); }
82#define REGISTER_MUXDEMUX(x)  REGISTER_MUXER(x); REGISTER_DEMUXER(x)
83#define REGISTER_PROTOCOL(x) { \
84        extern URLProtocol x##_protocol; \
85                register_protocol(&x##_protocol); }
86
87#define REGISTER_ENCODER(x) { \
88        extern AVCodec x##_encoder; \
89                register_avcodec(&x##_encoder); }
90#define REGISTER_DECODER(x) { \
91        extern AVCodec x##_decoder; \
92                avcodec_register(&x##_decoder); }
93#define REGISTER_ENCDEC(x)  REGISTER_ENCODER(x); REGISTER_DECODER(x)
94
95#define REGISTER_PARSER(x) { \
96        extern AVCodecParser x##_parser; \
97                av_register_codec_parser(&x##_parser); }
98#define REGISTER_BSF(x) { \
99        extern AVBitStreamFilter x##_bsf; \
100                av_register_bitstream_filter(&x##_bsf); }
101
102void init_FFmpeg()
103{
104        /* This one is used because Global variables are initialized ONE time
105        * until the application quits. Thus, we have to make sure we're initialize
106        * the libavformat only once or we get an endlos loop when registering the same
107        * element twice!! */
108        static Boolean inited = FALSE;
109        int unlock = PerianInitEnter(&inited);
110       
111        /* Register the Parser of ffmpeg, needed because we do no proper setup of the libraries */
112        if(!inited) {
113                inited = TRUE;
114                avcodec_init();
115                av_lockmgr_register(PerianLockMgrCallback);
116
117                REGISTER_DEMUXER(avi);
118                REGISTER_DEMUXER(flv);
119                REGISTER_DEMUXER(tta);
120                REGISTER_DEMUXER(nuv);
121                register_parsers();
122               
123                REGISTER_DECODER(msmpeg4v1);
124                REGISTER_DECODER(msmpeg4v2);
125                REGISTER_DECODER(msmpeg4v3);
126                REGISTER_DECODER(mpeg4);
127                REGISTER_DECODER(h264);
128                REGISTER_DECODER(flv);
129                REGISTER_DECODER(flashsv);
130                REGISTER_DECODER(vp3);
131                REGISTER_DECODER(vp6);
132                REGISTER_DECODER(vp6f);
133                REGISTER_DECODER(h263i);
134                REGISTER_DECODER(huffyuv);
135                REGISTER_DECODER(ffvhuff);
136                REGISTER_DECODER(mpeg1video);
137                REGISTER_DECODER(mpeg2video);
138                REGISTER_DECODER(fraps);
139                REGISTER_DECODER(snow);
140                REGISTER_DECODER(nuv);
141               
142                REGISTER_DECODER(wmav1);
143                REGISTER_DECODER(wmav2);
144                REGISTER_DECODER(adpcm_swf);
145                REGISTER_DECODER(vorbis);
146                REGISTER_DECODER(mp1);
147                REGISTER_DECODER(mp2);
148                REGISTER_DECODER(tta);
149                REGISTER_DECODER(dca);
150                REGISTER_DECODER(nellymoser);
151               
152                REGISTER_DECODER(dvdsub);
153                REGISTER_DECODER(tscc);
154                REGISTER_DECODER(vp6a);
155                REGISTER_DECODER(zmbv);
156                REGISTER_DECODER(indeo2);
157                REGISTER_DECODER(indeo3);
158               
159                av_log_set_callback(FFMpegCodecprintf);
160        }
161       
162        PerianInitExit(unlock);
163}
164
165/************************************
166** Base Component Manager Routines **
167*************************************/
168
169ComponentResult FFAvi_MovieImportOpen(ff_global_ptr storage, ComponentInstance self)
170{
171        ComponentResult result;
172    ComponentDescription descout;
173       
174        /* Check for Mac OS 10.4 & QT 7 */
175        result = check_system();
176        require_noerr(result,bail);
177       
178    GetComponentInfo((Component)self, &descout, 0, 0, 0);
179       
180        storage = malloc(sizeof(ff_global_context));
181        if(!storage) goto bail;
182       
183        memset(storage, 0, sizeof(ff_global_context));
184        storage->ci = self;
185       
186        SetComponentInstanceStorage(storage->ci, (Handle)storage);
187       
188        storage->componentType = descout.componentSubType;
189        storage->movieLoadState = kMovieLoadStateLoading;
190bail:
191                return result;
192} /* FFAvi_MovieImportOpen() */
193
194ComponentResult FFAvi_MovieImportClose(ff_global_ptr storage, ComponentInstance self)
195{
196        /* Free all global storage */
197        if(storage->imgHdl)
198                DisposeHandle((Handle)storage->imgHdl);
199        if(storage->sndHdl)
200                DisposeHandle((Handle)storage->sndHdl);
201       
202        if(storage->stream_map)
203                av_free(storage->stream_map);
204       
205        if(storage->format_context)
206                av_close_input_file(storage->format_context);
207       
208        int i;
209        for(i=0; i<MAX_STREAMS; i++)
210        {
211                if(storage->firstFrames[i].data != NULL)
212                        av_free_packet(storage->firstFrames + i);
213        }
214       
215        if(storage)
216                free(storage);
217       
218        return noErr;
219} /* FFAvi_MovieImportClose() */
220
221ComponentResult FFAvi_MovieImportVersion(ff_global_ptr storage)
222{
223        return kFFAviComponentVersion;
224} /* FFAvi_MovieImportVersion() */
225
226#pragma mark -
227
228/********************************
229** Configuration Stuff is here **
230*********************************/
231
232ComponentResult FFAvi_MovieImportGetMIMETypeList(ff_global_ptr storage, QTAtomContainer *mimeInfo)
233{
234        ComponentResult err = noErr;
235        switch (storage->componentType) {
236                case 'VfW ':
237                case 'VFW ':
238                case 'AVI ':
239                        err = GetComponentResource((Component)storage->ci, 'mime', kAVIthngResID, (Handle*)mimeInfo);
240                        break;
241                case 'FLV ':
242                        err = GetComponentResource((Component)storage->ci, 'mime', kFLVthngResID, (Handle*)mimeInfo);
243                        break;
244                case 'TTA ':
245                        err = GetComponentResource((Component)storage->ci, 'mime', kTTAthngResID, (Handle*)mimeInfo);
246                        break;
247                case 'NUV ':
248                        err = GetComponentResource((Component)storage->ci, 'mime', kNuvthngResID, (Handle*)mimeInfo);
249                        break;
250                default:
251                        err = GetComponentResource((Component)storage->ci, 'mime', kAVIthngResID, (Handle*)mimeInfo);
252                        break;
253        }
254        return err;
255} /* FFAvi_MovieImportGetMIMETypeList() */
256
257ComponentResult FFAvi_MovieImportSetProgressProc(ff_global_ptr storage, MovieProgressUPP prog, long refcon)
258{
259        storage->prog = prog;
260        storage->refcon = refcon;
261       
262        if(!storage->prog)
263                storage->refcon = 0;
264       
265        return noErr;
266} /* FFAvi_MovieImportSetProgressProc() */
267
268ComponentResult FFAvi_MovieImportSetSampleDescription(ff_global_ptr storage, SampleDescriptionHandle desc, OSType media)
269{
270        ComponentResult result = noErr;
271       
272        switch(media) {
273                case VideoMediaType:
274                        if(storage->imgHdl) /* already one stored */
275                                DisposeHandle((Handle)storage->imgHdl);
276                       
277                        storage->imgHdl = (ImageDescriptionHandle)desc;
278                        if(storage->imgHdl)
279                                result = HandToHand((Handle*)&storage->imgHdl);
280                                break;
281                case SoundMediaType:
282                        if(storage->sndHdl)
283                                DisposeHandle((Handle)storage->sndHdl);
284                       
285                        storage->sndHdl = (SoundDescriptionHandle)desc;
286                        if(storage->sndHdl)
287                                result = HandToHand((Handle*)&storage->sndHdl);
288                                break;
289                default:
290                        break;
291        }
292       
293        return result;
294} /* FFAvi_MovieImportSetSampleDescription() */
295
296ComponentResult FFAvi_MovieImportGetDestinationMediaType(ff_global_ptr storage, OSType *mediaType)
297{
298        *mediaType = VideoMediaType;
299        return noErr;
300} /* FFAvi_MovieImportGetDestinationMediaType() */
301
302#pragma mark -
303
304/****************************
305** Import Stuff comes here **
306*****************************/
307
308ComponentResult FFAvi_MovieImportValidate(ff_global_ptr storage, const FSSpec *theFile, Handle theData, Boolean *valid)
309{
310        ComponentResult result = noErr;
311        Handle dataRef = NULL;
312        OSType dataRefType;
313       
314        *valid = FALSE;
315       
316        result = QTNewDataReferenceFromFSSpec(theFile, 0, &dataRef, &dataRefType);
317        require_noerr(result,bail);
318       
319        result = MovieImportValidateDataRef(storage->ci, dataRef, dataRefType, (UInt8*)valid);
320       
321bail:
322                if(dataRef)
323                        DisposeHandle(dataRef);
324       
325        return result;
326} /* FFAvi_MovieImportValidate() */
327
328// this function is a small avi parser to get the video track's fourcc as
329// fast as possible, so we can decide whether we can handle the necessary
330// image description extensions for the format in ValidateDataRef() quickly
331OSType get_avi_strf_fourcc(ByteIOContext *pb)
332{
333        OSType tag, subtag;
334        unsigned int size;
335       
336        if (get_be32(pb) != 'RIFF')
337                return 0;
338       
339        // file size
340        get_le32(pb);
341       
342        if (get_be32(pb) != 'AVI ')
343                return 0;
344       
345        while (!url_feof(pb)) {
346                tag = get_be32(pb);
347                size = get_le32(pb);
348               
349                if (tag == 'LIST') {
350                        subtag = get_be32(pb);
351                       
352                        // only lists we care about: hdrl & strl, so skip the rest
353                        if (subtag != 'hdrl' && subtag != 'strl')
354                                url_fskip(pb, size - 4 + (size & 1));
355                       
356                } else if (tag == 'strf') {
357                        // 16-byte offset to the fourcc
358                        url_fskip(pb, 16);
359                        return get_be32(pb);
360                } else if (tag == 'strh'){
361                        // 4-byte offset to the fourcc
362                        OSType tag1 = get_be32(pb);
363                        if(tag1 == 'iavs' || tag1 == 'ivas')
364                                return get_be32(pb);
365                        else
366                                url_fskip(pb, size + (size & 1) - 4);
367                } else
368                        url_fskip(pb, size + (size & 1));
369        }
370        return 0;
371}
372
373ComponentResult FFAvi_MovieImportValidateDataRef(ff_global_ptr storage, Handle dataRef, OSType dataRefType, UInt8 *valid)
374{
375        ComponentResult result = noErr;
376        DataHandler dataHandler = NULL;
377        uint8_t buf[PROBE_BUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE] = {0};
378        AVProbeData pd;
379        ByteIOContext *byteContext;
380
381        /* default */
382        *valid = 0;
383       
384        /* Get a data handle and read a probe of data */
385        result = OpenADataHandler(dataRef, dataRefType, NULL, 0, NULL, kDataHCanRead, &dataHandler);
386        if(result || !dataHandler) goto bail;
387       
388        pd.buf = buf;
389        pd.buf_size = PROBE_BUF_SIZE;
390       
391        result = DataHScheduleData(dataHandler, (Ptr)pd.buf, 0, PROBE_BUF_SIZE, 0, NULL, NULL);
392        require_noerr(result,bail);
393       
394        init_FFmpeg();
395        storage->format = av_probe_input_format(&pd, 1);
396        if(storage->format != NULL) {
397                *valid = 255; /* This means we can read the data */
398               
399                /* we don't do MJPEG's Huffman tables right yet, and DV video seems to have
400                an aspect ratio coded in the bitstream that ffmpeg doesn't read, so let Apple's
401                AVI importer handle AVIs with these two video types */
402                if (IS_AVI(storage->componentType)) {
403                        /* Prepare the iocontext structure */
404                        result = url_open_dataref(&byteContext, dataRef, dataRefType, NULL, NULL, NULL);
405                        require_noerr(result, bail);
406                       
407                        OSType fourcc = get_avi_strf_fourcc(byteContext);
408                        enum CodecID id = ff_codec_get_id(ff_codec_bmp_tags, BSWAP(fourcc));
409                       
410                        if (id == CODEC_ID_MJPEG || id == CODEC_ID_DVVIDEO || id == CODEC_ID_RAWVIDEO || id == CODEC_ID_MSVIDEO1 || id == CODEC_ID_MSRLE)
411                                *valid = 0;
412                       
413                        url_fclose(byteContext);
414                }
415        }
416               
417bail:
418                if(dataHandler)
419                        CloseComponent(dataHandler);
420       
421        return result;
422} /* FFAvi_MovieImportValidateDataRef() */
423
424
425ComponentResult FFAvi_MovieImportFile(ff_global_ptr storage, const FSSpec *theFile, Movie theMovie, Track targetTrack,
426                                                                          Track *usedTrack, TimeValue atTime, TimeValue *addedDuration, long inFlags, long *outFlags)
427{
428        ComponentResult result;
429        Handle dataRef = NULL;
430        OSType dataRefType;
431        FSRef theFileFSRef;
432       
433        *outFlags = 0;
434       
435        result = QTNewDataReferenceFromFSSpec(theFile, 0, &dataRef, &dataRefType);
436        require_noerr(result,bail);
437       
438        result = MovieImportDataRef(storage->ci, dataRef, dataRefType, theMovie, targetTrack, usedTrack, atTime, addedDuration,
439                                                                inFlags, outFlags);
440        require_noerr(result, bail);
441       
442        result = FSpMakeFSRef(theFile, &theFileFSRef);
443        require_noerr(result, bail);
444               
445bail:
446                if(dataRef)
447                        DisposeHandle(dataRef);
448       
449        return result;
450} /* FFAvi_MovieImportFile() */
451
452ComponentResult FFAvi_MovieImportDataRef(ff_global_ptr storage, Handle dataRef, OSType dataRefType, Movie theMovie, Track targetTrack,
453                                                                                 Track *usedTrack, TimeValue atTime, TimeValue *addedDuration, long inFlags, long *outFlags)
454{
455        ComponentResult result = noErr;
456        ByteIOContext *byteContext;
457        AVFormatContext *ic = NULL;
458        AVFormatParameters params;
459        OSType mediaType;
460        Media media;
461        int count, hadIndex, j;
462               
463        /* make sure that in case of error, the flag movieImportResultComplete is not set */
464        *outFlags = 0;
465       
466        /* probe the format first */
467        UInt8 valid = 0;
468        FFAvi_MovieImportValidateDataRef(storage, dataRef, dataRefType, &valid);
469        if(valid != 255)
470                goto bail;
471                       
472        /* Prepare the iocontext structure */
473        result = url_open_dataref(&byteContext, dataRef, dataRefType, &storage->dataHandler, &storage->dataHandlerSupportsWideOffsets, &storage->dataSize);
474        storage->isStreamed = dataRefType == URLDataHandlerSubType;
475        require_noerr(result, bail);
476       
477        /* Open the Format Context */
478        memset(&params, 0, sizeof(params));
479        result = av_open_input_stream(&ic, byteContext, "", storage->format, &params);
480        require_noerr(result,bail);
481        storage->format_context = ic;
482       
483        // AVIs without an index currently add a few entries to the index so it can
484        // determine codec parameters.  Check for index existence here before it
485        // reads any packets.
486        hadIndex = 1;
487        for (j = 0; j < ic->nb_streams; j++) {
488                if (ic->streams[j]->nb_index_entries <= 1)
489                {
490                        hadIndex = 0;
491                        break;
492                }
493        }
494       
495        /* Get the Stream Infos if not already read */
496        result = av_find_stream_info(ic);
497       
498        // -1 means it couldn't understand at least one stream
499        // which might just mean we don't have its video decoder enabled
500        if(result < 0 && result != -1)
501                goto bail;
502       
503        // we couldn't find any streams, bail with an error.
504        if(ic->nb_streams == 0) {
505                result = -1; //is there a more appropriate error code?
506                goto bail;
507        }
508       
509        //determine a header offset (needed by index-based import).
510        result = determine_header_offset(storage);
511        if(result < 0)
512                goto bail;
513       
514        /* Initialize the Movie */
515        storage->movie = theMovie;
516        if(inFlags & movieImportMustUseTrack) {
517                storage->map_count = 1;
518                prepare_track(storage, targetTrack, dataRef, dataRefType);
519        } else {
520                storage->map_count = ic->nb_streams;
521                result = prepare_movie(storage, theMovie, dataRef, dataRefType);
522                if (result != 0)
523                        goto bail;
524        }
525       
526        /* replace the SampleDescription if user called MovieImportSetSampleDescription() */
527        if(storage->imgHdl) {
528                for(j = 0; j < storage->map_count; j++) {
529                        NCStream ncstream = storage->stream_map[j];
530                        GetMediaHandlerDescription(ncstream.media, &mediaType, NULL, NULL);
531                        if(mediaType == VideoMediaType && ncstream.sampleHdl) {
532                                DisposeHandle((Handle)ncstream.sampleHdl);
533                                ncstream.sampleHdl = (SampleDescriptionHandle)storage->imgHdl;
534                        }
535                }
536        }
537        if(storage->sndHdl) {
538                for(j = 0; j < storage->map_count; j++) {
539                        NCStream ncstream = storage->stream_map[j];
540                        GetMediaHandlerDescription(ncstream.media, &mediaType, NULL, NULL);
541                        if(mediaType == SoundMediaType && ncstream.sampleHdl) {
542                                DisposeHandle((Handle)ncstream.sampleHdl);
543                                ncstream.sampleHdl = (SampleDescriptionHandle)storage->sndHdl;
544                        }
545                }
546        }
547       
548        count = 0; media = NULL;
549        for(j = 0; j < storage->map_count; j++) {
550                media = storage->stream_map[j].media;
551                if(media)
552                        count++;
553        }
554       
555        if(count > 1)
556                *outFlags |= movieImportResultUsedMultipleTracks;
557       
558        /* The usedTrack parameter. Count the number of Tracks and set usedTrack if we operated
559                * on a single track. Note that this requires the media to be set by track counting above*/
560        if(usedTrack && count == 1 && media)
561                *usedTrack = GetMediaTrack(media);
562       
563        result = noErr;
564
565        *addedDuration = 0;
566       
567        //attempt to import using indexes.
568        result = import_using_index(storage, &hadIndex, addedDuration);
569        require_noerr(result, bail);
570       
571        if(hadIndex) {
572                //file had an index and was imported; we are done.
573                *outFlags |= movieImportResultComplete;
574               
575        } else if(inFlags & movieImportWithIdle) {
576                if(addedDuration && ic->duration > 0) {
577                        TimeScale movieTimeScale = GetMovieTimeScale(theMovie);
578                        *addedDuration = movieTimeScale * ic->duration / AV_TIME_BASE;
579                       
580                        //create a placeholder track so that progress displays correctly.
581                        create_placeholder_track(storage->movie, &storage->placeholderTrack, *addedDuration, dataRef, dataRefType);
582                       
583                        //give the data handler a hint as to how fast we need the data.
584                        //suggest a speed that's faster than the bare minimum.
585                        //if there's an error, the data handler probably doesn't support
586                        //this, so we can just ignore.
587                        DataHPlaybackHints(storage->dataHandler, 0, 0, -1, (storage->dataSize * 1.15) / ((double)ic->duration / AV_TIME_BASE));
588                }
589                       
590                //import with idle. Decode a little bit of data now.
591                import_with_idle(storage, inFlags, outFlags, 10, 300, true);
592        } else {
593                //QuickTime didn't request import with idle, so do it all now.
594                import_with_idle(storage, inFlags, outFlags, 0, 0, true);                       
595        }
596       
597        LoadExternalSubtitlesFromFileDataRef(dataRef, dataRefType, theMovie);
598
599bail:
600        if(result == noErr)
601                storage->movieLoadState = kMovieLoadStateLoaded;
602        else
603                storage->movieLoadState = kMovieLoadStateError;
604               
605        if (result == -1)
606                result = invalidMovie; // a bit better error message
607       
608        return result;
609} /* FFAvi_MovieImportDataRef */
610
611ComponentResult FFAvi_MovieImportSetIdleManager(ff_global_ptr storage, IdleManager im) {
612        storage->idleManager = im;
613        return noErr;
614}
615
616ComponentResult FFAvi_MovieImportIdle(ff_global_ptr storage, long inFlags, long *outFlags) {
617        ComponentResult err = noErr;
618        TimeValue currentIdleTime = GetMovieTime(storage->movie, NULL);
619        TimeScale movieTimeScale = GetMovieTimeScale(storage->movie);
620        int addSamples = false;
621       
622        storage->idlesSinceLastAdd++;
623       
624        if (currentIdleTime == storage->lastIdleTime && storage->idlesSinceLastAdd > 5 || 
625                storage->loadedTime < currentIdleTime + 5*movieTimeScale)
626        {
627                storage->idlesSinceLastAdd = 0;
628                addSamples = true;
629        }
630       
631        err = import_with_idle(storage, inFlags | movieImportWithIdle, outFlags, 0, 1000, addSamples);
632       
633        storage->lastIdleTime = currentIdleTime;
634        return err;
635}
636
637ComponentResult FFAvi_MovieImportGetLoadState(ff_global_ptr storage, long *importerLoadState) {
638        *importerLoadState = storage->movieLoadState;
639        return(noErr);
640}
641
642ComponentResult FFAvi_MovieImportGetMaxLoadedTime(ff_global_ptr storage, TimeValue *time) {
643        *time = storage->loadedTime;
644        return(noErr);
645}
Note: See TracBrowser for help on using the repository browser.