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 124 125 126 127 128 129 130 131 132 133
            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 < 0)
        return AVERROR(EINVAL);
    if (ret == Z_STREAM_END)
        return AVERROR_EOF;

    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 398 399
            pkt->pos = pos;
            pkt->stream_index = st->index;

            switch (bmp_fmt) {
            case 3:
400
                pix_fmt = AV_PIX_FMT_PAL8;
401 402 403 404 405 406 407 408 409 410 411
                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:
412
                pix_fmt = AV_PIX_FMT_RGB555;
413 414
                break;
            case 5:
415
                pix_fmt = alpha_bmp ? AV_PIX_FMT_ARGB : AV_PIX_FMT_0RGB;
416 417 418 419
                break;
            default:
                av_assert0(0);
            }
420
            if (st->codecpar->format != AV_PIX_FMT_NONE && st->codecpar->format != pix_fmt) {
421
                av_log(s, AV_LOG_ERROR, "pixel format change unsupported\n");
422
            } else
423
                st->codecpar->format = pix_fmt;
424 425 426 427 428

            if (linesize * height > pkt->size) {
                res = AVERROR_INVALIDDATA;
                goto bitmap_end;
            }
429 430 431 432 433 434 435 436 437 438 439 440 441 442
            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
443
        } else if (tag == TAG_STREAMBLOCK) {
444 445
            for (i = 0; i < s->nb_streams; i++) {
                st = s->streams[i];
446 447
                if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->id == -1) {
                    if (st->codecpar->codec_id == AV_CODEC_ID_MP3) {
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
                        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;
463 464
                }
            }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
465 466 467
        } else if (tag == TAG_JPEG2) {
            for (i=0; i<s->nb_streams; i++) {
                st = s->streams[i];
468
                if (st->codecpar->codec_id == AV_CODEC_ID_MJPEG && st->id == -2)
Baptiste Coudurier's avatar
Baptiste Coudurier committed
469 470 471
                    break;
            }
            if (i == s->nb_streams) {
472
                vst = avformat_new_stream(s, NULL);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
473
                if (!vst)
474
                    return AVERROR(ENOMEM);
475
                vst->id = -2; /* -2 to avoid clash with video stream and audio stream */
476 477
                vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
                vst->codecpar->codec_id = AV_CODEC_ID_MJPEG;
478
                avpriv_set_pts_info(vst, 64, 256, swf->frame_rate);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
479 480
                st = vst;
            }
481
            avio_rl16(pb); /* BITMAP_ID */
482 483 484 485
            len -= 2;
            if (len < 4)
                goto skip;
            if ((res = av_new_packet(pkt, len)) < 0)
486
                return res;
487
            if (avio_read(pb, pkt->data, 4) != 4) {
488
                av_packet_unref(pkt);
489 490
                return AVERROR_INVALIDDATA;
            }
Baptiste Coudurier's avatar
Baptiste Coudurier committed
491 492 493 494 495
            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;
496
                memset(pkt->data+pkt->size, 0, 4);
497
                res = avio_read(pb, pkt->data, pkt->size);
Baptiste Coudurier's avatar
Baptiste Coudurier committed
498
            } else {
499 500 501
                res = avio_read(pb, pkt->data + 4, pkt->size - 4);
                if (res >= 0)
                    res += 4;
Baptiste Coudurier's avatar
Baptiste Coudurier committed
502
            }
503 504
            if (res != pkt->size) {
                if (res < 0) {
505
                    av_packet_unref(pkt);
506 507 508 509 510
                    return res;
                }
                av_shrink_packet(pkt, res);
            }

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

525 526 527 528 529 530 531 532 533 534 535 536
#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

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