mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-10 03:19:40 +00:00
dc920d924b
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.
746 lines
24 KiB
C
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);
|
|
}
|