Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  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/channel_layout.h"
  22. #include "libavutil/eval.h"
  23. #include "libavutil/intreadwrite.h"
  24. #include "libavutil/opt.h"
  25. #include "libavutil/parseutils.h"
  26. #include "libavutil/xga_font_data.h"
  27. #include "avfilter.h"
  28. #include "formats.h"
  29. #include "audio.h"
  30. #include "video.h"
  31. #include "internal.h"
  32.  
  33. typedef struct ShowVolumeContext {
  34.     const AVClass *class;
  35.     int w, h;
  36.     int f, b;
  37.     AVRational frame_rate;
  38.     char *color;
  39.  
  40.     AVFrame *out;
  41.     AVExpr *c_expr;
  42.     int draw_text;
  43. } ShowVolumeContext;
  44.  
  45. #define OFFSET(x) offsetof(ShowVolumeContext, x)
  46. #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
  47.  
  48. static const AVOption showvolume_options[] = {
  49.     { "rate", "set video rate",  OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, 0, FLAGS },
  50.     { "r",    "set video rate",  OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, 0, FLAGS },
  51.     { "b", "set border width",   OFFSET(b), AV_OPT_TYPE_INT, {.i64=1}, 0, 5, FLAGS },
  52.     { "w", "set channel width",  OFFSET(w), AV_OPT_TYPE_INT, {.i64=400}, 40, 1080, FLAGS },
  53.     { "h", "set channel height", OFFSET(h), AV_OPT_TYPE_INT, {.i64=20}, 1, 100, FLAGS },
  54.     { "f", "set fade",           OFFSET(f), AV_OPT_TYPE_INT, {.i64=20}, 1, 255, FLAGS },
  55.     { "c", "set volume color expression", OFFSET(color), AV_OPT_TYPE_STRING, {.str="if(gte(VOLUME,-2), if(gte(VOLUME,-1),0xff0000ff, 0xff00ffff),0xff00ff00)"}, 0, 0, FLAGS },
  56.     { "t", "display channel names", OFFSET(draw_text), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS },
  57.     { NULL }
  58. };
  59.  
  60. AVFILTER_DEFINE_CLASS(showvolume);
  61.  
  62. static const char *const var_names[] = {   "VOLUME",   "CHANNEL",        NULL };
  63. enum                                   { VAR_VOLUME, VAR_CHANNEL, VAR_VARS_NB };
  64.  
  65. static av_cold int init(AVFilterContext *ctx)
  66. {
  67.     ShowVolumeContext *s = ctx->priv;
  68.     int ret;
  69.  
  70.     if (s->color) {
  71.         ret = av_expr_parse(&s->c_expr, s->color, var_names,
  72.                             NULL, NULL, NULL, NULL, 0, ctx);
  73.         if (ret < 0)
  74.             return ret;
  75.     }
  76.  
  77.     return 0;
  78. }
  79.  
  80. static int query_formats(AVFilterContext *ctx)
  81. {
  82.     AVFilterFormats *formats = NULL;
  83.     AVFilterChannelLayouts *layouts = NULL;
  84.     AVFilterLink *inlink = ctx->inputs[0];
  85.     AVFilterLink *outlink = ctx->outputs[0];
  86.     static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE };
  87.     static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGBA, AV_PIX_FMT_NONE };
  88.  
  89.     formats = ff_make_format_list(sample_fmts);
  90.     if (!formats)
  91.         return AVERROR(ENOMEM);
  92.     ff_formats_ref(formats, &inlink->out_formats);
  93.  
  94.     layouts = ff_all_channel_layouts();
  95.     if (!layouts)
  96.         return AVERROR(ENOMEM);
  97.     ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts);
  98.  
  99.     formats = ff_all_samplerates();
  100.     if (!formats)
  101.         return AVERROR(ENOMEM);
  102.     ff_formats_ref(formats, &inlink->out_samplerates);
  103.  
  104.     formats = ff_make_format_list(pix_fmts);
  105.     if (!formats)
  106.         return AVERROR(ENOMEM);
  107.     ff_formats_ref(formats, &outlink->in_formats);
  108.  
  109.     return 0;
  110. }
  111.  
  112. static int config_input(AVFilterLink *inlink)
  113. {
  114.     AVFilterContext *ctx = inlink->dst;
  115.     ShowVolumeContext *s = ctx->priv;
  116.     int nb_samples;
  117.  
  118.     nb_samples = FFMAX(1024, ((double)inlink->sample_rate / av_q2d(s->frame_rate)) + 0.5);
  119.     inlink->partial_buf_size =
  120.     inlink->min_samples =
  121.     inlink->max_samples = nb_samples;
  122.  
  123.     return 0;
  124. }
  125.  
  126. static int config_output(AVFilterLink *outlink)
  127. {
  128.     ShowVolumeContext *s = outlink->src->priv;
  129.     AVFilterLink *inlink = outlink->src->inputs[0];
  130.  
  131.     outlink->w = s->w;
  132.     outlink->h = s->h * inlink->channels + (inlink->channels - 1) * s->b;
  133.     outlink->sample_aspect_ratio = (AVRational){1,1};
  134.     outlink->frame_rate = s->frame_rate;
  135.  
  136.     return 0;
  137. }
  138.  
  139. static void drawtext(AVFrame *pic, int x, int y, const char *txt)
  140. {
  141.     const uint8_t *font;
  142.     int font_height;
  143.     int i;
  144.  
  145.     font = avpriv_cga_font,   font_height =  8;
  146.  
  147.     for (i = 0; txt[i]; i++) {
  148.         int char_y, mask;
  149.         uint8_t *p = pic->data[0] + y*pic->linesize[0] + (x + i*8)*4;
  150.  
  151.         for (char_y = 0; char_y < font_height; char_y++) {
  152.             for (mask = 0x80; mask; mask >>= 1) {
  153.                 if (font[txt[i] * font_height + char_y] & mask)
  154.                     AV_WN32(p, ~AV_RN32(p));
  155.                 p += 4;
  156.             }
  157.             p += pic->linesize[0] - 8*4;
  158.         }
  159.     }
  160. }
  161.  
  162. static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
  163. {
  164.     AVFilterContext *ctx = inlink->dst;
  165.     AVFilterLink *outlink = ctx->outputs[0];
  166.     ShowVolumeContext *s = ctx->priv;
  167.     int c, i, j, k;
  168.     double values[VAR_VARS_NB];
  169.  
  170.     if (!s->out || s->out->width  != outlink->w ||
  171.                    s->out->height != outlink->h) {
  172.         av_frame_free(&s->out);
  173.         s->out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
  174.         if (!s->out) {
  175.             av_frame_free(&insamples);
  176.             return AVERROR(ENOMEM);
  177.         }
  178.  
  179.         for (i = 0; i < outlink->h; i++)
  180.             memset(s->out->data[0] + i * s->out->linesize[0], 0, outlink->w * 4);
  181.     }
  182.     s->out->pts = insamples->pts;
  183.  
  184.     for (j = 0; j < outlink->h; j++) {
  185.         uint8_t *dst = s->out->data[0] + j * s->out->linesize[0];
  186.         for (k = 0; k < s->w; k++) {
  187.             dst[k * 4 + 0] = FFMAX(dst[k * 4 + 0] - s->f, 0);
  188.             dst[k * 4 + 1] = FFMAX(dst[k * 4 + 1] - s->f, 0);
  189.             dst[k * 4 + 2] = FFMAX(dst[k * 4 + 2] - s->f, 0);
  190.             dst[k * 4 + 3] = FFMAX(dst[k * 4 + 3] - s->f, 0);
  191.         }
  192.     }
  193.  
  194.     for (c = 0; c < inlink->channels; c++) {
  195.         float *src = (float *)insamples->extended_data[c];
  196.         float max = 0;
  197.         uint32_t color;
  198.  
  199.         for (i = 0; i < insamples->nb_samples; i++)
  200.             max = FFMAX(max, src[i]);
  201.  
  202.         max = av_clipf(max, 0, 1);
  203.         values[VAR_VOLUME] = 20.0 * log(max) / M_LN10;
  204.         values[VAR_CHANNEL] = c;
  205.         color = av_expr_eval(s->c_expr, values, NULL);
  206.  
  207.         for (j = 0; j < s->h; j++) {
  208.             uint8_t *dst = s->out->data[0] + (c * s->h + c * s->b + j) * s->out->linesize[0];
  209.  
  210.             for (k = 0; k < s->w * max; k++)
  211.                 AV_WN32A(dst + k * 4, color);
  212.         }
  213.  
  214.         if (s->h >= 8 && s->draw_text)
  215.             drawtext(s->out, 2, c * (s->h + s->b) + (s->h - 8) / 2,
  216.                      av_get_channel_name(av_channel_layout_extract_channel(insamples->channel_layout, c)));
  217.     }
  218.  
  219.     av_frame_free(&insamples);
  220.  
  221.     return ff_filter_frame(outlink, av_frame_clone(s->out));
  222. }
  223.  
  224. static av_cold void uninit(AVFilterContext *ctx)
  225. {
  226.     ShowVolumeContext *s = ctx->priv;
  227.  
  228.     av_frame_free(&s->out);
  229.     av_expr_free(s->c_expr);
  230. }
  231.  
  232. static const AVFilterPad showvolume_inputs[] = {
  233.     {
  234.         .name         = "default",
  235.         .type         = AVMEDIA_TYPE_AUDIO,
  236.         .config_props = config_input,
  237.         .filter_frame = filter_frame,
  238.     },
  239.     { NULL }
  240. };
  241.  
  242. static const AVFilterPad showvolume_outputs[] = {
  243.     {
  244.         .name         = "default",
  245.         .type         = AVMEDIA_TYPE_VIDEO,
  246.         .config_props = config_output,
  247.     },
  248.     { NULL }
  249. };
  250.  
  251. AVFilter ff_avf_showvolume = {
  252.     .name          = "showvolume",
  253.     .description   = NULL_IF_CONFIG_SMALL("Convert input audio volume to video output."),
  254.     .init          = init,
  255.     .uninit        = uninit,
  256.     .query_formats = query_formats,
  257.     .priv_size     = sizeof(ShowVolumeContext),
  258.     .inputs        = showvolume_inputs,
  259.     .outputs       = showvolume_outputs,
  260.     .priv_class    = &showvolume_class,
  261. };
  262.