| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Copyright (c) 2015 Paul B Mahol | ||
| 3 | * | ||
| 4 | * This file is part of FFmpeg. | ||
| 5 | * | ||
| 6 | * FFmpeg is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU Lesser General Public | ||
| 8 | * License as published by the Free Software Foundation; either | ||
| 9 | * version 2.1 of the License, or (at your option) any later version. | ||
| 10 | * | ||
| 11 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 14 | * Lesser General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU Lesser General Public | ||
| 17 | * License along with FFmpeg; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include "libavutil/avassert.h" | ||
| 22 | #include "libavutil/mem.h" | ||
| 23 | #include "libavutil/opt.h" | ||
| 24 | #include "avfilter.h" | ||
| 25 | #include "filters.h" | ||
| 26 | #include "formats.h" | ||
| 27 | #include "video.h" | ||
| 28 | |||
| 29 | enum DisplayScale { LINEAR, SQRT, CBRT, LOG, RLOG, NB_SCALES }; | ||
| 30 | enum AmplitudeScale { ALINEAR, ALOG, NB_ASCALES }; | ||
| 31 | enum SlideMode { REPLACE, SCROLL, NB_SLIDES }; | ||
| 32 | enum DisplayMode { SINGLE, SEPARATE, NB_DMODES }; | ||
| 33 | enum HistogramMode { ABS, SIGN, NB_HMODES }; | ||
| 34 | |||
| 35 | typedef struct AudioHistogramContext { | ||
| 36 | const AVClass *class; | ||
| 37 | AVFrame *out; | ||
| 38 | int w, h; | ||
| 39 | AVRational frame_rate; | ||
| 40 | uint64_t *achistogram; | ||
| 41 | uint64_t *shistogram; | ||
| 42 | int ascale; | ||
| 43 | int scale; | ||
| 44 | float phisto; | ||
| 45 | int histogram_h; | ||
| 46 | int apos; | ||
| 47 | int ypos; | ||
| 48 | int slide; | ||
| 49 | int dmode; | ||
| 50 | int hmode; | ||
| 51 | int dchannels; | ||
| 52 | int count; | ||
| 53 | int frame_count; | ||
| 54 | float *combine_buffer; | ||
| 55 | AVFrame *in[101]; | ||
| 56 | int first; | ||
| 57 | int nb_samples; | ||
| 58 | |||
| 59 | int (*get_bin)(float in, int w); | ||
| 60 | } AudioHistogramContext; | ||
| 61 | |||
| 62 | #define OFFSET(x) offsetof(AudioHistogramContext, x) | ||
| 63 | #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM | ||
| 64 | |||
| 65 | static const AVOption ahistogram_options[] = { | ||
| 66 | { "dmode", "set method to display channels", OFFSET(dmode), AV_OPT_TYPE_INT, {.i64=SINGLE}, 0, NB_DMODES-1, FLAGS, .unit = "dmode" }, | ||
| 67 | { "single", "all channels use single histogram", 0, AV_OPT_TYPE_CONST, {.i64=SINGLE}, 0, 0, FLAGS, .unit = "dmode" }, | ||
| 68 | { "separate", "each channel have own histogram", 0, AV_OPT_TYPE_CONST, {.i64=SEPARATE}, 0, 0, FLAGS, .unit = "dmode" }, | ||
| 69 | { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, INT_MAX, FLAGS }, | ||
| 70 | { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, INT_MAX, FLAGS }, | ||
| 71 | { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="hd720"}, 0, 0, FLAGS }, | ||
| 72 | { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="hd720"}, 0, 0, FLAGS }, | ||
| 73 | { "scale", "set display scale", OFFSET(scale), AV_OPT_TYPE_INT, {.i64=LOG}, LINEAR, NB_SCALES-1, FLAGS, .unit = "scale" }, | ||
| 74 | { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=LOG}, 0, 0, FLAGS, .unit = "scale" }, | ||
| 75 | { "sqrt", "square root", 0, AV_OPT_TYPE_CONST, {.i64=SQRT}, 0, 0, FLAGS, .unit = "scale" }, | ||
| 76 | { "cbrt", "cubic root", 0, AV_OPT_TYPE_CONST, {.i64=CBRT}, 0, 0, FLAGS, .unit = "scale" }, | ||
| 77 | { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, FLAGS, .unit = "scale" }, | ||
| 78 | { "rlog", "reverse logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=RLOG}, 0, 0, FLAGS, .unit = "scale" }, | ||
| 79 | { "ascale", "set amplitude scale", OFFSET(ascale), AV_OPT_TYPE_INT, {.i64=ALOG}, LINEAR, NB_ASCALES-1, FLAGS, .unit = "ascale" }, | ||
| 80 | { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=ALOG}, 0, 0, FLAGS, .unit = "ascale" }, | ||
| 81 | { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=ALINEAR}, 0, 0, FLAGS, .unit = "ascale" }, | ||
| 82 | { "acount", "how much frames to accumulate", OFFSET(count), AV_OPT_TYPE_INT, {.i64=1}, -1, 100, FLAGS }, | ||
| 83 | { "rheight", "set histogram ratio of window height", OFFSET(phisto), AV_OPT_TYPE_FLOAT, {.dbl=0.10}, 0, 1, FLAGS }, | ||
| 84 | { "slide", "set sonogram sliding", OFFSET(slide), AV_OPT_TYPE_INT, {.i64=REPLACE}, 0, NB_SLIDES-1, FLAGS, .unit = "slide" }, | ||
| 85 | { "replace", "replace old rows with new", 0, AV_OPT_TYPE_CONST, {.i64=REPLACE}, 0, 0, FLAGS, .unit = "slide" }, | ||
| 86 | { "scroll", "scroll from top to bottom", 0, AV_OPT_TYPE_CONST, {.i64=SCROLL}, 0, 0, FLAGS, .unit = "slide" }, | ||
| 87 | { "hmode", "set histograms mode", OFFSET(hmode), AV_OPT_TYPE_INT, {.i64=ABS}, 0, NB_HMODES-1, FLAGS, .unit = "hmode" }, | ||
| 88 | { "abs", "use absolute samples", 0, AV_OPT_TYPE_CONST, {.i64=ABS}, 0, 0, FLAGS, .unit = "hmode" }, | ||
| 89 | { "sign", "use unchanged samples", 0, AV_OPT_TYPE_CONST, {.i64=SIGN},0, 0, FLAGS, .unit = "hmode" }, | ||
| 90 | { NULL } | ||
| 91 | }; | ||
| 92 | |||
| 93 | AVFILTER_DEFINE_CLASS(ahistogram); | ||
| 94 | |||
| 95 | ✗ | static int query_formats(const AVFilterContext *ctx, | |
| 96 | AVFilterFormatsConfig **cfg_in, | ||
| 97 | AVFilterFormatsConfig **cfg_out) | ||
| 98 | { | ||
| 99 | ✗ | AVFilterFormats *formats = NULL; | |
| 100 | static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }; | ||
| 101 | static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUVA444P, AV_PIX_FMT_NONE }; | ||
| 102 | ✗ | int ret = AVERROR(EINVAL); | |
| 103 | |||
| 104 | ✗ | formats = ff_make_sample_format_list(sample_fmts); | |
| 105 | ✗ | if ((ret = ff_formats_ref(formats, &cfg_in[0]->formats)) < 0) | |
| 106 | ✗ | return ret; | |
| 107 | |||
| 108 | ✗ | formats = ff_make_pixel_format_list(pix_fmts); | |
| 109 | ✗ | if ((ret = ff_formats_ref(formats, &cfg_out[0]->formats)) < 0) | |
| 110 | ✗ | return ret; | |
| 111 | |||
| 112 | ✗ | return 0; | |
| 113 | } | ||
| 114 | |||
| 115 | ✗ | static int config_input(AVFilterLink *inlink) | |
| 116 | { | ||
| 117 | ✗ | AVFilterContext *ctx = inlink->dst; | |
| 118 | ✗ | AudioHistogramContext *s = ctx->priv; | |
| 119 | |||
| 120 | ✗ | s->nb_samples = FFMAX(1, av_rescale(inlink->sample_rate, s->frame_rate.den, s->frame_rate.num)); | |
| 121 | ✗ | s->dchannels = s->dmode == SINGLE ? 1 : inlink->ch_layout.nb_channels; | |
| 122 | ✗ | s->shistogram = av_calloc(s->w, s->dchannels * sizeof(*s->shistogram)); | |
| 123 | ✗ | if (!s->shistogram) | |
| 124 | ✗ | return AVERROR(ENOMEM); | |
| 125 | |||
| 126 | ✗ | s->achistogram = av_calloc(s->w, s->dchannels * sizeof(*s->achistogram)); | |
| 127 | ✗ | if (!s->achistogram) | |
| 128 | ✗ | return AVERROR(ENOMEM); | |
| 129 | |||
| 130 | ✗ | return 0; | |
| 131 | } | ||
| 132 | |||
| 133 | ✗ | static int get_lin_bin_abs(float in, int w) | |
| 134 | { | ||
| 135 | ✗ | return lrintf(av_clipf(fabsf(in), 0.f, 1.f) * (w - 1)); | |
| 136 | } | ||
| 137 | |||
| 138 | ✗ | static int get_lin_bin_sign(float in, int w) | |
| 139 | { | ||
| 140 | ✗ | return lrintf((1.f + av_clipf(in, -1.f, 1.f)) * 0.5f * (w - 1)); | |
| 141 | } | ||
| 142 | |||
| 143 | ✗ | static int get_log_bin_abs(float in, int w) | |
| 144 | { | ||
| 145 | ✗ | return lrintf(av_clipf(1.f + log10f(fabsf(in)) / 6.f, 0.f, 1.f) * (w - 1)); | |
| 146 | } | ||
| 147 | |||
| 148 | ✗ | static int get_log_bin_sign(float in, int w) | |
| 149 | { | ||
| 150 | ✗ | return (w / 2) + FFSIGN(in) * lrintf(av_clipf(1.f + log10f(fabsf(in)) / 6.f, 0.f, 1.f) * (w / 2)); | |
| 151 | } | ||
| 152 | |||
| 153 | ✗ | static int config_output(AVFilterLink *outlink) | |
| 154 | { | ||
| 155 | ✗ | AudioHistogramContext *s = outlink->src->priv; | |
| 156 | ✗ | FilterLink *l = ff_filter_link(outlink); | |
| 157 | |||
| 158 | ✗ | outlink->w = s->w; | |
| 159 | ✗ | outlink->h = s->h; | |
| 160 | ✗ | outlink->sample_aspect_ratio = (AVRational){1,1}; | |
| 161 | ✗ | l->frame_rate = s->frame_rate; | |
| 162 | ✗ | outlink->time_base = av_inv_q(l->frame_rate); | |
| 163 | |||
| 164 | ✗ | s->histogram_h = s->h * s->phisto; | |
| 165 | ✗ | s->ypos = s->h * s->phisto; | |
| 166 | |||
| 167 | ✗ | switch (s->ascale) { | |
| 168 | ✗ | case ALINEAR: | |
| 169 | ✗ | switch (s->hmode) { | |
| 170 | ✗ | case ABS: s->get_bin = get_lin_bin_abs; break; | |
| 171 | ✗ | case SIGN: s->get_bin = get_lin_bin_sign; break; | |
| 172 | ✗ | default: | |
| 173 | ✗ | return AVERROR_BUG; | |
| 174 | } | ||
| 175 | ✗ | break; | |
| 176 | ✗ | case ALOG: | |
| 177 | ✗ | switch (s->hmode) { | |
| 178 | ✗ | case ABS: s->get_bin = get_log_bin_abs; break; | |
| 179 | ✗ | case SIGN: s->get_bin = get_log_bin_sign; break; | |
| 180 | ✗ | default: | |
| 181 | ✗ | return AVERROR_BUG; | |
| 182 | } | ||
| 183 | ✗ | break; | |
| 184 | ✗ | default: | |
| 185 | ✗ | return AVERROR_BUG; | |
| 186 | } | ||
| 187 | |||
| 188 | ✗ | if (s->dmode == SEPARATE) { | |
| 189 | ✗ | s->combine_buffer = av_malloc_array(outlink->w * 3, sizeof(*s->combine_buffer)); | |
| 190 | ✗ | if (!s->combine_buffer) | |
| 191 | ✗ | return AVERROR(ENOMEM); | |
| 192 | } | ||
| 193 | |||
| 194 | ✗ | return 0; | |
| 195 | } | ||
| 196 | |||
| 197 | ✗ | static int filter_frame(AVFilterLink *inlink, AVFrame *in) | |
| 198 | { | ||
| 199 | ✗ | AVFilterContext *ctx = inlink->dst; | |
| 200 | ✗ | AVFilterLink *outlink = ctx->outputs[0]; | |
| 201 | ✗ | AudioHistogramContext *s = ctx->priv; | |
| 202 | ✗ | const int nb_samples = in->nb_samples; | |
| 203 | ✗ | const int H = s->histogram_h; | |
| 204 | ✗ | const int w = s->w; | |
| 205 | int c, y, n, p, bin, ret; | ||
| 206 | ✗ | uint64_t acmax = 1; | |
| 207 | AVFrame *clone; | ||
| 208 | |||
| 209 | ✗ | if (!s->out || s->out->width != outlink->w || | |
| 210 | ✗ | s->out->height != outlink->h) { | |
| 211 | ✗ | av_frame_free(&s->out); | |
| 212 | ✗ | s->out = ff_get_video_buffer(outlink, outlink->w, outlink->h); | |
| 213 | ✗ | if (!s->out) { | |
| 214 | ✗ | av_frame_free(&in); | |
| 215 | ✗ | return AVERROR(ENOMEM); | |
| 216 | } | ||
| 217 | ✗ | for (n = H; n < s->h; n++) { | |
| 218 | ✗ | memset(s->out->data[0] + n * s->out->linesize[0], 0, w); | |
| 219 | ✗ | memset(s->out->data[1] + n * s->out->linesize[0], 127, w); | |
| 220 | ✗ | memset(s->out->data[2] + n * s->out->linesize[0], 127, w); | |
| 221 | ✗ | memset(s->out->data[3] + n * s->out->linesize[0], 0, w); | |
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | ✗ | ret = ff_inlink_make_frame_writable(outlink, &s->out); | |
| 226 | ✗ | if (ret < 0) { | |
| 227 | ✗ | av_frame_free(&in); | |
| 228 | ✗ | return ret; | |
| 229 | } | ||
| 230 | |||
| 231 | ✗ | if (s->dmode == SEPARATE) { | |
| 232 | ✗ | for (y = 0; y < w; y++) { | |
| 233 | ✗ | s->combine_buffer[3 * y ] = 0; | |
| 234 | ✗ | s->combine_buffer[3 * y + 1] = 127.5; | |
| 235 | ✗ | s->combine_buffer[3 * y + 2] = 127.5; | |
| 236 | } | ||
| 237 | } | ||
| 238 | |||
| 239 | ✗ | for (n = 0; n < H; n++) { | |
| 240 | ✗ | memset(s->out->data[0] + n * s->out->linesize[0], 0, w); | |
| 241 | ✗ | memset(s->out->data[1] + n * s->out->linesize[0], 127, w); | |
| 242 | ✗ | memset(s->out->data[2] + n * s->out->linesize[0], 127, w); | |
| 243 | ✗ | memset(s->out->data[3] + n * s->out->linesize[0], 0, w); | |
| 244 | } | ||
| 245 | ✗ | s->out->pts = av_rescale_q(in->pts, inlink->time_base, outlink->time_base); | |
| 246 | ✗ | s->out->duration = 1; | |
| 247 | |||
| 248 | ✗ | s->first = s->frame_count; | |
| 249 | |||
| 250 | ✗ | switch (s->ascale) { | |
| 251 | ✗ | case ALINEAR: | |
| 252 | ✗ | for (c = 0; c < inlink->ch_layout.nb_channels; c++) { | |
| 253 | ✗ | const float *src = (const float *)in->extended_data[c]; | |
| 254 | ✗ | uint64_t *achistogram = &s->achistogram[(s->dmode == SINGLE ? 0: c) * w]; | |
| 255 | |||
| 256 | ✗ | for (n = 0; n < nb_samples; n++) { | |
| 257 | ✗ | bin = s->get_bin(src[n], w); | |
| 258 | |||
| 259 | ✗ | achistogram[bin]++; | |
| 260 | } | ||
| 261 | |||
| 262 | ✗ | if (s->in[s->first] && s->count >= 0) { | |
| 263 | ✗ | uint64_t *shistogram = &s->shistogram[(s->dmode == SINGLE ? 0: c) * w]; | |
| 264 | ✗ | const float *src2 = (const float *)s->in[s->first]->extended_data[c]; | |
| 265 | |||
| 266 | ✗ | for (n = 0; n < nb_samples; n++) { | |
| 267 | ✗ | bin = s->get_bin(src2[n], w); | |
| 268 | |||
| 269 | ✗ | shistogram[bin]++; | |
| 270 | } | ||
| 271 | } | ||
| 272 | } | ||
| 273 | ✗ | break; | |
| 274 | ✗ | case ALOG: | |
| 275 | ✗ | for (c = 0; c < inlink->ch_layout.nb_channels; c++) { | |
| 276 | ✗ | const float *src = (const float *)in->extended_data[c]; | |
| 277 | ✗ | uint64_t *achistogram = &s->achistogram[(s->dmode == SINGLE ? 0: c) * w]; | |
| 278 | |||
| 279 | ✗ | for (n = 0; n < nb_samples; n++) { | |
| 280 | ✗ | bin = s->get_bin(src[n], w); | |
| 281 | |||
| 282 | ✗ | achistogram[bin]++; | |
| 283 | } | ||
| 284 | |||
| 285 | ✗ | if (s->in[s->first] && s->count >= 0) { | |
| 286 | ✗ | uint64_t *shistogram = &s->shistogram[(s->dmode == SINGLE ? 0: c) * w]; | |
| 287 | ✗ | const float *src2 = (const float *)s->in[s->first]->extended_data[c]; | |
| 288 | |||
| 289 | ✗ | for (n = 0; n < nb_samples; n++) { | |
| 290 | ✗ | bin = s->get_bin(src2[n], w); | |
| 291 | |||
| 292 | ✗ | shistogram[bin]++; | |
| 293 | } | ||
| 294 | } | ||
| 295 | } | ||
| 296 | ✗ | break; | |
| 297 | } | ||
| 298 | |||
| 299 | ✗ | av_frame_free(&s->in[s->frame_count]); | |
| 300 | ✗ | s->in[s->frame_count] = in; | |
| 301 | ✗ | s->frame_count++; | |
| 302 | ✗ | if (s->frame_count > s->count) | |
| 303 | ✗ | s->frame_count = 0; | |
| 304 | |||
| 305 | ✗ | for (n = 0; n < w * s->dchannels; n++) { | |
| 306 | ✗ | acmax = FFMAX(s->achistogram[n] - s->shistogram[n], acmax); | |
| 307 | } | ||
| 308 | |||
| 309 | ✗ | for (c = 0; c < s->dchannels; c++) { | |
| 310 | ✗ | uint64_t *shistogram = &s->shistogram[c * w]; | |
| 311 | ✗ | uint64_t *achistogram = &s->achistogram[c * w]; | |
| 312 | float yf, uf, vf; | ||
| 313 | |||
| 314 | ✗ | if (s->dmode == SEPARATE) { | |
| 315 | ✗ | yf = 255.0f / s->dchannels; | |
| 316 | ✗ | uf = yf * M_PI; | |
| 317 | ✗ | vf = yf * M_PI; | |
| 318 | ✗ | uf *= 0.5 * sin((2 * M_PI * c) / s->dchannels); | |
| 319 | ✗ | vf *= 0.5 * cos((2 * M_PI * c) / s->dchannels); | |
| 320 | } | ||
| 321 | |||
| 322 | ✗ | for (n = 0; n < w; n++) { | |
| 323 | double a, aa; | ||
| 324 | int h; | ||
| 325 | |||
| 326 | ✗ | a = achistogram[n] - shistogram[n]; | |
| 327 | |||
| 328 | ✗ | switch (s->scale) { | |
| 329 | ✗ | case LINEAR: | |
| 330 | ✗ | aa = a / (double)acmax; | |
| 331 | ✗ | break; | |
| 332 | ✗ | case SQRT: | |
| 333 | ✗ | aa = sqrt(a) / sqrt(acmax); | |
| 334 | ✗ | break; | |
| 335 | ✗ | case CBRT: | |
| 336 | ✗ | aa = cbrt(a) / cbrt(acmax); | |
| 337 | ✗ | break; | |
| 338 | ✗ | case LOG: | |
| 339 | ✗ | aa = log2(a + 1) / log2(acmax + 1); | |
| 340 | ✗ | break; | |
| 341 | ✗ | case RLOG: | |
| 342 | ✗ | aa = 1. - log2(a + 1) / log2(acmax + 1); | |
| 343 | ✗ | if (aa == 1.) | |
| 344 | ✗ | aa = 0; | |
| 345 | ✗ | break; | |
| 346 | ✗ | default: | |
| 347 | ✗ | av_assert0(0); | |
| 348 | } | ||
| 349 | |||
| 350 | ✗ | h = aa * (H - 1); | |
| 351 | |||
| 352 | ✗ | if (s->dmode == SINGLE) { | |
| 353 | ✗ | int start = H - h, end = H; | |
| 354 | ✗ | const int linesizey = s->out->linesize[0]; | |
| 355 | ✗ | const int linesizea = s->out->linesize[3]; | |
| 356 | ✗ | uint8_t *dsty = s->out->data[0] + start * linesizey; | |
| 357 | ✗ | uint8_t *dsta = s->out->data[3] + start * linesizea; | |
| 358 | |||
| 359 | ✗ | for (y = start; y < end; y++, dsty += linesizey, dsta += linesizea) { | |
| 360 | ✗ | dsty[n] = 255; | |
| 361 | ✗ | dsta[n] = 255; | |
| 362 | } | ||
| 363 | |||
| 364 | ✗ | if (s->h - H > 0) { | |
| 365 | ✗ | h = aa * 255; | |
| 366 | |||
| 367 | ✗ | s->out->data[0][s->ypos * s->out->linesize[0] + n] = av_clip_uint8(h); | |
| 368 | ✗ | s->out->data[1][s->ypos * s->out->linesize[1] + n] = 127; | |
| 369 | ✗ | s->out->data[2][s->ypos * s->out->linesize[2] + n] = 127; | |
| 370 | ✗ | s->out->data[3][s->ypos * s->out->linesize[3] + n] = 255; | |
| 371 | } | ||
| 372 | ✗ | } else if (s->dmode == SEPARATE) { | |
| 373 | ✗ | int start = H - h, end = H; | |
| 374 | ✗ | float *out = &s->combine_buffer[3 * n]; | |
| 375 | ✗ | const int linesizey = s->out->linesize[0]; | |
| 376 | ✗ | const int linesizeu = s->out->linesize[1]; | |
| 377 | ✗ | const int linesizev = s->out->linesize[2]; | |
| 378 | ✗ | const int linesizea = s->out->linesize[3]; | |
| 379 | ✗ | uint8_t *dsty = s->out->data[0] + start * linesizey; | |
| 380 | ✗ | uint8_t *dstu = s->out->data[1] + start * linesizeu; | |
| 381 | ✗ | uint8_t *dstv = s->out->data[2] + start * linesizev; | |
| 382 | ✗ | uint8_t *dsta = s->out->data[3] + start * linesizea; | |
| 383 | int old; | ||
| 384 | |||
| 385 | ✗ | old = dsty[n]; | |
| 386 | ✗ | for (y = start; y < end; y++) { | |
| 387 | ✗ | if (dsty[n] != old) | |
| 388 | ✗ | break; | |
| 389 | ✗ | old = dsty[n]; | |
| 390 | ✗ | dsty[n] = av_clip_uint8(yf); | |
| 391 | ✗ | dstu[n] = av_clip_uint8(128.f+uf); | |
| 392 | ✗ | dstv[n] = av_clip_uint8(128.f+vf); | |
| 393 | ✗ | dsta[n] = 255; | |
| 394 | |||
| 395 | ✗ | dsty += linesizey; | |
| 396 | ✗ | dstu += linesizeu; | |
| 397 | ✗ | dstv += linesizev; | |
| 398 | ✗ | dsta += linesizea; | |
| 399 | } | ||
| 400 | |||
| 401 | ✗ | out[0] += aa * yf; | |
| 402 | ✗ | out[1] += aa * uf; | |
| 403 | ✗ | out[2] += aa * vf; | |
| 404 | } | ||
| 405 | } | ||
| 406 | } | ||
| 407 | |||
| 408 | ✗ | if (s->h - H > 0) { | |
| 409 | ✗ | if (s->dmode == SEPARATE) { | |
| 410 | ✗ | for (n = 0; n < w; n++) { | |
| 411 | ✗ | float *cb = &s->combine_buffer[3 * n]; | |
| 412 | |||
| 413 | ✗ | s->out->data[0][s->ypos * s->out->linesize[0] + n] = cb[0]; | |
| 414 | ✗ | s->out->data[1][s->ypos * s->out->linesize[1] + n] = cb[1]; | |
| 415 | ✗ | s->out->data[2][s->ypos * s->out->linesize[2] + n] = cb[2]; | |
| 416 | ✗ | s->out->data[3][s->ypos * s->out->linesize[3] + n] = 255; | |
| 417 | } | ||
| 418 | } | ||
| 419 | |||
| 420 | ✗ | if (s->slide == SCROLL) { | |
| 421 | ✗ | for (p = 0; p < 4; p++) { | |
| 422 | ✗ | for (y = s->h - 1; y >= H + 1; y--) { | |
| 423 | ✗ | memmove(s->out->data[p] + (y ) * s->out->linesize[p], | |
| 424 | ✗ | s->out->data[p] + (y-1) * s->out->linesize[p], w); | |
| 425 | } | ||
| 426 | } | ||
| 427 | } | ||
| 428 | |||
| 429 | ✗ | s->ypos++; | |
| 430 | ✗ | if (s->slide == SCROLL || s->ypos >= s->h) | |
| 431 | ✗ | s->ypos = H; | |
| 432 | } | ||
| 433 | |||
| 434 | ✗ | clone = av_frame_clone(s->out); | |
| 435 | ✗ | if (!clone) | |
| 436 | ✗ | return AVERROR(ENOMEM); | |
| 437 | |||
| 438 | ✗ | return ff_filter_frame(outlink, clone); | |
| 439 | } | ||
| 440 | |||
| 441 | ✗ | static int activate(AVFilterContext *ctx) | |
| 442 | { | ||
| 443 | ✗ | AVFilterLink *inlink = ctx->inputs[0]; | |
| 444 | ✗ | AVFilterLink *outlink = ctx->outputs[0]; | |
| 445 | ✗ | AudioHistogramContext *s = ctx->priv; | |
| 446 | AVFrame *in; | ||
| 447 | int ret; | ||
| 448 | |||
| 449 | ✗ | FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); | |
| 450 | |||
| 451 | ✗ | ret = ff_inlink_consume_samples(inlink, s->nb_samples, s->nb_samples, &in); | |
| 452 | ✗ | if (ret < 0) | |
| 453 | ✗ | return ret; | |
| 454 | ✗ | if (ret > 0) | |
| 455 | ✗ | return filter_frame(inlink, in); | |
| 456 | |||
| 457 | ✗ | if (ff_inlink_queued_samples(inlink) >= s->nb_samples) { | |
| 458 | ✗ | ff_filter_set_ready(ctx, 10); | |
| 459 | ✗ | return 0; | |
| 460 | } | ||
| 461 | |||
| 462 | ✗ | FF_FILTER_FORWARD_STATUS(inlink, outlink); | |
| 463 | ✗ | FF_FILTER_FORWARD_WANTED(outlink, inlink); | |
| 464 | |||
| 465 | ✗ | return FFERROR_NOT_READY; | |
| 466 | } | ||
| 467 | |||
| 468 | ✗ | static av_cold void uninit(AVFilterContext *ctx) | |
| 469 | { | ||
| 470 | ✗ | AudioHistogramContext *s = ctx->priv; | |
| 471 | int i; | ||
| 472 | |||
| 473 | ✗ | av_frame_free(&s->out); | |
| 474 | ✗ | av_freep(&s->shistogram); | |
| 475 | ✗ | av_freep(&s->achistogram); | |
| 476 | ✗ | av_freep(&s->combine_buffer); | |
| 477 | ✗ | for (i = 0; i < 101; i++) | |
| 478 | ✗ | av_frame_free(&s->in[i]); | |
| 479 | ✗ | } | |
| 480 | |||
| 481 | static const AVFilterPad ahistogram_inputs[] = { | ||
| 482 | { | ||
| 483 | .name = "default", | ||
| 484 | .type = AVMEDIA_TYPE_AUDIO, | ||
| 485 | .config_props = config_input, | ||
| 486 | }, | ||
| 487 | }; | ||
| 488 | |||
| 489 | static const AVFilterPad ahistogram_outputs[] = { | ||
| 490 | { | ||
| 491 | .name = "default", | ||
| 492 | .type = AVMEDIA_TYPE_VIDEO, | ||
| 493 | .config_props = config_output, | ||
| 494 | }, | ||
| 495 | }; | ||
| 496 | |||
| 497 | const FFFilter ff_avf_ahistogram = { | ||
| 498 | .p.name = "ahistogram", | ||
| 499 | .p.description = NULL_IF_CONFIG_SMALL("Convert input audio to histogram video output."), | ||
| 500 | .p.priv_class = &ahistogram_class, | ||
| 501 | .uninit = uninit, | ||
| 502 | .priv_size = sizeof(AudioHistogramContext), | ||
| 503 | .activate = activate, | ||
| 504 | FILTER_INPUTS(ahistogram_inputs), | ||
| 505 | FILTER_OUTPUTS(ahistogram_outputs), | ||
| 506 | FILTER_QUERY_FUNC2(query_formats), | ||
| 507 | }; | ||
| 508 |