diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 999632cf9e..22d85ca009 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -634,6 +634,7 @@ OBJS-$(CONFIG_VPLAYER_DECODER)         += textdec.o ass.o
 OBJS-$(CONFIG_VQA_DECODER)             += vqavideo.o
 OBJS-$(CONFIG_WAVPACK_DECODER)         += wavpack.o
 OBJS-$(CONFIG_WAVPACK_ENCODER)         += wavpackenc.o
+OBJS-$(CONFIG_WCAP_DECODER)            += wcapdec.o
 OBJS-$(CONFIG_WEBP_DECODER)            += webp.o
 OBJS-$(CONFIG_WEBVTT_DECODER)          += webvttdec.o ass.o
 OBJS-$(CONFIG_WEBVTT_ENCODER)          += webvttenc.o ass_split.o
@@ -980,6 +981,7 @@ OBJS-$(CONFIG_VC1_PARSER)              += vc1_parser.o vc1.o vc1data.o  \
 OBJS-$(CONFIG_VP3_PARSER)              += vp3_parser.o
 OBJS-$(CONFIG_VP8_PARSER)              += vp8_parser.o
 OBJS-$(CONFIG_VP9_PARSER)              += vp9_parser.o
+OBJS-$(CONFIG_WCAP_PARSER)             += wcap_parser.o
 OBJS-$(CONFIG_XMA_PARSER)              += xma_parser.o
 
 # bitstream filters
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index ce0bc7ecf3..a1886590c8 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -376,6 +376,7 @@ static void register_all(void)
     REGISTER_DECODER(VP9,               vp9);
     REGISTER_DECODER(VQA,               vqa);
     REGISTER_DECODER(BITPACKED,         bitpacked);
+    REGISTER_DECODER(WCAP,              wcap);
     REGISTER_DECODER(WEBP,              webp);
     REGISTER_ENCODER(WRAPPED_AVFRAME,   wrapped_avframe);
     REGISTER_ENCDEC (WMV1,              wmv1);
@@ -730,6 +731,7 @@ static void register_all(void)
     REGISTER_PARSER(VP3,                vp3);
     REGISTER_PARSER(VP8,                vp8);
     REGISTER_PARSER(VP9,                vp9);
+    REGISTER_PARSER(WCAP,               wcap);
     REGISTER_PARSER(XMA,                xma);
 }
 
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 7708bb2adb..d9e3b5c84f 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -448,6 +448,7 @@ enum AVCodecID {
     AV_CODEC_ID_SVG,
     AV_CODEC_ID_GDV,
     AV_CODEC_ID_FITS,
+    AV_CODEC_ID_WCAP,
 
     /* various PCM "codecs" */
     AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecs
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index 6a13bbbf0e..d5c174b3c1 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -1417,6 +1417,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
         .long_name = NULL_IF_CONFIG_SMALL("Gremlin Digital Video"),
         .props     = AV_CODEC_PROP_LOSSY,
     },
+    {
+        .id        = AV_CODEC_ID_WCAP,
+        .type      = AVMEDIA_TYPE_VIDEO,
+        .name      = "wcap",
+        .long_name = NULL_IF_CONFIG_SMALL("Weston capture"),
+        .props     = AV_CODEC_PROP_LOSSLESS,
+    },
 
     /* image codecs */
     {
diff --git a/libavcodec/wcap_parser.c b/libavcodec/wcap_parser.c
new file mode 100644
index 0000000000..7b2d6fdf80
--- /dev/null
+++ b/libavcodec/wcap_parser.c
@@ -0,0 +1,124 @@
+/*
+ * WCAP parser
+ * Copyright (c) 2017 Paul B Mahol
+ *
+ * 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
+ */
+
+#include "libavutil/bswap.h"
+#include "parser.h"
+
+typedef struct WCAPParseContext {
+    ParseContext pc;
+    uint32_t pos;
+    uint32_t nrects;
+    int got_msec;
+    int got_nrects;
+    int got_rectangle;
+    int got_frame;
+    uint32_t rect_counter;
+    uint32_t current_rect;
+    uint32_t x1, y1, x2, y2;
+    int first;
+    int64_t pts;
+} WCAPParseContext;
+
+static int wcap_parse(AVCodecParserContext *s, AVCodecContext *avctx,
+                      const uint8_t **poutbuf, int *poutbuf_size,
+                      const uint8_t *buf, int buf_size)
+{
+    WCAPParseContext *ppc = s->priv_data;
+    uint32_t state = ppc->pc.state;
+    int i, next = END_NOT_FOUND;
+
+    *poutbuf_size = 0;
+
+    for (i = 0; i < buf_size; i++) {
+        state = (state << 8) | buf[i];
+
+        if (((ppc->pc.index + i) % 4) != 3)
+            continue;
+
+        if (!ppc->got_msec) {
+            ppc->got_msec = 1;
+            ppc->pts = av_bswap32(state);
+            ppc->current_rect = 0;
+        } else if (ppc->got_msec && !ppc->got_nrects) {
+            ppc->got_nrects = 1;
+            ppc->nrects = av_bswap32(state);
+        } else if (!ppc->got_rectangle && ppc->got_msec && ppc->got_nrects) {
+            ppc->rect_counter++;
+            if (ppc->rect_counter == 4) {
+                ppc->got_rectangle = 1;
+                ppc->y2 = av_bswap32(state);
+            } else if (ppc->rect_counter == 3) {
+                ppc->x2 = av_bswap32(state);
+            } else if (ppc->rect_counter == 2) {
+                ppc->y1 = av_bswap32(state);
+            } else if (ppc->rect_counter == 1) {
+                ppc->x1 = av_bswap32(state);
+            }
+        } else if (ppc->got_rectangle && ppc->got_nrects && ppc->got_msec) {
+            if (!ppc->got_frame) {
+                int l = state & 0xFF;
+
+                if (l < 0xE0)
+                    l = l + 1;
+                else
+                    l = 1 << (l - 0xE0 + 7);
+                ppc->pos += l;
+                if (ppc->pos >= (ppc->x2 - ppc->x1) * (ppc->y2 - ppc->y1)) {
+                    ppc->got_frame = 1;
+                }
+            }
+            if (ppc->got_frame) {
+                ppc->current_rect++;
+                ppc->got_rectangle = 0;
+                ppc->rect_counter = 0;
+                ppc->got_frame = 0;
+                ppc->pos = 0;
+                if (ppc->current_rect == ppc->nrects) {
+                    ppc->current_rect = 0;
+                    ppc->nrects = 0;
+                    ppc->got_msec = 0;
+                    ppc->got_nrects = 0;
+                    next = i + 1;
+                    s->key_frame = !ppc->first;
+                    s->pict_type = s->key_frame ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P;
+                    s->pts = ppc->pts;
+                    ppc->first++;
+                    break;
+                }
+            }
+        }
+    }
+    ppc->pc.state = state;
+
+    if (ff_combine_frame(&ppc->pc, next, &buf, &buf_size) < 0)
+        return buf_size;
+
+    *poutbuf      = buf;
+    *poutbuf_size = buf_size;
+    return next;
+}
+
+AVCodecParser ff_wcap_parser = {
+    .codec_ids      = { AV_CODEC_ID_WCAP },
+    .priv_data_size = sizeof(WCAPParseContext),
+    .parser_parse   = wcap_parse,
+    .parser_close   = ff_parse_close,
+};
diff --git a/libavcodec/wcapdec.c b/libavcodec/wcapdec.c
new file mode 100644
index 0000000000..f9cffedc03
--- /dev/null
+++ b/libavcodec/wcapdec.c
@@ -0,0 +1,151 @@
+/*
+ * WCAP video decoder
+ *
+ * Copyright (c) 2012 Paul B Mahol
+ *
+ * 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
+ */
+
+#include <inttypes.h>
+
+#include "libavutil/imgutils.h"
+#include "avcodec.h"
+#include "bytestream.h"
+#include "internal.h"
+
+typedef struct WCAPContext {
+    AVFrame *frame;
+} WCAPContext;
+
+static av_cold int wcap_decode_init(AVCodecContext *avctx)
+{
+    WCAPContext *s = avctx->priv_data;
+    uint32_t format;
+
+    if (avctx->extradata && avctx->extradata_size >= 4) {
+        format = AV_RL32(avctx->extradata);
+
+        switch (format) {
+        case 0x34325852: avctx->pix_fmt = AV_PIX_FMT_RGB0; break;
+        case 0x34325842: avctx->pix_fmt = AV_PIX_FMT_BGR0; break;
+        case 0x34325258: avctx->pix_fmt = AV_PIX_FMT_0RGB; break;
+        case 0x34324258: avctx->pix_fmt = AV_PIX_FMT_0BGR; break;
+        }
+    }
+
+    s->frame = av_frame_alloc();
+    if (!s->frame)
+        return AVERROR(ENOMEM);
+
+    return 0;
+}
+
+static void clear(AVCodecContext *avctx)
+{
+    WCAPContext *s = avctx->priv_data;
+    int y;
+
+    if (!s->frame->buf[0])
+        return;
+
+    for (y = 0; y < avctx->height; y++) {
+        memset(s->frame->data[0] + y * s->frame->linesize[0], 0, avctx->width * 4);
+    }
+}
+
+static int wcap_decode_frame(AVCodecContext *avctx, void *data,
+                            int *got_frame, AVPacket *avpkt)
+{
+    WCAPContext *s = avctx->priv_data;
+    AVFrame *frame = s->frame;
+    uint32_t nrects, x1, y1, x2, y2;
+    int ret, n, i, k, x;
+    GetByteContext gb;
+    uint8_t *dst;
+
+    if ((ret = av_image_check_size(avctx->width, avctx->height, 0, NULL)) < 0)
+        return ret;
+
+    bytestream2_init(&gb, avpkt->data, avpkt->size);
+
+    if ((ret = ff_reget_buffer(avctx, frame)) < 0)
+        return ret;
+
+    if (avpkt->flags & AV_PKT_FLAG_KEY) {
+        clear(avctx);
+    }
+
+    bytestream2_skip(&gb, 4);
+    nrects = bytestream2_get_le32(&gb);
+
+    for (n = 0; n < nrects; n++) {
+        x1 = bytestream2_get_le32(&gb);
+        y1 = bytestream2_get_le32(&gb);
+        x2 = bytestream2_get_le32(&gb);
+        y2 = bytestream2_get_le32(&gb);
+
+        if (x1 >= x2 || y1 >= y2 || x2 > avctx->width || y2 > avctx->height ||
+            (x2 - x1) > avctx->width || (y2 - y1) > avctx->height)
+            return AVERROR_INVALIDDATA;
+
+        x = x1;
+        dst = frame->data[0] + (avctx->height - y1 - 1) * frame->linesize[0];
+
+        for (i = 0; i < (x2 - x1) * (y2 - y1);) {
+            unsigned v = bytestream2_get_le32(&gb);
+            int run_len = v >> 24;
+
+            if (run_len < 0xE0)
+                run_len++;
+            else
+                run_len = 1 << (run_len - 0xE0 + 7);
+
+            i += run_len;
+            for (k = 0; k < run_len; k++) {
+                dst[x*4 + 1] += v & 0xFF;
+                dst[x*4 + 2] += (v >> 8) & 0xFF;
+                dst[x*4 + 3] += (v >> 16) & 0xFF;
+                x++;
+                if (x == x2) {
+                    x = x1;
+                    dst -= frame->linesize[0];
+                }
+            }
+        }
+    }
+
+    frame->key_frame = (avpkt->flags & AV_PKT_FLAG_KEY) ? 1 : 0;
+    frame->pict_type = (avpkt->flags & AV_PKT_FLAG_KEY) ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P;
+
+    if ((ret = av_frame_ref(data, s->frame)) < 0)
+        return ret;
+
+    *got_frame       = 1;
+
+    return avpkt->size;
+}
+
+AVCodec ff_wcap_decoder = {
+    .name           = "wcap",
+    .long_name      = NULL_IF_CONFIG_SMALL("Weston capture"),
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_WCAP,
+    .priv_data_size = sizeof(WCAPContext),
+    .init           = wcap_decode_init,
+    .decode         = wcap_decode_frame,
+    .capabilities   = AV_CODEC_CAP_DR1,
+};
diff --git a/libavformat/Makefile b/libavformat/Makefile
index df709c29dd..4eb42f2727 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -513,6 +513,7 @@ OBJS-$(CONFIG_W64_MUXER)                 += wavenc.o w64.o
 OBJS-$(CONFIG_WAV_DEMUXER)               += wavdec.o pcm.o
 OBJS-$(CONFIG_WAV_MUXER)                 += wavenc.o
 OBJS-$(CONFIG_WC3_DEMUXER)               += wc3movie.o
+OBJS-$(CONFIG_WCAP_DEMUXER)              += wcapdec.o
 OBJS-$(CONFIG_WEBM_MUXER)                += matroskaenc.o matroska.o \
                                             avc.o hevc.o \
                                             flacenc_header.o avlanguage.o \
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index 405ddb5ad9..b90f8291dc 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -336,6 +336,7 @@ static void register_all(void)
     REGISTER_MUXDEMUX(W64,              w64);
     REGISTER_MUXDEMUX(WAV,              wav);
     REGISTER_DEMUXER (WC3,              wc3);
+    REGISTER_DEMUXER (WCAP,             wcap);
     REGISTER_MUXER   (WEBM,             webm);
     REGISTER_MUXDEMUX(WEBM_DASH_MANIFEST, webm_dash_manifest);
     REGISTER_MUXER   (WEBM_CHUNK,       webm_chunk);
diff --git a/libavformat/wcapdec.c b/libavformat/wcapdec.c
new file mode 100644
index 0000000000..c96dc68985
--- /dev/null
+++ b/libavformat/wcapdec.c
@@ -0,0 +1,72 @@
+/*
+ * WCAP demuxer
+ * Copyright (c) 2017 Paul B Mahol
+ *
+ * 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
+ */
+
+#include "libavutil/intreadwrite.h"
+
+#include "avformat.h"
+#include "internal.h"
+#include "rawdec.h"
+
+static int wcap_probe(AVProbeData *pd)
+{
+    if (AV_RB32(pd->buf) == MKTAG('W','C','A','P'))
+        return AVPROBE_SCORE_MAX;
+    else
+        return 0;
+}
+
+static int wcap_read_header(AVFormatContext *s)
+{
+    AVIOContext *pb = s->pb;
+    AVStream *st;
+
+    avio_skip(pb, 4);
+
+    st = avformat_new_stream(s, NULL);
+    if (!st)
+        return AVERROR(ENOMEM);
+
+    if (ff_get_extradata(s, st->codecpar, pb, 4) < 0)
+        return AVERROR(ENOMEM);
+
+    st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+    st->codecpar->codec_id   = AV_CODEC_ID_WCAP;
+    st->codecpar->width      = avio_rl32(pb);
+    st->codecpar->height     = avio_rl32(pb);
+    st->need_parsing         = AVSTREAM_PARSE_FULL_RAW;
+    avpriv_set_pts_info(st, 64, 1, 1000);
+
+    st->start_time = avio_rl32(pb);
+    avio_seek(pb, -4, SEEK_CUR);
+
+    return 0;
+}
+
+AVInputFormat ff_wcap_demuxer = {
+    .name           = "wcap",
+    .long_name      = NULL_IF_CONFIG_SMALL("Weston capture"),
+    .read_probe     = wcap_probe,
+    .read_header    = wcap_read_header,
+    .read_packet    = ff_raw_read_partial_packet,
+    .raw_codec_id   = AV_CODEC_ID_WCAP,
+    .extensions     = "wcap",
+    .flags          = AVFMT_GENERIC_INDEX,
+};
