1 /*
2 * Copyright (c) 2012 Stefano Sabatini
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 /**
22 * @file
23 * send commands filter
24 */
25
36
37 #define COMMAND_FLAG_ENTER 1
38 #define COMMAND_FLAG_LEAVE 2
39
41 {
42 static const char * const flag_strings[] = { "enter", "leave" };
43 int i, is_first = 1;
44
47 if (flags & 1<<i) {
48 if (!is_first)
51 is_first = 0;
52 }
53 }
54
55 return pbuf->str;
56 }
57
63
65 int64_t
start_ts;
///< start timestamp expressed as microseconds units
66 int64_t
end_ts;
///< end timestamp expressed as microseconds units
67 int index;
///< unique index for these interval commands
70 int enabled;
///< current time detected inside this interval
72
77
81
82 #define OFFSET(x) offsetof(SendCmdContext, x)
83 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_VIDEO_PARAM
89 { NULL }
90 };
91
92 #define SPACES " \f\t\n\r"
93
95 {
96 while (**buf) {
97 /* skip leading spaces */
98 *buf += strspn(*buf,
SPACES);
99 if (**buf != '#')
100 break;
101
102 (*buf)++;
103
104 /* skip comment until the end of line */
105 *buf += strcspn(*buf, "\n");
106 if (**buf)
107 (*buf)++;
108 }
109 }
110
111 #define COMMAND_DELIMS " \f\t\n\r,;"
112
114 const char **
buf,
void *log_ctx)
115 {
117
118 memset(cmd, 0,
sizeof(
Command));
119 cmd->
index = cmd_count;
120
121 /* format: [FLAGS] target command arg */
122 *buf += strspn(*buf,
SPACES);
123
124 /* parse flags */
125 if (**buf == '[') {
126 (*buf)++; /* skip "[" */
127
128 while (**buf) {
129 int len = strcspn(*buf,
"|+]");
130
133 else {
134 char flag_buf[64];
137 "Unknown flag '%s' in interval #%d, command #%d\n",
138 flag_buf, interval_count, cmd_count);
140 }
142 if (**buf == ']')
143 break;
144 if (!strspn(*buf, "+|")) {
146 "Invalid flags char '%c' in interval #%d, command #%d\n",
147 **buf, interval_count, cmd_count);
149 }
150 if (**buf)
151 (*buf)++;
152 }
153
154 if (**buf != ']') {
156 "Missing flag terminator or extraneous data found at the end of flags "
157 "in interval #%d, command #%d\n", interval_count, cmd_count);
159 }
160 (*buf)++; /* skip "]" */
161 } else {
163 }
164
165 *buf += strspn(*buf,
SPACES);
169 "No target specified in interval #%d, command #%d\n",
170 interval_count, cmd_count);
172 goto fail;
173 }
174
175 *buf += strspn(*buf,
SPACES);
179 "No command specified in interval #%d, command #%d\n",
180 interval_count, cmd_count);
182 goto fail;
183 }
184
185 *buf += strspn(*buf,
SPACES);
187
188 return 1;
189
190 fail:
195 }
196
198 const char **
buf,
void *log_ctx)
199 {
200 int cmd_count = 0;
203
204 *cmds = NULL;
205 *nb_cmds = 0;
206
207 while (**buf) {
209
210 if ((ret =
parse_command(&cmd, cmd_count, interval_count, buf, log_ctx)) < 0)
212 cmd_count++;
213
214 /* (re)allocate commands array if required */
215 if (*nb_cmds == n) {
216 n =
FFMAX(16, 2*n);
/* first allocation = 16, or double the number */
218 if (!*cmds) {
220 "Could not (re)allocate command array\n");
222 }
223 }
224
225 (*cmds)[(*nb_cmds)++] = cmd;
226
227 *buf += strspn(*buf,
SPACES);
228 if (**buf && **buf != ';' && **buf != ',') {
230 "Missing separator or extraneous data found at the end of "
231 "interval #%d, in command #%d\n",
232 interval_count, cmd_count);
234 "Command was parsed as: flags:[%s] target:%s command:%s arg:%s\n",
237 }
238 if (**buf == ';')
239 break;
240 if (**buf == ',')
241 (*buf)++;
242 }
243
244 return 0;
245 }
246
247 #define DELIMS " \f\t\n\r,;"
248
250 const char **
buf,
void *log_ctx)
251 {
252 char *intervalstr;
254
255 *buf += strspn(*buf,
SPACES);
256 if (!**buf)
257 return 0;
258
259 /* reset data */
260 memset(interval, 0,
sizeof(
Interval));
261 interval->
index = interval_count;
262
263 /* format: INTERVAL COMMANDS */
264
265 /* parse interval */
267 if (intervalstr && intervalstr[0]) {
269
270 start =
av_strtok(intervalstr,
"-", &end);
273 "Invalid start time specification '%s' in interval #%d\n",
274 start, interval_count);
276 }
277
278 if (end) {
281 "Invalid end time specification '%s' in interval #%d\n",
282 end, interval_count);
284 }
285 } else {
286 interval->
end_ts = INT64_MAX;
287 }
290 "Invalid end time '%s' in interval #%d: "
291 "cannot be lesser than start time '%s'\n",
292 end, interval_count, start);
295 }
296 } else {
298 "No interval specified for interval #%d\n", interval_count);
301 }
302
303 /* parse commands */
305 interval_count, buf, log_ctx);
306
310 }
311
313 const char *
buf,
void *log_ctx)
314 {
315 int interval_count = 0;
317
318 *intervals = NULL;
319 *nb_intervals = 0;
320
321 while (1) {
323
325 if (!(*buf))
326 break;
327
328 if ((ret =
parse_interval(&interval, interval_count, &buf, log_ctx)) < 0)
330
331 buf += strspn(buf,
SPACES);
332 if (*buf) {
333 if (*buf != ';') {
335 "Missing terminator or extraneous data found at the end of interval #%d\n",
336 interval_count);
338 }
339 buf++; /* skip ';' */
340 }
341 interval_count++;
342
343 /* (re)allocate commands array if required */
344 if (*nb_intervals == n) {
345 n =
FFMAX(16, 2*n);
/* first allocation = 16, or double the number */
347 if (!*intervals) {
349 "Could not (re)allocate intervals array\n");
351 }
352 }
353
354 (*intervals)[(*nb_intervals)++] = interval;
355 }
356
357 return 0;
358 }
359
361 {
366
367 ret = ts_diff > 0 ? 1 : ts_diff < 0 ? -1 : 0;
369 }
370
372 {
375
378 "Only one of the filename or commands options must be specified\n");
380 }
381
384 size_t file_bufsize;
386 &file_buf, &file_bufsize, 0, ctx);
387 if (ret < 0)
389
390 /* create a 0-terminated string based on the read file */
392 if (!buf) {
395 }
396 memcpy(buf, file_buf, file_bufsize);
397 buf[file_bufsize] = 0;
400 }
401
405
407
417 " [%s] target:%s command:%s arg:%s index:%d\n",
419 }
420 }
421
422 return 0;
423 }
424
426 {
428 int i, j;
429
437 }
439 }
441 }
442
444 {
447 int64_t ts;
449
452
454
455 #define WITHIN_INTERVAL(ts, start_ts, end_ts) ((ts) >= (start_ts) && (ts) < (end_ts))
456
460
464 }
468 }
469
470 if (flags) {
473 "[%s] interval #%d start_ts:%f end_ts:%f ts:%f\n",
475 (
double)interval->
start_ts/1000000, (
double)interval->
end_ts/1000000,
476 (double)ts/1000000);
477
478 for (j = 0; flags && j < interval->
nb_commands; j++) {
481
482 if (cmd->
flags & flags) {
484 "Processing command #%d target:%s command:%s arg:%s\n",
488 buf, sizeof(buf),
491 "Command reply for command #%d: ret:%s res:%s\n",
493 }
494 }
495 }
496 }
497
499 switch (inlink->
type) {
503 }
504
506 }
507
508 #if CONFIG_SENDCMD_FILTER
509
510 #define sendcmd_options options
512
514 {
518 },
519 { NULL }
520 };
521
523 {
526 },
527 { NULL }
528 };
529
538 .priv_class = &sendcmd_class,
539 };
540
541 #endif
542
543 #if CONFIG_ASENDCMD_FILTER
544
545 #define asendcmd_options options
547
549 {
553 },
554 { NULL }
555 };
556
558 {
561 },
562 { NULL }
563 };
564
571 .
inputs = asendcmd_inputs,
573 .priv_class = &asendcmd_class,
574 };
575
576 #endif