root/tags/perian-0.5/ff_MovieImport.c

Revision 112, 15.2 kB (checked in by tick, 2 years ago)

Here's to hoping I did this right. Merging [110] into the .5 branch.

Line 
1 /*****************************************************************************
2 *
3 *  Avi Import Component QuickTime Component Interface
4 *
5 *  Copyright(C) 2006 Christoph Naegeli <chn1@mac.com>
6 *
7 *  This program is free software ; you can redistribute it and/or modify
8 *  it under the terms of the GNU General Public License as published by
9 *  the Free Software Foundation ; either version 2 of the License, or
10 *  (at your option) any later version.
11 *
12 *  This program 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
15 *  GNU General Public License for more details.
16 *
17 *  You should have received a copy of the GNU General Public License
18 *  along with this program ; if not, write to the Free Software
19 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
20 *
21 ****************************************************************************/
22
23 #include "ff_MovieImportVersion.h"
24 #include "avformat.h"
25 #include "ff_private.h"
26 #include "allformats.h"
27 #include "Codecprintf.h"
28 #include "riff.h"
29
30 /* This one is a little big in ffmpeg and private anyway */
31 #define PROBE_BUF_SIZE 64
32
33 #include <CoreServices/CoreServices.h>
34 #include <QuickTime/QuickTime.h>
35
36 #define MOVIEIMPORT_BASENAME()          FFAvi_MovieImport
37 #define MOVIEIMPORT_GLOBALS()           ff_global_ptr storage
38
39 #define CALLCOMPONENT_BASENAME()        MOVIEIMPORT_BASENAME()
40 #define CALLCOMPONENT_GLOBALS()         MOVIEIMPORT_GLOBALS()
41
42 #define COMPONENT_DISPATCH_FILE         "ff_MovieImportDispatch.h"
43 #define COMPONENT_UPP_SELECT_ROOT()     MovieImport
44
45 struct _ff_global_context {
46         ComponentInstance ci;
47         OSType componentType;
48        
49         /* For feedback during import */
50         MovieProgressUPP prog;
51         long refcon;
52        
53         /* for overwriting the default sample descriptions */
54         ImageDescriptionHandle imgHdl;
55         SoundDescriptionHandle sndHdl;
56         AVInputFormat           *format;
57 };
58 typedef struct _ff_global_context ff_global_context;
59 typedef ff_global_context *ff_global_ptr;
60
61 #include <CoreServices/Components.k.h>
62 #include <QuickTime/QuickTimeComponents.k.h>
63 #include <QuickTime/ComponentDispatchHelper.c>
64
65 #pragma mark -
66
67 void initLib()
68 {
69         /* This one is used because Global variables are initialized ONE time
70         * until the application quits. Thus, we have to make sure we're initialize
71         * the libavformat only once or we get an endlos loop when registering the same
72         * element twice!! */
73         static Boolean inited = FALSE;
74        
75         /* Register the Parser of ffmpeg, needed because we do no proper setup of the libraries */
76         if(!inited) {
77                 inited = TRUE;
78                 av_register_input_format(&avi_demuxer);
79                 av_register_input_format(&flv_demuxer);
80                 register_parsers();
81                
82                 avcodec_init();
83                 register_avcodec(&msmpeg4v1_decoder);
84                 register_avcodec(&msmpeg4v2_decoder);
85                 register_avcodec(&msmpeg4v3_decoder);
86                 register_avcodec(&mpeg4_decoder);
87                 register_avcodec(&h264_decoder);
88                 register_avcodec(&flv_decoder);
89                 register_avcodec(&flashsv_decoder);
90                 register_avcodec(&vp6_decoder);
91                 register_avcodec(&vp6f_decoder);
92                
93                 av_log_set_callback(FFMpegCodecprintf);
94         }
95 }
96
97 /************************************
98 ** Base Component Manager Routines **
99 *************************************/
100
101 ComponentResult FFAvi_MovieImportOpen(ff_global_ptr storage, ComponentInstance self)
102 {
103         ComponentResult result;
104     ComponentDescription descout;
105        
106         /* Check for Mac OS 10.4 & QT 7 */
107         result = check_system();
108         require_noerr(result,bail);
109        
110     GetComponentInfo((Component)self, &descout, 0, 0, 0);
111        
112         storage = malloc(sizeof(ff_global_context));
113         if(!storage) goto bail;
114        
115         memset(storage, 0, sizeof(ff_global_context));
116         storage->ci = self;
117        
118         SetComponentInstanceStorage(storage->ci, (Handle)storage);
119        
120         storage->componentType = descout.componentSubType;
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)
134                 free(storage);
135        
136         return noErr;
137 } /* FFAvi_MovieImportClose() */
138
139 ComponentResult FFAvi_MovieImportVersion(ff_global_ptr storage)
140 {
141         return kFFAviComponentVersion;
142 } /* FFAvi_MovieImportVersion() */
143
144 #pragma mark -
145
146 /********************************
147 ** Configuration Stuff is here **
148 *********************************/
149
150 ComponentResult FFAvi_MovieImportGetMIMETypeList(ff_global_ptr storage, QTAtomContainer *mimeInfo)
151 {
152         ComponentResult err = noErr;
153         switch (storage->componentType) {
154                 case 'VfW ':
155                 case 'VFW ':
156                 case 'AVI ':
157                         err = GetComponentResource((Component)storage->ci, 'mime', kAVIthngResID, (Handle*)mimeInfo);
158                         break;
159                 case 'FLV ':
160                         err = GetComponentResource((Component)storage->ci, 'mime', kFLVthngResID, (Handle*)mimeInfo);
161                         break;
162                 default:
163                         err = GetComponentResource((Component)storage->ci, 'mime', kAVIthngResID, (Handle*)mimeInfo);
164                         break;
165         }
166         return err;
167 } /* FFAvi_MovieImportGetMIMETypeList() */
168
169 ComponentResult FFAvi_MovieImportSetProgressProc(ff_global_ptr storage, MovieProgressUPP prog, long refcon)
170 {
171         storage->prog = prog;
172         storage->refcon = refcon;
173        
174         if(!storage->prog)
175                 storage->refcon = 0;
176        
177         return noErr;
178 } /* FFAvi_MovieImportSetProgressProc() */
179
180 ComponentResult FFAvi_MovieImportSetSampleDescription(ff_global_ptr storage, SampleDescriptionHandle desc, OSType media)
181 {
182         ComponentResult result = noErr;
183        
184         switch(media) {
185                 case VideoMediaType:
186                         if(storage->imgHdl) /* already one stored */
187                                 DisposeHandle((Handle)storage->imgHdl);
188                        
189                         storage->imgHdl = (ImageDescriptionHandle)desc;
190                         if(storage->imgHdl)
191                                 result = HandToHand((Handle*)&storage->imgHdl);
192                                 break;
193                 case SoundMediaType:
194                         if(storage->sndHdl)
195                                 DisposeHandle((Handle)storage->sndHdl);
196                        
197                         storage->sndHdl = (SoundDescriptionHandle)desc;
198                         if(storage->sndHdl)
199                                 result = HandToHand((Handle*)&storage->sndHdl);
200                                 break;
201                 default:
202                         break;
203         }
204        
205         return result;
206 } /* FFAvi_MovieImportSetSampleDescription() */
207
208 ComponentResult FFAvi_MovieImportGetDestinationMediaType(ff_global_ptr storage, OSType *mediaType)
209 {
210         *mediaType = VideoMediaType;
211         return noErr;
212 } /* FFAvi_MovieImportGetDestinationMediaType() */
213
214 #pragma mark -
215
216 /****************************
217 ** Import Stuff comes here **
218 *****************************/
219
220 ComponentResult FFAvi_MovieImportValidate(ff_global_ptr storage, const FSSpec *theFile, Handle theData, Boolean *valid)
221 {
222         ComponentResult result = noErr;
223         Handle dataRef = NULL;
224         OSType dataRefType;
225        
226         *valid = FALSE;
227        
228         result = QTNewDataReferenceFromFSSpec(theFile, 0, &dataRef, &dataRefType);
229         require_noerr(result,bail);
230        
231         result = MovieImportValidateDataRef(storage->ci, dataRef, dataRefType, (UInt8*)valid);
232        
233 bail:
234                 if(dataRef)
235                         DisposeHandle(dataRef);
236        
237         return result;
238 } /* FFAvi_MovieImportValidate() */
239
240 // this function is a small avi parser to get the video track's fourcc as
241 // fast as possible, so we can decide whether we can handle the necessary
242 // image description extensions for the format in ValidateDataRef() quickly
243 OSType get_avi_strf_fourcc(ByteIOContext *pb)
244 {
245         OSType tag, subtag;
246         unsigned int size;
247        
248         if (get_be32(pb) != 'RIFF')
249                 return 0;
250        
251         // file size
252         get_le32(pb);
253        
254         if (get_be32(pb) != 'AVI ')
255                 return 0;
256        
257         while (!url_feof(pb)) {
258                 tag = get_be32(pb);
259                 size = get_le32(pb);
260                
261                 if (tag == 'LIST') {
262                         subtag = get_be32(pb);
263                        
264                         // only lists we care about: hdrl & strl, so skip the rest
265                         if (subtag != 'hdrl' && subtag != 'strl')
266                                 url_fskip(pb, size - 4 + (size & 1));
267                        
268                 } else if (tag == 'strf') {
269                         // 16-byte offset to the fourcc
270                         url_fskip(pb, 16);
271                         return get_be32(pb);
272                 } else
273                         url_fskip(pb, size + (size & 1));
274         }
275         return 0;
276 }
277
278 ComponentResult FFAvi_MovieImportValidateDataRef(ff_global_ptr storage, Handle dataRef, OSType dataRefType, UInt8 *valid)
279 {
280         ComponentResult result = noErr;
281         DataHandler dataHandler = NULL;
282         uint8_t buf[PROBE_BUF_SIZE];
283         AVProbeData *pd = (AVProbeData *)malloc(sizeof(AVProbeData));
284         ByteIOContext byteContext;
285         AVFormatParameters params;
286         int success, i;
287
288         /* default */
289         *valid = 0;
290        
291         /* Get a data handle and read a probe of data */
292         result = OpenADataHandler(dataRef, dataRefType, NULL, 0, NULL, kDataHCanRead, &dataHandler);
293         if(result || !dataHandler) goto bail;
294        
295         pd->buf = buf;
296         pd->buf_size = PROBE_BUF_SIZE;
297        
298         result = DataHScheduleData(dataHandler, (Ptr)(pd->buf), 0, PROBE_BUF_SIZE, 0, NULL, NULL);
299         require_noerr(result,bail);
300        
301         initLib();
302         storage->format = av_probe_input_format(pd, 1);
303         if(storage->format != NULL) {
304                 *valid = 255; /* This means we can read the data */
305                
306                 /* we don't do MJPEG's Huffman tables right yet, and DV video seems to have
307                 an aspect ratio coded in the bitstream that ffmpeg doesn't read, so let Apple's
308                 AVI importer handle AVIs with these two video types */
309                 if (IS_AVI(storage->componentType)) {
310                         /* Prepare the iocontext structure */
311                         memset(&byteContext, 0, sizeof(byteContext));
312                         result = url_open_dataref(&byteContext, dataRef, dataRefType);
313                         require_noerr(result, bail);
314                        
315                         OSType fourcc = get_avi_strf_fourcc(&byteContext);
316                        
317                         if (codec_get_bmp_id(BSWAP(fourcc)) == CODEC_ID_MJPEG ||
318                                 codec_get_bmp_id(BSWAP(fourcc)) == CODEC_ID_DVVIDEO)
319                                 *valid = 0;
320                        
321                         url_fclose(&byteContext);
322                 }
323         }
324                
325 bail:
326                 if(dataHandler)
327                         CloseComponent(dataHandler);
328         free(pd);
329        
330         return result;
331 } /* FFAvi_MovieImportValidateDataRef() */
332
333
334 ComponentResult FFAvi_MovieImportFile(ff_global_ptr storage, const FSSpec *theFile, Movie theMovie, Track targetTrack,
335                                                                           Track *usedTrack, TimeValue atTime, TimeValue *addedDuration, long inFlags, long *outFlags)
336 {
337         ComponentResult result;
338         Handle dataRef = NULL;
339         OSType dataRefType;
340        
341         *outFlags = 0;
342        
343         result = QTNewDataReferenceFromFSSpec(theFile, 0, &dataRef, &dataRefType);
344         require_noerr(result,bail);
345        
346         result = MovieImportDataRef(storage->ci, dataRef, dataRefType, theMovie, targetTrack, usedTrack, atTime, addedDuration,
347                                                                 inFlags, outFlags);
348 bail:
349                 if(dataRef)
350                         DisposeHandle(dataRef);
351        
352         return result;
353 } /* FFAvi_MovieImportFile() */
354
355 ComponentResult FFAvi_MovieImportDataRef(ff_global_ptr storage, Handle dataRef, OSType dataRefType, Movie theMovie, Track targetTrack,
356                                                                                  Track *usedTrack, TimeValue atTime, TimeValue *addedDuration, long inFlags, long *outFlags)
357 {
358         ComponentResult result;
359         ByteIOContext byteContext;
360         AVFormatContext *ic = NULL;
361         AVFormatParameters params;
362         int64_t dataOffset;
363         AVPacket pkt;
364         NCStream *map = NULL;
365         int map_count,j,count;
366         OSType mediaType;
367         Track track;
368         Media media;
369         TimeRecord time;
370         int i;
371         short hadIndex = 0;
372        
373         /* make sure that in case of error, the flag movieImportResultComplete is not set */
374         *outFlags = 0;
375        
376         /* probe the format first */
377         UInt8 valid = 0;
378         FFAvi_MovieImportValidateDataRef(storage, dataRef, dataRefType, &valid);
379         if(valid != 255)
380                 goto bail;
381        
382         /* Prepare the iocontext structure */
383         memset(&byteContext, 0, sizeof(byteContext));
384         result = url_open_dataref(&byteContext, dataRef, dataRefType);
385         require_noerr(result, bail);
386        
387         /* Open the Format Context */
388         memset(&params, 0, sizeof(params));
389         result = av_open_input_stream(&ic, &byteContext, "", storage->format, &params);
390         require_noerr(result,bail);
391        
392         /* Get the Stream Infos if not already read */
393         result = av_find_stream_info(ic);
394         if(result < 0)
395                 goto bail;
396        
397         /* Seek backwards to get a manually read packet for file offset */
398         if(ic->streams[0]->index_entries == NULL || storage->componentType == 'FLV ')
399         {
400                 if (IS_AVI(storage->componentType))
401                         //Try to seek to the first frame; don't care if it fails
402                         // Is this really needed for AVIs w/out an index? It seems to work fine without,
403                         // and it seems that with it the first frame is skipped.
404                         av_seek_frame(ic, -1, 0, 0);
405                 dataOffset = 0;
406         }
407         else
408         {
409                 result = av_seek_frame(ic, -1, 0, 0);
410                 if(result < 0) goto bail;
411                
412                 ic->iformat->read_packet(ic, &pkt);
413                 /* read_packet will give the first decodable packet. However, that isn't necessarily
414                         the first entry in the index, so look for an entry with a matching size. */
415                 for (i = 0; i < ic->streams[pkt.stream_index]->nb_index_entries; i++) {
416                         if (pkt.size == ic->streams[pkt.stream_index]->index_entries[i].size) {
417                                 dataOffset = pkt.pos - ic->streams[pkt.stream_index]->index_entries[i].pos;
418                                 break;
419                         }
420                 }
421                 av_free_packet(&pkt);
422         }
423        
424         /* Initialize the Movie */
425         if(inFlags & movieImportMustUseTrack) {
426                 map_count = 1;
427                 prepare_track(ic, &map, targetTrack, dataRef, dataRefType);
428         } else {
429                 map_count = ic->nb_streams;
430                 prepare_movie(ic, &map, theMovie, dataRef, dataRefType);
431         }
432        
433         /* replace the SampleDescription if user called MovieImportSetSampleDescription() */
434         if(storage->imgHdl) {
435                 for(j = 0; j < map_count; j++) {
436                         GetMediaHandlerDescription(map[j].media, &mediaType, NULL, NULL);
437                         if(mediaType == VideoMediaType && map[j].sampleHdl) {
438                                 DisposeHandle((Handle)map[j].sampleHdl);
439                                 map[j].sampleHdl = (SampleDescriptionHandle)storage->imgHdl;
440                         }
441                 }
442         }
443         if(storage->sndHdl) {
444                 for(j = 0; j < map_count; j++) {
445                         GetMediaHandlerDescription(map[j].media, &mediaType, NULL, NULL);
446                         if(mediaType == SoundMediaType && map[j].sampleHdl) {
447                                 DisposeHandle((Handle)map[j].sampleHdl);
448                                 map[j].sampleHdl = (SampleDescriptionHandle)storage->sndHdl;
449                         }
450                 }
451         }
452        
453         /* Import the Data*/
454         /* FIXME: Implement the progress upp */
455         /* note: flv builds a partial index that's unusable, so force importing without an index */
456         if (!(storage->componentType == 'FLV '))
457                 hadIndex = import_avi(ic, map, dataOffset);
458        
459         if (!hadIndex)
460                 import_without_index(ic, map, dataOffset);
461        
462         /* Insert the Medias into the Tracks */
463         result = noErr;
464         for(j = 0; j < map_count && result == noErr; j++) {
465                 media = map[j].media;
466                 if(media) {
467                         /* we could handle this stream.
468                         * convert the atTime parameter to track scale.
469                         * FIXME: check if that's correct */                     
470                         time.value.hi = 0;
471                         time.value.lo = atTime;
472                         time.scale = GetMovieTimeScale(theMovie);
473                         time.base = NULL;
474                         ConvertTimeScale(&time, GetMediaTimeScale(media));
475                        
476                         track = GetMediaTrack(media);
477                         result = InsertMediaIntoTrack(track, time.value.lo, 0, GetMediaDuration(media), fixed1);
478                 }
479         }
480         require_noerr(result,bail);
481        
482         /* Set return values of the function */
483        
484         count = 0; media = NULL;
485         for(j = 0; j < map_count; j++) {
486                 media = map[j].media;
487                 if(media)
488                         count++;
489         }
490        
491         /* The usedTrack parameter. Count the number of Tracks and set usedTrack if we operated
492                 * on a single track. Note that this requires the media to be set by track counting above*/
493         if(usedTrack && count == 1 && media)
494                 *usedTrack = GetMediaTrack(media);
495        
496         /* the addedDuration parameter */
497         if(addedDuration) {
498                 *addedDuration = 0;
499                 for(j = 0; j < map_count; j++) {
500                         media = map[j].media;
501                         if(media) {
502                                 time.value.hi = 0;
503                                 time.value.lo = GetMediaDuration(media);
504                                 time.scale = GetMediaTimeScale(media);
505                                 time.base = NULL;
506                                 ConvertTimeScale(&time, GetMovieTimeScale(theMovie));
507                                
508                                 /* if that's longer than before, replace */
509                                 if(time.value.lo > *addedDuration)
510                                         *addedDuration = time.value.lo;
511                         }
512                 }
513         }
514        
515         /* now set the outflags, set to zero at the beginning of the function */
516         if(outFlags) {
517                 if(count > 1)
518                         *outFlags |= movieImportResultUsedMultipleTracks;
519                
520                 /* set the finished flag */
521                 *outFlags |= movieImportResultComplete;
522         }
523        
524 bail:
525                 /* Free all the data structures used */
526                 if(ic)
527                         av_close_input_file(ic);
528         if(map)
529                 av_free(map);
530        
531         return result;
532 } /* FFAvi_MovieImportDataRef */
Note: See TracBrowser for help on using the browser.