root/trunk/ff_MovieImport.c

Revision 899, 17.6 kB (checked in by gbooker, 2 months ago)

Reverted r894

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
76 void 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
134 ComponentResult 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;
155 bail:
156                 return result;
157 } /* FFAvi_MovieImportOpen() */
158
159 ComponentResult 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
186 ComponentResult 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
197 ComponentResult 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
222 ComponentResult 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
233 ComponentResult 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
261 ComponentResult 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
273 ComponentResult 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        
286 bail:
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
296 OSType 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
338 ComponentResult 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                
382 bail:
383                 if(dataHandler)
384                         CloseComponent(dataHandler);
385         free(pd);
386        
387         return result;
388 } /* FFAvi_MovieImportValidateDataRef() */
389
390
391 ComponentResult 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                
411 bail:
412                 if(dataRef)
413                         DisposeHandle(dataRef);
414        
415         return result;
416 } /* FFAvi_MovieImportFile() */
417
418 ComponentResult 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
553 bail:
554         if(result == noErr)
555                 storage->movieLoadState == kMovieLoadStateLoaded;
556         else
557                 storage->movieLoadState == kMovieLoadStateError;
558                
559         return result;
560 } /* FFAvi_MovieImportDataRef */
561
562 ComponentResult FFAvi_MovieImportSetIdleManager(ff_global_ptr storage, IdleManager im) {
563         storage->idleManager = im;
564         return noErr;
565 }
566
567 ComponentResult 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
588 ComponentResult FFAvi_MovieImportGetLoadState(ff_global_ptr storage, long *importerLoadState) {
589         *importerLoadState = storage->movieLoadState;
590         return(noErr);
591 }
592
593 ComponentResult FFAvi_MovieImportGetMaxLoadedTime(ff_global_ptr storage, TimeValue *time) {
594         *time = storage->loadedTime;
595         return(noErr);
596 }
Note: See TracBrowser for help on using the browser.