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.

635 lines
19KB

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