-
-noinst_PROGRAMS = gst-rtsp-server
+noinst_PROGRAMS = test-video test-ogg test-mp4
INCLUDES = -I$(top_srcdir) -I$(srcdir)
-gst_rtsp_server_SOURCES = \
- main.c
-
-gst_rtsp_server_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
-gst_rtsp_server_LDFLAGS = \
+AM_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
+AM_LDFLAGS = \
$(GST_LIBS) \
$(top_builddir)/gst/rtsp-server/libgstrtspserver-@GST_MAJORMINOR@.la
+++ /dev/null
-/* GStreamer
- * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include <gst/gst.h>
-
-#include <gst/rtsp-server/rtsp-server.h>
-
-int
-main (int argc, char *argv[])
-{
- GMainLoop *loop;
- GstRTSPServer *server;
- GstRTSPMediaMapping *mapping;
- GstRTSPMediaFactory *factory;
-
- gst_init (&argc, &argv);
-
- loop = g_main_loop_new (NULL, FALSE);
-
- /* create a server instance */
- server = gst_rtsp_server_new ();
-
- /* get the mapping for this server, every server has a default mapper object
- * that be used to map uri mount points to media factories */
- mapping = gst_rtsp_server_get_media_mapping (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 pay%d. Each
- * element with pay%d names will be a stream */
- factory = gst_rtsp_media_factory_new ();
- gst_rtsp_media_factory_set_launch (factory, "( "
- "videotestsrc ! video/x-raw-yuv,width=352,height=288,framerate=15/1 ! "
- "x264enc bitrate=300 ! rtph264pay name=pay0 pt=96 "
- "audiotestsrc ! audio/x-raw-int,rate=8000 ! "
- "alawenc ! rtppcmapay name=pay1 pt=97 "
- ")");
- /* attach the test factory to the /test url */
- gst_rtsp_media_mapping_add_factory (mapping, "/test", factory);
-
- /* don't need the ref to the mapper anymore */
- g_object_unref (mapping);
-
- /* attach the server to the default maincontext */
- gst_rtsp_server_attach (server, NULL);
-
- /* start serving */
- g_main_loop_run (loop);
-
- return 0;
-}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gst/gst.h>
+
+#include <gst/rtsp-server/rtsp-server.h>
+
+int
+main (int argc, char *argv[])
+{
+ GMainLoop *loop;
+ GstRTSPServer *server;
+ GstRTSPMediaMapping *mapping;
+ GstRTSPMediaFactory *factory;
+ gchar *str;
+
+ gst_init (&argc, &argv);
+
+ if (argc < 2) {
+ g_message ("usage: %s <filename.ogg>", argv[0]);
+ return -1;
+ }
+
+ loop = g_main_loop_new (NULL, FALSE);
+
+ /* create a server instance */
+ server = gst_rtsp_server_new ();
+
+ /* get the mapping for this server, every server has a default mapper object
+ * that be used to map uri mount points to media factories */
+ mapping = gst_rtsp_server_get_media_mapping (server);
+
+ str = g_strdup_printf (
+ "( "
+ "filesrc location=%s ! qtdemux name=d "
+ "d. ! queue ! rtph264pay pt=96 name=pay0 "
+ "d. ! queue ! rtpmp4apay pt=97 name=pay1 "
+ ")", argv[1]);
+
+ /* 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 pay%d. Each
+ * element with pay%d names will be a stream */
+ factory = gst_rtsp_media_factory_new ();
+ gst_rtsp_media_factory_set_launch (factory, str);
+ g_free (str);
+
+ /* attach the test factory to the /test url */
+ gst_rtsp_media_mapping_add_factory (mapping, "/test", factory);
+
+ /* don't need the ref to the mapper anymore */
+ g_object_unref (mapping);
+
+ /* attach the server to the default maincontext */
+ gst_rtsp_server_attach (server, NULL);
+
+ /* start serving */
+ g_main_loop_run (loop);
+
+ return 0;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gst/gst.h>
+
+#include <gst/rtsp-server/rtsp-server.h>
+
+int
+main (int argc, char *argv[])
+{
+ GMainLoop *loop;
+ GstRTSPServer *server;
+ GstRTSPMediaMapping *mapping;
+ GstRTSPMediaFactory *factory;
+ gchar *str;
+
+ gst_init (&argc, &argv);
+
+ if (argc < 2) {
+ g_message ("usage: %s <filename.ogg>", argv[0]);
+ return -1;
+ }
+
+ loop = g_main_loop_new (NULL, FALSE);
+
+ /* create a server instance */
+ server = gst_rtsp_server_new ();
+
+ /* get the mapping for this server, every server has a default mapper object
+ * that be used to map uri mount points to media factories */
+ mapping = gst_rtsp_server_get_media_mapping (server);
+
+ str = g_strdup_printf (
+ "( "
+ "filesrc location=%s ! oggdemux name=d "
+ "d. ! queue ! theoraparse ! rtptheorapay name=pay0 "
+ "d. ! queue ! vorbisparse ! rtpvorbispay name=pay1 "
+ ")", argv[1]);
+
+ /* 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 pay%d. Each
+ * element with pay%d names will be a stream */
+ factory = gst_rtsp_media_factory_new ();
+ gst_rtsp_media_factory_set_launch (factory, str);
+ g_free (str);
+
+ /* attach the test factory to the /test url */
+ gst_rtsp_media_mapping_add_factory (mapping, "/test", factory);
+
+ /* don't need the ref to the mapper anymore */
+ g_object_unref (mapping);
+
+ /* attach the server to the default maincontext */
+ gst_rtsp_server_attach (server, NULL);
+
+ /* start serving */
+ g_main_loop_run (loop);
+
+ return 0;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gst/gst.h>
+
+#include <gst/rtsp-server/rtsp-server.h>
+
+int
+main (int argc, char *argv[])
+{
+ GMainLoop *loop;
+ GstRTSPServer *server;
+ GstRTSPMediaMapping *mapping;
+ GstRTSPMediaFactory *factory;
+
+ gst_init (&argc, &argv);
+
+ loop = g_main_loop_new (NULL, FALSE);
+
+ /* create a server instance */
+ server = gst_rtsp_server_new ();
+
+ /* get the mapping for this server, every server has a default mapper object
+ * that be used to map uri mount points to media factories */
+ mapping = gst_rtsp_server_get_media_mapping (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 pay%d. Each
+ * element with pay%d names will be a stream */
+ factory = gst_rtsp_media_factory_new ();
+ gst_rtsp_media_factory_set_launch (factory, "( "
+ "videotestsrc ! video/x-raw-yuv,width=352,height=288,framerate=15/1 ! "
+ "ffenc_h263 ! rtph263pay name=pay0 pt=96 "
+ "audiotestsrc ! audio/x-raw-int,rate=8000 ! "
+ "alawenc ! rtppcmapay name=pay1 pt=97 "
+ ")");
+
+ /* attach the test factory to the /test url */
+ gst_rtsp_media_mapping_add_factory (mapping, "/test", factory);
+
+ /* don't need the ref to the mapper anymore */
+ g_object_unref (mapping);
+
+ /* attach the server to the default maincontext */
+ gst_rtsp_server_attach (server, NULL);
+
+ /* start serving */
+ g_main_loop_run (loop);
+
+ return 0;
+}
rtsp-media.h \
rtsp-media-factory.h \
rtsp-media-mapping.h \
+ rtsp-sdp.h \
rtsp-session-pool.h \
rtsp-session.h
rtsp-media.c \
rtsp-media-factory.c \
rtsp-media-mapping.c \
+ rtsp-sdp.c \
rtsp-session-pool.c \
rtsp-session.c
#include <sys/ioctl.h>
-#include <gst/sdp/gstsdpmessage.h>
-
#include "rtsp-client.h"
+#include "rtsp-sdp.h"
#undef DEBUG
return result;
}
+static void
+handle_response (GstRTSPClient *client, GstRTSPMessage *response)
+{
+#ifdef DEBUG
+ gst_rtsp_message_dump (response);
+#endif
+
+ gst_rtsp_connection_send (client->connection, response, NULL);
+}
+
static void
handle_generic_response (GstRTSPClient *client, GstRTSPStatusCode code,
GstRTSPMessage *request)
gst_rtsp_message_init_response (&response, code,
gst_rtsp_status_as_text (code), request);
- gst_rtsp_connection_send (client->connection, &response, NULL);
+ handle_response (client, &response);
}
-static gboolean
-handle_teardown_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *request)
+static GstRTSPMedia *
+find_media (GstRTSPClient *client, const GstRTSPUrl *uri, GstRTSPMessage *request)
+{
+ GstRTSPMediaFactory *factory;
+ GstRTSPMedia *media;
+
+ /* find the factory for the uri first */
+ if (!(factory = gst_rtsp_media_mapping_find_factory (client->media_mapping, uri)))
+ goto no_factory;
+
+ /* prepare the media and add it to the pipeline */
+ if (!(media = gst_rtsp_media_factory_construct (factory, uri)))
+ goto no_media;
+
+ /* prepare the media */
+ if (!(gst_rtsp_media_prepare (media)))
+ goto no_prepare;
+
+ return media;
+
+ /* ERRORS */
+no_factory:
+ {
+ handle_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
+ return NULL;
+ }
+no_media:
+ {
+ handle_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
+ g_object_unref (factory);
+ return NULL;
+ }
+no_prepare:
+ {
+ handle_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
+ g_object_unref (media);
+ g_object_unref (factory);
+ return NULL;
+ }
+}
+
+/* Get the session or NULL when there was no session */
+static GstRTSPSession *
+ensure_session (GstRTSPClient *client, GstRTSPMessage *request)
{
GstRTSPResult res;
- GstRTSPSessionMedia *media;
GstRTSPSession *session;
gchar *sessid;
- GstRTSPMessage response = { 0 };
- GstRTSPStatusCode code;
res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
if (res == GST_RTSP_OK) {
/* we had a session in the request, find it again */
- if (!(session = gst_rtsp_session_pool_find (client->pool, sessid)))
+ if (!(session = gst_rtsp_session_pool_find (client->session_pool, sessid)))
goto session_not_found;
}
else
goto service_unavailable;
+ return session;
+
+ /* ERRORS */
+session_not_found:
+ {
+ handle_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
+ return NULL;
+ }
+service_unavailable:
+ {
+ handle_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
+ return NULL;
+ }
+}
+
+static gboolean
+handle_teardown_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *request)
+{
+ GstRTSPSessionMedia *media;
+ GstRTSPSession *session;
+ GstRTSPMessage response = { 0 };
+ GstRTSPStatusCode code;
+
+ if (!(session = ensure_session (client, request)))
+ goto no_session;
+
/* get a handle to the configuration of the media in the session */
- media = gst_rtsp_session_get_media (session, uri, client->factory);
+ media = gst_rtsp_session_get_media (session, uri);
if (!media)
goto not_found;
gst_rtsp_session_media_stop (media);
- gst_rtsp_session_pool_remove (client->pool, session);
+ gst_rtsp_session_pool_remove (client->session_pool, session);
g_object_unref (session);
/* remove the session id from the request, which will also remove it from the
code = GST_RTSP_STS_OK;
gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
- gst_rtsp_connection_send (client->connection, &response, NULL);
+ handle_response (client, &response);
return FALSE;
/* ERRORS */
-session_not_found:
+no_session:
{
- handle_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
- return FALSE;
- }
-service_unavailable:
- {
- handle_generic_response (client, GST_RTSP_STS_OK, request);
+ /* error was sent already */
return FALSE;
}
not_found:
static gboolean
handle_pause_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *request)
{
- GstRTSPResult res;
GstRTSPSessionMedia *media;
GstRTSPSession *session;
- gchar *sessid;
GstRTSPMessage response = { 0 };
GstRTSPStatusCode code;
- res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
- if (res == GST_RTSP_OK) {
- /* we had a session in the request, find it again */
- if (!(session = gst_rtsp_session_pool_find (client->pool, sessid)))
- goto session_not_found;
- }
- else
- goto service_unavailable;
+ if (!(session = ensure_session (client, request)))
+ goto no_session;
/* get a handle to the configuration of the media in the session */
- media = gst_rtsp_session_get_media (session, uri, client->factory);
+ media = gst_rtsp_session_get_media (session, uri);
if (!media)
goto not_found;
code = GST_RTSP_STS_OK;
gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), request);
- gst_rtsp_connection_send (client->connection, &response, NULL);
+ handle_response (client, &response);
return FALSE;
/* ERRORS */
-session_not_found:
- {
- handle_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
- return FALSE;
- }
-service_unavailable:
+no_session:
{
return FALSE;
}
static gboolean
handle_play_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *request)
{
- GstRTSPResult res;
GstRTSPSessionMedia *media;
GstRTSPSession *session;
- gchar *sessid;
GstRTSPMessage response = { 0 };
GstRTSPStatusCode code;
- GstStateChangeReturn ret;
GString *rtpinfo;
guint n_streams, i;
guint timestamp, seqnum;
- res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
- if (res == GST_RTSP_OK) {
- /* we had a session in the request, find it again */
- if (!(session = gst_rtsp_session_pool_find (client->pool, sessid)))
- goto session_not_found;
- }
- else
- goto service_unavailable;
+ if (!(session = ensure_session (client, request)))
+ goto no_session;
/* get a handle to the configuration of the media in the session */
- media = gst_rtsp_session_get_media (session, uri, client->factory);
+ media = gst_rtsp_session_get_media (session, uri);
if (!media)
goto not_found;
- /* wait for paused to get the caps */
- ret = gst_rtsp_session_media_pause (media);
- switch (ret) {
- case GST_STATE_CHANGE_NO_PREROLL:
- break;
- case GST_STATE_CHANGE_SUCCESS:
- break;
- case GST_STATE_CHANGE_FAILURE:
- goto service_unavailable;
- case GST_STATE_CHANGE_ASYNC:
- /* wait for paused state change to complete */
- ret = gst_element_get_state (media->pipeline, NULL, NULL, -1);
- break;
- }
-
/* grab RTPInfo from the payloaders now */
rtpinfo = g_string_new ("");
+
n_streams = gst_rtsp_media_n_streams (media->media);
for (i = 0; i < n_streams; i++) {
GstRTSPMediaStream *stream;
gst_rtsp_message_add_header (&response, GST_RTSP_HDR_RTP_INFO, rtpinfo->str);
g_string_free (rtpinfo, TRUE);
- gst_rtsp_connection_send (client->connection, &response, NULL);
+ handle_response (client, &response);
/* start playing after sending the request */
gst_rtsp_session_media_play (media);
return FALSE;
/* ERRORS */
-session_not_found:
- {
- handle_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
- return FALSE;
- }
-service_unavailable:
+no_session:
{
- handle_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
+ /* error was sent */
return FALSE;
}
not_found:
if (sscanf (pos, "%u", &streamid) != 1)
goto bad_request;
- /* find the media associated with the uri */
- if (client->factory == NULL) {
- if ((client->factory = gst_rtsp_media_mapping_find_factory (client->mapping, uri)) == NULL)
- goto not_found;
- }
-
/* parse the transport */
res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_TRANSPORT, &transport, 0);
if (res != GST_RTSP_OK)
- goto unsupported_transports;
+ goto no_transport;
transports = g_strsplit (transport, ",", 0);
gst_rtsp_transport_new (&ct);
}
g_strfreev (transports);
+ g_free (ct->destination);
+ ct->destination = g_strdup (inet_ntoa (client->address.sin_addr));
+
/* we have not found anything usable, error out */
if (!have_transport) {
- gst_rtsp_transport_free (ct);
goto unsupported_transports;
}
res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
if (res == GST_RTSP_OK) {
/* we had a session in the request, find it again */
- if (!(session = gst_rtsp_session_pool_find (client->pool, sessid)))
+ if (!(session = gst_rtsp_session_pool_find (client->session_pool, sessid)))
goto session_not_found;
need_session = FALSE;
}
else {
/* create a session if this fails we probably reached our session limit or
* something. */
- if (!(session = gst_rtsp_session_pool_create (client->pool)))
+ if (!(session = gst_rtsp_session_pool_create (client->session_pool)))
goto service_unavailable;
need_session = TRUE;
}
+ if (need_session) {
+ GstRTSPMedia *m;
+
+ /* get a handle to the configuration of the media in the session */
+ if ((m = find_media (client, uri, request))) {
+ media = gst_rtsp_session_manage_media (session, uri, m);
+ }
+ }
/* get a handle to the configuration of the media in the session */
- media = gst_rtsp_session_get_media (session, uri, client->factory);
- if (!media)
+ if (!(media = gst_rtsp_session_get_media (session, uri)))
goto not_found;
/* get a handle to the stream in the media */
- stream = gst_rtsp_session_media_get_stream (media, streamid);
+ if (!(stream = gst_rtsp_session_media_get_stream (media, streamid)))
+ goto no_stream;
/* setup the server transport from the client transport */
- st = gst_rtsp_session_stream_set_transport (stream, inet_ntoa (client->address.sin_addr), ct);
+ st = gst_rtsp_session_stream_set_transport (stream, ct);
/* serialize the server transport */
trans_str = gst_rtsp_transport_as_text (st);
if (need_session)
gst_rtsp_message_add_header (&response, GST_RTSP_HDR_SESSION, session->sessionid);
+
gst_rtsp_message_add_header (&response, GST_RTSP_HDR_TRANSPORT, trans_str);
g_free (trans_str);
g_object_unref (session);
- gst_rtsp_connection_send (client->connection, &response, NULL);
+ handle_response (client, &response);
return TRUE;
handle_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
return FALSE;
}
+no_stream:
+ {
+ handle_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
+ return FALSE;
+ }
session_not_found:
{
handle_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
return FALSE;
}
+no_transport:
+ {
+ handle_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request);
+ return FALSE;
+ }
unsupported_transports:
{
handle_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request);
+ gst_rtsp_transport_free (ct);
return FALSE;
}
service_unavailable:
GstRTSPMessage response = { 0 };
GstRTSPResult res;
GstSDPMessage *sdp;
- guint n_streams, i;
- gchar *sdptext;
- GstRTSPMediaFactory *factory;
+ guint i;
+ gchar *str;
GstRTSPMedia *media;
- GstElement *pipeline;
- GstStateChangeReturn ret;
-
/* check what kind of format is accepted, we don't really do anything with it
* and always return SDP for now. */
break;
}
- /* find the factory for the uri first */
- if (!(factory = gst_rtsp_media_mapping_find_factory (client->mapping, uri)))
- goto no_factory;
-
- /* prepare the media and add it to the pipeline */
- if (!(media = gst_rtsp_media_factory_construct (factory, uri)))
+ /* find the media object for the uri */
+ if (!(media = find_media (client, uri, request)))
goto no_media;
- /* create a pipeline to preroll the media */
- pipeline = gst_pipeline_new ("client-describe-pipeline");
-
- gst_bin_add (GST_BIN_CAST (pipeline), media->element);
-
- /* link fakesink to all stream pads and set the pipeline to PLAYING */
- n_streams = gst_rtsp_media_n_streams (media);
- for (i = 0; i < n_streams; i++) {
- GstRTSPMediaStream *stream;
- GstElement *sink;
- GstPad *sinkpad;
- GstPadLinkReturn lret;
-
- stream = gst_rtsp_media_get_stream (media, i);
-
- sink = gst_element_factory_make ("fakesink", NULL);
- gst_bin_add (GST_BIN (pipeline), sink);
-
- sinkpad = gst_element_get_static_pad (sink, "sink");
- lret = gst_pad_link (stream->srcpad, sinkpad);
- if (lret != GST_PAD_LINK_OK) {
- g_warning ("failed to link pad to sink: %d", lret);
- }
- gst_object_unref (sinkpad);
- }
-
- /* now play and wait till we get the pads blocked. At that time the pipeline
- * is prerolled and we have the caps on the streams too. */
- ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
- if (ret == GST_STATE_CHANGE_FAILURE)
- goto cant_play;
-
- /* wait for state change to complete */
- gst_element_get_state (pipeline, NULL, NULL, -1);
-
- /* we should now be able to construct the SDP message */
- gst_sdp_message_new (&sdp);
-
- /* some standard things first */
- gst_sdp_message_set_version (sdp, "0");
- gst_sdp_message_set_origin (sdp, "-", "1188340656180883", "1", "IN", "IP4", "127.0.0.1");
- gst_sdp_message_set_session_name (sdp, "Session streamed with GStreamer");
- gst_sdp_message_set_information (sdp, "rtsp-server");
- gst_sdp_message_add_time (sdp, "0", "0", NULL);
- gst_sdp_message_add_attribute (sdp, "tool", "GStreamer");
- gst_sdp_message_add_attribute (sdp, "type", "broadcast");
-
- for (i = 0; i < n_streams; i++) {
- GstRTSPMediaStream *stream;
- GstSDPMedia *smedia;
- GstStructure *s;
- const gchar *caps_str, *caps_enc, *caps_params;
- gchar *tmp;
- gint caps_pt, caps_rate;
- guint n_fields, j;
- gboolean first;
- GString *fmtp;
-
- stream = gst_rtsp_media_get_stream (media, i);
- gst_sdp_media_new (&smedia);
-
- s = gst_caps_get_structure (stream->caps, 0);
-
- /* get media type and payload for the m= line */
- caps_str = gst_structure_get_string (s, "media");
- gst_sdp_media_set_media (smedia, caps_str);
-
- gst_structure_get_int (s, "payload", &caps_pt);
- tmp = g_strdup_printf ("%d", caps_pt);
- gst_sdp_media_add_format (smedia, tmp);
- g_free (tmp);
-
- gst_sdp_media_set_port_info (smedia, 0, 1);
- gst_sdp_media_set_proto (smedia, "RTP/AVP");
-
- /* for the c= line */
- gst_sdp_media_add_connection (smedia, "IN", "IP4", "127.0.0.1", 0, 0);
-
- /* get clock-rate, media type and params for the rtpmap attribute */
- gst_structure_get_int (s, "clock-rate", &caps_rate);
- caps_enc = gst_structure_get_string (s, "encoding-name");
- caps_params = gst_structure_get_string (s, "encoding-params");
-
- if (caps_params)
- tmp = g_strdup_printf ("%d %s/%d/%s", caps_pt, caps_enc, caps_rate,
- caps_params);
- else
- tmp = g_strdup_printf ("%d %s/%d", caps_pt, caps_enc, caps_rate);
-
- gst_sdp_media_add_attribute (smedia, "rtpmap", tmp);
- g_free (tmp);
-
- /* the config uri */
- tmp = g_strdup_printf ("stream=%d", i);
- gst_sdp_media_add_attribute (smedia, "control", tmp);
- g_free (tmp);
-
- /* collect all other properties and add them to fmtp */
- fmtp = g_string_new ("");
- g_string_append_printf (fmtp, "%d ", caps_pt);
- first = TRUE;
- n_fields = gst_structure_n_fields (s);
- for (j = 0; j < n_fields; j++) {
- const gchar *fname, *fval;
-
- fname = gst_structure_nth_field_name (s, j);
-
- /* filter out standard properties */
- if (!strcmp (fname, "media"))
- continue;
- if (!strcmp (fname, "payload"))
- continue;
- if (!strcmp (fname, "clock-rate"))
- continue;
- if (!strcmp (fname, "encoding-name"))
- continue;
- if (!strcmp (fname, "encoding-params"))
- continue;
- if (!strcmp (fname, "ssrc"))
- continue;
- if (!strcmp (fname, "clock-base"))
- continue;
- if (!strcmp (fname, "seqnum-base"))
- continue;
-
- if ((fval = gst_structure_get_string (s, fname))) {
- g_string_append_printf (fmtp, "%s%s=%s", first ? "":";", fname, fval);
- first = FALSE;
- }
- }
- if (!first) {
- tmp = g_string_free (fmtp, FALSE);
- gst_sdp_media_add_attribute (smedia, "fmtp", tmp);
- g_free (tmp);
- }
- else {
- g_string_free (fmtp, TRUE);
- }
- gst_sdp_message_add_media (sdp, smedia);
- }
- /* go back to NULL */
- gst_element_set_state (pipeline, GST_STATE_NULL);
-
- g_object_unref (factory);
-
- gst_object_unref (pipeline);
- pipeline = NULL;
+ /* create an SDP for the media object */
+ if (!(sdp = gst_rtsp_sdp_from_media (media)))
+ goto no_sdp;
gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK,
gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONTENT_TYPE, "application/sdp");
+ str = g_strdup_printf ("rtsp://%s:%u%s/", uri->host, uri->port, uri->abspath);
+ gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONTENT_BASE, str);
+ g_free (str);
+
/* add SDP to the response body */
- sdptext = gst_sdp_message_as_text (sdp);
- gst_rtsp_message_take_body (&response, (guint8 *)sdptext, strlen (sdptext));
+ str = gst_sdp_message_as_text (sdp);
+ gst_rtsp_message_take_body (&response, (guint8 *)str, strlen (str));
gst_sdp_message_free (sdp);
- gst_rtsp_connection_send (client->connection, &response, NULL);
+ handle_response (client, &response);
return TRUE;
/* ERRORS */
-no_factory:
- {
- handle_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
- return FALSE;
- }
no_media:
{
- handle_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
- g_object_unref (factory);
+ /* error reply is already sent */
return FALSE;
}
-cant_play:
+no_sdp:
{
handle_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
- gst_object_unref (pipeline);
- g_object_unref (factory);
+ g_object_unref (media);
return FALSE;
}
}
{
GstRTSPMessage response = { 0 };
GstRTSPMethod options;
- GString *str;
+ gchar *str;
options = GST_RTSP_DESCRIBE |
GST_RTSP_OPTIONS |
GST_RTSP_SETUP |
GST_RTSP_TEARDOWN;
- /* always return options.. */
- str = g_string_new ("OPTIONS");
-
- if (options & GST_RTSP_DESCRIBE)
- g_string_append (str, ", DESCRIBE");
- if (options & GST_RTSP_ANNOUNCE)
- g_string_append (str, ", ANNOUNCE");
- if (options & GST_RTSP_GET_PARAMETER)
- g_string_append (str, ", GET_PARAMETER");
- if (options & GST_RTSP_PAUSE)
- g_string_append (str, ", PAUSE");
- if (options & GST_RTSP_PLAY)
- g_string_append (str, ", PLAY");
- if (options & GST_RTSP_RECORD)
- g_string_append (str, ", RECORD");
- if (options & GST_RTSP_REDIRECT)
- g_string_append (str, ", REDIRECT");
- if (options & GST_RTSP_SETUP)
- g_string_append (str, ", SETUP");
- if (options & GST_RTSP_SET_PARAMETER)
- g_string_append (str, ", SET_PARAMETER");
- if (options & GST_RTSP_TEARDOWN)
- g_string_append (str, ", TEARDOWN");
+ str = gst_rtsp_options_as_text (options);
gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK,
gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
- gst_rtsp_message_add_header (&response, GST_RTSP_HDR_PUBLIC, str->str);
+ gst_rtsp_message_add_header (&response, GST_RTSP_HDR_PUBLIC, str);
+ g_free (str);
- g_string_free (str, TRUE);
+ handle_response (client, &response);
+}
- gst_rtsp_connection_send (client->connection, &response, NULL);
+/* remove duplicate and trailing '/' */
+static void
+santize_uri (GstRTSPUrl *uri)
+{
+ gint i, len;
+ gchar *s, *d;
+ gboolean have_slash, prev_slash;
+
+ s = d = uri->abspath;
+ len = strlen (uri->abspath);
+
+ prev_slash = FALSE;
+
+ for (i = 0; i < len; i++) {
+ have_slash = s[i] == '/';
+ *d = s[i];
+ if (!have_slash || !prev_slash)
+ d++;
+ prev_slash = have_slash;
+ }
+ len = d - uri->abspath;
+ /* don't remove the first slash if that's the only thing left */
+ if (len > 1 && *(d-1) == '/')
+ d--;
+ *d = '\0';
}
/* this function runs in a client specific thread and handles all rtsp messages
continue;
}
+ /* sanitize the uri */
+ santize_uri (uri);
+
/* now see what is asked and dispatch to a dedicated handler */
switch (method) {
case GST_RTSP_OPTIONS:
{
GstRTSPSessionPool *old;
- old = client->pool;
+ old = client->session_pool;
if (old != pool) {
if (pool)
g_object_ref (pool);
- client->pool = pool;
+ client->session_pool = pool;
if (old)
g_object_unref (old);
}
{
GstRTSPSessionPool *result;
- if ((result = client->pool))
+ if ((result = client->session_pool))
g_object_ref (result);
return result;
{
GstRTSPMediaMapping *old;
- old = client->mapping;
+ old = client->media_mapping;
if (old != mapping) {
if (mapping)
g_object_ref (mapping);
- client->mapping = mapping;
+ client->media_mapping = mapping;
if (old)
g_object_unref (old);
}
{
GstRTSPMediaMapping *result;
- if ((result = client->mapping))
+ if ((result = client->media_mapping))
g_object_ref (result);
return result;
}
-
/**
* gst_rtsp_client_attach:
* @client: a #GstRTSPClient
struct sockaddr_in address;
GThread *thread;
- GstRTSPSessionPool *pool;
-
- GstRTSPMediaFactory *factory;
- GstRTSPMediaMapping *mapping;
+ GstRTSPSessionPool *session_pool;
+ GstRTSPMediaMapping *media_mapping;
};
struct _GstRTSPClientClass {
return res;
}
-static void
-caps_notify (GstPad * pad, GParamSpec * unused, GstRTSPMediaStream * stream)
-{
- if (stream->caps)
- gst_caps_unref (stream->caps);
- if ((stream->caps = GST_PAD_CAPS (pad)))
- gst_caps_ref (stream->caps);
-}
-
static GstElement *
default_get_element (GstRTSPMediaFactory *factory, const GstRTSPUrl *url)
{
if (element == NULL)
goto no_element;
- media = g_object_new (GST_TYPE_RTSP_MEDIA, NULL);
+ /* create a new empty media */
+ media = gst_rtsp_media_new ();
media->element = element;
/* try to find all the payloader elements, they should be named 'pay%d'. for
- * each of the payloaders we will create a stream, collect the source pad and
- * add a notify::caps on the pad. */
+ * each of the payloaders we will create a stream and collect the source pad.
+ */
for (i = 0; ; i++) {
gchar *name;
/* create the stream */
stream = g_new0 (GstRTSPMediaStream, 1);
stream->media = media;
- stream->element = element;
stream->payloader = pay;
stream->idx = media->streams->len;
/* ghost the pad of the payloader to the element */
stream->srcpad = gst_ghost_pad_new (name, pad);
- gst_element_add_pad (stream->element, stream->srcpad);
-
- stream->caps_sig = g_signal_connect (pad, "notify::caps", (GCallback) caps_notify, stream);
- gst_object_unref (pad);
+ gst_element_add_pad (media->element, stream->srcpad);
+ gst_object_unref (pay);
+ g_free (name);
/* add stream now */
g_array_append_val (media->streams, stream);
- gst_object_unref (pay);
-
- g_free (name);
}
return media;
/**
* GstRTSPMediaFactoryClass:
- * @get_element: Construct an return a #GstElement thast is a #GstBin containing
- * the pipeline to use for the media. The bin should contain elements
- * pay%d for each stream. The default implementation of this functions
- * returns the bin created from the launch parameter.
* @construct: the vmethod that will be called when the factory has to create the
* #GstRTSPMedia for @url. The default implementation of this
* function calls get_element to retrieve an element and then looks for
* pay%d to create the streams.
-
+ * @handle_message: Handle a bus message for @media created from @factory.
+ * @get_element: Construct an return a #GstElement thast is a #GstBin containing
+ * the pipeline to use for the media. The bin should contain elements
+ * pay%d for each stream. The default implementation of this functions
+ * returns the bin created from the launch parameter.
+ *
* the #GstRTSPMediaFactory class structure.
*/
struct _GstRTSPMediaFactoryClass {
GObjectClass parent_class;
- GstElement * (*get_element) (GstRTSPMediaFactory *factory, const GstRTSPUrl *url);
- GstRTSPMedia * (*construct) (GstRTSPMediaFactory *factory, const GstRTSPUrl *url);
+ GstRTSPMedia * (*construct) (GstRTSPMediaFactory *factory, const GstRTSPUrl *url);
+ void (*handle_message) (GstRTSPMediaFactory *factory, GstRTSPMedia *media,
+ GstMessage *message);
+
+ GstElement * (*get_element) (GstRTSPMediaFactory *factory, const GstRTSPUrl *url);
+
};
GType gst_rtsp_media_factory_get_type (void);
G_OBJECT_CLASS (gst_rtsp_media_parent_class)->finalize (obj);
}
+/**
+ * gst_rtsp_media_new:
+ *
+ * Create a new #GstRTSPMedia instance. The #GstRTSPMedia object contains the
+ * element to produde RTP data for one or more related (audio/video/..)
+ * streams.
+ *
+ * Returns: a new #GstRTSPMedia object.
+ */
+GstRTSPMedia *
+gst_rtsp_media_new (void)
+{
+ GstRTSPMedia *result;
+
+ result = g_object_new (GST_TYPE_RTSP_MEDIA, NULL);
+
+ return result;
+}
+
+
/**
* gst_rtsp_media_n_streams:
* @media: a #GstRTSPMedia
return res;
}
+/* Allocate the udp ports and sockets */
+static gboolean
+alloc_udp_ports (GstRTSPMediaStream * stream)
+{
+ GstStateChangeReturn ret;
+ GstElement *udpsrc0, *udpsrc1;
+ GstElement *udpsink0, *udpsink1;
+ gint tmp_rtp, tmp_rtcp;
+ guint count;
+ gint rtpport, rtcpport, sockfd;
+
+ udpsrc0 = NULL;
+ udpsrc1 = NULL;
+ udpsink0 = NULL;
+ udpsink1 = NULL;
+ count = 0;
+
+ /* Start with random port */
+ tmp_rtp = 0;
+
+ /* try to allocate 2 UDP ports, the RTP port should be an even
+ * number and the RTCP port should be the next (uneven) port */
+again:
+ udpsrc0 = gst_element_make_from_uri (GST_URI_SRC, "udp://0.0.0.0", NULL);
+ if (udpsrc0 == NULL)
+ goto no_udp_protocol;
+ g_object_set (G_OBJECT (udpsrc0), "port", tmp_rtp, NULL);
+
+ ret = gst_element_set_state (udpsrc0, GST_STATE_PAUSED);
+ if (ret == GST_STATE_CHANGE_FAILURE) {
+ if (tmp_rtp != 0) {
+ tmp_rtp += 2;
+ if (++count > 20)
+ goto no_ports;
+
+ gst_element_set_state (udpsrc0, GST_STATE_NULL);
+ gst_object_unref (udpsrc0);
+
+ goto again;
+ }
+ goto no_udp_protocol;
+ }
+
+ g_object_get (G_OBJECT (udpsrc0), "port", &tmp_rtp, NULL);
+
+ /* check if port is even */
+ if ((tmp_rtp & 1) != 0) {
+ /* port not even, close and allocate another */
+ if (++count > 20)
+ goto no_ports;
+
+ gst_element_set_state (udpsrc0, GST_STATE_NULL);
+ gst_object_unref (udpsrc0);
+
+ tmp_rtp++;
+ goto again;
+ }
+
+ /* allocate port+1 for RTCP now */
+ udpsrc1 = gst_element_make_from_uri (GST_URI_SRC, "udp://0.0.0.0", NULL);
+ if (udpsrc1 == NULL)
+ goto no_udp_rtcp_protocol;
+
+ /* set port */
+ tmp_rtcp = tmp_rtp + 1;
+ g_object_set (G_OBJECT (udpsrc1), "port", tmp_rtcp, NULL);
+
+ ret = gst_element_set_state (udpsrc1, GST_STATE_PAUSED);
+ /* tmp_rtcp port is busy already : retry to make rtp/rtcp pair */
+ if (ret == GST_STATE_CHANGE_FAILURE) {
+
+ if (++count > 20)
+ goto no_ports;
+
+ gst_element_set_state (udpsrc0, GST_STATE_NULL);
+ gst_object_unref (udpsrc0);
+
+ gst_element_set_state (udpsrc1, GST_STATE_NULL);
+ gst_object_unref (udpsrc1);
+
+ tmp_rtp += 2;
+ goto again;
+ }
+
+ /* all fine, do port check */
+ g_object_get (G_OBJECT (udpsrc0), "port", &rtpport, NULL);
+ g_object_get (G_OBJECT (udpsrc1), "port", &rtcpport, NULL);
+
+ /* this should not happen... */
+ if (rtpport != tmp_rtp || rtcpport != tmp_rtcp)
+ goto port_error;
+
+ udpsink0 = gst_element_factory_make ("multiudpsink", NULL);
+ if (!udpsink0)
+ goto no_udp_protocol;
+
+ g_object_get (G_OBJECT (udpsrc0), "sock", &sockfd, NULL);
+ g_object_set (G_OBJECT (udpsink0), "sockfd", sockfd, NULL);
+ g_object_set (G_OBJECT (udpsink0), "closefd", FALSE, NULL);
+
+ udpsink1 = gst_element_factory_make ("multiudpsink", NULL);
+ if (!udpsink1)
+ goto no_udp_protocol;
+
+ g_object_get (G_OBJECT (udpsrc1), "sock", &sockfd, NULL);
+ g_object_set (G_OBJECT (udpsink1), "sockfd", sockfd, NULL);
+ g_object_set (G_OBJECT (udpsink1), "closefd", FALSE, NULL);
+ g_object_set (G_OBJECT (udpsink1), "sync", FALSE, NULL);
+ g_object_set (G_OBJECT (udpsink1), "async", FALSE, NULL);
+
+ /* we keep these elements, we configure all in configure_transport when the
+ * server told us to really use the UDP ports. */
+ stream->udpsrc[0] = gst_object_ref (udpsrc0);
+ stream->udpsrc[1] = gst_object_ref (udpsrc1);
+ stream->udpsink[0] = gst_object_ref (udpsink0);
+ stream->udpsink[1] = gst_object_ref (udpsink1);
+ stream->server_port.min = rtpport;
+ stream->server_port.max = rtcpport;
+
+ /* they are ours now */
+ gst_object_sink (udpsrc0);
+ gst_object_sink (udpsrc1);
+ gst_object_sink (udpsink0);
+ gst_object_sink (udpsink1);
+
+ return TRUE;
+
+ /* ERRORS */
+no_udp_protocol:
+ {
+ goto cleanup;
+ }
+no_ports:
+ {
+ goto cleanup;
+ }
+no_udp_rtcp_protocol:
+ {
+ goto cleanup;
+ }
+port_error:
+ {
+ goto cleanup;
+ }
+cleanup:
+ {
+ if (udpsrc0) {
+ gst_element_set_state (udpsrc0, GST_STATE_NULL);
+ gst_object_unref (udpsrc0);
+ }
+ if (udpsrc1) {
+ gst_element_set_state (udpsrc1, GST_STATE_NULL);
+ gst_object_unref (udpsrc1);
+ }
+ if (udpsink0) {
+ gst_element_set_state (udpsink0, GST_STATE_NULL);
+ gst_object_unref (udpsink0);
+ }
+ if (udpsink1) {
+ gst_element_set_state (udpsink1, GST_STATE_NULL);
+ gst_object_unref (udpsink1);
+ }
+ return FALSE;
+ }
+}
+
+static void
+caps_notify (GstPad * pad, GParamSpec * unused, GstRTSPMediaStream * stream)
+{
+ gchar *capsstr;
+
+ if (stream->caps)
+ gst_caps_unref (stream->caps);
+ if ((stream->caps = GST_PAD_CAPS (pad)))
+ gst_caps_ref (stream->caps);
+
+ capsstr = gst_caps_to_string (stream->caps);
+ g_message ("stream %p received caps %s", stream, capsstr);
+ g_free (capsstr);
+}
+
+/* prepare the pipeline objects to handle @stream in @media */
+static gboolean
+setup_stream (GstRTSPMediaStream *stream, GstRTSPMedia *media)
+{
+ gchar *name;
+ GstPad *pad;
+
+ alloc_udp_ports (stream);
+
+ gst_bin_add (GST_BIN_CAST (media->pipeline), stream->udpsink[0]);
+ gst_bin_add (GST_BIN_CAST (media->pipeline), stream->udpsink[1]);
+ gst_bin_add (GST_BIN_CAST (media->pipeline), stream->udpsrc[1]);
+
+ /* hook up the stream to the RTP session elements. */
+ name = g_strdup_printf ("send_rtp_sink_%d", stream->idx);
+ stream->send_rtp_sink = gst_element_get_request_pad (media->rtpbin, name);
+ g_free (name);
+ name = g_strdup_printf ("send_rtp_src_%d", stream->idx);
+ stream->send_rtp_src = gst_element_get_static_pad (media->rtpbin, name);
+ g_free (name);
+ name = g_strdup_printf ("send_rtcp_src_%d", stream->idx);
+ stream->send_rtcp_src = gst_element_get_request_pad (media->rtpbin, name);
+ g_free (name);
+ name = g_strdup_printf ("recv_rtcp_sink_%d", stream->idx);
+ stream->recv_rtcp_sink = gst_element_get_request_pad (media->rtpbin, name);
+ g_free (name);
+
+ /* link the RTP pad to the session manager */
+ gst_pad_link (stream->srcpad, stream->send_rtp_sink);
+
+ /* link udp elements */
+ pad = gst_element_get_static_pad (stream->udpsink[0], "sink");
+ gst_pad_link (stream->send_rtp_src, pad);
+ gst_object_unref (pad);
+ pad = gst_element_get_static_pad (stream->udpsink[1], "sink");
+ gst_pad_link (stream->send_rtcp_src, pad);
+ gst_object_unref (pad);
+ pad = gst_element_get_static_pad (stream->udpsrc[1], "src");
+ gst_pad_link (pad, stream->recv_rtcp_sink);
+ gst_object_unref (pad);
+
+ /* we set and keep these to playing so that they don't cause NO_PREROLL return
+ * values */
+ gst_element_set_state (stream->udpsrc[0], GST_STATE_PLAYING);
+ gst_element_set_state (stream->udpsrc[1], GST_STATE_PLAYING);
+ gst_element_set_locked_state (stream->udpsrc[0], TRUE);
+ gst_element_set_locked_state (stream->udpsrc[1], TRUE);
+
+ /* be notified of caps changes */
+ stream->caps_sig = g_signal_connect (stream->send_rtp_sink, "notify::caps",
+ (GCallback) caps_notify, stream);
+
+ stream->prepared = TRUE;
+
+ return TRUE;
+}
+
+
+/**
+ * gst_rtsp_media_prepare:
+ * @obj: a #GstRTSPMedia
+ *
+ * Prepare @media for streaming. This function will create the pipeline and
+ * other objects to manage the streaming.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean
+gst_rtsp_media_prepare (GstRTSPMedia *media)
+{
+ GstStateChangeReturn ret;
+ guint i, n_streams;
+
+ if (media->prepared)
+ goto was_prepared;
+
+ media->pipeline = gst_pipeline_new ("media-pipeline");
+
+ gst_bin_add (GST_BIN_CAST (media->pipeline), media->element);
+
+ media->rtpbin = gst_element_factory_make ("gstrtpbin", "rtpbin");
+
+ /* add stuf to the bin */
+ gst_bin_add (GST_BIN (media->pipeline), media->rtpbin);
+
+ ret = gst_element_set_state (media->pipeline, GST_STATE_READY);
+
+ n_streams = gst_rtsp_media_n_streams (media);
+ for (i = 0; i < n_streams; i++) {
+ GstRTSPMediaStream *stream;
+
+ stream = gst_rtsp_media_get_stream (media, i);
+
+ setup_stream (stream, media);
+ }
+
+ /* first go to PAUSED */
+ ret = gst_element_set_state (media->pipeline, GST_STATE_PAUSED);
+
+ switch (ret) {
+ case GST_STATE_CHANGE_SUCCESS:
+ break;
+ case GST_STATE_CHANGE_ASYNC:
+ break;
+ case GST_STATE_CHANGE_NO_PREROLL:
+ /* we need to go to PLAYING */
+ g_message ("live media %p", media);
+ ret = gst_element_set_state (media->pipeline, GST_STATE_PLAYING);
+ break;
+ case GST_STATE_CHANGE_FAILURE:
+ goto state_failed;
+ }
+
+ /* no wait for all pads to be prerolled */
+ ret = gst_element_get_state (media->pipeline, NULL, NULL, -1);
+
+ /* and back to PAUSED for live pipelines */
+ ret = gst_element_set_state (media->pipeline, GST_STATE_PAUSED);
+
+ g_message ("object %p is prerolled", media);
+ media->prepared = TRUE;
+
+ return TRUE;
+
+ /* OK */
+was_prepared:
+ {
+ return TRUE;
+ }
+ /* ERRORS */
+state_failed:
+ {
+ g_message ("state change failed for media %p", media);
+ return FALSE;
+ }
+}
+
+gboolean
+gst_rtsp_media_stream_add (GstRTSPMediaStream *stream, GstRTSPTransport *ct)
+{
+ g_return_val_if_fail (stream != NULL, FALSE);
+ g_return_val_if_fail (ct != NULL, FALSE);
+ g_return_val_if_fail (stream->prepared, FALSE);
+
+ g_message ("adding %s:%d", ct->destination, ct->client_port.min);
+
+ g_signal_emit_by_name (stream->udpsink[0], "add", ct->destination, ct->client_port.min, NULL);
+ g_signal_emit_by_name (stream->udpsink[1], "add", ct->destination, ct->client_port.max, NULL);
+
+ return TRUE;
+}
+
+gboolean
+gst_rtsp_media_stream_remove (GstRTSPMediaStream *stream, GstRTSPTransport *ct)
+{
+ g_return_val_if_fail (stream != NULL, FALSE);
+ g_return_val_if_fail (ct != NULL, FALSE);
+ g_return_val_if_fail (stream->prepared, FALSE);
+
+ g_message ("removing %s:%d", ct->destination, ct->client_port.min);
+
+ g_signal_emit_by_name (stream->udpsink[0], "remove", ct->destination, ct->client_port.min, NULL);
+ g_signal_emit_by_name (stream->udpsink[1], "remove", ct->destination, ct->client_port.max, NULL);
+
+ return TRUE;
+}
+
+/**
+ * gst_rtsp_media_play:
+ * @media: a #GstRTSPMedia
+ *
+ * Tell the @media to start playing and streaming to the client.
+ *
+ * Returns: a #GstStateChangeReturn
+ */
+GstStateChangeReturn
+gst_rtsp_media_play (GstRTSPMedia *media)
+{
+ GstStateChangeReturn ret;
+
+ g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), GST_STATE_CHANGE_FAILURE);
+ g_return_val_if_fail (media->prepared, GST_STATE_CHANGE_FAILURE);
+
+ g_message ("playing");
+ ret = gst_element_set_state (media->pipeline, GST_STATE_PLAYING);
+
+ return ret;
+}
+
+/**
+ * gst_rtsp_media_pause:
+ * @media: a #GstRTSPMedia
+ *
+ * Tell the @media to pause.
+ *
+ * Returns: a #GstStateChangeReturn
+ */
+GstStateChangeReturn
+gst_rtsp_media_pause (GstRTSPMedia *media)
+{
+ GstStateChangeReturn ret;
+
+ g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), GST_STATE_CHANGE_FAILURE);
+ g_return_val_if_fail (media->prepared, GST_STATE_CHANGE_FAILURE);
+
+ g_message ("paused");
+ ret = gst_element_set_state (media->pipeline, GST_STATE_PAUSED);
+
+ return ret;
+}
+
+/**
+ * gst_rtsp_media_stop:
+ * @media: a #GstRTSPMedia
+ *
+ * Tell the @media to stop playing. After this call the media
+ * cannot be played or paused anymore
+ *
+ * Returns: a #GstStateChangeReturn
+ */
+GstStateChangeReturn
+gst_rtsp_media_stop (GstRTSPMedia *media)
+{
+ GstStateChangeReturn ret;
+
+ g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), GST_STATE_CHANGE_FAILURE);
+ g_return_val_if_fail (media->prepared, GST_STATE_CHANGE_FAILURE);
+
+ g_message ("stop");
+ ret = gst_element_set_state (media->pipeline, GST_STATE_NULL);
+
+ return ret;
+}
+
/**
* GstRTSPMediaStream:
*
+ * @media: the owner #GstRTSPMedia
* @idx: the stream index
- * @element: the toplevel element
* @srcpad: the srcpad of the stream
* @payloader: the payloader of the format
+ * @prepared: if the stream is prepared for streaming
+ * @server_port: the server udp ports
+ * @recv_rtp_sink: sinkpad for RTP buffers
+ * @recv_rtcp_sink: sinkpad for RTCP buffers
+ * @recv_rtp_src: srcpad for RTP buffers
+ * @recv_rtcp_src: srcpad for RTCP buffers
+ * @udpsrc: the udp source elements for RTP/RTCP
+ * @udpsink: the udp sink elements for RTP/RTCP
* @caps_sig: the signal id for detecting caps
* @caps: the caps of the stream
*
struct _GstRTSPMediaStream {
GstRTSPMedia *media;
- guint idx;
+ guint idx;
+
+ GstPad *srcpad;
+ GstElement *payloader;
+ gboolean prepared;
+
+ GstRTSPRange server_port;
+
+ /* pads on the rtpbin */
+ GstPad *recv_rtcp_sink;
+ GstPad *send_rtp_sink;
+ GstPad *send_rtp_src;
+ GstPad *send_rtcp_src;
- GstElement *element;
- GstPad *srcpad;
- GstElement *payloader;
- gulong caps_sig;
- GstCaps *caps;
+ /* sinks used for sending and receiving RTP and RTCP, they share
+ * sockets */
+ GstElement *udpsrc[2];
+ GstElement *udpsink[2];
+
+ /* the caps of the stream */
+ gulong caps_sig;
+ GstCaps *caps;
};
/**
* GstRTSPMedia:
- * @media: the owner #GstRTSPMedia
+ * @element: the data providing element
+ * @stream: the different streams provided by @element
+ * @prepared: if the media is prepared for streaming
+ * @pipeline: the toplevel pipeline
+ * @rtpbin: the rtpbin
+ * @multifdsink: multifdsink element for TCP transport
*
* A class that contains the GStreamer element along with a list of
* #GstRTSPediaStream objects that can produce data.
GstElement *element;
GArray *streams;
+ gboolean prepared;
+
+ /* the pipeline for the media */
+ GstElement *pipeline;
+
+ /* RTP session manager */
+ GstElement *rtpbin;
+
+ /* for TCP transport */
+ GstElement *multifdsink;
};
struct _GstRTSPMediaClass {
GType gst_rtsp_media_get_type (void);
+/* creating the media */
+GstRTSPMedia * gst_rtsp_media_new (void);
+
/* dealing with the media */
guint gst_rtsp_media_n_streams (GstRTSPMedia *media);
GstRTSPMediaStream * gst_rtsp_media_get_stream (GstRTSPMedia *media, guint idx);
+/* prepare the media for playback */
+gboolean gst_rtsp_media_prepare (GstRTSPMedia *media);
+
+/* add destinations to a stream */
+gboolean gst_rtsp_media_stream_add (GstRTSPMediaStream *stream, GstRTSPTransport *ct);
+gboolean gst_rtsp_media_stream_remove (GstRTSPMediaStream *stream, GstRTSPTransport *ct);
+
+GstStateChangeReturn gst_rtsp_media_play (GstRTSPMedia *media);
+GstStateChangeReturn gst_rtsp_media_pause (GstRTSPMedia *media);
+GstStateChangeReturn gst_rtsp_media_stop (GstRTSPMedia *media);
+
G_END_DECLS
#endif /* __GST_RTSP_MEDIA_H__ */
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include <string.h>
+
+#include "rtsp-sdp.h"
+
+/**
+ * gst_rtsp_sdp_from_media:
+ * @media: a #GstRTSPMedia
+ *
+ * Create a new sdp message for @media.
+ *
+ * Returns: a new sdp message for @media. gst_sdp_message_free() after usage.
+ */
+GstSDPMessage *
+gst_rtsp_sdp_from_media (GstRTSPMedia *media)
+{
+ GstSDPMessage *sdp;
+ guint i, n_streams;
+
+ n_streams = gst_rtsp_media_n_streams (media);
+
+ gst_sdp_message_new (&sdp);
+
+ /* some standard things first */
+ gst_sdp_message_set_version (sdp, "0");
+ gst_sdp_message_set_origin (sdp, "-", "1188340656180883", "1", "IN", "IP4", "127.0.0.1");
+ gst_sdp_message_set_session_name (sdp, "Session streamed with GStreamer");
+ gst_sdp_message_set_information (sdp, "rtsp-server");
+ gst_sdp_message_add_time (sdp, "0", "0", NULL);
+ gst_sdp_message_add_attribute (sdp, "tool", "GStreamer");
+ gst_sdp_message_add_attribute (sdp, "type", "broadcast");
+
+ for (i = 0; i < n_streams; i++) {
+ GstRTSPMediaStream *stream;
+ GstSDPMedia *smedia;
+ GstStructure *s;
+ const gchar *caps_str, *caps_enc, *caps_params;
+ gchar *tmp;
+ gint caps_pt, caps_rate;
+ guint n_fields, j;
+ gboolean first;
+ GString *fmtp;
+
+ stream = gst_rtsp_media_get_stream (media, i);
+ gst_sdp_media_new (&smedia);
+
+ s = gst_caps_get_structure (stream->caps, 0);
+
+ /* get media type and payload for the m= line */
+ caps_str = gst_structure_get_string (s, "media");
+ gst_sdp_media_set_media (smedia, caps_str);
+
+ gst_structure_get_int (s, "payload", &caps_pt);
+ tmp = g_strdup_printf ("%d", caps_pt);
+ gst_sdp_media_add_format (smedia, tmp);
+ g_free (tmp);
+
+ gst_sdp_media_set_port_info (smedia, 0, 1);
+ gst_sdp_media_set_proto (smedia, "RTP/AVP");
+
+ /* for the c= line */
+ gst_sdp_media_add_connection (smedia, "IN", "IP4", "127.0.0.1", 0, 0);
+
+ /* get clock-rate, media type and params for the rtpmap attribute */
+ gst_structure_get_int (s, "clock-rate", &caps_rate);
+ caps_enc = gst_structure_get_string (s, "encoding-name");
+ caps_params = gst_structure_get_string (s, "encoding-params");
+
+ if (caps_params)
+ tmp = g_strdup_printf ("%d %s/%d/%s", caps_pt, caps_enc, caps_rate,
+ caps_params);
+ else
+ tmp = g_strdup_printf ("%d %s/%d", caps_pt, caps_enc, caps_rate);
+
+ gst_sdp_media_add_attribute (smedia, "rtpmap", tmp);
+ g_free (tmp);
+
+ /* the config uri */
+ tmp = g_strdup_printf ("stream=%d", i);
+ gst_sdp_media_add_attribute (smedia, "control", tmp);
+ g_free (tmp);
+
+ /* collect all other properties and add them to fmtp */
+ fmtp = g_string_new ("");
+ g_string_append_printf (fmtp, "%d ", caps_pt);
+ first = TRUE;
+ n_fields = gst_structure_n_fields (s);
+ for (j = 0; j < n_fields; j++) {
+ const gchar *fname, *fval;
+
+ fname = gst_structure_nth_field_name (s, j);
+
+ /* filter out standard properties */
+ if (!strcmp (fname, "media"))
+ continue;
+ if (!strcmp (fname, "payload"))
+ continue;
+ if (!strcmp (fname, "clock-rate"))
+ continue;
+ if (!strcmp (fname, "encoding-name"))
+ continue;
+ if (!strcmp (fname, "encoding-params"))
+ continue;
+ if (!strcmp (fname, "ssrc"))
+ continue;
+ if (!strcmp (fname, "clock-base"))
+ continue;
+ if (!strcmp (fname, "seqnum-base"))
+ continue;
+
+ if ((fval = gst_structure_get_string (s, fname))) {
+ g_string_append_printf (fmtp, "%s%s=%s", first ? "":";", fname, fval);
+ first = FALSE;
+ }
+ }
+ if (!first) {
+ tmp = g_string_free (fmtp, FALSE);
+ gst_sdp_media_add_attribute (smedia, "fmtp", tmp);
+ g_free (tmp);
+ }
+ else {
+ g_string_free (fmtp, TRUE);
+ }
+ gst_sdp_message_add_media (sdp, smedia);
+ }
+
+ return sdp;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gst/gst.h>
+#include <gst/sdp/gstsdpmessage.h>
+
+#include "rtsp-media.h"
+
+#ifndef __GST_RTSP_SDP_H__
+#define __GST_RTSP_SDP_H__
+
+G_BEGIN_DECLS
+
+/* creating SDP */
+GstSDPMessage * gst_rtsp_sdp_from_media (GstRTSPMedia *media);
+
+G_END_DECLS
+
+#endif /* __GST_RTSP_SDP_H__ */
PROP_0,
PROP_BACKLOG,
PROP_PORT,
- PROP_POOL,
- PROP_MAPPING,
+ PROP_SESSION_POOL,
+ PROP_MEDIA_MAPPING,
PROP_LAST
};
g_param_spec_int ("port", "Port", "The port the server uses to listen on",
1, 65535, DEFAULT_PORT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
- * GstRTSPServer::pool
+ * GstRTSPServer::session-pool
*
* The session pool of the server. By default each server has a separate
* session pool but sessions can be shared between servers by setting the same
* session pool on multiple servers.
*/
- g_object_class_install_property (gobject_class, PROP_POOL,
- g_param_spec_object ("pool", "Pool", "The session pool to use for client session",
+ g_object_class_install_property (gobject_class, PROP_SESSION_POOL,
+ g_param_spec_object ("session-pool", "Session Pool",
+ "The session pool to use for client session",
GST_TYPE_RTSP_SESSION_POOL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
- * GstRTSPServer::mapping
+ * GstRTSPServer::media-mapping
*
* The media mapping to use for this server. By default the server has no
* media mapping and thus cannot map urls to media streams.
*/
- g_object_class_install_property (gobject_class, PROP_MAPPING,
- g_param_spec_object ("mapping", "Mapping", "The media mapping to use for client session",
+ g_object_class_install_property (gobject_class, PROP_MEDIA_MAPPING,
+ g_param_spec_object ("media-mapping", "Media Mapping",
+ "The media mapping to use for client session",
GST_TYPE_RTSP_MEDIA_MAPPING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
klass->accept_client = gst_rtsp_server_accept_client;
{
server->server_port = DEFAULT_PORT;
server->backlog = DEFAULT_BACKLOG;
- server->pool = gst_rtsp_session_pool_new ();
- server->mapping = gst_rtsp_media_mapping_new ();
+ server->session_pool = gst_rtsp_session_pool_new ();
+ server->media_mapping = gst_rtsp_media_mapping_new ();
}
/**
g_return_if_fail (GST_IS_RTSP_SERVER (server));
- old = server->pool;
+ old = server->session_pool;
if (old != pool) {
if (pool)
g_object_ref (pool);
- server->pool = pool;
+ server->session_pool = pool;
if (old)
g_object_unref (old);
}
g_return_val_if_fail (GST_IS_RTSP_SERVER (server), NULL);
- if ((result = server->pool))
+ if ((result = server->session_pool))
g_object_ref (result);
return result;
g_return_if_fail (GST_IS_RTSP_SERVER (server));
- old = server->mapping;
+ old = server->media_mapping;
if (old != mapping) {
if (mapping)
g_object_ref (mapping);
- server->mapping = mapping;
+ server->media_mapping = mapping;
if (old)
g_object_unref (old);
}
g_return_val_if_fail (GST_IS_RTSP_SERVER (server), NULL);
- if ((result = server->mapping))
+ if ((result = server->media_mapping))
g_object_ref (result);
return result;
case PROP_BACKLOG:
g_value_set_int (value, gst_rtsp_server_get_backlog (server));
break;
- case PROP_POOL:
+ case PROP_SESSION_POOL:
g_value_take_object (value, gst_rtsp_server_get_session_pool (server));
break;
- case PROP_MAPPING:
+ case PROP_MEDIA_MAPPING:
g_value_take_object (value, gst_rtsp_server_get_media_mapping (server));
break;
default:
case PROP_BACKLOG:
gst_rtsp_server_set_backlog (server, g_value_get_int (value));
break;
- case PROP_POOL:
+ case PROP_SESSION_POOL:
gst_rtsp_server_set_session_pool (server, g_value_get_object (value));
break;
- case PROP_MAPPING:
+ case PROP_MEDIA_MAPPING:
gst_rtsp_server_set_media_mapping (server, g_value_get_object (value));
break;
default:
client = gst_rtsp_client_new ();
/* set the session pool that this client should use */
- gst_rtsp_client_set_session_pool (client, server->pool);
+ gst_rtsp_client_set_session_pool (client, server->session_pool);
/* set the session pool that this client should use */
- gst_rtsp_client_set_media_mapping (client, server->mapping);
+ gst_rtsp_client_set_media_mapping (client, server->media_mapping);
/* accept connections for that client, this function returns after accepting
* the connection and will run the remainder of the communication with the
GObject parent;
/* server information */
- gint server_port;
- gint backlog;
- gchar *host;
- struct sockaddr_in server_sin;
+ gint server_port;
+ gint backlog;
+ gchar *host;
+ struct sockaddr_in server_sin;
- /* socket */
- GstPollFD server_sock;
-
- GIOChannel *io_channel;
- GSource *io_watch;
+ /* socket and channels */
+ GstPollFD server_sock;
+ GIOChannel *io_channel;
+ GSource *io_watch;
/* sessions on this server */
- GstRTSPSessionPool *pool;
+ GstRTSPSessionPool *session_pool;
/* media mapper for this server */
- GstRTSPMediaMapping *mapping;
+ GstRTSPMediaMapping *media_mapping;
};
/**
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
+#include <string.h>
#include "rtsp-session.h"
{
if (stream->client_trans)
gst_rtsp_transport_free (stream->client_trans);
- g_free (stream->destination);
- if (stream->server_trans)
- gst_rtsp_transport_free (stream->server_trans);
-
- if (stream->udpsrc[0])
- gst_object_unref (stream->udpsrc[0]);
g_free (stream);
}
{
GList *walk;
- gst_element_set_state (media->pipeline, GST_STATE_NULL);
-
- if (media->factory)
- g_object_unref (media->factory);
-
for (walk = media->streams; walk; walk = g_list_next (walk)) {
GstRTSPSessionStream *stream = (GstRTSPSessionStream *) walk->data;
gst_rtsp_session_free_stream (stream);
}
- if (media->pipeline)
- gst_object_unref (media->pipeline);
g_list_free (media->streams);
}
G_OBJECT_CLASS (gst_rtsp_session_parent_class)->finalize (obj);
}
+/**
+ * gst_rtsp_session_manage_media:
+ * @sess: a #GstRTSPSession
+ * @url: the url for the media
+ * @obj: a #GstRTSPMediaObject
+ *
+ * Manage the media object @obj in @sess. @url will be used to retrieve this
+ * media object from the session with gst_rtsp_session_get_media().
+ *
+ * Returns: a new @GstRTSPSessionMedia object.
+ */
+GstRTSPSessionMedia *
+gst_rtsp_session_manage_media (GstRTSPSession *sess, const GstRTSPUrl *uri,
+ GstRTSPMedia *media)
+{
+ GstRTSPSessionMedia *result;
+
+ result = g_new0 (GstRTSPSessionMedia, 1);
+ result->media = media;
+ result->url = gst_rtsp_url_copy ((GstRTSPUrl *)uri);
+
+ sess->medias = g_list_prepend (sess->medias, result);
+
+ g_message ("manage new media %p in session %p", media, sess);
+
+ return result;
+}
+
/**
* gst_rtsp_session_get_media:
* @sess: a #GstRTSPSession
* @url: the url for the media
- * @factory: a #GstRTSPMediaFactory
*
- * Get or create the session information for @factory.
+ * Get the session media of the @url.
*
- * Returns: the configuration for @factory in @sess.
+ * Returns: the configuration for @url in @sess.
*/
GstRTSPSessionMedia *
-gst_rtsp_session_get_media (GstRTSPSession *sess, const GstRTSPUrl *url, GstRTSPMediaFactory *factory)
+gst_rtsp_session_get_media (GstRTSPSession *sess, const GstRTSPUrl *url)
{
GstRTSPSessionMedia *result;
GList *walk;
+ g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
+ g_return_val_if_fail (url != NULL, NULL);
+
result = NULL;
for (walk = sess->medias; walk; walk = g_list_next (walk)) {
result = (GstRTSPSessionMedia *) walk->data;
- if (result->factory == factory)
+ if (strcmp (result->url->abspath, url->abspath) == 0)
break;
result = NULL;
}
- if (result == NULL) {
- result = g_new0 (GstRTSPSessionMedia, 1);
- result->factory = factory;
- result->pipeline = gst_pipeline_new ("pipeline");
-
- /* construct media and add to the pipeline */
- result->media = gst_rtsp_media_factory_construct (factory, url);
- if (result->media == NULL)
- goto no_media;
-
- gst_bin_add (GST_BIN_CAST (result->pipeline), result->media->element);
-
- result->rtpbin = gst_element_factory_make ("gstrtpbin", "rtpbin");
-
- /* add stuf to the bin */
- gst_bin_add (GST_BIN (result->pipeline), result->rtpbin);
-
- gst_element_set_state (result->pipeline, GST_STATE_READY);
-
- sess->medias = g_list_prepend (sess->medias, result);
- }
return result;
-
- /* ERRORS */
-no_media:
- {
- gst_rtsp_session_free_media (result);
- return NULL;
- }
}
/**
return result;
}
-static gboolean
-alloc_udp_ports (GstRTSPSessionStream * stream)
-{
- GstStateChangeReturn ret;
- GstElement *udpsrc0, *udpsrc1;
- GstElement *udpsink0, *udpsink1;
- gint tmp_rtp, tmp_rtcp;
- guint count;
- gint rtpport, rtcpport, sockfd;
- gchar *name;
-
- udpsrc0 = NULL;
- udpsrc1 = NULL;
- udpsink0 = NULL;
- udpsink1 = NULL;
- count = 0;
-
- /* Start with random port */
- tmp_rtp = 0;
-
- /* try to allocate 2 UDP ports, the RTP port should be an even
- * number and the RTCP port should be the next (uneven) port */
-again:
- udpsrc0 = gst_element_make_from_uri (GST_URI_SRC, "udp://0.0.0.0", NULL);
- if (udpsrc0 == NULL)
- goto no_udp_protocol;
- g_object_set (G_OBJECT (udpsrc0), "port", tmp_rtp, NULL);
-
- ret = gst_element_set_state (udpsrc0, GST_STATE_PAUSED);
- if (ret == GST_STATE_CHANGE_FAILURE) {
- if (tmp_rtp != 0) {
- tmp_rtp += 2;
- if (++count > 20)
- goto no_ports;
-
- gst_element_set_state (udpsrc0, GST_STATE_NULL);
- gst_object_unref (udpsrc0);
-
- goto again;
- }
- goto no_udp_protocol;
- }
-
- g_object_get (G_OBJECT (udpsrc0), "port", &tmp_rtp, NULL);
-
- /* check if port is even */
- if ((tmp_rtp & 1) != 0) {
- /* port not even, close and allocate another */
- if (++count > 20)
- goto no_ports;
-
- gst_element_set_state (udpsrc0, GST_STATE_NULL);
- gst_object_unref (udpsrc0);
-
- tmp_rtp++;
- goto again;
- }
-
- /* allocate port+1 for RTCP now */
- udpsrc1 = gst_element_make_from_uri (GST_URI_SRC, "udp://0.0.0.0", NULL);
- if (udpsrc1 == NULL)
- goto no_udp_rtcp_protocol;
-
- /* set port */
- tmp_rtcp = tmp_rtp + 1;
- g_object_set (G_OBJECT (udpsrc1), "port", tmp_rtcp, NULL);
-
- ret = gst_element_set_state (udpsrc1, GST_STATE_PAUSED);
- /* tmp_rtcp port is busy already : retry to make rtp/rtcp pair */
- if (ret == GST_STATE_CHANGE_FAILURE) {
-
- if (++count > 20)
- goto no_ports;
-
- gst_element_set_state (udpsrc0, GST_STATE_NULL);
- gst_object_unref (udpsrc0);
-
- gst_element_set_state (udpsrc1, GST_STATE_NULL);
- gst_object_unref (udpsrc1);
-
- tmp_rtp += 2;
- goto again;
- }
-
- /* all fine, do port check */
- g_object_get (G_OBJECT (udpsrc0), "port", &rtpport, NULL);
- g_object_get (G_OBJECT (udpsrc1), "port", &rtcpport, NULL);
-
- /* this should not happen... */
- if (rtpport != tmp_rtp || rtcpport != tmp_rtcp)
- goto port_error;
-
- name = g_strdup_printf ("udp://%s:%d", stream->destination, stream->client_trans->client_port.min);
- udpsink0 = gst_element_make_from_uri (GST_URI_SINK, name, NULL);
- g_free (name);
-
- if (!udpsink0)
- goto no_udp_protocol;
-
- g_object_get (G_OBJECT (udpsrc0), "sock", &sockfd, NULL);
- g_object_set (G_OBJECT (udpsink0), "sockfd", sockfd, NULL);
- g_object_set (G_OBJECT (udpsink0), "closefd", FALSE, NULL);
-
- name = g_strdup_printf ("udp://%s:%d", stream->destination, stream->client_trans->client_port.max);
- udpsink1 = gst_element_make_from_uri (GST_URI_SINK, name, NULL);
- g_free (name);
-
- if (!udpsink1)
- goto no_udp_protocol;
-
- g_object_get (G_OBJECT (udpsrc1), "sock", &sockfd, NULL);
- g_object_set (G_OBJECT (udpsink1), "sockfd", sockfd, NULL);
- g_object_set (G_OBJECT (udpsink1), "closefd", FALSE, NULL);
- g_object_set (G_OBJECT (udpsink1), "sync", FALSE, NULL);
- g_object_set (G_OBJECT (udpsink1), "async", FALSE, NULL);
-
-
- /* we keep these elements, we configure all in configure_transport when the
- * server told us to really use the UDP ports. */
- stream->udpsrc[0] = gst_object_ref (udpsrc0);
- stream->udpsrc[1] = gst_object_ref (udpsrc1);
- stream->udpsink[0] = gst_object_ref (udpsink0);
- stream->udpsink[1] = gst_object_ref (udpsink1);
- stream->server_trans->server_port.min = rtpport;
- stream->server_trans->server_port.max = rtcpport;
-
- /* they are ours now */
- gst_object_sink (udpsrc0);
- gst_object_sink (udpsrc1);
- gst_object_sink (udpsink0);
- gst_object_sink (udpsink1);
-
- return TRUE;
-
- /* ERRORS */
-no_udp_protocol:
- {
- goto cleanup;
- }
-no_ports:
- {
- goto cleanup;
- }
-no_udp_rtcp_protocol:
- {
- goto cleanup;
- }
-port_error:
- {
- goto cleanup;
- }
-cleanup:
- {
- if (udpsrc0) {
- gst_element_set_state (udpsrc0, GST_STATE_NULL);
- gst_object_unref (udpsrc0);
- }
- if (udpsrc1) {
- gst_element_set_state (udpsrc1, GST_STATE_NULL);
- gst_object_unref (udpsrc1);
- }
- if (udpsink0) {
- gst_element_set_state (udpsink0, GST_STATE_NULL);
- gst_object_unref (udpsink0);
- }
- if (udpsink1) {
- gst_element_set_state (udpsink1, GST_STATE_NULL);
- gst_object_unref (udpsink1);
- }
- return FALSE;
- }
-}
-
-
/**
* gst_rtsp_session_stream_init_udp:
* @stream: a #GstRTSPSessionStream
* @ct: a client #GstRTSPTransport
*
* Set @ct as the client transport and create and return a matching server
- * transport. After this call the needed ports and elements will be created and
- * initialized.
+ * transport.
*
* Returns: a server transport or NULL if something went wrong.
*/
GstRTSPTransport *
gst_rtsp_session_stream_set_transport (GstRTSPSessionStream *stream,
- const gchar *destination, GstRTSPTransport *ct)
+ GstRTSPTransport *ct)
{
GstRTSPTransport *st;
- GstPad *pad;
- gchar *name;
GstRTSPSessionMedia *media;
media = stream->media;
st->client_port = ct->client_port;
/* keep track of the transports */
- g_free (stream->destination);
- stream->destination = g_strdup (destination);
if (stream->client_trans)
gst_rtsp_transport_free (stream->client_trans);
stream->client_trans = ct;
- if (stream->server_trans)
- gst_rtsp_transport_free (stream->server_trans);
- stream->server_trans = st;
- alloc_udp_ports (stream);
-
- gst_bin_add (GST_BIN (media->pipeline), stream->udpsink[0]);
- gst_bin_add (GST_BIN (media->pipeline), stream->udpsink[1]);
- gst_bin_add (GST_BIN (media->pipeline), stream->udpsrc[1]);
-
- /* hook up the stream to the RTP session elements. */
- name = g_strdup_printf ("send_rtp_sink_%d", stream->idx);
- stream->send_rtp_sink = gst_element_get_request_pad (media->rtpbin, name);
- g_free (name);
- name = g_strdup_printf ("send_rtp_src_%d", stream->idx);
- stream->send_rtp_src = gst_element_get_static_pad (media->rtpbin, name);
- g_free (name);
- name = g_strdup_printf ("send_rtcp_src_%d", stream->idx);
- stream->send_rtcp_src = gst_element_get_request_pad (media->rtpbin, name);
- g_free (name);
- name = g_strdup_printf ("recv_rtcp_sink_%d", stream->idx);
- stream->recv_rtcp_sink = gst_element_get_request_pad (media->rtpbin, name);
- g_free (name);
-
- gst_pad_link (stream->media_stream->srcpad, stream->send_rtp_sink);
- pad = gst_element_get_static_pad (stream->udpsink[0], "sink");
- gst_pad_link (stream->send_rtp_src, pad);
- gst_object_unref (pad);
- pad = gst_element_get_static_pad (stream->udpsink[1], "sink");
- gst_pad_link (stream->send_rtcp_src, pad);
- gst_object_unref (pad);
- pad = gst_element_get_static_pad (stream->udpsrc[1], "src");
- gst_pad_link (pad, stream->recv_rtcp_sink);
- gst_object_unref (pad);
+ st->server_port.min = stream->media_stream->server_port.min;
+ st->server_port.max = stream->media_stream->server_port.max;
+ stream->server_trans = st;
return st;
}
+
/**
* gst_rtsp_session_media_play:
* @media: a #GstRTSPSessionMedia
gst_rtsp_session_media_play (GstRTSPSessionMedia *media)
{
GstStateChangeReturn ret;
+ GstRTSPSessionStream *stream;
+ GList *walk;
+
+ for (walk = media->streams; walk; walk = g_list_next (walk)) {
+ stream = (GstRTSPSessionStream *) walk->data;
- ret = gst_element_set_state (media->pipeline, GST_STATE_PLAYING);
+ gst_rtsp_media_stream_add (stream->media_stream, stream->client_trans);
+ }
+ ret = gst_rtsp_media_play (media->media);
return ret;
}
{
GstStateChangeReturn ret;
- ret = gst_element_set_state (media->pipeline, GST_STATE_PAUSED);
+ ret = gst_rtsp_media_pause (media->media);
return ret;
}
{
GstStateChangeReturn ret;
- ret = gst_element_set_state (media->pipeline, GST_STATE_NULL);
+ ret = gst_rtsp_media_stop (media->media);
return ret;
}
#include <gst/rtsp/gstrtsptransport.h>
#include "rtsp-media.h"
-#include "rtsp-media-factory.h"
#ifndef __GST_RTSP_SESSION_H__
#define __GST_RTSP_SESSION_H__
GstRTSPMediaStream *media_stream;
/* client and server transports */
- gchar *destination;
GstRTSPTransport *client_trans;
GstRTSPTransport *server_trans;
-
- /* pads on the rtpbin */
- GstPad *recv_rtcp_sink;
- GstPad *send_rtp_sink;
- GstPad *send_rtp_src;
- GstPad *send_rtcp_src;
-
- /* sinks used for sending and receiving RTP and RTCP, they share sockets */
- GstElement *udpsrc[2];
- GstElement *udpsink[2];
};
/**
* GstRTSPSessionMedia:
*
- * State of a client session regarding a specific media. The media is identified
- * with the media factory. The media is typically composed of multiple streams,
- * such as an audio and video stream.
+ * State of a client session regarding a specific media.
*/
struct _GstRTSPSessionMedia
{
- /* the owner session */
- GstRTSPSession *session;
-
- /* the media we are handling */
- GstRTSPMediaFactory *factory;
+ /* the url of the media */
+ GstRTSPUrl *url;
/* the pipeline for the media */
- GstElement *pipeline;
GstRTSPMedia *media;
- /* RTP session manager */
- GstElement *rtpbin;
-
- /* for TCP transport */
- GstElement *fdsink;
-
/* configuration for the different streams */
GList *streams;
};
*
* Session information kept by the server for a specific client.
* One client session, identified with a session id, can handle multiple medias
- * identified with the media factory.
+ * identified with the media object.
*/
struct _GstRTSPSession {
GObject parent;
GstRTSPSession * gst_rtsp_session_new (const gchar *sessionid);
-GstRTSPSessionMedia * gst_rtsp_session_get_media (GstRTSPSession *sess, const GstRTSPUrl *url,
- GstRTSPMediaFactory *factory);
+GstRTSPSessionMedia * gst_rtsp_session_manage_media (GstRTSPSession *sess,
+ const GstRTSPUrl *uri,
+ GstRTSPMedia *media);
+GstRTSPSessionMedia * gst_rtsp_session_get_media (GstRTSPSession *sess,
+ const GstRTSPUrl *uri);
GstStateChangeReturn gst_rtsp_session_media_play (GstRTSPSessionMedia *media);
GstStateChangeReturn gst_rtsp_session_media_pause (GstRTSPSessionMedia *media);
guint idx);
GstRTSPTransport * gst_rtsp_session_stream_set_transport (GstRTSPSessionStream *stream,
- const gchar *destination,
GstRTSPTransport *ct);
G_END_DECLS