1
|
|
2
|
|
3
|
|
4
|
|
5
|
|
6
|
|
7
|
|
8
|
|
9
|
|
10
|
|
11
|
|
12
|
|
13
|
|
14
|
|
15
|
|
16
|
|
17
|
|
18
|
|
19
|
|
20
|
|
21
|
|
22
|
|
23
|
|
24
|
|
25
|
|
26
|
|
27
|
|
28
|
|
29
|
|
30
|
|
31
|
|
32
|
|
33
|
|
34
|
|
35
|
|
36
|
|
37
|
|
38
|
|
39
|
|
40
|
|
41
|
|
42
|
|
43
|
|
44
|
|
45
|
|
46
|
|
47
|
|
48
|
|
49
|
|
50
|
|
51
|
|
52
|
|
53
|
|
54
|
|
55
|
#include "libavutil/avstring.h"
|
56
|
#include "libavutil/colorspace.h"
|
57
|
#include "libavutil/lfg.h"
|
58
|
#include "libavutil/pixdesc.h"
|
59
|
#include "libavutil/parseutils.h"
|
60
|
#include "avfilter.h"
|
61
|
#include <strings.h>
|
62
|
|
63
|
enum { Y, U, V, A };
|
64
|
static int logging = 0;
|
65
|
typedef enum {redact_solid,
|
66
|
redact_pixellate,
|
67
|
redact_inverse_pixellate,
|
68
|
redact_blur}
|
69
|
redaction_method;
|
70
|
typedef struct {
|
71
|
int l, r, t, b;
|
72
|
double start, end;
|
73
|
redaction_method method;
|
74
|
unsigned char yuv_color[4];
|
75
|
} BoxTrack;
|
76
|
|
77
|
typedef struct {
|
78
|
int vsub, hsub;
|
79
|
int numtracks;
|
80
|
double time_seconds;
|
81
|
BoxTrack **boxtracks;
|
82
|
AVLFG random;
|
83
|
AVFilterBufferRef *lastredacted;
|
84
|
} RedactionContext;
|
85
|
|
86
|
static void log_box_track(BoxTrack *bt,
|
87
|
AVFilterContext *ctx) {
|
88
|
av_log(ctx, AV_LOG_INFO, "Box track: %d: (%.1f-%.1fs) %d-%d x %d-%d\n",
|
89
|
bt->method, bt->start, bt->end,
|
90
|
bt->l, bt->r, bt->t, bt->b);
|
91
|
}
|
92
|
|
93
|
|
94
|
|
95
|
typedef struct {
|
96
|
unsigned long size,resident,share,text,lib,data,dt;
|
97
|
} statm_t;
|
98
|
|
99
|
static void read_off_memory_status(statm_t * result)
|
100
|
{
|
101
|
const char* statm_path = "/proc/self/statm";
|
102
|
|
103
|
FILE *f = fopen(statm_path,"r");
|
104
|
if(!f){
|
105
|
abort();
|
106
|
}
|
107
|
if(7 != fscanf(f,"%ld %ld %ld %ld %ld %ld %ld",
|
108
|
&result->size,
|
109
|
&result->resident,
|
110
|
&result->share,
|
111
|
&result->text,
|
112
|
&result->lib,
|
113
|
&result->data,
|
114
|
&result->dt))
|
115
|
{
|
116
|
abort();
|
117
|
}
|
118
|
fclose(f);
|
119
|
}
|
120
|
|
121
|
static BoxTrack *box_track_from_string(const char *track_def,
|
122
|
AVFilterContext *ctx) {
|
123
|
BoxTrack *boxtrack = NULL;
|
124
|
int rv = 0;
|
125
|
int l, r, t, b;
|
126
|
#define BUFLEN 1000
|
127
|
char method[BUFLEN];
|
128
|
double start, end;
|
129
|
|
130
|
|
131
|
if (track_def[0] == '#' || track_def[0] == '\0')
|
132
|
return NULL;
|
133
|
|
134
|
rv = sscanf(track_def, "%lf,%lf,%d,%d,%d,%d,%s", &start,&end,
|
135
|
&l, &r, &t, &b, method);
|
136
|
if (rv != 7) {
|
137
|
av_log(ctx, AV_LOG_ERROR, "Failed to parse boxtrack '%s' .\n", track_def);
|
138
|
return NULL;
|
139
|
}
|
140
|
boxtrack = (BoxTrack *)av_malloc(sizeof(BoxTrack));
|
141
|
boxtrack->l = l;
|
142
|
boxtrack->r = r;
|
143
|
boxtrack->t = t;
|
144
|
boxtrack->b = b;
|
145
|
boxtrack->start = start;
|
146
|
boxtrack->end = end;
|
147
|
boxtrack->method = redact_pixellate;
|
148
|
|
149
|
|
150
|
if (av_strncasecmp(method, "pixel", 5) == 0)
|
151
|
boxtrack->method = redact_pixellate;
|
152
|
else if (av_strncasecmp(method, "inv", 3) == 0)
|
153
|
boxtrack->method = redact_inverse_pixellate;
|
154
|
else if (av_strncasecmp(method, "blur", 4) == 0)
|
155
|
boxtrack->method = redact_blur;
|
156
|
else {
|
157
|
uint8_t rgba_color[4];
|
158
|
|
159
|
boxtrack->method = redact_solid;
|
160
|
|
161
|
if (av_parse_color(rgba_color, method, -1, ctx) < 0)
|
162
|
av_log(ctx, AV_LOG_ERROR, "Couldn't parse colour '%s' .\n", method);
|
163
|
|
164
|
boxtrack->yuv_color[Y] =
|
165
|
RGB_TO_Y_CCIR(rgba_color[0], rgba_color[1], rgba_color[2]);
|
166
|
boxtrack->yuv_color[U] =
|
167
|
RGB_TO_U_CCIR(rgba_color[0], rgba_color[1], rgba_color[2], 0);
|
168
|
boxtrack->yuv_color[V] =
|
169
|
RGB_TO_V_CCIR(rgba_color[0], rgba_color[1], rgba_color[2], 0);
|
170
|
boxtrack->yuv_color[A] = rgba_color[3];
|
171
|
}
|
172
|
return boxtrack;
|
173
|
}
|
174
|
|
175
|
static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
|
176
|
{
|
177
|
RedactionContext *redaction= ctx->priv;
|
178
|
FILE *file = NULL;
|
179
|
char buf[BUFLEN];
|
180
|
unsigned int seed=298379;
|
181
|
redaction->boxtracks = NULL;
|
182
|
redaction->lastredacted = NULL;
|
183
|
redaction->time_seconds = NAN;
|
184
|
if (!args) {
|
185
|
av_log(ctx, AV_LOG_ERROR, "No arguments given to redact.\n");
|
186
|
return 1;
|
187
|
}
|
188
|
file = fopen(args, "r");
|
189
|
if (!file) {
|
190
|
av_log(ctx, AV_LOG_ERROR, "Can't open redaction file: '%s'\n", args);
|
191
|
return 2;
|
192
|
}
|
193
|
redaction->numtracks = 0;
|
194
|
|
195
|
while (!feof(file)) {
|
196
|
BoxTrack **boxtracks = NULL;
|
197
|
BoxTrack *new_track = NULL;
|
198
|
|
199
|
if (fgets(buf, BUFLEN, file) == NULL) break;
|
200
|
if (strncmp(buf, "seed", 4) == 0) {
|
201
|
int rv = sscanf(buf, "seed %ud", &seed);
|
202
|
if (rv != 1)
|
203
|
av_log(ctx, AV_LOG_ERROR, "Didn't parse seed: %s.\n", buf);
|
204
|
|
205
|
continue;
|
206
|
}
|
207
|
new_track = box_track_from_string(buf, ctx);
|
208
|
if (new_track == NULL)
|
209
|
continue;
|
210
|
log_box_track(new_track, ctx);
|
211
|
|
212
|
boxtracks = (BoxTrack **)av_malloc((redaction->numtracks + 1) *
|
213
|
sizeof(BoxTrack *));
|
214
|
for (int i = 0; i < redaction->numtracks; ++i)
|
215
|
boxtracks[i] = redaction->boxtracks[i];
|
216
|
boxtracks[redaction->numtracks++] = new_track;
|
217
|
av_free(redaction->boxtracks);
|
218
|
redaction->boxtracks = boxtracks;
|
219
|
}
|
220
|
av_log(ctx, AV_LOG_INFO, "Seed is : '%ud'\n", seed);
|
221
|
av_lfg_init(&redaction->random, seed);
|
222
|
fclose(file);
|
223
|
|
224
|
for (int j = 0; j < redaction->numtracks -1; ++j)
|
225
|
for (int k = j + 1; k < redaction->numtracks; ++k)
|
226
|
if (redaction->boxtracks[j]->start < redaction->boxtracks[k]->start) {
|
227
|
BoxTrack *temp = redaction->boxtracks[j];
|
228
|
redaction->boxtracks[j] = redaction->boxtracks[k];
|
229
|
redaction->boxtracks[k] = temp;
|
230
|
}
|
231
|
|
232
|
return 0;
|
233
|
}
|
234
|
|
235
|
static int query_formats(AVFilterContext *ctx)
|
236
|
{
|
237
|
enum PixelFormat pix_fmts[] = {
|
238
|
PIX_FMT_YUV444P, PIX_FMT_YUV422P, PIX_FMT_YUV420P,
|
239
|
PIX_FMT_YUV411P, PIX_FMT_YUV410P,
|
240
|
PIX_FMT_YUVJ444P, PIX_FMT_YUVJ422P, PIX_FMT_YUVJ420P,
|
241
|
PIX_FMT_YUV440P, PIX_FMT_YUVJ440P,
|
242
|
PIX_FMT_NONE
|
243
|
};
|
244
|
|
245
|
avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
|
246
|
return 0;
|
247
|
}
|
248
|
|
249
|
static int config_input(AVFilterLink *inlink)
|
250
|
{
|
251
|
RedactionContext *redaction = inlink->dst->priv;
|
252
|
|
253
|
redaction->hsub = av_pix_fmt_descriptors[inlink->format].log2_chroma_w;
|
254
|
redaction->vsub = av_pix_fmt_descriptors[inlink->format].log2_chroma_h;
|
255
|
|
256
|
av_log(inlink->dst, AV_LOG_INFO, "Redaction with %d tracks %d %d\n",
|
257
|
redaction->numtracks, redaction->hsub, redaction->vsub);
|
258
|
|
259
|
return 0;
|
260
|
}
|
261
|
|
262
|
|
263
|
static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *picref)
|
264
|
{
|
265
|
AVFilterContext *ctx = inlink->dst;
|
266
|
RedactionContext *redaction = inlink->dst->priv;
|
267
|
AVFilterLink *outlink0 = inlink->dst->outputs[0];
|
268
|
AVFilterLink *outlink1 = inlink->dst->outputs[1];
|
269
|
AVFilterBufferRef *outpicref = NULL;
|
270
|
|
271
|
if (logging)
|
272
|
av_log(ctx, AV_LOG_INFO, "startstartframe\n");
|
273
|
redaction->time_seconds = picref->pts * av_q2d(inlink->time_base);
|
274
|
outpicref = avfilter_get_video_buffer(outlink0, AV_PERM_WRITE,
|
275
|
outlink0->w, outlink0->h);
|
276
|
avfilter_copy_buffer_ref_props(outpicref, picref);
|
277
|
outpicref->video->w = outlink0->w;
|
278
|
outpicref->video->h = outlink0->h;
|
279
|
outlink0->out_buf = outpicref;
|
280
|
outlink0->out_buf->pts = picref->pts;
|
281
|
outlink0->out_buf->pos = picref->pos;
|
282
|
|
283
|
avfilter_start_frame(outlink0,
|
284
|
avfilter_ref_buffer(outlink0->out_buf, ~0));
|
285
|
|
286
|
outpicref = avfilter_get_video_buffer(outlink1, AV_PERM_WRITE,
|
287
|
outlink1->w, outlink1->h);
|
288
|
avfilter_copy_buffer_ref_props(outpicref, picref);
|
289
|
outpicref->video->w = outlink0->w;
|
290
|
outpicref->video->h = outlink0->h;
|
291
|
outlink1->out_buf = outpicref;
|
292
|
outlink1->out_buf->pts = picref->pts;
|
293
|
outlink1->out_buf->pos = picref->pos;
|
294
|
avfilter_start_frame(outlink1,outlink1->out_buf);
|
295
|
|
296
|
if (logging)
|
297
|
av_log(ctx, AV_LOG_INFO, "endstartframe\n");
|
298
|
}
|
299
|
|
300
|
static int noise = 10;
|
301
|
static void convolve_ny(int t, int b, int maxy,
|
302
|
unsigned char *row, int blur, unsigned char *blurbuf,
|
303
|
int step,
|
304
|
AVLFG *random) {
|
305
|
int halfblur = blur/2;
|
306
|
int blursum = 0;
|
307
|
int i;
|
308
|
for (i = 0; i < blur; ++i) {
|
309
|
int pos = t + i - halfblur;
|
310
|
blurbuf[i] = (pos <= 0) ? row[0] : row[pos * step];
|
311
|
blursum += blurbuf[i];
|
312
|
}
|
313
|
for (int y = t; y < b; ++y, ++i) {
|
314
|
int newval = blursum / blur;
|
315
|
int newpos = y + (blur + 1)/2;
|
316
|
i %= blur;
|
317
|
if (noise > 0) {
|
318
|
newval += (av_lfg_get(random) % (2 * noise + 1)) - noise;
|
319
|
if (newval < 0 ) newval = 0;
|
320
|
else if (newval > 255) newval = 255;
|
321
|
}
|
322
|
row[y*step] = newval;
|
323
|
blursum -= blurbuf[i];
|
324
|
blurbuf[i] = row[step * ((newpos < maxy) ? newpos : (maxy - 1))];
|
325
|
blursum += blurbuf[i];
|
326
|
}
|
327
|
}
|
328
|
|
329
|
|
330
|
|
331
|
static void convolve_nx(int l, int r, int maxx,
|
332
|
unsigned char *row, int blur, unsigned char *blurbuf,
|
333
|
AVLFG *random) {
|
334
|
int halfblur = blur/2;
|
335
|
int blursum = 0;
|
336
|
int i;
|
337
|
for (i = 0; i < blur; ++i) {
|
338
|
int pos = l + i - halfblur;
|
339
|
blurbuf[i] = (pos <= 0) ? row[0] : row[pos];
|
340
|
blursum += blurbuf[i];
|
341
|
}
|
342
|
for (int x = l; x < r; ++x, ++i) {
|
343
|
int newval = blursum / blur;
|
344
|
int newpos = x + (blur + 1)/2;
|
345
|
i %= blur;
|
346
|
if (noise > 0) {
|
347
|
newval += (av_lfg_get(random) % (2 * noise + 1)) - noise;
|
348
|
if (newval < 0 ) newval = 0;
|
349
|
else if (newval > 255) newval = 255;
|
350
|
}
|
351
|
row[x] = newval;
|
352
|
blursum -= blurbuf[i];
|
353
|
blurbuf[i] = row[(newpos < maxx) ? newpos : (maxx - 1)];
|
354
|
blursum += blurbuf[i];
|
355
|
}
|
356
|
}
|
357
|
|
358
|
static void blur_one_round(AVFilterBufferRef *picref, BoxTrack *boxtrack,
|
359
|
int y0, int h, int hsub, int vsub,
|
360
|
int blur, unsigned char *blurbuf,
|
361
|
AVLFG *random) {
|
362
|
int xb = boxtrack->l, yb = boxtrack->t;
|
363
|
int hb = boxtrack->b - boxtrack->t;
|
364
|
int wb = boxtrack->r - boxtrack->l;
|
365
|
int x, y;
|
366
|
int xmax, ymax;
|
367
|
|
368
|
#define BLURX
|
369
|
#define BLURY
|
370
|
#ifdef BLURX
|
371
|
x = FFMAX(xb, 0);
|
372
|
blur = wb/2;
|
373
|
xmax = FFMIN( (xb + wb), picref->video->w);
|
374
|
for (y = FFMAX(yb, y0); y < (y0 + h) && y < (yb + hb); ++y) {
|
375
|
for (int plane = 0; plane < 3; plane++) {
|
376
|
int ds = (plane == 0) ? 0 : hsub;
|
377
|
unsigned char *row = picref->data[plane] +
|
378
|
picref->linesize[plane] * (y >> ((plane == 0) ? 0 : vsub));
|
379
|
convolve_nx(x >> ds, (xmax + ((1<<ds) -1))>> ds, picref->video->w >> ds,
|
380
|
row,
|
381
|
(blur + ((1<<ds) -1))>> ds, blurbuf, random);
|
382
|
}
|
383
|
}
|
384
|
#endif
|
385
|
#ifdef BLURY
|
386
|
y = FFMAX(yb, y0);
|
387
|
ymax = FFMIN( (yb + hb), (y0 + h));
|
388
|
blur = hb/2;
|
389
|
for (x = FFMAX(xb, 0); x < (xb + wb) && x < picref->video->w; x++) {
|
390
|
for (int plane = 0; plane < 3; plane++) {
|
391
|
int ds = (plane == 0) ? 0 : vsub;
|
392
|
unsigned char *col = picref->data[plane] + (x >> ((plane == 0) ? 0 : hsub));
|
393
|
convolve_ny(y >> ds, (ymax + ((1<<ds) -1)) >> ds,
|
394
|
(y0 + h) >> ds,
|
395
|
col, (blur + ((1<<ds) -1))>> ds, blurbuf,
|
396
|
picref->linesize[plane], random);
|
397
|
}
|
398
|
}
|
399
|
#endif
|
400
|
}
|
401
|
|
402
|
static void copybox_mixold_alpha(AVFilterBufferRef *source,
|
403
|
AVFilterBufferRef *picref,
|
404
|
AVFilterBufferRef *lastref,
|
405
|
BoxTrack *boxtrack,
|
406
|
int hsub, int vsub,
|
407
|
AVLFG *random) {
|
408
|
int xb = boxtrack->l;
|
409
|
int yb = boxtrack->t;
|
410
|
int hb = boxtrack->b - boxtrack->t;
|
411
|
int wb = boxtrack->r - boxtrack->l;
|
412
|
|
413
|
float blur_boundary = 0.2;
|
414
|
for (int y = FFMAX(yb, 0); y < picref->video->h && y < boxtrack->b; y++) {
|
415
|
float ynormsq = (y * 2.0 - (boxtrack->b + boxtrack->t)) / hb;
|
416
|
ynormsq *= ynormsq;
|
417
|
for (int plane = 0; plane < 3; plane++) {
|
418
|
int ysub = (y >> ((plane == 0) ? 0 : vsub));
|
419
|
unsigned char *row = picref->data[plane] +
|
420
|
picref->linesize[plane] * ysub;
|
421
|
unsigned char *srcrow = source->data[plane] +
|
422
|
picref->linesize[plane] * ysub;
|
423
|
unsigned char *lastrow = lastref->data[plane] +
|
424
|
picref->linesize[plane] * ysub;
|
425
|
|
426
|
int thishsub = (plane == 0) ? 0 : hsub;
|
427
|
int xmin = FFMAX(xb, 0) >> thishsub;
|
428
|
int xmax = FFMIN(boxtrack->r, picref->video->w)
|
429
|
>> thishsub;
|
430
|
for (int x = xmin; x < xmax; x++) {
|
431
|
|
432
|
|
433
|
float xnorm = ((x << thishsub) * 2.0 - (boxtrack->l + boxtrack->r))
|
434
|
/ wb;
|
435
|
float mixlast = ((av_lfg_get(random) % 20) + 10) / 40.0;
|
436
|
float alphax = (1 - sqrt(xnorm * xnorm + ynormsq));
|
437
|
if (alphax < 0) {
|
438
|
row[x] = srcrow[x];
|
439
|
continue;
|
440
|
}
|
441
|
if (alphax > blur_boundary)
|
442
|
alphax = 1;
|
443
|
else
|
444
|
alphax /= blur_boundary;
|
445
|
row[x] = (1 - alphax) * srcrow[x] +
|
446
|
alphax * ((1 - mixlast) * row[x] + mixlast * lastrow[x]);
|
447
|
}
|
448
|
}
|
449
|
}
|
450
|
}
|
451
|
|
452
|
|
453
|
static void obscure_one_box(AVFilterBufferRef *source,
|
454
|
AVFilterBufferRef *picref,
|
455
|
AVFilterBufferRef *lastref,
|
456
|
BoxTrack *boxtrack,
|
457
|
int y0, int h, int hsub, int vsub,
|
458
|
AVLFG *random) {
|
459
|
unsigned char *row[4];
|
460
|
int xb = boxtrack->l, yb = boxtrack->t;
|
461
|
int hb = boxtrack->b - boxtrack->t;
|
462
|
int wb = boxtrack->r - boxtrack->l;
|
463
|
int megapixel_size = 64;
|
464
|
int x, y;
|
465
|
if (boxtrack->method == redact_blur) {
|
466
|
const int blur = FFMAX(hb, wb);
|
467
|
unsigned char *blurbuf = (unsigned char *)av_malloc(blur);
|
468
|
blur_one_round(picref, boxtrack, y0, h, hsub, vsub, blur, blurbuf, random);
|
469
|
copybox_mixold_alpha(source, picref,
|
470
|
((lastref==NULL)?source:lastref),
|
471
|
boxtrack, hsub, vsub, random);
|
472
|
av_free(blurbuf);
|
473
|
return;
|
474
|
}
|
475
|
|
476
|
for (y = FFMAX(yb, y0); y < (y0 + h) && y < (yb + hb); y++) {
|
477
|
row[0] = picref->data[0] + y * picref->linesize[0];
|
478
|
|
479
|
for (int plane = 1; plane < 3; plane++)
|
480
|
row[plane] = picref->data[plane] +
|
481
|
picref->linesize[plane] * (y >> vsub);
|
482
|
|
483
|
for (x = FFMAX(xb, 0); x < (xb + wb) && x < picref->video->w; x++) {
|
484
|
double alpha = (double)boxtrack->yuv_color[A] / 255;
|
485
|
if (boxtrack->method == redact_solid) {
|
486
|
row[0][x] = (1 - alpha) * row[0][x] +
|
487
|
alpha * boxtrack->yuv_color[Y];
|
488
|
|
489
|
|
490
|
row[1][x >> hsub] = (1 - alpha) * row[1][x >> hsub] +
|
491
|
alpha * boxtrack->yuv_color[U];
|
492
|
row[2][x >> hsub] = (1 - alpha) * row[2][x >> hsub] +
|
493
|
alpha * boxtrack->yuv_color[V];
|
494
|
} else if (boxtrack->method == redact_pixellate) {
|
495
|
int x_quant = (x / megapixel_size) * megapixel_size;
|
496
|
int y_quant = (y / megapixel_size) * megapixel_size;
|
497
|
row[0][x] = (picref->data[0] + y_quant *
|
498
|
picref->linesize[0])[x_quant];
|
499
|
row[1][x >> hsub] = (picref->data[1] + picref->linesize[1] *
|
500
|
(y_quant >> vsub))[x_quant >> hsub];
|
501
|
row[2][x >> hsub] = (picref->data[2] + picref->linesize[2] *
|
502
|
(y_quant >> vsub))[x_quant >> hsub];
|
503
|
}
|
504
|
}
|
505
|
}
|
506
|
}
|
507
|
|
508
|
|
509
|
static void erase_output2(AVFilterBufferRef *outpic,
|
510
|
int y0, int h, int hsub, int vsub,
|
511
|
unsigned char val) {
|
512
|
unsigned char v[] = { 16, 128, 128};
|
513
|
for (int y = y0; y < (y0 + h); y++) {
|
514
|
for (int plane = 0; plane < 3; plane++) {
|
515
|
uint8_t *outrow = outpic->data[plane] +
|
516
|
outpic->linesize[plane] * (y >> ((plane == 0)?0:vsub));
|
517
|
const int xmax = outpic->video->w >> ((plane == 0)?0:hsub);
|
518
|
memset(outrow, v[plane], xmax);
|
519
|
}
|
520
|
}
|
521
|
}
|
522
|
static void copy_all(AVFilterBufferRef *picref,
|
523
|
AVFilterBufferRef *outpic,
|
524
|
int hsub, int vsub) {
|
525
|
for (int y = 0; y < picref->video->h; y++) {
|
526
|
for (int plane = 0; plane < 3; plane++) {
|
527
|
uint8_t *row = picref->data[plane] +
|
528
|
picref->linesize[plane] * (y >> ((plane == 0)?0:vsub));
|
529
|
uint8_t *outrow = outpic->data[plane] +
|
530
|
outpic->linesize[plane] * (y >> ((plane == 0)?0:vsub));
|
531
|
int xwid = picref->video->w >> ((plane == 0)?0:hsub);
|
532
|
memcpy(outrow, row, xwid);
|
533
|
}
|
534
|
}
|
535
|
}
|
536
|
|
537
|
|
538
|
static void copy_one_box(AVFilterBufferRef *picref,
|
539
|
AVFilterBufferRef *outpic,
|
540
|
BoxTrack *boxtrack,
|
541
|
int y0, int h, int hsub, int vsub) {
|
542
|
int xb = boxtrack->l;
|
543
|
int yb = boxtrack->t;
|
544
|
int hb = boxtrack->b - boxtrack->t;
|
545
|
int wb = boxtrack->r - boxtrack->l;
|
546
|
|
547
|
for (int y = FFMAX(yb, y0); y < (y0 + h) && y < (yb + hb); y++) {
|
548
|
for (int plane = 0; plane < 3; plane++) {
|
549
|
uint8_t *row = picref->data[plane] +
|
550
|
picref->linesize[plane] * (y >> ((plane == 0)?0:vsub));
|
551
|
uint8_t *outrow = outpic->data[plane] +
|
552
|
outpic->linesize[plane] * (y >> ((plane == 0)?0:vsub));
|
553
|
int xmin = FFMAX(xb, 0) >> ((plane == 0)?0:hsub);
|
554
|
int xmax = FFMIN((xb + wb), picref->video->w)
|
555
|
>> ((plane == 0)?0:hsub);
|
556
|
memcpy(outrow + xmin, row + xmin, xmax - xmin);
|
557
|
}
|
558
|
}
|
559
|
}
|
560
|
|
561
|
static void end_frame(AVFilterLink *inlink)
|
562
|
{
|
563
|
AVFilterContext *ctx = inlink->dst;
|
564
|
RedactionContext *redaction = inlink->dst->priv;
|
565
|
AVFilterBufferRef *picref = inlink->cur_buf;
|
566
|
AVFilterLink *outlink0 = inlink->dst->outputs[0];
|
567
|
AVFilterBufferRef *outpic0 = outlink0->out_buf;
|
568
|
AVFilterLink *outlink1 = inlink->dst->outputs[1];
|
569
|
AVFilterBufferRef *outpic1 = outlink1->out_buf;
|
570
|
int box = 0;
|
571
|
statm_t status;
|
572
|
|
573
|
if (logging)
|
574
|
av_log(ctx, AV_LOG_INFO, "vf_redact start\n");
|
575
|
|
576
|
copy_all(picref, outpic0, redaction->hsub, redaction->vsub);
|
577
|
|
578
|
|
579
|
|
580
|
erase_output2(outpic1, 0, inlink->h, redaction->hsub, redaction->vsub, 40);
|
581
|
|
582
|
for (box = redaction->numtracks -1; box >= 0; --box) {
|
583
|
BoxTrack *boxtrack = redaction->boxtracks[box];
|
584
|
|
585
|
|
586
|
|
587
|
if (boxtrack->start > redaction->time_seconds)
|
588
|
break;
|
589
|
if (boxtrack->end < redaction->time_seconds) {
|
590
|
|
591
|
av_free(redaction->boxtracks[box]);
|
592
|
|
593
|
|
594
|
for (int t = box + 1; t < redaction->numtracks; ++t)
|
595
|
redaction->boxtracks[t - 1] = redaction->boxtracks[t];
|
596
|
|
597
|
redaction->boxtracks[--redaction->numtracks] = NULL;
|
598
|
} else {
|
599
|
copy_one_box(picref, outpic1, boxtrack, 0, inlink->h,
|
600
|
redaction->hsub, redaction->vsub);
|
601
|
}
|
602
|
}
|
603
|
|
604
|
for (box = redaction->numtracks -1; box >= 0; --box) {
|
605
|
BoxTrack *boxtrack = redaction->boxtracks[box];
|
606
|
|
607
|
if (boxtrack->start > redaction->time_seconds)
|
608
|
break;
|
609
|
obscure_one_box(picref, outpic0, redaction->lastredacted,
|
610
|
boxtrack, 0, inlink->h,
|
611
|
redaction->hsub, redaction->vsub,
|
612
|
&redaction->random);
|
613
|
}
|
614
|
avfilter_draw_slice(inlink->dst->outputs[0], 0, inlink->h, 1);
|
615
|
avfilter_draw_slice(inlink->dst->outputs[1], 0, inlink->h, 1);
|
616
|
|
617
|
if (redaction->lastredacted != NULL)
|
618
|
avfilter_unref_buffer(redaction->lastredacted);
|
619
|
redaction->lastredacted = avfilter_ref_buffer(outlink0->out_buf, ~0);
|
620
|
avfilter_end_frame(inlink->dst->outputs[0]);
|
621
|
avfilter_end_frame(inlink->dst->outputs[1]);
|
622
|
avfilter_unref_buffer(picref);
|
623
|
avfilter_unref_buffer(outlink0->out_buf);
|
624
|
avfilter_unref_buffer(outlink1->out_buf);
|
625
|
if (logging) {
|
626
|
av_log(ctx, AV_LOG_INFO, "doneendframe1\n");
|
627
|
read_off_memory_status(&status);
|
628
|
|
629
|
av_log(ctx, AV_LOG_INFO, "Redaction memory RSS %lu data %lu\n",
|
630
|
status.resident, status.data);
|
631
|
}
|
632
|
}
|
633
|
|
634
|
static av_cold void uninit(AVFilterContext *ctx)
|
635
|
{
|
636
|
RedactionContext *redaction= ctx->priv;
|
637
|
if (redaction->lastredacted != NULL)
|
638
|
avfilter_unref_buffer(redaction->lastredacted);
|
639
|
for (int i = 0; i < redaction->numtracks; ++i) {
|
640
|
av_free(redaction->boxtracks[i]);
|
641
|
}
|
642
|
av_free(redaction->boxtracks);
|
643
|
}
|
644
|
|
645
|
AVFilter avfilter_vf_redact = {
|
646
|
.name = "redact",
|
647
|
.description =
|
648
|
NULL_IF_CONFIG_SMALL("Redact the input video according to a track file."),
|
649
|
.priv_size = sizeof(RedactionContext),
|
650
|
.init = init,
|
651
|
.uninit = uninit,
|
652
|
|
653
|
.query_formats = query_formats,
|
654
|
.inputs = (const AVFilterPad[]) {
|
655
|
{ .name = "default",
|
656
|
.type = AVMEDIA_TYPE_VIDEO,
|
657
|
.config_props = config_input,
|
658
|
.get_video_buffer =
|
659
|
avfilter_null_get_video_buffer,
|
660
|
.start_frame = start_frame,
|
661
|
.draw_slice = avfilter_null_draw_slice,
|
662
|
.end_frame = end_frame,
|
663
|
.min_perms = AV_PERM_WRITE | AV_PERM_READ,
|
664
|
|
665
|
},
|
666
|
{ .name = NULL}},
|
667
|
.outputs = (const AVFilterPad[]) {
|
668
|
{ .name = "output1",
|
669
|
.type = AVMEDIA_TYPE_VIDEO, },
|
670
|
{ .name = "output2",
|
671
|
.type = AVMEDIA_TYPE_VIDEO, },
|
672
|
{ .name = NULL}},
|
673
|
};
|