yading@10: /* yading@10: * Psygnosis YOP decoder yading@10: * yading@10: * Copyright (C) 2010 Mohamed Naufal Basheer yading@10: * derived from the code by yading@10: * Copyright (C) 2009 Thomas P. Higdon yading@10: * yading@10: * This file is part of FFmpeg. yading@10: * yading@10: * FFmpeg is free software; you can redistribute it and/or yading@10: * modify it under the terms of the GNU Lesser General Public yading@10: * License as published by the Free Software Foundation; either yading@10: * version 2.1 of the License, or (at your option) any later version. yading@10: * yading@10: * FFmpeg is distributed in the hope that it will be useful, yading@10: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@10: * Lesser General Public License for more details. yading@10: * yading@10: * You should have received a copy of the GNU Lesser General Public yading@10: * License along with FFmpeg; if not, write to the Free Software yading@10: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@10: */ yading@10: yading@10: #include "libavutil/intreadwrite.h" yading@10: #include "libavutil/imgutils.h" yading@10: yading@10: #include "avcodec.h" yading@10: #include "get_bits.h" yading@10: #include "internal.h" yading@10: yading@10: typedef struct YopDecContext { yading@10: AVCodecContext *avctx; yading@10: AVFrame *frame; yading@10: yading@10: int num_pal_colors; yading@10: int first_color[2]; yading@10: int frame_data_length; yading@10: yading@10: uint8_t *low_nibble; yading@10: uint8_t *srcptr; yading@10: uint8_t *src_end; yading@10: uint8_t *dstptr; yading@10: uint8_t *dstbuf; yading@10: } YopDecContext; yading@10: yading@10: // These tables are taken directly from: yading@10: // http://wiki.multimedia.cx/index.php?title=Psygnosis_YOP yading@10: yading@10: /** yading@10: * Lookup table for painting macroblocks. Bytes 0-2 of each entry contain yading@10: * the macroblock positions to be painted (taken as (0, B0, B1, B2)). yading@10: * Byte 3 contains the number of bytes consumed on the input, yading@10: * equal to max(bytes 0-2) + 1. yading@10: */ yading@10: static const uint8_t paint_lut[15][4] = yading@10: {{1, 2, 3, 4}, {1, 2, 0, 3}, yading@10: {1, 2, 1, 3}, {1, 2, 2, 3}, yading@10: {1, 0, 2, 3}, {1, 0, 0, 2}, yading@10: {1, 0, 1, 2}, {1, 1, 2, 3}, yading@10: {0, 1, 2, 3}, {0, 1, 0, 2}, yading@10: {1, 1, 0, 2}, {0, 1, 1, 2}, yading@10: {0, 0, 1, 2}, {0, 0, 0, 1}, yading@10: {1, 1, 1, 2}, yading@10: }; yading@10: yading@10: /** yading@10: * Lookup table for copying macroblocks. Each entry contains the respective yading@10: * x and y pixel offset for the copy source. yading@10: */ yading@10: static const int8_t motion_vector[16][2] = yading@10: {{-4, -4}, {-2, -4}, yading@10: { 0, -4}, { 2, -4}, yading@10: {-4, -2}, {-4, 0}, yading@10: {-3, -3}, {-1, -3}, yading@10: { 1, -3}, { 3, -3}, yading@10: {-3, -1}, {-2, -2}, yading@10: { 0, -2}, { 2, -2}, yading@10: { 4, -2}, {-2, 0}, yading@10: }; yading@10: yading@10: static av_cold int yop_decode_close(AVCodecContext *avctx) yading@10: { yading@10: YopDecContext *s = avctx->priv_data; yading@10: yading@10: av_frame_free(&s->frame); yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: static av_cold int yop_decode_init(AVCodecContext *avctx) yading@10: { yading@10: YopDecContext *s = avctx->priv_data; yading@10: s->avctx = avctx; yading@10: yading@10: if (avctx->width & 1 || avctx->height & 1 || yading@10: av_image_check_size(avctx->width, avctx->height, 0, avctx) < 0) { yading@10: av_log(avctx, AV_LOG_ERROR, "YOP has invalid dimensions\n"); yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: yading@10: if (avctx->extradata_size < 3) { yading@10: av_log(avctx, AV_LOG_ERROR, "Missing or incomplete extradata.\n"); yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: yading@10: avctx->pix_fmt = AV_PIX_FMT_PAL8; yading@10: yading@10: s->num_pal_colors = avctx->extradata[0]; yading@10: s->first_color[0] = avctx->extradata[1]; yading@10: s->first_color[1] = avctx->extradata[2]; yading@10: yading@10: if (s->num_pal_colors + s->first_color[0] > 256 || yading@10: s->num_pal_colors + s->first_color[1] > 256) { yading@10: av_log(avctx, AV_LOG_ERROR, yading@10: "Palette parameters invalid, header probably corrupt\n"); yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: yading@10: s->frame = av_frame_alloc(); yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: /** yading@10: * Paint a macroblock using the pattern in paint_lut. yading@10: * @param s codec context yading@10: * @param tag the tag that was in the nibble yading@10: */ yading@10: static int yop_paint_block(YopDecContext *s, int linesize, int tag) yading@10: { yading@10: if (s->src_end - s->srcptr < paint_lut[tag][3]) { yading@10: av_log(s->avctx, AV_LOG_ERROR, "Packet too small.\n"); yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: yading@10: s->dstptr[0] = s->srcptr[0]; yading@10: s->dstptr[1] = s->srcptr[paint_lut[tag][0]]; yading@10: s->dstptr[linesize] = s->srcptr[paint_lut[tag][1]]; yading@10: s->dstptr[linesize + 1] = s->srcptr[paint_lut[tag][2]]; yading@10: yading@10: // The number of src bytes consumed is in the last part of the lut entry. yading@10: s->srcptr += paint_lut[tag][3]; yading@10: return 0; yading@10: } yading@10: yading@10: /** yading@10: * Copy a previously painted macroblock to the current_block. yading@10: * @param copy_tag the tag that was in the nibble yading@10: */ yading@10: static int yop_copy_previous_block(YopDecContext *s, int linesize, int copy_tag) yading@10: { yading@10: uint8_t *bufptr; yading@10: yading@10: // Calculate position for the copy source yading@10: bufptr = s->dstptr + motion_vector[copy_tag][0] + yading@10: linesize * motion_vector[copy_tag][1]; yading@10: if (bufptr < s->dstbuf) { yading@10: av_log(s->avctx, AV_LOG_ERROR, "File probably corrupt\n"); yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: yading@10: s->dstptr[0] = bufptr[0]; yading@10: s->dstptr[1] = bufptr[1]; yading@10: s->dstptr[linesize] = bufptr[linesize]; yading@10: s->dstptr[linesize + 1] = bufptr[linesize + 1]; yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: /** yading@10: * Return the next nibble in sequence, consuming a new byte on the input yading@10: * only if necessary. yading@10: */ yading@10: static uint8_t yop_get_next_nibble(YopDecContext *s) yading@10: { yading@10: int ret; yading@10: yading@10: if (s->low_nibble) { yading@10: ret = *s->low_nibble & 0xf; yading@10: s->low_nibble = NULL; yading@10: }else { yading@10: s->low_nibble = s->srcptr++; yading@10: ret = *s->low_nibble >> 4; yading@10: } yading@10: return ret; yading@10: } yading@10: yading@10: static int yop_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, yading@10: AVPacket *avpkt) yading@10: { yading@10: YopDecContext *s = avctx->priv_data; yading@10: AVFrame *frame = s->frame; yading@10: int tag, firstcolor, is_odd_frame; yading@10: int ret, i, x, y; yading@10: uint32_t *palette; yading@10: yading@10: if (avpkt->size < 4 + 3 * s->num_pal_colors) { yading@10: av_log(avctx, AV_LOG_ERROR, "Packet too small.\n"); yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: yading@10: if ((ret = ff_reget_buffer(avctx, frame)) < 0) yading@10: return ret; yading@10: yading@10: if (!avctx->frame_number) yading@10: memset(frame->data[1], 0, AVPALETTE_SIZE); yading@10: yading@10: s->dstbuf = frame->data[0]; yading@10: s->dstptr = frame->data[0]; yading@10: s->srcptr = avpkt->data + 4; yading@10: s->src_end = avpkt->data + avpkt->size; yading@10: s->low_nibble = NULL; yading@10: yading@10: is_odd_frame = avpkt->data[0]; yading@10: if(is_odd_frame>1){ yading@10: av_log(avctx, AV_LOG_ERROR, "frame is too odd %d\n", is_odd_frame); yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: firstcolor = s->first_color[is_odd_frame]; yading@10: palette = (uint32_t *)frame->data[1]; yading@10: yading@10: for (i = 0; i < s->num_pal_colors; i++, s->srcptr += 3) { yading@10: palette[i + firstcolor] = (s->srcptr[0] << 18) | yading@10: (s->srcptr[1] << 10) | yading@10: (s->srcptr[2] << 2); yading@10: palette[i + firstcolor] |= 0xFFU << 24 | yading@10: (palette[i + firstcolor] >> 6) & 0x30303; yading@10: } yading@10: yading@10: frame->palette_has_changed = 1; yading@10: yading@10: for (y = 0; y < avctx->height; y += 2) { yading@10: for (x = 0; x < avctx->width; x += 2) { yading@10: if (s->srcptr - avpkt->data >= avpkt->size) { yading@10: av_log(avctx, AV_LOG_ERROR, "Packet too small.\n"); yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: yading@10: tag = yop_get_next_nibble(s); yading@10: yading@10: if (tag != 0xf) { yading@10: ret = yop_paint_block(s, frame->linesize[0], tag); yading@10: if (ret < 0) yading@10: return ret; yading@10: } else { yading@10: tag = yop_get_next_nibble(s); yading@10: ret = yop_copy_previous_block(s, frame->linesize[0], tag); yading@10: if (ret < 0) yading@10: return ret; yading@10: } yading@10: s->dstptr += 2; yading@10: } yading@10: s->dstptr += 2*frame->linesize[0] - x; yading@10: } yading@10: yading@10: if ((ret = av_frame_ref(data, s->frame)) < 0) yading@10: return ret; yading@10: yading@10: *got_frame = 1; yading@10: return avpkt->size; yading@10: } yading@10: yading@10: AVCodec ff_yop_decoder = { yading@10: .name = "yop", yading@10: .type = AVMEDIA_TYPE_VIDEO, yading@10: .id = AV_CODEC_ID_YOP, yading@10: .priv_data_size = sizeof(YopDecContext), yading@10: .init = yop_decode_init, yading@10: .close = yop_decode_close, yading@10: .decode = yop_decode_frame, yading@10: .long_name = NULL_IF_CONFIG_SMALL("Psygnosis YOP Video"), yading@10: };