FFmpeg
bmvvideo.c
Go to the documentation of this file.
1 /*
2  * Discworld II BMV video decoder
3  * Copyright (c) 2011 Konstantin Shishkov
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * FFmpeg is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with FFmpeg; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
22 #include "libavutil/avassert.h"
23 #include "libavutil/common.h"
24 
25 #include "avcodec.h"
26 #include "bytestream.h"
27 #include "internal.h"
28 
29 enum BMVFlags{
30  BMV_NOP = 0,
34 
35  BMV_SCROLL = 0x04,
36  BMV_PALETTE = 0x08,
37  BMV_COMMAND = 0x10,
38  BMV_AUDIO = 0x20,
39  BMV_EXT = 0x40,
40  BMV_PRINT = 0x80
41 };
42 
43 #define SCREEN_WIDE 640
44 #define SCREEN_HIGH 429
45 
46 typedef struct BMVDecContext {
48 
50  uint32_t pal[256];
51  const uint8_t *stream;
53 
54 #define NEXT_BYTE(v) (v) = forward ? (v) + 1 : (v) - 1;
55 
56 static int decode_bmv_frame(const uint8_t *source, int src_len, uint8_t *frame, int frame_off)
57 {
58  unsigned val, saved_val = 0;
59  int tmplen = src_len;
60  const uint8_t *src, *source_end = source + src_len;
62  uint8_t *dst, *dst_end;
63  int len, mask;
64  int forward = (frame_off <= -SCREEN_WIDE) || (frame_off >= 0);
65  int read_two_nibbles, flag;
66  int advance_mode;
67  int mode = 0;
68  int i;
69 
70  if (src_len <= 0)
71  return AVERROR_INVALIDDATA;
72 
73  if (forward) {
74  src = source;
75  dst = frame;
76  dst_end = frame_end;
77  } else {
78  src = source + src_len - 1;
79  dst = frame_end - 1;
80  dst_end = frame - 1;
81  }
82  for (;;) {
83  int shift = 0;
84  flag = 0;
85 
86  /* The mode/len decoding is a bit strange:
87  * values are coded as variable-length codes with nibble units,
88  * code end is signalled by two top bits in the nibble being nonzero.
89  * And since data is bytepacked and we read two nibbles at a time,
90  * we may get a nibble belonging to the next code.
91  * Hence this convoluted loop.
92  */
93  if (!mode || (tmplen == 4)) {
94  if (src < source || src >= source_end)
95  return AVERROR_INVALIDDATA;
96  val = *src;
97  read_two_nibbles = 1;
98  } else {
99  val = saved_val;
100  read_two_nibbles = 0;
101  }
102  if (!(val & 0xC)) {
103  for (;;) {
104  if(shift>22)
105  return -1;
106  if (!read_two_nibbles) {
107  if (src < source || src >= source_end)
108  return AVERROR_INVALIDDATA;
109  shift += 2;
110  val |= (unsigned)*src << shift;
111  if (*src & 0xC)
112  break;
113  }
114  // two upper bits of the nibble is zero,
115  // so shift top nibble value down into their place
116  read_two_nibbles = 0;
117  shift += 2;
118  mask = (1 << shift) - 1;
119  val = ((val >> 2) & ~mask) | (val & mask);
120  NEXT_BYTE(src);
121  if ((val & (0xC << shift))) {
122  flag = 1;
123  break;
124  }
125  }
126  } else if (mode) {
127  flag = tmplen != 4;
128  }
129  if (flag) {
130  tmplen = 4;
131  } else {
132  saved_val = val >> (4 + shift);
133  tmplen = 0;
134  val &= (1 << (shift + 4)) - 1;
135  NEXT_BYTE(src);
136  }
137  advance_mode = val & 1;
138  len = (val >> 1) - 1;
139  av_assert0(len>0);
140  mode += 1 + advance_mode;
141  if (mode >= 4)
142  mode -= 3;
143  if (len <= 0 || FFABS(dst_end - dst) < len)
144  return AVERROR_INVALIDDATA;
145  switch (mode) {
146  case 1:
147  if (forward) {
148  if (dst - frame + SCREEN_WIDE < frame_off ||
149  dst - frame + SCREEN_WIDE + frame_off < 0 ||
150  frame_end - dst < frame_off + len ||
151  frame_end - dst < len)
152  return AVERROR_INVALIDDATA;
153  for (i = 0; i < len; i++)
154  dst[i] = dst[frame_off + i];
155  dst += len;
156  } else {
157  dst -= len;
158  if (dst - frame + SCREEN_WIDE < frame_off ||
159  dst - frame + SCREEN_WIDE + frame_off < 0 ||
160  frame_end - dst < frame_off + len ||
161  frame_end - dst < len)
162  return AVERROR_INVALIDDATA;
163  for (i = len - 1; i >= 0; i--)
164  dst[i] = dst[frame_off + i];
165  }
166  break;
167  case 2:
168  if (forward) {
169  if (source + src_len - src < len)
170  return AVERROR_INVALIDDATA;
171  memcpy(dst, src, len);
172  dst += len;
173  src += len;
174  } else {
175  if (src - source < len)
176  return AVERROR_INVALIDDATA;
177  dst -= len;
178  src -= len;
179  memcpy(dst, src, len);
180  }
181  break;
182  case 3:
183  val = forward ? dst[-1] : dst[1];
184  if (forward) {
185  memset(dst, val, len);
186  dst += len;
187  } else {
188  dst -= len;
189  memset(dst, val, len);
190  }
191  break;
192  }
193  if (dst == dst_end)
194  return 0;
195  }
196 }
197 
198 static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame,
199  AVPacket *pkt)
200 {
201  BMVDecContext * const c = avctx->priv_data;
202  AVFrame *frame = data;
203  int type, scr_off;
204  int i, ret;
205  uint8_t *srcptr, *outptr;
206 
207  c->stream = pkt->data;
208  type = bytestream_get_byte(&c->stream);
209  if (type & BMV_AUDIO) {
210  int blobs = bytestream_get_byte(&c->stream);
211  if (pkt->size < blobs * 65 + 2) {
212  av_log(avctx, AV_LOG_ERROR, "Audio data doesn't fit in frame\n");
213  return AVERROR_INVALIDDATA;
214  }
215  c->stream += blobs * 65;
216  }
217  if (type & BMV_COMMAND) {
218  int command_size = (type & BMV_PRINT) ? 8 : 10;
219  if (c->stream - pkt->data + command_size > pkt->size) {
220  av_log(avctx, AV_LOG_ERROR, "Command data doesn't fit in frame\n");
221  return AVERROR_INVALIDDATA;
222  }
223  c->stream += command_size;
224  }
225  if (type & BMV_PALETTE) {
226  if (c->stream - pkt->data > pkt->size - 768) {
227  av_log(avctx, AV_LOG_ERROR, "Palette data doesn't fit in frame\n");
228  return AVERROR_INVALIDDATA;
229  }
230  for (i = 0; i < 256; i++)
231  c->pal[i] = 0xFFU << 24 | bytestream_get_be24(&c->stream);
232  }
233  if (type & BMV_SCROLL) {
234  if (c->stream - pkt->data > pkt->size - 2) {
235  av_log(avctx, AV_LOG_ERROR, "Screen offset data doesn't fit in frame\n");
236  return AVERROR_INVALIDDATA;
237  }
238  scr_off = (int16_t)bytestream_get_le16(&c->stream);
239  } else if ((type & BMV_INTRA) == BMV_INTRA) {
240  scr_off = -640;
241  } else {
242  scr_off = 0;
243  }
244 
245  if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
246  return ret;
247 
248  if (decode_bmv_frame(c->stream, pkt->size - (c->stream - pkt->data), c->frame, scr_off)) {
249  av_log(avctx, AV_LOG_ERROR, "Error decoding frame data\n");
250  return AVERROR_INVALIDDATA;
251  }
252 
253  memcpy(frame->data[1], c->pal, AVPALETTE_SIZE);
254  frame->palette_has_changed = type & BMV_PALETTE;
255 
256  outptr = frame->data[0];
257  srcptr = c->frame;
258 
259  for (i = 0; i < avctx->height; i++) {
260  memcpy(outptr, srcptr, avctx->width);
261  srcptr += avctx->width;
262  outptr += frame->linesize[0];
263  }
264 
265  *got_frame = 1;
266 
267  /* always report that the buffer was completely consumed */
268  return pkt->size;
269 }
270 
272 {
273  BMVDecContext * const c = avctx->priv_data;
274 
275  c->avctx = avctx;
276  avctx->pix_fmt = AV_PIX_FMT_PAL8;
277 
278  if (avctx->width != SCREEN_WIDE || avctx->height != SCREEN_HIGH) {
279  av_log(avctx, AV_LOG_ERROR, "Invalid dimension %dx%d\n", avctx->width, avctx->height);
280  return AVERROR_INVALIDDATA;
281  }
282 
283  c->frame = c->frame_base + 640;
284 
285  return 0;
286 }
287 
289  .name = "bmv_video",
290  .long_name = NULL_IF_CONFIG_SMALL("Discworld II BMV video"),
291  .type = AVMEDIA_TYPE_VIDEO,
292  .id = AV_CODEC_ID_BMV_VIDEO,
293  .priv_data_size = sizeof(BMVDecContext),
294  .init = decode_init,
295  .decode = decode_frame,
296  .capabilities = AV_CODEC_CAP_DR1,
297  .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
298 };
const char const char void * val
Definition: avisynth_c.h:863
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:59
static int shift(int a, int b)
Definition: sonic.c:82
This structure describes decoded (raw) audio or video data.
Definition: frame.h:295
ptrdiff_t const GLvoid * data
Definition: opengl_enc.c:100
static av_cold int init(AVCodecContext *avctx)
Definition: avrndec.c:35
int size
Definition: avcodec.h:1481
enum AVPixelFormat pix_fmt
Pixel format, see AV_PIX_FMT_xxx.
Definition: avcodec.h:1778
const uint8_t * stream
Definition: bmvvideo.c:51
GLint GLenum type
Definition: opengl_enc.c:104
static AVPacket pkt
#define src
Definition: vp8dsp.c:254
AVCodec.
Definition: avcodec.h:3492
static void decode(AVCodecContext *dec_ctx, AVPacket *pkt, AVFrame *frame, FILE *outfile)
Definition: decode_audio.c:71
AVCodecContext * avctx
Definition: bmvvideo.c:47
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:37
static int decode_bmv_frame(const uint8_t *source, int src_len, uint8_t *frame, int frame_off)
Definition: bmvvideo.c:56
BMVFlags
Definition: bmvvideo.c:29
#define FF_CODEC_CAP_INIT_THREADSAFE
The codec does not modify any global variables in the init function, allowing to call the init functi...
Definition: internal.h:40
uint8_t
#define av_cold
Definition: attributes.h:82
8 bits with AV_PIX_FMT_RGB32 palette
Definition: pixfmt.h:77
#define AVPALETTE_SIZE
Definition: pixfmt.h:32
#define SCREEN_HIGH
Definition: bmvvideo.c:44
Undefined Behavior In the C some operations are like signed integer dereferencing freed accessing outside allocated Undefined Behavior must not occur in a C it is not safe even if the output of undefined operations is unused The unsafety may seem nit picking but Optimizing compilers have in fact optimized code on the assumption that no undefined Behavior occurs Optimizing code based on wrong assumptions can and has in some cases lead to effects beyond the output of computations The signed integer overflow problem in speed critical code Code which is highly optimized and works with signed integers sometimes has the problem that often the output of the computation does not c
Definition: undefined.txt:32
static av_cold int decode_init(AVCodecContext *avctx)
Definition: bmvvideo.c:271
uint8_t * data
Definition: avcodec.h:1480
these buffered frames must be flushed immediately if a new input produces new the filter must not call request_frame to get more It must just process the frame or queue it The task of requesting more frames is left to the filter s request_frame method or the application If a filter has several the filter must be ready for frames arriving randomly on any input any filter with several inputs will most likely require some kind of queuing mechanism It is perfectly acceptable to have a limited queue and to drop frames when the inputs are too unbalanced request_frame For filters that do not use the this method is called when a frame is wanted on an output For a source
#define av_log(a,...)
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:259
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
#define NEXT_BYTE(v)
Definition: bmvvideo.c:54
static const uint16_t mask[17]
Definition: lzw.c:38
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
Definition: internal.h:186
simple assert() macros that are a bit more flexible than ISO C assert().
const char * name
Name of the codec implementation.
Definition: avcodec.h:3499
static void frame_end(MpegEncContext *s)
int width
picture width / height.
Definition: avcodec.h:1741
#define FFABS(a)
Absolute value, Note, INT_MIN / INT64_MIN result in undefined behavior as they are not representable ...
Definition: common.h:72
Libavcodec external API header.
int linesize[AV_NUM_DATA_POINTERS]
For video, size in bytes of each picture line.
Definition: frame.h:326
main external API structure.
Definition: avcodec.h:1568
uint32_t pal[256]
Definition: bmvvideo.c:50
int ff_get_buffer(AVCodecContext *avctx, AVFrame *frame, int flags)
Get a buffer for a frame.
Definition: decode.c:1968
int palette_has_changed
Tell user application that palette has changed from previous frame.
Definition: frame.h:452
#define SCREEN_WIDE
Definition: bmvvideo.c:43
uint8_t * frame
Definition: bmvvideo.c:49
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition: frame.h:309
common internal api header.
common internal and external API header
#define flag(name)
Definition: cbs_av1.c:553
void * priv_data
Definition: avcodec.h:1595
int len
uint8_t frame_base[SCREEN_WIDE *(SCREEN_HIGH+1)]
Definition: bmvvideo.c:49
This structure stores compressed data.
Definition: avcodec.h:1457
mode
Use these values in ebur128_init (or&#39;ed).
Definition: ebur128.h:83
#define AV_CODEC_CAP_DR1
Codec uses get_buffer() for allocating buffers and supports custom allocators.
Definition: avcodec.h:984
AVCodec ff_bmv_video_decoder
Definition: bmvvideo.c:288
static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPacket *pkt)
Definition: bmvvideo.c:198