swfdec.c 18 KB
Newer Older
Baptiste Coudurier's avatar
Baptiste Coudurier committed
1
/*
2
 * Flash Compatible Streaming Format demuxer
3 4
 * Copyright (c) 2000 Fabrice Bellard
 * Copyright (c) 2003 Tinic Uro
Baptiste Coudurier's avatar
Baptiste Coudurier committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

23 24 25 26 27 28
#include "config.h"

#if CONFIG_ZLIB
#include <zlib.h>
#endif

29
#include "libavutil/avassert.h"
30
#include "libavutil/channel_layout.h"
31
#include "libavutil/imgutils.h"
32
#include "libavutil/internal.h"
33
#include "libavutil/intreadwrite.h"
34
#include "libavcodec/get_bits.h"
Baptiste Coudurier's avatar
Baptiste Coudurier committed
35 36
#include "swf.h"

37
static const AVCodecTag swf_audio_codec_tags[] = {
38 39 40 41 42 43
    { AV_CODEC_ID_PCM_S16LE,  0x00 },
    { AV_CODEC_ID_ADPCM_SWF,  0x01 },
    { AV_CODEC_ID_MP3,        0x02 },
    { AV_CODEC_ID_PCM_S16LE,  0x03 },
//  { AV_CODEC_ID_NELLYMOSER, 0x06 },
    { AV_CODEC_ID_NONE,          0 },
44 45
};

46
static int get_swf_tag(AVIOContext *pb, int *len_ptr)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
47 48 49
{
    int tag, len;

50
    if (avio_feof(pb))
51
        return AVERROR_EOF;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
52

53
    tag = avio_rl16(pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
54 55 56
    len = tag & 0x3f;
    tag = tag >> 6;
    if (len == 0x3f) {
57
        len = avio_rl32(pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
58 59 60 61 62 63 64 65
    }
    *len_ptr = len;
    return tag;
}


static int swf_probe(AVProbeData *p)
{
66 67 68
    GetBitContext gb;
    int len, xmin, xmax, ymin, ymax;

69 70 71
    if(p->buf_size < 15)
        return 0;

Baptiste Coudurier's avatar
Baptiste Coudurier committed
72
    /* check file header */
73 74
    if (   AV_RB24(p->buf) != AV_RB24("CWS")
        && AV_RB24(p->buf) != AV_RB24("FWS"))
Baptiste Coudurier's avatar
Baptiste Coudurier committed
75
        return 0;
76

77 78 79 80
    if (   AV_RB24(p->buf) == AV_RB24("CWS")
        && p->buf[3] <= 20)
        return AVPROBE_SCORE_MAX / 4 + 1;

81 82
    if (init_get_bits8(&gb, p->buf + 3, p->buf_size - 3) < 0)
        return 0;
83 84 85 86 87 88 89 90 91 92 93 94 95

    skip_bits(&gb, 40);
    len = get_bits(&gb, 5);
    if (!len)
        return 0;
    xmin = get_bits_long(&gb, len);
    xmax = get_bits_long(&gb, len);
    ymin = get_bits_long(&gb, len);
    ymax = get_bits_long(&gb, len);
    if (xmin || ymin || !xmax || !ymax)
        return 0;

    if (p->buf[3] >= 20 || xmax < 16 || ymax < 16)
96 97 98
        return AVPROBE_SCORE_MAX / 4;

    return AVPROBE_SCORE_MAX;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
99 100
}

101 102 103 104 105 106 107 108 109 110 111
#if CONFIG_ZLIB
static int zlib_refill(void *opaque, uint8_t *buf, int buf_size)
{
    AVFormatContext *s = opaque;
    SWFContext *swf = s->priv_data;
    z_stream *z = &swf->zstream;
    int ret;

retry:
    if (!z->avail_in) {
        int n = avio_read(s->pb, swf->zbuf_in, ZBUF_SIZE);
112
        if (n < 0)
113 114 115 116 117 118 119 120 121 122 123
            return n;
        z->next_in  = swf->zbuf_in;
        z->avail_in = n;
    }

    z->next_out  = buf;
    z->avail_out = buf_size;

    ret = inflate(z, Z_NO_FLUSH);
    if (ret == Z_STREAM_END)
        return AVERROR_EOF;
124 125
    if (ret != Z_OK)
        return AVERROR(EINVAL);
126 127 128 129 130 131 132 133

    if (buf_size - z->avail_out == 0)
        goto retry;

    return buf_size - z->avail_out;
}
#endif

134
static int swf_read_header(AVFormatContext *s)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
135 136
{
    SWFContext *swf = s->priv_data;
137
    AVIOContext *pb = s->pb;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
138 139
    int nbits, len, tag;

140
    tag = avio_rb32(pb) & 0xffffff00;
141
    avio_rl32(pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
142 143

    if (tag == MKBETAG('C', 'W', 'S', 0)) {
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
        av_log(s, AV_LOG_INFO, "SWF compressed file detected\n");
#if CONFIG_ZLIB
        swf->zbuf_in  = av_malloc(ZBUF_SIZE);
        swf->zbuf_out = av_malloc(ZBUF_SIZE);
        swf->zpb = avio_alloc_context(swf->zbuf_out, ZBUF_SIZE, 0, s,
                                      zlib_refill, NULL, NULL);
        if (!swf->zbuf_in || !swf->zbuf_out || !swf->zpb)
            return AVERROR(ENOMEM);
        swf->zpb->seekable = 0;
        if (inflateInit(&swf->zstream) != Z_OK) {
            av_log(s, AV_LOG_ERROR, "Unable to init zlib context\n");
            return AVERROR(EINVAL);
        }
        pb = swf->zpb;
#else
        av_log(s, AV_LOG_ERROR, "zlib support is required to read SWF compressed files\n");
Baptiste Coudurier's avatar
Baptiste Coudurier committed
160
        return AVERROR(EIO);
161 162
#endif
    } else if (tag != MKBETAG('F', 'W', 'S', 0))
Baptiste Coudurier's avatar
Baptiste Coudurier committed
163 164
        return AVERROR(EIO);
    /* skip rectangle size */
165
    nbits = avio_r8(pb) >> 3;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
166
    len = (4 * nbits - 3 + 7) / 8;
167
    avio_skip(pb, len);
168 169
    swf->frame_rate = avio_rl16(pb); /* 8.8 fixed */
    avio_rl16(pb); /* frame count */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
170 171 172 173 174 175

    swf->samples_per_frame = 0;
    s->ctx_flags |= AVFMTCTX_NOHEADER;
    return 0;
}

176 177
static AVStream *create_new_audio_stream(AVFormatContext *s, int id, int info)
{
178
    int sample_rate_code, sample_size_code;
179 180 181 182 183
    AVStream *ast = avformat_new_stream(s, NULL);
    if (!ast)
        return NULL;
    ast->id = id;
    if (info & 1) {
184 185
        ast->codecpar->channels       = 2;
        ast->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
186
    } else {
187 188
        ast->codecpar->channels       = 1;
        ast->codecpar->channel_layout = AV_CH_LAYOUT_MONO;
189
    }
190 191
    ast->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
    ast->codecpar->codec_id   = ff_codec_get_id(swf_audio_codec_tags, info>>4 & 15);
192 193
    ast->need_parsing = AVSTREAM_PARSE_FULL;
    sample_rate_code = info>>2 & 3;
194
    sample_size_code = info>>1 & 1;
195 196 197 198
    if (!sample_size_code && ast->codecpar->codec_id == AV_CODEC_ID_PCM_S16LE)
        ast->codecpar->codec_id = AV_CODEC_ID_PCM_U8;
    ast->codecpar->sample_rate = 44100 >> (3 - sample_rate_code);
    avpriv_set_pts_info(ast, 64, 1, ast->codecpar->sample_rate);
199 200 201
    return ast;
}

Baptiste Coudurier's avatar
Baptiste Coudurier committed
202 203 204
static int swf_read_packet(AVFormatContext *s, AVPacket *pkt)
{
    SWFContext *swf = s->priv_data;
205
    AVIOContext *pb = s->pb;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
206
    AVStream *vst = NULL, *ast = NULL, *st = 0;
207
    int tag, len, i, frame, v, res;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
208

209 210 211 212 213
#if CONFIG_ZLIB
    if (swf->zpb)
        pb = swf->zpb;
#endif

Baptiste Coudurier's avatar
Baptiste Coudurier committed
214
    for(;;) {
215
        uint64_t pos = avio_tell(pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
216 217
        tag = get_swf_tag(pb, &len);
        if (tag < 0)
218
            return tag;
219
        if (len < 0) {
220
            av_log(s, AV_LOG_ERROR, "invalid tag length: %d\n", len);
221 222
            return AVERROR_INVALIDDATA;
        }
223
        if (tag == TAG_VIDEOSTREAM) {
224
            int ch_id = avio_rl16(pb);
225 226 227 228
            len -= 2;

            for (i=0; i<s->nb_streams; i++) {
                st = s->streams[i];
229
                if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && st->id == ch_id)
230 231 232
                    goto skip;
            }

233 234 235 236
            avio_rl16(pb);
            avio_rl16(pb);
            avio_rl16(pb);
            avio_r8(pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
237
            /* Check for FLV1 */
238
            vst = avformat_new_stream(s, NULL);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
239
            if (!vst)
240
                return AVERROR(ENOMEM);
241
            vst->id = ch_id;
242 243
            vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
            vst->codecpar->codec_id = ff_codec_get_id(ff_swf_codec_tags, avio_r8(pb));
244
            avpriv_set_pts_info(vst, 16, 256, swf->frame_rate);
245 246
            len -= 8;
        } else if (tag == TAG_STREAMHEAD || tag == TAG_STREAMHEAD2) {
Baptiste Coudurier's avatar
Baptiste Coudurier committed
247
            /* streaming found */
248 249 250

            for (i=0; i<s->nb_streams; i++) {
                st = s->streams[i];
251
                if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->id == -1)
252 253 254
                    goto skip;
            }

255 256 257
            avio_r8(pb);
            v = avio_r8(pb);
            swf->samples_per_frame = avio_rl16(pb);
258
            ast = create_new_audio_stream(s, -1, v); /* -1 to avoid clash with video stream ch_id */
Baptiste Coudurier's avatar
Baptiste Coudurier committed
259
            if (!ast)
260
                return AVERROR(ENOMEM);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
261
            len -= 4;
262 263 264 265 266 267
        } else if (tag == TAG_DEFINESOUND) {
            /* audio stream */
            int ch_id = avio_rl16(pb);

            for (i=0; i<s->nb_streams; i++) {
                st = s->streams[i];
268
                if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->id == ch_id)
269 270 271 272 273 274 275
                    goto skip;
            }

            // FIXME: The entire audio stream is stored in a single chunk/tag. Normally,
            // these are smaller audio streams in DEFINESOUND tags, but it's technically
            // possible they could be huge. Break it up into multiple packets if it's big.
            v = avio_r8(pb);
276
            ast = create_new_audio_stream(s, ch_id, v);
277
            if (!ast)
278
                return AVERROR(ENOMEM);
279 280 281 282 283 284 285 286 287 288 289
            ast->duration = avio_rl32(pb); // number of samples
            if (((v>>4) & 15) == 2) { // MP3 sound data record
                ast->skip_samples = avio_rl16(pb);
                len -= 2;
            }
            len -= 7;
            if ((res = av_get_packet(pb, pkt, len)) < 0)
                return res;
            pkt->pos = pos;
            pkt->stream_index = ast->index;
            return pkt->size;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
290
        } else if (tag == TAG_VIDEOFRAME) {
291
            int ch_id = avio_rl16(pb);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
292 293 294
            len -= 2;
            for(i=0; i<s->nb_streams; i++) {
                st = s->streams[i];
295
                if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && st->id == ch_id) {
296
                    frame = avio_rl16(pb);
297 298 299 300
                    len -= 2;
                    if (len <= 0)
                        goto skip;
                    if ((res = av_get_packet(pb, pkt, len)) < 0)
301
                        return res;
302
                    pkt->pos = pos;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
303 304 305 306 307
                    pkt->pts = frame;
                    pkt->stream_index = st->index;
                    return pkt->size;
                }
            }
308 309 310 311 312 313 314 315 316 317 318 319 320
        } else if (tag == TAG_DEFINEBITSLOSSLESS || tag == TAG_DEFINEBITSLOSSLESS2) {
#if CONFIG_ZLIB
            long out_len;
            uint8_t *buf = NULL, *zbuf = NULL, *pal;
            uint32_t colormap[AVPALETTE_COUNT] = {0};
            const int alpha_bmp = tag == TAG_DEFINEBITSLOSSLESS2;
            const int colormapbpp = 3 + alpha_bmp;
            int linesize, colormapsize = 0;

            const int ch_id   = avio_rl16(pb);
            const int bmp_fmt = avio_r8(pb);
            const int width   = avio_rl16(pb);
            const int height  = avio_rl16(pb);
321
            int pix_fmt;
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352

            len -= 2+1+2+2;

            switch (bmp_fmt) {
            case 3: // PAL-8
                linesize = width;
                colormapsize = avio_r8(pb) + 1;
                len--;
                break;
            case 4: // RGB15
                linesize = width * 2;
                break;
            case 5: // RGB24 (0RGB)
                linesize = width * 4;
                break;
            default:
                av_log(s, AV_LOG_ERROR, "invalid bitmap format %d, skipped\n", bmp_fmt);
                goto bitmap_end_skip;
            }

            linesize = FFALIGN(linesize, 4);

            if (av_image_check_size(width, height, 0, s) < 0 ||
                linesize >= INT_MAX / height ||
                linesize * height >= INT_MAX - colormapsize * colormapbpp) {
                av_log(s, AV_LOG_ERROR, "invalid frame size %dx%d\n", width, height);
                goto bitmap_end_skip;
            }

            out_len = colormapsize * colormapbpp + linesize * height;

353
            ff_dlog(s, "bitmap: ch=%d fmt=%d %dx%d (linesize=%d) len=%d->%ld pal=%d\n",
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
                    ch_id, bmp_fmt, width, height, linesize, len, out_len, colormapsize);

            zbuf = av_malloc(len);
            buf  = av_malloc(out_len);
            if (!zbuf || !buf) {
                res = AVERROR(ENOMEM);
                goto bitmap_end;
            }

            len = avio_read(pb, zbuf, len);
            if (len < 0 || (res = uncompress(buf, &out_len, zbuf, len)) != Z_OK) {
                av_log(s, AV_LOG_WARNING, "Failed to uncompress one bitmap\n");
                goto bitmap_end_skip;
            }

            for (i = 0; i < s->nb_streams; i++) {
                st = s->streams[i];
371
                if (st->codecpar->codec_id == AV_CODEC_ID_RAWVIDEO && st->id == -3)
372 373 374 375 376 377 378 379 380
                    break;
            }
            if (i == s->nb_streams) {
                vst = avformat_new_stream(s, NULL);
                if (!vst) {
                    res = AVERROR(ENOMEM);
                    goto bitmap_end;
                }
                vst->id = -3; /* -3 to avoid clash with video stream and audio stream */
381 382
                vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
                vst->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
383 384 385 386 387 388
                avpriv_set_pts_info(vst, 64, 256, swf->frame_rate);
                st = vst;
            }

            if ((res = av_new_packet(pkt, out_len - colormapsize * colormapbpp)) < 0)
                goto bitmap_end;
389 390 391
            if (!st->codecpar->width && !st->codecpar->height) {
                st->codecpar->width  = width;
                st->codecpar->height = height;
392 393 394
            } else {
                ff_add_param_change(pkt, 0, 0, 0, width, height);
            }
395 396 397
            pkt->pos = pos;
            pkt->stream_index = st->index;

398 399
            if (linesize * height > pkt->size) {
                res = AVERROR_INVALIDDATA;
400
                av_packet_unref(pkt);
401 402 403
                goto bitmap_end;
            }

404 405
            switch (bmp_fmt) {
            case 3:
406
                pix_fmt = AV_PIX_FMT_PAL8;
407 408 409 410 411 412 413 414 415 416 417
                for (i = 0; i < colormapsize; i++)
                    if (alpha_bmp)  colormap[i] = buf[3]<<24 | AV_RB24(buf + 4*i);
                    else            colormap[i] = 0xffU <<24 | AV_RB24(buf + 3*i);
                pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE);
                if (!pal) {
                    res = AVERROR(ENOMEM);
                    goto bitmap_end;
                }
                memcpy(pal, colormap, AVPALETTE_SIZE);
                break;
            case 4:
418
                pix_fmt = AV_PIX_FMT_RGB555;
419 420
                break;
            case 5:
421
                pix_fmt = alpha_bmp ? AV_PIX_FMT_ARGB : AV_PIX_FMT_0RGB;
422 423 424 425
                break;
            default:
                av_assert0(0);
            }
426
            if (st->codecpar->format != AV_PIX_FMT_NONE && st->codecpar->format != pix_fmt) {
427
                av_log(s, AV_LOG_ERROR, "pixel format change unsupported\n");
428
            } else
429
                st->codecpar->format = pix_fmt;
430

431 432 433 434 435 436 437 438 439 440 441 442 443 444
            memcpy(pkt->data, buf + colormapsize*colormapbpp, linesize * height);

            res = pkt->size;

bitmap_end:
            av_freep(&zbuf);
            av_freep(&buf);
            return res;
bitmap_end_skip:
            av_freep(&zbuf);
            av_freep(&buf);
#else
            av_log(s, AV_LOG_ERROR, "this file requires zlib support compiled in\n");
#endif
Baptiste Coudurier's avatar
Baptiste Coudurier committed
445
        } else if (tag == TAG_STREAMBLOCK) {
446 447
            for (i = 0; i < s->nb_streams; i++) {
                st = s->streams[i];
448 449
                if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->id == -1) {
                    if (st->codecpar->codec_id == AV_CODEC_ID_MP3) {
450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
                        avio_skip(pb, 4);
                        len -= 4;
                        if (len <= 0)
                            goto skip;
                        if ((res = av_get_packet(pb, pkt, len)) < 0)
                            return res;
                    } else { // ADPCM, PCM
                        if (len <= 0)
                            goto skip;
                        if ((res = av_get_packet(pb, pkt, len)) < 0)
                            return res;
                    }
                    pkt->pos          = pos;
                    pkt->stream_index = st->index;
                    return pkt->size;
465 466
                }
            }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
467 468 469
        } else if (tag == TAG_JPEG2) {
            for (i=0; i<s->nb_streams; i++) {
                st = s->streams[i];
470
                if (st->codecpar->codec_id == AV_CODEC_ID_MJPEG && st->id == -2)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
471 472 473
                    break;
            }
            if (i == s->nb_streams) {
474
                vst = avformat_new_stream(s, NULL);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
475
                if (!vst)
476
                    return AVERROR(ENOMEM);
477
                vst->id = -2; /* -2 to avoid clash with video stream and audio stream */
478 479
                vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
                vst->codecpar->codec_id = AV_CODEC_ID_MJPEG;
480
                avpriv_set_pts_info(vst, 64, 256, swf->frame_rate);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
481 482
                st = vst;
            }
483
            avio_rl16(pb); /* BITMAP_ID */
484 485 486 487
            len -= 2;
            if (len < 4)
                goto skip;
            if ((res = av_new_packet(pkt, len)) < 0)
488
                return res;
489
            if (avio_read(pb, pkt->data, 4) != 4) {
490
                av_packet_unref(pkt);
491 492
                return AVERROR_INVALIDDATA;
            }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
493 494 495 496 497
            if (AV_RB32(pkt->data) == 0xffd8ffd9 ||
                AV_RB32(pkt->data) == 0xffd9ffd8) {
                /* old SWF files containing SOI/EOI as data start */
                /* files created by swink have reversed tag */
                pkt->size -= 4;
498
                memset(pkt->data+pkt->size, 0, 4);
499
                res = avio_read(pb, pkt->data, pkt->size);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
500
            } else {
501 502 503
                res = avio_read(pb, pkt->data + 4, pkt->size - 4);
                if (res >= 0)
                    res += 4;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
504
            }
505 506
            if (res != pkt->size) {
                if (res < 0) {
507
                    av_packet_unref(pkt);
508 509 510 511 512
                    return res;
                }
                av_shrink_packet(pkt, res);
            }

513
            pkt->pos = pos;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
514 515
            pkt->stream_index = st->index;
            return pkt->size;
516 517
        } else {
            av_log(s, AV_LOG_DEBUG, "Unknown tag: %d\n", tag);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
518
        }
519
    skip:
520 521
        if(len<0)
            av_log(s, AV_LOG_WARNING, "Cliping len %d\n", len);
522
        len = FFMAX(0, len);
523
        avio_skip(pb, len);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
524 525 526
    }
}

527 528 529 530 531 532 533 534 535 536 537 538
#if CONFIG_ZLIB
static av_cold int swf_read_close(AVFormatContext *avctx)
{
    SWFContext *s = avctx->priv_data;
    inflateEnd(&s->zstream);
    av_freep(&s->zbuf_in);
    av_freep(&s->zbuf_out);
    av_freep(&s->zpb);
    return 0;
}
#endif

539
AVInputFormat ff_swf_demuxer = {
540
    .name           = "swf",
541
    .long_name      = NULL_IF_CONFIG_SMALL("SWF (ShockWave Flash)"),
542 543 544 545
    .priv_data_size = sizeof(SWFContext),
    .read_probe     = swf_probe,
    .read_header    = swf_read_header,
    .read_packet    = swf_read_packet,
546 547 548
#if CONFIG_ZLIB
    .read_close     = swf_read_close,
#endif
Baptiste Coudurier's avatar
Baptiste Coudurier committed
549
};