source: trunk/ff_private.c @ 1381

Revision 1381, 40.3 KB checked in by astrange, 3 years ago (diff)

AC3 has 1536 mFramesPerPacket, not 0

It's possible that this fixes something.

Refs #534

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