realmedia: remove RealServer RTSP extension, RDT handling and PNM source

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6091>
This commit is contained in:
Tim-Philipp Müller 2024-02-10 19:47:24 +00:00 committed by GStreamer Marge Bot
parent 1ef7e9be73
commit 68d62a8433
19 changed files with 6 additions and 5533 deletions

View file

@ -13890,9 +13890,6 @@
"GstOverlayComposition!src", "GstOverlayComposition!src",
"GstOverlayComposition::caps-changed", "GstOverlayComposition::caps-changed",
"GstOverlayComposition::draw", "GstOverlayComposition::draw",
"GstPNMSrc",
"GstPNMSrc!src",
"GstPNMSrc:location",
"GstPad", "GstPad",
"GstPad.ABI._gst_reserved", "GstPad.ABI._gst_reserved",
"GstPad.ABI.abi.eventfullfunc", "GstPad.ABI.abi.eventfullfunc",
@ -14612,21 +14609,6 @@
"GstQueueLeaky::downstream", "GstQueueLeaky::downstream",
"GstQueueLeaky::no", "GstQueueLeaky::no",
"GstQueueLeaky::upstream", "GstQueueLeaky::upstream",
"GstRDTDepay",
"GstRDTDepay!sink",
"GstRDTDepay!src",
"GstRDTManager",
"GstRDTManager!recv_rtcp_sink_%u",
"GstRDTManager!recv_rtp_sink_%u",
"GstRDTManager!recv_rtp_src_%u_%u_%u",
"GstRDTManager!rtcp_src_%u",
"GstRDTManager::clear-pt-map",
"GstRDTManager::on-bye-ssrc",
"GstRDTManager::on-bye-timeout",
"GstRDTManager::on-npt-stop",
"GstRDTManager::on-timeout",
"GstRDTManager::request-pt-map",
"GstRDTManager:latency",
"GstRGB2Bayer", "GstRGB2Bayer",
"GstRGB2Bayer!sink", "GstRGB2Bayer!sink",
"GstRGB2Bayer!src", "GstRGB2Bayer!src",
@ -15234,7 +15216,6 @@
"GstRTSPRange.max", "GstRTSPRange.max",
"GstRTSPRange.min", "GstRTSPRange.min",
"GstRTSPRangeUnit", "GstRTSPRangeUnit",
"GstRTSPReal",
"GstRTSPResult", "GstRTSPResult",
"GstRTSPSendFunc", "GstRTSPSendFunc",
"GstRTSPSendListFunc", "GstRTSPSendListFunc",
@ -36698,7 +36679,6 @@
"element-pngparse", "element-pngparse",
"element-pnmdec", "element-pnmdec",
"element-pnmenc", "element-pnmenc",
"element-pnmsrc",
"element-progressreport", "element-progressreport",
"element-proxysink", "element-proxysink",
"element-proxysrc", "element-proxysrc",
@ -36718,8 +36698,6 @@
"element-radioactv", "element-radioactv",
"element-rawaudioparse", "element-rawaudioparse",
"element-rawvideoparse", "element-rawvideoparse",
"element-rdtdepay",
"element-rdtmanager",
"element-removesilence", "element-removesilence",
"element-retinex", "element-retinex",
"element-revtv", "element-revtv",
@ -36860,7 +36838,6 @@
"element-rtpvrawpay", "element-rtpvrawpay",
"element-rtpxqtdepay", "element-rtpxqtdepay",
"element-rtspclientsink", "element-rtspclientsink",
"element-rtspreal",
"element-rtspsrc", "element-rtspsrc",
"element-rtspwms", "element-rtspwms",
"element-sbcdec", "element-sbcdec",
@ -66572,8 +66549,6 @@
"pnmdec", "pnmdec",
"pnmenc", "pnmenc",
"pnmenc:ascii", "pnmenc:ascii",
"pnmsrc",
"pnmsrc:location",
"progressreport", "progressreport",
"progressreport:do-query", "progressreport:do-query",
"progressreport:format", "progressreport:format",
@ -66834,15 +66809,6 @@
"rawvideoparse:plane-strides", "rawvideoparse:plane-strides",
"rawvideoparse:top-field-first", "rawvideoparse:top-field-first",
"rawvideoparse:width", "rawvideoparse:width",
"rdtdepay",
"rdtmanager",
"rdtmanager::clear-pt-map",
"rdtmanager::on-bye-ssrc",
"rdtmanager::on-bye-timeout",
"rdtmanager::on-npt-stop",
"rdtmanager::on-timeout",
"rdtmanager::request-pt-map",
"rdtmanager:latency",
"removesilence", "removesilence",
"removesilence:hysteresis", "removesilence:hysteresis",
"removesilence:minimum-silence-buffers", "removesilence:minimum-silence-buffers",
@ -67462,7 +67428,6 @@
"rtspclientsink:user-agent", "rtspclientsink:user-agent",
"rtspclientsink:user-id", "rtspclientsink:user-id",
"rtspclientsink:user-pw", "rtspclientsink:user-pw",
"rtspreal",
"rtspsrc", "rtspsrc",
"rtspsrc::accept-certificate", "rtspsrc::accept-certificate",
"rtspsrc::before-send", "rtspsrc::before-send",

View file

@ -99,6 +99,10 @@ static const GstRTSPTransMap transports[] = {
{"srtpf", GST_RTSP_TRANS_RTP, GST_RTSP_PROFILE_SAVPF, {"srtpf", GST_RTSP_TRANS_RTP, GST_RTSP_PROFILE_SAVPF,
GST_RTSP_LOWER_TRANS_UDP_MCAST, "application/x-srtp", GST_RTSP_LOWER_TRANS_UDP_MCAST, "application/x-srtp",
{"rtpbin", "rtpdec"}}, {"rtpbin", "rtpdec"}},
/* FIXME: these two are no longer supported (the rdtmanager element and
* related code has been removed from the realmedia plugin), but let's keep
* this around for now so it can still be added back via plugins if needed.
* FIXME 2.0: remove */
{"x-real-rdt", GST_RTSP_TRANS_RDT, GST_RTSP_PROFILE_AVP, {"x-real-rdt", GST_RTSP_TRANS_RDT, GST_RTSP_PROFILE_AVP,
GST_RTSP_LOWER_TRANS_UNKNOWN, "application/x-rdt", GST_RTSP_LOWER_TRANS_UNKNOWN, "application/x-rdt",
{"rdtmanager", NULL}}, {"rdtmanager", NULL}},

View file

@ -507,46 +507,6 @@
"realmedia": { "realmedia": {
"description": "RealMedia support plugins", "description": "RealMedia support plugins",
"elements": { "elements": {
"pnmsrc": {
"author": "Wim Taymans <wim.taymans@gmail.com>",
"description": "Receive data over the network via PNM",
"hierarchy": [
"GstPNMSrc",
"GstPushSrc",
"GstBaseSrc",
"GstElement",
"GstObject",
"GInitiallyUnowned",
"GObject"
],
"interfaces": [
"GstURIHandler"
],
"klass": "Source/Network",
"long-name": "PNM packet receiver",
"pad-templates": {
"src": {
"caps": "application/vnd.rn-realmedia:\n",
"direction": "src",
"presence": "always"
}
},
"properties": {
"location": {
"blurb": "Location of the PNM url to read",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "NULL",
"mutable": "null",
"readable": true,
"type": "gchararray",
"writable": true
}
},
"rank": "marginal"
},
"rademux": { "rademux": {
"author": "Tim-Philipp Müller <tim centricular net>", "author": "Tim-Philipp Müller <tim centricular net>",
"description": "Demultiplex a RealAudio file", "description": "Demultiplex a RealAudio file",
@ -558,7 +518,6 @@
"GObject" "GObject"
], ],
"klass": "Codec/Demuxer", "klass": "Codec/Demuxer",
"long-name": "RealAudio Demuxer",
"pad-templates": { "pad-templates": {
"sink": { "sink": {
"caps": "application/x-pn-realaudio:\n", "caps": "application/x-pn-realaudio:\n",
@ -571,163 +530,7 @@
"presence": "sometimes" "presence": "sometimes"
} }
}, },
"rank": "secondary", "rank": "secondary"
"signals": {}
},
"rdtdepay": {
"author": "Lutz Mueller <lutz at topfrose dot de>, Wim Taymans <wim@fluendo.com>",
"description": "Extracts RealMedia from RDT packets",
"hierarchy": [
"GstRDTDepay",
"GstElement",
"GstObject",
"GInitiallyUnowned",
"GObject"
],
"klass": "Codec/Depayloader/Network",
"long-name": "RDT packet parser",
"pad-templates": {
"sink": {
"caps": "application/x-rdt:\n media: application\n clock-rate: [ 1, 2147483647 ]\n encoding-name: X-REAL-RDT\n",
"direction": "sink",
"presence": "always"
},
"src": {
"caps": "application/vnd.rn-realmedia:\n",
"direction": "src",
"presence": "always"
}
},
"rank": "marginal"
},
"rdtmanager": {
"author": "Wim Taymans <wim.taymans@gmail.com>",
"description": "Accepts raw RTP and RTCP packets and sends them forward",
"hierarchy": [
"GstRDTManager",
"GstElement",
"GstObject",
"GInitiallyUnowned",
"GObject"
],
"klass": "Codec/Parser/Network",
"long-name": "RTP Decoder",
"pad-templates": {
"recv_rtcp_sink_%%u": {
"caps": "application/x-rtcp:\n",
"direction": "sink",
"presence": "request"
},
"recv_rtp_sink_%%u": {
"caps": "application/x-rdt:\n",
"direction": "sink",
"presence": "request"
},
"recv_rtp_src_%%u_%%u_%%u": {
"caps": "application/x-rdt:\n",
"direction": "src",
"presence": "sometimes"
},
"rtcp_src_%%u": {
"caps": "application/x-rtcp:\n",
"direction": "src",
"presence": "request"
}
},
"properties": {
"latency": {
"blurb": "Amount of ms to buffer",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "200",
"max": "-1",
"min": "0",
"mutable": "null",
"readable": true,
"type": "guint",
"writable": true
}
},
"rank": "none",
"signals": {
"clear-pt-map": {
"args": [],
"return-type": "void",
"when": "last"
},
"on-bye-ssrc": {
"args": [
{
"name": "arg0",
"type": "guint"
},
{
"name": "arg1",
"type": "guint"
}
],
"return-type": "void",
"when": "last"
},
"on-bye-timeout": {
"args": [
{
"name": "arg0",
"type": "guint"
},
{
"name": "arg1",
"type": "guint"
}
],
"return-type": "void",
"when": "last"
},
"on-npt-stop": {
"args": [
{
"name": "arg0",
"type": "guint"
},
{
"name": "arg1",
"type": "guint"
}
],
"return-type": "void",
"when": "last"
},
"on-timeout": {
"args": [
{
"name": "arg0",
"type": "guint"
},
{
"name": "arg1",
"type": "guint"
}
],
"return-type": "void",
"when": "last"
},
"request-pt-map": {
"args": [
{
"name": "arg0",
"type": "guint"
},
{
"name": "arg1",
"type": "guint"
}
],
"return-type": "GstCaps",
"when": "last"
}
}
}, },
"rmdemux": { "rmdemux": {
"author": "David Schleef <ds@schleef.org>", "author": "David Schleef <ds@schleef.org>",
@ -740,7 +543,6 @@
"GObject" "GObject"
], ],
"klass": "Codec/Demuxer", "klass": "Codec/Demuxer",
"long-name": "RealMedia Demuxer",
"pad-templates": { "pad-templates": {
"audio_%%u": { "audio_%%u": {
"caps": "ANY", "caps": "ANY",
@ -758,25 +560,7 @@
"presence": "sometimes" "presence": "sometimes"
} }
}, },
"rank": "primary", "rank": "primary"
"signals": {}
},
"rtspreal": {
"author": "Wim Taymans <wim.taymans@gmail.com>",
"description": "Extends RTSP so that it can handle RealMedia setup",
"hierarchy": [
"GstRTSPReal",
"GstElement",
"GstObject",
"GInitiallyUnowned",
"GObject"
],
"interfaces": [
"GstRTSPExtension"
],
"klass": "Network/Extension/Protocol",
"long-name": "RealMedia RTSP Extension",
"rank": "marginal"
} }
}, },
"filename": "gstrealmedia", "filename": "gstrealmedia",

View file

@ -1,712 +0,0 @@
/* 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <string.h>
#include <stdlib.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) == ';') || ((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);
if (ch == '\\')
ch = NEXT_CHAR (scan);
}
scan->val[i] = '\0';
if (ch == delim)
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_destroy (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 = (GstASMOp) 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 = (GstASMOp) 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");
g_free (key);
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;
}
static 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;
}
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\\\";";
static const gchar rules3[] =
"#(($Bandwidth >= 27500) && ($OldPNMPlayer)),AverageBandwidth=27500,priority=9,PNMKeyframeRule=T;#(($Bandwidth >= 27500) && ($OldPNMPlayer)),AverageBandwidth=0,priority=5,PNMNonKeyframeRule=T;#(($Bandwidth < 27500) && ($OldPNMPlayer)),TimestampDelivery=T,DropByN=T,priority=9,PNMThinningRule=T;#($Bandwidth < 13899),TimestampDelivery=T,DropByN=T,priority=9;#($Bandwidth >= 13899) && ($Bandwidth < 19000),AverageBandwidth=13899,Priority=9;#($Bandwidth >= 13899) && ($Bandwidth < 19000),AverageBandwidth=0,Priority=5,OnDepend=\\\"4\\\";#($Bandwidth >= 19000) && ($Bandwidth < 27500),AverageBandwidth=19000,Priority=9;#($Bandwidth >= 19000) && ($Bandwidth < 27500),AverageBandwidth=0,Priority=5,OnDepend=\\\"6\\\";#($Bandwidth >= 27500) && ($Bandwidth < 132958),AverageBandwidth=27500,Priority=9;#($Bandwidth >= 27500) && ($Bandwidth < 132958),AverageBandwidth=0,Priority=5,OnDepend=\\\"8\\\";#($Bandwidth >= 132958) && ($Bandwidth < 187958),AverageBandwidth=132958,Priority=9;#($Bandwidth >= 132958) && ($Bandwidth < 187958),AverageBandwidth=0,Priority=5,OnDepend=\\\"10\\\";#($Bandwidth >= 187958),AverageBandwidth=187958,Priority=9;#($Bandwidth >= 187958),AverageBandwidth=0,Priority=5,OnDepend=\\\"12\\\";";
vars = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (vars, (gchar *) "Bandwidth", (gchar *) "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_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 (rules3);
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]);
}
g_hash_table_destroy (vars);
return 0;
}
#endif

View file

@ -1,115 +0,0 @@
/* 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, 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

@ -1,477 +0,0 @@
/* GStreamer
* Copyright (C) <2008> 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <string.h>
#include "gstrdtbuffer.h"
gboolean
gst_rdt_buffer_validate_data (guint8 * data, guint len)
{
return TRUE;
}
gboolean
gst_rdt_buffer_validate (GstBuffer * buffer)
{
return TRUE;
}
guint
gst_rdt_buffer_get_packet_count (GstBuffer * buffer)
{
GstRDTPacket packet;
guint count;
g_return_val_if_fail (GST_IS_BUFFER (buffer), 0);
count = 0;
if (gst_rdt_buffer_get_first_packet (buffer, &packet)) {
do {
count++;
} while (gst_rdt_packet_move_to_next (&packet));
}
return count;
}
static gboolean
read_packet_header (GstRDTPacket * packet)
{
GstMapInfo map;
guint8 *data;
gsize size;
guint offset;
guint length;
guint length_offset;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (GST_IS_BUFFER (packet->buffer), FALSE);
gst_buffer_map (packet->buffer, &map, GST_MAP_READ);
data = map.data;
size = map.size;
offset = packet->offset;
/* check if we are at the end of the buffer, we add 3 because we also want to
* ensure we can read the type, which is always at offset 1 and 2 bytes long. */
if (offset + 3 > size)
goto packet_end;
/* read type */
packet->type = GST_READ_UINT16_BE (&data[offset + 1]);
length = -1;
length_offset = -1;
/* figure out the length of the packet, this depends on the type */
if (GST_RDT_IS_DATA_TYPE (packet->type)) {
if (data[offset] & 0x80)
/* length is present */
length_offset = 3;
} else {
switch (packet->type) {
case GST_RDT_TYPE_ASMACTION:
if (data[offset] & 0x80)
length_offset = 5;
break;
case GST_RDT_TYPE_BWREPORT:
if (data[offset] & 0x80)
length_offset = 3;
break;
case GST_RDT_TYPE_ACK:
if (data[offset] & 0x80)
length_offset = 3;
break;
case GST_RDT_TYPE_RTTREQ:
length = 3;
break;
case GST_RDT_TYPE_RTTRESP:
length = 11;
break;
case GST_RDT_TYPE_CONGESTION:
length = 11;
break;
case GST_RDT_TYPE_STREAMEND:
length = 9;
/* total_reliable */
if (data[offset] & 0x80)
length += 2;
/* stream_id_expansion */
if ((data[offset] & 0x7c) == 0x7c)
length += 2;
/* ext_flag, FIXME, get string length */
if ((data[offset] & 0x1) == 0x1)
length += 7;
break;
case GST_RDT_TYPE_REPORT:
if (data[offset] & 0x80)
length_offset = 3;
break;
case GST_RDT_TYPE_LATENCY:
if (data[offset] & 0x80)
length_offset = 3;
break;
case GST_RDT_TYPE_INFOREQ:
length = 3;
/* request_time_ms */
if (data[offset] & 0x2)
length += 2;
break;
case GST_RDT_TYPE_INFORESP:
length = 3;
/* has_rtt_info */
if (data[offset] & 0x4) {
length += 4;
/* is_delayed */
if (data[offset] & 0x2) {
length += 4;
}
}
if (data[offset] & 0x1) {
/* buffer_info_count, FIXME read and skip */
length += 2;
}
break;
case GST_RDT_TYPE_AUTOBW:
if (data[offset] & 0x80)
length_offset = 3;
break;
case GST_RDT_TYPE_INVALID:
default:
goto unknown_packet;
}
}
if (length != -1) {
/* we have a fixed length */
packet->length = length;
} else if (length_offset != -1) {
/* we can read the length from an offset */
packet->length = GST_READ_UINT16_BE (&data[length_offset]);
} else {
/* length is remainder of packet */
packet->length = size - offset;
}
gst_buffer_unmap (packet->buffer, &map);
/* the length should be smaller than the remaining size */
if (packet->length + offset > size)
goto invalid_length;
return TRUE;
/* ERRORS */
packet_end:
{
gst_buffer_unmap (packet->buffer, &map);
return FALSE;
}
unknown_packet:
{
packet->type = GST_RDT_TYPE_INVALID;
gst_buffer_unmap (packet->buffer, &map);
return FALSE;
}
invalid_length:
{
packet->type = GST_RDT_TYPE_INVALID;
packet->length = 0;
return FALSE;
}
}
gboolean
gst_rdt_buffer_get_first_packet (GstBuffer * buffer, GstRDTPacket * packet)
{
g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE);
g_return_val_if_fail (packet != NULL, FALSE);
/* init to 0 */
packet->buffer = buffer;
packet->offset = 0;
packet->type = GST_RDT_TYPE_INVALID;
memset (&packet->map, 0, sizeof (GstMapInfo));
if (!read_packet_header (packet))
return FALSE;
return TRUE;
}
gboolean
gst_rdt_packet_move_to_next (GstRDTPacket * packet)
{
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->type != GST_RDT_TYPE_INVALID, FALSE);
g_return_val_if_fail (GST_IS_BUFFER (packet->buffer), FALSE);
/* if we have an invalid packet, it must be the last,
* return FALSE */
if (packet->type == GST_RDT_TYPE_INVALID)
goto end;
/* move to next packet */
packet->offset += packet->length;
/* try to read new header */
if (!read_packet_header (packet))
goto end;
return TRUE;
/* ERRORS */
end:
{
packet->type = GST_RDT_TYPE_INVALID;
return FALSE;
}
}
GstRDTType
gst_rdt_packet_get_type (GstRDTPacket * packet)
{
g_return_val_if_fail (packet != NULL, GST_RDT_TYPE_INVALID);
g_return_val_if_fail (packet->type != GST_RDT_TYPE_INVALID,
GST_RDT_TYPE_INVALID);
return packet->type;
}
guint16
gst_rdt_packet_get_length (GstRDTPacket * packet)
{
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (packet->type != GST_RDT_TYPE_INVALID, 0);
return packet->length;
}
GstBuffer *
gst_rdt_packet_to_buffer (GstRDTPacket * packet)
{
GstBuffer *result;
g_return_val_if_fail (packet != NULL, NULL);
g_return_val_if_fail (packet->type != GST_RDT_TYPE_INVALID, NULL);
result =
gst_buffer_copy_region (packet->buffer, GST_BUFFER_COPY_ALL,
packet->offset, packet->length);
/* timestamp applies to all packets in this buffer */
GST_BUFFER_TIMESTAMP (result) = GST_BUFFER_TIMESTAMP (packet->buffer);
return result;
}
gint
gst_rdt_buffer_compare_seqnum (guint16 seqnum1, guint16 seqnum2)
{
return (gint16) (seqnum2 - seqnum1);
}
guint16
gst_rdt_packet_data_get_seq (GstRDTPacket * packet)
{
GstMapInfo map;
guint header;
guint16 result;
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), FALSE);
gst_buffer_map (packet->buffer, &map, GST_MAP_READ);
/* skip header bits */
header = packet->offset + 1;
/* read seq_no */
result = GST_READ_UINT16_BE (&map.data[header]);
gst_buffer_unmap (packet->buffer, &map);
return result;
}
guint8 *
gst_rdt_packet_data_map (GstRDTPacket * packet, guint * size)
{
GstMapInfo map;
guint header;
gboolean length_included_flag;
gboolean need_reliable_flag;
guint8 stream_id;
guint8 asm_rule_number;
g_return_val_if_fail (packet != NULL, NULL);
g_return_val_if_fail (packet->map.data == NULL, NULL);
g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), NULL);
gst_buffer_map (packet->buffer, &map, GST_MAP_READ);
header = packet->offset;
length_included_flag = (map.data[header] & 0x80) == 0x80;
need_reliable_flag = (map.data[header] & 0x40) == 0x40;
stream_id = (map.data[header] & 0x3e) >> 1;
/* skip seq_no and header bits */
header += 3;
if (length_included_flag) {
/* skip length */
header += 2;
}
asm_rule_number = (map.data[header] & 0x3f);
/* skip timestamp and asm_rule_number */
header += 5;
if (stream_id == 0x1f) {
/* skip stream_id_expansion */
header += 2;
}
if (need_reliable_flag) {
/* skip total_reliable */
header += 2;
}
if (asm_rule_number == 63) {
/* skip asm_rule_number_expansion */
header += 2;
}
if (size)
*size = packet->length - (header - packet->offset);
packet->map = map;
return &map.data[header];
}
gboolean
gst_rdt_packet_data_unmap (GstRDTPacket * packet)
{
g_return_val_if_fail (packet != NULL, FALSE);
g_return_val_if_fail (packet->map.data != NULL, FALSE);
gst_buffer_unmap (packet->buffer, &packet->map);
packet->map.data = NULL;
return TRUE;
}
guint16
gst_rdt_packet_data_get_stream_id (GstRDTPacket * packet)
{
GstMapInfo map;
guint16 result;
guint header;
gboolean length_included_flag;
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), 0);
gst_buffer_map (packet->buffer, &map, GST_MAP_READ);
header = packet->offset;
length_included_flag = (map.data[header] & 0x80) == 0x80;
result = (map.data[header] & 0x3e) >> 1;
if (result == 31) {
/* skip seq_no and header bits */
header += 3;
if (length_included_flag) {
/* skip length */
header += 2;
}
/* skip asm_rule_number and timestamp */
header += 5;
/* stream_id_expansion */
result = GST_READ_UINT16_BE (&map.data[header]);
}
gst_buffer_unmap (packet->buffer, &map);
return result;
}
guint32
gst_rdt_packet_data_get_timestamp (GstRDTPacket * packet)
{
GstMapInfo map;
guint header;
gboolean length_included_flag;
guint32 result;
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), 0);
gst_buffer_map (packet->buffer, &map, GST_MAP_READ);
header = packet->offset;
length_included_flag = (map.data[header] & 0x80) == 0x80;
/* skip seq_no and header bits */
header += 3;
if (length_included_flag) {
/* skip length */
header += 2;
}
/* skip asm_rule_number */
header += 1;
/* get timestamp */
result = GST_READ_UINT32_BE (&map.data[header]);
gst_buffer_unmap (packet->buffer, &map);
return result;
}
guint8
gst_rdt_packet_data_get_flags (GstRDTPacket * packet)
{
GstMapInfo map;
guint8 result;
guint header;
gboolean length_included_flag;
g_return_val_if_fail (packet != NULL, 0);
g_return_val_if_fail (GST_RDT_IS_DATA_TYPE (packet->type), 0);
gst_buffer_map (packet->buffer, &map, GST_MAP_READ);
header = packet->offset;
length_included_flag = (map.data[header] & 0x80) == 0x80;
/* skip seq_no and header bits */
header += 3;
if (length_included_flag) {
/* skip length */
header += 2;
}
/* get flags */
result = map.data[header];
gst_buffer_unmap (packet->buffer, &map);
return result;
}

View file

@ -1,121 +0,0 @@
/* GStreamer
* Copyright (C) <2008> Wim Taymans <wim.taymans@gmail.com>
*
* gstrdtbuffer.h: various helper functions to manipulate buffers
* with RDT payload.
*
* 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_RDTBUFFER_H__
#define __GST_RDTBUFFER_H__
#include <gst/gst.h>
G_BEGIN_DECLS
/**
* GstRDTType:
* @GST_RDT_TYPE_INVALID:
* @GST_RDT_TYPE_ASMACTION:
* @GST_RDT_TYPE_ACK:
* @GST_RDT_TYPE_RTTREQ:
* @GST_RDT_TYPE_RTTRESP:
* @GST_RDT_TYPE_CONGESTION:
* @GST_RDT_TYPE_STREAMEND:
* @GST_RDT_TYPE_LATENCY:
* @GST_RDT_TYPE_INFOREQ:
* @GST_RDT_TYPE_INFORESP:
* @GST_RDT_TYPE_AUTOBW:
*
* Different RDT packet types.
*/
typedef enum
{
GST_RDT_TYPE_INVALID = 0xffff,
GST_RDT_TYPE_ASMACTION = 0xff00,
GST_RDT_TYPE_BWREPORT = 0xff01,
GST_RDT_TYPE_ACK = 0xff02,
GST_RDT_TYPE_RTTREQ = 0xff03,
GST_RDT_TYPE_RTTRESP = 0xff04,
GST_RDT_TYPE_CONGESTION = 0xff05,
GST_RDT_TYPE_STREAMEND = 0xff06,
GST_RDT_TYPE_REPORT = 0xff07,
GST_RDT_TYPE_LATENCY = 0xff08,
GST_RDT_TYPE_INFOREQ = 0xff09,
GST_RDT_TYPE_INFORESP = 0xff0a,
GST_RDT_TYPE_AUTOBW = 0xff0b
} GstRDTType;
/**
* GST_RDT_IS_DATA_TYPE:
* @t: the #GstRDTType to check
*
* Check if @t is a data packet type.
*/
#define GST_RDT_IS_DATA_TYPE(t) ((t) < 0xff00)
typedef struct _GstRDTPacket GstRDTPacket;
/**
* GstRDTPacket:
* @buffer: pointer to RDT buffer
* @offset: offset of packet in buffer data
*
* Data structure that points to a packet at @offset in @buffer.
* The size of the structure is made public to allow stack allocations.
*/
struct _GstRDTPacket
{
GstBuffer *buffer;
guint offset;
/*< private >*/
GstRDTType type; /* type of current packet */
guint16 length; /* length of current packet in bytes */
GstMapInfo map; /* last mapped data */
};
/* validate buffers */
gboolean gst_rdt_buffer_validate_data (guint8 *data, guint len);
gboolean gst_rdt_buffer_validate (GstBuffer *buffer);
/* retrieving packets */
guint gst_rdt_buffer_get_packet_count (GstBuffer *buffer);
gboolean gst_rdt_buffer_get_first_packet (GstBuffer *buffer, GstRDTPacket *packet);
gboolean gst_rdt_packet_move_to_next (GstRDTPacket *packet);
/* working with packets */
GstRDTType gst_rdt_packet_get_type (GstRDTPacket *packet);
guint16 gst_rdt_packet_get_length (GstRDTPacket *packet);
GstBuffer* gst_rdt_packet_to_buffer (GstRDTPacket *packet);
/* data packets */
guint16 gst_rdt_packet_data_get_seq (GstRDTPacket *packet);
guint8 * gst_rdt_packet_data_map (GstRDTPacket *packet, guint *size);
gboolean gst_rdt_packet_data_unmap (GstRDTPacket *packet);
guint16 gst_rdt_packet_data_get_stream_id (GstRDTPacket *packet);
guint32 gst_rdt_packet_data_get_timestamp (GstRDTPacket *packet);
guint8 gst_rdt_packet_data_get_flags (GstRDTPacket * packet);
/* utils */
gint gst_rdt_buffer_compare_seqnum (guint16 seqnum1, guint16 seqnum2);
G_END_DECLS
#endif /* __GST_RDTBUFFER_H__ */

View file

@ -2,14 +2,6 @@ real_sources = [
'rademux.c', 'rademux.c',
'rmdemux.c', 'rmdemux.c',
'rmutils.c', 'rmutils.c',
'rdtdepay.c',
'rdtmanager.c',
'rtspreal.c',
'realhash.c',
'asmrules.c',
'rdtjitterbuffer.c',
'gstrdtbuffer.c',
'pnmsrc.c',
'realmedia.c' 'realmedia.c'
] ]

View file

@ -1,496 +0,0 @@
/* GStreamer
* Copyright (C) <2006> Lutz Mueller <lutz at topfrose dot de>
* <2006> Wim Taymans <wim@fluendo.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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
#include "gstrdtbuffer.h"
#include "rdtdepay.h"
GST_DEBUG_CATEGORY_STATIC (rdtdepay_debug);
#define GST_CAT_DEFAULT rdtdepay_debug
/* RDTDepay signals and args */
enum
{
/* FILL ME */
LAST_SIGNAL
};
enum
{
PROP_0,
};
static GstStaticPadTemplate gst_rdt_depay_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("application/vnd.rn-realmedia")
);
static GstStaticPadTemplate gst_rdt_depay_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("application/x-rdt, "
"media = (string) \"application\", "
"clock-rate = (int) [1, MAX ], "
"encoding-name = (string) \"X-REAL-RDT\""
/* All optional parameters
*
* "config="
*/
)
);
#define gst_rdt_depay_parent_class parent_class
G_DEFINE_TYPE (GstRDTDepay, gst_rdt_depay, GST_TYPE_ELEMENT);
GST_ELEMENT_REGISTER_DEFINE (rdtdepay, "rdtdepay",
GST_RANK_MARGINAL, GST_TYPE_RDT_DEPAY);
static void gst_rdt_depay_finalize (GObject * object);
static GstStateChangeReturn gst_rdt_depay_change_state (GstElement *
element, GstStateChange transition);
static gboolean gst_rdt_depay_sink_event (GstPad * pad, GstObject * parent,
GstEvent * event);
static GstFlowReturn gst_rdt_depay_chain (GstPad * pad, GstObject * parent,
GstBuffer * buf);
static void
gst_rdt_depay_class_init (GstRDTDepayClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
parent_class = g_type_class_peek_parent (klass);
gobject_class->finalize = gst_rdt_depay_finalize;
gstelement_class->change_state = gst_rdt_depay_change_state;
gst_element_class_add_static_pad_template (gstelement_class,
&gst_rdt_depay_src_template);
gst_element_class_add_static_pad_template (gstelement_class,
&gst_rdt_depay_sink_template);
gst_element_class_set_static_metadata (gstelement_class, "RDT packet parser",
"Codec/Depayloader/Network",
"Extracts RealMedia from RDT packets",
"Lutz Mueller <lutz at topfrose dot de>, "
"Wim Taymans <wim@fluendo.com>");
GST_DEBUG_CATEGORY_INIT (rdtdepay_debug, "rdtdepay",
0, "Depayloader for RDT RealMedia packets");
}
static void
gst_rdt_depay_init (GstRDTDepay * rdtdepay)
{
rdtdepay->sinkpad =
gst_pad_new_from_static_template (&gst_rdt_depay_sink_template, "sink");
gst_pad_set_chain_function (rdtdepay->sinkpad, gst_rdt_depay_chain);
gst_pad_set_event_function (rdtdepay->sinkpad, gst_rdt_depay_sink_event);
gst_element_add_pad (GST_ELEMENT_CAST (rdtdepay), rdtdepay->sinkpad);
rdtdepay->srcpad =
gst_pad_new_from_static_template (&gst_rdt_depay_src_template, "src");
gst_element_add_pad (GST_ELEMENT_CAST (rdtdepay), rdtdepay->srcpad);
}
static void
gst_rdt_depay_finalize (GObject * object)
{
GstRDTDepay *rdtdepay;
rdtdepay = GST_RDT_DEPAY (object);
if (rdtdepay->header)
gst_buffer_unref (rdtdepay->header);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gboolean
gst_rdt_depay_setcaps (GstPad * pad, GstCaps * caps)
{
GstStructure *structure;
GstRDTDepay *rdtdepay;
GstCaps *srccaps;
gint clock_rate = 1000; /* default */
const GValue *value;
GstBuffer *header;
rdtdepay = GST_RDT_DEPAY (GST_PAD_PARENT (pad));
structure = gst_caps_get_structure (caps, 0);
if (gst_structure_has_field (structure, "clock-rate"))
gst_structure_get_int (structure, "clock-rate", &clock_rate);
/* config contains the RealMedia header as a buffer. */
value = gst_structure_get_value (structure, "config");
if (!value)
goto no_header;
header = gst_value_get_buffer (value);
if (!header)
goto no_header;
/* get other values for newsegment */
value = gst_structure_get_value (structure, "npt-start");
if (value && G_VALUE_HOLDS_UINT64 (value))
rdtdepay->npt_start = g_value_get_uint64 (value);
else
rdtdepay->npt_start = 0;
GST_DEBUG_OBJECT (rdtdepay, "NPT start %" G_GUINT64_FORMAT,
rdtdepay->npt_start);
value = gst_structure_get_value (structure, "npt-stop");
if (value && G_VALUE_HOLDS_UINT64 (value))
rdtdepay->npt_stop = g_value_get_uint64 (value);
else
rdtdepay->npt_stop = -1;
GST_DEBUG_OBJECT (rdtdepay, "NPT stop %" G_GUINT64_FORMAT,
rdtdepay->npt_stop);
value = gst_structure_get_value (structure, "play-speed");
if (value && G_VALUE_HOLDS_DOUBLE (value))
rdtdepay->play_speed = g_value_get_double (value);
else
rdtdepay->play_speed = 1.0;
value = gst_structure_get_value (structure, "play-scale");
if (value && G_VALUE_HOLDS_DOUBLE (value))
rdtdepay->play_scale = g_value_get_double (value);
else
rdtdepay->play_scale = 1.0;
/* caps seem good, configure element */
rdtdepay->clock_rate = clock_rate;
/* set caps on pad and on header */
srccaps = gst_caps_new_empty_simple ("application/vnd.rn-realmedia");
gst_pad_set_caps (rdtdepay->srcpad, srccaps);
gst_caps_unref (srccaps);
if (rdtdepay->header)
gst_buffer_unref (rdtdepay->header);
rdtdepay->header = gst_buffer_ref (header);
return TRUE;
/* ERRORS */
no_header:
{
GST_ERROR_OBJECT (rdtdepay, "no header found in caps, no 'config' field");
return FALSE;
}
}
static gboolean
gst_rdt_depay_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
GstRDTDepay *depay;
gboolean res = TRUE;
depay = GST_RDT_DEPAY (parent);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_CAPS:
{
GstCaps *caps;
gst_event_parse_caps (event, &caps);
res = gst_rdt_depay_setcaps (pad, caps);
gst_event_unref (event);
break;
}
case GST_EVENT_FLUSH_STOP:
res = gst_pad_push_event (depay->srcpad, event);
gst_segment_init (&depay->segment, GST_FORMAT_UNDEFINED);
depay->need_newsegment = TRUE;
depay->next_seqnum = -1;
break;
case GST_EVENT_SEGMENT:
{
gst_event_copy_segment (event, &depay->segment);
/* don't pass the event downstream, we generate our own segment
* including the NTP time and other things we receive in caps */
gst_event_unref (event);
break;
}
default:
/* pass other events forward */
res = gst_pad_push_event (depay->srcpad, event);
break;
}
return res;
}
static GstEvent *
create_segment_event (GstRDTDepay * depay, gboolean update,
GstClockTime position)
{
GstSegment segment;
gst_segment_init (&segment, GST_FORMAT_TIME);
segment.rate = depay->play_speed;
segment.applied_rate = depay->play_scale;
segment.start = position;
if (depay->npt_stop != -1)
segment.stop = depay->npt_stop - depay->npt_start;
else
segment.stop = -1;
segment.time = position + depay->npt_start;
return gst_event_new_segment (&segment);
}
static GstFlowReturn
gst_rdt_depay_push (GstRDTDepay * rdtdepay, GstBuffer * buffer)
{
GstFlowReturn ret;
if (rdtdepay->need_newsegment) {
GstEvent *event;
event = create_segment_event (rdtdepay, FALSE, 0);
gst_pad_push_event (rdtdepay->srcpad, event);
rdtdepay->need_newsegment = FALSE;
}
if (rdtdepay->discont) {
GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
rdtdepay->discont = FALSE;
}
ret = gst_pad_push (rdtdepay->srcpad, buffer);
return ret;
}
static GstFlowReturn
gst_rdt_depay_handle_data (GstRDTDepay * rdtdepay, GstClockTime outtime,
GstRDTPacket * packet)
{
GstFlowReturn ret;
GstBuffer *outbuf;
GstMapInfo outmap;
guint8 *data, *outdata;
guint size;
guint16 stream_id;
guint32 timestamp;
gint gap;
guint16 seqnum;
guint8 flags;
guint16 outflags;
/* get pointers to the packet data */
data = gst_rdt_packet_data_map (packet, &size);
outbuf = gst_buffer_new_and_alloc (12 + size);
GST_BUFFER_TIMESTAMP (outbuf) = outtime;
GST_DEBUG_OBJECT (rdtdepay, "have size %u", size);
/* copy over some things */
stream_id = gst_rdt_packet_data_get_stream_id (packet);
timestamp = gst_rdt_packet_data_get_timestamp (packet);
flags = gst_rdt_packet_data_get_flags (packet);
seqnum = gst_rdt_packet_data_get_seq (packet);
GST_DEBUG_OBJECT (rdtdepay, "stream_id %u, timestamp %u, seqnum %d, flags %d",
stream_id, timestamp, seqnum, flags);
if (rdtdepay->next_seqnum != -1) {
gap = gst_rdt_buffer_compare_seqnum (seqnum, rdtdepay->next_seqnum);
/* if we have no gap, all is fine */
if (G_UNLIKELY (gap != 0)) {
GST_LOG_OBJECT (rdtdepay, "got packet %u, expected %u, gap %d", seqnum,
rdtdepay->next_seqnum, gap);
if (gap < 0) {
/* seqnum > next_seqnum, we are missing some packets, this is always a
* DISCONT. */
GST_LOG_OBJECT (rdtdepay, "%d missing packets", gap);
rdtdepay->discont = TRUE;
} else {
/* seqnum < next_seqnum, we have seen this packet before or the sender
* could be restarted. If the packet is not too old, we throw it away as
* a duplicate, otherwise we mark discont and continue. 100 misordered
* packets is a good threshold. See also RFC 4737. */
if (gap < 100)
goto dropping;
GST_LOG_OBJECT (rdtdepay,
"%d > 100, packet too old, sender likely restarted", gap);
rdtdepay->discont = TRUE;
}
}
}
rdtdepay->next_seqnum = (seqnum + 1);
if (rdtdepay->next_seqnum == 0xff00)
rdtdepay->next_seqnum = 0;
if ((flags & 1) == 0)
outflags = 2;
else
outflags = 0;
gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE);
outdata = outmap.data;
GST_WRITE_UINT16_BE (outdata + 0, 0); /* version */
GST_WRITE_UINT16_BE (outdata + 2, size + 12); /* length */
GST_WRITE_UINT16_BE (outdata + 4, stream_id); /* stream */
GST_WRITE_UINT32_BE (outdata + 6, timestamp); /* timestamp */
GST_WRITE_UINT16_BE (outdata + 10, outflags); /* flags */
memcpy (outdata + 12, data, size);
gst_buffer_unmap (outbuf, &outmap);
gst_buffer_resize (outbuf, 0, 12 + size);
gst_rdt_packet_data_unmap (packet);
GST_DEBUG_OBJECT (rdtdepay, "Pushing packet, outtime %" GST_TIME_FORMAT,
GST_TIME_ARGS (outtime));
ret = gst_rdt_depay_push (rdtdepay, outbuf);
return ret;
/* ERRORS */
dropping:
{
GST_WARNING_OBJECT (rdtdepay, "%d <= 100, dropping old packet", gap);
return GST_FLOW_OK;
}
}
static GstFlowReturn
gst_rdt_depay_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
{
GstRDTDepay *rdtdepay;
GstFlowReturn ret;
GstClockTime timestamp;
gboolean more;
GstRDTPacket packet;
rdtdepay = GST_RDT_DEPAY (parent);
if (GST_BUFFER_IS_DISCONT (buf)) {
GST_LOG_OBJECT (rdtdepay, "received discont");
rdtdepay->discont = TRUE;
}
if (rdtdepay->header) {
GstBuffer *out;
out = rdtdepay->header;
rdtdepay->header = NULL;
/* push header data first */
gst_rdt_depay_push (rdtdepay, out);
}
/* save timestamp */
timestamp = GST_BUFFER_TIMESTAMP (buf);
ret = GST_FLOW_OK;
GST_LOG_OBJECT (rdtdepay, "received buffer timestamp %" GST_TIME_FORMAT,
GST_TIME_ARGS (timestamp));
/* data is in RDT format. */
more = gst_rdt_buffer_get_first_packet (buf, &packet);
while (more) {
GstRDTType type;
type = gst_rdt_packet_get_type (&packet);
GST_DEBUG_OBJECT (rdtdepay, "Have packet of type %04x", type);
if (GST_RDT_IS_DATA_TYPE (type)) {
GST_DEBUG_OBJECT (rdtdepay, "We have a data packet");
ret = gst_rdt_depay_handle_data (rdtdepay, timestamp, &packet);
} else {
switch (type) {
default:
GST_DEBUG_OBJECT (rdtdepay, "Ignoring packet");
break;
}
}
if (ret != GST_FLOW_OK)
break;
more = gst_rdt_packet_move_to_next (&packet);
}
gst_buffer_unref (buf);
return ret;
}
static GstStateChangeReturn
gst_rdt_depay_change_state (GstElement * element, GstStateChange transition)
{
GstRDTDepay *rdtdepay;
GstStateChangeReturn ret;
rdtdepay = GST_RDT_DEPAY (element);
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
gst_segment_init (&rdtdepay->segment, GST_FORMAT_UNDEFINED);
rdtdepay->next_seqnum = -1;
rdtdepay->need_newsegment = TRUE;
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
if (rdtdepay->header)
gst_buffer_unref (rdtdepay->header);
rdtdepay->header = NULL;
break;
case GST_STATE_CHANGE_READY_TO_NULL:
break;
default:
break;
}
return ret;
}

View file

@ -1,74 +0,0 @@
/* GStreamer
* Copyright (C) <2006> Lutz Mueller <lutz at topfrose dot de>
* <2006> Wim Taymans <wim@fluendo.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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_RDT_DEPAY_H__
#define __GST_RDT_DEPAY_H__
#include <gst/gst.h>
G_BEGIN_DECLS
#define GST_TYPE_RDT_DEPAY \
(gst_rdt_depay_get_type())
#define GST_RDT_DEPAY(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RDT_DEPAY,GstRDTDepay))
#define GST_RDT_DEPAY_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RDT_DEPAY,GstRDTDepayClass))
#define GST_IS_RDT_DEPAY(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RDT_DEPAY))
#define GST_IS_RDT_DEPAY_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RDT_DEPAY))
typedef struct _GstRDTDepay GstRDTDepay;
typedef struct _GstRDTDepayClass GstRDTDepayClass;
struct _GstRDTDepay
{
GstElement parent;
GstPad *sinkpad;
GstPad *srcpad;
guint clock_rate;
GstClockTime npt_start;
GstClockTime npt_stop;
gdouble play_speed;
gdouble play_scale;
guint32 next_seqnum;
gboolean discont;
gboolean need_newsegment;
GstSegment segment;
GstBuffer *header;
};
struct _GstRDTDepayClass
{
GstElementClass parent_class;
};
GType gst_rdt_depay_get_type (void);
GST_ELEMENT_REGISTER_DECLARE (rdtdepay);
G_END_DECLS
#endif /* __GST_RDT_DEPAY_H__ */

View file

@ -1,531 +0,0 @@
/* 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <string.h>
#include <stdlib.h>
#include "rdtjitterbuffer.h"
#include "gstrdtbuffer.h"
GST_DEBUG_CATEGORY_STATIC (rdt_jitter_buffer_debug);
#define GST_CAT_DEFAULT rdt_jitter_buffer_debug
#define MAX_WINDOW RDT_JITTER_BUFFER_MAX_WINDOW
#define MAX_TIME (2 * GST_SECOND)
/* signals and args */
enum
{
LAST_SIGNAL
};
enum
{
PROP_0
};
/* GObject vmethods */
static void rdt_jitter_buffer_finalize (GObject * object);
/* static guint rdt_jitter_buffer_signals[LAST_SIGNAL] = { 0 }; */
G_DEFINE_TYPE (RDTJitterBuffer, rdt_jitter_buffer, G_TYPE_OBJECT);
static void
rdt_jitter_buffer_class_init (RDTJitterBufferClass * klass)
{
GObjectClass *gobject_class;
gobject_class = (GObjectClass *) klass;
gobject_class->finalize = rdt_jitter_buffer_finalize;
GST_DEBUG_CATEGORY_INIT (rdt_jitter_buffer_debug, "rdtjitterbuffer", 0,
"RDT Jitter Buffer");
}
static void
rdt_jitter_buffer_init (RDTJitterBuffer * jbuf)
{
jbuf->packets = g_queue_new ();
rdt_jitter_buffer_reset_skew (jbuf);
}
static void
rdt_jitter_buffer_finalize (GObject * object)
{
RDTJitterBuffer *jbuf;
jbuf = RDT_JITTER_BUFFER_CAST (object);
rdt_jitter_buffer_flush (jbuf);
g_queue_free (jbuf->packets);
G_OBJECT_CLASS (rdt_jitter_buffer_parent_class)->finalize (object);
}
/**
* rdt_jitter_buffer_new:
*
* Create an #RDTJitterBuffer.
*
* Returns: a new #RDTJitterBuffer. Use g_object_unref() after usage.
*/
RDTJitterBuffer *
rdt_jitter_buffer_new (void)
{
RDTJitterBuffer *jbuf;
jbuf = g_object_new (RDT_TYPE_JITTER_BUFFER, NULL);
return jbuf;
}
void
rdt_jitter_buffer_reset_skew (RDTJitterBuffer * jbuf)
{
jbuf->base_time = -1;
jbuf->base_rtptime = -1;
jbuf->ext_rtptime = -1;
jbuf->window_pos = 0;
jbuf->window_filling = TRUE;
jbuf->window_min = 0;
jbuf->skew = 0;
jbuf->prev_send_diff = -1;
}
/* For the clock skew we use a windowed low point averaging algorithm as can be
* found in http://www.grame.fr/pub/TR-050601.pdf. The idea is that the jitter is
* composed of:
*
* J = N + n
*
* N : a constant network delay.
* n : random added noise. The noise is concentrated around 0
*
* In the receiver we can track the elapsed time at the sender with:
*
* send_diff(i) = (Tsi - Ts0);
*
* Tsi : The time at the sender at packet i
* Ts0 : The time at the sender at the first packet
*
* This is the difference between the RDT timestamp in the first received packet
* and the current packet.
*
* At the receiver we have to deal with the jitter introduced by the network.
*
* recv_diff(i) = (Tri - Tr0)
*
* Tri : The time at the receiver at packet i
* Tr0 : The time at the receiver at the first packet
*
* Both of these values contain a jitter Ji, a jitter for packet i, so we can
* write:
*
* recv_diff(i) = (Cri + D + ni) - (Cr0 + D + n0))
*
* Cri : The time of the clock at the receiver for packet i
* D + ni : The jitter when receiving packet i
*
* We see that the network delay is irrelevant here as we can eliminate D:
*
* recv_diff(i) = (Cri + ni) - (Cr0 + n0))
*
* The drift is now expressed as:
*
* Drift(i) = recv_diff(i) - send_diff(i);
*
* We now keep the W latest values of Drift and find the minimum (this is the
* one with the lowest network jitter and thus the one which is least affected
* by it). We average this lowest value to smooth out the resulting network skew.
*
* Both the window and the weighting used for averaging influence the accuracy
* of the drift estimation. Finding the correct parameters turns out to be a
* compromise between accuracy and inertia.
*
* We use a 2 second window or up to 512 data points, which is statistically big
* enough to catch spikes (FIXME, detect spikes).
* We also use a rather large weighting factor (125) to smoothly adapt. During
* startup, when filling the window, we use a parabolic weighting factor, the
* more the window is filled, the faster we move to the detected possible skew.
*
* Returns: @time adjusted with the clock skew.
*/
static GstClockTime
calculate_skew (RDTJitterBuffer * jbuf, guint32 rtptime, GstClockTime time,
guint32 clock_rate)
{
guint64 ext_rtptime;
guint64 send_diff, recv_diff;
gint64 delta;
gint64 old;
gint pos, i;
GstClockTime gstrtptime, out_time;
//ext_rtptime = gst_rtp_buffer_ext_timestamp (&jbuf->ext_rtptime, rtptime);
ext_rtptime = rtptime;
gstrtptime = gst_util_uint64_scale_int (ext_rtptime, GST_SECOND, clock_rate);
again:
/* first time, lock on to time and gstrtptime */
if (jbuf->base_time == -1)
jbuf->base_time = time;
if (jbuf->base_rtptime == -1)
jbuf->base_rtptime = gstrtptime;
if (gstrtptime >= jbuf->base_rtptime)
send_diff = gstrtptime - jbuf->base_rtptime;
else {
/* elapsed time at sender, timestamps can go backwards and thus be smaller
* than our base time, take a new base time in that case. */
GST_DEBUG ("backward timestamps at server, taking new base time");
jbuf->base_rtptime = gstrtptime;
jbuf->base_time = time;
send_diff = 0;
}
GST_DEBUG ("extrtp %" G_GUINT64_FORMAT ", gstrtp %" GST_TIME_FORMAT ", base %"
GST_TIME_FORMAT ", send_diff %" GST_TIME_FORMAT, ext_rtptime,
GST_TIME_ARGS (gstrtptime), GST_TIME_ARGS (jbuf->base_rtptime),
GST_TIME_ARGS (send_diff));
if (jbuf->prev_send_diff != -1 && time != -1) {
gint64 delta_diff;
if (send_diff > jbuf->prev_send_diff)
delta_diff = send_diff - jbuf->prev_send_diff;
else
delta_diff = jbuf->prev_send_diff - send_diff;
/* server changed rtp timestamps too quickly, reset skew detection and start
* again. This value is sortof arbitrary and can be a bad measurement up if
* there are many packets missing because then we get a big gap that is
* unrelated to a timestamp switch. */
if (delta_diff > GST_SECOND) {
GST_DEBUG ("delta changed too quickly %" GST_TIME_FORMAT " reset skew",
GST_TIME_ARGS (delta_diff));
rdt_jitter_buffer_reset_skew (jbuf);
goto again;
}
}
jbuf->prev_send_diff = send_diff;
/* we don't have an arrival timestamp so we can't do skew detection. we
* should still apply a timestamp based on RDT timestamp and base_time */
if (time == -1)
goto no_skew;
/* elapsed time at receiver, includes the jitter */
recv_diff = time - jbuf->base_time;
GST_DEBUG ("time %" GST_TIME_FORMAT ", base %" GST_TIME_FORMAT ", recv_diff %"
GST_TIME_FORMAT, GST_TIME_ARGS (time), GST_TIME_ARGS (jbuf->base_time),
GST_TIME_ARGS (recv_diff));
/* measure the diff */
delta = ((gint64) recv_diff) - ((gint64) send_diff);
pos = jbuf->window_pos;
if (jbuf->window_filling) {
/* we are filling the window */
GST_DEBUG ("filling %d, delta %" G_GINT64_FORMAT, pos, delta);
jbuf->window[pos++] = delta;
/* calc the min delta we observed */
if (pos == 1 || delta < jbuf->window_min)
jbuf->window_min = delta;
if (send_diff >= MAX_TIME || pos >= MAX_WINDOW) {
jbuf->window_size = pos;
/* window filled */
GST_DEBUG ("min %" G_GINT64_FORMAT, jbuf->window_min);
/* the skew is now the min */
jbuf->skew = jbuf->window_min;
jbuf->window_filling = FALSE;
} else {
gint perc_time, perc_window, perc;
/* figure out how much we filled the window, this depends on the amount of
* time we have or the max number of points we keep. */
perc_time = send_diff * 100 / MAX_TIME;
perc_window = pos * 100 / MAX_WINDOW;
perc = MAX (perc_time, perc_window);
/* make a parabolic function, the closer we get to the MAX, the more value
* we give to the scaling factor of the new value */
perc = perc * perc;
/* quickly go to the min value when we are filling up, slowly when we are
* just starting because we're not sure it's a good value yet. */
jbuf->skew =
(perc * jbuf->window_min + ((10000 - perc) * jbuf->skew)) / 10000;
jbuf->window_size = pos + 1;
}
} else {
/* pick old value and store new value. We keep the previous value in order
* to quickly check if the min of the window changed */
old = jbuf->window[pos];
jbuf->window[pos++] = delta;
if (delta <= jbuf->window_min) {
/* if the new value we inserted is smaller or equal to the current min,
* it becomes the new min */
jbuf->window_min = delta;
} else if (old == jbuf->window_min) {
gint64 min = G_MAXINT64;
/* if we removed the old min, we have to find a new min */
for (i = 0; i < jbuf->window_size; i++) {
/* we found another value equal to the old min, we can stop searching now */
if (jbuf->window[i] == old) {
min = old;
break;
}
if (jbuf->window[i] < min)
min = jbuf->window[i];
}
jbuf->window_min = min;
}
/* average the min values */
jbuf->skew = (jbuf->window_min + (124 * jbuf->skew)) / 125;
GST_DEBUG ("delta %" G_GINT64_FORMAT ", new min: %" G_GINT64_FORMAT,
delta, jbuf->window_min);
}
/* wrap around in the window */
if (pos >= jbuf->window_size)
pos = 0;
jbuf->window_pos = pos;
no_skew:
/* the output time is defined as the base timestamp plus the RDT time
* adjusted for the clock skew .*/
out_time = jbuf->base_time + send_diff + jbuf->skew;
GST_DEBUG ("skew %" G_GINT64_FORMAT ", out %" GST_TIME_FORMAT,
jbuf->skew, GST_TIME_ARGS (out_time));
return out_time;
}
/**
* rdt_jitter_buffer_insert:
* @jbuf: an #RDTJitterBuffer
* @buf: a buffer
* @time: a running_time when this buffer was received in nanoseconds
* @clock_rate: the clock-rate of the payload of @buf
* @tail: TRUE when the tail element changed.
*
* Inserts @buf into the packet queue of @jbuf. The sequence number of the
* packet will be used to sort the packets. This function takes ownerhip of
* @buf when the function returns %TRUE.
* @buf should have writable metadata when calling this function.
*
* Returns: %FALSE if a packet with the same number already existed.
*/
gboolean
rdt_jitter_buffer_insert (RDTJitterBuffer * jbuf, GstBuffer * buf,
GstClockTime time, guint32 clock_rate, gboolean * tail)
{
GList *list;
guint32 rtptime;
guint16 seqnum;
GstRDTPacket packet;
gboolean more;
g_return_val_if_fail (jbuf != NULL, FALSE);
g_return_val_if_fail (buf != NULL, FALSE);
more = gst_rdt_buffer_get_first_packet (buf, &packet);
/* programmer error */
g_return_val_if_fail (more == TRUE, FALSE);
seqnum = gst_rdt_packet_data_get_seq (&packet);
/* do skew calculation by measuring the difference between rtptime and the
* receive time, this function will retimestamp @buf with the skew corrected
* running time. */
rtptime = gst_rdt_packet_data_get_timestamp (&packet);
/* loop the list to skip strictly smaller seqnum buffers */
for (list = jbuf->packets->head; list; list = g_list_next (list)) {
guint16 qseq;
gint gap;
more =
gst_rdt_buffer_get_first_packet (GST_BUFFER_CAST (list->data), &packet);
/* programmer error */
g_return_val_if_fail (more == TRUE, FALSE);
qseq = gst_rdt_packet_data_get_seq (&packet);
/* compare the new seqnum to the one in the buffer */
gap = gst_rdt_buffer_compare_seqnum (seqnum, qseq);
/* we hit a packet with the same seqnum, notify a duplicate */
if (G_UNLIKELY (gap == 0))
goto duplicate;
/* seqnum > qseq, we can stop looking */
if (G_LIKELY (gap < 0))
break;
}
if (clock_rate) {
time = calculate_skew (jbuf, rtptime, time, clock_rate);
GST_BUFFER_TIMESTAMP (buf) = time;
}
if (list)
g_queue_insert_before (jbuf->packets, list, buf);
else
g_queue_push_tail (jbuf->packets, buf);
/* tail was changed when we did not find a previous packet, we set the return
* flag when requested. */
if (tail)
*tail = (list == NULL);
return TRUE;
/* ERRORS */
duplicate:
{
GST_WARNING ("duplicate packet %d found", (gint) seqnum);
return FALSE;
}
}
/**
* rdt_jitter_buffer_pop:
* @jbuf: an #RDTJitterBuffer
*
* Pops the oldest buffer from the packet queue of @jbuf. The popped buffer will
* have its timestamp adjusted with the incoming running_time and the detected
* clock skew.
*
* Returns: a #GstBuffer or %NULL when there was no packet in the queue.
*/
GstBuffer *
rdt_jitter_buffer_pop (RDTJitterBuffer * jbuf)
{
GstBuffer *buf;
g_return_val_if_fail (jbuf != NULL, FALSE);
buf = g_queue_pop_tail (jbuf->packets);
return buf;
}
/**
* rdt_jitter_buffer_peek:
* @jbuf: an #RDTJitterBuffer
*
* Peek the oldest buffer from the packet queue of @jbuf. Register a callback
* with rdt_jitter_buffer_set_tail_changed() to be notified when an older packet
* was inserted in the queue.
*
* Returns: a #GstBuffer or %NULL when there was no packet in the queue.
*/
GstBuffer *
rdt_jitter_buffer_peek (RDTJitterBuffer * jbuf)
{
GstBuffer *buf;
g_return_val_if_fail (jbuf != NULL, FALSE);
buf = g_queue_peek_tail (jbuf->packets);
return buf;
}
/**
* rdt_jitter_buffer_flush:
* @jbuf: an #RDTJitterBuffer
*
* Flush all packets from the jitterbuffer.
*/
void
rdt_jitter_buffer_flush (RDTJitterBuffer * jbuf)
{
GstBuffer *buffer;
g_return_if_fail (jbuf != NULL);
while ((buffer = g_queue_pop_head (jbuf->packets)))
gst_buffer_unref (buffer);
}
/**
* rdt_jitter_buffer_num_packets:
* @jbuf: an #RDTJitterBuffer
*
* Get the number of packets currently in "jbuf.
*
* Returns: The number of packets in @jbuf.
*/
guint
rdt_jitter_buffer_num_packets (RDTJitterBuffer * jbuf)
{
g_return_val_if_fail (jbuf != NULL, 0);
return jbuf->packets->length;
}
/**
* rdt_jitter_buffer_get_ts_diff:
* @jbuf: an #RDTJitterBuffer
*
* Get the difference between the timestamps of first and last packet in the
* jitterbuffer.
*
* Returns: The difference expressed in the timestamp units of the packets.
*/
guint32
rdt_jitter_buffer_get_ts_diff (RDTJitterBuffer * jbuf)
{
guint64 high_ts, low_ts;
GstBuffer *high_buf, *low_buf;
guint32 result;
g_return_val_if_fail (jbuf != NULL, 0);
high_buf = g_queue_peek_head (jbuf->packets);
low_buf = g_queue_peek_tail (jbuf->packets);
if (!high_buf || !low_buf || high_buf == low_buf)
return 0;
//high_ts = gst_rtp_buffer_get_timestamp (high_buf);
//low_ts = gst_rtp_buffer_get_timestamp (low_buf);
high_ts = 0;
low_ts = 0;
/* it needs to work if ts wraps */
if (high_ts >= low_ts) {
result = (guint32) (high_ts - low_ts);
} else {
result = (guint32) (high_ts + G_MAXUINT32 + 1 - low_ts);
}
return result;
}

View file

@ -1,91 +0,0 @@
/* 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __RDT_JITTER_BUFFER_H__
#define __RDT_JITTER_BUFFER_H__
#include <gst/gst.h>
typedef struct _RDTJitterBuffer RDTJitterBuffer;
typedef struct _RDTJitterBufferClass RDTJitterBufferClass;
#define RDT_TYPE_JITTER_BUFFER (rdt_jitter_buffer_get_type())
#define RDT_JITTER_BUFFER(src) (G_TYPE_CHECK_INSTANCE_CAST((src),RDT_TYPE_JITTER_BUFFER,RDTJitterBuffer))
#define RDT_JITTER_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),RDT_TYPE_JITTER_BUFFER,RDTJitterBufferClass))
#define RDT_IS_JITTER_BUFFER(src) (G_TYPE_CHECK_INSTANCE_TYPE((src),RDT_TYPE_JITTER_BUFFER))
#define RDT_IS_JITTER_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),RDT_TYPE_JITTER_BUFFER))
#define RDT_JITTER_BUFFER_CAST(src) ((RDTJitterBuffer *)(src))
/**
* RTPTailChanged:
* @jbuf: an #RDTJitterBuffer
* @user_data: user data specified when registering
*
* This callback will be called when the tail buffer of @jbuf changed.
*/
typedef void (*RTPTailChanged) (RDTJitterBuffer *jbuf, gpointer user_data);
#define RDT_JITTER_BUFFER_MAX_WINDOW 512
/**
* RDTJitterBuffer:
*
* A JitterBuffer in the #RTPSession
*/
struct _RDTJitterBuffer {
GObject object;
GQueue *packets;
/* for calculating skew */
GstClockTime base_time;
GstClockTime base_rtptime;
guint64 ext_rtptime;
gint64 window[RDT_JITTER_BUFFER_MAX_WINDOW];
guint window_pos;
guint window_size;
gboolean window_filling;
gint64 window_min;
gint64 skew;
gint64 prev_send_diff;
};
struct _RDTJitterBufferClass {
GObjectClass parent_class;
};
GType rdt_jitter_buffer_get_type (void);
/* managing lifetime */
RDTJitterBuffer* rdt_jitter_buffer_new (void);
void rdt_jitter_buffer_reset_skew (RDTJitterBuffer *jbuf);
gboolean rdt_jitter_buffer_insert (RDTJitterBuffer *jbuf, GstBuffer *buf,
GstClockTime time,
guint32 clock_rate,
gboolean *tail);
GstBuffer * rdt_jitter_buffer_peek (RDTJitterBuffer *jbuf);
GstBuffer * rdt_jitter_buffer_pop (RDTJitterBuffer *jbuf);
void rdt_jitter_buffer_flush (RDTJitterBuffer *jbuf);
guint rdt_jitter_buffer_num_packets (RDTJitterBuffer *jbuf);
guint32 rdt_jitter_buffer_get_ts_diff (RDTJitterBuffer *jbuf);
#endif /* __RDT_JITTER_BUFFER_H__ */

File diff suppressed because it is too large Load diff

View file

@ -1,93 +0,0 @@
/* GStreamer
* Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/*
* Unless otherwise indicated, Source Code is licensed under MIT license.
* See further explanation attached in License Statement (distributed in the file
* LICENSE).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __GST_RDT_MANAGER_H__
#define __GST_RDT_MANAGER_H__
#include <gst/gst.h>
G_BEGIN_DECLS
#define GST_TYPE_RDT_MANAGER (gst_rdt_manager_get_type())
#define GST_IS_RDT_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RDT_MANAGER))
#define GST_IS_RDT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RDT_MANAGER))
#define GST_RDT_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RDT_MANAGER, GstRDTManager))
#define GST_RDT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RDT_MANAGER, GstRDTManagerClass))
typedef struct _GstRDTManager GstRDTManager;
typedef struct _GstRDTManagerClass GstRDTManagerClass;
typedef struct _GstRDTManagerSession GstRDTManagerSession;
struct _GstRDTManager {
GstElement element;
guint latency;
GSList *sessions;
GstClock *provided_clock;
};
struct _GstRDTManagerClass {
GstElementClass parent_class;
/* get the caps for pt */
GstCaps* (*request_pt_map) (GstRDTManager *rtpdec, guint session, guint pt);
void (*clear_pt_map) (GstRDTManager *rtpdec);
void (*on_new_ssrc) (GstRDTManager *rtpdec, guint session, guint32 ssrc);
void (*on_ssrc_collision) (GstRDTManager *rtpdec, guint session, guint32 ssrc);
void (*on_ssrc_validated) (GstRDTManager *rtpdec, guint session, guint32 ssrc);
void (*on_ssrc_active) (GstRDTManager *rtpdec, guint session, guint32 ssrc);
void (*on_ssrc_sdes) (GstRDTManager *rtpdec, guint session, guint32 ssrc);
void (*on_bye_ssrc) (GstRDTManager *rtpdec, guint session, guint32 ssrc);
void (*on_bye_timeout) (GstRDTManager *rtpdec, guint session, guint32 ssrc);
void (*on_timeout) (GstRDTManager *rtpdec, guint session, guint32 ssrc);
void (*on_npt_stop) (GstRDTManager *rtpdec, guint session, guint32 ssrc);
};
GType gst_rdt_manager_get_type(void);
GST_ELEMENT_REGISTER_DECLARE (rdtmanager);
G_END_DECLS
#endif /* __GST_RDT_MANAGER_H__ */

View file

@ -1,324 +0,0 @@
/* GStreamer
* Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/* Element-Checklist-Version: 5 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include <string.h>
#include <gst/gst.h>
#include "realhash.h"
void rtsp_ext_real_calc_response_and_checksum (char *response,
char *chksum, char *challenge);
/*
* The following code has been copied from
* xine-lib-1.1.1/src/input/libreal/real.c.
*/
static const unsigned char xor_table[] = {
0x05, 0x18, 0x74, 0xd0, 0x0d, 0x09, 0x02, 0x53,
0xc0, 0x01, 0x05, 0x05, 0x67, 0x03, 0x19, 0x70,
0x08, 0x27, 0x66, 0x10, 0x10, 0x72, 0x08, 0x09,
0x63, 0x11, 0x03, 0x71, 0x08, 0x08, 0x70, 0x02,
0x10, 0x57, 0x05, 0x18, 0x54, 0x00, 0x00, 0x00
};
#define LE_32(x) GST_READ_UINT32_LE(x)
#define BE_32C(x,y) GST_WRITE_UINT32_BE(x,y)
#define LE_32C(x,y) GST_WRITE_UINT32_LE(x,y)
static void
hash (char *field, char *param)
{
uint32_t a, b, c, d;
/* fill variables */
a = LE_32 (field);
b = LE_32 (field + 4);
c = LE_32 (field + 8);
d = LE_32 (field + 12);
a = ((b & c) | (~b & d)) + LE_32 ((param + 0x00)) + a - 0x28955B88;
a = ((a << 0x07) | (a >> 0x19)) + b;
d = ((a & b) | (~a & c)) + LE_32 ((param + 0x04)) + d - 0x173848AA;
d = ((d << 0x0c) | (d >> 0x14)) + a;
c = ((d & a) | (~d & b)) + LE_32 ((param + 0x08)) + c + 0x242070DB;
c = ((c << 0x11) | (c >> 0x0f)) + d;
b = ((c & d) | (~c & a)) + LE_32 ((param + 0x0c)) + b - 0x3E423112;
b = ((b << 0x16) | (b >> 0x0a)) + c;
a = ((b & c) | (~b & d)) + LE_32 ((param + 0x10)) + a - 0x0A83F051;
a = ((a << 0x07) | (a >> 0x19)) + b;
d = ((a & b) | (~a & c)) + LE_32 ((param + 0x14)) + d + 0x4787C62A;
d = ((d << 0x0c) | (d >> 0x14)) + a;
c = ((d & a) | (~d & b)) + LE_32 ((param + 0x18)) + c - 0x57CFB9ED;
c = ((c << 0x11) | (c >> 0x0f)) + d;
b = ((c & d) | (~c & a)) + LE_32 ((param + 0x1c)) + b - 0x02B96AFF;
b = ((b << 0x16) | (b >> 0x0a)) + c;
a = ((b & c) | (~b & d)) + LE_32 ((param + 0x20)) + a + 0x698098D8;
a = ((a << 0x07) | (a >> 0x19)) + b;
d = ((a & b) | (~a & c)) + LE_32 ((param + 0x24)) + d - 0x74BB0851;
d = ((d << 0x0c) | (d >> 0x14)) + a;
c = ((d & a) | (~d & b)) + LE_32 ((param + 0x28)) + c - 0x0000A44F;
c = ((c << 0x11) | (c >> 0x0f)) + d;
b = ((c & d) | (~c & a)) + LE_32 ((param + 0x2C)) + b - 0x76A32842;
b = ((b << 0x16) | (b >> 0x0a)) + c;
a = ((b & c) | (~b & d)) + LE_32 ((param + 0x30)) + a + 0x6B901122;
a = ((a << 0x07) | (a >> 0x19)) + b;
d = ((a & b) | (~a & c)) + LE_32 ((param + 0x34)) + d - 0x02678E6D;
d = ((d << 0x0c) | (d >> 0x14)) + a;
c = ((d & a) | (~d & b)) + LE_32 ((param + 0x38)) + c - 0x5986BC72;
c = ((c << 0x11) | (c >> 0x0f)) + d;
b = ((c & d) | (~c & a)) + LE_32 ((param + 0x3c)) + b + 0x49B40821;
b = ((b << 0x16) | (b >> 0x0a)) + c;
a = ((b & d) | (~d & c)) + LE_32 ((param + 0x04)) + a - 0x09E1DA9E;
a = ((a << 0x05) | (a >> 0x1b)) + b;
d = ((a & c) | (~c & b)) + LE_32 ((param + 0x18)) + d - 0x3FBF4CC0;
d = ((d << 0x09) | (d >> 0x17)) + a;
c = ((d & b) | (~b & a)) + LE_32 ((param + 0x2c)) + c + 0x265E5A51;
c = ((c << 0x0e) | (c >> 0x12)) + d;
b = ((c & a) | (~a & d)) + LE_32 ((param + 0x00)) + b - 0x16493856;
b = ((b << 0x14) | (b >> 0x0c)) + c;
a = ((b & d) | (~d & c)) + LE_32 ((param + 0x14)) + a - 0x29D0EFA3;
a = ((a << 0x05) | (a >> 0x1b)) + b;
d = ((a & c) | (~c & b)) + LE_32 ((param + 0x28)) + d + 0x02441453;
d = ((d << 0x09) | (d >> 0x17)) + a;
c = ((d & b) | (~b & a)) + LE_32 ((param + 0x3c)) + c - 0x275E197F;
c = ((c << 0x0e) | (c >> 0x12)) + d;
b = ((c & a) | (~a & d)) + LE_32 ((param + 0x10)) + b - 0x182C0438;
b = ((b << 0x14) | (b >> 0x0c)) + c;
a = ((b & d) | (~d & c)) + LE_32 ((param + 0x24)) + a + 0x21E1CDE6;
a = ((a << 0x05) | (a >> 0x1b)) + b;
d = ((a & c) | (~c & b)) + LE_32 ((param + 0x38)) + d - 0x3CC8F82A;
d = ((d << 0x09) | (d >> 0x17)) + a;
c = ((d & b) | (~b & a)) + LE_32 ((param + 0x0c)) + c - 0x0B2AF279;
c = ((c << 0x0e) | (c >> 0x12)) + d;
b = ((c & a) | (~a & d)) + LE_32 ((param + 0x20)) + b + 0x455A14ED;
b = ((b << 0x14) | (b >> 0x0c)) + c;
a = ((b & d) | (~d & c)) + LE_32 ((param + 0x34)) + a - 0x561C16FB;
a = ((a << 0x05) | (a >> 0x1b)) + b;
d = ((a & c) | (~c & b)) + LE_32 ((param + 0x08)) + d - 0x03105C08;
d = ((d << 0x09) | (d >> 0x17)) + a;
c = ((d & b) | (~b & a)) + LE_32 ((param + 0x1c)) + c + 0x676F02D9;
c = ((c << 0x0e) | (c >> 0x12)) + d;
b = ((c & a) | (~a & d)) + LE_32 ((param + 0x30)) + b - 0x72D5B376;
b = ((b << 0x14) | (b >> 0x0c)) + c;
a = (b ^ c ^ d) + LE_32 ((param + 0x14)) + a - 0x0005C6BE;
a = ((a << 0x04) | (a >> 0x1c)) + b;
d = (a ^ b ^ c) + LE_32 ((param + 0x20)) + d - 0x788E097F;
d = ((d << 0x0b) | (d >> 0x15)) + a;
c = (d ^ a ^ b) + LE_32 ((param + 0x2c)) + c + 0x6D9D6122;
c = ((c << 0x10) | (c >> 0x10)) + d;
b = (c ^ d ^ a) + LE_32 ((param + 0x38)) + b - 0x021AC7F4;
b = ((b << 0x17) | (b >> 0x09)) + c;
a = (b ^ c ^ d) + LE_32 ((param + 0x04)) + a - 0x5B4115BC;
a = ((a << 0x04) | (a >> 0x1c)) + b;
d = (a ^ b ^ c) + LE_32 ((param + 0x10)) + d + 0x4BDECFA9;
d = ((d << 0x0b) | (d >> 0x15)) + a;
c = (d ^ a ^ b) + LE_32 ((param + 0x1c)) + c - 0x0944B4A0;
c = ((c << 0x10) | (c >> 0x10)) + d;
b = (c ^ d ^ a) + LE_32 ((param + 0x28)) + b - 0x41404390;
b = ((b << 0x17) | (b >> 0x09)) + c;
a = (b ^ c ^ d) + LE_32 ((param + 0x34)) + a + 0x289B7EC6;
a = ((a << 0x04) | (a >> 0x1c)) + b;
d = (a ^ b ^ c) + LE_32 ((param + 0x00)) + d - 0x155ED806;
d = ((d << 0x0b) | (d >> 0x15)) + a;
c = (d ^ a ^ b) + LE_32 ((param + 0x0c)) + c - 0x2B10CF7B;
c = ((c << 0x10) | (c >> 0x10)) + d;
b = (c ^ d ^ a) + LE_32 ((param + 0x18)) + b + 0x04881D05;
b = ((b << 0x17) | (b >> 0x09)) + c;
a = (b ^ c ^ d) + LE_32 ((param + 0x24)) + a - 0x262B2FC7;
a = ((a << 0x04) | (a >> 0x1c)) + b;
d = (a ^ b ^ c) + LE_32 ((param + 0x30)) + d - 0x1924661B;
d = ((d << 0x0b) | (d >> 0x15)) + a;
c = (d ^ a ^ b) + LE_32 ((param + 0x3c)) + c + 0x1fa27cf8;
c = ((c << 0x10) | (c >> 0x10)) + d;
b = (c ^ d ^ a) + LE_32 ((param + 0x08)) + b - 0x3B53A99B;
b = ((b << 0x17) | (b >> 0x09)) + c;
a = ((~d | b) ^ c) + LE_32 ((param + 0x00)) + a - 0x0BD6DDBC;
a = ((a << 0x06) | (a >> 0x1a)) + b;
d = ((~c | a) ^ b) + LE_32 ((param + 0x1c)) + d + 0x432AFF97;
d = ((d << 0x0a) | (d >> 0x16)) + a;
c = ((~b | d) ^ a) + LE_32 ((param + 0x38)) + c - 0x546BDC59;
c = ((c << 0x0f) | (c >> 0x11)) + d;
b = ((~a | c) ^ d) + LE_32 ((param + 0x14)) + b - 0x036C5FC7;
b = ((b << 0x15) | (b >> 0x0b)) + c;
a = ((~d | b) ^ c) + LE_32 ((param + 0x30)) + a + 0x655B59C3;
a = ((a << 0x06) | (a >> 0x1a)) + b;
d = ((~c | a) ^ b) + LE_32 ((param + 0x0C)) + d - 0x70F3336E;
d = ((d << 0x0a) | (d >> 0x16)) + a;
c = ((~b | d) ^ a) + LE_32 ((param + 0x28)) + c - 0x00100B83;
c = ((c << 0x0f) | (c >> 0x11)) + d;
b = ((~a | c) ^ d) + LE_32 ((param + 0x04)) + b - 0x7A7BA22F;
b = ((b << 0x15) | (b >> 0x0b)) + c;
a = ((~d | b) ^ c) + LE_32 ((param + 0x20)) + a + 0x6FA87E4F;
a = ((a << 0x06) | (a >> 0x1a)) + b;
d = ((~c | a) ^ b) + LE_32 ((param + 0x3c)) + d - 0x01D31920;
d = ((d << 0x0a) | (d >> 0x16)) + a;
c = ((~b | d) ^ a) + LE_32 ((param + 0x18)) + c - 0x5CFEBCEC;
c = ((c << 0x0f) | (c >> 0x11)) + d;
b = ((~a | c) ^ d) + LE_32 ((param + 0x34)) + b + 0x4E0811A1;
b = ((b << 0x15) | (b >> 0x0b)) + c;
a = ((~d | b) ^ c) + LE_32 ((param + 0x10)) + a - 0x08AC817E;
a = ((a << 0x06) | (a >> 0x1a)) + b;
d = ((~c | a) ^ b) + LE_32 ((param + 0x2c)) + d - 0x42C50DCB;
d = ((d << 0x0a) | (d >> 0x16)) + a;
c = ((~b | d) ^ a) + LE_32 ((param + 0x08)) + c + 0x2AD7D2BB;
c = ((c << 0x0f) | (c >> 0x11)) + d;
b = ((~a | c) ^ d) + LE_32 ((param + 0x24)) + b - 0x14792C6F;
b = ((b << 0x15) | (b >> 0x0b)) + c;
a += LE_32 (field);
b += LE_32 (field + 4);
c += LE_32 (field + 8);
d += LE_32 (field + 12);
LE_32C (field, a);
LE_32C (field + 4, b);
LE_32C (field + 8, c);
LE_32C (field + 12, d);
}
static void
call_hash (char *key, char *challenge, int len)
{
uint8_t *ptr1, *ptr2;
uint32_t a, b, c, d, tmp;
ptr1 = (uint8_t *) (key + 16);
ptr2 = (uint8_t *) (key + 20);
a = LE_32 (ptr1);
b = (a >> 3) & 0x3f;
a += len * 8;
LE_32C (ptr1, a);
if (a < (len << 3))
ptr2 += 4;
tmp = LE_32 (ptr2) + (len >> 0x1d);
LE_32C (ptr2, tmp);
a = 64 - b;
c = 0;
if (a <= len) {
memcpy (key + b + 24, challenge, a);
hash (key, key + 24);
c = a;
d = c + 0x3f;
while (d < len) {
hash (key, challenge + d - 0x3f);
d += 64;
c += 64;
}
b = 0;
}
memcpy (key + b + 24, challenge + c, len - c);
}
void
gst_rtsp_ext_real_calc_response_and_checksum (char *response, char *chksum,
char *challenge)
{
int ch_len, table_len, resp_len;
int i;
char *ptr;
char buf[128];
char field[128];
char zres[20];
char buf1[128];
char buf2[128];
/* initialize return values */
memset (response, 0, 64);
memset (chksum, 0, 34);
/* initialize buffer */
memset (buf, 0, 128);
ptr = buf;
BE_32C (ptr, 0xa1e9149d);
ptr += 4;
BE_32C (ptr, 0x0e6b3b59);
ptr += 4;
if ((ch_len = MIN (strlen (challenge), 56)) == 40) {
challenge[32] = 0;
ch_len = 32;
}
memcpy (ptr, challenge, ch_len);
/* xor challenge bytewise with xor_table */
table_len = MIN (strlen ((char *) xor_table), 56);
for (i = 0; i < table_len; i++)
ptr[i] = ptr[i] ^ xor_table[i];
/* initialize our field */
BE_32C (field, 0x01234567);
BE_32C (field + 4, 0x89ABCDEF);
BE_32C (field + 8, 0xFEDCBA98);
BE_32C (field + 12, 0x76543210);
BE_32C (field + 16, 0x00000000);
BE_32C (field + 20, 0x00000000);
/* calculate response */
call_hash (field, buf, 64);
memset (buf1, 0, 64);
*buf1 = (char) 128;
memcpy (buf2, field + 16, 8);
i = (LE_32 ((buf2)) >> 3) & 0x3f;
if (i < 56)
i = 56 - i;
else
i = 120 - i;
call_hash (field, buf1, i);
call_hash (field, buf2, 8);
memcpy (zres, field, 16);
/* convert zres to ascii string */
for (i = 0; i < 16; i++) {
char a, b;
a = (zres[i] >> 4) & 15;
b = zres[i] & 15;
response[i * 2] = ((a < 10) ? (a + 48) : (a + 87)) & 255;
response[i * 2 + 1] = ((b < 10) ? (b + 48) : (b + 87)) & 255;
}
/* add tail */
resp_len = strlen (response);
strcpy (&response[resp_len], "01d0a8e3");
/* calculate checksum */
for (i = 0; i < resp_len / 4; i++)
chksum[i] = response[i * 4];
}

View file

@ -1,31 +0,0 @@
/* GStreamer
* Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_RTSP_HASH_H__
#define __GST_RTSP_HASH_H__
G_BEGIN_DECLS
void
gst_rtsp_ext_real_calc_response_and_checksum (char *response, char *chksum,
char *challenge);
G_END_DECLS
#endif /* __GST_RTSP_HASH_H__ */

View file

@ -23,10 +23,6 @@
#include "rmdemux.h" #include "rmdemux.h"
#include "rademux.h" #include "rademux.h"
#include "rdtdepay.h"
#include "rdtmanager.h"
#include "rtspreal.h"
#include "pnmsrc.h"
static gboolean static gboolean
plugin_init (GstPlugin * plugin) plugin_init (GstPlugin * plugin)
@ -35,10 +31,6 @@ plugin_init (GstPlugin * plugin)
ret |= GST_ELEMENT_REGISTER (rmdemux, plugin); ret |= GST_ELEMENT_REGISTER (rmdemux, plugin);
ret |= GST_ELEMENT_REGISTER (rademux, plugin); ret |= GST_ELEMENT_REGISTER (rademux, plugin);
ret |= GST_ELEMENT_REGISTER (rdtdepay, plugin);
ret |= GST_ELEMENT_REGISTER (rdtmanager, plugin);
ret |= GST_ELEMENT_REGISTER (rtspreal, plugin);
ret |= GST_ELEMENT_REGISTER (pnmsrc, plugin);
return ret; return ret;
} }

View file

@ -1,735 +0,0 @@
/* GStreamer
* Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/* Element-Checklist-Version: 5 */
/**
* SECTION:element-rtspreal
* @title: rtspreal
*
* A RealMedia RTSP extension
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <gst/rtsp/gstrtspextension.h>
#include "realhash.h"
#include "rtspreal.h"
#include "asmrules.h"
GST_DEBUG_CATEGORY_STATIC (rtspreal_debug);
#define GST_CAT_DEFAULT (rtspreal_debug)
#define SERVER_PREFIX "RealServer"
#define DEFAULT_BANDWIDTH "10485800"
static GstRTSPResult
rtsp_ext_real_get_transports (GstRTSPExtension * ext,
GstRTSPLowerTrans protocols, gchar ** transport)
{
GstRTSPReal *ctx = (GstRTSPReal *) ext;
GString *str;
if (!ctx->isreal)
return GST_RTSP_OK;
GST_DEBUG_OBJECT (ext, "generating transports for %d", protocols);
str = g_string_new ("");
/*
if (protocols & GST_RTSP_LOWER_TRANS_UDP_MCAST) {
g_string_append (str, "x-real-rdt/mcast;client_port=%%u1;mode=play,");
}
if (protocols & GST_RTSP_LOWER_TRANS_UDP) {
g_string_append (str, "x-real-rdt/udp;client_port=%%u1;mode=play,");
g_string_append (str, "x-pn-tng/udp;client_port=%%u1;mode=play,");
}
*/
if (protocols & GST_RTSP_LOWER_TRANS_TCP) {
g_string_append (str, "x-real-rdt/tcp;mode=play,");
g_string_append (str, "x-pn-tng/tcp;mode=play,");
}
/* if we added something, remove trailing ',' */
if (str->len > 0)
g_string_truncate (str, str->len - 1);
*transport = g_string_free (str, FALSE);
return GST_RTSP_OK;
}
static GstRTSPResult
rtsp_ext_real_before_send (GstRTSPExtension * ext, GstRTSPMessage * request)
{
GstRTSPReal *ctx = (GstRTSPReal *) ext;
switch (request->type_data.request.method) {
case GST_RTSP_OPTIONS:
{
gst_rtsp_message_add_header (request, GST_RTSP_HDR_USER_AGENT,
//"RealMedia Player (" GST_PACKAGE_NAME ")");
"RealMedia Player Version 6.0.9.1235 (linux-2.0-libc6-i386-gcc2.95)");
gst_rtsp_message_add_header (request, GST_RTSP_HDR_CLIENT_CHALLENGE,
"9e26d33f2984236010ef6253fb1887f7");
gst_rtsp_message_add_header (request, GST_RTSP_HDR_COMPANY_ID,
"KnKV4M4I/B2FjJ1TToLycw==");
gst_rtsp_message_add_header (request, GST_RTSP_HDR_GUID,
"00000000-0000-0000-0000-000000000000");
gst_rtsp_message_add_header (request, GST_RTSP_HDR_REGION_DATA, "0");
gst_rtsp_message_add_header (request, GST_RTSP_HDR_PLAYER_START_TIME,
"[28/03/2003:22:50:23 00:00]");
gst_rtsp_message_add_header (request, GST_RTSP_HDR_CLIENT_ID,
"Linux_2.4_6.0.9.1235_play32_RN01_EN_586");
ctx->isreal = FALSE;
break;
}
case GST_RTSP_DESCRIBE:
{
if (ctx->isreal) {
gst_rtsp_message_add_header (request, GST_RTSP_HDR_BANDWIDTH,
DEFAULT_BANDWIDTH);
gst_rtsp_message_add_header (request, GST_RTSP_HDR_GUID,
"00000000-0000-0000-0000-000000000000");
gst_rtsp_message_add_header (request, GST_RTSP_HDR_REGION_DATA, "0");
gst_rtsp_message_add_header (request, GST_RTSP_HDR_CLIENT_ID,
"Linux_2.4_6.0.9.1235_play32_RN01_EN_586");
gst_rtsp_message_add_header (request, GST_RTSP_HDR_MAX_ASM_WIDTH, "1");
gst_rtsp_message_add_header (request, GST_RTSP_HDR_LANGUAGE, "en-US");
gst_rtsp_message_add_header (request, GST_RTSP_HDR_REQUIRE,
"com.real.retain-entity-for-setup");
}
break;
}
case GST_RTSP_SETUP:
{
if (ctx->isreal) {
gchar *value =
g_strdup_printf ("%s, sd=%s", ctx->challenge2, ctx->checksum);
gst_rtsp_message_add_header (request, GST_RTSP_HDR_REAL_CHALLENGE2,
value);
gst_rtsp_message_add_header (request, GST_RTSP_HDR_IF_MATCH, ctx->etag);
g_free (value);
}
break;
}
default:
break;
}
return GST_RTSP_OK;
}
static GstRTSPResult
rtsp_ext_real_after_send (GstRTSPExtension * ext, GstRTSPMessage * req,
GstRTSPMessage * resp)
{
GstRTSPReal *ctx = (GstRTSPReal *) ext;
switch (req->type_data.request.method) {
case GST_RTSP_OPTIONS:
{
gchar *challenge1 = NULL;
gchar *server = NULL;
gst_rtsp_message_get_header (resp, GST_RTSP_HDR_SERVER, &server, 0);
gst_rtsp_message_get_header (resp, GST_RTSP_HDR_REAL_CHALLENGE1,
&challenge1, 0);
if (!challenge1)
goto no_challenge1;
gst_rtsp_ext_real_calc_response_and_checksum (ctx->challenge2,
ctx->checksum, challenge1);
GST_DEBUG_OBJECT (ctx, "Found Real challenge tag");
ctx->isreal = TRUE;
break;
}
case GST_RTSP_DESCRIBE:
{
gchar *etag = NULL;
guint len;
gst_rtsp_message_get_header (resp, GST_RTSP_HDR_ETAG, &etag, 0);
if (etag) {
len = sizeof (ctx->etag);
strncpy (ctx->etag, etag, len);
ctx->etag[len - 1] = '\0';
}
break;
}
default:
break;
}
return GST_RTSP_OK;
/* ERRORS */
no_challenge1:
{
GST_DEBUG_OBJECT (ctx, "Could not find challenge tag.");
ctx->isreal = FALSE;
return GST_RTSP_OK;
}
}
#define ENSURE_SIZE(size) \
G_STMT_START { \
while (data_len < size) { \
data_len += 1024; \
data = g_realloc (data, data_len); \
} \
} G_STMT_END
#define READ_BUFFER_GEN(src, func, name, dest, dest_len) \
G_STMT_START { \
dest = (gchar *)func (src, name); \
dest_len = 0; \
if (!dest) { \
dest = (char *) ""; \
} \
else if (!strncmp (dest, "buffer;\"", 8)) { \
dest += 8; \
dest_len = strlen (dest) - 1; \
dest[dest_len] = '\0'; \
g_base64_decode_inplace (dest, &dest_len); \
} \
} G_STMT_END
#define READ_BUFFER(sdp, name, dest, dest_len) \
READ_BUFFER_GEN(sdp, gst_sdp_message_get_attribute_val, name, dest, dest_len)
#define READ_BUFFER_M(media, name, dest, dest_len) \
READ_BUFFER_GEN(media, gst_sdp_media_get_attribute_val, name, dest, dest_len)
#define READ_INT_GEN(src, func, name, dest) \
G_STMT_START { \
const gchar *val = func (src, name); \
if (val && !strncmp (val, "integer;", 8)) \
dest = atoi (val + 8); \
else \
dest = 0; \
} G_STMT_END
#define READ_INT(sdp, name, dest) \
READ_INT_GEN(sdp, gst_sdp_message_get_attribute_val, name, dest)
#define READ_INT_M(media, name, dest) \
READ_INT_GEN(media, gst_sdp_media_get_attribute_val, name, dest)
#define READ_STRING(media, name, dest, dest_len) \
G_STMT_START { \
const gchar *val = gst_sdp_media_get_attribute_val (media, name); \
if (val && !strncmp (val, "string;\"", 8)) { \
dest = (gchar *) val + 8; \
dest_len = strlen (dest) - 1; \
dest[dest_len] = '\0'; \
} else { \
dest = (char *) ""; \
dest_len = 0; \
} \
} G_STMT_END
#define WRITE_STRING1(datap, str, str_len) \
G_STMT_START { \
*datap = str_len; \
memcpy ((datap) + 1, str, str_len); \
datap += str_len + 1; \
} G_STMT_END
#define WRITE_STRING2(datap, str, str_len) \
G_STMT_START { \
GST_WRITE_UINT16_BE (datap, str_len); \
memcpy (datap + 2, str, str_len); \
datap += str_len + 2; \
} G_STMT_END
static GstRTSPResult
rtsp_ext_real_parse_sdp (GstRTSPExtension * ext, GstSDPMessage * sdp,
GstStructure * props)
{
GstRTSPReal *ctx = (GstRTSPReal *) ext;
guint size;
gint i;
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, asm_rule_book_len;
GHashTable *vars;
GString *rules;
/* don't bother for non-real formats */
READ_INT (sdp, "IsRealDataType", ctx->isreal);
if (!ctx->isreal)
return TRUE;
/* Force PAUSE | PLAY */
//src->methods |= GST_RTSP_PLAY | GST_RTSP_PAUSE;
ctx->n_streams = gst_sdp_message_medias_len (sdp);
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);
ctx->max_bit_rate += intval;
READ_INT_M (media, "AvgBitRate", intval);
ctx->avg_bit_rate += intval;
READ_INT_M (media, "MaxPacketSize", intval);
ctx->max_packet_size = MAX (ctx->max_packet_size, intval);
READ_INT_M (media, "AvgPacketSize", intval);
ctx->avg_packet_size = (ctx->avg_packet_size * i + intval) / (i + 1);
READ_INT_M (media, "Duration", intval);
ctx->duration = MAX (ctx->duration, intval);
}
/* FIXME: use GstByteWriter to write the header */
/* PROP */
offset = 0;
size = 50;
ENSURE_SIZE (size);
datap = data + offset;
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, 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, 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, ctx->n_streams);
GST_WRITE_UINT16_BE (datap + 48, 0);
offset += size;
/* CONT */
READ_BUFFER (sdp, "Title", title, title_len);
READ_BUFFER (sdp, "Author", author, author_len);
READ_BUFFER (sdp, "Comment", comment, comment_len);
READ_BUFFER (sdp, "Copyright", copyright, copyright_len);
size = 18 + title_len + author_len + comment_len + copyright_len;
ENSURE_SIZE (offset + size);
datap = data + offset;
memcpy (datap, "CONT", 4);
GST_WRITE_UINT32_BE (datap + 4, size);
GST_WRITE_UINT16_BE (datap + 8, 0); /* Version */
datap += 10;
WRITE_STRING2 (datap, title, title_len);
WRITE_STRING2 (datap, author, author_len);
WRITE_STRING2 (datap, copyright, copyright_len);
WRITE_STRING2 (datap, comment, comment_len);
offset += size;
/* fix the hashtale for the rule parser */
rules = g_string_new ("");
vars = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (vars, (gchar *) "Bandwidth",
(gchar *) DEFAULT_BANDWIDTH);
/* MDPR */
for (i = 0; i < ctx->n_streams; i++) {
const GstSDPMedia *media;
guint32 len;
GstRTSPRealStream *stream;
gchar *str;
gint rulematches[MAX_RULEMATCHES];
gint sel, j, n;
media = gst_sdp_message_get_media (sdp, i);
if (media->media && !strcmp (media->media, "data"))
continue;
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
* bandwidth out of a list offered by the server. Someone needs to write
* a parser for strings like
*
* #($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\";
*
* As I don't know how to do that, I just use the first entry (sel = 0).
* But to give you a starting point, I offer you above string
* in the variable 'asm_rule_book'.
*/
READ_STRING (media, "ASMRuleBook", str, asm_rule_book_len);
stream->rulebook = gst_asm_rule_book_new (str);
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]);
}
/* get the MLTI for the first matched rules */
sel = rulematches[0];
READ_BUFFER_M (media, "OpaqueData", opaque_data, opaque_data_len);
if (opaque_data_len < 4) {
GST_DEBUG_OBJECT (ctx, "opaque_data_len %" G_GSIZE_FORMAT " < 4",
opaque_data_len);
goto strange_opaque_data;
}
if (strncmp (opaque_data, "MLTI", 4)) {
GST_DEBUG_OBJECT (ctx, "no MLTI found, appending all");
stream->type_specific_data_len = opaque_data_len;
stream->type_specific_data = g_memdup2 (opaque_data, opaque_data_len);
goto no_type_specific;
}
opaque_data += 4;
opaque_data_len -= 4;
if (opaque_data_len < 2) {
GST_DEBUG_OBJECT (ctx, "opaque_data_len %" G_GSIZE_FORMAT " < 2",
opaque_data_len);
goto strange_opaque_data;
}
stream->num_rules = GST_READ_UINT16_BE (opaque_data);
opaque_data += 2;
opaque_data_len -= 2;
if (sel >= stream->num_rules) {
GST_DEBUG_OBJECT (ctx, "sel %d >= num_rules %d", sel, stream->num_rules);
goto strange_opaque_data;
}
if (opaque_data_len < 2 * sel) {
GST_DEBUG_OBJECT (ctx, "opaque_data_len %" G_GSIZE_FORMAT
" < 2 * sel (%d)", opaque_data_len, 2 * sel);
goto strange_opaque_data;
}
opaque_data += 2 * sel;
opaque_data_len -= 2 * sel;
if (opaque_data_len < 2) {
GST_DEBUG_OBJECT (ctx, "opaque_data_len %" G_GSIZE_FORMAT " < 2",
opaque_data_len);
goto strange_opaque_data;
}
stream->codec = GST_READ_UINT16_BE (opaque_data);
opaque_data += 2;
opaque_data_len -= 2;
if (opaque_data_len < 2 * (stream->num_rules - sel - 1)) {
GST_DEBUG_OBJECT (ctx, "opaque_data_len %" G_GSIZE_FORMAT
" < %d", opaque_data_len, 2 * (stream->num_rules - sel - 1));
goto strange_opaque_data;
}
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 %" G_GSIZE_FORMAT " < 2",
opaque_data_len);
goto strange_opaque_data;
}
stream->num_rules = GST_READ_UINT16_BE (opaque_data);
opaque_data += 2;
opaque_data_len -= 2;
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 < stream->codec; j++) {
if (opaque_data_len < 4) {
GST_DEBUG_OBJECT (ctx, "opaque_data_len %" G_GSIZE_FORMAT " < 4",
opaque_data_len);
goto strange_opaque_data;
}
len = GST_READ_UINT32_BE (opaque_data);
opaque_data += 4;
opaque_data_len -= 4;
if (opaque_data_len < len) {
GST_DEBUG_OBJECT (ctx, "opaque_data_len %" G_GSIZE_FORMAT " < len %d",
opaque_data_len, len);
goto strange_opaque_data;
}
opaque_data += len;
opaque_data_len -= len;
}
if (opaque_data_len < 4) {
GST_DEBUG_OBJECT (ctx, "opaque_data_len %" G_GSIZE_FORMAT " < 4",
opaque_data_len);
goto strange_opaque_data;
}
stream->type_specific_data_len = GST_READ_UINT32_BE (opaque_data);
opaque_data += 4;
opaque_data_len -= 4;
if (opaque_data_len < stream->type_specific_data_len) {
GST_DEBUG_OBJECT (ctx, "opaque_data_len %" G_GSIZE_FORMAT " < %d",
opaque_data_len, stream->type_specific_data_len);
goto strange_opaque_data;
}
stream->type_specific_data =
g_memdup2 (opaque_data, stream->type_specific_data_len);
no_type_specific:
size =
46 + stream->stream_name_len + stream->mime_type_len +
stream->type_specific_data_len;
ENSURE_SIZE (offset + size);
datap = data + offset;
memcpy (datap, "MDPR", 4);
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, 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->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;
}
/* destroy the rulebook hashtable now */
g_hash_table_destroy (vars);
/* strip final , if we added some stream rules */
if (rules->len > 0) {
rules = g_string_truncate (rules, rules->len - 1);
}
/* and store rules in the context */
ctx->rules = g_string_free (rules, FALSE);
/* DATA */
size = 18;
ENSURE_SIZE (offset + size);
datap = data + offset;
memcpy (datap, "DATA", 4);
GST_WRITE_UINT32_BE (datap + 4, size);
GST_WRITE_UINT16_BE (datap + 8, 0);
GST_WRITE_UINT32_BE (datap + 10, 0); /* number of packets */
GST_WRITE_UINT32_BE (datap + 14, 0); /* next data header */
offset += size;
buf = gst_buffer_new_wrapped (data, offset);
/* Set on caps */
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
gst_structure_set (props, "config", GST_TYPE_BUFFER, buf, NULL);
gst_buffer_unref (buf);
/* Overwrite encoding and media fields */
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;
/* ERRORS */
strange_opaque_data:
{
g_string_free (rules, TRUE);
g_hash_table_destroy (vars);
g_free (data);
GST_ELEMENT_ERROR (ctx, RESOURCE, WRITE, ("Strange opaque data."), (NULL));
return FALSE;
}
}
static GstRTSPResult
rtsp_ext_real_stream_select (GstRTSPExtension * ext, GstRTSPUrl * url)
{
GstRTSPReal *ctx = (GstRTSPReal *) ext;
GstRTSPResult res;
GstRTSPMessage request = { 0 };
GstRTSPMessage response = { 0 };
gchar *req_url;
if (!ctx->isreal)
return GST_RTSP_OK;
if (!ctx->rules)
return GST_RTSP_OK;
req_url = gst_rtsp_url_get_request_uri (url);
/* create SET_PARAMETER */
if ((res = gst_rtsp_message_init_request (&request, GST_RTSP_SET_PARAMETER,
req_url)) < 0)
goto create_request_failed;
g_free (req_url);
gst_rtsp_message_add_header (&request, GST_RTSP_HDR_SUBSCRIBE, ctx->rules);
/* send SET_PARAMETER */
if ((res = gst_rtsp_extension_send (ext, &request, &response)) < 0)
goto send_error;
gst_rtsp_message_unset (&request);
gst_rtsp_message_unset (&response);
return GST_RTSP_OK;
/* ERRORS */
create_request_failed:
{
GST_ELEMENT_ERROR (ctx, LIBRARY, INIT,
("Could not create request."), (NULL));
g_free (req_url);
goto reset;
}
send_error:
{
GST_ELEMENT_ERROR (ctx, RESOURCE, WRITE,
("Could not send message."), (NULL));
goto reset;
}
reset:
{
gst_rtsp_message_unset (&request);
gst_rtsp_message_unset (&response);
return res;
}
}
static void gst_rtsp_real_extension_init (gpointer g_iface,
gpointer iface_data);
static void gst_rtsp_real_finalize (GObject * obj);
#define gst_rtsp_real_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstRTSPReal, gst_rtsp_real, GST_TYPE_ELEMENT,
G_IMPLEMENT_INTERFACE (GST_TYPE_RTSP_EXTENSION,
gst_rtsp_real_extension_init));
GST_ELEMENT_REGISTER_DEFINE (rtspreal, "rtspreal",
GST_RANK_MARGINAL, GST_TYPE_RTSP_REAL);
static void
gst_rtsp_real_class_init (GstRTSPRealClass * g_class)
{
GObjectClass *gobject_class = (GObjectClass *) g_class;
GstElementClass *gstelement_class = (GstElementClass *) g_class;
gobject_class->finalize = gst_rtsp_real_finalize;
gst_element_class_set_static_metadata (gstelement_class,
"RealMedia RTSP Extension", "Network/Extension/Protocol",
"Extends RTSP so that it can handle RealMedia setup",
"Wim Taymans <wim.taymans@gmail.com>");
GST_DEBUG_CATEGORY_INIT (rtspreal_debug, "rtspreal", 0,
"RealMedia RTSP extension");
}
static void
gst_rtsp_real_init (GstRTSPReal * rtspreal)
{
rtspreal->isreal = FALSE;
}
static void
gst_rtsp_stream_free (GstRTSPRealStream * stream)
{
g_free (stream->stream_name);
g_free (stream->mime_type);
gst_asm_rule_book_free (stream->rulebook);
g_free (stream->type_specific_data);
g_free (stream);
}
static void
gst_rtsp_real_finalize (GObject * obj)
{
GstRTSPReal *r = (GstRTSPReal *) obj;
g_list_foreach (r->streams, (GFunc) gst_rtsp_stream_free, NULL);
g_list_free (r->streams);
g_free (r->rules);
G_OBJECT_CLASS (parent_class)->finalize (obj);
}
static void
gst_rtsp_real_extension_init (gpointer g_iface, gpointer iface_data)
{
GstRTSPExtensionInterface *iface = (GstRTSPExtensionInterface *) g_iface;
iface->before_send = rtsp_ext_real_before_send;
iface->after_send = rtsp_ext_real_after_send;
iface->parse_sdp = rtsp_ext_real_parse_sdp;
iface->stream_select = rtsp_ext_real_stream_select;
iface->get_transports = rtsp_ext_real_get_transports;
}

View file

@ -1,93 +0,0 @@
/* GStreamer
* Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_RTSP_REAL_H__
#define __GST_RTSP_REAL_H__
#include <gst/gst.h>
#include "asmrules.h"
G_BEGIN_DECLS
#define GST_TYPE_RTSP_REAL (gst_rtsp_real_get_type())
#define GST_IS_RTSP_REAL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTSP_REAL))
#define GST_IS_RTSP_REAL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTSP_REAL))
#define GST_RTSP_REAL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTSP_REAL, GstRTSPReal))
#define GST_RTSP_REAL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTSP_REAL, GstRTSPRealClass))
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];
gchar etag[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;
gchar *rules;
};
struct _GstRTSPRealClass {
GstElementClass parent_class;
};
GType gst_rtsp_real_get_type(void);
GST_ELEMENT_REGISTER_DECLARE (rtspreal);
G_END_DECLS
#endif /* __GST_RTSP_REAL_H__ */