source: trunk/ff_MovieImport.c @ 1042

Revision 1042, 17.8 KB checked in by astrange, 5 years ago (diff)

MovieImport?:

  • allow importing AVI with no video track (fixes a file I generated while testing)
  • improve audio sample descriptions (MP3 should work better, don't know how to test it)
  • pass magic cookies through to all API that supports them, minus AAC since that breaks

FFusion:

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