2 * Psygnosis YOP decoder
4 * Copyright (C) 2010 Mohamed Naufal Basheer <naufal11@gmail.com>
5 * derived from the code by
6 * Copyright (C) 2009 Thomas P. Higdon <thomas.p.higdon@gmail.com>
8 * This file is part of FFmpeg.
10 * FFmpeg is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * FFmpeg is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with FFmpeg; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 #include "libavutil/intreadwrite.h"
26 #include "libavutil/imgutils.h"
32 typedef struct YopDecContext
{
33 AVCodecContext
*avctx
;
38 int frame_data_length
;
47 // These tables are taken directly from:
48 // http://wiki.multimedia.cx/index.php?title=Psygnosis_YOP
51 * Lookup table for painting macroblocks. Bytes 0-2 of each entry contain
52 * the macroblock positions to be painted (taken as (0, B0, B1, B2)).
53 * Byte 3 contains the number of bytes consumed on the input,
54 * equal to max(bytes 0-2) + 1.
56 static const uint8_t paint_lut
[15][4] =
57 {{1, 2, 3, 4}, {1, 2, 0, 3},
58 {1, 2, 1, 3}, {1, 2, 2, 3},
59 {1, 0, 2, 3}, {1, 0, 0, 2},
60 {1, 0, 1, 2}, {1, 1, 2, 3},
61 {0, 1, 2, 3}, {0, 1, 0, 2},
62 {1, 1, 0, 2}, {0, 1, 1, 2},
63 {0, 0, 1, 2}, {0, 0, 0, 1},
68 * Lookup table for copying macroblocks. Each entry contains the respective
69 * x and y pixel offset for the copy source.
71 static const int8_t motion_vector
[16][2] =
82 static av_cold
int yop_decode_close(AVCodecContext
*avctx
)
84 YopDecContext
*s
= avctx
->priv_data
;
86 av_frame_free(&s
->frame
);
91 static av_cold
int yop_decode_init(AVCodecContext
*avctx
)
93 YopDecContext
*s
= avctx
->priv_data
;
96 if (avctx
->width
& 1 || avctx
->height
& 1 ||
97 av_image_check_size(avctx
->width
, avctx
->height
, 0, avctx
) < 0) {
98 av_log(avctx
, AV_LOG_ERROR
, "YOP has invalid dimensions\n");
99 return AVERROR_INVALIDDATA
;
102 if (avctx
->extradata_size
< 3) {
103 av_log(avctx
, AV_LOG_ERROR
, "Missing or incomplete extradata.\n");
104 return AVERROR_INVALIDDATA
;
107 avctx
->pix_fmt
= AV_PIX_FMT_PAL8
;
109 s
->num_pal_colors
= avctx
->extradata
[0];
110 s
->first_color
[0] = avctx
->extradata
[1];
111 s
->first_color
[1] = avctx
->extradata
[2];
113 if (s
->num_pal_colors
+ s
->first_color
[0] > 256 ||
114 s
->num_pal_colors
+ s
->first_color
[1] > 256) {
115 av_log(avctx
, AV_LOG_ERROR
,
116 "Palette parameters invalid, header probably corrupt\n");
117 return AVERROR_INVALIDDATA
;
120 s
->frame
= av_frame_alloc();
122 return AVERROR(ENOMEM
);
128 * Paint a macroblock using the pattern in paint_lut.
129 * @param s codec context
130 * @param tag the tag that was in the nibble
132 static int yop_paint_block(YopDecContext
*s
, int linesize
, int tag
)
134 if (s
->src_end
- s
->srcptr
< paint_lut
[tag
][3]) {
135 av_log(s
->avctx
, AV_LOG_ERROR
, "Packet too small.\n");
136 return AVERROR_INVALIDDATA
;
139 s
->dstptr
[0] = s
->srcptr
[0];
140 s
->dstptr
[1] = s
->srcptr
[paint_lut
[tag
][0]];
141 s
->dstptr
[linesize
] = s
->srcptr
[paint_lut
[tag
][1]];
142 s
->dstptr
[linesize
+ 1] = s
->srcptr
[paint_lut
[tag
][2]];
144 // The number of src bytes consumed is in the last part of the lut entry.
145 s
->srcptr
+= paint_lut
[tag
][3];
150 * Copy a previously painted macroblock to the current_block.
151 * @param copy_tag the tag that was in the nibble
153 static int yop_copy_previous_block(YopDecContext
*s
, int linesize
, int copy_tag
)
157 // Calculate position for the copy source
158 bufptr
= s
->dstptr
+ motion_vector
[copy_tag
][0] +
159 linesize
* motion_vector
[copy_tag
][1];
160 if (bufptr
< s
->dstbuf
) {
161 av_log(s
->avctx
, AV_LOG_ERROR
, "File probably corrupt\n");
162 return AVERROR_INVALIDDATA
;
165 s
->dstptr
[0] = bufptr
[0];
166 s
->dstptr
[1] = bufptr
[1];
167 s
->dstptr
[linesize
] = bufptr
[linesize
];
168 s
->dstptr
[linesize
+ 1] = bufptr
[linesize
+ 1];
174 * Return the next nibble in sequence, consuming a new byte on the input
177 static uint8_t yop_get_next_nibble(YopDecContext
*s
)
182 ret
= *s
->low_nibble
& 0xf;
183 s
->low_nibble
= NULL
;
185 s
->low_nibble
= s
->srcptr
++;
186 ret
= *s
->low_nibble
>> 4;
191 static int yop_decode_frame(AVCodecContext
*avctx
, void *data
, int *got_frame
,
194 YopDecContext
*s
= avctx
->priv_data
;
195 AVFrame
*frame
= s
->frame
;
196 int tag
, firstcolor
, is_odd_frame
;
200 if (avpkt
->size
< 4 + 3 * s
->num_pal_colors
) {
201 av_log(avctx
, AV_LOG_ERROR
, "Packet too small.\n");
202 return AVERROR_INVALIDDATA
;
205 if ((ret
= ff_reget_buffer(avctx
, frame
)) < 0)
208 if (!avctx
->frame_number
)
209 memset(frame
->data
[1], 0, AVPALETTE_SIZE
);
211 s
->dstbuf
= frame
->data
[0];
212 s
->dstptr
= frame
->data
[0];
213 s
->srcptr
= avpkt
->data
+ 4;
214 s
->src_end
= avpkt
->data
+ avpkt
->size
;
215 s
->low_nibble
= NULL
;
217 is_odd_frame
= avpkt
->data
[0];
219 av_log(avctx
, AV_LOG_ERROR
, "frame is too odd %d\n", is_odd_frame
);
220 return AVERROR_INVALIDDATA
;
222 firstcolor
= s
->first_color
[is_odd_frame
];
223 palette
= (uint32_t *)frame
->data
[1];
225 for (i
= 0; i
< s
->num_pal_colors
; i
++, s
->srcptr
+= 3) {
226 palette
[i
+ firstcolor
] = (s
->srcptr
[0] << 18) |
227 (s
->srcptr
[1] << 10) |
229 palette
[i
+ firstcolor
] |= 0xFFU
<< 24 |
230 (palette
[i
+ firstcolor
] >> 6) & 0x30303;
233 frame
->palette_has_changed
= 1;
235 for (y
= 0; y
< avctx
->height
; y
+= 2) {
236 for (x
= 0; x
< avctx
->width
; x
+= 2) {
237 if (s
->srcptr
- avpkt
->data
>= avpkt
->size
) {
238 av_log(avctx
, AV_LOG_ERROR
, "Packet too small.\n");
239 return AVERROR_INVALIDDATA
;
242 tag
= yop_get_next_nibble(s
);
245 ret
= yop_paint_block(s
, frame
->linesize
[0], tag
);
249 tag
= yop_get_next_nibble(s
);
250 ret
= yop_copy_previous_block(s
, frame
->linesize
[0], tag
);
256 s
->dstptr
+= 2*frame
->linesize
[0] - x
;
259 if ((ret
= av_frame_ref(data
, s
->frame
)) < 0)
266 AVCodec ff_yop_decoder
= {
268 .long_name
= NULL_IF_CONFIG_SMALL("Psygnosis YOP Video"),
269 .type
= AVMEDIA_TYPE_VIDEO
,
270 .id
= AV_CODEC_ID_YOP
,
271 .priv_data_size
= sizeof(YopDecContext
),
272 .init
= yop_decode_init
,
273 .close
= yop_decode_close
,
274 .decode
= yop_decode_frame
,