root/branches/perian-1.1/ff_MovieImport.c

Revision 923, 17.7 kB (checked in by astrange, 1 month ago)

Merge trunk to the branch.
Disable ssse3 qpel stuff (https://roundup.mplayerhq.hu/roundup/ffmpeg/issue463)

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