mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-28 03:00:35 +00:00
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:
parent
82a684e1b9
commit
41dd6399a6
17 changed files with 1140 additions and 667 deletions
|
@ -1,13 +1,9 @@
|
|||
|
||||
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
|
||||
|
||||
|
|
77
examples/test-mp4.c
Normal file
77
examples/test-mp4.c
Normal 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
77
examples/test-ogg.c
Normal 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;
|
||||
}
|
|
@ -47,10 +47,11 @@ main (int argc, char *argv[])
|
|||
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 "
|
||||
"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);
|
||||
|
|
@ -4,6 +4,7 @@ public_headers = \
|
|||
rtsp-media.h \
|
||||
rtsp-media-factory.h \
|
||||
rtsp-media-mapping.h \
|
||||
rtsp-sdp.h \
|
||||
rtsp-session-pool.h \
|
||||
rtsp-session.h
|
||||
|
||||
|
@ -13,6 +14,7 @@ c_sources = \
|
|||
rtsp-media.c \
|
||||
rtsp-media-factory.c \
|
||||
rtsp-media-mapping.c \
|
||||
rtsp-sdp.c \
|
||||
rtsp-session-pool.c \
|
||||
rtsp-session.c
|
||||
|
||||
|
|
|
@ -19,9 +19,8 @@
|
|||
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <gst/sdp/gstsdpmessage.h>
|
||||
|
||||
#include "rtsp-client.h"
|
||||
#include "rtsp-sdp.h"
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
|
@ -65,6 +64,16 @@ gst_rtsp_client_new (void)
|
|||
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)
|
||||
|
@ -74,36 +83,101 @@ handle_generic_response (GstRTSPClient *client, GstRTSPStatusCode code,
|
|||
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
|
||||
|
@ -114,19 +188,14 @@ handle_teardown_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage
|
|||
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:
|
||||
|
@ -139,24 +208,16 @@ 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;
|
||||
|
||||
|
@ -167,17 +228,12 @@ handle_pause_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *r
|
|||
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;
|
||||
}
|
||||
|
@ -191,48 +247,25 @@ not_found:
|
|||
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;
|
||||
|
@ -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);
|
||||
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);
|
||||
|
@ -268,14 +301,9 @@ handle_play_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *re
|
|||
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_SERVICE_UNAVAILABLE, request);
|
||||
/* error was sent */
|
||||
return FALSE;
|
||||
}
|
||||
not_found:
|
||||
|
@ -321,16 +349,10 @@ handle_setup_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *r
|
|||
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);
|
||||
|
@ -348,9 +370,11 @@ handle_setup_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *r
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
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);
|
||||
|
@ -401,11 +433,12 @@ handle_setup_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage *r
|
|||
|
||||
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;
|
||||
|
||||
|
@ -420,14 +453,25 @@ not_found:
|
|||
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:
|
||||
|
@ -444,13 +488,9 @@ handle_describe_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage
|
|||
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. */
|
||||
|
@ -465,193 +505,42 @@ handle_describe_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
@ -661,7 +550,7 @@ handle_options_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage
|
|||
{
|
||||
GstRTSPMessage response = { 0 };
|
||||
GstRTSPMethod options;
|
||||
GString *str;
|
||||
gchar *str;
|
||||
|
||||
options = GST_RTSP_DESCRIBE |
|
||||
GST_RTSP_OPTIONS |
|
||||
|
@ -670,38 +559,42 @@ handle_options_response (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPMessage
|
|||
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
|
||||
|
@ -740,6 +633,9 @@ handle_client (GstRTSPClient *client)
|
|||
continue;
|
||||
}
|
||||
|
||||
/* sanitize the uri */
|
||||
santize_uri (uri);
|
||||
|
||||
/* now see what is asked and dispatch to a dedicated handler */
|
||||
switch (method) {
|
||||
case GST_RTSP_OPTIONS:
|
||||
|
@ -845,11 +741,11 @@ gst_rtsp_client_set_session_pool (GstRTSPClient *client, GstRTSPSessionPool *poo
|
|||
{
|
||||
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);
|
||||
}
|
||||
|
@ -868,7 +764,7 @@ gst_rtsp_client_get_session_pool (GstRTSPClient *client)
|
|||
{
|
||||
GstRTSPSessionPool *result;
|
||||
|
||||
if ((result = client->pool))
|
||||
if ((result = client->session_pool))
|
||||
g_object_ref (result);
|
||||
|
||||
return result;
|
||||
|
@ -888,12 +784,12 @@ gst_rtsp_client_set_media_mapping (GstRTSPClient *client, GstRTSPMediaMapping *m
|
|||
{
|
||||
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);
|
||||
}
|
||||
|
@ -912,13 +808,12 @@ gst_rtsp_client_get_media_mapping (GstRTSPClient *client)
|
|||
{
|
||||
GstRTSPMediaMapping *result;
|
||||
|
||||
if ((result = client->mapping))
|
||||
if ((result = client->media_mapping))
|
||||
g_object_ref (result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gst_rtsp_client_attach:
|
||||
* @client: a #GstRTSPClient
|
||||
|
|
|
@ -73,10 +73,8 @@ struct _GstRTSPClient {
|
|||
struct sockaddr_in address;
|
||||
GThread *thread;
|
||||
|
||||
GstRTSPSessionPool *pool;
|
||||
|
||||
GstRTSPMediaFactory *factory;
|
||||
GstRTSPMediaMapping *mapping;
|
||||
GstRTSPSessionPool *session_pool;
|
||||
GstRTSPMediaMapping *media_mapping;
|
||||
};
|
||||
|
||||
struct _GstRTSPClientClass {
|
||||
|
|
|
@ -210,15 +210,6 @@ gst_rtsp_media_factory_construct (GstRTSPMediaFactory *factory, const GstRTSPUrl
|
|||
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)
|
||||
{
|
||||
|
@ -276,12 +267,13 @@ default_construct (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;
|
||||
|
||||
|
@ -297,7 +289,6 @@ default_construct (GstRTSPMediaFactory *factory, const GstRTSPUrl *url)
|
|||
/* create the stream */
|
||||
stream = g_new0 (GstRTSPMediaStream, 1);
|
||||
stream->media = media;
|
||||
stream->element = element;
|
||||
stream->payloader = pay;
|
||||
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 */
|
||||
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;
|
||||
|
|
|
@ -56,22 +56,27 @@ struct _GstRTSPMediaFactory {
|
|||
|
||||
/**
|
||||
* 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);
|
||||
|
|
|
@ -64,6 +64,26 @@ gst_rtsp_media_finalize (GObject * 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:
|
||||
* @media: a #GstRTSPMedia
|
||||
|
@ -102,3 +122,418 @@ gst_rtsp_media_get_stream (GstRTSPMedia *media, guint idx)
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,10 +42,18 @@ typedef struct _GstRTSPMediaClass GstRTSPMediaClass;
|
|||
/**
|
||||
* 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
|
||||
*
|
||||
|
@ -54,18 +62,38 @@ typedef struct _GstRTSPMediaClass GstRTSPMediaClass;
|
|||
struct _GstRTSPMediaStream {
|
||||
GstRTSPMedia *media;
|
||||
|
||||
guint idx;
|
||||
guint idx;
|
||||
|
||||
GstElement *element;
|
||||
GstPad *srcpad;
|
||||
GstElement *payloader;
|
||||
gulong caps_sig;
|
||||
GstCaps *caps;
|
||||
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;
|
||||
|
||||
/* 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.
|
||||
|
@ -77,6 +105,16 @@ struct _GstRTSPMedia {
|
|||
|
||||
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 {
|
||||
|
@ -85,10 +123,24 @@ 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__ */
|
||||
|
|
145
gst/rtsp-server/rtsp-sdp.c
Normal file
145
gst/rtsp-server/rtsp-sdp.c
Normal 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;
|
||||
}
|
35
gst/rtsp-server/rtsp-sdp.h
Normal file
35
gst/rtsp-server/rtsp-sdp.h
Normal 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__ */
|
|
@ -30,8 +30,8 @@ enum
|
|||
PROP_0,
|
||||
PROP_BACKLOG,
|
||||
PROP_PORT,
|
||||
PROP_POOL,
|
||||
PROP_MAPPING,
|
||||
PROP_SESSION_POOL,
|
||||
PROP_MEDIA_MAPPING,
|
||||
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",
|
||||
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;
|
||||
|
@ -105,8 +107,8 @@ gst_rtsp_server_init (GstRTSPServer * server)
|
|||
{
|
||||
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 ();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -207,12 +209,12 @@ gst_rtsp_server_set_session_pool (GstRTSPServer *server, GstRTSPSessionPool *poo
|
|||
|
||||
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);
|
||||
}
|
||||
|
@ -235,7 +237,7 @@ gst_rtsp_server_get_session_pool (GstRTSPServer *server)
|
|||
|
||||
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;
|
||||
|
@ -255,12 +257,12 @@ gst_rtsp_server_set_media_mapping (GstRTSPServer *server, GstRTSPMediaMapping *m
|
|||
|
||||
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);
|
||||
}
|
||||
|
@ -283,7 +285,7 @@ gst_rtsp_server_get_media_mapping (GstRTSPServer *server)
|
|||
|
||||
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;
|
||||
|
@ -302,10 +304,10 @@ gst_rtsp_server_get_property (GObject *object, guint propid,
|
|||
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:
|
||||
|
@ -326,10 +328,10 @@ gst_rtsp_server_set_property (GObject *object, guint propid,
|
|||
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:
|
||||
|
@ -444,10 +446,10 @@ gst_rtsp_server_accept_client (GstRTSPServer *server, GIOChannel *channel)
|
|||
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
|
||||
|
|
|
@ -58,22 +58,21 @@ struct _GstRTSPServer {
|
|||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#include <string.h>
|
||||
|
||||
#include "rtsp-session.h"
|
||||
|
||||
|
@ -45,12 +46,6 @@ gst_rtsp_session_free_stream (GstRTSPSessionStream *stream)
|
|||
{
|
||||
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);
|
||||
}
|
||||
|
@ -60,18 +55,11 @@ gst_rtsp_session_free_media (GstRTSPSessionMedia *media)
|
|||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -95,61 +83,63 @@ gst_rtsp_session_finalize (GObject * 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:
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -205,198 +195,21 @@ gst_rtsp_session_new (const gchar *sessionid)
|
|||
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;
|
||||
|
@ -410,49 +223,18 @@ gst_rtsp_session_stream_set_transport (GstRTSPSessionStream *stream,
|
|||
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);
|
||||
|
||||
st->server_port.min = stream->media_stream->server_port.min;
|
||||
st->server_port.max = stream->media_stream->server_port.max;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gst_rtsp_session_media_play:
|
||||
* @media: a #GstRTSPSessionMedia
|
||||
|
@ -465,8 +247,15 @@ GstStateChangeReturn
|
|||
gst_rtsp_session_media_play (GstRTSPSessionMedia *media)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
@ -484,7 +273,7 @@ gst_rtsp_session_media_pause (GstRTSPSessionMedia *media)
|
|||
{
|
||||
GstStateChangeReturn ret;
|
||||
|
||||
ret = gst_element_set_state (media->pipeline, GST_STATE_PAUSED);
|
||||
ret = gst_rtsp_media_pause (media->media);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -503,7 +292,7 @@ gst_rtsp_session_media_stop (GstRTSPSessionMedia *media)
|
|||
{
|
||||
GstStateChangeReturn ret;
|
||||
|
||||
ret = gst_element_set_state (media->pipeline, GST_STATE_NULL);
|
||||
ret = gst_rtsp_media_stop (media->media);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include <gst/rtsp/gstrtsptransport.h>
|
||||
|
||||
#include "rtsp-media.h"
|
||||
#include "rtsp-media-factory.h"
|
||||
|
||||
#ifndef __GST_RTSP_SESSION_H__
|
||||
#define __GST_RTSP_SESSION_H__
|
||||
|
@ -60,46 +59,23 @@ struct _GstRTSPSessionStream
|
|||
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;
|
||||
};
|
||||
|
@ -109,7 +85,7 @@ struct _GstRTSPSessionMedia
|
|||
*
|
||||
* 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;
|
||||
|
@ -127,8 +103,11 @@ GType gst_rtsp_session_get_type (void);
|
|||
|
||||
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);
|
||||
|
@ -138,7 +117,6 @@ GstRTSPSessionStream * gst_rtsp_session_media_get_stream (GstRTSPSessionMedi
|
|||
guint idx);
|
||||
|
||||
GstRTSPTransport * gst_rtsp_session_stream_set_transport (GstRTSPSessionStream *stream,
|
||||
const gchar *destination,
|
||||
GstRTSPTransport *ct);
|
||||
|
||||
G_END_DECLS
|
||||
|
|
Loading…
Reference in a new issue