| 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/bswap.h" |
| 29 | #include "libavutil/channel_layout.h" |
| 30 | #include "libavutil/intreadwrite.h" |
| 31 | #include "avformat.h" |
| 32 | #include "internal.h" |
| 33 | |
| 34 | #define SMACKER_PAL 0x01 |
| 35 | #define SMACKER_FLAG_RING_FRAME 0x01 |
| 36 | |
| 37 | enum SAudFlags { |
| 38 | SMK_AUD_PACKED = 0x80, |
| 39 | SMK_AUD_16BITS = 0x20, |
| 40 | SMK_AUD_STEREO = 0x10, |
| 41 | SMK_AUD_BINKAUD = 0x08, |
| 42 | SMK_AUD_USEDCT = 0x04 |
| 43 | }; |
| 44 | |
| 45 | typedef struct SmackerContext { |
| 46 | /* Smacker file header */ |
| 47 | uint32_t magic; |
| 48 | uint32_t width, height; |
| 49 | uint32_t frames; |
| 50 | int pts_inc; |
| 51 | uint32_t flags; |
| 52 | uint32_t audio[7]; |
| 53 | uint32_t treesize; |
| 54 | uint32_t mmap_size, mclr_size, full_size, type_size; |
| 55 | uint8_t aflags[7]; |
| 56 | uint32_t rates[7]; |
| 57 | uint32_t pad; |
| 58 | /* frame info */ |
| 59 | uint32_t *frm_size; |
| 60 | uint8_t *frm_flags; |
| 61 | /* internal variables */ |
| 62 | int cur_frame; |
| 63 | int is_ver4; |
| 64 | int64_t cur_pts; |
| 65 | /* current frame for demuxing */ |
| 66 | uint8_t pal[768]; |
| 67 | int indexes[7]; |
| 68 | int videoindex; |
| 69 | uint8_t *bufs[7]; |
| 70 | int buf_sizes[7]; |
| 71 | int stream_id[7]; |
| 72 | int curstream; |
| 73 | int64_t nextpos; |
| 74 | int64_t aud_pts[7]; |
| 75 | } SmackerContext; |
| 76 | |
| 77 | typedef struct SmackerFrame { |
| 78 | int64_t pts; |
| 79 | int stream; |
| 80 | } SmackerFrame; |
| 81 | |
| 82 | /* palette used in Smacker */ |
| 83 | static const uint8_t smk_pal[64] = { |
| 84 | 0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C, |
| 85 | 0x20, 0x24, 0x28, 0x2C, 0x30, 0x34, 0x38, 0x3C, |
| 86 | 0x41, 0x45, 0x49, 0x4D, 0x51, 0x55, 0x59, 0x5D, |
| 87 | 0x61, 0x65, 0x69, 0x6D, 0x71, 0x75, 0x79, 0x7D, |
| 88 | 0x82, 0x86, 0x8A, 0x8E, 0x92, 0x96, 0x9A, 0x9E, |
| 89 | 0xA2, 0xA6, 0xAA, 0xAE, 0xB2, 0xB6, 0xBA, 0xBE, |
| 90 | 0xC3, 0xC7, 0xCB, 0xCF, 0xD3, 0xD7, 0xDB, 0xDF, |
| 91 | 0xE3, 0xE7, 0xEB, 0xEF, 0xF3, 0xF7, 0xFB, 0xFF |
| 92 | }; |
| 93 | |
| 94 | |
| 95 | static int smacker_probe(AVProbeData *p) |
| 96 | { |
| 97 | if ( AV_RL32(p->buf) != MKTAG('S', 'M', 'K', '2') |
| 98 | && AV_RL32(p->buf) != MKTAG('S', 'M', 'K', '4')) |
| 99 | return 0; |
| 100 | |
| 101 | if (AV_RL32(p->buf+4) > 32768U || AV_RL32(p->buf+8) > 32768U) |
| 102 | return AVPROBE_SCORE_MAX/4; |
| 103 | |
| 104 | return AVPROBE_SCORE_MAX; |
| 105 | } |
| 106 | |
| 107 | static int smacker_read_header(AVFormatContext *s) |
| 108 | { |
| 109 | AVIOContext *pb = s->pb; |
| 110 | SmackerContext *smk = s->priv_data; |
| 111 | AVStream *st, *ast[7]; |
| 112 | int i, ret; |
| 113 | int tbase; |
| 114 | |
| 115 | /* read and check header */ |
| 116 | smk->magic = avio_rl32(pb); |
| 117 | if (smk->magic != MKTAG('S', 'M', 'K', '2') && smk->magic != MKTAG('S', 'M', 'K', '4')) |
| 118 | return AVERROR_INVALIDDATA; |
| 119 | smk->width = avio_rl32(pb); |
| 120 | smk->height = avio_rl32(pb); |
| 121 | smk->frames = avio_rl32(pb); |
| 122 | smk->pts_inc = (int32_t)avio_rl32(pb); |
| 123 | smk->flags = avio_rl32(pb); |
| 124 | if(smk->flags & SMACKER_FLAG_RING_FRAME) |
| 125 | smk->frames++; |
| 126 | for(i = 0; i < 7; i++) |
| 127 | smk->audio[i] = avio_rl32(pb); |
| 128 | smk->treesize = avio_rl32(pb); |
| 129 | |
| 130 | if(smk->treesize >= UINT_MAX/4){ // smk->treesize + 16 must not overflow (this check is probably redundant) |
| 131 | av_log(s, AV_LOG_ERROR, "treesize too large\n"); |
| 132 | return AVERROR_INVALIDDATA; |
| 133 | } |
| 134 | |
| 135 | //FIXME remove extradata "rebuilding" |
| 136 | smk->mmap_size = avio_rl32(pb); |
| 137 | smk->mclr_size = avio_rl32(pb); |
| 138 | smk->full_size = avio_rl32(pb); |
| 139 | smk->type_size = avio_rl32(pb); |
| 140 | for(i = 0; i < 7; i++) { |
| 141 | smk->rates[i] = avio_rl24(pb); |
| 142 | smk->aflags[i] = avio_r8(pb); |
| 143 | } |
| 144 | smk->pad = avio_rl32(pb); |
| 145 | /* setup data */ |
| 146 | if(smk->frames > 0xFFFFFF) { |
| 147 | av_log(s, AV_LOG_ERROR, "Too many frames: %"PRIu32"\n", smk->frames); |
| 148 | return AVERROR_INVALIDDATA; |
| 149 | } |
| 150 | smk->frm_size = av_malloc_array(smk->frames, sizeof(*smk->frm_size)); |
| 151 | smk->frm_flags = av_malloc(smk->frames); |
| 152 | if (!smk->frm_size || !smk->frm_flags) { |
| 153 | av_freep(&smk->frm_size); |
| 154 | av_freep(&smk->frm_flags); |
| 155 | return AVERROR(ENOMEM); |
| 156 | } |
| 157 | |
| 158 | smk->is_ver4 = (smk->magic != MKTAG('S', 'M', 'K', '2')); |
| 159 | |
| 160 | /* read frame info */ |
| 161 | for(i = 0; i < smk->frames; i++) { |
| 162 | smk->frm_size[i] = avio_rl32(pb); |
| 163 | } |
| 164 | for(i = 0; i < smk->frames; i++) { |
| 165 | smk->frm_flags[i] = avio_r8(pb); |
| 166 | } |
| 167 | |
| 168 | /* init video codec */ |
| 169 | st = avformat_new_stream(s, NULL); |
| 170 | if (!st) |
| 171 | return AVERROR(ENOMEM); |
| 172 | smk->videoindex = st->index; |
| 173 | st->codec->width = smk->width; |
| 174 | st->codec->height = smk->height; |
| 175 | st->codec->pix_fmt = AV_PIX_FMT_PAL8; |
| 176 | st->codec->codec_type = AVMEDIA_TYPE_VIDEO; |
| 177 | st->codec->codec_id = AV_CODEC_ID_SMACKVIDEO; |
| 178 | st->codec->codec_tag = smk->magic; |
| 179 | /* Smacker uses 100000 as internal timebase */ |
| 180 | if(smk->pts_inc < 0) |
| 181 | smk->pts_inc = -smk->pts_inc; |
| 182 | else |
| 183 | smk->pts_inc *= 100; |
| 184 | tbase = 100000; |
| 185 | av_reduce(&tbase, &smk->pts_inc, tbase, smk->pts_inc, (1UL<<31)-1); |
| 186 | avpriv_set_pts_info(st, 33, smk->pts_inc, tbase); |
| 187 | st->duration = smk->frames; |
| 188 | /* handle possible audio streams */ |
| 189 | for(i = 0; i < 7; i++) { |
| 190 | smk->indexes[i] = -1; |
| 191 | if (smk->rates[i]) { |
| 192 | ast[i] = avformat_new_stream(s, NULL); |
| 193 | if (!ast[i]) |
| 194 | return AVERROR(ENOMEM); |
| 195 | smk->indexes[i] = ast[i]->index; |
| 196 | ast[i]->codec->codec_type = AVMEDIA_TYPE_AUDIO; |
| 197 | if (smk->aflags[i] & SMK_AUD_BINKAUD) { |
| 198 | ast[i]->codec->codec_id = AV_CODEC_ID_BINKAUDIO_RDFT; |
| 199 | } else if (smk->aflags[i] & SMK_AUD_USEDCT) { |
| 200 | ast[i]->codec->codec_id = AV_CODEC_ID_BINKAUDIO_DCT; |
| 201 | } else if (smk->aflags[i] & SMK_AUD_PACKED){ |
| 202 | ast[i]->codec->codec_id = AV_CODEC_ID_SMACKAUDIO; |
| 203 | ast[i]->codec->codec_tag = MKTAG('S', 'M', 'K', 'A'); |
| 204 | } else { |
| 205 | ast[i]->codec->codec_id = AV_CODEC_ID_PCM_U8; |
| 206 | } |
| 207 | if (smk->aflags[i] & SMK_AUD_STEREO) { |
| 208 | ast[i]->codec->channels = 2; |
| 209 | ast[i]->codec->channel_layout = AV_CH_LAYOUT_STEREO; |
| 210 | } else { |
| 211 | ast[i]->codec->channels = 1; |
| 212 | ast[i]->codec->channel_layout = AV_CH_LAYOUT_MONO; |
| 213 | } |
| 214 | ast[i]->codec->sample_rate = smk->rates[i]; |
| 215 | ast[i]->codec->bits_per_coded_sample = (smk->aflags[i] & SMK_AUD_16BITS) ? 16 : 8; |
| 216 | if(ast[i]->codec->bits_per_coded_sample == 16 && ast[i]->codec->codec_id == AV_CODEC_ID_PCM_U8) |
| 217 | ast[i]->codec->codec_id = AV_CODEC_ID_PCM_S16LE; |
| 218 | avpriv_set_pts_info(ast[i], 64, 1, ast[i]->codec->sample_rate |
| 219 | * ast[i]->codec->channels * ast[i]->codec->bits_per_coded_sample / 8); |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | |
| 224 | /* load trees to extradata, they will be unpacked by decoder */ |
| 225 | if(ff_alloc_extradata(st->codec, smk->treesize + 16)){ |
| 226 | av_log(s, AV_LOG_ERROR, |
| 227 | "Cannot allocate %"PRIu32" bytes of extradata\n", |
| 228 | smk->treesize + 16); |
| 229 | av_freep(&smk->frm_size); |
| 230 | av_freep(&smk->frm_flags); |
| 231 | return AVERROR(ENOMEM); |
| 232 | } |
| 233 | ret = avio_read(pb, st->codec->extradata + 16, st->codec->extradata_size - 16); |
| 234 | if(ret != st->codec->extradata_size - 16){ |
| 235 | av_freep(&smk->frm_size); |
| 236 | av_freep(&smk->frm_flags); |
| 237 | return AVERROR(EIO); |
| 238 | } |
| 239 | ((int32_t*)st->codec->extradata)[0] = av_le2ne32(smk->mmap_size); |
| 240 | ((int32_t*)st->codec->extradata)[1] = av_le2ne32(smk->mclr_size); |
| 241 | ((int32_t*)st->codec->extradata)[2] = av_le2ne32(smk->full_size); |
| 242 | ((int32_t*)st->codec->extradata)[3] = av_le2ne32(smk->type_size); |
| 243 | |
| 244 | smk->curstream = -1; |
| 245 | smk->nextpos = avio_tell(pb); |
| 246 | |
| 247 | return 0; |
| 248 | } |
| 249 | |
| 250 | |
| 251 | static int smacker_read_packet(AVFormatContext *s, AVPacket *pkt) |
| 252 | { |
| 253 | SmackerContext *smk = s->priv_data; |
| 254 | int flags; |
| 255 | int ret; |
| 256 | int i; |
| 257 | int frame_size = 0; |
| 258 | int palchange = 0; |
| 259 | |
| 260 | if (avio_feof(s->pb) || smk->cur_frame >= smk->frames) |
| 261 | return AVERROR_EOF; |
| 262 | |
| 263 | /* if we demuxed all streams, pass another frame */ |
| 264 | if(smk->curstream < 0) { |
| 265 | avio_seek(s->pb, smk->nextpos, 0); |
| 266 | frame_size = smk->frm_size[smk->cur_frame] & (~3); |
| 267 | flags = smk->frm_flags[smk->cur_frame]; |
| 268 | /* handle palette change event */ |
| 269 | if(flags & SMACKER_PAL){ |
| 270 | int size, sz, t, off, j, pos; |
| 271 | uint8_t *pal = smk->pal; |
| 272 | uint8_t oldpal[768]; |
| 273 | |
| 274 | memcpy(oldpal, pal, 768); |
| 275 | size = avio_r8(s->pb); |
| 276 | size = size * 4 - 1; |
| 277 | if(size + 1 > frame_size) |
| 278 | return AVERROR_INVALIDDATA; |
| 279 | frame_size -= size; |
| 280 | frame_size--; |
| 281 | sz = 0; |
| 282 | pos = avio_tell(s->pb) + size; |
| 283 | while(sz < 256){ |
| 284 | t = avio_r8(s->pb); |
| 285 | if(t & 0x80){ /* skip palette entries */ |
| 286 | sz += (t & 0x7F) + 1; |
| 287 | pal += ((t & 0x7F) + 1) * 3; |
| 288 | } else if(t & 0x40){ /* copy with offset */ |
| 289 | off = avio_r8(s->pb); |
| 290 | j = (t & 0x3F) + 1; |
| 291 | if (off + j > 0x100) { |
| 292 | av_log(s, AV_LOG_ERROR, |
| 293 | "Invalid palette update, offset=%d length=%d extends beyond palette size\n", |
| 294 | off, j); |
| 295 | return AVERROR_INVALIDDATA; |
| 296 | } |
| 297 | off *= 3; |
| 298 | while(j-- && sz < 256) { |
| 299 | *pal++ = oldpal[off + 0]; |
| 300 | *pal++ = oldpal[off + 1]; |
| 301 | *pal++ = oldpal[off + 2]; |
| 302 | sz++; |
| 303 | off += 3; |
| 304 | } |
| 305 | } else { /* new entries */ |
| 306 | *pal++ = smk_pal[t]; |
| 307 | *pal++ = smk_pal[avio_r8(s->pb) & 0x3F]; |
| 308 | *pal++ = smk_pal[avio_r8(s->pb) & 0x3F]; |
| 309 | sz++; |
| 310 | } |
| 311 | } |
| 312 | avio_seek(s->pb, pos, 0); |
| 313 | palchange |= 1; |
| 314 | } |
| 315 | flags >>= 1; |
| 316 | smk->curstream = -1; |
| 317 | /* if audio chunks are present, put them to stack and retrieve later */ |
| 318 | for(i = 0; i < 7; i++) { |
| 319 | if(flags & 1) { |
| 320 | uint32_t size; |
| 321 | int err; |
| 322 | |
| 323 | size = avio_rl32(s->pb) - 4; |
| 324 | if (!size || size + 4L > frame_size) { |
| 325 | av_log(s, AV_LOG_ERROR, "Invalid audio part size\n"); |
| 326 | return AVERROR_INVALIDDATA; |
| 327 | } |
| 328 | frame_size -= size; |
| 329 | frame_size -= 4; |
| 330 | smk->curstream++; |
| 331 | if ((err = av_reallocp(&smk->bufs[smk->curstream], size)) < 0) { |
| 332 | smk->buf_sizes[smk->curstream] = 0; |
| 333 | return err; |
| 334 | } |
| 335 | smk->buf_sizes[smk->curstream] = size; |
| 336 | ret = avio_read(s->pb, smk->bufs[smk->curstream], size); |
| 337 | if(ret != size) |
| 338 | return AVERROR(EIO); |
| 339 | smk->stream_id[smk->curstream] = smk->indexes[i]; |
| 340 | } |
| 341 | flags >>= 1; |
| 342 | } |
| 343 | if (frame_size < 0 || frame_size >= INT_MAX/2) |
| 344 | return AVERROR_INVALIDDATA; |
| 345 | if (av_new_packet(pkt, frame_size + 769)) |
| 346 | return AVERROR(ENOMEM); |
| 347 | if(smk->frm_size[smk->cur_frame] & 1) |
| 348 | palchange |= 2; |
| 349 | pkt->data[0] = palchange; |
| 350 | memcpy(pkt->data + 1, smk->pal, 768); |
| 351 | ret = avio_read(s->pb, pkt->data + 769, frame_size); |
| 352 | if(ret != frame_size) |
| 353 | return AVERROR(EIO); |
| 354 | pkt->stream_index = smk->videoindex; |
| 355 | pkt->pts = smk->cur_frame; |
| 356 | pkt->size = ret + 769; |
| 357 | smk->cur_frame++; |
| 358 | smk->nextpos = avio_tell(s->pb); |
| 359 | } else { |
| 360 | if (smk->stream_id[smk->curstream] < 0 || !smk->bufs[smk->curstream]) |
| 361 | return AVERROR_INVALIDDATA; |
| 362 | if (av_new_packet(pkt, smk->buf_sizes[smk->curstream])) |
| 363 | return AVERROR(ENOMEM); |
| 364 | memcpy(pkt->data, smk->bufs[smk->curstream], smk->buf_sizes[smk->curstream]); |
| 365 | pkt->size = smk->buf_sizes[smk->curstream]; |
| 366 | pkt->stream_index = smk->stream_id[smk->curstream]; |
| 367 | pkt->pts = smk->aud_pts[smk->curstream]; |
| 368 | smk->aud_pts[smk->curstream] += AV_RL32(pkt->data); |
| 369 | smk->curstream--; |
| 370 | } |
| 371 | |
| 372 | return 0; |
| 373 | } |
| 374 | |
| 375 | static int smacker_read_close(AVFormatContext *s) |
| 376 | { |
| 377 | SmackerContext *smk = s->priv_data; |
| 378 | int i; |
| 379 | |
| 380 | for(i = 0; i < 7; i++) |
| 381 | av_freep(&smk->bufs[i]); |
| 382 | av_freep(&smk->frm_size); |
| 383 | av_freep(&smk->frm_flags); |
| 384 | |
| 385 | return 0; |
| 386 | } |
| 387 | |
| 388 | AVInputFormat ff_smacker_demuxer = { |
| 389 | .name = "smk", |
| 390 | .long_name = NULL_IF_CONFIG_SMALL("Smacker"), |
| 391 | .priv_data_size = sizeof(SmackerContext), |
| 392 | .read_probe = smacker_probe, |
| 393 | .read_header = smacker_read_header, |
| 394 | .read_packet = smacker_read_packet, |
| 395 | .read_close = smacker_read_close, |
| 396 | }; |