source: trunk/ff_private.c @ 194

Revision 194, 24.4 KB checked in by durin42, 8 years ago (diff)

Patch from Allan Hsu (allan <at> counterpop <dot> net):
Minor fixes to the fourcc printf patch and removal of an unused variable from my data handler fix.

Fixes #34. Many thanks Allan!

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