gst/realmedia/.cvsignore: Add test to ignore.

Original commit message from CVS:
* 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.
This commit is contained in:
Wim Taymans 2007-08-02 19:30:05 +00:00
parent 5ef116fd1f
commit 04424d07ef
7 changed files with 1008 additions and 79 deletions

View file

@ -1,3 +1,30 @@
2007-08-02 Wim Taymans <wim.taymans@gmail.com>
* 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 <ensonic@users.sf.net>
* gst/realmedia/rtspreal.c:

1
gst/realmedia/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
asmrules

View file

@ -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)

702
gst/realmedia/asmrules.c Normal file
View file

@ -0,0 +1,702 @@
/* GStreamer
* Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
*
* 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 <string.h>
#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 ("$ <number> 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

115
gst/realmedia/asmrules.h Normal file
View file

@ -0,0 +1,115 @@
/* GStreamer
* Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
*
* 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 <gst/gst.h>
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__ */

View file

@ -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)

View file

@ -22,6 +22,8 @@
#include <gst/gst.h>
#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 {