blob: 0f3e3594c1f315fd0751b149958e912cdcee33ee
1 | /* |
2 | * Copyright (c) 2016 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/avstring.h" |
23 | #include "libavutil/intreadwrite.h" |
24 | #include "libavutil/opt.h" |
25 | #include "libavutil/parseutils.h" |
26 | #include "avfilter.h" |
27 | #include "formats.h" |
28 | #include "audio.h" |
29 | #include "video.h" |
30 | #include "internal.h" |
31 | |
32 | typedef struct AudioBitScopeContext { |
33 | const AVClass *class; |
34 | int w, h; |
35 | AVRational frame_rate; |
36 | char *colors; |
37 | |
38 | int nb_channels; |
39 | int depth; |
40 | uint8_t *fg; |
41 | |
42 | uint64_t counter[64]; |
43 | } AudioBitScopeContext; |
44 | |
45 | #define OFFSET(x) offsetof(AudioBitScopeContext, x) |
46 | #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM |
47 | |
48 | static const AVOption abitscope_options[] = { |
49 | { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, INT_MAX, FLAGS }, |
50 | { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, INT_MAX, FLAGS }, |
51 | { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="1024x256"}, 0, 0, FLAGS }, |
52 | { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="1024x256"}, 0, 0, FLAGS }, |
53 | { "colors", "set channels colors", OFFSET(colors), AV_OPT_TYPE_STRING, {.str = "red|green|blue|yellow|orange|lime|pink|magenta|brown" }, 0, 0, FLAGS }, |
54 | { NULL } |
55 | }; |
56 | |
57 | AVFILTER_DEFINE_CLASS(abitscope); |
58 | |
59 | static int query_formats(AVFilterContext *ctx) |
60 | { |
61 | AVFilterFormats *formats = NULL; |
62 | AVFilterChannelLayouts *layouts; |
63 | AVFilterLink *inlink = ctx->inputs[0]; |
64 | AVFilterLink *outlink = ctx->outputs[0]; |
65 | static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_S32P, AV_SAMPLE_FMT_NONE }; |
66 | static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGBA, AV_PIX_FMT_NONE }; |
67 | int ret; |
68 | |
69 | formats = ff_make_format_list(sample_fmts); |
70 | if ((ret = ff_formats_ref(formats, &inlink->out_formats)) < 0) |
71 | return ret; |
72 | |
73 | layouts = ff_all_channel_counts(); |
74 | if (!layouts) |
75 | return AVERROR(ENOMEM); |
76 | if ((ret = ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts)) < 0) |
77 | return ret; |
78 | |
79 | formats = ff_all_samplerates(); |
80 | if ((ret = ff_formats_ref(formats, &inlink->out_samplerates)) < 0) |
81 | return ret; |
82 | |
83 | formats = ff_make_format_list(pix_fmts); |
84 | if ((ret = ff_formats_ref(formats, &outlink->in_formats)) < 0) |
85 | return ret; |
86 | |
87 | return 0; |
88 | } |
89 | |
90 | static int config_input(AVFilterLink *inlink) |
91 | { |
92 | AVFilterContext *ctx = inlink->dst; |
93 | AudioBitScopeContext *s = ctx->priv; |
94 | int ch, nb_samples; |
95 | char *colors, *saveptr = NULL; |
96 | |
97 | nb_samples = FFMAX(1024, ((double)inlink->sample_rate / av_q2d(s->frame_rate)) + 0.5); |
98 | inlink->partial_buf_size = |
99 | inlink->min_samples = |
100 | inlink->max_samples = nb_samples; |
101 | s->nb_channels = inlink->channels; |
102 | s->depth = inlink->format == AV_SAMPLE_FMT_S16P ? 16 : 32; |
103 | |
104 | s->fg = av_malloc_array(s->nb_channels, 4 * sizeof(*s->fg)); |
105 | if (!s->fg) |
106 | return AVERROR(ENOMEM); |
107 | |
108 | colors = av_strdup(s->colors); |
109 | if (!colors) |
110 | return AVERROR(ENOMEM); |
111 | |
112 | for (ch = 0; ch < s->nb_channels; ch++) { |
113 | uint8_t fg[4] = { 0xff, 0xff, 0xff, 0xff }; |
114 | char *color; |
115 | |
116 | color = av_strtok(ch == 0 ? colors : NULL, " |", &saveptr); |
117 | if (color) |
118 | av_parse_color(fg, color, -1, ctx); |
119 | s->fg[4 * ch + 0] = fg[0]; |
120 | s->fg[4 * ch + 1] = fg[1]; |
121 | s->fg[4 * ch + 2] = fg[2]; |
122 | s->fg[4 * ch + 3] = fg[3]; |
123 | } |
124 | av_free(colors); |
125 | |
126 | return 0; |
127 | } |
128 | |
129 | static int config_output(AVFilterLink *outlink) |
130 | { |
131 | AudioBitScopeContext *s = outlink->src->priv; |
132 | |
133 | outlink->w = s->w; |
134 | outlink->h = s->h; |
135 | outlink->sample_aspect_ratio = (AVRational){1,1}; |
136 | outlink->frame_rate = s->frame_rate; |
137 | |
138 | return 0; |
139 | } |
140 | |
141 | static void count_bits(AudioBitScopeContext *s, uint32_t sample, int max) |
142 | { |
143 | int i; |
144 | |
145 | for (i = 0; i < max; i++) { |
146 | if (sample & (1 << i)) |
147 | s->counter[i]++; |
148 | } |
149 | } |
150 | |
151 | static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) |
152 | { |
153 | AVFilterContext *ctx = inlink->dst; |
154 | AVFilterLink *outlink = ctx->outputs[0]; |
155 | AudioBitScopeContext *s = ctx->priv; |
156 | AVFrame *outpicref; |
157 | int ch, i, j, b; |
158 | |
159 | outpicref = ff_get_video_buffer(outlink, outlink->w, outlink->h); |
160 | if (!outpicref) { |
161 | av_frame_free(&insamples); |
162 | return AVERROR(ENOMEM); |
163 | } |
164 | |
165 | for (i = 0; i < outlink->h; i++) |
166 | memset(outpicref->data[0] + i * outpicref->linesize[0], 0, outlink->w * 4); |
167 | |
168 | outpicref->pts = insamples->pts; |
169 | switch (insamples->format) { |
170 | case AV_SAMPLE_FMT_S16P: |
171 | for (ch = 0; ch < inlink->channels; ch++) { |
172 | uint16_t *in = (uint16_t *)insamples->extended_data[ch]; |
173 | int w = outpicref->width / inlink->channels; |
174 | int h = outpicref->height / 16; |
175 | uint32_t color = AV_RN32(&s->fg[4 * ch]); |
176 | |
177 | memset(s->counter, 0, sizeof(s->counter)); |
178 | for (i = 0; i < insamples->nb_samples; i++) |
179 | count_bits(s, in[i], 16); |
180 | |
181 | for (b = 0; b < 16; b++) { |
182 | for (j = 1; j < h - 1; j++) { |
183 | uint8_t *dst = outpicref->data[0] + (b * h + j) * outpicref->linesize[0] + w * ch * 4; |
184 | int ww = (s->counter[16 - b - 1] / (float)insamples->nb_samples) * (w - 1); |
185 | |
186 | for (i = 0; i < ww; i++) { |
187 | AV_WN32(&dst[i * 4], color); |
188 | } |
189 | } |
190 | } |
191 | } |
192 | break; |
193 | case AV_SAMPLE_FMT_S32P: |
194 | for (ch = 0; ch < inlink->channels; ch++) { |
195 | uint32_t *in = (uint32_t *)insamples->extended_data[ch]; |
196 | int w = outpicref->width / inlink->channels; |
197 | int h = outpicref->height / 32; |
198 | uint32_t color = AV_RN32(&s->fg[4 * ch]); |
199 | |
200 | memset(s->counter, 0, sizeof(s->counter)); |
201 | for (i = 0; i < insamples->nb_samples; i++) |
202 | count_bits(s, in[i], 32); |
203 | |
204 | for (b = 0; b < 32; b++) { |
205 | for (j = 1; j < h - 1; j++) { |
206 | uint8_t *dst = outpicref->data[0] + (b * h + j) * outpicref->linesize[0] + w * ch * 4; |
207 | int ww = (s->counter[32 - b - 1] / (float)insamples->nb_samples) * (w - 1); |
208 | |
209 | for (i = 0; i < ww; i++) { |
210 | AV_WN32(&dst[i * 4], color); |
211 | } |
212 | } |
213 | } |
214 | } |
215 | break; |
216 | } |
217 | |
218 | av_frame_free(&insamples); |
219 | |
220 | return ff_filter_frame(outlink, outpicref); |
221 | } |
222 | |
223 | static const AVFilterPad inputs[] = { |
224 | { |
225 | .name = "default", |
226 | .type = AVMEDIA_TYPE_AUDIO, |
227 | .config_props = config_input, |
228 | .filter_frame = filter_frame, |
229 | }, |
230 | { NULL } |
231 | }; |
232 | |
233 | static const AVFilterPad outputs[] = { |
234 | { |
235 | .name = "default", |
236 | .type = AVMEDIA_TYPE_VIDEO, |
237 | .config_props = config_output, |
238 | }, |
239 | { NULL } |
240 | }; |
241 | |
242 | AVFilter ff_avf_abitscope = { |
243 | .name = "abitscope", |
244 | .description = NULL_IF_CONFIG_SMALL("Convert input audio to audio bit scope video output."), |
245 | .query_formats = query_formats, |
246 | .priv_size = sizeof(AudioBitScopeContext), |
247 | .inputs = inputs, |
248 | .outputs = outputs, |
249 | .priv_class = &abitscope_class, |
250 | }; |
251 |