Reorganize things, prepare for media sharing

Added various other test server examples
Move the SDP message generation to a separate helper.
Refactor common code for finding the session.
Add content-base for realplayer compatibility
Clean up request uris before processing for better vlc compatibility.
Move prerolling and pipeline construction to the RTSPMedia object.
Use multiudpsink for future pipeline reuse.
This commit is contained in:
Wim Taymans 2009-01-29 13:31:27 +01:00
parent 82a684e1b9
commit 41dd6399a6
17 changed files with 1140 additions and 667 deletions

View file

@ -1,13 +1,9 @@
noinst_PROGRAMS = test-video test-ogg test-mp4
noinst_PROGRAMS = gst-rtsp-server
INCLUDES = -I$(top_srcdir) -I$(srcdir) INCLUDES = -I$(top_srcdir) -I$(srcdir)
gst_rtsp_server_SOURCES = \ AM_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
main.c AM_LDFLAGS = \
gst_rtsp_server_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
gst_rtsp_server_LDFLAGS = \
$(GST_LIBS) \ $(GST_LIBS) \
$(top_builddir)/gst/rtsp-server/libgstrtspserver-@GST_MAJORMINOR@.la $(top_builddir)/gst/rtsp-server/libgstrtspserver-@GST_MAJORMINOR@.la

77
examples/test-mp4.c Normal file
View file

@ -0,0 +1,77 @@
/* 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;
}

77
examples/test-ogg.c Normal file
View file

@ -0,0 +1,77 @@
/* 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;
}

View file

@ -47,10 +47,11 @@ main (int argc, char *argv[])
factory = gst_rtsp_media_factory_new (); factory = gst_rtsp_media_factory_new ();
gst_rtsp_media_factory_set_launch (factory, "( " gst_rtsp_media_factory_set_launch (factory, "( "
"videotestsrc ! video/x-raw-yuv,width=352,height=288,framerate=15/1 ! " "videotestsrc ! video/x-raw-yuv,width=352,height=288,framerate=15/1 ! "
"x264enc bitrate=300 ! rtph264pay name=pay0 pt=96 " "ffenc_h263 ! rtph263pay name=pay0 pt=96 "
"audiotestsrc ! audio/x-raw-int,rate=8000 ! " "audiotestsrc ! audio/x-raw-int,rate=8000 ! "
"alawenc ! rtppcmapay name=pay1 pt=97 " "alawenc ! rtppcmapay name=pay1 pt=97 "
")"); ")");
/* attach the test factory to the /test url */ /* attach the test factory to the /test url */
gst_rtsp_media_mapping_add_factory (mapping, "/test", factory); gst_rtsp_media_mapping_add_factory (mapping, "/test", factory);

View file

@ -4,6 +4,7 @@ public_headers = \
rtsp-media.h \ rtsp-media.h \
rtsp-media-factory.h \ rtsp-media-factory.h \
rtsp-media-mapping.h \ rtsp-media-mapping.h \
rtsp-sdp.h \
rtsp-session-pool.h \ rtsp-session-pool.h \
rtsp-session.h rtsp-session.h
@ -13,6 +14,7 @@ c_sources = \
rtsp-media.c \ rtsp-media.c \
rtsp-media-factory.c \ rtsp-media-factory.c \
rtsp-media-mapping.c \ rtsp-media-mapping.c \
rtsp-sdp.c \
rtsp-session-pool.c \ rtsp-session-pool.c \
rtsp-session.c rtsp-session.c

View file

@ -19,9 +19,8 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <gst/sdp/gstsdpmessage.h>
#include "rtsp-client.h" #include "rtsp-client.h"
#include "rtsp-sdp.h"
#undef DEBUG #undef DEBUG
@ -65,6 +64,16 @@ gst_rtsp_client_new (void)
return result; 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 static void
handle_generic_response (GstRTSPClient *client, GstRTSPStatusCode code, handle_generic_response (GstRTSPClient *client, GstRTSPStatusCode code,
GstRTSPMessage *request) GstRTSPMessage *request)
@ -74,36 +83,101 @@ handle_generic_response (GstRTSPClient *client, GstRTSPStatusCode code,
gst_rtsp_message_init_response (&response, code, gst_rtsp_message_init_response (&response, code,
gst_rtsp_status_as_text (code), request); gst_rtsp_status_as_text (code), request);
gst_rtsp_connection_send (client->connection, &response, NULL); handle_response (client, &response);
} }
static gboolean static GstRTSPMedia *
handle_teardown_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *request) 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; GstRTSPResult res;
GstRTSPSessionMedia *media;
GstRTSPSession *session; GstRTSPSession *session;
gchar *sessid; gchar *sessid;
GstRTSPMessage response = { 0 };
GstRTSPStatusCode code;
res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0); res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
if (res == GST_RTSP_OK) { if (res == GST_RTSP_OK) {
/* we had a session in the request, find it again */ /* 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; goto session_not_found;
} }
else else
goto service_unavailable; 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 */ /* 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) if (!media)
goto not_found; goto not_found;
gst_rtsp_session_media_stop (media); 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); g_object_unref (session);
/* remove the session id from the request, which will also remove it from the /* remove the session id from the request, which will also remove it from the
@ -114,19 +188,14 @@ handle_teardown_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage
code = GST_RTSP_STS_OK; code = GST_RTSP_STS_OK;
gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), 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);
return FALSE; return FALSE;
/* ERRORS */ /* ERRORS */
session_not_found: no_session:
{ {
handle_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request); /* error was sent already */
return FALSE;
}
service_unavailable:
{
handle_generic_response (client, GST_RTSP_STS_OK, request);
return FALSE; return FALSE;
} }
not_found: not_found:
@ -139,24 +208,16 @@ not_found:
static gboolean static gboolean
handle_pause_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *request) handle_pause_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *request)
{ {
GstRTSPResult res;
GstRTSPSessionMedia *media; GstRTSPSessionMedia *media;
GstRTSPSession *session; GstRTSPSession *session;
gchar *sessid;
GstRTSPMessage response = { 0 }; GstRTSPMessage response = { 0 };
GstRTSPStatusCode code; GstRTSPStatusCode code;
res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0); if (!(session = ensure_session (client, request)))
if (res == GST_RTSP_OK) { goto no_session;
/* 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;
/* get a handle to the configuration of the media in the 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) if (!media)
goto not_found; goto not_found;
@ -167,17 +228,12 @@ handle_pause_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *r
code = GST_RTSP_STS_OK; code = GST_RTSP_STS_OK;
gst_rtsp_message_init_response (&response, code, gst_rtsp_status_as_text (code), 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);
return FALSE; return FALSE;
/* ERRORS */ /* ERRORS */
session_not_found: no_session:
{
handle_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
return FALSE;
}
service_unavailable:
{ {
return FALSE; return FALSE;
} }
@ -191,48 +247,25 @@ not_found:
static gboolean static gboolean
handle_play_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *request) handle_play_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *request)
{ {
GstRTSPResult res;
GstRTSPSessionMedia *media; GstRTSPSessionMedia *media;
GstRTSPSession *session; GstRTSPSession *session;
gchar *sessid;
GstRTSPMessage response = { 0 }; GstRTSPMessage response = { 0 };
GstRTSPStatusCode code; GstRTSPStatusCode code;
GstStateChangeReturn ret;
GString *rtpinfo; GString *rtpinfo;
guint n_streams, i; guint n_streams, i;
guint timestamp, seqnum; guint timestamp, seqnum;
res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0); if (!(session = ensure_session (client, request)))
if (res == GST_RTSP_OK) { goto no_session;
/* 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;
/* get a handle to the configuration of the media in the 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) if (!media)
goto not_found; 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 */ /* grab RTPInfo from the payloaders now */
rtpinfo = g_string_new (""); rtpinfo = g_string_new ("");
n_streams = gst_rtsp_media_n_streams (media->media); n_streams = gst_rtsp_media_n_streams (media->media);
for (i = 0; i < n_streams; i++) { for (i = 0; i < n_streams; i++) {
GstRTSPMediaStream *stream; GstRTSPMediaStream *stream;
@ -259,7 +292,7 @@ handle_play_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *re
gst_rtsp_message_add_header (&response, GST_RTSP_HDR_RTP_INFO, rtpinfo->str); gst_rtsp_message_add_header (&response, GST_RTSP_HDR_RTP_INFO, rtpinfo->str);
g_string_free (rtpinfo, TRUE); g_string_free (rtpinfo, TRUE);
gst_rtsp_connection_send (client->connection, &response, NULL); handle_response (client, &response);
/* start playing after sending the request */ /* start playing after sending the request */
gst_rtsp_session_media_play (media); gst_rtsp_session_media_play (media);
@ -268,14 +301,9 @@ handle_play_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *re
return FALSE; return FALSE;
/* ERRORS */ /* ERRORS */
session_not_found: no_session:
{ {
handle_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request); /* error was sent */
return FALSE;
}
service_unavailable:
{
handle_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
return FALSE; return FALSE;
} }
not_found: not_found:
@ -321,16 +349,10 @@ handle_setup_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *r
if (sscanf (pos, "%u", &streamid) != 1) if (sscanf (pos, "%u", &streamid) != 1)
goto bad_request; 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 */ /* parse the transport */
res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_TRANSPORT, &transport, 0); res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_TRANSPORT, &transport, 0);
if (res != GST_RTSP_OK) if (res != GST_RTSP_OK)
goto unsupported_transports; goto no_transport;
transports = g_strsplit (transport, ",", 0); transports = g_strsplit (transport, ",", 0);
gst_rtsp_transport_new (&ct); gst_rtsp_transport_new (&ct);
@ -348,9 +370,11 @@ handle_setup_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *r
} }
g_strfreev (transports); 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 */ /* we have not found anything usable, error out */
if (!have_transport) { if (!have_transport) {
gst_rtsp_transport_free (ct);
goto unsupported_transports; goto unsupported_transports;
} }
@ -369,28 +393,36 @@ handle_setup_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *r
res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0); res = gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &sessid, 0);
if (res == GST_RTSP_OK) { if (res == GST_RTSP_OK) {
/* we had a session in the request, find it again */ /* 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; goto session_not_found;
need_session = FALSE; need_session = FALSE;
} }
else { else {
/* create a session if this fails we probably reached our session limit or /* create a session if this fails we probably reached our session limit or
* something. */ * something. */
if (!(session = gst_rtsp_session_pool_create (client->pool))) if (!(session = gst_rtsp_session_pool_create (client->session_pool)))
goto service_unavailable; goto service_unavailable;
need_session = TRUE; 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 */ /* get a handle to the configuration of the media in the session */
media = gst_rtsp_session_get_media (session, uri, client->factory); if (!(media = gst_rtsp_session_get_media (session, uri)))
if (!media)
goto not_found; goto not_found;
/* get a handle to the stream in the media */ /* 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 */ /* 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 */ /* serialize the server transport */
trans_str = gst_rtsp_transport_as_text (st); trans_str = gst_rtsp_transport_as_text (st);
@ -401,11 +433,12 @@ handle_setup_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *r
if (need_session) if (need_session)
gst_rtsp_message_add_header (&response, GST_RTSP_HDR_SESSION, session->sessionid); gst_rtsp_message_add_header (&response, GST_RTSP_HDR_SESSION, session->sessionid);
gst_rtsp_message_add_header (&response, GST_RTSP_HDR_TRANSPORT, trans_str); gst_rtsp_message_add_header (&response, GST_RTSP_HDR_TRANSPORT, trans_str);
g_free (trans_str); g_free (trans_str);
g_object_unref (session); g_object_unref (session);
gst_rtsp_connection_send (client->connection, &response, NULL); handle_response (client, &response);
return TRUE; return TRUE;
@ -420,14 +453,25 @@ not_found:
handle_generic_response (client, GST_RTSP_STS_NOT_FOUND, request); handle_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
return FALSE; return FALSE;
} }
no_stream:
{
handle_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
return FALSE;
}
session_not_found: session_not_found:
{ {
handle_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request); handle_generic_response (client, GST_RTSP_STS_SESSION_NOT_FOUND, request);
return FALSE; return FALSE;
} }
no_transport:
{
handle_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request);
return FALSE;
}
unsupported_transports: unsupported_transports:
{ {
handle_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request); handle_generic_response (client, GST_RTSP_STS_UNSUPPORTED_TRANSPORT, request);
gst_rtsp_transport_free (ct);
return FALSE; return FALSE;
} }
service_unavailable: service_unavailable:
@ -444,13 +488,9 @@ handle_describe_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage
GstRTSPMessage response = { 0 }; GstRTSPMessage response = { 0 };
GstRTSPResult res; GstRTSPResult res;
GstSDPMessage *sdp; GstSDPMessage *sdp;
guint n_streams, i; guint i;
gchar *sdptext; gchar *str;
GstRTSPMediaFactory *factory;
GstRTSPMedia *media; GstRTSPMedia *media;
GstElement *pipeline;
GstStateChangeReturn ret;
/* check what kind of format is accepted, we don't really do anything with it /* check what kind of format is accepted, we don't really do anything with it
* and always return SDP for now. */ * and always return SDP for now. */
@ -465,193 +505,42 @@ handle_describe_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage
break; break;
} }
/* find the factory for the uri first */ /* find the media object for the uri */
if (!(factory = gst_rtsp_media_mapping_find_factory (client->mapping, uri))) if (!(media = find_media (client, uri, request)))
goto no_factory;
/* prepare the media and add it to the pipeline */
if (!(media = gst_rtsp_media_factory_construct (factory, uri)))
goto no_media; goto no_media;
/* create a pipeline to preroll the media */ /* create an SDP for the media object */
pipeline = gst_pipeline_new ("client-describe-pipeline"); if (!(sdp = gst_rtsp_sdp_from_media (media)))
goto no_sdp;
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;
gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK,
gst_rtsp_status_as_text (GST_RTSP_STS_OK), request); gst_rtsp_status_as_text (GST_RTSP_STS_OK), request);
gst_rtsp_message_add_header (&response, GST_RTSP_HDR_CONTENT_TYPE, "application/sdp"); 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 */ /* add SDP to the response body */
sdptext = gst_sdp_message_as_text (sdp); str = gst_sdp_message_as_text (sdp);
gst_rtsp_message_take_body (&response, (guint8 *)sdptext, strlen (sdptext)); gst_rtsp_message_take_body (&response, (guint8 *)str, strlen (str));
gst_sdp_message_free (sdp); gst_sdp_message_free (sdp);
gst_rtsp_connection_send (client->connection, &response, NULL); handle_response (client, &response);
return TRUE; return TRUE;
/* ERRORS */ /* ERRORS */
no_factory:
{
handle_generic_response (client, GST_RTSP_STS_NOT_FOUND, request);
return FALSE;
}
no_media: no_media:
{ {
handle_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request); /* error reply is already sent */
g_object_unref (factory);
return FALSE; return FALSE;
} }
cant_play: no_sdp:
{ {
handle_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request); handle_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request);
gst_object_unref (pipeline); g_object_unref (media);
g_object_unref (factory);
return FALSE; return FALSE;
} }
} }
@ -661,7 +550,7 @@ handle_options_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage
{ {
GstRTSPMessage response = { 0 }; GstRTSPMessage response = { 0 };
GstRTSPMethod options; GstRTSPMethod options;
GString *str; gchar *str;
options = GST_RTSP_DESCRIBE | options = GST_RTSP_DESCRIBE |
GST_RTSP_OPTIONS | GST_RTSP_OPTIONS |
@ -670,38 +559,42 @@ handle_options_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage
GST_RTSP_SETUP | GST_RTSP_SETUP |
GST_RTSP_TEARDOWN; GST_RTSP_TEARDOWN;
/* always return options.. */ str = gst_rtsp_options_as_text (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");
gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK, gst_rtsp_message_init_response (&response, GST_RTSP_STS_OK,
gst_rtsp_status_as_text (GST_RTSP_STS_OK), request); 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 /* this function runs in a client specific thread and handles all rtsp messages
@ -740,6 +633,9 @@ handle_client (GstRTSPClient *client)
continue; continue;
} }
/* sanitize the uri */
santize_uri (uri);
/* now see what is asked and dispatch to a dedicated handler */ /* now see what is asked and dispatch to a dedicated handler */
switch (method) { switch (method) {
case GST_RTSP_OPTIONS: case GST_RTSP_OPTIONS:
@ -845,11 +741,11 @@ gst_rtsp_client_set_session_pool (GstRTSPClient *client, GstRTSPSessionPool *poo
{ {
GstRTSPSessionPool *old; GstRTSPSessionPool *old;
old = client->pool; old = client->session_pool;
if (old != pool) { if (old != pool) {
if (pool) if (pool)
g_object_ref (pool); g_object_ref (pool);
client->pool = pool; client->session_pool = pool;
if (old) if (old)
g_object_unref (old); g_object_unref (old);
} }
@ -868,7 +764,7 @@ gst_rtsp_client_get_session_pool (GstRTSPClient *client)
{ {
GstRTSPSessionPool *result; GstRTSPSessionPool *result;
if ((result = client->pool)) if ((result = client->session_pool))
g_object_ref (result); g_object_ref (result);
return result; return result;
@ -888,12 +784,12 @@ gst_rtsp_client_set_media_mapping (GstRTSPClient *client, GstRTSPMediaMapping *m
{ {
GstRTSPMediaMapping *old; GstRTSPMediaMapping *old;
old = client->mapping; old = client->media_mapping;
if (old != mapping) { if (old != mapping) {
if (mapping) if (mapping)
g_object_ref (mapping); g_object_ref (mapping);
client->mapping = mapping; client->media_mapping = mapping;
if (old) if (old)
g_object_unref (old); g_object_unref (old);
} }
@ -912,13 +808,12 @@ gst_rtsp_client_get_media_mapping (GstRTSPClient *client)
{ {
GstRTSPMediaMapping *result; GstRTSPMediaMapping *result;
if ((result = client->mapping)) if ((result = client->media_mapping))
g_object_ref (result); g_object_ref (result);
return result; return result;
} }
/** /**
* gst_rtsp_client_attach: * gst_rtsp_client_attach:
* @client: a #GstRTSPClient * @client: a #GstRTSPClient

View file

@ -73,10 +73,8 @@ struct _GstRTSPClient {
struct sockaddr_in address; struct sockaddr_in address;
GThread *thread; GThread *thread;
GstRTSPSessionPool *pool; GstRTSPSessionPool *session_pool;
GstRTSPMediaMapping *media_mapping;
GstRTSPMediaFactory *factory;
GstRTSPMediaMapping *mapping;
}; };
struct _GstRTSPClientClass { struct _GstRTSPClientClass {

View file

@ -210,15 +210,6 @@ gst_rtsp_media_factory_construct (GstRTSPMediaFactory *factory, const GstRTSPUrl
return res; 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 * static GstElement *
default_get_element (GstRTSPMediaFactory *factory, const GstRTSPUrl *url) default_get_element (GstRTSPMediaFactory *factory, const GstRTSPUrl *url)
{ {
@ -276,12 +267,13 @@ default_construct (GstRTSPMediaFactory *factory, const GstRTSPUrl *url)
if (element == NULL) if (element == NULL)
goto no_element; 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; media->element = element;
/* try to find all the payloader elements, they should be named 'pay%d'. for /* 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 * each of the payloaders we will create a stream and collect the source pad.
* add a notify::caps on the pad. */ */
for (i = 0; ; i++) { for (i = 0; ; i++) {
gchar *name; gchar *name;
@ -297,7 +289,6 @@ default_construct (GstRTSPMediaFactory *factory, const GstRTSPUrl *url)
/* create the stream */ /* create the stream */
stream = g_new0 (GstRTSPMediaStream, 1); stream = g_new0 (GstRTSPMediaStream, 1);
stream->media = media; stream->media = media;
stream->element = element;
stream->payloader = pay; stream->payloader = pay;
stream->idx = media->streams->len; stream->idx = media->streams->len;
@ -305,16 +296,12 @@ default_construct (GstRTSPMediaFactory *factory, const GstRTSPUrl *url)
/* ghost the pad of the payloader to the element */ /* ghost the pad of the payloader to the element */
stream->srcpad = gst_ghost_pad_new (name, pad); stream->srcpad = gst_ghost_pad_new (name, pad);
gst_element_add_pad (stream->element, stream->srcpad); gst_element_add_pad (media->element, stream->srcpad);
gst_object_unref (pay);
stream->caps_sig = g_signal_connect (pad, "notify::caps", (GCallback) caps_notify, stream); g_free (name);
gst_object_unref (pad);
/* add stream now */ /* add stream now */
g_array_append_val (media->streams, stream); g_array_append_val (media->streams, stream);
gst_object_unref (pay);
g_free (name);
} }
return media; return media;

View file

@ -56,22 +56,27 @@ struct _GstRTSPMediaFactory {
/** /**
* GstRTSPMediaFactoryClass: * 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 * @construct: the vmethod that will be called when the factory has to create the
* #GstRTSPMedia for @url. The default implementation of this * #GstRTSPMedia for @url. The default implementation of this
* function calls get_element to retrieve an element and then looks for * function calls get_element to retrieve an element and then looks for
* pay%d to create the streams. * 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. * the #GstRTSPMediaFactory class structure.
*/ */
struct _GstRTSPMediaFactoryClass { struct _GstRTSPMediaFactoryClass {
GObjectClass parent_class; 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); GType gst_rtsp_media_factory_get_type (void);

View file

@ -64,6 +64,26 @@ gst_rtsp_media_finalize (GObject * obj)
G_OBJECT_CLASS (gst_rtsp_media_parent_class)->finalize (obj); 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: * gst_rtsp_media_n_streams:
* @media: a #GstRTSPMedia * @media: a #GstRTSPMedia
@ -102,3 +122,418 @@ gst_rtsp_media_get_stream (GstRTSPMedia *media, guint idx)
return res; 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;
}

View file

@ -42,10 +42,18 @@ typedef struct _GstRTSPMediaClass GstRTSPMediaClass;
/** /**
* GstRTSPMediaStream: * GstRTSPMediaStream:
* *
* @media: the owner #GstRTSPMedia
* @idx: the stream index * @idx: the stream index
* @element: the toplevel element
* @srcpad: the srcpad of the stream * @srcpad: the srcpad of the stream
* @payloader: the payloader of the format * @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_sig: the signal id for detecting caps
* @caps: the caps of the stream * @caps: the caps of the stream
* *
@ -54,18 +62,38 @@ typedef struct _GstRTSPMediaClass GstRTSPMediaClass;
struct _GstRTSPMediaStream { struct _GstRTSPMediaStream {
GstRTSPMedia *media; GstRTSPMedia *media;
guint idx; guint idx;
GstElement *element; GstPad *srcpad;
GstPad *srcpad; GstElement *payloader;
GstElement *payloader; gboolean prepared;
gulong caps_sig;
GstCaps *caps; GstRTSPRange server_port;
/* 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];
/* the caps of the stream */
gulong caps_sig;
GstCaps *caps;
}; };
/** /**
* GstRTSPMedia: * 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 * A class that contains the GStreamer element along with a list of
* #GstRTSPediaStream objects that can produce data. * #GstRTSPediaStream objects that can produce data.
@ -77,6 +105,16 @@ struct _GstRTSPMedia {
GstElement *element; GstElement *element;
GArray *streams; GArray *streams;
gboolean prepared;
/* the pipeline for the media */
GstElement *pipeline;
/* RTP session manager */
GstElement *rtpbin;
/* for TCP transport */
GstElement *multifdsink;
}; };
struct _GstRTSPMediaClass { struct _GstRTSPMediaClass {
@ -85,10 +123,24 @@ struct _GstRTSPMediaClass {
GType gst_rtsp_media_get_type (void); GType gst_rtsp_media_get_type (void);
/* creating the media */
GstRTSPMedia * gst_rtsp_media_new (void);
/* dealing with the media */ /* dealing with the media */
guint gst_rtsp_media_n_streams (GstRTSPMedia *media); guint gst_rtsp_media_n_streams (GstRTSPMedia *media);
GstRTSPMediaStream * gst_rtsp_media_get_stream (GstRTSPMedia *media, guint idx); 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 G_END_DECLS
#endif /* __GST_RTSP_MEDIA_H__ */ #endif /* __GST_RTSP_MEDIA_H__ */

145
gst/rtsp-server/rtsp-sdp.c Normal file
View file

@ -0,0 +1,145 @@
/* 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;
}

View file

@ -0,0 +1,35 @@
/* 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__ */

View file

@ -30,8 +30,8 @@ enum
PROP_0, PROP_0,
PROP_BACKLOG, PROP_BACKLOG,
PROP_PORT, PROP_PORT,
PROP_POOL, PROP_SESSION_POOL,
PROP_MAPPING, PROP_MEDIA_MAPPING,
PROP_LAST PROP_LAST
}; };
@ -78,23 +78,25 @@ gst_rtsp_server_class_init (GstRTSPServerClass * klass)
g_param_spec_int ("port", "Port", "The port the server uses to listen on", 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)); 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 * 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 but sessions can be shared between servers by setting the same
* session pool on multiple servers. * session pool on multiple servers.
*/ */
g_object_class_install_property (gobject_class, PROP_POOL, g_object_class_install_property (gobject_class, PROP_SESSION_POOL,
g_param_spec_object ("pool", "Pool", "The session pool to use for client session", 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)); 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 * The media mapping to use for this server. By default the server has no
* media mapping and thus cannot map urls to media streams. * media mapping and thus cannot map urls to media streams.
*/ */
g_object_class_install_property (gobject_class, PROP_MAPPING, g_object_class_install_property (gobject_class, PROP_MEDIA_MAPPING,
g_param_spec_object ("mapping", "Mapping", "The media mapping to use for client session", 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)); GST_TYPE_RTSP_MEDIA_MAPPING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
klass->accept_client = gst_rtsp_server_accept_client; klass->accept_client = gst_rtsp_server_accept_client;
@ -105,8 +107,8 @@ gst_rtsp_server_init (GstRTSPServer * server)
{ {
server->server_port = DEFAULT_PORT; server->server_port = DEFAULT_PORT;
server->backlog = DEFAULT_BACKLOG; server->backlog = DEFAULT_BACKLOG;
server->pool = gst_rtsp_session_pool_new (); server->session_pool = gst_rtsp_session_pool_new ();
server->mapping = gst_rtsp_media_mapping_new (); server->media_mapping = gst_rtsp_media_mapping_new ();
} }
/** /**
@ -207,12 +209,12 @@ gst_rtsp_server_set_session_pool (GstRTSPServer *server, GstRTSPSessionPool *poo
g_return_if_fail (GST_IS_RTSP_SERVER (server)); g_return_if_fail (GST_IS_RTSP_SERVER (server));
old = server->pool; old = server->session_pool;
if (old != pool) { if (old != pool) {
if (pool) if (pool)
g_object_ref (pool); g_object_ref (pool);
server->pool = pool; server->session_pool = pool;
if (old) if (old)
g_object_unref (old); g_object_unref (old);
} }
@ -235,7 +237,7 @@ gst_rtsp_server_get_session_pool (GstRTSPServer *server)
g_return_val_if_fail (GST_IS_RTSP_SERVER (server), NULL); g_return_val_if_fail (GST_IS_RTSP_SERVER (server), NULL);
if ((result = server->pool)) if ((result = server->session_pool))
g_object_ref (result); g_object_ref (result);
return result; return result;
@ -255,12 +257,12 @@ gst_rtsp_server_set_media_mapping (GstRTSPServer *server, GstRTSPMediaMapping *m
g_return_if_fail (GST_IS_RTSP_SERVER (server)); g_return_if_fail (GST_IS_RTSP_SERVER (server));
old = server->mapping; old = server->media_mapping;
if (old != mapping) { if (old != mapping) {
if (mapping) if (mapping)
g_object_ref (mapping); g_object_ref (mapping);
server->mapping = mapping; server->media_mapping = mapping;
if (old) if (old)
g_object_unref (old); g_object_unref (old);
} }
@ -283,7 +285,7 @@ gst_rtsp_server_get_media_mapping (GstRTSPServer *server)
g_return_val_if_fail (GST_IS_RTSP_SERVER (server), NULL); g_return_val_if_fail (GST_IS_RTSP_SERVER (server), NULL);
if ((result = server->mapping)) if ((result = server->media_mapping))
g_object_ref (result); g_object_ref (result);
return result; return result;
@ -302,10 +304,10 @@ gst_rtsp_server_get_property (GObject *object, guint propid,
case PROP_BACKLOG: case PROP_BACKLOG:
g_value_set_int (value, gst_rtsp_server_get_backlog (server)); g_value_set_int (value, gst_rtsp_server_get_backlog (server));
break; break;
case PROP_POOL: case PROP_SESSION_POOL:
g_value_take_object (value, gst_rtsp_server_get_session_pool (server)); g_value_take_object (value, gst_rtsp_server_get_session_pool (server));
break; break;
case PROP_MAPPING: case PROP_MEDIA_MAPPING:
g_value_take_object (value, gst_rtsp_server_get_media_mapping (server)); g_value_take_object (value, gst_rtsp_server_get_media_mapping (server));
break; break;
default: default:
@ -326,10 +328,10 @@ gst_rtsp_server_set_property (GObject *object, guint propid,
case PROP_BACKLOG: case PROP_BACKLOG:
gst_rtsp_server_set_backlog (server, g_value_get_int (value)); gst_rtsp_server_set_backlog (server, g_value_get_int (value));
break; break;
case PROP_POOL: case PROP_SESSION_POOL:
gst_rtsp_server_set_session_pool (server, g_value_get_object (value)); gst_rtsp_server_set_session_pool (server, g_value_get_object (value));
break; break;
case PROP_MAPPING: case PROP_MEDIA_MAPPING:
gst_rtsp_server_set_media_mapping (server, g_value_get_object (value)); gst_rtsp_server_set_media_mapping (server, g_value_get_object (value));
break; break;
default: default:
@ -444,10 +446,10 @@ gst_rtsp_server_accept_client (GstRTSPServer *server, GIOChannel *channel)
client = gst_rtsp_client_new (); client = gst_rtsp_client_new ();
/* set the session pool that this client should use */ /* 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 */ /* 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 /* accept connections for that client, this function returns after accepting
* the connection and will run the remainder of the communication with the * the connection and will run the remainder of the communication with the

View file

@ -58,22 +58,21 @@ struct _GstRTSPServer {
GObject parent; GObject parent;
/* server information */ /* server information */
gint server_port; gint server_port;
gint backlog; gint backlog;
gchar *host; gchar *host;
struct sockaddr_in server_sin; struct sockaddr_in server_sin;
/* socket */ /* socket and channels */
GstPollFD server_sock; GstPollFD server_sock;
GIOChannel *io_channel;
GIOChannel *io_channel; GSource *io_watch;
GSource *io_watch;
/* sessions on this server */ /* sessions on this server */
GstRTSPSessionPool *pool; GstRTSPSessionPool *session_pool;
/* media mapper for this server */ /* media mapper for this server */
GstRTSPMediaMapping *mapping; GstRTSPMediaMapping *media_mapping;
}; };
/** /**

View file

@ -16,6 +16,7 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA. * Boston, MA 02111-1307, USA.
*/ */
#include <string.h>
#include "rtsp-session.h" #include "rtsp-session.h"
@ -45,12 +46,6 @@ gst_rtsp_session_free_stream (GstRTSPSessionStream *stream)
{ {
if (stream->client_trans) if (stream->client_trans)
gst_rtsp_transport_free (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); g_free (stream);
} }
@ -60,18 +55,11 @@ gst_rtsp_session_free_media (GstRTSPSessionMedia *media)
{ {
GList *walk; 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)) { for (walk = media->streams; walk; walk = g_list_next (walk)) {
GstRTSPSessionStream *stream = (GstRTSPSessionStream *) walk->data; GstRTSPSessionStream *stream = (GstRTSPSessionStream *) walk->data;
gst_rtsp_session_free_stream (stream); gst_rtsp_session_free_stream (stream);
} }
if (media->pipeline)
gst_object_unref (media->pipeline);
g_list_free (media->streams); g_list_free (media->streams);
} }
@ -95,61 +83,63 @@ gst_rtsp_session_finalize (GObject * obj)
G_OBJECT_CLASS (gst_rtsp_session_parent_class)->finalize (obj); 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: * gst_rtsp_session_get_media:
* @sess: a #GstRTSPSession * @sess: a #GstRTSPSession
* @url: the url for the media * @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 * 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; GstRTSPSessionMedia *result;
GList *walk; GList *walk;
g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
g_return_val_if_fail (url != NULL, NULL);
result = NULL; result = NULL;
for (walk = sess->medias; walk; walk = g_list_next (walk)) { for (walk = sess->medias; walk; walk = g_list_next (walk)) {
result = (GstRTSPSessionMedia *) walk->data; result = (GstRTSPSessionMedia *) walk->data;
if (result->factory == factory) if (strcmp (result->url->abspath, url->abspath) == 0)
break; break;
result = NULL; 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; return result;
/* ERRORS */
no_media:
{
gst_rtsp_session_free_media (result);
return NULL;
}
} }
/** /**
@ -205,198 +195,21 @@ gst_rtsp_session_new (const gchar *sessionid)
return result; 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: * gst_rtsp_session_stream_init_udp:
* @stream: a #GstRTSPSessionStream * @stream: a #GstRTSPSessionStream
* @ct: a client #GstRTSPTransport * @ct: a client #GstRTSPTransport
* *
* Set @ct as the client transport and create and return a matching server * 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 * transport.
* initialized.
* *
* Returns: a server transport or NULL if something went wrong. * Returns: a server transport or NULL if something went wrong.
*/ */
GstRTSPTransport * GstRTSPTransport *
gst_rtsp_session_stream_set_transport (GstRTSPSessionStream *stream, gst_rtsp_session_stream_set_transport (GstRTSPSessionStream *stream,
const gchar *destination, GstRTSPTransport *ct) GstRTSPTransport *ct)
{ {
GstRTSPTransport *st; GstRTSPTransport *st;
GstPad *pad;
gchar *name;
GstRTSPSessionMedia *media; GstRTSPSessionMedia *media;
media = stream->media; media = stream->media;
@ -410,49 +223,18 @@ gst_rtsp_session_stream_set_transport (GstRTSPSessionStream *stream,
st->client_port = ct->client_port; st->client_port = ct->client_port;
/* keep track of the transports */ /* keep track of the transports */
g_free (stream->destination);
stream->destination = g_strdup (destination);
if (stream->client_trans) if (stream->client_trans)
gst_rtsp_transport_free (stream->client_trans); gst_rtsp_transport_free (stream->client_trans);
stream->client_trans = ct; stream->client_trans = ct;
if (stream->server_trans)
gst_rtsp_transport_free (stream->server_trans); st->server_port.min = stream->media_stream->server_port.min;
st->server_port.max = stream->media_stream->server_port.max;
stream->server_trans = st; 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);
return st; return st;
} }
/** /**
* gst_rtsp_session_media_play: * gst_rtsp_session_media_play:
* @media: a #GstRTSPSessionMedia * @media: a #GstRTSPSessionMedia
@ -465,8 +247,15 @@ GstStateChangeReturn
gst_rtsp_session_media_play (GstRTSPSessionMedia *media) gst_rtsp_session_media_play (GstRTSPSessionMedia *media)
{ {
GstStateChangeReturn ret; GstStateChangeReturn ret;
GstRTSPSessionStream *stream;
GList *walk;
ret = gst_element_set_state (media->pipeline, GST_STATE_PLAYING); for (walk = media->streams; walk; walk = g_list_next (walk)) {
stream = (GstRTSPSessionStream *) walk->data;
gst_rtsp_media_stream_add (stream->media_stream, stream->client_trans);
}
ret = gst_rtsp_media_play (media->media);
return ret; return ret;
} }
@ -484,7 +273,7 @@ gst_rtsp_session_media_pause (GstRTSPSessionMedia *media)
{ {
GstStateChangeReturn ret; GstStateChangeReturn ret;
ret = gst_element_set_state (media->pipeline, GST_STATE_PAUSED); ret = gst_rtsp_media_pause (media->media);
return ret; return ret;
} }
@ -503,7 +292,7 @@ gst_rtsp_session_media_stop (GstRTSPSessionMedia *media)
{ {
GstStateChangeReturn ret; GstStateChangeReturn ret;
ret = gst_element_set_state (media->pipeline, GST_STATE_NULL); ret = gst_rtsp_media_stop (media->media);
return ret; return ret;
} }

View file

@ -22,7 +22,6 @@
#include <gst/rtsp/gstrtsptransport.h> #include <gst/rtsp/gstrtsptransport.h>
#include "rtsp-media.h" #include "rtsp-media.h"
#include "rtsp-media-factory.h"
#ifndef __GST_RTSP_SESSION_H__ #ifndef __GST_RTSP_SESSION_H__
#define __GST_RTSP_SESSION_H__ #define __GST_RTSP_SESSION_H__
@ -60,46 +59,23 @@ struct _GstRTSPSessionStream
GstRTSPMediaStream *media_stream; GstRTSPMediaStream *media_stream;
/* client and server transports */ /* client and server transports */
gchar *destination;
GstRTSPTransport *client_trans; GstRTSPTransport *client_trans;
GstRTSPTransport *server_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: * GstRTSPSessionMedia:
* *
* State of a client session regarding a specific media. The media is identified * State of a client session regarding a specific media.
* with the media factory. The media is typically composed of multiple streams,
* such as an audio and video stream.
*/ */
struct _GstRTSPSessionMedia struct _GstRTSPSessionMedia
{ {
/* the owner session */ /* the url of the media */
GstRTSPSession *session; GstRTSPUrl *url;
/* the media we are handling */
GstRTSPMediaFactory *factory;
/* the pipeline for the media */ /* the pipeline for the media */
GstElement *pipeline;
GstRTSPMedia *media; GstRTSPMedia *media;
/* RTP session manager */
GstElement *rtpbin;
/* for TCP transport */
GstElement *fdsink;
/* configuration for the different streams */ /* configuration for the different streams */
GList *streams; GList *streams;
}; };
@ -109,7 +85,7 @@ struct _GstRTSPSessionMedia
* *
* Session information kept by the server for a specific client. * Session information kept by the server for a specific client.
* One client session, identified with a session id, can handle multiple medias * One client session, identified with a session id, can handle multiple medias
* identified with the media factory. * identified with the media object.
*/ */
struct _GstRTSPSession { struct _GstRTSPSession {
GObject parent; GObject parent;
@ -127,8 +103,11 @@ GType gst_rtsp_session_get_type (void);
GstRTSPSession * gst_rtsp_session_new (const gchar *sessionid); GstRTSPSession * gst_rtsp_session_new (const gchar *sessionid);
GstRTSPSessionMedia * gst_rtsp_session_get_media (GstRTSPSession *sess, const GstRTSPUrl *url, GstRTSPSessionMedia * gst_rtsp_session_manage_media (GstRTSPSession *sess,
GstRTSPMediaFactory *factory); 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_play (GstRTSPSessionMedia *media);
GstStateChangeReturn gst_rtsp_session_media_pause (GstRTSPSessionMedia *media); GstStateChangeReturn gst_rtsp_session_media_pause (GstRTSPSessionMedia *media);
@ -138,7 +117,6 @@ GstRTSPSessionStream * gst_rtsp_session_media_get_stream (GstRTSPSessionMedi
guint idx); guint idx);
GstRTSPTransport * gst_rtsp_session_stream_set_transport (GstRTSPSessionStream *stream, GstRTSPTransport * gst_rtsp_session_stream_set_transport (GstRTSPSessionStream *stream,
const gchar *destination,
GstRTSPTransport *ct); GstRTSPTransport *ct);
G_END_DECLS G_END_DECLS