source: trunk/ff_MovieImport.c @ 706

Revision 706, 17.3 KB checked in by astrange, 7 years ago (diff)

Delegate more AVI codecs to the native decoders.
Ignore "Could not find codec parameters" in AVI loading.
Modified patch by Ritsuka; closes #123.

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