GCC Code Coverage Report
Directory: ../../../ffmpeg/ Exec Total Coverage
File: src/libavcodec/wcmv.c Lines: 0 129 0.0 %
Date: 2020-10-23 17:01:47 Branches: 0 78 0.0 %

Line Branch Exec Source
1
/*
2
 * WinCAM Motion Video decoder
3
 *
4
 * Copyright (c) 2018 Paul B Mahol
5
 *
6
 * This file is part of FFmpeg.
7
 *
8
 * FFmpeg is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2.1 of the License, or (at your option) any later version.
12
 *
13
 * FFmpeg is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19
 * License along with FFmpeg; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
 */
22
23
#include <stdio.h>
24
#include <stdlib.h>
25
#include <string.h>
26
27
#include "libavutil/imgutils.h"
28
29
#include "avcodec.h"
30
#include "bytestream.h"
31
#include "internal.h"
32
33
#include <zlib.h>
34
35
typedef struct WCMVContext {
36
    int         bpp;
37
    z_stream    zstream;
38
    AVFrame    *prev_frame;
39
    uint8_t     block_data[65536*8];
40
} WCMVContext;
41
42
static int decode_frame(AVCodecContext *avctx,
43
                        void *data, int *got_frame,
44
                        AVPacket *avpkt)
45
{
46
    WCMVContext *s = avctx->priv_data;
47
    AVFrame *frame = data;
48
    int skip, blocks, zret, ret, intra = 0, flags = 0, bpp = s->bpp;
49
    GetByteContext gb;
50
    uint8_t *dst;
51
52
    ret = inflateReset(&s->zstream);
53
    if (ret != Z_OK) {
54
        av_log(avctx, AV_LOG_ERROR, "Inflate reset error: %d\n", ret);
55
        return AVERROR_EXTERNAL;
56
    }
57
58
    bytestream2_init(&gb, avpkt->data, avpkt->size);
59
    blocks = bytestream2_get_le16(&gb);
60
    if (!blocks)
61
        flags |= FF_REGET_BUFFER_FLAG_READONLY;
62
63
    if ((ret = ff_reget_buffer(avctx, s->prev_frame, flags)) < 0)
64
        return ret;
65
66
    if (blocks > 5) {
67
        GetByteContext bgb;
68
        int x = 0, size;
69
70
        if (blocks * 8 >= 0xFFFF)
71
            size = bytestream2_get_le24(&gb);
72
        else if (blocks * 8 >= 0xFF)
73
            size = bytestream2_get_le16(&gb);
74
        else
75
            size = bytestream2_get_byte(&gb);
76
77
        skip = bytestream2_tell(&gb);
78
        if (size > avpkt->size - skip)
79
            return AVERROR_INVALIDDATA;
80
81
        s->zstream.next_in  = avpkt->data + skip;
82
        s->zstream.avail_in = size;
83
        s->zstream.next_out  = s->block_data;
84
        s->zstream.avail_out = sizeof(s->block_data);
85
86
        zret = inflate(&s->zstream, Z_FINISH);
87
        if (zret != Z_STREAM_END) {
88
            av_log(avctx, AV_LOG_ERROR,
89
                   "Inflate failed with return code: %d.\n", zret);
90
            return AVERROR_INVALIDDATA;
91
        }
92
93
        ret = inflateReset(&s->zstream);
94
        if (ret != Z_OK) {
95
            av_log(avctx, AV_LOG_ERROR, "Inflate reset error: %d\n", ret);
96
            return AVERROR_EXTERNAL;
97
        }
98
99
        bytestream2_skip(&gb, size);
100
        bytestream2_init(&bgb, s->block_data, blocks * 8);
101
102
        for (int i = 0; i < blocks; i++) {
103
            int w, h;
104
105
            bytestream2_skip(&bgb, 4);
106
            w = bytestream2_get_le16(&bgb);
107
            h = bytestream2_get_le16(&bgb);
108
            if (x + bpp * (int64_t)w * h > INT_MAX)
109
                return AVERROR_INVALIDDATA;
110
            x += bpp * w * h;
111
        }
112
113
        if (x >= 0xFFFF)
114
            bytestream2_skip(&gb, 3);
115
        else if (x >= 0xFF)
116
            bytestream2_skip(&gb, 2);
117
        else
118
            bytestream2_skip(&gb, 1);
119
120
        skip = bytestream2_tell(&gb);
121
122
        s->zstream.next_in  = avpkt->data + skip;
123
        s->zstream.avail_in = avpkt->size - skip;
124
125
        bytestream2_init(&gb, s->block_data, blocks * 8);
126
    } else if (blocks) {
127
        int x = 0;
128
129
        bytestream2_seek(&gb, 2, SEEK_SET);
130
131
        for (int i = 0; i < blocks; i++) {
132
            int w, h;
133
134
            bytestream2_skip(&gb, 4);
135
            w = bytestream2_get_le16(&gb);
136
            h = bytestream2_get_le16(&gb);
137
            if (x + bpp * (int64_t)w * h > INT_MAX)
138
                return AVERROR_INVALIDDATA;
139
            x += bpp * w * h;
140
        }
141
142
        if (x >= 0xFFFF)
143
            bytestream2_skip(&gb, 3);
144
        else if (x >= 0xFF)
145
            bytestream2_skip(&gb, 2);
146
        else
147
            bytestream2_skip(&gb, 1);
148
149
        skip = bytestream2_tell(&gb);
150
151
        s->zstream.next_in  = avpkt->data + skip;
152
        s->zstream.avail_in = avpkt->size - skip;
153
154
        bytestream2_seek(&gb, 2, SEEK_SET);
155
    }
156
157
    if (bytestream2_get_bytes_left(&gb) < 8LL * blocks)
158
        return AVERROR_INVALIDDATA;
159
160
    if (!avctx->frame_number) {
161
        ptrdiff_t linesize[4] = { s->prev_frame->linesize[0], 0, 0, 0 };
162
        av_image_fill_black(s->prev_frame->data, linesize, avctx->pix_fmt, 0,
163
                            avctx->width, avctx->height);
164
    }
165
166
    for (int block = 0; block < blocks; block++) {
167
        int x, y, w, h;
168
169
        x = bytestream2_get_le16(&gb);
170
        y = bytestream2_get_le16(&gb);
171
        w = bytestream2_get_le16(&gb);
172
        h = bytestream2_get_le16(&gb);
173
174
        if (blocks == 1 && x == 0 && y == 0 && w == avctx->width && h == avctx->height)
175
            intra = 1;
176
177
        if (x + w > avctx->width || y + h > avctx->height)
178
            return AVERROR_INVALIDDATA;
179
180
        if (w > avctx->width || h > avctx->height)
181
            return AVERROR_INVALIDDATA;
182
183
        dst = s->prev_frame->data[0] + (avctx->height - y - 1) * s->prev_frame->linesize[0] + x * bpp;
184
        for (int i = 0; i < h; i++) {
185
            s->zstream.next_out  = dst;
186
            s->zstream.avail_out = w * bpp;
187
188
            zret = inflate(&s->zstream, Z_SYNC_FLUSH);
189
            if (zret != Z_OK && zret != Z_STREAM_END) {
190
                av_log(avctx, AV_LOG_ERROR,
191
                       "Inflate failed with return code: %d.\n", zret);
192
                return AVERROR_INVALIDDATA;
193
            }
194
195
            dst -= s->prev_frame->linesize[0];
196
        }
197
    }
198
199
    s->prev_frame->key_frame = intra;
200
    s->prev_frame->pict_type = intra ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P;
201
202
    if ((ret = av_frame_ref(frame, s->prev_frame)) < 0)
203
        return ret;
204
205
    *got_frame = 1;
206
207
    return avpkt->size;
208
}
209
210
static av_cold int decode_init(AVCodecContext *avctx)
211
{
212
    WCMVContext *s = avctx->priv_data;
213
    int zret;
214
215
    switch (avctx->bits_per_coded_sample) {
216
    case 16: avctx->pix_fmt = AV_PIX_FMT_RGB565LE; break;
217
    case 24: avctx->pix_fmt = AV_PIX_FMT_BGR24;  break;
218
    case 32: avctx->pix_fmt = AV_PIX_FMT_BGRA;   break;
219
    default: av_log(avctx, AV_LOG_ERROR, "Unsupported bits_per_coded_sample: %d\n",
220
                    avctx->bits_per_coded_sample);
221
             return AVERROR_PATCHWELCOME;
222
    }
223
224
    s->bpp = avctx->bits_per_coded_sample >> 3;
225
226
    s->zstream.zalloc = Z_NULL;
227
    s->zstream.zfree = Z_NULL;
228
    s->zstream.opaque = Z_NULL;
229
    zret = inflateInit(&s->zstream);
230
    if (zret != Z_OK) {
231
        av_log(avctx, AV_LOG_ERROR, "Inflate init error: %d\n", zret);
232
        return AVERROR_EXTERNAL;
233
    }
234
235
    s->prev_frame = av_frame_alloc();
236
    if (!s->prev_frame)
237
        return AVERROR(ENOMEM);
238
239
    return 0;
240
}
241
242
static av_cold int decode_close(AVCodecContext *avctx)
243
{
244
    WCMVContext *s = avctx->priv_data;
245
246
    av_frame_free(&s->prev_frame);
247
    inflateEnd(&s->zstream);
248
249
    return 0;
250
}
251
252
AVCodec ff_wcmv_decoder = {
253
    .name             = "wcmv",
254
    .long_name        = NULL_IF_CONFIG_SMALL("WinCAM Motion Video"),
255
    .type             = AVMEDIA_TYPE_VIDEO,
256
    .id               = AV_CODEC_ID_WCMV,
257
    .priv_data_size   = sizeof(WCMVContext),
258
    .init             = decode_init,
259
    .close            = decode_close,
260
    .decode           = decode_frame,
261
    .capabilities     = AV_CODEC_CAP_DR1,
262
    .caps_internal    = FF_CODEC_CAP_INIT_THREADSAFE |
263
                        FF_CODEC_CAP_INIT_CLEANUP,
264
};