]> git.sesse.net Git - ffmpeg/blobdiff - libavcodec/cfhdenc.c
doc/filters: Documentation to add sess_config option for tensorflow backend
[ffmpeg] / libavcodec / cfhdenc.c
index 10cf7c03ac1efb096c6b8f62e906e4a5dff72baf..e0b1172ede61a8362a7d1ca4de4aa3f0fa30299f 100644 (file)
@@ -33,6 +33,7 @@
 #include "avcodec.h"
 #include "bytestream.h"
 #include "cfhd.h"
+#include "cfhdencdsp.h"
 #include "put_bits.h"
 #include "internal.h"
 #include "thread.h"
@@ -94,8 +95,11 @@ static const unsigned codebook[256][2] = {
 };
 
 /* Derived by extracting runcodes from existing tables from decoder */
-static const uint16_t runbook[8][3] = {
-    {1,  0x0000,   1}, {7,  0x0069,  12}, {8,  0x00D1,  20}, {9,  0x018A,  32},
+static const uint16_t runbook[18][3] = {
+    {1,  0x0000,   1}, {2,  0x0000,   2}, {3,  0x0000,   3}, {4,  0x0000,   4},
+    {5,  0x0000,   5}, {6,  0x0000,   6}, {7,  0x0000,   7}, {8,  0x0000,   8},
+    {9,  0x0000,   9}, {10, 0x0000,  10}, {11, 0x0000,  11},
+    {7,  0x0069,  12}, {8,  0x00D1,  20}, {9,  0x018A,  32},
     {10, 0x0343,  60}, {11, 0x0685, 100}, {13, 0x18BF, 180}, {13, 0x1BA5, 320},
 };
 
@@ -103,84 +107,96 @@ static const uint16_t runbook[8][3] = {
  * Derived by inspecting various quality encodes
  * and adding some more from scratch.
  */
-static const uint16_t quantization_per_subband[2][3][11][9] = {
+static const uint16_t quantization_per_subband[2][3][13][9] = {
     {{
         { 16, 16,  8,  4,  4,  2,   6,   6,   9, }, // film3+
         { 16, 16,  8,  4,  4,  2,   6,   6,   9, }, // film3
-        { 16, 16,  8,  4,  4,  2,   8,   8,  12, }, // film2+
+        { 16, 16,  8,  4,  4,  2,   7,   7,  10, }, // film2+
         { 16, 16,  8,  4,  4,  2,   8,   8,  12, }, // film2
+        { 16, 16,  8,  4,  4,  2,  16,  16,  26, }, // film1++
         { 24, 24, 12,  6,  6,  3,  24,  24,  36, }, // film1+
         { 24, 24, 12,  6,  6,  3,  24,  24,  36, }, // film1
         { 32, 32, 24,  8,  8,  6,  32,  32,  48, }, // high+
         { 32, 32, 24,  8,  8,  6,  32,  32,  48, }, // high
         { 48, 48, 32, 12, 12,  8,  64,  64,  96, }, // medium+
         { 48, 48, 32, 12, 12,  8,  64,  64,  96, }, // medium
+        { 64, 64, 48, 16, 16, 12,  96,  96, 144, }, // low+
         { 64, 64, 48, 16, 16, 12, 128, 128, 192, }, // low
     },
     {
-        { 16, 16,  8,  4,  4,  2,   6,   6,   9, },
-        { 16, 16,  8,  4,  4,  2,   6,   6,  12, },
-        { 16, 16,  8,  4,  4,  2,   8,   8,  12, },
-        { 16, 16,  8,  4,  4,  2,   8,   8,  16, },
-        { 24, 24, 12,  6,  6,  3,  24,  24,  36, },
-        { 24, 24, 12,  6,  6,  3,  24,  24,  48, },
-        { 32, 32, 24,  8,  8,  6,  32,  32,  48, },
-        { 48, 48, 32, 12, 12,  8,  32,  32,  64, },
-        { 48, 48, 32, 12, 12,  8,  64,  64,  96, },
-        { 48, 48, 32, 12, 12,  8,  64,  64, 128, },
-        { 64, 64, 48, 16, 16, 12, 128, 128, 192, },
+        { 16, 16,  8,  4,  4,  2,   6,   6,   9, }, // film3+
+        { 16, 16,  8,  4,  4,  2,   6,   6,  12, }, // film3
+        { 16, 16,  8,  4,  4,  2,   7,   7,  14, }, // film2+
+        { 16, 16,  8,  4,  4,  2,   8,   8,  16, }, // film2
+        { 16, 16,  8,  4,  4,  2,  16,  16,  26, }, // film1++
+        { 24, 24, 12,  6,  6,  3,  24,  24,  36, }, // film1+
+        { 24, 24, 12,  6,  6,  3,  24,  24,  48, }, // film1
+        { 32, 32, 24,  8,  8,  6,  32,  32,  48, }, // high+
+        { 48, 48, 32, 12, 12,  8,  32,  32,  64, }, // high
+        { 48, 48, 32, 12, 12,  8,  64,  64,  96, }, // medium+
+        { 48, 48, 32, 12, 12,  8,  64,  64, 128, }, // medium
+        { 64, 64, 48, 16, 16, 12,  96,  96, 160, }, // low+
+        { 64, 64, 48, 16, 16, 12, 128, 128, 192, }, // low
     },
     {
-        { 16, 16,  8,  4,  4,  2,   6,   6,   9, },
-        { 16, 16,  8,  4,  4,  2,   6,   6,  12, },
-        { 16, 16,  8,  4,  4,  2,   8,   8,  12, },
-        { 16, 16,  8,  4,  4,  2,   8,   8,  16, },
-        { 24, 24, 12,  6,  6,  3,  24,  24,  36, },
-        { 24, 24, 12,  6,  6,  3,  24,  24,  48, },
-        { 32, 32, 24,  8,  8,  6,  32,  32,  48, },
-        { 48, 48, 32, 12, 12,  8,  32,  32,  64, },
-        { 48, 48, 32, 12, 12,  8,  64,  64,  96, },
-        { 48, 48, 32, 12, 12,  8,  64,  64, 128, },
-        { 64, 64, 48, 16, 16, 12, 128, 128, 192, },
+        { 16, 16,  8,  4,  4,  2,   6,   6,   9, }, // film3+
+        { 16, 16,  8,  4,  4,  2,   6,   6,  12, }, // film3
+        { 16, 16,  8,  4,  4,  2,   7,   7,  14, }, // film2+
+        { 16, 16,  8,  4,  4,  2,   8,   8,  16, }, // film2
+        { 16, 16,  8,  4,  4,  2,  16,  16,  26, }, // film1++
+        { 24, 24, 12,  6,  6,  3,  24,  24,  36, }, // film1+
+        { 24, 24, 12,  6,  6,  3,  24,  24,  48, }, // film1
+        { 32, 32, 24,  8,  8,  6,  32,  32,  48, }, // high+
+        { 48, 48, 32, 12, 12,  8,  32,  32,  64, }, // high
+        { 48, 48, 32, 12, 12,  8,  64,  64,  96, }, // medium+
+        { 48, 48, 32, 12, 12,  8,  64,  64, 128, }, // medium
+        { 64, 64, 48, 16, 16, 12,  96,  96, 160, }, // low+
+        { 64, 64, 48, 16, 16, 12, 128, 128, 192, }, // low
     }},
     {{
-        { 16, 16,  8, 16, 16,  8,  24,  24,  36, },
-        { 16, 16,  8, 16, 16,  8,  32,  32,  48, },
-        { 16, 16,  8, 16, 16,  8,  48,  48,  72, },
-        { 16, 16,  8, 16, 16,  8,  64,  64,  96, },
-        { 24, 24, 12, 24, 24, 12,  96,  96, 144, },
-        { 24, 24, 12, 24, 24, 12, 192, 192, 288, },
-        { 32, 32, 24, 32, 32, 24, 128, 128, 192, },
-        { 32, 32, 24, 32, 32, 24, 256, 256, 384, },
-        { 48, 48, 32, 48, 48, 32, 256, 256, 384, },
-        { 48, 48, 32, 48, 48, 32, 512, 512, 768, },
-        { 64, 64, 48, 64, 64, 48, 512, 512, 768, },
+        { 16, 16,  8, 16, 16,  8,  24,  24,  36, }, // film3+
+        { 16, 16,  8, 16, 16,  8,  24,  24,  36, }, // film3
+        { 16, 16,  8, 16, 16,  8,  32,  32,  48, }, // film2+
+        { 16, 16,  8, 16, 16,  8,  32,  32,  48, }, // film2
+        { 16, 16,  8, 20, 20, 10,  80,  80, 128, }, // film1++
+        { 24, 24, 12, 24, 24, 12,  96,  96, 144, }, // film1+
+        { 24, 24, 12, 24, 24, 12,  96,  96, 144, }, // film1
+        { 32, 32, 24, 32, 32, 24, 128, 128, 192, }, // high+
+        { 32, 32, 24, 32, 32, 24, 128, 128, 192, }, // high
+        { 48, 48, 32, 48, 48, 32, 256, 256, 384, }, // medium+
+        { 48, 48, 32, 48, 48, 32, 256, 256, 384, }, // medium
+        { 56, 56, 40, 56, 56, 40, 512, 512, 768, }, // low+
+        { 64, 64, 48, 64, 64, 48, 512, 512, 768, }, // low
     },
     {
-        { 16, 16,  8, 16, 16,  8,  24,  24,  36, },
-        { 16, 16,  8, 16, 16,  8,  32,  32,  48, },
-        { 16, 16,  8, 16, 16,  8,  48,  48,  72, },
-        { 16, 16,  8, 16, 16,  8,  64,  64,  96, },
-        { 24, 24, 12, 24, 24, 12,  96,  96, 144, },
-        { 24, 24, 12, 24, 24, 12, 192, 192, 288, },
-        { 32, 32, 24, 32, 32, 24, 128, 128, 192, },
-        { 32, 32, 24, 32, 32, 24, 256, 256, 384, },
-        { 48, 48, 32, 48, 48, 32, 256, 256, 384, },
-        { 48, 48, 32, 48, 48, 32, 512, 512, 768, },
-        { 64, 64, 48, 64, 64, 48, 512, 512, 768, },
+        { 16, 16,  8, 16, 16,  8,  24,  24,  36, }, // film3+
+        { 16, 16,  8, 16, 16,  8,  48,  48,  72, }, // film3
+        { 16, 16,  8, 16, 16,  8,  48,  48,  72, }, // film2+
+        { 16, 16,  8, 16, 16,  8,  64,  64,  96, }, // film2
+        { 16, 16,  8, 20, 20, 10,  80,  80, 128, }, // film1++
+        { 24, 24, 12, 24, 24, 12,  96,  96, 144, }, // film1+
+        { 24, 24, 12, 24, 24, 12, 192, 192, 288, }, // film1
+        { 32, 32, 24, 32, 32, 24, 128, 128, 192, }, // high+
+        { 32, 32, 24, 32, 32, 24, 256, 256, 384, }, // high
+        { 48, 48, 32, 48, 48, 32, 256, 256, 384, }, // medium+
+        { 48, 48, 32, 48, 48, 32, 512, 512, 768, }, // medium
+        { 56, 56, 40, 56, 56, 40, 512, 512, 768, }, // low+
+        { 64, 64, 48, 64, 64, 48,1024,1024,1536, }, // low
     },
     {
-        { 16, 16,  8, 16, 16,  8,  24,  24,  36, },
-        { 16, 16,  8, 16, 16,  8,  32,  32,  48, },
-        { 16, 16,  8, 16, 16,  8,  48,  48,  72, },
-        { 16, 16,  8, 16, 16,  8,  64,  64,  96, },
-        { 24, 24, 12, 24, 24, 12,  96,  96, 144, },
-        { 24, 24, 12, 24, 24, 12, 192, 192, 288, },
-        { 32, 32, 24, 32, 32, 24, 128, 128, 192, },
-        { 32, 32, 24, 32, 32, 24, 256, 256, 384, },
-        { 48, 48, 32, 48, 48, 32, 256, 256, 384, },
-        { 48, 48, 32, 48, 48, 32, 512, 512, 768, },
-        { 64, 64, 48, 64, 64, 48, 512, 512, 768, },
+        { 16, 16,  8, 16, 16,  8,  24,  24,  36, }, // film3+
+        { 16, 16,  8, 16, 16,  8,  48,  48,  72, }, // film3
+        { 16, 16,  8, 16, 16,  8,  48,  48,  72, }, // film2+
+        { 16, 16,  8, 16, 16,  8,  64,  64,  96, }, // film2
+        { 16, 16, 10, 20, 20, 10,  80,  80, 128, }, // film1++
+        { 24, 24, 12, 24, 24, 12,  96,  96, 144, }, // film1+
+        { 24, 24, 12, 24, 24, 12, 192, 192, 288, }, // film1
+        { 32, 32, 24, 32, 32, 24, 128, 128, 192, }, // high+
+        { 32, 32, 24, 32, 32, 24, 256, 256, 384, }, // high
+        { 48, 48, 32, 48, 48, 32, 256, 256, 384, }, // medium+
+        { 48, 48, 32, 48, 48, 32, 512, 512, 768, }, // medium
+        { 56, 56, 40, 56, 56, 40, 512, 512, 768, }, // low+
+        { 64, 64, 48, 64, 64, 48,1024,1024,1536, }, // low
     }},
 };
 
@@ -209,17 +225,23 @@ typedef struct PlaneEnc {
 } PlaneEnc;
 
 typedef struct CFHDEncContext {
+    const AVClass *class;
+
     PutBitContext       pb;
     PutByteContext      pby;
 
-    int compression;
+    int quality;
     int planes;
     int chroma_h_shift;
     int chroma_v_shift;
     PlaneEnc plane[4];
 
+    uint16_t lut[1024];
     Runbook  rb[321];
     Codebook cb[513];
+    int16_t *alpha;
+
+    CFHDEncDSPContext dsp;
 } CFHDEncContext;
 
 static av_cold int cfhd_encode_init(AVCodecContext *avctx)
@@ -228,7 +250,7 @@ static av_cold int cfhd_encode_init(AVCodecContext *avctx)
     const int sign_mask = 256;
     const int twos_complement = -sign_mask;
     const int mag_mask = sign_mask - 1;
-    int ret;
+    int ret, last = 0;
 
     ret = av_pix_fmt_get_chroma_sub_sample(avctx->pix_fmt,
                                            &s->chroma_h_shift,
@@ -242,21 +264,16 @@ static av_cold int cfhd_encode_init(AVCodecContext *avctx)
     }
 
     s->planes = av_pix_fmt_count_planes(avctx->pix_fmt);
-    s->compression = avctx->compression_level;
-
-    if (s->compression == FF_COMPRESSION_DEFAULT)
-        s->compression = 2;
-    s->compression = av_clip(s->compression, 0, 10);
 
     for (int i = 0; i < s->planes; i++) {
         int w8, h8, w4, h4, w2, h2;
         int width  = i ? avctx->width >> s->chroma_h_shift : avctx->width;
         int height = i ? FFALIGN(avctx->height >> s->chroma_v_shift, 8) :
                          FFALIGN(avctx->height >> s->chroma_v_shift, 8);
-        ptrdiff_t stride = FFALIGN(width / 8, 8) * 8;
+        ptrdiff_t stride = (FFALIGN(width / 8, 8) + 64) * 8;
 
-        w8 = FFALIGN(width / 8, 8);
-        h8 = height / 8;
+        w8 = FFALIGN(width / 8, 8) + 64;
+        h8 = FFALIGN(height, 8) / 8;
         w4 = w8 * 2;
         h4 = h8 * 2;
         w2 = w4 * 2;
@@ -318,7 +335,7 @@ static av_cold int cfhd_encode_init(AVCodecContext *avctx)
 
     s->rb[0].run = 0;
 
-    for (int i = 1, j = 0; i < 320 && j < 7; j++) {
+    for (int i = 1, j = 0; i < 320 && j < 17; j++) {
         int run = runbook[j][2];
         int end = runbook[j+1][2];
 
@@ -329,57 +346,42 @@ static av_cold int cfhd_encode_init(AVCodecContext *avctx)
         }
     }
 
-    s->rb[320].bits = runbook[7][1];
-    s->rb[320].size = runbook[7][0];
+    s->rb[320].bits = runbook[17][1];
+    s->rb[320].size = runbook[17][0];
     s->rb[320].run = 320;
 
-    return 0;
-}
+    for (int i = 0; i < 256; i++) {
+        int idx = i + ((768LL * i * i * i) / (256 * 256 * 256));
 
-static inline void filter(int16_t *input, ptrdiff_t in_stride,
-                          int16_t *low, ptrdiff_t low_stride,
-                          int16_t *high, ptrdiff_t high_stride,
-                          int len)
-{
-    low[(0>>1) * low_stride]   = av_clip_int16(input[0*in_stride] + input[1*in_stride]);
-    high[(0>>1) * high_stride] = av_clip_int16((5 * input[0*in_stride] - 11 * input[1*in_stride] +
-                                                4 * input[2*in_stride] +  4 * input[3*in_stride] -
-                                                1 * input[4*in_stride] -  1 * input[5*in_stride] + 4) >> 3);
-
-    for (int i = 2; i < len - 2; i += 2) {
-        low[(i>>1) * low_stride]   = av_clip_int16(input[i*in_stride] + input[(i+1)*in_stride]);
-        high[(i>>1) * high_stride] = av_clip_int16(((-input[(i-2)*in_stride] - input[(i-1)*in_stride] +
-                                                      input[(i+2)*in_stride] + input[(i+3)*in_stride] + 4) >> 3) +
-                                                      input[(i+0)*in_stride] - input[(i+1)*in_stride]);
+        s->lut[idx] = i;
+    }
+    for (int i = 0; i < 1024; i++) {
+        if (s->lut[i])
+            last = s->lut[i];
+        else
+            s->lut[i] = last;
     }
 
-    low[((len-2)>>1) * low_stride]   = av_clip_int16(input[((len-2)+0)*in_stride] + input[((len-2)+1)*in_stride]);
-    high[((len-2)>>1) * high_stride] = av_clip_int16((11* input[((len-2)+0)*in_stride] - 5 * input[((len-2)+1)*in_stride] -
-                                                      4 * input[((len-2)-1)*in_stride] - 4 * input[((len-2)-2)*in_stride] +
-                                                      1 * input[((len-2)-3)*in_stride] + 1 * input[((len-2)-4)*in_stride] + 4) >> 3);
-}
+    ff_cfhdencdsp_init(&s->dsp);
 
-static void horiz_filter(int16_t *input, int16_t *low, int16_t *high,
-                         int width)
-{
-    filter(input, 1, low, 1, high, 1, width);
-}
+    if (s->planes != 4)
+        return 0;
 
-static void vert_filter(int16_t *input, ptrdiff_t in_stride,
-                        int16_t *low, ptrdiff_t low_stride,
-                        int16_t *high, ptrdiff_t high_stride, int len)
-{
-    filter(input, in_stride, low, low_stride, high, high_stride, len);
+    s->alpha = av_calloc(avctx->width * avctx->height, sizeof(*s->alpha));
+    if (!s->alpha)
+        return AVERROR(ENOMEM);
+
+    return 0;
 }
 
 static void quantize_band(int16_t *input, int width, int a_width,
                           int height, unsigned quantization)
 {
-    const int factor = (1 << 16) / quantization;
+    const int16_t factor = (uint32_t)(1U << 15) / quantization;
 
     for (int i = 0; i < height; i++) {
         for (int j = 0; j < width; j++)
-            input[j] = av_clip_intp2((input[j] * factor) / 65536, 8);
+            input[j] = av_clip_intp2(((input[j] * factor + 16384 * FFSIGN(input[j])) / 32768), 10);
         input += a_width;
     }
 }
@@ -396,18 +398,41 @@ static int put_runcode(PutBitContext *pb, int count, const Runbook *const rb)
     return 0;
 }
 
+static void process_alpha(const int16_t *src, int width, int height, ptrdiff_t stride, int16_t *dst)
+{
+    for (int i = 0; i < height; i++) {
+        for (int j = 0; j < width; j++) {
+            int alpha = src[j];
+
+            if (alpha > 0 && alpha < 4080) {
+                alpha *= 223;
+                alpha += 128;
+                alpha >>= 8;
+                alpha += 256;
+            }
+
+            dst[j] = av_clip_uintp2(alpha, 12);
+        }
+
+        src += stride;
+        dst += width;
+    }
+}
+
 static int cfhd_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
                              const AVFrame *frame, int *got_packet)
 {
     CFHDEncContext *s = avctx->priv_data;
+    CFHDEncDSPContext *dsp = &s->dsp;
     PutByteContext *pby = &s->pby;
     PutBitContext *pb = &s->pb;
     const Codebook *const cb = s->cb;
     const Runbook *const rb = s->rb;
+    const uint16_t *lut = s->lut;
     unsigned pos;
-    int ret = 0;
+    int ret;
 
-    for (int plane = 0; plane < s->planes && !ret; plane++) {
+    for (int plane = 0; plane < s->planes; plane++) {
         int width = s->plane[plane].band[2][0].width;
         int a_width = s->plane[plane].band[2][0].a_width;
         int height = s->plane[plane].band[2][0].height;
@@ -415,39 +440,37 @@ static int cfhd_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
         int16_t *input = (int16_t *)frame->data[act_plane];
         int16_t *low = s->plane[plane].l_h[6];
         int16_t *high = s->plane[plane].l_h[7];
-        const ptrdiff_t in_stride = frame->linesize[act_plane] / 2;
+        ptrdiff_t in_stride = frame->linesize[act_plane] / 2;
         int low_stride, high_stride;
 
-        for (int i = 0; i < height * 2; i++) {
-            horiz_filter(input, low, high, width * 2);
-            input += in_stride;
-            low += a_width;
-            high += a_width;
+        if (plane == 3) {
+            process_alpha(input, avctx->width, avctx->height,
+                          in_stride, s->alpha);
+            input = s->alpha;
+            in_stride = avctx->width;
         }
 
+        dsp->horiz_filter(input, low, high,
+                          in_stride, a_width, a_width,
+                          width * 2, height * 2);
+
         input = s->plane[plane].l_h[7];
         low = s->plane[plane].subband[7];
         low_stride = s->plane[plane].band[2][0].a_width;
         high = s->plane[plane].subband[9];
         high_stride = s->plane[plane].band[2][0].a_width;
 
-        for (int i = 0; i < width; i++) {
-            vert_filter(input, a_width, low, low_stride, high, high_stride, height * 2);
-            input++;
-            low++;
-            high++;
-        }
+        dsp->vert_filter(input, low, high,
+                         a_width, low_stride, high_stride,
+                         width, height * 2);
 
         input = s->plane[plane].l_h[6];
         low = s->plane[plane].l_h[7];
         high = s->plane[plane].subband[8];
 
-        for (int i = 0; i < width; i++) {
-            vert_filter(input, a_width, low, low_stride, high, high_stride, height * 2);
-            input++;
-            low++;
-            high++;
-        }
+        dsp->vert_filter(input, low, high,
+                         a_width, low_stride, high_stride,
+                         width, height * 2);
 
         a_width = s->plane[plane].band[1][0].a_width;
         width = s->plane[plane].band[1][0].width;
@@ -465,34 +488,25 @@ static int cfhd_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
         }
 
         input = s->plane[plane].l_h[7];
-        for (int i = 0; i < height * 2; i++) {
-            horiz_filter(input, low, high, width * 2);
-            input += a_width * 2;
-            low += low_stride;
-            high += high_stride;
-        }
+        dsp->horiz_filter(input, low, high,
+                          a_width * 2, low_stride, high_stride,
+                          width * 2, height * 2);
 
         input = s->plane[plane].l_h[4];
         low = s->plane[plane].subband[4];
         high = s->plane[plane].subband[6];
 
-        for (int i = 0; i < width; i++) {
-            vert_filter(input, a_width, low, low_stride, high, high_stride, height * 2);
-            input++;
-            low++;
-            high++;
-        }
+        dsp->vert_filter(input, low, high,
+                         a_width, low_stride, high_stride,
+                         width, height * 2);
 
         input = s->plane[plane].l_h[3];
         low = s->plane[plane].l_h[4];
         high = s->plane[plane].subband[5];
 
-        for (int i = 0; i < width; i++) {
-            vert_filter(input, a_width, low, low_stride, high, high_stride, height * 2);
-            input++;
-            low++;
-            high++;
-        }
+        dsp->vert_filter(input, low, high,
+                         a_width, low_stride, high_stride,
+                         width, height * 2);
 
         a_width = s->plane[plane].band[0][0].a_width;
         width = s->plane[plane].band[0][0].width;
@@ -512,37 +526,28 @@ static int cfhd_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
         }
 
         input = s->plane[plane].l_h[4];
-        for (int i = 0; i < height * 2; i++) {
-            horiz_filter(input, low, high, width * 2);
-            input += a_width * 2;
-            low += low_stride;
-            high += high_stride;
-        }
+        dsp->horiz_filter(input, low, high,
+                          a_width * 2, low_stride, high_stride,
+                          width * 2, height * 2);
 
         low = s->plane[plane].subband[1];
         high = s->plane[plane].subband[3];
         input = s->plane[plane].l_h[1];
 
-        for (int i = 0; i < width; i++) {
-            vert_filter(input, a_width, low, low_stride, high, high_stride, height * 2);
-            input++;
-            low++;
-            high++;
-        }
+        dsp->vert_filter(input, low, high,
+                         a_width, low_stride, high_stride,
+                         width, height * 2);
 
         low = s->plane[plane].subband[0];
         high = s->plane[plane].subband[2];
         input = s->plane[plane].l_h[0];
 
-        for (int i = 0; i < width; i++) {
-            vert_filter(input, a_width, low, low_stride, high, high_stride, height * 2);
-            input++;
-            low++;
-            high++;
-        }
+        dsp->vert_filter(input, low, high,
+                         a_width, low_stride, high_stride,
+                         width, height * 2);
     }
 
-    ret = ff_alloc_packet2(avctx, pkt, 60LL + s->planes * (2LL * avctx->width * avctx->height + 1000LL), 0);
+    ret = ff_alloc_packet2(avctx, pkt, 64LL + s->planes * (2LL * avctx->width * avctx->height + 1000LL), 0);
     if (ret < 0)
         return ret;
 
@@ -567,7 +572,7 @@ static int cfhd_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
     bytestream2_put_be16(pby, s->planes);
 
     bytestream2_put_be16(pby, EncodedFormat);
-    bytestream2_put_be16(pby, avctx->pix_fmt == AV_PIX_FMT_YUV422P10 ? 1 : 3);
+    bytestream2_put_be16(pby, avctx->pix_fmt == AV_PIX_FMT_YUV422P10 ? 1 : 3 + (s->planes == 4));
 
     bytestream2_put_be16(pby, WaveletCount);
     bytestream2_put_be16(pby, 3);
@@ -587,6 +592,9 @@ static int cfhd_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
     bytestream2_put_be16(pby, ImageHeight);
     bytestream2_put_be16(pby, avctx->height);
 
+    bytestream2_put_be16(pby, -FrameNumber);
+    bytestream2_put_be16(pby, frame->pts & 0xFFFF);
+
     bytestream2_put_be16(pby, Precision);
     bytestream2_put_be16(pby, avctx->pix_fmt == AV_PIX_FMT_YUV422P10 ? 10 : 12);
 
@@ -650,7 +658,7 @@ static int cfhd_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
 
         for (int l = 0; l < 3; l++) {
             for (int i = 0; i < 3; i++) {
-                s->plane[p].quantization[1 + l * 3 + i] = quantization_per_subband[avctx->pix_fmt != AV_PIX_FMT_YUV422P10][p][s->compression][l * 3 + i];
+                s->plane[p].quantization[1 + l * 3 + i] = quantization_per_subband[avctx->pix_fmt != AV_PIX_FMT_YUV422P10][p >= 3 ? 0 : p][s->quality][l * 3 + i];
             }
         }
 
@@ -704,7 +712,7 @@ static int cfhd_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
                 bytestream2_put_be16(pby, i + 1);
 
                 bytestream2_put_be16(pby, BandCodingFlags);
-                bytestream2_put_be16(pby, 2);
+                bytestream2_put_be16(pby, 1);
 
                 bytestream2_put_be16(pby, BandWidth);
                 bytestream2_put_be16(pby, width);
@@ -734,7 +742,7 @@ static int cfhd_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
 
                 for (int m = 0; m < height; m++) {
                     for (int j = 0; j < stride; j++) {
-                        int16_t index = data[j];
+                        int16_t index = j >= width ? 0 : FFSIGN(data[j]) * lut[FFABS(data[j])];
 
                         if (index < 0)
                             index += 512;
@@ -758,7 +766,7 @@ static int cfhd_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
                 put_bits(pb, cb[512].size, cb[512].bits);
 
                 flush_put_bits(pb);
-                bytestream2_skip_p(pby, put_bits_count(pb) >> 3);
+                bytestream2_skip_p(pby, put_bytes_output(pb));
                 padd = (4 - (bytestream2_tell_p(pby) & 3)) & 3;
                 while (padd--)
                     bytestream2_put_byte(pby, 0);
@@ -805,15 +813,45 @@ static av_cold int cfhd_encode_close(AVCodecContext *avctx)
             s->plane[i].l_h[j] = NULL;
     }
 
+    av_freep(&s->alpha);
+
     return 0;
 }
 
-AVCodec ff_cfhd_encoder = {
+#define OFFSET(x) offsetof(CFHDEncContext, x)
+#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+    { "quality", "set quality", OFFSET(quality), AV_OPT_TYPE_INT,   {.i64= 0}, 0, 12, VE, "q" },
+    { "film3+",   NULL,         0,               AV_OPT_TYPE_CONST, {.i64= 0}, 0,  0, VE, "q" },
+    { "film3",    NULL,         0,               AV_OPT_TYPE_CONST, {.i64= 1}, 0,  0, VE, "q" },
+    { "film2+",   NULL,         0,               AV_OPT_TYPE_CONST, {.i64= 2}, 0,  0, VE, "q" },
+    { "film2",    NULL,         0,               AV_OPT_TYPE_CONST, {.i64= 3}, 0,  0, VE, "q" },
+    { "film1.5",  NULL,         0,               AV_OPT_TYPE_CONST, {.i64= 4}, 0,  0, VE, "q" },
+    { "film1+",   NULL,         0,               AV_OPT_TYPE_CONST, {.i64= 5}, 0,  0, VE, "q" },
+    { "film1",    NULL,         0,               AV_OPT_TYPE_CONST, {.i64= 6}, 0,  0, VE, "q" },
+    { "high+",    NULL,         0,               AV_OPT_TYPE_CONST, {.i64= 7}, 0,  0, VE, "q" },
+    { "high",     NULL,         0,               AV_OPT_TYPE_CONST, {.i64= 8}, 0,  0, VE, "q" },
+    { "medium+",  NULL,         0,               AV_OPT_TYPE_CONST, {.i64= 9}, 0,  0, VE, "q" },
+    { "medium",   NULL,         0,               AV_OPT_TYPE_CONST, {.i64=10}, 0,  0, VE, "q" },
+    { "low+",     NULL,         0,               AV_OPT_TYPE_CONST, {.i64=11}, 0,  0, VE, "q" },
+    { "low",      NULL,         0,               AV_OPT_TYPE_CONST, {.i64=12}, 0,  0, VE, "q" },
+    { NULL},
+};
+
+static const AVClass cfhd_class = {
+    .class_name = "cfhd",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+const AVCodec ff_cfhd_encoder = {
     .name             = "cfhd",
-    .long_name        = NULL_IF_CONFIG_SMALL("Cineform HD"),
+    .long_name        = NULL_IF_CONFIG_SMALL("GoPro CineForm HD"),
     .type             = AVMEDIA_TYPE_VIDEO,
     .id               = AV_CODEC_ID_CFHD,
     .priv_data_size   = sizeof(CFHDEncContext),
+    .priv_class       = &cfhd_class,
     .init             = cfhd_encode_init,
     .close            = cfhd_encode_close,
     .encode2          = cfhd_encode_frame,
@@ -821,6 +859,8 @@ AVCodec ff_cfhd_encoder = {
     .pix_fmts         = (const enum AVPixelFormat[]) {
                           AV_PIX_FMT_YUV422P10,
                           AV_PIX_FMT_GBRP12,
+                          AV_PIX_FMT_GBRAP12,
                           AV_PIX_FMT_NONE
                         },
+    .caps_internal    = FF_CODEC_CAP_INIT_CLEANUP,
 };