[FFmpeg-devel] [PATCH] [RFC] lavc, lavfmt: add FLIF decoding support
Andreas Rheinhardt
andreas.rheinhardt at gmail.com
Wed Jul 29 22:37:59 EEST 2020
Anamitra Ghorui:
> This patch implements non interlaced decoding of FLIF16 images with all
> transforms except for one, FrameLookback which is being tested.
>
> Several test files have been attached. The above mentioned transform
> and interlaced decoding is being tested and will be implemented shortly.
>
> The main reason of posting this patch now is to ask advice on deflate
> decompression (or inflate) on the metadata segments of the format.
> The reference decoder of the format [1] uses lodepng, and by extension,
> deflate for generating the metadata segments. Therefore these segments
> should be able to be decoded by zlib's deflate routines. However it does
> not work and returns saying that the data is invalid.
> I have tried isolating the metadata using a hex editor, and then using
> zlib's example program [2] to decode it. However it also says that it's
> invalid data.
> Can you please tell me what I am doing wrong in libavformat/flifdec.c?
>
> [1]: https://github.com/FLIF-hub/FLIF
> [2]: https://zlib.net/zpipe.c
>
> Co-authored-by: Anamitra Ghorui <aghorui at teknik.io>
> Co-authored-by: Karhtik K. Khullar <kartikkhullar840 at gmail.com>
>
> Signed-off-by: Anamitra Ghorui <aghorui at teknik.io>
> ---
[...]
> +
> +#if 0
> +// CONFIG_ZLIB
> +static int flif_inflate(FLIFDemuxContext *s, unsigned char *buf, int buf_size,
> + unsigned char *out_buf, int *out_buf_size)
I did not look thoroughly at it at all, but this is already wrong:
flif_inflate allocates out_buf and in order to return it, the parameter
must be of type unsigned char ** (or uint8_t ** (that is preferred here)).
> +{
> + int ret;
> + z_stream *stream = &s->stream;
> +
> + if (!s->active) {
> + s->active = 1;
> + stream->zalloc = Z_NULL;
> + stream->zfree = Z_NULL;
> + stream->opaque = Z_NULL;
> + stream->avail_in = 0;
> + stream->next_in = Z_NULL;
> + ret = inflateInit(stream);
> +
> + if (ret != Z_OK)
> + return ret;
> +
> + *out_buf_size = buf_size;
> + out_buf = av_realloc(out_buf, buf_size);
> + if (!out_buf)
> + return AVERROR(ENOMEM);
> + }
> +
> + stream->next_in = buf;
> + stream->avail_in = buf_size;
> + if(stream->total_out >= *out_buf_size) {
> + out_buf = av_realloc(out_buf, (*out_buf_size) * 2);
> + if (!out_buf)
> + return AVERROR(ENOMEM);
> + *out_buf_size *= 2;
> + }
> +
> + stream->next_out = out_buf + stream->total_out;
> + stream->avail_out = *out_buf_size - stream->total_out - 1; // Last byte should be NULL char
> +
> + ret = inflate(stream, Z_PARTIAL_FLUSH);
> +
> + switch (ret) {
> + case Z_NEED_DICT:
> + case Z_DATA_ERROR:
> + (void)inflateEnd(stream);
> + return AVERROR(EINVAL);
> + case Z_MEM_ERROR:
> + (void)inflateEnd(stream);
> + return AVERROR(ENOMEM);
> + }
> +
> + if (ret == Z_STREAM_END) {
> + ret = 0;
> + s->active = 0;
> + out_buf[stream->total_out - 1] = '\0';
> + (void) inflateEnd(stream);
> + } else
> + ret = AVERROR(EAGAIN);
> +
> + return ret; // Return Z_BUF_ERROR/EAGAN as long as input is incomplete.
> +}
> +
> +#endif
> +
> +static int flif16_probe(const AVProbeData *p)
> +{
> + uint32_t vlist[3] = {0};
> + unsigned int count = 0, pos = 0;
> +
> + // Magic Number
> + if (memcmp(p->buf, flif16_header, 4)) {
> + return 0;
> + }
> +
> + for(int i = 0; i < 2 + (((p->buf[4] >> 4) > 4) ? 1 : 0); ++i) {
> + while (p->buf[5 + pos] > 127) {
> + if (!(count--)) {
> + return 0;
> + }
> + VARINT_APPEND(vlist[i], p->buf[5 + pos]);
> + ++pos;
> + }
> + VARINT_APPEND(vlist[i], p->buf[5 + pos]);
> + count = 0;
> + }
> +
> + if (!((vlist[0] + 1) && (vlist[1] + 1)))
> + return 0;
> +
> + if (((p->buf[4] >> 4) > 4) && !(vlist[2] + 2))
> + return 0;
> +
> + return AVPROBE_SCORE_MAX;
> +}
> +
> +static int flif16_read_header(AVFormatContext *s)
> +{
> + // FLIFDemuxContext *dc = s->priv_data;
> + GetByteContext gb;
> + FLIF16RangeCoder rc;
> +
> + AVIOContext *pb = s->pb;
> + AVStream *st;
> +
> + uint32_t vlist[3] = {0};
> + uint32_t flag, animated, temp;
> + uint32_t bpc = 0;
> + // uint8_t tag[5] = {0};
> + uint8_t buf[BUF_SIZE];
> + uint32_t metadata_size = 0;
> + // uint8_t *out_buf = NULL;
> + // int out_buf_size = 0;
> +
> + unsigned int count = 4;
> + int ret;
> + int format;
> + int segment = 0, i = 0;
> + int64_t duration = 0;
> + uint8_t loops = 0;
> + uint8_t num_planes;
> + uint8_t num_frames;
> +
> + // Magic Number
> + if (avio_rl32(pb) != (*((uint32_t *) flif16_header))) {
> + av_log(s, AV_LOG_ERROR, "bad magic number\n");
> + return AVERROR(EINVAL);
> + }
> +
> + st = avformat_new_stream(s, NULL);
> + flag = avio_r8(pb);
> + animated = (flag >> 4) > 4;
> + duration = !animated;
> + bpc = avio_r8(pb); // Bytes per channel
> +
> + num_planes = flag & 0x0F;
> +
> + for (int i = 0; i < (2 + animated); ++i) {
> + while ((temp = avio_r8(pb)) > 127) {
> + if (!(count--))
> + return AVERROR_INVALIDDATA;
> + VARINT_APPEND(vlist[i], temp);
> + }
> + VARINT_APPEND(vlist[i], temp);
> + count = 4;
> + }
> +
> +
> + ++vlist[0];
> + ++vlist[1];
> + if (animated)
> + vlist[2] += 2;
> + else
> + vlist[2] = 1;
> +
> + num_frames = vlist[2];
> +
> + while ((temp = avio_r8(pb))) {
> + // Get metadata identifier
> + #if 0
> + tag[0] = temp;
> + for(int i = 1; i <= 3; ++i)
> + tag[i] = avio_r8(pb);
> + #else
> + avio_skip(pb, 3);
> + #endif
> +
> + // Read varint
> + while ((temp = avio_r8(pb)) > 127) {
> + if (!(count--))
> + return AVERROR_INVALIDDATA;
> + VARINT_APPEND(metadata_size, temp);
> + }
> + VARINT_APPEND(metadata_size, temp);
> + count = 4;
> +
> + #if 0
> + // CONFIG_ZLIB
> + // TODO see why this does not work.
> + // Decompression Routines
> + while (metadata_size > 0) {
> + ret = avio_read(pb, metadata_buf, FFMIN(METADATA_BUF_SIZE, metadata_size));
> + metadata_size -= ret;
> + if((ret = flif_inflate(dc, metadata_buf, ret, out_buf, &out_buf_size)) < 0 &&
out_buf here will always be what it was at the beginning, because
flif_inflate() works with a copy of out_buf. (C uses call by value.)
> + ret != AVERROR(EAGAIN)) {
> + av_log(s, AV_LOG_ERROR, "could not decode metadata\n");
> + return ret;
> + }
> + }
> + av_dict_set(&s->metadata, tag, out_buf, 0);
> + #else
> + avio_skip(pb, metadata_size);
> + #endif
> + }
> +
> + #if 0
> + // CONFIG_ZLIB
> + if (out_buf)
> + av_freep(&out_buf);
> + #endif
> +
> + avio_read(pb, buf, FLIF16_RAC_MAX_RANGE_BYTES);
> + ff_flif16_rac_init(&rc, NULL, buf, FLIF16_RAC_MAX_RANGE_BYTES);
> + ret = avio_read_partial(pb, buf, BUF_SIZE);
> + bytestream2_init(&gb, buf, ret);
> + rc.gb = &gb;
> +
> + while (1) {
> + switch (segment) {
> + case 0:
> + if (bpc == '0') {
> + bpc = 0;
> + for (; i < num_planes; ++i) {
> + RAC_GET(&rc, NULL, 1, 15, &temp, FLIF16_RAC_UNI_INT8);
> + bpc = FFMAX(bpc, (1 << temp) - 1);
> + }
> + i = 0;
> + } else
> + bpc = (bpc == '1') ? 255 : 65535;
> + // MSG("planes : %d & bpc : %d\n", num_planes, bpc);
> + if (num_frames < 2)
> + goto end;
> + ++segment;
> +
> + case 1:
> + if (num_planes > 3) {
> + RAC_GET(&rc, NULL, 0, 1, &temp, FLIF16_RAC_UNI_INT8);
> + }
> + ++segment;
> +
> + case 2:
> + if (num_frames > 1) {
> + RAC_GET(&rc, NULL, 0, 100, &loops, FLIF16_RAC_UNI_INT8);
> + }
> + loops = (!loops) ? 1 : loops;
> + ++segment;
> +
> + case 3:
> + if (num_frames > 1) {
> + for (; i < num_frames; ++i) {
> + temp = 0;
> + RAC_GET(&rc, NULL, 0, 60000, &(temp), FLIF16_RAC_UNI_INT16);
> + duration += temp;
> + }
> + i = 0;
> + }
> + goto end;
> + }
> +
> + need_more_data:
> + avio_read_partial(pb, buf, BUF_SIZE);
> + bytestream2_init(&gb, buf, BUF_SIZE);
> + }
> +
> + end:
> +
> + if (bpc > 65535) {
> + av_log(s, AV_LOG_ERROR, "depth per channel greater than 16 bits not supported\n");
> + return AVERROR_PATCHWELCOME;
> + }
> +
> + format = flif16_out_frame_type[FFMIN(num_planes, 4)][bpc > 255];
> +
> + // The minimum possible delay in a FLIF16 image is 1 millisecond.
> + // Therefore time base is 10^-3, i.e. 1/1000
> + avpriv_set_pts_info(st, 64, 1, 1000);
> + st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
> + st->codecpar->codec_id = AV_CODEC_ID_FLIF16;
> + st->codecpar->width = vlist[0];
> + st->codecpar->height = vlist[1];
> + st->codecpar->format = format;
> + st->duration = duration * loops;
> + st->start_time = 0;
> + st->nb_frames = vlist[2];
> + // st->need_parsing = 1;
> +
> + // Jump to start because flif16 decoder needs header data too
> + if (avio_seek(pb, 0, SEEK_SET) != 0)
> + return AVERROR(EIO);
> + return 0;
> +}
> +
> +
> +static int flif16_read_packet(AVFormatContext *s, AVPacket *pkt)
> +{
> + AVIOContext *pb = s->pb;
> + int ret;
> + // FFMIN(BUF_SIZE, avio_size(pb))
> + ret = av_get_packet(pb, pkt, avio_size(pb));
> + return ret;
> +}
> +
> +
> +static const AVOption options[] = {
> + { NULL }
> +};
> +
> +static const AVClass demuxer_class = {
> + .class_name = "FLIF demuxer",
> + .item_name = av_default_item_name,
> + .option = options,
> + .version = LIBAVUTIL_VERSION_INT,
> + .category = AV_CLASS_CATEGORY_DEMUXER,
> +};
> +
> +AVInputFormat ff_flif_demuxer = {
> + .name = "flif",
> + .long_name = NULL_IF_CONFIG_SMALL("Free Lossless Image Format (FLIF)"),
> + .priv_data_size = sizeof(FLIFDemuxContext),
> + .extensions = "flif",
> + .read_probe = flif16_probe,
> + .read_header = flif16_read_header,
> + .read_packet = flif16_read_packet,
> + //.flags = AVFMT_NOTIMESTAMPS,
> + .priv_class = &demuxer_class,
> +};
> diff --git a/libavformat/gifdec.c b/libavformat/gifdec.c
> index a31644c2a8..58e2d368ca 100644
> --- a/libavformat/gifdec.c
> +++ b/libavformat/gifdec.c
> @@ -268,7 +268,6 @@ static int gif_read_packet(AVFormatContext *s, AVPacket *pkt)
> keyframe, frame_parsed = 0, ret;
> int64_t frame_start = avio_tell(pb), frame_end;
> unsigned char buf[6];
> -
> if ((ret = avio_read(pb, buf, 6)) == 6) {
> keyframe = memcmp(buf, gif87a_sig, 6) == 0 ||
> memcmp(buf, gif89a_sig, 6) == 0;
> diff --git a/libavformat/version.h b/libavformat/version.h
> index 4724269b3c..a233b67351 100644
> --- a/libavformat/version.h
> +++ b/libavformat/version.h
> @@ -32,8 +32,8 @@
> // Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium)
> // Also please add any ticket numbers that you believe might be affected here
> #define LIBAVFORMAT_VERSION_MAJOR 58
> -#define LIBAVFORMAT_VERSION_MINOR 39
> -#define LIBAVFORMAT_VERSION_MICRO 101
> +#define LIBAVFORMAT_VERSION_MINOR 40
> +#define LIBAVFORMAT_VERSION_MICRO 100
>
> #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
> LIBAVFORMAT_VERSION_MINOR, \
>
More information about the ffmpeg-devel
mailing list