Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
From: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
To: ffmpeg-devel@ffmpeg.org
Cc: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
Subject: [FFmpeg-devel] [PATCH 2/2] avcodec/flashsv: Avoid deflating data
Date: Fri, 11 Mar 2022 16:59:37 +0100
Message-ID: <AM7PR03MB666031EC6C63FCA4F0C3CE498F0C9@AM7PR03MB6660.eurprd03.prod.outlook.com> (raw)
In-Reply-To: <AM7PR03MB6660918E362242F545F0172D8F0C9@AM7PR03MB6660.eurprd03.prod.outlook.com>

Currently priming the zlib decompressor involves compressing
data directly after having decompressed it and decompressing
it again in order to set the "dictionary" and to initialize
the adler32-checksum. Yet this is wasteful and can be simplified
by synthetizing the compressed data via non-compressed blocks.

This reduced the amount of allocations for the decoding part
of fate-vsynth1-flashsv2, namely from
total heap usage: 9,135 allocs, 9,135 frees, 376,503,427 bytes allocated
to
total heap usage: 2,373 allocs, 2,373 frees, 14,144,083 bytes allocated

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
---
Unfortunately one can't simply use inflateSetDictionary() to set
the dictionary, as this does not initialize the adler32-checksum.
More precisely, the main adler32 does not cover the dictionary data;
so it would decode correctly, but one would get checksum errors.

Notice that I do not have a single flashsv2 file that has not been
created by our encoder and it might be that our encoder is wrong.
Looking at the mailing list archive showed lots of mails that complain
about the paucacity of samples.
The spec simply speaks of "the ZLIB priming technique of compression".
How very helpful.

 libavcodec/flashsv.c | 89 +++++++++++++++++++-------------------------
 1 file changed, 38 insertions(+), 51 deletions(-)

diff --git a/libavcodec/flashsv.c b/libavcodec/flashsv.c
index 5f0bc0c6df..e284439972 100644
--- a/libavcodec/flashsv.c
+++ b/libavcodec/flashsv.c
@@ -63,11 +63,10 @@ typedef struct FlashSVContext {
     AVBufferRef    *keyframedata_buf;
     uint8_t        *keyframe;
     BlockInfo      *blocks;
-    uint8_t        *deflate_block;
-    int             deflate_block_size;
     int             color_depth;
     int             zlibprime_curr, zlibprime_prev;
     int             diff_start, diff_height;
+    uint8_t         tmp[UINT16_MAX];
 } FlashSVContext;
 
 static int decode_hybrid(const uint8_t *sptr, const uint8_t *sptr_end, uint8_t *dptr, int dx, int dy,
@@ -141,41 +140,59 @@ static av_cold int flashsv_decode_init(AVCodecContext *avctx)
 
 static int flashsv2_prime(FlashSVContext *s, const uint8_t *src, int size)
 {
-    z_stream zs;
     int zret; // Zlib return code
+    static const uint8_t zlib_header[] = { 0x78, 0x01 };
+    uint8_t *data = s->tmpblock;
+    unsigned remaining;
 
     if (!src)
         return AVERROR_INVALIDDATA;
 
-    zs.zalloc = NULL;
-    zs.zfree  = NULL;
-    zs.opaque = NULL;
-
     s->zstream.next_in   = src;
     s->zstream.avail_in  = size;
-    s->zstream.next_out  = s->tmpblock;
+    s->zstream.next_out  = data;
     s->zstream.avail_out = s->block_size * 3;
     inflate(&s->zstream, Z_SYNC_FLUSH);
-
-    if (deflateInit(&zs, 0) != Z_OK)
-        return -1;
-    zs.next_in   = s->tmpblock;
-    zs.avail_in  = s->block_size * 3 - s->zstream.avail_out;
-    zs.next_out  = s->deflate_block;
-    zs.avail_out = s->deflate_block_size;
-    deflate(&zs, Z_SYNC_FLUSH);
-    deflateEnd(&zs);
+    remaining = s->block_size * 3 - s->zstream.avail_out;
 
     if ((zret = inflateReset(&s->zstream)) != Z_OK) {
         av_log(s->avctx, AV_LOG_ERROR, "Inflate reset error: %d\n", zret);
         return AVERROR_UNKNOWN;
     }
 
-    s->zstream.next_in   = s->deflate_block;
-    s->zstream.avail_in  = s->deflate_block_size - zs.avail_out;
-    s->zstream.next_out  = s->tmpblock;
-    s->zstream.avail_out = s->block_size * 3;
+    /* Create input for zlib that is equivalent to encoding the output
+     * from above and decoding it again (the net result of this is that
+     * the dictionary of past decoded data is correctly primed and
+     * the adler32 checksum is correctly initialized).
+     * This is accomplished by synthetizing blocks of uncompressed data
+     * out of the output from above. See section 3.2.4 of RFC 1951. */
+    s->zstream.next_in  = zlib_header;
+    s->zstream.avail_in = sizeof(zlib_header);
     inflate(&s->zstream, Z_SYNC_FLUSH);
+    while (remaining > 0) {
+        unsigned block_size = FFMIN(UINT16_MAX, remaining);
+        uint8_t header[5];
+        /* Bit 0: Non-last-block, bits 1-2: BTYPE for uncompressed block */
+        header[0] = 0;
+        /* Block size */
+        AV_WL16(header + 1, block_size);
+        /* Block size (one's complement) */
+        AV_WL16(header + 3, block_size ^ 0xFFFF);
+        s->zstream.next_in   = header;
+        s->zstream.avail_in  = sizeof(header);
+        s->zstream.next_out  = s->tmp;
+        s->zstream.avail_out = sizeof(s->tmp);
+        zret = inflate(&s->zstream, Z_SYNC_FLUSH);
+        if (zret != Z_OK)
+            return AVERROR_UNKNOWN;
+        s->zstream.next_in   = data;
+        s->zstream.avail_in  = block_size;
+        zret = inflate(&s->zstream, Z_SYNC_FLUSH);
+        if (zret != Z_OK)
+            return AVERROR_UNKNOWN;
+        data      += block_size;
+        remaining -= block_size;
+    }
 
     return 0;
 }
@@ -248,22 +265,6 @@ static int flashsv_decode_block(AVCodecContext *avctx, const AVPacket *avpkt,
     return 0;
 }
 
-static int calc_deflate_block_size(int tmpblock_size)
-{
-    z_stream zstream;
-    int size;
-
-    zstream.zalloc = Z_NULL;
-    zstream.zfree  = Z_NULL;
-    zstream.opaque = Z_NULL;
-    if (deflateInit(&zstream, 0) != Z_OK)
-        return -1;
-    size = deflateBound(&zstream, tmpblock_size);
-    deflateEnd(&zstream);
-
-    return size;
-}
-
 static int flashsv_decode_frame(AVCodecContext *avctx, void *data,
                                 int *got_frame, AVPacket *avpkt)
 {
@@ -322,19 +323,6 @@ static int flashsv_decode_frame(AVCodecContext *avctx, void *data,
                    "Cannot allocate decompression buffer.\n");
             return err;
         }
-        if (s->ver == 2) {
-            s->deflate_block_size = calc_deflate_block_size(tmpblock_size);
-            if (s->deflate_block_size <= 0) {
-                av_log(avctx, AV_LOG_ERROR,
-                       "Cannot determine deflate buffer size.\n");
-                return -1;
-            }
-            if ((err = av_reallocp(&s->deflate_block, s->deflate_block_size)) < 0) {
-                s->block_size = 0;
-                av_log(avctx, AV_LOG_ERROR, "Cannot allocate deflate buffer.\n");
-                return err;
-            }
-        }
     }
     s->block_size = s->block_width * s->block_height;
 
@@ -570,7 +558,6 @@ static av_cold int flashsv2_decode_end(AVCodecContext *avctx)
     av_buffer_unref(&s->keyframedata_buf);
     av_freep(&s->blocks);
     av_freep(&s->keyframe);
-    av_freep(&s->deflate_block);
     flashsv_decode_end(avctx);
 
     return 0;
-- 
2.32.0

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

      reply	other threads:[~2022-03-11 15:59 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-03-11 15:44 [FFmpeg-devel] [PATCH 1/2] avcodec/flashsv: Avoid copying packet Andreas Rheinhardt
2022-03-11 15:59 ` Andreas Rheinhardt [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=AM7PR03MB666031EC6C63FCA4F0C3CE498F0C9@AM7PR03MB6660.eurprd03.prod.outlook.com \
    --to=andreas.rheinhardt@outlook.com \
    --cc=ffmpeg-devel@ffmpeg.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git