root/branches/perian-1.0/ff_MovieImport.c

Revision 575, 16.3 kB (checked in by astrange, 1 year ago)

Make Perian build with latest ffmpeg. Which seems to have broken H.264 in mkv.

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