source: trunk/ff_MovieImport.c @ 1427

Revision 1427, 15.7 KB checked in by astrange, 3 years ago (diff)

Add mcfg resources for the AVI importer and polish rez

  • Some whitespace changes
  • Less preprocessing
  • Add names to resources for better DeRez?

The mcfg don't seem to show up anywhere...

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#include "FFmpegUtils.h"
30
31/* This one is a little big in ffmpeg and private anyway */
32#define PROBE_BUF_SIZE 64
33
34#include <CoreServices/CoreServices.h>
35#include <QuickTime/QuickTime.h>
36#include <pthread.h>
37
38#define MOVIEIMPORT_BASENAME()          FFAvi_MovieImport
39#define MOVIEIMPORT_GLOBALS()           ff_global_ptr storage
40
41#define CALLCOMPONENT_BASENAME()        MOVIEIMPORT_BASENAME()
42#define CALLCOMPONENT_GLOBALS()         MOVIEIMPORT_GLOBALS()
43
44#define COMPONENT_DISPATCH_FILE         "ff_MovieImportDispatch.h"
45#define COMPONENT_UPP_SELECT_ROOT()     MovieImport
46
47#include <CoreServices/Components.k.h>
48#include <QuickTime/QuickTimeComponents.k.h>
49#include <QuickTime/ComponentDispatchHelper.c>
50
51#pragma mark -
52
53/************************************
54** Base Component Manager Routines **
55*************************************/
56
57ComponentResult FFAvi_MovieImportOpen(ff_global_ptr storage, ComponentInstance self)
58{
59        ComponentResult result = noErr;
60    ComponentDescription descout;
61       
62    GetComponentInfo((Component)self, &descout, 0, 0, 0);
63       
64        storage = malloc(sizeof(ff_global_context));
65        if(!storage) goto bail;
66       
67        memset(storage, 0, sizeof(ff_global_context));
68        storage->ci = self;
69       
70        SetComponentInstanceStorage(storage->ci, (Handle)storage);
71       
72        storage->componentType = descout.componentSubType;
73        storage->movieLoadState = kMovieLoadStateLoading;
74bail:
75                return result;
76} /* FFAvi_MovieImportOpen() */
77
78ComponentResult FFAvi_MovieImportClose(ff_global_ptr storage, ComponentInstance self)
79{
80        /* Free all global storage */
81        if(storage->imgHdl)
82                DisposeHandle((Handle)storage->imgHdl);
83        if(storage->sndHdl)
84                DisposeHandle((Handle)storage->sndHdl);
85       
86        if(storage->stream_map)
87                av_free(storage->stream_map);
88       
89        if(storage->format_context)
90                av_close_input_file(storage->format_context);
91       
92        int i;
93        for(i=0; i<MAX_STREAMS; i++)
94        {
95                if(storage->firstFrames[i].data != NULL)
96                        av_free_packet(storage->firstFrames + i);
97        }
98       
99        if(storage)
100                free(storage);
101       
102        return noErr;
103} /* FFAvi_MovieImportClose() */
104
105ComponentResult FFAvi_MovieImportVersion(ff_global_ptr storage)
106{
107        return kFFAviComponentVersion;
108} /* FFAvi_MovieImportVersion() */
109
110#pragma mark -
111
112/********************************
113** Configuration Stuff is here **
114*********************************/
115
116ComponentResult FFAvi_MovieImportGetMIMETypeList(ff_global_ptr storage, QTAtomContainer *mimeInfo)
117{
118        ComponentResult err = noErr;
119        switch (storage->componentType) {
120                case 'VfW ':
121                case 'VFW ':
122                case 'AVI ':
123                        err = GetComponentResource((Component)storage->ci, 'mime', kAVIthngResID, (Handle*)mimeInfo);
124                        break;
125                case 'FLV ':
126                        err = GetComponentResource((Component)storage->ci, 'mime', kFLVthngResID, (Handle*)mimeInfo);
127                        break;
128                case 'TTA ':
129                        err = GetComponentResource((Component)storage->ci, 'mime', kTTAthngResID, (Handle*)mimeInfo);
130                        break;
131                case 'NUV ':
132                        err = GetComponentResource((Component)storage->ci, 'mime', kNUVthngResID, (Handle*)mimeInfo);
133                        break;
134                default:
135                        err = GetComponentResource((Component)storage->ci, 'mime', kAVIthngResID, (Handle*)mimeInfo);
136                        break;
137        }
138        return err;
139} /* FFAvi_MovieImportGetMIMETypeList() */
140
141ComponentResult FFAvi_MovieImportSetProgressProc(ff_global_ptr storage, MovieProgressUPP prog, long refcon)
142{
143        storage->prog = prog;
144        storage->refcon = refcon;
145       
146        if(!storage->prog)
147                storage->refcon = 0;
148       
149        return noErr;
150} /* FFAvi_MovieImportSetProgressProc() */
151
152ComponentResult FFAvi_MovieImportSetSampleDescription(ff_global_ptr storage, SampleDescriptionHandle desc, OSType media)
153{
154        ComponentResult result = noErr;
155       
156        switch(media) {
157                case VideoMediaType:
158                        if(storage->imgHdl) /* already one stored */
159                                DisposeHandle((Handle)storage->imgHdl);
160                       
161                        storage->imgHdl = (ImageDescriptionHandle)desc;
162                        if(storage->imgHdl)
163                                result = HandToHand((Handle*)&storage->imgHdl);
164                                break;
165                case SoundMediaType:
166                        if(storage->sndHdl)
167                                DisposeHandle((Handle)storage->sndHdl);
168                       
169                        storage->sndHdl = (SoundDescriptionHandle)desc;
170                        if(storage->sndHdl)
171                                result = HandToHand((Handle*)&storage->sndHdl);
172                                break;
173                default:
174                        break;
175        }
176       
177        return result;
178} /* FFAvi_MovieImportSetSampleDescription() */
179
180ComponentResult FFAvi_MovieImportGetDestinationMediaType(ff_global_ptr storage, OSType *mediaType)
181{
182        *mediaType = VideoMediaType;
183        return noErr;
184} /* FFAvi_MovieImportGetDestinationMediaType() */
185
186#pragma mark -
187
188/****************************
189** Import Stuff comes here **
190*****************************/
191
192ComponentResult FFAvi_MovieImportValidate(ff_global_ptr storage, const FSSpec *theFile, Handle theData, Boolean *valid)
193{
194        ComponentResult result = noErr;
195        Handle dataRef = NULL;
196        OSType dataRefType;
197       
198        *valid = FALSE;
199       
200        result = QTNewDataReferenceFromFSSpec(theFile, 0, &dataRef, &dataRefType);
201        require_noerr(result,bail);
202       
203        result = MovieImportValidateDataRef(storage->ci, dataRef, dataRefType, (UInt8*)valid);
204       
205bail:
206                if(dataRef)
207                        DisposeHandle(dataRef);
208       
209        return result;
210} /* FFAvi_MovieImportValidate() */
211
212// this function is a small avi parser to get the video track's fourcc as
213// fast as possible, so we can decide whether we can handle the necessary
214// image description extensions for the format in ValidateDataRef() quickly
215OSType get_avi_strf_fourcc(ByteIOContext *pb)
216{
217        OSType tag, subtag;
218        unsigned int size;
219       
220        if (get_be32(pb) != 'RIFF')
221                return 0;
222       
223        // file size
224        get_le32(pb);
225       
226        if (get_be32(pb) != 'AVI ')
227                return 0;
228       
229        while (!url_feof(pb)) {
230                tag = get_be32(pb);
231                size = get_le32(pb);
232               
233                if (tag == 'LIST') {
234                        subtag = get_be32(pb);
235                       
236                        // only lists we care about: hdrl & strl, so skip the rest
237                        if (subtag != 'hdrl' && subtag != 'strl')
238                                url_fskip(pb, size - 4 + (size & 1));
239                       
240                } else if (tag == 'strf') {
241                        // 16-byte offset to the fourcc
242                        url_fskip(pb, 16);
243                        return get_be32(pb);
244                } else if (tag == 'strh'){
245                        // 4-byte offset to the fourcc
246                        OSType tag1 = get_be32(pb);
247                        if(tag1 == 'iavs' || tag1 == 'ivas')
248                                return get_be32(pb);
249                        else
250                                url_fskip(pb, size + (size & 1) - 4);
251                } else
252                        url_fskip(pb, size + (size & 1));
253        }
254        return 0;
255}
256
257ComponentResult FFAvi_MovieImportValidateDataRef(ff_global_ptr storage, Handle dataRef, OSType dataRefType, UInt8 *valid)
258{
259        ComponentResult result = noErr;
260        DataHandler dataHandler = NULL;
261        uint8_t buf[PROBE_BUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE] = {0};
262        AVProbeData pd;
263        ByteIOContext *byteContext;
264
265        /* default */
266        *valid = 0;
267       
268        /* Get a data handle and read a probe of data */
269        result = OpenADataHandler(dataRef, dataRefType, NULL, 0, NULL, kDataHCanRead, &dataHandler);
270        if(result || !dataHandler) goto bail;
271       
272        pd.buf = buf;
273        pd.buf_size = PROBE_BUF_SIZE;
274       
275        result = DataHScheduleData(dataHandler, (Ptr)pd.buf, 0, PROBE_BUF_SIZE, 0, NULL, NULL);
276        require_noerr(result,bail);
277       
278        FFInitFFmpeg();
279        storage->format = av_probe_input_format(&pd, 1);
280        if(storage->format != NULL) {
281                *valid = 255; /* This means we can read the data */
282               
283                /* we don't do MJPEG's Huffman tables right yet, and DV video seems to have
284                an aspect ratio coded in the bitstream that ffmpeg doesn't read, so let Apple's
285                AVI importer handle AVIs with these two video types */
286                if (IS_AVI(storage->componentType)) {
287                        /* Prepare the iocontext structure */
288                        result = url_open_dataref(&byteContext, dataRef, dataRefType, NULL, NULL, NULL);
289                        require_noerr(result, bail);
290                       
291                        OSType fourcc = get_avi_strf_fourcc(byteContext);
292                        enum CodecID id = ff_codec_get_id(ff_codec_bmp_tags, BSWAP(fourcc));
293                       
294                        if (id == CODEC_ID_MJPEG || id == CODEC_ID_DVVIDEO || id == CODEC_ID_RAWVIDEO || id == CODEC_ID_MSVIDEO1 || id == CODEC_ID_MSRLE)
295                                *valid = 0;
296                       
297                        url_fclose(byteContext);
298                }
299        }
300               
301bail:
302                if(dataHandler)
303                        CloseComponent(dataHandler);
304       
305        return result;
306} /* FFAvi_MovieImportValidateDataRef() */
307
308
309ComponentResult FFAvi_MovieImportFile(ff_global_ptr storage, const FSSpec *theFile, Movie theMovie, Track targetTrack,
310                                                                          Track *usedTrack, TimeValue atTime, TimeValue *addedDuration, long inFlags, long *outFlags)
311{
312        ComponentResult result;
313        Handle dataRef = NULL;
314        OSType dataRefType;
315        FSRef theFileFSRef;
316       
317        *outFlags = 0;
318       
319        result = QTNewDataReferenceFromFSSpec(theFile, 0, &dataRef, &dataRefType);
320        require_noerr(result,bail);
321       
322        result = MovieImportDataRef(storage->ci, dataRef, dataRefType, theMovie, targetTrack, usedTrack, atTime, addedDuration,
323                                                                inFlags, outFlags);
324        require_noerr(result, bail);
325       
326        result = FSpMakeFSRef(theFile, &theFileFSRef);
327        require_noerr(result, bail);
328               
329bail:
330                if(dataRef)
331                        DisposeHandle(dataRef);
332       
333        return result;
334} /* FFAvi_MovieImportFile() */
335
336ComponentResult FFAvi_MovieImportDataRef(ff_global_ptr storage, Handle dataRef, OSType dataRefType, Movie theMovie, Track targetTrack,
337                                                                                 Track *usedTrack, TimeValue atTime, TimeValue *addedDuration, long inFlags, long *outFlags)
338{
339        ComponentResult result = noErr;
340        ByteIOContext *byteContext;
341        AVFormatContext *ic = NULL;
342        AVFormatParameters params;
343        OSType mediaType;
344        Media media;
345        int count, hadIndex, j;
346               
347        /* make sure that in case of error, the flag movieImportResultComplete is not set */
348        *outFlags = 0;
349       
350        /* probe the format first */
351        UInt8 valid = 0;
352        FFAvi_MovieImportValidateDataRef(storage, dataRef, dataRefType, &valid);
353        if(valid != 255)
354                goto bail;
355                       
356        /* Prepare the iocontext structure */
357        result = url_open_dataref(&byteContext, dataRef, dataRefType, &storage->dataHandler, &storage->dataHandlerSupportsWideOffsets, &storage->dataSize);
358        storage->isStreamed = dataRefType == URLDataHandlerSubType;
359        require_noerr(result, bail);
360       
361        /* Open the Format Context */
362        memset(&params, 0, sizeof(params));
363        result = av_open_input_stream(&ic, byteContext, "", storage->format, &params);
364        require_noerr(result,bail);
365        storage->format_context = ic;
366       
367        // AVIs without an index currently add a few entries to the index so it can
368        // determine codec parameters.  Check for index existence here before it
369        // reads any packets.
370        hadIndex = 1;
371        for (j = 0; j < ic->nb_streams; j++) {
372                if (ic->streams[j]->nb_index_entries <= 1)
373                {
374                        hadIndex = 0;
375                        break;
376                }
377        }
378       
379        /* Get the Stream Infos if not already read */
380        result = av_find_stream_info(ic);
381       
382        // -1 means it couldn't understand at least one stream
383        // which might just mean we don't have its video decoder enabled
384        if(result < 0 && result != -1)
385                goto bail;
386       
387        // we couldn't find any streams, bail with an error.
388        if(ic->nb_streams == 0) {
389                result = -1; //is there a more appropriate error code?
390                goto bail;
391        }
392       
393        //determine a header offset (needed by index-based import).
394        result = determine_header_offset(storage);
395        if(result < 0)
396                goto bail;
397       
398        /* Initialize the Movie */
399        storage->movie = theMovie;
400        if(inFlags & movieImportMustUseTrack) {
401                storage->map_count = 1;
402                prepare_track(storage, targetTrack, dataRef, dataRefType);
403        } else {
404                storage->map_count = ic->nb_streams;
405                result = prepare_movie(storage, theMovie, dataRef, dataRefType);
406                if (result != 0)
407                        goto bail;
408        }
409       
410        /* replace the SampleDescription if user called MovieImportSetSampleDescription() */
411        if(storage->imgHdl) {
412                for(j = 0; j < storage->map_count; j++) {
413                        NCStream ncstream = storage->stream_map[j];
414                        GetMediaHandlerDescription(ncstream.media, &mediaType, NULL, NULL);
415                        if(mediaType == VideoMediaType && ncstream.sampleHdl) {
416                                DisposeHandle((Handle)ncstream.sampleHdl);
417                                ncstream.sampleHdl = (SampleDescriptionHandle)storage->imgHdl;
418                        }
419                }
420        }
421        if(storage->sndHdl) {
422                for(j = 0; j < storage->map_count; j++) {
423                        NCStream ncstream = storage->stream_map[j];
424                        GetMediaHandlerDescription(ncstream.media, &mediaType, NULL, NULL);
425                        if(mediaType == SoundMediaType && ncstream.sampleHdl) {
426                                DisposeHandle((Handle)ncstream.sampleHdl);
427                                ncstream.sampleHdl = (SampleDescriptionHandle)storage->sndHdl;
428                        }
429                }
430        }
431       
432        count = 0; media = NULL;
433        for(j = 0; j < storage->map_count; j++) {
434                media = storage->stream_map[j].media;
435                if(media)
436                        count++;
437        }
438       
439        if(count > 1)
440                *outFlags |= movieImportResultUsedMultipleTracks;
441       
442        /* The usedTrack parameter. Count the number of Tracks and set usedTrack if we operated
443                * on a single track. Note that this requires the media to be set by track counting above*/
444        if(usedTrack && count == 1 && media)
445                *usedTrack = GetMediaTrack(media);
446       
447        result = noErr;
448
449        *addedDuration = 0;
450       
451        //attempt to import using indexes.
452        result = import_using_index(storage, &hadIndex, addedDuration);
453        require_noerr(result, bail);
454       
455        if(hadIndex) {
456                //file had an index and was imported; we are done.
457                *outFlags |= movieImportResultComplete;
458               
459        } else if(inFlags & movieImportWithIdle) {
460                if(addedDuration && ic->duration > 0) {
461                        TimeScale movieTimeScale = GetMovieTimeScale(theMovie);
462                        *addedDuration = movieTimeScale * ic->duration / AV_TIME_BASE;
463                       
464                        //create a placeholder track so that progress displays correctly.
465                        create_placeholder_track(storage->movie, &storage->placeholderTrack, *addedDuration, dataRef, dataRefType);
466                       
467                        //give the data handler a hint as to how fast we need the data.
468                        //suggest a speed that's faster than the bare minimum.
469                        //if there's an error, the data handler probably doesn't support
470                        //this, so we can just ignore.
471                        DataHPlaybackHints(storage->dataHandler, 0, 0, -1, (storage->dataSize * 1.15) / ((double)ic->duration / AV_TIME_BASE));
472                }
473                       
474                //import with idle. Decode a little bit of data now.
475                import_with_idle(storage, inFlags, outFlags, 10, 300, true);
476        } else {
477                //QuickTime didn't request import with idle, so do it all now.
478                import_with_idle(storage, inFlags, outFlags, 0, 0, true);                       
479        }
480       
481        LoadExternalSubtitlesFromFileDataRef(dataRef, dataRefType, theMovie);
482
483bail:
484        if(result == noErr)
485                storage->movieLoadState = kMovieLoadStateLoaded;
486        else
487                storage->movieLoadState = kMovieLoadStateError;
488               
489        if (result == -1)
490                result = invalidMovie; // a bit better error message
491       
492        return result;
493} /* FFAvi_MovieImportDataRef */
494
495ComponentResult FFAvi_MovieImportSetIdleManager(ff_global_ptr storage, IdleManager im) {
496        storage->idleManager = im;
497        return noErr;
498}
499
500ComponentResult FFAvi_MovieImportIdle(ff_global_ptr storage, long inFlags, long *outFlags) {
501        ComponentResult err = noErr;
502        TimeValue currentIdleTime = GetMovieTime(storage->movie, NULL);
503        TimeScale movieTimeScale = GetMovieTimeScale(storage->movie);
504        int addSamples = false;
505       
506        storage->idlesSinceLastAdd++;
507       
508        if (currentIdleTime == storage->lastIdleTime && storage->idlesSinceLastAdd > 5 || 
509                storage->loadedTime < currentIdleTime + 5*movieTimeScale)
510        {
511                storage->idlesSinceLastAdd = 0;
512                addSamples = true;
513        }
514       
515        err = import_with_idle(storage, inFlags | movieImportWithIdle, outFlags, 0, 1000, addSamples);
516       
517        storage->lastIdleTime = currentIdleTime;
518        return err;
519}
520
521ComponentResult FFAvi_MovieImportGetLoadState(ff_global_ptr storage, long *importerLoadState) {
522        *importerLoadState = storage->movieLoadState;
523        return(noErr);
524}
525
526ComponentResult FFAvi_MovieImportGetMaxLoadedTime(ff_global_ptr storage, TimeValue *time) {
527        *time = storage->loadedTime;
528        return(noErr);
529}
Note: See TracBrowser for help on using the repository browser.