| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Copyright (c) 2012 Stefano Sabatini | ||
| 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 | /** | ||
| 22 | * @file | ||
| 23 | * audio to video multimedia filter | ||
| 24 | */ | ||
| 25 | |||
| 26 | #include "config_components.h" | ||
| 27 | |||
| 28 | #include "libavutil/avassert.h" | ||
| 29 | #include "libavutil/avstring.h" | ||
| 30 | #include "libavutil/channel_layout.h" | ||
| 31 | #include "libavutil/intreadwrite.h" | ||
| 32 | #include "libavutil/mem.h" | ||
| 33 | #include "libavutil/opt.h" | ||
| 34 | #include "libavutil/parseutils.h" | ||
| 35 | #include "avfilter.h" | ||
| 36 | #include "filters.h" | ||
| 37 | #include "formats.h" | ||
| 38 | #include "audio.h" | ||
| 39 | #include "video.h" | ||
| 40 | |||
| 41 | enum ShowWavesMode { | ||
| 42 | MODE_POINT, | ||
| 43 | MODE_LINE, | ||
| 44 | MODE_P2P, | ||
| 45 | MODE_CENTERED_LINE, | ||
| 46 | MODE_NB, | ||
| 47 | }; | ||
| 48 | |||
| 49 | enum ShowWavesScale { | ||
| 50 | SCALE_LIN, | ||
| 51 | SCALE_LOG, | ||
| 52 | SCALE_SQRT, | ||
| 53 | SCALE_CBRT, | ||
| 54 | SCALE_NB, | ||
| 55 | }; | ||
| 56 | |||
| 57 | enum ShowWavesDrawMode { | ||
| 58 | DRAW_SCALE, | ||
| 59 | DRAW_FULL, | ||
| 60 | DRAW_NB, | ||
| 61 | }; | ||
| 62 | |||
| 63 | enum ShowWavesFilterMode { | ||
| 64 | FILTER_AVERAGE, | ||
| 65 | FILTER_PEAK, | ||
| 66 | FILTER_NB, | ||
| 67 | }; | ||
| 68 | |||
| 69 | struct frame_node { | ||
| 70 | AVFrame *frame; | ||
| 71 | struct frame_node *next; | ||
| 72 | }; | ||
| 73 | |||
| 74 | typedef struct ShowWavesContext { | ||
| 75 | const AVClass *class; | ||
| 76 | int w, h; | ||
| 77 | AVRational rate; | ||
| 78 | char *colors; | ||
| 79 | int buf_idx; | ||
| 80 | int16_t *buf_idy; /* y coordinate of previous sample for each channel */ | ||
| 81 | int16_t *history; | ||
| 82 | int history_nb_samples; | ||
| 83 | int history_index; | ||
| 84 | AVFrame *outpicref; | ||
| 85 | AVRational n, q, c; | ||
| 86 | int pixstep; | ||
| 87 | int mode; ///< ShowWavesMode | ||
| 88 | int scale; ///< ShowWavesScale | ||
| 89 | int draw_mode; ///< ShowWavesDrawMode | ||
| 90 | int split_channels; | ||
| 91 | int filter_mode; | ||
| 92 | uint8_t *fg; | ||
| 93 | |||
| 94 | int (*get_h)(int16_t sample, int height); | ||
| 95 | void (*draw_sample)(uint8_t *buf, int height, int linesize, | ||
| 96 | int16_t *prev_y, const uint8_t color[4], int h); | ||
| 97 | |||
| 98 | /* single picture */ | ||
| 99 | int single_pic; | ||
| 100 | struct frame_node *audio_frames; | ||
| 101 | struct frame_node *last_frame; | ||
| 102 | int64_t total_samples; | ||
| 103 | int64_t *sum; /* abs sum of the samples per channel */ | ||
| 104 | } ShowWavesContext; | ||
| 105 | |||
| 106 | #define OFFSET(x) offsetof(ShowWavesContext, x) | ||
| 107 | #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM | ||
| 108 | |||
| 109 | static const AVOption showwaves_options[] = { | ||
| 110 | { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "600x240"}, 0, 0, FLAGS }, | ||
| 111 | { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "600x240"}, 0, 0, FLAGS }, | ||
| 112 | { "mode", "select display mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_POINT}, 0, MODE_NB-1, .flags=FLAGS, .unit="mode"}, | ||
| 113 | { "point", "draw a point for each sample", 0, AV_OPT_TYPE_CONST, {.i64=MODE_POINT}, .flags=FLAGS, .unit="mode"}, | ||
| 114 | { "line", "draw a line for each sample", 0, AV_OPT_TYPE_CONST, {.i64=MODE_LINE}, .flags=FLAGS, .unit="mode"}, | ||
| 115 | { "p2p", "draw a line between samples", 0, AV_OPT_TYPE_CONST, {.i64=MODE_P2P}, .flags=FLAGS, .unit="mode"}, | ||
| 116 | { "cline", "draw a centered line for each sample", 0, AV_OPT_TYPE_CONST, {.i64=MODE_CENTERED_LINE}, .flags=FLAGS, .unit="mode"}, | ||
| 117 | { "n", "set how many samples to show in the same point", OFFSET(n), AV_OPT_TYPE_RATIONAL, {.i64 = 0}, 0, INT_MAX, FLAGS }, | ||
| 118 | { "rate", "set video rate", OFFSET(rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, FLAGS }, | ||
| 119 | { "r", "set video rate", OFFSET(rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, FLAGS }, | ||
| 120 | { "split_channels", "draw channels separately", OFFSET(split_channels), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS }, | ||
| 121 | { "colors", "set channels colors", OFFSET(colors), AV_OPT_TYPE_STRING, {.str = "red|green|blue|yellow|orange|lime|pink|magenta|brown" }, 0, 0, FLAGS }, | ||
| 122 | { "scale", "set amplitude scale", OFFSET(scale), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, SCALE_NB-1, FLAGS, .unit="scale" }, | ||
| 123 | { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=SCALE_LIN}, .flags=FLAGS, .unit="scale"}, | ||
| 124 | { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=SCALE_LOG}, .flags=FLAGS, .unit="scale"}, | ||
| 125 | { "sqrt", "square root", 0, AV_OPT_TYPE_CONST, {.i64=SCALE_SQRT}, .flags=FLAGS, .unit="scale"}, | ||
| 126 | { "cbrt", "cubic root", 0, AV_OPT_TYPE_CONST, {.i64=SCALE_CBRT}, .flags=FLAGS, .unit="scale"}, | ||
| 127 | { "draw", "set draw mode", OFFSET(draw_mode), AV_OPT_TYPE_INT, {.i64 = DRAW_SCALE}, 0, DRAW_NB-1, FLAGS, .unit="draw" }, | ||
| 128 | { "scale", "scale pixel values for each drawn sample", 0, AV_OPT_TYPE_CONST, {.i64=DRAW_SCALE}, .flags=FLAGS, .unit="draw"}, | ||
| 129 | { "full", "draw every pixel for sample directly", 0, AV_OPT_TYPE_CONST, {.i64=DRAW_FULL}, .flags=FLAGS, .unit="draw"}, | ||
| 130 | { NULL } | ||
| 131 | }; | ||
| 132 | |||
| 133 | AVFILTER_DEFINE_CLASS(showwaves); | ||
| 134 | |||
| 135 | ✗ | static av_cold void uninit(AVFilterContext *ctx) | |
| 136 | { | ||
| 137 | ✗ | ShowWavesContext *showwaves = ctx->priv; | |
| 138 | |||
| 139 | ✗ | av_frame_free(&showwaves->outpicref); | |
| 140 | ✗ | av_freep(&showwaves->buf_idy); | |
| 141 | ✗ | av_freep(&showwaves->history); | |
| 142 | ✗ | av_freep(&showwaves->fg); | |
| 143 | |||
| 144 | ✗ | if (showwaves->single_pic) { | |
| 145 | ✗ | struct frame_node *node = showwaves->audio_frames; | |
| 146 | ✗ | while (node) { | |
| 147 | ✗ | struct frame_node *tmp = node; | |
| 148 | |||
| 149 | ✗ | node = node->next; | |
| 150 | ✗ | av_frame_free(&tmp->frame); | |
| 151 | ✗ | av_freep(&tmp); | |
| 152 | } | ||
| 153 | ✗ | av_freep(&showwaves->sum); | |
| 154 | ✗ | showwaves->last_frame = NULL; | |
| 155 | } | ||
| 156 | ✗ | } | |
| 157 | |||
| 158 | ✗ | static int query_formats(const AVFilterContext *ctx, | |
| 159 | AVFilterFormatsConfig **cfg_in, | ||
| 160 | AVFilterFormatsConfig **cfg_out) | ||
| 161 | { | ||
| 162 | ✗ | AVFilterFormats *formats = NULL; | |
| 163 | static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE }; | ||
| 164 | static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGBA, AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE }; | ||
| 165 | int ret; | ||
| 166 | |||
| 167 | /* set input audio formats */ | ||
| 168 | ✗ | formats = ff_make_format_list(sample_fmts); | |
| 169 | ✗ | if ((ret = ff_formats_ref(formats, &cfg_in[0]->formats)) < 0) | |
| 170 | ✗ | return ret; | |
| 171 | |||
| 172 | /* set output video format */ | ||
| 173 | ✗ | formats = ff_make_format_list(pix_fmts); | |
| 174 | ✗ | if ((ret = ff_formats_ref(formats, &cfg_out[0]->formats)) < 0) | |
| 175 | ✗ | return ret; | |
| 176 | |||
| 177 | ✗ | return 0; | |
| 178 | } | ||
| 179 | |||
| 180 | ✗ | static int get_lin_h(int16_t sample, int height) | |
| 181 | { | ||
| 182 | ✗ | return height/2 - av_rescale(sample, height/2, INT16_MAX); | |
| 183 | } | ||
| 184 | |||
| 185 | ✗ | static int get_lin_h2(int16_t sample, int height) | |
| 186 | { | ||
| 187 | ✗ | return av_rescale(FFABS(sample), height, INT16_MAX); | |
| 188 | } | ||
| 189 | |||
| 190 | ✗ | static int get_log_h(int16_t sample, int height) | |
| 191 | { | ||
| 192 | ✗ | return height/2 - FFSIGN(sample) * (log10(1 + FFABS(sample)) * (height/2) / log10(1 + INT16_MAX)); | |
| 193 | } | ||
| 194 | |||
| 195 | ✗ | static int get_log_h2(int16_t sample, int height) | |
| 196 | { | ||
| 197 | ✗ | return log10(1 + FFABS(sample)) * height / log10(1 + INT16_MAX); | |
| 198 | } | ||
| 199 | |||
| 200 | ✗ | static int get_sqrt_h(int16_t sample, int height) | |
| 201 | { | ||
| 202 | ✗ | return height/2 - FFSIGN(sample) * (sqrt(FFABS(sample)) * (height/2) / sqrt(INT16_MAX)); | |
| 203 | } | ||
| 204 | |||
| 205 | ✗ | static int get_sqrt_h2(int16_t sample, int height) | |
| 206 | { | ||
| 207 | ✗ | return sqrt(FFABS(sample)) * height / sqrt(INT16_MAX); | |
| 208 | } | ||
| 209 | |||
| 210 | ✗ | static int get_cbrt_h(int16_t sample, int height) | |
| 211 | { | ||
| 212 | ✗ | return height/2 - FFSIGN(sample) * (cbrt(FFABS(sample)) * (height/2) / cbrt(INT16_MAX)); | |
| 213 | } | ||
| 214 | |||
| 215 | ✗ | static int get_cbrt_h2(int16_t sample, int height) | |
| 216 | { | ||
| 217 | ✗ | return cbrt(FFABS(sample)) * height / cbrt(INT16_MAX); | |
| 218 | } | ||
| 219 | |||
| 220 | ✗ | static void draw_sample_point_rgba_scale(uint8_t *buf, int height, int linesize, | |
| 221 | int16_t *prev_y, | ||
| 222 | const uint8_t color[4], int h) | ||
| 223 | { | ||
| 224 | ✗ | if (h >= 0 && h < height) { | |
| 225 | ✗ | buf[h * linesize + 0] += color[0]; | |
| 226 | ✗ | buf[h * linesize + 1] += color[1]; | |
| 227 | ✗ | buf[h * linesize + 2] += color[2]; | |
| 228 | ✗ | buf[h * linesize + 3] += color[3]; | |
| 229 | } | ||
| 230 | ✗ | } | |
| 231 | |||
| 232 | ✗ | static void draw_sample_point_rgba_full(uint8_t *buf, int height, int linesize, | |
| 233 | int16_t *prev_y, | ||
| 234 | const uint8_t color[4], int h) | ||
| 235 | { | ||
| 236 | ✗ | uint32_t clr = AV_RN32(color); | |
| 237 | ✗ | if (h >= 0 && h < height) | |
| 238 | ✗ | AV_WN32(buf + h * linesize, clr); | |
| 239 | ✗ | } | |
| 240 | |||
| 241 | ✗ | static void draw_sample_line_rgba_scale(uint8_t *buf, int height, int linesize, | |
| 242 | int16_t *prev_y, | ||
| 243 | const uint8_t color[4], int h) | ||
| 244 | { | ||
| 245 | ✗ | int start = height/2; | |
| 246 | ✗ | int end = av_clip(h, 0, height-1); | |
| 247 | uint8_t *bufk; | ||
| 248 | ✗ | if (start > end) | |
| 249 | ✗ | FFSWAP(int16_t, start, end); | |
| 250 | ✗ | bufk = buf + start * linesize; | |
| 251 | ✗ | for (int k = start; k < end; k++, bufk += linesize) { | |
| 252 | ✗ | bufk[0] += color[0]; | |
| 253 | ✗ | bufk[1] += color[1]; | |
| 254 | ✗ | bufk[2] += color[2]; | |
| 255 | ✗ | bufk[3] += color[3]; | |
| 256 | } | ||
| 257 | ✗ | } | |
| 258 | |||
| 259 | ✗ | static void draw_sample_line_rgba_full(uint8_t *buf, int height, int linesize, | |
| 260 | int16_t *prev_y, | ||
| 261 | const uint8_t color[4], int h) | ||
| 262 | { | ||
| 263 | ✗ | int start = height/2; | |
| 264 | ✗ | int end = av_clip(h, 0, height-1); | |
| 265 | ✗ | uint32_t clr = AV_RN32(color); | |
| 266 | uint8_t *bufk; | ||
| 267 | ✗ | if (start > end) | |
| 268 | ✗ | FFSWAP(int16_t, start, end); | |
| 269 | ✗ | bufk = buf + start * linesize; | |
| 270 | ✗ | for (int k = start; k < end; k++, bufk += linesize) | |
| 271 | ✗ | AV_WN32(bufk, clr); | |
| 272 | ✗ | } | |
| 273 | |||
| 274 | ✗ | static void draw_sample_p2p_rgba_scale(uint8_t *buf, int height, int linesize, | |
| 275 | int16_t *prev_y, | ||
| 276 | const uint8_t color[4], int h) | ||
| 277 | { | ||
| 278 | ✗ | if (h >= 0 && h < height) { | |
| 279 | ✗ | buf[h * linesize + 0] += color[0]; | |
| 280 | ✗ | buf[h * linesize + 1] += color[1]; | |
| 281 | ✗ | buf[h * linesize + 2] += color[2]; | |
| 282 | ✗ | buf[h * linesize + 3] += color[3]; | |
| 283 | ✗ | if (*prev_y && h != *prev_y) { | |
| 284 | ✗ | int start = *prev_y; | |
| 285 | uint8_t *bufk; | ||
| 286 | ✗ | int end = av_clip(h, 0, height-1); | |
| 287 | ✗ | if (start > end) | |
| 288 | ✗ | FFSWAP(int16_t, start, end); | |
| 289 | ✗ | bufk = buf + (start + 1) * linesize; | |
| 290 | ✗ | for (int k = start + 1; k < end; k++, bufk += linesize) { | |
| 291 | ✗ | bufk[0] += color[0]; | |
| 292 | ✗ | bufk[1] += color[1]; | |
| 293 | ✗ | bufk[2] += color[2]; | |
| 294 | ✗ | bufk[3] += color[3]; | |
| 295 | } | ||
| 296 | } | ||
| 297 | } | ||
| 298 | ✗ | *prev_y = h; | |
| 299 | ✗ | } | |
| 300 | |||
| 301 | ✗ | static void draw_sample_p2p_rgba_full(uint8_t *buf, int height, int linesize, | |
| 302 | int16_t *prev_y, | ||
| 303 | const uint8_t color[4], int h) | ||
| 304 | { | ||
| 305 | ✗ | uint32_t clr = AV_RN32(color); | |
| 306 | ✗ | if (h >= 0 && h < height) { | |
| 307 | ✗ | AV_WN32(buf + h * linesize, clr); | |
| 308 | ✗ | if (*prev_y && h != *prev_y) { | |
| 309 | ✗ | int start = *prev_y; | |
| 310 | uint8_t *bufk; | ||
| 311 | ✗ | int end = av_clip(h, 0, height-1); | |
| 312 | ✗ | if (start > end) | |
| 313 | ✗ | FFSWAP(int16_t, start, end); | |
| 314 | ✗ | bufk = buf + (start + 1) * linesize; | |
| 315 | ✗ | for (int k = start + 1; k < end; k++, bufk += linesize) | |
| 316 | ✗ | AV_WN32(bufk, clr); | |
| 317 | } | ||
| 318 | } | ||
| 319 | ✗ | *prev_y = h; | |
| 320 | ✗ | } | |
| 321 | |||
| 322 | ✗ | static void draw_sample_cline_rgba_scale(uint8_t *buf, int height, int linesize, | |
| 323 | int16_t *prev_y, | ||
| 324 | const uint8_t color[4], int h) | ||
| 325 | { | ||
| 326 | ✗ | const int start = (height - h) / 2; | |
| 327 | ✗ | const int end = start + h; | |
| 328 | ✗ | uint8_t *bufk = buf + start * linesize; | |
| 329 | ✗ | for (int k = start; k < end; k++, bufk += linesize) { | |
| 330 | ✗ | bufk[0] += color[0]; | |
| 331 | ✗ | bufk[1] += color[1]; | |
| 332 | ✗ | bufk[2] += color[2]; | |
| 333 | ✗ | bufk[3] += color[3]; | |
| 334 | } | ||
| 335 | ✗ | } | |
| 336 | |||
| 337 | ✗ | static void draw_sample_cline_rgba_full(uint8_t *buf, int height, int linesize, | |
| 338 | int16_t *prev_y, | ||
| 339 | const uint8_t color[4], int h) | ||
| 340 | { | ||
| 341 | ✗ | uint32_t clr = AV_RN32(color); | |
| 342 | ✗ | const int start = (height - h) / 2; | |
| 343 | ✗ | const int end = start + h; | |
| 344 | ✗ | uint8_t *bufk = buf + start * linesize; | |
| 345 | ✗ | for (int k = start; k < end; k++, bufk += linesize) | |
| 346 | ✗ | AV_WN32(bufk, clr); | |
| 347 | ✗ | } | |
| 348 | |||
| 349 | ✗ | static void draw_sample_point_gray(uint8_t *buf, int height, int linesize, | |
| 350 | int16_t *prev_y, | ||
| 351 | const uint8_t color[4], int h) | ||
| 352 | { | ||
| 353 | ✗ | if (h >= 0 && h < height) | |
| 354 | ✗ | buf[h * linesize] += color[0]; | |
| 355 | ✗ | } | |
| 356 | |||
| 357 | ✗ | static void draw_sample_line_gray(uint8_t *buf, int height, int linesize, | |
| 358 | int16_t *prev_y, | ||
| 359 | const uint8_t color[4], int h) | ||
| 360 | { | ||
| 361 | int k; | ||
| 362 | ✗ | int start = height/2; | |
| 363 | ✗ | int end = av_clip(h, 0, height-1); | |
| 364 | ✗ | if (start > end) | |
| 365 | ✗ | FFSWAP(int16_t, start, end); | |
| 366 | ✗ | for (k = start; k < end; k++) | |
| 367 | ✗ | buf[k * linesize] += color[0]; | |
| 368 | ✗ | } | |
| 369 | |||
| 370 | ✗ | static void draw_sample_p2p_gray(uint8_t *buf, int height, int linesize, | |
| 371 | int16_t *prev_y, | ||
| 372 | const uint8_t color[4], int h) | ||
| 373 | { | ||
| 374 | int k; | ||
| 375 | ✗ | if (h >= 0 && h < height) { | |
| 376 | ✗ | buf[h * linesize] += color[0]; | |
| 377 | ✗ | if (*prev_y && h != *prev_y) { | |
| 378 | ✗ | int start = *prev_y; | |
| 379 | ✗ | int end = av_clip(h, 0, height-1); | |
| 380 | ✗ | if (start > end) | |
| 381 | ✗ | FFSWAP(int16_t, start, end); | |
| 382 | ✗ | for (k = start + 1; k < end; k++) | |
| 383 | ✗ | buf[k * linesize] += color[0]; | |
| 384 | } | ||
| 385 | } | ||
| 386 | ✗ | *prev_y = h; | |
| 387 | ✗ | } | |
| 388 | |||
| 389 | ✗ | static void draw_sample_cline_gray(uint8_t *buf, int height, int linesize, | |
| 390 | int16_t *prev_y, | ||
| 391 | const uint8_t color[4], int h) | ||
| 392 | { | ||
| 393 | int k; | ||
| 394 | ✗ | const int start = (height - h) / 2; | |
| 395 | ✗ | const int end = start + h; | |
| 396 | ✗ | for (k = start; k < end; k++) | |
| 397 | ✗ | buf[k * linesize] += color[0]; | |
| 398 | ✗ | } | |
| 399 | |||
| 400 | ✗ | static int config_output(AVFilterLink *outlink) | |
| 401 | { | ||
| 402 | ✗ | FilterLink *l = ff_filter_link(outlink); | |
| 403 | ✗ | AVFilterContext *ctx = outlink->src; | |
| 404 | ✗ | AVFilterLink *inlink = ctx->inputs[0]; | |
| 405 | ✗ | ShowWavesContext *showwaves = ctx->priv; | |
| 406 | ✗ | int nb_channels = inlink->ch_layout.nb_channels; | |
| 407 | ✗ | char *colors, *saveptr = NULL; | |
| 408 | uint8_t x; | ||
| 409 | int ch; | ||
| 410 | |||
| 411 | ✗ | showwaves->q = av_make_q(0, 1); | |
| 412 | ✗ | showwaves->c = av_make_q(0, 1); | |
| 413 | |||
| 414 | ✗ | if (showwaves->single_pic) { | |
| 415 | ✗ | showwaves->n = av_make_q(1, 1); | |
| 416 | ✗ | l->frame_rate = av_make_q(1, 1); | |
| 417 | } else { | ||
| 418 | ✗ | if (!showwaves->n.num || !showwaves->n.den) { | |
| 419 | ✗ | showwaves->n = av_mul_q(av_make_q(inlink->sample_rate, | |
| 420 | showwaves->w), av_inv_q(showwaves->rate)); | ||
| 421 | ✗ | l->frame_rate = showwaves->rate; | |
| 422 | } else { | ||
| 423 | ✗ | l->frame_rate = av_div_q(av_make_q(inlink->sample_rate, showwaves->w), showwaves->n); | |
| 424 | } | ||
| 425 | } | ||
| 426 | |||
| 427 | ✗ | showwaves->buf_idx = 0; | |
| 428 | ✗ | if (!FF_ALLOCZ_TYPED_ARRAY(showwaves->buf_idy, nb_channels)) { | |
| 429 | ✗ | av_log(ctx, AV_LOG_ERROR, "Could not allocate showwaves buffer\n"); | |
| 430 | ✗ | return AVERROR(ENOMEM); | |
| 431 | } | ||
| 432 | |||
| 433 | ✗ | showwaves->history_nb_samples = av_rescale(showwaves->w * nb_channels * 2, | |
| 434 | ✗ | showwaves->n.num, showwaves->n.den); | |
| 435 | ✗ | if (showwaves->history_nb_samples <= 0) | |
| 436 | ✗ | return AVERROR(EINVAL); | |
| 437 | ✗ | showwaves->history = av_calloc(showwaves->history_nb_samples, | |
| 438 | sizeof(*showwaves->history)); | ||
| 439 | ✗ | if (!showwaves->history) | |
| 440 | ✗ | return AVERROR(ENOMEM); | |
| 441 | |||
| 442 | ✗ | outlink->time_base = av_inv_q(l->frame_rate); | |
| 443 | ✗ | outlink->w = showwaves->w; | |
| 444 | ✗ | outlink->h = showwaves->h; | |
| 445 | ✗ | outlink->sample_aspect_ratio = (AVRational){1,1}; | |
| 446 | |||
| 447 | ✗ | av_log(ctx, AV_LOG_VERBOSE, "s:%dx%d r:%f n:%f\n", | |
| 448 | showwaves->w, showwaves->h, av_q2d(l->frame_rate), av_q2d(showwaves->n)); | ||
| 449 | |||
| 450 | ✗ | switch (outlink->format) { | |
| 451 | ✗ | case AV_PIX_FMT_GRAY8: | |
| 452 | ✗ | switch (showwaves->mode) { | |
| 453 | ✗ | case MODE_POINT: showwaves->draw_sample = draw_sample_point_gray; break; | |
| 454 | ✗ | case MODE_LINE: showwaves->draw_sample = draw_sample_line_gray; break; | |
| 455 | ✗ | case MODE_P2P: showwaves->draw_sample = draw_sample_p2p_gray; break; | |
| 456 | ✗ | case MODE_CENTERED_LINE: showwaves->draw_sample = draw_sample_cline_gray; break; | |
| 457 | ✗ | default: | |
| 458 | ✗ | return AVERROR_BUG; | |
| 459 | } | ||
| 460 | ✗ | showwaves->pixstep = 1; | |
| 461 | ✗ | break; | |
| 462 | ✗ | case AV_PIX_FMT_RGBA: | |
| 463 | ✗ | switch (showwaves->mode) { | |
| 464 | ✗ | case MODE_POINT: showwaves->draw_sample = showwaves->draw_mode == DRAW_SCALE ? draw_sample_point_rgba_scale : draw_sample_point_rgba_full; break; | |
| 465 | ✗ | case MODE_LINE: showwaves->draw_sample = showwaves->draw_mode == DRAW_SCALE ? draw_sample_line_rgba_scale : draw_sample_line_rgba_full; break; | |
| 466 | ✗ | case MODE_P2P: showwaves->draw_sample = showwaves->draw_mode == DRAW_SCALE ? draw_sample_p2p_rgba_scale : draw_sample_p2p_rgba_full; break; | |
| 467 | ✗ | case MODE_CENTERED_LINE: showwaves->draw_sample = showwaves->draw_mode == DRAW_SCALE ? draw_sample_cline_rgba_scale : draw_sample_cline_rgba_full; break; | |
| 468 | ✗ | default: | |
| 469 | ✗ | return AVERROR_BUG; | |
| 470 | } | ||
| 471 | ✗ | showwaves->pixstep = 4; | |
| 472 | ✗ | break; | |
| 473 | } | ||
| 474 | |||
| 475 | ✗ | switch (showwaves->scale) { | |
| 476 | ✗ | case SCALE_LIN: | |
| 477 | ✗ | switch (showwaves->mode) { | |
| 478 | ✗ | case MODE_POINT: | |
| 479 | case MODE_LINE: | ||
| 480 | ✗ | case MODE_P2P: showwaves->get_h = get_lin_h; break; | |
| 481 | ✗ | case MODE_CENTERED_LINE: showwaves->get_h = get_lin_h2; break; | |
| 482 | ✗ | default: | |
| 483 | ✗ | return AVERROR_BUG; | |
| 484 | } | ||
| 485 | ✗ | break; | |
| 486 | ✗ | case SCALE_LOG: | |
| 487 | ✗ | switch (showwaves->mode) { | |
| 488 | ✗ | case MODE_POINT: | |
| 489 | case MODE_LINE: | ||
| 490 | ✗ | case MODE_P2P: showwaves->get_h = get_log_h; break; | |
| 491 | ✗ | case MODE_CENTERED_LINE: showwaves->get_h = get_log_h2; break; | |
| 492 | ✗ | default: | |
| 493 | ✗ | return AVERROR_BUG; | |
| 494 | } | ||
| 495 | ✗ | break; | |
| 496 | ✗ | case SCALE_SQRT: | |
| 497 | ✗ | switch (showwaves->mode) { | |
| 498 | ✗ | case MODE_POINT: | |
| 499 | case MODE_LINE: | ||
| 500 | ✗ | case MODE_P2P: showwaves->get_h = get_sqrt_h; break; | |
| 501 | ✗ | case MODE_CENTERED_LINE: showwaves->get_h = get_sqrt_h2; break; | |
| 502 | ✗ | default: | |
| 503 | ✗ | return AVERROR_BUG; | |
| 504 | } | ||
| 505 | ✗ | break; | |
| 506 | ✗ | case SCALE_CBRT: | |
| 507 | ✗ | switch (showwaves->mode) { | |
| 508 | ✗ | case MODE_POINT: | |
| 509 | case MODE_LINE: | ||
| 510 | ✗ | case MODE_P2P: showwaves->get_h = get_cbrt_h; break; | |
| 511 | ✗ | case MODE_CENTERED_LINE: showwaves->get_h = get_cbrt_h2; break; | |
| 512 | ✗ | default: | |
| 513 | ✗ | return AVERROR_BUG; | |
| 514 | } | ||
| 515 | ✗ | break; | |
| 516 | } | ||
| 517 | |||
| 518 | ✗ | showwaves->fg = av_malloc_array(nb_channels, 4 * sizeof(*showwaves->fg)); | |
| 519 | ✗ | if (!showwaves->fg) | |
| 520 | ✗ | return AVERROR(ENOMEM); | |
| 521 | |||
| 522 | ✗ | colors = av_strdup(showwaves->colors); | |
| 523 | ✗ | if (!colors) | |
| 524 | ✗ | return AVERROR(ENOMEM); | |
| 525 | |||
| 526 | ✗ | if (showwaves->draw_mode == DRAW_SCALE) { | |
| 527 | /* multiplication factor, pre-computed to avoid in-loop divisions */ | ||
| 528 | ✗ | x = (showwaves->n.den * 255) / ((showwaves->split_channels ? 1 : nb_channels) * showwaves->n.num); | |
| 529 | } else { | ||
| 530 | ✗ | x = 255; | |
| 531 | } | ||
| 532 | ✗ | if (outlink->format == AV_PIX_FMT_RGBA) { | |
| 533 | ✗ | uint8_t fg[4] = { 0xff, 0xff, 0xff, 0xff }; | |
| 534 | |||
| 535 | ✗ | for (ch = 0; ch < nb_channels; ch++) { | |
| 536 | char *color; | ||
| 537 | |||
| 538 | ✗ | color = av_strtok(ch == 0 ? colors : NULL, " |", &saveptr); | |
| 539 | ✗ | if (color) | |
| 540 | ✗ | av_parse_color(fg, color, -1, ctx); | |
| 541 | ✗ | showwaves->fg[4*ch + 0] = fg[0] * x / 255.; | |
| 542 | ✗ | showwaves->fg[4*ch + 1] = fg[1] * x / 255.; | |
| 543 | ✗ | showwaves->fg[4*ch + 2] = fg[2] * x / 255.; | |
| 544 | ✗ | showwaves->fg[4*ch + 3] = fg[3] * x / 255.; | |
| 545 | } | ||
| 546 | } else { | ||
| 547 | ✗ | for (ch = 0; ch < nb_channels; ch++) | |
| 548 | ✗ | showwaves->fg[4 * ch + 0] = x; | |
| 549 | } | ||
| 550 | ✗ | av_free(colors); | |
| 551 | |||
| 552 | ✗ | return 0; | |
| 553 | } | ||
| 554 | |||
| 555 | ✗ | inline static int push_frame(AVFilterLink *outlink, int i, int64_t pts) | |
| 556 | { | ||
| 557 | ✗ | AVFilterContext *ctx = outlink->src; | |
| 558 | ✗ | AVFilterLink *inlink = ctx->inputs[0]; | |
| 559 | ✗ | ShowWavesContext *showwaves = outlink->src->priv; | |
| 560 | ✗ | int nb_channels = inlink->ch_layout.nb_channels; | |
| 561 | int ret; | ||
| 562 | |||
| 563 | ✗ | showwaves->outpicref->duration = 1; | |
| 564 | ✗ | showwaves->outpicref->pts = av_rescale_q(pts + i, | |
| 565 | inlink->time_base, | ||
| 566 | outlink->time_base); | ||
| 567 | |||
| 568 | ✗ | ret = ff_filter_frame(outlink, showwaves->outpicref); | |
| 569 | ✗ | showwaves->outpicref = NULL; | |
| 570 | ✗ | showwaves->buf_idx = 0; | |
| 571 | ✗ | for (int i = 0; i < nb_channels; i++) | |
| 572 | ✗ | showwaves->buf_idy[i] = 0; | |
| 573 | ✗ | return ret; | |
| 574 | } | ||
| 575 | |||
| 576 | ✗ | static int push_single_pic(AVFilterLink *outlink) | |
| 577 | { | ||
| 578 | ✗ | AVFilterContext *ctx = outlink->src; | |
| 579 | ✗ | AVFilterLink *inlink = ctx->inputs[0]; | |
| 580 | ✗ | ShowWavesContext *showwaves = ctx->priv; | |
| 581 | ✗ | int64_t n = 0, column_max_samples = showwaves->total_samples / outlink->w; | |
| 582 | ✗ | int64_t remaining_samples = showwaves->total_samples - (column_max_samples * outlink->w); | |
| 583 | ✗ | int64_t last_column_samples = column_max_samples + remaining_samples; | |
| 584 | ✗ | AVFrame *out = showwaves->outpicref; | |
| 585 | struct frame_node *node; | ||
| 586 | ✗ | const int nb_channels = inlink->ch_layout.nb_channels; | |
| 587 | ✗ | const int ch_height = showwaves->split_channels ? outlink->h / nb_channels : outlink->h; | |
| 588 | ✗ | const int linesize = out->linesize[0]; | |
| 589 | ✗ | const int pixstep = showwaves->pixstep; | |
| 590 | ✗ | int col = 0; | |
| 591 | ✗ | int64_t *sum = showwaves->sum; | |
| 592 | |||
| 593 | ✗ | if (column_max_samples == 0) { | |
| 594 | ✗ | av_log(ctx, AV_LOG_ERROR, "Too few samples\n"); | |
| 595 | ✗ | return AVERROR(EINVAL); | |
| 596 | } | ||
| 597 | |||
| 598 | ✗ | av_log(ctx, AV_LOG_DEBUG, "Create frame averaging %"PRId64" samples per column\n", column_max_samples); | |
| 599 | |||
| 600 | ✗ | memset(sum, 0, nb_channels * sizeof(*sum)); | |
| 601 | |||
| 602 | ✗ | for (node = showwaves->audio_frames; node; node = node->next) { | |
| 603 | int i; | ||
| 604 | ✗ | const AVFrame *frame = node->frame; | |
| 605 | ✗ | const int16_t *p = (const int16_t *)frame->data[0]; | |
| 606 | |||
| 607 | ✗ | for (i = 0; i < frame->nb_samples; i++) { | |
| 608 | ✗ | int64_t max_samples = col == outlink->w - 1 ? last_column_samples: column_max_samples; | |
| 609 | int ch; | ||
| 610 | |||
| 611 | ✗ | switch (showwaves->filter_mode) { | |
| 612 | ✗ | case FILTER_AVERAGE: | |
| 613 | ✗ | for (ch = 0; ch < nb_channels; ch++) | |
| 614 | ✗ | sum[ch] += abs(p[ch + i*nb_channels]); | |
| 615 | ✗ | break; | |
| 616 | ✗ | case FILTER_PEAK: | |
| 617 | ✗ | for (ch = 0; ch < nb_channels; ch++) | |
| 618 | ✗ | sum[ch] = FFMAX(sum[ch], abs(p[ch + i*nb_channels])); | |
| 619 | ✗ | break; | |
| 620 | } | ||
| 621 | |||
| 622 | ✗ | n++; | |
| 623 | ✗ | if (n == max_samples) { | |
| 624 | ✗ | for (ch = 0; ch < nb_channels; ch++) { | |
| 625 | ✗ | int16_t sample = sum[ch] / (showwaves->filter_mode == FILTER_AVERAGE ? max_samples : 1); | |
| 626 | ✗ | uint8_t *buf = out->data[0] + col * pixstep; | |
| 627 | int h; | ||
| 628 | |||
| 629 | ✗ | if (showwaves->split_channels) | |
| 630 | ✗ | buf += ch*ch_height*linesize; | |
| 631 | ✗ | av_assert0(col < outlink->w); | |
| 632 | ✗ | h = showwaves->get_h(sample, ch_height); | |
| 633 | ✗ | showwaves->draw_sample(buf, ch_height, linesize, &showwaves->buf_idy[ch], &showwaves->fg[ch * 4], h); | |
| 634 | ✗ | sum[ch] = 0; | |
| 635 | } | ||
| 636 | ✗ | col++; | |
| 637 | ✗ | n = 0; | |
| 638 | } | ||
| 639 | } | ||
| 640 | } | ||
| 641 | |||
| 642 | ✗ | return push_frame(outlink, 0, 0); | |
| 643 | } | ||
| 644 | |||
| 645 | |||
| 646 | ✗ | static int request_frame(AVFilterLink *outlink) | |
| 647 | { | ||
| 648 | ✗ | ShowWavesContext *showwaves = outlink->src->priv; | |
| 649 | ✗ | AVFilterLink *inlink = outlink->src->inputs[0]; | |
| 650 | int ret; | ||
| 651 | |||
| 652 | ✗ | ret = ff_request_frame(inlink); | |
| 653 | ✗ | if (ret == AVERROR_EOF && showwaves->outpicref) { | |
| 654 | ✗ | push_single_pic(outlink); | |
| 655 | } | ||
| 656 | |||
| 657 | ✗ | return ret; | |
| 658 | } | ||
| 659 | |||
| 660 | ✗ | static int alloc_out_frame(ShowWavesContext *showwaves, | |
| 661 | AVFilterLink *outlink) | ||
| 662 | { | ||
| 663 | ✗ | if (!showwaves->outpicref) { | |
| 664 | ✗ | AVFrame *out = showwaves->outpicref = | |
| 665 | ✗ | ff_get_video_buffer(outlink, outlink->w, outlink->h); | |
| 666 | ✗ | if (!out) | |
| 667 | ✗ | return AVERROR(ENOMEM); | |
| 668 | ✗ | out->width = outlink->w; | |
| 669 | ✗ | out->height = outlink->h; | |
| 670 | ✗ | for (int j = 0; j < outlink->h; j++) | |
| 671 | ✗ | memset(out->data[0] + j*out->linesize[0], 0, outlink->w * showwaves->pixstep); | |
| 672 | } | ||
| 673 | ✗ | return 0; | |
| 674 | } | ||
| 675 | |||
| 676 | ✗ | static av_cold int init(AVFilterContext *ctx) | |
| 677 | { | ||
| 678 | ✗ | ShowWavesContext *showwaves = ctx->priv; | |
| 679 | |||
| 680 | ✗ | if (!strcmp(ctx->filter->name, "showwavespic")) { | |
| 681 | ✗ | showwaves->single_pic = 1; | |
| 682 | ✗ | showwaves->mode = MODE_CENTERED_LINE; | |
| 683 | } | ||
| 684 | |||
| 685 | ✗ | return 0; | |
| 686 | } | ||
| 687 | |||
| 688 | #if CONFIG_SHOWWAVES_FILTER | ||
| 689 | |||
| 690 | ✗ | static int showwaves_filter_frame(AVFilterLink *inlink, AVFrame *insamples) | |
| 691 | { | ||
| 692 | ✗ | AVFilterContext *ctx = inlink->dst; | |
| 693 | ✗ | AVFilterLink *outlink = ctx->outputs[0]; | |
| 694 | ✗ | ShowWavesContext *showwaves = ctx->priv; | |
| 695 | ✗ | const int nb_samples = insamples->nb_samples; | |
| 696 | ✗ | AVFrame *outpicref = showwaves->outpicref; | |
| 697 | ✗ | const int16_t *p = (const int16_t *)insamples->data[0]; | |
| 698 | ✗ | int16_t *history = showwaves->history; | |
| 699 | ✗ | const int nb_channels = inlink->ch_layout.nb_channels; | |
| 700 | ✗ | int i, j, ret = 0, linesize; | |
| 701 | ✗ | const int pixstep = showwaves->pixstep; | |
| 702 | ✗ | const int ch_height = showwaves->split_channels ? outlink->h / nb_channels : outlink->h; | |
| 703 | ✗ | const int history_nb_samples = showwaves->history_nb_samples; | |
| 704 | ✗ | const int split_channels = showwaves->split_channels; | |
| 705 | ✗ | const AVRational i_n = av_inv_q(showwaves->n); | |
| 706 | ✗ | const AVRational u_q = av_make_q(1, 1); | |
| 707 | ✗ | const AVRational z_q = av_make_q(0, 1); | |
| 708 | ✗ | int16_t *buf_idy = showwaves->buf_idy; | |
| 709 | ✗ | int idx = showwaves->history_index; | |
| 710 | ✗ | int buf_idx = showwaves->buf_idx; | |
| 711 | ✗ | const uint8_t *fg = showwaves->fg; | |
| 712 | ✗ | const int w = showwaves->w; | |
| 713 | uint8_t *dst; | ||
| 714 | |||
| 715 | ✗ | for (int n = 0; n < nb_samples * nb_channels; n++) { | |
| 716 | ✗ | history[idx++] = p[n]; | |
| 717 | ✗ | if (idx >= history_nb_samples) | |
| 718 | ✗ | idx = 0; | |
| 719 | } | ||
| 720 | ✗ | showwaves->history_index = idx; | |
| 721 | |||
| 722 | ✗ | ret = alloc_out_frame(showwaves, outlink); | |
| 723 | ✗ | if (ret < 0) | |
| 724 | ✗ | goto end; | |
| 725 | ✗ | outpicref = showwaves->outpicref; | |
| 726 | ✗ | linesize = outpicref->linesize[0]; | |
| 727 | |||
| 728 | /* draw data in the buffer */ | ||
| 729 | ✗ | dst = outpicref->data[0]; | |
| 730 | ✗ | for (i = 0; i < history_nb_samples; i++) { | |
| 731 | ✗ | for (j = 0; j < nb_channels; j++) { | |
| 732 | ✗ | uint8_t *buf = dst + buf_idx * pixstep; | |
| 733 | int h; | ||
| 734 | |||
| 735 | ✗ | if (split_channels) | |
| 736 | ✗ | buf += j*ch_height*linesize; | |
| 737 | ✗ | h = showwaves->get_h(history[idx++], ch_height); | |
| 738 | ✗ | if (idx >= history_nb_samples) | |
| 739 | ✗ | idx = 0; | |
| 740 | ✗ | showwaves->draw_sample(buf, ch_height, linesize, | |
| 741 | ✗ | &buf_idy[j], &fg[j * 4], h); | |
| 742 | } | ||
| 743 | |||
| 744 | ✗ | showwaves->c = av_add_q(showwaves->c, i_n); | |
| 745 | ✗ | if (av_cmp_q(showwaves->c, u_q) >= 0) { | |
| 746 | ✗ | showwaves->c = z_q; | |
| 747 | ✗ | buf_idx++; | |
| 748 | } | ||
| 749 | ✗ | if (buf_idx == w) | |
| 750 | ✗ | break; | |
| 751 | } | ||
| 752 | |||
| 753 | ✗ | showwaves->buf_idx = buf_idx; | |
| 754 | |||
| 755 | ✗ | if ((ret = push_frame(outlink, history_nb_samples - i - 1, insamples->pts)) < 0) | |
| 756 | ✗ | goto end; | |
| 757 | ✗ | outpicref = showwaves->outpicref; | |
| 758 | ✗ | end: | |
| 759 | ✗ | av_frame_free(&insamples); | |
| 760 | ✗ | return ret; | |
| 761 | } | ||
| 762 | |||
| 763 | ✗ | static int activate(AVFilterContext *ctx) | |
| 764 | { | ||
| 765 | ✗ | AVFilterLink *inlink = ctx->inputs[0]; | |
| 766 | ✗ | AVFilterLink *outlink = ctx->outputs[0]; | |
| 767 | ✗ | ShowWavesContext *showwaves = ctx->priv; | |
| 768 | AVRational q; | ||
| 769 | AVFrame *in; | ||
| 770 | int nb_samples; | ||
| 771 | int ret; | ||
| 772 | |||
| 773 | ✗ | FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); | |
| 774 | |||
| 775 | ✗ | q = av_add_q(showwaves->q, av_mul_q(av_make_q(outlink->w, 1), showwaves->n)); | |
| 776 | ✗ | nb_samples = (q.num + (q.den / 2)) / q.den; | |
| 777 | ✗ | ret = ff_inlink_consume_samples(inlink, nb_samples, nb_samples, &in); | |
| 778 | ✗ | if (ret < 0) | |
| 779 | ✗ | return ret; | |
| 780 | ✗ | if (ret > 0) { | |
| 781 | ✗ | showwaves->q = av_sub_q(q, av_make_q(nb_samples, 1)); | |
| 782 | ✗ | return showwaves_filter_frame(inlink, in); | |
| 783 | } | ||
| 784 | |||
| 785 | ✗ | FF_FILTER_FORWARD_STATUS(inlink, outlink); | |
| 786 | ✗ | FF_FILTER_FORWARD_WANTED(outlink, inlink); | |
| 787 | |||
| 788 | ✗ | return FFERROR_NOT_READY; | |
| 789 | } | ||
| 790 | |||
| 791 | static const AVFilterPad showwaves_outputs[] = { | ||
| 792 | { | ||
| 793 | .name = "default", | ||
| 794 | .type = AVMEDIA_TYPE_VIDEO, | ||
| 795 | .config_props = config_output, | ||
| 796 | }, | ||
| 797 | }; | ||
| 798 | |||
| 799 | const FFFilter ff_avf_showwaves = { | ||
| 800 | .p.name = "showwaves", | ||
| 801 | .p.description = NULL_IF_CONFIG_SMALL("Convert input audio to a video output."), | ||
| 802 | .p.priv_class = &showwaves_class, | ||
| 803 | .init = init, | ||
| 804 | .uninit = uninit, | ||
| 805 | .priv_size = sizeof(ShowWavesContext), | ||
| 806 | FILTER_INPUTS(ff_audio_default_filterpad), | ||
| 807 | .activate = activate, | ||
| 808 | FILTER_OUTPUTS(showwaves_outputs), | ||
| 809 | FILTER_QUERY_FUNC2(query_formats), | ||
| 810 | }; | ||
| 811 | |||
| 812 | #endif // CONFIG_SHOWWAVES_FILTER | ||
| 813 | |||
| 814 | #if CONFIG_SHOWWAVESPIC_FILTER | ||
| 815 | |||
| 816 | #define OFFSET(x) offsetof(ShowWavesContext, x) | ||
| 817 | #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM | ||
| 818 | |||
| 819 | static const AVOption showwavespic_options[] = { | ||
| 820 | { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "600x240"}, 0, 0, FLAGS }, | ||
| 821 | { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "600x240"}, 0, 0, FLAGS }, | ||
| 822 | { "split_channels", "draw channels separately", OFFSET(split_channels), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS }, | ||
| 823 | { "colors", "set channels colors", OFFSET(colors), AV_OPT_TYPE_STRING, {.str = "red|green|blue|yellow|orange|lime|pink|magenta|brown" }, 0, 0, FLAGS }, | ||
| 824 | { "scale", "set amplitude scale", OFFSET(scale), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, SCALE_NB-1, FLAGS, .unit="scale" }, | ||
| 825 | { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=SCALE_LIN}, .flags=FLAGS, .unit="scale"}, | ||
| 826 | { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=SCALE_LOG}, .flags=FLAGS, .unit="scale"}, | ||
| 827 | { "sqrt", "square root", 0, AV_OPT_TYPE_CONST, {.i64=SCALE_SQRT}, .flags=FLAGS, .unit="scale"}, | ||
| 828 | { "cbrt", "cubic root", 0, AV_OPT_TYPE_CONST, {.i64=SCALE_CBRT}, .flags=FLAGS, .unit="scale"}, | ||
| 829 | { "draw", "set draw mode", OFFSET(draw_mode), AV_OPT_TYPE_INT, {.i64 = DRAW_SCALE}, 0, DRAW_NB-1, FLAGS, .unit="draw" }, | ||
| 830 | { "scale", "scale pixel values for each drawn sample", 0, AV_OPT_TYPE_CONST, {.i64=DRAW_SCALE}, .flags=FLAGS, .unit="draw"}, | ||
| 831 | { "full", "draw every pixel for sample directly", 0, AV_OPT_TYPE_CONST, {.i64=DRAW_FULL}, .flags=FLAGS, .unit="draw"}, | ||
| 832 | { "filter", "set filter mode", OFFSET(filter_mode), AV_OPT_TYPE_INT, {.i64 = FILTER_AVERAGE}, 0, FILTER_NB-1, FLAGS, .unit="filter" }, | ||
| 833 | { "average", "use average samples", 0, AV_OPT_TYPE_CONST, {.i64=FILTER_AVERAGE}, .flags=FLAGS, .unit="filter"}, | ||
| 834 | { "peak", "use peak samples", 0, AV_OPT_TYPE_CONST, {.i64=FILTER_PEAK}, .flags=FLAGS, .unit="filter"}, | ||
| 835 | { NULL } | ||
| 836 | }; | ||
| 837 | |||
| 838 | AVFILTER_DEFINE_CLASS(showwavespic); | ||
| 839 | |||
| 840 | ✗ | static int showwavespic_config_input(AVFilterLink *inlink) | |
| 841 | { | ||
| 842 | ✗ | AVFilterContext *ctx = inlink->dst; | |
| 843 | ✗ | ShowWavesContext *showwaves = ctx->priv; | |
| 844 | |||
| 845 | ✗ | if (showwaves->single_pic) { | |
| 846 | ✗ | showwaves->sum = av_calloc(inlink->ch_layout.nb_channels, sizeof(*showwaves->sum)); | |
| 847 | ✗ | if (!showwaves->sum) | |
| 848 | ✗ | return AVERROR(ENOMEM); | |
| 849 | } | ||
| 850 | |||
| 851 | ✗ | return 0; | |
| 852 | } | ||
| 853 | |||
| 854 | ✗ | static int showwavespic_filter_frame(AVFilterLink *inlink, AVFrame *insamples) | |
| 855 | { | ||
| 856 | ✗ | AVFilterContext *ctx = inlink->dst; | |
| 857 | ✗ | AVFilterLink *outlink = ctx->outputs[0]; | |
| 858 | ✗ | ShowWavesContext *showwaves = ctx->priv; | |
| 859 | ✗ | int ret = 0; | |
| 860 | |||
| 861 | ✗ | if (showwaves->single_pic) { | |
| 862 | struct frame_node *f; | ||
| 863 | |||
| 864 | ✗ | ret = alloc_out_frame(showwaves, outlink); | |
| 865 | ✗ | if (ret < 0) | |
| 866 | ✗ | goto end; | |
| 867 | |||
| 868 | /* queue the audio frame */ | ||
| 869 | ✗ | f = av_malloc(sizeof(*f)); | |
| 870 | ✗ | if (!f) { | |
| 871 | ✗ | ret = AVERROR(ENOMEM); | |
| 872 | ✗ | goto end; | |
| 873 | } | ||
| 874 | ✗ | f->frame = insamples; | |
| 875 | ✗ | f->next = NULL; | |
| 876 | ✗ | if (!showwaves->last_frame) { | |
| 877 | ✗ | showwaves->audio_frames = | |
| 878 | ✗ | showwaves->last_frame = f; | |
| 879 | } else { | ||
| 880 | ✗ | showwaves->last_frame->next = f; | |
| 881 | ✗ | showwaves->last_frame = f; | |
| 882 | } | ||
| 883 | ✗ | showwaves->total_samples += insamples->nb_samples; | |
| 884 | |||
| 885 | ✗ | return 0; | |
| 886 | } | ||
| 887 | |||
| 888 | ✗ | end: | |
| 889 | ✗ | av_frame_free(&insamples); | |
| 890 | ✗ | return ret; | |
| 891 | } | ||
| 892 | |||
| 893 | static const AVFilterPad showwavespic_inputs[] = { | ||
| 894 | { | ||
| 895 | .name = "default", | ||
| 896 | .type = AVMEDIA_TYPE_AUDIO, | ||
| 897 | .config_props = showwavespic_config_input, | ||
| 898 | .filter_frame = showwavespic_filter_frame, | ||
| 899 | }, | ||
| 900 | }; | ||
| 901 | |||
| 902 | static const AVFilterPad showwavespic_outputs[] = { | ||
| 903 | { | ||
| 904 | .name = "default", | ||
| 905 | .type = AVMEDIA_TYPE_VIDEO, | ||
| 906 | .config_props = config_output, | ||
| 907 | .request_frame = request_frame, | ||
| 908 | }, | ||
| 909 | }; | ||
| 910 | |||
| 911 | const FFFilter ff_avf_showwavespic = { | ||
| 912 | .p.name = "showwavespic", | ||
| 913 | .p.description = NULL_IF_CONFIG_SMALL("Convert input audio to a video output single picture."), | ||
| 914 | .p.priv_class = &showwavespic_class, | ||
| 915 | .init = init, | ||
| 916 | .uninit = uninit, | ||
| 917 | .priv_size = sizeof(ShowWavesContext), | ||
| 918 | FILTER_INPUTS(showwavespic_inputs), | ||
| 919 | FILTER_OUTPUTS(showwavespic_outputs), | ||
| 920 | FILTER_QUERY_FUNC2(query_formats), | ||
| 921 | }; | ||
| 922 | |||
| 923 | #endif // CONFIG_SHOWWAVESPIC_FILTER | ||
| 924 |