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 };
AVCodec
AVCodec.
Definition: avcodec.h:3481
FF_CODEC_CAP_INIT_THREADSAFE
#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
init
static av_cold int init(AVCodecContext *avctx)
Definition: avrndec.c:35
BMV_DELTA
@ BMV_DELTA
Definition: bmvvideo.c:32
decode_frame
static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPacket *pkt)
Definition: bmvvideo.c:198
BMV_EXT
@ BMV_EXT
Definition: bmvvideo.c:39
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:295
internal.h
AVPacket::data
uint8_t * data
Definition: avcodec.h:1477
data
const char data[16]
Definition: mxf.c:91
type
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf type
Definition: writing_filters.txt:86
src
#define src
Definition: vp8dsp.c:254
decode_bmv_frame
static int decode_bmv_frame(const uint8_t *source, int src_len, uint8_t *frame, int frame_off)
Definition: bmvvideo.c:56
BMVFlags
BMVFlags
Definition: bmvvideo.c:29
avassert.h
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
av_cold
#define av_cold
Definition: attributes.h:84
mask
static const uint16_t mask[17]
Definition: lzw.c:38
decode
static void decode(AVCodecContext *dec_ctx, AVPacket *pkt, AVFrame *frame, FILE *outfile)
Definition: decode_audio.c:42
av_assert0
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:37
decode_init
static av_cold int decode_init(AVCodecContext *avctx)
Definition: bmvvideo.c:271
BMVDecContext::stream
const uint8_t * stream
Definition: bmvvideo.c:51
BMV_INTRA
@ BMV_INTRA
Definition: bmvvideo.c:33
ff_bmv_video_decoder
AVCodec ff_bmv_video_decoder
Definition: bmvvideo.c:288
FFABS
#define FFABS(a)
Absolute value, Note, INT_MIN / INT64_MIN result in undefined behavior as they are not representable ...
Definition: common.h:72
BMVDecContext::pal
uint32_t pal[256]
Definition: bmvvideo.c:50
BMVDecContext::frame
uint8_t * frame
Definition: bmvvideo.c:49
AVPALETTE_SIZE
#define AVPALETTE_SIZE
Definition: pixfmt.h:32
BMV_PALETTE
@ BMV_PALETTE
Definition: bmvvideo.c:36
BMV_PRINT
@ BMV_PRINT
Definition: bmvvideo.c:40
c
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
source
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
Definition: filter_design.txt:255
BMV_COMMAND
@ BMV_COMMAND
Definition: bmvvideo.c:37
ff_get_buffer
int ff_get_buffer(AVCodecContext *avctx, AVFrame *frame, int flags)
Get a buffer for a frame.
Definition: decode.c:1965
AV_CODEC_CAP_DR1
#define AV_CODEC_CAP_DR1
Codec uses get_buffer() for allocating buffers and supports custom allocators.
Definition: avcodec.h:981
AVPacket::size
int size
Definition: avcodec.h:1478
NULL_IF_CONFIG_SMALL
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:188
val
const char const char void * val
Definition: avisynth_c.h:863
BMVDecContext::frame_base
uint8_t frame_base[SCREEN_WIDE *(SCREEN_HIGH+1)]
Definition: bmvvideo.c:49
flag
#define flag(name)
Definition: cbs_av1.c:557
SCREEN_WIDE
#define SCREEN_WIDE
Definition: bmvvideo.c:43
BMV_SCROLL
@ BMV_SCROLL
Definition: bmvvideo.c:35
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:259
common.h
uint8_t
uint8_t
Definition: audio_convert.c:194
BMVDecContext
Definition: bmvvideo.c:46
AVCodec::name
const char * name
Name of the codec implementation.
Definition: avcodec.h:3488
len
int len
Definition: vorbis_enc_data.h:452
AVCodecContext::height
int height
Definition: avcodec.h:1738
AVCodecContext::pix_fmt
enum AVPixelFormat pix_fmt
Pixel format, see AV_PIX_FMT_xxx.
Definition: avcodec.h:1775
avcodec.h
AV_PIX_FMT_PAL8
@ AV_PIX_FMT_PAL8
8 bits with AV_PIX_FMT_RGB32 palette
Definition: pixfmt.h:77
ret
ret
Definition: filter_design.txt:187
AV_CODEC_ID_BMV_VIDEO
@ AV_CODEC_ID_BMV_VIDEO
Definition: avcodec.h:372
frame
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 it should directly call filter_frame on the corresponding output For a if there are queued frames already one of these frames should be pushed If the filter should request a frame on one of its repeatedly until at least one frame has been pushed Return or at least make progress towards producing a frame
Definition: filter_design.txt:264
AVCodecContext
main external API structure.
Definition: avcodec.h:1565
pkt
static AVPacket pkt
Definition: demuxing_decoding.c:54
mode
mode
Definition: ebur128.h:83
SCREEN_HIGH
#define SCREEN_HIGH
Definition: bmvvideo.c:44
frame_end
static void frame_end(MpegEncContext *s)
Definition: mpegvideo_enc.c:1680
shift
static int shift(int a, int b)
Definition: sonic.c:82
AVMEDIA_TYPE_VIDEO
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:201
BMV_NOP
@ BMV_NOP
Definition: bmvvideo.c:30
BMV_AUDIO
@ BMV_AUDIO
Definition: bmvvideo.c:38
AVCodecContext::priv_data
void * priv_data
Definition: avcodec.h:1592
AVPacket
This structure stores compressed data.
Definition: avcodec.h:1454
NEXT_BYTE
#define NEXT_BYTE(v)
Definition: bmvvideo.c:54
AVCodecContext::width
int width
picture width / height.
Definition: avcodec.h:1738
bytestream.h
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:28
AVERROR_INVALIDDATA
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:59
BMV_END
@ BMV_END
Definition: bmvvideo.c:31
BMVDecContext::avctx
AVCodecContext * avctx
Definition: bmvvideo.c:47