Mercurial > libavformat.hg
annotate smacker.c @ 1942:70b741fa63eb libavformat
The NSV demuxer assumes that a video frame's timestamp increases by one on each
frame, but some low-bitrate NSV files omit video frames for some NSV frames,
and expect the timestamp to increase by one every NSV frame. This is noticeable
in 64vp3.nsv where the video runs several times faster than the audio. Fix this
by unconditionally incrementing the video's timestamp with each NSV frame.
patch by David Conrad, umovimus gmail com
author | diego |
---|---|
date | Wed, 21 Mar 2007 11:05:35 +0000 |
parents | eb16c64144ee |
children | 1a3c9056982a |
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 |
1019 | 3 * Copyright (c) 2006 Konstantin Shishkov. |
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 | |
26 #include "avformat.h" | |
1172
6a5e58d2114b
move common stuff from avienc.c and wav.c to new file riff.c
mru
parents:
1169
diff
changeset
|
27 #include "riff.h" |
1019 | 28 #include "bswap.h" |
29 | |
30 #define SMACKER_PAL 0x01 | |
31 | |
32 enum SAudFlags { | |
33 SMK_AUD_PACKED = 0x80000000, | |
34 SMK_AUD_16BITS = 0x20000000, | |
35 SMK_AUD_STEREO = 0x10000000, | |
36 SMK_AUD_BINKAUD = 0x08000000, | |
37 SMK_AUD_USEDCT = 0x04000000 | |
38 }; | |
39 | |
40 typedef struct SmackerContext { | |
41 /* Smacker file header */ | |
42 uint32_t magic; | |
43 uint32_t width, height; | |
44 uint32_t frames; | |
45 int pts_inc; | |
46 uint32_t flags; | |
47 uint32_t audio[7]; | |
48 uint32_t treesize; | |
49 uint32_t mmap_size, mclr_size, full_size, type_size; | |
50 uint32_t rates[7]; | |
51 uint32_t pad; | |
52 /* frame info */ | |
53 uint32_t *frm_size; | |
54 uint8_t *frm_flags; | |
55 /* internal variables */ | |
56 int cur_frame; | |
57 int is_ver4; | |
58 int64_t cur_pts; | |
59 /* current frame for demuxing */ | |
60 uint8_t pal[768]; | |
61 int indexes[7]; | |
62 int videoindex; | |
63 uint8_t *bufs[7]; | |
64 int buf_sizes[7]; | |
65 int stream_id[7]; | |
66 int curstream; | |
67 offset_t nextpos; | |
1090 | 68 int64_t aud_pts[7]; |
1019 | 69 } SmackerContext; |
70 | |
71 typedef struct SmackerFrame { | |
72 int64_t pts; | |
73 int stream; | |
74 } SmackerFrame; | |
75 | |
76 /* palette used in Smacker */ | |
77 static const uint8_t smk_pal[64] = { | |
78 0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C, | |
79 0x20, 0x24, 0x28, 0x2C, 0x30, 0x34, 0x38, 0x3C, | |
80 0x41, 0x45, 0x49, 0x4D, 0x51, 0x55, 0x59, 0x5D, | |
81 0x61, 0x65, 0x69, 0x6D, 0x71, 0x75, 0x79, 0x7D, | |
82 0x82, 0x86, 0x8A, 0x8E, 0x92, 0x96, 0x9A, 0x9E, | |
83 0xA2, 0xA6, 0xAA, 0xAE, 0xB2, 0xB6, 0xBA, 0xBE, | |
84 0xC3, 0xC7, 0xCB, 0xCF, 0xD3, 0xD7, 0xDB, 0xDF, | |
85 0xE3, 0xE7, 0xEB, 0xEF, 0xF3, 0xF7, 0xFB, 0xFF | |
86 }; | |
87 | |
88 | |
89 static int smacker_probe(AVProbeData *p) | |
90 { | |
91 if (p->buf_size < 4) | |
92 return 0; | |
93 if(p->buf[0] == 'S' && p->buf[1] == 'M' && p->buf[2] == 'K' | |
94 && (p->buf[3] == '2' || p->buf[3] == '4')) | |
95 return AVPROBE_SCORE_MAX; | |
96 else | |
97 return 0; | |
98 } | |
99 | |
100 static int smacker_read_header(AVFormatContext *s, AVFormatParameters *ap) | |
101 { | |
102 ByteIOContext *pb = &s->pb; | |
103 SmackerContext *smk = (SmackerContext *)s->priv_data; | |
104 AVStream *st, *ast[7]; | |
105 int i, ret; | |
106 int tbase; | |
107 | |
108 /* read and check header */ | |
109 smk->magic = get_le32(pb); | |
110 if (smk->magic != MKTAG('S', 'M', 'K', '2') && smk->magic != MKTAG('S', 'M', 'K', '4')) | |
111 return -1; | |
112 smk->width = get_le32(pb); | |
113 smk->height = get_le32(pb); | |
114 smk->frames = get_le32(pb); | |
115 smk->pts_inc = (int32_t)get_le32(pb); | |
116 smk->flags = get_le32(pb); | |
117 for(i = 0; i < 7; i++) | |
118 smk->audio[i] = get_le32(pb); | |
119 smk->treesize = get_le32(pb); | |
1079 | 120 |
121 if(smk->treesize >= UINT_MAX/4){ // smk->treesize + 16 must not overflow (this check is probably redundant) | |
122 av_log(s, AV_LOG_ERROR, "treesize too large\n"); | |
123 return -1; | |
124 } | |
125 | |
126 //FIXME remove extradata "rebuilding" | |
1019 | 127 smk->mmap_size = get_le32(pb); |
128 smk->mclr_size = get_le32(pb); | |
129 smk->full_size = get_le32(pb); | |
130 smk->type_size = get_le32(pb); | |
131 for(i = 0; i < 7; i++) | |
132 smk->rates[i] = get_le32(pb); | |
133 smk->pad = get_le32(pb); | |
134 /* setup data */ | |
135 if(smk->frames > 0xFFFFFF) { | |
136 av_log(s, AV_LOG_ERROR, "Too many frames: %i\n", smk->frames); | |
137 return -1; | |
138 } | |
139 smk->frm_size = av_malloc(smk->frames * 4); | |
140 smk->frm_flags = av_malloc(smk->frames); | |
141 | |
142 smk->is_ver4 = (smk->magic != MKTAG('S', 'M', 'K', '2')); | |
143 | |
144 /* read frame info */ | |
145 for(i = 0; i < smk->frames; i++) { | |
146 smk->frm_size[i] = get_le32(pb); | |
147 } | |
148 for(i = 0; i < smk->frames; i++) { | |
149 smk->frm_flags[i] = get_byte(pb); | |
150 } | |
151 | |
152 /* init video codec */ | |
153 st = av_new_stream(s, 0); | |
154 if (!st) | |
155 return -1; | |
156 smk->videoindex = st->index; | |
157 st->codec->width = smk->width; | |
158 st->codec->height = smk->height; | |
159 st->codec->pix_fmt = PIX_FMT_PAL8; | |
160 st->codec->codec_type = CODEC_TYPE_VIDEO; | |
161 st->codec->codec_id = CODEC_ID_SMACKVIDEO; | |
1089
0319672689ef
Now MPlayer should understand Smacker audio and video codecs.
kostya
parents:
1079
diff
changeset
|
162 st->codec->codec_tag = smk->magic; |
1019 | 163 /* Smacker uses 100000 as internal timebase */ |
164 if(smk->pts_inc < 0) | |
165 smk->pts_inc = -smk->pts_inc; | |
166 else | |
167 smk->pts_inc *= 100; | |
168 tbase = 100000; | |
169 av_reduce(&tbase, &smk->pts_inc, tbase, smk->pts_inc, (1UL<<31)-1); | |
170 av_set_pts_info(st, 33, smk->pts_inc, tbase); | |
171 /* handle possible audio streams */ | |
172 for(i = 0; i < 7; i++) { | |
173 smk->indexes[i] = -1; | |
174 if((smk->rates[i] & 0xFFFFFF) && !(smk->rates[i] & SMK_AUD_BINKAUD)){ | |
175 ast[i] = av_new_stream(s, 0); | |
176 smk->indexes[i] = ast[i]->index; | |
177 ast[i]->codec->codec_type = CODEC_TYPE_AUDIO; | |
178 ast[i]->codec->codec_id = (smk->rates[i] & SMK_AUD_PACKED) ? CODEC_ID_SMACKAUDIO : CODEC_ID_PCM_U8; | |
1089
0319672689ef
Now MPlayer should understand Smacker audio and video codecs.
kostya
parents:
1079
diff
changeset
|
179 ast[i]->codec->codec_tag = MKTAG('S', 'M', 'K', 'A'); |
1019 | 180 ast[i]->codec->channels = (smk->rates[i] & SMK_AUD_STEREO) ? 2 : 1; |
181 ast[i]->codec->sample_rate = smk->rates[i] & 0xFFFFFF; | |
182 ast[i]->codec->bits_per_sample = (smk->rates[i] & SMK_AUD_16BITS) ? 16 : 8; | |
183 if(ast[i]->codec->bits_per_sample == 16 && ast[i]->codec->codec_id == CODEC_ID_PCM_U8) | |
184 ast[i]->codec->codec_id = CODEC_ID_PCM_S16LE; | |
1090 | 185 av_set_pts_info(ast[i], 64, 1, ast[i]->codec->sample_rate |
186 * ast[i]->codec->channels * ast[i]->codec->bits_per_sample / 8); | |
1019 | 187 } |
188 } | |
189 | |
190 | |
191 /* load trees to extradata, they will be unpacked by decoder */ | |
192 st->codec->extradata = av_malloc(smk->treesize + 16); | |
193 st->codec->extradata_size = smk->treesize + 16; | |
194 if(!st->codec->extradata){ | |
195 av_log(s, AV_LOG_ERROR, "Cannot allocate %i bytes of extradata\n", smk->treesize + 16); | |
196 av_free(smk->frm_size); | |
197 av_free(smk->frm_flags); | |
198 return -1; | |
199 } | |
200 ret = get_buffer(pb, st->codec->extradata + 16, st->codec->extradata_size - 16); | |
201 if(ret != st->codec->extradata_size - 16){ | |
202 av_free(smk->frm_size); | |
203 av_free(smk->frm_flags); | |
204 return AVERROR_IO; | |
205 } | |
206 ((int32_t*)st->codec->extradata)[0] = le2me_32(smk->mmap_size); | |
207 ((int32_t*)st->codec->extradata)[1] = le2me_32(smk->mclr_size); | |
208 ((int32_t*)st->codec->extradata)[2] = le2me_32(smk->full_size); | |
209 ((int32_t*)st->codec->extradata)[3] = le2me_32(smk->type_size); | |
210 | |
211 smk->curstream = -1; | |
212 smk->nextpos = url_ftell(pb); | |
213 | |
214 return 0; | |
215 } | |
216 | |
217 | |
218 static int smacker_read_packet(AVFormatContext *s, AVPacket *pkt) | |
219 { | |
220 SmackerContext *smk = (SmackerContext *)s->priv_data; | |
221 int flags; | |
222 int ret; | |
223 int i; | |
224 int frame_size = 0; | |
225 int palchange = 0; | |
226 int pos; | |
227 | |
228 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
|
229 return AVERROR(EIO); |
1019 | 230 |
231 /* if we demuxed all streams, pass another frame */ | |
232 if(smk->curstream < 0) { | |
233 url_fseek(&s->pb, smk->nextpos, 0); | |
234 frame_size = smk->frm_size[smk->cur_frame] & (~3); | |
235 flags = smk->frm_flags[smk->cur_frame]; | |
236 /* handle palette change event */ | |
237 pos = url_ftell(&s->pb); | |
238 if(flags & SMACKER_PAL){ | |
239 int size, sz, t, off, j, pos; | |
240 uint8_t *pal = smk->pal; | |
241 uint8_t oldpal[768]; | |
242 | |
243 memcpy(oldpal, pal, 768); | |
244 size = get_byte(&s->pb); | |
245 size = size * 4 - 1; | |
246 frame_size -= size; | |
247 frame_size--; | |
248 sz = 0; | |
249 pos = url_ftell(&s->pb) + size; | |
250 while(sz < 256){ | |
251 t = get_byte(&s->pb); | |
252 if(t & 0x80){ /* skip palette entries */ | |
253 sz += (t & 0x7F) + 1; | |
254 pal += ((t & 0x7F) + 1) * 3; | |
255 } else if(t & 0x40){ /* copy with offset */ | |
256 off = get_byte(&s->pb) * 3; | |
257 j = (t & 0x3F) + 1; | |
258 while(j-- && sz < 256) { | |
259 *pal++ = oldpal[off + 0]; | |
260 *pal++ = oldpal[off + 1]; | |
261 *pal++ = oldpal[off + 2]; | |
262 sz++; | |
263 off += 3; | |
264 } | |
265 } else { /* new entries */ | |
266 *pal++ = smk_pal[t]; | |
267 *pal++ = smk_pal[get_byte(&s->pb) & 0x3F]; | |
268 *pal++ = smk_pal[get_byte(&s->pb) & 0x3F]; | |
269 sz++; | |
270 } | |
271 } | |
272 url_fseek(&s->pb, pos, 0); | |
273 palchange |= 1; | |
274 } | |
275 flags >>= 1; | |
276 smk->curstream = -1; | |
277 /* if audio chunks are present, put them to stack and retrieve later */ | |
278 for(i = 0; i < 7; i++) { | |
279 if(flags & 1) { | |
280 int size; | |
281 size = get_le32(&s->pb) - 4; | |
282 frame_size -= size; | |
283 frame_size -= 4; | |
284 smk->curstream++; | |
285 smk->bufs[smk->curstream] = av_realloc(smk->bufs[smk->curstream], size); | |
286 smk->buf_sizes[smk->curstream] = size; | |
287 ret = get_buffer(&s->pb, smk->bufs[smk->curstream], size); | |
288 if(ret != size) | |
289 return AVERROR_IO; | |
290 smk->stream_id[smk->curstream] = smk->indexes[i]; | |
291 } | |
292 flags >>= 1; | |
293 } | |
294 if (av_new_packet(pkt, frame_size + 768)) | |
295 return AVERROR_NOMEM; | |
296 if(smk->frm_size[smk->cur_frame] & 1) | |
297 palchange |= 2; | |
298 pkt->data[0] = palchange; | |
299 memcpy(pkt->data + 1, smk->pal, 768); | |
300 ret = get_buffer(&s->pb, pkt->data + 769, frame_size); | |
301 if(ret != frame_size) | |
302 return AVERROR_IO; | |
303 pkt->stream_index = smk->videoindex; | |
304 pkt->size = ret + 769; | |
305 smk->cur_frame++; | |
306 smk->nextpos = url_ftell(&s->pb); | |
307 } else { | |
308 if (av_new_packet(pkt, smk->buf_sizes[smk->curstream])) | |
309 return AVERROR_NOMEM; | |
310 memcpy(pkt->data, smk->bufs[smk->curstream], smk->buf_sizes[smk->curstream]); | |
311 pkt->size = smk->buf_sizes[smk->curstream]; | |
312 pkt->stream_index = smk->stream_id[smk->curstream]; | |
1090 | 313 pkt->pts = smk->aud_pts[smk->curstream]; |
1673 | 314 smk->aud_pts[smk->curstream] += AV_RL32(pkt->data); |
1019 | 315 smk->curstream--; |
316 } | |
317 | |
318 return 0; | |
319 } | |
320 | |
321 static int smacker_read_close(AVFormatContext *s) | |
322 { | |
323 SmackerContext *smk = (SmackerContext *)s->priv_data; | |
324 int i; | |
325 | |
326 for(i = 0; i < 7; i++) | |
327 if(smk->bufs[i]) | |
328 av_free(smk->bufs[i]); | |
329 if(smk->frm_size) | |
330 av_free(smk->frm_size); | |
331 if(smk->frm_flags) | |
332 av_free(smk->frm_flags); | |
333 | |
334 return 0; | |
335 } | |
336 | |
1169 | 337 AVInputFormat smacker_demuxer = { |
1019 | 338 "smk", |
339 "Smacker Video", | |
340 sizeof(SmackerContext), | |
341 smacker_probe, | |
342 smacker_read_header, | |
343 smacker_read_packet, | |
344 smacker_read_close, | |
345 }; |