/src/ffmpeg/libavformat/smacker.c
Line | Count | Source |
1 | | /* |
2 | | * Smacker demuxer |
3 | | * Copyright (c) 2006 Konstantin Shishkov |
4 | | * |
5 | | * This file is part of FFmpeg. |
6 | | * |
7 | | * FFmpeg is free software; you can redistribute it and/or |
8 | | * modify it under the terms of the GNU Lesser General Public |
9 | | * License as published by the Free Software Foundation; either |
10 | | * version 2.1 of the License, or (at your option) any later version. |
11 | | * |
12 | | * FFmpeg is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | | * Lesser General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU Lesser General Public |
18 | | * License along with FFmpeg; if not, write to the Free Software |
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 <inttypes.h> |
27 | | |
28 | | #include "libavutil/channel_layout.h" |
29 | | #include "libavutil/intreadwrite.h" |
30 | | #include "libavutil/mem.h" |
31 | | #include "avformat.h" |
32 | | #include "avio_internal.h" |
33 | | #include "demux.h" |
34 | | #include "internal.h" |
35 | | |
36 | 218k | #define SMACKER_PAL 0x01 |
37 | 2.69k | #define SMACKER_FLAG_RING_FRAME 0x01 |
38 | 2.64k | #define SMACKER_FLAG_Y_INTERLACE (1 << 1) |
39 | 2.64k | #define SMACKER_FLAG_Y_DOUBLE (1 << 2) |
40 | | |
41 | | enum SAudFlags { |
42 | | SMK_AUD_PACKED = 0x80, |
43 | | SMK_AUD_16BITS = 0x20, |
44 | | SMK_AUD_STEREO = 0x10, |
45 | | SMK_AUD_BINKAUD = 0x08, |
46 | | SMK_AUD_USEDCT = 0x04 |
47 | | }; |
48 | | |
49 | | typedef struct SmackerContext { |
50 | | uint32_t frames; |
51 | | /* frame info */ |
52 | | uint32_t *frm_size; |
53 | | uint8_t *frm_flags; |
54 | | /* internal variables */ |
55 | | int64_t next_frame_pos; |
56 | | int cur_frame; |
57 | | int videoindex; |
58 | | int indexes[7]; |
59 | | int duration_size[7]; |
60 | | /* current frame for demuxing */ |
61 | | uint32_t frame_size; |
62 | | int flags; |
63 | | int next_audio_index; |
64 | | int new_palette; |
65 | | uint8_t pal[768]; |
66 | | int64_t aud_pts[7]; |
67 | | } SmackerContext; |
68 | | |
69 | | /* palette used in Smacker */ |
70 | | static const uint8_t smk_pal[64] = { |
71 | | 0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C, |
72 | | 0x20, 0x24, 0x28, 0x2C, 0x30, 0x34, 0x38, 0x3C, |
73 | | 0x41, 0x45, 0x49, 0x4D, 0x51, 0x55, 0x59, 0x5D, |
74 | | 0x61, 0x65, 0x69, 0x6D, 0x71, 0x75, 0x79, 0x7D, |
75 | | 0x82, 0x86, 0x8A, 0x8E, 0x92, 0x96, 0x9A, 0x9E, |
76 | | 0xA2, 0xA6, 0xAA, 0xAE, 0xB2, 0xB6, 0xBA, 0xBE, |
77 | | 0xC3, 0xC7, 0xCB, 0xCF, 0xD3, 0xD7, 0xDB, 0xDF, |
78 | | 0xE3, 0xE7, 0xEB, 0xEF, 0xF3, 0xF7, 0xFB, 0xFF |
79 | | }; |
80 | | |
81 | | |
82 | | static int smacker_probe(const AVProbeData *p) |
83 | 958k | { |
84 | 958k | if ( AV_RL32(p->buf) != MKTAG('S', 'M', 'K', '2') |
85 | 957k | && AV_RL32(p->buf) != MKTAG('S', 'M', 'K', '4')) |
86 | 957k | return 0; |
87 | | |
88 | 1.05k | if (AV_RL32(p->buf+4) > 32768U || AV_RL32(p->buf+8) > 32768U) |
89 | 841 | return AVPROBE_SCORE_MAX/4; |
90 | | |
91 | 209 | return AVPROBE_SCORE_MAX; |
92 | 1.05k | } |
93 | | |
94 | | static int smacker_read_header(AVFormatContext *s) |
95 | 2.89k | { |
96 | 2.89k | AVIOContext *pb = s->pb; |
97 | 2.89k | SmackerContext *smk = s->priv_data; |
98 | 2.89k | AVStream *st; |
99 | 2.89k | AVCodecParameters *par; |
100 | 2.89k | uint32_t magic, width, height, flags, treesize; |
101 | 2.89k | int64_t pos; |
102 | 2.89k | int i, ret, pts_inc; |
103 | 2.89k | int tbase; |
104 | | |
105 | | /* read and check header */ |
106 | 2.89k | magic = avio_rl32(pb); |
107 | 2.89k | if (magic != MKTAG('S', 'M', 'K', '2') && magic != MKTAG('S', 'M', 'K', '4')) |
108 | 196 | return AVERROR_INVALIDDATA; |
109 | 2.70k | width = avio_rl32(pb); |
110 | 2.70k | height = avio_rl32(pb); |
111 | 2.70k | smk->frames = avio_rl32(pb); |
112 | 2.70k | pts_inc = avio_rl32(pb); |
113 | 2.70k | if (pts_inc > INT_MAX / 100 || pts_inc == INT_MIN) { |
114 | 8 | av_log(s, AV_LOG_ERROR, "pts_inc %d is invalid\n", pts_inc); |
115 | 8 | return AVERROR_INVALIDDATA; |
116 | 8 | } |
117 | | |
118 | 2.69k | flags = avio_rl32(pb); |
119 | 2.69k | if (flags & SMACKER_FLAG_RING_FRAME) |
120 | 1.26k | smk->frames++; |
121 | 2.69k | if (smk->frames > 0xFFFFFF) { |
122 | 30 | av_log(s, AV_LOG_ERROR, "Too many frames: %"PRIu32"\n", smk->frames); |
123 | 30 | return AVERROR_INVALIDDATA; |
124 | 30 | } |
125 | | |
126 | 2.66k | avio_skip(pb, 28); /* Unused audio related data */ |
127 | | |
128 | 2.66k | treesize = avio_rl32(pb); |
129 | 2.66k | if (treesize >= UINT_MAX/4) { |
130 | | // treesize + 16 must not overflow (this check is probably redundant) |
131 | 14 | av_log(s, AV_LOG_ERROR, "treesize too large\n"); |
132 | 14 | return AVERROR_INVALIDDATA; |
133 | 14 | } |
134 | | |
135 | 2.64k | st = avformat_new_stream(s, NULL); |
136 | 2.64k | if (!st) |
137 | 0 | return AVERROR(ENOMEM); |
138 | | |
139 | 2.64k | smk->videoindex = st->index; |
140 | | /* Smacker uses 100000 as internal timebase */ |
141 | 2.64k | if (pts_inc < 0) |
142 | 1.08k | pts_inc = -pts_inc; |
143 | 1.56k | else |
144 | 1.56k | pts_inc *= 100; |
145 | 2.64k | tbase = 100000; |
146 | 2.64k | av_reduce(&tbase, &pts_inc, tbase, pts_inc, (1UL << 31) - 1); |
147 | 2.64k | avpriv_set_pts_info(st, 33, pts_inc, tbase); |
148 | 2.64k | st->duration = smk->frames; |
149 | | |
150 | 2.64k | st->sample_aspect_ratio = (AVRational){ 1, 1 + |
151 | 2.64k | !!(flags & (SMACKER_FLAG_Y_INTERLACE | SMACKER_FLAG_Y_DOUBLE)) }; |
152 | | |
153 | | /* init video codec */ |
154 | 2.64k | par = st->codecpar; |
155 | 2.64k | par->width = width; |
156 | 2.64k | par->height = height; |
157 | 2.64k | par->format = AV_PIX_FMT_PAL8; |
158 | 2.64k | par->codec_type = AVMEDIA_TYPE_VIDEO; |
159 | 2.64k | par->codec_id = AV_CODEC_ID_SMACKVIDEO; |
160 | 2.64k | par->codec_tag = magic; |
161 | | |
162 | 2.64k | if ((ret = ff_alloc_extradata(par, treesize + 16)) < 0) { |
163 | 0 | av_log(s, AV_LOG_ERROR, |
164 | 0 | "Cannot allocate %"PRIu32" bytes of extradata\n", |
165 | 0 | treesize + 16); |
166 | 0 | return ret; |
167 | 0 | } |
168 | 2.64k | if ((ret = ffio_read_size(pb, par->extradata, 16)) < 0) |
169 | 278 | return ret; |
170 | | |
171 | | /* handle possible audio streams */ |
172 | 18.9k | for (i = 0; i < 7; i++) { |
173 | 16.5k | uint32_t rate = avio_rl24(pb); |
174 | 16.5k | uint8_t aflag = avio_r8(pb); |
175 | | |
176 | 16.5k | smk->indexes[i] = -1; |
177 | | |
178 | 16.5k | if (rate) { |
179 | 11.9k | AVStream *ast = avformat_new_stream(s, NULL); |
180 | 11.9k | AVCodecParameters *par; |
181 | 11.9k | if (!ast) |
182 | 0 | return AVERROR(ENOMEM); |
183 | | |
184 | 11.9k | smk->indexes[i] = ast->index; |
185 | 11.9k | par = ast->codecpar; |
186 | 11.9k | par->codec_type = AVMEDIA_TYPE_AUDIO; |
187 | 11.9k | if (aflag & SMK_AUD_BINKAUD) { |
188 | 4.48k | par->codec_id = AV_CODEC_ID_BINKAUDIO_RDFT; |
189 | 7.43k | } else if (aflag & SMK_AUD_USEDCT) { |
190 | 1.54k | par->codec_id = AV_CODEC_ID_BINKAUDIO_DCT; |
191 | 5.89k | } else if (aflag & SMK_AUD_PACKED) { |
192 | 564 | par->codec_id = AV_CODEC_ID_SMACKAUDIO; |
193 | 564 | par->codec_tag = MKTAG('S', 'M', 'K', 'A'); |
194 | 5.33k | } else { |
195 | 5.33k | par->codec_id = AV_CODEC_ID_PCM_U8; |
196 | 5.33k | } |
197 | 11.9k | av_channel_layout_default(&par->ch_layout, |
198 | 11.9k | !!(aflag & SMK_AUD_STEREO) + 1); |
199 | 11.9k | par->sample_rate = rate; |
200 | 11.9k | par->bits_per_coded_sample = (aflag & SMK_AUD_16BITS) ? 16 : 8; |
201 | 11.9k | if (par->bits_per_coded_sample == 16 && |
202 | 4.42k | par->codec_id == AV_CODEC_ID_PCM_U8) |
203 | 751 | par->codec_id = AV_CODEC_ID_PCM_S16LE; |
204 | 11.1k | else |
205 | 11.1k | smk->duration_size[i] = 4; |
206 | 11.9k | avpriv_set_pts_info(ast, 64, 1, par->sample_rate * par->ch_layout.nb_channels |
207 | 11.9k | * par->bits_per_coded_sample / 8); |
208 | 11.9k | } |
209 | 16.5k | } |
210 | | |
211 | 2.37k | avio_rl32(pb); /* padding */ |
212 | | |
213 | | /* setup data */ |
214 | 2.37k | st->priv_data = av_malloc_array(smk->frames, sizeof(*smk->frm_size) + |
215 | 2.37k | sizeof(*smk->frm_flags)); |
216 | 2.37k | if (!st->priv_data) |
217 | 0 | return AVERROR(ENOMEM); |
218 | 2.37k | smk->frm_size = st->priv_data; |
219 | 2.37k | smk->frm_flags = (void*)(smk->frm_size + smk->frames); |
220 | | |
221 | | /* read frame info */ |
222 | 2.37k | pos = 0; |
223 | 187M | for (i = 0; i < smk->frames; i++) { |
224 | 187M | smk->frm_size[i] = avio_rl32(pb); |
225 | 187M | if ((ret = av_add_index_entry(st, pos, i, smk->frm_size[i], 0, |
226 | 187M | (i == 0 || (smk->frm_size[i] & 1)) ? AVINDEX_KEYFRAME : 0)) < 0) |
227 | 46 | return ret; |
228 | 187M | pos += smk->frm_size[i]; |
229 | 187M | } |
230 | 2.32k | if ((ret = ffio_read_size(pb, smk->frm_flags, smk->frames)) < 0 || |
231 | | /* load trees to extradata, they will be unpacked by decoder */ |
232 | 2.13k | (ret = ffio_read_size(pb, par->extradata + 16, |
233 | 2.13k | par->extradata_size - 16)) < 0) { |
234 | 223 | return ret; |
235 | 223 | } |
236 | | |
237 | 2.10k | return 0; |
238 | 2.32k | } |
239 | | |
240 | | static int smacker_read_packet(AVFormatContext *s, AVPacket *pkt) |
241 | 222k | { |
242 | 222k | SmackerContext *smk = s->priv_data; |
243 | 222k | int flags; |
244 | 222k | int ret; |
245 | | |
246 | 222k | if (avio_feof(s->pb) || smk->cur_frame >= smk->frames) |
247 | 2.07k | return AVERROR_EOF; |
248 | | |
249 | | /* if we demuxed all streams, pass another frame */ |
250 | 220k | if (!smk->next_audio_index) { |
251 | 218k | smk->frame_size = smk->frm_size[smk->cur_frame] & (~3); |
252 | 218k | smk->next_frame_pos = avio_tell(s->pb) + smk->frame_size; |
253 | 218k | flags = smk->frm_flags[smk->cur_frame]; |
254 | 218k | smk->flags = flags >> 1; |
255 | | /* handle palette change event */ |
256 | 218k | if (flags & SMACKER_PAL) { |
257 | 1.64k | int size, sz, t, off, j, pos; |
258 | 1.64k | uint8_t *pal = smk->pal; |
259 | 1.64k | uint8_t oldpal[768]; |
260 | | |
261 | 1.64k | memcpy(oldpal, pal, 768); |
262 | 1.64k | size = avio_r8(s->pb); |
263 | 1.64k | size = size * 4; |
264 | 1.64k | if (size > smk->frame_size) { |
265 | 38 | ret = AVERROR_INVALIDDATA; |
266 | 38 | goto next_frame; |
267 | 38 | } |
268 | 1.60k | smk->frame_size -= size--; |
269 | 1.60k | sz = 0; |
270 | 1.60k | pos = avio_tell(s->pb) + size; |
271 | 200k | while (sz < 256) { |
272 | 199k | t = avio_r8(s->pb); |
273 | 199k | if (t & 0x80) { /* skip palette entries */ |
274 | 2.44k | sz += (t & 0x7F) + 1; |
275 | 2.44k | pal += ((t & 0x7F) + 1) * 3; |
276 | 196k | } else if (t & 0x40) { /* copy with offset */ |
277 | 2.51k | off = avio_r8(s->pb); |
278 | 2.51k | j = (t & 0x3F) + 1; |
279 | 2.51k | if (off + j > 0x100) { |
280 | 46 | av_log(s, AV_LOG_ERROR, |
281 | 46 | "Invalid palette update, offset=%d length=%d extends beyond palette size\n", |
282 | 46 | off, j); |
283 | 46 | ret = AVERROR_INVALIDDATA; |
284 | 46 | goto next_frame; |
285 | 46 | } |
286 | 2.47k | off *= 3; |
287 | 51.3k | while (j-- && sz < 256) { |
288 | 48.8k | *pal++ = oldpal[off + 0]; |
289 | 48.8k | *pal++ = oldpal[off + 1]; |
290 | 48.8k | *pal++ = oldpal[off + 2]; |
291 | 48.8k | sz++; |
292 | 48.8k | off += 3; |
293 | 48.8k | } |
294 | 194k | } else { /* new entries */ |
295 | 194k | *pal++ = smk_pal[t]; |
296 | 194k | *pal++ = smk_pal[avio_r8(s->pb) & 0x3F]; |
297 | 194k | *pal++ = smk_pal[avio_r8(s->pb) & 0x3F]; |
298 | 194k | sz++; |
299 | 194k | } |
300 | 199k | } |
301 | 1.56k | avio_seek(s->pb, pos, 0); |
302 | 1.56k | smk->new_palette = 1; |
303 | 1.56k | } |
304 | 218k | } |
305 | | |
306 | 1.74M | for (int i = smk->next_audio_index; i < 7; i++) { |
307 | 1.52M | if (smk->flags & (1 << i)) { |
308 | 3.60k | uint32_t size; |
309 | | |
310 | 3.60k | size = avio_rl32(s->pb); |
311 | 3.60k | if ((int)size < 4 + smk->duration_size[i] || size > smk->frame_size) { |
312 | 767 | av_log(s, AV_LOG_ERROR, "Invalid audio part size\n"); |
313 | 767 | ret = AVERROR_INVALIDDATA; |
314 | 767 | goto next_frame; |
315 | 767 | } |
316 | 2.83k | smk->frame_size -= size; |
317 | 2.83k | size -= 4; |
318 | | |
319 | 2.83k | if (smk->indexes[i] < 0 || |
320 | 2.59k | s->streams[smk->indexes[i]]->discard >= AVDISCARD_ALL) { |
321 | 243 | smk->aud_pts[i] += smk->duration_size[i] ? avio_rl32(s->pb) |
322 | 243 | : size; |
323 | 243 | avio_skip(s->pb, size - smk->duration_size[i]); |
324 | 243 | continue; |
325 | 243 | } |
326 | 2.59k | if ((ret = av_get_packet(s->pb, pkt, size)) != size) { |
327 | 532 | ret = ret < 0 ? ret : AVERROR_INVALIDDATA; |
328 | 532 | goto next_frame; |
329 | 532 | } |
330 | 2.06k | pkt->stream_index = smk->indexes[i]; |
331 | 2.06k | pkt->pts = smk->aud_pts[i]; |
332 | 2.06k | pkt->duration = smk->duration_size[i] ? AV_RL32(pkt->data) |
333 | 2.06k | : size; |
334 | 2.06k | smk->aud_pts[i] += pkt->duration; |
335 | 2.06k | smk->next_audio_index = i + 1; |
336 | 2.06k | return 0; |
337 | 2.59k | } |
338 | 1.52M | } |
339 | | |
340 | 217k | if (s->streams[smk->videoindex]->discard >= AVDISCARD_ALL) { |
341 | 0 | ret = FFERROR_REDO; |
342 | 0 | goto next_frame; |
343 | 0 | } |
344 | 217k | if (smk->frame_size >= INT_MAX/2) { |
345 | 0 | ret = AVERROR_INVALIDDATA; |
346 | 0 | goto next_frame; |
347 | 0 | } |
348 | 217k | if ((ret = av_new_packet(pkt, smk->frame_size + 769)) < 0) |
349 | 0 | goto next_frame; |
350 | 217k | flags = smk->new_palette; |
351 | 217k | if ((smk->frm_size[smk->cur_frame] & 1) || smk->cur_frame == 0) |
352 | 1.43k | flags |= 2; |
353 | 217k | pkt->data[0] = flags; |
354 | 217k | memcpy(pkt->data + 1, smk->pal, 768); |
355 | 217k | ret = ffio_read_size(s->pb, pkt->data + 769, smk->frame_size); |
356 | 217k | if (ret < 0) |
357 | 405 | goto next_frame; |
358 | 216k | pkt->stream_index = smk->videoindex; |
359 | 216k | pkt->pts = smk->cur_frame; |
360 | 216k | pkt->duration = 1; |
361 | 216k | if (flags & 2) |
362 | 1.22k | pkt->flags |= AV_PKT_FLAG_KEY; |
363 | 216k | smk->next_audio_index = 0; |
364 | 216k | smk->new_palette = 0; |
365 | 216k | smk->cur_frame++; |
366 | | |
367 | 216k | return 0; |
368 | 1.78k | next_frame: |
369 | 1.78k | avio_seek(s->pb, smk->next_frame_pos, SEEK_SET); |
370 | 1.78k | smk->next_audio_index = 0; |
371 | 1.78k | smk->cur_frame++; |
372 | 1.78k | return ret; |
373 | 217k | } |
374 | | |
375 | | static int smacker_read_seek(AVFormatContext *s, int stream_index, |
376 | | int64_t timestamp, int flags) |
377 | 0 | { |
378 | 0 | AVStream *st = s->streams[stream_index]; |
379 | 0 | SmackerContext *smk = s->priv_data; |
380 | 0 | int64_t pos; |
381 | 0 | int ret; |
382 | |
|
383 | 0 | if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL)) |
384 | 0 | return -1; |
385 | | |
386 | 0 | if (timestamp < 0 || timestamp >= smk->frames) |
387 | 0 | return AVERROR(EINVAL); |
388 | | |
389 | 0 | ret = av_index_search_timestamp(st, timestamp, flags); |
390 | 0 | if (ret < 0) |
391 | 0 | return ret; |
392 | | |
393 | 0 | pos = ffformatcontext(s)->data_offset; |
394 | 0 | pos += ffstream(st)->index_entries[ret].pos; |
395 | 0 | pos = avio_seek(s->pb, pos, SEEK_SET); |
396 | 0 | if (pos < 0) |
397 | 0 | return pos; |
398 | | |
399 | 0 | smk->cur_frame = ret; |
400 | 0 | smk->next_audio_index = 0; |
401 | 0 | smk->new_palette = 0; |
402 | 0 | memset(smk->pal, 0, sizeof(smk->pal)); |
403 | 0 | memset(smk->aud_pts, 0, sizeof(smk->aud_pts)); |
404 | |
|
405 | 0 | return 0; |
406 | 0 | } |
407 | | |
408 | | const FFInputFormat ff_smacker_demuxer = { |
409 | | .p.name = "smk", |
410 | | .p.long_name = NULL_IF_CONFIG_SMALL("Smacker"), |
411 | | .priv_data_size = sizeof(SmackerContext), |
412 | | .read_probe = smacker_probe, |
413 | | .read_header = smacker_read_header, |
414 | | .read_packet = smacker_read_packet, |
415 | | .read_seek = smacker_read_seek, |
416 | | }; |