source: trunk/ff_private.c @ 72

Revision 72, 22.8 KB checked in by anonymous, 8 years ago (diff)

VP6 and Flash Screen Video decoders

Line 
1/*****************************************************************************
2*
3*  Avi Import Component Private Functions
4*
5*  Copyright(C) 2006 Christoph Naegeli <chn1@mac.com>
6*
7*  This program is free software ; you can redistribute it and/or modify
8*  it under the terms of the GNU General Public License as published by
9*  the Free Software Foundation ; either version 2 of the License, or
10*  (at your option) any later version.
11*
12*  This program is distributed in the hope that it will be useful,
13*  but WITHOUT ANY WARRANTY ; without even the implied warranty of
14*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15*  GNU General Public License for more details.
16*
17*  You should have received a copy of the GNU General Public License
18*  along with this program ; if not, write to the Free Software
19*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
20*
21****************************************************************************/
22
23#include "ff_private.h"
24#include "avcodec.h"
25#include "Codecprintf.h"
26
27#include <CoreServices/CoreServices.h>
28#include <AudioToolbox/AudioToolbox.h>
29
30#define BSWAP(a) ( (((a)&0xff) << 24) | (((a)&0xff00) << 8) | (((a)&0xff0000) >> 8) | (((a) >> 24) & 0xff) )
31
32/* This routine checks if the system requirements are fullfilled */
33ComponentResult check_system()
34{
35        ComponentResult result;
36        long systemVersion;
37       
38        result = Gestalt(gestaltSystemVersion, &systemVersion);
39        require_noerr(result,bail);
40       
41        /* Make sure we have at least 10.4 installed...*/
42        if(systemVersion < 0x00001040)
43                result = -1;
44       
45bail:
46                return result;
47} /* check_system() */
48
49/* This routine does register the ffmpeg parsers which would normally
50 * be registred through the normal initialization process */
51void register_parsers()
52{
53        /* Do we need more parsers here? */
54    av_register_codec_parser(&mpegaudio_parser);
55        av_register_codec_parser(&ac3_parser);
56} /* register_parsers() */
57
58
59/* This function prepares the target Track to receivve the movie data,
60 * it is called if QT has asked an import operation which should just
61 * load this track. After success, *outmap points to a valid stream maping
62 * Return values:
63 *        0: ok
64 *      < 0: couldn't find a matching track
65 */
66int prepare_track(AVFormatContext *ic, NCStream **out_map, Track targetTrack, Handle dataRef, OSType dataRefType)
67{
68        int j;
69        AVStream *st;
70        AVStream *outstr = NULL;
71        Media media;
72        NCStream *map = NULL;
73       
74        /* If the track has already a media, return an err */
75        media = GetTrackMedia(targetTrack);
76        if(media) goto err;
77       
78        /* Search the AVFormatContext for a video stream */
79        for(j = 0; j < ic->nb_streams && !outstr; j++) {
80                st = ic->streams[j];
81                if(st->codec->codec_type == CODEC_TYPE_VIDEO)
82                        outstr = st;
83        }
84        /* Search the AVFormatContext for an audio stream (no video stream exists) */
85        for(j = 0; j < ic->nb_streams && !outstr; j++) {
86                st = ic->streams[j];
87                if(st->codec->codec_type == CODEC_TYPE_AUDIO)
88                        outstr = st;
89        }
90        /* Still no stream, then err */
91        if(!outstr) goto err;
92       
93        /* prepare the stream map & initialize*/
94        map = av_mallocz(sizeof(NCStream));
95        map->index = st->index;
96        map->str = outstr;
97       
98        if(st->codec->codec_type == CODEC_TYPE_VIDEO)
99                initialize_video_map(map, targetTrack, dataRef, dataRefType);
100        else if(st->codec->codec_type == CODEC_TYPE_AUDIO)
101                initialize_audio_map(map, targetTrack, dataRef, dataRefType);
102       
103        /* return the map */
104        *out_map = map;
105       
106        return 0;
107err:
108                if(map)
109                        av_free(map);
110        return -1;
111} /* prepare_track() */
112
113/* Initializes the map & targetTrack to receive video data */
114void initialize_video_map(NCStream *map, Track targetTrack, Handle dataRef, OSType dataRefType)
115{
116        Media media;
117        ImageDescriptionHandle imgHdl;
118        AVCodecContext *codec;
119        double num,den;
120       
121        codec = map->str->codec;
122       
123        map->base = map->str->time_base;
124        if(map->base.den > 100000) {
125                /* if that's the case, then we probably ran out of timevalues!
126                * a timescale of 100000 allows a movie duration of 5-6 hours
127                * so I think this will be enough */
128                den = map->base.den;
129                num = map->base.num;
130               
131                /* we use the following approach ##.### frames per second.
132                        * afterwards rounding */
133                den = den / num * 1000.0;
134                map->base.num = 1000;
135                map->base.den = round(den);
136        }
137       
138        media = NewTrackMedia(targetTrack, VideoMediaType, map->base.den, dataRef, dataRefType);
139        map->media = media;
140       
141        /* Create the Image Description Handle */
142        imgHdl = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription));
143        (*imgHdl)->idSize = sizeof(ImageDescription);
144       
145        if (codec->codec_tag)
146                (*imgHdl)->cType = BSWAP(codec->codec_tag);
147        else
148                // need to lookup the fourcc from the codec_id
149                (*imgHdl)->cType = map_video_codec_to_mov_tag(codec->codec_id);
150        Codecprintf(NULL, "fourcc: %c%c%c%c\n",0xff & (*imgHdl)->cType,0xff & (*imgHdl)->cType>>8,0xff & (*imgHdl)->cType>>16,0xff & (*imgHdl)->cType>>24);
151       
152        (*imgHdl)->temporalQuality = codecMaxQuality;
153        (*imgHdl)->spatialQuality = codecMaxQuality;
154        (*imgHdl)->width = codec->width;
155        (*imgHdl)->height = codec->height;
156        (*imgHdl)->hRes = 72 << 16;
157        (*imgHdl)->vRes = 72 << 16;
158        (*imgHdl)->depth = codec->bits_per_sample;
159        (*imgHdl)->clutID = -1; // no color lookup table...
160       
161        map->sampleHdl = (SampleDescriptionHandle)imgHdl;
162       
163} /* initialize_video_map() */
164
165/* Initializes the map & targetTrack to receive audio data */
166void initialize_audio_map(NCStream *map, Track targetTrack, Handle dataRef, OSType dataRefType)
167{
168        Media media;
169        SoundDescriptionHandle sndHdl;
170        AudioStreamBasicDescription asbd;
171        AVCodecContext *codec;
172        UInt32 ioSize;
173        OSStatus err;
174       
175        uint8_t *cookie = NULL;
176        int cookieSize = 0;
177       
178        codec = map->str->codec;
179        map->base = map->str->time_base;
180       
181        media = NewTrackMedia(targetTrack, SoundMediaType, codec->sample_rate, dataRef, dataRefType);
182       
183        map->media = media;
184       
185        memset(&asbd,0,sizeof(asbd));
186        map_avi_to_mov_tag(codec->codec_id, &asbd);
187        if(asbd.mFormatID == 0) /* no know codec, use the ms tag */
188                asbd.mFormatID = 'ms\0\0' + codec->codec_tag; /* the number is stored in the last byte => big endian */
189       
190        /* Ask the AudioToolbox about vbr of the codec */
191        ioSize = sizeof(UInt32);
192        AudioFormatGetProperty(kAudioFormatProperty_FormatIsVBR, sizeof(AudioStreamBasicDescription), &asbd, &ioSize, &map->vbr);
193       
194        /* Set some fields of the AudioStreamBasicDescription. Then ask the AudioToolbox
195                * to fill as much as possible before creating the SoundDescriptionHandle */
196        asbd.mSampleRate = codec->sample_rate;
197        asbd.mChannelsPerFrame = codec->channels;
198        if(!map->vbr) /* This works for all the tested codecs. but is there any better way? */
199                asbd.mBytesPerPacket = codec->block_align; /* this is tested for alaw/mulaw/msadpcm */
200        asbd.mFramesPerPacket = codec->frame_size; /* works for mp3, all other codecs this is 0 anyway */
201        asbd.mBitsPerChannel = codec->bits_per_sample;
202       
203        // this probably isn't quite right; FLV doesn't set frame_size or block_align,
204        // but we need > 0 frames per packet or Apple's mp3 decoder won't work
205        if (asbd.mBytesPerPacket == 0 && asbd.mFramesPerPacket == 0)
206                asbd.mFramesPerPacket = 1;
207       
208        /* ask the toolbox about more information */
209        ioSize = sizeof(AudioStreamBasicDescription);
210        AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &ioSize, &asbd);
211       
212        /* If we have vbr audio, the media scale most likely has to be set to the time_base denumerator */
213        if(map->vbr) {
214                /* if we have mFramesPerPacket, set mBytesPerPacket to 0 as this can cause
215                * errors if set incorrectly. But in vbr, we just need the mFramesPerPacket
216                * value */
217                if(asbd.mFramesPerPacket)
218                        asbd.mBytesPerPacket = 0;
219                SetMediaTimeScale(media, map->str->time_base.den);
220        }
221       
222        /* FIXME. in the MSADPCM codec, we get a wrong mFramesPerPacket entry because
223                * of the difference in the sample_rate and the time_base denumerator. So we
224                * recalculate here the mFramesPerPacket entry */
225        if(asbd.mBytesPerPacket) {
226                /* For calculation, lets assume a packet duration of 1, use ioSize as tmp storage */
227                ioSize = map->str->time_base.num * codec->sample_rate / map->str->time_base.den;
228                /* downscale to correct bytes_per_packet */
229                asbd.mFramesPerPacket = ioSize * asbd.mBytesPerPacket / codec->block_align;
230        }
231       
232        /* here use a version1, because version2 will fail! (no idea why)
233                * and as we are using version1, we may not have more than 2 channels.
234                * perhaps we should go to version2 some day. */
235        if(asbd.mChannelsPerFrame > 2)
236                asbd.mChannelsPerFrame = 2;
237        err = QTSoundDescriptionCreate(&asbd, NULL, 0, NULL, 0, kQTSoundDescriptionKind_Movie_LowestPossibleVersion, &sndHdl);
238        if(err) fprintf(stderr, "AVI IMPORTER: Error creating the sound description\n");
239       
240        /* Create the magic cookie */
241        cookie = create_cookie(codec, &cookieSize, asbd.mFormatID);
242        if(cookie) {
243                err = QTSoundDescriptionSetProperty(sndHdl, kQTPropertyClass_SoundDescription, kQTSoundDescriptionPropertyID_MagicCookie,
244                                                                                        cookieSize, cookie);
245                if(err) fprintf(stderr, "AVI IMPORTER: Error appending the magic cookie to the sound description\n");
246                av_free(cookie);
247        }
248       
249        map->sampleHdl = (SampleDescriptionHandle)sndHdl;
250        map->asbd = asbd;
251} /* initialize_audio_map() */
252
253OSType map_video_codec_to_mov_tag(enum CodecID codec_id)
254{
255        switch(codec_id) {
256                case CODEC_ID_FLV1:
257                        return 'FLV1';
258                case CODEC_ID_VP6F:
259                        return 'VP6F';
260                case CODEC_ID_FLASHSV:
261                        return 'FSV1';
262        }
263        return 0;
264}
265
266/* maps the codec_id tag of libavformat to a constant the AudioToolbox can work with */
267void map_avi_to_mov_tag(enum CodecID codec_id, AudioStreamBasicDescription *asbd)
268{
269        switch(codec_id) {
270                case CODEC_ID_MP2:
271                        asbd->mFormatID = kAudioFormatMPEGLayer2;
272                        break;
273                case CODEC_ID_MP3:
274                        asbd->mFormatID = kAudioFormatMPEGLayer3;
275                        break;
276                        /* currently we use ms four_char_code for ac3 */
277                        /* case CODEC_ID_AC3:
278                        asbd->mFormatID = kAudioFormatAC3;
279                        break; */
280                case CODEC_ID_PCM_S16LE:
281                        asbd->mFormatID = kAudioFormatLinearPCM;
282                        asbd->mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
283                        break;
284                case CODEC_ID_PCM_U8:
285                        asbd->mFormatID = kAudioFormatLinearPCM;
286                        asbd->mFormatFlags = kLinearPCMFormatFlagIsBigEndian;
287                        break;
288                case CODEC_ID_PCM_ALAW:
289                        asbd->mFormatID = kAudioFormatALaw;
290                        break;
291                case CODEC_ID_PCM_MULAW:
292                        asbd->mFormatID = kAudioFormatULaw;
293                        break;
294                case CODEC_ID_ADPCM_MS:
295                        asbd->mFormatID = kMicrosoftADPCMFormat;
296                        break;
297                case CODEC_ID_AAC:
298                case CODEC_ID_MPEG4AAC:
299                        asbd->mFormatID = kAudioFormatMPEG4AAC;
300                        break;
301                case CODEC_ID_VORBIS:
302                        asbd->mFormatID = 'OggV';
303                        break;
304                default:
305                        break;
306        }
307} /* map_avi_to_mov_tag() */
308
309/* This function creates a magic cookie basec on the codec parameter and formatID
310 * Return value: a pointer to a magic cookie which has to be av_free()'d
311 * in cookieSize, the size of the magic cookie is returned to the caller */
312uint8_t *create_cookie(AVCodecContext *codec, int *cookieSize, UInt32 formatID)
313{
314        uint8_t *result = NULL;
315        uint8_t *ptr;
316        AudioFormatAtom formatAtom;
317        AudioTerminatorAtom termAtom;
318        long waveSize;
319        uint8_t *waveAtom = NULL;
320        int size = 0;
321       
322        /* Do we need an endia atom, too? */
323       
324        /* initialize the user Atom
325                * 8 bytes                       for the atom size & atom type
326                * 18 bytes                      for the already extracted part, see wav.c in the ffmpeg project
327                * extradata_size        for the data still stored in the AVCodecContext structure */
328        waveSize = 18 + codec->extradata_size + 8;
329        waveAtom = av_malloc(waveSize);
330       
331        /* now construct the wave atom */
332        /* QT Atoms are big endian, I think but the WAVE data should be little endian */
333        ptr = write_int32(waveAtom, EndianS32_NtoB(waveSize));
334        ptr = write_int32(ptr, formatID);
335        ptr = write_int16(ptr, EndianS16_NtoL(codec->codec_tag));
336        ptr = write_int16(ptr, EndianS16_NtoL(codec->channels));
337        ptr = write_int32(ptr, EndianS32_NtoL(codec->sample_rate));
338        ptr = write_int32(ptr, EndianS32_NtoL(codec->bit_rate / 8));
339        ptr = write_int16(ptr, EndianS16_NtoL(codec->block_align));
340        ptr = write_int16(ptr, EndianS16_NtoL(codec->bits_per_sample));
341        ptr = write_int16(ptr, EndianS16_NtoL(codec->extradata_size));
342        /* now the remaining stuff */
343        ptr = write_data(ptr, codec->extradata, codec->extradata_size);
344       
345        /* Calculate the size of the cookie */
346        size  = sizeof(formatAtom) + sizeof(termAtom) + waveSize;
347       
348        /* Audio Format Atom */
349        formatAtom.size = sizeof(AudioFormatAtom);
350        formatAtom.atomType = kAudioFormatAtomType;
351        formatAtom.format = formatID;
352       
353        /* Terminator Atom */
354        termAtom.atomType = kAudioTerminatorAtomType;
355        termAtom.size = sizeof(AudioTerminatorAtom);
356       
357        result = av_malloc(size);
358       
359        /* use ptr to write to the result */
360        ptr = result;
361       
362        /* format atom */
363        ptr = write_data(ptr, (uint8_t*)&formatAtom, sizeof(formatAtom));
364        ptr = write_data(ptr, waveAtom, waveSize);
365        ptr = write_data(ptr, (uint8_t*)&termAtom, sizeof(termAtom));
366       
367bail:
368                *cookieSize = size;
369        if(waveAtom)
370                av_free(waveAtom);
371        return result;
372} /* create_cookie() */
373
374/* write the int32_t data to target & then return a pointer which points after that data */
375uint8_t *write_int32(uint8_t *target, int32_t data)
376{
377        return write_data(target, (uint8_t*)&data, sizeof(data));
378} /* write_int32() */
379
380/* write the int16_t data to target & then return a pointer which points after that data */
381uint8_t *write_int16(uint8_t *target, int16_t data)
382{
383        return write_data(target, (uint8_t*)&data, sizeof(data));
384} /* write_int16() */
385
386/* write the data to the target adress & then return a pointer which points after the written data */
387uint8_t *write_data(uint8_t *target, uint8_t* data, int32_t data_size)
388{
389        if(data_size > 0)
390                memcpy(target, data, data_size);
391        return (target + data_size);
392} /* write_data() */
393
394
395/* Add the meta data that lavf exposes to the movie */
396void add_metadata(AVFormatContext *ic, Movie theMovie)
397{
398    QTMetaDataRef movie_metadata;
399    OSType key, err;
400   
401    err = QTCopyMovieMetaData(theMovie, &movie_metadata);
402    if (err) return;
403   
404    key = kQTMetaDataCommonKeyDisplayName;
405    QTMetaDataAddItem(movie_metadata, kQTMetaDataStorageFormatQuickTime, kQTMetaDataKeyFormatCommon, 
406                      (UInt8 *)&key, sizeof(key), (UInt8 *)ic->title, strlen(ic->title), kQTMetaDataTypeUTF8, NULL);
407
408    key = kQTMetaDataCommonKeyAuthor;
409    QTMetaDataAddItem(movie_metadata, kQTMetaDataStorageFormatQuickTime, kQTMetaDataKeyFormatCommon, 
410                      (UInt8 *)&key, sizeof(key), (UInt8 *)ic->author, strlen(ic->author), kQTMetaDataTypeUTF8, NULL);
411
412    key = kQTMetaDataCommonKeyCopyright;
413    QTMetaDataAddItem(movie_metadata, kQTMetaDataStorageFormatQuickTime, kQTMetaDataKeyFormatCommon, 
414                      (UInt8 *)&key, sizeof(key), (UInt8 *)ic->copyright, strlen(ic->copyright), kQTMetaDataTypeUTF8, NULL);
415
416    key = kQTMetaDataCommonKeyComment;
417    QTMetaDataAddItem(movie_metadata, kQTMetaDataStorageFormatQuickTime, kQTMetaDataKeyFormatCommon, 
418                      (UInt8 *)&key, sizeof(key), (UInt8 *)ic->comment, strlen(ic->comment), kQTMetaDataTypeUTF8, NULL);
419
420    key = kQTMetaDataCommonKeyComment;
421    QTMetaDataAddItem(movie_metadata, kQTMetaDataStorageFormatQuickTime, kQTMetaDataKeyFormatCommon, 
422                      (UInt8 *)&key, sizeof(key), (UInt8 *)ic->album, strlen(ic->album), kQTMetaDataTypeUTF8, NULL);
423
424    key = kQTMetaDataCommonKeyGenre;
425    QTMetaDataAddItem(movie_metadata, kQTMetaDataStorageFormatQuickTime, kQTMetaDataKeyFormatCommon, 
426                      (UInt8 *)&key, sizeof(key), (UInt8 *)ic->genre, strlen(ic->genre), kQTMetaDataTypeUTF8, NULL);
427    QTMetaDataRelease(movie_metadata);
428}
429
430/* This function prepares the movie to receivve the movie data,
431 * After success, *out_map points to a valid stream maping
432 * Return values:
433 *        0: ok
434 */
435int prepare_movie(AVFormatContext *ic, NCStream **out_map, Movie theMovie, Handle dataRef, OSType dataRefType)
436{
437        int j;
438        AVStream *st;
439        NCStream *map;
440        Track track;
441        Track first_audio_track = NULL;
442       
443        /* make the stream map structure */
444        map = av_mallocz(ic->nb_streams * sizeof(NCStream));
445       
446        for(j = 0; j < ic->nb_streams; j++) {
447               
448                st = ic->streams[j];
449                map[j].index = st->index;
450                map[j].str = st;
451               
452                if(st->codec->codec_type == CODEC_TYPE_VIDEO) {
453                        track = NewMovieTrack(theMovie, st->codec->width << 16, st->codec->height << 16, kNoVolume);
454                        initialize_video_map(&map[j], track, dataRef, dataRefType);
455                } else {
456                        track = NewMovieTrack(theMovie, 0, 0, kFullVolume);
457                        initialize_audio_map(&map[j], track, dataRef, dataRefType);
458                       
459                        if (first_audio_track == NULL)
460                                first_audio_track = track;
461                        else
462                                SetTrackAlternate(track, first_audio_track);
463                }
464        }
465       
466    add_metadata(ic, theMovie);
467   
468        *out_map = map;
469       
470        return 0;
471} /* prepare_movie() */
472
473/* This function imports the avi represented by the AVFormatContext to the movie media represented
474 * in the map function. The aviheader_offset is used to calculate the packet offset from the
475 * beginning of the file */
476void import_avi(AVFormatContext *ic, NCStream *map, int64_t aviheader_offset)
477{
478        int j, k, l;
479        NCStream *ncstr;
480        AVStream *stream;
481        AVCodecContext *codec;
482        SampleReference64Record sampleRec;
483        int64_t offset,duration;
484        OSStatus err;
485        short flags;
486        short hadIndex = 0;
487       
488        /* process each stream in ic */
489        for(j = 0; j < ic->nb_streams; j++) {
490               
491                ncstr = &map[j];
492                stream = ncstr->str;
493                codec = stream->codec;
494               
495                /* no stream we can read */
496                if(!ncstr->media)
497                        continue;
498               
499                /* now parse the index entries */
500                for(k = 0; k < stream->nb_index_entries; k++) {
501                       
502                        hadIndex = 1;
503                       
504                        /* file offset */
505                        offset = aviheader_offset + stream->index_entries[k].pos;
506                       
507                        /* flags */
508                        flags = 0;
509                        if((stream->index_entries[k].flags & AVINDEX_KEYFRAME) == 0)
510                                flags |= mediaSampleNotSync;
511                       
512                        /* set as many fields in sampleRec as possible */
513                        memset(&sampleRec, 0, sizeof(sampleRec));
514                        sampleRec.dataOffset.hi = offset >> 32;
515                        sampleRec.dataOffset.lo = (uint32_t)offset;
516                        sampleRec.dataSize = stream->index_entries[k].size;
517                        sampleRec.sampleFlags = flags;
518                       
519                        /* some samples have a data_size of zero. if that's the case, ignore them
520                                * they seem to be used to stretch the frame duration & are already handled
521                                * by the previous pkt */
522                        if(sampleRec.dataSize <= 0)
523                                continue;
524                       
525                        /* switch for the remaining fields */
526                        if(codec->codec_type == CODEC_TYPE_VIDEO) {
527                               
528                                /* Calculate the frame duration */
529                                duration = 1;
530                                for(l = k+1; l < stream->nb_index_entries; l++) {
531                                        if(stream->index_entries[l].size > 0)
532                                                break;
533                                        duration++;
534                                }
535                               
536                                sampleRec.durationPerSample = map->base.num * duration;
537                                sampleRec.numberOfSamples = 1;
538                        }
539                        else if(codec->codec_type == CODEC_TYPE_AUDIO) {
540                               
541                                /* FIXME: check if that's really the right thing to do here */
542                                if(ncstr->vbr) {
543                                        if(codec->frame_size == ncstr->base.num) {
544                                                sampleRec.durationPerSample = codec->frame_size;
545                                                sampleRec.numberOfSamples = 1;
546                                        } else if (ncstr->asbd.mFormatID == kAudioFormatMPEG4AAC) {
547                                                /* AVI-mux GUI, the author of which created this hack in the first place,
548                                                * seems to special-case getting an AAC audio sample's duration this way */
549                                                sampleRec.durationPerSample = ic->streams[j]->time_base.num;
550                                                sampleRec.numberOfSamples = 1;
551                                        } else {
552                                                /* This seems to work. Although I have no idea why.
553                                                * Perhaps the stream's timebase is adjusted to
554                                                * let that work. as the timebase has strange values...*/
555                                                sampleRec.durationPerSample = sampleRec.dataSize;
556                                                sampleRec.numberOfSamples = 1;
557                                        }
558                                } else {
559                                        sampleRec.durationPerSample = 1;
560                                        sampleRec.numberOfSamples = (stream->index_entries[k].size * ncstr->asbd.mFramesPerPacket) / ncstr->asbd.mBytesPerPacket;
561                                }
562                        }
563                       
564                        /* Add the sample to the media */
565                        err = AddMediaSampleReferences64(ncstr->media, ncstr->sampleHdl, 1, &sampleRec, NULL);
566                }
567        }
568        if(hadIndex == 0)
569        {
570                AVPacket pkt;
571                while(av_read_frame(ic, &pkt) == noErr)
572                {
573                        ncstr = &map[pkt.stream_index];
574                        stream = ncstr->str;
575                        codec = stream->codec;
576                       
577                        flags = 0;
578                        if((pkt.flags & PKT_FLAG_KEY) == 0)
579                                flags |= mediaSampleNotSync;
580                       
581                        memset(&sampleRec, 0, sizeof(sampleRec));
582                        sampleRec.dataOffset.hi = pkt.pos >> 32;
583                        sampleRec.dataOffset.lo = (uint32_t) pkt.pos;
584                        sampleRec.dataSize = pkt.size;
585                        sampleRec.sampleFlags = flags;
586                       
587                        if(sampleRec.dataSize <= 0)
588                                continue;
589                       
590                        if (codec->codec_type == CODEC_TYPE_AUDIO && !ncstr->vbr)
591                                sampleRec.numberOfSamples = (pkt.size * ncstr->asbd.mFramesPerPacket) / ncstr->asbd.mBytesPerPacket;
592                        else
593                                sampleRec.numberOfSamples = 1;
594                               
595                        // we have a sample waiting to be added; calculate the duration and add it
596                        if (ncstr->lastSample.numberOfSamples > 0) {
597                                ncstr->lastSample.durationPerSample = (pkt.pts - ncstr->lastpts) * ncstr->base.num;
598                                AddMediaSampleReferences64(ncstr->media, ncstr->sampleHdl, 1, &ncstr->lastSample, NULL);
599                        }
600                       
601                        if (pkt.duration == 0) {
602                                // no duration, we'll have to wait for the next packet to calculate it
603                                // keep the duration of the last sample, so we can use it if it's the last frame
604                                sampleRec.durationPerSample = ncstr->lastSample.durationPerSample;
605                                ncstr->lastSample = sampleRec;
606                                ncstr->lastpts = pkt.pts;
607                               
608                        } else {
609                                ncstr->lastSample.numberOfSamples = 0;
610                                if (codec->codec_type == CODEC_TYPE_AUDIO && !ncstr->vbr)
611                                        sampleRec.durationPerSample = 1;
612                                else
613                                        sampleRec.durationPerSample = pkt.duration * ncstr->base.num;
614                                AddMediaSampleReferences64(ncstr->media, ncstr->sampleHdl, 1, &sampleRec, NULL);
615                        }
616#if 0
617                        if(codec->codec_type == CODEC_TYPE_VIDEO)
618                        {
619                                if(pkt.duration == 0)
620                                        sampleRec.durationPerSample = map->base.num;
621                                else
622                                        sampleRec.durationPerSample = map->base.num * pkt.duration;
623                                sampleRec.numberOfSamples = 1;
624                        }
625                        else if (codec->codec_type == CODEC_TYPE_AUDIO)
626                        {
627                                if(ncstr->vbr) {
628                                        if(codec->frame_size == ncstr->base.num) {
629                                                sampleRec.durationPerSample = codec->frame_size;
630                                                sampleRec.numberOfSamples = 1;
631                                        } else if (ncstr->asbd.mFormatID == kAudioFormatMPEG4AAC) {
632                                                /* AVI-mux GUI, the author of which created this hack in the first place,
633                                                * seems to special-case getting an AAC audio sample's duration this way */
634                                                sampleRec.durationPerSample = ic->streams[pkt.stream_index]->time_base.num;
635                                                sampleRec.numberOfSamples = 1;
636                                        } else {
637                                                /* This seems to work. Although I have no idea why.
638                                                * Perhaps the stream's timebase is adjusted to
639                                                * let that work. as the timebase has strange values...*/
640                                                sampleRec.durationPerSample = sampleRec.dataSize;
641                                                sampleRec.numberOfSamples = 1;
642                                        }
643                                } else {
644                                        sampleRec.durationPerSample = 1;
645                                        sampleRec.numberOfSamples = (pkt.size * ncstr->asbd.mFramesPerPacket) / ncstr->asbd.mBytesPerPacket;
646                                }
647                        }
648                        err = AddMediaSampleReferences64(ncstr->media, ncstr->sampleHdl, 1, &sampleRec, NULL);
649#endif
650                        //Need to do something like this when the libavformat doesn't give us a position
651/*                      Handle dataIn = NewHandle(pkt.size);
652                        HLock(dataIn);
653                        memcpy(*dataIn, pkt.data, pkt.size);
654                        HUnlock(dataIn);
655                        err = AddMediaSample(ncstr->media, dataIn, 0, pkt.size, 1, ncstr->sampleHdl, pkt.duration, sampleRec.sampleFlags, NULL);*/
656                        av_free_packet(&pkt);
657                }
658                // import the last frames
659                for (j = 0; j < ic->nb_streams; j++) {
660                        ncstr = &map[j];
661                        if (ncstr->lastSample.numberOfSamples > 0)
662                                AddMediaSampleReferences64(ncstr->media, ncstr->sampleHdl, 1, &ncstr->lastSample, NULL);
663                }
664        }
665} /* import_avi() */
Note: See TracBrowser for help on using the repository browser.