You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

644 lines
20KB

  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. * @file
  22. * send commands filter
  23. */
  24. #include "libavutil/avstring.h"
  25. #include "libavutil/bprint.h"
  26. #include "libavutil/eval.h"
  27. #include "libavutil/file.h"
  28. #include "libavutil/opt.h"
  29. #include "libavutil/parseutils.h"
  30. #include "avfilter.h"
  31. #include "internal.h"
  32. #include "audio.h"
  33. #include "video.h"
  34. #define COMMAND_FLAG_ENTER 1
  35. #define COMMAND_FLAG_LEAVE 2
  36. #define COMMAND_FLAG_EXPR 4
  37. static const char *const var_names[] = {
  38. "N", /* frame number */
  39. "T", /* frame time in seconds */
  40. "POS", /* original position in the file of the frame */
  41. "PTS", /* frame pts */
  42. "TS", /* interval start time in seconds */
  43. "TE", /* interval end time in seconds */
  44. "TI", /* interval interpolated value: TI = (T - TS) / (TE - TS) */
  45. NULL
  46. };
  47. enum var_name {
  48. VAR_N,
  49. VAR_T,
  50. VAR_POS,
  51. VAR_PTS,
  52. VAR_TS,
  53. VAR_TE,
  54. VAR_TI,
  55. VAR_VARS_NB
  56. };
  57. static inline char *make_command_flags_str(AVBPrint *pbuf, int flags)
  58. {
  59. static const char * const flag_strings[] = { "enter", "leave", "expr" };
  60. int i, is_first = 1;
  61. av_bprint_init(pbuf, 0, AV_BPRINT_SIZE_AUTOMATIC);
  62. for (i = 0; i < FF_ARRAY_ELEMS(flag_strings); i++) {
  63. if (flags & 1<<i) {
  64. if (!is_first)
  65. av_bprint_chars(pbuf, '+', 1);
  66. av_bprintf(pbuf, "%s", flag_strings[i]);
  67. is_first = 0;
  68. }
  69. }
  70. return pbuf->str;
  71. }
  72. typedef struct Command {
  73. int flags;
  74. char *target, *command, *arg;
  75. int index;
  76. } Command;
  77. typedef struct Interval {
  78. int64_t start_ts; ///< start timestamp expressed as microseconds units
  79. int64_t end_ts; ///< end timestamp expressed as microseconds units
  80. int index; ///< unique index for these interval commands
  81. Command *commands;
  82. int nb_commands;
  83. int enabled; ///< current time detected inside this interval
  84. } Interval;
  85. typedef struct SendCmdContext {
  86. const AVClass *class;
  87. Interval *intervals;
  88. int nb_intervals;
  89. char *commands_filename;
  90. char *commands_str;
  91. } SendCmdContext;
  92. #define OFFSET(x) offsetof(SendCmdContext, x)
  93. #define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_VIDEO_PARAM
  94. static const AVOption options[] = {
  95. { "commands", "set commands", OFFSET(commands_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
  96. { "c", "set commands", OFFSET(commands_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
  97. { "filename", "set commands file", OFFSET(commands_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
  98. { "f", "set commands file", OFFSET(commands_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
  99. { NULL }
  100. };
  101. #define SPACES " \f\t\n\r"
  102. static void skip_comments(const char **buf)
  103. {
  104. while (**buf) {
  105. /* skip leading spaces */
  106. *buf += strspn(*buf, SPACES);
  107. if (**buf != '#')
  108. break;
  109. (*buf)++;
  110. /* skip comment until the end of line */
  111. *buf += strcspn(*buf, "\n");
  112. if (**buf)
  113. (*buf)++;
  114. }
  115. }
  116. #define COMMAND_DELIMS " \f\t\n\r,;"
  117. static int parse_command(Command *cmd, int cmd_count, int interval_count,
  118. const char **buf, void *log_ctx)
  119. {
  120. int ret;
  121. memset(cmd, 0, sizeof(Command));
  122. cmd->index = cmd_count;
  123. /* format: [FLAGS] target command arg */
  124. *buf += strspn(*buf, SPACES);
  125. /* parse flags */
  126. if (**buf == '[') {
  127. (*buf)++; /* skip "[" */
  128. while (**buf) {
  129. int len = strcspn(*buf, "|+]");
  130. if (!strncmp(*buf, "enter", strlen("enter"))) cmd->flags |= COMMAND_FLAG_ENTER;
  131. else if (!strncmp(*buf, "leave", strlen("leave"))) cmd->flags |= COMMAND_FLAG_LEAVE;
  132. else if (!strncmp(*buf, "expr", strlen("expr"))) cmd->flags |= COMMAND_FLAG_EXPR;
  133. else {
  134. char flag_buf[64];
  135. av_strlcpy(flag_buf, *buf, sizeof(flag_buf));
  136. av_log(log_ctx, AV_LOG_ERROR,
  137. "Unknown flag '%s' in interval #%d, command #%d\n",
  138. flag_buf, interval_count, cmd_count);
  139. return AVERROR(EINVAL);
  140. }
  141. *buf += len;
  142. if (**buf == ']')
  143. break;
  144. if (!strspn(*buf, "+|")) {
  145. av_log(log_ctx, AV_LOG_ERROR,
  146. "Invalid flags char '%c' in interval #%d, command #%d\n",
  147. **buf, interval_count, cmd_count);
  148. return AVERROR(EINVAL);
  149. }
  150. if (**buf)
  151. (*buf)++;
  152. }
  153. if (**buf != ']') {
  154. av_log(log_ctx, AV_LOG_ERROR,
  155. "Missing flag terminator or extraneous data found at the end of flags "
  156. "in interval #%d, command #%d\n", interval_count, cmd_count);
  157. return AVERROR(EINVAL);
  158. }
  159. (*buf)++; /* skip "]" */
  160. } else {
  161. cmd->flags = COMMAND_FLAG_ENTER;
  162. }
  163. *buf += strspn(*buf, SPACES);
  164. cmd->target = av_get_token(buf, COMMAND_DELIMS);
  165. if (!cmd->target || !cmd->target[0]) {
  166. av_log(log_ctx, AV_LOG_ERROR,
  167. "No target specified in interval #%d, command #%d\n",
  168. interval_count, cmd_count);
  169. ret = AVERROR(EINVAL);
  170. goto fail;
  171. }
  172. *buf += strspn(*buf, SPACES);
  173. cmd->command = av_get_token(buf, COMMAND_DELIMS);
  174. if (!cmd->command || !cmd->command[0]) {
  175. av_log(log_ctx, AV_LOG_ERROR,
  176. "No command specified in interval #%d, command #%d\n",
  177. interval_count, cmd_count);
  178. ret = AVERROR(EINVAL);
  179. goto fail;
  180. }
  181. *buf += strspn(*buf, SPACES);
  182. cmd->arg = av_get_token(buf, COMMAND_DELIMS);
  183. return 1;
  184. fail:
  185. av_freep(&cmd->target);
  186. av_freep(&cmd->command);
  187. av_freep(&cmd->arg);
  188. return ret;
  189. }
  190. static int parse_commands(Command **cmds, int *nb_cmds, int interval_count,
  191. const char **buf, void *log_ctx)
  192. {
  193. int cmd_count = 0;
  194. int ret, n = 0;
  195. AVBPrint pbuf;
  196. *cmds = NULL;
  197. *nb_cmds = 0;
  198. while (**buf) {
  199. Command cmd;
  200. if ((ret = parse_command(&cmd, cmd_count, interval_count, buf, log_ctx)) < 0)
  201. return ret;
  202. cmd_count++;
  203. /* (re)allocate commands array if required */
  204. if (*nb_cmds == n) {
  205. n = FFMAX(16, 2*n); /* first allocation = 16, or double the number */
  206. *cmds = av_realloc_f(*cmds, n, 2*sizeof(Command));
  207. if (!*cmds) {
  208. av_log(log_ctx, AV_LOG_ERROR,
  209. "Could not (re)allocate command array\n");
  210. return AVERROR(ENOMEM);
  211. }
  212. }
  213. (*cmds)[(*nb_cmds)++] = cmd;
  214. *buf += strspn(*buf, SPACES);
  215. if (**buf && **buf != ';' && **buf != ',') {
  216. av_log(log_ctx, AV_LOG_ERROR,
  217. "Missing separator or extraneous data found at the end of "
  218. "interval #%d, in command #%d\n",
  219. interval_count, cmd_count);
  220. av_log(log_ctx, AV_LOG_ERROR,
  221. "Command was parsed as: flags:[%s] target:%s command:%s arg:%s\n",
  222. make_command_flags_str(&pbuf, cmd.flags), cmd.target, cmd.command, cmd.arg);
  223. return AVERROR(EINVAL);
  224. }
  225. if (**buf == ';')
  226. break;
  227. if (**buf == ',')
  228. (*buf)++;
  229. }
  230. return 0;
  231. }
  232. #define DELIMS " \f\t\n\r,;"
  233. static int parse_interval(Interval *interval, int interval_count,
  234. const char **buf, void *log_ctx)
  235. {
  236. char *intervalstr;
  237. int ret;
  238. *buf += strspn(*buf, SPACES);
  239. if (!**buf)
  240. return 0;
  241. /* reset data */
  242. memset(interval, 0, sizeof(Interval));
  243. interval->index = interval_count;
  244. /* format: INTERVAL COMMANDS */
  245. /* parse interval */
  246. intervalstr = av_get_token(buf, DELIMS);
  247. if (intervalstr && intervalstr[0]) {
  248. char *start, *end;
  249. start = av_strtok(intervalstr, "-", &end);
  250. if (!start) {
  251. ret = AVERROR(EINVAL);
  252. av_log(log_ctx, AV_LOG_ERROR,
  253. "Invalid interval specification '%s' in interval #%d\n",
  254. intervalstr, interval_count);
  255. goto end;
  256. }
  257. if ((ret = av_parse_time(&interval->start_ts, start, 1)) < 0) {
  258. av_log(log_ctx, AV_LOG_ERROR,
  259. "Invalid start time specification '%s' in interval #%d\n",
  260. start, interval_count);
  261. goto end;
  262. }
  263. if (end) {
  264. if ((ret = av_parse_time(&interval->end_ts, end, 1)) < 0) {
  265. av_log(log_ctx, AV_LOG_ERROR,
  266. "Invalid end time specification '%s' in interval #%d\n",
  267. end, interval_count);
  268. goto end;
  269. }
  270. } else {
  271. interval->end_ts = INT64_MAX;
  272. }
  273. if (interval->end_ts < interval->start_ts) {
  274. av_log(log_ctx, AV_LOG_ERROR,
  275. "Invalid end time '%s' in interval #%d: "
  276. "cannot be lesser than start time '%s'\n",
  277. end, interval_count, start);
  278. ret = AVERROR(EINVAL);
  279. goto end;
  280. }
  281. } else {
  282. av_log(log_ctx, AV_LOG_ERROR,
  283. "No interval specified for interval #%d\n", interval_count);
  284. ret = AVERROR(EINVAL);
  285. goto end;
  286. }
  287. /* parse commands */
  288. ret = parse_commands(&interval->commands, &interval->nb_commands,
  289. interval_count, buf, log_ctx);
  290. end:
  291. av_free(intervalstr);
  292. return ret;
  293. }
  294. static int parse_intervals(Interval **intervals, int *nb_intervals,
  295. const char *buf, void *log_ctx)
  296. {
  297. int interval_count = 0;
  298. int ret, n = 0;
  299. *intervals = NULL;
  300. *nb_intervals = 0;
  301. if (!buf)
  302. return 0;
  303. while (1) {
  304. Interval interval;
  305. skip_comments(&buf);
  306. if (!(*buf))
  307. break;
  308. if ((ret = parse_interval(&interval, interval_count, &buf, log_ctx)) < 0)
  309. return ret;
  310. buf += strspn(buf, SPACES);
  311. if (*buf) {
  312. if (*buf != ';') {
  313. av_log(log_ctx, AV_LOG_ERROR,
  314. "Missing terminator or extraneous data found at the end of interval #%d\n",
  315. interval_count);
  316. return AVERROR(EINVAL);
  317. }
  318. buf++; /* skip ';' */
  319. }
  320. interval_count++;
  321. /* (re)allocate commands array if required */
  322. if (*nb_intervals == n) {
  323. n = FFMAX(16, 2*n); /* first allocation = 16, or double the number */
  324. *intervals = av_realloc_f(*intervals, n, 2*sizeof(Interval));
  325. if (!*intervals) {
  326. av_log(log_ctx, AV_LOG_ERROR,
  327. "Could not (re)allocate intervals array\n");
  328. return AVERROR(ENOMEM);
  329. }
  330. }
  331. (*intervals)[(*nb_intervals)++] = interval;
  332. }
  333. return 0;
  334. }
  335. static int cmp_intervals(const void *a, const void *b)
  336. {
  337. const Interval *i1 = a;
  338. const Interval *i2 = b;
  339. return 2 * FFDIFFSIGN(i1->start_ts, i2->start_ts) + FFDIFFSIGN(i1->index, i2->index);
  340. }
  341. static av_cold int init(AVFilterContext *ctx)
  342. {
  343. SendCmdContext *s = ctx->priv;
  344. int ret, i, j;
  345. if ((!!s->commands_filename + !!s->commands_str) != 1) {
  346. av_log(ctx, AV_LOG_ERROR,
  347. "One and only one of the filename or commands options must be specified\n");
  348. return AVERROR(EINVAL);
  349. }
  350. if (s->commands_filename) {
  351. uint8_t *file_buf, *buf;
  352. size_t file_bufsize;
  353. ret = av_file_map(s->commands_filename,
  354. &file_buf, &file_bufsize, 0, ctx);
  355. if (ret < 0)
  356. return ret;
  357. /* create a 0-terminated string based on the read file */
  358. buf = av_malloc(file_bufsize + 1);
  359. if (!buf) {
  360. av_file_unmap(file_buf, file_bufsize);
  361. return AVERROR(ENOMEM);
  362. }
  363. memcpy(buf, file_buf, file_bufsize);
  364. buf[file_bufsize] = 0;
  365. av_file_unmap(file_buf, file_bufsize);
  366. s->commands_str = buf;
  367. }
  368. if ((ret = parse_intervals(&s->intervals, &s->nb_intervals,
  369. s->commands_str, ctx)) < 0)
  370. return ret;
  371. if (s->nb_intervals == 0) {
  372. av_log(ctx, AV_LOG_ERROR, "No commands were specified\n");
  373. return AVERROR(EINVAL);
  374. }
  375. qsort(s->intervals, s->nb_intervals, sizeof(Interval), cmp_intervals);
  376. av_log(ctx, AV_LOG_DEBUG, "Parsed commands:\n");
  377. for (i = 0; i < s->nb_intervals; i++) {
  378. AVBPrint pbuf;
  379. Interval *interval = &s->intervals[i];
  380. av_log(ctx, AV_LOG_VERBOSE, "start_time:%f end_time:%f index:%d\n",
  381. (double)interval->start_ts/1000000, (double)interval->end_ts/1000000, interval->index);
  382. for (j = 0; j < interval->nb_commands; j++) {
  383. Command *cmd = &interval->commands[j];
  384. av_log(ctx, AV_LOG_VERBOSE,
  385. " [%s] target:%s command:%s arg:%s index:%d\n",
  386. make_command_flags_str(&pbuf, cmd->flags), cmd->target, cmd->command, cmd->arg, cmd->index);
  387. }
  388. }
  389. return 0;
  390. }
  391. static av_cold void uninit(AVFilterContext *ctx)
  392. {
  393. SendCmdContext *s = ctx->priv;
  394. int i, j;
  395. for (i = 0; i < s->nb_intervals; i++) {
  396. Interval *interval = &s->intervals[i];
  397. for (j = 0; j < interval->nb_commands; j++) {
  398. Command *cmd = &interval->commands[j];
  399. av_freep(&cmd->target);
  400. av_freep(&cmd->command);
  401. av_freep(&cmd->arg);
  402. }
  403. av_freep(&interval->commands);
  404. }
  405. av_freep(&s->intervals);
  406. }
  407. static int filter_frame(AVFilterLink *inlink, AVFrame *ref)
  408. {
  409. AVFilterContext *ctx = inlink->dst;
  410. SendCmdContext *s = ctx->priv;
  411. int64_t ts;
  412. int i, j, ret;
  413. if (ref->pts == AV_NOPTS_VALUE)
  414. goto end;
  415. ts = av_rescale_q(ref->pts, inlink->time_base, AV_TIME_BASE_Q);
  416. #define WITHIN_INTERVAL(ts, start_ts, end_ts) ((ts) >= (start_ts) && (ts) < (end_ts))
  417. for (i = 0; i < s->nb_intervals; i++) {
  418. Interval *interval = &s->intervals[i];
  419. int flags = 0;
  420. if (!interval->enabled && WITHIN_INTERVAL(ts, interval->start_ts, interval->end_ts)) {
  421. flags += COMMAND_FLAG_ENTER;
  422. interval->enabled = 1;
  423. }
  424. if (interval->enabled && !WITHIN_INTERVAL(ts, interval->start_ts, interval->end_ts)) {
  425. flags += COMMAND_FLAG_LEAVE;
  426. interval->enabled = 0;
  427. }
  428. if (interval->enabled)
  429. flags += COMMAND_FLAG_EXPR;
  430. if (flags) {
  431. AVBPrint pbuf;
  432. av_log(ctx, AV_LOG_VERBOSE,
  433. "[%s] interval #%d start_ts:%f end_ts:%f ts:%f\n",
  434. make_command_flags_str(&pbuf, flags), interval->index,
  435. (double)interval->start_ts/1000000, (double)interval->end_ts/1000000,
  436. (double)ts/1000000);
  437. for (j = 0; flags && j < interval->nb_commands; j++) {
  438. Command *cmd = &interval->commands[j];
  439. char *cmd_arg = cmd->arg;
  440. char buf[1024];
  441. if (cmd->flags & flags) {
  442. if (cmd->flags & COMMAND_FLAG_EXPR) {
  443. double var_values[VAR_VARS_NB], res;
  444. double start = TS2T(interval->start_ts, AV_TIME_BASE_Q);
  445. double end = TS2T(interval->end_ts, AV_TIME_BASE_Q);
  446. double current = TS2T(ref->pts, inlink->time_base);
  447. var_values[VAR_N] = inlink->frame_count_in;
  448. var_values[VAR_POS] = ref->pkt_pos == -1 ? NAN : ref->pkt_pos;
  449. var_values[VAR_PTS] = TS2D(ref->pts);
  450. var_values[VAR_T] = current;
  451. var_values[VAR_TS] = start;
  452. var_values[VAR_TE] = end;
  453. var_values[VAR_TI] = (current - start) / (end - start);
  454. if ((ret = av_expr_parse_and_eval(&res, cmd->arg, var_names, var_values,
  455. NULL, NULL, NULL, NULL, NULL, 0, NULL)) < 0) {
  456. av_log(ctx, AV_LOG_ERROR, "Invalid expression '%s' for command argument.\n", cmd->arg);
  457. av_frame_free(&ref);
  458. return AVERROR(EINVAL);
  459. }
  460. cmd_arg = av_asprintf("%g", res);
  461. if (!cmd_arg) {
  462. av_frame_free(&ref);
  463. return AVERROR(ENOMEM);
  464. }
  465. }
  466. av_log(ctx, AV_LOG_VERBOSE,
  467. "Processing command #%d target:%s command:%s arg:%s\n",
  468. cmd->index, cmd->target, cmd->command, cmd_arg);
  469. ret = avfilter_graph_send_command(inlink->graph,
  470. cmd->target, cmd->command, cmd_arg,
  471. buf, sizeof(buf),
  472. AVFILTER_CMD_FLAG_ONE);
  473. av_log(ctx, AV_LOG_VERBOSE,
  474. "Command reply for command #%d: ret:%s res:%s\n",
  475. cmd->index, av_err2str(ret), buf);
  476. if (cmd->flags & COMMAND_FLAG_EXPR)
  477. av_freep(&cmd_arg);
  478. }
  479. }
  480. }
  481. }
  482. end:
  483. switch (inlink->type) {
  484. case AVMEDIA_TYPE_VIDEO:
  485. case AVMEDIA_TYPE_AUDIO:
  486. return ff_filter_frame(inlink->dst->outputs[0], ref);
  487. }
  488. return AVERROR(ENOSYS);
  489. }
  490. #if CONFIG_SENDCMD_FILTER
  491. #define sendcmd_options options
  492. AVFILTER_DEFINE_CLASS(sendcmd);
  493. static const AVFilterPad sendcmd_inputs[] = {
  494. {
  495. .name = "default",
  496. .type = AVMEDIA_TYPE_VIDEO,
  497. .filter_frame = filter_frame,
  498. },
  499. { NULL }
  500. };
  501. static const AVFilterPad sendcmd_outputs[] = {
  502. {
  503. .name = "default",
  504. .type = AVMEDIA_TYPE_VIDEO,
  505. },
  506. { NULL }
  507. };
  508. AVFilter ff_vf_sendcmd = {
  509. .name = "sendcmd",
  510. .description = NULL_IF_CONFIG_SMALL("Send commands to filters."),
  511. .init = init,
  512. .uninit = uninit,
  513. .priv_size = sizeof(SendCmdContext),
  514. .inputs = sendcmd_inputs,
  515. .outputs = sendcmd_outputs,
  516. .priv_class = &sendcmd_class,
  517. };
  518. #endif
  519. #if CONFIG_ASENDCMD_FILTER
  520. #define asendcmd_options options
  521. AVFILTER_DEFINE_CLASS(asendcmd);
  522. static const AVFilterPad asendcmd_inputs[] = {
  523. {
  524. .name = "default",
  525. .type = AVMEDIA_TYPE_AUDIO,
  526. .filter_frame = filter_frame,
  527. },
  528. { NULL }
  529. };
  530. static const AVFilterPad asendcmd_outputs[] = {
  531. {
  532. .name = "default",
  533. .type = AVMEDIA_TYPE_AUDIO,
  534. },
  535. { NULL }
  536. };
  537. AVFilter ff_af_asendcmd = {
  538. .name = "asendcmd",
  539. .description = NULL_IF_CONFIG_SMALL("Send commands to filters."),
  540. .init = init,
  541. .uninit = uninit,
  542. .priv_size = sizeof(SendCmdContext),
  543. .inputs = asendcmd_inputs,
  544. .outputs = asendcmd_outputs,
  545. .priv_class = &asendcmd_class,
  546. };
  547. #endif