diff --git a/doc/filters.texi b/doc/filters.texi index 10f1111e35..7e7d8ff93b 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -15762,10 +15762,10 @@ Set video rate. Set border width, allowed range is [0, 5]. Default is 1. @item w -Set channel width, allowed range is [80, 1080]. Default is 400. +Set channel width, allowed range is [80, 8192]. Default is 400. @item h -Set channel height, allowed range is [1, 100]. Default is 20. +Set channel height, allowed range is [1, 900]. Default is 20. @item f Set fade, allowed range is [0.001, 1]. Default is 0.95. @@ -15788,6 +15788,14 @@ If set, displays channel names. Default is enabled. @item v If set, displays volume values. Default is enabled. + +@item o +Set orientation, can be @code{horizontal} or @code{vertical}, +default is @code{horizontal}. + +@item s +Set step size, allowed range s [0, 5]. Default is 0, which means +step is disabled. @end table @section showwaves diff --git a/libavfilter/avf_showvolume.c b/libavfilter/avf_showvolume.c index f7ccdf739e..0414b0732e 100644 --- a/libavfilter/avf_showvolume.c +++ b/libavfilter/avf_showvolume.c @@ -41,6 +41,8 @@ typedef struct ShowVolumeContext { double f; AVRational frame_rate; char *color; + int orientation; + int step; AVFrame *out; AVExpr *c_expr; @@ -56,12 +58,16 @@ static const AVOption showvolume_options[] = { { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, 0, FLAGS }, { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, 0, FLAGS }, { "b", "set border width", OFFSET(b), AV_OPT_TYPE_INT, {.i64=1}, 0, 5, FLAGS }, - { "w", "set channel width", OFFSET(w), AV_OPT_TYPE_INT, {.i64=400}, 80, 1080, FLAGS }, - { "h", "set channel height", OFFSET(h), AV_OPT_TYPE_INT, {.i64=20}, 1, 100, FLAGS }, + { "w", "set channel width", OFFSET(w), AV_OPT_TYPE_INT, {.i64=400}, 80, 8192, FLAGS }, + { "h", "set channel height", OFFSET(h), AV_OPT_TYPE_INT, {.i64=20}, 1, 900, FLAGS }, { "f", "set fade", OFFSET(f), AV_OPT_TYPE_DOUBLE, {.dbl=0.95}, 0.001, 1, FLAGS }, { "c", "set volume color expression", OFFSET(color), AV_OPT_TYPE_STRING, {.str="if(gte(VOLUME,-6), if(gte(VOLUME,-2), if(gte(VOLUME,-1), if(gt(VOLUME,0), 0xff0000ff, 0xff0066ff), 0xff00ffff),0xff00ff00),0xffff0000)"}, 0, 0, FLAGS }, { "t", "display channel names", OFFSET(draw_text), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS }, { "v", "display volume value", OFFSET(draw_volume), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS }, + { "o", "set orientation", OFFSET(orientation), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "orientation" }, + { "h", "horizontal", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "orientation" }, + { "v", "vertical", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "orientation" }, + { "s", "set step size", OFFSET(step), AV_OPT_TYPE_INT, {.i64=0}, 0, 5, FLAGS }, { NULL } }; @@ -132,15 +138,21 @@ static int config_output(AVFilterLink *outlink) ShowVolumeContext *s = outlink->src->priv; AVFilterLink *inlink = outlink->src->inputs[0]; - outlink->w = s->w; - outlink->h = s->h * inlink->channels + (inlink->channels - 1) * s->b; + if (s->orientation) { + outlink->h = s->w; + outlink->w = s->h * inlink->channels + (inlink->channels - 1) * s->b; + } else { + outlink->w = s->w; + outlink->h = s->h * inlink->channels + (inlink->channels - 1) * s->b; + } + outlink->sample_aspect_ratio = (AVRational){1,1}; outlink->frame_rate = s->frame_rate; return 0; } -static void drawtext(AVFrame *pic, int x, int y, const char *txt) +static void drawtext(AVFrame *pic, int x, int y, const char *txt, int o) { const uint8_t *font; int font_height; @@ -150,15 +162,26 @@ static void drawtext(AVFrame *pic, int x, int y, const char *txt) for (i = 0; txt[i]; i++) { int char_y, mask; - uint8_t *p = pic->data[0] + y*pic->linesize[0] + (x + i*8)*4; - for (char_y = 0; char_y < font_height; char_y++) { - for (mask = 0x80; mask; mask >>= 1) { - if (font[txt[i] * font_height + char_y] & mask) - AV_WN32(p, ~AV_RN32(p)); - p += 4; + if (o) { + for (char_y = font_height - 1; char_y >= 0; char_y--) { + uint8_t *p = pic->data[0] + (y + i * 10) * pic->linesize[0] + x * 4; + for (mask = 0x80; mask; mask >>= 1) { + if (font[txt[i] * font_height + font_height - 1 - char_y] & mask) + AV_WN32(&p[char_y * 4], ~AV_RN32(&p[char_y * 4])); + p += pic->linesize[0]; + } + } + } else { + uint8_t *p = pic->data[0] + y * pic->linesize[0] + (x + i * 8) * 4; + for (char_y = 0; char_y < font_height; char_y++) { + for (mask = 0x80; mask; mask >>= 1) { + if (font[txt[i] * font_height + char_y] & mask) + AV_WN32(p, ~AV_RN32(p)); + p += 4; + } + p += pic->linesize[0] - 8 * 4; } - p += pic->linesize[0] - 8*4; } } } @@ -168,6 +191,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) AVFilterContext *ctx = inlink->dst; AVFilterLink *outlink = ctx->outputs[0]; ShowVolumeContext *s = ctx->priv; + const int step = s->step; int c, i, j, k; AVFrame *out; @@ -187,7 +211,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) for (j = 0; j < outlink->h; j++) { uint8_t *dst = s->out->data[0] + j * s->out->linesize[0]; - for (k = 0; k < s->w; k++) { + for (k = 0; k < outlink->w; k++) { dst[k * 4 + 0] = FFMAX(dst[k * 4 + 0] * s->f, 0); dst[k * 4 + 1] = FFMAX(dst[k * 4 + 1] * s->f, 0); dst[k * 4 + 2] = FFMAX(dst[k * 4 + 2] * s->f, 0); @@ -195,29 +219,61 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) } } - for (c = 0; c < inlink->channels; c++) { - float *src = (float *)insamples->extended_data[c]; - float max = 0; - uint32_t color; + if (s->orientation) { + for (c = 0; c < inlink->channels; c++) { + float *src = (float *)insamples->extended_data[c]; + float max = 0; + uint32_t color; - for (i = 0; i < insamples->nb_samples; i++) - max = FFMAX(max, src[i]); + for (i = 0; i < insamples->nb_samples; i++) + max = FFMAX(max, src[i]); - s->values[c * VAR_VARS_NB + VAR_VOLUME] = 20.0 * log10(max); - max = av_clipf(max, 0, 1); - s->values[c * VAR_VARS_NB + VAR_CHANNEL] = c; - color = av_expr_eval(s->c_expr, &s->values[c * VAR_VARS_NB], NULL); + s->values[c * VAR_VARS_NB + VAR_VOLUME] = 20.0 * log10(max); + max = av_clipf(max, 0, 1); + s->values[c * VAR_VARS_NB + VAR_CHANNEL] = c; + color = av_expr_eval(s->c_expr, &s->values[c * VAR_VARS_NB], NULL); - for (j = 0; j < s->h; j++) { - uint8_t *dst = s->out->data[0] + (c * s->h + c * s->b + j) * s->out->linesize[0]; + for (j = outlink->h - outlink->h * max; j < outlink->h; j++) { + uint8_t *dst = s->out->data[0] + j * s->out->linesize[0] + c * (s->b + s->h) * 4; + for (k = 0; k < s->h; k++) { + AV_WN32A(&dst[k * 4], color); + if (j & step) + j += step; + } + } - for (k = 0; k < s->w * max; k++) - AV_WN32A(dst + k * 4, color); + if (outlink->h > 40 && s->draw_text) + drawtext(s->out, c * (s->h + s->b) + (s->h - 10) / 2, outlink->h - 35, + av_get_channel_name(av_channel_layout_extract_channel(insamples->channel_layout, c)), 1); } + } else { + for (c = 0; c < inlink->channels; c++) { + float *src = (float *)insamples->extended_data[c]; + float max = 0; + uint32_t color; - if (s->h >= 8 && s->draw_text) - drawtext(s->out, 2, c * (s->h + s->b) + (s->h - 8) / 2, - av_get_channel_name(av_channel_layout_extract_channel(insamples->channel_layout, c))); + for (i = 0; i < insamples->nb_samples; i++) + max = FFMAX(max, src[i]); + + s->values[c * VAR_VARS_NB + VAR_VOLUME] = 20.0 * log10(max); + max = av_clipf(max, 0, 1); + s->values[c * VAR_VARS_NB + VAR_CHANNEL] = c; + color = av_expr_eval(s->c_expr, &s->values[c * VAR_VARS_NB], NULL); + + for (j = 0; j < s->h; j++) { + uint8_t *dst = s->out->data[0] + (c * s->h + c * s->b + j) * s->out->linesize[0]; + + for (k = 0; k < s->w * max; k++) { + AV_WN32A(dst + k * 4, color); + if (k & step) + k += step; + } + } + + if (s->h >= 8 && s->draw_text) + drawtext(s->out, 2, c * (s->h + s->b) + (s->h - 8) / 2, + av_get_channel_name(av_channel_layout_extract_channel(insamples->channel_layout, c)), 0); + } } av_frame_free(&insamples); @@ -227,11 +283,17 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) av_frame_make_writable(out); for (c = 0; c < inlink->channels && s->draw_volume; c++) { - if (s->h >= 8) { - char buf[16]; - - snprintf(buf, sizeof(buf), "%.2f", s->values[c * VAR_VARS_NB + VAR_VOLUME]); - drawtext(out, FFMAX(0, s->w - 8 * (int)strlen(buf)), c * (s->h + s->b) + (s->h - 8) / 2, buf); + char buf[16]; + if (s->orientation) { + if (s->h >= 8) { + snprintf(buf, sizeof(buf), "%.2f", s->values[c * VAR_VARS_NB + VAR_VOLUME]); + drawtext(out, c * (s->h + s->b) + (s->h - 8) / 2, 2, buf, 1); + } + } else { + if (s->h >= 8) { + snprintf(buf, sizeof(buf), "%.2f", s->values[c * VAR_VARS_NB + VAR_VOLUME]); + drawtext(out, FFMAX(0, s->w - 8 * (int)strlen(buf)), c * (s->h + s->b) + (s->h - 8) / 2, buf, 0); + } } }