Commit | Line | Data |
---|---|---|
6cea494e ZK |
1 | /* |
2 | * MOV decoder. | |
19720f15 | 3 | * Copyright (c) 2001 Fabrice Bellard. |
6cea494e | 4 | * |
19720f15 FB |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Lesser General Public | |
7 | * License as published by the Free Software Foundation; either | |
8 | * version 2 of the License, or (at your option) any later version. | |
6cea494e | 9 | * |
19720f15 | 10 | * This library is distributed in the hope that it will be useful, |
6cea494e | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
19720f15 FB |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Lesser General Public License for more details. | |
6cea494e | 14 | * |
19720f15 FB |
15 | * You should have received a copy of the GNU Lesser General Public |
16 | * License along with this library; if not, write to the Free Software | |
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
6cea494e | 18 | */ |
d957696f MN |
19 | |
20 | #include <limits.h> | |
21 | ||
6cea494e ZK |
22 | #include "avformat.h" |
23 | #include "avi.h" | |
24 | ||
0147f198 FR |
25 | #ifdef CONFIG_ZLIB |
26 | #include <zlib.h> | |
27 | #endif | |
28 | ||
6cea494e ZK |
29 | /* |
30 | * First version by Francois Revol revol@free.fr | |
baf25c9d | 31 | * Seek function by Gael Chardon gael.dev@4now.net |
5ca1d879 | 32 | * |
6cea494e | 33 | * Features and limitations: |
5ca1d879 | 34 | * - reads most of the QT files I have (at least the structure), |
6cea494e | 35 | * the exceptions are .mov with zlib compressed headers ('cmov' section). It shouldn't be hard to implement. |
0147f198 | 36 | * FIXED, Francois Revol, 07/17/2002 |
6cea494e ZK |
37 | * - ffmpeg has nearly none of the usual QuickTime codecs, |
38 | * although I succesfully dumped raw and mp3 audio tracks off .mov files. | |
39 | * Sample QuickTime files with mp3 audio can be found at: http://www.3ivx.com/showcase.html | |
40 | * - .mp4 parsing is still hazardous, although the format really is QuickTime with some minor changes | |
41 | * (to make .mov parser crash maybe ?), despite what they say in the MPEG FAQ at | |
42 | * http://mpeg.telecomitalialab.com/faq.htm | |
43 | * - the code is quite ugly... maybe I won't do it recursive next time :-) | |
baf25c9d | 44 | * - seek is not supported with files that contain edit list |
5ca1d879 | 45 | * |
6cea494e ZK |
46 | * Funny I didn't know about http://sourceforge.net/projects/qt-ffmpeg/ |
47 | * when coding this :) (it's a writer anyway) | |
5ca1d879 | 48 | * |
6cea494e ZK |
49 | * Reference documents: |
50 | * http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt | |
51 | * Apple: | |
baf25c9d | 52 | * http://developer.apple.com/documentation/QuickTime/QTFF/ |
15c8dbe7 | 53 | * http://developer.apple.com/documentation/QuickTime/QTFF/qtff.pdf |
6cea494e ZK |
54 | * QuickTime is a trademark of Apple (AFAIK :)) |
55 | */ | |
56 | ||
c9a65ca8 | 57 | //#define DEBUG |
9ed83b0a ZK |
58 | #ifdef DEBUG |
59 | #include <stdio.h> | |
60 | #include <fcntl.h> | |
61 | #endif | |
6cea494e | 62 | |
b595afaa MM |
63 | #include "qtpalette.h" |
64 | ||
baf25c9d GC |
65 | |
66 | /* Allows seeking (MOV_SPLIT_CHUNKS should also be defined) */ | |
67 | #define MOV_SEEK | |
68 | ||
3ffe3793 FR |
69 | /* allows chunk splitting - should work now... */ |
70 | /* in case you can't read a file, try commenting */ | |
71 | #define MOV_SPLIT_CHUNKS | |
72 | ||
14342fd5 JC |
73 | /* Special handling for movies created with Minolta Dimaxe Xi*/ |
74 | /* this fix should not interfere with other .mov files, but just in case*/ | |
75 | #define MOV_MINOLTA_FIX | |
76 | ||
6cea494e ZK |
77 | /* some streams in QT (and in MP4 mostly) aren't either video nor audio */ |
78 | /* so we first list them as this, then clean up the list of streams we give back, */ | |
79 | /* getting rid of these */ | |
b6a17df4 | 80 | #define CODEC_TYPE_MOV_OTHER (enum CodecType) 2 |
6cea494e | 81 | |
a266644f | 82 | static const CodecTag mov_video_tags[] = { |
6cea494e | 83 | /* { CODEC_ID_, MKTAG('c', 'v', 'i', 'd') }, *//* Cinepak */ |
0147f198 FR |
84 | /* { CODEC_ID_H263, MKTAG('r', 'a', 'w', ' ') }, *//* Uncompressed RGB */ |
85 | /* { CODEC_ID_H263, MKTAG('Y', 'u', 'v', '2') }, *//* Uncompressed YUV422 */ | |
53e27dd5 | 86 | /* { CODEC_ID_RAWVIDEO, MKTAG('A', 'V', 'U', 'I') }, *//* YUV with alpha-channel (AVID Uncompressed) */ |
6cea494e ZK |
87 | /* Graphics */ |
88 | /* Animation */ | |
89 | /* Apple video */ | |
90 | /* Kodak Photo CD */ | |
3ffe3793 | 91 | { CODEC_ID_MJPEG, MKTAG('j', 'p', 'e', 'g') }, /* PhotoJPEG */ |
6cea494e | 92 | { CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', 'e', 'g') }, /* MPEG */ |
3ffe3793 | 93 | { CODEC_ID_MJPEG, MKTAG('m', 'j', 'p', 'a') }, /* Motion-JPEG (format A) */ |
6cea494e | 94 | { CODEC_ID_MJPEG, MKTAG('m', 'j', 'p', 'b') }, /* Motion-JPEG (format B) */ |
53e27dd5 SB |
95 | { CODEC_ID_MJPEG, MKTAG('A', 'V', 'D', 'J') }, /* MJPEG with alpha-channel (AVID JFIF meridien compressed) */ |
96 | /* { CODEC_ID_MJPEG, MKTAG('A', 'V', 'R', 'n') }, *//* MJPEG with alpha-channel (AVID ABVB/Truevision NuVista) */ | |
6cea494e ZK |
97 | /* { CODEC_ID_GIF, MKTAG('g', 'i', 'f', ' ') }, *//* embedded gif files as frames (usually one "click to play movie" frame) */ |
98 | /* Sorenson video */ | |
0147f198 FR |
99 | { CODEC_ID_SVQ1, MKTAG('S', 'V', 'Q', '1') }, /* Sorenson Video v1 */ |
100 | { CODEC_ID_SVQ1, MKTAG('s', 'v', 'q', '1') }, /* Sorenson Video v1 */ | |
3ffe3793 | 101 | { CODEC_ID_SVQ1, MKTAG('s', 'v', 'q', 'i') }, /* Sorenson Video v1 (from QT specs)*/ |
8b82a956 | 102 | { CODEC_ID_SVQ3, MKTAG('S', 'V', 'Q', '3') }, /* Sorenson Video v3 */ |
0e7eed09 | 103 | { CODEC_ID_MPEG4, MKTAG('m', 'p', '4', 'v') }, |
6cea494e | 104 | { CODEC_ID_MPEG4, MKTAG('D', 'I', 'V', 'X') }, /* OpenDiVX *//* sample files at http://heroinewarrior.com/xmovie.php3 use this tag */ |
8dafdb88 | 105 | { CODEC_ID_MPEG4, MKTAG('X', 'V', 'I', 'D') }, |
6cea494e | 106 | /* { CODEC_ID_, MKTAG('I', 'V', '5', '0') }, *//* Indeo 5.0 */ |
0147f198 | 107 | { CODEC_ID_H263, MKTAG('h', '2', '6', '3') }, /* H263 */ |
25fa62e1 | 108 | { CODEC_ID_H263, MKTAG('s', '2', '6', '3') }, /* H263 ?? works */ |
e1687cc0 FB |
109 | { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', ' ') }, /* DV NTSC */ |
110 | { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', 'p') }, /* DV PAL */ | |
53e27dd5 | 111 | /* { CODEC_ID_DVVIDEO, MKTAG('A', 'V', 'd', 'v') }, *//* AVID dv */ |
d86053a4 | 112 | { CODEC_ID_VP3, MKTAG('V', 'P', '3', '1') }, /* On2 VP3 */ |
2fdf638b MM |
113 | { CODEC_ID_RPZA, MKTAG('r', 'p', 'z', 'a') }, /* Apple Video (RPZA) */ |
114 | { CODEC_ID_CINEPAK, MKTAG('c', 'v', 'i', 'd') }, /* Cinepak */ | |
1dc1ed99 | 115 | { CODEC_ID_8BPS, MKTAG('8', 'B', 'P', 'S') }, /* Planar RGB (8BPS) */ |
b595afaa | 116 | { CODEC_ID_SMC, MKTAG('s', 'm', 'c', ' ') }, /* Apple Graphics (SMC) */ |
070ed1bc | 117 | { CODEC_ID_QTRLE, MKTAG('r', 'l', 'e', ' ') }, /* Apple Animation (RLE) */ |
d08d7142 | 118 | { CODEC_ID_QDRAW, MKTAG('q', 'd', 'r', 'w') }, /* QuickDraw */ |
169eb021 | 119 | { CODEC_ID_H264, MKTAG('a', 'v', 'c', '1') }, /* AVC-1/H.264 */ |
5ca1d879 | 120 | { CODEC_ID_NONE, 0 }, |
6cea494e ZK |
121 | }; |
122 | ||
a266644f | 123 | static const CodecTag mov_audio_tags[] = { |
6cea494e ZK |
124 | /* { CODEC_ID_PCM_S16BE, MKTAG('N', 'O', 'N', 'E') }, *//* uncompressed */ |
125 | { CODEC_ID_PCM_S16BE, MKTAG('t', 'w', 'o', 's') }, /* 16 bits */ | |
5ca1d879 | 126 | /* { CODEC_ID_PCM_S8, MKTAG('t', 'w', 'o', 's') },*/ /* 8 bits */ |
5cd62665 | 127 | { CODEC_ID_PCM_U8, MKTAG('r', 'a', 'w', ' ') }, /* 8 bits unsigned */ |
6cea494e ZK |
128 | { CODEC_ID_PCM_S16LE, MKTAG('s', 'o', 'w', 't') }, /* */ |
129 | { CODEC_ID_PCM_MULAW, MKTAG('u', 'l', 'a', 'w') }, /* */ | |
130 | { CODEC_ID_PCM_ALAW, MKTAG('a', 'l', 'a', 'w') }, /* */ | |
0147f198 | 131 | { CODEC_ID_ADPCM_IMA_QT, MKTAG('i', 'm', 'a', '4') }, /* IMA-4 ADPCM */ |
3f95e843 FR |
132 | { CODEC_ID_MACE3, MKTAG('M', 'A', 'C', '3') }, /* Macintosh Audio Compression and Expansion 3:1 */ |
133 | { CODEC_ID_MACE6, MKTAG('M', 'A', 'C', '6') }, /* Macintosh Audio Compression and Expansion 6:1 */ | |
6cea494e ZK |
134 | |
135 | { CODEC_ID_MP2, MKTAG('.', 'm', 'p', '3') }, /* MPEG layer 3 */ /* sample files at http://www.3ivx.com/showcase.html use this tag */ | |
136 | { CODEC_ID_MP2, 0x6D730055 }, /* MPEG layer 3 */ | |
137 | { CODEC_ID_MP2, 0x5500736D }, /* MPEG layer 3 *//* XXX: check endianness */ | |
138 | /* { CODEC_ID_OGG_VORBIS, MKTAG('O', 'g', 'g', 'S') }, *//* sample files at http://heroinewarrior.com/xmovie.php3 use this tag */ | |
139 | /* MP4 tags */ | |
4cb3f3b6 | 140 | { CODEC_ID_MPEG4AAC, MKTAG('m', 'p', '4', 'a') }, /* MPEG-4 AAC */ |
5ca1d879 | 141 | /* The standard for mpeg4 audio is still not normalised AFAIK anyway */ |
891f64b3 | 142 | { CODEC_ID_AMR_NB, MKTAG('s', 'a', 'm', 'r') }, /* AMR-NB 3gp */ |
d663a1fd | 143 | { CODEC_ID_AMR_WB, MKTAG('s', 'a', 'w', 'b') }, /* AMR-WB 3gp */ |
8dafdb88 | 144 | { CODEC_ID_AC3, MKTAG('m', 's', 0x20, 0x00) }, /* Dolby AC-3 */ |
5ca1d879 | 145 | { CODEC_ID_NONE, 0 }, |
6cea494e ZK |
146 | }; |
147 | ||
148 | /* the QuickTime file format is quite convoluted... | |
149 | * it has lots of index tables, each indexing something in another one... | |
150 | * Here we just use what is needed to read the chunks | |
151 | */ | |
152 | ||
153 | typedef struct MOV_sample_to_chunk_tbl { | |
154 | long first; | |
155 | long count; | |
156 | long id; | |
157 | } MOV_sample_to_chunk_tbl; | |
158 | ||
5cd62665 | 159 | typedef struct { |
5ca1d879 ZK |
160 | uint32_t type; |
161 | int64_t offset; | |
162 | int64_t size; /* total size (excluding the size and type fields) */ | |
163 | } MOV_atom_t; | |
164 | ||
165 | typedef struct { | |
166 | int seed; | |
167 | int flags; | |
168 | int size; | |
169 | void* clrs; | |
170 | } MOV_ctab_t; | |
171 | ||
172 | typedef struct { | |
5cd62665 ZK |
173 | uint8_t version; |
174 | uint32_t flags; // 24bit | |
175 | ||
176 | /* 0x03 ESDescrTag */ | |
177 | uint16_t es_id; | |
178 | #define MP4ODescrTag 0x01 | |
179 | #define MP4IODescrTag 0x02 | |
180 | #define MP4ESDescrTag 0x03 | |
181 | #define MP4DecConfigDescrTag 0x04 | |
182 | #define MP4DecSpecificDescrTag 0x05 | |
183 | #define MP4SLConfigDescrTag 0x06 | |
184 | #define MP4ContentIdDescrTag 0x07 | |
185 | #define MP4SupplContentIdDescrTag 0x08 | |
186 | #define MP4IPIPtrDescrTag 0x09 | |
187 | #define MP4IPMPPtrDescrTag 0x0A | |
188 | #define MP4IPMPDescrTag 0x0B | |
189 | #define MP4RegistrationDescrTag 0x0D | |
190 | #define MP4ESIDIncDescrTag 0x0E | |
191 | #define MP4ESIDRefDescrTag 0x0F | |
192 | #define MP4FileIODescrTag 0x10 | |
193 | #define MP4FileODescrTag 0x11 | |
194 | #define MP4ExtProfileLevelDescrTag 0x13 | |
195 | #define MP4ExtDescrTagsStart 0x80 | |
196 | #define MP4ExtDescrTagsEnd 0xFE | |
197 | uint8_t stream_priority; | |
198 | ||
199 | /* 0x04 DecConfigDescrTag */ | |
200 | uint8_t object_type_id; | |
201 | uint8_t stream_type; | |
202 | /* XXX: really streamType is | |
203 | * only 6bit, followed by: | |
204 | * 1bit upStream | |
205 | * 1bit reserved | |
206 | */ | |
207 | uint32_t buffer_size_db; // 24 | |
208 | uint32_t max_bitrate; | |
209 | uint32_t avg_bitrate; | |
210 | ||
211 | /* 0x05 DecSpecificDescrTag */ | |
212 | uint8_t decoder_cfg_len; | |
213 | uint8_t *decoder_cfg; | |
214 | ||
215 | /* 0x06 SLConfigDescrTag */ | |
216 | uint8_t sl_config_len; | |
217 | uint8_t *sl_config; | |
218 | } MOV_esds_t; | |
219 | ||
b6a17df4 ZK |
220 | struct MOVParseTableEntry; |
221 | ||
6cea494e ZK |
222 | typedef struct MOVStreamContext { |
223 | int ffindex; /* the ffmpeg stream id */ | |
224 | int is_ff_stream; /* Is this stream presented to ffmpeg ? i.e. is this an audio or video stream ? */ | |
225 | long next_chunk; | |
226 | long chunk_count; | |
0c1a9eda | 227 | int64_t *chunk_offsets; |
baf25c9d GC |
228 | int32_t stts_count; |
229 | uint64_t *stts_data; /* concatenated data from the time-to-sample atom (count|duration) */ | |
230 | int32_t edit_count; /* number of 'edit' (elst atom) */ | |
6cea494e ZK |
231 | long sample_to_chunk_sz; |
232 | MOV_sample_to_chunk_tbl *sample_to_chunk; | |
3ffe3793 | 233 | long sample_to_chunk_index; |
6cea494e ZK |
234 | long sample_size; |
235 | long sample_count; | |
236 | long *sample_sizes; | |
247d56f5 BB |
237 | long keyframe_count; |
238 | long *keyframes; | |
5cd62665 | 239 | int time_scale; |
3ffe3793 FR |
240 | long current_sample; |
241 | long left_in_chunk; /* how many samples before next chunk */ | |
0e7eed09 | 242 | /* specific MPEG4 header which is added at the beginning of the stream */ |
7fea94ce | 243 | unsigned int header_len; |
0e7eed09 | 244 | uint8_t *header_data; |
5ca1d879 | 245 | MOV_esds_t esds; |
6cea494e ZK |
246 | } MOVStreamContext; |
247 | ||
248 | typedef struct MOVContext { | |
249 | int mp4; /* set to 1 as soon as we are sure that the file is an .mp4 file (even some header parsing depends on this) */ | |
250 | AVFormatContext *fc; | |
5cd62665 ZK |
251 | int time_scale; |
252 | int duration; /* duration of the longest track */ | |
6cea494e ZK |
253 | int found_moov; /* when both 'moov' and 'mdat' sections has been found */ |
254 | int found_mdat; /* we suppose we have enough data to read the file */ | |
0c1a9eda ZK |
255 | int64_t mdat_size; |
256 | int64_t mdat_offset; | |
6cea494e ZK |
257 | int total_streams; |
258 | /* some streams listed here aren't presented to the ffmpeg API, since they aren't either video nor audio | |
259 | * but we need the info to be able to skip data from those streams in the 'mdat' section | |
260 | */ | |
261 | MOVStreamContext *streams[MAX_STREAMS]; | |
5cd62665 | 262 | |
0c1a9eda | 263 | int64_t next_chunk_offset; |
5cd62665 | 264 | MOVStreamContext *partial; /* != 0 : there is still to read in the current chunk */ |
5ca1d879 ZK |
265 | int ctab_size; |
266 | MOV_ctab_t **ctab; /* color tables */ | |
b6a17df4 ZK |
267 | const struct MOVParseTableEntry *parse_table; /* could be eventually used to change the table */ |
268 | /* NOTE: for recursion save to/ restore from local variable! */ | |
b595afaa MM |
269 | |
270 | AVPaletteControl palette_control; | |
6cea494e ZK |
271 | } MOVContext; |
272 | ||
273 | ||
6cea494e ZK |
274 | /* XXX: it's the first time I make a recursive parser I think... sorry if it's ugly :P */ |
275 | ||
276 | /* those functions parse an atom */ | |
277 | /* return code: | |
278 | 1: found what I wanted, exit | |
279 | 0: continue to parse next atom | |
280 | -1: error occured, exit | |
281 | */ | |
5ca1d879 | 282 | typedef int (*mov_parse_function)(MOVContext *ctx, ByteIOContext *pb, MOV_atom_t atom); |
6cea494e ZK |
283 | |
284 | /* links atom IDs to parse functions */ | |
285 | typedef struct MOVParseTableEntry { | |
0c1a9eda | 286 | uint32_t type; |
6cea494e ZK |
287 | mov_parse_function func; |
288 | } MOVParseTableEntry; | |
289 | ||
5ca1d879 ZK |
290 | #ifdef DEBUG |
291 | /* | |
292 | * XXX: static sux, even more in a multithreaded environment... | |
293 | * Avoid them. This is here just to help debugging. | |
294 | */ | |
295 | static int debug_indent = 0; | |
296 | void print_atom(const char *str, MOV_atom_t atom) | |
297 | { | |
298 | unsigned int tag, i; | |
299 | tag = (unsigned int) atom.type; | |
300 | i=debug_indent; | |
301 | if(tag == 0) tag = MKTAG('N', 'U', 'L', 'L'); | |
302 | while(i--) | |
baf25c9d GC |
303 | av_log(NULL, AV_LOG_DEBUG, "|"); |
304 | av_log(NULL, AV_LOG_DEBUG, "parse:"); | |
305 | av_log(NULL, AV_LOG_DEBUG, " %s: tag=%c%c%c%c offset=0x%x size=0x%x\n", | |
5ca1d879 ZK |
306 | str, tag & 0xff, |
307 | (tag >> 8) & 0xff, | |
308 | (tag >> 16) & 0xff, | |
309 | (tag >> 24) & 0xff, | |
310 | (unsigned int)atom.offset, | |
311 | (unsigned int)atom.size); | |
312 | assert((unsigned int)atom.size < 0x7fffffff);// catching errors | |
313 | } | |
314 | #else | |
315 | #define print_atom(a,b) | |
316 | #endif | |
317 | ||
318 | ||
319 | static int mov_read_leaf(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) | |
6cea494e | 320 | { |
5ca1d879 | 321 | print_atom("leaf", atom); |
b6a17df4 | 322 | |
5ca1d879 ZK |
323 | if (atom.size>1) |
324 | url_fskip(pb, atom.size); | |
325 | /* url_seek(pb, atom_offset+atom.size, SEEK_SET); */ | |
6cea494e ZK |
326 | return 0; |
327 | } | |
328 | ||
5ca1d879 | 329 | static int mov_read_default(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) |
6cea494e | 330 | { |
b6a17df4 | 331 | int64_t total_size = 0; |
5ca1d879 | 332 | MOV_atom_t a; |
6cea494e ZK |
333 | int i; |
334 | int err = 0; | |
5ca1d879 | 335 | |
6cea494e | 336 | #ifdef DEBUG |
5ca1d879 | 337 | print_atom("default", atom); |
6cea494e ZK |
338 | debug_indent++; |
339 | #endif | |
5cd62665 | 340 | |
5ca1d879 | 341 | a.offset = atom.offset; |
6cea494e | 342 | |
9ed83b0a ZK |
343 | if (atom.size < 0) |
344 | atom.size = 0x7fffffffffffffffLL; | |
5ca1d879 ZK |
345 | while(((total_size + 8) < atom.size) && !url_feof(pb) && !err) { |
346 | a.size = atom.size; | |
347 | a.type=0L; | |
348 | if(atom.size >= 8) { | |
349 | a.size = get_be32(pb); | |
350 | a.type = get_le32(pb); | |
6cea494e | 351 | } |
5cd62665 | 352 | total_size += 8; |
5ca1d879 | 353 | a.offset += 8; |
baf25c9d | 354 | //av_log(NULL, AV_LOG_DEBUG, "type: %08x %.4s sz: %Lx %Lx %Lx\n", type, (char*)&type, size, atom.size, total_size); |
5ca1d879 ZK |
355 | if (a.size == 1) { /* 64 bit extended size */ |
356 | a.size = get_be64(pb) - 8; | |
357 | a.offset += 8; | |
358 | total_size += 8; | |
6cea494e | 359 | } |
5ca1d879 ZK |
360 | if (a.size == 0) { |
361 | a.size = atom.size - total_size; | |
362 | if (a.size <= 8) | |
5cd62665 ZK |
363 | break; |
364 | } | |
5ca1d879 ZK |
365 | for (i = 0; c->parse_table[i].type != 0L |
366 | && c->parse_table[i].type != a.type; i++) | |
5cd62665 ZK |
367 | /* empty */; |
368 | ||
5ca1d879 | 369 | a.size -= 8; |
baf25c9d | 370 | // av_log(NULL, AV_LOG_DEBUG, " i=%ld\n", i); |
b6a17df4 | 371 | if (c->parse_table[i].type == 0) { /* skip leaf atoms data */ |
5ca1d879 | 372 | // url_seek(pb, atom.offset+atom.size, SEEK_SET); |
6cea494e | 373 | #ifdef DEBUG |
5ca1d879 | 374 | print_atom("unknown", a); |
6cea494e | 375 | #endif |
5ca1d879 | 376 | url_fskip(pb, a.size); |
b6a17df4 ZK |
377 | } else { |
378 | #ifdef DEBUG | |
379 | //char b[5] = { type & 0xff, (type >> 8) & 0xff, (type >> 16) & 0xff, (type >> 24) & 0xff, 0 }; | |
380 | //print_atom(b, type, offset, size); | |
381 | #endif | |
5ca1d879 | 382 | err = (c->parse_table[i].func)(c, pb, a); |
b6a17df4 | 383 | } |
6cea494e | 384 | |
5ca1d879 ZK |
385 | a.offset += a.size; |
386 | total_size += a.size; | |
6cea494e ZK |
387 | } |
388 | ||
5ca1d879 | 389 | if (!err && total_size < atom.size && atom.size < 0x7ffff) { |
baf25c9d | 390 | //av_log(NULL, AV_LOG_DEBUG, "RESET %Ld %Ld err:%d\n", atom.size, total_size, err); |
5ca1d879 | 391 | url_fskip(pb, atom.size - total_size); |
5cd62665 ZK |
392 | } |
393 | ||
6cea494e ZK |
394 | #ifdef DEBUG |
395 | debug_indent--; | |
396 | #endif | |
397 | return err; | |
398 | } | |
399 | ||
5ca1d879 | 400 | static int mov_read_ctab(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) |
6cea494e | 401 | { |
5ca1d879 ZK |
402 | unsigned int len; |
403 | MOV_ctab_t *t; | |
404 | //url_fskip(pb, atom.size); // for now | |
405 | c->ctab = av_realloc(c->ctab, ++c->ctab_size); | |
406 | t = c->ctab[c->ctab_size]; | |
407 | t->seed = get_be32(pb); | |
408 | t->flags = get_be16(pb); | |
409 | t->size = get_be16(pb) + 1; | |
410 | len = 2 * t->size * 4; | |
411 | if (len > 0) { | |
412 | t->clrs = av_malloc(len); // 16bit A R G B | |
413 | if (t->clrs) | |
414 | get_buffer(pb, t->clrs, len); | |
3ffe3793 | 415 | } |
5cd62665 | 416 | |
0147f198 FR |
417 | return 0; |
418 | } | |
419 | ||
5ca1d879 | 420 | static int mov_read_hdlr(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) |
6cea494e | 421 | { |
5ca1d879 | 422 | AVStream *st = c->fc->streams[c->fc->nb_streams-1]; |
3ffe3793 | 423 | int len = 0; |
0c1a9eda | 424 | uint32_t type; |
0c1a9eda | 425 | uint32_t ctype; |
b6a17df4 | 426 | |
5ca1d879 | 427 | print_atom("hdlr", atom); |
6cea494e ZK |
428 | |
429 | get_byte(pb); /* version */ | |
430 | get_byte(pb); get_byte(pb); get_byte(pb); /* flags */ | |
431 | ||
432 | /* component type */ | |
433 | ctype = get_le32(pb); | |
434 | type = get_le32(pb); /* component subtype */ | |
435 | ||
436 | #ifdef DEBUG | |
baf25c9d GC |
437 | av_log(NULL, AV_LOG_DEBUG, "ctype= %c%c%c%c (0x%08lx)\n", *((char *)&ctype), ((char *)&ctype)[1], ((char *)&ctype)[2], ((char *)&ctype)[3], (long) ctype); |
438 | av_log(NULL, AV_LOG_DEBUG, "stype= %c%c%c%c\n", *((char *)&type), ((char *)&type)[1], ((char *)&type)[2], ((char *)&type)[3]); | |
6cea494e ZK |
439 | #endif |
440 | #ifdef DEBUG | |
441 | /* XXX: yeah this is ugly... */ | |
442 | if(ctype == MKTAG('m', 'h', 'l', 'r')) { /* MOV */ | |
443 | if(type == MKTAG('v', 'i', 'd', 'e')) | |
444 | puts("hdlr: vide"); | |
445 | else if(type == MKTAG('s', 'o', 'u', 'n')) | |
446 | puts("hdlr: soun"); | |
447 | } else if(ctype == 0) { /* MP4 */ | |
448 | if(type == MKTAG('v', 'i', 'd', 'e')) | |
449 | puts("hdlr: vide"); | |
450 | else if(type == MKTAG('s', 'o', 'u', 'n')) | |
451 | puts("hdlr: soun"); | |
452 | else if(type == MKTAG('o', 'd', 's', 'm')) | |
453 | puts("hdlr: odsm"); | |
454 | else if(type == MKTAG('s', 'd', 's', 'm')) | |
455 | puts("hdlr: sdsm"); | |
456 | } else puts("hdlr: meta"); | |
457 | #endif | |
458 | ||
459 | if(ctype == MKTAG('m', 'h', 'l', 'r')) { /* MOV */ | |
0147f198 FR |
460 | /* helps parsing the string hereafter... */ |
461 | c->mp4 = 0; | |
6cea494e ZK |
462 | if(type == MKTAG('v', 'i', 'd', 'e')) |
463 | st->codec.codec_type = CODEC_TYPE_VIDEO; | |
464 | else if(type == MKTAG('s', 'o', 'u', 'n')) | |
465 | st->codec.codec_type = CODEC_TYPE_AUDIO; | |
466 | } else if(ctype == 0) { /* MP4 */ | |
0147f198 FR |
467 | /* helps parsing the string hereafter... */ |
468 | c->mp4 = 1; | |
6cea494e ZK |
469 | if(type == MKTAG('v', 'i', 'd', 'e')) |
470 | st->codec.codec_type = CODEC_TYPE_VIDEO; | |
471 | else if(type == MKTAG('s', 'o', 'u', 'n')) | |
472 | st->codec.codec_type = CODEC_TYPE_AUDIO; | |
473 | } | |
474 | get_be32(pb); /* component manufacture */ | |
475 | get_be32(pb); /* component flags */ | |
476 | get_be32(pb); /* component flags mask */ | |
477 | ||
5ca1d879 | 478 | if(atom.size <= 24) |
6cea494e ZK |
479 | return 0; /* nothing left to read */ |
480 | /* XXX: MP4 uses a C string, not a pascal one */ | |
481 | /* component name */ | |
0147f198 FR |
482 | |
483 | if(c->mp4) { | |
484 | /* .mp4: C string */ | |
5ca1d879 | 485 | while(get_byte(pb) && (++len < (atom.size - 24))); |
0147f198 FR |
486 | } else { |
487 | /* .mov: PASCAL string */ | |
9ed83b0a ZK |
488 | #ifdef DEBUG |
489 | char* buf; | |
490 | #endif | |
0147f198 | 491 | len = get_byte(pb); |
5ca1d879 | 492 | #ifdef DEBUG |
b6a17df4 ZK |
493 | buf = (uint8_t*) av_malloc(len+1); |
494 | if (buf) { | |
495 | get_buffer(pb, buf, len); | |
b6a17df4 | 496 | buf[len] = '\0'; |
baf25c9d | 497 | av_log(NULL, AV_LOG_DEBUG, "**buf='%s'\n", buf); |
b6a17df4 ZK |
498 | av_free(buf); |
499 | } else | |
6cea494e | 500 | #endif |
5ca1d879 | 501 | url_fskip(pb, len); |
6cea494e | 502 | } |
5cd62665 | 503 | |
3c13647a | 504 | url_fskip(pb, atom.size - (url_ftell(pb) - atom.offset)); |
6cea494e ZK |
505 | return 0; |
506 | } | |
507 | ||
5ca1d879 | 508 | static int mov_mp4_read_descr_len(ByteIOContext *pb) |
0e7eed09 | 509 | { |
5cd62665 | 510 | int len = 0; |
5ca1d879 ZK |
511 | int count = 4; |
512 | while (count--) { | |
5cd62665 ZK |
513 | int c = get_byte(pb); |
514 | len = (len << 7) | (c & 0x7f); | |
5ca1d879 ZK |
515 | if (!(c & 0x80)) |
516 | break; | |
0e7eed09 FB |
517 | } |
518 | return len; | |
519 | } | |
520 | ||
5ca1d879 | 521 | static int mov_mp4_read_descr(ByteIOContext *pb, int *tag) |
0e7eed09 FB |
522 | { |
523 | int len; | |
524 | *tag = get_byte(pb); | |
5ca1d879 | 525 | len = mov_mp4_read_descr_len(pb); |
0e7eed09 | 526 | #ifdef DEBUG |
baf25c9d | 527 | av_log(NULL, AV_LOG_DEBUG, "MPEG4 description: tag=0x%02x len=%d\n", *tag, len); |
0e7eed09 FB |
528 | #endif |
529 | return len; | |
530 | } | |
531 | ||
5cd62665 ZK |
532 | static inline unsigned int get_be24(ByteIOContext *s) |
533 | { | |
534 | unsigned int val; | |
535 | val = get_byte(s) << 16; | |
536 | val |= get_byte(s) << 8; | |
537 | val |= get_byte(s); | |
538 | return val; | |
539 | } | |
540 | ||
5ca1d879 | 541 | static int mov_read_esds(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) |
5cd62665 | 542 | { |
5cd62665 ZK |
543 | AVStream *st = c->fc->streams[c->fc->nb_streams-1]; |
544 | MOVStreamContext *sc = (MOVStreamContext *)st->priv_data; | |
5ca1d879 | 545 | int64_t start_pos = url_ftell(pb); |
5cd62665 | 546 | int tag, len; |
b6a17df4 | 547 | |
5ca1d879 | 548 | print_atom("esds", atom); |
5cd62665 ZK |
549 | |
550 | /* Well, broken but suffisant for some MP4 streams */ | |
551 | get_be32(pb); /* version + flags */ | |
5ca1d879 | 552 | len = mov_mp4_read_descr(pb, &tag); |
5cd62665 ZK |
553 | if (tag == MP4ESDescrTag) { |
554 | get_be16(pb); /* ID */ | |
555 | get_byte(pb); /* priority */ | |
556 | } else | |
557 | get_be16(pb); /* ID */ | |
558 | ||
5ca1d879 | 559 | len = mov_mp4_read_descr(pb, &tag); |
5cd62665 ZK |
560 | if (tag == MP4DecConfigDescrTag) { |
561 | sc->esds.object_type_id = get_byte(pb); | |
5ca1d879 ZK |
562 | sc->esds.stream_type = get_byte(pb); |
563 | sc->esds.buffer_size_db = get_be24(pb); | |
5cd62665 ZK |
564 | sc->esds.max_bitrate = get_be32(pb); |
565 | sc->esds.avg_bitrate = get_be32(pb); | |
566 | ||
5ca1d879 | 567 | len = mov_mp4_read_descr(pb, &tag); |
baf25c9d | 568 | //av_log(NULL, AV_LOG_DEBUG, "LEN %d TAG %d m:%d a:%d\n", len, tag, sc->esds.max_bitrate, sc->esds.avg_bitrate); |
5cd62665 ZK |
569 | if (tag == MP4DecSpecificDescrTag) { |
570 | #ifdef DEBUG | |
baf25c9d | 571 | av_log(NULL, AV_LOG_DEBUG, "Specific MPEG4 header len=%d\n", len); |
5cd62665 | 572 | #endif |
3129cd01 | 573 | st->codec.extradata = (uint8_t*) av_mallocz(len + FF_INPUT_BUFFER_PADDING_SIZE); |
5ca1d879 ZK |
574 | if (st->codec.extradata) { |
575 | get_buffer(pb, st->codec.extradata, len); | |
576 | st->codec.extradata_size = len; | |
5cd62665 ZK |
577 | } |
578 | } | |
579 | } | |
580 | /* in any case, skip garbage */ | |
5ca1d879 | 581 | url_fskip(pb, atom.size - ((url_ftell(pb) - start_pos))); |
5cd62665 ZK |
582 | return 0; |
583 | } | |
584 | ||
5ca1d879 ZK |
585 | /* this atom contains actual media data */ |
586 | static int mov_read_mdat(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) | |
6cea494e | 587 | { |
5ca1d879 ZK |
588 | print_atom("mdat", atom); |
589 | ||
590 | if(atom.size == 0) /* wrong one (MP4) */ | |
591 | return 0; | |
592 | c->found_mdat=1; | |
593 | c->mdat_offset = atom.offset; | |
594 | c->mdat_size = atom.size; | |
595 | if(c->found_moov) | |
596 | return 1; /* found both, just go */ | |
597 | url_fskip(pb, atom.size); | |
598 | return 0; /* now go for moov */ | |
599 | } | |
600 | ||
601 | /* this atom should contain all header atoms */ | |
602 | static int mov_read_moov(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) | |
603 | { | |
604 | int err; | |
b6a17df4 | 605 | |
5ca1d879 | 606 | print_atom("moov", atom); |
b6a17df4 | 607 | |
5ca1d879 ZK |
608 | err = mov_read_default(c, pb, atom); |
609 | /* we parsed the 'moov' atom, we can terminate the parsing as soon as we find the 'mdat' */ | |
610 | /* so we don't parse the whole file if over a network */ | |
611 | c->found_moov=1; | |
612 | if(c->found_mdat) | |
613 | return 1; /* found both, just go */ | |
614 | return 0; /* now go for mdat */ | |
615 | } | |
616 | ||
617 | ||
618 | static int mov_read_mdhd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) | |
619 | { | |
5ca1d879 ZK |
620 | print_atom("mdhd", atom); |
621 | ||
622 | get_byte(pb); /* version */ | |
623 | ||
624 | get_byte(pb); get_byte(pb); | |
625 | get_byte(pb); /* flags */ | |
626 | ||
627 | get_be32(pb); /* creation time */ | |
628 | get_be32(pb); /* modification time */ | |
629 | ||
baf25c9d | 630 | c->streams[c->fc->nb_streams-1]->time_scale = get_be32(pb); |
5ca1d879 ZK |
631 | |
632 | #ifdef DEBUG | |
baf25c9d | 633 | av_log(NULL, AV_LOG_DEBUG, "track[%i].time_scale = %i\n", c->fc->nb_streams-1, c->streams[c->fc->nb_streams-1]->time_scale); /* time scale */ |
5ca1d879 ZK |
634 | #endif |
635 | get_be32(pb); /* duration */ | |
636 | ||
637 | get_be16(pb); /* language */ | |
638 | get_be16(pb); /* quality */ | |
639 | ||
640 | return 0; | |
641 | } | |
642 | ||
643 | static int mov_read_mvhd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) | |
644 | { | |
645 | print_atom("mvhd", atom); | |
646 | ||
647 | get_byte(pb); /* version */ | |
648 | get_byte(pb); get_byte(pb); get_byte(pb); /* flags */ | |
649 | ||
650 | get_be32(pb); /* creation time */ | |
651 | get_be32(pb); /* modification time */ | |
652 | c->time_scale = get_be32(pb); /* time scale */ | |
653 | #ifdef DEBUG | |
baf25c9d | 654 | av_log(NULL, AV_LOG_DEBUG, "time scale = %i\n", c->time_scale); |
5ca1d879 ZK |
655 | #endif |
656 | c->duration = get_be32(pb); /* duration */ | |
657 | get_be32(pb); /* preferred scale */ | |
658 | ||
659 | get_be16(pb); /* preferred volume */ | |
660 | ||
661 | url_fskip(pb, 10); /* reserved */ | |
662 | ||
663 | url_fskip(pb, 36); /* display matrix */ | |
664 | ||
665 | get_be32(pb); /* preview time */ | |
666 | get_be32(pb); /* preview duration */ | |
667 | get_be32(pb); /* poster time */ | |
668 | get_be32(pb); /* selection time */ | |
669 | get_be32(pb); /* selection duration */ | |
670 | get_be32(pb); /* current time */ | |
671 | get_be32(pb); /* next track ID */ | |
672 | ||
673 | return 0; | |
674 | } | |
675 | ||
9ed83b0a ZK |
676 | static int mov_read_smi(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) |
677 | { | |
678 | AVStream *st = c->fc->streams[c->fc->nb_streams-1]; | |
679 | ||
680 | // currently SVQ3 decoder expect full STSD header - so let's fake it | |
681 | // this should be fixed and just SMI header should be passed | |
682 | av_free(st->codec.extradata); | |
683 | st->codec.extradata_size = 0x5a + atom.size; | |
3129cd01 | 684 | st->codec.extradata = (uint8_t*) av_mallocz(st->codec.extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); |
9ed83b0a ZK |
685 | |
686 | if (st->codec.extradata) { | |
9ed83b0a ZK |
687 | strcpy(st->codec.extradata, "SVQ3"); // fake |
688 | get_buffer(pb, st->codec.extradata + 0x5a, atom.size); | |
baf25c9d | 689 | //av_log(NULL, AV_LOG_DEBUG, "Reading SMI %Ld %s\n", atom.size, (char*)st->codec.extradata + 0x5a); |
9ed83b0a ZK |
690 | } else |
691 | url_fskip(pb, atom.size); | |
692 | ||
693 | return 0; | |
694 | } | |
5ca1d879 | 695 | |
169eb021 MM |
696 | static int mov_read_avcC(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) |
697 | { | |
698 | AVStream *st = c->fc->streams[c->fc->nb_streams-1]; | |
699 | ||
700 | av_free(st->codec.extradata); | |
701 | ||
702 | st->codec.extradata_size = atom.size; | |
703 | st->codec.extradata = (uint8_t*) av_mallocz(st->codec.extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); | |
704 | ||
705 | if (st->codec.extradata) { | |
706 | get_buffer(pb, st->codec.extradata, atom.size); | |
707 | } else | |
708 | url_fskip(pb, atom.size); | |
709 | ||
710 | return 0; | |
711 | } | |
712 | ||
5ca1d879 ZK |
713 | static int mov_read_stco(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) |
714 | { | |
715 | AVStream *st = c->fc->streams[c->fc->nb_streams-1]; | |
716 | MOVStreamContext *sc = (MOVStreamContext *)st->priv_data; | |
717 | int entries, i; | |
718 | ||
719 | print_atom("stco", atom); | |
720 | ||
721 | get_byte(pb); /* version */ | |
722 | get_byte(pb); get_byte(pb); get_byte(pb); /* flags */ | |
723 | ||
724 | entries = get_be32(pb); | |
725 | sc->chunk_count = entries; | |
726 | sc->chunk_offsets = (int64_t*) av_malloc(entries * sizeof(int64_t)); | |
727 | if (!sc->chunk_offsets) | |
728 | return -1; | |
729 | if (atom.type == MKTAG('s', 't', 'c', 'o')) { | |
730 | for(i=0; i<entries; i++) { | |
731 | sc->chunk_offsets[i] = get_be32(pb); | |
732 | } | |
733 | } else if (atom.type == MKTAG('c', 'o', '6', '4')) { | |
734 | for(i=0; i<entries; i++) { | |
735 | sc->chunk_offsets[i] = get_be64(pb); | |
736 | } | |
737 | } else | |
738 | return -1; | |
739 | #ifdef DEBUG | |
740 | /* | |
741 | for(i=0; i<entries; i++) { | |
baf25c9d | 742 | av_log(NULL, AV_LOG_DEBUG, "chunk offset=0x%Lx\n", sc->chunk_offsets[i]); |
5ca1d879 ZK |
743 | } |
744 | */ | |
745 | #endif | |
746 | return 0; | |
747 | } | |
748 | ||
749 | static int mov_read_stsd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) | |
750 | { | |
751 | AVStream *st = c->fc->streams[c->fc->nb_streams-1]; | |
fd6e513e | 752 | //MOVStreamContext *sc = (MOVStreamContext *)st->priv_data; |
5ca1d879 ZK |
753 | int entries, frames_per_sample; |
754 | uint32_t format; | |
755 | ||
b595afaa MM |
756 | /* for palette traversal */ |
757 | int color_depth; | |
758 | int color_start; | |
759 | int color_count; | |
760 | int color_end; | |
761 | int color_index; | |
762 | int color_dec; | |
763 | int color_greyscale; | |
764 | unsigned char *color_table; | |
765 | int j; | |
766 | unsigned char r, g, b; | |
767 | ||
5ca1d879 | 768 | print_atom("stsd", atom); |
6cea494e ZK |
769 | |
770 | get_byte(pb); /* version */ | |
771 | get_byte(pb); get_byte(pb); get_byte(pb); /* flags */ | |
772 | ||
6cea494e ZK |
773 | entries = get_be32(pb); |
774 | ||
891f64b3 | 775 | while(entries--) { //Parsing Sample description table |
b6a17df4 | 776 | enum CodecID id; |
5ca1d879 | 777 | int size = get_be32(pb); /* size */ |
6cea494e | 778 | format = get_le32(pb); /* data format */ |
5cd62665 | 779 | |
6cea494e ZK |
780 | get_be32(pb); /* reserved */ |
781 | get_be16(pb); /* reserved */ | |
782 | get_be16(pb); /* index */ | |
0e7eed09 FB |
783 | |
784 | /* for MPEG4: set codec type by looking for it */ | |
785 | id = codec_get_id(mov_video_tags, format); | |
786 | if (id >= 0) { | |
787 | AVCodec *codec; | |
b6a17df4 | 788 | codec = avcodec_find_decoder(id); |
0e7eed09 | 789 | if (codec) |
b6a17df4 | 790 | st->codec.codec_type = codec->type; |
0e7eed09 FB |
791 | } |
792 | #ifdef DEBUG | |
baf25c9d | 793 | av_log(NULL, AV_LOG_DEBUG, "size=%d 4CC= %c%c%c%c codec_type=%d\n", |
5cd62665 | 794 | size, |
0e7eed09 FB |
795 | (format >> 0) & 0xff, |
796 | (format >> 8) & 0xff, | |
797 | (format >> 16) & 0xff, | |
798 | (format >> 24) & 0xff, | |
799 | st->codec.codec_type); | |
800 | #endif | |
5cd62665 | 801 | st->codec.codec_tag = format; |
5ca1d879 ZK |
802 | if(st->codec.codec_type==CODEC_TYPE_VIDEO) { |
803 | MOV_atom_t a = { 0, 0, 0 }; | |
804 | st->codec.codec_id = id; | |
6cea494e ZK |
805 | get_be16(pb); /* version */ |
806 | get_be16(pb); /* revision level */ | |
807 | get_be32(pb); /* vendor */ | |
808 | get_be32(pb); /* temporal quality */ | |
809 | get_be32(pb); /* spacial quality */ | |
810 | st->codec.width = get_be16(pb); /* width */ | |
811 | st->codec.height = get_be16(pb); /* height */ | |
0e7eed09 FB |
812 | #if 1 |
813 | if (st->codec.codec_id == CODEC_ID_MPEG4) { | |
814 | /* in some MPEG4 the width/height are not correct, so | |
815 | we ignore this info */ | |
816 | st->codec.width = 0; | |
817 | st->codec.height = 0; | |
818 | } | |
819 | #endif | |
6cea494e ZK |
820 | get_be32(pb); /* horiz resolution */ |
821 | get_be32(pb); /* vert resolution */ | |
822 | get_be32(pb); /* data size, always 0 */ | |
5cd62665 | 823 | frames_per_sample = get_be16(pb); /* frames per samples */ |
6cea494e | 824 | #ifdef DEBUG |
baf25c9d | 825 | av_log(NULL, AV_LOG_DEBUG, "frames/samples = %d\n", frames_per_sample); |
6cea494e | 826 | #endif |
b6a17df4 | 827 | get_buffer(pb, (uint8_t *)st->codec.codec_name, 32); /* codec name */ |
5cd62665 ZK |
828 | |
829 | st->codec.bits_per_sample = get_be16(pb); /* depth */ | |
830 | st->codec.color_table_id = get_be16(pb); /* colortable id */ | |
6cea494e | 831 | |
14342fd5 | 832 | /* These are set in mov_read_stts and might already be set! |
14bea432 MN |
833 | st->codec.frame_rate = 25; |
834 | st->codec.frame_rate_base = 1; | |
14342fd5 | 835 | */ |
5ca1d879 | 836 | size -= (16+8*4+2+32+2*2); |
5cd62665 ZK |
837 | #if 0 |
838 | while (size >= 8) { | |
5ca1d879 | 839 | MOV_atom_t a; |
0c1a9eda | 840 | int64_t start_pos; |
5cd62665 | 841 | |
5ca1d879 ZK |
842 | a.size = get_be32(pb); |
843 | a.type = get_le32(pb); | |
5cd62665 | 844 | size -= 8; |
0e7eed09 | 845 | #ifdef DEBUG |
baf25c9d | 846 | av_log(NULL, AV_LOG_DEBUG, "VIDEO: atom_type=%c%c%c%c atom.size=%Ld size_left=%d\n", |
5ca1d879 ZK |
847 | (a.type >> 0) & 0xff, |
848 | (a.type >> 8) & 0xff, | |
849 | (a.type >> 16) & 0xff, | |
850 | (a.type >> 24) & 0xff, | |
851 | a.size, size); | |
0e7eed09 FB |
852 | #endif |
853 | start_pos = url_ftell(pb); | |
854 | ||
5ca1d879 | 855 | switch(a.type) { |
0e7eed09 FB |
856 | case MKTAG('e', 's', 'd', 's'): |
857 | { | |
858 | int tag, len; | |
859 | /* Well, broken but suffisant for some MP4 streams */ | |
860 | get_be32(pb); /* version + flags */ | |
5ca1d879 | 861 | len = mov_mp4_read_descr(pb, &tag); |
0e7eed09 FB |
862 | if (tag == 0x03) { |
863 | /* MP4ESDescrTag */ | |
864 | get_be16(pb); /* ID */ | |
865 | get_byte(pb); /* priority */ | |
5ca1d879 | 866 | len = mov_mp4_read_descr(pb, &tag); |
0e7eed09 FB |
867 | if (tag != 0x04) |
868 | goto fail; | |
869 | /* MP4DecConfigDescrTag */ | |
870 | get_byte(pb); /* objectTypeId */ | |
871 | get_be32(pb); /* streamType + buffer size */ | |
5cd62665 | 872 | get_be32(pb); /* max bit rate */ |
0e7eed09 | 873 | get_be32(pb); /* avg bit rate */ |
891f64b3 | 874 | len = mov_mp4_read_descr(pb, &tag); |
0e7eed09 FB |
875 | if (tag != 0x05) |
876 | goto fail; | |
877 | /* MP4DecSpecificDescrTag */ | |
878 | #ifdef DEBUG | |
baf25c9d | 879 | av_log(NULL, AV_LOG_DEBUG, "Specific MPEG4 header len=%d\n", len); |
0e7eed09 FB |
880 | #endif |
881 | sc->header_data = av_mallocz(len); | |
882 | if (sc->header_data) { | |
883 | get_buffer(pb, sc->header_data, len); | |
5cd62665 | 884 | sc->header_len = len; |
0e7eed09 FB |
885 | } |
886 | } | |
887 | /* in any case, skip garbage */ | |
888 | } | |
889 | break; | |
890 | default: | |
891 | break; | |
892 | } | |
5cd62665 | 893 | fail: |
baf25c9d | 894 | av_log(NULL, AV_LOG_DEBUG, "ATOMENEWSIZE %Ld %d\n", atom.size, url_ftell(pb) - start_pos); |
5ca1d879 ZK |
895 | if (atom.size > 8) { |
896 | url_fskip(pb, (atom.size - 8) - | |
5cd62665 | 897 | ((url_ftell(pb) - start_pos))); |
5ca1d879 | 898 | size -= atom.size - 8; |
5cd62665 ZK |
899 | } |
900 | } | |
0e7eed09 FB |
901 | if (size > 0) { |
902 | /* unknown extension */ | |
903 | url_fskip(pb, size); | |
904 | } | |
5cd62665 | 905 | #else |
b595afaa MM |
906 | |
907 | /* figure out the palette situation */ | |
908 | color_depth = st->codec.bits_per_sample & 0x1F; | |
909 | color_greyscale = st->codec.bits_per_sample & 0x20; | |
910 | ||
911 | /* if the depth is 2, 4, or 8 bpp, file is palettized */ | |
912 | if ((color_depth == 2) || (color_depth == 4) || | |
913 | (color_depth == 8)) { | |
914 | ||
915 | if (color_greyscale) { | |
916 | ||
917 | /* compute the greyscale palette */ | |
918 | color_count = 1 << color_depth; | |
919 | color_index = 255; | |
920 | color_dec = 256 / (color_count - 1); | |
921 | for (j = 0; j < color_count; j++) { | |
922 | r = g = b = color_index; | |
923 | c->palette_control.palette[j] = | |
924 | (r << 16) | (g << 8) | (b); | |
925 | color_index -= color_dec; | |
926 | if (color_index < 0) | |
927 | color_index = 0; | |
928 | } | |
929 | ||
930 | } else if (st->codec.color_table_id & 0x08) { | |
931 | ||
932 | /* if flag bit 3 is set, use the default palette */ | |
933 | color_count = 1 << color_depth; | |
934 | if (color_depth == 2) | |
a90466f7 | 935 | color_table = ff_qt_default_palette_4; |
b595afaa | 936 | else if (color_depth == 4) |
a90466f7 | 937 | color_table = ff_qt_default_palette_16; |
b595afaa | 938 | else |
a90466f7 | 939 | color_table = ff_qt_default_palette_256; |
b595afaa MM |
940 | |
941 | for (j = 0; j < color_count; j++) { | |
942 | r = color_table[j * 4 + 0]; | |
943 | g = color_table[j * 4 + 1]; | |
944 | b = color_table[j * 4 + 2]; | |
945 | c->palette_control.palette[j] = | |
946 | (r << 16) | (g << 8) | (b); | |
947 | } | |
948 | ||
949 | } else { | |
950 | ||
951 | /* load the palette from the file */ | |
952 | color_start = get_be32(pb); | |
953 | color_count = get_be16(pb); | |
954 | color_end = get_be16(pb); | |
955 | for (j = color_start; j <= color_end; j++) { | |
956 | /* each R, G, or B component is 16 bits; | |
957 | * only use the top 8 bits; skip alpha bytes | |
958 | * up front */ | |
959 | get_byte(pb); | |
960 | get_byte(pb); | |
961 | r = get_byte(pb); | |
962 | get_byte(pb); | |
963 | g = get_byte(pb); | |
964 | get_byte(pb); | |
965 | b = get_byte(pb); | |
966 | get_byte(pb); | |
967 | c->palette_control.palette[j] = | |
968 | (r << 16) | (g << 8) | (b); | |
969 | } | |
970 | } | |
971 | ||
972 | st->codec.palctrl = &c->palette_control; | |
973 | st->codec.palctrl->palette_changed = 1; | |
974 | } else | |
975 | st->codec.palctrl = NULL; | |
976 | ||
5ca1d879 ZK |
977 | a.size = size; |
978 | mov_read_default(c, pb, a); | |
5cd62665 ZK |
979 | #endif |
980 | } else { | |
891f64b3 | 981 | st->codec.codec_id = codec_get_id(mov_audio_tags, format); |
d663a1fd | 982 | if(st->codec.codec_id==CODEC_ID_AMR_NB || st->codec.codec_id==CODEC_ID_AMR_WB) //from TS26.244 |
891f64b3 | 983 | { |
984 | #ifdef DEBUG | |
baf25c9d | 985 | av_log(NULL, AV_LOG_DEBUG, "AMR-NB or AMR-WB audio identified!!\n"); |
891f64b3 | 986 | #endif |
987 | get_be32(pb);get_be32(pb); //Reserved_8 | |
988 | get_be16(pb);//Reserved_2 | |
989 | get_be16(pb);//Reserved_2 | |
990 | get_be32(pb);//Reserved_4 | |
991 | get_be16(pb);//TimeScale | |
992 | get_be16(pb);//Reserved_2 | |
993 | ||
994 | //AMRSpecificBox.(10 bytes) | |
d663a1fd | 995 | |
891f64b3 | 996 | get_be32(pb); //size |
891f64b3 | 997 | get_be32(pb); //type=='damr' |
891f64b3 | 998 | get_be32(pb); //vendor |
999 | get_byte(pb); //decoder version | |
1000 | get_be16(pb); //mode_set | |
1001 | get_byte(pb); //mode_change_period | |
1002 | get_byte(pb); //frames_per_sample | |
6cea494e | 1003 | |
25c4950e | 1004 | st->duration = AV_NOPTS_VALUE;//Not possible to get from this info, must count number of AMR frames |
d663a1fd MN |
1005 | if(st->codec.codec_id==CODEC_ID_AMR_NB) |
1006 | { | |
1007 | st->codec.sample_rate=8000; | |
1008 | st->codec.channels=1; | |
1009 | } | |
1010 | else //AMR-WB | |
1011 | { | |
1012 | st->codec.sample_rate=16000; | |
1013 | st->codec.channels=1; | |
1014 | } | |
891f64b3 | 1015 | st->codec.bits_per_sample=16; |
1016 | st->codec.bit_rate=0; /*It is not possible to tell this before we have | |
1017 | an audio frame and even then every frame can be different*/ | |
5cd62665 | 1018 | } |
14342fd5 JC |
1019 | else if( st->codec.codec_tag == MKTAG( 'm', 'p', '4', 's' )) |
1020 | { | |
1021 | //This is some stuff for the hint track, lets ignore it! | |
1022 | //Do some mp4 auto detect. | |
1023 | c->mp4=1; | |
1024 | size-=(16); | |
1025 | url_fskip(pb, size); /* The mp4s atom also contians a esds atom that we can skip*/ | |
1026 | } | |
2768b0d9 TR |
1027 | else if( st->codec.codec_tag == MKTAG( 'm', 'p', '4', 'a' )) |
1028 | { | |
1029 | /* Handle mp4 audio tag */ | |
1030 | get_be32(pb); /* version */ | |
1031 | get_be32(pb); | |
1032 | st->codec.channels = get_be16(pb); /* channels */ | |
1033 | st->codec.bits_per_sample = get_be16(pb); /* bits per sample */ | |
1034 | get_be32(pb); | |
1035 | st->codec.sample_rate = get_be16(pb); /* sample rate, not always correct */ | |
1036 | get_be16(pb); | |
1037 | c->mp4=1; | |
3ec34bfd | 1038 | { |
2768b0d9 TR |
1039 | MOV_atom_t a = { format, url_ftell(pb), size - (20 + 20 + 8) }; |
1040 | mov_read_default(c, pb, a); | |
3ec34bfd | 1041 | } |
2768b0d9 TR |
1042 | /* Get correct sample rate from extradata */ |
1043 | if(st->codec.extradata_size) { | |
1044 | const int samplerate_table[] = { | |
1045 | 96000, 88200, 64000, 48000, 44100, 32000, | |
1046 | 24000, 22050, 16000, 12000, 11025, 8000, | |
1047 | 7350, 0, 0, 0 | |
1048 | }; | |
1049 | unsigned char *px = st->codec.extradata; | |
1050 | // 5 bits objectTypeIndex, 4 bits sampleRateIndex, 4 bits channels | |
1051 | int samplerate_index = ((px[0] & 7) << 1) + ((px[1] >> 7) & 1); | |
1052 | st->codec.sample_rate = samplerate_table[samplerate_index]; | |
1053 | } | |
1054 | } | |
14342fd5 JC |
1055 | else if(size>=(16+20)) |
1056 | {//16 bytes read, reading atleast 20 more | |
1057 | #ifdef DEBUG | |
baf25c9d | 1058 | av_log(NULL, AV_LOG_DEBUG, "audio size=0x%X\n",size); |
14342fd5 JC |
1059 | #endif |
1060 | uint16_t version = get_be16(pb); /* version */ | |
891f64b3 | 1061 | get_be16(pb); /* revision level */ |
1062 | get_be32(pb); /* vendor */ | |
1063 | ||
1064 | st->codec.channels = get_be16(pb); /* channel count */ | |
1065 | st->codec.bits_per_sample = get_be16(pb); /* sample size */ | |
1066 | ||
1067 | /* handle specific s8 codec */ | |
1068 | get_be16(pb); /* compression id = 0*/ | |
1069 | get_be16(pb); /* packet size = 0 */ | |
1070 | ||
1071 | st->codec.sample_rate = ((get_be32(pb) >> 16)); | |
baf25c9d | 1072 | //av_log(NULL, AV_LOG_DEBUG, "CODECID %d %d %.4s\n", st->codec.codec_id, CODEC_ID_PCM_S16BE, (char*)&format); |
891f64b3 | 1073 | |
1074 | switch (st->codec.codec_id) { | |
1075 | case CODEC_ID_PCM_S16BE: | |
1076 | if (st->codec.bits_per_sample == 8) | |
1077 | st->codec.codec_id = CODEC_ID_PCM_S8; | |
1078 | /* fall */ | |
1079 | case CODEC_ID_PCM_U8: | |
1080 | st->codec.bit_rate = st->codec.sample_rate * 8; | |
1081 | break; | |
1082 | default: | |
1083 | ; | |
1084 | } | |
14342fd5 JC |
1085 | |
1086 | //Read QT version 1 fields. In version 0 theese dont exist | |
1087 | #ifdef DEBUG | |
baf25c9d GC |
1088 | av_log(NULL, AV_LOG_DEBUG, "version =%d mp4=%d\n",version,c->mp4); |
1089 | av_log(NULL, AV_LOG_DEBUG, "size-(16+20+16)=%d\n",size-(16+20+16)); | |
14342fd5 JC |
1090 | #endif |
1091 | if((version==1) && size>=(16+20+16)) | |
1092 | { | |
1093 | get_be32(pb); /* samples per packet */ | |
1094 | get_be32(pb); /* bytes per packet */ | |
1095 | get_be32(pb); /* bytes per frame */ | |
1096 | get_be32(pb); /* bytes per sample */ | |
1097 | if(size>(16+20+16)) | |
1098 | { | |
1099 | //Optional, additional atom-based fields | |
1100 | #ifdef DEBUG | |
baf25c9d | 1101 | av_log(NULL, AV_LOG_DEBUG, "offest=0x%X, sizeleft=%d=0x%x,format=%c%c%c%c\n",(int)url_ftell(pb),size - (16 + 20 + 16 ),size - (16 + 20 + 16 ), |
14342fd5 JC |
1102 | (format >> 0) & 0xff, |
1103 | (format >> 8) & 0xff, | |
1104 | (format >> 16) & 0xff, | |
1105 | (format >> 24) & 0xff); | |
1106 | #endif | |
1107 | MOV_atom_t a = { format, url_ftell(pb), size - (16 + 20 + 16 + 8) }; | |
1108 | mov_read_default(c, pb, a); | |
1109 | } | |
1110 | } | |
1111 | else | |
1112 | { | |
1113 | //We should be down to 0 bytes here, but lets make sure. | |
1114 | size-=(16+20); | |
1115 | #ifdef DEBUG | |
1116 | if(size>0) | |
baf25c9d | 1117 | av_log(NULL, AV_LOG_DEBUG, "skipping 0x%X bytes\n",size-(16+20)); |
14342fd5 JC |
1118 | #endif |
1119 | url_fskip(pb, size); | |
1120 | } | |
1121 | } | |
1122 | else | |
1123 | { | |
1124 | size-=16; | |
1125 | //Unknown size, but lets do our best and skip the rest. | |
1126 | #ifdef DEBUG | |
baf25c9d | 1127 | av_log(NULL, AV_LOG_DEBUG, "Strange size, skipping 0x%X bytes\n",size); |
14342fd5 JC |
1128 | #endif |
1129 | url_fskip(pb, size); | |
891f64b3 | 1130 | } |
6cea494e | 1131 | } |
6cea494e | 1132 | } |
5cd62665 | 1133 | |
6cea494e ZK |
1134 | return 0; |
1135 | } | |
1136 | ||
5ca1d879 | 1137 | static int mov_read_stsc(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) |
6cea494e | 1138 | { |
5ca1d879 ZK |
1139 | AVStream *st = c->fc->streams[c->fc->nb_streams-1]; |
1140 | MOVStreamContext *sc = (MOVStreamContext *)st->priv_data; | |
6cea494e | 1141 | int entries, i; |
b6a17df4 | 1142 | |
5ca1d879 | 1143 | print_atom("stsc", atom); |
5cd62665 | 1144 | |
6cea494e ZK |
1145 | get_byte(pb); /* version */ |
1146 | get_byte(pb); get_byte(pb); get_byte(pb); /* flags */ | |
1147 | ||
1148 | entries = get_be32(pb); | |
3ffe3793 | 1149 | #ifdef DEBUG |
baf25c9d | 1150 | av_log(NULL, AV_LOG_DEBUG, "track[%i].stsc.entries = %i\n", c->fc->nb_streams-1, entries); |
3ffe3793 | 1151 | #endif |
6cea494e | 1152 | sc->sample_to_chunk_sz = entries; |
b6a17df4 ZK |
1153 | sc->sample_to_chunk = (MOV_sample_to_chunk_tbl*) av_malloc(entries * sizeof(MOV_sample_to_chunk_tbl)); |
1154 | if (!sc->sample_to_chunk) | |
1155 | return -1; | |
6cea494e ZK |
1156 | for(i=0; i<entries; i++) { |
1157 | sc->sample_to_chunk[i].first = get_be32(pb); | |
1158 | sc->sample_to_chunk[i].count = get_be32(pb); | |
1159 | sc->sample_to_chunk[i].id = get_be32(pb); | |
1160 | #ifdef DEBUG | |
baf25c9d | 1161 | /* av_log(NULL, AV_LOG_DEBUG, "sample_to_chunk first=%ld count=%ld, id=%ld\n", sc->sample_to_chunk[i].first, sc->sample_to_chunk[i].count, sc->sample_to_chunk[i].id); */ |
6cea494e ZK |
1162 | #endif |
1163 | } | |
1164 | return 0; | |
1165 | } | |
1166 | ||
247d56f5 BB |
1167 | static int mov_read_stss(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) |
1168 | { | |
1169 | AVStream *st = c->fc->streams[c->fc->nb_streams-1]; | |
1170 | MOVStreamContext *sc = (MOVStreamContext *)st->priv_data; | |
1171 | int entries, i; | |
1172 | ||
1173 | print_atom("stss", atom); | |
1174 | ||
1175 | get_byte(pb); /* version */ | |
1176 | get_byte(pb); get_byte(pb); get_byte(pb); /* flags */ | |
1177 | ||
1178 | entries = get_be32(pb); | |
1179 | sc->keyframe_count = entries; | |
1180 | #ifdef DEBUG | |
1181 | av_log(NULL, AV_LOG_DEBUG, "keyframe_count = %ld\n", sc->keyframe_count); | |
1182 | #endif | |
1183 | sc->keyframes = (long*) av_malloc(entries * sizeof(long)); | |
1184 | if (!sc->keyframes) | |
1185 | return -1; | |
1186 | for(i=0; i<entries; i++) { | |
1187 | sc->keyframes[i] = get_be32(pb); | |
1188 | #ifdef DEBUG | |
1189 | /* av_log(NULL, AV_LOG_DEBUG, "keyframes[]=%ld\n", sc->keyframes[i]); */ | |
1190 | #endif | |
1191 | } | |
1192 | return 0; | |
1193 | } | |
1194 | ||
5ca1d879 | 1195 | static int mov_read_stsz(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) |
6cea494e | 1196 | { |
5ca1d879 ZK |
1197 | AVStream *st = c->fc->streams[c->fc->nb_streams-1]; |
1198 | MOVStreamContext *sc = (MOVStreamContext *)st->priv_data; | |
6cea494e | 1199 | int entries, i; |
b6a17df4 | 1200 | |
5ca1d879 | 1201 | print_atom("stsz", atom); |
5cd62665 | 1202 | |
6cea494e ZK |
1203 | get_byte(pb); /* version */ |
1204 | get_byte(pb); get_byte(pb); get_byte(pb); /* flags */ | |
5cd62665 | 1205 | |
6cea494e ZK |
1206 | sc->sample_size = get_be32(pb); |
1207 | entries = get_be32(pb); | |
1208 | sc->sample_count = entries; | |
0147f198 | 1209 | #ifdef DEBUG |
baf25c9d | 1210 | av_log(NULL, AV_LOG_DEBUG, "sample_size = %ld sample_count = %ld\n", sc->sample_size, sc->sample_count); |
0147f198 | 1211 | #endif |
6cea494e ZK |
1212 | if(sc->sample_size) |
1213 | return 0; /* there isn't any table following */ | |
b6a17df4 ZK |
1214 | sc->sample_sizes = (long*) av_malloc(entries * sizeof(long)); |
1215 | if (!sc->sample_sizes) | |
1216 | return -1; | |
6cea494e ZK |
1217 | for(i=0; i<entries; i++) { |
1218 | sc->sample_sizes[i] = get_be32(pb); | |
1219 | #ifdef DEBUG | |
baf25c9d | 1220 | /* av_log(NULL, AV_LOG_DEBUG, "sample_sizes[]=%ld\n", sc->sample_sizes[i]); */ |
6cea494e ZK |
1221 | #endif |
1222 | } | |
1223 | return 0; | |
1224 | } | |
1225 | ||
5ca1d879 | 1226 | static int mov_read_stts(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) |
0147f198 | 1227 | { |
5ca1d879 | 1228 | AVStream *st = c->fc->streams[c->fc->nb_streams-1]; |
fd6e513e | 1229 | //MOVStreamContext *sc = (MOVStreamContext *)st->priv_data; |
0147f198 | 1230 | int entries, i; |
d957696f MN |
1231 | int64_t duration=0; |
1232 | int64_t total_sample_count=0; | |
b6a17df4 | 1233 | |
5ca1d879 | 1234 | print_atom("stts", atom); |
0147f198 FR |
1235 | |
1236 | get_byte(pb); /* version */ | |
1237 | get_byte(pb); get_byte(pb); get_byte(pb); /* flags */ | |
1238 | entries = get_be32(pb); | |
891f64b3 | 1239 | |
baf25c9d GC |
1240 | c->streams[c->fc->nb_streams-1]->stts_count = entries; |
1241 | c->streams[c->fc->nb_streams-1]->stts_data = (uint64_t*) av_malloc(entries * sizeof(uint64_t)); | |
891f64b3 | 1242 | |
3ffe3793 | 1243 | #ifdef DEBUG |
baf25c9d | 1244 | av_log(NULL, AV_LOG_DEBUG, "track[%i].stts.entries = %i\n", c->fc->nb_streams-1, entries); |
3ffe3793 | 1245 | #endif |
0147f198 | 1246 | for(i=0; i<entries; i++) { |
baf25c9d GC |
1247 | int32_t sample_duration; |
1248 | int32_t sample_count; | |
0147f198 | 1249 | |
891f64b3 | 1250 | sample_count=get_be32(pb); |
0147f198 | 1251 | sample_duration = get_be32(pb); |
baf25c9d | 1252 | c->streams[c->fc->nb_streams - 1]->stts_data[i] = (uint64_t)sample_count<<32 | (uint64_t)sample_duration; |
891f64b3 | 1253 | #ifdef DEBUG |
baf25c9d | 1254 | av_log(NULL, AV_LOG_DEBUG, "sample_count=%d, sample_duration=%d\n",sample_count,sample_duration); |
891f64b3 | 1255 | #endif |
1256 | duration+=sample_duration*sample_count; | |
1257 | total_sample_count+=sample_count; | |
1258 | ||
1259 | #if 0 //We calculate an average instead, needed by .mp4-files created with nec e606 3g phone | |
0147f198 FR |
1260 | |
1261 | if (!i && st->codec.codec_type==CODEC_TYPE_VIDEO) { | |
14bea432 | 1262 | st->codec.frame_rate_base = sample_duration ? sample_duration : 1; |
baf25c9d | 1263 | st->codec.frame_rate = c->streams[c->fc->nb_streams-1]->time_scale; |
0147f198 | 1264 | #ifdef DEBUG |
baf25c9d | 1265 | av_log(NULL, AV_LOG_DEBUG, "VIDEO FRAME RATE= %i (sd= %i)\n", st->codec.frame_rate, sample_duration); |
0147f198 FR |
1266 | #endif |
1267 | } | |
891f64b3 | 1268 | #endif |
1269 | } | |
1270 | ||
14342fd5 JC |
1271 | /*The stsd atom which contain codec type sometimes comes after the stts so we cannot check for codec_type*/ |
1272 | if(duration>0) | |
891f64b3 | 1273 | { |
d957696f MN |
1274 | av_reduce( |
1275 | &st->codec.frame_rate, | |
1276 | &st->codec.frame_rate_base, | |
baf25c9d | 1277 | c->streams[c->fc->nb_streams-1]->time_scale * total_sample_count, |
d957696f MN |
1278 | duration, |
1279 | INT_MAX | |
1280 | ); | |
14342fd5 | 1281 | |
891f64b3 | 1282 | #ifdef DEBUG |
baf25c9d | 1283 | av_log(NULL, AV_LOG_DEBUG, "FRAME RATE average (video or audio)= %f (tot sample count= %i ,tot dur= %i timescale=%d)\n", (float)st->codec.frame_rate/st->codec.frame_rate_base,total_sample_count,duration,c->streams[c->fc->nb_streams-1]->time_scale); |
891f64b3 | 1284 | #endif |
0147f198 | 1285 | } |
14342fd5 JC |
1286 | else |
1287 | { | |
1288 | st->codec.frame_rate_base = 1; | |
baf25c9d | 1289 | st->codec.frame_rate = c->streams[c->fc->nb_streams-1]->time_scale; |
14342fd5 | 1290 | } |
0147f198 FR |
1291 | return 0; |
1292 | } | |
1293 | ||
5ca1d879 ZK |
1294 | static int mov_read_trak(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) |
1295 | { | |
1296 | AVStream *st; | |
1297 | MOVStreamContext *sc; | |
1298 | ||
1299 | print_atom("trak", atom); | |
1300 | ||
1301 | st = av_new_stream(c->fc, c->fc->nb_streams); | |
1302 | if (!st) return -2; | |
1303 | sc = (MOVStreamContext*) av_mallocz(sizeof(MOVStreamContext)); | |
1304 | if (!sc) { | |
1305 | av_free(st); | |
1306 | return -1; | |
1307 | } | |
1308 | ||
1309 | sc->sample_to_chunk_index = -1; | |
1310 | st->priv_data = sc; | |
1311 | st->codec.codec_type = CODEC_TYPE_MOV_OTHER; | |
25c4950e FB |
1312 | st->start_time = 0; /* XXX: check */ |
1313 | st->duration = (c->duration * (int64_t)AV_TIME_BASE) / c->time_scale; | |
5ca1d879 ZK |
1314 | c->streams[c->fc->nb_streams-1] = sc; |
1315 | ||
1316 | return mov_read_default(c, pb, atom); | |
1317 | } | |
1318 | ||
1319 | static int mov_read_tkhd(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) | |
1320 | { | |
1321 | AVStream *st; | |
1322 | ||
1323 | print_atom("tkhd", atom); | |
1324 | ||
1325 | st = c->fc->streams[c->fc->nb_streams-1]; | |
1326 | ||
1327 | get_byte(pb); /* version */ | |
1328 | ||
1329 | get_byte(pb); get_byte(pb); | |
1330 | get_byte(pb); /* flags */ | |
1331 | /* | |
1332 | MOV_TRACK_ENABLED 0x0001 | |
1333 | MOV_TRACK_IN_MOVIE 0x0002 | |
1334 | MOV_TRACK_IN_PREVIEW 0x0004 | |
1335 | MOV_TRACK_IN_POSTER 0x0008 | |
1336 | */ | |
1337 | ||
1338 | get_be32(pb); /* creation time */ | |
1339 | get_be32(pb); /* modification time */ | |
1340 | st->id = (int)get_be32(pb); /* track id (NOT 0 !)*/ | |
1341 | get_be32(pb); /* reserved */ | |
25c4950e FB |
1342 | st->start_time = 0; /* check */ |
1343 | st->duration = (get_be32(pb) * (int64_t)AV_TIME_BASE) / c->time_scale; /* duration */ | |
5ca1d879 ZK |
1344 | get_be32(pb); /* reserved */ |
1345 | get_be32(pb); /* reserved */ | |
1346 | ||
1347 | get_be16(pb); /* layer */ | |
1348 | get_be16(pb); /* alternate group */ | |
1349 | get_be16(pb); /* volume */ | |
1350 | get_be16(pb); /* reserved */ | |
1351 | ||
1352 | url_fskip(pb, 36); /* display matrix */ | |
1353 | ||
1354 | /* those are fixed-point */ | |
1355 | st->codec.width = get_be32(pb) >> 16; /* track width */ | |
1356 | st->codec.height = get_be32(pb) >> 16; /* track height */ | |
1357 | ||
1358 | return 0; | |
1359 | } | |
1360 | ||
1361 | /* this atom should be null (from specs), but some buggy files put the 'moov' atom inside it... */ | |
1362 | /* like the files created with Adobe Premiere 5.0, for samples see */ | |
1363 | /* http://graphics.tudelft.nl/~wouter/publications/soundtests/ */ | |
1364 | static int mov_read_wide(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) | |
1365 | { | |
1366 | int err; | |
5ca1d879 ZK |
1367 | |
1368 | #ifdef DEBUG | |
1369 | print_atom("wide", atom); | |
1370 | debug_indent++; | |
1371 | #endif | |
1372 | if (atom.size < 8) | |
1373 | return 0; /* continue */ | |
1374 | if (get_be32(pb) != 0) { /* 0 sized mdat atom... use the 'wide' atom size */ | |
1375 | url_fskip(pb, atom.size - 4); | |
1376 | return 0; | |
1377 | } | |
1378 | atom.type = get_le32(pb); | |
1379 | atom.offset += 8; | |
1380 | atom.size -= 8; | |
fd6e513e | 1381 | if (atom.type != MKTAG('m', 'd', 'a', 't')) { |
5ca1d879 ZK |
1382 | url_fskip(pb, atom.size); |
1383 | return 0; | |
1384 | } | |
1385 | err = mov_read_mdat(c, pb, atom); | |
1386 | #ifdef DEBUG | |
1387 | debug_indent--; | |
1388 | #endif | |
1389 | return err; | |
1390 | } | |
1391 | ||
1392 | ||
0147f198 | 1393 | #ifdef CONFIG_ZLIB |
0c1a9eda | 1394 | static int null_read_packet(void *opaque, uint8_t *buf, int buf_size) |
0147f198 FR |
1395 | { |
1396 | return -1; | |
1397 | } | |
1398 | ||
5ca1d879 | 1399 | static int mov_read_cmov(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) |
0147f198 | 1400 | { |
0147f198 | 1401 | ByteIOContext ctx; |
b6a17df4 ZK |
1402 | uint8_t *cmov_data; |
1403 | uint8_t *moov_data; /* uncompressed data */ | |
0147f198 FR |
1404 | long cmov_len, moov_len; |
1405 | int ret; | |
b6a17df4 | 1406 | |
5ca1d879 | 1407 | print_atom("cmov", atom); |
0147f198 FR |
1408 | |
1409 | get_be32(pb); /* dcom atom */ | |
1410 | if (get_le32(pb) != MKTAG( 'd', 'c', 'o', 'm' )) | |
1411 | return -1; | |
1412 | if (get_le32(pb) != MKTAG( 'z', 'l', 'i', 'b' )) { | |
baf25c9d | 1413 | av_log(NULL, AV_LOG_DEBUG, "unknown compression for cmov atom !"); |
0147f198 FR |
1414 | return -1; |
1415 | } | |
1416 | get_be32(pb); /* cmvd atom */ | |
1417 | if (get_le32(pb) != MKTAG( 'c', 'm', 'v', 'd' )) | |
1418 | return -1; | |
1419 | moov_len = get_be32(pb); /* uncompressed size */ | |
5ca1d879 | 1420 | cmov_len = atom.size - 6 * 4; |
5cd62665 | 1421 | |
b6a17df4 | 1422 | cmov_data = (uint8_t *) av_malloc(cmov_len); |
0147f198 FR |
1423 | if (!cmov_data) |
1424 | return -1; | |
b6a17df4 | 1425 | moov_data = (uint8_t *) av_malloc(moov_len); |
0147f198 FR |
1426 | if (!moov_data) { |
1427 | av_free(cmov_data); | |
1428 | return -1; | |
1429 | } | |
1430 | get_buffer(pb, cmov_data, cmov_len); | |
b6a17df4 | 1431 | if(uncompress (moov_data, (uLongf *) &moov_len, (const Bytef *)cmov_data, cmov_len) != Z_OK) |
0147f198 FR |
1432 | return -1; |
1433 | if(init_put_byte(&ctx, moov_data, moov_len, 0, NULL, null_read_packet, NULL, NULL) != 0) | |
1434 | return -1; | |
1435 | ctx.buf_end = ctx.buffer + moov_len; | |
5ca1d879 ZK |
1436 | atom.type = MKTAG( 'm', 'o', 'o', 'v' ); |
1437 | atom.offset = 0; | |
1438 | atom.size = moov_len; | |
9ed83b0a ZK |
1439 | #ifdef DEBUG |
1440 | { int fd = open("/tmp/uncompheader.mov", O_WRONLY | O_CREAT); write(fd, moov_data, moov_len); close(fd); } | |
1441 | #endif | |
5ca1d879 | 1442 | ret = mov_read_default(c, &ctx, atom); |
0147f198 FR |
1443 | av_free(moov_data); |
1444 | av_free(cmov_data); | |
9ed83b0a | 1445 | |
0147f198 FR |
1446 | return ret; |
1447 | } | |
1448 | #endif | |
1449 | ||
baf25c9d GC |
1450 | /* edit list atom */ |
1451 | static int mov_read_elst(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom) | |
1452 | { | |
58e555d4 | 1453 | int i, edit_count; |
baf25c9d GC |
1454 | print_atom("elst", atom); |
1455 | ||
1456 | get_byte(pb); /* version */ | |
1457 | get_byte(pb); get_byte(pb); get_byte(pb); /* flags */ | |
58e555d4 MN |
1458 | edit_count= c->streams[c->fc->nb_streams-1]->edit_count = get_be32(pb); /* entries */ |
1459 | ||
1460 | for(i=0; i<edit_count; i++){ | |
1461 | get_be32(pb); /* Track duration */ | |
1462 | get_be32(pb); /* Media time */ | |
1463 | get_be32(pb); /* Media rate */ | |
1464 | } | |
baf25c9d GC |
1465 | #ifdef DEBUG |
1466 | av_log(NULL, AV_LOG_DEBUG, "track[%i].edit_count = %i\n", c->fc->nb_streams-1, c->streams[c->fc->nb_streams-1]->edit_count); | |
1467 | #endif | |
1468 | return 0; | |
1469 | } | |
1470 | ||
6cea494e ZK |
1471 | static const MOVParseTableEntry mov_default_parse_table[] = { |
1472 | /* mp4 atoms */ | |
5ca1d879 ZK |
1473 | { MKTAG( 'c', 'o', '6', '4' ), mov_read_stco }, |
1474 | { MKTAG( 'c', 'p', 'r', 't' ), mov_read_default }, | |
1475 | { MKTAG( 'c', 'r', 'h', 'd' ), mov_read_default }, | |
1476 | { MKTAG( 'c', 't', 't', 's' ), mov_read_leaf }, /* composition time to sample */ | |
1477 | { MKTAG( 'd', 'i', 'n', 'f' ), mov_read_default }, /* data information */ | |
1478 | { MKTAG( 'd', 'p', 'n', 'd' ), mov_read_leaf }, | |
1479 | { MKTAG( 'd', 'r', 'e', 'f' ), mov_read_leaf }, | |
1480 | { MKTAG( 'e', 'd', 't', 's' ), mov_read_default }, | |
58e555d4 | 1481 | { MKTAG( 'e', 'l', 's', 't' ), mov_read_elst }, |
5ca1d879 ZK |
1482 | { MKTAG( 'f', 'r', 'e', 'e' ), mov_read_leaf }, |
1483 | { MKTAG( 'h', 'd', 'l', 'r' ), mov_read_hdlr }, | |
1484 | { MKTAG( 'h', 'i', 'n', 't' ), mov_read_leaf }, | |
1485 | { MKTAG( 'h', 'm', 'h', 'd' ), mov_read_leaf }, | |
1486 | { MKTAG( 'i', 'o', 'd', 's' ), mov_read_leaf }, | |
1487 | { MKTAG( 'm', 'd', 'a', 't' ), mov_read_mdat }, | |
1488 | { MKTAG( 'm', 'd', 'h', 'd' ), mov_read_mdhd }, | |
1489 | { MKTAG( 'm', 'd', 'i', 'a' ), mov_read_default }, | |
1490 | { MKTAG( 'm', 'i', 'n', 'f' ), mov_read_default }, | |
1491 | { MKTAG( 'm', 'o', 'o', 'v' ), mov_read_moov }, | |
1492 | { MKTAG( 'm', 'p', '4', 'a' ), mov_read_default }, | |
1493 | { MKTAG( 'm', 'p', '4', 's' ), mov_read_default }, | |
1494 | { MKTAG( 'm', 'p', '4', 'v' ), mov_read_default }, | |
1495 | { MKTAG( 'm', 'p', 'o', 'd' ), mov_read_leaf }, | |
1496 | { MKTAG( 'm', 'v', 'h', 'd' ), mov_read_mvhd }, | |
1497 | { MKTAG( 'n', 'm', 'h', 'd' ), mov_read_leaf }, | |
1498 | { MKTAG( 'o', 'd', 'h', 'd' ), mov_read_default }, | |
1499 | { MKTAG( 's', 'd', 'h', 'd' ), mov_read_default }, | |
3c13647a | 1500 | { MKTAG( 's', 'k', 'i', 'p' ), mov_read_leaf }, |
5ca1d879 | 1501 | { MKTAG( 's', 'm', 'h', 'd' ), mov_read_leaf }, /* sound media info header */ |
169eb021 MM |
1502 | { MKTAG( 'S', 'M', 'I', ' ' ), mov_read_smi }, /* Sorenson extension ??? */ |
1503 | { MKTAG( 'a', 'v', 'c', 'C' ), mov_read_avcC }, | |
5ca1d879 ZK |
1504 | { MKTAG( 's', 't', 'b', 'l' ), mov_read_default }, |
1505 | { MKTAG( 's', 't', 'c', 'o' ), mov_read_stco }, | |
1506 | { MKTAG( 's', 't', 'd', 'p' ), mov_read_default }, | |
1507 | { MKTAG( 's', 't', 's', 'c' ), mov_read_stsc }, | |
1508 | { MKTAG( 's', 't', 's', 'd' ), mov_read_stsd }, /* sample description */ | |
1509 | { MKTAG( 's', 't', 's', 'h' ), mov_read_default }, | |
247d56f5 | 1510 | { MKTAG( 's', 't', 's', 's' ), mov_read_stss }, /* sync sample */ |
5ca1d879 ZK |
1511 | { MKTAG( 's', 't', 's', 'z' ), mov_read_stsz }, /* sample size */ |
1512 | { MKTAG( 's', 't', 't', 's' ), mov_read_stts }, | |
1513 | { MKTAG( 't', 'k', 'h', 'd' ), mov_read_tkhd }, /* track header */ | |
1514 | { MKTAG( 't', 'r', 'a', 'k' ), mov_read_trak }, | |
1515 | { MKTAG( 't', 'r', 'e', 'f' ), mov_read_default }, /* not really */ | |
1516 | { MKTAG( 'u', 'd', 't', 'a' ), mov_read_leaf }, | |
1517 | { MKTAG( 'u', 'r', 'l', ' ' ), mov_read_leaf }, | |
1518 | { MKTAG( 'u', 'r', 'n', ' ' ), mov_read_leaf }, | |
1519 | { MKTAG( 'u', 'u', 'i', 'd' ), mov_read_default }, | |
1520 | { MKTAG( 'v', 'm', 'h', 'd' ), mov_read_leaf }, /* video media info header */ | |
1521 | { MKTAG( 'w', 'a', 'v', 'e' ), mov_read_default }, | |
6cea494e | 1522 | /* extra mp4 */ |
5ca1d879 | 1523 | { MKTAG( 'M', 'D', 'E', 'S' ), mov_read_leaf }, |
6cea494e | 1524 | /* QT atoms */ |
5ca1d879 ZK |
1525 | { MKTAG( 'c', 'h', 'a', 'p' ), mov_read_leaf }, |
1526 | { MKTAG( 'c', 'l', 'i', 'p' ), mov_read_default }, | |
1527 | { MKTAG( 'c', 'r', 'g', 'n' ), mov_read_leaf }, | |
1528 | { MKTAG( 'c', 't', 'a', 'b' ), mov_read_ctab }, | |
1529 | { MKTAG( 'e', 's', 'd', 's' ), mov_read_esds }, | |
1530 | { MKTAG( 'k', 'm', 'a', 't' ), mov_read_leaf }, | |
1531 | { MKTAG( 'm', 'a', 't', 't' ), mov_read_default }, | |
1532 | { MKTAG( 'r', 'd', 'r', 'f' ), mov_read_leaf }, | |
1533 | { MKTAG( 'r', 'm', 'd', 'a' ), mov_read_default }, | |
1534 | { MKTAG( 'r', 'm', 'd', 'r' ), mov_read_leaf }, | |
1535 | { MKTAG( 'r', 'm', 'r', 'a' ), mov_read_default }, | |
1536 | { MKTAG( 's', 'c', 'p', 't' ), mov_read_leaf }, | |
1537 | { MKTAG( 's', 's', 'r', 'c' ), mov_read_leaf }, | |
1538 | { MKTAG( 's', 'y', 'n', 'c' ), mov_read_leaf }, | |
1539 | { MKTAG( 't', 'c', 'm', 'd' ), mov_read_leaf }, | |
1540 | { MKTAG( 'w', 'i', 'd', 'e' ), mov_read_wide }, /* place holder */ | |
1541 | //{ MKTAG( 'r', 'm', 'q', 'u' ), mov_read_leaf }, | |
0147f198 | 1542 | #ifdef CONFIG_ZLIB |
5ca1d879 | 1543 | { MKTAG( 'c', 'm', 'o', 'v' ), mov_read_cmov }, |
0147f198 | 1544 | #else |
5ca1d879 | 1545 | { MKTAG( 'c', 'm', 'o', 'v' ), mov_read_leaf }, |
0147f198 | 1546 | #endif |
5ca1d879 | 1547 | { 0L, mov_read_leaf } |
6cea494e ZK |
1548 | }; |
1549 | ||
1550 | static void mov_free_stream_context(MOVStreamContext *sc) | |
1551 | { | |
1552 | if(sc) { | |
1ea4f593 FB |
1553 | av_free(sc->chunk_offsets); |
1554 | av_free(sc->sample_to_chunk); | |
5cd62665 | 1555 | av_free(sc->sample_sizes); |
247d56f5 | 1556 | av_free(sc->keyframes); |
0e7eed09 | 1557 | av_free(sc->header_data); |
baf25c9d | 1558 | av_free(sc->stts_data); |
1ea4f593 | 1559 | av_free(sc); |
6cea494e ZK |
1560 | } |
1561 | } | |
1562 | ||
5ca1d879 | 1563 | static inline uint32_t mov_to_tag(uint8_t *buf) |
0e7eed09 | 1564 | { |
5ca1d879 | 1565 | return MKTAG(buf[0], buf[1], buf[2], buf[3]); |
0e7eed09 FB |
1566 | } |
1567 | ||
5cd62665 | 1568 | static inline uint32_t to_be32(uint8_t *buf) |
0e7eed09 FB |
1569 | { |
1570 | return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; | |
1571 | } | |
1572 | ||
b6a17df4 | 1573 | /* XXX: is it sufficient ? */ |
c9a65ca8 FB |
1574 | static int mov_probe(AVProbeData *p) |
1575 | { | |
0e7eed09 FB |
1576 | unsigned int offset; |
1577 | uint32_t tag; | |
3ffe3793 | 1578 | |
c9a65ca8 FB |
1579 | /* check file header */ |
1580 | if (p->buf_size <= 12) | |
1581 | return 0; | |
0e7eed09 FB |
1582 | offset = 0; |
1583 | for(;;) { | |
1584 | /* ignore invalid offset */ | |
1585 | if ((offset + 8) > (unsigned int)p->buf_size) | |
3ffe3793 | 1586 | return 0; |
5ca1d879 | 1587 | tag = mov_to_tag(p->buf + offset + 4); |
0e7eed09 FB |
1588 | switch(tag) { |
1589 | case MKTAG( 'm', 'o', 'o', 'v' ): | |
1590 | case MKTAG( 'w', 'i', 'd', 'e' ): | |
1591 | case MKTAG( 'f', 'r', 'e', 'e' ): | |
8b879f18 FR |
1592 | case MKTAG( 'm', 'd', 'a', 't' ): |
1593 | case MKTAG( 'p', 'n', 'o', 't' ): /* detect movs with preview pics like ew.mov and april.mov */ | |
bc634f6f | 1594 | case MKTAG( 'u', 'd', 't', 'a' ): /* Packet Video PVAuthor adds this and a lot of more junk */ |
3ffe3793 | 1595 | return AVPROBE_SCORE_MAX; |
0e7eed09 | 1596 | case MKTAG( 'f', 't', 'y', 'p' ): |
5ca1d879 | 1597 | case MKTAG( 's', 'k', 'i', 'p' ): |
14342fd5 | 1598 | offset = to_be32(p->buf+offset) + offset; |
0e7eed09 FB |
1599 | break; |
1600 | default: | |
1601 | /* unrecognized tag */ | |
1602 | return 0; | |
1603 | } | |
3ffe3793 FR |
1604 | } |
1605 | return 0; | |
c9a65ca8 FB |
1606 | } |
1607 | ||
a266644f | 1608 | static int mov_read_header(AVFormatContext *s, AVFormatParameters *ap) |
6cea494e | 1609 | { |
b6a17df4 | 1610 | MOVContext *mov = (MOVContext *) s->priv_data; |
6cea494e | 1611 | ByteIOContext *pb = &s->pb; |
684f44d9 | 1612 | int i, j, nb, err; |
5ca1d879 | 1613 | MOV_atom_t atom = { 0, 0, 0 }; |
6cea494e | 1614 | |
6cea494e | 1615 | mov->fc = s; |
b6a17df4 | 1616 | mov->parse_table = mov_default_parse_table; |
c9a65ca8 FB |
1617 | #if 0 |
1618 | /* XXX: I think we should auto detect */ | |
1619 | if(s->iformat->name[1] == 'p') | |
6cea494e | 1620 | mov->mp4 = 1; |
c9a65ca8 | 1621 | #endif |
6cea494e | 1622 | if(!url_is_streamed(pb)) /* .mov and .mp4 aren't streamable anyway (only progressive download if moov is before mdat) */ |
5ca1d879 | 1623 | atom.size = url_filesize(url_fileno(pb)); |
6cea494e | 1624 | else |
9ed83b0a | 1625 | atom.size = 0x7FFFFFFFFFFFFFFFLL; |
6cea494e ZK |
1626 | |
1627 | #ifdef DEBUG | |
baf25c9d | 1628 | av_log(NULL, AV_LOG_DEBUG, "filesz=%Ld\n", atom.size); |
6cea494e ZK |
1629 | #endif |
1630 | ||
1631 | /* check MOV header */ | |
5ca1d879 | 1632 | err = mov_read_default(mov, pb, atom); |
25fa62e1 | 1633 | if (err<0 || (!mov->found_moov && !mov->found_mdat)) { |
bc874dae | 1634 | av_log(s, AV_LOG_ERROR, "mov: header not found !!! (err:%d, moov:%d, mdat:%d) pos:%lld\n", |
25fa62e1 ZK |
1635 | err, mov->found_moov, mov->found_mdat, url_ftell(pb)); |
1636 | return -1; | |
6cea494e ZK |
1637 | } |
1638 | #ifdef DEBUG | |
baf25c9d | 1639 | av_log(NULL, AV_LOG_DEBUG, "on_parse_exit_offset=%d\n", (int) url_ftell(pb)); |
6cea494e ZK |
1640 | #endif |
1641 | /* some cleanup : make sure we are on the mdat atom */ | |
1642 | if(!url_is_streamed(pb) && (url_ftell(pb) != mov->mdat_offset)) | |
1643 | url_fseek(pb, mov->mdat_offset, SEEK_SET); | |
1644 | ||
1645 | mov->next_chunk_offset = mov->mdat_offset; /* initialise reading */ | |
1646 | ||
1647 | #ifdef DEBUG | |
baf25c9d | 1648 | av_log(NULL, AV_LOG_DEBUG, "mdat_reset_offset=%d\n", (int) url_ftell(pb)); |
6cea494e ZK |
1649 | #endif |
1650 | ||
1651 | #ifdef DEBUG | |
baf25c9d | 1652 | av_log(NULL, AV_LOG_DEBUG, "streams= %d\n", s->nb_streams); |
6cea494e ZK |
1653 | #endif |
1654 | mov->total_streams = nb = s->nb_streams; | |
5ca1d879 | 1655 | |
6cea494e ZK |
1656 | #if 1 |
1657 | for(i=0; i<s->nb_streams;) { | |
1658 | if(s->streams[i]->codec.codec_type == CODEC_TYPE_MOV_OTHER) {/* not audio, not video, delete */ | |
1ea4f593 | 1659 | av_free(s->streams[i]); |
6cea494e ZK |
1660 | for(j=i+1; j<s->nb_streams; j++) |
1661 | s->streams[j-1] = s->streams[j]; | |
1662 | s->nb_streams--; | |
1663 | } else | |
1664 | i++; | |
1665 | } | |
1666 | for(i=0; i<s->nb_streams;i++) { | |
1667 | MOVStreamContext *sc; | |
1668 | sc = (MOVStreamContext *)s->streams[i]->priv_data; | |
1669 | sc->ffindex = i; | |
1670 | sc->is_ff_stream = 1; | |
1671 | } | |
1672 | #endif | |
1673 | #ifdef DEBUG | |
baf25c9d | 1674 | av_log(NULL, AV_LOG_DEBUG, "real streams= %d\n", s->nb_streams); |
6cea494e ZK |
1675 | #endif |
1676 | return 0; | |
1677 | } | |
1678 | ||
1679 | /* Yes, this is ugly... I didn't write the specs of QT :p */ | |
1680 | /* XXX:remove useless commented code sometime */ | |
a266644f | 1681 | static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) |
6cea494e | 1682 | { |
b6a17df4 | 1683 | MOVContext *mov = (MOVContext *) s->priv_data; |
0e7eed09 | 1684 | MOVStreamContext *sc; |
9ed83b0a | 1685 | int64_t offset = 0x0FFFFFFFFFFFFFFFLL; |
247d56f5 | 1686 | int i, a, b, m; |
5cd62665 | 1687 | int size; |
6cea494e | 1688 | size = 0x0FFFFFFF; |
5cd62665 | 1689 | |
3ffe3793 FR |
1690 | #ifdef MOV_SPLIT_CHUNKS |
1691 | if (mov->partial) { | |
5cd62665 | 1692 | |
3ffe3793 FR |
1693 | int idx; |
1694 | ||
5cd62665 ZK |
1695 | sc = mov->partial; |
1696 | idx = sc->sample_to_chunk_index; | |
1697 | ||
3ffe3793 | 1698 | if (idx < 0) return 0; |
5cd62665 | 1699 | size = sc->sample_sizes[sc->current_sample]; |
3ffe3793 | 1700 | |
5cd62665 ZK |
1701 | sc->current_sample++; |
1702 | sc->left_in_chunk--; | |
3ffe3793 | 1703 | |
5cd62665 | 1704 | if (sc->left_in_chunk <= 0) |
3ffe3793 FR |
1705 | mov->partial = 0; |
1706 | offset = mov->next_chunk_offset; | |
1707 | /* extract the sample */ | |
1708 | ||
1709 | goto readchunk; | |
1710 | } | |
1711 | #endif | |
1712 | ||
6cea494e | 1713 | again: |
5cd62665 | 1714 | sc = 0; |
6cea494e | 1715 | for(i=0; i<mov->total_streams; i++) { |
5cd62665 | 1716 | MOVStreamContext *msc = mov->streams[i]; |
baf25c9d | 1717 | //av_log(NULL, AV_LOG_DEBUG, "MOCHUNK %ld %d %p pos:%Ld\n", mov->streams[i]->next_chunk, mov->total_streams, mov->streams[i], url_ftell(&s->pb)); |
5cd62665 ZK |
1718 | if ((msc->next_chunk < msc->chunk_count) && msc->next_chunk >= 0 |
1719 | && (msc->chunk_offsets[msc->next_chunk] < offset)) { | |
1720 | sc = msc; | |
1721 | offset = msc->chunk_offsets[msc->next_chunk]; | |
baf25c9d | 1722 | //av_log(NULL, AV_LOG_DEBUG, "SELETED %Ld i:%d\n", offset, i); |
6cea494e | 1723 | } |
6cea494e | 1724 | } |
9ed83b0a | 1725 | if (!sc || offset==0x0FFFFFFFFFFFFFFFLL) |
5cd62665 ZK |
1726 | return -1; |
1727 | ||
1728 | sc->next_chunk++; | |
1729 | ||
3ffe3793 | 1730 | if(mov->next_chunk_offset < offset) { /* some meta data */ |
6cea494e | 1731 | url_fskip(&s->pb, (offset - mov->next_chunk_offset)); |
3ffe3793 FR |
1732 | mov->next_chunk_offset = offset; |
1733 | } | |
1734 | ||
baf25c9d | 1735 | //av_log(NULL, AV_LOG_DEBUG, "chunk: [%i] %lli -> %lli\n", st_id, mov->next_chunk_offset, offset); |
5cd62665 | 1736 | if(!sc->is_ff_stream) { |
6cea494e | 1737 | url_fskip(&s->pb, (offset - mov->next_chunk_offset)); |
3ffe3793 | 1738 | mov->next_chunk_offset = offset; |
9ed83b0a | 1739 | offset = 0x0FFFFFFFFFFFFFFFLL; |
6cea494e ZK |
1740 | goto again; |
1741 | } | |
6cea494e ZK |
1742 | |
1743 | /* now get the chunk size... */ | |
1744 | ||
1745 | for(i=0; i<mov->total_streams; i++) { | |
5cd62665 ZK |
1746 | MOVStreamContext *msc = mov->streams[i]; |
1747 | if ((msc->next_chunk < msc->chunk_count) | |
1748 | && ((msc->chunk_offsets[msc->next_chunk] - offset) < size)) | |
1749 | size = msc->chunk_offsets[msc->next_chunk] - offset; | |
6cea494e | 1750 | } |
bc634f6f ZK |
1751 | |
1752 | #ifdef MOV_MINOLTA_FIX | |
1753 | //Make sure that size is according to sample_size (Needed by .mov files | |
1754 | //created on a Minolta Dimage Xi where audio chunks contains waste data in the end) | |
1755 | //Maybe we should really not only check sc->sample_size, but also sc->sample_sizes | |
1756 | //but I have no such movies | |
1757 | if (sc->sample_size > 0) { | |
1758 | int foundsize=0; | |
1759 | for(i=0; i<(sc->sample_to_chunk_sz); i++) { | |
1760 | if( (sc->sample_to_chunk[i].first)<=(sc->next_chunk) && (sc->sample_size>0) ) | |
1761 | { | |
cac0a56c RS |
1762 | // I can't figure out why for PCM audio sample_size is always 1 |
1763 | // (it should actually be channels*bits_per_second/8) but it is. | |
1764 | AVCodecContext* cod = &s->streams[sc->ffindex]->codec; | |
1765 | if (sc->sample_size == 1 && (cod->codec_id == CODEC_ID_PCM_S16BE || cod->codec_id == CODEC_ID_PCM_S16LE)) | |
1766 | foundsize=(sc->sample_to_chunk[i].count*cod->channels*cod->bits_per_sample)/8; | |
1767 | else | |
1768 | foundsize=sc->sample_to_chunk[i].count*sc->sample_size; | |
bc634f6f ZK |
1769 | } |
1770 | #ifdef DEBUG | |
baf25c9d | 1771 | /*av_log(NULL, AV_LOG_DEBUG, "sample_to_chunk first=%ld count=%ld, id=%ld\n", sc->sample_to_chunk[i].first, sc->sample_to_chunk[i].count, sc->sample_to_chunk[i].id);*/ |
bc634f6f ZK |
1772 | #endif |
1773 | } | |
1774 | if( (foundsize>0) && (foundsize<size) ) | |
1775 | { | |
1776 | #ifdef DEBUG | |
baf25c9d | 1777 | /*av_log(NULL, AV_LOG_DEBUG, "this size should actually be %d\n",foundsize);*/ |
bc634f6f ZK |
1778 | #endif |
1779 | size=foundsize; | |
1780 | } | |
1781 | } | |
1782 | #endif //MOV_MINOLTA_FIX | |
1783 | ||
3ffe3793 FR |
1784 | #ifdef MOV_SPLIT_CHUNKS |
1785 | /* split chunks into samples */ | |
5cd62665 ZK |
1786 | if (sc->sample_size == 0) { |
1787 | int idx = sc->sample_to_chunk_index; | |
1788 | if ((idx + 1 < sc->sample_to_chunk_sz) | |
1789 | && (sc->next_chunk >= sc->sample_to_chunk[idx + 1].first)) | |
1790 | idx++; | |
1791 | sc->sample_to_chunk_index = idx; | |
1792 | if (idx >= 0 && sc->sample_to_chunk[idx].count != 1) { | |
1793 | mov->partial = sc; | |
3ffe3793 | 1794 | /* we'll have to get those samples before next chunk */ |
5cd62665 ZK |
1795 | sc->left_in_chunk = sc->sample_to_chunk[idx].count - 1; |
1796 | size = sc->sample_sizes[sc->current_sample]; | |
3ffe3793 FR |
1797 | } |
1798 | ||
5cd62665 | 1799 | sc->current_sample++; |
3ffe3793 FR |
1800 | } |
1801 | #endif | |
1802 | ||
1803 | readchunk: | |
baf25c9d | 1804 | //av_log(NULL, AV_LOG_DEBUG, "chunk: [%i] %lli -> %lli (%i)\n", st_id, offset, offset + size, size); |
6cea494e ZK |
1805 | if(size == 0x0FFFFFFF) |
1806 | size = mov->mdat_size + mov->mdat_offset - offset; | |
1807 | if(size < 0) | |
1808 | return -1; | |
1809 | if(size == 0) | |
1810 | return -1; | |
0e7eed09 | 1811 | url_fseek(&s->pb, offset, SEEK_SET); |
5cd62665 | 1812 | |
baf25c9d | 1813 | //av_log(NULL, AV_LOG_DEBUG, "READCHUNK hlen: %d %d off: %Ld pos:%Ld\n", size, sc->header_len, offset, url_ftell(&s->pb)); |
0e7eed09 FB |
1814 | if (sc->header_len > 0) { |
1815 | av_new_packet(pkt, size + sc->header_len); | |
1816 | memcpy(pkt->data, sc->header_data, sc->header_len); | |
1817 | get_buffer(&s->pb, pkt->data + sc->header_len, size); | |
1818 | /* free header */ | |
1819 | av_freep(&sc->header_data); | |
1820 | sc->header_len = 0; | |
1821 | } else { | |
1822 | av_new_packet(pkt, size); | |
1823 | get_buffer(&s->pb, pkt->data, pkt->size); | |
1824 | } | |
1825 | pkt->stream_index = sc->ffindex; | |
247d56f5 BB |
1826 | |
1827 | // If the keyframes table exists, mark any samples that are in the table as key frames. | |
1828 | // If no table exists, treat very sample as a key frame. | |
1829 | if (sc->keyframes) { | |
1830 | a = 0; | |
1831 | b = sc->keyframe_count - 1; | |
1832 | ||
1833 | while (a < b) { | |
1834 | m = (a + b + 1) >> 1; | |
1835 | if (sc->keyframes[m] > sc->current_sample) { | |
1836 | b = m - 1; | |
1837 | } else { | |
1838 | a = m; | |
1839 | } | |
1840 | } | |
1841 | ||
1842 | if (sc->keyframes[a] == sc->current_sample) | |
1843 | pkt->flags |= PKT_FLAG_KEY; | |
1844 | } | |
1845 | else | |
1846 | pkt->flags |= PKT_FLAG_KEY; | |
6cea494e ZK |
1847 | |
1848 | #ifdef DEBUG | |
1849 | /* | |
baf25c9d | 1850 | av_log(NULL, AV_LOG_DEBUG, "Packet (%d, %d, %ld) ", pkt->stream_index, st_id, pkt->size); |
6cea494e | 1851 | for(i=0; i<8; i++) |
baf25c9d | 1852 | av_log(NULL, AV_LOG_DEBUG, "%02x ", pkt->data[i]); |
6cea494e | 1853 | for(i=0; i<8; i++) |
baf25c9d | 1854 | av_log(NULL, AV_LOG_DEBUG, "%c ", (pkt->data[i]) & 0x7F); |
6cea494e ZK |
1855 | puts(""); |
1856 | */ | |
1857 | #endif | |
1858 | ||
1859 | mov->next_chunk_offset = offset + size; | |
1860 | ||
1861 | return 0; | |
1862 | } | |
1863 | ||
baf25c9d GC |
1864 | #if defined(MOV_SPLIT_CHUNKS) && defined(MOV_SEEK) |
1865 | /** | |
1866 | * Seek method based on the one described in the Appendix C of QTFileFormat.pdf | |
1867 | */ | |
7b3c1382 | 1868 | static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) |
baf25c9d GC |
1869 | { |
1870 | MOVContext* mov = (MOVContext *) s->priv_data; | |
1871 | MOVStreamContext* sc; | |
1872 | int32_t i, a, b, m; | |
1873 | int64_t sample_time; | |
1874 | int64_t start_time; | |
1875 | int32_t seek_sample, sample; | |
1876 | int32_t duration; | |
1877 | int32_t count; | |
1878 | int32_t chunk; | |
1879 | int32_t left_in_chunk; | |
1880 | int64_t chunk_file_offset; | |
1881 | int64_t sample_file_offset; | |
1882 | int32_t first_chunk_sample; | |
1883 | int32_t sample_to_chunk_idx; | |
1884 | int mov_idx; | |
1885 | ||
1886 | // Find the corresponding mov stream | |
1887 | for (mov_idx = 0; mov_idx < mov->total_streams; mov_idx++) | |
1888 | if (mov->streams[mov_idx]->ffindex == stream_index) | |
1889 | break; | |
1890 | if (mov_idx == mov->total_streams) { | |
1891 | av_log(s, AV_LOG_ERROR, "mov: requested stream was not found in mov streams (idx=%i)\n", stream_index); | |
1892 | return -1; | |
1893 | } | |
1894 | sc = mov->streams[mov_idx]; | |
1895 | ||
1896 | // Step 1. Find the edit that contains the requested time (elst) | |
1897 | if (sc->edit_count) { | |
1898 | // FIXME should handle edit list | |
1899 | av_log(s, AV_LOG_ERROR, "mov: does not handle seeking in files that contain edit list (c:%d)\n", sc->edit_count); | |
1900 | return -1; | |
1901 | } | |
1902 | ||
1903 | // Step 2. Find the corresponding sample using the Time-to-sample atom (stts) */ | |
1904 | #ifdef DEBUG | |
1905 | av_log(s, AV_LOG_DEBUG, "Searching for time %li in stream #%i (time_scale=%i)\n", (long)timestamp, mov_idx, sc->time_scale); | |
1906 | #endif | |
1907 | // convert timestamp from time_base unit to timescale unit | |
1908 | sample_time = av_rescale( timestamp, | |
1909 | (int64_t)sc->time_scale * s->streams[stream_index]->time_base.num, | |
1910 | (int64_t)s->streams[stream_index]->time_base.den); | |
1911 | start_time = 0; // FIXME use elst atom | |
1912 | sample = 1; // sample are 0 based in table | |
1913 | #ifdef DEBUG | |
1914 | av_log(s, AV_LOG_DEBUG, "Searching for sample_time %li \n", (long)sample_time); | |
1915 | #endif | |
1916 | for (i = 0; i < sc->stts_count; i++) { | |
1917 | count = (uint32_t)(sc->stts_data[i]>>32); | |
1918 | duration = (uint32_t)(sc->stts_data[i]&0xffff); | |
1919 | //av_log(s, AV_LOG_DEBUG, "> sample_time %lli \n", (long)sample_time); | |
1920 | //av_log(s, AV_LOG_DEBUG, "> count=%i duration=%i\n", count, duration); | |
1921 | if ((start_time + count*duration) > sample_time) { | |
1922 | sample += (sample_time - start_time) / duration; | |
1923 | break; | |
1924 | } | |
1925 | sample += count; | |
1926 | start_time += count * duration; | |
1927 | } | |
1928 | /* NOTE: despite what qt doc say, the dt value (Display Time in qt vocabulary) computed with the stts atom | |
1929 | is a decoding time stamp (dts) not a presentation time stamp. And as usual dts != pts for stream with b frames */ | |
1930 | ||
1931 | #ifdef DEBUG | |
1932 | av_log(s, AV_LOG_DEBUG, "Found time %li at sample #%u\n", (long)sample_time, sample); | |
1933 | #endif | |
1934 | if (sample > sc->sample_count) { | |
1935 | av_log(s, AV_LOG_ERROR, "mov: sample pos is too high, unable to seek (req. sample=%i, sample count=%ld)\n", sample, sc->sample_count); | |
1936 | return -1; | |
1937 | } | |
1938 | ||
1939 | // Step 3. Find the prior sync. sample using the Sync sample atom (stss) | |
1940 | if (sc->keyframes) { | |
1941 | a = 0; | |
1942 | b = sc->keyframe_count - 1; | |
1943 | while (a < b) { | |
1944 | m = (a + b + 1) >> 1; | |
1945 | if (sc->keyframes[m] > sample) { | |
1946 | b = m - 1; | |
1947 | } else { | |
1948 | a = m; | |
1949 | } | |
1950 | #ifdef DEBUG | |
1951 | // av_log(s, AV_LOG_DEBUG, "a=%i (%i) b=%i (%i) m=%i (%i) stream #%i\n", a, sc->keyframes[a], b, sc->keyframes[b], m, sc->keyframes[m], mov_idx); | |
1952 | #endif | |
1953 | } | |
1954 | seek_sample = sc->keyframes[a]; | |
1955 | } | |
1956 | else | |
1957 | seek_sample = sample; // else all samples are key frames | |
1958 | #ifdef DEBUG | |
1959 | av_log(s, AV_LOG_DEBUG, "Found nearest keyframe at sample #%i \n", seek_sample); | |
1960 | #endif | |
1961 | ||
1962 | // Step 4. Find the chunk of the sample using the Sample-to-chunk-atom (stsc) | |
1963 | for (first_chunk_sample = 1, i = 0; i < (sc->sample_to_chunk_sz - 1); i++) { | |
1964 | b = (sc->sample_to_chunk[i + 1].first - sc->sample_to_chunk[i].first) * sc->sample_to_chunk[i].count; | |
1965 | if (seek_sample >= first_chunk_sample && seek_sample < (first_chunk_sample + b)) | |
1966 | break; | |
1967 | first_chunk_sample += b; | |
1968 | } | |
1969 | chunk = sc->sample_to_chunk[i].first + (seek_sample - first_chunk_sample) / sc->sample_to_chunk[i].count; | |
1970 | left_in_chunk = sc->sample_to_chunk[i].count - (seek_sample - first_chunk_sample) % sc->sample_to_chunk[i].count; | |
1971 | first_chunk_sample += ((seek_sample - first_chunk_sample) / sc->sample_to_chunk[i].count) * sc->sample_to_chunk[i].count; | |
1972 | sample_to_chunk_idx = i; | |
1973 | #ifdef DEBUG | |
1974 | av_log(s, AV_LOG_DEBUG, "Sample was found in chunk #%i at sample offset %i (idx %i)\n", chunk, seek_sample - first_chunk_sample, sample_to_chunk_idx); | |
1975 | #endif | |
1976 | ||
1977 | // Step 5. Find the offset of the chunk using the chunk offset atom | |
1978 | if (!sc->chunk_offsets) { | |
1979 | av_log(s, AV_LOG_ERROR, "mov: no chunk offset atom, unable to seek\n"); | |
1980 | return -1; | |
1981 | } | |
1982 | if (chunk > sc->chunk_count) { | |
1983 | av_log(s, AV_LOG_ERROR, "mov: chunk offset atom too short, unable to seek (req. chunk=%i, chunk count=%li)\n", chunk, sc->chunk_count); | |
1984 | return -1; | |
1985 | } | |
1986 | chunk_file_offset = sc->chunk_offsets[chunk - 1]; | |
1987 | #ifdef DEBUG | |
1988 | av_log(s, AV_LOG_DEBUG, "Chunk file offset is #%llu \n", chunk_file_offset); | |
1989 | #endif | |
1990 | ||
1991 | // Step 6. Find the byte offset within the chunk using the sample size atom | |
1992 | sample_file_offset = chunk_file_offset; | |
1993 | if (sc->sample_size) | |
1994 | sample_file_offset += (seek_sample - first_chunk_sample) * sc->sample_size; | |
1995 | else { | |
1996 | for (i = 0; i < (seek_sample - first_chunk_sample); i++) { | |
1997 | sample_file_offset += sc->sample_sizes[first_chunk_sample + i - 1]; | |
1998 | } | |
1999 | } | |
2000 | #ifdef DEBUG | |
2001 | av_log(s, AV_LOG_DEBUG, "Sample file offset is #%llu \n", sample_file_offset); | |
2002 | #endif | |
2003 | ||
2004 | // Step 6. Update the parser | |
2005 | mov->partial = sc; | |
2006 | mov->next_chunk_offset = sample_file_offset; | |
2007 | // Update current stream state | |
2008 | sc->current_sample = seek_sample - 1; // zero based | |
2009 | sc->left_in_chunk = left_in_chunk; | |
2010 | sc->next_chunk = chunk; // +1 -1 (zero based) | |
2011 | sc->sample_to_chunk_index = sample_to_chunk_idx; | |
2012 | ||
2013 | // Update other streams | |
2014 | for (i = 0; i<mov->total_streams; i++) { | |
5c030d3e | 2015 | MOVStreamContext *msc; |
baf25c9d GC |
2016 | if (i == mov_idx) continue; |
2017 | // Find the nearest 'next' chunk | |
5c030d3e | 2018 | msc = mov->streams[i]; |
baf25c9d GC |
2019 | a = 0; |
2020 | b = msc->chunk_count - 1; | |
2021 | while (a < b) { | |
2022 | m = (a + b + 1) >> 1; | |
2023 | if (msc->chunk_offsets[m] > chunk_file_offset) { | |
2024 | b = m - 1; | |
2025 | } else { | |
2026 | a = m; | |
2027 | } | |
2028 | #ifdef DEBUG | |
2029 | /* av_log(s, AV_LOG_DEBUG, "a=%i (%li) b=%i (%li) m=%i (%li) stream #%i\n" | |
2030 | , a, (long)msc->chunk_offsets[a], b, (long)msc->chunk_offsets[b], m, (long)msc->chunk_offsets[m], i); */ | |
2031 | #endif | |
2032 | } | |
2033 | msc->next_chunk = a; | |
2034 | if (msc->chunk_offsets[a] < chunk_file_offset && a < (msc->chunk_count-1)) | |
2035 | msc->next_chunk ++; | |
2036 | #ifdef DEBUG | |
2037 | av_log(s, AV_LOG_DEBUG, "Nearest next chunk for stream #%i is #%i @%lli\n", i, msc->next_chunk+1, msc->chunk_offsets[msc->next_chunk]); | |
2038 | #endif | |
2039 | // Compute sample count and index in the sample_to_chunk table (what a pity) | |
2040 | msc->sample_to_chunk_index = 0; | |
2041 | msc->current_sample = 0; | |
2042 | for(; msc->sample_to_chunk_index < (msc->sample_to_chunk_sz - 1) | |
2043 | && msc->sample_to_chunk[msc->sample_to_chunk_index + 1].first <= (1 + msc->next_chunk); msc->sample_to_chunk_index++) { | |
2044 | msc->current_sample += (msc->sample_to_chunk[msc->sample_to_chunk_index + 1].first - msc->sample_to_chunk[msc->sample_to_chunk_index].first) \ | |
2045 | * msc->sample_to_chunk[msc->sample_to_chunk_index].count; | |
2046 | } | |
2047 | msc->current_sample += (msc->next_chunk - (msc->sample_to_chunk[msc->sample_to_chunk_index].first - 1)) * sc->sample_to_chunk[msc->sample_to_chunk_index].count; | |
2048 | msc->left_in_chunk = msc->sample_to_chunk[msc->sample_to_chunk_index].count - 1; | |
2049 | #ifdef DEBUG | |
2050 | av_log(s, AV_LOG_DEBUG, "Next Sample for stream #%i is #%i @%i\n", i, msc->current_sample + 1, msc->sample_to_chunk_index + 1); | |
2051 | #endif | |
2052 | } | |
2053 | return 0; | |
2054 | } | |
2055 | #endif | |
2056 | ||
a266644f | 2057 | static int mov_read_close(AVFormatContext *s) |
6cea494e ZK |
2058 | { |
2059 | int i; | |
b6a17df4 | 2060 | MOVContext *mov = (MOVContext *) s->priv_data; |
6cea494e ZK |
2061 | for(i=0; i<mov->total_streams; i++) |
2062 | mov_free_stream_context(mov->streams[i]); | |
5ca1d879 ZK |
2063 | /* free color tabs */ |
2064 | for(i=0; i<mov->ctab_size; i++) | |
2065 | av_freep(&mov->ctab[i]); | |
2066 | av_freep(&mov->ctab); | |
6cea494e ZK |
2067 | return 0; |
2068 | } | |
2069 | ||
c9a65ca8 | 2070 | static AVInputFormat mov_iformat = { |
4cb3f3b6 | 2071 | "mov,mp4,m4a,3gp", |
c9a65ca8 FB |
2072 | "QuickTime/MPEG4 format", |
2073 | sizeof(MOVContext), | |
2074 | mov_probe, | |
6cea494e ZK |
2075 | mov_read_header, |
2076 | mov_read_packet, | |
2077 | mov_read_close, | |
baf25c9d GC |
2078 | #if defined(MOV_SPLIT_CHUNKS) && defined(MOV_SEEK) |
2079 | mov_read_seek, | |
2080 | #endif | |
6cea494e ZK |
2081 | }; |
2082 | ||
c9a65ca8 FB |
2083 | int mov_init(void) |
2084 | { | |
2085 | av_register_input_format(&mov_iformat); | |
2086 | return 0; | |
2087 | } |