gstreamer/gst/realmedia/rtspreal.c
Wim Taymans dc920d924b gst/realmedia/rtspreal.*: Move assembly rule parsing to the place where we parse the SDP as it's also there that we c...
Original commit message from CVS:
* gst/realmedia/rtspreal.c: (rtsp_ext_real_before_send),
(rtsp_ext_real_parse_sdp), (rtsp_ext_real_stream_select):
* gst/realmedia/rtspreal.h:
Move assembly rule parsing to the place where we parse the SDP as it's
also there that we create the MDPR and we need the currently selected
asmrule in order to select the right MTLI.
Fixes #529359.
2008-04-30 17:16:47 +00:00

746 lines
24 KiB
C

/* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/* Element-Checklist-Version: 5 */
/**
* SECTION:element-rtspreal
*
* <refsect2>
* <para>
* A RealMedia RTSP extension
* </para>
* </refsect2>
*
* Last reviewed on 2007-07-25 (0.10.14)
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include "_stdint.h"
#include <string.h>
#include <gst/rtsp/gstrtspextension.h>
#include <gst/rtsp/gstrtspbase64.h>
#include "realhash.h"
#include "rtspreal.h"
#include "asmrules.h"
GST_DEBUG_CATEGORY_STATIC (rtspreal_debug);
#define GST_CAT_DEFAULT (rtspreal_debug)
/* elementfactory information */
static const GstElementDetails rtspreal_details =
GST_ELEMENT_DETAILS ("RealMedia RTSP Extension",
"Network/Extension/Protocol",
"Extends RTSP so that it can handle RealMedia setup",
"Wim Taymans <wim.taymans@gmail.com>");
#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:
{
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");
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);
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);
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); \
if (!dest) { \
dest = ""; \
dest_len = 0; \
} \
else if (!strncmp (dest, "buffer;\"", 8)) { \
dest += 8; \
dest_len = strlen (dest) - 1; \
dest[dest_len] = '\0'; \
gst_rtsp_base64_decode_ip (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 = ""; \
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);
}
/* 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 = 22 + 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, "Bandwidth", 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);
stream = g_new0 (GstRTSPRealStream, 1);
ctx->streams = g_list_append (ctx->streams, stream);
READ_INT_M (media, "MaxBitRate", stream->max_bit_rate);
READ_INT_M (media, "AvgBitRate", stream->avg_bit_rate);
READ_INT_M (media, "MaxPacketSize", stream->max_packet_size);
READ_INT_M (media, "AvgPacketSize", stream->avg_packet_size);
READ_INT_M (media, "StartTime", stream->start_time);
READ_INT_M (media, "Preroll", stream->preroll);
READ_INT_M (media, "Duration", stream->duration);
READ_STRING (media, "StreamName", str, stream->stream_name_len);
stream->stream_name = g_strndup (str, stream->stream_name_len);
READ_STRING (media, "mimetype", str, stream->mime_type_len);
stream->mime_type = g_strndup (str, stream->mime_type_len);
/* FIXME: Depending on the current bandwidth, we need to select one
* bandwith out of a list offered by the server. Someone needs to write
* 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_memdup (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_memdup (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 ();
GST_BUFFER_DATA (buf) = data;
GST_BUFFER_MALLOCDATA (buf) = data;
GST_BUFFER_SIZE (buf) = offset;
/* Set on caps */
gst_structure_set (props, "config", GST_TYPE_BUFFER, buf, NULL);
/* Overwrite encoding and media fields */
gst_structure_set (props, "encoding-name", G_TYPE_STRING, "X-REAL-RDT", NULL);
gst_structure_set (props, "media", G_TYPE_STRING, "application", NULL);
return TRUE;
/* ERRORS */
strange_opaque_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));
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_finalize (GObject * object);
static GstStateChangeReturn gst_rtsp_real_change_state (GstElement * element,
GstStateChange transition);
static void gst_rtsp_real_extension_init (gpointer g_iface,
gpointer iface_data);
static void
_do_init (GType rtspreal_type)
{
static const GInterfaceInfo rtspextension_info = {
gst_rtsp_real_extension_init,
NULL,
NULL
};
g_type_add_interface_static (rtspreal_type, GST_TYPE_RTSP_EXTENSION,
&rtspextension_info);
}
GST_BOILERPLATE_FULL (GstRTSPReal, gst_rtsp_real, GstElement, GST_TYPE_ELEMENT,
_do_init);
static void
gst_rtsp_real_base_init (gpointer klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
gst_element_class_set_details (element_class, &rtspreal_details);
}
static void
gst_rtsp_real_class_init (GstRTSPRealClass * g_class)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GstRTSPRealClass *klass;
klass = (GstRTSPRealClass *) g_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gobject_class->finalize = gst_rtsp_real_finalize;
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_rtsp_real_change_state);
GST_DEBUG_CATEGORY_INIT (rtspreal_debug, "rtspreal", 0,
"RealMedia RTSP extension");
}
static void
gst_rtsp_real_init (GstRTSPReal * rtspreal, GstRTSPRealClass * klass)
{
rtspreal->isreal = FALSE;
}
static void
gst_rtsp_real_finalize (GObject * object)
{
GstRTSPReal *rtspreal;
rtspreal = GST_RTSP_REAL (object);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static GstStateChangeReturn
gst_rtsp_real_change_state (GstElement * element, GstStateChange transition)
{
GstStateChangeReturn ret;
GstRTSPReal *rtspreal;
rtspreal = GST_RTSP_REAL (element);
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
return ret;
}
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;
}
gboolean
gst_rtsp_real_plugin_init (GstPlugin * plugin)
{
return gst_element_register (plugin, "rtspreal",
GST_RANK_MARGINAL, GST_TYPE_RTSP_REAL);
}