Line |
Branch |
Exec |
Source |
1 |
|
|
/* |
2 |
|
|
* Copyright (c) 2017 Gerion Entrup |
3 |
|
|
* |
4 |
|
|
* This file is part of FFmpeg. |
5 |
|
|
* |
6 |
|
|
* FFmpeg is free software; you can redistribute it and/or modify |
7 |
|
|
* it under the terms of the GNU General Public License as published by |
8 |
|
|
* the Free Software Foundation; either version 2 of the License, or |
9 |
|
|
* (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 |
14 |
|
|
* GNU General Public License for more details. |
15 |
|
|
* |
16 |
|
|
* You should have received a copy of the GNU General Public License along |
17 |
|
|
* with FFmpeg; if not, write to the Free Software Foundation, Inc., |
18 |
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
19 |
|
|
*/ |
20 |
|
|
|
21 |
|
|
/** |
22 |
|
|
* @file |
23 |
|
|
* MPEG-7 video signature calculation and lookup filter |
24 |
|
|
* @see http://epubs.surrey.ac.uk/531590/1/MPEG-7%20Video%20Signature%20Author%27s%20Copy.pdf |
25 |
|
|
*/ |
26 |
|
|
|
27 |
|
|
#include "libavcodec/put_bits.h" |
28 |
|
|
#include "libavformat/avformat.h" |
29 |
|
|
#include "libavutil/mem.h" |
30 |
|
|
#include "libavutil/opt.h" |
31 |
|
|
#include "libavutil/avstring.h" |
32 |
|
|
#include "libavutil/file_open.h" |
33 |
|
|
#include "avfilter.h" |
34 |
|
|
#include "filters.h" |
35 |
|
|
#include "signature.h" |
36 |
|
|
#include "signature_lookup.c" |
37 |
|
|
|
38 |
|
|
#define OFFSET(x) offsetof(SignatureContext, x) |
39 |
|
|
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM |
40 |
|
|
#define BLOCK_LCM (int64_t) 476985600 |
41 |
|
|
|
42 |
|
|
static const AVOption signature_options[] = { |
43 |
|
|
{ "detectmode", "set the detectmode", |
44 |
|
|
OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = MODE_OFF}, 0, NB_LOOKUP_MODE-1, FLAGS, .unit = "mode" }, |
45 |
|
|
{ "off", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = MODE_OFF}, 0, 0, .flags = FLAGS, .unit = "mode" }, |
46 |
|
|
{ "full", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = MODE_FULL}, 0, 0, .flags = FLAGS, .unit = "mode" }, |
47 |
|
|
{ "fast", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = MODE_FAST}, 0, 0, .flags = FLAGS, .unit = "mode" }, |
48 |
|
|
{ "nb_inputs", "number of inputs", |
49 |
|
|
OFFSET(nb_inputs), AV_OPT_TYPE_INT, {.i64 = 1}, 1, INT_MAX, FLAGS }, |
50 |
|
|
{ "filename", "filename for output files", |
51 |
|
|
OFFSET(filename), AV_OPT_TYPE_STRING, {.str = ""}, 0, NB_FORMATS-1, FLAGS }, |
52 |
|
|
{ "format", "set output format", |
53 |
|
|
OFFSET(format), AV_OPT_TYPE_INT, {.i64 = FORMAT_BINARY}, 0, 1, FLAGS , .unit = "format" }, |
54 |
|
|
{ "binary", 0, 0, AV_OPT_TYPE_CONST, {.i64=FORMAT_BINARY}, 0, 0, FLAGS, .unit = "format" }, |
55 |
|
|
{ "xml", 0, 0, AV_OPT_TYPE_CONST, {.i64=FORMAT_XML}, 0, 0, FLAGS, .unit = "format" }, |
56 |
|
|
{ "th_d", "threshold to detect one word as similar", |
57 |
|
|
OFFSET(thworddist), AV_OPT_TYPE_INT, {.i64 = 9000}, 1, INT_MAX, FLAGS }, |
58 |
|
|
{ "th_dc", "threshold to detect all words as similar", |
59 |
|
|
OFFSET(thcomposdist), AV_OPT_TYPE_INT, {.i64 = 60000}, 1, INT_MAX, FLAGS }, |
60 |
|
|
{ "th_xh", "threshold to detect frames as similar", |
61 |
|
|
OFFSET(thl1), AV_OPT_TYPE_INT, {.i64 = 116}, 1, INT_MAX, FLAGS }, |
62 |
|
|
{ "th_di", "minimum length of matching sequence in frames", |
63 |
|
|
OFFSET(thdi), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, FLAGS }, |
64 |
|
|
{ "th_it", "threshold for relation of good to all frames", |
65 |
|
|
OFFSET(thit), AV_OPT_TYPE_DOUBLE, {.dbl = 0.5}, 0.0, 1.0, FLAGS }, |
66 |
|
|
{ NULL } |
67 |
|
|
}; |
68 |
|
|
|
69 |
|
|
AVFILTER_DEFINE_CLASS(signature); |
70 |
|
|
|
71 |
|
|
/* all formats with a separate gray value */ |
72 |
|
|
static const enum AVPixelFormat pix_fmts[] = { |
73 |
|
|
AV_PIX_FMT_GRAY8, |
74 |
|
|
AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P, |
75 |
|
|
AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, |
76 |
|
|
AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P, |
77 |
|
|
AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUVJ420P, |
78 |
|
|
AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, |
79 |
|
|
AV_PIX_FMT_YUVJ440P, |
80 |
|
|
AV_PIX_FMT_NV12, AV_PIX_FMT_NV21, |
81 |
|
|
AV_PIX_FMT_NONE |
82 |
|
|
}; |
83 |
|
|
|
84 |
|
✗ |
static int config_input(AVFilterLink *inlink) |
85 |
|
|
{ |
86 |
|
✗ |
AVFilterContext *ctx = inlink->dst; |
87 |
|
✗ |
SignatureContext *sic = ctx->priv; |
88 |
|
✗ |
StreamContext *sc = &(sic->streamcontexts[FF_INLINK_IDX(inlink)]); |
89 |
|
|
|
90 |
|
✗ |
sc->time_base = inlink->time_base; |
91 |
|
|
/* test for overflow */ |
92 |
|
✗ |
sc->divide = (((uint64_t) inlink->w/32) * (inlink->w/32 + 1) * (inlink->h/32 * inlink->h/32 + 1) > INT64_MAX / (BLOCK_LCM * 255)); |
93 |
|
✗ |
if (sc->divide) { |
94 |
|
✗ |
av_log(ctx, AV_LOG_WARNING, "Input dimension too high for precise calculation, numbers will be rounded.\n"); |
95 |
|
|
} |
96 |
|
✗ |
sc->w = inlink->w; |
97 |
|
✗ |
sc->h = inlink->h; |
98 |
|
✗ |
return 0; |
99 |
|
|
} |
100 |
|
|
|
101 |
|
✗ |
static int get_block_size(const Block *b) |
102 |
|
|
{ |
103 |
|
✗ |
return (b->to.y - b->up.y + 1) * (b->to.x - b->up.x + 1); |
104 |
|
|
} |
105 |
|
|
|
106 |
|
✗ |
static uint64_t get_block_sum(StreamContext *sc, uint64_t intpic[32][32], const Block *b) |
107 |
|
|
{ |
108 |
|
✗ |
uint64_t sum = 0; |
109 |
|
|
|
110 |
|
|
int x0, y0, x1, y1; |
111 |
|
|
|
112 |
|
✗ |
x0 = b->up.x; |
113 |
|
✗ |
y0 = b->up.y; |
114 |
|
✗ |
x1 = b->to.x; |
115 |
|
✗ |
y1 = b->to.y; |
116 |
|
|
|
117 |
|
✗ |
if (x0-1 >= 0 && y0-1 >= 0) { |
118 |
|
✗ |
sum = intpic[y1][x1] + intpic[y0-1][x0-1] - intpic[y1][x0-1] - intpic[y0-1][x1]; |
119 |
|
✗ |
} else if (x0-1 >= 0) { |
120 |
|
✗ |
sum = intpic[y1][x1] - intpic[y1][x0-1]; |
121 |
|
✗ |
} else if (y0-1 >= 0) { |
122 |
|
✗ |
sum = intpic[y1][x1] - intpic[y0-1][x1]; |
123 |
|
|
} else { |
124 |
|
✗ |
sum = intpic[y1][x1]; |
125 |
|
|
} |
126 |
|
✗ |
return sum; |
127 |
|
|
} |
128 |
|
|
|
129 |
|
✗ |
static int cmp(const void *x, const void *y) |
130 |
|
|
{ |
131 |
|
✗ |
const uint64_t *a = x, *b = y; |
132 |
|
✗ |
return *a < *b ? -1 : ( *a > *b ? 1 : 0 ); |
133 |
|
|
} |
134 |
|
|
|
135 |
|
|
/** |
136 |
|
|
* sets the bit at position pos to 1 in data |
137 |
|
|
*/ |
138 |
|
✗ |
static void set_bit(uint8_t* data, size_t pos) |
139 |
|
|
{ |
140 |
|
✗ |
uint8_t mask = 1 << 7-(pos%8); |
141 |
|
✗ |
data[pos/8] |= mask; |
142 |
|
✗ |
} |
143 |
|
|
|
144 |
|
✗ |
static int filter_frame(AVFilterLink *inlink, AVFrame *picref) |
145 |
|
|
{ |
146 |
|
✗ |
AVFilterContext *ctx = inlink->dst; |
147 |
|
✗ |
SignatureContext *sic = ctx->priv; |
148 |
|
✗ |
StreamContext *sc = &(sic->streamcontexts[FF_INLINK_IDX(inlink)]); |
149 |
|
|
FineSignature* fs; |
150 |
|
|
|
151 |
|
|
static const uint8_t pot3[5] = { 3*3*3*3, 3*3*3, 3*3, 3, 1 }; |
152 |
|
|
/* indexes of words : 210,217,219,274,334 44,175,233,270,273 57,70,103,237,269 100,285,295,337,354 101,102,111,275,296 |
153 |
|
|
s2usw = sorted to unsorted wordvec: 44 is at index 5, 57 at index 10... |
154 |
|
|
*/ |
155 |
|
|
static const unsigned int wordvec[25] = {44,57,70,100,101,102,103,111,175,210,217,219,233,237,269,270,273,274,275,285,295,296,334,337,354}; |
156 |
|
|
static const uint8_t s2usw[25] = { 5,10,11, 15, 20, 21, 12, 22, 6, 0, 1, 2, 7, 13, 14, 8, 9, 3, 23, 16, 17, 24, 4, 18, 19}; |
157 |
|
|
|
158 |
|
✗ |
uint8_t wordt2b[5] = { 0, 0, 0, 0, 0 }; /* word ternary to binary */ |
159 |
|
|
uint64_t intpic[32][32]; |
160 |
|
|
uint64_t rowcount; |
161 |
|
✗ |
uint8_t *p = picref->data[0]; |
162 |
|
|
int inti, intj; |
163 |
|
|
int *intjlut; |
164 |
|
|
|
165 |
|
|
uint64_t conflist[DIFFELEM_SIZE]; |
166 |
|
✗ |
int f = 0, g = 0, w = 0; |
167 |
|
✗ |
int32_t dh1 = 1, dh2 = 1, dw1 = 1, dw2 = 1, a, b; |
168 |
|
|
int64_t denom; |
169 |
|
|
int i, j, k, ternary; |
170 |
|
|
uint64_t blocksum; |
171 |
|
|
int blocksize; |
172 |
|
|
int64_t th; /* threshold */ |
173 |
|
|
int64_t sum; |
174 |
|
|
|
175 |
|
✗ |
int64_t precfactor = (sc->divide) ? 65536 : BLOCK_LCM; |
176 |
|
|
|
177 |
|
|
/* initialize fs */ |
178 |
|
✗ |
if (sc->curfinesig) { |
179 |
|
✗ |
fs = av_mallocz(sizeof(FineSignature)); |
180 |
|
✗ |
if (!fs) |
181 |
|
✗ |
return AVERROR(ENOMEM); |
182 |
|
✗ |
sc->curfinesig->next = fs; |
183 |
|
✗ |
fs->prev = sc->curfinesig; |
184 |
|
✗ |
sc->curfinesig = fs; |
185 |
|
|
} else { |
186 |
|
✗ |
fs = sc->curfinesig = sc->finesiglist; |
187 |
|
✗ |
sc->curcoarsesig1->first = fs; |
188 |
|
|
} |
189 |
|
|
|
190 |
|
✗ |
fs->pts = picref->pts; |
191 |
|
✗ |
fs->index = sc->lastindex++; |
192 |
|
|
|
193 |
|
✗ |
memset(intpic, 0, sizeof(uint64_t)*32*32); |
194 |
|
✗ |
intjlut = av_malloc_array(inlink->w, sizeof(int)); |
195 |
|
✗ |
if (!intjlut) |
196 |
|
✗ |
return AVERROR(ENOMEM); |
197 |
|
✗ |
for (i = 0; i < inlink->w; i++) { |
198 |
|
✗ |
intjlut[i] = (i*32)/inlink->w; |
199 |
|
|
} |
200 |
|
|
|
201 |
|
✗ |
for (i = 0; i < inlink->h; i++) { |
202 |
|
✗ |
inti = (i*32)/inlink->h; |
203 |
|
✗ |
for (j = 0; j < inlink->w; j++) { |
204 |
|
✗ |
intj = intjlut[j]; |
205 |
|
✗ |
intpic[inti][intj] += p[j]; |
206 |
|
|
} |
207 |
|
✗ |
p += picref->linesize[0]; |
208 |
|
|
} |
209 |
|
✗ |
av_freep(&intjlut); |
210 |
|
|
|
211 |
|
|
/* The following calculates a summed area table (intpic) and brings the numbers |
212 |
|
|
* in intpic to the same denominator. |
213 |
|
|
* So you only have to handle the numinator in the following sections. |
214 |
|
|
*/ |
215 |
|
✗ |
dh1 = inlink->h / 32; |
216 |
|
✗ |
if (inlink->h % 32) |
217 |
|
✗ |
dh2 = dh1 + 1; |
218 |
|
✗ |
dw1 = inlink->w / 32; |
219 |
|
✗ |
if (inlink->w % 32) |
220 |
|
✗ |
dw2 = dw1 + 1; |
221 |
|
✗ |
denom = (sc->divide) ? dh1 * (int64_t)dh2 * dw1 * dw2 : 1; |
222 |
|
|
|
223 |
|
✗ |
for (i = 0; i < 32; i++) { |
224 |
|
✗ |
rowcount = 0; |
225 |
|
✗ |
a = 1; |
226 |
|
✗ |
if (dh2 > 1) { |
227 |
|
✗ |
a = ((inlink->h*(i+1))%32 == 0) ? (inlink->h*(i+1))/32 - 1 : (inlink->h*(i+1))/32; |
228 |
|
✗ |
a -= ((inlink->h*i)%32 == 0) ? (inlink->h*i)/32 - 1 : (inlink->h*i)/32; |
229 |
|
✗ |
a = (a == dh1)? dh2 : dh1; |
230 |
|
|
} |
231 |
|
✗ |
for (j = 0; j < 32; j++) { |
232 |
|
✗ |
b = 1; |
233 |
|
✗ |
if (dw2 > 1) { |
234 |
|
✗ |
b = ((inlink->w*(j+1))%32 == 0) ? (inlink->w*(j+1))/32 - 1 : (inlink->w*(j+1))/32; |
235 |
|
✗ |
b -= ((inlink->w*j)%32 == 0) ? (inlink->w*j)/32 - 1 : (inlink->w*j)/32; |
236 |
|
✗ |
b = (b == dw1)? dw2 : dw1; |
237 |
|
|
} |
238 |
|
✗ |
rowcount += intpic[i][j] * a * b * precfactor / denom; |
239 |
|
✗ |
if (i > 0) { |
240 |
|
✗ |
intpic[i][j] = intpic[i-1][j] + rowcount; |
241 |
|
|
} else { |
242 |
|
✗ |
intpic[i][j] = rowcount; |
243 |
|
|
} |
244 |
|
|
} |
245 |
|
|
} |
246 |
|
|
|
247 |
|
✗ |
denom = (sc->divide) ? 1 : dh1 * (int64_t)dh2 * dw1 * dw2; |
248 |
|
|
|
249 |
|
✗ |
for (i = 0; i < ELEMENT_COUNT; i++) { |
250 |
|
✗ |
const ElemCat* elemcat = elements[i]; |
251 |
|
|
int64_t* elemsignature; |
252 |
|
|
uint64_t* sortsignature; |
253 |
|
|
|
254 |
|
✗ |
elemsignature = av_malloc_array(elemcat->elem_count, 2 * sizeof(int64_t)); |
255 |
|
✗ |
if (!elemsignature) |
256 |
|
✗ |
return AVERROR(ENOMEM); |
257 |
|
✗ |
sortsignature = elemsignature + elemcat->elem_count; |
258 |
|
|
|
259 |
|
✗ |
for (j = 0; j < elemcat->elem_count; j++) { |
260 |
|
✗ |
blocksum = 0; |
261 |
|
✗ |
blocksize = 0; |
262 |
|
✗ |
for (k = 0; k < elemcat->left_count; k++) { |
263 |
|
✗ |
blocksum += get_block_sum(sc, intpic, &elemcat->blocks[j*elemcat->block_count+k]); |
264 |
|
✗ |
blocksize += get_block_size(&elemcat->blocks[j*elemcat->block_count+k]); |
265 |
|
|
} |
266 |
|
✗ |
sum = blocksum / blocksize; |
267 |
|
✗ |
if (elemcat->av_elem) { |
268 |
|
✗ |
sum -= 128 * precfactor * denom; |
269 |
|
|
} else { |
270 |
|
✗ |
blocksum = 0; |
271 |
|
✗ |
blocksize = 0; |
272 |
|
✗ |
for (; k < elemcat->block_count; k++) { |
273 |
|
✗ |
blocksum += get_block_sum(sc, intpic, &elemcat->blocks[j*elemcat->block_count+k]); |
274 |
|
✗ |
blocksize += get_block_size(&elemcat->blocks[j*elemcat->block_count+k]); |
275 |
|
|
} |
276 |
|
✗ |
sum -= blocksum / blocksize; |
277 |
|
✗ |
conflist[g++] = FFABS(sum * 8 / (precfactor * denom)); |
278 |
|
|
} |
279 |
|
|
|
280 |
|
✗ |
elemsignature[j] = sum; |
281 |
|
✗ |
sortsignature[j] = FFABS(sum); |
282 |
|
|
} |
283 |
|
|
|
284 |
|
|
/* get threshold */ |
285 |
|
✗ |
qsort(sortsignature, elemcat->elem_count, sizeof(uint64_t), cmp); |
286 |
|
✗ |
th = sortsignature[(int) (elemcat->elem_count*0.333)]; |
287 |
|
|
|
288 |
|
|
/* ternarize */ |
289 |
|
✗ |
for (j = 0; j < elemcat->elem_count; j++) { |
290 |
|
✗ |
if (elemsignature[j] < -th) { |
291 |
|
✗ |
ternary = 0; |
292 |
|
✗ |
} else if (elemsignature[j] <= th) { |
293 |
|
✗ |
ternary = 1; |
294 |
|
|
} else { |
295 |
|
✗ |
ternary = 2; |
296 |
|
|
} |
297 |
|
✗ |
fs->framesig[f/5] += ternary * pot3[f%5]; |
298 |
|
|
|
299 |
|
✗ |
if (f == wordvec[w]) { |
300 |
|
✗ |
fs->words[s2usw[w]/5] += ternary * pot3[wordt2b[s2usw[w]/5]++]; |
301 |
|
✗ |
if (w < 24) |
302 |
|
✗ |
w++; |
303 |
|
|
} |
304 |
|
✗ |
f++; |
305 |
|
|
} |
306 |
|
✗ |
av_freep(&elemsignature); |
307 |
|
|
} |
308 |
|
|
|
309 |
|
|
/* confidence */ |
310 |
|
✗ |
qsort(conflist, DIFFELEM_SIZE, sizeof(uint64_t), cmp); |
311 |
|
✗ |
fs->confidence = FFMIN(conflist[DIFFELEM_SIZE/2], 255); |
312 |
|
|
|
313 |
|
|
/* coarsesignature */ |
314 |
|
✗ |
if (sc->coarsecount == 0) { |
315 |
|
✗ |
if (sc->curcoarsesig2) { |
316 |
|
✗ |
sc->curcoarsesig1 = av_mallocz(sizeof(CoarseSignature)); |
317 |
|
✗ |
if (!sc->curcoarsesig1) |
318 |
|
✗ |
return AVERROR(ENOMEM); |
319 |
|
✗ |
sc->curcoarsesig1->first = fs; |
320 |
|
✗ |
sc->curcoarsesig2->next = sc->curcoarsesig1; |
321 |
|
✗ |
sc->coarseend = sc->curcoarsesig1; |
322 |
|
|
} |
323 |
|
|
} |
324 |
|
✗ |
if (sc->coarsecount == 45) { |
325 |
|
✗ |
sc->midcoarse = 1; |
326 |
|
✗ |
sc->curcoarsesig2 = av_mallocz(sizeof(CoarseSignature)); |
327 |
|
✗ |
if (!sc->curcoarsesig2) |
328 |
|
✗ |
return AVERROR(ENOMEM); |
329 |
|
✗ |
sc->curcoarsesig2->first = fs; |
330 |
|
✗ |
sc->curcoarsesig1->next = sc->curcoarsesig2; |
331 |
|
✗ |
sc->coarseend = sc->curcoarsesig2; |
332 |
|
|
} |
333 |
|
✗ |
for (i = 0; i < 5; i++) { |
334 |
|
✗ |
set_bit(sc->curcoarsesig1->data[i], fs->words[i]); |
335 |
|
|
} |
336 |
|
|
/* assuming the actual frame is the last */ |
337 |
|
✗ |
sc->curcoarsesig1->last = fs; |
338 |
|
✗ |
if (sc->midcoarse) { |
339 |
|
✗ |
for (i = 0; i < 5; i++) { |
340 |
|
✗ |
set_bit(sc->curcoarsesig2->data[i], fs->words[i]); |
341 |
|
|
} |
342 |
|
✗ |
sc->curcoarsesig2->last = fs; |
343 |
|
|
} |
344 |
|
|
|
345 |
|
✗ |
sc->coarsecount = (sc->coarsecount+1)%90; |
346 |
|
|
|
347 |
|
|
/* debug printing finesignature */ |
348 |
|
✗ |
if (av_log_get_level() == AV_LOG_DEBUG) { |
349 |
|
✗ |
av_log(ctx, AV_LOG_DEBUG, "input %d, confidence: %d\n", FF_INLINK_IDX(inlink), fs->confidence); |
350 |
|
|
|
351 |
|
✗ |
av_log(ctx, AV_LOG_DEBUG, "words:"); |
352 |
|
✗ |
for (i = 0; i < 5; i++) { |
353 |
|
✗ |
av_log(ctx, AV_LOG_DEBUG, " %d:", fs->words[i] ); |
354 |
|
✗ |
av_log(ctx, AV_LOG_DEBUG, " %d", fs->words[i] / pot3[0] ); |
355 |
|
✗ |
for (j = 1; j < 5; j++) |
356 |
|
✗ |
av_log(ctx, AV_LOG_DEBUG, ",%d", fs->words[i] % pot3[j-1] / pot3[j] ); |
357 |
|
✗ |
av_log(ctx, AV_LOG_DEBUG, ";"); |
358 |
|
|
} |
359 |
|
✗ |
av_log(ctx, AV_LOG_DEBUG, "\n"); |
360 |
|
|
|
361 |
|
✗ |
av_log(ctx, AV_LOG_DEBUG, "framesignature:"); |
362 |
|
✗ |
for (i = 0; i < SIGELEM_SIZE/5; i++) { |
363 |
|
✗ |
av_log(ctx, AV_LOG_DEBUG, " %d", fs->framesig[i] / pot3[0] ); |
364 |
|
✗ |
for (j = 1; j < 5; j++) |
365 |
|
✗ |
av_log(ctx, AV_LOG_DEBUG, ",%d", fs->framesig[i] % pot3[j-1] / pot3[j] ); |
366 |
|
|
} |
367 |
|
✗ |
av_log(ctx, AV_LOG_DEBUG, "\n"); |
368 |
|
|
} |
369 |
|
|
|
370 |
|
✗ |
if (FF_INLINK_IDX(inlink) == 0) |
371 |
|
✗ |
return ff_filter_frame(inlink->dst->outputs[0], picref); |
372 |
|
✗ |
return 1; |
373 |
|
|
} |
374 |
|
|
|
375 |
|
✗ |
static int xml_export(AVFilterContext *ctx, StreamContext *sc, const char* filename) |
376 |
|
|
{ |
377 |
|
|
FineSignature* fs; |
378 |
|
|
CoarseSignature* cs; |
379 |
|
|
int i, j; |
380 |
|
|
FILE* f; |
381 |
|
✗ |
unsigned int pot3[5] = { 3*3*3*3, 3*3*3, 3*3, 3, 1 }; |
382 |
|
|
|
383 |
|
✗ |
if (!sc->coarseend->last) |
384 |
|
✗ |
return AVERROR(EINVAL); // No frames ? |
385 |
|
|
|
386 |
|
✗ |
f = avpriv_fopen_utf8(filename, "w"); |
387 |
|
✗ |
if (!f) { |
388 |
|
✗ |
int err = AVERROR(EINVAL); |
389 |
|
✗ |
av_log(ctx, AV_LOG_ERROR, "cannot open xml file %s: %s\n", filename, av_err2str(err)); |
390 |
|
✗ |
return err; |
391 |
|
|
} |
392 |
|
|
|
393 |
|
|
/* header */ |
394 |
|
✗ |
fprintf(f, "<?xml version='1.0' encoding='ASCII' ?>\n"); |
395 |
|
✗ |
fprintf(f, "<Mpeg7 xmlns=\"urn:mpeg:mpeg7:schema:2001\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"urn:mpeg:mpeg7:schema:2001 schema/Mpeg7-2001.xsd\">\n"); |
396 |
|
✗ |
fprintf(f, " <DescriptionUnit xsi:type=\"DescriptorCollectionType\">\n"); |
397 |
|
✗ |
fprintf(f, " <Descriptor xsi:type=\"VideoSignatureType\">\n"); |
398 |
|
✗ |
fprintf(f, " <VideoSignatureRegion>\n"); |
399 |
|
✗ |
fprintf(f, " <VideoSignatureSpatialRegion>\n"); |
400 |
|
✗ |
fprintf(f, " <Pixel>0 0 </Pixel>\n"); |
401 |
|
✗ |
fprintf(f, " <Pixel>%d %d </Pixel>\n", sc->w - 1, sc->h - 1); |
402 |
|
✗ |
fprintf(f, " </VideoSignatureSpatialRegion>\n"); |
403 |
|
✗ |
fprintf(f, " <StartFrameOfSpatialRegion>0</StartFrameOfSpatialRegion>\n"); |
404 |
|
|
/* hoping num is 1, other values are vague */ |
405 |
|
✗ |
fprintf(f, " <MediaTimeUnit>%d</MediaTimeUnit>\n", sc->time_base.den / sc->time_base.num); |
406 |
|
✗ |
fprintf(f, " <MediaTimeOfSpatialRegion>\n"); |
407 |
|
✗ |
fprintf(f, " <StartMediaTimeOfSpatialRegion>0</StartMediaTimeOfSpatialRegion>\n"); |
408 |
|
✗ |
fprintf(f, " <EndMediaTimeOfSpatialRegion>%" PRIu64 "</EndMediaTimeOfSpatialRegion>\n", sc->coarseend->last->pts); |
409 |
|
✗ |
fprintf(f, " </MediaTimeOfSpatialRegion>\n"); |
410 |
|
|
|
411 |
|
|
/* coarsesignatures */ |
412 |
|
✗ |
for (cs = sc->coarsesiglist; cs; cs = cs->next) { |
413 |
|
✗ |
fprintf(f, " <VSVideoSegment>\n"); |
414 |
|
✗ |
fprintf(f, " <StartFrameOfSegment>%" PRIu32 "</StartFrameOfSegment>\n", cs->first->index); |
415 |
|
✗ |
fprintf(f, " <EndFrameOfSegment>%" PRIu32 "</EndFrameOfSegment>\n", cs->last->index); |
416 |
|
✗ |
fprintf(f, " <MediaTimeOfSegment>\n"); |
417 |
|
✗ |
fprintf(f, " <StartMediaTimeOfSegment>%" PRIu64 "</StartMediaTimeOfSegment>\n", cs->first->pts); |
418 |
|
✗ |
fprintf(f, " <EndMediaTimeOfSegment>%" PRIu64 "</EndMediaTimeOfSegment>\n", cs->last->pts); |
419 |
|
✗ |
fprintf(f, " </MediaTimeOfSegment>\n"); |
420 |
|
✗ |
for (i = 0; i < 5; i++) { |
421 |
|
✗ |
fprintf(f, " <BagOfWords>"); |
422 |
|
✗ |
for (j = 0; j < 31; j++) { |
423 |
|
✗ |
uint8_t n = cs->data[i][j]; |
424 |
|
✗ |
if (j < 30) { |
425 |
|
✗ |
fprintf(f, "%d %d %d %d %d %d %d %d ", (n & 0x80) >> 7, |
426 |
|
✗ |
(n & 0x40) >> 6, |
427 |
|
✗ |
(n & 0x20) >> 5, |
428 |
|
✗ |
(n & 0x10) >> 4, |
429 |
|
✗ |
(n & 0x08) >> 3, |
430 |
|
✗ |
(n & 0x04) >> 2, |
431 |
|
✗ |
(n & 0x02) >> 1, |
432 |
|
|
(n & 0x01)); |
433 |
|
|
} else { |
434 |
|
|
/* print only 3 bit in last byte */ |
435 |
|
✗ |
fprintf(f, "%d %d %d ", (n & 0x80) >> 7, |
436 |
|
✗ |
(n & 0x40) >> 6, |
437 |
|
✗ |
(n & 0x20) >> 5); |
438 |
|
|
} |
439 |
|
|
} |
440 |
|
✗ |
fprintf(f, "</BagOfWords>\n"); |
441 |
|
|
} |
442 |
|
✗ |
fprintf(f, " </VSVideoSegment>\n"); |
443 |
|
|
} |
444 |
|
|
|
445 |
|
|
/* finesignatures */ |
446 |
|
✗ |
for (fs = sc->finesiglist; fs; fs = fs->next) { |
447 |
|
✗ |
fprintf(f, " <VideoFrame>\n"); |
448 |
|
✗ |
fprintf(f, " <MediaTimeOfFrame>%" PRIu64 "</MediaTimeOfFrame>\n", fs->pts); |
449 |
|
|
/* confidence */ |
450 |
|
✗ |
fprintf(f, " <FrameConfidence>%d</FrameConfidence>\n", fs->confidence); |
451 |
|
|
/* words */ |
452 |
|
✗ |
fprintf(f, " <Word>"); |
453 |
|
✗ |
for (i = 0; i < 5; i++) { |
454 |
|
✗ |
fprintf(f, "%d ", fs->words[i]); |
455 |
|
✗ |
if (i < 4) { |
456 |
|
✗ |
fprintf(f, " "); |
457 |
|
|
} |
458 |
|
|
} |
459 |
|
✗ |
fprintf(f, "</Word>\n"); |
460 |
|
|
/* framesignature */ |
461 |
|
✗ |
fprintf(f, " <FrameSignature>"); |
462 |
|
✗ |
for (i = 0; i< SIGELEM_SIZE/5; i++) { |
463 |
|
✗ |
if (i > 0) { |
464 |
|
✗ |
fprintf(f, " "); |
465 |
|
|
} |
466 |
|
✗ |
fprintf(f, "%d ", fs->framesig[i] / pot3[0]); |
467 |
|
✗ |
for (j = 1; j < 5; j++) |
468 |
|
✗ |
fprintf(f, " %d ", fs->framesig[i] % pot3[j-1] / pot3[j] ); |
469 |
|
|
} |
470 |
|
✗ |
fprintf(f, "</FrameSignature>\n"); |
471 |
|
✗ |
fprintf(f, " </VideoFrame>\n"); |
472 |
|
|
} |
473 |
|
✗ |
fprintf(f, " </VideoSignatureRegion>\n"); |
474 |
|
✗ |
fprintf(f, " </Descriptor>\n"); |
475 |
|
✗ |
fprintf(f, " </DescriptionUnit>\n"); |
476 |
|
✗ |
fprintf(f, "</Mpeg7>\n"); |
477 |
|
|
|
478 |
|
✗ |
fclose(f); |
479 |
|
✗ |
return 0; |
480 |
|
|
} |
481 |
|
|
|
482 |
|
✗ |
static int binary_export(AVFilterContext *ctx, StreamContext *sc, const char* filename) |
483 |
|
|
{ |
484 |
|
|
FILE* f; |
485 |
|
|
FineSignature* fs; |
486 |
|
|
CoarseSignature* cs; |
487 |
|
✗ |
uint32_t numofsegments = (sc->lastindex + 44)/45; |
488 |
|
|
int i, j; |
489 |
|
|
PutBitContext buf; |
490 |
|
|
/* buffer + header + coarsesignatures + finesignature */ |
491 |
|
✗ |
int len = (512 + 6 * 32 + 3*16 + 2 + |
492 |
|
✗ |
numofsegments * (4*32 + 1 + 5*243) + |
493 |
|
✗ |
sc->lastindex * (2 + 32 + 6*8 + 608)) / 8; |
494 |
|
✗ |
uint8_t* buffer = av_malloc_array(len, sizeof(uint8_t)); |
495 |
|
✗ |
if (!buffer) |
496 |
|
✗ |
return AVERROR(ENOMEM); |
497 |
|
|
|
498 |
|
✗ |
f = avpriv_fopen_utf8(filename, "wb"); |
499 |
|
✗ |
if (!f) { |
500 |
|
✗ |
int err = AVERROR(EINVAL); |
501 |
|
✗ |
av_log(ctx, AV_LOG_ERROR, "cannot open file %s: %s\n", filename, av_err2str(err)); |
502 |
|
✗ |
av_freep(&buffer); |
503 |
|
✗ |
return err; |
504 |
|
|
} |
505 |
|
✗ |
init_put_bits(&buf, buffer, len); |
506 |
|
|
|
507 |
|
✗ |
put_bits32(&buf, 1); /* NumOfSpatial Regions, only 1 supported */ |
508 |
|
✗ |
put_bits(&buf, 1, 1); /* SpatialLocationFlag, always the whole image */ |
509 |
|
✗ |
put_bits32(&buf, 0); /* PixelX,1 PixelY,1, 0,0 */ |
510 |
|
✗ |
put_bits(&buf, 16, sc->w-1 & 0xFFFF); /* PixelX,2 */ |
511 |
|
✗ |
put_bits(&buf, 16, sc->h-1 & 0xFFFF); /* PixelY,2 */ |
512 |
|
✗ |
put_bits32(&buf, 0); /* StartFrameOfSpatialRegion */ |
513 |
|
✗ |
put_bits32(&buf, sc->lastindex); /* NumOfFrames */ |
514 |
|
|
/* hoping num is 1, other values are vague */ |
515 |
|
|
/* den/num might be greater than 16 bit, so cutting it */ |
516 |
|
✗ |
put_bits(&buf, 16, 0xFFFF & (sc->time_base.den / sc->time_base.num)); /* MediaTimeUnit */ |
517 |
|
✗ |
put_bits(&buf, 1, 1); /* MediaTimeFlagOfSpatialRegion */ |
518 |
|
✗ |
put_bits32(&buf, 0); /* StartMediaTimeOfSpatialRegion */ |
519 |
|
✗ |
put_bits32(&buf, 0xFFFFFFFF & sc->coarseend->last->pts); /* EndMediaTimeOfSpatialRegion */ |
520 |
|
✗ |
put_bits32(&buf, numofsegments); /* NumOfSegments */ |
521 |
|
|
/* coarsesignatures */ |
522 |
|
✗ |
for (cs = sc->coarsesiglist; cs; cs = cs->next) { |
523 |
|
✗ |
put_bits32(&buf, cs->first->index); /* StartFrameOfSegment */ |
524 |
|
✗ |
put_bits32(&buf, cs->last->index); /* EndFrameOfSegment */ |
525 |
|
✗ |
put_bits(&buf, 1, 1); /* MediaTimeFlagOfSegment */ |
526 |
|
✗ |
put_bits32(&buf, 0xFFFFFFFF & cs->first->pts); /* StartMediaTimeOfSegment */ |
527 |
|
✗ |
put_bits32(&buf, 0xFFFFFFFF & cs->last->pts); /* EndMediaTimeOfSegment */ |
528 |
|
✗ |
for (i = 0; i < 5; i++) { |
529 |
|
|
/* put 243 bits ( = 7 * 32 + 19 = 8 * 28 + 19) into buffer */ |
530 |
|
✗ |
for (j = 0; j < 30; j++) { |
531 |
|
✗ |
put_bits(&buf, 8, cs->data[i][j]); |
532 |
|
|
} |
533 |
|
✗ |
put_bits(&buf, 3, cs->data[i][30] >> 5); |
534 |
|
|
} |
535 |
|
|
} |
536 |
|
|
/* finesignatures */ |
537 |
|
✗ |
put_bits(&buf, 1, 0); /* CompressionFlag, only 0 supported */ |
538 |
|
✗ |
for (fs = sc->finesiglist; fs; fs = fs->next) { |
539 |
|
✗ |
put_bits(&buf, 1, 1); /* MediaTimeFlagOfFrame */ |
540 |
|
✗ |
put_bits32(&buf, 0xFFFFFFFF & fs->pts); /* MediaTimeOfFrame */ |
541 |
|
✗ |
put_bits(&buf, 8, fs->confidence); /* FrameConfidence */ |
542 |
|
✗ |
for (i = 0; i < 5; i++) { |
543 |
|
✗ |
put_bits(&buf, 8, fs->words[i]); /* Words */ |
544 |
|
|
} |
545 |
|
|
/* framesignature */ |
546 |
|
✗ |
for (i = 0; i < SIGELEM_SIZE/5; i++) { |
547 |
|
✗ |
put_bits(&buf, 8, fs->framesig[i]); |
548 |
|
|
} |
549 |
|
|
} |
550 |
|
|
|
551 |
|
✗ |
flush_put_bits(&buf); |
552 |
|
✗ |
fwrite(buffer, 1, put_bytes_output(&buf), f); |
553 |
|
✗ |
fclose(f); |
554 |
|
✗ |
av_freep(&buffer); |
555 |
|
✗ |
return 0; |
556 |
|
|
} |
557 |
|
|
|
558 |
|
✗ |
static int export(AVFilterContext *ctx, StreamContext *sc, int input) |
559 |
|
|
{ |
560 |
|
✗ |
SignatureContext* sic = ctx->priv; |
561 |
|
|
char filename[1024]; |
562 |
|
|
|
563 |
|
✗ |
if (sic->nb_inputs > 1) { |
564 |
|
|
/* error already handled */ |
565 |
|
✗ |
av_assert0(av_get_frame_filename(filename, sizeof(filename), sic->filename, input) == 0); |
566 |
|
|
} else { |
567 |
|
✗ |
if (av_strlcpy(filename, sic->filename, sizeof(filename)) >= sizeof(filename)) |
568 |
|
✗ |
return AVERROR(EINVAL); |
569 |
|
|
} |
570 |
|
✗ |
if (sic->format == FORMAT_XML) { |
571 |
|
✗ |
return xml_export(ctx, sc, filename); |
572 |
|
|
} else { |
573 |
|
✗ |
return binary_export(ctx, sc, filename); |
574 |
|
|
} |
575 |
|
|
} |
576 |
|
|
|
577 |
|
✗ |
static int request_frame(AVFilterLink *outlink) |
578 |
|
|
{ |
579 |
|
✗ |
AVFilterContext *ctx = outlink->src; |
580 |
|
✗ |
SignatureContext *sic = ctx->priv; |
581 |
|
|
StreamContext *sc, *sc2; |
582 |
|
|
MatchingInfo match; |
583 |
|
|
int i, j, ret; |
584 |
|
✗ |
int lookup = 1; /* indicates wheather EOF of all files is reached */ |
585 |
|
|
|
586 |
|
|
/* process all inputs */ |
587 |
|
✗ |
for (i = 0; i < sic->nb_inputs; i++){ |
588 |
|
✗ |
sc = &(sic->streamcontexts[i]); |
589 |
|
|
|
590 |
|
✗ |
ret = ff_request_frame(ctx->inputs[i]); |
591 |
|
|
|
592 |
|
|
/* return if unexpected error occurs in input stream */ |
593 |
|
✗ |
if (ret < 0 && ret != AVERROR_EOF) |
594 |
|
✗ |
return ret; |
595 |
|
|
|
596 |
|
|
/* export signature at EOF */ |
597 |
|
✗ |
if (ret == AVERROR_EOF && !sc->exported) { |
598 |
|
|
/* export if wanted */ |
599 |
|
✗ |
if (strlen(sic->filename) > 0) { |
600 |
|
✗ |
if (export(ctx, sc, i) < 0) |
601 |
|
✗ |
return ret; |
602 |
|
|
} |
603 |
|
✗ |
sc->exported = 1; |
604 |
|
|
} |
605 |
|
✗ |
lookup &= sc->exported; |
606 |
|
|
} |
607 |
|
|
|
608 |
|
|
/* signature lookup */ |
609 |
|
✗ |
if (lookup && sic->mode != MODE_OFF) { |
610 |
|
|
/* iterate over every pair */ |
611 |
|
✗ |
for (i = 0; i < sic->nb_inputs; i++) { |
612 |
|
✗ |
sc = &(sic->streamcontexts[i]); |
613 |
|
✗ |
for (j = i+1; j < sic->nb_inputs; j++) { |
614 |
|
✗ |
sc2 = &(sic->streamcontexts[j]); |
615 |
|
✗ |
match = lookup_signatures(ctx, sic, sc, sc2, sic->mode); |
616 |
|
✗ |
if (match.score != 0) { |
617 |
|
✗ |
av_log(ctx, AV_LOG_INFO, "matching of video %d at %f and %d at %f, %d frames matching\n", |
618 |
|
✗ |
i, ((double) match.first->pts * sc->time_base.num) / sc->time_base.den, |
619 |
|
✗ |
j, ((double) match.second->pts * sc2->time_base.num) / sc2->time_base.den, |
620 |
|
|
match.matchframes); |
621 |
|
✗ |
if (match.whole) |
622 |
|
✗ |
av_log(ctx, AV_LOG_INFO, "whole video matching\n"); |
623 |
|
|
} else { |
624 |
|
✗ |
av_log(ctx, AV_LOG_INFO, "no matching of video %d and %d\n", i, j); |
625 |
|
|
} |
626 |
|
|
} |
627 |
|
|
} |
628 |
|
|
} |
629 |
|
|
|
630 |
|
✗ |
return ret; |
631 |
|
|
} |
632 |
|
|
|
633 |
|
✗ |
static av_cold int init(AVFilterContext *ctx) |
634 |
|
|
{ |
635 |
|
|
|
636 |
|
✗ |
SignatureContext *sic = ctx->priv; |
637 |
|
|
StreamContext *sc; |
638 |
|
|
int i, ret; |
639 |
|
|
char tmp[1024]; |
640 |
|
|
|
641 |
|
✗ |
sic->streamcontexts = av_mallocz(sic->nb_inputs * sizeof(StreamContext)); |
642 |
|
✗ |
if (!sic->streamcontexts) |
643 |
|
✗ |
return AVERROR(ENOMEM); |
644 |
|
|
|
645 |
|
✗ |
for (i = 0; i < sic->nb_inputs; i++) { |
646 |
|
✗ |
AVFilterPad pad = { |
647 |
|
|
.type = AVMEDIA_TYPE_VIDEO, |
648 |
|
✗ |
.name = av_asprintf("in%d", i), |
649 |
|
|
.config_props = config_input, |
650 |
|
|
.filter_frame = filter_frame, |
651 |
|
|
}; |
652 |
|
|
|
653 |
|
✗ |
if (!pad.name) |
654 |
|
✗ |
return AVERROR(ENOMEM); |
655 |
|
✗ |
if ((ret = ff_append_inpad_free_name(ctx, &pad)) < 0) |
656 |
|
✗ |
return ret; |
657 |
|
|
|
658 |
|
✗ |
sc = &(sic->streamcontexts[i]); |
659 |
|
|
|
660 |
|
✗ |
sc->lastindex = 0; |
661 |
|
✗ |
sc->finesiglist = av_mallocz(sizeof(FineSignature)); |
662 |
|
✗ |
if (!sc->finesiglist) |
663 |
|
✗ |
return AVERROR(ENOMEM); |
664 |
|
✗ |
sc->curfinesig = NULL; |
665 |
|
|
|
666 |
|
✗ |
sc->coarsesiglist = av_mallocz(sizeof(CoarseSignature)); |
667 |
|
✗ |
if (!sc->coarsesiglist) |
668 |
|
✗ |
return AVERROR(ENOMEM); |
669 |
|
✗ |
sc->curcoarsesig1 = sc->coarsesiglist; |
670 |
|
✗ |
sc->coarseend = sc->coarsesiglist; |
671 |
|
✗ |
sc->coarsecount = 0; |
672 |
|
✗ |
sc->midcoarse = 0; |
673 |
|
|
} |
674 |
|
|
|
675 |
|
|
/* check filename */ |
676 |
|
✗ |
if (sic->nb_inputs > 1 && strlen(sic->filename) > 0 && av_get_frame_filename(tmp, sizeof(tmp), sic->filename, 0) == -1) { |
677 |
|
✗ |
av_log(ctx, AV_LOG_ERROR, "The filename must contain %%d or %%0nd, if you have more than one input.\n"); |
678 |
|
✗ |
return AVERROR(EINVAL); |
679 |
|
|
} |
680 |
|
|
|
681 |
|
✗ |
return 0; |
682 |
|
|
} |
683 |
|
|
|
684 |
|
|
|
685 |
|
|
|
686 |
|
✗ |
static av_cold void uninit(AVFilterContext *ctx) |
687 |
|
|
{ |
688 |
|
✗ |
SignatureContext *sic = ctx->priv; |
689 |
|
|
StreamContext *sc; |
690 |
|
|
void* tmp; |
691 |
|
|
FineSignature* finsig; |
692 |
|
|
CoarseSignature* cousig; |
693 |
|
|
int i; |
694 |
|
|
|
695 |
|
|
|
696 |
|
|
/* free the lists */ |
697 |
|
✗ |
if (sic->streamcontexts != NULL) { |
698 |
|
✗ |
for (i = 0; i < sic->nb_inputs; i++) { |
699 |
|
✗ |
sc = &(sic->streamcontexts[i]); |
700 |
|
✗ |
finsig = sc->finesiglist; |
701 |
|
✗ |
cousig = sc->coarsesiglist; |
702 |
|
|
|
703 |
|
✗ |
while (finsig) { |
704 |
|
✗ |
tmp = finsig; |
705 |
|
✗ |
finsig = finsig->next; |
706 |
|
✗ |
av_freep(&tmp); |
707 |
|
|
} |
708 |
|
✗ |
sc->finesiglist = NULL; |
709 |
|
|
|
710 |
|
✗ |
while (cousig) { |
711 |
|
✗ |
tmp = cousig; |
712 |
|
✗ |
cousig = cousig->next; |
713 |
|
✗ |
av_freep(&tmp); |
714 |
|
|
} |
715 |
|
✗ |
sc->coarsesiglist = NULL; |
716 |
|
|
} |
717 |
|
✗ |
av_freep(&sic->streamcontexts); |
718 |
|
|
} |
719 |
|
✗ |
} |
720 |
|
|
|
721 |
|
✗ |
static int config_output(AVFilterLink *outlink) |
722 |
|
|
{ |
723 |
|
✗ |
AVFilterContext *ctx = outlink->src; |
724 |
|
✗ |
AVFilterLink *inlink = ctx->inputs[0]; |
725 |
|
✗ |
FilterLink *il = ff_filter_link(inlink); |
726 |
|
✗ |
FilterLink *ol = ff_filter_link(outlink); |
727 |
|
|
|
728 |
|
✗ |
outlink->time_base = inlink->time_base; |
729 |
|
✗ |
ol->frame_rate = il->frame_rate; |
730 |
|
✗ |
outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; |
731 |
|
✗ |
outlink->w = inlink->w; |
732 |
|
✗ |
outlink->h = inlink->h; |
733 |
|
|
|
734 |
|
✗ |
return 0; |
735 |
|
|
} |
736 |
|
|
|
737 |
|
|
static const AVFilterPad signature_outputs[] = { |
738 |
|
|
{ |
739 |
|
|
.name = "default", |
740 |
|
|
.type = AVMEDIA_TYPE_VIDEO, |
741 |
|
|
.request_frame = request_frame, |
742 |
|
|
.config_props = config_output, |
743 |
|
|
}, |
744 |
|
|
}; |
745 |
|
|
|
746 |
|
|
const AVFilter ff_vf_signature = { |
747 |
|
|
.name = "signature", |
748 |
|
|
.description = NULL_IF_CONFIG_SMALL("Calculate the MPEG-7 video signature"), |
749 |
|
|
.priv_size = sizeof(SignatureContext), |
750 |
|
|
.priv_class = &signature_class, |
751 |
|
|
.init = init, |
752 |
|
|
.uninit = uninit, |
753 |
|
|
FILTER_OUTPUTS(signature_outputs), |
754 |
|
|
.inputs = NULL, |
755 |
|
|
FILTER_PIXFMTS_ARRAY(pix_fmts), |
756 |
|
|
.flags = AVFILTER_FLAG_DYNAMIC_INPUTS, |
757 |
|
|
}; |
758 |
|
|
|