]> git.sesse.net Git - ffmpeg/blobdiff - libavfilter/vf_histogram.c
Merge commit '4dc9f00365853b1239936d771856cf1881b5c5ce'
[ffmpeg] / libavfilter / vf_histogram.c
index 31004b71dbffe3389f3a39dd900ce87d26c57cd8..443cbda66069e16decf5a1d73665bc799377d03c 100644 (file)
@@ -22,6 +22,8 @@
 #include "libavutil/opt.h"
 #include "libavutil/parseutils.h"
 #include "libavutil/pixdesc.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/intreadwrite.h"
 #include "avfilter.h"
 #include "formats.h"
 #include "internal.h"
@@ -38,7 +40,9 @@ enum HistogramMode {
 typedef struct HistogramContext {
     const AVClass *class;               ///< AVClass context for log and options purpose
     int mode;                           ///< HistogramMode
-    unsigned       histogram[256];
+    unsigned       histogram[256*256];
+    int            histogram_size;
+    int            mult;
     int            ncomp;
     const uint8_t  *bg_color;
     const uint8_t  *fg_color;
@@ -49,7 +53,10 @@ typedef struct HistogramContext {
     int            waveform_mirror;
     int            display_mode;
     int            levels_mode;
-    const AVPixFmtDescriptor *desc;
+    const AVPixFmtDescriptor *desc, *odesc;
+    int            components;
+    int            planewidth[4];
+    int            planeheight[4];
 } HistogramContext;
 
 #define OFFSET(x) offsetof(HistogramContext, x)
@@ -74,6 +81,7 @@ static const AVOption histogram_options[] = {
     { "levels_mode", "set levels mode", OFFSET(levels_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "levels_mode"},
     { "linear",      NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "levels_mode" },
     { "logarithmic", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "levels_mode" },
+    { "components", "set color components to display", OFFSET(components), AV_OPT_TYPE_INT, {.i64=7}, 1, 15, FLAGS},
     { NULL }
 };
 
@@ -84,9 +92,50 @@ static const enum AVPixelFormat color_pix_fmts[] = {
     AV_PIX_FMT_NONE
 };
 
-static const enum AVPixelFormat levels_pix_fmts[] = {
-    AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVJ444P,
-    AV_PIX_FMT_GRAY8, AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, AV_PIX_FMT_NONE
+static const enum AVPixelFormat levels_in_pix_fmts[] = {
+    AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P,
+    AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVJ422P,
+    AV_PIX_FMT_YUV411P,  AV_PIX_FMT_YUVJ411P,
+    AV_PIX_FMT_YUV440P,  AV_PIX_FMT_YUV410P,
+    AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P,
+    AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
+    AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9,
+    AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
+    AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10,
+    AV_PIX_FMT_GBRAP,    AV_PIX_FMT_GBRP,
+    AV_PIX_FMT_GBRP9,    AV_PIX_FMT_GBRP10,
+    AV_PIX_FMT_GRAY8,
+    AV_PIX_FMT_NONE
+};
+
+static const enum AVPixelFormat levels_out_yuv8_pix_fmts[] = {
+    AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P,
+    AV_PIX_FMT_NONE
+};
+
+static const enum AVPixelFormat levels_out_yuv9_pix_fmts[] = {
+    AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUV444P9,
+    AV_PIX_FMT_NONE
+};
+
+static const enum AVPixelFormat levels_out_yuv10_pix_fmts[] = {
+    AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUV444P10,
+    AV_PIX_FMT_NONE
+};
+
+static const enum AVPixelFormat levels_out_rgb8_pix_fmts[] = {
+    AV_PIX_FMT_GBRAP,    AV_PIX_FMT_GBRP,
+    AV_PIX_FMT_NONE
+};
+
+static const enum AVPixelFormat levels_out_rgb9_pix_fmts[] = {
+    AV_PIX_FMT_GBRP9,
+    AV_PIX_FMT_NONE
+};
+
+static const enum AVPixelFormat levels_out_rgb10_pix_fmts[] = {
+    AV_PIX_FMT_GBRP10,
+    AV_PIX_FMT_NONE
 };
 
 static const enum AVPixelFormat waveform_pix_fmts[] = {
@@ -112,7 +161,46 @@ static int query_formats(AVFilterContext *ctx)
         pix_fmts = waveform_pix_fmts;
         break;
     case MODE_LEVELS:
-        pix_fmts = levels_pix_fmts;
+    {
+        AVFilterFormats *avff;
+        const AVPixFmtDescriptor *desc;
+        const enum AVPixelFormat *out_pix_fmts;
+        int rgb, i, bits;
+
+        if (!ctx->inputs[0]->in_formats ||
+            !ctx->inputs[0]->in_formats->nb_formats) {
+            return AVERROR(EAGAIN);
+        }
+
+        if (!ctx->inputs[0]->out_formats)
+            ff_formats_ref(ff_make_format_list(levels_in_pix_fmts), &ctx->inputs[0]->out_formats);
+        avff = ctx->inputs[0]->in_formats;
+        desc = av_pix_fmt_desc_get(avff->formats[0]);
+        rgb = desc->flags & AV_PIX_FMT_FLAG_RGB;
+        bits = desc->comp[0].depth_minus1;
+        for (i = 1; i < avff->nb_formats; i++) {
+            desc = av_pix_fmt_desc_get(avff->formats[i]);
+            if ((rgb != (desc->flags & AV_PIX_FMT_FLAG_RGB)) ||
+                (bits != desc->comp[0].depth_minus1))
+                return AVERROR(EAGAIN);
+        }
+
+        if (rgb && bits == 7)
+            out_pix_fmts = levels_out_rgb8_pix_fmts;
+        else if (rgb && bits == 8)
+            out_pix_fmts = levels_out_rgb9_pix_fmts;
+        else if (rgb && bits == 9)
+            out_pix_fmts = levels_out_rgb10_pix_fmts;
+        else if (bits == 7)
+            out_pix_fmts = levels_out_yuv8_pix_fmts;
+        else if (bits == 8)
+            out_pix_fmts = levels_out_yuv9_pix_fmts;
+        else // if (bits == 9)
+            out_pix_fmts = levels_out_yuv10_pix_fmts;
+        ff_formats_ref(ff_make_format_list(out_pix_fmts), &ctx->outputs[0]->in_formats);
+
+        return 0;
+    }
         break;
     case MODE_COLOR:
     case MODE_COLOR2:
@@ -139,8 +227,12 @@ static int config_input(AVFilterLink *inlink)
 
     h->desc  = av_pix_fmt_desc_get(inlink->format);
     h->ncomp = h->desc->nb_components;
+    h->histogram_size = 1 << (h->desc->comp[0].depth_minus1 + 1);
+    h->mult = h->histogram_size / 256;
 
     switch (inlink->format) {
+    case AV_PIX_FMT_GBRP10:
+    case AV_PIX_FMT_GBRP9:
     case AV_PIX_FMT_GBRAP:
     case AV_PIX_FMT_GBRP:
         h->bg_color = black_gbrp_color;
@@ -151,6 +243,11 @@ static int config_input(AVFilterLink *inlink)
         h->fg_color = white_yuva_color;
     }
 
+    h->planeheight[1] = h->planeheight[2] = FF_CEIL_RSHIFT(inlink->h, h->desc->log2_chroma_h);
+    h->planeheight[0] = h->planeheight[3] = inlink->h;
+    h->planewidth[1]  = h->planewidth[2]  = FF_CEIL_RSHIFT(inlink->w, h->desc->log2_chroma_w);
+    h->planewidth[0]  = h->planewidth[3]  = inlink->w;
+
     return 0;
 }
 
@@ -158,13 +255,19 @@ static int config_output(AVFilterLink *outlink)
 {
     AVFilterContext *ctx = outlink->src;
     HistogramContext *h = ctx->priv;
+    int ncomp = 0, i;
 
     switch (h->mode) {
     case MODE_LEVELS:
-        outlink->w = 256;
-        outlink->h = (h->level_height + h->scale_height) * FFMAX(h->ncomp * h->display_mode, 1);
+        for (i = 0; i < h->ncomp; i++) {
+            if ((1 << i) & h->components)
+                ncomp++;
+        }
+        outlink->w = h->histogram_size;
+        outlink->h = (h->level_height + h->scale_height) * FFMAX(ncomp * h->display_mode, 1);
         break;
     case MODE_WAVEFORM:
+        av_log(ctx, AV_LOG_WARNING, "This mode is deprecated, please use waveform filter instead.\n");
         if (h->waveform_mode)
             outlink->h = 256 * FFMAX(h->ncomp * h->display_mode, 1);
         else
@@ -172,12 +275,14 @@ static int config_output(AVFilterLink *outlink)
         break;
     case MODE_COLOR:
     case MODE_COLOR2:
+        av_log(ctx, AV_LOG_WARNING, "This mode is deprecated, use vectorscope filter instead.");
         outlink->h = outlink->w = 256;
         break;
     default:
         av_assert0(0);
     }
 
+    h->odesc = av_pix_fmt_desc_get(outlink->format);
     outlink->sample_aspect_ratio = (AVRational){1,1};
 
     return 0;
@@ -236,9 +341,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
     AVFilterContext *ctx  = inlink->dst;
     AVFilterLink *outlink = ctx->outputs[0];
     AVFrame *out;
-    const uint8_t *src;
     uint8_t *dst;
-    int i, j, k, l;
+    int i, j, k, l, m;
 
     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
     if (!out) {
@@ -248,31 +352,56 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
 
     out->pts = in->pts;
 
-    for (k = 0; k < h->ncomp; k++) {
+    for (k = 0; k < 4 && out->data[k]; k++) {
         const int is_chroma = (k == 1 || k == 2);
-        const int dst_h = FF_CEIL_RSHIFT(outlink->h, (is_chroma ? h->desc->log2_chroma_h : 0));
-        const int dst_w = FF_CEIL_RSHIFT(outlink->w, (is_chroma ? h->desc->log2_chroma_w : 0));
-        for (i = 0; i < dst_h ; i++)
-            memset(out->data[h->desc->comp[k].plane] +
-                   i * out->linesize[h->desc->comp[k].plane],
-                   h->bg_color[k], dst_w);
+        const int dst_h = FF_CEIL_RSHIFT(outlink->h, (is_chroma ? h->odesc->log2_chroma_h : 0));
+        const int dst_w = FF_CEIL_RSHIFT(outlink->w, (is_chroma ? h->odesc->log2_chroma_w : 0));
+
+        if (h->histogram_size <= 256) {
+            for (i = 0; i < dst_h ; i++)
+                memset(out->data[h->odesc->comp[k].plane] +
+                       i * out->linesize[h->odesc->comp[k].plane],
+                       h->bg_color[k], dst_w);
+        } else {
+            const int mult = h->mult;
+
+            for (i = 0; i < dst_h ; i++)
+                for (j = 0; j < dst_w; j++)
+                    AV_WN16(out->data[h->odesc->comp[k].plane] +
+                        i * out->linesize[h->odesc->comp[k].plane] + j * 2,
+                        h->bg_color[k] * mult);
+        }
     }
 
     switch (h->mode) {
     case MODE_LEVELS:
-        for (k = 0; k < h->ncomp; k++) {
+        for (m = 0, k = 0; k < h->ncomp; k++) {
             const int p = h->desc->comp[k].plane;
-            const int start = k * (h->level_height + h->scale_height) * h->display_mode;
+            const int height = h->planeheight[p];
+            const int width = h->planewidth[p];
             double max_hval_log;
             unsigned max_hval = 0;
+            int start;
+
+            if (!((1 << k) & h->components))
+                continue;
+            start = m++ * (h->level_height + h->scale_height) * h->display_mode;
 
-            for (i = 0; i < in->height; i++) {
-                src = in->data[p] + i * in->linesize[p];
-                for (j = 0; j < in->width; j++)
-                    h->histogram[src[j]]++;
+            if (h->histogram_size <= 256) {
+                for (i = 0; i < height; i++) {
+                    const uint8_t *src = in->data[p] + i * in->linesize[p];
+                    for (j = 0; j < width; j++)
+                        h->histogram[src[j]]++;
+                }
+            } else {
+                for (i = 0; i < height; i++) {
+                    const uint16_t *src = (const uint16_t *)(in->data[p] + i * in->linesize[p]);
+                    for (j = 0; j < width; j++)
+                        h->histogram[src[j]]++;
+                }
             }
 
-            for (i = 0; i < 256; i++)
+            for (i = 0; i < h->histogram_size; i++)
                 max_hval = FFMAX(max_hval, h->histogram[i]);
             max_hval_log = log2(max_hval + 1);
 
@@ -284,19 +413,34 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
                 else
                     col_height = h->level_height - (h->histogram[i] * (int64_t)h->level_height + max_hval - 1) / max_hval;
 
-                for (j = h->level_height - 1; j >= col_height; j--) {
-                    if (h->display_mode) {
-                        for (l = 0; l < h->ncomp; l++)
-                            out->data[l][(j + start) * out->linesize[l] + i] = h->fg_color[l];
-                    } else {
-                        out->data[p][(j + start) * out->linesize[p] + i] = 255;
+                if (h->histogram_size <= 256) {
+                    for (j = h->level_height - 1; j >= col_height; j--) {
+                        if (h->display_mode) {
+                            for (l = 0; l < h->ncomp; l++)
+                                out->data[l][(j + start) * out->linesize[l] + i] = h->fg_color[l];
+                        } else {
+                            out->data[p][(j + start) * out->linesize[p] + i] = 255;
+                        }
+                    }
+                    for (j = h->level_height + h->scale_height - 1; j >= h->level_height; j--)
+                        out->data[p][(j + start) * out->linesize[p] + i] = i;
+                } else {
+                    const int mult = h->mult;
+
+                    for (j = h->level_height - 1; j >= col_height; j--) {
+                        if (h->display_mode) {
+                            for (l = 0; l < h->ncomp; l++)
+                                AV_WN16(out->data[l] + (j + start) * out->linesize[l] + i * 2, h->fg_color[l] * mult);
+                        } else {
+                            AV_WN16(out->data[p] + (j + start) * out->linesize[p] + i * 2, 255 * mult);
+                        }
                     }
+                    for (j = h->level_height + h->scale_height - 1; j >= h->level_height; j--)
+                        AV_WN16(out->data[p] + (j + start) * out->linesize[p] + i * 2, i);
                 }
-                for (j = h->level_height + h->scale_height - 1; j >= h->level_height; j--)
-                    out->data[p][(j + start) * out->linesize[p] + i] = i;
             }
 
-            memset(h->histogram, 0, 256 * sizeof(unsigned));
+            memset(h->histogram, 0, h->histogram_size * sizeof(unsigned));
         }
         break;
     case MODE_WAVEFORM: