Imported Upstream version 6.1
[platform/upstream/ffmpeg.git] / libavcodec / wcmv.c
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
25 #include "libavutil/imgutils.h"
26
27 #include "avcodec.h"
28 #include "bytestream.h"
29 #include "codec_internal.h"
30 #include "decode.h"
31 #include "zlib_wrapper.h"
32
33 #include <zlib.h>
34
35 typedef struct WCMVContext {
36     int         bpp;
37     FFZStream   zstream;
38     AVFrame    *prev_frame;
39     uint8_t     block_data[65536*8];
40 } WCMVContext;
41
42 static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
43                         int *got_frame, AVPacket *avpkt)
44 {
45     WCMVContext *s = avctx->priv_data;
46     z_stream *const zstream = &s->zstream.zstream;
47     int skip, blocks, zret, ret, intra = 0, flags = 0, bpp = s->bpp;
48     GetByteContext gb;
49     uint8_t *dst;
50
51     ret = inflateReset(zstream);
52     if (ret != Z_OK) {
53         av_log(avctx, AV_LOG_ERROR, "Inflate reset error: %d\n", ret);
54         return AVERROR_EXTERNAL;
55     }
56
57     bytestream2_init(&gb, avpkt->data, avpkt->size);
58     blocks = bytestream2_get_le16(&gb);
59     if (!blocks)
60         flags |= FF_REGET_BUFFER_FLAG_READONLY;
61
62     if ((ret = ff_reget_buffer(avctx, s->prev_frame, flags)) < 0)
63         return ret;
64
65     if (blocks > 5) {
66         GetByteContext bgb;
67         int x = 0, size;
68
69         if (blocks * 8 >= 0xFFFF)
70             size = bytestream2_get_le24(&gb);
71         else if (blocks * 8 >= 0xFF)
72             size = bytestream2_get_le16(&gb);
73         else
74             size = bytestream2_get_byte(&gb);
75
76         skip = bytestream2_tell(&gb);
77         if (size > avpkt->size - skip)
78             return AVERROR_INVALIDDATA;
79
80         zstream->next_in   = avpkt->data + skip;
81         zstream->avail_in  = size;
82         zstream->next_out  = s->block_data;
83         zstream->avail_out = sizeof(s->block_data);
84
85         zret = inflate(zstream, Z_FINISH);
86         if (zret != Z_STREAM_END) {
87             av_log(avctx, AV_LOG_ERROR,
88                    "Inflate failed with return code: %d.\n", zret);
89             return AVERROR_INVALIDDATA;
90         }
91
92         ret = inflateReset(zstream);
93         if (ret != Z_OK) {
94             av_log(avctx, AV_LOG_ERROR, "Inflate reset error: %d\n", ret);
95             return AVERROR_EXTERNAL;
96         }
97
98         bytestream2_skip(&gb, size);
99         bytestream2_init(&bgb, s->block_data, blocks * 8);
100
101         for (int i = 0; i < blocks; i++) {
102             int w, h;
103
104             bytestream2_skip(&bgb, 4);
105             w = bytestream2_get_le16(&bgb);
106             h = bytestream2_get_le16(&bgb);
107             if (x + bpp * (int64_t)w * h > INT_MAX)
108                 return AVERROR_INVALIDDATA;
109             x += bpp * w * h;
110         }
111
112         if (x >= 0xFFFF)
113             bytestream2_skip(&gb, 3);
114         else if (x >= 0xFF)
115             bytestream2_skip(&gb, 2);
116         else
117             bytestream2_skip(&gb, 1);
118
119         skip = bytestream2_tell(&gb);
120
121         zstream->next_in  = avpkt->data + skip;
122         zstream->avail_in = avpkt->size - skip;
123
124         bytestream2_init(&gb, s->block_data, blocks * 8);
125     } else if (blocks) {
126         int x = 0;
127
128         bytestream2_seek(&gb, 2, SEEK_SET);
129
130         for (int i = 0; i < blocks; i++) {
131             int w, h;
132
133             bytestream2_skip(&gb, 4);
134             w = bytestream2_get_le16(&gb);
135             h = bytestream2_get_le16(&gb);
136             if (x + bpp * (int64_t)w * h > INT_MAX)
137                 return AVERROR_INVALIDDATA;
138             x += bpp * w * h;
139         }
140
141         if (x >= 0xFFFF)
142             bytestream2_skip(&gb, 3);
143         else if (x >= 0xFF)
144             bytestream2_skip(&gb, 2);
145         else
146             bytestream2_skip(&gb, 1);
147
148         skip = bytestream2_tell(&gb);
149
150         zstream->next_in  = avpkt->data + skip;
151         zstream->avail_in = avpkt->size - skip;
152
153         bytestream2_seek(&gb, 2, SEEK_SET);
154     }
155
156     if (bytestream2_get_bytes_left(&gb) < 8LL * blocks)
157         return AVERROR_INVALIDDATA;
158
159     if (!avctx->frame_num) {
160         ptrdiff_t linesize[4] = { s->prev_frame->linesize[0], 0, 0, 0 };
161         av_image_fill_black(s->prev_frame->data, linesize, avctx->pix_fmt, 0,
162                             avctx->width, avctx->height);
163     }
164
165     for (int block = 0; block < blocks; block++) {
166         int x, y, w, h;
167
168         x = bytestream2_get_le16(&gb);
169         y = bytestream2_get_le16(&gb);
170         w = bytestream2_get_le16(&gb);
171         h = bytestream2_get_le16(&gb);
172
173         if (blocks == 1 && x == 0 && y == 0 && w == avctx->width && h == avctx->height)
174             intra = 1;
175
176         if (x + w > avctx->width || y + h > avctx->height)
177             return AVERROR_INVALIDDATA;
178
179         if (w > avctx->width || h > avctx->height)
180             return AVERROR_INVALIDDATA;
181
182         dst = s->prev_frame->data[0] + (avctx->height - y - 1) * s->prev_frame->linesize[0] + x * bpp;
183         for (int i = 0; i < h; i++) {
184             zstream->next_out  = dst;
185             zstream->avail_out = w * bpp;
186
187             zret = inflate(zstream, Z_SYNC_FLUSH);
188             if (zret != Z_OK && zret != Z_STREAM_END) {
189                 av_log(avctx, AV_LOG_ERROR,
190                        "Inflate failed with return code: %d.\n", zret);
191                 return AVERROR_INVALIDDATA;
192             }
193
194             dst -= s->prev_frame->linesize[0];
195         }
196     }
197
198     if (intra)
199         s->prev_frame->flags |= AV_FRAME_FLAG_KEY;
200     else
201         s->prev_frame->flags &= ~AV_FRAME_FLAG_KEY;
202     s->prev_frame->pict_type = intra ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P;
203
204     if ((ret = av_frame_ref(frame, s->prev_frame)) < 0)
205         return ret;
206
207     *got_frame = 1;
208
209     return avpkt->size;
210 }
211
212 static av_cold int decode_init(AVCodecContext *avctx)
213 {
214     WCMVContext *s = avctx->priv_data;
215
216     switch (avctx->bits_per_coded_sample) {
217     case 16: avctx->pix_fmt = AV_PIX_FMT_RGB565LE; break;
218     case 24: avctx->pix_fmt = AV_PIX_FMT_BGR24;  break;
219     case 32: avctx->pix_fmt = AV_PIX_FMT_BGRA;   break;
220     default: av_log(avctx, AV_LOG_ERROR, "Unsupported bits_per_coded_sample: %d\n",
221                     avctx->bits_per_coded_sample);
222              return AVERROR_PATCHWELCOME;
223     }
224
225     s->bpp = avctx->bits_per_coded_sample >> 3;
226
227     s->prev_frame = av_frame_alloc();
228     if (!s->prev_frame)
229         return AVERROR(ENOMEM);
230
231     return ff_inflate_init(&s->zstream, avctx);
232 }
233
234 static av_cold int decode_close(AVCodecContext *avctx)
235 {
236     WCMVContext *s = avctx->priv_data;
237
238     av_frame_free(&s->prev_frame);
239     ff_inflate_end(&s->zstream);
240
241     return 0;
242 }
243
244 const FFCodec ff_wcmv_decoder = {
245     .p.name           = "wcmv",
246     CODEC_LONG_NAME("WinCAM Motion Video"),
247     .p.type           = AVMEDIA_TYPE_VIDEO,
248     .p.id             = AV_CODEC_ID_WCMV,
249     .priv_data_size   = sizeof(WCMVContext),
250     .init             = decode_init,
251     .close            = decode_close,
252     FF_CODEC_DECODE_CB(decode_frame),
253     .p.capabilities   = AV_CODEC_CAP_DR1,
254     .caps_internal    = FF_CODEC_CAP_INIT_CLEANUP,
255 };