source: trunk/ff_private.c @ 1031

Revision 1031, 38.6 KB checked in by astrange, 6 years ago (diff)
  • Make ffmpeg compile (vhook is gone)
  • Fix several memory leaks and unused variables noticed by clang checker
  • Switch to DWARF debugging info again; it seems to work ok in Shark and gdb now

Import:

  • Remove an apparently pointles seek in AVI files
  • Fix one of my AVIs with no keyframes not importing
  • Return invalidMovie instead of -1 for movies that can't be opened

FFusion:

  • Delete offsetH/V and spooling code, which didn't do anything

Subtitles:

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