[FFmpeg-devel] request for feedback on video codec idea

Roger Pack rogerdpack2 at gmail.com
Tue Dec 1 21:19:26 CET 2015


On 10/14/15, Nicolas George <george at nsup.org> wrote:
> Le tridi 23 vendémiaire, an CCXXIV, Roger Pack a écrit :
>> For instance, given 3 frames of video ("one after another" from the
>> incoming video stream), "combine them" into one stream like:
>> pixel 1 frame 1, pixel 1 frame 2, pixel 1 frame 3, pixel 2 frame 2,
>> pixel 2 frame 2, pixel 2 frame 3 ...
>>
>> then basically apply LZ4 or density algorithm to those bytes.
>>
>> The theory being that if there is a lot of repeated content between
>> frames, it will compress well.
>
> Standard answer: profile, don't speculate.
>
> I have no idea if this can prove promising, but I am pretty sure you could
> implement enough of it to get an estimate in just a few minutes with your
> language of choice.
>
> Choose a test clip that has convenient properties (frame size multiple of
> your chosen block size, number of frames multiple of the frame group size,
> etc.).
>
> In the program, hardcode the frame size, pixel format, etc., and forego
> error checks. Read a group of frames from standard input, assuming rawvideo
> format, then output the pixels to standard output in the order you want.
>
> Run with standard input piped from ffmpeg to get the rawvideo contents and
> standard output piped to your favorite command-line stream compression
> utility (gzip, xz).
>
> If the result looks promising, enhance. If not, you know you tried.

Great idea.  I think I will do that.
I did get as far as "single frame" encoding using lzo.
It works *great* for synthetically generated data like testsrc.  I
mean like twice as fast as anything else, with better compression LOL.
With real data, not so much.  I'm attaching it here not for it to be
committed but for followers in case it saves anybody some time that
also wants to go down this path.
I will hopefully find time to work on the "multi frame" aspect soon,
and probably save time by doing what you suggested there.
Thanks!
-------------- next part --------------
diff --git a/configure b/configure
index a30d831..43108e4 100755
--- a/configure
+++ b/configure
@@ -1961,6 +1961,8 @@ CONFIG_EXTRA="
     hpeldsp
     huffman
     huffyuvdsp
+    rzipenc
+    rzipdec
     huffyuvencdsp
     idctdsp
     iirfilter
@@ -2283,6 +2285,8 @@ hevc_qsv_decoder_select="hevc_mp4toannexb_bsf hevc_parser qsvdec hevc_qsv_hwacce
 hevc_qsv_encoder_select="qsvenc"
 huffyuv_decoder_select="bswapdsp huffyuvdsp llviddsp"
 huffyuv_encoder_select="bswapdsp huffman huffyuvencdsp llviddsp"
+rzip_encoder_select="lzo"
+#rzip_decoder_select="lzo"
 iac_decoder_select="imc_decoder"
 imc_decoder_select="bswapdsp fft mdct sinewin"
 indeo3_decoder_select="hpeldsp"
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index d85215d..d54d0ff 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -310,6 +310,8 @@ OBJS-$(CONFIG_HNM4_VIDEO_DECODER)      += hnm4video.o
 OBJS-$(CONFIG_HQ_HQA_DECODER)          += hq_hqa.o hq_hqadata.o hq_hqadsp.o \
                                           canopus.o
 OBJS-$(CONFIG_HQX_DECODER)             += hqx.o hqxvlc.o hqxdsp.o canopus.o
+OBJS-$(CONFIG_RZIP_DECODER)            += rzip.o rzipdec.o
+OBJS-$(CONFIG_RZIP_ENCODER)            += rzip.o rzipenc.o
 OBJS-$(CONFIG_HUFFYUV_DECODER)         += huffyuv.o huffyuvdec.o
 OBJS-$(CONFIG_HUFFYUV_ENCODER)         += huffyuv.o huffyuvenc.o
 OBJS-$(CONFIG_IDCIN_DECODER)           += idcinvideo.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 9f60d7c..7335b24 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -203,6 +203,7 @@ void avcodec_register_all(void)
     REGISTER_DECODER(HQ_HQA,            hq_hqa);
     REGISTER_DECODER(HQX,               hqx);
     REGISTER_ENCDEC (HUFFYUV,           huffyuv);
+    REGISTER_ENCDEC (RZIP,              rzip);
     REGISTER_DECODER(IDCIN,             idcin);
     REGISTER_DECODER(IFF_ILBM,          iff_ilbm);
     REGISTER_DECODER(INDEO2,            indeo2);
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 5e36c94..33c3cd6 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -130,6 +130,7 @@ enum AVCodecID {
     AV_CODEC_ID_SVQ3,
     AV_CODEC_ID_DVVIDEO,
     AV_CODEC_ID_HUFFYUV,
+    AV_CODEC_ID_RZIP,
     AV_CODEC_ID_CYUV,
     AV_CODEC_ID_H264,
     AV_CODEC_ID_INDEO3,
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index 9cad3e6..616069b 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -188,6 +188,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
         .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
     },
     {
+        .id        = AV_CODEC_ID_RZIP,
+        .type      = AVMEDIA_TYPE_VIDEO,
+        .name      = "RZIP",
+        .long_name = NULL_IF_CONFIG_SMALL("Rzip"),
+        .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS,
+    },
+    {
         .id        = AV_CODEC_ID_HUFFYUV,
         .type      = AVMEDIA_TYPE_VIDEO,
         .name      = "huffyuv",
diff --git a/libavcodec/rzip.c b/libavcodec/rzip.c
new file mode 100644
index 0000000..e69de29
diff --git a/libavcodec/rzip.h b/libavcodec/rzip.h
new file mode 100644
index 0000000..8a37917
--- /dev/null
+++ b/libavcodec/rzip.h
@@ -0,0 +1,14 @@
+// TODO headers
+
+#ifndef AVCODEC_RZIP_H
+#define AVCODEC_RZIP_H
+
+#include "avcodec.h" // AVClass
+
+typedef struct RzipContext {
+  AVClass *class;
+  //int rzip_gop;
+  int image_size; // decode only
+} RzipContext;
+
+#endif
diff --git a/libavcodec/rzipdec.c b/libavcodec/rzipdec.c
new file mode 100644
index 0000000..bfe2cb4
--- /dev/null
+++ b/libavcodec/rzipdec.c
@@ -0,0 +1,75 @@
+
+#include "rzip.h"
+#include "avcodec.h"
+#include "libavutil/lzo.h"
+#include "libavutil/imgutils.h" // av_image_check_size
+#include "internal.h" // ff_get_buffer 
+
+static av_cold int decode_init(AVCodecContext *avctx)
+{
+    //RzipContext *s = avctx->priv_data;
+    int ret;
+
+    ret = av_image_check_size(avctx->width, avctx->height, 0, avctx);
+    if (ret < 0)
+        return ret;
+
+    avctx->pix_fmt = AV_PIX_FMT_RGB24;
+
+    // everybody seems to just use "extradata" [XXXX adjust the main docu for the same yikes]
+    // guess its stable for height/width as well...
+    // some use the first few bytes of each frame [hrm...]
+
+    return 0;
+}
+
+static av_cold int decode_end(AVCodecContext *avctx)
+{
+    //RzipContext *s = avctx->priv_data;
+    return 0;
+}
+
+static int rzip_decode_frame(AVCodecContext *avctx, void *data, int *got_frame,
+                        AVPacket *avpkt)
+{
+    //RzipContext *s = avctx->priv_data;
+    AVFrame *frame = data;
+    const uint8_t *src = avpkt->data;
+    int inlen = avpkt->size;
+    int expected_output_size = avctx->width * avctx->height * 3; // RGB24
+    int outlen = expected_output_size;
+    int ret;
+
+    if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) // allocate us a buffer which matches expected output size, apparently
+        return ret;
+
+    ret = av_lzo1x_decode(frame->data[0], &outlen, src, &inlen);
+    if (ret < 0)
+        return ret;
+    // this outlen is actually invalid, somehow, but still works
+    // av_log(avctx, AV_LOG_INFO, "decompressed from %d to %d (uncompressed) %d\n", avpkt->size, outlen, inlen); 
+    av_log(avctx, AV_LOG_VERBOSE, "decompressed from %d to (presumably) %d\n", avpkt->size, expected_output_size);
+
+    frame->pict_type        = AV_PICTURE_TYPE_I;
+    frame->key_frame        = 1;
+
+    *got_frame = 1;
+    return outlen; // XXXX add some doc for "decode" [ret value. that is]
+}
+
+AVCodec ff_rzip_decoder = {
+    .name             = "rzip",
+    .long_name        = NULL_IF_CONFIG_SMALL("rzip [Roger zip] decoder"),
+    .type             = AVMEDIA_TYPE_VIDEO,
+    .id               = AV_CODEC_ID_RZIP,
+    .priv_data_size   = sizeof(RzipContext),
+    .init             = decode_init,
+    .close            = decode_end,
+    .decode           = rzip_decode_frame,
+    .capabilities     =  AV_CODEC_CAP_EXPERIMENTAL | AV_CODEC_CAP_LOSSLESS 
+                 | AV_CODEC_CAP_DR1  // allow customer buffer allocation, seems benign
+// TODO more? thread stuff?
+//AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DRAW_HORIZ_BAND | // TODO 
+ //                       AV_CODEC_CAP_FRAME_THREADS,
+//    .init_thread_copy = ONLY_IF_THREADS_ENABLED(decode_init_thread_copy),
+};
diff --git a/libavcodec/rzipenc.c b/libavcodec/rzipenc.c
new file mode 100644
index 0000000..aa28a50
--- /dev/null
+++ b/libavcodec/rzipenc.c
@@ -0,0 +1,106 @@
+// for now enable like configure --extra-libs=-llzo2 TODO
+// TODO requires gpl [for now, with lzo that is...]
+#include "rzip.h"
+#include "avcodec.h" // AV_CODEC_CAP_INTRA_ONLY
+#include "libavutil/opt.h" // AVOption
+#include "internal.h" // AV_CODEC_CAP_FRAME_THREADS
+#include <lzo/lzo1x.h>
+#include "libavutil/lzo.h"
+#include "libavutil/mem.h" # av_malloc
+
+static const AVOption options[] = {
+    { NULL }, // gop is universal option
+};
+
+static const AVClass rzipclass = {
+    .class_name = "rzip",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+static av_cold int encode_init(AVCodecContext *avctx)
+{
+    //RzipContext *s = avctx->priv_data;
+    // no gop stuff yet, we're all i-frame all the time
+    //s->rzip_gop = 30*10; // 10s default, assuming x264 has good values :)
+    //if (avctx->gop_size > 0)
+    //  s->rzip_gop = avctx->gop_size;
+    av_log(avctx, AV_LOG_INFO, "rzip encode_init\n");
+    return lzo_init(); // XXXX threadsafe? avoid multiples? TODO handle ret here
+}
+
+//#define USE_MORE_LZO_COMPRESSION
+
+static int rdp_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
+                        const AVFrame *frame, int *got_packet)
+{
+    //RzipContext *s = avctx->priv_data;
+    int ret;
+    lzo_uint clen = 0; // compressed length
+#ifdef USE_MORE_LZO_COMPRESSION
+    long *tmp = av_malloc(LZO1X_999_MEM_COMPRESS);
+    int using_high = 1;
+#else
+    long *tmp = av_malloc(LZO1X_1_MEM_COMPRESS); // lzo temp working space, has to be this size
+    int using_high = 0;
+#endif
+    int incoming_size = avpicture_get_size(frame->format, frame->width, frame->height);
+    if (incoming_size < 0)
+        return incoming_size;
+
+    if ((ret = ff_alloc_packet2(avctx, pkt, incoming_size + incoming_size/16 + 64 + 3, 0)) < 0) // extra data in case compression inflates it
+        return ret;
+   
+#ifdef USE_MORE_LZO_COMPRESSION
+    ret = lzo1x_999_compress(frame->data[0], incoming_size, pkt->data, &clen, tmp);
+#else
+    ret = lzo1x_1_compress(frame->data[0], incoming_size, pkt->data, &clen, tmp);
+#endif
+    av_log(avctx, AV_LOG_VERBOSE, "compressing to lzo was %d -> %lu (compressed) %d using_high=%d\n", incoming_size, clen, ret, using_high);
+    if (ret != LZO_E_OK) {
+      av_log(avctx, AV_LOG_INFO, "compression failed?");
+      return -1;
+    }
+    pkt->flags |= AV_PKT_FLAG_KEY;
+    pkt->size   = clen;
+
+
+    int outlen = incoming_size;
+    int inlen = clen;
+
+    *got_packet = 1; // I gave you a packet
+    av_free(tmp);
+
+    return 0;
+}
+
+static av_cold int encode_end(AVCodecContext *avctx)
+{
+    //RzipContext *s = avctx->priv_data;
+    return 0;
+}
+
+AVCodec ff_rzip_encoder = {
+    .name           = "rzip",
+    .long_name      = NULL_IF_CONFIG_SMALL("RZIP [Roger Zip] encoder"),
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_RZIP,
+    .priv_data_size = sizeof(RzipContext),
+    .init           = encode_init,
+    .encode2        = rdp_encode_frame,
+    .close          = encode_end,
+     // AV_CODEC_CAP_FRAME_THREADS I think means "interleae threads" or something
+     // INTRA_ONLY I think means "no inter anything" between frames [?] todo fix docu
+    .capabilities   = AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_INTRA_ONLY | 
+                      AV_CODEC_CAP_EXPERIMENTAL | AV_CODEC_CAP_LOSSLESS,
+    .priv_class     = &rzipclass,
+    .pix_fmts       = (const enum AVPixelFormat[]){
+        //AV_PIX_FMT_YUV422P, AV_PIX_FMT_RGB32, AV_PIX_FMT_NONE
+        AV_PIX_FMT_RGB24, AV_PIX_FMT_NONE // none just means "end of list"
+    },
+//    .caps_internal  = //FF_CODEC_CAP_INIT_THREADSAFE | // multiple init method calls OK
+ //                     FF_CODEC_CAP_INIT_CLEANUP | // still call close even if open failed
+  //                    AV_CODEC_CAP_DELAY, // send a NULL at the end meaning "close it up"
+};
+
diff --git a/libavfilter/formats.c b/libavfilter/formats.c
index 2b13cbf..71c433f 100644
--- a/libavfilter/formats.c
+++ b/libavfilter/formats.c
@@ -598,7 +598,7 @@ int ff_parse_pixel_format(enum AVPixelFormat *ret, const char *arg, void *log_ct
     if (pix_fmt == AV_PIX_FMT_NONE) {
         pix_fmt = strtol(arg, &tail, 0);
         if (*tail || !av_pix_fmt_desc_get(pix_fmt)) {
-            av_log(log_ctx, AV_LOG_ERROR, "Invalid pixel format '%s'\n", arg);
+            av_log(log_ctx, AV_LOG_ERROR, "Invalid pixel format '%s' hello\n", arg);
             return AVERROR(EINVAL);
         }
     }
diff --git a/libavformat/riff.c b/libavformat/riff.c
index f297dd4..13f5db6 100644
--- a/libavformat/riff.c
+++ b/libavformat/riff.c
@@ -205,6 +205,7 @@ const AVCodecTag ff_codec_bmp_tags[] = {
     { AV_CODEC_ID_MJPEG,        MKTAG('Z', 'J', 'P', 'G') },
     { AV_CODEC_ID_MJPEG,        MKTAG('M', 'M', 'J', 'P') },
     { AV_CODEC_ID_HUFFYUV,      MKTAG('H', 'F', 'Y', 'U') },
+    { AV_CODEC_ID_RZIP,         MKTAG('R', 'Z', 'I', 'P') },
     { AV_CODEC_ID_FFVHUFF,      MKTAG('F', 'F', 'V', 'H') },
     { AV_CODEC_ID_CYUV,         MKTAG('C', 'Y', 'U', 'V') },
     { AV_CODEC_ID_RAWVIDEO,     MKTAG( 0 ,  0 ,  0 ,  0 ) },


More information about the ffmpeg-devel mailing list