FFmpeg
rpza.c
Go to the documentation of this file.
1 /*
2  * Quicktime Video (RPZA) Video Decoder
3  * Copyright (C) 2003 The FFmpeg project
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 /**
23  * @file
24  * QT RPZA Video Decoder by Roberto Togni
25  * For more information about the RPZA format, visit:
26  * http://www.pcisys.net/~melanson/codecs/
27  *
28  * The RPZA decoder outputs RGB555 colorspace data.
29  *
30  * Note that this decoder reads big endian RGB555 pixel values from the
31  * bytestream, arranges them in the host's endian order, and outputs
32  * them to the final rendered map in the same host endian order. This is
33  * intended behavior as the libavcodec documentation states that RGB555
34  * pixels shall be stored in native CPU endianness.
35  */
36 
37 #include <stdint.h>
38 
39 #include "libavutil/internal.h"
40 #include "avcodec.h"
41 #include "bytestream.h"
42 #include "codec_internal.h"
43 #include "decode.h"
44 
45 typedef struct RpzaContext {
46 
49 
51 } RpzaContext;
52 
53 #define CHECK_BLOCK() \
54  if (total_blocks < 1) { \
55  av_log(s->avctx, AV_LOG_ERROR, \
56  "Block counter just went negative (this should not happen)\n"); \
57  return AVERROR_INVALIDDATA; \
58  } \
59 
60 #define ADVANCE_BLOCK() \
61  { \
62  pixel_ptr += 4; \
63  if (pixel_ptr >= width) \
64  { \
65  pixel_ptr = 0; \
66  row_ptr += stride * 4; \
67  } \
68  total_blocks--; \
69  }
70 
72 {
73  int width = s->avctx->width;
74  int stride, row_inc, ret;
75  int chunk_size;
76  uint16_t colorA = 0, colorB;
77  uint16_t color4[4];
78  uint16_t ta, tb;
79  uint16_t *pixels;
80 
81  int row_ptr = 0;
82  int pixel_ptr = 0;
83  int block_ptr;
84  int pixel_x, pixel_y;
85  int total_blocks;
86 
87  /* First byte is always 0xe1. Warn if it's different */
88  if (bytestream2_peek_byte(&s->gb) != 0xe1)
89  av_log(s->avctx, AV_LOG_ERROR, "First chunk byte is 0x%02x instead of 0xe1\n",
90  bytestream2_peek_byte(&s->gb));
91 
92  /* Get chunk size, ignoring first byte */
93  chunk_size = bytestream2_get_be32(&s->gb) & 0x00FFFFFF;
94 
95  /* If length mismatch use size from MOV file and try to decode anyway */
96  if (chunk_size != bytestream2_get_bytes_left(&s->gb) + 4)
97  av_log(s->avctx, AV_LOG_WARNING,
98  "MOV chunk size %d != encoded chunk size %d\n",
99  chunk_size,
100  bytestream2_get_bytes_left(&s->gb) + 4
101  );
102 
103  /* Number of 4x4 blocks in frame. */
104  total_blocks = ((s->avctx->width + 3) / 4) * ((s->avctx->height + 3) / 4);
105 
106  if (total_blocks / 32 > bytestream2_get_bytes_left(&s->gb))
107  return AVERROR_INVALIDDATA;
108 
109  if ((ret = ff_reget_buffer(s->avctx, s->frame, 0)) < 0)
110  return ret;
111  pixels = (uint16_t *)s->frame->data[0];
112  stride = s->frame->linesize[0] / 2;
113  row_inc = stride - 4;
114 
115  /* Process chunk data */
116  while (bytestream2_get_bytes_left(&s->gb)) {
117  uint8_t opcode = bytestream2_get_byte(&s->gb); /* Get opcode */
118 
119  int n_blocks = (opcode & 0x1f) + 1; /* Extract block counter from opcode */
120 
121  /* If opcode MSbit is 0, we need more data to decide what to do */
122  if ((opcode & 0x80) == 0) {
123  colorA = (opcode << 8) | bytestream2_get_byte(&s->gb);
124  opcode = 0;
125  if ((bytestream2_peek_byte(&s->gb) & 0x80) != 0) {
126  /* Must behave as opcode 110xxxxx, using colorA computed
127  * above. Use fake opcode 0x20 to enter switch block at
128  * the right place */
129  opcode = 0x20;
130  n_blocks = 1;
131  }
132  }
133 
134  n_blocks = FFMIN(n_blocks, total_blocks);
135 
136  switch (opcode & 0xe0) {
137 
138  /* Skip blocks */
139  case 0x80:
140  while (n_blocks--) {
141  CHECK_BLOCK();
142  ADVANCE_BLOCK();
143  }
144  break;
145 
146  /* Fill blocks with one color */
147  case 0xa0:
148  colorA = bytestream2_get_be16(&s->gb);
149  while (n_blocks--) {
150  CHECK_BLOCK();
151  block_ptr = row_ptr + pixel_ptr;
152  for (pixel_y = 0; pixel_y < 4; pixel_y++) {
153  for (pixel_x = 0; pixel_x < 4; pixel_x++){
154  pixels[block_ptr] = colorA;
155  block_ptr++;
156  }
157  block_ptr += row_inc;
158  }
159  ADVANCE_BLOCK();
160  }
161  break;
162 
163  /* Fill blocks with 4 colors */
164  case 0xc0:
165  colorA = bytestream2_get_be16(&s->gb);
166  case 0x20:
167  colorB = bytestream2_get_be16(&s->gb);
168 
169  /* sort out the colors */
170  color4[0] = colorB;
171  color4[1] = 0;
172  color4[2] = 0;
173  color4[3] = colorA;
174 
175  /* red components */
176  ta = (colorA >> 10) & 0x1F;
177  tb = (colorB >> 10) & 0x1F;
178  color4[1] |= ((11 * ta + 21 * tb) >> 5) << 10;
179  color4[2] |= ((21 * ta + 11 * tb) >> 5) << 10;
180 
181  /* green components */
182  ta = (colorA >> 5) & 0x1F;
183  tb = (colorB >> 5) & 0x1F;
184  color4[1] |= ((11 * ta + 21 * tb) >> 5) << 5;
185  color4[2] |= ((21 * ta + 11 * tb) >> 5) << 5;
186 
187  /* blue components */
188  ta = colorA & 0x1F;
189  tb = colorB & 0x1F;
190  color4[1] |= ((11 * ta + 21 * tb) >> 5);
191  color4[2] |= ((21 * ta + 11 * tb) >> 5);
192 
193  if (bytestream2_get_bytes_left(&s->gb) < n_blocks * 4)
194  return AVERROR_INVALIDDATA;
195  while (n_blocks--) {
196  CHECK_BLOCK();
197  block_ptr = row_ptr + pixel_ptr;
198  for (pixel_y = 0; pixel_y < 4; pixel_y++) {
199  uint8_t index = bytestream2_get_byteu(&s->gb);
200  for (pixel_x = 0; pixel_x < 4; pixel_x++){
201  uint8_t idx = (index >> (2 * (3 - pixel_x))) & 0x03;
202  pixels[block_ptr] = color4[idx];
203  block_ptr++;
204  }
205  block_ptr += row_inc;
206  }
207  ADVANCE_BLOCK();
208  }
209  break;
210 
211  /* Fill block with 16 colors */
212  case 0x00:
213  if (bytestream2_get_bytes_left(&s->gb) < 30)
214  return AVERROR_INVALIDDATA;
215  CHECK_BLOCK();
216  block_ptr = row_ptr + pixel_ptr;
217  for (pixel_y = 0; pixel_y < 4; pixel_y++) {
218  for (pixel_x = 0; pixel_x < 4; pixel_x++){
219  /* We already have color of upper left pixel */
220  if ((pixel_y != 0) || (pixel_x != 0))
221  colorA = bytestream2_get_be16u(&s->gb);
222  pixels[block_ptr] = colorA;
223  block_ptr++;
224  }
225  block_ptr += row_inc;
226  }
227  ADVANCE_BLOCK();
228  break;
229 
230  /* Unknown opcode */
231  default:
232  av_log(s->avctx, AV_LOG_ERROR, "Unknown opcode %d in rpza chunk."
233  " Skip remaining %d bytes of chunk data.\n", opcode,
235  return AVERROR_INVALIDDATA;
236  } /* Opcode switch */
237  }
238 
239  return 0;
240 }
241 
243 {
244  RpzaContext *s = avctx->priv_data;
245 
246  s->avctx = avctx;
247  avctx->pix_fmt = AV_PIX_FMT_RGB555;
248 
249  s->frame = av_frame_alloc();
250  if (!s->frame)
251  return AVERROR(ENOMEM);
252 
253  return 0;
254 }
255 
256 static int rpza_decode_frame(AVCodecContext *avctx, AVFrame *rframe,
257  int *got_frame, AVPacket *avpkt)
258 {
259  RpzaContext *s = avctx->priv_data;
260  int ret;
261 
262  bytestream2_init(&s->gb, avpkt->data, avpkt->size);
263 
265  if (ret < 0)
266  return ret;
267 
268  if ((ret = av_frame_ref(rframe, s->frame)) < 0)
269  return ret;
270 
271  *got_frame = 1;
272 
273  /* always report that the buffer was completely consumed */
274  return avpkt->size;
275 }
276 
278 {
279  RpzaContext *s = avctx->priv_data;
280 
281  av_frame_free(&s->frame);
282 
283  return 0;
284 }
285 
287  .p.name = "rpza",
288  CODEC_LONG_NAME("QuickTime video (RPZA)"),
289  .p.type = AVMEDIA_TYPE_VIDEO,
290  .p.id = AV_CODEC_ID_RPZA,
291  .priv_data_size = sizeof(RpzaContext),
293  .close = rpza_decode_end,
295  .p.capabilities = AV_CODEC_CAP_DR1,
296 };
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:186
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
GetByteContext
Definition: bytestream.h:33
av_frame_free
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:100
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:340
AVPacket::data
uint8_t * data
Definition: packet.h:491
RpzaContext
Definition: rpza.c:45
FFCodec
Definition: codec_internal.h:127
ff_rpza_decoder
const FFCodec ff_rpza_decoder
Definition: rpza.c:286
rpza_decode_end
static av_cold int rpza_decode_end(AVCodecContext *avctx)
Definition: rpza.c:277
FFCodec::p
AVCodec p
The public AVCodec.
Definition: codec_internal.h:131
rpza_decode_frame
static int rpza_decode_frame(AVCodecContext *avctx, AVFrame *rframe, int *got_frame, AVPacket *avpkt)
Definition: rpza.c:256
av_frame_alloc
AVFrame * av_frame_alloc(void)
Allocate an AVFrame and set its fields to default values.
Definition: frame.c:88
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:180
av_cold
#define av_cold
Definition: attributes.h:90
width
#define width
FF_CODEC_DECODE_CB
#define FF_CODEC_DECODE_CB(func)
Definition: codec_internal.h:306
s
#define s(width, name)
Definition: cbs_vp9.c:198
init
int(* init)(AVBSFContext *ctx)
Definition: dts2pts_bsf.c:365
rpza_decode_stream
static int rpza_decode_stream(RpzaContext *s)
Definition: rpza.c:71
decode.h
CODEC_LONG_NAME
#define CODEC_LONG_NAME(str)
Definition: codec_internal.h:272
RpzaContext::frame
AVFrame * frame
Definition: rpza.c:48
index
int index
Definition: gxfenc.c:89
bytestream2_get_bytes_left
static av_always_inline int bytestream2_get_bytes_left(GetByteContext *g)
Definition: bytestream.h:158
RpzaContext::gb
GetByteContext gb
Definition: rpza.c:50
AV_CODEC_CAP_DR1
#define AV_CODEC_CAP_DR1
Codec uses get_buffer() or get_encode_buffer() for allocating buffers and supports custom allocators.
Definition: codec.h:52
AVPacket::size
int size
Definition: packet.h:492
av_frame_ref
int av_frame_ref(AVFrame *dst, const AVFrame *src)
Set up a new reference to the data described by the source frame.
Definition: frame.c:361
codec_internal.h
ADVANCE_BLOCK
#define ADVANCE_BLOCK()
Definition: rpza.c:60
CHECK_BLOCK
#define CHECK_BLOCK()
Definition: rpza.c:53
internal.h
AV_PIX_FMT_RGB555
#define AV_PIX_FMT_RGB555
Definition: pixfmt.h:456
FFMIN
#define FFMIN(a, b)
Definition: macros.h:49
tb
#define tb
Definition: regdef.h:68
AVCodec::name
const char * name
Name of the codec implementation.
Definition: codec.h:194
AVCodecContext::pix_fmt
enum AVPixelFormat pix_fmt
Pixel format, see AV_PIX_FMT_xxx.
Definition: avcodec.h:658
avcodec.h
stride
#define stride
Definition: h264pred_template.c:537
ff_reget_buffer
int ff_reget_buffer(AVCodecContext *avctx, AVFrame *frame, int flags)
Identical in function to ff_get_buffer(), except it reuses the existing buffer if available.
Definition: decode.c:1735
ret
ret
Definition: filter_design.txt:187
RpzaContext::avctx
AVCodecContext * avctx
Definition: rpza.c:47
AVCodecContext
main external API structure.
Definition: avcodec.h:441
AVMEDIA_TYPE_VIDEO
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:201
AV_CODEC_ID_RPZA
@ AV_CODEC_ID_RPZA
Definition: codec_id.h:94
rpza_decode_init
static av_cold int rpza_decode_init(AVCodecContext *avctx)
Definition: rpza.c:242
AVPacket
This structure stores compressed data.
Definition: packet.h:468
AVCodecContext::priv_data
void * priv_data
Definition: avcodec.h:468
bytestream.h
bytestream2_init
static av_always_inline void bytestream2_init(GetByteContext *g, const uint8_t *buf, int buf_size)
Definition: bytestream.h:137
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
ta
#define ta
Definition: regdef.h:67
AVERROR_INVALIDDATA
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:61