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