root/tags/perian-0.5/ff_private.c

Revision 103, 25.0 kB (checked in by dconrad, 2 years ago)

Merge [98] to [100] into 0.5

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