Quoting Vittorio Giovara (2015-09-27 16:39:54)
> Signed-off-by: Vittorio Giovara <[email protected]>
> ---
> In this version:
> - added pkt size check
> - reworded comments a little
> - included fate test
>
> Big thanks to nevcariel for providing the test file.
> Vittorio
>
> Changelog | 1 +
> configure | 1 +
> doc/general.texi | 1 +
> libavcodec/Makefile | 1 +
> libavcodec/allcodecs.c | 1 +
> libavcodec/avcodec.h | 1 +
> libavcodec/codec_desc.c | 7 ++
> libavcodec/screenpresso.c | 190
> ++++++++++++++++++++++++++++++++++++++++++++
> libavcodec/version.h | 2 +-
> libavformat/riff.c | 1 +
> tests/fate/video.mak | 3 +
> tests/ref/fate/screenpresso | 5 ++
> 12 files changed, 213 insertions(+), 1 deletion(-)
> create mode 100644 libavcodec/screenpresso.c
> create mode 100644 tests/ref/fate/screenpresso
>
> diff --git a/Changelog b/Changelog
> index 461c5b1..696c881 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -43,6 +43,7 @@ version <next>:
> - Intel QSV-accelerated MPEG-2 video and HEVC decoding
> - Support DNx100 (1440x1080@8)
> - DXV decoding
> +- Screenpresso SPV1 decoding
>
>
> version 11:
> diff --git a/configure b/configure
> index 852b931..ba827f9 100755
> --- a/configure
> +++ b/configure
> @@ -1962,6 +1962,7 @@ rv20_decoder_select="error_resilience h263_decoder
> h263dsp mpeg_er"
> rv20_encoder_select="h263_encoder"
> rv30_decoder_select="error_resilience golomb h264chroma h264pred h264qpel
> mpeg_er mpegvideo rv34dsp videodsp"
> rv40_decoder_select="error_resilience golomb h264chroma h264pred h264qpel
> mpeg_er mpegvideo rv34dsp videodsp"
> +screenpresso_decoder_deps="zlib"
> shorten_decoder_select="golomb"
> sipr_decoder_select="lsp"
> sp5x_decoder_select="mjpeg_decoder"
> diff --git a/doc/general.texi b/doc/general.texi
> index 7cd7693..6e43269 100644
> --- a/doc/general.texi
> +++ b/doc/general.texi
> @@ -409,6 +409,7 @@ library:
> @item RTP @tab X @tab X
> @item RTSP @tab X @tab X
> @item SAP @tab X @tab X
> +@item Screenpresso @tab @tab X
> @item SDP @tab @tab X
> @item Sega FILM/CPK @tab @tab X
> @tab Used in many Sega Saturn console games.
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index f163a79..632a8a6 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -385,6 +385,7 @@ OBJS-$(CONFIG_RV30_DECODER) += rv30.o rv34.o
> rv30dsp.o
> OBJS-$(CONFIG_RV40_DECODER) += rv40.o rv34.o rv40dsp.o
> OBJS-$(CONFIG_S302M_DECODER) += s302m.o
> OBJS-$(CONFIG_SANM_DECODER) += sanm.o
> +OBJS-$(CONFIG_SCREENPRESSO_DECODER) += screenpresso.o
> OBJS-$(CONFIG_SGI_DECODER) += sgidec.o
> OBJS-$(CONFIG_SGI_ENCODER) += sgienc.o rle.o
> OBJS-$(CONFIG_SGIRLE_DECODER) += sgirledec.o
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index 49f5a70..2d8474a 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -249,6 +249,7 @@ void avcodec_register_all(void)
> REGISTER_DECODER(RV40, rv40);
> REGISTER_DECODER(S302M, s302m);
> REGISTER_DECODER(SANM, sanm);
> + REGISTER_DECODER(SCREENPRESSO, screenpresso);
> REGISTER_ENCDEC (SGI, sgi);
> REGISTER_DECODER(SGIRLE, sgirle);
> REGISTER_DECODER(SMACKER, smacker);
> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
> index 8c635d7..de59320 100644
> --- a/libavcodec/avcodec.h
> +++ b/libavcodec/avcodec.h
> @@ -299,6 +299,7 @@ enum AVCodecID {
> AV_CODEC_ID_HAP,
> AV_CODEC_ID_DDS,
> AV_CODEC_ID_DXV,
> + AV_CODEC_ID_SCREENPRESSO,
>
> /* various PCM "codecs" */
> AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the
> start of audio codecs
> diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
> index c1be40b..d28fe9a 100644
> --- a/libavcodec/codec_desc.c
> +++ b/libavcodec/codec_desc.c
> @@ -1162,6 +1162,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
> .long_name = NULL_IF_CONFIG_SMALL("Resolume DXV"),
> .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
> },
> + {
> + .id = AV_CODEC_ID_SCREENPRESSO,
> + .type = AVMEDIA_TYPE_VIDEO,
> + .name = "screenpresso",
> + .long_name = NULL_IF_CONFIG_SMALL("Screenpresso"),
> + .props = AV_CODEC_PROP_LOSSLESS,
> + },
>
> /* image codecs */
> {
> diff --git a/libavcodec/screenpresso.c b/libavcodec/screenpresso.c
> new file mode 100644
> index 0000000..bb869d5
> --- /dev/null
> +++ b/libavcodec/screenpresso.c
> @@ -0,0 +1,190 @@
> +/*
> + * Screenpresso decoder
> + * Copyright (C) 2015 Vittorio Giovara <[email protected]>
> + *
> + * This file is part of Libav.
> + *
> + * Libav is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * Libav is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with Libav; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
> USA
> + */
> +
> +/**
> + * @file
> + * Screenpresso decoder
> + *
> + * Fourcc: SPV1
> + *
> + * Screenpresso simply horizontally flips and then deflates frames,
> + * alternating full pictures and deltas. Deltas are related to the currently
> + * rebuilt frame (not the reference), and since there is no coordinate system
> + * they contain exactly as many pixel as the keyframe.
> + *
> + * Supports: BGR24
> + */
> +
> +#include <stdint.h>
> +#include <string.h>
> +#include <zlib.h>
> +
> +#include "libavutil/imgutils.h"
> +#include "libavutil/internal.h"
> +
> +#include "avcodec.h"
> +#include "internal.h"
> +
> +typedef struct ScreenpressoContext {
> + AVFrame *current;
> +
> + /* zlib interation */
> + uint8_t *deflatebuffer;
> + uLongf deflatelen;
> +} ScreenpressoContext;
> +
> +static av_cold int screenpresso_close(AVCodecContext *avctx)
> +{
> + ScreenpressoContext *ctx = avctx->priv_data;
> +
> + av_frame_free(&ctx->current);
> + av_freep(&ctx->deflatebuffer);
> +
> + return 0;
> +}
> +
> +static av_cold int screenpresso_init(AVCodecContext *avctx)
> +{
> + ScreenpressoContext *ctx = avctx->priv_data;
> +
> + /* These needs to be set to estimate uncompressed buffer */
> + int ret = av_image_check_size(avctx->width, avctx->height, 0, avctx);
> + if (ret < 0) {
> + av_log(avctx, AV_LOG_ERROR, "Invalid image size %dx%d.\n",
> + avctx->width, avctx->height);
> + return ret;
> + }
> +
> + /* Allocate current frame */
> + ctx->current = av_frame_alloc();
> + if (!ctx->current)
> + return AVERROR(ENOMEM);
> +
> + avctx->pix_fmt = AV_PIX_FMT_BGR24;
> +
> + return 0;
> +}
> +
> +static void copy_plane_flipped(uint8_t *dst, int dst_linesize,
> + const uint8_t *src, int src_linesize,
> + int bytewidth, int height)
> +{
> + for (; height > 0; height--) {
> + memcpy(dst, src + (height - 1) * src_linesize, bytewidth);
> + dst += dst_linesize;
> + }
> +}
> +
> +static void copy_delta_flipped(uint8_t *dst, int dst_linesize,
> + const uint8_t *src, int src_linesize,
> + int bytewidth, int height)
> +{
> + int i;
> + for (; height > 0; height--) {
> + for (i = 0; i < bytewidth; i++)
> + dst[i] += src[(height - 1) * src_linesize + i];
> + dst += dst_linesize;
> + }
> +}
> +
> +static int screenpresso_decode_frame(AVCodecContext *avctx, void *data,
> + int *got_frame, AVPacket *avpkt)
> +{
> + ScreenpressoContext *ctx = avctx->priv_data;
> + AVFrame *frame = data;
> + int keyframe;
> + int ret;
> +
> + /* Size check */
> + if (avpkt->size < 3) {
> + av_log(avctx, AV_LOG_ERROR, "Packet too small (%d)\n", avpkt->size);
> + return AVERROR_INVALIDDATA;
> + }
> +
> + /* Basic sanity check, but not really harmful */
> + if ((avpkt->data[0] != 0x73 && avpkt->data[0] != 0x72) ||
> + avpkt->data[1] != 8) { // bpp probably
> + av_log(avctx, AV_LOG_WARNING, "Unknown header 0x%02X%02X\n",
> + avpkt->data[0], avpkt->data[1]);
> + }
> + keyframe = (avpkt->data[0] == 0x73);
> +
> + /* Resize deflate buffer and frame on resolution change */
> + if (ctx->deflatelen != avctx->width * avctx->height * 3) {
> + ret = ff_get_buffer(avctx, ctx->current, 0);
You're keeping this buffer for later use, so the flag should not be 0.
> + if (ret < 0)
> + return ret;
> +
> + ctx->deflatelen = avctx->width * avctx->height * 3;
> + ret = av_reallocp(&ctx->deflatebuffer, ctx->deflatelen);
> + if (ret < 0)
> + return ret;
You should probably set deflatelen to 0 on fail, otherwise BadStuff
could happen.
> + }
> +
> + /* Inflate the frame after the 2 byte header */
> + ret = uncompress(ctx->deflatebuffer, &ctx->deflatelen,
> + avpkt->data + 2, avpkt->size - 2);
> + if (ret) {
> + av_log(avctx, AV_LOG_ERROR, "Deflate error %d.\n", ret);
> + return AVERROR_UNKNOWN;
> + }
> +
> + /* When a keyframe is found, copy it as it contains the whole picture */
> + if (keyframe)
> + copy_plane_flipped(ctx->current->data[0], ctx->current->linesize[0],
> + ctx->deflatebuffer, avctx->width * 3,
> + avctx->width * 3, avctx->height);
Why not do what other flipped codecs do -- multiply linesize by -1 and
advance data[0] appropriately.
--
Anton Khirnov
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel