1/* 2 * Screenpresso decoder 3 * Copyright (C) 2015 Vittorio Giovara <vittorio.giovara@gmail.com> 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 * Screenpresso decoder 25 * 26 * Fourcc: SPV1 27 * 28 * Screenpresso simply horizontally flips and then deflates frames, 29 * alternating full pictures and deltas. Deltas are related to the currently 30 * rebuilt frame (not the reference), and since there is no coordinate system 31 * they contain exactly as many pixel as the keyframe. 32 * 33 * Supports: BGR0, BGR24, RGB555 34 */ 35 36#include <stdint.h> 37#include <string.h> 38#include <zlib.h> 39 40#include "libavutil/imgutils.h" 41#include "libavutil/internal.h" 42#include "libavutil/mem.h" 43 44#include "avcodec.h" 45#include "codec_internal.h" 46#include "internal.h" 47 48typedef struct ScreenpressoContext { 49 AVFrame *current; 50 51 /* zlib interaction */ 52 uint8_t *inflated_buf; 53 uLongf inflated_size; 54} ScreenpressoContext; 55 56static av_cold int screenpresso_close(AVCodecContext *avctx) 57{ 58 ScreenpressoContext *ctx = avctx->priv_data; 59 60 av_frame_free(&ctx->current); 61 av_freep(&ctx->inflated_buf); 62 63 return 0; 64} 65 66static av_cold int screenpresso_init(AVCodecContext *avctx) 67{ 68 ScreenpressoContext *ctx = avctx->priv_data; 69 70 /* These needs to be set to estimate uncompressed buffer */ 71 int ret = av_image_check_size(avctx->width, avctx->height, 0, avctx); 72 if (ret < 0) { 73 av_log(avctx, AV_LOG_ERROR, "Invalid image size %dx%d.\n", 74 avctx->width, avctx->height); 75 return ret; 76 } 77 78 /* Allocate current frame */ 79 ctx->current = av_frame_alloc(); 80 if (!ctx->current) 81 return AVERROR(ENOMEM); 82 83 /* Allocate maximum size possible, a full RGBA frame */ 84 ctx->inflated_size = avctx->width * avctx->height * 4; 85 ctx->inflated_buf = av_malloc(ctx->inflated_size); 86 if (!ctx->inflated_buf) 87 return AVERROR(ENOMEM); 88 89 return 0; 90} 91 92static void sum_delta_flipped(uint8_t *dst, int dst_linesize, 93 const uint8_t *src, int src_linesize, 94 int bytewidth, int height) 95{ 96 int i; 97 for (; height > 0; height--) { 98 const uint8_t *src1 = &src[(height - 1) * src_linesize]; 99 for (i = 0; i < bytewidth; i++) 100 dst[i] += src1[i]; 101 dst += dst_linesize; 102 } 103} 104 105static int screenpresso_decode_frame(AVCodecContext *avctx, AVFrame *frame, 106 int *got_frame, AVPacket *avpkt) 107{ 108 ScreenpressoContext *ctx = avctx->priv_data; 109 uLongf length = ctx->inflated_size; 110 int keyframe, component_size, src_linesize; 111 int ret; 112 113 /* Size check */ 114 if (avpkt->size < 3) { 115 av_log(avctx, AV_LOG_ERROR, "Packet too small (%d)\n", avpkt->size); 116 return AVERROR_INVALIDDATA; 117 } 118 119 /* Compression level (4 bits) and keyframe information (1 bit) */ 120 av_log(avctx, AV_LOG_DEBUG, "Compression level %d\n", avpkt->data[0] >> 4); 121 keyframe = avpkt->data[0] & 1; 122 123 /* Pixel size */ 124 component_size = ((avpkt->data[1] >> 2) & 0x03) + 1; 125 switch (component_size) { 126 case 2: 127 avctx->pix_fmt = AV_PIX_FMT_RGB555LE; 128 break; 129 case 3: 130 avctx->pix_fmt = AV_PIX_FMT_BGR24; 131 break; 132 case 4: 133 avctx->pix_fmt = AV_PIX_FMT_BGR0; 134 break; 135 default: 136 av_log(avctx, AV_LOG_ERROR, "Invalid bits per pixel value (%d)\n", 137 component_size); 138 return AVERROR_INVALIDDATA; 139 } 140 141 /* Inflate the frame after the 2 byte header */ 142 ret = uncompress(ctx->inflated_buf, &length, 143 avpkt->data + 2, avpkt->size - 2); 144 if (ret) { 145 av_log(avctx, AV_LOG_ERROR, "Deflate error %d.\n", ret); 146 return AVERROR_UNKNOWN; 147 } 148 149 ret = ff_reget_buffer(avctx, ctx->current, 0); 150 if (ret < 0) 151 return ret; 152 153 /* Codec has aligned strides */ 154 src_linesize = FFALIGN(avctx->width * component_size, 4); 155 156 /* When a keyframe is found, copy it (flipped) */ 157 if (keyframe) 158 av_image_copy_plane(ctx->current->data[0] + 159 ctx->current->linesize[0] * (avctx->height - 1), 160 -1 * ctx->current->linesize[0], 161 ctx->inflated_buf, src_linesize, 162 avctx->width * component_size, avctx->height); 163 /* Otherwise sum the delta on top of the current frame */ 164 else 165 sum_delta_flipped(ctx->current->data[0], ctx->current->linesize[0], 166 ctx->inflated_buf, src_linesize, 167 avctx->width * component_size, avctx->height); 168 169 /* Frame is ready to be output */ 170 ret = av_frame_ref(frame, ctx->current); 171 if (ret < 0) 172 return ret; 173 174 /* Usual properties */ 175 if (keyframe) { 176 frame->pict_type = AV_PICTURE_TYPE_I; 177 frame->key_frame = 1; 178 } else { 179 frame->pict_type = AV_PICTURE_TYPE_P; 180 } 181 *got_frame = 1; 182 183 return avpkt->size; 184} 185 186const FFCodec ff_screenpresso_decoder = { 187 .p.name = "screenpresso", 188 .p.long_name = NULL_IF_CONFIG_SMALL("Screenpresso"), 189 .p.type = AVMEDIA_TYPE_VIDEO, 190 .p.id = AV_CODEC_ID_SCREENPRESSO, 191 .init = screenpresso_init, 192 FF_CODEC_DECODE_CB(screenpresso_decode_frame), 193 .close = screenpresso_close, 194 .priv_data_size = sizeof(ScreenpressoContext), 195 .p.capabilities = AV_CODEC_CAP_DR1, 196 .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | 197 FF_CODEC_CAP_INIT_CLEANUP, 198}; 199