From patchwork Fri May 29 16:55:14 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anamitra Ghorui X-Patchwork-Id: 19971 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 4F9AE44BB03 for ; Fri, 29 May 2020 19:56:12 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 2310B68AD3D; Fri, 29 May 2020 19:56:12 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail.teknik.io (mail.teknik.io [5.79.72.163]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id A8E2868A0D3 for ; Fri, 29 May 2020 19:56:05 +0300 (EEST) dkim-signature: v=1; a=rsa-sha256; d=teknik.io; s=dkim; c=relaxed/relaxed; q=dns/txt; h=From:Subject:Date:Message-ID:To:CC; bh=A5Plbzmqk3gngc6tCrt2jv6gQKlSckvL1yLPogRV7IY=; b=eLAWu6pjyxNnuZebIi0Z62HRt7DLGzgtVRLX80rPi4YfFeDua0or8uy9zrijxr29Y/O+BAZbED5fly6ppD60NGBwlaqa/EvN6bKemph2oy5p2pV05xjDNHPR7Mf2H/K7Yujfut6wC7MT0sygdS/qYJURh/G1crNoircizUY/3LKRSlCVnQKtcKW3Ql4rqxerr+7DcQCD55pLuRfpwzqdodWESnS1Of70vmE5inHfd5RJq7fn14nh+V8bP4 Y8VF3ItQsLdqIbvT9vnYmPznM8vjN91FC83UkahvSoxroO2dTHezExAl4N5fvbDwGPVSiaRDWf0PqUZbPf17OXlcJCbQ== Received: from localhost.localdomain (Unknown [49.38.215.76]) by mail.teknik.io with ESMTPSA (version=TLSv1.2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128) ; Fri, 29 May 2020 09:56:04 -0700 From: Anamitra Ghorui To: ffmpeg-devel@ffmpeg.org Date: Fri, 29 May 2020 22:25:14 +0530 Message-Id: <20200529165514.12604-1-aghorui@teknik.io> X-Mailer: git-send-email 2.17.1 Subject: [FFmpeg-devel] [WIP][RFC] [PATCH] FLIF16 Decoder X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Anamitra Ghorui MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Hello, This is a partial implementation of the decoder of the FLIF16 image format [1]. A large amount of this code has been adapted from the reference implementation of the FLIF16 (de)coder, written in C++ [2]. The main reason I am posting this patch is to get comments on the method the rangecoder used in FLIF is written. Since this format has no way to predetermine the size of the rangecoded bitstream, we have to deal with arbitrary packet sizes in the decoder. This means that the rangecoder's renormalisation loop has to be written in such a manner that the moment we run out of bytes in the AVPacket, the decoder is able to exit, and is able to start back at the same state it was in, in the next call of the decode function. There are higher order functions for reading integer types from the bitstream (uni_int, nz_int etc.[3]), that depend on the "get" function of the rangecoder, which adds another layer of complexity. Plus there are conditional statements dependent on the encoded values. I ultimately decided on a switch statement and a segment variable that will be incremented to go to the next segment in the bitstream, with the whole switch statement being in an infinite loop. The last switch case or segment jumps out of this loop. All state variables are kept in the decoder and rangecoder contexts. This whole solution however looks quite cumbersome. Is there a better way to do this? Please look here [4] for the same functions if they were written in the "normal" manner, i.e. if packet size is non-arbitrary. Please ignore the following files and functions in the code: * flif16_transform.(c|h): These files are being handled by Kartik K. Khullar , and are not ready yet. * ff_flif16_rac_read_nz_int, ff_flif16_rac_read_gnz_int: These functions are still being written from the old to the new format. * There are several printf statements in the code and are meant solely for debugging. These will be removed later. (An entry in libavformat needs to be made in the future, but for now the entry is in img2.c which works but is incorrect) Regards, Anamitra [1]: https://flif.info/spec.html [2]: https://github.com/FLIF-hub/FLIF [3]: https://flif.info/spec.html#_uniform_symbol_coding [4]: https://github.com/daujerrine/ffmpeg/blob/master/libavcodec/flif16_rangecoder.h Signed-off-by: Anamitra Ghorui --- Changelog | 1 + configure | 2 + libavcodec/Makefile | 6 + libavcodec/allcodecs.c | 2 + libavcodec/avcodec.h | 1 + libavcodec/codec_desc.c | 7 + libavcodec/flif16.h | 37 +++ libavcodec/flif16_parser.c | 189 ++++++++++++++++ libavcodec/flif16_rangecoder.c | 134 +++++++++++ libavcodec/flif16_rangecoder.h | 395 +++++++++++++++++++++++++++++++++ libavcodec/flif16_transform.c | 247 +++++++++++++++++++++ libavcodec/flif16_transform.h | 60 +++++ libavcodec/flif16dec.c | 372 +++++++++++++++++++++++++++++++ libavcodec/flif16enc.c | 25 +++ libavcodec/parsers.c | 1 + libavformat/img2.c | 1 + 16 files changed, 1480 insertions(+) create mode 100644 libavcodec/flif16.h create mode 100644 libavcodec/flif16_parser.c create mode 100644 libavcodec/flif16_rangecoder.c create mode 100644 libavcodec/flif16_rangecoder.h create mode 100644 libavcodec/flif16_transform.c create mode 100644 libavcodec/flif16_transform.h create mode 100644 libavcodec/flif16dec.c create mode 100644 libavcodec/flif16enc.c diff --git a/Changelog b/Changelog index cb310a3abc..4f88dbaadb 100644 --- a/Changelog +++ b/Changelog @@ -43,6 +43,7 @@ version : - Rayman 2 ADPCM decoder - Rayman 2 APM demuxer - cas video filter +- FLIF16 decoder version 4.2: diff --git a/configure b/configure index 06e3a7b2a8..872ee750e0 100755 --- a/configure +++ b/configure @@ -2709,6 +2709,8 @@ ffvhuff_encoder_select="huffyuv_encoder" fic_decoder_select="golomb" flac_decoder_select="flacdsp" flac_encoder_select="bswapdsp flacdsp lpc" +flif16_decoder_select="flif16dec" +flif16_encoder_select="flif16enc" flashsv2_decoder_deps="zlib" flashsv2_encoder_deps="zlib" flashsv_decoder_deps="zlib" diff --git a/libavcodec/Makefile b/libavcodec/Makefile index f1c032b456..2b86341a75 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -76,6 +76,7 @@ OBJS-$(CONFIG_EXIF) += exif.o tiff_common.o OBJS-$(CONFIG_FAANDCT) += faandct.o OBJS-$(CONFIG_FAANIDCT) += faanidct.o OBJS-$(CONFIG_FDCTDSP) += fdctdsp.o jfdctfst.o jfdctint.o +OBJS-$(CONFIG_FLIF16_RANGECODER) += flif16_rangecoder.o FFT-OBJS-$(CONFIG_HARDCODED_TABLES) += cos_tables.o cos_fixed_tables.o OBJS-$(CONFIG_FFT) += avfft.o fft_fixed.o fft_float.o \ fft_fixed_32.o fft_init_table.o \ @@ -314,11 +315,15 @@ OBJS-$(CONFIG_FITS_DECODER) += fitsdec.o fits.o OBJS-$(CONFIG_FITS_ENCODER) += fitsenc.o OBJS-$(CONFIG_FLAC_DECODER) += flacdec.o flacdata.o flac.o OBJS-$(CONFIG_FLAC_ENCODER) += flacenc.o flacdata.o flac.o vorbis_data.o +OBJS-$(CONFIG_FLIF16_DECODER) += flif16dec.o flif16_rangecoder.o +OBJS-$(CONFIG_FLIF16_ENCODER) += flif16enc.o flif16_rangecoder.o OBJS-$(CONFIG_FLASHSV_DECODER) += flashsv.o OBJS-$(CONFIG_FLASHSV_ENCODER) += flashsvenc.o OBJS-$(CONFIG_FLASHSV2_ENCODER) += flashsv2enc.o OBJS-$(CONFIG_FLASHSV2_DECODER) += flashsv.o OBJS-$(CONFIG_FLIC_DECODER) += flicvideo.o +OBJS-$(CONFIG_FLIF16_DECODER) += flif16dec.o +OBJS-$(CONFIG_FLIF16_ENCODER) += flif16enc.o OBJS-$(CONFIG_FMVC_DECODER) += fmvc.o OBJS-$(CONFIG_FOURXM_DECODER) += 4xm.o OBJS-$(CONFIG_FRAPS_DECODER) += fraps.o @@ -1046,6 +1051,7 @@ OBJS-$(CONFIG_DVD_NAV_PARSER) += dvd_nav_parser.o OBJS-$(CONFIG_DVDSUB_PARSER) += dvdsub_parser.o OBJS-$(CONFIG_FLAC_PARSER) += flac_parser.o flacdata.o flac.o \ vorbis_data.o +OBJS-$(CONFIG_FLIF16_PARSER) += flif16_parser.o OBJS-$(CONFIG_G723_1_PARSER) += g723_1_parser.o OBJS-$(CONFIG_G729_PARSER) += g729_parser.o OBJS-$(CONFIG_GIF_PARSER) += gif_parser.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 674995df72..79a2d89ee8 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -119,6 +119,8 @@ extern AVCodec ff_flashsv_decoder; extern AVCodec ff_flashsv2_encoder; extern AVCodec ff_flashsv2_decoder; extern AVCodec ff_flic_decoder; +extern AVCodec ff_flif16_decoder; +extern AVCodec ff_flif16_encoder; extern AVCodec ff_flv_encoder; extern AVCodec ff_flv_decoder; extern AVCodec ff_fmvc_decoder; diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 894a9e5565..4e2af45dd0 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -461,6 +461,7 @@ enum AVCodecID { AV_CODEC_ID_MVDV, AV_CODEC_ID_MVHA, AV_CODEC_ID_CDTOONS, + AV_CODEC_ID_FLIF16, /* 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 52178e7afe..993ec450c1 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -1754,6 +1754,13 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("CDToons video"), .props = AV_CODEC_PROP_LOSSLESS, }, + { + .id = AV_CODEC_ID_FLIF16, + .type = AVMEDIA_TYPE_VIDEO, + .name = "flif16", + .long_name = NULL_IF_CONFIG_SMALL("FLIF16 (Free Lossless Image Format)"), + .props = AV_CODEC_PROP_LOSSLESS, + }, /* various PCM "codecs" */ { diff --git a/libavcodec/flif16.h b/libavcodec/flif16.h new file mode 100644 index 0000000000..8206f82a64 --- /dev/null +++ b/libavcodec/flif16.h @@ -0,0 +1,37 @@ +/* + * FLIF16 Image Format Definitions + * Copyright (c) 2020 Anamitra Ghorui + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * FLIF16 format definitions and functions. + */ + +#ifndef AVCODEC_FLIF16_H +#define AVCODEC_FLIF16_H + +#include +#include +// Remove this +#define __PLN__ printf("At: [%s] %s, %d\n", __func__, __FILE__, __LINE__); +static const uint8_t flif16_header[4] = "FLIF"; +#define FF_FLIF16_VARINT_APPEND(a,x) a = (a << 7) | (uint64_t) (x & 127) + +#endif /* AVCODEC_FLIF16_H */ diff --git a/libavcodec/flif16_parser.c b/libavcodec/flif16_parser.c new file mode 100644 index 0000000000..b56c330443 --- /dev/null +++ b/libavcodec/flif16_parser.c @@ -0,0 +1,189 @@ +/* + * FLIF16 parser + * Copyright (c) 2020 Anamitra Ghorui + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + /** + * @file + * FLIF16 parser + */ + +#include "flif16.h" +#include "parser.h" +#include "libavutil/avassert.h" +#include "libavutil/bswap.h" + +#include //remove +#include +#include + +typedef enum FLIF16ParseStates { + FLIF16_HEADER = 1, + FLIF16_METADATA, + FLIF16_BITSTREAM, + FLIF16_CHECKSUM, + FLIF16_VARINT +} FLIF16ParseStates; + +typedef struct FLIF16ParseContext { + ParseContext pc; + int state; ///< The section of the file the parser is in currently. + unsigned int index; ///< An index based on the current state. + uint8_t animated; ///< Is image animated or not + uint8_t varint; ///< Number of varints to process in sequence + uint64_t width; + uint64_t height; + uint64_t frames; + uint64_t meta; ///< Size of a meta chunk + uint64_t count; +} FLIF16ParseContext; + +static int flif16_find_frame(FLIF16ParseContext *f, const uint8_t *buf, + int buf_size) +{ + int next = END_NOT_FOUND; + int index; + + for (index = 0; index < buf_size; index++) { + if (!f->state) { + if (!memcmp(flif16_header, buf + index, 4)) + f->state = FLIF16_HEADER; + ++f->index; + } else if (f->state == FLIF16_HEADER) { + if (f->index == 3 + 1) { + // See whether image is animated or not + f->animated = (((buf[index] >> 4) > 4)?1:0); + } else if (f->index == (3 + 1 + 1)) { + // Start - 1 of the first varint + f->varint = 1; + } else if (f->varint) { + // Count varint + if (f->count == 9) + return AVERROR(ENOMEM); + + switch (f->varint) { + case 1: + FF_FLIF16_VARINT_APPEND(f->width, buf[index]); + break; + + case 2: + FF_FLIF16_VARINT_APPEND(f->height, buf[index]); + break; + + case 3: + FF_FLIF16_VARINT_APPEND(f->frames, buf[index]); + break; + } + if (buf[index] < 128) { + if (f->varint < (2 + f->animated)) { + switch (f->varint) { + case 1: f->width++; break; + case 2: f->height++; break; + } + f->varint++; + f->count = 0; + } else { + if (f->varint == 2) + f->height++; + if (f->animated) + f->frames += 2; + else + f->frames = 1; + f->state = FLIF16_METADATA; + f->varint = 0; + f->index = 0; + f->count = 0; + continue; + } + } else { + f->count++; + } + } + f->index++; + } else if (f->state == FLIF16_METADATA) { + if (f->index == 0) { + // Identifier for the bitstream chunk is a null byte. + if (buf[index] == 0) + f->state = FLIF16_BITSTREAM; + return buf_size; + } else if (f->index < 3) { + // nop + } else if (f->index == 3) { + // Handle the size varint + f->varint = 1; + } else if (f->varint) { + if (f->count == 9) + return AVERROR(ENOMEM); + if (buf[index] < 128) { + f->varint = 0; + f->count = 0; + } + FF_FLIF16_VARINT_APPEND(f->meta, buf[index]); + f->count++; + } else if (f->meta > 1) { + // increment varint until equal to size + f->meta--; + } else { + f->meta = 0; + f->index = 0; + continue; + } + f->index++; + } else if (f->state == FLIF16_BITSTREAM) { + /* Since we cannot find the end of the bitstream without any + * processing, we will simply return each read chunk as a packet + * to the decoder. + */ + printf("\n", buf_size); + return buf_size; + } + } + printf("End not found\n"); + return next; +} + +static int flif16_parse(AVCodecParserContext *s, AVCodecContext *avctx, + const uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size) +{ + FLIF16ParseContext *fpc = s->priv_data; + int next; + + next = flif16_find_frame(fpc, buf, buf_size); + + if (ff_combine_frame(&fpc->pc, next, &buf, &buf_size) < 0) { + *poutbuf = NULL; + *poutbuf_size = 0; + return buf_size; + } + printf("Width:%lu\nHeight:%lu\nFrames:%lu\nEnd:%d\n", + fpc->width, fpc->height, fpc->frames, buf_size); + + *poutbuf = buf; + *poutbuf_size = buf_size; + return next; +} + +AVCodecParser ff_flif16_parser = { + .codec_ids = { AV_CODEC_ID_FLIF16 }, + .priv_data_size = sizeof(FLIF16ParseContext), + .parser_parse = flif16_parse, + .parser_close = ff_parse_close, +}; + diff --git a/libavcodec/flif16_rangecoder.c b/libavcodec/flif16_rangecoder.c new file mode 100644 index 0000000000..4585693146 --- /dev/null +++ b/libavcodec/flif16_rangecoder.c @@ -0,0 +1,134 @@ +/* + * Range coder for FLIF16 + * Copyright (c) 2004, 2020 Jon Sneyers, Michael Niedermayer, Anamitra Ghorui + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + /** + * @file + * Range coder for FLIF16 + */ + +#include "flif16_rangecoder.h" + +// TODO write separate function for RAC decoder + +// The coder requires a certain number of bytes for initiialization. buf +// provides it. gb is used by the coder functions for actual coding. + +FLIF16RangeCoder *ff_flif16_rac_init(GetByteContext *gb, + uint8_t *buf, + uint8_t buf_size) +{ + FLIF16RangeCoder *rc = av_mallocz(sizeof(*rc)); + GetByteContext gbi; + + if (!rc) + return NULL; + + if(buf_size < FLIF16_RAC_MAX_RANGE_BYTES) + return NULL; + + bytestream2_init(&gbi, buf, buf_size); + + rc->range = FLIF16_RAC_MAX_RANGE; + rc->gb = gb; + + for (uint32_t r = FLIF16_RAC_MAX_RANGE; r > 1; r >>= 8) { + rc->low <<= 8; + rc->low |= bytestream2_get_byte(&gbi); + } + printf("[%s] low = %d\n", __func__, rc->low); + return rc; +} + +void ff_flif16_rac_free(FLIF16RangeCoder *rc) +{ + free(rc->ct); + free(rc->log4k); + free(rc); +} + +// TODO Maybe restructure rangecoder.c/h to fit a more generic case +static void build_table(uint16_t *zero_state, uint16_t *one_state, size_t size, + uint32_t factor, unsigned int max_p) +{ + const int64_t one = 1LL << 32; + int64_t p = one / 2; + unsigned int last_p8 = 0, p8; + unsigned int i; + + for (i = 0; i < size / 2; i++) { + p8 = (size * p + one / 2) >> 32; + if (p8 <= last_p8) + p8 = last_p8 + 1; + if (last_p8 && last_p8 < size && p8 <= max_p) + one_state[last_p8] = p8; + p += ((one - p) * factor + one / 2) >> 32; + last_p8 = p8; + } + + for (i = size - max_p; i <= max_p; i++) { + if (one_state[i]) + continue; + p = (i * one + size / 2) / size; + p += ((one - p) * factor + one / 2) >> 32; + p8 = (size * p + one / 2) >> 32; //FIXME try without the one + if (p8 <= i) + p8 = i + 1; + if (p8 > max_p) + p8 = max_p; + one_state[i] = p8; + } + + for (i = 1; i < size; i++) + zero_state[i] = size - one_state[size - i]; +} + +static uint32_t log4kf(int x, uint32_t base) +{ + int bits = 8 * sizeof(int) - ff_clz(x); + uint64_t y = ((uint64_t)x) << (32 - bits); + uint32_t res = base * (13 - bits); + uint32_t add = base; + while ((add > 1) && ((y & 0x7FFFFFFF) != 0)) { + y = (((uint64_t)y) * y + 0x40000000) >> 31; + add >>= 1; + if ((y >> 32) != 0) { + res -= add; + y >>= 1; + } + } + return res; +} + +void ff_flif16_build_log4k_table(FLIF16RangeCoder *rc) +{ + rc->log4k->table[0] = 0; + for (int i = 1; i < 4096; i++) + rc->log4k->table[i] = (log4kf(i, (65535UL << 16) / 12) + + (1 << 15)) >> 16; + rc->log4k->scale = 65535 / 12; +} + +void ff_flif16_chancetable_init(FLIF16RangeCoder *rc, int alpha, int cut) { + rc->chance = 0x800; + rc->ct = av_mallocz(sizeof(*(rc->ct))); + build_table(rc->ct->zero_state, rc->ct->one_state, 4096, alpha, 4096 - cut); + ff_flif16_build_log4k_table(rc); +} diff --git a/libavcodec/flif16_rangecoder.h b/libavcodec/flif16_rangecoder.h new file mode 100644 index 0000000000..a2bbd6d61e --- /dev/null +++ b/libavcodec/flif16_rangecoder.h @@ -0,0 +1,395 @@ +/* + * Range coder for FLIF16 + * Copyright (c) 2020 Anamitra Ghorui + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Range coder for FLIF16. + */ + +#ifndef FLIF16_RANGECODER_H +#define FLIF16_RANGECODER_H + +#include "rangecoder.h" +#include "flif16.h" + +#include "libavutil/mem.h" +#include "libavutil/intmath.h" +#include "bytestream.h" + +#include // Remove +#include +#include + +#define FLIF16_RAC_MAX_RANGE_BITS 24 +#define FLIF16_RAC_MAX_RANGE_BYTES (FLIF16_RAC_MAX_RANGE_BITS / 8) +#define FLIF16_RAC_MIN_RANGE_BITS 16 +#define FLIF16_RAC_MAX_RANGE (uint32_t) 1 << FLIF16_RAC_MAX_RANGE_BITS +#define FLIF16_RAC_MIN_RANGE (uint32_t) 1 << FLIF16_RAC_MIN_RANGE_BITS + + +typedef enum FLIF16RACTypes { + FLIF16_RAC_BIT = 0, + FLIF16_RAC_UNI_INT, + FLIF16_RAC_CHANCE, + FLIF16_RAC_NZ_INT, + FLIF16_RAC_GNZ_INT +} FLIF16RACReaders; + +typedef struct FLIF16ChanceTable { + uint16_t zero_state[4096]; + uint16_t one_state[4096]; +} FLIF16ChanceTable; + +typedef struct FLIF16Log4kTable { + uint16_t table[4096]; + int scale; +} FLIF16Log4kTable; + +typedef struct FLIF16RangeCoder { + unsigned int range; + unsigned int low; + uint16_t chance; + uint8_t empty; ///< Is bytestream empty + uint8_t renorm; ///< Is a renormalisation required + uint8_t active; ///< Is an integer reader currently active (to save/ + /// transfer state) + + // uni_int state management + uint32_t min; + uint32_t len; + + FLIF16ChanceTable *ct; + FLIF16Log4kTable *log4k; + GetByteContext *gb; +} FLIF16RangeCoder; + +FLIF16RangeCoder *ff_flif16_rac_init(GetByteContext *gb, uint8_t *buf, + uint8_t buf_size); +void ff_flif16_rac_free(FLIF16RangeCoder *rc); +void ff_flif16_chancetable_init(FLIF16RangeCoder *rc, int alpha, int cut); +void ff_flif16_build_log4k_table(FLIF16RangeCoder *rc); + +// NearZero Integer Definitions: +// Maybe pad with extra 2048s for faster access like in original code. +static uint16_t flif16_nz_int_chances[20] = { + 1000, // Zero + 2048, // Sign + + // Exponents + 1000, 1200, 1500, 1750, 2000, 2300, 2800, 2400, 2300, + 2048, // <- exp >= 9 + + // Mantisaa + 1900, 1850, 1800, 1750, 1650, 1600, 1600, + 2048 // <- mant > 7 +}; + +#define NZ_INT_ZERO (flif16_nz_int_chances[0]) +#define NZ_INT_SIGN (flif16_nz_int_chances[1]) +#define NZ_INT_EXP(k) ((k < 9) ? flif16_nz_int_chances[2 + (k)] : \ + flif16_nz_int_chances[11]) +#define NZ_INT_MANT(k) ((k < 8) ? flif16_nz_int_chances[12 + (k)] : \ + flif16_nz_int_chances[19]) + +// Functions + +static inline int ff_flif16_rac_renorm(FLIF16RangeCoder *rc) +{ + uint32_t left = bytestream2_get_bytes_left(rc->gb); + printf("[%s] left = %d\n", __func__, left); + if (!left) + return 0; + while (rc->range <= FLIF16_RAC_MIN_RANGE) { + rc->low <<= 8; + rc->range <<= 8; + rc->low |= bytestream2_get_byte(rc->gb); + if(!left) + return 0; + else + --left; + } + rc->renorm = 0; + return 1; +} + +static inline uint8_t ff_flif16_rac_get(FLIF16RangeCoder *rc, uint32_t chance, + uint8_t *target) +{ + // assert(rc->chance > 0); + // assert(rc->chance < rc->range); + + // printf("[%s] low: %u range: %u chance: %u\n", __func__, rc->low, rc->range, chance); + if (rc->renorm) { + printf("[%s] Triggered\n", __func__); + return 0; + } + + if (rc->low >= rc->range - chance) { + rc->low -= rc->range - chance; + rc->range = chance; + *target = 1; + } else { + rc->range -= chance; + *target = 0; + } + + rc->renorm = 1; + + return 1; +} + +static inline uint8_t ff_flif16_rac_read_bit(FLIF16RangeCoder *rc, + uint8_t *target) +{ + return ff_flif16_rac_get(rc, rc->range >> 1, target); +} + +static inline uint32_t ff_flif16_rac_read_chance(FLIF16RangeCoder *rc, + uint16_t b12, uint8_t *target) +{ + uint32_t ret; + + if (rc->renorm) { + printf("[%s] Triggered\n", __func__); + return 0; + } + // assert((b12 > 0) && (b12 >> 12) == 0); + // Optimisation based on CPU bus size (32/64 bit) + if (sizeof(rc->range) > 4) + ret = ((rc->range) * b12 + 0x800) >> 12; + else + ret = (((((rc->range) & 0xFFF) * b12 + 0x800) >> 12) + + (((rc->range) >> 12) * b12)); + + return ff_flif16_rac_get(rc, ret, target); +} + +/** + * Reads a Uniform Symbol Coded Integer. + */ +static inline int ff_flif16_rac_read_uni_int(FLIF16RangeCoder *rc, + uint32_t min, uint32_t len, + uint32_t *target) +{ + // assert(len >= 0); + int med; + uint8_t bit; + + if (rc->renorm) { + printf("[%s] Triggered\n", __func__); + return 0; + } + + if (!rc->active) { + rc->min = min; + rc->len = len; + rc->active = 1; + } + + if ((rc->len) > 0) { + ff_flif16_rac_read_bit(rc, &bit); + med = (rc->len) / 2; + if (bit) { + rc->min += med + 1; + rc->len -= med + 1; + } else { + rc->len = med; + } + //__PLN__ + printf("[%s] min = %d , len = %d\n", __func__, rc->min, rc->len); + return 0; + } else { + *target = rc->min; + printf("[%s] target = %d\n", __func__, rc->min); + rc->active = 0; + return 1; + } +} + +// Overload of above function in original code + +static inline int ff_flif16_rac_read_uni_int_bits(FLIF16RangeCoder *rc, + int bits, uint32_t *target) +{ + return ff_flif16_rac_read_uni_int(rc, 0, (1 << bits) - 1, target); +} + +// Nearzero integer definitions + +void inline ff_flif16_chancetable_put(FLIF16RangeCoder *rc, + uint8_t bit, uint16_t chance) +{ + rc->chance = (!bit) ? rc->ct->zero_state[chance] + : rc->ct->one_state[chance]; +} + +/** + * Reads a near-zero encoded symbol into the RAC probability model/chance table; + * @param chance The symbol chance specified by the NZ_INT_* macros + */ +/* +static inline uint8_t ff_flif16_rac_read_symbol(FLIF16RangeCoder *rc, + uint16_t chance) +{ + uint8_t bit; + ff_flif16_rac_read_chance(rc, chance, &bit); + ff_flif16_chancetable_put(rc, bit, chance); + return bit; +} +*/ +/** + * Returns an integer encoded by near zero integer encoding. + */ +/* +static inline int ff_flif16_rac_read_nz_int(FLIF16RangeCoder *rc, int min, + int max, uint32_t *target) +{ + uint8_t sign; + uint32_t temp; + const int amin = 1; + const int amax = (sign ? max : -min); + + const int emax = ff_log2(amax); + int e = ff_log2(amin); + int have, left, minabs1, maxabs0; + + if (min == max) + return min; + + switch (rc->segment) { + case 0: + ff_flif16_rac_read_symbol(rc, NZ_INT_ZERO, &temp); + + if (temp) { + *target = 0; + return 1; + } + break; + + case 1: + if (min < 0) { + if (max > 0) + sign = ff_flif16_rac_read_symbol(rc, NZ_INT_SIGN); + else + sign = 0; + } else + sign = 1; + break; + + case 2: + for (; e < emax; e++) { + ff_flif16_rac_read_symbol(rc, NZ_INT_EXP((e << 1) + sign), + target); + if (*target) + break; + } + have = (1 << e); + left = have - 1; + break; + + case 3: + for (int pos = e; pos > 0;) { + left >>= 1; + pos--; + minabs1 = have | (1 << pos); + maxabs0 = have | left; + if (minabs1 > amax) { + continue; + } else if (maxabs0 >= amin) { + if (ff_flif16_rac_read_symbol(rc, NZ_INT_MANT(pos))) + have = minabs1; + } + else + have = minabs1; + } + + *target = (sign ? have : -have); + break; + } + + return 1; +} + +static inline int ff_flif16_rac_read_gnz_int(FLIF16RangeCoder *rc, int min, + int max) +{ + if (min > 0) + return ff_flif16_rac_read_nz_int(rc, 0, max - min) + min; + else if (max < 0) + return ff_flif16_rac_read_nz_int(rc, min - max, 0) + max; + else + return ff_flif16_rac_read_nz_int(rc, min, max); +} +*/ + +/** + * Reads an integer encoded by FLIF's RAC. + * @param val1 A generic value, chosen according to the required type + * @param val2 Same as val1 + * @param target The place where the resultant value should be written to + * @param type The type of the integer to be decoded specified by FLIF16RACTypes + * + * @return -1 on bytestream empty, 0 on successful decoding. + */ +static inline int ff_flif16_rac_process(FLIF16RangeCoder *rc, uint32_t val1, + uint32_t val2, uint32_t *target, + int type) +{ + int flag = 0; + while (!flag) { + printf("[%s] low = %d range = %d renorm = %d\n", __func__, rc->low, + rc->range, rc->renorm); + if(rc->renorm) { + if(!ff_flif16_rac_renorm(rc)) + return 0; // EAGAIN condition + } + + switch (type) { + case FLIF16_RAC_BIT: + flag = ff_flif16_rac_read_bit(rc, (uint8_t *) target); + break; + + case FLIF16_RAC_UNI_INT: + flag = ff_flif16_rac_read_uni_int(rc, val1, val2, target); + break; + + case FLIF16_RAC_CHANCE: + flag = ff_flif16_rac_read_chance(rc, val1, (uint8_t *) target); + break; + + case FLIF16_RAC_NZ_INT: + // handle nz_ints + break; + + case FLIF16_RAC_GNZ_INT: + // handle gnz_ints + break; + + default: + printf("[%s] unknown rac reader\n", __func__); + break; + } + } + + return 1; +} + +#endif /* FLIF16_RANGECODER_H */ diff --git a/libavcodec/flif16_transform.c b/libavcodec/flif16_transform.c new file mode 100644 index 0000000000..3e66a972e9 --- /dev/null +++ b/libavcodec/flif16_transform.c @@ -0,0 +1,247 @@ +#include "flif16_transform.h" + +int process(Transform transform, AVFrame *frame, int planes, interimPixelData *pixelData){ + if(!pixelData->initialized){ + int r, c; + int p; + int width = frame->width, height = frame->height; + pixelData->height = height; + pixelData->width = width; + pixelData->num_planes = planes; + for(p = 0; p < p; p++){ + pixelData->data[p] = (ColorVal*)malloc(height*width*sizeof(ColorVal)); + for(r = 0; r < frame->height; r++){ + for(c = 0; c < frame->width; c++){ + pixelData->data[p][r*width + c] = frame->data[p][r*width + c]; + } + } + } + pixelData->initialized = 1; + } + switch(transform.transform_number){ + case 1: //Transform number for YCoCg transformation is 1 officially. + if(!transform.done){ + TransformYCoCg transformYCoCg = initYCoCg(pixelData); + if(!processYCoCg(pixelData)) + transform.done = 1; + return 0; + } + break; + + /* + Rest of the cases will be implemented here. + */ + + default: + break; + } + return 0; +} + +TransformYCoCg initYCoCg(interimPixelData *pixelData){ + TransformYCoCg transform; + transform.ranges = getRanges(pixelData); + transform.origmax4 = max(transform.ranges[0].max, transform.ranges[1].max, transform.ranges[2].max)/4 -1; + int p; + for(p=0; pnum_planes; p++){ + transform.ranges[p].max = max_range_YCoCg(p, transform.origmax4); + transform.ranges[p].min = min_range_YCoCg(p, transform.origmax4); + } + return transform; +} + +int processYCoCg(interimPixelData *pixelData){ + int r, c; + ColorVal R,G,B,Y,Co,Cg; + + //Assuming all the channels will have same width and height + int height = pixelData->height; + int width = pixelData->width; + + for (r=0; rdata[0][r*width + c]; + G = pixelData->data[1][r*width + c]; + B = pixelData->data[2][r*width + c]; + + Y = (((R + B)>>1) + G)>>1; + Co = R - B; + Cg = G - ((R + B)>>1); + + pixelData->data[0][r*width + c] = Y; + pixelData->data[1][r*width + c] = Co; + pixelData->data[2][r*width + c] = Cg; + } + } + return 0; +} + +// TODO inversetransform requires more work. +/* +int invProcessYCoCg(AVFrame *frame){ + int r, c; + ColorVal R,G,B,Y,Co,Cg; + + //Assuming all the channels will have same width and height + int height = frame[0].height; + int width = frame[0].width; + + for (r=0; rdata[0][r*width + c]; + Co= frame->data[1][r*width + c]; + Cg= frame->data[2][r*width + c]; + + R = Co + Y + ((1-Cg)>>1) - (Co>>1); + G = Y - ((-Cg)>>1); + B = Y + ((1-Cg)>>1) - (Co>>1); + + frame->data[0][r*width + c] = R; + frame->data[1][r*width + c] = G; + frame->data[2][r*width + c] = B; + } + } + return 0; +} +*/ + +ColorRanges* getRanges(interimPixelData *pixelData){ + int p = pixelData->num_planes; + ColorRanges ranges[p]; + int i, c, r, width, height; + ColorVal min, max; + + for(i=0; iwidth; + height = pixelData->height; + min = pixelData->data[p][0]; + max = pixelData->data[p][0]; + for(r=0; rheight; r++){ + for(c=0; cwidth; r++){ + if(min > pixelData->data[p][r*width + c]) + min = pixelData->data[p][r*width + c]; + if(max < pixelData->data[p][r*width + c]) + max = pixelData->data[p][r*width + c]; + } + } + ranges[p].min = min; + ranges[p].max = max; + } + return ranges; +} + +int max(int a, int b, int c){ + if(a > b){ + if(a > c) + return a; + else + return c; + } + else + return b; +} + +int min(int a, int b){ + if(a < b) + return a; + else + return b; +} + +int get_min_y(int origmax4){ + return 0; +} + +int get_max_y(int origmax4){ + return 4*origmax4-1; +} + +int get_min_co(int origmax4, int yval){ + int newmax = 4*origmax4 - 1; + if (yval < origmax4-1) + return -3 - 4*yval; + else if (yval >= 3*origmax4) + return 4*(yval - newmax); + else + return -newmax; +} + +int get_max_co(int origmax4, int yval){ + int newmax = 4*origmax4 - 1; + if (yval < origmax4-1) + return 3 + 4*yval; + else if (yval >= 3*origmax4) + return 4*(newmax - yval); + else + return newmax; +} + +int get_min_cg(int origmax4, int yval, int coval){ + int newmax = 4*origmax4 - 1; + if (yval < origmax4-1) + return -2 - 2*yval; + else if (yval >= 3*origmax4) + return -2*(newmax-yval) + 2*((abs(coval)+1)/2); + else{ + return min(2*yval + 1, 2*newmax - 2*yval - 2*abs(coval)+1)/2; + } +} + +int get_max_cg(int origmax4, int yval, int coval){ + int newmax = 4*origmax4 - 1; + if (yval < origmax4-1) + return 1 + 2*yval - 2*(abs(coval)/2); + else if (yval >= 3*origmax4) + return 2 * (newmax-yval); + else + return min(2*(yval- newmax), -2*yval - 1 + 2*(abs(coval)/2)); +} + +int min_range_YCoCg(int p, int origmax4){ + switch(p){ + case 0: + return 0; + case 1: + return -4*origmax4+1; + case 2: + return -4*origmax4+1; + default: + return 0; + break; + } +} + +int max_range_YCoCg(int p, int origmax4){ + switch(p){ + case 0: + return 0; + case 1: + return 4*origmax4+1; + case 2: + return 4*origmax4+1; + default: + return 0; + break; + } +} + +ColorRanges crangeYCoCg(int p, ColorVal* prevPlanes, TransformYCoCg transform){ + ColorRanges crange; + switch(p){ + case 0: + crange.min = get_min_y(transform.origmax4); + crange.max = get_max_y(transform.origmax4); + break; + case 1: + crange.min = get_min_co(transform.origmax4, prevPlanes[0]); + crange.max = get_max_co(transform.origmax4, prevPlanes[0]); + break; + case 2: + crange.min = get_min_cg(transform.origmax4, prevPlanes[0], prevPlanes[1]); + crange.max = get_max_cg(transform.origmax4, prevPlanes[0], prevPlanes[1]); + break; + default: + break; + } + return crange; +} diff --git a/libavcodec/flif16_transform.h b/libavcodec/flif16_transform.h new file mode 100644 index 0000000000..318c5638e2 --- /dev/null +++ b/libavcodec/flif16_transform.h @@ -0,0 +1,60 @@ +#include +#include "../libavutil/frame.h" +#define MAX_PLANES 5 + +typedef int16_t ColorVal; + +typedef struct{ + ColorVal min, max; +}ColorRanges; + +typedef struct{ + char* desc; + int transform_number; + uint8_t done; +}Transform; + +typedef struct{ + int origmax4; + ColorRanges *ranges; +}TransformYCoCg; + +typedef struct{ + uint8_t initialized; + int height, width; + int num_planes; + ColorVal *data[MAX_PLANES]; +}interimPixelData; + +typedef struct TransformPermute{ + uint8_t subtract; + uint8_t permutation[MAX_PLANES]; + ColorRanges *ranges; +}TransformPermute; + +ColorRanges* getRanges(interimPixelData* pixelData); + +int getmax(ColorRanges* ranges, int p); + +int max_range_YCoCg(int p, int origmax4); +int min_range_YCoCg(int p, int origmax4); + +int get_min_y(int); +int get_max_y(int orgimax4); + +int get_min_co(int orgimax4, int yval); +int get_max_co(int orgimax4, int yval); + +int get_min_cg(int orgimax4, int yval, int coval); +int get_max_cg(int orgimax4, int yval, int coval); + +int min(int, int); +int max(int, int, int); + +ColorRanges crangesYCoCg(int p, ColorVal* prevPlanes, TransformYCoCg transform); + +int process(Transform transform, AVFrame *frame, int p, interimPixelData *pixelData); +TransformYCoCg initYCoCg(interimPixelData *pixelData); +int processYCoCg(interimPixelData *pixelData); +int invProcessYCoCg(AVFrame *frame); + diff --git a/libavcodec/flif16dec.c b/libavcodec/flif16dec.c new file mode 100644 index 0000000000..37ac1d2175 --- /dev/null +++ b/libavcodec/flif16dec.c @@ -0,0 +1,372 @@ +/* + * FLIF16 Decoder + * Copyright (c) 2020 Anamitra Ghorui + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * FLIF16 Decoder +*/ + +#include // Remove + +#include "flif16.h" +#include "flif16_rangecoder.h" + +#include "avcodec.h" +#include "libavutil/common.h" +#include "bytestream.h" + +/* + * Due to the nature of the format, the decoder has to take the entirety of the + * data before it can generate any frames. The decoder has to return + * AVERROR(EAGAIN) as long as the bitstream is incomplete. + */ + + +// TODO prefix approprate functions with ff_* + +enum FLIF16States { + FLIF16_HEADER = 1, + FLIF16_SECONDHEADER, + FLIF16_TRANSFORM, + FLIF16_PIXELDATA, + FLIF16_CHECKSUM +}; + +typedef struct FLIF16RACQueue { + uint32_t val1; + uint32_t val2; + uint32_t *target; + int done; + int type; +} FLIF16RACQueue; + +typedef struct FLIF16DecoderContext { + GetByteContext gb; + FLIF16RangeCoder *rc; + uint8_t buf[FLIF16_RAC_MAX_RANGE_BYTES]; ///< Storage for initial RAC buffer + uint8_t buf_count; ///< Count for initial RAC buffer + int state; ///< The section of the file the parser is in currently. + unsigned int segment;///< The "segment" the code is supposed to jump to + int i; ///< A generic iterator used to save states between + /// for loops. + // Primary Header + uint8_t ia; ///< Is image interlaced or/and animated or not + uint8_t bpc; ///< Bytes per channel + uint8_t channels; ///< Number of channels + uint8_t varint; ///< Number of varints to process in sequence + + // Secondary Header + uint8_t channelbpc; ///< bpc per channel. Size == 1 if bpc == '0' + /// else equal to number of frames + + // Flags. TODO Merge all these flags + uint8_t alphazero; ///< Alphazero + uint8_t custombc; ///< Custom Bitchance + + uint8_t cutoff; + uint8_t alphadiv; + + uint8_t loops; ///< Number of times animation loops + uint16_t *framedelay;///< Frame delay for each frame + + // Dimensions and other things. + uint32_t width; + uint32_t height; + uint32_t frames; + uint32_t meta; ///< Size of a meta chunk +} FLIF16DecoderContext; + + +static int flif16_read_header(AVCodecContext *avctx) +{ + uint8_t temp, count = 3; + FLIF16DecoderContext *s = avctx->priv_data; + // TODO Make do without this array + uint32_t *vlist[] = { &s->width, &s->height, &s->frames }; + // Minimum size has empirically found to be 14 bytes. + __PLN__ + /* + if (bytestream2_size(&s->gb) < 14) { + av_log(avctx, AV_LOG_ERROR, "buf size too small (%d)\n", + bytestream2_size(&s->gb)); + return AVERROR(EINVAL); + }*/ + __PLN__ + if (bytestream2_get_le32(&s->gb) != (*((uint32_t *) flif16_header))) { + av_log(avctx, AV_LOG_ERROR, "bad magic number\n"); + return AVERROR(EINVAL); + } + __PLN__ + s->state = FLIF16_HEADER; + + temp = bytestream2_get_byte(&s->gb); + s->ia = temp >> 4; + s->channels = (0x0F & temp); + s->bpc = bytestream2_get_byte(&s->gb); + __PLN__ + // Will be later updated by the secondary header step, if bpc = 3 + s->channelbpc = (s->bpc == '1') ? 8 : 16; + + // Handle dimensions and frames + for(int i = 0; i < 2 + ((s->ia > 4) ? 1 : 0); ++i) { + while ((temp = bytestream2_get_byte(&s->gb)) > 127) { + FF_FLIF16_VARINT_APPEND(*vlist[i], temp); + if (!count) { + av_log(avctx, AV_LOG_ERROR, "image dimensions too big\n"); + return AVERROR(ENOMEM); + } + } + FF_FLIF16_VARINT_APPEND(*vlist[i], temp); + count = 3; + } + __PLN__ + s->width++; + s->height++; + (s->ia > 4) ? (s->frames += 2) : (s->frames = 1); + + // Handle Metadata Chunk. Currently it discards all data. + __PLN__ + while ((temp = bytestream2_get_byte(&s->gb)) != 0) { + bytestream2_seek(&s->gb, 3, SEEK_CUR); + // Read varint + while ((temp = bytestream2_get_byte(&s->gb)) > 127) { + FF_FLIF16_VARINT_APPEND(s->meta, temp); + if (!count) { + av_log(avctx, AV_LOG_ERROR, "metadata chunk too big \n"); + return AVERROR(ENOMEM); + } + } + FF_FLIF16_VARINT_APPEND(s->meta, temp); + bytestream2_seek(&s->gb, s->meta, SEEK_CUR); + } + + printf("[%s] left = %d\n", __func__, bytestream2_get_bytes_left(&s->gb)); + s->state = FLIF16_SECONDHEADER; + return 0; +} + +#define RAC_GET(rc, val1, val2, target, type) \ + if (!ff_flif16_rac_process((rc), (val1), (val2), \ + (uint32_t *) (target), (type))) \ + goto need_more_data; + +static int fli16_read_second_header(AVCodecContext *avctx) +{ + uint32_t temp; + FLIF16DecoderContext *s = avctx->priv_data; + + if (!s->rc) { + s->buf_count += bytestream2_get_buffer(&s->gb, s->buf, + FFMIN(bytestream2_get_bytes_left(&s->gb), + (FLIF16_RAC_MAX_RANGE_BYTES - + s->buf_count))); + if (s->buf_count < FLIF16_RAC_MAX_RANGE_BYTES) + return AVERROR(EAGAIN); + + s->rc = ff_flif16_rac_init(&s->gb, s->buf, s->buf_count); + } + + loop: + switch (s->segment) { + default: case 0: + // In original source this is handled in what seems to be a very + // bogus manner. It takes all the bpps of all channels and then + // takes the max. + if (s->bpc == '0') { + for (; s->i < s->channels; ++s->i) { + RAC_GET(s->rc, 1, 15, &temp, FLIF16_RAC_UNI_INT); + s->channelbpc = FFMAX(s->channelbpc, (1 << temp) - 1); + } + } + s->i = 0; + ++s->segment; __PLN__ + break; + + case 1: + if (s->channels > 3) + RAC_GET(s->rc, 0, 1, (uint32_t *) &s->alphazero, + FLIF16_RAC_UNI_INT); + ++s->segment; __PLN__ + break; + + case 2: + if (s->frames > 1) { + RAC_GET(s->rc, 0, 100, (uint32_t *) &s->loops, + FLIF16_RAC_UNI_INT); + s->framedelay = av_mallocz(sizeof(*(s->framedelay)) * s->frames); + } + ++s->segment; __PLN__ + break; + + case 3: + if (s->frames > 1) { + for (; (s->i) < (s->frames); ++(s->i)) { + RAC_GET(s->rc, 0, 60000, &(s->framedelay[(s->i)]), + FLIF16_RAC_UNI_INT); + } + s->i = 0; + } + ++s->segment; + break; + + case 4: + // Has custom alpha flag + RAC_GET(s->rc, 0, 1, &temp, FLIF16_RAC_UNI_INT); + printf("[%s] has_custom_cutoff_alpha = %d\n", __func__, temp); + ++s->segment; + break; + + case 5: + if (temp) + RAC_GET(s->rc, 1, 128, &s->cutoff, FLIF16_RAC_UNI_INT); + ++s->segment; + break; + + case 6: + if (temp) + RAC_GET(s->rc, 2, 128, &s->alphadiv, FLIF16_RAC_UNI_INT); + ++s->segment; + break; + + case 7: + if (temp) + RAC_GET(s->rc, 0, 1, &s->cutoff, FLIF16_RAC_UNI_INT); + if (s->custombc) { + av_log(avctx, AV_LOG_ERROR, + "custom bitchances not implemented\n"); + return AVERROR_PATCHWELCOME; + } + goto end; + break; + } + + goto loop; + + end: + s->state = FLIF16_TRANSFORM; + return 0; + + need_more_data: + printf("[%s] Need more data\n", __func__); + return AVERROR(EAGAIN); +} + + +static int flif16_read_transforms(AVCodecContext *avctx) { + + /* + while (ff_flif16_rac_read_bit(s->rc)) { + temp = ff_flif16_rac_read_uni_int(s->rc, 0, 13); // Transform number + // ff_flif16_transform_process(temp, tlist, rc); + } + */ + + return AVERROR_EOF; +} + +static int flif16_read_pixeldata(AVCodecContext *avctx, AVFrame *p) +{ + return AVERROR_EOF; +} + +static int flif16_read_checksum(AVCodecContext *avctx) +{ + return AVERROR_EOF; +} + +static int flif16_decode_frame(AVCodecContext *avctx, + void *data, int *got_frame, + AVPacket *avpkt) +{ + int ret = AVERROR(EINVAL); + FLIF16DecoderContext *s = avctx->priv_data; + const uint8_t *buf = avpkt->data; + int buf_size = avpkt->size; + AVFrame *p = data; + printf("[Decode] Packet Size = %d\n", buf_size); + bytestream2_init(&s->gb, buf, buf_size); + __PLN__ + // Looping is done to change states in between functions. + // Function will either exit on AVERROR(EAGAIN) or AVERROR_EOF + do { + switch(s->state) { + case 0: case FLIF16_HEADER: + ret = flif16_read_header(avctx); + break; + + case FLIF16_SECONDHEADER: + ret = fli16_read_second_header(avctx); + break; + + case FLIF16_TRANSFORM: + ret = flif16_read_transforms(avctx); + break; + + case FLIF16_PIXELDATA: + __PLN__ + ret = flif16_read_pixeldata(avctx, p); + break; + + case FLIF16_CHECKSUM: + ret = flif16_read_checksum(avctx); + break; + } + } while (!ret); + + printf("[Decode Result]\n" \ + "Width: %u, Height: %u, Frames: %u\n"\ + "ia: %x bpc: %c channels: %u\n" \ + "channelbpc: %u\n" \ + "alphazero: %u custombc: %u\n" \ + "cutoff: %u alphadiv: %u \n" \ + "loops: %u\n", s->width, s->height, s->frames, s->ia, s->bpc, + s->channels, s->channelbpc, s->alphazero, s->custombc, s->cutoff, + s->alphadiv, s->loops); + if (s->framedelay) { + printf("Framedelays:\n"); + for(uint32_t i = 0; i < s->frames; ++i) + printf("%u, ", s->framedelay[i]); + printf("\n"); + } + return ret; +} + +static av_cold int flif16_decode_end(AVCodecContext *avctx) +{ + FLIF16DecoderContext *s = avctx->priv_data; + av_free(s->rc); + if(s->framedelay) + av_free(s->framedelay); + return 0; +} + +AVCodec ff_flif16_decoder = { + .name = "flif16", + .long_name = NULL_IF_CONFIG_SMALL("FLIF (Free Lossless Image Format)"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_FLIF16, + .close = flif16_decode_end, + .priv_data_size = sizeof(FLIF16DecoderContext), + .decode = flif16_decode_frame, + //.capabilities = 0, + //.caps_internal = 0, + .priv_class = NULL, +}; diff --git a/libavcodec/flif16enc.c b/libavcodec/flif16enc.c new file mode 100644 index 0000000000..8cad9973be --- /dev/null +++ b/libavcodec/flif16enc.c @@ -0,0 +1,25 @@ +#include "flif16.h" +#include "avcodec.h" + +#include "bytestream.h" + +// TODO Add all Functions +static int flif16_encode_frame(AVCodecContext *avctx, AVPacket *pkt, + const AVFrame *pict, int *got_packet) +{ + + return AVERROR(EINVAL); +} + +AVCodec ff_flif16_encoder = { + .name = "flif16", + .long_name = NULL_IF_CONFIG_SMALL("FLIF (Free Lossless Image Format)"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_FLIF16, + .priv_data_size = 0, + //.init = NULL, + .encode2 = flif16_encode_frame, + //.close = NULL, + //.pix_fmts = (const enum AVPixelFormat[]){}, + .priv_class = NULL, +}; diff --git a/libavcodec/parsers.c b/libavcodec/parsers.c index 33a71de8a0..8b6eb954b3 100644 --- a/libavcodec/parsers.c +++ b/libavcodec/parsers.c @@ -40,6 +40,7 @@ extern AVCodecParser ff_dvbsub_parser; extern AVCodecParser ff_dvdsub_parser; extern AVCodecParser ff_dvd_nav_parser; extern AVCodecParser ff_flac_parser; +extern AVCodecParser ff_flif16_parser; extern AVCodecParser ff_g723_1_parser; extern AVCodecParser ff_g729_parser; extern AVCodecParser ff_gif_parser; diff --git a/libavformat/img2.c b/libavformat/img2.c index 16bc9d2abd..14c11d0c82 100644 --- a/libavformat/img2.c +++ b/libavformat/img2.c @@ -81,6 +81,7 @@ const IdStrMap ff_img_tags[] = { { AV_CODEC_ID_XPM, "xpm" }, { AV_CODEC_ID_XFACE, "xface" }, { AV_CODEC_ID_XWD, "xwd" }, + { AV_CODEC_ID_FLIF16, "flif16" }, { AV_CODEC_ID_NONE, NULL } };