source: branches/perian-1.1/ff_MovieImport.c @ 887

Revision 887, 17.6 KB checked in by astrange, 7 years ago (diff)

Merge trunk to 1.1 branch.

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