Mercurial > libavformat.hg
annotate smacker.c @ 6118:6780dc315f36 libavformat
Remove support for pre-Haiku, non-POSIX, non-C99 BeOS variants.
BeOS support has been broken for many years and the "maintainer" of the port
has not reacted to countless requests to get the port fixed.
approved by Mans
author | diego |
---|---|
date | Thu, 10 Jun 2010 16:51:14 +0000 |
parents | 536e5527c1e0 |
children | 224077f3c564 |
rev | line source |
---|---|
1019 | 1 /* |
1415
3b00fb8ef8e4
replace coder/decoder file description in libavformat by muxer/demuxer
aurel
parents:
1358
diff
changeset
|
2 * Smacker demuxer |
4251
77e0c7511d41
cosmetics: Remove pointless period after copyright statement non-sentences.
diego
parents:
4201
diff
changeset
|
3 * Copyright (c) 2006 Konstantin Shishkov |
1019 | 4 * |
1358
0899bfe4105c
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
1172
diff
changeset
|
5 * This file is part of FFmpeg. |
0899bfe4105c
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
1172
diff
changeset
|
6 * |
0899bfe4105c
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
1172
diff
changeset
|
7 * FFmpeg is free software; you can redistribute it and/or |
1019 | 8 * modify it under the terms of the GNU Lesser General Public |
9 * License as published by the Free Software Foundation; either | |
1358
0899bfe4105c
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
1172
diff
changeset
|
10 * version 2.1 of the License, or (at your option) any later version. |
1019 | 11 * |
1358
0899bfe4105c
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
1172
diff
changeset
|
12 * FFmpeg is distributed in the hope that it will be useful, |
1019 | 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 * Lesser General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU Lesser General Public | |
1358
0899bfe4105c
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
1172
diff
changeset
|
18 * License along with FFmpeg; if not, write to the Free Software |
1019 | 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 */ | |
21 | |
22 /* | |
23 * Based on http://wiki.multimedia.cx/index.php?title=Smacker | |
24 */ | |
25 | |
3286 | 26 #include "libavutil/bswap.h" |
4201
7d2f3f1b68d8
Fix build: Add intreadwrite.h and bswap.h #includes where necessary.
diego
parents:
3973
diff
changeset
|
27 #include "libavutil/intreadwrite.h" |
1019 | 28 #include "avformat.h" |
29 | |
30 #define SMACKER_PAL 0x01 | |
2404 | 31 #define SMACKER_FLAG_RING_FRAME 0x01 |
1019 | 32 |
33 enum SAudFlags { | |
34 SMK_AUD_PACKED = 0x80000000, | |
35 SMK_AUD_16BITS = 0x20000000, | |
36 SMK_AUD_STEREO = 0x10000000, | |
37 SMK_AUD_BINKAUD = 0x08000000, | |
38 SMK_AUD_USEDCT = 0x04000000 | |
39 }; | |
40 | |
41 typedef struct SmackerContext { | |
42 /* Smacker file header */ | |
43 uint32_t magic; | |
44 uint32_t width, height; | |
45 uint32_t frames; | |
46 int pts_inc; | |
47 uint32_t flags; | |
48 uint32_t audio[7]; | |
49 uint32_t treesize; | |
50 uint32_t mmap_size, mclr_size, full_size, type_size; | |
51 uint32_t rates[7]; | |
52 uint32_t pad; | |
53 /* frame info */ | |
54 uint32_t *frm_size; | |
55 uint8_t *frm_flags; | |
56 /* internal variables */ | |
57 int cur_frame; | |
58 int is_ver4; | |
59 int64_t cur_pts; | |
60 /* current frame for demuxing */ | |
61 uint8_t pal[768]; | |
62 int indexes[7]; | |
63 int videoindex; | |
64 uint8_t *bufs[7]; | |
65 int buf_sizes[7]; | |
66 int stream_id[7]; | |
67 int curstream; | |
3973
549a09cf23fe
Remove offset_t typedef and use int64_t directly instead.
diego
parents:
3908
diff
changeset
|
68 int64_t nextpos; |
1090 | 69 int64_t aud_pts[7]; |
1019 | 70 } SmackerContext; |
71 | |
72 typedef struct SmackerFrame { | |
73 int64_t pts; | |
74 int stream; | |
75 } SmackerFrame; | |
76 | |
77 /* palette used in Smacker */ | |
78 static const uint8_t smk_pal[64] = { | |
79 0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C, | |
80 0x20, 0x24, 0x28, 0x2C, 0x30, 0x34, 0x38, 0x3C, | |
81 0x41, 0x45, 0x49, 0x4D, 0x51, 0x55, 0x59, 0x5D, | |
82 0x61, 0x65, 0x69, 0x6D, 0x71, 0x75, 0x79, 0x7D, | |
83 0x82, 0x86, 0x8A, 0x8E, 0x92, 0x96, 0x9A, 0x9E, | |
84 0xA2, 0xA6, 0xAA, 0xAE, 0xB2, 0xB6, 0xBA, 0xBE, | |
85 0xC3, 0xC7, 0xCB, 0xCF, 0xD3, 0xD7, 0xDB, 0xDF, | |
86 0xE3, 0xE7, 0xEB, 0xEF, 0xF3, 0xF7, 0xFB, 0xFF | |
87 }; | |
88 | |
89 | |
90 static int smacker_probe(AVProbeData *p) | |
91 { | |
92 if(p->buf[0] == 'S' && p->buf[1] == 'M' && p->buf[2] == 'K' | |
93 && (p->buf[3] == '2' || p->buf[3] == '4')) | |
94 return AVPROBE_SCORE_MAX; | |
95 else | |
96 return 0; | |
97 } | |
98 | |
99 static int smacker_read_header(AVFormatContext *s, AVFormatParameters *ap) | |
100 { | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2404
diff
changeset
|
101 ByteIOContext *pb = s->pb; |
2006 | 102 SmackerContext *smk = s->priv_data; |
1019 | 103 AVStream *st, *ast[7]; |
104 int i, ret; | |
105 int tbase; | |
106 | |
107 /* read and check header */ | |
108 smk->magic = get_le32(pb); | |
109 if (smk->magic != MKTAG('S', 'M', 'K', '2') && smk->magic != MKTAG('S', 'M', 'K', '4')) | |
110 return -1; | |
111 smk->width = get_le32(pb); | |
112 smk->height = get_le32(pb); | |
113 smk->frames = get_le32(pb); | |
114 smk->pts_inc = (int32_t)get_le32(pb); | |
115 smk->flags = get_le32(pb); | |
2404 | 116 if(smk->flags & SMACKER_FLAG_RING_FRAME) |
117 smk->frames++; | |
1019 | 118 for(i = 0; i < 7; i++) |
119 smk->audio[i] = get_le32(pb); | |
120 smk->treesize = get_le32(pb); | |
1079 | 121 |
122 if(smk->treesize >= UINT_MAX/4){ // smk->treesize + 16 must not overflow (this check is probably redundant) | |
123 av_log(s, AV_LOG_ERROR, "treesize too large\n"); | |
124 return -1; | |
125 } | |
126 | |
127 //FIXME remove extradata "rebuilding" | |
1019 | 128 smk->mmap_size = get_le32(pb); |
129 smk->mclr_size = get_le32(pb); | |
130 smk->full_size = get_le32(pb); | |
131 smk->type_size = get_le32(pb); | |
132 for(i = 0; i < 7; i++) | |
133 smk->rates[i] = get_le32(pb); | |
134 smk->pad = get_le32(pb); | |
135 /* setup data */ | |
136 if(smk->frames > 0xFFFFFF) { | |
137 av_log(s, AV_LOG_ERROR, "Too many frames: %i\n", smk->frames); | |
138 return -1; | |
139 } | |
140 smk->frm_size = av_malloc(smk->frames * 4); | |
141 smk->frm_flags = av_malloc(smk->frames); | |
142 | |
143 smk->is_ver4 = (smk->magic != MKTAG('S', 'M', 'K', '2')); | |
144 | |
145 /* read frame info */ | |
146 for(i = 0; i < smk->frames; i++) { | |
147 smk->frm_size[i] = get_le32(pb); | |
148 } | |
149 for(i = 0; i < smk->frames; i++) { | |
150 smk->frm_flags[i] = get_byte(pb); | |
151 } | |
152 | |
153 /* init video codec */ | |
154 st = av_new_stream(s, 0); | |
155 if (!st) | |
156 return -1; | |
157 smk->videoindex = st->index; | |
158 st->codec->width = smk->width; | |
159 st->codec->height = smk->height; | |
160 st->codec->pix_fmt = PIX_FMT_PAL8; | |
5910
536e5527c1e0
Define AVMediaType enum, and use it instead of enum CodecType, which
stefano
parents:
5608
diff
changeset
|
161 st->codec->codec_type = AVMEDIA_TYPE_VIDEO; |
1019 | 162 st->codec->codec_id = CODEC_ID_SMACKVIDEO; |
1089
0319672689ef
Now MPlayer should understand Smacker audio and video codecs.
kostya
parents:
1079
diff
changeset
|
163 st->codec->codec_tag = smk->magic; |
1019 | 164 /* Smacker uses 100000 as internal timebase */ |
165 if(smk->pts_inc < 0) | |
166 smk->pts_inc = -smk->pts_inc; | |
167 else | |
168 smk->pts_inc *= 100; | |
169 tbase = 100000; | |
170 av_reduce(&tbase, &smk->pts_inc, tbase, smk->pts_inc, (1UL<<31)-1); | |
171 av_set_pts_info(st, 33, smk->pts_inc, tbase); | |
5608 | 172 st->duration = smk->frames; |
1019 | 173 /* handle possible audio streams */ |
174 for(i = 0; i < 7; i++) { | |
175 smk->indexes[i] = -1; | |
5607 | 176 if(smk->rates[i] & 0xFFFFFF){ |
1019 | 177 ast[i] = av_new_stream(s, 0); |
178 smk->indexes[i] = ast[i]->index; | |
5910
536e5527c1e0
Define AVMediaType enum, and use it instead of enum CodecType, which
stefano
parents:
5608
diff
changeset
|
179 ast[i]->codec->codec_type = AVMEDIA_TYPE_AUDIO; |
5607 | 180 if (smk->rates[i] & SMK_AUD_BINKAUD) { |
181 ast[i]->codec->codec_id = CODEC_ID_BINKAUDIO_RDFT; | |
182 } else if (smk->rates[i] & SMK_AUD_USEDCT) { | |
183 ast[i]->codec->codec_id = CODEC_ID_BINKAUDIO_DCT; | |
184 } else if (smk->rates[i] & SMK_AUD_PACKED){ | |
185 ast[i]->codec->codec_id = CODEC_ID_SMACKAUDIO; | |
186 ast[i]->codec->codec_tag = MKTAG('S', 'M', 'K', 'A'); | |
187 } else { | |
188 ast[i]->codec->codec_id = CODEC_ID_PCM_U8; | |
189 } | |
1019 | 190 ast[i]->codec->channels = (smk->rates[i] & SMK_AUD_STEREO) ? 2 : 1; |
191 ast[i]->codec->sample_rate = smk->rates[i] & 0xFFFFFF; | |
3908
1d3d17de20ba
Bump Major version, this commit is almost just renaming bits_per_sample to
michael
parents:
3424
diff
changeset
|
192 ast[i]->codec->bits_per_coded_sample = (smk->rates[i] & SMK_AUD_16BITS) ? 16 : 8; |
1d3d17de20ba
Bump Major version, this commit is almost just renaming bits_per_sample to
michael
parents:
3424
diff
changeset
|
193 if(ast[i]->codec->bits_per_coded_sample == 16 && ast[i]->codec->codec_id == CODEC_ID_PCM_U8) |
1019 | 194 ast[i]->codec->codec_id = CODEC_ID_PCM_S16LE; |
1090 | 195 av_set_pts_info(ast[i], 64, 1, ast[i]->codec->sample_rate |
3908
1d3d17de20ba
Bump Major version, this commit is almost just renaming bits_per_sample to
michael
parents:
3424
diff
changeset
|
196 * ast[i]->codec->channels * ast[i]->codec->bits_per_coded_sample / 8); |
1019 | 197 } |
198 } | |
199 | |
200 | |
201 /* load trees to extradata, they will be unpacked by decoder */ | |
202 st->codec->extradata = av_malloc(smk->treesize + 16); | |
203 st->codec->extradata_size = smk->treesize + 16; | |
204 if(!st->codec->extradata){ | |
205 av_log(s, AV_LOG_ERROR, "Cannot allocate %i bytes of extradata\n", smk->treesize + 16); | |
206 av_free(smk->frm_size); | |
207 av_free(smk->frm_flags); | |
208 return -1; | |
209 } | |
210 ret = get_buffer(pb, st->codec->extradata + 16, st->codec->extradata_size - 16); | |
211 if(ret != st->codec->extradata_size - 16){ | |
212 av_free(smk->frm_size); | |
213 av_free(smk->frm_flags); | |
2274
b21c2af60bc9
Replace all occurrences of AVERROR_IO with AVERROR(EIO).
takis
parents:
2273
diff
changeset
|
214 return AVERROR(EIO); |
1019 | 215 } |
216 ((int32_t*)st->codec->extradata)[0] = le2me_32(smk->mmap_size); | |
217 ((int32_t*)st->codec->extradata)[1] = le2me_32(smk->mclr_size); | |
218 ((int32_t*)st->codec->extradata)[2] = le2me_32(smk->full_size); | |
219 ((int32_t*)st->codec->extradata)[3] = le2me_32(smk->type_size); | |
220 | |
221 smk->curstream = -1; | |
222 smk->nextpos = url_ftell(pb); | |
223 | |
224 return 0; | |
225 } | |
226 | |
227 | |
228 static int smacker_read_packet(AVFormatContext *s, AVPacket *pkt) | |
229 { | |
2006 | 230 SmackerContext *smk = s->priv_data; |
1019 | 231 int flags; |
232 int ret; | |
233 int i; | |
234 int frame_size = 0; | |
235 int palchange = 0; | |
236 int pos; | |
237 | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2404
diff
changeset
|
238 if (url_feof(s->pb) || smk->cur_frame >= smk->frames) |
1787
eb16c64144ee
This fixes error handling for BeOS, removing the need for some ifdefs.
mmu_man
parents:
1673
diff
changeset
|
239 return AVERROR(EIO); |
1019 | 240 |
241 /* if we demuxed all streams, pass another frame */ | |
242 if(smk->curstream < 0) { | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2404
diff
changeset
|
243 url_fseek(s->pb, smk->nextpos, 0); |
1019 | 244 frame_size = smk->frm_size[smk->cur_frame] & (~3); |
245 flags = smk->frm_flags[smk->cur_frame]; | |
246 /* handle palette change event */ | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2404
diff
changeset
|
247 pos = url_ftell(s->pb); |
1019 | 248 if(flags & SMACKER_PAL){ |
249 int size, sz, t, off, j, pos; | |
250 uint8_t *pal = smk->pal; | |
251 uint8_t oldpal[768]; | |
252 | |
253 memcpy(oldpal, pal, 768); | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2404
diff
changeset
|
254 size = get_byte(s->pb); |
1019 | 255 size = size * 4 - 1; |
256 frame_size -= size; | |
257 frame_size--; | |
258 sz = 0; | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2404
diff
changeset
|
259 pos = url_ftell(s->pb) + size; |
1019 | 260 while(sz < 256){ |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2404
diff
changeset
|
261 t = get_byte(s->pb); |
1019 | 262 if(t & 0x80){ /* skip palette entries */ |
263 sz += (t & 0x7F) + 1; | |
264 pal += ((t & 0x7F) + 1) * 3; | |
265 } else if(t & 0x40){ /* copy with offset */ | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2404
diff
changeset
|
266 off = get_byte(s->pb) * 3; |
1019 | 267 j = (t & 0x3F) + 1; |
268 while(j-- && sz < 256) { | |
269 *pal++ = oldpal[off + 0]; | |
270 *pal++ = oldpal[off + 1]; | |
271 *pal++ = oldpal[off + 2]; | |
272 sz++; | |
273 off += 3; | |
274 } | |
275 } else { /* new entries */ | |
276 *pal++ = smk_pal[t]; | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2404
diff
changeset
|
277 *pal++ = smk_pal[get_byte(s->pb) & 0x3F]; |
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2404
diff
changeset
|
278 *pal++ = smk_pal[get_byte(s->pb) & 0x3F]; |
1019 | 279 sz++; |
280 } | |
281 } | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2404
diff
changeset
|
282 url_fseek(s->pb, pos, 0); |
1019 | 283 palchange |= 1; |
284 } | |
285 flags >>= 1; | |
286 smk->curstream = -1; | |
287 /* if audio chunks are present, put them to stack and retrieve later */ | |
288 for(i = 0; i < 7; i++) { | |
289 if(flags & 1) { | |
290 int size; | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2404
diff
changeset
|
291 size = get_le32(s->pb) - 4; |
1019 | 292 frame_size -= size; |
293 frame_size -= 4; | |
294 smk->curstream++; | |
295 smk->bufs[smk->curstream] = av_realloc(smk->bufs[smk->curstream], size); | |
296 smk->buf_sizes[smk->curstream] = size; | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2404
diff
changeset
|
297 ret = get_buffer(s->pb, smk->bufs[smk->curstream], size); |
1019 | 298 if(ret != size) |
2274
b21c2af60bc9
Replace all occurrences of AVERROR_IO with AVERROR(EIO).
takis
parents:
2273
diff
changeset
|
299 return AVERROR(EIO); |
1019 | 300 smk->stream_id[smk->curstream] = smk->indexes[i]; |
301 } | |
302 flags >>= 1; | |
303 } | |
304 if (av_new_packet(pkt, frame_size + 768)) | |
2273
7eb456c4ed8a
Replace all occurrences of AVERROR_NOMEM with AVERROR(ENOMEM).
takis
parents:
2006
diff
changeset
|
305 return AVERROR(ENOMEM); |
1019 | 306 if(smk->frm_size[smk->cur_frame] & 1) |
307 palchange |= 2; | |
308 pkt->data[0] = palchange; | |
309 memcpy(pkt->data + 1, smk->pal, 768); | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2404
diff
changeset
|
310 ret = get_buffer(s->pb, pkt->data + 769, frame_size); |
1019 | 311 if(ret != frame_size) |
2274
b21c2af60bc9
Replace all occurrences of AVERROR_IO with AVERROR(EIO).
takis
parents:
2273
diff
changeset
|
312 return AVERROR(EIO); |
1019 | 313 pkt->stream_index = smk->videoindex; |
314 pkt->size = ret + 769; | |
315 smk->cur_frame++; | |
2771
d52c718e83f9
Use dynamically allocated ByteIOContext in AVFormatContext
andoma
parents:
2404
diff
changeset
|
316 smk->nextpos = url_ftell(s->pb); |
1019 | 317 } else { |
318 if (av_new_packet(pkt, smk->buf_sizes[smk->curstream])) | |
2273
7eb456c4ed8a
Replace all occurrences of AVERROR_NOMEM with AVERROR(ENOMEM).
takis
parents:
2006
diff
changeset
|
319 return AVERROR(ENOMEM); |
1019 | 320 memcpy(pkt->data, smk->bufs[smk->curstream], smk->buf_sizes[smk->curstream]); |
321 pkt->size = smk->buf_sizes[smk->curstream]; | |
322 pkt->stream_index = smk->stream_id[smk->curstream]; | |
1090 | 323 pkt->pts = smk->aud_pts[smk->curstream]; |
1673 | 324 smk->aud_pts[smk->curstream] += AV_RL32(pkt->data); |
1019 | 325 smk->curstream--; |
326 } | |
327 | |
328 return 0; | |
329 } | |
330 | |
331 static int smacker_read_close(AVFormatContext *s) | |
332 { | |
2006 | 333 SmackerContext *smk = s->priv_data; |
1019 | 334 int i; |
335 | |
336 for(i = 0; i < 7; i++) | |
337 if(smk->bufs[i]) | |
338 av_free(smk->bufs[i]); | |
339 if(smk->frm_size) | |
340 av_free(smk->frm_size); | |
341 if(smk->frm_flags) | |
342 av_free(smk->frm_flags); | |
343 | |
344 return 0; | |
345 } | |
346 | |
1169 | 347 AVInputFormat smacker_demuxer = { |
1019 | 348 "smk", |
3424
7a0230981402
Make long_names in lavf/lavdev optional depending on CONFIG_SMALL.
diego
parents:
3399
diff
changeset
|
349 NULL_IF_CONFIG_SMALL("Smacker video"), |
1019 | 350 sizeof(SmackerContext), |
351 smacker_probe, | |
352 smacker_read_header, | |
353 smacker_read_packet, | |
354 smacker_read_close, | |
355 }; |