mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 08:46:40 +00:00
Add initial support for RECORD
We currently only support media that is RECORD or PLAY only, not both at once. https://bugzilla.gnome.org/show_bug.cgi?id=743175
This commit is contained in:
parent
18668bf495
commit
ccf6c6eb53
11 changed files with 1468 additions and 86 deletions
|
@ -1,7 +1,7 @@
|
|||
noinst_PROGRAMS = test-video test-ogg test-mp4 test-readme \
|
||||
test-launch test-sdp test-uri test-auth \
|
||||
test-multicast test-multicast2 test-appsrc \
|
||||
test-video-rtx
|
||||
test-video-rtx test-record
|
||||
|
||||
#INCLUDES = -I$(top_srcdir) -I$(srcdir)
|
||||
|
||||
|
|
68
examples/test-record.c
Normal file
68
examples/test-record.c
Normal file
|
@ -0,0 +1,68 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
* Copyright (C) 2015 Centricular Ltd
|
||||
* Author: Sebastian Dröge <sebastian@centricular.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 <gst/gst.h>
|
||||
|
||||
#include <gst/rtsp-server/rtsp-server.h>
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GstRTSPServer *server;
|
||||
GstRTSPMountPoints *mounts;
|
||||
GstRTSPMediaFactory *factory;
|
||||
|
||||
gst_init (&argc, &argv);
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
/* create a server instance */
|
||||
server = gst_rtsp_server_new ();
|
||||
|
||||
/* get the mount points for this server, every server has a default object
|
||||
* that be used to map uri mount points to media factories */
|
||||
mounts = gst_rtsp_server_get_mount_points (server);
|
||||
|
||||
/* make a media factory for a test stream. The default media factory can use
|
||||
* gst-launch syntax to create pipelines.
|
||||
* any launch line works as long as it contains elements named depay%d. Each
|
||||
* element with depay%d names will be a stream */
|
||||
factory = gst_rtsp_media_factory_new ();
|
||||
gst_rtsp_media_factory_set_record (factory, TRUE);
|
||||
gst_rtsp_media_factory_set_launch (factory,
|
||||
"( decodebin name=depay0 ! autovideosink decodebin name=depay1 ! autoaudiosink )");
|
||||
|
||||
/* attach the test factory to the /test url */
|
||||
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||
|
||||
/* don't need the ref to the mapper anymore */
|
||||
g_object_unref (mounts);
|
||||
|
||||
/* attach the server to the default maincontext */
|
||||
gst_rtsp_server_attach (server, NULL);
|
||||
|
||||
/* start serving */
|
||||
g_print ("stream ready at rtsp://127.0.0.1:8554/test\n");
|
||||
g_main_loop_run (loop);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
* Copyright (C) 2015 Centricular Ltd
|
||||
* Author: Sebastian Dröge <sebastian@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
|
@ -123,6 +125,8 @@ enum
|
|||
SIGNAL_GET_PARAMETER_REQUEST,
|
||||
SIGNAL_HANDLE_RESPONSE,
|
||||
SIGNAL_SEND_MESSAGE,
|
||||
SIGNAL_ANNOUNCE_REQUEST,
|
||||
SIGNAL_RECORD_REQUEST,
|
||||
SIGNAL_LAST
|
||||
};
|
||||
|
||||
|
@ -138,6 +142,8 @@ static void gst_rtsp_client_set_property (GObject * object, guint propid,
|
|||
static void gst_rtsp_client_finalize (GObject * obj);
|
||||
|
||||
static GstSDPMessage *create_sdp (GstRTSPClient * client, GstRTSPMedia * media);
|
||||
static gboolean handle_sdp (GstRTSPClient * client, GstRTSPContext * ctx,
|
||||
GstRTSPMedia * media, GstSDPMessage * sdp);
|
||||
static gboolean default_configure_client_media (GstRTSPClient * client,
|
||||
GstRTSPMedia * media, GstRTSPStream * stream, GstRTSPContext * ctx);
|
||||
static gboolean default_configure_client_transport (GstRTSPClient * client,
|
||||
|
@ -167,6 +173,7 @@ gst_rtsp_client_class_init (GstRTSPClientClass * klass)
|
|||
gobject_class->finalize = gst_rtsp_client_finalize;
|
||||
|
||||
klass->create_sdp = create_sdp;
|
||||
klass->handle_sdp = handle_sdp;
|
||||
klass->configure_client_media = default_configure_client_media;
|
||||
klass->configure_client_transport = default_configure_client_transport;
|
||||
klass->params_set = default_params_set;
|
||||
|
@ -266,6 +273,18 @@ gst_rtsp_client_class_init (GstRTSPClientClass * klass)
|
|||
send_message), NULL, NULL, g_cclosure_marshal_generic,
|
||||
G_TYPE_NONE, 2, GST_TYPE_RTSP_CONTEXT, G_TYPE_POINTER);
|
||||
|
||||
gst_rtsp_client_signals[SIGNAL_ANNOUNCE_REQUEST] =
|
||||
g_signal_new ("announce-request", G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, announce_request),
|
||||
NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
|
||||
GST_TYPE_RTSP_CONTEXT);
|
||||
|
||||
gst_rtsp_client_signals[SIGNAL_RECORD_REQUEST] =
|
||||
g_signal_new ("record-request", G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPClientClass, record_request),
|
||||
NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
|
||||
GST_TYPE_RTSP_CONTEXT);
|
||||
|
||||
tunnels =
|
||||
g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
|
||||
g_mutex_init (&tunnels_lock);
|
||||
|
@ -606,8 +625,6 @@ find_media (GstRTSPClient * client, GstRTSPContext * ctx, gchar * path,
|
|||
path_len = strlen (path);
|
||||
|
||||
if (!paths_are_equal (priv->path, path, path_len)) {
|
||||
GstRTSPThread *thread;
|
||||
|
||||
/* remove any previously cached values before we try to construct a new
|
||||
* media for uri */
|
||||
clean_cached_media (client, TRUE);
|
||||
|
@ -618,14 +635,18 @@ find_media (GstRTSPClient * client, GstRTSPContext * ctx, gchar * path,
|
|||
|
||||
ctx->media = media;
|
||||
|
||||
thread = gst_rtsp_thread_pool_get_thread (priv->thread_pool,
|
||||
GST_RTSP_THREAD_TYPE_MEDIA, ctx);
|
||||
if (thread == NULL)
|
||||
goto no_thread;
|
||||
if (!gst_rtsp_media_is_record (media)) {
|
||||
GstRTSPThread *thread;
|
||||
|
||||
/* prepare the media */
|
||||
if (!(gst_rtsp_media_prepare (media, thread)))
|
||||
goto no_prepare;
|
||||
thread = gst_rtsp_thread_pool_get_thread (priv->thread_pool,
|
||||
GST_RTSP_THREAD_TYPE_MEDIA, ctx);
|
||||
if (thread == NULL)
|
||||
goto no_thread;
|
||||
|
||||
/* prepare the media */
|
||||
if (!gst_rtsp_media_prepare (media, thread))
|
||||
goto no_prepare;
|
||||
}
|
||||
|
||||
/* now keep track of the uri and the media */
|
||||
priv->path = g_strndup (path, path_len);
|
||||
|
@ -1124,6 +1145,9 @@ handle_play_request (GstRTSPClient * client, GstRTSPContext * ctx)
|
|||
ctx->sessmedia = sessmedia;
|
||||
ctx->media = media = gst_rtsp_session_media_get_media (sessmedia);
|
||||
|
||||
if (gst_rtsp_media_is_record (media))
|
||||
goto record_media;
|
||||
|
||||
/* the session state must be playing or ready */
|
||||
rtspstate = gst_rtsp_session_media_get_rtsp_state (sessmedia);
|
||||
if (rtspstate != GST_RTSP_STATE_PLAYING && rtspstate != GST_RTSP_STATE_READY)
|
||||
|
@ -1213,6 +1237,12 @@ unsuspend_failed:
|
|||
send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
|
||||
return FALSE;
|
||||
}
|
||||
record_media:
|
||||
{
|
||||
GST_ERROR ("client %p: RECORD media does not support PLAY", client);
|
||||
send_generic_response (client, GST_RTSP_STS_METHOD_NOT_ALLOWED, ctx);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1400,8 +1430,8 @@ no_address:
|
|||
}
|
||||
|
||||
static GstRTSPTransport *
|
||||
make_server_transport (GstRTSPClient * client, GstRTSPContext * ctx,
|
||||
GstRTSPTransport * ct)
|
||||
make_server_transport (GstRTSPClient * client, GstRTSPMedia * media,
|
||||
GstRTSPContext * ctx, GstRTSPTransport * ct)
|
||||
{
|
||||
GstRTSPTransport *st;
|
||||
GInetAddress *addr;
|
||||
|
@ -1413,6 +1443,8 @@ make_server_transport (GstRTSPClient * client, GstRTSPContext * ctx,
|
|||
st->trans = ct->trans;
|
||||
st->profile = ct->profile;
|
||||
st->lower_transport = ct->lower_transport;
|
||||
st->mode_play = ct->mode_play;
|
||||
st->mode_record = ct->mode_record;
|
||||
|
||||
addr = g_inet_address_new_from_string (ct->destination);
|
||||
|
||||
|
@ -1443,7 +1475,8 @@ make_server_transport (GstRTSPClient * client, GstRTSPContext * ctx,
|
|||
break;
|
||||
}
|
||||
|
||||
gst_rtsp_stream_get_ssrc (ctx->stream, &st->ssrc);
|
||||
if (!gst_rtsp_media_is_record (media))
|
||||
gst_rtsp_stream_get_ssrc (ctx->stream, &st->ssrc);
|
||||
|
||||
return st;
|
||||
}
|
||||
|
@ -1824,6 +1857,11 @@ handle_setup_request (GstRTSPClient * client, GstRTSPContext * ctx)
|
|||
if (!parse_transport (transport, stream, ct))
|
||||
goto unsupported_transports;
|
||||
|
||||
/* TODO: Add support for PLAY,RECORD media */
|
||||
if ((ct->mode_play && gst_rtsp_media_is_record (media)) ||
|
||||
(ct->mode_record && !gst_rtsp_media_is_record (media)))
|
||||
goto unsupported_mode;
|
||||
|
||||
/* parse the keymgmt */
|
||||
if (gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_KEYMGMT,
|
||||
&keymgmt, 0) == GST_RTSP_OK) {
|
||||
|
@ -1877,7 +1915,7 @@ handle_setup_request (GstRTSPClient * client, GstRTSPContext * ctx)
|
|||
}
|
||||
|
||||
/* create and serialize the server transport */
|
||||
st = make_server_transport (client, ctx, ct);
|
||||
st = make_server_transport (client, media, ctx, ct);
|
||||
trans_str = gst_rtsp_transport_as_text (st);
|
||||
gst_rtsp_transport_free (st);
|
||||
|
||||
|
@ -1989,6 +2027,14 @@ unsupported_client_transport:
|
|||
send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, ctx);
|
||||
goto cleanup_transport;
|
||||
}
|
||||
unsupported_mode:
|
||||
{
|
||||
GST_ERROR ("client %p: unsupported mode (media record: %d, mode play: %d"
|
||||
", mode record: %d)", client, gst_rtsp_media_is_record (media),
|
||||
ct->mode_play, ct->mode_record);
|
||||
send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, ctx);
|
||||
goto cleanup_transport;
|
||||
}
|
||||
keymgmt_error:
|
||||
{
|
||||
GST_ERROR ("client %p: keymgmt error", client);
|
||||
|
@ -2102,6 +2148,9 @@ handle_describe_request (GstRTSPClient * client, GstRTSPContext * ctx)
|
|||
if (!(media = find_media (client, ctx, path, NULL)))
|
||||
goto no_media;
|
||||
|
||||
if (gst_rtsp_media_is_record (media))
|
||||
goto record_media;
|
||||
|
||||
/* create an SDP for the media object on this client */
|
||||
if (!(sdp = klass->create_sdp (client, media)))
|
||||
goto no_sdp;
|
||||
|
@ -2161,6 +2210,14 @@ no_media:
|
|||
/* error reply is already sent */
|
||||
return FALSE;
|
||||
}
|
||||
record_media:
|
||||
{
|
||||
GST_ERROR ("client %p: RECORD media does not support DESCRIBE", client);
|
||||
send_generic_response (client, GST_RTSP_STS_METHOD_NOT_ALLOWED, ctx);
|
||||
g_free (path);
|
||||
g_object_unref (media);
|
||||
return FALSE;
|
||||
}
|
||||
no_sdp:
|
||||
{
|
||||
GST_ERROR ("client %p: can't create SDP", client);
|
||||
|
@ -2171,6 +2228,291 @@ no_sdp:
|
|||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_sdp (GstRTSPClient * client, GstRTSPContext * ctx, GstRTSPMedia * media,
|
||||
GstSDPMessage * sdp)
|
||||
{
|
||||
GstRTSPClientPrivate *priv = client->priv;
|
||||
GstRTSPThread *thread;
|
||||
|
||||
/* create an SDP for the media object */
|
||||
if (!gst_rtsp_media_handle_sdp (media, sdp))
|
||||
goto unhandled_sdp;
|
||||
|
||||
thread = gst_rtsp_thread_pool_get_thread (priv->thread_pool,
|
||||
GST_RTSP_THREAD_TYPE_MEDIA, ctx);
|
||||
if (thread == NULL)
|
||||
goto no_thread;
|
||||
|
||||
/* prepare the media */
|
||||
if (!gst_rtsp_media_prepare (media, thread))
|
||||
goto no_prepare;
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
unhandled_sdp:
|
||||
{
|
||||
GST_ERROR ("client %p: could not handle SDP", client);
|
||||
return FALSE;
|
||||
}
|
||||
no_thread:
|
||||
{
|
||||
GST_ERROR ("client %p: can't create thread", client);
|
||||
return FALSE;
|
||||
}
|
||||
no_prepare:
|
||||
{
|
||||
GST_ERROR ("client %p: can't prepare media", client);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_announce_request (GstRTSPClient * client, GstRTSPContext * ctx)
|
||||
{
|
||||
GstRTSPClientPrivate *priv = client->priv;
|
||||
GstRTSPClientClass *klass;
|
||||
GstSDPResult sres;
|
||||
GstSDPMessage *sdp;
|
||||
GstRTSPMedia *media;
|
||||
gchar *path, *cont = NULL;
|
||||
guint8 *data;
|
||||
guint size;
|
||||
|
||||
klass = GST_RTSP_CLIENT_GET_CLASS (client);
|
||||
|
||||
if (!ctx->uri)
|
||||
goto no_uri;
|
||||
|
||||
if (!priv->mount_points)
|
||||
goto no_mount_points;
|
||||
|
||||
if (!(path = gst_rtsp_mount_points_make_path (priv->mount_points, ctx->uri)))
|
||||
goto no_path;
|
||||
|
||||
/* check if reply is SDP */
|
||||
gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_CONTENT_TYPE, &cont,
|
||||
0);
|
||||
/* could not be set but since the request returned OK, we assume it
|
||||
* was SDP, else check it. */
|
||||
if (cont) {
|
||||
if (!g_ascii_strcasecmp (cont, "application/sdp") == 0)
|
||||
goto wrong_content_type;
|
||||
}
|
||||
|
||||
/* get message body and parse as SDP */
|
||||
gst_rtsp_message_get_body (ctx->request, &data, &size);
|
||||
if (data == NULL || size == 0)
|
||||
goto no_message;
|
||||
|
||||
GST_DEBUG ("client %p: parse SDP...", client);
|
||||
gst_sdp_message_new (&sdp);
|
||||
sres = gst_sdp_message_parse_buffer (data, size, sdp);
|
||||
if (sres != GST_SDP_OK)
|
||||
goto sdp_parse_failed;
|
||||
|
||||
if (!(path = gst_rtsp_mount_points_make_path (priv->mount_points, ctx->uri)))
|
||||
goto no_path;
|
||||
|
||||
/* find the media object for the uri */
|
||||
if (!(media = find_media (client, ctx, path, NULL)))
|
||||
goto no_media;
|
||||
|
||||
if (!gst_rtsp_media_is_record (media))
|
||||
goto play_media;
|
||||
|
||||
/* Tell client subclass about the media */
|
||||
if (!klass->handle_sdp (client, ctx, media, sdp))
|
||||
goto unhandled_sdp;
|
||||
|
||||
/* we suspend after the announce */
|
||||
gst_rtsp_media_suspend (media);
|
||||
g_object_unref (media);
|
||||
|
||||
gst_rtsp_message_init_response (ctx->response, GST_RTSP_STS_OK,
|
||||
gst_rtsp_status_as_text (GST_RTSP_STS_OK), ctx->request);
|
||||
|
||||
send_message (client, ctx, ctx->response, FALSE);
|
||||
|
||||
g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_ANNOUNCE_REQUEST],
|
||||
0, ctx);
|
||||
|
||||
return TRUE;
|
||||
|
||||
no_uri:
|
||||
{
|
||||
GST_ERROR ("client %p: no uri", client);
|
||||
send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
|
||||
return FALSE;
|
||||
}
|
||||
no_mount_points:
|
||||
{
|
||||
GST_ERROR ("client %p: no mount points configured", client);
|
||||
send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
|
||||
return FALSE;
|
||||
}
|
||||
no_path:
|
||||
{
|
||||
GST_ERROR ("client %p: can't find path for url", client);
|
||||
send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
|
||||
return FALSE;
|
||||
}
|
||||
wrong_content_type:
|
||||
{
|
||||
GST_ERROR ("client %p: unknown content type", client);
|
||||
send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
|
||||
g_free (path);
|
||||
return FALSE;
|
||||
}
|
||||
no_message:
|
||||
{
|
||||
GST_ERROR ("client %p: can't find SDP message", client);
|
||||
send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
|
||||
g_free (path);
|
||||
return FALSE;
|
||||
}
|
||||
sdp_parse_failed:
|
||||
{
|
||||
GST_ERROR ("client %p: failed to parse SDP message", client);
|
||||
send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
|
||||
g_free (path);
|
||||
return FALSE;
|
||||
}
|
||||
no_media:
|
||||
{
|
||||
GST_ERROR ("client %p: no media", client);
|
||||
g_free (path);
|
||||
/* error reply is already sent */
|
||||
return FALSE;
|
||||
}
|
||||
play_media:
|
||||
{
|
||||
GST_ERROR ("client %p: PLAY media does not support ANNOUNCE", client);
|
||||
send_generic_response (client, GST_RTSP_STS_METHOD_NOT_ALLOWED, ctx);
|
||||
g_free (path);
|
||||
g_object_unref (media);
|
||||
return FALSE;
|
||||
}
|
||||
unhandled_sdp:
|
||||
{
|
||||
GST_ERROR ("client %p: can't handle SDP", client);
|
||||
send_generic_response (client, GST_RTSP_STS_UNSUPPORTED_MEDIA_TYPE, ctx);
|
||||
g_free (path);
|
||||
g_object_unref (media);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_record_request (GstRTSPClient * client, GstRTSPContext * ctx)
|
||||
{
|
||||
GstRTSPSession *session;
|
||||
GstRTSPClientClass *klass;
|
||||
GstRTSPSessionMedia *sessmedia;
|
||||
GstRTSPMedia *media;
|
||||
GstRTSPUrl *uri;
|
||||
GstRTSPState rtspstate;
|
||||
gchar *path;
|
||||
gint matched;
|
||||
|
||||
if (!(session = ctx->session))
|
||||
goto no_session;
|
||||
|
||||
if (!(uri = ctx->uri))
|
||||
goto no_uri;
|
||||
|
||||
klass = GST_RTSP_CLIENT_GET_CLASS (client);
|
||||
path = klass->make_path_from_uri (client, uri);
|
||||
|
||||
/* get a handle to the configuration of the media in the session */
|
||||
sessmedia = gst_rtsp_session_get_media (session, path, &matched);
|
||||
if (!sessmedia)
|
||||
goto not_found;
|
||||
|
||||
if (path[matched] != '\0')
|
||||
goto no_aggregate;
|
||||
|
||||
g_free (path);
|
||||
|
||||
ctx->sessmedia = sessmedia;
|
||||
ctx->media = media = gst_rtsp_session_media_get_media (sessmedia);
|
||||
|
||||
if (!gst_rtsp_media_is_record (media))
|
||||
goto play_media;
|
||||
|
||||
/* the session state must be playing or ready */
|
||||
rtspstate = gst_rtsp_session_media_get_rtsp_state (sessmedia);
|
||||
if (rtspstate != GST_RTSP_STATE_PLAYING && rtspstate != GST_RTSP_STATE_READY)
|
||||
goto invalid_state;
|
||||
|
||||
/* in play we first unsuspend, media could be suspended from SDP or PAUSED */
|
||||
if (!gst_rtsp_media_unsuspend (media))
|
||||
goto unsuspend_failed;
|
||||
|
||||
gst_rtsp_message_init_response (ctx->response, GST_RTSP_STS_OK,
|
||||
gst_rtsp_status_as_text (GST_RTSP_STS_OK), ctx->request);
|
||||
|
||||
send_message (client, ctx, ctx->response, FALSE);
|
||||
|
||||
/* start playing after sending the response */
|
||||
gst_rtsp_session_media_set_state (sessmedia, GST_STATE_PLAYING);
|
||||
|
||||
gst_rtsp_session_media_set_rtsp_state (sessmedia, GST_RTSP_STATE_PLAYING);
|
||||
|
||||
g_signal_emit (client, gst_rtsp_client_signals[SIGNAL_RECORD_REQUEST], 0,
|
||||
ctx);
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
no_session:
|
||||
{
|
||||
GST_ERROR ("client %p: no session", client);
|
||||
send_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, ctx);
|
||||
return FALSE;
|
||||
}
|
||||
no_uri:
|
||||
{
|
||||
GST_ERROR ("client %p: no uri supplied", client);
|
||||
send_generic_response (client, GST_RTSP_STS_BAD_REQUEST, ctx);
|
||||
return FALSE;
|
||||
}
|
||||
not_found:
|
||||
{
|
||||
GST_ERROR ("client %p: media not found", client);
|
||||
send_generic_response (client, GST_RTSP_STS_NOT_FOUND, ctx);
|
||||
return FALSE;
|
||||
}
|
||||
no_aggregate:
|
||||
{
|
||||
GST_ERROR ("client %p: no aggregate path %s", client, path);
|
||||
send_generic_response (client,
|
||||
GST_RTSP_STS_ONLY_AGGREGATE_OPERATION_ALLOWED, ctx);
|
||||
g_free (path);
|
||||
return FALSE;
|
||||
}
|
||||
play_media:
|
||||
{
|
||||
GST_ERROR ("client %p: PLAY media does not support RECORD", client);
|
||||
send_generic_response (client, GST_RTSP_STS_METHOD_NOT_ALLOWED, ctx);
|
||||
return FALSE;
|
||||
}
|
||||
invalid_state:
|
||||
{
|
||||
GST_ERROR ("client %p: not PLAYING or READY", client);
|
||||
send_generic_response (client, GST_RTSP_STS_METHOD_NOT_VALID_IN_THIS_STATE,
|
||||
ctx);
|
||||
return FALSE;
|
||||
}
|
||||
unsuspend_failed:
|
||||
{
|
||||
GST_ERROR ("client %p: unsuspend failed", client);
|
||||
send_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, ctx);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_options_request (GstRTSPClient * client, GstRTSPContext * ctx)
|
||||
{
|
||||
|
@ -2456,7 +2798,11 @@ handle_request (GstRTSPClient * client, GstRTSPMessage * request)
|
|||
handle_get_param_request (client, ctx);
|
||||
break;
|
||||
case GST_RTSP_ANNOUNCE:
|
||||
handle_announce_request (client, ctx);
|
||||
break;
|
||||
case GST_RTSP_RECORD:
|
||||
handle_record_request (client, ctx);
|
||||
break;
|
||||
case GST_RTSP_REDIRECT:
|
||||
if (priv->watch != NULL)
|
||||
gst_rtsp_watch_set_send_backlog (priv->watch, 0, WATCH_BACKLOG_SIZE);
|
||||
|
|
|
@ -121,8 +121,14 @@ struct _GstRTSPClientClass {
|
|||
GstRTSPMessage * response);
|
||||
void (*send_message) (GstRTSPClient * client, GstRTSPContext *ctx,
|
||||
GstRTSPMessage * response);
|
||||
|
||||
gboolean (*handle_sdp) (GstRTSPClient *client, GstRTSPContext *ctx, GstRTSPMedia *media, GstSDPMessage *sdp);
|
||||
|
||||
void (*announce_request) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
void (*record_request) (GstRTSPClient *client, GstRTSPContext *ctx);
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING_LARGE-2];
|
||||
gpointer _gst_reserved[GST_PADDING_LARGE-5];
|
||||
};
|
||||
|
||||
GType gst_rtsp_client_get_type (void);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
* Copyright (C) 2015 Centricular Ltd
|
||||
* Author: Sebastian Dröge <sebastian@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
|
@ -51,6 +53,7 @@ struct _GstRTSPMediaFactoryPrivate
|
|||
GstRTSPPermissions *permissions;
|
||||
gchar *launch;
|
||||
gboolean shared;
|
||||
gboolean record;
|
||||
GstRTSPSuspendMode suspend_mode;
|
||||
gboolean eos_shutdown;
|
||||
GstRTSPProfile profiles;
|
||||
|
@ -72,6 +75,7 @@ struct _GstRTSPMediaFactoryPrivate
|
|||
#define DEFAULT_PROTOCOLS GST_RTSP_LOWER_TRANS_UDP | GST_RTSP_LOWER_TRANS_UDP_MCAST | \
|
||||
GST_RTSP_LOWER_TRANS_TCP
|
||||
#define DEFAULT_BUFFER_SIZE 0x80000
|
||||
#define DEFAULT_RECORD FALSE
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -83,6 +87,7 @@ enum
|
|||
PROP_PROFILES,
|
||||
PROP_PROTOCOLS,
|
||||
PROP_BUFFER_SIZE,
|
||||
PROP_RECORD,
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
|
@ -181,6 +186,14 @@ gst_rtsp_media_factory_class_init (GstRTSPMediaFactoryClass * klass)
|
|||
"The kernel UDP buffer size to use", 0, G_MAXUINT,
|
||||
DEFAULT_BUFFER_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/* FIXME: Should this be a flag property to allow RECORD and PLAY?
|
||||
* Or just another boolean PLAY property that default to TRUE?
|
||||
*/
|
||||
g_object_class_install_property (gobject_class, PROP_RECORD,
|
||||
g_param_spec_boolean ("record", "Record",
|
||||
"If media from this factory is for PLAY or RECORD", DEFAULT_RECORD,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
gst_rtsp_media_factory_signals[SIGNAL_MEDIA_CONSTRUCTED] =
|
||||
g_signal_new ("media-constructed", G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPMediaFactoryClass,
|
||||
|
@ -273,6 +286,9 @@ gst_rtsp_media_factory_get_property (GObject * object, guint propid,
|
|||
g_value_set_uint (value,
|
||||
gst_rtsp_media_factory_get_buffer_size (factory));
|
||||
break;
|
||||
case PROP_RECORD:
|
||||
g_value_set_boolean (value, gst_rtsp_media_factory_is_record (factory));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
|
||||
}
|
||||
|
@ -309,6 +325,9 @@ gst_rtsp_media_factory_set_property (GObject * object, guint propid,
|
|||
gst_rtsp_media_factory_set_buffer_size (factory,
|
||||
g_value_get_uint (value));
|
||||
break;
|
||||
case PROP_RECORD:
|
||||
gst_rtsp_media_factory_set_record (factory, g_value_get_boolean (value));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
|
||||
}
|
||||
|
@ -1136,6 +1155,7 @@ default_configure (GstRTSPMediaFactory * factory, GstRTSPMedia * media)
|
|||
GstRTSPAddressPool *pool;
|
||||
GstRTSPPermissions *perms;
|
||||
GstClockTime rtx_time;
|
||||
gboolean record;
|
||||
|
||||
/* configure the sharedness */
|
||||
GST_RTSP_MEDIA_FACTORY_LOCK (factory);
|
||||
|
@ -1146,6 +1166,7 @@ default_configure (GstRTSPMediaFactory * factory, GstRTSPMedia * media)
|
|||
profiles = priv->profiles;
|
||||
protocols = priv->protocols;
|
||||
rtx_time = priv->rtx_time;
|
||||
record = priv->record;
|
||||
GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
|
||||
|
||||
gst_rtsp_media_set_suspend_mode (media, suspend_mode);
|
||||
|
@ -1155,6 +1176,7 @@ default_configure (GstRTSPMediaFactory * factory, GstRTSPMedia * media)
|
|||
gst_rtsp_media_set_profiles (media, profiles);
|
||||
gst_rtsp_media_set_protocols (media, protocols);
|
||||
gst_rtsp_media_set_retransmission_time (media, rtx_time);
|
||||
gst_rtsp_media_set_record (media, record);
|
||||
|
||||
if ((pool = gst_rtsp_media_factory_get_address_pool (factory))) {
|
||||
gst_rtsp_media_set_address_pool (media, pool);
|
||||
|
@ -1199,3 +1221,51 @@ gst_rtsp_media_factory_create_element (GstRTSPMediaFactory * factory,
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_media_factory_set_record:
|
||||
* @factory: a #GstRTSPMediaFactory
|
||||
* @record: the new value
|
||||
*
|
||||
* Configure if this factory creates media for PLAY or RECORD methods.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_media_factory_set_record (GstRTSPMediaFactory * factory,
|
||||
gboolean record)
|
||||
{
|
||||
GstRTSPMediaFactoryPrivate *priv;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory));
|
||||
|
||||
priv = factory->priv;
|
||||
|
||||
GST_RTSP_MEDIA_FACTORY_LOCK (factory);
|
||||
priv->record = record;
|
||||
GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_media_factory_is_record:
|
||||
* @factory: a #GstRTSPMediaFactory
|
||||
*
|
||||
* Get if media created from this factory can be used for PLAY or RECORD
|
||||
* methods.
|
||||
*
|
||||
* Returns: %TRUE if the media will be record between clients.
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_media_factory_is_record (GstRTSPMediaFactory * factory)
|
||||
{
|
||||
GstRTSPMediaFactoryPrivate *priv;
|
||||
gboolean result;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory), FALSE);
|
||||
|
||||
priv = factory->priv;
|
||||
|
||||
GST_RTSP_MEDIA_FACTORY_LOCK (factory);
|
||||
result = priv->record;
|
||||
GST_RTSP_MEDIA_FACTORY_UNLOCK (factory);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -145,6 +145,10 @@ void gst_rtsp_media_factory_set_retransmission_time (GstRTSPMed
|
|||
GstClockTime time);
|
||||
GstClockTime gst_rtsp_media_factory_get_retransmission_time (GstRTSPMediaFactory * factory);
|
||||
|
||||
void gst_rtsp_media_factory_set_record (GstRTSPMediaFactory *factory,
|
||||
gboolean record);
|
||||
gboolean gst_rtsp_media_factory_is_record (GstRTSPMediaFactory *factory);
|
||||
|
||||
/* creating the media from the factory and a url */
|
||||
GstRTSPMedia * gst_rtsp_media_factory_construct (GstRTSPMediaFactory *factory,
|
||||
const GstRTSPUrl *url);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
* Copyright (C) 2015 Centricular Ltd
|
||||
* Author: Sebastian Dröge <sebastian@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
|
@ -61,12 +63,22 @@
|
|||
* Last reviewed on 2013-07-11 (1.0.0)
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <gst/app/gstappsrc.h>
|
||||
#include <gst/app/gstappsink.h>
|
||||
|
||||
#include <gst/sdp/gstmikey.h>
|
||||
#include <gst/rtp/gstrtppayloads.h>
|
||||
|
||||
#define AES_128_KEY_LEN 16
|
||||
#define AES_256_KEY_LEN 32
|
||||
|
||||
#define HMAC_32_KEY_LEN 4
|
||||
#define HMAC_80_KEY_LEN 10
|
||||
|
||||
#include "rtsp-media.h"
|
||||
|
||||
#define GST_RTSP_MEDIA_GET_PRIVATE(obj) \
|
||||
|
@ -89,6 +101,7 @@ struct _GstRTSPMediaPrivate
|
|||
guint buffer_size;
|
||||
GstRTSPAddressPool *pool;
|
||||
gboolean blocked;
|
||||
gboolean record;
|
||||
|
||||
GstElement *element;
|
||||
GRecMutex state_lock; /* locking order: state lock, lock */
|
||||
|
@ -135,6 +148,7 @@ struct _GstRTSPMediaPrivate
|
|||
#define DEFAULT_EOS_SHUTDOWN FALSE
|
||||
#define DEFAULT_BUFFER_SIZE 0x80000
|
||||
#define DEFAULT_TIME_PROVIDER FALSE
|
||||
#define DEFAULT_RECORD FALSE
|
||||
|
||||
/* define to dump received RTCP packets */
|
||||
#undef DUMP_STATS
|
||||
|
@ -151,6 +165,7 @@ enum
|
|||
PROP_BUFFER_SIZE,
|
||||
PROP_ELEMENT,
|
||||
PROP_TIME_PROVIDER,
|
||||
PROP_RECORD,
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
|
@ -189,6 +204,7 @@ static gboolean default_query_stop (GstRTSPMedia * media, gint64 * stop);
|
|||
static GstElement *default_create_rtpbin (GstRTSPMedia * media);
|
||||
static gboolean default_setup_sdp (GstRTSPMedia * media, GstSDPMessage * sdp,
|
||||
GstSDPInfo * info);
|
||||
static gboolean default_handle_sdp (GstRTSPMedia * media, GstSDPMessage * sdp);
|
||||
|
||||
static gboolean wait_preroll (GstRTSPMedia * media);
|
||||
|
||||
|
@ -277,6 +293,11 @@ gst_rtsp_media_class_init (GstRTSPMediaClass * klass)
|
|||
"Use a NetTimeProvider for clients",
|
||||
DEFAULT_TIME_PROVIDER, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_RECORD,
|
||||
g_param_spec_boolean ("record", "Record",
|
||||
"If this media pipeline can be used for PLAY or RECORD",
|
||||
DEFAULT_RECORD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
gst_rtsp_media_signals[SIGNAL_NEW_STREAM] =
|
||||
g_signal_new ("new-stream", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (GstRTSPMediaClass, new_stream), NULL, NULL,
|
||||
|
@ -320,6 +341,7 @@ gst_rtsp_media_class_init (GstRTSPMediaClass * klass)
|
|||
klass->query_stop = default_query_stop;
|
||||
klass->create_rtpbin = default_create_rtpbin;
|
||||
klass->setup_sdp = default_setup_sdp;
|
||||
klass->handle_sdp = default_handle_sdp;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -342,6 +364,7 @@ gst_rtsp_media_init (GstRTSPMedia * media)
|
|||
priv->eos_shutdown = DEFAULT_EOS_SHUTDOWN;
|
||||
priv->buffer_size = DEFAULT_BUFFER_SIZE;
|
||||
priv->time_provider = DEFAULT_TIME_PROVIDER;
|
||||
priv->record = DEFAULT_RECORD;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -412,6 +435,9 @@ gst_rtsp_media_get_property (GObject * object, guint propid,
|
|||
case PROP_TIME_PROVIDER:
|
||||
g_value_set_boolean (value, gst_rtsp_media_is_time_provider (media));
|
||||
break;
|
||||
case PROP_RECORD:
|
||||
g_value_set_boolean (value, gst_rtsp_media_is_record (media));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
|
||||
}
|
||||
|
@ -452,6 +478,9 @@ gst_rtsp_media_set_property (GObject * object, guint propid,
|
|||
case PROP_TIME_PROVIDER:
|
||||
gst_rtsp_media_use_time_provider (media, g_value_get_boolean (value));
|
||||
break;
|
||||
case PROP_RECORD:
|
||||
gst_rtsp_media_set_record (media, g_value_get_boolean (value));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
|
||||
}
|
||||
|
@ -1300,6 +1329,9 @@ _next_available_pt (GList * payloads)
|
|||
*
|
||||
* Collect all dynamic elements, named dynpay\%d, and add them to
|
||||
* the list of dynamic elements.
|
||||
*
|
||||
* Find all depayloader elements, they should be named depay\%d in the
|
||||
* element of @media, and create #GstRTSPStreams for them.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_media_collect_streams (GstRTSPMedia * media)
|
||||
|
@ -1348,6 +1380,21 @@ gst_rtsp_media_collect_streams (GstRTSPMedia * media)
|
|||
have_elem = TRUE;
|
||||
}
|
||||
g_free (name);
|
||||
|
||||
name = g_strdup_printf ("depay%d", i);
|
||||
if ((elem = gst_bin_get_by_name (GST_BIN (element), name))) {
|
||||
GST_INFO ("found stream %d with depayloader %p", i, elem);
|
||||
|
||||
/* take the pad of the payloader */
|
||||
pad = gst_element_get_static_pad (elem, "sink");
|
||||
/* create the stream */
|
||||
gst_rtsp_media_create_stream (media, elem, pad);
|
||||
gst_object_unref (pad);
|
||||
gst_object_unref (elem);
|
||||
|
||||
have_elem = TRUE;
|
||||
}
|
||||
g_free (name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1355,10 +1402,10 @@ gst_rtsp_media_collect_streams (GstRTSPMedia * media)
|
|||
* gst_rtsp_media_create_stream:
|
||||
* @media: a #GstRTSPMedia
|
||||
* @payloader: a #GstElement
|
||||
* @srcpad: a source #GstPad
|
||||
* @pad: a #GstPad
|
||||
*
|
||||
* Create a new stream in @media that provides RTP data on @srcpad.
|
||||
* @srcpad should be a pad of an element inside @media->element.
|
||||
* Create a new stream in @media that provides RTP data on @pad.
|
||||
* @pad should be a pad of an element inside @media->element.
|
||||
*
|
||||
* Returns: (transfer none): a new #GstRTSPStream that remains valid for as long
|
||||
* as @media exists.
|
||||
|
@ -1369,15 +1416,13 @@ gst_rtsp_media_create_stream (GstRTSPMedia * media, GstElement * payloader,
|
|||
{
|
||||
GstRTSPMediaPrivate *priv;
|
||||
GstRTSPStream *stream;
|
||||
GstPad *srcpad;
|
||||
GstPad *ghostpad;
|
||||
gchar *name;
|
||||
gint idx;
|
||||
gint i, n;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
|
||||
g_return_val_if_fail (GST_IS_ELEMENT (payloader), NULL);
|
||||
g_return_val_if_fail (GST_IS_PAD (pad), NULL);
|
||||
g_return_val_if_fail (GST_PAD_IS_SRC (pad), NULL);
|
||||
|
||||
priv = media->priv;
|
||||
|
||||
|
@ -1386,13 +1431,17 @@ gst_rtsp_media_create_stream (GstRTSPMedia * media, GstElement * payloader,
|
|||
|
||||
GST_DEBUG ("media %p: creating stream with index %d", media, idx);
|
||||
|
||||
name = g_strdup_printf ("src_%u", idx);
|
||||
srcpad = gst_ghost_pad_new (name, pad);
|
||||
gst_pad_set_active (srcpad, TRUE);
|
||||
gst_element_add_pad (priv->element, srcpad);
|
||||
if (GST_PAD_IS_SRC (pad))
|
||||
name = g_strdup_printf ("src_%u", idx);
|
||||
else
|
||||
name = g_strdup_printf ("sink_%u", idx);
|
||||
|
||||
ghostpad = gst_ghost_pad_new (name, pad);
|
||||
gst_pad_set_active (ghostpad, TRUE);
|
||||
gst_element_add_pad (priv->element, ghostpad);
|
||||
g_free (name);
|
||||
|
||||
stream = gst_rtsp_stream_new (idx, payloader, srcpad);
|
||||
stream = gst_rtsp_stream_new (idx, payloader, ghostpad);
|
||||
if (priv->pool)
|
||||
gst_rtsp_stream_set_address_pool (stream, priv->pool);
|
||||
gst_rtsp_stream_set_profiles (stream, priv->profiles);
|
||||
|
@ -1401,23 +1450,28 @@ gst_rtsp_media_create_stream (GstRTSPMedia * media, GstElement * payloader,
|
|||
|
||||
g_ptr_array_add (priv->streams, stream);
|
||||
|
||||
if (priv->payloads)
|
||||
g_list_free (priv->payloads);
|
||||
priv->payloads = _find_payload_types (media);
|
||||
if (GST_PAD_IS_SRC (pad)) {
|
||||
gint i, n;
|
||||
|
||||
n = priv->streams->len;
|
||||
for (i = 0; i < n; i++) {
|
||||
GstRTSPStream *stream = g_ptr_array_index (priv->streams, i);
|
||||
guint rtx_pt = _next_available_pt (priv->payloads);
|
||||
if (priv->payloads)
|
||||
g_list_free (priv->payloads);
|
||||
priv->payloads = _find_payload_types (media);
|
||||
|
||||
if (rtx_pt == 0) {
|
||||
GST_WARNING ("Ran out of space of dynamic payload types");
|
||||
break;
|
||||
n = priv->streams->len;
|
||||
for (i = 0; i < n; i++) {
|
||||
GstRTSPStream *stream = g_ptr_array_index (priv->streams, i);
|
||||
guint rtx_pt = _next_available_pt (priv->payloads);
|
||||
|
||||
if (rtx_pt == 0) {
|
||||
GST_WARNING ("Ran out of space of dynamic payload types");
|
||||
break;
|
||||
}
|
||||
|
||||
gst_rtsp_stream_set_retransmission_pt (stream, rtx_pt);
|
||||
|
||||
priv->payloads =
|
||||
g_list_append (priv->payloads, GUINT_TO_POINTER (rtx_pt));
|
||||
}
|
||||
|
||||
gst_rtsp_stream_set_retransmission_pt (stream, rtx_pt);
|
||||
|
||||
priv->payloads = g_list_append (priv->payloads, GUINT_TO_POINTER (rtx_pt));
|
||||
}
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
|
@ -1716,16 +1770,21 @@ gst_rtsp_media_seek (GstRTSPMedia * media, GstRTSPTimeRange * range)
|
|||
goto not_prepared;
|
||||
|
||||
/* Update the seekable state of the pipeline in case it changed */
|
||||
query = gst_query_new_seeking (GST_FORMAT_TIME);
|
||||
if (gst_element_query (priv->pipeline, query)) {
|
||||
GstFormat format;
|
||||
gboolean seekable;
|
||||
gint64 start, end;
|
||||
if (gst_rtsp_media_is_record (media)) {
|
||||
/* TODO: Seeking for RECORD? */
|
||||
priv->seekable = FALSE;
|
||||
} else {
|
||||
query = gst_query_new_seeking (GST_FORMAT_TIME);
|
||||
if (gst_element_query (priv->pipeline, query)) {
|
||||
GstFormat format;
|
||||
gboolean seekable;
|
||||
gint64 start, end;
|
||||
|
||||
gst_query_parse_seeking (query, &format, &seekable, &start, &end);
|
||||
priv->seekable = seekable;
|
||||
gst_query_parse_seeking (query, &format, &seekable, &start, &end);
|
||||
priv->seekable = seekable;
|
||||
}
|
||||
gst_query_unref (query);
|
||||
}
|
||||
gst_query_unref (query);
|
||||
|
||||
if (!priv->seekable)
|
||||
goto not_seekable;
|
||||
|
@ -1898,7 +1957,28 @@ default_handle_message (GstRTSPMedia * media, GstMessage * message)
|
|||
|
||||
switch (type) {
|
||||
case GST_MESSAGE_STATE_CHANGED:
|
||||
{
|
||||
GstState old, new, pending;
|
||||
|
||||
if (GST_MESSAGE_SRC (message) != GST_OBJECT (priv->pipeline))
|
||||
break;
|
||||
|
||||
gst_message_parse_state_changed (message, &old, &new, &pending);
|
||||
|
||||
GST_DEBUG ("%p: went from %s to %s (pending %s)", media,
|
||||
gst_element_state_get_name (old), gst_element_state_get_name (new),
|
||||
gst_element_state_get_name (pending));
|
||||
if (gst_rtsp_media_is_record (media)
|
||||
&& old == GST_STATE_READY && new == GST_STATE_PAUSED) {
|
||||
GST_INFO ("%p: went to PAUSED, prepared now", media);
|
||||
collect_media_stats (media);
|
||||
|
||||
if (priv->status == GST_RTSP_MEDIA_STATUS_PREPARING)
|
||||
gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARED);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case GST_MESSAGE_BUFFERING:
|
||||
{
|
||||
gint percent;
|
||||
|
@ -2203,8 +2283,10 @@ start_preroll (GstRTSPMedia * media)
|
|||
* seeking query in preroll instead */
|
||||
priv->seekable = FALSE;
|
||||
priv->is_live = TRUE;
|
||||
/* start blocked to make sure nothing goes to the sink */
|
||||
media_streams_set_blocked (media, TRUE);
|
||||
if (!gst_rtsp_media_is_record (media)) {
|
||||
/* start blocked to make sure nothing goes to the sink */
|
||||
media_streams_set_blocked (media, TRUE);
|
||||
}
|
||||
ret = set_state (media, GST_STATE_PLAYING);
|
||||
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||
goto state_failed;
|
||||
|
@ -2830,6 +2912,583 @@ no_setup_sdp:
|
|||
}
|
||||
}
|
||||
|
||||
static const gchar *
|
||||
rtsp_get_attribute_for_pt (const GstSDPMedia * media, const gchar * name,
|
||||
gint pt)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0;; i++) {
|
||||
const gchar *attr;
|
||||
gint val;
|
||||
|
||||
if ((attr = gst_sdp_media_get_attribute_val_n (media, name, i)) == NULL)
|
||||
break;
|
||||
|
||||
if (sscanf (attr, "%d ", &val) != 1)
|
||||
continue;
|
||||
|
||||
if (val == pt)
|
||||
return attr;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define PARSE_INT(p, del, res) \
|
||||
G_STMT_START { \
|
||||
gchar *t = p; \
|
||||
p = strstr (p, del); \
|
||||
if (p == NULL) \
|
||||
res = -1; \
|
||||
else { \
|
||||
*p = '\0'; \
|
||||
p++; \
|
||||
res = atoi (t); \
|
||||
} \
|
||||
} G_STMT_END
|
||||
|
||||
#define PARSE_STRING(p, del, res) \
|
||||
G_STMT_START { \
|
||||
gchar *t = p; \
|
||||
p = strstr (p, del); \
|
||||
if (p == NULL) { \
|
||||
res = NULL; \
|
||||
p = t; \
|
||||
} \
|
||||
else { \
|
||||
*p = '\0'; \
|
||||
p++; \
|
||||
res = t; \
|
||||
} \
|
||||
} G_STMT_END
|
||||
|
||||
#define SKIP_SPACES(p) \
|
||||
while (*p && g_ascii_isspace (*p)) \
|
||||
p++;
|
||||
|
||||
/* rtpmap contains:
|
||||
*
|
||||
* <payload> <encoding_name>/<clock_rate>[/<encoding_params>]
|
||||
*/
|
||||
static gboolean
|
||||
parse_rtpmap (const gchar * rtpmap, gint * payload, gchar ** name,
|
||||
gint * rate, gchar ** params)
|
||||
{
|
||||
gchar *p, *t;
|
||||
|
||||
p = (gchar *) rtpmap;
|
||||
|
||||
PARSE_INT (p, " ", *payload);
|
||||
if (*payload == -1)
|
||||
return FALSE;
|
||||
|
||||
SKIP_SPACES (p);
|
||||
if (*p == '\0')
|
||||
return FALSE;
|
||||
|
||||
PARSE_STRING (p, "/", *name);
|
||||
if (*name == NULL) {
|
||||
GST_DEBUG ("no rate, name %s", p);
|
||||
/* no rate, assume -1 then, this is not supposed to happen but RealMedia
|
||||
* streams seem to omit the rate. */
|
||||
*name = p;
|
||||
*rate = -1;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
t = p;
|
||||
p = strstr (p, "/");
|
||||
if (p == NULL) {
|
||||
*rate = atoi (t);
|
||||
return TRUE;
|
||||
}
|
||||
*p = '\0';
|
||||
p++;
|
||||
*rate = atoi (t);
|
||||
|
||||
t = p;
|
||||
if (*p == '\0')
|
||||
return TRUE;
|
||||
*params = t;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mapping of caps to and from SDP fields:
|
||||
*
|
||||
* a=rtpmap:<payload> <encoding_name>/<clock_rate>[/<encoding_params>]
|
||||
* a=fmtp:<payload> <param>[=<value>];...
|
||||
*/
|
||||
static GstCaps *
|
||||
media_to_caps (gint pt, const GstSDPMedia * media)
|
||||
{
|
||||
GstCaps *caps;
|
||||
const gchar *rtpmap;
|
||||
const gchar *fmtp;
|
||||
gchar *name = NULL;
|
||||
gint rate = -1;
|
||||
gchar *params = NULL;
|
||||
gchar *tmp;
|
||||
GstStructure *s;
|
||||
gint payload = 0;
|
||||
gboolean ret;
|
||||
|
||||
/* get and parse rtpmap */
|
||||
rtpmap = rtsp_get_attribute_for_pt (media, "rtpmap", pt);
|
||||
|
||||
if (rtpmap) {
|
||||
ret = parse_rtpmap (rtpmap, &payload, &name, &rate, ¶ms);
|
||||
if (!ret) {
|
||||
g_warning ("error parsing rtpmap, ignoring");
|
||||
rtpmap = NULL;
|
||||
}
|
||||
}
|
||||
/* dynamic payloads need rtpmap or we fail */
|
||||
if (rtpmap == NULL && pt >= 96)
|
||||
goto no_rtpmap;
|
||||
|
||||
/* check if we have a rate, if not, we need to look up the rate from the
|
||||
* default rates based on the payload types. */
|
||||
if (rate == -1) {
|
||||
const GstRTPPayloadInfo *info;
|
||||
|
||||
if (GST_RTP_PAYLOAD_IS_DYNAMIC (pt)) {
|
||||
/* dynamic types, use media and encoding_name */
|
||||
tmp = g_ascii_strdown (media->media, -1);
|
||||
info = gst_rtp_payload_info_for_name (tmp, name);
|
||||
g_free (tmp);
|
||||
} else {
|
||||
/* static types, use payload type */
|
||||
info = gst_rtp_payload_info_for_pt (pt);
|
||||
}
|
||||
|
||||
if (info) {
|
||||
if ((rate = info->clock_rate) == 0)
|
||||
rate = -1;
|
||||
}
|
||||
/* we fail if we cannot find one */
|
||||
if (rate == -1)
|
||||
goto no_rate;
|
||||
}
|
||||
|
||||
tmp = g_ascii_strdown (media->media, -1);
|
||||
caps = gst_caps_new_simple ("application/x-unknown",
|
||||
"media", G_TYPE_STRING, tmp, "payload", G_TYPE_INT, pt, NULL);
|
||||
g_free (tmp);
|
||||
s = gst_caps_get_structure (caps, 0);
|
||||
|
||||
gst_structure_set (s, "clock-rate", G_TYPE_INT, rate, NULL);
|
||||
|
||||
/* encoding name must be upper case */
|
||||
if (name != NULL) {
|
||||
tmp = g_ascii_strup (name, -1);
|
||||
gst_structure_set (s, "encoding-name", G_TYPE_STRING, tmp, NULL);
|
||||
g_free (tmp);
|
||||
}
|
||||
|
||||
/* params must be lower case */
|
||||
if (params != NULL) {
|
||||
tmp = g_ascii_strdown (params, -1);
|
||||
gst_structure_set (s, "encoding-params", G_TYPE_STRING, tmp, NULL);
|
||||
g_free (tmp);
|
||||
}
|
||||
|
||||
/* parse optional fmtp: field */
|
||||
if ((fmtp = rtsp_get_attribute_for_pt (media, "fmtp", pt))) {
|
||||
gchar *p;
|
||||
gint payload = 0;
|
||||
|
||||
p = (gchar *) fmtp;
|
||||
|
||||
/* p is now of the format <payload> <param>[=<value>];... */
|
||||
PARSE_INT (p, " ", payload);
|
||||
if (payload != -1 && payload == pt) {
|
||||
gchar **pairs;
|
||||
gint i;
|
||||
|
||||
/* <param>[=<value>] are separated with ';' */
|
||||
pairs = g_strsplit (p, ";", 0);
|
||||
for (i = 0; pairs[i]; i++) {
|
||||
gchar *valpos;
|
||||
const gchar *val, *key;
|
||||
|
||||
/* the key may not have a '=', the value can have other '='s */
|
||||
valpos = strstr (pairs[i], "=");
|
||||
if (valpos) {
|
||||
/* we have a '=' and thus a value, remove the '=' with \0 */
|
||||
*valpos = '\0';
|
||||
/* value is everything between '=' and ';'. We split the pairs at ;
|
||||
* boundaries so we can take the remainder of the value. Some servers
|
||||
* put spaces around the value which we strip off here. Alternatively
|
||||
* we could strip those spaces in the depayloaders should these spaces
|
||||
* actually carry any meaning in the future. */
|
||||
val = g_strstrip (valpos + 1);
|
||||
} else {
|
||||
/* simple <param>;.. is translated into <param>=1;... */
|
||||
val = "1";
|
||||
}
|
||||
/* strip the key of spaces, convert key to lowercase but not the value. */
|
||||
key = g_strstrip (pairs[i]);
|
||||
if (strlen (key) > 1) {
|
||||
tmp = g_ascii_strdown (key, -1);
|
||||
gst_structure_set (s, tmp, G_TYPE_STRING, val, NULL);
|
||||
g_free (tmp);
|
||||
}
|
||||
}
|
||||
g_strfreev (pairs);
|
||||
}
|
||||
}
|
||||
return caps;
|
||||
|
||||
/* ERRORS */
|
||||
no_rtpmap:
|
||||
{
|
||||
g_warning ("rtpmap type not given for dynamic payload %d", pt);
|
||||
return NULL;
|
||||
}
|
||||
no_rate:
|
||||
{
|
||||
g_warning ("rate unknown for payload type %d", pt);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_keymgmt (const gchar * keymgmt, GstCaps * caps)
|
||||
{
|
||||
gboolean res = FALSE;
|
||||
gchar *p, *kmpid;
|
||||
gsize size;
|
||||
guchar *data;
|
||||
GstMIKEYMessage *msg;
|
||||
const GstMIKEYPayload *payload;
|
||||
const gchar *srtp_cipher;
|
||||
const gchar *srtp_auth;
|
||||
|
||||
p = (gchar *) keymgmt;
|
||||
|
||||
SKIP_SPACES (p);
|
||||
if (*p == '\0')
|
||||
return FALSE;
|
||||
|
||||
PARSE_STRING (p, " ", kmpid);
|
||||
if (!g_str_equal (kmpid, "mikey"))
|
||||
return FALSE;
|
||||
|
||||
data = g_base64_decode (p, &size);
|
||||
if (data == NULL)
|
||||
return FALSE;
|
||||
|
||||
msg = gst_mikey_message_new_from_data (data, size, NULL, NULL);
|
||||
g_free (data);
|
||||
if (msg == NULL)
|
||||
return FALSE;
|
||||
|
||||
srtp_cipher = "aes-128-icm";
|
||||
srtp_auth = "hmac-sha1-80";
|
||||
|
||||
/* check the Security policy if any */
|
||||
if ((payload = gst_mikey_message_find_payload (msg, GST_MIKEY_PT_SP, 0))) {
|
||||
GstMIKEYPayloadSP *p = (GstMIKEYPayloadSP *) payload;
|
||||
guint len, i;
|
||||
|
||||
if (p->proto != GST_MIKEY_SEC_PROTO_SRTP)
|
||||
goto done;
|
||||
|
||||
len = gst_mikey_payload_sp_get_n_params (payload);
|
||||
for (i = 0; i < len; i++) {
|
||||
const GstMIKEYPayloadSPParam *param =
|
||||
gst_mikey_payload_sp_get_param (payload, i);
|
||||
|
||||
switch (param->type) {
|
||||
case GST_MIKEY_SP_SRTP_ENC_ALG:
|
||||
switch (param->val[0]) {
|
||||
case 0:
|
||||
srtp_cipher = "null";
|
||||
break;
|
||||
case 2:
|
||||
case 1:
|
||||
srtp_cipher = "aes-128-icm";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case GST_MIKEY_SP_SRTP_ENC_KEY_LEN:
|
||||
switch (param->val[0]) {
|
||||
case AES_128_KEY_LEN:
|
||||
srtp_cipher = "aes-128-icm";
|
||||
break;
|
||||
case AES_256_KEY_LEN:
|
||||
srtp_cipher = "aes-256-icm";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case GST_MIKEY_SP_SRTP_AUTH_ALG:
|
||||
switch (param->val[0]) {
|
||||
case 0:
|
||||
srtp_auth = "null";
|
||||
break;
|
||||
case 2:
|
||||
case 1:
|
||||
srtp_auth = "hmac-sha1-80";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case GST_MIKEY_SP_SRTP_AUTH_KEY_LEN:
|
||||
switch (param->val[0]) {
|
||||
case HMAC_32_KEY_LEN:
|
||||
srtp_auth = "hmac-sha1-32";
|
||||
break;
|
||||
case HMAC_80_KEY_LEN:
|
||||
srtp_auth = "hmac-sha1-80";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case GST_MIKEY_SP_SRTP_SRTP_ENC:
|
||||
break;
|
||||
case GST_MIKEY_SP_SRTP_SRTCP_ENC:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(payload = gst_mikey_message_find_payload (msg, GST_MIKEY_PT_KEMAC, 0)))
|
||||
goto done;
|
||||
else {
|
||||
GstMIKEYPayloadKEMAC *p = (GstMIKEYPayloadKEMAC *) payload;
|
||||
const GstMIKEYPayload *sub;
|
||||
GstMIKEYPayloadKeyData *pkd;
|
||||
GstBuffer *buf;
|
||||
|
||||
if (p->enc_alg != GST_MIKEY_ENC_NULL || p->mac_alg != GST_MIKEY_MAC_NULL)
|
||||
goto done;
|
||||
|
||||
if (!(sub = gst_mikey_payload_kemac_get_sub (payload, 0)))
|
||||
goto done;
|
||||
|
||||
if (sub->type != GST_MIKEY_PT_KEY_DATA)
|
||||
goto done;
|
||||
|
||||
pkd = (GstMIKEYPayloadKeyData *) sub;
|
||||
buf =
|
||||
gst_buffer_new_wrapped (g_memdup (pkd->key_data, pkd->key_len),
|
||||
pkd->key_len);
|
||||
gst_caps_set_simple (caps, "srtp-key", GST_TYPE_BUFFER, buf, NULL);
|
||||
}
|
||||
|
||||
gst_caps_set_simple (caps,
|
||||
"srtp-cipher", G_TYPE_STRING, srtp_cipher,
|
||||
"srtp-auth", G_TYPE_STRING, srtp_auth,
|
||||
"srtcp-cipher", G_TYPE_STRING, srtp_cipher,
|
||||
"srtcp-auth", G_TYPE_STRING, srtp_auth, NULL);
|
||||
|
||||
res = TRUE;
|
||||
done:
|
||||
gst_mikey_message_unref (msg);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mapping SDP attributes to caps
|
||||
*
|
||||
* prepend 'a-' to IANA registered sdp attributes names
|
||||
* (ie: not prefixed with 'x-') in order to avoid
|
||||
* collision with gstreamer standard caps properties names
|
||||
*/
|
||||
static void
|
||||
sdp_attributes_to_caps (GArray * attributes, GstCaps * caps)
|
||||
{
|
||||
if (attributes->len > 0) {
|
||||
GstStructure *s;
|
||||
guint i;
|
||||
|
||||
s = gst_caps_get_structure (caps, 0);
|
||||
|
||||
for (i = 0; i < attributes->len; i++) {
|
||||
GstSDPAttribute *attr = &g_array_index (attributes, GstSDPAttribute, i);
|
||||
gchar *tofree, *key;
|
||||
|
||||
key = attr->key;
|
||||
|
||||
/* skip some of the attribute we already handle */
|
||||
if (!strcmp (key, "fmtp"))
|
||||
continue;
|
||||
if (!strcmp (key, "rtpmap"))
|
||||
continue;
|
||||
if (!strcmp (key, "control"))
|
||||
continue;
|
||||
if (!strcmp (key, "range"))
|
||||
continue;
|
||||
if (g_str_equal (key, "key-mgmt")) {
|
||||
parse_keymgmt (attr->value, caps);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* string must be valid UTF8 */
|
||||
if (!g_utf8_validate (attr->value, -1, NULL))
|
||||
continue;
|
||||
|
||||
if (!g_str_has_prefix (key, "x-"))
|
||||
tofree = key = g_strdup_printf ("a-%s", key);
|
||||
else
|
||||
tofree = NULL;
|
||||
|
||||
GST_DEBUG ("adding caps: %s=%s", key, attr->value);
|
||||
gst_structure_set (s, key, G_TYPE_STRING, attr->value, NULL);
|
||||
g_free (tofree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
default_handle_sdp (GstRTSPMedia * media, GstSDPMessage * sdp)
|
||||
{
|
||||
GstRTSPMediaPrivate *priv = media->priv;
|
||||
gint i, medias_len;
|
||||
|
||||
medias_len = gst_sdp_message_medias_len (sdp);
|
||||
if (medias_len != priv->streams->len) {
|
||||
GST_ERROR ("%p: Media has more or less streams than SDP (%d /= %d)", media,
|
||||
priv->streams->len, medias_len);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (i = 0; i < medias_len; i++) {
|
||||
const gchar *proto, *media_type;
|
||||
const GstSDPMedia *sdp_media = gst_sdp_message_get_media (sdp, i);
|
||||
GstRTSPStream *stream;
|
||||
gint j, formats_len;
|
||||
const gchar *control;
|
||||
GstRTSPProfile profile, profiles;
|
||||
|
||||
stream = g_ptr_array_index (priv->streams, i);
|
||||
|
||||
/* TODO: Should we do something with the other SDP information? */
|
||||
|
||||
/* get proto */
|
||||
proto = gst_sdp_media_get_proto (sdp_media);
|
||||
if (proto == NULL) {
|
||||
GST_ERROR ("%p: SDP media %d has no proto", media, i);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (g_str_equal (proto, "RTP/AVP")) {
|
||||
media_type = "application/x-rtp";
|
||||
profile = GST_RTSP_PROFILE_AVP;
|
||||
} else if (g_str_equal (proto, "RTP/SAVP")) {
|
||||
media_type = "application/x-srtp";
|
||||
profile = GST_RTSP_PROFILE_SAVP;
|
||||
} else if (g_str_equal (proto, "RTP/AVPF")) {
|
||||
media_type = "application/x-rtp";
|
||||
profile = GST_RTSP_PROFILE_AVPF;
|
||||
} else if (g_str_equal (proto, "RTP/SAVPF")) {
|
||||
media_type = "application/x-srtp";
|
||||
profile = GST_RTSP_PROFILE_SAVPF;
|
||||
} else {
|
||||
GST_ERROR ("%p: unsupported profile '%s' for stream %d", media, proto, i);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
profiles = gst_rtsp_stream_get_profiles (stream);
|
||||
if ((profiles & profile) == 0) {
|
||||
GST_ERROR ("%p: unsupported profile '%s' for stream %d", media, proto, i);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
formats_len = gst_sdp_media_formats_len (sdp_media);
|
||||
for (j = 0; j < formats_len; j++) {
|
||||
gint pt;
|
||||
GstCaps *caps;
|
||||
GstStructure *s;
|
||||
|
||||
pt = atoi (gst_sdp_media_get_format (sdp_media, j));
|
||||
|
||||
GST_DEBUG (" looking at %d pt: %d", j, pt);
|
||||
|
||||
/* convert caps */
|
||||
caps = media_to_caps (pt, sdp_media);
|
||||
if (caps == NULL) {
|
||||
GST_WARNING (" skipping pt %d without caps", pt);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* do some tweaks */
|
||||
GST_DEBUG ("mapping sdp session level attributes to caps");
|
||||
sdp_attributes_to_caps (sdp->attributes, caps);
|
||||
GST_DEBUG ("mapping sdp media level attributes to caps");
|
||||
sdp_attributes_to_caps (sdp_media->attributes, caps);
|
||||
|
||||
s = gst_caps_get_structure (caps, 0);
|
||||
gst_structure_set_name (s, media_type);
|
||||
|
||||
gst_rtsp_stream_set_pt_map (stream, pt, caps);
|
||||
gst_caps_unref (caps);
|
||||
}
|
||||
|
||||
control = gst_sdp_media_get_attribute_val (sdp_media, "control");
|
||||
if (control)
|
||||
gst_rtsp_stream_set_control (stream, control);
|
||||
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_media_handle_sdp:
|
||||
* @media: a #GstRTSPMedia
|
||||
* @sdp: (transfer none): a #GstSDPMessage
|
||||
*
|
||||
* Configure an SDP on @media for receiving streams
|
||||
*
|
||||
* Returns: TRUE on success.
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_media_handle_sdp (GstRTSPMedia * media, GstSDPMessage * sdp)
|
||||
{
|
||||
GstRTSPMediaPrivate *priv;
|
||||
GstRTSPMediaClass *klass;
|
||||
gboolean res;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
|
||||
g_return_val_if_fail (sdp != NULL, FALSE);
|
||||
|
||||
priv = media->priv;
|
||||
|
||||
g_rec_mutex_lock (&priv->state_lock);
|
||||
|
||||
klass = GST_RTSP_MEDIA_GET_CLASS (media);
|
||||
|
||||
if (!klass->handle_sdp)
|
||||
goto no_handle_sdp;
|
||||
|
||||
res = klass->handle_sdp (media, sdp);
|
||||
|
||||
g_rec_mutex_unlock (&priv->state_lock);
|
||||
|
||||
return res;
|
||||
|
||||
/* ERRORS */
|
||||
no_handle_sdp:
|
||||
{
|
||||
g_rec_mutex_unlock (&priv->state_lock);
|
||||
GST_ERROR ("no handle_sdp function");
|
||||
g_critical ("no handle_sdp vmethod function set");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_set_seqnum (GstRTSPStream * stream)
|
||||
{
|
||||
|
@ -3214,3 +3873,50 @@ error_status:
|
|||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_media_set_record:
|
||||
* @media: a #GstRTSPMedia
|
||||
* @record: the new value
|
||||
*
|
||||
* Set or unset if the pipeline for @media can be used for PLAY or RECORD
|
||||
* methods.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_media_set_record (GstRTSPMedia * media, gboolean record)
|
||||
{
|
||||
GstRTSPMediaPrivate *priv;
|
||||
|
||||
g_return_if_fail (GST_IS_RTSP_MEDIA (media));
|
||||
|
||||
priv = media->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
priv->record = record;
|
||||
g_mutex_unlock (&priv->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_media_is_record:
|
||||
* @media: a #GstRTSPMedia
|
||||
*
|
||||
* Check if the pipeline for @media can be used for PLAY or RECORD methods.
|
||||
*
|
||||
* Returns: %TRUE if the media can be record between clients.
|
||||
*/
|
||||
gboolean
|
||||
gst_rtsp_media_is_record (GstRTSPMedia * media)
|
||||
{
|
||||
GstRTSPMediaPrivate *priv;
|
||||
gboolean res;
|
||||
|
||||
g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
|
||||
|
||||
priv = media->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
res = priv->record;
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -149,8 +149,10 @@ struct _GstRTSPMediaClass {
|
|||
void (*target_state) (GstRTSPMedia *media, GstState state);
|
||||
void (*new_state) (GstRTSPMedia *media, GstState state);
|
||||
|
||||
gboolean (*handle_sdp) (GstRTSPMedia *media, GstSDPMessage *sdp);
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING_LARGE];
|
||||
gpointer _gst_reserved[GST_PADDING_LARGE-1];
|
||||
};
|
||||
|
||||
GType gst_rtsp_media_get_type (void);
|
||||
|
@ -170,6 +172,9 @@ GstRTSPPermissions * gst_rtsp_media_get_permissions (GstRTSPMedia *media);
|
|||
void gst_rtsp_media_set_shared (GstRTSPMedia *media, gboolean shared);
|
||||
gboolean gst_rtsp_media_is_shared (GstRTSPMedia *media);
|
||||
|
||||
void gst_rtsp_media_set_record (GstRTSPMedia *media, gboolean record);
|
||||
gboolean gst_rtsp_media_is_record (GstRTSPMedia *media);
|
||||
|
||||
void gst_rtsp_media_set_reusable (GstRTSPMedia *media, gboolean reusable);
|
||||
gboolean gst_rtsp_media_is_reusable (GstRTSPMedia *media);
|
||||
|
||||
|
@ -209,6 +214,9 @@ gboolean gst_rtsp_media_unsuspend (GstRTSPMedia *media);
|
|||
gboolean gst_rtsp_media_setup_sdp (GstRTSPMedia * media, GstSDPMessage * sdp,
|
||||
GstSDPInfo * info);
|
||||
|
||||
gboolean gst_rtsp_media_handle_sdp (GstRTSPMedia * media, GstSDPMessage * sdp);
|
||||
|
||||
|
||||
/* creating streams */
|
||||
void gst_rtsp_media_collect_streams (GstRTSPMedia *media);
|
||||
GstRTSPStream * gst_rtsp_media_create_stream (GstRTSPMedia *media,
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
* Copyright (C) 2015 Centricular Ltd
|
||||
* Author: Sebastian Dröge <sebastian@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
|
@ -144,6 +146,7 @@ gst_rtsp_session_media_new (const gchar * path, GstRTSPMedia * media)
|
|||
|
||||
g_return_val_if_fail (path != NULL, NULL);
|
||||
g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
|
||||
|
||||
status = gst_rtsp_media_get_status (media);
|
||||
g_return_val_if_fail (status == GST_RTSP_MEDIA_STATUS_PREPARED || status ==
|
||||
GST_RTSP_MEDIA_STATUS_SUSPENDED, NULL);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||
* Copyright (C) 2015 Centricular Ltd
|
||||
* Author: Sebastian Dröge <sebastian@centricular.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
|
@ -71,7 +73,8 @@ struct _GstRTSPStreamPrivate
|
|||
{
|
||||
GMutex lock;
|
||||
guint idx;
|
||||
GstPad *srcpad;
|
||||
/* Only one pad is ever set */
|
||||
GstPad *srcpad, *sinkpad;
|
||||
GstElement *payloader;
|
||||
guint buffer_size;
|
||||
gboolean is_joined;
|
||||
|
@ -82,6 +85,7 @@ struct _GstRTSPStreamPrivate
|
|||
|
||||
/* pads on the rtpbin */
|
||||
GstPad *send_rtp_sink;
|
||||
GstPad *recv_rtp_src;
|
||||
GstPad *recv_sink[2];
|
||||
GstPad *send_src[2];
|
||||
|
||||
|
@ -153,6 +157,9 @@ struct _GstRTSPStreamPrivate
|
|||
/* stream blocking */
|
||||
gulong blocked_id;
|
||||
gboolean blocking;
|
||||
|
||||
/* pt->caps map for RECORD streams */
|
||||
GHashTable *ptmap;
|
||||
};
|
||||
|
||||
#define DEFAULT_CONTROL NULL
|
||||
|
@ -253,6 +260,8 @@ gst_rtsp_stream_init (GstRTSPStream * stream)
|
|||
|
||||
priv->keys = g_hash_table_new_full (g_direct_hash, g_direct_equal,
|
||||
NULL, (GDestroyNotify) gst_caps_unref);
|
||||
priv->ptmap = g_hash_table_new_full (NULL, NULL, NULL,
|
||||
(GDestroyNotify) gst_caps_unref);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -283,11 +292,15 @@ gst_rtsp_stream_finalize (GObject * obj)
|
|||
g_object_unref (priv->rtxsend);
|
||||
|
||||
gst_object_unref (priv->payloader);
|
||||
gst_object_unref (priv->srcpad);
|
||||
if (priv->srcpad)
|
||||
gst_object_unref (priv->srcpad);
|
||||
if (priv->sinkpad)
|
||||
gst_object_unref (priv->sinkpad);
|
||||
g_free (priv->control);
|
||||
g_mutex_clear (&priv->lock);
|
||||
|
||||
g_hash_table_unref (priv->keys);
|
||||
g_hash_table_destroy (priv->ptmap);
|
||||
|
||||
G_OBJECT_CLASS (gst_rtsp_stream_parent_class)->finalize (obj);
|
||||
}
|
||||
|
@ -337,29 +350,32 @@ gst_rtsp_stream_set_property (GObject * object, guint propid,
|
|||
/**
|
||||
* gst_rtsp_stream_new:
|
||||
* @idx: an index
|
||||
* @srcpad: a #GstPad
|
||||
* @pad: a #GstPad
|
||||
* @payloader: a #GstElement
|
||||
*
|
||||
* Create a new media stream with index @idx that handles RTP data on
|
||||
* @srcpad and has a payloader element @payloader.
|
||||
* @pad and has a payloader element @payloader if @pad is a source pad
|
||||
* or a depayloader element @payloader if @pad is a sink pad.
|
||||
*
|
||||
* Returns: (transfer full): a new #GstRTSPStream
|
||||
*/
|
||||
GstRTSPStream *
|
||||
gst_rtsp_stream_new (guint idx, GstElement * payloader, GstPad * srcpad)
|
||||
gst_rtsp_stream_new (guint idx, GstElement * payloader, GstPad * pad)
|
||||
{
|
||||
GstRTSPStreamPrivate *priv;
|
||||
GstRTSPStream *stream;
|
||||
|
||||
g_return_val_if_fail (GST_IS_ELEMENT (payloader), NULL);
|
||||
g_return_val_if_fail (GST_IS_PAD (srcpad), NULL);
|
||||
g_return_val_if_fail (GST_PAD_IS_SRC (srcpad), NULL);
|
||||
g_return_val_if_fail (GST_IS_PAD (pad), NULL);
|
||||
|
||||
stream = g_object_new (GST_TYPE_RTSP_STREAM, NULL);
|
||||
priv = stream->priv;
|
||||
priv->idx = idx;
|
||||
priv->payloader = gst_object_ref (payloader);
|
||||
priv->srcpad = gst_object_ref (srcpad);
|
||||
if (GST_PAD_IS_SRC (pad))
|
||||
priv->srcpad = gst_object_ref (pad);
|
||||
else
|
||||
priv->sinkpad = gst_object_ref (pad);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
@ -416,9 +432,31 @@ gst_rtsp_stream_get_srcpad (GstRTSPStream * stream)
|
|||
{
|
||||
g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), NULL);
|
||||
|
||||
if (!stream->priv->srcpad)
|
||||
return NULL;
|
||||
|
||||
return gst_object_ref (stream->priv->srcpad);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_get_sinkpad:
|
||||
* @stream: a #GstRTSPStream
|
||||
*
|
||||
* Get the sinkpad associated with @stream.
|
||||
*
|
||||
* Returns: (transfer full): the sinkpad. Unref after usage.
|
||||
*/
|
||||
GstPad *
|
||||
gst_rtsp_stream_get_sinkpad (GstRTSPStream * stream)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), NULL);
|
||||
|
||||
if (!stream->priv->sinkpad)
|
||||
return NULL;
|
||||
|
||||
return gst_object_ref (stream->priv->sinkpad);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_get_control:
|
||||
* @stream: a #GstRTSPStream
|
||||
|
@ -975,11 +1013,12 @@ different_address:
|
|||
}
|
||||
|
||||
static gboolean
|
||||
alloc_ports_one_family (GstRTSPAddressPool * pool, gint buffer_size,
|
||||
GSocketFamily family, GstElement * udpsrc_out[2],
|
||||
alloc_ports_one_family (GstRTSPStream * stream, GstRTSPAddressPool * pool,
|
||||
gint buffer_size, GSocketFamily family, GstElement * udpsrc_out[2],
|
||||
GstElement * udpsink_out[2], GstRTSPRange * server_port_out,
|
||||
GstRTSPAddress ** server_addr_out)
|
||||
{
|
||||
GstRTSPStreamPrivate *priv = stream->priv;
|
||||
GstStateChangeReturn ret;
|
||||
GstElement *udpsrc0, *udpsrc1;
|
||||
GstElement *udpsink0, *udpsink1;
|
||||
|
@ -1148,6 +1187,11 @@ again:
|
|||
g_object_set (G_OBJECT (udpsink1), "close-socket", FALSE, NULL);
|
||||
g_object_set (G_OBJECT (udpsink1), multisink_socket, rtcp_socket, NULL);
|
||||
g_object_set (G_OBJECT (udpsink1), "sync", FALSE, NULL);
|
||||
/* Needs to be async for RECORD streams, otherwise we will never go to
|
||||
* PLAYING because the sinks will wait for data while the udpsrc can't
|
||||
* provide data with timestamps in PAUSED. */
|
||||
if (priv->sinkpad)
|
||||
g_object_set (G_OBJECT (udpsink0), "async", FALSE, NULL);
|
||||
g_object_set (G_OBJECT (udpsink1), "async", FALSE, NULL);
|
||||
g_object_set (G_OBJECT (udpsink0), "auto-multicast", FALSE, NULL);
|
||||
g_object_set (G_OBJECT (udpsink0), "loop", FALSE, NULL);
|
||||
|
@ -1227,11 +1271,13 @@ alloc_ports (GstRTSPStream * stream)
|
|||
{
|
||||
GstRTSPStreamPrivate *priv = stream->priv;
|
||||
|
||||
priv->have_ipv4 = alloc_ports_one_family (priv->pool, priv->buffer_size,
|
||||
priv->have_ipv4 =
|
||||
alloc_ports_one_family (stream, priv->pool, priv->buffer_size,
|
||||
G_SOCKET_FAMILY_IPV4, priv->udpsrc_v4, priv->udpsink,
|
||||
&priv->server_port_v4, &priv->server_addr_v4);
|
||||
|
||||
priv->have_ipv6 = alloc_ports_one_family (priv->pool, priv->buffer_size,
|
||||
priv->have_ipv6 =
|
||||
alloc_ports_one_family (stream, priv->pool, priv->buffer_size,
|
||||
G_SOCKET_FAMILY_IPV6, priv->udpsrc_v6, priv->udpsink,
|
||||
&priv->server_port_v6, &priv->server_addr_v6);
|
||||
|
||||
|
@ -1746,7 +1792,7 @@ request_key (GstElement * srtpdec, guint ssrc, GstRTSPStream * stream)
|
|||
}
|
||||
|
||||
static GstElement *
|
||||
request_rtcp_decoder (GstElement * rtpbin, guint session,
|
||||
request_rtp_rtcp_decoder (GstElement * rtpbin, guint session,
|
||||
GstRTSPStream * stream)
|
||||
{
|
||||
GstRTSPStreamPrivate *priv = stream->priv;
|
||||
|
@ -1808,6 +1854,101 @@ request_aux_sender (GstElement * rtpbin, guint sessid, GstRTSPStream * stream)
|
|||
return bin;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_set_pt_map:
|
||||
* @stream: a #GstRTSPStream
|
||||
* @pt: the pt
|
||||
* @caps: a #GstCaps
|
||||
*
|
||||
* Configure a pt map between @pt and @caps.
|
||||
*/
|
||||
void
|
||||
gst_rtsp_stream_set_pt_map (GstRTSPStream * stream, guint pt, GstCaps * caps)
|
||||
{
|
||||
GstRTSPStreamPrivate *priv = stream->priv;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
g_hash_table_insert (priv->ptmap, GINT_TO_POINTER (pt), gst_caps_ref (caps));
|
||||
g_mutex_unlock (&priv->lock);
|
||||
}
|
||||
|
||||
static GstCaps *
|
||||
request_pt_map (GstElement * rtpbin, guint session, guint pt,
|
||||
GstRTSPStream * stream)
|
||||
{
|
||||
GstRTSPStreamPrivate *priv = stream->priv;
|
||||
GstCaps *caps = NULL;
|
||||
|
||||
g_mutex_lock (&priv->lock);
|
||||
|
||||
if (priv->idx == session) {
|
||||
caps = g_hash_table_lookup (priv->ptmap, GINT_TO_POINTER (pt));
|
||||
if (caps) {
|
||||
GST_DEBUG ("Stream %p, pt %u: caps %" GST_PTR_FORMAT, stream, pt, caps);
|
||||
gst_caps_ref (caps);
|
||||
} else {
|
||||
GST_DEBUG ("Stream %p, pt %u: no caps", stream, pt);
|
||||
}
|
||||
}
|
||||
|
||||
g_mutex_unlock (&priv->lock);
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
static void
|
||||
pad_added (GstElement * rtpbin, GstPad * pad, GstRTSPStream * stream)
|
||||
{
|
||||
GstRTSPStreamPrivate *priv = stream->priv;
|
||||
gchar *name;
|
||||
GstPadLinkReturn ret;
|
||||
guint sessid;
|
||||
|
||||
GST_DEBUG ("Stream %p added pad %s:%s for pad %s:%s", stream,
|
||||
GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (priv->sinkpad));
|
||||
|
||||
name = gst_pad_get_name (pad);
|
||||
if (sscanf (name, "recv_rtp_src_%u", &sessid) != 1) {
|
||||
g_free (name);
|
||||
return;
|
||||
}
|
||||
g_free (name);
|
||||
|
||||
if (priv->idx != sessid)
|
||||
return;
|
||||
|
||||
if (gst_pad_is_linked (priv->sinkpad)) {
|
||||
GST_WARNING ("Stream %p: Pad %s:%s is linked already", stream,
|
||||
GST_DEBUG_PAD_NAME (priv->sinkpad));
|
||||
return;
|
||||
}
|
||||
|
||||
/* link the RTP pad to the session manager, it should not really fail unless
|
||||
* this is not really an RTP pad */
|
||||
ret = gst_pad_link (pad, priv->sinkpad);
|
||||
if (ret != GST_PAD_LINK_OK)
|
||||
goto link_failed;
|
||||
priv->recv_rtp_src = gst_object_ref (pad);
|
||||
|
||||
return;
|
||||
|
||||
/* ERRORS */
|
||||
link_failed:
|
||||
{
|
||||
GST_ERROR ("Stream %p: Failed to link pads %s:%s and %s:%s", stream,
|
||||
GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (priv->sinkpad));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_npt_stop (GstElement * rtpbin, guint session, guint ssrc,
|
||||
GstRTSPStream * stream)
|
||||
{
|
||||
/* TODO: What to do here other than this? */
|
||||
GST_DEBUG ("Stream %p: Got EOS", stream);
|
||||
gst_pad_send_event (stream->priv->sinkpad, gst_event_new_eos ());
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_rtsp_stream_join_bin:
|
||||
* @stream: a #GstRTSPStream
|
||||
|
@ -1861,37 +2002,52 @@ gst_rtsp_stream_join_bin (GstRTSPStream * stream, GstBin * bin,
|
|||
(GCallback) request_rtp_encoder, stream);
|
||||
g_signal_connect (rtpbin, "request-rtcp-encoder",
|
||||
(GCallback) request_rtcp_encoder, stream);
|
||||
g_signal_connect (rtpbin, "request-rtp-decoder",
|
||||
(GCallback) request_rtp_rtcp_decoder, stream);
|
||||
g_signal_connect (rtpbin, "request-rtcp-decoder",
|
||||
(GCallback) request_rtcp_decoder, stream);
|
||||
(GCallback) request_rtp_rtcp_decoder, stream);
|
||||
}
|
||||
|
||||
if (priv->rtx_time > 0) {
|
||||
if (priv->rtx_time > 0 && priv->srcpad) {
|
||||
/* enable retransmission by setting rtprtxsend as the "aux" element of rtpbin */
|
||||
g_signal_connect (rtpbin, "request-aux-sender",
|
||||
(GCallback) request_aux_sender, stream);
|
||||
}
|
||||
if (priv->sinkpad) {
|
||||
g_signal_connect (rtpbin, "request-pt-map",
|
||||
(GCallback) request_pt_map, stream);
|
||||
}
|
||||
|
||||
/* get a pad for sending RTP */
|
||||
name = g_strdup_printf ("send_rtp_sink_%u", idx);
|
||||
priv->send_rtp_sink = gst_element_get_request_pad (rtpbin, name);
|
||||
g_free (name);
|
||||
/* link the RTP pad to the session manager, it should not really fail unless
|
||||
* this is not really an RTP pad */
|
||||
ret = gst_pad_link (priv->srcpad, priv->send_rtp_sink);
|
||||
if (ret != GST_PAD_LINK_OK)
|
||||
goto link_failed;
|
||||
|
||||
if (priv->srcpad) {
|
||||
/* link the RTP pad to the session manager, it should not really fail unless
|
||||
* this is not really an RTP pad */
|
||||
ret = gst_pad_link (priv->srcpad, priv->send_rtp_sink);
|
||||
if (ret != GST_PAD_LINK_OK)
|
||||
goto link_failed;
|
||||
} else {
|
||||
/* Need to connect our sinkpad from here */
|
||||
g_signal_connect (rtpbin, "pad-added", (GCallback) pad_added, stream);
|
||||
/* EOS */
|
||||
g_signal_connect (rtpbin, "on-npt-stop", (GCallback) on_npt_stop, stream);
|
||||
}
|
||||
|
||||
/* get pads from the RTP session element for sending and receiving
|
||||
* RTP/RTCP*/
|
||||
name = g_strdup_printf ("send_rtp_src_%u", idx);
|
||||
priv->send_src[0] = gst_element_get_static_pad (rtpbin, name);
|
||||
g_free (name);
|
||||
name = g_strdup_printf ("send_rtcp_src_%u", idx);
|
||||
priv->send_src[1] = gst_element_get_request_pad (rtpbin, name);
|
||||
g_free (name);
|
||||
name = g_strdup_printf ("recv_rtp_sink_%u", idx);
|
||||
priv->recv_sink[0] = gst_element_get_request_pad (rtpbin, name);
|
||||
g_free (name);
|
||||
|
||||
name = g_strdup_printf ("send_rtcp_src_%u", idx);
|
||||
priv->send_src[1] = gst_element_get_request_pad (rtpbin, name);
|
||||
g_free (name);
|
||||
name = g_strdup_printf ("recv_rtcp_sink_%u", idx);
|
||||
priv->recv_sink[1] = gst_element_get_request_pad (rtpbin, name);
|
||||
g_free (name);
|
||||
|
@ -2002,10 +2158,12 @@ gst_rtsp_stream_join_bin (GstRTSPStream * stream, GstBin * bin,
|
|||
gst_object_unref (pad);
|
||||
|
||||
if (priv->udpsrc_v4[i]) {
|
||||
/* we set and keep these to playing so that they don't cause NO_PREROLL return
|
||||
* values */
|
||||
gst_element_set_state (priv->udpsrc_v4[i], GST_STATE_PLAYING);
|
||||
gst_element_set_locked_state (priv->udpsrc_v4[i], TRUE);
|
||||
if (priv->srcpad) {
|
||||
/* we set and keep these to playing so that they don't cause NO_PREROLL return
|
||||
* values. This is only relevant for PLAY pipelines */
|
||||
gst_element_set_state (priv->udpsrc_v4[i], GST_STATE_PLAYING);
|
||||
gst_element_set_locked_state (priv->udpsrc_v4[i], TRUE);
|
||||
}
|
||||
/* add udpsrc */
|
||||
gst_bin_add (bin, priv->udpsrc_v4[i]);
|
||||
|
||||
|
@ -2018,8 +2176,10 @@ gst_rtsp_stream_join_bin (GstRTSPStream * stream, GstBin * bin,
|
|||
}
|
||||
|
||||
if (priv->udpsrc_v6[i]) {
|
||||
gst_element_set_state (priv->udpsrc_v6[i], GST_STATE_PLAYING);
|
||||
gst_element_set_locked_state (priv->udpsrc_v6[i], TRUE);
|
||||
if (priv->srcpad) {
|
||||
gst_element_set_state (priv->udpsrc_v6[i], GST_STATE_PLAYING);
|
||||
gst_element_set_locked_state (priv->udpsrc_v6[i], TRUE);
|
||||
}
|
||||
gst_bin_add (bin, priv->udpsrc_v6[i]);
|
||||
|
||||
/* and link to the funnel v6 */
|
||||
|
@ -2128,7 +2288,13 @@ gst_rtsp_stream_leave_bin (GstRTSPStream * stream, GstBin * bin,
|
|||
|
||||
GST_INFO ("stream %p leaving bin", stream);
|
||||
|
||||
gst_pad_unlink (priv->srcpad, priv->send_rtp_sink);
|
||||
if (priv->srcpad) {
|
||||
gst_pad_unlink (priv->srcpad, priv->send_rtp_sink);
|
||||
} else if (priv->recv_rtp_src) {
|
||||
gst_pad_unlink (priv->recv_rtp_src, priv->sinkpad);
|
||||
gst_object_unref (priv->recv_rtp_src);
|
||||
priv->recv_rtp_src = NULL;
|
||||
}
|
||||
g_signal_handler_disconnect (priv->send_src[0], priv->caps_sig);
|
||||
gst_element_release_request_pad (rtpbin, priv->send_rtp_sink);
|
||||
gst_object_unref (priv->send_rtp_sink);
|
||||
|
@ -2464,10 +2630,12 @@ update_transport (GstRTSPStream * stream, GstRTSPStreamTransport * trans,
|
|||
gst_element_make_from_uri (GST_URI_SRC, host, NULL, NULL);
|
||||
g_free (host);
|
||||
|
||||
/* we set and keep these to playing so that they don't cause NO_PREROLL return
|
||||
* values */
|
||||
gst_element_set_state (source->udpsrc[i], GST_STATE_PLAYING);
|
||||
gst_element_set_locked_state (source->udpsrc[i], TRUE);
|
||||
if (priv->srcpad) {
|
||||
/* we set and keep these to playing so that they don't cause NO_PREROLL return
|
||||
* values. This is only relevant for PLAY pipelines */
|
||||
gst_element_set_state (source->udpsrc[i], GST_STATE_PLAYING);
|
||||
gst_element_set_locked_state (source->udpsrc[i], TRUE);
|
||||
}
|
||||
/* add udpsrc */
|
||||
gst_bin_add (bin, source->udpsrc[i]);
|
||||
|
||||
|
|
|
@ -67,10 +67,11 @@ struct _GstRTSPStreamClass {
|
|||
GType gst_rtsp_stream_get_type (void);
|
||||
|
||||
GstRTSPStream * gst_rtsp_stream_new (guint idx, GstElement *payloader,
|
||||
GstPad *srcpad);
|
||||
GstPad *pad);
|
||||
guint gst_rtsp_stream_get_index (GstRTSPStream *stream);
|
||||
guint gst_rtsp_stream_get_pt (GstRTSPStream *stream);
|
||||
GstPad * gst_rtsp_stream_get_srcpad (GstRTSPStream *stream);
|
||||
GstPad * gst_rtsp_stream_get_sinkpad (GstRTSPStream *stream);
|
||||
|
||||
void gst_rtsp_stream_set_control (GstRTSPStream *stream, const gchar *control);
|
||||
gchar * gst_rtsp_stream_get_control (GstRTSPStream *stream);
|
||||
|
@ -160,6 +161,8 @@ guint gst_rtsp_stream_get_retransmission_pt (GstRTSPStream * s
|
|||
void gst_rtsp_stream_set_retransmission_pt (GstRTSPStream * stream,
|
||||
guint rtx_pt);
|
||||
|
||||
void gst_rtsp_stream_set_pt_map (GstRTSPStream * stream, guint pt, GstCaps * caps);
|
||||
|
||||
/**
|
||||
* GstRTSPStreamTransportFilterFunc:
|
||||
* @stream: a #GstRTSPStream object
|
||||
|
|
Loading…
Reference in a new issue