source: trunk/ff_MovieImport.c @ 1538

Revision 1538, 16.7 KB checked in by astrange, 21 months ago (diff)

Distrust all packet durations in flv, parse headers a bit more

Seems to help with audio sync in flv

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