Mercurial > libavformat.hg
annotate iff.c @ 5984:55c714a8fb00 libavformat
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
fields of common media header chunks (these can have different sizes depending
on the type of IFF file you read), better handle odd sizes (like RIFF, every
field is padded to word) and handle headerchunks after the BODY chunk.
Patch by Sebastian Vater <cdgs.basty googlemail com>.
author | rbultje |
---|---|
date | Tue, 27 Apr 2010 14:03:47 +0000 |
parents | 43548500ffbc |
children | 35c3858d457f |
rev | line source |
---|---|
3189 | 1 /* |
3198 | 2 * IFF (.iff) file demuxer |
3189 | 3 * Copyright (c) 2008 Jaikrishnan Menon <realityman@gmx.net> |
5625 | 4 * Copyright (c) 2010 Peter Ross <pross@xvid.org> |
5984
55c714a8fb00
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
rbultje
parents:
5971
diff
changeset
|
5 * Copyright (c) 2010 Sebastian Vater <cdgs.basty@googlemail.com> |
3189 | 6 * |
7 * This file is part of FFmpeg. | |
8 * | |
9 * FFmpeg is free software; you can redistribute it and/or | |
10 * modify it under the terms of the GNU Lesser General Public | |
11 * License as published by the Free Software Foundation; either | |
12 * version 2.1 of the License, or (at your option) any later version. | |
13 * | |
14 * FFmpeg is distributed in the hope that it will be useful, | |
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 * Lesser General Public License for more details. | |
18 * | |
19 * You should have received a copy of the GNU Lesser General Public | |
20 * License along with FFmpeg; if not, write to the Free Software | |
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
22 */ | |
23 | |
24 /** | |
5969
178de7695c6c
Remove explicit filename from Doxygen @file commands.
diego
parents:
5913
diff
changeset
|
25 * @file |
3198 | 26 * IFF file demuxer |
3189 | 27 * by Jaikrishnan Menon |
28 * for more information on the .iff file format, visit: | |
29 * http://wiki.multimedia.cx/index.php?title=IFF | |
30 */ | |
31 | |
4201
7d2f3f1b68d8
Fix build: Add intreadwrite.h and bswap.h #includes where necessary.
diego
parents:
4090
diff
changeset
|
32 #include "libavutil/intreadwrite.h" |
5773
fd3b7b9b63a8
IFF: move ff_cmap_read_palette() prototype to a header file
mru
parents:
5660
diff
changeset
|
33 #include "libavcodec/iff.h" |
3189 | 34 #include "avformat.h" |
35 | |
36 #define ID_8SVX MKTAG('8','S','V','X') | |
37 #define ID_VHDR MKTAG('V','H','D','R') | |
38 #define ID_ATAK MKTAG('A','T','A','K') | |
39 #define ID_RLSE MKTAG('R','L','S','E') | |
40 #define ID_CHAN MKTAG('C','H','A','N') | |
5625 | 41 #define ID_PBM MKTAG('P','B','M',' ') |
42 #define ID_ILBM MKTAG('I','L','B','M') | |
43 #define ID_BMHD MKTAG('B','M','H','D') | |
44 #define ID_CMAP MKTAG('C','M','A','P') | |
3189 | 45 |
46 #define ID_FORM MKTAG('F','O','R','M') | |
47 #define ID_ANNO MKTAG('A','N','N','O') | |
48 #define ID_AUTH MKTAG('A','U','T','H') | |
49 #define ID_CHRS MKTAG('C','H','R','S') | |
50 #define ID_COPYRIGHT MKTAG('(','c',')',' ') | |
51 #define ID_CSET MKTAG('C','S','E','T') | |
52 #define ID_FVER MKTAG('F','V','E','R') | |
53 #define ID_NAME MKTAG('N','A','M','E') | |
54 #define ID_TEXT MKTAG('T','E','X','T') | |
55 #define ID_BODY MKTAG('B','O','D','Y') | |
5660 | 56 #define ID_ANNO MKTAG('A','N','N','O') |
3189 | 57 |
58 #define LEFT 2 | |
59 #define RIGHT 4 | |
60 #define STEREO 6 | |
61 | |
62 #define PACKET_SIZE 1024 | |
63 | |
5971
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
64 typedef enum { |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
65 COMP_NONE, |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
66 COMP_FIB, |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
67 COMP_EXP |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
68 } svx8_compression_type; |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
69 |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
70 typedef enum { |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
71 BITMAP_RAW, |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
72 BITMAP_BYTERUN1 |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
73 } bitmap_compression_type; |
3189 | 74 |
75 typedef struct { | |
5984
55c714a8fb00
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
rbultje
parents:
5971
diff
changeset
|
76 uint64_t body_pos; |
3189 | 77 uint32_t body_size; |
78 uint32_t sent_bytes; | |
79 uint32_t audio_frame_count; | |
80 } IffDemuxContext; | |
81 | |
3285 | 82 |
83 static void interleave_stereo(const uint8_t *src, uint8_t *dest, int size) | |
84 { | |
85 uint8_t *end = dest + size; | |
86 size = size>>1; | |
87 | |
88 while(dest < end) { | |
89 *dest++ = *src; | |
90 *dest++ = *(src+size); | |
91 src++; | |
92 } | |
93 } | |
94 | |
3189 | 95 static int iff_probe(AVProbeData *p) |
96 { | |
97 const uint8_t *d = p->buf; | |
98 | |
99 if ( AV_RL32(d) == ID_FORM && | |
5625 | 100 (AV_RL32(d+8) == ID_8SVX || AV_RL32(d+8) == ID_PBM || AV_RL32(d+8) == ID_ILBM) ) |
3189 | 101 return AVPROBE_SCORE_MAX; |
102 return 0; | |
103 } | |
104 | |
105 static int iff_read_header(AVFormatContext *s, | |
106 AVFormatParameters *ap) | |
107 { | |
108 IffDemuxContext *iff = s->priv_data; | |
109 ByteIOContext *pb = s->pb; | |
110 AVStream *st; | |
111 uint32_t chunk_id, data_size; | |
5625 | 112 int compression = -1; |
5660 | 113 char *buf; |
3189 | 114 |
115 st = av_new_stream(s, 0); | |
116 if (!st) | |
117 return AVERROR(ENOMEM); | |
118 | |
119 st->codec->channels = 1; | |
5625 | 120 url_fskip(pb, 8); |
121 // codec_tag used by ByteRun1 decoder to distinguish progressive (PBM) and interlaced (ILBM) content | |
122 st->codec->codec_tag = get_le32(pb); | |
3189 | 123 |
5984
55c714a8fb00
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
rbultje
parents:
5971
diff
changeset
|
124 while(!url_feof(pb)) { |
55c714a8fb00
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
rbultje
parents:
5971
diff
changeset
|
125 uint64_t orig_pos; |
3189 | 126 chunk_id = get_le32(pb); |
127 data_size = get_be32(pb); | |
5984
55c714a8fb00
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
rbultje
parents:
5971
diff
changeset
|
128 orig_pos = url_ftell(pb); |
3189 | 129 |
130 switch(chunk_id) { | |
131 case ID_VHDR: | |
5910
536e5527c1e0
Define AVMediaType enum, and use it instead of enum CodecType, which
stefano
parents:
5773
diff
changeset
|
132 st->codec->codec_type = AVMEDIA_TYPE_AUDIO; |
5984
55c714a8fb00
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
rbultje
parents:
5971
diff
changeset
|
133 |
55c714a8fb00
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
rbultje
parents:
5971
diff
changeset
|
134 if (data_size < 14) |
55c714a8fb00
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
rbultje
parents:
5971
diff
changeset
|
135 return AVERROR_INVALIDDATA; |
3189 | 136 url_fskip(pb, 12); |
137 st->codec->sample_rate = get_be16(pb); | |
5984
55c714a8fb00
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
rbultje
parents:
5971
diff
changeset
|
138 if (data_size >= 16) { |
3189 | 139 url_fskip(pb, 1); |
5625 | 140 compression = get_byte(pb); |
5984
55c714a8fb00
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
rbultje
parents:
5971
diff
changeset
|
141 } |
3189 | 142 break; |
143 | |
144 case ID_BODY: | |
5984
55c714a8fb00
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
rbultje
parents:
5971
diff
changeset
|
145 iff->body_pos = url_ftell(pb); |
3189 | 146 iff->body_size = data_size; |
147 break; | |
148 | |
149 case ID_CHAN: | |
5984
55c714a8fb00
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
rbultje
parents:
5971
diff
changeset
|
150 if (data_size < 4) |
55c714a8fb00
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
rbultje
parents:
5971
diff
changeset
|
151 return AVERROR_INVALIDDATA; |
3189 | 152 st->codec->channels = (get_be32(pb) < 6) ? 1 : 2; |
153 break; | |
154 | |
5625 | 155 case ID_CMAP: |
156 st->codec->extradata_size = data_size; | |
157 st->codec->extradata = av_malloc(data_size); | |
158 if (!st->codec->extradata) | |
159 return AVERROR(ENOMEM); | |
160 if (get_buffer(pb, st->codec->extradata, data_size) < 0) | |
161 return AVERROR(EIO); | |
162 break; | |
163 | |
164 case ID_BMHD: | |
5910
536e5527c1e0
Define AVMediaType enum, and use it instead of enum CodecType, which
stefano
parents:
5773
diff
changeset
|
165 st->codec->codec_type = AVMEDIA_TYPE_VIDEO; |
5984
55c714a8fb00
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
rbultje
parents:
5971
diff
changeset
|
166 if (data_size <= 8) |
55c714a8fb00
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
rbultje
parents:
5971
diff
changeset
|
167 return AVERROR_INVALIDDATA; |
5625 | 168 st->codec->width = get_be16(pb); |
169 st->codec->height = get_be16(pb); | |
170 url_fskip(pb, 4); // x, y offset | |
171 st->codec->bits_per_coded_sample = get_byte(pb); | |
5984
55c714a8fb00
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
rbultje
parents:
5971
diff
changeset
|
172 if (data_size >= 11) { |
5625 | 173 url_fskip(pb, 1); // masking |
174 compression = get_byte(pb); | |
5984
55c714a8fb00
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
rbultje
parents:
5971
diff
changeset
|
175 } |
55c714a8fb00
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
rbultje
parents:
5971
diff
changeset
|
176 if (data_size >= 16) { |
5625 | 177 url_fskip(pb, 3); // paddding, transparent |
178 st->sample_aspect_ratio.num = get_byte(pb); | |
179 st->sample_aspect_ratio.den = get_byte(pb); | |
5984
55c714a8fb00
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
rbultje
parents:
5971
diff
changeset
|
180 } |
5625 | 181 break; |
182 | |
5660 | 183 case ID_ANNO: |
184 buf = av_malloc(data_size + 1); | |
185 if (!buf) | |
186 break; | |
187 get_buffer(pb, buf, data_size); | |
188 buf[data_size] = 0; | |
189 av_metadata_set2(&s->metadata, "comment", buf, AV_METADATA_DONT_STRDUP_VAL); | |
190 break; | |
5984
55c714a8fb00
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
rbultje
parents:
5971
diff
changeset
|
191 } |
5660 | 192 |
5984
55c714a8fb00
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
rbultje
parents:
5971
diff
changeset
|
193 url_fskip(pb, data_size - (url_ftell(pb) - orig_pos) + (data_size & 1)); |
3189 | 194 } |
195 | |
5984
55c714a8fb00
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
rbultje
parents:
5971
diff
changeset
|
196 url_fseek(pb, iff->body_pos, SEEK_SET); |
55c714a8fb00
Make the IFF demuxer a little more standards-compliant, e.g. respect the size
rbultje
parents:
5971
diff
changeset
|
197 |
5625 | 198 switch(st->codec->codec_type) { |
5910
536e5527c1e0
Define AVMediaType enum, and use it instead of enum CodecType, which
stefano
parents:
5773
diff
changeset
|
199 case AVMEDIA_TYPE_AUDIO: |
5971
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
200 av_set_pts_info(st, 32, 1, st->codec->sample_rate); |
3189 | 201 |
5971
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
202 switch(compression) { |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
203 case COMP_NONE: |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
204 st->codec->codec_id = CODEC_ID_PCM_S8; |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
205 break; |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
206 case COMP_FIB: |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
207 st->codec->codec_id = CODEC_ID_8SVX_FIB; |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
208 break; |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
209 case COMP_EXP: |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
210 st->codec->codec_id = CODEC_ID_8SVX_EXP; |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
211 break; |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
212 default: |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
213 av_log(s, AV_LOG_ERROR, "iff: unknown compression method\n"); |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
214 return -1; |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
215 } |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
216 |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
217 st->codec->bits_per_coded_sample = 8; |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
218 st->codec->bit_rate = st->codec->channels * st->codec->sample_rate * st->codec->bits_per_coded_sample; |
43548500ffbc
Reindent / reformat some code with broken indenting.
rbultje
parents:
5969
diff
changeset
|
219 st->codec->block_align = st->codec->channels * st->codec->bits_per_coded_sample; |
3189 | 220 break; |
5625 | 221 |
5910
536e5527c1e0
Define AVMediaType enum, and use it instead of enum CodecType, which
stefano
parents:
5773
diff
changeset
|
222 case AVMEDIA_TYPE_VIDEO: |
5625 | 223 switch (compression) { |
224 case BITMAP_RAW: | |
225 if (st->codec->codec_tag == ID_ILBM) { | |
226 st->codec->codec_id = CODEC_ID_IFF_ILBM; | |
227 } else { | |
228 st->codec->codec_id = CODEC_ID_RAWVIDEO; | |
229 st->codec->pix_fmt = PIX_FMT_PAL8; | |
230 st->codec->codec_tag = 0; | |
231 } | |
232 break; | |
233 case BITMAP_BYTERUN1: | |
234 st->codec->codec_id = CODEC_ID_IFF_BYTERUN1; | |
235 break; | |
236 default: | |
237 av_log(s, AV_LOG_ERROR, "unknown compression method\n"); | |
238 return AVERROR_INVALIDDATA; | |
239 } | |
240 break; | |
241 default: | |
242 return -1; | |
243 } | |
3189 | 244 |
245 return 0; | |
246 } | |
247 | |
248 static int iff_read_packet(AVFormatContext *s, | |
249 AVPacket *pkt) | |
250 { | |
251 IffDemuxContext *iff = s->priv_data; | |
252 ByteIOContext *pb = s->pb; | |
5625 | 253 AVStream *st = s->streams[0]; |
3189 | 254 int ret; |
255 | |
5625 | 256 if(iff->sent_bytes >= iff->body_size) |
3189 | 257 return AVERROR(EIO); |
3285 | 258 |
259 if(s->streams[0]->codec->channels == 2) { | |
260 uint8_t sample_buffer[PACKET_SIZE]; | |
261 | |
262 ret = get_buffer(pb, sample_buffer, PACKET_SIZE); | |
263 if(av_new_packet(pkt, PACKET_SIZE) < 0) { | |
264 av_log(s, AV_LOG_ERROR, "iff: cannot allocate packet \n"); | |
265 return AVERROR(ENOMEM); | |
266 } | |
267 interleave_stereo(sample_buffer, pkt->data, PACKET_SIZE); | |
5625 | 268 } else if (s->streams[0]->codec->codec_id == CODEC_ID_RAWVIDEO) { |
269 if(av_new_packet(pkt, iff->body_size + AVPALETTE_SIZE) < 0) { | |
270 return AVERROR(ENOMEM); | |
271 } | |
272 | |
273 ret = ff_cmap_read_palette(st->codec, (uint32_t*)(pkt->data + iff->body_size)); | |
274 if (ret < 0) | |
275 return ret; | |
276 av_freep(&st->codec->extradata); | |
277 st->codec->extradata_size = 0; | |
278 | |
279 ret = get_buffer(pb, pkt->data, iff->body_size); | |
5910
536e5527c1e0
Define AVMediaType enum, and use it instead of enum CodecType, which
stefano
parents:
5773
diff
changeset
|
280 } else if (s->streams[0]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { |
5625 | 281 ret = av_get_packet(pb, pkt, iff->body_size); |
282 } else { | |
3285 | 283 ret = av_get_packet(pb, pkt, PACKET_SIZE); |
284 } | |
3189 | 285 |
286 if(iff->sent_bytes == 0) | |
5913
11bb10c37225
Replace all occurences of PKT_FLAG_KEY with AV_PKT_FLAG_KEY.
cehoyos
parents:
5910
diff
changeset
|
287 pkt->flags |= AV_PKT_FLAG_KEY; |
3189 | 288 |
5910
536e5527c1e0
Define AVMediaType enum, and use it instead of enum CodecType, which
stefano
parents:
5773
diff
changeset
|
289 if(s->streams[0]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { |
5626 | 290 iff->sent_bytes += PACKET_SIZE; |
5625 | 291 } else { |
292 iff->sent_bytes = iff->body_size; | |
293 } | |
3189 | 294 pkt->stream_index = 0; |
5910
536e5527c1e0
Define AVMediaType enum, and use it instead of enum CodecType, which
stefano
parents:
5773
diff
changeset
|
295 if(s->streams[0]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { |
5626 | 296 pkt->pts = iff->audio_frame_count; |
297 iff->audio_frame_count += ret / s->streams[0]->codec->channels; | |
5625 | 298 } |
3189 | 299 return ret; |
300 } | |
301 | |
302 AVInputFormat iff_demuxer = { | |
303 "IFF", | |
3424
7a0230981402
Make long_names in lavf/lavdev optional depending on CONFIG_SMALL.
diego
parents:
3285
diff
changeset
|
304 NULL_IF_CONFIG_SMALL("IFF format"), |
3189 | 305 sizeof(IffDemuxContext), |
306 iff_probe, | |
307 iff_read_header, | |
308 iff_read_packet, | |
309 }; |