source: trunk/ff_MovieImport.c @ 1217

Revision 1217, 18.4 KB checked in by astrange, 5 years ago (diff)

File import probing was missing FF_INPUT_BUFFER_PADDING_SIZE.

Also allocate probe data on the stack, since it's small.

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(mp2);
147                REGISTER_DECODER(tta);
148                REGISTER_DECODER(dca);
149                REGISTER_DECODER(nellymoser);
150               
151                REGISTER_DECODER(dvdsub);
152                REGISTER_DECODER(tscc);
153                REGISTER_DECODER(vp6a);
154                REGISTER_DECODER(zmbv);
155                REGISTER_DECODER(indeo2);
156                REGISTER_DECODER(indeo3);
157               
158                av_log_set_callback(FFMpegCodecprintf);
159        }
160       
161        PerianInitExit(unlock);
162}
163
164/************************************
165** Base Component Manager Routines **
166*************************************/
167
168ComponentResult FFAvi_MovieImportOpen(ff_global_ptr storage, ComponentInstance self)
169{
170        ComponentResult result;
171    ComponentDescription descout;
172       
173        /* Check for Mac OS 10.4 & QT 7 */
174        result = check_system();
175        require_noerr(result,bail);
176       
177    GetComponentInfo((Component)self, &descout, 0, 0, 0);
178       
179        storage = malloc(sizeof(ff_global_context));
180        if(!storage) goto bail;
181       
182        memset(storage, 0, sizeof(ff_global_context));
183        storage->ci = self;
184       
185        SetComponentInstanceStorage(storage->ci, (Handle)storage);
186       
187        storage->componentType = descout.componentSubType;
188        storage->movieLoadState = kMovieLoadStateLoading;
189bail:
190                return result;
191} /* FFAvi_MovieImportOpen() */
192
193ComponentResult FFAvi_MovieImportClose(ff_global_ptr storage, ComponentInstance self)
194{
195        /* Free all global storage */
196        if(storage->imgHdl)
197                DisposeHandle((Handle)storage->imgHdl);
198        if(storage->sndHdl)
199                DisposeHandle((Handle)storage->sndHdl);
200       
201        if(storage->stream_map)
202                av_free(storage->stream_map);
203       
204        if(storage->format_context)
205                av_close_input_file(storage->format_context);
206       
207        int i;
208        for(i=0; i<MAX_STREAMS; i++)
209        {
210                if(storage->firstFrames[i].data != NULL)
211                        av_free_packet(storage->firstFrames + i);
212        }
213       
214        if(storage)
215                free(storage);
216       
217        return noErr;
218} /* FFAvi_MovieImportClose() */
219
220ComponentResult FFAvi_MovieImportVersion(ff_global_ptr storage)
221{
222        return kFFAviComponentVersion;
223} /* FFAvi_MovieImportVersion() */
224
225#pragma mark -
226
227/********************************
228** Configuration Stuff is here **
229*********************************/
230
231ComponentResult FFAvi_MovieImportGetMIMETypeList(ff_global_ptr storage, QTAtomContainer *mimeInfo)
232{
233        ComponentResult err = noErr;
234        switch (storage->componentType) {
235                case 'VfW ':
236                case 'VFW ':
237                case 'AVI ':
238                        err = GetComponentResource((Component)storage->ci, 'mime', kAVIthngResID, (Handle*)mimeInfo);
239                        break;
240                case 'FLV ':
241                        err = GetComponentResource((Component)storage->ci, 'mime', kFLVthngResID, (Handle*)mimeInfo);
242                        break;
243                case 'TTA ':
244                        err = GetComponentResource((Component)storage->ci, 'mime', kTTAthngResID, (Handle*)mimeInfo);
245                        break;
246                case 'NUV ':
247                        err = GetComponentResource((Component)storage->ci, 'mime', kNuvthngResID, (Handle*)mimeInfo);
248                        break;
249                default:
250                        err = GetComponentResource((Component)storage->ci, 'mime', kAVIthngResID, (Handle*)mimeInfo);
251                        break;
252        }
253        return err;
254} /* FFAvi_MovieImportGetMIMETypeList() */
255
256ComponentResult FFAvi_MovieImportSetProgressProc(ff_global_ptr storage, MovieProgressUPP prog, long refcon)
257{
258        storage->prog = prog;
259        storage->refcon = refcon;
260       
261        if(!storage->prog)
262                storage->refcon = 0;
263       
264        return noErr;
265} /* FFAvi_MovieImportSetProgressProc() */
266
267ComponentResult FFAvi_MovieImportSetSampleDescription(ff_global_ptr storage, SampleDescriptionHandle desc, OSType media)
268{
269        ComponentResult result = noErr;
270       
271        switch(media) {
272                case VideoMediaType:
273                        if(storage->imgHdl) /* already one stored */
274                                DisposeHandle((Handle)storage->imgHdl);
275                       
276                        storage->imgHdl = (ImageDescriptionHandle)desc;
277                        if(storage->imgHdl)
278                                result = HandToHand((Handle*)&storage->imgHdl);
279                                break;
280                case SoundMediaType:
281                        if(storage->sndHdl)
282                                DisposeHandle((Handle)storage->sndHdl);
283                       
284                        storage->sndHdl = (SoundDescriptionHandle)desc;
285                        if(storage->sndHdl)
286                                result = HandToHand((Handle*)&storage->sndHdl);
287                                break;
288                default:
289                        break;
290        }
291       
292        return result;
293} /* FFAvi_MovieImportSetSampleDescription() */
294
295ComponentResult FFAvi_MovieImportGetDestinationMediaType(ff_global_ptr storage, OSType *mediaType)
296{
297        *mediaType = VideoMediaType;
298        return noErr;
299} /* FFAvi_MovieImportGetDestinationMediaType() */
300
301#pragma mark -
302
303/****************************
304** Import Stuff comes here **
305*****************************/
306
307ComponentResult FFAvi_MovieImportValidate(ff_global_ptr storage, const FSSpec *theFile, Handle theData, Boolean *valid)
308{
309        ComponentResult result = noErr;
310        Handle dataRef = NULL;
311        OSType dataRefType;
312       
313        *valid = FALSE;
314       
315        result = QTNewDataReferenceFromFSSpec(theFile, 0, &dataRef, &dataRefType);
316        require_noerr(result,bail);
317       
318        result = MovieImportValidateDataRef(storage->ci, dataRef, dataRefType, (UInt8*)valid);
319       
320bail:
321                if(dataRef)
322                        DisposeHandle(dataRef);
323       
324        return result;
325} /* FFAvi_MovieImportValidate() */
326
327// this function is a small avi parser to get the video track's fourcc as
328// fast as possible, so we can decide whether we can handle the necessary
329// image description extensions for the format in ValidateDataRef() quickly
330OSType get_avi_strf_fourcc(ByteIOContext *pb)
331{
332        OSType tag, subtag;
333        unsigned int size;
334       
335        if (get_be32(pb) != 'RIFF')
336                return 0;
337       
338        // file size
339        get_le32(pb);
340       
341        if (get_be32(pb) != 'AVI ')
342                return 0;
343       
344        while (!url_feof(pb)) {
345                tag = get_be32(pb);
346                size = get_le32(pb);
347               
348                if (tag == 'LIST') {
349                        subtag = get_be32(pb);
350                       
351                        // only lists we care about: hdrl & strl, so skip the rest
352                        if (subtag != 'hdrl' && subtag != 'strl')
353                                url_fskip(pb, size - 4 + (size & 1));
354                       
355                } else if (tag == 'strf') {
356                        // 16-byte offset to the fourcc
357                        url_fskip(pb, 16);
358                        return get_be32(pb);
359                } else if (tag == 'strh'){
360                        // 4-byte offset to the fourcc
361                        OSType tag1 = get_be32(pb);
362                        if(tag1 == 'iavs' || tag1 == 'ivas')
363                                return get_be32(pb);
364                        else
365                                url_fskip(pb, size + (size & 1) - 4);
366                } else
367                        url_fskip(pb, size + (size & 1));
368        }
369        return 0;
370}
371
372ComponentResult FFAvi_MovieImportValidateDataRef(ff_global_ptr storage, Handle dataRef, OSType dataRefType, UInt8 *valid)
373{
374        ComponentResult result = noErr;
375        DataHandler dataHandler = NULL;
376        uint8_t buf[PROBE_BUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE] = {0};
377        AVProbeData pd;
378        ByteIOContext *byteContext;
379
380        /* default */
381        *valid = 0;
382       
383        /* Get a data handle and read a probe of data */
384        result = OpenADataHandler(dataRef, dataRefType, NULL, 0, NULL, kDataHCanRead, &dataHandler);
385        if(result || !dataHandler) goto bail;
386       
387        pd.buf = buf;
388        pd.buf_size = PROBE_BUF_SIZE;
389       
390        result = DataHScheduleData(dataHandler, (Ptr)pd.buf, 0, PROBE_BUF_SIZE, 0, NULL, NULL);
391        require_noerr(result,bail);
392       
393        init_FFmpeg();
394        storage->format = av_probe_input_format(&pd, 1);
395        if(storage->format != NULL) {
396                *valid = 255; /* This means we can read the data */
397               
398                /* we don't do MJPEG's Huffman tables right yet, and DV video seems to have
399                an aspect ratio coded in the bitstream that ffmpeg doesn't read, so let Apple's
400                AVI importer handle AVIs with these two video types */
401                if (IS_AVI(storage->componentType)) {
402                        /* Prepare the iocontext structure */
403                        result = url_open_dataref(&byteContext, dataRef, dataRefType, NULL, NULL, NULL);
404                        require_noerr(result, bail);
405                       
406                        OSType fourcc = get_avi_strf_fourcc(byteContext);
407                        enum CodecID id = ff_codec_get_id(ff_codec_bmp_tags, BSWAP(fourcc));
408                       
409                        if (id == CODEC_ID_MJPEG || id == CODEC_ID_DVVIDEO || id == CODEC_ID_RAWVIDEO || id == CODEC_ID_MSVIDEO1 || id == CODEC_ID_MSRLE)
410                                *valid = 0;
411                       
412                        url_fclose(byteContext);
413                }
414        }
415               
416bail:
417                if(dataHandler)
418                        CloseComponent(dataHandler);
419       
420        return result;
421} /* FFAvi_MovieImportValidateDataRef() */
422
423
424ComponentResult FFAvi_MovieImportFile(ff_global_ptr storage, const FSSpec *theFile, Movie theMovie, Track targetTrack,
425                                                                          Track *usedTrack, TimeValue atTime, TimeValue *addedDuration, long inFlags, long *outFlags)
426{
427        ComponentResult result;
428        Handle dataRef = NULL;
429        OSType dataRefType;
430        FSRef theFileFSRef;
431       
432        *outFlags = 0;
433       
434        result = QTNewDataReferenceFromFSSpec(theFile, 0, &dataRef, &dataRefType);
435        require_noerr(result,bail);
436       
437        result = MovieImportDataRef(storage->ci, dataRef, dataRefType, theMovie, targetTrack, usedTrack, atTime, addedDuration,
438                                                                inFlags, outFlags);
439        require_noerr(result, bail);
440       
441        result = FSpMakeFSRef(theFile, &theFileFSRef);
442        require_noerr(result, bail);
443               
444bail:
445                if(dataRef)
446                        DisposeHandle(dataRef);
447       
448        return result;
449} /* FFAvi_MovieImportFile() */
450
451ComponentResult FFAvi_MovieImportDataRef(ff_global_ptr storage, Handle dataRef, OSType dataRefType, Movie theMovie, Track targetTrack,
452                                                                                 Track *usedTrack, TimeValue atTime, TimeValue *addedDuration, long inFlags, long *outFlags)
453{
454        ComponentResult result = noErr;
455        ByteIOContext *byteContext;
456        AVFormatContext *ic = NULL;
457        AVFormatParameters params;
458        OSType mediaType;
459        Media media;
460        int count, hadIndex, j;
461               
462        /* make sure that in case of error, the flag movieImportResultComplete is not set */
463        *outFlags = 0;
464       
465        /* probe the format first */
466        UInt8 valid = 0;
467        FFAvi_MovieImportValidateDataRef(storage, dataRef, dataRefType, &valid);
468        if(valid != 255)
469                goto bail;
470                       
471        /* Prepare the iocontext structure */
472        result = url_open_dataref(&byteContext, dataRef, dataRefType, &storage->dataHandler, &storage->dataHandlerSupportsWideOffsets, &storage->dataSize);
473        storage->isStreamed = dataRefType == URLDataHandlerSubType;
474        require_noerr(result, bail);
475       
476        /* Open the Format Context */
477        memset(&params, 0, sizeof(params));
478        result = av_open_input_stream(&ic, byteContext, "", storage->format, &params);
479        require_noerr(result,bail);
480        storage->format_context = ic;
481       
482        /* Get the Stream Infos if not already read */
483        result = av_find_stream_info(ic);
484       
485        // -1 means it couldn't understand at least one stream
486        // which might just mean we don't have its video decoder enabled
487        if(result < 0 && result != -1)
488                goto bail;
489       
490        // we couldn't find any streams, bail with an error.
491        if(ic->nb_streams == 0) {
492                result = -1; //is there a more appropriate error code?
493                goto bail;
494        }
495       
496        //determine a header offset (needed by index-based import).
497        result = determine_header_offset(storage);
498        if(result < 0)
499                goto bail;
500       
501        /* Initialize the Movie */
502        storage->movie = theMovie;
503        if(inFlags & movieImportMustUseTrack) {
504                storage->map_count = 1;
505                prepare_track(storage, targetTrack, dataRef, dataRefType);
506        } else {
507                storage->map_count = ic->nb_streams;
508                result = prepare_movie(storage, theMovie, dataRef, dataRefType);
509                if (result != 0)
510                        goto bail;
511        }
512       
513        /* replace the SampleDescription if user called MovieImportSetSampleDescription() */
514        if(storage->imgHdl) {
515                for(j = 0; j < storage->map_count; j++) {
516                        NCStream ncstream = storage->stream_map[j];
517                        GetMediaHandlerDescription(ncstream.media, &mediaType, NULL, NULL);
518                        if(mediaType == VideoMediaType && ncstream.sampleHdl) {
519                                DisposeHandle((Handle)ncstream.sampleHdl);
520                                ncstream.sampleHdl = (SampleDescriptionHandle)storage->imgHdl;
521                        }
522                }
523        }
524        if(storage->sndHdl) {
525                for(j = 0; j < storage->map_count; j++) {
526                        NCStream ncstream = storage->stream_map[j];
527                        GetMediaHandlerDescription(ncstream.media, &mediaType, NULL, NULL);
528                        if(mediaType == SoundMediaType && ncstream.sampleHdl) {
529                                DisposeHandle((Handle)ncstream.sampleHdl);
530                                ncstream.sampleHdl = (SampleDescriptionHandle)storage->sndHdl;
531                        }
532                }
533        }
534       
535        count = 0; media = NULL;
536        for(j = 0; j < storage->map_count; j++) {
537                media = storage->stream_map[j].media;
538                if(media)
539                        count++;
540        }
541       
542        if(count > 1)
543                *outFlags |= movieImportResultUsedMultipleTracks;
544       
545        /* The usedTrack parameter. Count the number of Tracks and set usedTrack if we operated
546                * on a single track. Note that this requires the media to be set by track counting above*/
547        if(usedTrack && count == 1 && media)
548                *usedTrack = GetMediaTrack(media);
549       
550        result = noErr;
551
552        *addedDuration = 0;
553       
554        //attempt to import using indexes.
555        result = import_using_index(storage, &hadIndex, addedDuration);
556        require_noerr(result, bail);
557       
558        if(hadIndex) {
559                //file had an index and was imported; we are done.
560                *outFlags |= movieImportResultComplete;
561               
562        } else if(inFlags & movieImportWithIdle) {
563                if(addedDuration && ic->duration > 0) {
564                        TimeScale movieTimeScale = GetMovieTimeScale(theMovie);
565                        *addedDuration = movieTimeScale * ic->duration / AV_TIME_BASE;
566                       
567                        //create a placeholder track so that progress displays correctly.
568                        create_placeholder_track(storage->movie, &storage->placeholderTrack, *addedDuration, dataRef, dataRefType);
569                       
570                        //give the data handler a hint as to how fast we need the data.
571                        //suggest a speed that's faster than the bare minimum.
572                        //if there's an error, the data handler probably doesn't support
573                        //this, so we can just ignore.
574                        DataHPlaybackHints(storage->dataHandler, 0, 0, -1, (storage->dataSize * 1.15) / ((double)ic->duration / AV_TIME_BASE));
575                }
576                       
577                //import with idle. Decode a little bit of data now.
578                import_with_idle(storage, inFlags, outFlags, 10, 300, true);
579        } else {
580                //QuickTime didn't request import with idle, so do it all now.
581                import_with_idle(storage, inFlags, outFlags, 0, 0, true);                       
582        }
583       
584        LoadExternalSubtitlesFromFileDataRef(dataRef, dataRefType, theMovie);
585
586bail:
587        if(result == noErr)
588                storage->movieLoadState = kMovieLoadStateLoaded;
589        else
590                storage->movieLoadState = kMovieLoadStateError;
591               
592        if (result == -1)
593                result = invalidMovie; // a bit better error message
594       
595        return result;
596} /* FFAvi_MovieImportDataRef */
597
598ComponentResult FFAvi_MovieImportSetIdleManager(ff_global_ptr storage, IdleManager im) {
599        storage->idleManager = im;
600        return noErr;
601}
602
603ComponentResult FFAvi_MovieImportIdle(ff_global_ptr storage, long inFlags, long *outFlags) {
604        ComponentResult err = noErr;
605        TimeValue currentIdleTime = GetMovieTime(storage->movie, NULL);
606        TimeScale movieTimeScale = GetMovieTimeScale(storage->movie);
607        int addSamples = false;
608       
609        storage->idlesSinceLastAdd++;
610       
611        if (currentIdleTime == storage->lastIdleTime && storage->idlesSinceLastAdd > 5 || 
612                storage->loadedTime < currentIdleTime + 5*movieTimeScale)
613        {
614                storage->idlesSinceLastAdd = 0;
615                addSamples = true;
616        }
617       
618        err = import_with_idle(storage, inFlags | movieImportWithIdle, outFlags, 0, 1000, addSamples);
619       
620        storage->lastIdleTime = currentIdleTime;
621        return err;
622}
623
624ComponentResult FFAvi_MovieImportGetLoadState(ff_global_ptr storage, long *importerLoadState) {
625        *importerLoadState = storage->movieLoadState;
626        return(noErr);
627}
628
629ComponentResult FFAvi_MovieImportGetMaxLoadedTime(ff_global_ptr storage, TimeValue *time) {
630        *time = storage->loadedTime;
631        return(noErr);
632}
Note: See TracBrowser for help on using the repository browser.