Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

  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.  
  26. #include "libavutil/avstring.h"
  27. #include "libavutil/bprint.h"
  28. #include "libavutil/file.h"
  29. #include "libavutil/opt.h"
  30. #include "libavutil/parseutils.h"
  31. #include "avfilter.h"
  32. #include "internal.h"
  33. #include "avfiltergraph.h"
  34. #include "audio.h"
  35. #include "video.h"
  36.  
  37. #define COMMAND_FLAG_ENTER 1
  38. #define COMMAND_FLAG_LEAVE 2
  39.  
  40. static inline char *make_command_flags_str(AVBPrint *pbuf, int flags)
  41. {
  42.     static const char * const flag_strings[] = { "enter", "leave" };
  43.     int i, is_first = 1;
  44.  
  45.     av_bprint_init(pbuf, 0, AV_BPRINT_SIZE_AUTOMATIC);
  46.     for (i = 0; i < FF_ARRAY_ELEMS(flag_strings); i++) {
  47.         if (flags & 1<<i) {
  48.             if (!is_first)
  49.                 av_bprint_chars(pbuf, '+', 1);
  50.             av_bprintf(pbuf, "%s", flag_strings[i]);
  51.             is_first = 0;
  52.         }
  53.     }
  54.  
  55.     return pbuf->str;
  56. }
  57.  
  58. typedef struct {
  59.     int flags;
  60.     char *target, *command, *arg;
  61.     int index;
  62. } Command;
  63.  
  64. typedef struct {
  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
  68.     Command *commands;
  69.     int   nb_commands;
  70.     int enabled;               ///< current time detected inside this interval
  71. } Interval;
  72.  
  73. typedef struct {
  74.     const AVClass *class;
  75.     Interval *intervals;
  76.     int   nb_intervals;
  77.  
  78.     char *commands_filename;
  79.     char *commands_str;
  80. } SendCmdContext;
  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
  84. static const AVOption options[] = {
  85.     { "commands", "set commands", OFFSET(commands_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
  86.     { "c",        "set commands", OFFSET(commands_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
  87.     { "filename", "set commands file",  OFFSET(commands_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
  88.     { "f",        "set commands file",  OFFSET(commands_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
  89.     { NULL }
  90. };
  91.  
  92. #define SPACES " \f\t\n\r"
  93.  
  94. static void skip_comments(const char **buf)
  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.  
  113. static int parse_command(Command *cmd, int cmd_count, int interval_count,
  114.                          const char **buf, void *log_ctx)
  115. {
  116.     int ret;
  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.  
  131.             if      (!strncmp(*buf, "enter", strlen("enter"))) cmd->flags |= COMMAND_FLAG_ENTER;
  132.             else if (!strncmp(*buf, "leave", strlen("leave"))) cmd->flags |= COMMAND_FLAG_LEAVE;
  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.  
  154.         if (**buf != ']') {
  155.             av_log(log_ctx, AV_LOG_ERROR,
  156.                    "Missing flag terminator or extraneous data found at the end of flags "
  157.                    "in interval #%d, command #%d\n", interval_count, cmd_count);
  158.             return AVERROR(EINVAL);
  159.         }
  160.         (*buf)++; /* skip "]" */
  161.     } else {
  162.         cmd->flags = COMMAND_FLAG_ENTER;
  163.     }
  164.  
  165.     *buf += strspn(*buf, SPACES);
  166.     cmd->target = av_get_token(buf, COMMAND_DELIMS);
  167.     if (!cmd->target || !cmd->target[0]) {
  168.         av_log(log_ctx, AV_LOG_ERROR,
  169.                "No target specified in interval #%d, command #%d\n",
  170.                interval_count, cmd_count);
  171.         ret = AVERROR(EINVAL);
  172.         goto fail;
  173.     }
  174.  
  175.     *buf += strspn(*buf, SPACES);
  176.     cmd->command = av_get_token(buf, COMMAND_DELIMS);
  177.     if (!cmd->command || !cmd->command[0]) {
  178.         av_log(log_ctx, AV_LOG_ERROR,
  179.                "No command specified in interval #%d, command #%d\n",
  180.                interval_count, cmd_count);
  181.         ret = AVERROR(EINVAL);
  182.         goto fail;
  183.     }
  184.  
  185.     *buf += strspn(*buf, SPACES);
  186.     cmd->arg = av_get_token(buf, COMMAND_DELIMS);
  187.  
  188.     return 1;
  189.  
  190. fail:
  191.     av_freep(&cmd->target);
  192.     av_freep(&cmd->command);
  193.     av_freep(&cmd->arg);
  194.     return ret;
  195. }
  196.  
  197. static int parse_commands(Command **cmds, int *nb_cmds, int interval_count,
  198.                           const char **buf, void *log_ctx)
  199. {
  200.     int cmd_count = 0;
  201.     int ret, n = 0;
  202.     AVBPrint pbuf;
  203.  
  204.     *cmds = NULL;
  205.     *nb_cmds = 0;
  206.  
  207.     while (**buf) {
  208.         Command cmd;
  209.  
  210.         if ((ret = parse_command(&cmd, cmd_count, interval_count, buf, log_ctx)) < 0)
  211.             return ret;
  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 */
  217.             *cmds = av_realloc_f(*cmds, n, 2*sizeof(Command));
  218.             if (!*cmds) {
  219.                 av_log(log_ctx, AV_LOG_ERROR,
  220.                        "Could not (re)allocate command array\n");
  221.                 return AVERROR(ENOMEM);
  222.             }
  223.         }
  224.  
  225.         (*cmds)[(*nb_cmds)++] = cmd;
  226.  
  227.         *buf += strspn(*buf, SPACES);
  228.         if (**buf && **buf != ';' && **buf != ',') {
  229.             av_log(log_ctx, AV_LOG_ERROR,
  230.                    "Missing separator or extraneous data found at the end of "
  231.                    "interval #%d, in command #%d\n",
  232.                    interval_count, cmd_count);
  233.             av_log(log_ctx, AV_LOG_ERROR,
  234.                    "Command was parsed as: flags:[%s] target:%s command:%s arg:%s\n",
  235.                    make_command_flags_str(&pbuf, cmd.flags), cmd.target, cmd.command, cmd.arg);
  236.             return AVERROR(EINVAL);
  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.  
  249. static int parse_interval(Interval *interval, int interval_count,
  250.                           const char **buf, void *log_ctx)
  251. {
  252.     char *intervalstr;
  253.     int ret;
  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 */
  266.     intervalstr = av_get_token(buf, DELIMS);
  267.     if (intervalstr && intervalstr[0]) {
  268.         char *start, *end;
  269.  
  270.         start = av_strtok(intervalstr, "-", &end);
  271.         if ((ret = av_parse_time(&interval->start_ts, start, 1)) < 0) {
  272.             av_log(log_ctx, AV_LOG_ERROR,
  273.                    "Invalid start time specification '%s' in interval #%d\n",
  274.                    start, interval_count);
  275.             goto end;
  276.         }
  277.  
  278.         if (end) {
  279.             if ((ret = av_parse_time(&interval->end_ts, end, 1)) < 0) {
  280.                 av_log(log_ctx, AV_LOG_ERROR,
  281.                        "Invalid end time specification '%s' in interval #%d\n",
  282.                        end, interval_count);
  283.                 goto end;
  284.             }
  285.         } else {
  286.             interval->end_ts = INT64_MAX;
  287.         }
  288.         if (interval->end_ts < interval->start_ts) {
  289.             av_log(log_ctx, AV_LOG_ERROR,
  290.                    "Invalid end time '%s' in interval #%d: "
  291.                    "cannot be lesser than start time '%s'\n",
  292.                    end, interval_count, start);
  293.             ret = AVERROR(EINVAL);
  294.             goto end;
  295.         }
  296.     } else {
  297.         av_log(log_ctx, AV_LOG_ERROR,
  298.                "No interval specified for interval #%d\n", interval_count);
  299.         ret = AVERROR(EINVAL);
  300.         goto end;
  301.     }
  302.  
  303.     /* parse commands */
  304.     ret = parse_commands(&interval->commands, &interval->nb_commands,
  305.                          interval_count, buf, log_ctx);
  306.  
  307. end:
  308.     av_free(intervalstr);
  309.     return ret;
  310. }
  311.  
  312. static int parse_intervals(Interval **intervals, int *nb_intervals,
  313.                            const char *buf, void *log_ctx)
  314. {
  315.     int interval_count = 0;
  316.     int ret, n = 0;
  317.  
  318.     *intervals = NULL;
  319.     *nb_intervals = 0;
  320.  
  321.     while (1) {
  322.         Interval interval;
  323.  
  324.         skip_comments(&buf);
  325.         if (!(*buf))
  326.             break;
  327.  
  328.         if ((ret = parse_interval(&interval, interval_count, &buf, log_ctx)) < 0)
  329.             return ret;
  330.  
  331.         buf += strspn(buf, SPACES);
  332.         if (*buf) {
  333.             if (*buf != ';') {
  334.                 av_log(log_ctx, AV_LOG_ERROR,
  335.                        "Missing terminator or extraneous data found at the end of interval #%d\n",
  336.                        interval_count);
  337.                 return AVERROR(EINVAL);
  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 */
  346.             *intervals = av_realloc_f(*intervals, n, 2*sizeof(Interval));
  347.             if (!*intervals) {
  348.                 av_log(log_ctx, AV_LOG_ERROR,
  349.                        "Could not (re)allocate intervals array\n");
  350.                 return AVERROR(ENOMEM);
  351.             }
  352.         }
  353.  
  354.         (*intervals)[(*nb_intervals)++] = interval;
  355.     }
  356.  
  357.     return 0;
  358. }
  359.  
  360. static int cmp_intervals(const void *a, const void *b)
  361. {
  362.     const Interval *i1 = a;
  363.     const Interval *i2 = b;
  364.     int64_t ts_diff = i1->start_ts - i2->start_ts;
  365.     int ret;
  366.  
  367.     ret = ts_diff > 0 ? 1 : ts_diff < 0 ? -1 : 0;
  368.     return ret == 0 ? i1->index - i2->index : ret;
  369. }
  370.  
  371. static av_cold int init(AVFilterContext *ctx)
  372. {
  373.     SendCmdContext *sendcmd = ctx->priv;
  374.     int ret, i, j;
  375.  
  376.     if (sendcmd->commands_filename && sendcmd->commands_str) {
  377.         av_log(ctx, AV_LOG_ERROR,
  378.                "Only one of the filename or commands options must be specified\n");
  379.         return AVERROR(EINVAL);
  380.     }
  381.  
  382.     if (sendcmd->commands_filename) {
  383.         uint8_t *file_buf, *buf;
  384.         size_t file_bufsize;
  385.         ret = av_file_map(sendcmd->commands_filename,
  386.                           &file_buf, &file_bufsize, 0, ctx);
  387.         if (ret < 0)
  388.             return ret;
  389.  
  390.         /* create a 0-terminated string based on the read file */
  391.         buf = av_malloc(file_bufsize + 1);
  392.         if (!buf) {
  393.             av_file_unmap(file_buf, file_bufsize);
  394.             return AVERROR(ENOMEM);
  395.         }
  396.         memcpy(buf, file_buf, file_bufsize);
  397.         buf[file_bufsize] = 0;
  398.         av_file_unmap(file_buf, file_bufsize);
  399.         sendcmd->commands_str = buf;
  400.     }
  401.  
  402.     if ((ret = parse_intervals(&sendcmd->intervals, &sendcmd->nb_intervals,
  403.                                sendcmd->commands_str, ctx)) < 0)
  404.         return ret;
  405.  
  406.     qsort(sendcmd->intervals, sendcmd->nb_intervals, sizeof(Interval), cmp_intervals);
  407.  
  408.     av_log(ctx, AV_LOG_DEBUG, "Parsed commands:\n");
  409.     for (i = 0; i < sendcmd->nb_intervals; i++) {
  410.         AVBPrint pbuf;
  411.         Interval *interval = &sendcmd->intervals[i];
  412.         av_log(ctx, AV_LOG_VERBOSE, "start_time:%f end_time:%f index:%d\n",
  413.                (double)interval->start_ts/1000000, (double)interval->end_ts/1000000, interval->index);
  414.         for (j = 0; j < interval->nb_commands; j++) {
  415.             Command *cmd = &interval->commands[j];
  416.             av_log(ctx, AV_LOG_VERBOSE,
  417.                    "    [%s] target:%s command:%s arg:%s index:%d\n",
  418.                    make_command_flags_str(&pbuf, cmd->flags), cmd->target, cmd->command, cmd->arg, cmd->index);
  419.         }
  420.     }
  421.  
  422.     return 0;
  423. }
  424.  
  425. static av_cold void uninit(AVFilterContext *ctx)
  426. {
  427.     SendCmdContext *sendcmd = ctx->priv;
  428.     int i, j;
  429.  
  430.     for (i = 0; i < sendcmd->nb_intervals; i++) {
  431.         Interval *interval = &sendcmd->intervals[i];
  432.         for (j = 0; j < interval->nb_commands; j++) {
  433.             Command *cmd = &interval->commands[j];
  434.             av_free(cmd->target);
  435.             av_free(cmd->command);
  436.             av_free(cmd->arg);
  437.         }
  438.         av_free(interval->commands);
  439.     }
  440.     av_freep(&sendcmd->intervals);
  441. }
  442.  
  443. static int filter_frame(AVFilterLink *inlink, AVFrame *ref)
  444. {
  445.     AVFilterContext *ctx = inlink->dst;
  446.     SendCmdContext *sendcmd = ctx->priv;
  447.     int64_t ts;
  448.     int i, j, ret;
  449.  
  450.     if (ref->pts == AV_NOPTS_VALUE)
  451.         goto end;
  452.  
  453.     ts = av_rescale_q(ref->pts, inlink->time_base, AV_TIME_BASE_Q);
  454.  
  455. #define WITHIN_INTERVAL(ts, start_ts, end_ts) ((ts) >= (start_ts) && (ts) < (end_ts))
  456.  
  457.     for (i = 0; i < sendcmd->nb_intervals; i++) {
  458.         Interval *interval = &sendcmd->intervals[i];
  459.         int flags = 0;
  460.  
  461.         if (!interval->enabled && WITHIN_INTERVAL(ts, interval->start_ts, interval->end_ts)) {
  462.             flags += COMMAND_FLAG_ENTER;
  463.             interval->enabled = 1;
  464.         }
  465.         if (interval->enabled && !WITHIN_INTERVAL(ts, interval->start_ts, interval->end_ts)) {
  466.             flags += COMMAND_FLAG_LEAVE;
  467.             interval->enabled = 0;
  468.         }
  469.  
  470.         if (flags) {
  471.             AVBPrint pbuf;
  472.             av_log(ctx, AV_LOG_VERBOSE,
  473.                    "[%s] interval #%d start_ts:%f end_ts:%f ts:%f\n",
  474.                    make_command_flags_str(&pbuf, flags), interval->index,
  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++) {
  479.                 Command *cmd = &interval->commands[j];
  480.                 char buf[1024];
  481.  
  482.                 if (cmd->flags & flags) {
  483.                     av_log(ctx, AV_LOG_VERBOSE,
  484.                            "Processing command #%d target:%s command:%s arg:%s\n",
  485.                            cmd->index, cmd->target, cmd->command, cmd->arg);
  486.                     ret = avfilter_graph_send_command(inlink->graph,
  487.                                                       cmd->target, cmd->command, cmd->arg,
  488.                                                       buf, sizeof(buf),
  489.                                                       AVFILTER_CMD_FLAG_ONE);
  490.                     av_log(ctx, AV_LOG_VERBOSE,
  491.                            "Command reply for command #%d: ret:%s res:%s\n",
  492.                            cmd->index, av_err2str(ret), buf);
  493.                 }
  494.             }
  495.         }
  496.     }
  497.  
  498. end:
  499.     switch (inlink->type) {
  500.     case AVMEDIA_TYPE_VIDEO:
  501.     case AVMEDIA_TYPE_AUDIO:
  502.         return ff_filter_frame(inlink->dst->outputs[0], ref);
  503.     }
  504.  
  505.     return AVERROR(ENOSYS);
  506. }
  507.  
  508. #if CONFIG_SENDCMD_FILTER
  509.  
  510. #define sendcmd_options options
  511. AVFILTER_DEFINE_CLASS(sendcmd);
  512.  
  513. static const AVFilterPad sendcmd_inputs[] = {
  514.     {
  515.         .name         = "default",
  516.         .type         = AVMEDIA_TYPE_VIDEO,
  517.         .filter_frame = filter_frame,
  518.     },
  519.     { NULL }
  520. };
  521.  
  522. static const AVFilterPad sendcmd_outputs[] = {
  523.     {
  524.         .name = "default",
  525.         .type = AVMEDIA_TYPE_VIDEO,
  526.     },
  527.     { NULL }
  528. };
  529.  
  530. AVFilter avfilter_vf_sendcmd = {
  531.     .name        = "sendcmd",
  532.     .description = NULL_IF_CONFIG_SMALL("Send commands to filters."),
  533.     .init        = init,
  534.     .uninit      = uninit,
  535.     .priv_size   = sizeof(SendCmdContext),
  536.     .inputs      = sendcmd_inputs,
  537.     .outputs     = sendcmd_outputs,
  538.     .priv_class  = &sendcmd_class,
  539. };
  540.  
  541. #endif
  542.  
  543. #if CONFIG_ASENDCMD_FILTER
  544.  
  545. #define asendcmd_options options
  546. AVFILTER_DEFINE_CLASS(asendcmd);
  547.  
  548. static const AVFilterPad asendcmd_inputs[] = {
  549.     {
  550.         .name         = "default",
  551.         .type         = AVMEDIA_TYPE_AUDIO,
  552.         .filter_frame = filter_frame,
  553.     },
  554.     { NULL }
  555. };
  556.  
  557. static const AVFilterPad asendcmd_outputs[] = {
  558.     {
  559.         .name = "default",
  560.         .type = AVMEDIA_TYPE_AUDIO,
  561.     },
  562.     { NULL }
  563. };
  564.  
  565. AVFilter avfilter_af_asendcmd = {
  566.     .name        = "asendcmd",
  567.     .description = NULL_IF_CONFIG_SMALL("Send commands to filters."),
  568.     .init        = init,
  569.     .uninit      = uninit,
  570.     .priv_size   = sizeof(SendCmdContext),
  571.     .inputs      = asendcmd_inputs,
  572.     .outputs     = asendcmd_outputs,
  573.     .priv_class  = &asendcmd_class,
  574. };
  575.  
  576. #endif
  577.