diff --git a/ChangeLog b/ChangeLog index 0277d78c2e..ba95e896cf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,30 @@ +2007-08-02 Wim Taymans + + * gst/realmedia/.cvsignore: + Add test to ignore. + + * gst/realmedia/Makefile.am: + * gst/realmedia/asmrules.c: (gst_asm_node_new), + (gst_asm_node_free), (gst_asm_operator_eval), + (gst_asm_node_evaluate), (gst_asm_scan_new), (gst_asm_scan_free), + (gst_asm_scan_string), (gst_asm_scan_number), + (gst_asm_scan_identifier), (gst_asm_scan_print_token), + (gst_asm_scan_next_token), (gst_asm_rule_new), (gst_asm_rule_free), + (gst_asm_rule_add_property), (gst_asm_scan_parse_operand), + (gst_asm_scan_parse_expression), (gst_asm_scan_parse_condition), + (gst_asm_scan_parse_property), (gst_asm_scan_parse_rule), + (gst_asm_rule_evaluate), (gst_asm_rule_book_new), + (gst_asm_rule_book_n_rules), (gst_asm_rule_book_free), + (gst_asm_rule_book_match), (main): + * gst/realmedia/asmrules.h: + Added asembler rule book parser and evaluator. + + * gst/realmedia/rtspreal.c: (rtsp_ext_real_parse_sdp), + (rtsp_ext_real_stream_select), (gst_rtsp_real_plugin_init): + * gst/realmedia/rtspreal.h: + Keep per stream config info. + Parse and evaluate asm rule books for stream selection. + 2007-07-31 Stefan Kost * gst/realmedia/rtspreal.c: diff --git a/gst/realmedia/.gitignore b/gst/realmedia/.gitignore new file mode 100644 index 0000000000..d43f944ce3 --- /dev/null +++ b/gst/realmedia/.gitignore @@ -0,0 +1 @@ +asmrules diff --git a/gst/realmedia/Makefile.am b/gst/realmedia/Makefile.am index 5017603f9b..cf7be04ff5 100644 --- a/gst/realmedia/Makefile.am +++ b/gst/realmedia/Makefile.am @@ -1,10 +1,17 @@ plugin_LTLIBRARIES = libgstrmdemux.la -libgstrmdemux_la_SOURCES = rademux.c rmdemux.c rmutils.c rdtdepay.c rtspreal.c realhash.c +libgstrmdemux_la_SOURCES = rademux.c rmdemux.c \ + rmutils.c rdtdepay.c \ + rtspreal.c realhash.c asmrules.c libgstrmdemux_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) libgstrmdemux_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(GST_LIBS)\ -lgstrtsp-@GST_MAJORMINOR@ -lgstsdp-@GST_MAJORMINOR@ libgstrmdemux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -noinst_HEADERS = rademux.h rmdemux.h rmutils.h rdtdepay.h rtspreal.h realhash.h +noinst_HEADERS = rademux.h rmdemux.h rmutils.h rdtdepay.h rtspreal.h realhash.h asmrules.h + +noinst_PROGRAMS = asmrules +asmrules_CFLAGS = $(GST_CFLAGS) -DTEST +asmrules_LDADD = $(GST_LIBS) $(LIBM) + diff --git a/gst/realmedia/asmrules.c b/gst/realmedia/asmrules.c new file mode 100644 index 0000000000..2508f1578e --- /dev/null +++ b/gst/realmedia/asmrules.c @@ -0,0 +1,702 @@ +/* GStreamer + * Copyright (C) <2007> Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#include "asmrules.h" + +#define MAX_RULE_LENGTH 2048 + +/* define to enable some more debug */ +#undef DEBUG + +static GstASMNode * +gst_asm_node_new (void) +{ + GstASMNode *node; + + node = g_new0 (GstASMNode, 1); + node->type = GST_ASM_NODE_UNKNOWN; + + return node; +} + +static void +gst_asm_node_free (GstASMNode * node) +{ + if (node->left) + gst_asm_node_free (node->left); + if (node->right) + gst_asm_node_free (node->right); + if (node->type == GST_ASM_NODE_VARIABLE && node->data.varname) + g_free (node->data.varname); + g_free (node); +} + +static gfloat +gst_asm_operator_eval (GstASMOp optype, gfloat left, gfloat right) +{ + gfloat result = 0.0; + + switch (optype) { + case GST_ASM_OP_GREATER: + result = (gfloat) (left > right); + break; + case GST_ASM_OP_LESS: + result = (gfloat) (left < right); + break; + case GST_ASM_OP_GREATEREQUAL: + result = (gfloat) (left >= right); + break; + case GST_ASM_OP_LESSEQUAL: + result = (gfloat) (left <= right); + break; + case GST_ASM_OP_EQUAL: + result = (gfloat) (left == right); + break; + case GST_ASM_OP_NOTEQUAL: + result = (gfloat) (left != right); + break; + case GST_ASM_OP_AND: + result = (gfloat) (left && right); + break; + case GST_ASM_OP_OR: + result = (gfloat) (left || right); + break; + default: + break; + } + return result; +} + +static gfloat +gst_asm_node_evaluate (GstASMNode * node, GHashTable * vars) +{ + gfloat result = 0.0; + + if (node == NULL) + return 0.0; + + switch (node->type) { + case GST_ASM_NODE_VARIABLE: + { + gchar *val; + + val = g_hash_table_lookup (vars, node->data.varname); + if (val) + result = (gfloat) atof (val); + break; + } + case GST_ASM_NODE_INTEGER: + result = (gfloat) node->data.intval; + break; + case GST_ASM_NODE_FLOAT: + result = node->data.floatval; + break; + case GST_ASM_NODE_OPERATOR: + { + gfloat left, right; + + left = gst_asm_node_evaluate (node->left, vars); + right = gst_asm_node_evaluate (node->right, vars); + + result = gst_asm_operator_eval (node->data.optype, left, right); + break; + } + default: + break; + } + return result; +} + +#define IS_SPACE(p) (((p) == ' ') || ((p) == '\n') || \ + ((p) == '\r') || ((p) == '\t')) +#define IS_RULE_DELIM(p) (((p) == ',') || ((p) == ';')) +#define IS_OPERATOR(p) (((p) == '>') || ((p) == '<') || \ + ((p) == '=') || ((p) == '!') || \ + ((p) == '&') || ((p) == '|')) +#define IS_NUMBER(p) ((((p) >= '0') && ((p) <= '9')) || ((p) == '.')) +#define IS_CHAR(p) (!IS_OPERATOR(ch) && !IS_RULE_DELIM(ch) && (ch != '\0')) + +#define IS_OP_TOKEN(t) (((t) == GST_ASM_TOKEN_AND) || ((t) == GST_ASM_TOKEN_OR)) +#define IS_COND_TOKEN(t) (((t) == GST_ASM_TOKEN_LESS) || ((t) == GST_ASM_TOKEN_LESSEQUAL) || \ + ((t) == GST_ASM_TOKEN_GREATER) || ((t) == GST_ASM_TOKEN_GREATEREQUAL) || \ + ((t) == GST_ASM_TOKEN_EQUAL) || ((t) == GST_ASM_TOKEN_NOTEQUAL)) + +typedef struct +{ + const gchar *buffer; + gint pos; + gchar ch; + + GstASMToken token; + gchar val[MAX_RULE_LENGTH]; +} GstASMScan; + +#define NEXT_CHAR(scan) ((scan)->ch = (scan)->buffer[(scan)->pos++]) +#define THIS_CHAR(scan) ((scan)->ch) + +static GstASMScan * +gst_asm_scan_new (const gchar * buffer) +{ + GstASMScan *scan; + + scan = g_new0 (GstASMScan, 1); + scan->buffer = buffer; + NEXT_CHAR (scan); + + return scan; +} + +static void +gst_asm_scan_free (GstASMScan * scan) +{ + g_free (scan); +} + +static void +gst_asm_scan_string (GstASMScan * scan, gchar delim) +{ + gchar ch; + gint i = 0; + + ch = THIS_CHAR (scan); + while ((ch != delim) && (ch != '\0')) { + if (i < MAX_RULE_LENGTH - 1) + scan->val[i++] = ch; + ch = NEXT_CHAR (scan); + } + scan->val[i] = '\0'; + + if (ch == delim) + ch = NEXT_CHAR (scan); + + scan->token = GST_ASM_TOKEN_STRING; +} + +static void +gst_asm_scan_number (GstASMScan * scan) +{ + gchar ch; + gint i = 0; + gboolean have_float = FALSE; + + ch = THIS_CHAR (scan); + /* real strips all spaces that are not inside quotes for numbers */ + while ((IS_NUMBER (ch) || IS_SPACE (ch))) { + if (i < (MAX_RULE_LENGTH - 1) && !IS_SPACE (ch)) + scan->val[i++] = ch; + if (ch == '.') + have_float = TRUE; + ch = NEXT_CHAR (scan); + } + scan->val[i] = '\0'; + + if (have_float) + scan->token = GST_ASM_TOKEN_FLOAT; + else + scan->token = GST_ASM_TOKEN_INT; +} + +static void +gst_asm_scan_identifier (GstASMScan * scan) +{ + gchar ch; + gint i = 0; + + ch = THIS_CHAR (scan); + /* real strips all spaces that are not inside quotes for identifiers */ + while ((IS_CHAR (ch) || IS_SPACE (ch))) { + if (i < (MAX_RULE_LENGTH - 1) && !IS_SPACE (ch)) + scan->val[i++] = ch; + ch = NEXT_CHAR (scan); + } + scan->val[i] = '\0'; + + scan->token = GST_ASM_TOKEN_IDENTIFIER; +} + +static void +gst_asm_scan_print_token (GstASMScan * scan) +{ +#ifdef DEBUG + switch (scan->token) { + case GST_ASM_TOKEN_NONE: + g_print ("none\n"); + break; + case GST_ASM_TOKEN_EOF: + g_print ("EOF\n"); + break; + + case GST_ASM_TOKEN_INT: + g_print ("INT %d\n", atoi (scan->val)); + break; + case GST_ASM_TOKEN_FLOAT: + g_print ("FLOAT %f\n", atof (scan->val)); + break; + case GST_ASM_TOKEN_IDENTIFIER: + g_print ("ID %s\n", scan->val); + break; + case GST_ASM_TOKEN_STRING: + g_print ("STRING %s\n", scan->val); + break; + + case GST_ASM_TOKEN_HASH: + g_print ("HASH\n"); + break; + case GST_ASM_TOKEN_SEMICOLON: + g_print ("SEMICOLON\n"); + break; + case GST_ASM_TOKEN_COMMA: + g_print ("COMMA\n"); + break; + case GST_ASM_TOKEN_EQUAL: + g_print ("==\n"); + break; + case GST_ASM_TOKEN_NOTEQUAL: + g_print ("!=\n"); + break; + case GST_ASM_TOKEN_AND: + g_print ("&&\n"); + break; + case GST_ASM_TOKEN_OR: + g_print ("||\n"); + break; + case GST_ASM_TOKEN_LESS: + g_print ("<\n"); + break; + case GST_ASM_TOKEN_LESSEQUAL: + g_print ("<=\n"); + break; + case GST_ASM_TOKEN_GREATER: + g_print (">\n"); + break; + case GST_ASM_TOKEN_GREATEREQUAL: + g_print (">=\n"); + break; + case GST_ASM_TOKEN_DOLLAR: + g_print ("$\n"); + break; + case GST_ASM_TOKEN_LPAREN: + g_print ("(\n"); + break; + case GST_ASM_TOKEN_RPAREN: + g_print (")\n"); + break; + default: + break; + } +#endif +} + +static GstASMToken +gst_asm_scan_next_token (GstASMScan * scan) +{ + gchar ch; + + ch = THIS_CHAR (scan); + + /* skip spaces */ + while (IS_SPACE (ch)) + ch = NEXT_CHAR (scan); + + /* remove \ which is common in front of " */ + while (ch == '\\') + ch = NEXT_CHAR (scan); + + switch (ch) { + case '#': + scan->token = GST_ASM_TOKEN_HASH; + NEXT_CHAR (scan); + break; + case ';': + scan->token = GST_ASM_TOKEN_SEMICOLON; + NEXT_CHAR (scan); + break; + case ',': + scan->token = GST_ASM_TOKEN_COMMA; + NEXT_CHAR (scan); + break; + case '=': + scan->token = GST_ASM_TOKEN_EQUAL; + if (NEXT_CHAR (scan) == '=') + NEXT_CHAR (scan); + break; + case '!': + if (NEXT_CHAR (scan) == '=') { + scan->token = GST_ASM_TOKEN_NOTEQUAL; + NEXT_CHAR (scan); + } + break; + case '&': + scan->token = GST_ASM_TOKEN_AND; + if (NEXT_CHAR (scan) == '&') + NEXT_CHAR (scan); + break; + case '|': + scan->token = GST_ASM_TOKEN_OR; + if (NEXT_CHAR (scan) == '|') + NEXT_CHAR (scan); + break; + case '<': + scan->token = GST_ASM_TOKEN_LESS; + if (NEXT_CHAR (scan) == '=') { + scan->token = GST_ASM_TOKEN_LESSEQUAL; + NEXT_CHAR (scan); + } + break; + case '>': + scan->token = GST_ASM_TOKEN_GREATER; + if (NEXT_CHAR (scan) == '=') { + scan->token = GST_ASM_TOKEN_GREATEREQUAL; + NEXT_CHAR (scan); + } + break; + case '$': + scan->token = GST_ASM_TOKEN_DOLLAR; + NEXT_CHAR (scan); + break; + case '(': + scan->token = GST_ASM_TOKEN_LPAREN; + NEXT_CHAR (scan); + break; + case ')': + scan->token = GST_ASM_TOKEN_RPAREN; + NEXT_CHAR (scan); + break; + case '"': + NEXT_CHAR (scan); + gst_asm_scan_string (scan, '"'); + break; + case '\'': + NEXT_CHAR (scan); + gst_asm_scan_string (scan, '\''); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + gst_asm_scan_number (scan); + break; + case '\0': + scan->token = GST_ASM_TOKEN_EOF; + break; + default: + gst_asm_scan_identifier (scan); + break; + } + gst_asm_scan_print_token (scan); + return scan->token; +} + +static GstASMRule * +gst_asm_rule_new (void) +{ + GstASMRule *rule; + + rule = g_new (GstASMRule, 1); + rule->root = NULL; + rule->props = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + return rule; +} + +static void +gst_asm_rule_free (GstASMRule * rule) +{ + g_hash_table_unref (rule->props); + if (rule->root) + gst_asm_node_free (rule->root); + g_free (rule); +} + +static void +gst_asm_rule_add_property (GstASMRule * rule, gchar * key, gchar * val) +{ + g_hash_table_insert (rule->props, key, val); +} + +static GstASMNode *gst_asm_scan_parse_condition (GstASMScan * scan); + +static GstASMNode * +gst_asm_scan_parse_operand (GstASMScan * scan) +{ + GstASMNode *node; + + switch (scan->token) { + case GST_ASM_TOKEN_DOLLAR: + gst_asm_scan_next_token (scan); + + if (scan->token != GST_ASM_TOKEN_IDENTIFIER) + g_warning ("identifier expected"); + + node = gst_asm_node_new (); + node->type = GST_ASM_NODE_VARIABLE; + node->data.varname = g_strdup (scan->val); + break; + case GST_ASM_TOKEN_INT: + node = gst_asm_node_new (); + node->type = GST_ASM_NODE_INTEGER; + node->data.intval = (gfloat) atof (scan->val); + break; + case GST_ASM_TOKEN_FLOAT: + node = gst_asm_node_new (); + node->type = GST_ASM_NODE_FLOAT; + node->data.floatval = atoi (scan->val); + break; + case GST_ASM_TOKEN_LPAREN: + gst_asm_scan_next_token (scan); + node = gst_asm_scan_parse_condition (scan); + if (scan->token != GST_ASM_TOKEN_RPAREN) + g_warning (") expected"); + break; + default: + g_warning ("$ or ) expected"); + node = NULL; + break; + } + gst_asm_scan_next_token (scan); + + return node; +} + +static GstASMNode * +gst_asm_scan_parse_expression (GstASMScan * scan) +{ + GstASMNode *node, *left; + + node = gst_asm_scan_parse_operand (scan); + + while (IS_COND_TOKEN (scan->token)) { + left = node; + + node = gst_asm_node_new (); + node->type = GST_ASM_NODE_OPERATOR; + node->data.optype = scan->token; + + gst_asm_scan_next_token (scan); + + node->right = gst_asm_scan_parse_operand (scan); + node->left = left; + } + return node; +} + +static GstASMNode * +gst_asm_scan_parse_condition (GstASMScan * scan) +{ + GstASMNode *node, *left; + + node = gst_asm_scan_parse_expression (scan); + + while (IS_OP_TOKEN (scan->token)) { + left = node; + + node = gst_asm_node_new (); + node->type = GST_ASM_NODE_OPERATOR; + node->data.optype = scan->token; + + gst_asm_scan_next_token (scan); + + node->right = gst_asm_scan_parse_expression (scan); + node->left = left; + } + return node; +} + +static void +gst_asm_scan_parse_property (GstASMRule * rule, GstASMScan * scan) +{ + gchar *key, *val; + + if (scan->token != GST_ASM_TOKEN_IDENTIFIER) { + g_warning ("identifier expected"); + return; + } + key = g_strdup (scan->val); + + gst_asm_scan_next_token (scan); + if (scan->token != GST_ASM_TOKEN_EQUAL) { + g_warning ("= expected"); + return; + } + gst_asm_scan_next_token (scan); + val = g_strdup (scan->val); + + gst_asm_rule_add_property (rule, key, val); + gst_asm_scan_next_token (scan); +} + +static GstASMRule * +gst_asm_scan_parse_rule (GstASMScan * scan) +{ + GstASMRule *rule; + + rule = gst_asm_rule_new (); + + if (scan->token == GST_ASM_TOKEN_HASH) { + gst_asm_scan_next_token (scan); + rule->root = gst_asm_scan_parse_condition (scan); + if (scan->token == GST_ASM_TOKEN_COMMA) + gst_asm_scan_next_token (scan); + } + + if (scan->token != GST_ASM_TOKEN_SEMICOLON) { + gst_asm_scan_parse_property (rule, scan); + while (scan->token == GST_ASM_TOKEN_COMMA) { + gst_asm_scan_next_token (scan); + gst_asm_scan_parse_property (rule, scan); + } + gst_asm_scan_next_token (scan); + } + return rule; +} + +gboolean +gst_asm_rule_evaluate (GstASMRule * rule, GHashTable * vars) +{ + gboolean res; + + if (rule->root) { + res = (gboolean) gst_asm_node_evaluate (rule->root, vars); + } else + res = TRUE; + + return res; +} + +GstASMRuleBook * +gst_asm_rule_book_new (const gchar * rulebook) +{ + GstASMRuleBook *book; + GstASMRule *rule = NULL; + GstASMScan *scan; + GstASMToken token; + + book = g_new0 (GstASMRuleBook, 1); + book->rulebook = rulebook; + + scan = gst_asm_scan_new (book->rulebook); + gst_asm_scan_next_token (scan); + + do { + rule = gst_asm_scan_parse_rule (scan); + if (rule) { + book->rules = g_list_append (book->rules, rule); + book->n_rules++; + } + token = scan->token; + } while (token != GST_ASM_TOKEN_EOF); + + gst_asm_scan_free (scan); + + return book; +} + +gint +gst_asm_rule_book_n_rules (GstASMRuleBook * book) +{ + return book->n_rules; +} + +void +gst_asm_rule_book_free (GstASMRuleBook * book) +{ + GList *walk; + + for (walk = book->rules; walk; walk = g_list_next (walk)) { + GstASMRule *rule = (GstASMRule *) walk->data; + + gst_asm_rule_free (rule); + } + g_list_free (book->rules); + g_free (book); +} + +gint +gst_asm_rule_book_match (GstASMRuleBook * book, GHashTable * vars, + gint * rulematches) +{ + GList *walk; + gint i, n = 0; + + for (walk = book->rules, i = 0; walk; walk = g_list_next (walk), i++) { + GstASMRule *rule = (GstASMRule *) walk->data; + + if (gst_asm_rule_evaluate (rule, vars)) { + rulematches[n++] = i; + } + } + return n; +} + +#ifdef TEST +gint +main (gint argc, gchar * argv[]) +{ + GstASMRuleBook *book; + gint rulematch[MAX_RULEMATCHES]; + GHashTable *vars; + gint i, n; + + static const gchar *rules1 = + "#($Bandwidth < 67959),TimestampDelivery=T,DropByN=T," + "priority=9;#($Bandwidth >= 67959) && ($Bandwidth < 167959)," + "AverageBandwidth=67959,Priority=9;#($Bandwidth >= 67959) && ($Bandwidth" + " < 167959),AverageBandwidth=0,Priority=5,OnDepend=\\\"1\\\";#($Bandwidth >= 167959)" + " && ($Bandwidth < 267959),AverageBandwidth=167959,Priority=9;#($Bandwidth >= 167959)" + " && ($Bandwidth < 267959),AverageBandwidth=0,Priority=5,OnDepend=\\\"3\\\";" + "#($Bandwidth >= 267959),AverageBandwidth=267959,Priority=9;#($Bandwidth >= 267959)" + ",AverageBandwidth=0,Priority=5,OnDepend=\\\"5\\\";"; + static const gchar *rules2 = + "AverageBandwidth=32041,Priority=5;AverageBandwidth=0," + "Priority=5,OnDepend=\\\"0\\\", OffDepend=\\\"0\\\";"; + + vars = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_insert (vars, "Bandwidth", "300000"); + + book = gst_asm_rule_book_new (rules1); + n = gst_asm_rule_book_match (book, vars, rulematch); + gst_asm_rule_book_free (book); + + g_print ("%d rules matched\n", n); + for (i = 0; i < n; i++) { + g_print ("rule %d matched\n", rulematch[i]); + } + + book = gst_asm_rule_book_new (rules2); + n = gst_asm_rule_book_match (book, vars, rulematch); + gst_asm_rule_book_free (book); + + g_hash_table_unref (vars); + + g_print ("%d rules matched\n", n); + for (i = 0; i < n; i++) { + g_print ("rule %d matched\n", rulematch[i]); + } + + return 0; +} +#endif diff --git a/gst/realmedia/asmrules.h b/gst/realmedia/asmrules.h new file mode 100644 index 0000000000..fc9e8a0b47 --- /dev/null +++ b/gst/realmedia/asmrules.h @@ -0,0 +1,115 @@ +/* GStreamer + * Copyright (C) <2007> Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_ASM_RULES_H__ +#define __GST_ASM_RULES_H__ + +#include + +G_BEGIN_DECLS + +#define MAX_RULEMATCHES 16 + +typedef struct _GstASMNode GstASMNode; +typedef struct _GstASMRule GstASMRule; +typedef struct _GstASMRuleBook GstASMRuleBook; + +typedef enum { + GST_ASM_TOKEN_NONE, + GST_ASM_TOKEN_EOF, + + GST_ASM_TOKEN_INT, + GST_ASM_TOKEN_FLOAT, + GST_ASM_TOKEN_IDENTIFIER, + GST_ASM_TOKEN_STRING, + + GST_ASM_TOKEN_HASH, + GST_ASM_TOKEN_SEMICOLON, + GST_ASM_TOKEN_COMMA, + GST_ASM_TOKEN_DOLLAR, + + GST_ASM_TOKEN_LPAREN, + GST_ASM_TOKEN_RPAREN, + + GST_ASM_TOKEN_GREATER, + GST_ASM_TOKEN_LESS, + GST_ASM_TOKEN_GREATEREQUAL, + GST_ASM_TOKEN_LESSEQUAL, + GST_ASM_TOKEN_EQUAL, + GST_ASM_TOKEN_NOTEQUAL, + + GST_ASM_TOKEN_AND, + GST_ASM_TOKEN_OR +} GstASMToken; + +typedef enum { + GST_ASM_NODE_UNKNOWN, + GST_ASM_NODE_VARIABLE, + GST_ASM_NODE_INTEGER, + GST_ASM_NODE_FLOAT, + GST_ASM_NODE_OPERATOR +} GstASMNodeType; + +typedef enum { + GST_ASM_OP_GREATER = GST_ASM_TOKEN_GREATER, + GST_ASM_OP_LESS = GST_ASM_TOKEN_LESS, + GST_ASM_OP_GREATEREQUAL = GST_ASM_TOKEN_GREATEREQUAL, + GST_ASM_OP_LESSEQUAL = GST_ASM_TOKEN_LESSEQUAL, + GST_ASM_OP_EQUAL = GST_ASM_TOKEN_EQUAL, + GST_ASM_OP_NOTEQUAL = GST_ASM_TOKEN_NOTEQUAL, + + GST_ASM_OP_AND = GST_ASM_TOKEN_AND, + GST_ASM_OP_OR = GST_ASM_TOKEN_OR +} GstASMOp; + +struct _GstASMNode { + GstASMNodeType type; + + union { + gchar *varname; + gint intval; + gfloat floatval; + GstASMOp optype; + } data; + + GstASMNode *left; + GstASMNode *right; +}; + +struct _GstASMRule { + GstASMNode *root; + GHashTable *props; +}; + +struct _GstASMRuleBook { + const gchar *rulebook; + + guint n_rules; + GList *rules; +}; + +G_END_DECLS + +GstASMRuleBook* gst_asm_rule_book_new (const gchar *rulebook); +void gst_asm_rule_book_free (GstASMRuleBook *book); + +gint gst_asm_rule_book_match (GstASMRuleBook *book, GHashTable *vars, + gint *rulematches); + +#endif /* __GST_ASM_RULES_H__ */ diff --git a/gst/realmedia/rtspreal.c b/gst/realmedia/rtspreal.c index f7a2ede69e..7e8425d882 100644 --- a/gst/realmedia/rtspreal.c +++ b/gst/realmedia/rtspreal.c @@ -43,6 +43,7 @@ #include "realhash.h" #include "rtspreal.h" +#include "asmrules.h" GST_DEBUG_CATEGORY_STATIC (rtspreal_debug); #define GST_CAT_DEFAULT (rtspreal_debug) @@ -256,18 +257,15 @@ rtsp_ext_real_parse_sdp (GstRTSPExtension * ext, GstSDPMessage * sdp, GstStructure * props) { GstRTSPReal *ctx = (GstRTSPReal *) ext; - guint size, n_streams; + guint size; gint i; - guint32 max_bit_rate = 0, avg_bit_rate = 0; - guint32 max_packet_size = 0, avg_packet_size = 0; - guint32 start_time, preroll, duration = 0; gchar *title, *author, *copyright, *comment; gsize title_len, author_len, copyright_len, comment_len; guint8 *data = NULL, *datap; guint data_len = 0, offset; GstBuffer *buf; gchar *opaque_data; - gsize opaque_data_len; + gsize opaque_data_len, asm_rule_book_len; /* don't bother for non-real formats */ READ_INT (sdp, "IsRealDataType", ctx->isreal); @@ -277,25 +275,33 @@ rtsp_ext_real_parse_sdp (GstRTSPExtension * ext, GstSDPMessage * sdp, /* Force PAUSE | PLAY */ //src->methods |= GST_RTSP_PLAY | GST_RTSP_PAUSE; - n_streams = gst_sdp_message_medias_len (sdp); + ctx->n_streams = gst_sdp_message_medias_len (sdp); - /* PROP */ - for (i = 0; i < n_streams; i++) { - const GstSDPMedia *media = gst_sdp_message_get_media (sdp, i); + ctx->max_bit_rate = 0; + ctx->avg_bit_rate = 0; + ctx->max_packet_size = 0; + ctx->avg_packet_size = 0; + ctx->duration = 0; + + for (i = 0; i < ctx->n_streams; i++) { + const GstSDPMedia *media; gint intval; + media = gst_sdp_message_get_media (sdp, i); + READ_INT_M (media, "MaxBitRate", intval); - max_bit_rate += intval; + ctx->max_bit_rate += intval; READ_INT_M (media, "AvgBitRate", intval); - avg_bit_rate += intval; + ctx->avg_bit_rate += intval; READ_INT_M (media, "MaxPacketSize", intval); - max_packet_size = MAX (max_packet_size, intval); + ctx->max_packet_size = MAX (ctx->max_packet_size, intval); READ_INT_M (media, "AvgPacketSize", intval); - avg_packet_size = (avg_packet_size * i + intval) / (i + 1); + ctx->avg_packet_size = (ctx->avg_packet_size * i + intval) / (i + 1); READ_INT_M (media, "Duration", intval); - duration = MAX (duration, intval); + ctx->duration = MAX (ctx->duration, intval); } + /* PROP */ offset = 0; size = 50; ENSURE_SIZE (size); @@ -304,16 +310,16 @@ rtsp_ext_real_parse_sdp (GstRTSPExtension * ext, GstSDPMessage * sdp, memcpy (datap + 0, "PROP", 4); GST_WRITE_UINT32_BE (datap + 4, size); GST_WRITE_UINT16_BE (datap + 8, 0); - GST_WRITE_UINT32_BE (datap + 10, max_bit_rate); - GST_WRITE_UINT32_BE (datap + 14, avg_bit_rate); - GST_WRITE_UINT32_BE (datap + 18, max_packet_size); - GST_WRITE_UINT32_BE (datap + 22, avg_packet_size); + GST_WRITE_UINT32_BE (datap + 10, ctx->max_bit_rate); + GST_WRITE_UINT32_BE (datap + 14, ctx->avg_bit_rate); + GST_WRITE_UINT32_BE (datap + 18, ctx->max_packet_size); + GST_WRITE_UINT32_BE (datap + 22, ctx->avg_packet_size); GST_WRITE_UINT32_BE (datap + 26, 0); - GST_WRITE_UINT32_BE (datap + 30, duration); + GST_WRITE_UINT32_BE (datap + 30, ctx->duration); GST_WRITE_UINT32_BE (datap + 34, 0); GST_WRITE_UINT32_BE (datap + 38, 0); GST_WRITE_UINT32_BE (datap + 42, 0); - GST_WRITE_UINT16_BE (datap + 46, n_streams); + GST_WRITE_UINT16_BE (datap + 46, ctx->n_streams); GST_WRITE_UINT16_BE (datap + 48, 0); offset += size; @@ -337,24 +343,29 @@ rtsp_ext_real_parse_sdp (GstRTSPExtension * ext, GstSDPMessage * sdp, offset += size; /* MDPR */ - for (i = 0; i < n_streams; i++) { - const GstSDPMedia *media = gst_sdp_message_get_media (sdp, i); - gchar *asm_rule_book, *type_specific_data; - guint asm_rule_book_len; - gchar *stream_name, *mime_type; - guint stream_name_len, mime_type_len; - guint16 num_rules, j, sel, codec; - guint32 type_specific_data_len, len; + for (i = 0; i < ctx->n_streams; i++) { + const GstSDPMedia *media; + guint16 j, sel; + guint32 len; + GstRTSPRealStream *stream; + gchar *str; - READ_INT_M (media, "MaxBitRate", max_bit_rate); - READ_INT_M (media, "AvgBitRate", avg_bit_rate); - READ_INT_M (media, "MaxPacketSize", max_packet_size); - READ_INT_M (media, "AvgPacketSize", avg_packet_size); - READ_INT_M (media, "StartTime", start_time); - READ_INT_M (media, "Preroll", preroll); - READ_INT_M (media, "Duration", duration); - READ_STRING (media, "StreamName", stream_name, stream_name_len); - READ_STRING (media, "mimetype", mime_type, mime_type_len); + media = gst_sdp_message_get_media (sdp, i); + + stream = g_new0 (GstRTSPRealStream, 1); + ctx->streams = g_list_append (ctx->streams, stream); + + READ_INT_M (media, "MaxBitRate", stream->max_bit_rate); + READ_INT_M (media, "AvgBitRate", stream->avg_bit_rate); + READ_INT_M (media, "MaxPacketSize", stream->max_packet_size); + READ_INT_M (media, "AvgPacketSize", stream->avg_packet_size); + READ_INT_M (media, "StartTime", stream->start_time); + READ_INT_M (media, "Preroll", stream->preroll); + READ_INT_M (media, "Duration", stream->duration); + READ_STRING (media, "StreamName", str, stream->stream_name_len); + stream->stream_name = g_strndup (str, stream->stream_name_len); + READ_STRING (media, "mimetype", str, stream->mime_type_len); + stream->mime_type = g_strndup (str, stream->mime_type_len); /* FIXME: Depending on the current bandwidth, we need to select one * bandwith out of a list offered by the server. Someone needs to write @@ -375,7 +386,8 @@ rtsp_ext_real_parse_sdp (GstRTSPExtension * ext, GstSDPMessage * sdp, * But to give you a starting point, I offer you above string * in the variable 'asm_rule_book'. */ - READ_STRING (media, "ASMRuleBook", asm_rule_book, asm_rule_book_len); + READ_STRING (media, "ASMRuleBook", str, asm_rule_book_len); + stream->rulebook = gst_asm_rule_book_new (str); sel = 0; READ_BUFFER_M (media, "OpaqueData", opaque_data, opaque_data_len); @@ -386,8 +398,8 @@ rtsp_ext_real_parse_sdp (GstRTSPExtension * ext, GstSDPMessage * sdp, } if (strncmp (opaque_data, "MLTI", 4)) { GST_DEBUG_OBJECT (ctx, "no MLTI found, appending all"); - type_specific_data_len = opaque_data_len; - type_specific_data = opaque_data; + stream->type_specific_data_len = opaque_data_len; + stream->type_specific_data = g_memdup (opaque_data, opaque_data_len); goto no_type_specific; } opaque_data += 4; @@ -397,12 +409,12 @@ rtsp_ext_real_parse_sdp (GstRTSPExtension * ext, GstSDPMessage * sdp, GST_DEBUG_OBJECT (ctx, "opaque_data_len %d < 2", opaque_data_len); goto strange_opaque_data; } - num_rules = GST_READ_UINT16_BE (opaque_data); + stream->num_rules = GST_READ_UINT16_BE (opaque_data); opaque_data += 2; opaque_data_len -= 2; - if (sel >= num_rules) { - GST_DEBUG_OBJECT (ctx, "sel %d >= num_rules %d", sel, num_rules); + if (sel >= stream->num_rules) { + GST_DEBUG_OBJECT (ctx, "sel %d >= num_rules %d", sel, stream->num_rules); goto strange_opaque_data; } @@ -418,32 +430,33 @@ rtsp_ext_real_parse_sdp (GstRTSPExtension * ext, GstSDPMessage * sdp, GST_DEBUG_OBJECT (ctx, "opaque_data_len %d < 2", opaque_data_len); goto strange_opaque_data; } - codec = GST_READ_UINT16_BE (opaque_data); + stream->codec = GST_READ_UINT16_BE (opaque_data); opaque_data += 2; opaque_data_len -= 2; - if (opaque_data_len < 2 * (num_rules - sel - 1)) { + if (opaque_data_len < 2 * (stream->num_rules - sel - 1)) { GST_DEBUG_OBJECT (ctx, "opaque_data_len %d < %d", opaque_data_len, - 2 * (num_rules - sel - 1)); + 2 * (stream->num_rules - sel - 1)); goto strange_opaque_data; } - opaque_data += 2 * (num_rules - sel - 1); - opaque_data_len -= 2 * (num_rules - sel - 1); + opaque_data += 2 * (stream->num_rules - sel - 1); + opaque_data_len -= 2 * (stream->num_rules - sel - 1); if (opaque_data_len < 2) { GST_DEBUG_OBJECT (ctx, "opaque_data_len %d < 2", opaque_data_len); goto strange_opaque_data; } - num_rules = GST_READ_UINT16_BE (opaque_data); + stream->num_rules = GST_READ_UINT16_BE (opaque_data); opaque_data += 2; opaque_data_len -= 2; - if (codec > num_rules) { - GST_DEBUG_OBJECT (ctx, "codec %d > num_rules %d", codec, num_rules); + if (stream->codec > stream->num_rules) { + GST_DEBUG_OBJECT (ctx, "codec %d > num_rules %d", stream->codec, + stream->num_rules); goto strange_opaque_data; } - for (j = 0; j < codec; j++) { + for (j = 0; j < stream->codec; j++) { if (opaque_data_len < 4) { GST_DEBUG_OBJECT (ctx, "opaque_data_len %d < 4", opaque_data_len); goto strange_opaque_data; @@ -465,19 +478,22 @@ rtsp_ext_real_parse_sdp (GstRTSPExtension * ext, GstSDPMessage * sdp, GST_DEBUG_OBJECT (ctx, "opaque_data_len %d < 4", opaque_data_len); goto strange_opaque_data; } - type_specific_data_len = GST_READ_UINT32_BE (opaque_data); + stream->type_specific_data_len = GST_READ_UINT32_BE (opaque_data); opaque_data += 4; opaque_data_len -= 4; - if (opaque_data_len < type_specific_data_len) { + if (opaque_data_len < stream->type_specific_data_len) { GST_DEBUG_OBJECT (ctx, "opaque_data_len %d < %d", opaque_data_len, - type_specific_data_len); + stream->type_specific_data_len); goto strange_opaque_data; } - type_specific_data = opaque_data; + stream->type_specific_data = + g_memdup (opaque_data, stream->type_specific_data_len); no_type_specific: - size = 46 + stream_name_len + mime_type_len + type_specific_data_len; + size = + 46 + stream->stream_name_len + stream->mime_type_len + + stream->type_specific_data_len; ENSURE_SIZE (offset + size); datap = data + offset; @@ -485,19 +501,20 @@ rtsp_ext_real_parse_sdp (GstRTSPExtension * ext, GstSDPMessage * sdp, GST_WRITE_UINT32_BE (datap + 4, size); GST_WRITE_UINT16_BE (datap + 8, 0); GST_WRITE_UINT16_BE (datap + 10, i); - GST_WRITE_UINT32_BE (datap + 12, max_bit_rate); - GST_WRITE_UINT32_BE (datap + 16, avg_bit_rate); - GST_WRITE_UINT32_BE (datap + 20, max_packet_size); - GST_WRITE_UINT32_BE (datap + 24, avg_packet_size); - GST_WRITE_UINT32_BE (datap + 28, start_time); - GST_WRITE_UINT32_BE (datap + 32, preroll); - GST_WRITE_UINT32_BE (datap + 36, duration); + GST_WRITE_UINT32_BE (datap + 12, stream->max_bit_rate); + GST_WRITE_UINT32_BE (datap + 16, stream->avg_bit_rate); + GST_WRITE_UINT32_BE (datap + 20, stream->max_packet_size); + GST_WRITE_UINT32_BE (datap + 24, stream->avg_packet_size); + GST_WRITE_UINT32_BE (datap + 28, stream->start_time); + GST_WRITE_UINT32_BE (datap + 32, stream->preroll); + GST_WRITE_UINT32_BE (datap + 36, stream->duration); datap += 40; - WRITE_STRING1 (datap, stream_name, stream_name_len); - WRITE_STRING1 (datap, mime_type, mime_type_len); - GST_WRITE_UINT32_BE (datap, type_specific_data_len); - if (type_specific_data_len) - memcpy (datap + 4, type_specific_data, type_specific_data_len); + WRITE_STRING1 (datap, stream->stream_name, stream->stream_name_len); + WRITE_STRING1 (datap, stream->mime_type, stream->mime_type_len); + GST_WRITE_UINT32_BE (datap, stream->type_specific_data_len); + if (stream->type_specific_data_len) + memcpy (datap + 4, stream->type_specific_data, + stream->type_specific_data_len); offset += size; } @@ -522,7 +539,7 @@ rtsp_ext_real_parse_sdp (GstRTSPExtension * ext, GstSDPMessage * sdp, gst_structure_set (props, "config", GST_TYPE_BUFFER, buf, NULL); /* Overwrite encoding and media fields */ - gst_structure_set (props, "encoding-name", G_TYPE_STRING, "x-real-rdt", NULL); + gst_structure_set (props, "encoding-name", G_TYPE_STRING, "X-REAL-RDT", NULL); gst_structure_set (props, "media", G_TYPE_STRING, "application", NULL); return TRUE; @@ -543,6 +560,10 @@ rtsp_ext_real_stream_select (GstRTSPExtension * ext, GstRTSPUrl * url) GstRTSPMessage request = { 0 }; GstRTSPMessage response = { 0 }; gchar *req_url; + GString *rules; + GList *walk; + gint i; + GHashTable *vars; if (!ctx->isreal) return GST_RTSP_OK; @@ -556,13 +577,34 @@ rtsp_ext_real_stream_select (GstRTSPExtension * ext, GstRTSPUrl * url) g_free (req_url); - /* FIXME, do selection instead of hardcoded values */ - gst_rtsp_message_add_header (&request, GST_RTSP_HDR_SUBSCRIBE, - //"stream=0;rule=5,stream=0;rule=6,stream=1;rule=0,stream=1;rule=1"); - //"stream=0;rule=0,stream=0;rule=1,stream=1;rule=0,stream=1;rule=1"); - //"stream=0;rule=5,stream=0;rule=6,stream=1;rule=2,stream=1;rule=3"); - "stream=0;rule=5,stream=0;rule=6,stream=1;rule=0,stream=1;rule=1"); - //"stream=0;rule=0,stream=0;rule=1"); + rules = g_string_new (""); + vars = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_insert (vars, "Bandwidth", "300000"); + + for (walk = ctx->streams, i = 0; walk; walk = g_list_next (walk), i++) { + GstRTSPRealStream *stream; + gint rulematches[MAX_RULEMATCHES]; + gint j, n; + + stream = (GstRTSPRealStream *) walk->data; + + n = gst_asm_rule_book_match (stream->rulebook, vars, rulematches); + for (j = 0; j < n; j++) { + g_string_append_printf (rules, "stream=%u;rule=%u,", i, rulematches[j]); + } + } + + g_hash_table_unref (vars); + + /* strip final , if we added some stream rules */ + if (rules->len > 0) { + rules = g_string_truncate (rules, rules->len - 1); + } + + /* do selection */ + gst_rtsp_message_add_header (&request, GST_RTSP_HDR_SUBSCRIBE, rules->str); + + g_string_free (rules, TRUE); /* send SET_PARAMETER */ if ((res = gst_rtsp_extension_send (ext, &request, &response)) < 0) diff --git a/gst/realmedia/rtspreal.h b/gst/realmedia/rtspreal.h index 5fbf9c0a11..976871a8cf 100644 --- a/gst/realmedia/rtspreal.h +++ b/gst/realmedia/rtspreal.h @@ -22,6 +22,8 @@ #include +#include "asmrules.h" + G_BEGIN_DECLS #define GST_TYPE_RTSP_REAL (gst_rtsp_real_get_type()) @@ -33,12 +35,45 @@ G_BEGIN_DECLS typedef struct _GstRTSPReal GstRTSPReal; typedef struct _GstRTSPRealClass GstRTSPRealClass; +typedef struct _GstRTSPRealStream GstRTSPRealStream; + +struct _GstRTSPRealStream { + guint id; + guint max_bit_rate; + guint avg_bit_rate; + guint max_packet_size; + guint avg_packet_size; + guint start_time; + guint preroll; + guint duration; + gchar *stream_name; + guint stream_name_len; + gchar *mime_type; + guint mime_type_len; + + GstASMRuleBook *rulebook; + + gchar *type_specific_data; + guint type_specific_data_len; + + guint16 num_rules, j, sel, codec; +}; + struct _GstRTSPReal { GstElement element; gchar checksum[34]; gchar challenge2[64]; gboolean isreal; + + guint n_streams; + GList *streams; + + guint max_bit_rate; + guint avg_bit_rate; + guint max_packet_size; + guint avg_packet_size; + guint duration; }; struct _GstRTSPRealClass {