source: trunk/ff_private.c @ 1197

Revision 1197, 41.4 KB checked in by astrange, 4 years ago (diff)

Check that a track's sample handle exists as well as its media.
Fixes crashing due to passing NULL sampleHdl to QT functions.

Closes #454.

Add a bail exit to the FormatInfo? error. No actual effect,
but the error is irrecoverable after all.

Line 
1/*****************************************************************************
2*
3*  Avi Import Component Private Functions
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_private.h"
24#include "avcodec.h"
25#include "libavutil/internal.h"
26#include "mpegaudio.h"
27#include "Codecprintf.h"
28#include "CommonUtils.h"
29#include "CodecIDs.h"
30
31#undef malloc
32#undef free
33
34#include <CoreServices/CoreServices.h>
35#include <AudioToolbox/AudioToolbox.h>
36#include <QuickTime/QuickTime.h>
37
38#include "bitstream_info.h"
39#include "MatroskaCodecIDs.h"
40
41/* This routine checks if the system requirements are fullfilled */
42ComponentResult check_system()
43{
44        ComponentResult result;
45        long systemVersion;
46       
47        result = Gestalt(gestaltSystemVersion, &systemVersion);
48        require_noerr(result,bail);
49       
50        /* Make sure we have at least 10.4 installed...*/
51        if(systemVersion < 0x00001040)
52                result = -1;
53       
54bail:
55                return result;
56} /* check_system() */
57
58/* This routine does register the ffmpeg parsers which would normally
59 * be registred through the normal initialization process */
60void register_parsers()
61{
62        /* Do we need more parsers here? */
63//    av_register_codec_parser(&mpegaudio_parser);
64//      av_register_codec_parser(&ac3_parser);
65} /* register_parsers() */
66
67
68/* This function prepares the target Track to receivve the movie data,
69 * it is called if QT has asked an import operation which should just
70 * load this track. After success, *outmap points to a valid stream maping
71 * Return values:
72 *        0: ok
73 *      < 0: couldn't find a matching track
74 */
75int prepare_track(ff_global_ptr storage, Track targetTrack, Handle dataRef, OSType dataRefType)
76{
77        int j;
78        AVStream *st = NULL;
79        AVStream *outstr = NULL;
80        Media media;
81        NCStream *map = NULL;
82        AVFormatContext *ic = storage->format_context;
83       
84        /* If the track has already a media, return an err */
85        media = GetTrackMedia(targetTrack);
86        if(media) goto err;
87       
88        /* Search the AVFormatContext for a video stream */
89        for(j = 0; j < ic->nb_streams && !outstr; j++) {
90                st = ic->streams[j];
91                if(st->codec->codec_type == CODEC_TYPE_VIDEO)
92                        outstr = st;
93        }
94        /* Search the AVFormatContext for an audio stream (no video stream exists) */
95        for(j = 0; j < ic->nb_streams && !outstr; j++) {
96                st = ic->streams[j];
97                if(st->codec->codec_type == CODEC_TYPE_AUDIO)
98                        outstr = st;
99        }
100        /* Still no stream, then err */
101        if(!outstr) goto err;
102       
103        /* prepare the stream map & initialize*/
104        map = av_mallocz(sizeof(NCStream));
105        map->index = st->index;
106        map->str = outstr;
107       
108        if(st->codec->codec_type == CODEC_TYPE_VIDEO)
109                initialize_video_map(map, targetTrack, dataRef, dataRefType, storage->firstFrames + st->index);
110        else if(st->codec->codec_type == CODEC_TYPE_AUDIO)
111                initialize_audio_map(map, targetTrack, dataRef, dataRefType, storage->firstFrames + st->index);
112       
113        map->valid = map->media && map->sampleHdl;
114       
115        /* return the map */
116        storage->stream_map = map;
117       
118        return 0;
119err:
120                if(map)
121                        av_free(map);
122        return -1;
123} /* prepare_track() */
124
125/* A very large percent of movies have NTSC timebases (30/1.001) with misrounded fractions, so let's recover them. */
126static void rescue_ntsc_timebase(AVRational *base)
127{
128        av_reduce(&base->num, &base->den, base->num, base->den, INT_MAX);
129       
130        if (base->num == 1) return; // XXX is this good enough?
131       
132        double fTimebase = av_q2d(*base), nearest_ntsc = floor(fTimebase * 1001. + .5) / 1001.;
133        const double small_interval = 1./120.;
134       
135        if (fabs(fTimebase - nearest_ntsc) < small_interval)
136        {
137                base->num = 1001;
138                base->den = (1001. / fTimebase) + .5;
139        }
140}
141
142/* Initializes the map & targetTrack to receive video data */
143void initialize_video_map(NCStream *map, Track targetTrack, Handle dataRef, OSType dataRefType, AVPacket *firstFrame)
144{
145        Media media;
146        ImageDescriptionHandle imgHdl;
147        Handle imgDescExt = NULL;
148        AVCodecContext *codec;
149        double num,den;
150       
151        codec = map->str->codec;
152       
153        map->base = map->str->time_base;
154       
155        rescue_ntsc_timebase(&map->base);
156        if(map->base.num != 1001 && map->base.den > 100000) {
157                /* if that's the case, then we probably ran out of timevalues!
158                * a timescale of 100000 allows a movie duration of 5-6 hours
159                * so I think this will be enough */
160                den = map->base.den;
161                num = map->base.num;
162               
163                /* we use the following approach ##.### frames per second.
164                        * afterwards rounding */
165                den = den / num * 1000.0;
166                map->base.num = 1000;
167                map->base.den = round(den);
168        }
169       
170        media = NewTrackMedia(targetTrack, VideoMediaType, map->base.den, dataRef, dataRefType);
171        map->media = media;
172       
173        /* Create the Image Description Handle */
174        imgHdl = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription));
175        (*imgHdl)->idSize = sizeof(ImageDescription);
176       
177        if (!((*imgHdl)->cType = map_video_codec_to_mov_tag(codec->codec_id)))
178                if(!((*imgHdl)->cType = BSWAP(codec->codec_tag)))
179                        (*imgHdl)->cType = forced_map_video_codec_to_mov_tag(codec->codec_id);
180//      FourCCprintf("fourcc: ", (*imgHdl)->cType);
181       
182        (*imgHdl)->temporalQuality = codecMaxQuality;
183        (*imgHdl)->spatialQuality = codecMaxQuality;
184        (*imgHdl)->width = codec->width;
185        (*imgHdl)->height = codec->height;
186        (*imgHdl)->hRes = 72 << 16;
187        (*imgHdl)->vRes = 72 << 16;
188        (*imgHdl)->depth = codec->bits_per_coded_sample;
189        (*imgHdl)->clutID = -1; // no color lookup table...
190       
191        // 12 is invalid in mov
192        // XXX it might be better to set this based entirely on pix_fmt
193        if ((*imgHdl)->depth == 12 || (*imgHdl)->depth == 0) (*imgHdl)->depth = codec->pix_fmt == PIX_FMT_YUVA420P ? 32 : 24;
194       
195        /* Create the strf image description extension (see AVI's BITMAPINFOHEADER) */
196        imgDescExt = create_strf_ext(codec);
197        if (imgDescExt) {
198                AddImageDescriptionExtension(imgHdl, imgDescExt, 'strf');
199                DisposeHandle(imgDescExt);
200        }
201       
202        map->sampleHdl = (SampleDescriptionHandle)imgHdl;
203       
204} /* initialize_video_map() */
205
206/* Initializes the map & targetTrack to receive audio data */
207OSStatus initialize_audio_map(NCStream *map, Track targetTrack, Handle dataRef, OSType dataRefType, AVPacket *firstFrame)
208{
209        Media media;
210        SoundDescriptionHandle sndHdl = NULL;
211        AudioStreamBasicDescription asbd;
212        AVCodecContext *codec;
213        UInt32 ioSize;
214        OSStatus err = noErr;
215       
216        uint8_t *cookie = NULL;
217        size_t cookieSize = 0;
218       
219        codec = map->str->codec;
220        map->base = map->str->time_base;
221       
222        media = NewTrackMedia(targetTrack, SoundMediaType, codec->sample_rate, dataRef, dataRefType);
223       
224        map->media = media;
225       
226        memset(&asbd,0,sizeof(asbd));
227        map_avi_to_mov_tag(codec->codec_id, &asbd, map, codec->channels);
228        if(asbd.mFormatID == 0) /* no know codec, use the ms tag */
229                asbd.mFormatID = 'ms\0\0' + codec->codec_tag; /* the number is stored in the last byte => big endian */
230       
231        /* Ask the AudioToolbox about vbr of the codec */
232        // FIXME this sets vbr even if it was encoded in CBR mode
233        // which means our mBytesPerPacket is wrong for CBR mp3 (does not really matter)
234        ioSize = sizeof(UInt32);
235        AudioFormatGetProperty(kAudioFormatProperty_FormatIsVBR, sizeof(AudioStreamBasicDescription), &asbd, &ioSize, &map->vbr);
236       
237        cookie = create_cookie(codec, &cookieSize, asbd.mFormatID, map->vbr);
238       
239        /* Set some fields of the AudioStreamBasicDescription. Then ask the AudioToolbox
240                * to fill as much as possible before creating the SoundDescriptionHandle */
241        asbd.mSampleRate = codec->sample_rate;
242        asbd.mChannelsPerFrame = codec->channels;
243        if(!map->vbr && !asbd.mBytesPerPacket) /* This works for all the tested codecs. but is there any better way? */
244                asbd.mBytesPerPacket = codec->block_align; /* this is tested for alaw/mulaw/msadpcm */
245       
246        /* ask the toolbox about more information */
247        ioSize = sizeof(AudioStreamBasicDescription);
248        err = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, cookieSize, cookie, &ioSize, &asbd);
249        if (err || !asbd.mFormatID) {
250                fprintf(stderr, "AudioFormatGetProperty dislikes the magic cookie (error %ld / format id %lx)\n", err, asbd.mFormatID);
251                goto bail;
252        }
253       
254        /*
255         FIXME:
256         - at least ffmp3 does not set these values without parsing+the decoder being enabled
257           (so we avoid overwriting them from above for now)
258         - this possibly should be 0 for formats with variable framesPerPacket like Vorbis
259         - lavc frame_size is in samples, mFramesPerPacket is in frames (maybe)
260         */
261        if (!asbd.mFramesPerPacket)
262                asbd.mFramesPerPacket = codec->frame_size;
263        asbd.mBitsPerChannel = codec->bits_per_coded_sample;
264       
265        // if we don't have mBytesPerPacket, we can't import as CBR. Probably should be VBR, and the codec
266        // either lied about kAudioFormatProperty_FormatIsVBR or isn't present
267        if (asbd.mBytesPerPacket == 0)
268                map->vbr = 1;
269       
270        /* If we have vbr audio, the media scale most likely has to be set to the time_base denumerator */
271        if(map->vbr) {
272                /* if we have mFramesPerPacket, set mBytesPerPacket to 0 as this can cause
273                * errors if set incorrectly. But in vbr, we just need the mFramesPerPacket
274                * value */
275                if(asbd.mFramesPerPacket)
276                        asbd.mBytesPerPacket = 0;
277                SetMediaTimeScale(media, map->str->time_base.den);
278        }
279       
280        if (asbd.mFormatID == kAudioFormatLinearPCM)
281                asbd.mFramesPerPacket = 1;
282        else if (asbd.mBytesPerPacket) {
283                /* FIXME. in the MSADPCM codec, we get a wrong mFramesPerPacket entry because
284                 * of the difference in the sample_rate and the time_base denumerator. So we
285                 * recalculate here the mFramesPerPacket entry */
286
287                /* For calculation, lets assume a packet duration of 1, use ioSize as tmp storage */
288                ioSize = map->str->time_base.num * codec->sample_rate / map->str->time_base.den;
289                /* downscale to correct bytes_per_packet */
290                asbd.mFramesPerPacket = ioSize * asbd.mBytesPerPacket / codec->block_align;
291        }
292       
293        AudioChannelLayout acl;
294        int aclSize = 0;  //Set this if you intend to use it
295        memset(&acl, 0, sizeof(AudioChannelLayout));
296
297        /* We have to parse the format */
298        int useDefault = 1;
299        if(asbd.mFormatID == kAudioFormatAC3 || asbd.mFormatID == 'ms \0')
300        {
301                QTMetaDataRef trackMetaData;
302                OSErr error = QTCopyTrackMetaData(targetTrack, &trackMetaData);
303                if(error == noErr)
304                {
305                        const char *prop = "Surround";
306                        OSType key = 'name';
307                        error = QTMetaDataAddItem(trackMetaData, kQTMetaDataStorageFormatUserData, kQTMetaDataKeyFormatUserData, (UInt8 *)&key, sizeof(key), (UInt8 *)prop, strlen(prop), kQTMetaDataTypeUTF8, NULL);
308                        QTMetaDataRelease(trackMetaData);
309                }
310                if(parse_ac3_bitstream(&asbd, &acl, firstFrame->data, firstFrame->size))
311                {
312                        useDefault = 0;
313                        aclSize = sizeof(AudioChannelLayout);
314                }
315        }
316        if(useDefault && asbd.mChannelsPerFrame > 2)
317        {
318                asbd.mFramesPerPacket = 0;
319                acl = GetDefaultChannelLayout(&asbd);
320                aclSize = sizeof(AudioChannelLayout);
321        }
322       
323        if (asbd.mSampleRate > 0) {             
324                err = QTSoundDescriptionCreate(&asbd, aclSize == 0 ? NULL : &acl, aclSize, cookie, cookieSize, kQTSoundDescriptionKind_Movie_LowestPossibleVersion, &sndHdl);
325               
326                if(err) {
327                        fprintf(stderr, "AVI IMPORTER: Error %ld creating the sound description\n", err);
328                        goto bail;
329                }
330        }       
331        map->sampleHdl = (SampleDescriptionHandle)sndHdl;
332        map->asbd = asbd;
333       
334bail:
335        if(cookie)
336                av_free(cookie);
337       
338        return noErr;
339} /* initialize_audio_map() */
340
341OSType map_video_codec_to_mov_tag(enum CodecID codec_id)
342{
343        switch(codec_id) {
344                case CODEC_ID_FLV1:
345                        return 'FLV1';
346                case CODEC_ID_VP6F:
347                        return 'VP6F';
348                case CODEC_ID_FLASHSV:
349                        return 'FSV1';
350                case CODEC_ID_VP6A:
351                        return 'VP6A';
352        }
353        return 0;
354}
355
356OSType forced_map_video_codec_to_mov_tag(enum CodecID codec_id)
357{
358        switch (codec_id) {
359                case CODEC_ID_H264:
360                        return 'H264';
361                case CODEC_ID_MPEG4:
362                        return 'MP4S';
363        }
364        return 0;
365}
366
367/* maps the codec_id tag of libavformat to a constant the AudioToolbox can work with */
368void map_avi_to_mov_tag(enum CodecID codec_id, AudioStreamBasicDescription *asbd, NCStream *map, int channels)
369{
370        switch(codec_id) {
371                case CODEC_ID_MP2:
372                        asbd->mFormatID = kAudioFormatMPEGLayer2;
373                        break;
374                case CODEC_ID_MP3:
375                        asbd->mFormatID = kAudioFormatMPEGLayer3;
376                        break;
377                case CODEC_ID_AC3:
378                        asbd->mFormatID = kAudioFormatAC3MS;
379                        map->vbr = 1;
380                        break;
381                case CODEC_ID_PCM_S16LE:
382                        asbd->mFormatID = kAudioFormatLinearPCM;
383                        asbd->mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
384                        asbd->mBytesPerPacket = 2 * channels;
385                        break;
386                case CODEC_ID_PCM_U8:
387                        asbd->mFormatID = kAudioFormatLinearPCM;
388                        asbd->mFormatFlags = kLinearPCMFormatFlagIsBigEndian;
389                        asbd->mBytesPerPacket = channels;
390                        break;
391                case CODEC_ID_PCM_ALAW:
392                        asbd->mFormatID = kAudioFormatALaw;
393                        break;
394                case CODEC_ID_PCM_MULAW:
395                        asbd->mFormatID = kAudioFormatULaw;
396                        break;
397                case CODEC_ID_ADPCM_MS:
398                        asbd->mFormatID = kMicrosoftADPCMFormat;
399                        break;
400                case CODEC_ID_AAC:
401                        asbd->mFormatID = kAudioFormatMPEG4AAC;
402                        break;
403                case CODEC_ID_VORBIS:
404                        asbd->mFormatID = 'OggV';
405                        break;
406                case CODEC_ID_DTS:
407                        asbd->mFormatID = kAudioFormatDTS;
408                        map->vbr = 1;
409                        break;
410                case CODEC_ID_ADPCM_SWF:
411                        asbd->mFormatID = kAudioFormatFlashADPCM;
412                        break;
413                case CODEC_ID_TTA:
414                        asbd->mFormatID = kAudioFormatTTA;
415                        break;
416                case CODEC_ID_NELLYMOSER:
417                        asbd->mFormatID = kAudioFormatNellymoser;
418                        break;
419                default:
420                        break;
421        }
422} /* map_avi_to_mov_tag() */
423
424/* This function creates a magic cookie basec on the codec parameter and formatID
425 * Return value: a pointer to a magic cookie which has to be av_free()'d
426 * in cookieSize, the size of the magic cookie is returned to the caller */
427uint8_t *create_cookie(AVCodecContext *codec, size_t *cookieSize, UInt32 formatID, int vbr)
428{
429        uint8_t *result = NULL;
430        uint8_t *ptr;
431        AudioFormatAtom formatAtom;
432        AudioTerminatorAtom termAtom;
433        long waveSize;
434        uint8_t *waveAtom = NULL;
435        int size = 0;
436       
437        if (formatID == kAudioFormatMPEG4AAC) {
438                return CreateEsdsFromSetupData(codec->extradata, codec->extradata_size, cookieSize, 1, true, false);
439        }
440       
441        /* Do we need an endia atom, too? */
442       
443        /* initialize the user Atom
444                * 8 bytes                       for the atom size & atom type
445                * 18 bytes                      for the already extracted part, see wav.c in the ffmpeg project
446                * extradata_size        for the data still stored in the AVCodecContext structure */
447        waveSize = 18 + codec->extradata_size + 8;
448        waveAtom = av_malloc(waveSize);
449       
450        /* now construct the wave atom */
451        /* QT Atoms are big endian, I think but the WAVE data should be little endian */
452        ptr = write_int32(waveAtom, EndianS32_NtoB(waveSize));
453        ptr = write_int32(ptr, EndianS32_NtoB(formatID));
454        ptr = write_int16(ptr, EndianS16_NtoL(codec->codec_tag));
455        ptr = write_int16(ptr, EndianS16_NtoL(codec->channels));
456        ptr = write_int32(ptr, EndianS32_NtoL(codec->sample_rate));
457        if(vbr)
458                ptr = write_int32(ptr, 0);
459        else
460                ptr = write_int32(ptr, EndianS32_NtoL(codec->bit_rate / 8));
461        ptr = write_int16(ptr, EndianS16_NtoL(codec->block_align));
462        ptr = write_int16(ptr, EndianS16_NtoL(codec->bits_per_coded_sample));
463        ptr = write_int16(ptr, EndianS16_NtoL(codec->extradata_size));
464        /* now the remaining stuff */
465        ptr = write_data(ptr, codec->extradata, codec->extradata_size);
466       
467        /* Calculate the size of the cookie */
468        size  = sizeof(formatAtom) + sizeof(termAtom) + waveSize;
469       
470        /* Audio Format Atom */
471        formatAtom.size = EndianS32_NtoB(sizeof(AudioFormatAtom));
472        formatAtom.atomType = EndianS32_NtoB(kAudioFormatAtomType);
473        formatAtom.format = EndianS32_NtoB(formatID);
474       
475        /* Terminator Atom */
476        termAtom.atomType = EndianS32_NtoB(kAudioTerminatorAtomType);
477        termAtom.size = EndianS32_NtoB(sizeof(AudioTerminatorAtom));
478       
479        result = av_malloc(size);
480       
481        /* use ptr to write to the result */
482        ptr = result;
483       
484        /* format atom */
485        ptr = write_data(ptr, (uint8_t*)&formatAtom, sizeof(formatAtom));
486        ptr = write_data(ptr, waveAtom, waveSize);
487        ptr = write_data(ptr, (uint8_t*)&termAtom, sizeof(termAtom));
488       
489bail:
490                *cookieSize = size;
491        if(waveAtom)
492                av_free(waveAtom);
493        return result;
494} /* create_cookie() */
495
496/* This function creates an image description extension that some codecs need to be able
497 * to decode properly, a copy of the strf (BITMAPINFOHEADER) chunk in the avi.
498 * Return value: a handle to an image description extension which has to be DisposeHandle()'d
499 * in cookieSize, the size of the image description extension is returned to the caller */
500Handle create_strf_ext(AVCodecContext *codec)
501{
502        Handle result = NULL;
503        uint8_t *ptr;
504        long size;
505       
506        /* initialize the extension
507                * 40 bytes                      for the BITMAPINFOHEADER stucture, see avienc.c in the ffmpeg project
508                * extradata_size        for the data still stored in the AVCodecContext structure */
509        size = 40 + codec->extradata_size;
510        result = NewHandle(size);
511        if (result == NULL)
512                goto bail;
513       
514        /* construct the BITMAPINFOHEADER structure */
515        /* QT Atoms are big endian, but the STRF atom should be little endian */
516        ptr = write_int32((uint8_t *)*result, EndianS32_NtoL(size)); /* size */
517        ptr = write_int32(ptr, EndianS32_NtoL(codec->width));
518        ptr = write_int32(ptr, EndianS32_NtoL(codec->height));
519        ptr = write_int16(ptr, EndianS16_NtoL(1)); /* planes */
520       
521        ptr = write_int16(ptr, EndianS16_NtoL(codec->bits_per_coded_sample ? codec->bits_per_coded_sample : 24)); /* depth */
522        /* compression type */
523        ptr = write_int32(ptr, EndianS32_NtoL(codec->codec_tag));
524        ptr = write_int32(ptr, EndianS32_NtoL(codec->width * codec->height * 3));
525        ptr = write_int32(ptr, EndianS32_NtoL(0));
526        ptr = write_int32(ptr, EndianS32_NtoL(0));
527        ptr = write_int32(ptr, EndianS32_NtoL(0));
528        ptr = write_int32(ptr, EndianS32_NtoL(0));
529       
530        /* now the remaining stuff */
531        ptr = write_data(ptr, codec->extradata, codec->extradata_size);
532       
533        if (codec->extradata_size & 1) {
534                uint8_t zero = 0;
535                ptr = write_data(ptr, &zero, 1);
536        }
537       
538bail:
539        return result;
540} /* create_extension() */
541
542/* Add the meta data that lavf exposes to the movie */
543static void add_metadata(AVFormatContext *ic, Movie theMovie)
544{
545    QTMetaDataRef movie_metadata;
546    OSType key, err;
547   
548    err = QTCopyMovieMetaData(theMovie, &movie_metadata);
549    if (err) return;
550   
551        if (strlen(ic->title)) {
552                key = kQTMetaDataCommonKeyDisplayName;
553                QTMetaDataAddItem(movie_metadata, kQTMetaDataStorageFormatQuickTime, kQTMetaDataKeyFormatCommon, 
554                                  (UInt8 *)&key, sizeof(key), (UInt8 *)ic->title, strlen(ic->title), kQTMetaDataTypeUTF8, NULL);
555        }
556        if (strlen(ic->author)) {
557                key = kQTMetaDataCommonKeyAuthor;
558                QTMetaDataAddItem(movie_metadata, kQTMetaDataStorageFormatQuickTime, kQTMetaDataKeyFormatCommon, 
559                                  (UInt8 *)&key, sizeof(key), (UInt8 *)ic->author, strlen(ic->author), kQTMetaDataTypeUTF8, NULL);
560        }
561        if (strlen(ic->copyright)) {
562                key = kQTMetaDataCommonKeyCopyright;
563                QTMetaDataAddItem(movie_metadata, kQTMetaDataStorageFormatQuickTime, kQTMetaDataKeyFormatCommon, 
564                                  (UInt8 *)&key, sizeof(key), (UInt8 *)ic->copyright, strlen(ic->copyright), kQTMetaDataTypeUTF8, NULL);
565        }
566        if (strlen(ic->comment)) {
567                key = kQTMetaDataCommonKeyComment;
568                QTMetaDataAddItem(movie_metadata, kQTMetaDataStorageFormatQuickTime, kQTMetaDataKeyFormatCommon, 
569                                  (UInt8 *)&key, sizeof(key), (UInt8 *)ic->comment, strlen(ic->comment), kQTMetaDataTypeUTF8, NULL);
570        }
571        if (strlen(ic->album)) {
572                key = kQTMetaDataCommonKeyComment;
573                QTMetaDataAddItem(movie_metadata, kQTMetaDataStorageFormatQuickTime, kQTMetaDataKeyFormatCommon, 
574                                  (UInt8 *)&key, sizeof(key), (UInt8 *)ic->album, strlen(ic->album), kQTMetaDataTypeUTF8, NULL);
575        }
576        if (strlen(ic->genre)) {
577                key = kQTMetaDataCommonKeyGenre;
578                QTMetaDataAddItem(movie_metadata, kQTMetaDataStorageFormatQuickTime, kQTMetaDataKeyFormatCommon, 
579                                  (UInt8 *)&key, sizeof(key), (UInt8 *)ic->genre, strlen(ic->genre), kQTMetaDataTypeUTF8, NULL);
580        }
581    QTMetaDataRelease(movie_metadata);
582}
583
584static void get_track_dimensions_for_codec(AVStream *st, Fixed *fixedWidth, Fixed *fixedHeight)
585{       
586        AVCodecContext *codec = st->codec;
587        *fixedHeight = IntToFixed(codec->height);
588
589        if (!st->sample_aspect_ratio.num) *fixedWidth = IntToFixed(codec->width);
590        else *fixedWidth = FloatToFixed(codec->width * av_q2d(st->sample_aspect_ratio));
591}
592
593// Create the 'pasp' atom for video tracks. No guesswork required.
594// References http://www.uwasa.fi/~f76998/video/conversion/
595void set_track_clean_aperture_ext(ImageDescriptionHandle imgDesc, Fixed displayW, Fixed displayH, Fixed pixelW, Fixed pixelH)
596{
597        if (displayW == pixelW && displayH == pixelH)
598                return;
599               
600        AVRational dar, invPixelSize, sar;
601       
602        dar                        = (AVRational){displayW, displayH};
603        invPixelSize   = (AVRational){pixelH, pixelW};
604        sar = av_mul_q(dar, invPixelSize);
605       
606        av_reduce(&sar.num, &sar.den, sar.num, sar.den, fixed1);
607       
608        if (sar.num == sar.den)
609                return;
610               
611        PixelAspectRatioImageDescriptionExtension **pasp = (PixelAspectRatioImageDescriptionExtension**)NewHandle(sizeof(PixelAspectRatioImageDescriptionExtension));
612       
613        **pasp = (PixelAspectRatioImageDescriptionExtension){EndianU32_NtoB(sar.num), EndianU32_NtoB(sar.den)};
614       
615        AddImageDescriptionExtension(imgDesc, (Handle)pasp, kPixelAspectRatioImageDescriptionExtension);
616       
617        DisposeHandle((Handle)pasp);
618}
619
620// Create the 'nclc' atom for video tracks. Guessed entirely from image size following ffdshow.
621// FIXME read H.264 VUI/MPEG2 etc and especially read chroma positioning information.
622// this needs the parsers working
623// References: http://developer.apple.com/quicktime/icefloe/dispatch019.html
624// http://www.mir.com/DMG/chroma.html
625void set_track_colorspace_ext(ImageDescriptionHandle imgDescHandle, Fixed displayW, Fixed displayH)
626{
627        ImageDescription *imgDesc = *imgDescHandle;
628        Boolean isHd, isPAL; // otherwise NTSC
629        AVRational palRatio = (AVRational){5, 4}, displayRatio = (AVRational){displayW, displayH};
630        int colorPrimaries, transferFunction, yuvMatrix;
631       
632        isHd  = imgDesc->height >  576;
633        isPAL = imgDesc->height == 576 || av_cmp_q(palRatio, displayRatio) == 0;
634       
635        NCLCColorInfoImageDescriptionExtension **nclc = (NCLCColorInfoImageDescriptionExtension**)NewHandle(sizeof(NCLCColorInfoImageDescriptionExtension));
636               
637        if (isHd) {
638                colorPrimaries = kQTPrimaries_ITU_R709_2;
639                transferFunction = kQTTransferFunction_ITU_R709_2;
640                yuvMatrix = kQTMatrix_ITU_R_709_2;
641        } else if (isPAL) {
642                colorPrimaries = kQTPrimaries_EBU_3213;
643                transferFunction = kQTTransferFunction_ITU_R709_2;
644                yuvMatrix = kQTMatrix_ITU_R_601_4;
645        } else {
646                colorPrimaries = kQTPrimaries_SMPTE_C;
647                transferFunction = kQTTransferFunction_ITU_R709_2;
648                yuvMatrix = kQTMatrix_ITU_R_601_4;
649        }
650       
651        **nclc = (NCLCColorInfoImageDescriptionExtension){EndianU32_NtoB(kVideoColorInfoImageDescriptionExtensionType),
652                                                                                                          EndianU16_NtoB(colorPrimaries),
653                                                                                                          EndianU16_NtoB(transferFunction),
654                                                                                                          EndianU16_NtoB(yuvMatrix)};
655       
656        AddImageDescriptionExtension(imgDescHandle, (Handle)nclc, kColorInfoImageDescriptionExtension);
657       
658        DisposeHandle((Handle)nclc);
659}
660
661/* This function prepares the movie to receivve the movie data,
662 * After success, *out_map points to a valid stream maping
663 * Return values:
664 *        0: ok
665 */
666OSStatus prepare_movie(ff_global_ptr storage, Movie theMovie, Handle dataRef, OSType dataRefType)
667{
668        int j;
669        AVStream *st;
670        NCStream *map;
671        Track track = NULL;
672        Track first_audio_track = NULL;
673        AVFormatContext *ic = storage->format_context;
674        OSStatus err = noErr;
675       
676        /* make the stream map structure */
677        map = av_mallocz(ic->nb_streams * sizeof(NCStream));
678       
679        for(j = 0; j < ic->nb_streams; j++) {
680               
681                st = ic->streams[j];
682                map[j].index = st->index;
683                map[j].str = st;
684                map[j].duration = -1;
685               
686                if(st->codec->codec_type == CODEC_TYPE_VIDEO) {
687                        Fixed width, height;
688                       
689                        get_track_dimensions_for_codec(st, &width, &height);
690                        track = NewMovieTrack(theMovie, width, height, kNoVolume);
691
692            // XXX Support for 'old' NUV files, that didn't put the codec_tag in the file.
693            if( st->codec->codec_id == CODEC_ID_NUV && st->codec->codec_tag == 0 ) {
694                st->codec->codec_tag = MKTAG( 'N', 'U', 'V', '1' );
695            }
696                       
697                        initialize_video_map(&map[j], track, dataRef, dataRefType, storage->firstFrames + j);
698                        set_track_clean_aperture_ext((ImageDescriptionHandle)map[j].sampleHdl, width, height, IntToFixed(st->codec->width), IntToFixed(st->codec->height));
699                        set_track_colorspace_ext((ImageDescriptionHandle)map[j].sampleHdl, width, height);
700                } else if (st->codec->codec_type == CODEC_TYPE_AUDIO) {
701                        if (st->codec->sample_rate > 0) {
702                                track = NewMovieTrack(theMovie, 0, 0, kFullVolume);
703                                err = initialize_audio_map(&map[j], track, dataRef, dataRefType, storage->firstFrames + j);
704                               
705                                if (first_audio_track == NULL)
706                                        first_audio_track = track;
707                                else
708                                        SetTrackAlternate(track, first_audio_track);
709                        }
710                } else
711                        continue;
712               
713                // can't import samples if neither of these were created.
714                map[j].valid = map[j].media && map[j].sampleHdl;
715               
716                if (err) {
717                        if (track)
718                                DisposeMovieTrack(track);
719                        return err;
720                }                       
721        }
722       
723    add_metadata(ic, theMovie);
724   
725        storage->stream_map = map;
726       
727        return 0;
728} /* prepare_movie() */
729
730int determine_header_offset(ff_global_ptr storage) {
731        AVFormatContext *formatContext;
732        AVPacket packet;
733        int result, i;
734       
735        formatContext = storage->format_context;
736        result = noErr;
737        storage->header_offset = 0;
738       
739        /* Seek backwards to get a manually read packet for file offset */
740        if(formatContext->streams[0]->index_entries == NULL || storage->componentType == 'FLV ')
741        {
742                storage->header_offset = 0;
743        }
744        else
745        {
746                int streamsRead = 0;
747                AVStream *st;
748               
749                result = av_seek_frame(formatContext, -1, 0, AVSEEK_FLAG_ANY);
750                if(result < 0) goto bail;
751               
752                result = av_read_frame(formatContext, &packet);
753                if(result < 0) goto bail;
754                st = formatContext->streams[packet.stream_index];
755               
756                /* read_packet will give the first decodable packet. However, that isn't necessarily
757                        the first entry in the index, so look for an entry with a matching size. */
758                for (i = 0; i < st->nb_index_entries; i++) {
759                        if (packet.dts == st->index_entries[i].timestamp) {
760                                storage->header_offset = packet.pos - st->index_entries[i].pos;
761                                break;
762                        }
763                }
764                while(streamsRead < formatContext->nb_streams)
765                {
766                        int streamIndex = packet.stream_index;
767                        if(storage->firstFrames[streamIndex].size == 0)
768                        {
769                                memcpy(storage->firstFrames + streamIndex, &packet, sizeof(AVPacket));
770                                streamsRead++;
771                                if(streamsRead == formatContext->nb_streams)
772                                        break;
773                        }
774                        else
775                                av_free_packet(&packet);
776                        formatContext->iformat->read_packet(formatContext, &packet);
777                }
778               
779                // seek back to the beginning, otherwise av_read_frame-based decoding will skip a few packets.
780                av_seek_frame(formatContext, -1, 0, AVSEEK_FLAG_ANY | AVSEEK_FLAG_BACKWARD);
781        }
782               
783bail:
784        return result;
785}
786
787/* This function imports the avi represented by the AVFormatContext to the movie media represented
788 * in the map function. The aviheader_offset is used to calculate the packet offset from the
789 * beginning of the file. It returns whether it was successful or not (i.e. whether the file had an index) */
790int import_using_index(ff_global_ptr storage, int *hadIndex, TimeValue *addedDuration) {
791        int j, k, l;
792        NCStream *map;
793        NCStream *ncstr;
794        AVFormatContext *ic;
795        AVStream *stream;
796        AVCodecContext *codec;
797        SampleReference64Ptr sampleRec;
798        int64_t header_offset, offset, duration;
799        short flags;
800        int sampleNum;
801        ComponentResult result = noErr;
802       
803        map = storage->stream_map;
804        ic = storage->format_context;
805        header_offset = storage->header_offset;
806
807        *hadIndex = 0;
808       
809        // AVIs without an index currently add one entry to the index; assume that
810        // such a small index means that we should idle import instead. It shouldn't
811        // take much longer if the index really is this small anyways.
812        for (j = 0; j < ic->nb_streams; j++) {
813                if (map[j].str->nb_index_entries <= 1)
814                        goto bail;
815        }
816       
817        //FLVs have unusable indexes, so don't even bother.
818        if(storage->componentType == 'FLV ')
819                goto bail;
820       
821        /* process each stream in ic */
822        for(j = 0; j < ic->nb_streams; j++) {
823                ncstr = &map[j];
824                stream = ncstr->str;
825                codec = stream->codec;
826               
827                /* no stream we can read */
828                if(!ncstr->valid)
829                        continue;
830               
831                /* no index, we might as well skip */
832                if(stream->nb_index_entries == 0)
833                        continue;
834               
835                sampleNum = 0;
836                ncstr->sampleTable = calloc(stream->nb_index_entries, sizeof(SampleReference64Record));
837                       
838                /* now parse the index entries */
839                for(k = 0; k < stream->nb_index_entries; k++) {
840                       
841                        /* file offset */
842                        offset = header_offset + stream->index_entries[k].pos;
843                       
844                        /* flags */
845                        flags = 0;
846                        if((stream->index_entries[k].flags & AVINDEX_KEYFRAME) == 0)
847                                flags |= mediaSampleNotSync;
848                       
849                        sampleRec = &ncstr->sampleTable[sampleNum++];
850                       
851                        /* set as many fields in sampleRec as possible */
852                        sampleRec->dataOffset.hi = offset >> 32;
853                        sampleRec->dataOffset.lo = (uint32_t)offset;
854                        sampleRec->dataSize = stream->index_entries[k].size;
855                        sampleRec->sampleFlags = flags;
856                       
857                        /* some samples have a data_size of zero. if that's the case, ignore them
858                                * they seem to be used to stretch the frame duration & are already handled
859                                * by the previous pkt */
860                        if(sampleRec->dataSize <= 0) {
861                                sampleNum--;
862                                continue;
863                        }
864                       
865                        /* switch for the remaining fields */
866                        if(codec->codec_type == CODEC_TYPE_VIDEO) {
867                               
868                                /* Calculate the frame duration */
869                                duration = 1;
870                                for(l = k+1; l < stream->nb_index_entries; l++) {
871                                        if(stream->index_entries[l].size > 0)
872                                                break;
873                                        duration++;
874                                }
875                               
876                                sampleRec->durationPerSample = map->base.num * duration;
877                                sampleRec->numberOfSamples = 1;
878                        }
879                        else if(codec->codec_type == CODEC_TYPE_AUDIO) {
880                               
881                                /* FIXME: check if that's really the right thing to do here */
882                                if(ncstr->vbr) {
883                                        sampleRec->numberOfSamples = 1;
884                                       
885                                        if (k + 1 < stream->nb_index_entries)
886                                                sampleRec->durationPerSample = (stream->index_entries[k+1].timestamp - stream->index_entries[k].timestamp) * ncstr->base.num;
887                                        else if (sampleNum - 2 >= 0)
888                                                // if we're at the last index entry, use the duration of the previous sample
889                                                // FIXME: this probably could be better
890                                                sampleRec->durationPerSample = ncstr->sampleTable[sampleNum-2].durationPerSample;
891                                       
892                                } else {
893                                        sampleRec->durationPerSample = 1;
894                                        sampleRec->numberOfSamples = (stream->index_entries[k].size * ncstr->asbd.mFramesPerPacket) / ncstr->asbd.mBytesPerPacket;
895                                }
896                        }
897                }
898                if(sampleNum != 0)
899                {
900                        /* Add all of the samples to the media */
901                        AddMediaSampleReferences64(ncstr->media, ncstr->sampleHdl, sampleNum, ncstr->sampleTable, NULL);
902
903                        /* The index is both present and not empty */
904                        *hadIndex = 1;
905                }
906                free(ncstr->sampleTable);                       
907        }
908       
909        if(*hadIndex == 0)
910                //No index, the remainder of this function will fail.
911                goto bail;
912       
913        // insert media and set addedDuration;
914        for(j = 0; j < storage->map_count && result == noErr; j++) {
915                ncstr = &map[j];
916                if(ncstr->valid) {
917                        Media media = ncstr->media;
918                        Track track;
919                        TimeRecord time;
920                        TimeValue mediaDuration;
921                        TimeScale mediaTimeScale;
922                        TimeScale movieTimeScale;
923                        int startTime = map[j].str->index_entries[0].timestamp;
924
925                        mediaDuration = GetMediaDuration(media);
926                        mediaTimeScale = GetMediaTimeScale(media);
927                        movieTimeScale = GetMovieTimeScale(storage->movie);
928                       
929                        /* we could handle this stream.
930                        * convert the atTime parameter to track scale.
931                        * FIXME: check if that's correct */
932                        time.value.hi = 0;
933                        time.value.lo = storage->atTime;
934                        time.scale = movieTimeScale;
935                        time.base = NULL;
936                        ConvertTimeScale(&time, mediaTimeScale);
937                       
938                        track = GetMediaTrack(media);
939                        result = InsertMediaIntoTrack(track, time.value.lo, 0, mediaDuration, fixed1);
940
941                        // set audio/video start delay
942                        // note str.start_time exists but is always 0 for AVI
943                        if (startTime) {
944                                TimeRecord startTimeRec;
945                                startTimeRec.value.hi = 0;
946                                startTimeRec.value.lo = startTime * map[j].str->time_base.num;
947                                startTimeRec.scale = map[j].str->time_base.den;
948                                startTimeRec.base = NULL;
949                                ConvertTimeScale(&startTimeRec, movieTimeScale);
950                                SetTrackOffset(track, startTimeRec.value.lo);
951                        }
952                       
953                        if(result != noErr)
954                                goto bail;
955                       
956                        time.value.hi = 0;
957                        time.value.lo = mediaDuration;
958                        time.scale = mediaTimeScale;
959                        time.base = NULL;
960                        ConvertTimeScale(&time, movieTimeScale);
961                       
962                        if(time.value.lo > *addedDuration)
963                                *addedDuration = time.value.lo;
964                }
965        }
966       
967        storage->loadedTime = *addedDuration;
968       
969bail:
970        return result;
971} /* import_using_index() */
972
973/* Import function for movies that lack an index.
974 * Supports progressive importing, but will not idle if maxFrames == 0.
975 */
976ComponentResult import_with_idle(ff_global_ptr storage, long inFlags, long *outFlags, int minFrames, int maxFrames, bool addSamples) {
977        SampleReference64Record sampleRec;
978        DataHandler dataHandler;
979        AVFormatContext *formatContext;
980        AVCodecContext *codecContext;
981        AVStream *stream;
982        AVPacket packet;
983        NCStream *ncstream;
984        ComponentResult dataResult; //used for data handler operations that can fail.
985        ComponentResult result;
986        TimeValue minLoadedTime;
987        TimeValue movieTimeScale = GetMovieTimeScale(storage->movie);
988        int64_t availableSize, margin;
989        long idling;
990        int readResult, framesProcessed, i;
991        int firstPts[storage->map_count];
992        short flags;
993       
994        dataHandler = storage->dataHandler;
995        formatContext = storage->format_context;
996        dataResult = noErr;
997        result = noErr;
998        minLoadedTime = -1;
999        availableSize = 0;
1000        margin = 0;
1001        idling = (inFlags & movieImportWithIdle);
1002        framesProcessed = 0;
1003               
1004        if(idling) {
1005                //get the size of immediately available data
1006                if(storage->dataHandlerSupportsWideOffsets) {
1007                        wide wideSize;
1008                       
1009                        dataResult = DataHGetAvailableFileSize64(storage->dataHandler, &wideSize);
1010                        if(dataResult == noErr) availableSize = ((int64_t)wideSize.hi << 32) + wideSize.lo;
1011                } else {
1012                        long longSize;
1013                       
1014                        dataResult = DataHGetAvailableFileSize(storage->dataHandler, &longSize);
1015                        if(dataResult == noErr) availableSize = longSize;
1016                }
1017        }
1018       
1019        for(i = 0; i < storage->map_count; i++)
1020                firstPts[i] = -1;
1021       
1022        // record stream durations before we add any samples so that we know what to tell InsertMediaIntoTrack later
1023        for(i = 0; i < storage->map_count; i++) {
1024                ncstream = &storage->stream_map[i];
1025                Media media = ncstream->media;
1026               
1027                if(media && ncstream->duration == -1)
1028                        ncstream->duration = GetMediaDuration(media);
1029        }
1030       
1031        while((readResult = av_read_frame(formatContext, &packet)) == 0) {             
1032                bool trustPacketDuration = true;
1033                ncstream = &storage->stream_map[packet.stream_index];
1034                stream = ncstream->str;
1035                codecContext = stream->codec;
1036                flags = 0;
1037               
1038                if (!ncstream->valid)
1039                        continue;
1040               
1041                if((packet.flags & PKT_FLAG_KEY) == 0)
1042                        flags |= mediaSampleNotSync;
1043               
1044                if(IS_NUV(storage->componentType) && codecContext->codec_id == CODEC_ID_MP3) trustPacketDuration = false;
1045                if(IS_FLV(storage->componentType) && codecContext->codec_id == CODEC_ID_H264) trustPacketDuration = false;
1046               
1047                memset(&sampleRec, 0, sizeof(sampleRec));
1048                sampleRec.dataOffset.hi = packet.pos >> 32;
1049                sampleRec.dataOffset.lo = (uint32_t)packet.pos;
1050                sampleRec.dataSize = packet.size;
1051                sampleRec.sampleFlags = flags;
1052                               
1053                if(firstPts[packet.stream_index] < 0)
1054                        firstPts[packet.stream_index] = packet.pts;
1055               
1056                if(packet.size > storage->largestPacketSize)
1057                        storage->largestPacketSize = packet.size;
1058               
1059                if(sampleRec.dataSize <= 0)
1060                        continue;
1061               
1062                if(codecContext->codec_type == CODEC_TYPE_AUDIO && !ncstream->vbr)
1063                        sampleRec.numberOfSamples = (packet.size * ncstream->asbd.mFramesPerPacket) / ncstream->asbd.mBytesPerPacket;
1064                else
1065                        sampleRec.numberOfSamples = 1; //packet.duration;
1066               
1067                //add any samples waiting to be added
1068                if(ncstream->lastSample.numberOfSamples > 0) {
1069                        //calculate the duration of the sample before adding it
1070                        ncstream->lastSample.durationPerSample = (packet.dts - ncstream->lastdts) * ncstream->base.num;
1071                       
1072                        AddMediaSampleReferences64(ncstream->media, ncstream->sampleHdl, 1, &ncstream->lastSample, NULL);
1073                }
1074               
1075
1076        // If this is a nuv file, then we want to set the duration to zero.
1077        // This is because the nuv container doesn't have the framesize info
1078        // for audio.
1079
1080                if(packet.duration == 0 || !trustPacketDuration) {
1081                        //no duration, we'll have to wait for the next packet to calculate it
1082                        // keep the duration of the last sample, so we can use it if it's the last frame
1083                        sampleRec.durationPerSample = ncstream->lastSample.durationPerSample;
1084                        ncstream->lastSample = sampleRec;
1085                        ncstream->lastdts = packet.dts;
1086                } else {
1087                        ncstream->lastSample.numberOfSamples = 0;
1088                       
1089                        if(codecContext->codec_type == CODEC_TYPE_AUDIO && !ncstream->vbr)
1090                                sampleRec.durationPerSample = 1;
1091                        else
1092                                sampleRec.durationPerSample = ncstream->base.num * packet.duration;
1093
1094                        AddMediaSampleReferences64(ncstream->media, ncstream->sampleHdl, 1, &sampleRec, NULL);
1095                }
1096               
1097                framesProcessed++;
1098               
1099                //if we're idling, try really not to read past the end of available data
1100                //otherwise we will cause blocking i/o.
1101                if(idling && framesProcessed >= minFrames && availableSize > 0 && availableSize < storage->dataSize) {
1102                        margin = availableSize - (packet.pos + packet.size);
1103                        if(margin < (storage->largestPacketSize * 8)) { // 8x fudge factor for comfortable margin, could be tweaked.
1104                                av_free_packet(&packet);
1105                                break;
1106                        }
1107                }
1108               
1109                av_free_packet(&packet);
1110               
1111                //stop processing if we've hit the max frame limit
1112                if(maxFrames > 0 && framesProcessed >= maxFrames)
1113                        break;
1114        }
1115               
1116        if(readResult != 0) {
1117                //if readResult != 0, we've hit the end of the stream.
1118                //add any pending last frames.
1119                for(i = 0; i < formatContext->nb_streams; i++) {
1120                        ncstream = &storage->stream_map[i];
1121                        if(ncstream->lastSample.numberOfSamples > 0)
1122                                AddMediaSampleReferences64(ncstream->media, ncstream->sampleHdl, 1, &ncstream->lastSample, NULL);
1123                }
1124        }
1125       
1126        for(i = 0; i < storage->map_count && result == noErr; i++) {
1127                ncstream = &storage->stream_map[i];
1128                Media media = ncstream->media;
1129               
1130                if(ncstream->valid && (addSamples || readResult != 0)) {
1131                        Track track = GetMediaTrack(media);
1132                        TimeScale mediaTimeScale = GetMediaTimeScale(media);
1133                        TimeValue prevDuration = ncstream->duration;
1134                        TimeValue mediaDuration = GetMediaDuration(media);
1135                        TimeValue addedDuration = mediaDuration - prevDuration;
1136                        TimeValue mediaLoadedTime = movieTimeScale * mediaDuration / mediaTimeScale;
1137                       
1138                        if(minLoadedTime == -1 || mediaLoadedTime < minLoadedTime)
1139                                minLoadedTime = mediaLoadedTime;
1140                       
1141                        if(addedDuration > 0) {
1142                                result = InsertMediaIntoTrack(track, -1, prevDuration, addedDuration, fixed1);
1143                        }
1144                       
1145                        if (!prevDuration && firstPts[i] > 0) {
1146                                TimeRecord startTimeRec;
1147                                startTimeRec.value.hi = 0;
1148                                startTimeRec.value.lo = firstPts[i] * formatContext->streams[i]->time_base.num;
1149                                startTimeRec.scale = formatContext->streams[i]->time_base.den;
1150                                startTimeRec.base = NULL;
1151                                ConvertTimeScale(&startTimeRec, movieTimeScale);
1152                                SetTrackOffset(track, startTimeRec.value.lo);
1153                        }
1154                        ncstream->duration = -1;
1155                }
1156        }
1157       
1158        //set the loaded time to the length of the shortest track.
1159        if(minLoadedTime > 0)
1160                storage->loadedTime = minLoadedTime;
1161       
1162        if(readResult != 0) {
1163                //remove the placeholder track
1164                if(storage->placeholderTrack != NULL) {
1165                        DisposeMovieTrack(storage->placeholderTrack);
1166                        storage->placeholderTrack = NULL;
1167                }
1168               
1169                //set the movie load state to complete, as well as mark the import output flag.
1170                storage->movieLoadState = kMovieLoadStateComplete;
1171                *outFlags |= movieImportResultComplete;         
1172        } else {
1173                //if we're not yet done with the import, calculate the movie load state.
1174                int64_t timeToCompleteFile; //time until the file should be completely available, in terms of AV_TIME_BASE
1175                long dataRate = 0;
1176               
1177                dataResult = DataHGetDataRate(storage->dataHandler, 0, &dataRate);
1178                if(dataResult == noErr && dataRate > 0) {
1179                        timeToCompleteFile = (AV_TIME_BASE * (storage->dataSize - availableSize)) / dataRate;
1180                       
1181                        if(storage->loadedTime > (10 * GetMovieTimeScale(storage->movie)) && timeToCompleteFile < (storage->format_context->duration * .85))
1182                                storage->movieLoadState = kMovieLoadStatePlaythroughOK;
1183                        else
1184                                storage->movieLoadState = kMovieLoadStatePlayable;
1185                       
1186                } else {
1187                        storage->movieLoadState = kMovieLoadStatePlayable;
1188                }
1189               
1190                *outFlags |= movieImportResultNeedIdles;
1191        }
1192       
1193        send_movie_changed_notification(storage->movie);
1194       
1195        //tell the idle manager to idle us again in 500ms.
1196        if(idling && storage->idleManager && storage->isStreamed)
1197                QTIdleManagerSetNextIdleTimeDelta(storage->idleManager, 1, 2);
1198       
1199        return(result);
1200} /* import_with_idle() */
1201
1202ComponentResult create_placeholder_track(Movie movie, Track *placeholderTrack, TimeValue duration, Handle dataRef, OSType dataRefType) {
1203        SampleDescriptionHandle sdH = NULL;
1204        Media placeholderMedia;
1205        TimeScale movieTimeScale;
1206        ComponentResult result = noErr;
1207       
1208        movieTimeScale = GetMovieTimeScale(movie);
1209       
1210        sdH = (SampleDescriptionHandle)NewHandleClear(sizeof(SampleDescription));
1211        (*sdH)->descSize = sizeof(SampleDescription);
1212       
1213        *placeholderTrack = NewMovieTrack(movie, 0, 0, kNoVolume);
1214        placeholderMedia = NewTrackMedia(*placeholderTrack, BaseMediaType, movieTimeScale, dataRef, dataRefType);
1215       
1216        result = AddMediaSampleReference(placeholderMedia, 0, 1, duration, sdH, 1, 0, NULL);
1217        if(result != noErr)
1218                goto bail;
1219       
1220        result = InsertMediaIntoTrack(*placeholderTrack, -1, 0, duration, fixed1);
1221       
1222bail:
1223        if (sdH)
1224                DisposeHandle((Handle) sdH);
1225        return(result);
1226}
1227
1228void send_movie_changed_notification(Movie movie) {
1229        QTAtomContainer container;
1230       
1231        if(QTNewAtomContainer(&container) == noErr) {
1232                QTAtom anAction;
1233                OSType whichAction = EndianU32_NtoB(kActionMovieChanged);
1234               
1235                OSErr err = QTInsertChild(container, kParentAtomIsContainer, kAction, 1, 0, 0, NULL, &anAction);
1236               
1237                if(err == noErr)
1238                        err = QTInsertChild(container, anAction, kWhichAction, 1, 0, sizeof(whichAction), &whichAction, NULL);
1239               
1240                if(err == noErr)
1241                        MovieExecuteWiredActions(movie, 0, container);
1242               
1243                err = QTDisposeAtomContainer(container);
1244        }       
1245}
Note: See TracBrowser for help on using the repository browser.