source: trunk/ff_private.c @ 989

Revision 989, 38.7 KB checked in by gbooker, 5 years ago (diff)

Added a boolean for indicating whether the container is well framed. Apparently, some AVI files are framed well for the first sound frame, but not thereafter. This breaks AC3 passthrough of some files on the ATV.

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