From bbc8ee17a859a85a4432a6fe21cf534fc88e751f Mon Sep 17 00:00:00 2001
From: Martin Vignali <martin.vignali@gmail.com>
Date: Sat, 17 Nov 2018 23:48:58 +0100
Subject: [PATCH 12/13] avcodec/proresdec : add 12b decoding

based on patch by Kieran Kunhya
---
 libavcodec/proresdec.h             |  8 +++++
 libavcodec/proresdec2.c            | 65 ++++++++++++++++++++++++++++++++++----
 tests/fate/prores.mak              |  8 ++---
 tests/ref/vsynth/vsynth3-prores    |  4 +--
 tests/ref/vsynth/vsynth3-prores_ks |  4 +--
 5 files changed, 75 insertions(+), 14 deletions(-)

diff --git a/libavcodec/proresdec.h b/libavcodec/proresdec.h
index 3d99eba078..80d00964b0 100644
--- a/libavcodec/proresdec.h
+++ b/libavcodec/proresdec.h
@@ -34,7 +34,14 @@ typedef struct {
     int ret;
 } SliceContext;
 
+enum ProresDecodingPrecision {
+    PRORES_PRECISION_AUTO = 0,
+    PRORES_PRECISION_10 = 10,
+    PRORES_PRECISION_12 = 12
+};
+
 typedef struct {
+    AVClass *class;
     BlockDSPContext bdsp;
     ProresDSPContext prodsp;
     AVFrame *frame;
@@ -50,6 +57,7 @@ typedef struct {
     const uint8_t *scan;
     int first_field;
     int alpha_info;
+    enum ProresDecodingPrecision decoding_precision; /* user value */
     void (*unpack_alpha)(GetBitContext *gb, uint16_t *dst, int num_coeffs, const int num_bits);
 } ProresContext;
 
diff --git a/libavcodec/proresdec2.c b/libavcodec/proresdec2.c
index f819f8db21..bc46f6a47a 100644
--- a/libavcodec/proresdec2.c
+++ b/libavcodec/proresdec2.c
@@ -29,6 +29,7 @@
 #define LONG_BITSTREAM_READER
 
 #include "libavutil/internal.h"
+#include "libavutil/opt.h"
 #include "avcodec.h"
 #include "get_bits.h"
 #include "idctdsp.h"
@@ -155,15 +156,31 @@ static av_cold int decode_init(AVCodecContext *avctx)
         break;
     case MKTAG('a','p','4','h'):
         avctx->profile = FF_PROFILE_PRORES_4444;
+        avctx->bits_per_raw_sample = 12;
         break;
     case MKTAG('a','p','4','x'):
         avctx->profile = FF_PROFILE_PRORES_XQ;
+        avctx->bits_per_raw_sample = 12;
         break;
     default:
         avctx->profile = FF_PROFILE_UNKNOWN;
         av_log(avctx, AV_LOG_WARNING, "Unknown prores profile %d\n", avctx->codec_tag);
     }
 
+    if (ctx->decoding_precision == PRORES_PRECISION_10) {
+        avctx->bits_per_raw_sample = 10;
+        av_log(avctx, AV_LOG_DEBUG, "Force 10b decoding");
+    } else if (ctx->decoding_precision == PRORES_PRECISION_12) {
+        avctx->bits_per_raw_sample = 12;
+        av_log(avctx, AV_LOG_DEBUG, "Force 12b decoding");
+    } else {/* auto mode, use codec tag */
+        if (avctx->bits_per_raw_sample == 10) {
+            av_log(avctx, AV_LOG_DEBUG, "Auto bitpdepth precision. Use 10b decoding based on codec tag");
+        } else { /* 12b */
+            av_log(avctx, AV_LOG_DEBUG, "Auto bitpdepth precision. Use 12b decoding based on codec tag");
+        }
+    }
+
     ff_blockdsp_init(&ctx->bdsp, avctx);
     ret = ff_proresdsp_init(&ctx->prodsp, avctx);
     if (ret < 0) {
@@ -211,6 +228,7 @@ static int decode_frame_header(ProresContext *ctx, const uint8_t *buf,
 
     width  = AV_RB16(buf + 8);
     height = AV_RB16(buf + 10);
+
     if (width != avctx->width || height != avctx->height) {
         av_log(avctx, AV_LOG_ERROR, "picture resolution change: %dx%d -> %dx%d\n",
                avctx->width, avctx->height, width, height);
@@ -237,9 +255,17 @@ static int decode_frame_header(ProresContext *ctx, const uint8_t *buf,
     }
 
     if (ctx->alpha_info) {
-        avctx->pix_fmt = (buf[12] & 0xC0) == 0xC0 ? AV_PIX_FMT_YUVA444P10 : AV_PIX_FMT_YUVA422P10;
+        if (avctx->bits_per_raw_sample == 10) {
+            avctx->pix_fmt = (buf[12] & 0xC0) == 0xC0 ? AV_PIX_FMT_YUVA444P10 : AV_PIX_FMT_YUVA422P10;
+        } else { /* 12b */
+            avctx->pix_fmt = (buf[12] & 0xC0) == 0xC0 ? AV_PIX_FMT_YUVA444P12 : AV_PIX_FMT_YUVA422P12;
+        }
     } else {
-        avctx->pix_fmt = (buf[12] & 0xC0) == 0xC0 ? AV_PIX_FMT_YUV444P10 : AV_PIX_FMT_YUV422P10;
+        if (avctx->bits_per_raw_sample == 10) {
+            avctx->pix_fmt = (buf[12] & 0xC0) == 0xC0 ? AV_PIX_FMT_YUV444P10 : AV_PIX_FMT_YUV422P10;
+        } else { /* 12b */
+            avctx->pix_fmt = (buf[12] & 0xC0) == 0xC0 ? AV_PIX_FMT_YUV444P12 : AV_PIX_FMT_YUV422P12;
+        }
     }
 
     avctx->color_primaries = buf[14];
@@ -585,6 +611,7 @@ static void decode_slice_alpha(ProresContext *ctx,
     }
 
     block = blocks;
+
     for (i = 0; i < 16; i++) {
         memcpy(dst, block, 16 * blocks_per_slice * sizeof(*dst));
         dst   += dst_stride >> 1;
@@ -606,6 +633,7 @@ static int decode_slice_thread(AVCodecContext *avctx, void *arg, int jobnr, int
     LOCAL_ALIGNED_16(int16_t, qmat_chroma_scaled,[64]);
     int mb_x_shift;
     int ret;
+    uint16_t val_no_chroma;
 
     slice->ret = -1;
     //av_log(avctx, AV_LOG_INFO, "slice %d mb width %d mb x %d y %d\n",
@@ -643,7 +671,8 @@ static int decode_slice_thread(AVCodecContext *avctx, void *arg, int jobnr, int
         chroma_stride = pic->linesize[1] << 1;
     }
 
-    if (avctx->pix_fmt == AV_PIX_FMT_YUV444P10 || avctx->pix_fmt == AV_PIX_FMT_YUVA444P10) {
+    if (avctx->pix_fmt == AV_PIX_FMT_YUV444P10 || avctx->pix_fmt == AV_PIX_FMT_YUVA444P10 ||
+        avctx->pix_fmt == AV_PIX_FMT_YUV444P12 || avctx->pix_fmt == AV_PIX_FMT_YUVA444P12) {
         mb_x_shift = 5;
         log2_chroma_blocks_per_mb = 2;
     } else {
@@ -684,18 +713,24 @@ static int decode_slice_thread(AVCodecContext *avctx, void *arg, int jobnr, int
     else {
         size_t mb_max_x = slice->mb_count << (mb_x_shift - 1);
         size_t i, j;
+        if (avctx->bits_per_raw_sample == 10) {
+            val_no_chroma = 511;
+        } else { /*12b */
+            val_no_chroma = 511 * 4;
+        }
         for (i = 0; i < 16; ++i)
             for (j = 0; j < mb_max_x; ++j) {
-                *(uint16_t*)(dest_u + (i * chroma_stride) + (j << 1)) = 511;
-                *(uint16_t*)(dest_v + (i * chroma_stride) + (j << 1)) = 511;
+                *(uint16_t*)(dest_u + (i * chroma_stride) + (j << 1)) = val_no_chroma;
+                *(uint16_t*)(dest_v + (i * chroma_stride) + (j << 1)) = val_no_chroma;
             }
     }
 
     /* decode alpha plane if available */
-    if (ctx->alpha_info && pic->data[3] && a_data_size)
+    if (ctx->alpha_info && pic->data[3] && a_data_size) {
         decode_slice_alpha(ctx, (uint16_t*)dest_a, luma_stride,
                            buf + y_data_size + u_data_size + v_data_size,
                            a_data_size, slice->mb_count);
+    }
 
     slice->ret = 0;
     return 0;
@@ -803,6 +838,23 @@ static av_cold int decode_close(AVCodecContext *avctx)
     return 0;
 }
 
+#define OFFSET(x) offsetof(ProresContext, x)
+#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM
+static const AVOption options[] = {
+    {"precision", "set decoding precision", OFFSET(decoding_precision), AV_OPT_TYPE_INT, {.i64=0}, PRORES_PRECISION_AUTO, PRORES_PRECISION_12, VD, "precision"},
+    {"auto", "use codec tag (10b for 422, 12b for 444)",  0, AV_OPT_TYPE_CONST, {.i64=PRORES_PRECISION_AUTO},               INT_MIN, INT_MAX, VD, "precision"},
+    {"10", "force 10b decoding",                          0, AV_OPT_TYPE_CONST, {.i64=PRORES_PRECISION_10},                 INT_MIN, INT_MAX, VD, "precision"},
+    {"12", "force 12b decoding",                          0, AV_OPT_TYPE_CONST, {.i64=PRORES_PRECISION_12},                 INT_MIN, INT_MAX, VD, "precision"},
+    { NULL },
+};
+
+static const AVClass prores_class = {
+    .class_name = "PRORES",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
 AVCodec ff_prores_decoder = {
     .name           = "prores",
     .long_name      = NULL_IF_CONFIG_SMALL("ProRes (iCodec Pro)"),
@@ -815,4 +867,5 @@ AVCodec ff_prores_decoder = {
     .decode         = decode_frame,
     .capabilities   = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS,
     .profiles       = NULL_IF_CONFIG_SMALL(ff_prores_profiles),
+    .priv_class     = &prores_class,
 };
diff --git a/tests/fate/prores.mak b/tests/fate/prores.mak
index dd52d68f70..b23714b65b 100644
--- a/tests/fate/prores.mak
+++ b/tests/fate/prores.mak
@@ -15,10 +15,10 @@ fate-prores-422:       CMD = framecrc -flags +bitexact -i $(TARGET_SAMPLES)/pror
 fate-prores-422_hq:    CMD = framecrc -flags +bitexact -i $(TARGET_SAMPLES)/prores/Sequence_1-Apple_ProRes_422_HQ.mov -pix_fmt yuv422p10le
 fate-prores-422_lt:    CMD = framecrc -flags +bitexact -i $(TARGET_SAMPLES)/prores/Sequence_1-Apple_ProRes_422_LT.mov -pix_fmt yuv422p10le
 fate-prores-422_proxy: CMD = framecrc -flags +bitexact -i $(TARGET_SAMPLES)/prores/Sequence_1-Apple_ProRes_422_Proxy.mov -pix_fmt yuv422p10le
-fate-prores-alpha:     CMD = framecrc -flags +bitexact -i $(TARGET_SAMPLES)/prores/Sequence_1-Apple_ProRes_with_Alpha.mov -pix_fmt yuva444p10le
-fate-prores-alpha_skip: CMD = framecrc -flags +bitexact -skip_alpha 1 -i $(TARGET_SAMPLES)/prores/Sequence_1-Apple_ProRes_with_Alpha.mov -pix_fmt yuv444p10le
-fate-prores-transparency: CMD = framecrc -flags +bitexact -i $(TARGET_SAMPLES)/prores/prores4444_with_transparency.mov -pix_fmt yuva444p10le
-fate-prores-transparency_skip: CMD = framecrc -flags +bitexact -skip_alpha 1 -i $(TARGET_SAMPLES)/prores/prores4444_with_transparency.mov -pix_fmt yuv444p10le
+fate-prores-alpha:     CMD = framecrc -flags +bitexact -precision 10 -i $(TARGET_SAMPLES)/prores/Sequence_1-Apple_ProRes_with_Alpha.mov -pix_fmt yuva444p10le
+fate-prores-alpha_skip: CMD = framecrc -flags +bitexact -precision 10 -skip_alpha 1 -i $(TARGET_SAMPLES)/prores/Sequence_1-Apple_ProRes_with_Alpha.mov -pix_fmt yuv444p10le
+fate-prores-transparency: CMD = framecrc -flags +bitexact -precision 10 -i $(TARGET_SAMPLES)/prores/prores4444_with_transparency.mov -pix_fmt yuva444p10le
+fate-prores-transparency_skip: CMD = framecrc -flags +bitexact -precision 10 -skip_alpha 1 -i $(TARGET_SAMPLES)/prores/prores4444_with_transparency.mov -pix_fmt yuv444p10le
 fate-prores-gray:      CMD = framecrc -flags +bitexact -c:a aac_fixed -i $(TARGET_SAMPLES)/prores/gray.mov -pix_fmt yuv422p10le
 
 #Test bsf prores-metadata
diff --git a/tests/ref/vsynth/vsynth3-prores b/tests/ref/vsynth/vsynth3-prores
index 2998dc1d79..367d374dc7 100644
--- a/tests/ref/vsynth/vsynth3-prores
+++ b/tests/ref/vsynth/vsynth3-prores
@@ -1,4 +1,4 @@
 6afd345a8f799d0459229349a30497cd *tests/data/fate/vsynth3-prores.mov
 105367 tests/data/fate/vsynth3-prores.mov
-fff5e7ad21d78501c8fa4749bf4bf289 *tests/data/fate/vsynth3-prores.out.rawvideo
-stddev:    2.80 PSNR: 39.17 MAXDIFF:   27 bytes:    86700/    86700
+dfd6ca041c31df20f2e7919d6c3a37d1 *tests/data/fate/vsynth3-prores.out.rawvideo
+stddev:   49.75 PSNR: 14.19 MAXDIFF:  216 bytes:    86700/    86700
diff --git a/tests/ref/vsynth/vsynth3-prores_ks b/tests/ref/vsynth/vsynth3-prores_ks
index 99cfc1397e..f23a3b65af 100644
--- a/tests/ref/vsynth/vsynth3-prores_ks
+++ b/tests/ref/vsynth/vsynth3-prores_ks
@@ -1,4 +1,4 @@
 7ceff8c9cffca766f8a167ba73dad0e2 *tests/data/fate/vsynth3-prores_ks.mov
 95053 tests/data/fate/vsynth3-prores_ks.mov
-9ab6d3e3cc7749796cd9fa984c60d890 *tests/data/fate/vsynth3-prores_ks.out.rawvideo
-stddev:    4.09 PSNR: 35.88 MAXDIFF:   35 bytes:    86700/    86700
+4c9b4a247982be95990b7450a36de31b *tests/data/fate/vsynth3-prores_ks.out.rawvideo
+stddev:   49.48 PSNR: 14.24 MAXDIFF:  211 bytes:    86700/    86700
-- 
2.14.3 (Apple Git-98)

