From 4b1c190a5fa5dc0b633072da5c9182b90afb300f Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 22 Jan 2009 15:33:29 +0100 Subject: [PATCH 1/1] Make the server handle arbitrary pipelines Make GstMediaFactory an object that can instantiate GstMediaBin objects. The GstMediaBin object has a handle to a bin with elements and to a list of GstMediaStream objects that this bin produces. Add GstMediaMapper that can map url mountpoints to GstMediaFactory objects along with methods to register and remove those mappings. Add methods and a property to GstRTSPServer to manage the GstMediaMapper object used by the server instance. Modify the example application so that it shows how to create custom pipelines attached to a specific mount point. Various misc cleanps. --- examples/main.c | 34 +++++ gst/rtsp-server/Makefile.am | 4 +- gst/rtsp-server/rtsp-client.c | 188 +++++++++++++++++--------- gst/rtsp-server/rtsp-client.h | 13 +- gst/rtsp-server/rtsp-media-factory.c | 250 ++++++++++++++++++++++++++++++++--- gst/rtsp-server/rtsp-media-factory.h | 34 +++-- gst/rtsp-server/rtsp-media-mapping.c | 146 ++++++++++++++++++++ gst/rtsp-server/rtsp-media-mapping.h | 76 +++++++++++ gst/rtsp-server/rtsp-media.c | 211 ++++------------------------- gst/rtsp-server/rtsp-media.h | 50 ++++--- gst/rtsp-server/rtsp-server.c | 75 ++++++----- gst/rtsp-server/rtsp-server.h | 10 +- gst/rtsp-server/rtsp-session.c | 30 +++-- gst/rtsp-server/rtsp-session.h | 13 +- 14 files changed, 756 insertions(+), 378 deletions(-) create mode 100644 gst/rtsp-server/rtsp-media-mapping.c create mode 100644 gst/rtsp-server/rtsp-media-mapping.h diff --git a/examples/main.c b/examples/main.c index 41a1cb9..07b28cd 100644 --- a/examples/main.c +++ b/examples/main.c @@ -26,6 +26,8 @@ main (int argc, char *argv[]) { GMainLoop *loop; GstRTSPServer *server; + GstRTSPMediaMapping *mapping; + GstRTSPMediaFactory *factory; gst_init (&argc, &argv); @@ -34,9 +36,41 @@ main (int argc, char *argv[]) /* create a server instance */ server = gst_rtsp_server_new (); + /* get the mapping for this server, every server has a default mapper object + * that be used to map uri mount points to media factories */ + mapping = gst_rtsp_server_get_media_mapping (server); + + /* make a media factory for a webcam. The default media factory can use + * gst-launch syntax to create pipelines. */ + factory = gst_rtsp_media_factory_new (); +#if 0 + gst_rtsp_media_factory_set_launch (factory, "( " + "v4l2src ! video/x-raw-yuv,width=352,height=288,framerate=15/1 ! " + "queue ! videorate ! ffmpegcolorspace ! " + "x264enc bitrate=300 ! rtph264pay name=pay0 pt=96 " + "alsasrc ! audio/x-raw-int,rate=8000 ! queue ! " + "amrnbenc ! rtpamrpay name=pay1 pt=97 " + ")"); +#endif + /* any launch line works as long as it contains elements named pay%d. Each + * element with pay%d names will be a stream */ + gst_rtsp_media_factory_set_launch (factory, "( " + "videotestsrc ! video/x-raw-yuv,width=352,height=288,framerate=15/1 ! " + "x264enc bitrate=300 ! rtph264pay name=pay0 pt=96 " + "audiotestsrc ! audio/x-raw-int,rate=8000 ! " + "alawenc ! rtppcmapay name=pay1 pt=97 " + ")"); + + /* attach the factory to the /camera url */ + gst_rtsp_media_mapping_add_factory (mapping, "/camera", 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; diff --git a/gst/rtsp-server/Makefile.am b/gst/rtsp-server/Makefile.am index 873619b..54ba251 100644 --- a/gst/rtsp-server/Makefile.am +++ b/gst/rtsp-server/Makefile.am @@ -3,6 +3,7 @@ public_headers = \ rtsp-client.h \ rtsp-media.h \ rtsp-media-factory.h \ + rtsp-media-mapping.h \ rtsp-session-pool.h \ rtsp-session.h @@ -10,7 +11,8 @@ c_sources = \ rtsp-server.c \ rtsp-client.c \ rtsp-media.c \ - rtsp-media-factory.c \ + rtsp-media-factory.c \ + rtsp-media-mapping.c \ rtsp-session-pool.c \ rtsp-session.c diff --git a/gst/rtsp-server/rtsp-client.c b/gst/rtsp-server/rtsp-client.c index b884941..a3372da 100644 --- a/gst/rtsp-server/rtsp-client.c +++ b/gst/rtsp-server/rtsp-client.c @@ -25,6 +25,8 @@ #undef DEBUG +static void gst_rtsp_client_finalize (GObject * obj); + G_DEFINE_TYPE (GstRTSPClient, gst_rtsp_client, G_TYPE_OBJECT); static void @@ -33,6 +35,8 @@ gst_rtsp_client_class_init (GstRTSPClientClass * klass) GObjectClass *gobject_class; gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = gst_rtsp_client_finalize; } static void @@ -40,6 +44,12 @@ gst_rtsp_client_init (GstRTSPClient * client) { } +static void +gst_rtsp_client_finalize (GObject * obj) +{ + G_OBJECT_CLASS (gst_rtsp_client_parent_class)->finalize (obj); +} + /** * gst_rtsp_client_new: * @@ -87,7 +97,7 @@ handle_teardown_response (GstRTSPClient *client, const gchar *uri, GstRTSPMessag goto service_unavailable; /* get a handle to the configuration of the media in the session */ - media = gst_rtsp_session_get_media (session, client->media); + media = gst_rtsp_session_get_media (session, uri, client->factory); if (!media) goto not_found; @@ -116,6 +126,7 @@ session_not_found: } service_unavailable: { + handle_generic_response (client, GST_RTSP_STS_OK, request); return FALSE; } not_found: @@ -145,7 +156,7 @@ handle_pause_response (GstRTSPClient *client, const gchar *uri, GstRTSPMessage * goto service_unavailable; /* get a handle to the configuration of the media in the session */ - media = gst_rtsp_session_get_media (session, client->media); + media = gst_rtsp_session_get_media (session, uri, client->factory); if (!media) goto not_found; @@ -201,7 +212,7 @@ handle_play_response (GstRTSPClient *client, const gchar *uri, GstRTSPMessage *r goto service_unavailable; /* get a handle to the configuration of the media in the session */ - media = gst_rtsp_session_get_media (session, client->media); + media = gst_rtsp_session_get_media (session, uri, client->factory); if (!media) goto not_found; @@ -222,11 +233,11 @@ handle_play_response (GstRTSPClient *client, const gchar *uri, GstRTSPMessage *r /* grab RTPInfo from the payloaders now */ rtpinfo = g_string_new (""); - n_streams = gst_rtsp_media_n_streams (client->media); + n_streams = gst_rtsp_media_bin_n_streams (media->mediabin); for (i = 0; i < n_streams; i++) { GstRTSPMediaStream *stream; - stream = gst_rtsp_media_get_stream (client->media, i); + stream = gst_rtsp_media_bin_get_stream (media->mediabin, i); g_object_get (G_OBJECT (stream->payloader), "seqnum", &seqnum, NULL); g_object_get (G_OBJECT (stream->payloader), "timestamp", ×tamp, NULL); @@ -271,7 +282,7 @@ not_found: } static gboolean -handle_setup_response (GstRTSPClient *client, const gchar *uri, GstRTSPMessage *request) +handle_setup_response (GstRTSPClient *client, const gchar *location, GstRTSPMessage *request) { GstRTSPResult res; gchar *sessid; @@ -279,6 +290,7 @@ handle_setup_response (GstRTSPClient *client, const gchar *uri, GstRTSPMessage * gchar **transports; gboolean have_transport; GstRTSPTransport *ct, *st; + GstRTSPUrl *uri; GstRTSPSession *session; gint i; GstRTSPLowerTrans supported; @@ -290,9 +302,28 @@ handle_setup_response (GstRTSPClient *client, const gchar *uri, GstRTSPMessage * GstRTSPSessionMedia *media; gboolean need_session; + /* the uri contains the stream number we added in the SDP config, which is + * always /stream=%d so we need to strip that off */ + if ((res = gst_rtsp_url_parse (location, &uri)) != GST_RTSP_OK) + goto bad_url; + + /* parse the stream we need to configure, look for the stream in the abspath + * first and then in the query. */ + if (!(pos = strstr (uri->abspath, "/stream="))) { + if (!(pos = strstr (uri->query, "/stream="))) + goto bad_request; + } + + /* we can mofify the parse uri in place */ + *pos = '\0'; + + pos += strlen ("/stream="); + if (sscanf (pos, "%u", &streamid) != 1) + goto bad_request; + /* find the media associated with the uri */ - if (client->media == NULL) { - if ((client->media = gst_rtsp_media_new (uri)) == NULL) + if (client->factory == NULL) { + if ((client->factory = gst_rtsp_media_mapping_find_factory (client->mapping, uri)) == NULL) goto not_found; } @@ -351,20 +382,12 @@ handle_setup_response (GstRTSPClient *client, const gchar *uri, GstRTSPMessage * } /* get a handle to the configuration of the media in the session */ - media = gst_rtsp_session_get_media (session, client->media); + media = gst_rtsp_session_get_media (session, uri->abspath, client->factory); if (!media) goto not_found; - /* parse the stream we need to configure */ - if (!(pos = strstr (uri, "stream="))) - goto bad_request; - - pos += strlen ("stream="); - if (sscanf (pos, "%u", &streamid) != 1) - goto bad_request; - /* get a handle to the stream in the media */ - stream = gst_rtsp_session_get_stream (media, streamid); + stream = gst_rtsp_session_media_get_stream (media, streamid); /* setup the server transport from the client transport */ st = gst_rtsp_session_stream_set_transport (stream, inet_ntoa (client->address.sin_addr), ct); @@ -387,9 +410,9 @@ handle_setup_response (GstRTSPClient *client, const gchar *uri, GstRTSPMessage * return TRUE; /* ERRORS */ -not_found: +bad_url: { - handle_generic_response (client, GST_RTSP_STS_NOT_FOUND, request); + handle_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request); return FALSE; } bad_request: @@ -397,6 +420,11 @@ bad_request: handle_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request); return FALSE; } +not_found: + { + 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); @@ -414,46 +442,58 @@ service_unavailable: } } +/* for the describe we must generate an SDP */ static gboolean -handle_describe_response (GstRTSPClient *client, const gchar *uri, GstRTSPMessage *request) +handle_describe_response (GstRTSPClient *client, const gchar *location, GstRTSPMessage *request) { GstRTSPMessage response = { 0 }; + GstRTSPResult res; GstSDPMessage *sdp; + GstRTSPUrl *uri; guint n_streams, i; gchar *sdptext; - GstRTSPMedia *media; - GstElement *pipeline = NULL; + GstRTSPMediaFactory *factory; + GstRTSPMediaBin *mediabin; + GstElement *pipeline; - /* check what kind of format is accepted */ + /* the uri contains the stream number we added in the SDP config, which is + * always /stream=%d so we need to strip that off */ + if ((res = gst_rtsp_url_parse (location, &uri)) != GST_RTSP_OK) + goto bad_url; + /* find the factory for the uri first */ + if (!(factory = gst_rtsp_media_mapping_find_factory (client->mapping, uri))) + goto no_factory; - /* for the describe we must generate an SDP */ - if (!(media = gst_rtsp_media_new (uri))) - goto no_media; + /* check what kind of format is accepted */ - /* create a pipeline if we have to */ - if (pipeline == NULL) { - pipeline = gst_pipeline_new ("client-pipeline"); - } + /* create a pipeline to preroll the media */ + pipeline = gst_pipeline_new ("client-describe-pipeline"); - /* prepare the media into the pipeline */ - if (!gst_rtsp_media_prepare (media, GST_BIN (pipeline))) - goto no_media; + /* prepare the media and add it to the pipeline */ + if (!(mediabin = gst_rtsp_media_factory_construct (factory, uri->abspath))) + goto no_media_bin; + + gst_bin_add (GST_BIN_CAST (pipeline), mediabin->element); /* link fakesink to all stream pads and set the pipeline to PLAYING */ - n_streams = gst_rtsp_media_n_streams (media); + n_streams = gst_rtsp_media_bin_n_streams (mediabin); for (i = 0; i < n_streams; i++) { GstRTSPMediaStream *stream; GstElement *sink; GstPad *sinkpad; + GstPadLinkReturn lret; - stream = gst_rtsp_media_get_stream (media, i); + stream = gst_rtsp_media_bin_get_stream (mediabin, i); sink = gst_element_factory_make ("fakesink", NULL); gst_bin_add (GST_BIN (pipeline), sink); sinkpad = gst_element_get_static_pad (sink, "sink"); - gst_pad_link (stream->srcpad, sinkpad); + 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); } @@ -487,7 +527,7 @@ handle_describe_response (GstRTSPClient *client, const gchar *uri, GstRTSPMessag gboolean first; GString *fmtp; - stream = gst_rtsp_media_get_stream (media, i); + stream = gst_rtsp_media_bin_get_stream (mediabin, i); gst_sdp_media_new (&smedia); s = gst_caps_get_structure (stream->caps, 0); @@ -572,7 +612,7 @@ handle_describe_response (GstRTSPClient *client, const gchar *uri, GstRTSPMessag /* go back to NULL */ gst_element_set_state (pipeline, GST_STATE_NULL); - g_object_unref (media); + g_object_unref (factory); gst_object_unref (pipeline); pipeline = NULL; @@ -590,11 +630,22 @@ handle_describe_response (GstRTSPClient *client, const gchar *uri, GstRTSPMessag return TRUE; /* ERRORS */ -no_media: +bad_url: + { + handle_generic_response (client, GST_RTSP_STS_BAD_REQUEST, request); + return FALSE; + } +no_factory: { handle_generic_response (client, GST_RTSP_STS_NOT_FOUND, request); return FALSE; } +no_media_bin: + { + handle_generic_response (client, GST_RTSP_STS_SERVICE_UNAVAILABLE, request); + g_object_unref (factory); + return FALSE; + } } static void @@ -713,7 +764,8 @@ handle_client (GstRTSPClient *client) /* ERRORS */ receive_failed: { - g_print ("receive failed, disconnect client %p\n", client); + g_message ("receive failed %d (%s), disconnect client %p", res, + gst_rtsp_strresult (res), client); gst_rtsp_connection_close (client->connection); g_object_unref (client); return NULL; @@ -747,7 +799,7 @@ client_accept (GstRTSPClient *client, GIOChannel *channel) * connections */ gst_poll_add_fd (conn->fdset, &conn->fd); - g_print ("added new client %p ip %s with fd %d\n", client, + g_message ("added new client %p ip %s with fd %d", client, inet_ntoa (client->address.sin_addr), conn->fd.fd); client->connection = conn; @@ -769,7 +821,8 @@ accept_failed: * @pool: a #GstRTSPSessionPool * * Set @pool as the sessionpool for @client which it will use to find - * or allocate sessions. + * or allocate sessions. the sessionpool is usually inherited from the server + * that created the client but can be overridden later. */ void gst_rtsp_client_set_session_pool (GstRTSPClient *client, GstRTSPSessionPool *pool) @@ -777,11 +830,13 @@ gst_rtsp_client_set_session_pool (GstRTSPClient *client, GstRTSPSessionPool *poo GstRTSPSessionPool *old; old = client->pool; - if (pool) - g_object_ref (pool); - client->pool = pool; - if (old) - g_object_unref (old); + if (old != pool) { + if (pool) + g_object_ref (pool); + client->pool = pool; + if (old) + g_object_unref (old); + } } /** @@ -804,43 +859,44 @@ gst_rtsp_client_get_session_pool (GstRTSPClient *client) } /** - * gst_rtsp_client_set_media_factory: + * gst_rtsp_client_set_media_mapping: * @client: a #GstRTSPClient - * @factory: a #GstRTSPMediaFactory + * @mapping: a #GstRTSPMediaMapping * - * Set @factory as the media factory for @client which it will use to map urls - * to media streams. + * Set @mapping as the media mapping for @client which it will use to map urls + * to media streams. These mapping is usually inherited from the server that + * created the client but can be overriden later. */ void -gst_rtsp_client_set_media_factory (GstRTSPClient *client, GstRTSPMediaFactory *factory) +gst_rtsp_client_set_media_mapping (GstRTSPClient *client, GstRTSPMediaMapping *mapping) { - GstRTSPMediaFactory *old; + GstRTSPMediaMapping *old; - old = client->factory; + old = client->mapping; - if (old != factory) { - if (factory) - g_object_ref (factory); - client->factory = factory; + if (old != mapping) { + if (mapping) + g_object_ref (mapping); + client->mapping = mapping; if (old) g_object_unref (old); } } /** - * gst_rtsp_client_get_media_factory: + * gst_rtsp_client_get_media_mapping: * @client: a #GstRTSPClient * - * Get the #GstRTSPMediaFactory object that @client uses to manage its sessions. + * Get the #GstRTSPMediaMapping object that @client uses to manage its sessions. * - * Returns: a #GstRTSPMediaFactory, unref after usage. + * Returns: a #GstRTSPMediaMapping, unref after usage. */ -GstRTSPMediaFactory * -gst_rtsp_client_get_media_factory (GstRTSPClient *client) +GstRTSPMediaMapping * +gst_rtsp_client_get_media_mapping (GstRTSPClient *client) { - GstRTSPMediaFactory *result; + GstRTSPMediaMapping *result; - if ((result = client->factory)) + if ((result = client->mapping)) g_object_ref (result); return result; diff --git a/gst/rtsp-server/rtsp-client.h b/gst/rtsp-server/rtsp-client.h index 5dff20c..11a375c 100644 --- a/gst/rtsp-server/rtsp-client.h +++ b/gst/rtsp-server/rtsp-client.h @@ -38,7 +38,7 @@ #define __GST_RTSP_CLIENT_H__ #include "rtsp-media.h" -#include "rtsp-media-factory.h" +#include "rtsp-media-mapping.h" #include "rtsp-session-pool.h" G_BEGIN_DECLS @@ -75,9 +75,8 @@ struct _GstRTSPClient { GstRTSPSessionPool *pool; - GstRTSPMedia *media; - GstRTSPMediaFactory *factory; - + GstRTSPMediaFactory *factory; + GstRTSPMediaMapping *mapping; }; struct _GstRTSPClientClass { @@ -92,9 +91,9 @@ void gst_rtsp_client_set_session_pool (GstRTSPClient *client, GstRTSPSessionPool *pool); GstRTSPSessionPool * gst_rtsp_client_get_session_pool (GstRTSPClient *client); -void gst_rtsp_client_set_media_factory (GstRTSPClient *client, - GstRTSPMediaFactory *factory); -GstRTSPMediaFactory * gst_rtsp_client_get_media_factory (GstRTSPClient *client); +void gst_rtsp_client_set_media_mapping (GstRTSPClient *client, + GstRTSPMediaMapping *mapping); +GstRTSPMediaMapping * gst_rtsp_client_get_media_mapping (GstRTSPClient *client); gboolean gst_rtsp_client_accept (GstRTSPClient *client, GIOChannel *channel); diff --git a/gst/rtsp-server/rtsp-media-factory.c b/gst/rtsp-server/rtsp-media-factory.c index d402bad..1412d6a 100644 --- a/gst/rtsp-server/rtsp-media-factory.c +++ b/gst/rtsp-server/rtsp-media-factory.c @@ -19,11 +19,24 @@ #include "rtsp-media-factory.h" -G_DEFINE_TYPE (GstRTSPMediaFactory, gst_rtsp_media_factory, G_TYPE_OBJECT); +#define DEFAULT_LAUNCH NULL + +enum +{ + PROP_0, + PROP_LAUNCH, + PROP_LAST +}; +static void gst_rtsp_media_factory_get_property (GObject *object, guint propid, + GValue *value, GParamSpec *pspec); +static void gst_rtsp_media_factory_set_property (GObject *object, guint propid, + const GValue *value, GParamSpec *pspec); static void gst_rtsp_media_factory_finalize (GObject * obj); -static GstRTSPMedia * create_media (GstRTSPMediaFactory *factory, const gchar *url); +static GstRTSPMediaBin * default_construct (GstRTSPMediaFactory *factory, const gchar *location); + +G_DEFINE_TYPE (GstRTSPMediaFactory, gst_rtsp_media_factory, G_TYPE_OBJECT); static void gst_rtsp_media_factory_class_init (GstRTSPMediaFactoryClass * klass) @@ -32,9 +45,28 @@ gst_rtsp_media_factory_class_init (GstRTSPMediaFactoryClass * klass) gobject_class = G_OBJECT_CLASS (klass); + gobject_class->get_property = gst_rtsp_media_factory_get_property; + gobject_class->set_property = gst_rtsp_media_factory_set_property; gobject_class->finalize = gst_rtsp_media_factory_finalize; - klass->create_media = create_media; + /** + * GstRTSPMediaFactory::launch + * + * The gst_parse_launch() line to use for constructing the pipeline in the + * default prepare vmethod. + * + * The pipeline description should return a GstBin as the toplevel element + * which can be accomplished by enclosing the dscription with brackets '(' + * ')'. + * + * The description should return a pipeline with payloaders named pay0, pay1, + * etc.. Each of the payloaders will result in a stream. + */ + g_object_class_install_property (gobject_class, PROP_LAUNCH, + g_param_spec_string ("launch", "Launch", "A launch description of the pipeline", + DEFAULT_LAUNCH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + klass->construct = default_construct; } static void @@ -45,9 +77,51 @@ gst_rtsp_media_factory_init (GstRTSPMediaFactory * factory) static void gst_rtsp_media_factory_finalize (GObject * obj) { + GstRTSPMediaFactory *factory = GST_RTSP_MEDIA_FACTORY (obj); + + g_free (factory->launch); + G_OBJECT_CLASS (gst_rtsp_media_factory_parent_class)->finalize (obj); } +static void +gst_rtsp_media_factory_get_property (GObject *object, guint propid, + GValue *value, GParamSpec *pspec) +{ + GstRTSPMediaFactory *factory = GST_RTSP_MEDIA_FACTORY (object); + + switch (propid) { + case PROP_LAUNCH: + g_value_take_string (value, gst_rtsp_media_factory_get_launch (factory)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec); + } +} + +static void +gst_rtsp_media_factory_set_property (GObject *object, guint propid, + const GValue *value, GParamSpec *pspec) +{ + GstRTSPMediaFactory *factory = GST_RTSP_MEDIA_FACTORY (object); + + switch (propid) { + case PROP_LAUNCH: + gst_rtsp_media_factory_set_launch (factory, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec); + } +} + +/** + * gst_rtsp_media_factory_new: + * + * Create a new #GstRTSPMediaFactory instance. + * + * Returns: a new #GstRTSPMediaFactory object or %NULL when location did not contain a + * valid or understood URL. + */ GstRTSPMediaFactory * gst_rtsp_media_factory_new (void) { @@ -58,40 +132,172 @@ gst_rtsp_media_factory_new (void) return result; } -static GstRTSPMedia * -create_media (GstRTSPMediaFactory *factory, const gchar *url) +/** + * gst_rtsp_media_factory_set_launch: + * @factory: a #GstRTSPMediaFactory + * @launch: the launch description + * + * + * The gst_parse_launch() line to use for constructing the pipeline in the + * default prepare vmethod. + * + * The pipeline description should return a GstBin as the toplevel element + * which can be accomplished by enclosing the dscription with brackets '(' + * ')'. + * + * The description should return a pipeline with payloaders named pay0, pay1, + * etc.. Each of the payloaders will result in a stream. + */ +void +gst_rtsp_media_factory_set_launch (GstRTSPMediaFactory *factory, const gchar *launch) { - GstRTSPMedia *result; + g_return_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory)); + g_return_if_fail (launch != NULL); - result = gst_rtsp_media_new (url); + factory->launch = g_strdup (launch); +} + +/** + * gst_rtsp_media_factory_get_launch: + * @factory: a #GstRTSPMediaFactory + * + * Get the gst_parse_launch() pipeline description that will be used in the + * default prepare vmethod. + * + * Returns: the configured launch description. g_free() after usage. + */ +gchar * +gst_rtsp_media_factory_get_launch (GstRTSPMediaFactory *factory) +{ + gchar *result; + + result = g_strdup (factory->launch); return result; } -GstRTSPMedia * -gst_rtsp_media_factory_create (GstRTSPMediaFactory *factory, const gchar *url) +/** + * gst_rtsp_media_factory_construct: + * @factory: a #GstRTSPMediaFactory + * @location: the url used + * + * Prepare the media bin object and create its streams. Implementations + * should create the needed gstreamer elements and add them to the result + * object. No state changes should be performed on them yet. + * + * One or more GstRTSPMediaStream objects should be added to the result with + * the srcpad member set to a source pad that produces buffer of type + * application/x-rtp. + * + * Returns: a new #GstRTSPMediaBin if the media could be prepared. + */ +GstRTSPMediaBin * +gst_rtsp_media_factory_construct (GstRTSPMediaFactory *factory, const gchar *location) { - GstRTSPMedia *result = NULL; + GstRTSPMediaBin *res; GstRTSPMediaFactoryClass *klass; klass = GST_RTSP_MEDIA_FACTORY_GET_CLASS (factory); - if (klass->create_media) - result = klass->create_media (factory, url); + if (klass->construct) + res = klass->construct (factory, location); + else + res = NULL; - return result; + g_message ("constructed mediabin %p for location %s", res, location); + + return res; } -void -gst_rtsp_media_factory_add (GstRTSPMediaFactory *factory, const gchar *path, - GType type) +static void +caps_notify (GstPad * pad, GParamSpec * unused, GstRTSPMediaStream * stream) { - g_warning ("gst_rtsp_media_factory_add: not implemented"); + if (stream->caps) + gst_caps_unref (stream->caps); + if ((stream->caps = GST_PAD_CAPS (pad))) + gst_caps_ref (stream->caps); } -void -gst_rtsp_media_factory_remove (GstRTSPMediaFactory *factory, const gchar *path, - GType type) + +static GstRTSPMediaBin * +default_construct (GstRTSPMediaFactory *factory, const gchar *location) { - g_warning ("gst_rtsp_media_factory_remove: not implemented"); -} + GstRTSPMediaBin *bin; + GstRTSPMediaStream *stream; + GstElement *pay, *element; + GstPad * pad; + gint i; + GError *error = NULL; + /* we need a parse syntax */ + if (factory->launch == NULL) + goto no_launch; + + /* parse the user provided launch line */ + element = gst_parse_launch (factory->launch, &error); + if (element == NULL) + goto parse_error; + + if (error != NULL) { + /* a recoverable error was encountered */ + g_warning ("recoverable parsing error: %s", error->message); + g_error_free (error); + } + + bin = g_object_new (GST_TYPE_RTSP_MEDIA_BIN, NULL); + bin->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. */ + for (i = 0; ; i++) { + gchar *name; + + name = g_strdup_printf ("pay%d", i); + + if (!(pay = gst_bin_get_by_name (GST_BIN (element), name))) { + /* no more payloaders found, we have found all the streams and we can + * end the loop */ + g_free (name); + break; + } + + /* create the stream */ + stream = g_new0 (GstRTSPMediaStream, 1); + stream->mediabin = bin; + stream->element = element; + stream->payloader = pay; + stream->idx = bin->streams->len; + + pad = gst_element_get_static_pad (pay, "src"); + + /* 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); + + /* add stream now */ + g_array_append_val (bin->streams, stream); + gst_object_unref (pay); + + g_free (name); + } + + return bin; + + /* ERRORS */ +no_launch: + { + g_critical ("no launch line specified"); + return NULL; + } +parse_error: + { + g_critical ("could not parse launch syntax (%s): %s", factory->launch, + (error ? error->message : "unknown reason")); + if (error) + g_error_free (error); + return NULL; + } +} diff --git a/gst/rtsp-server/rtsp-media-factory.h b/gst/rtsp-server/rtsp-media-factory.h index 1399649..5a98a96 100644 --- a/gst/rtsp-server/rtsp-media-factory.h +++ b/gst/rtsp-server/rtsp-media-factory.h @@ -18,7 +18,6 @@ */ #include - #include #include "rtsp-media.h" @@ -28,7 +27,8 @@ G_BEGIN_DECLS -#define GST_TYPE_RTSP_MEDIA_FACTORY (gst_rtsp_media_get_type ()) +/* types for the media factory */ +#define GST_TYPE_RTSP_MEDIA_FACTORY (gst_rtsp_media_factory_get_type ()) #define GST_IS_RTSP_MEDIA_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_MEDIA_FACTORY)) #define GST_IS_RTSP_MEDIA_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_MEDIA_FACTORY)) #define GST_RTSP_MEDIA_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_MEDIA_FACTORY, GstRTSPMediaFactoryClass)) @@ -42,33 +42,41 @@ typedef struct _GstRTSPMediaFactoryClass GstRTSPMediaFactoryClass; /** * GstRTSPMediaFactory: + * @launch: the launch description + * @streams: the array of #GstRTSPMediaStream objects for this media. * - * Creates a #GstRTSPMedia object for a given url. + * The definition and logic for constructing the pipeline for a media. The media + * can contain multiple streams like audio and video. */ struct _GstRTSPMediaFactory { GObject parent; + + gchar *launch; }; +/** + * GstRTSPMediaFactoryClass: + * @construct: the vmethod that will be called when the factory has to create the + * #GstRTSPMediaBin for @location. + * + * the #GstRTSPMediaFactory class structure. + */ struct _GstRTSPMediaFactoryClass { GObjectClass parent_class; - GstRTSPMedia * (*create_media) (GstRTSPMediaFactory *factory, const gchar *url); + GstRTSPMediaBin * (*construct) (GstRTSPMediaFactory *factory, const gchar *location); }; GType gst_rtsp_media_factory_get_type (void); -/* creating a factory */ +/* configuring the factory */ GstRTSPMediaFactory * gst_rtsp_media_factory_new (void); -/* creating a media */ -GstRTSPMedia * gst_rtsp_media_factory_create (GstRTSPMediaFactory *factory, const gchar *url); - +void gst_rtsp_media_factory_set_launch (GstRTSPMediaFactory *factory, const gchar *launch); +gchar * gst_rtsp_media_factory_get_launch (GstRTSPMediaFactory *factory); -/* managing media GTypes to a path */ -void gst_rtsp_media_factory_add (GstRTSPMediaFactory *factory, const gchar *path, - GType type); -void gst_rtsp_media_factory_remove (GstRTSPMediaFactory *factory, const gchar *path, - GType type); +/* creating the media bin from the factory */ +GstRTSPMediaBin * gst_rtsp_media_factory_construct (GstRTSPMediaFactory *factory, const gchar *location); G_END_DECLS diff --git a/gst/rtsp-server/rtsp-media-mapping.c b/gst/rtsp-server/rtsp-media-mapping.c new file mode 100644 index 0000000..2471be7 --- /dev/null +++ b/gst/rtsp-server/rtsp-media-mapping.c @@ -0,0 +1,146 @@ +/* GStreamer + * Copyright (C) 2008 Wim Taymans + * + * 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 "rtsp-media-mapping.h" + +G_DEFINE_TYPE (GstRTSPMediaMapping, gst_rtsp_media_mapping, G_TYPE_OBJECT); + +static void gst_rtsp_media_mapping_finalize (GObject * obj); + +static GstRTSPMediaFactory * find_media (GstRTSPMediaMapping *mapping, const GstRTSPUrl *url); + +static void +gst_rtsp_media_mapping_class_init (GstRTSPMediaMappingClass * klass) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = gst_rtsp_media_mapping_finalize; + + klass->find_media = find_media; +} + +static void +gst_rtsp_media_mapping_init (GstRTSPMediaMapping * mapping) +{ + mapping->mappings = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, g_object_unref); +} + +static void +gst_rtsp_media_mapping_finalize (GObject * obj) +{ + GstRTSPMediaMapping *mapping = GST_RTSP_MEDIA_MAPPING (obj); + + g_hash_table_unref (mapping->mappings); + + G_OBJECT_CLASS (gst_rtsp_media_mapping_parent_class)->finalize (obj); +} + +GstRTSPMediaMapping * +gst_rtsp_media_mapping_new (void) +{ + GstRTSPMediaMapping *result; + + result = g_object_new (GST_TYPE_RTSP_MEDIA_MAPPING, NULL); + + return result; +} + +static GstRTSPMediaFactory * +find_media (GstRTSPMediaMapping *mapping, const GstRTSPUrl *url) +{ + GstRTSPMediaFactory *result; + + /* find the location of the media in the hashtable */ + result = g_hash_table_lookup (mapping->mappings, url->abspath); + if (result) + g_object_ref (result); + + g_message ("found media %p for url abspath %s", result, url->abspath); + + return result; +} + +/** + * gst_rtsp_media_mapping_find_factory: + * @mapping: a #GstRTSPMediaMapping + * @url: a url + * + * Find the #GstRTSPMediaFactory for @url from the mappings registered in @mapping. + * + * Returns: the #GstRTSPMediaFactory for @url. g_object_unref() after usage. + */ +GstRTSPMediaFactory * +gst_rtsp_media_mapping_find_factory (GstRTSPMediaMapping *mapping, const GstRTSPUrl *url) +{ + GstRTSPMediaFactory *result; + GstRTSPMediaMappingClass *klass; + + klass = GST_RTSP_MEDIA_MAPPING_GET_CLASS (mapping); + + if (klass->find_media) + result = klass->find_media (mapping, url); + else + result = NULL; + + return result; +} + +/** + * gst_rtsp_media_mapping_add_factory: + * @mapping: a #GstRTSPMediaMapping + * @path: a mount point + * @factory: a #GstRTSPMediaFactory + * + * Attach @factory to the mount point @path in @mapping. + * + * @path is of the form (/node)+. Any previous mapping will be freed. + * + * Ownership is taken of the reference on @factory so that @factory should not be + * used after calling this function. + */ +void +gst_rtsp_media_mapping_add_factory (GstRTSPMediaMapping *mapping, const gchar *path, + GstRTSPMediaFactory *factory) +{ + g_return_if_fail (GST_IS_RTSP_MEDIA_MAPPING (mapping)); + g_return_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory)); + g_return_if_fail (path != NULL); + + g_hash_table_insert (mapping->mappings, g_strdup (path), factory); +} + +/** + * gst_rtsp_media_mapping_remove_factory: + * @mapping: a #GstRTSPMediaMapping + * @path: a mount point + * + * Remove the #GstRTSPMediaFactory associated with @path in @mapping. + */ +void +gst_rtsp_media_mapping_remove_factory (GstRTSPMediaMapping *mapping, const gchar *path) +{ + g_return_if_fail (GST_IS_RTSP_MEDIA_MAPPING (mapping)); + g_return_if_fail (path != NULL); + + g_hash_table_remove (mapping->mappings, path); +} + diff --git a/gst/rtsp-server/rtsp-media-mapping.h b/gst/rtsp-server/rtsp-media-mapping.h new file mode 100644 index 0000000..6544246 --- /dev/null +++ b/gst/rtsp-server/rtsp-media-mapping.h @@ -0,0 +1,76 @@ +/* GStreamer + * Copyright (C) 2008 Wim Taymans + * + * 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 + +#include + +#include "rtsp-media-factory.h" + +#ifndef __GST_RTSP_MEDIA_MAPPING_H__ +#define __GST_RTSP_MEDIA_MAPPING_H__ + +G_BEGIN_DECLS + +#define GST_TYPE_RTSP_MEDIA_MAPPING (gst_rtsp_media_mapping_get_type ()) +#define GST_IS_RTSP_MEDIA_MAPPING(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_MEDIA_MAPPING)) +#define GST_IS_RTSP_MEDIA_MAPPING_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_MEDIA_MAPPING)) +#define GST_RTSP_MEDIA_MAPPING_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_MEDIA_MAPPING, GstRTSPMediaMappingClass)) +#define GST_RTSP_MEDIA_MAPPING(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_MEDIA_MAPPING, GstRTSPMediaMapping)) +#define GST_RTSP_MEDIA_MAPPING_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_MEDIA_MAPPING, GstRTSPMediaMappingClass)) +#define GST_RTSP_MEDIA_MAPPING_CAST(obj) ((GstRTSPMediaMapping*)(obj)) +#define GST_RTSP_MEDIA_MAPPING_CLASS_CAST(klass) ((GstRTSPMediaMappingClass*)(klass)) + +typedef struct _GstRTSPMediaMapping GstRTSPMediaMapping; +typedef struct _GstRTSPMediaMappingClass GstRTSPMediaMappingClass; + +/** + * GstRTSPMediaMapping: + * @mappings: the mountpoint to media mappings + * + * Creates a #GstRTSPMedia object for a given url. + */ +struct _GstRTSPMediaMapping { + GObject parent; + + GHashTable *mappings; +}; + +struct _GstRTSPMediaMappingClass { + GObjectClass parent_class; + + GstRTSPMediaFactory * (*find_media) (GstRTSPMediaMapping *mapping, const GstRTSPUrl *url); +}; + +GType gst_rtsp_media_mapping_get_type (void); + +/* creating a mapping */ +GstRTSPMediaMapping * gst_rtsp_media_mapping_new (void); + +/* finding a media factory */ +GstRTSPMediaFactory * gst_rtsp_media_mapping_find_factory (GstRTSPMediaMapping *mapping, const GstRTSPUrl *url); + +/* managing media to a path */ +void gst_rtsp_media_mapping_add_factory (GstRTSPMediaMapping *mapping, const gchar *path, + GstRTSPMediaFactory *factory); +void gst_rtsp_media_mapping_remove_factory (GstRTSPMediaMapping *mapping, const gchar *path); + +G_END_DECLS + +#endif /* __GST_RTSP_MEDIA_MAPPING_H__ */ diff --git a/gst/rtsp-server/rtsp-media.c b/gst/rtsp-server/rtsp-media.c index 13425d0..036d09f 100644 --- a/gst/rtsp-server/rtsp-media.c +++ b/gst/rtsp-server/rtsp-media.c @@ -19,24 +19,24 @@ #include "rtsp-media.h" -G_DEFINE_TYPE (GstRTSPMedia, gst_rtsp_media, G_TYPE_OBJECT); +static void gst_rtsp_media_bin_finalize (GObject * obj); -static void gst_rtsp_media_finalize (GObject * obj); +G_DEFINE_TYPE (GstRTSPMediaBin, gst_rtsp_media_bin, G_TYPE_OBJECT); static void -gst_rtsp_media_class_init (GstRTSPMediaClass * klass) +gst_rtsp_media_bin_class_init (GstRTSPMediaBinClass * klass) { GObjectClass *gobject_class; gobject_class = G_OBJECT_CLASS (klass); - gobject_class->finalize = gst_rtsp_media_finalize; + gobject_class->finalize = gst_rtsp_media_bin_finalize; } static void -gst_rtsp_media_init (GstRTSPMedia * media) +gst_rtsp_media_bin_init (GstRTSPMediaBin * bin) { - media->streams = g_array_new (FALSE, TRUE, sizeof (GstRTSPMediaStream *)); + bin->streams = g_array_new (FALSE, TRUE, sizeof (GstRTSPMediaStream *)); } static void @@ -45,218 +45,59 @@ gst_rtsp_media_stream_free (GstRTSPMediaStream *stream) } static void -gst_rtsp_media_finalize (GObject * obj) +gst_rtsp_media_bin_finalize (GObject * obj) { - GstRTSPMedia *media; + GstRTSPMediaBin *bin; guint i; - media = GST_RTSP_MEDIA (obj); + bin = GST_RTSP_MEDIA_BIN (obj); - g_free (media->location); - gst_rtsp_url_free (media->url); - - for (i = 0; i < media->streams->len; i++) { + for (i = 0; i < bin->streams->len; i++) { GstRTSPMediaStream *stream; - stream = g_array_index (media->streams, GstRTSPMediaStream *, i); + stream = g_array_index (bin->streams, GstRTSPMediaStream *, i); gst_rtsp_media_stream_free (stream); } - g_array_free (media->streams, TRUE); - - G_OBJECT_CLASS (gst_rtsp_media_parent_class)->finalize (obj); -} - -/** - * gst_rtsp_media_new: - * @location: the URL of the media - * - * Create a new #GstRTSPMedia instance. - * - * Returns: a new #GstRTSPMedia object or %NULL when location did not contain a - * valid or understood URL. - */ -GstRTSPMedia * -gst_rtsp_media_new (const gchar *location) -{ - GstRTSPMedia *result; - GstRTSPUrl *url; - - url = NULL; - - if (gst_rtsp_url_parse (location, &url) != GST_RTSP_OK) - goto invalid_url; - - result = g_object_new (GST_TYPE_RTSP_MEDIA, NULL); - result->location = g_strdup (location); - result->url = url; - - return result; - - /* ERRORS */ -invalid_url: - { - return NULL; - } -} - -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); -} - -/** - * gst_rtsp_media_prepare: - * @media: a #GstRTSPMedia - * @bin: the parent bin to create the elements in. - * - * Prepare the media object so that it creates its streams. Implementations - * should crate the needed gstreamer elements and add them to @bin. No state - * changes should be performed on them yet. - * - * One or more GstRTSPMediaStream objects should be added to @media with the - * srcpad member set to a source pad that produces buffer of type - * application/x-rtp. - * - * Returns: %TRUE if the media could be prepared. - */ -gboolean -gst_rtsp_media_prepare (GstRTSPMedia *media, GstBin *bin) -{ - GstRTSPMediaStream *stream; - GstElement *pay, *element; - GstPad * pad; - gint i; - - /* if we're already prepared we must exit */ - g_return_val_if_fail (media->prepared == FALSE, FALSE); - - g_print ("%s\n", media->url->abspath); - - if (g_str_has_prefix (media->url->abspath, "/camera")) { - /* live */ - element = gst_parse_launch ("( " - "v4l2src ! video/x-raw-yuv,width=352,height=288,framerate=15/1 ! " - "queue ! videorate ! ffmpegcolorspace ! " - "x264enc bitrate=300 ! rtph264pay name=pay0 pt=96 " - "alsasrc ! audio/x-raw-int,rate=8000 ! queue ! " - "amrnbenc ! rtpamrpay name=pay1 pt=97 " - ")", NULL); - } - else if (g_str_has_prefix (media->url->abspath, "/h264")) { - /* transcode h264 */ - element = gst_parse_launch ("( uridecodebin " - "uri=file:///home/cschalle/Videos/mi2.avi ! " - "x264enc bitrate=300 ! rtph264pay name=pay0 )", NULL); - } - else if (g_str_has_prefix (media->url->abspath, "/theora")) { - /* transcode theora */ - element = gst_parse_launch ("( uridecodebin " - "uri=file:///home/wim/data/mi2.avi ! " - "theoraenc ! rtptheorapay name=pay0 )", NULL); - } - else if (g_str_has_prefix (media->url->abspath, "/macpclinux")) { - /* theora/vorbis */ - element = gst_parse_launch ("( filesrc " - "location=/home/cschalle/Videos/mac_pc_linux_2.ogg ! oggdemux name=d ! " - "queue ! theoraparse ! rtptheorapay name=pay0 " - "d. ! queue ! vorbisparse ! rtpvorbispay name=pay1 )", NULL); - } - else if (g_str_has_prefix (media->url->abspath, "/rtspproxy")) { - /* proxy RTSP transcode */ - element = gst_parse_launch ("( uridecodebin " - "uri=rtsp://ia300135.us.archive.org:554/0/items/uncovered_interviews/uncovered_interviews_3_256kb.mp4 ! " - "x264enc bitrate=1800 ! rtph264pay name=pay0 )", NULL); - } - else if (g_str_has_prefix (media->url->abspath, "/httpproxy")) { - /* proxy HTTP transcode */ - element = gst_parse_launch ("( uridecodebin " - "uri=http://movies.apple.com/movies/fox/maxpayne/maxpayne-tlre_h480.mov name=d " - "d. ! queue ! x264enc bitrate=1800 ! rtph264pay name=pay0 pt=96 " - "d. ! queue ! faac ! rtpmp4gpay name=pay1 pt=97 )", NULL); - } - else - return FALSE; - - gst_bin_add (bin, element); - - for (i = 0; ; i++) { - gchar *name; - - name = g_strdup_printf ("pay%d", i); - - if (!(pay = gst_bin_get_by_name (GST_BIN (element), name))) { - g_free (name); - break; - } - - /* create the stream */ - stream = g_new0 (GstRTSPMediaStream, 1); - stream->media = media; - stream->element = element; - stream->payloader = pay; - stream->idx = media->streams->len; - - pad = gst_element_get_static_pad (pay, "src"); - - 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); - - /* add stream now */ - g_array_append_val (media->streams, stream); - gst_object_unref (pay); - - g_free (name); - } - - media->prepared = TRUE; + g_array_free (bin->streams, TRUE); - return TRUE; + G_OBJECT_CLASS (gst_rtsp_media_bin_parent_class)->finalize (obj); } /** - * gst_rtsp_media_n_streams: - * @media: a #GstRTSPMedia + * gst_rtsp_media_bin_n_streams: + * @media: a #GstRTSPMediaBin * - * Get the number of streams in this media. + * Get the number of streams in this mediabin. * * Returns: The number of streams. */ guint -gst_rtsp_media_n_streams (GstRTSPMedia *media) +gst_rtsp_media_bin_n_streams (GstRTSPMediaBin *bin) { - g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), 0); - g_return_val_if_fail (media->prepared, 0); + g_return_val_if_fail (GST_IS_RTSP_MEDIA_BIN (bin), 0); - return media->streams->len; + return bin->streams->len; } /** - * gst_rtsp_media_get_stream: - * @media: a #GstRTSPMedia + * gst_rtsp_media_bin_get_stream: + * @bin: a #GstRTSPMediaBin * @idx: the stream index * - * Retrieve the stream with index @idx from @media. + * Retrieve the stream with index @idx from @bin. * * Returns: the #GstRTSPMediaStream at index @idx. */ GstRTSPMediaStream * -gst_rtsp_media_get_stream (GstRTSPMedia *media, guint idx) +gst_rtsp_media_bin_get_stream (GstRTSPMediaBin *bin, guint idx) { GstRTSPMediaStream *res; - g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL); - g_return_val_if_fail (media->prepared, 0); - g_return_val_if_fail (idx < media->streams->len, NULL); + g_return_val_if_fail (GST_IS_RTSP_MEDIA_BIN (bin), NULL); + g_return_val_if_fail (idx < bin->streams->len, NULL); - res = g_array_index (media->streams, GstRTSPMediaStream *, idx); + res = g_array_index (bin->streams, GstRTSPMediaStream *, idx); return res; } diff --git a/gst/rtsp-server/rtsp-media.h b/gst/rtsp-server/rtsp-media.h index f96ab1f..8c6e64d 100644 --- a/gst/rtsp-server/rtsp-media.h +++ b/gst/rtsp-server/rtsp-media.h @@ -25,18 +25,19 @@ G_BEGIN_DECLS -#define GST_TYPE_RTSP_MEDIA (gst_rtsp_media_get_type ()) -#define GST_IS_RTSP_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_MEDIA)) -#define GST_IS_RTSP_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_MEDIA)) -#define GST_RTSP_MEDIA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_MEDIA, GstRTSPMediaClass)) -#define GST_RTSP_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_MEDIA, GstRTSPMedia)) -#define GST_RTSP_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_MEDIA, GstRTSPMediaClass)) -#define GST_RTSP_MEDIA_CAST(obj) ((GstRTSPMedia*)(obj)) -#define GST_RTSP_MEDIA_CLASS_CAST(klass) ((GstRTSPMediaClass*)(klass)) +/* types for the media bin */ +#define GST_TYPE_RTSP_MEDIA_BIN (gst_rtsp_media_bin_get_type ()) +#define GST_IS_RTSP_MEDIA_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_MEDIA_BIN)) +#define GST_IS_RTSP_MEDIA_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_MEDIA_BIN)) +#define GST_RTSP_MEDIA_BIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_MEDIA_BIN, GstRTSPMediaBinClass)) +#define GST_RTSP_MEDIA_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_MEDIA_BIN, GstRTSPMediaBin)) +#define GST_RTSP_MEDIA_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_MEDIA_BIN, GstRTSPMediaBinClass)) +#define GST_RTSP_MEDIA_BIN_CAST(obj) ((GstRTSPMediaBin*)(obj)) +#define GST_RTSP_MEDIA_BIN_CLASS_CAST(klass) ((GstRTSPMediaBinClass*)(klass)) -typedef struct _GstRTSPMedia GstRTSPMedia; typedef struct _GstRTSPMediaStream GstRTSPMediaStream; -typedef struct _GstRTSPMediaClass GstRTSPMediaClass; +typedef struct _GstRTSPMediaBin GstRTSPMediaBin; +typedef struct _GstRTSPMediaBinClass GstRTSPMediaBinClass; /** * GstRTSPMediaStream: @@ -51,7 +52,7 @@ typedef struct _GstRTSPMediaClass GstRTSPMediaClass; * The definition of a media stream. The streams are identified by @id. */ struct _GstRTSPMediaStream { - GstRTSPMedia *media; + GstRTSPMediaBin *mediabin; guint idx; @@ -63,33 +64,28 @@ struct _GstRTSPMediaStream { }; /** - * GstRTSPMedia: + * GstRTSPMediaBin: + * @media: the owner #GstRTSPMedia * - * The definition and logic for constructing the pipeline for a media. The media - * can contain multiple streams like audio and video. + * A class that contains the elements to handle the media + * provided by @media. */ -struct _GstRTSPMedia { +struct _GstRTSPMediaBin { GObject parent; - gchar *location; - GstRTSPUrl *url; - - gboolean prepared; + GstElement *element; GArray *streams; }; -struct _GstRTSPMediaClass { +struct _GstRTSPMediaBinClass { GObjectClass parent_class; }; -GType gst_rtsp_media_get_type (void); - -GstRTSPMedia * gst_rtsp_media_new (const gchar *name); - -gboolean gst_rtsp_media_prepare (GstRTSPMedia *media, GstBin *bin); +GType gst_rtsp_media_bin_get_type (void); -guint gst_rtsp_media_n_streams (GstRTSPMedia *media); -GstRTSPMediaStream * gst_rtsp_media_get_stream (GstRTSPMedia *media, guint idx); +/* dealing with the media bin */ +guint gst_rtsp_media_bin_n_streams (GstRTSPMediaBin *bin); +GstRTSPMediaStream * gst_rtsp_media_bin_get_stream (GstRTSPMediaBin *bin, guint idx); G_END_DECLS diff --git a/gst/rtsp-server/rtsp-server.c b/gst/rtsp-server/rtsp-server.c index cea1bf0..b34bc80 100644 --- a/gst/rtsp-server/rtsp-server.c +++ b/gst/rtsp-server/rtsp-server.c @@ -31,7 +31,7 @@ enum PROP_BACKLOG, PROP_PORT, PROP_POOL, - PROP_FACTORY, + PROP_MAPPING, PROP_LAST }; @@ -88,14 +88,14 @@ gst_rtsp_server_class_init (GstRTSPServerClass * klass) g_param_spec_object ("pool", "Pool", "The session pool to use for client session", GST_TYPE_RTSP_SESSION_POOL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** - * GstRTSPServer::factory + * GstRTSPServer::mapping * - * The media factory to use for this server. By default the server has no - * media factories and thus cannot map urls to media streams. + * 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_POOL, - g_param_spec_object ("factory", "Factory", "The media factory to use for client session", - GST_TYPE_RTSP_MEDIA_FACTORY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_MAPPING, + g_param_spec_object ("mapping", "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; } @@ -106,6 +106,7 @@ 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 (); } /** @@ -202,13 +203,19 @@ gst_rtsp_server_get_backlog (GstRTSPServer *server) void gst_rtsp_server_set_session_pool (GstRTSPServer *server, GstRTSPSessionPool *pool) { + GstRTSPSessionPool *old; + g_return_if_fail (GST_IS_RTSP_SERVER (server)); - if (server->pool) - g_object_unref (server->pool); - if (pool) - g_object_ref (pool); - server->pool = pool; + old = server->pool; + + if (old != pool) { + if (pool) + g_object_ref (pool); + server->pool = pool; + if (old) + g_object_unref (old); + } } @@ -235,25 +242,25 @@ gst_rtsp_server_get_session_pool (GstRTSPServer *server) } /** - * gst_rtsp_server_set_media_factory: + * gst_rtsp_server_set_media_mapping: * @server: a #GstRTSPServer - * @factory: a #GstRTSPMediaFactory + * @mapping: a #GstRTSPMediaMapping * - * configure @factory to be used as the media factory of @server. + * configure @mapping to be used as the media mapping of @server. */ void -gst_rtsp_server_set_media_factory (GstRTSPServer *server, GstRTSPMediaFactory *factory) +gst_rtsp_server_set_media_mapping (GstRTSPServer *server, GstRTSPMediaMapping *mapping) { - GstRTSPMediaFactory *old; + GstRTSPMediaMapping *old; g_return_if_fail (GST_IS_RTSP_SERVER (server)); - old = server->factory; + old = server->mapping; - if (old != factory) { - if (factory) - g_object_ref (factory); - server->factory = factory; + if (old != mapping) { + if (mapping) + g_object_ref (mapping); + server->mapping = mapping; if (old) g_object_unref (old); } @@ -261,22 +268,22 @@ gst_rtsp_server_set_media_factory (GstRTSPServer *server, GstRTSPMediaFactory *f /** - * gst_rtsp_server_get_media_factory: + * gst_rtsp_server_get_media_mapping: * @server: a #GstRTSPServer * - * Get the #GstRTSPMediaFactory used as the media factory of @server. + * Get the #GstRTSPMediaMapping used as the media mapping of @server. * - * Returns: the #GstRTSPMediaFactory of @server. g_object_unref() after + * Returns: the #GstRTSPMediaMapping of @server. g_object_unref() after * usage. */ -GstRTSPMediaFactory * -gst_rtsp_server_get_media_factory (GstRTSPServer *server) +GstRTSPMediaMapping * +gst_rtsp_server_get_media_mapping (GstRTSPServer *server) { - GstRTSPMediaFactory *result; + GstRTSPMediaMapping *result; g_return_val_if_fail (GST_IS_RTSP_SERVER (server), NULL); - if ((result = server->factory)) + if ((result = server->mapping)) g_object_ref (result); return result; @@ -298,8 +305,8 @@ gst_rtsp_server_get_property (GObject *object, guint propid, case PROP_POOL: g_value_take_object (value, gst_rtsp_server_get_session_pool (server)); break; - case PROP_FACTORY: - g_value_take_object (value, gst_rtsp_server_get_media_factory (server)); + case PROP_MAPPING: + g_value_take_object (value, gst_rtsp_server_get_media_mapping (server)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec); @@ -322,8 +329,8 @@ gst_rtsp_server_set_property (GObject *object, guint propid, case PROP_POOL: gst_rtsp_server_set_session_pool (server, g_value_get_object (value)); break; - case PROP_FACTORY: - gst_rtsp_server_set_media_factory (server, g_value_get_object (value)); + case PROP_MAPPING: + gst_rtsp_server_set_media_mapping (server, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec); @@ -440,7 +447,7 @@ gst_rtsp_server_accept_client (GstRTSPServer *server, GIOChannel *channel) gst_rtsp_client_set_session_pool (client, server->pool); /* set the session pool that this client should use */ - gst_rtsp_client_set_media_factory (client, server->factory); + gst_rtsp_client_set_media_mapping (client, server->mapping); /* accept connections for that client, this function returns after accepting * the connection and will run the remainder of the communication with the diff --git a/gst/rtsp-server/rtsp-server.h b/gst/rtsp-server/rtsp-server.h index 5dd5629..277b841 100644 --- a/gst/rtsp-server/rtsp-server.h +++ b/gst/rtsp-server/rtsp-server.h @@ -34,7 +34,7 @@ #include #include "rtsp-session-pool.h" -#include "rtsp-media-factory.h" +#include "rtsp-media-mapping.h" #include "rtsp-client.h" #ifndef __GST_RTSP_SERVER_H__ @@ -72,8 +72,8 @@ struct _GstRTSPServer { /* sessions on this server */ GstRTSPSessionPool *pool; - /* media factory for this server */ - GstRTSPMediaFactory *factory; + /* media mapper for this server */ + GstRTSPMediaMapping *mapping; }; /** @@ -103,8 +103,8 @@ gint gst_rtsp_server_get_backlog (GstRTSPServer *serve void gst_rtsp_server_set_session_pool (GstRTSPServer *server, GstRTSPSessionPool *pool); GstRTSPSessionPool * gst_rtsp_server_get_session_pool (GstRTSPServer *server); -void gst_rtsp_server_set_media_factory (GstRTSPServer *server, GstRTSPMediaFactory *factory); -GstRTSPMediaFactory * gst_rtsp_server_get_media_factory (GstRTSPServer *server); +void gst_rtsp_server_set_media_mapping (GstRTSPServer *server, GstRTSPMediaMapping *mapping); +GstRTSPMediaMapping * gst_rtsp_server_get_media_mapping (GstRTSPServer *server); gboolean gst_rtsp_server_io_func (GIOChannel *channel, GIOCondition condition, GstRTSPServer *server); diff --git a/gst/rtsp-server/rtsp-session.c b/gst/rtsp-server/rtsp-session.c index 6eddd12..dd608c7 100644 --- a/gst/rtsp-server/rtsp-session.c +++ b/gst/rtsp-server/rtsp-session.c @@ -62,8 +62,8 @@ gst_rtsp_session_free_media (GstRTSPSessionMedia *media) gst_element_set_state (media->pipeline, GST_STATE_NULL); - if (media->media) - g_object_unref (media->media); + if (media->factory) + g_object_unref (media->factory); for (walk = media->streams; walk; walk = g_list_next (walk)) { GstRTSPSessionStream *stream = (GstRTSPSessionStream *) walk->data; @@ -98,14 +98,15 @@ gst_rtsp_session_finalize (GObject * obj) /** * gst_rtsp_session_get_media: * @sess: a #GstRTSPSession - * @media: a #GstRTSPSessionMedia + * @location: the url for the media + * @factory: a #GstRTSPMediaFactory * - * Get or create the session information for @media. + * Get or create the session information for @factory. * - * Returns: the configuration for @media in @sess. + * Returns: the configuration for @factory in @sess. */ GstRTSPSessionMedia * -gst_rtsp_session_get_media (GstRTSPSession *sess, GstRTSPMedia *media) +gst_rtsp_session_get_media (GstRTSPSession *sess, const gchar *location, GstRTSPMediaFactory *factory) { GstRTSPSessionMedia *result; GList *walk; @@ -115,19 +116,22 @@ gst_rtsp_session_get_media (GstRTSPSession *sess, GstRTSPMedia *media) for (walk = sess->medias; walk; walk = g_list_next (walk)) { result = (GstRTSPSessionMedia *) walk->data; - if (result->media == media) + if (result->factory == factory) break; result = NULL; } if (result == NULL) { result = g_new0 (GstRTSPSessionMedia, 1); - result->media = media; + result->factory = factory; result->pipeline = gst_pipeline_new ("pipeline"); - /* prepare media into the pipeline */ - if (!gst_rtsp_media_prepare (media, GST_BIN (result->pipeline))) + /* construct media and add to the pipeline */ + result->mediabin = gst_rtsp_media_factory_construct (factory, location); + if (result->mediabin == NULL) goto no_media; + + gst_bin_add (GST_BIN_CAST (result->pipeline), result->mediabin->element); result->rtpbin = gst_element_factory_make ("gstrtpbin", "rtpbin"); @@ -149,7 +153,7 @@ no_media: } /** - * gst_rtsp_session_get_stream: + * gst_rtsp_session_media_get_stream: * @media: a #GstRTSPSessionMedia * @idx: the stream index * @@ -159,7 +163,7 @@ no_media: * is unreffed. */ GstRTSPSessionStream * -gst_rtsp_session_get_stream (GstRTSPSessionMedia *media, guint idx) +gst_rtsp_session_media_get_stream (GstRTSPSessionMedia *media, guint idx) { GstRTSPSessionStream *result; GList *walk; @@ -178,7 +182,7 @@ gst_rtsp_session_get_stream (GstRTSPSessionMedia *media, guint idx) result = g_new0 (GstRTSPSessionStream, 1); result->idx = idx; result->media = media; - result->media_stream = gst_rtsp_media_get_stream (media->media, idx); + result->media_stream = gst_rtsp_media_bin_get_stream (media->mediabin, idx); media->streams = g_list_prepend (media->streams, result); } diff --git a/gst/rtsp-server/rtsp-session.h b/gst/rtsp-server/rtsp-session.h index c87a139..5db5ba8 100644 --- a/gst/rtsp-server/rtsp-session.h +++ b/gst/rtsp-server/rtsp-session.h @@ -22,6 +22,7 @@ #include #include "rtsp-media.h" +#include "rtsp-media-factory.h" #ifndef __GST_RTSP_SESSION_H__ #define __GST_RTSP_SESSION_H__ @@ -87,10 +88,11 @@ struct _GstRTSPSessionMedia GstRTSPSession *session; /* the media we are handling */ - GstRTSPMedia *media; + GstRTSPMediaFactory *factory; /* the pipeline for the media */ GstElement *pipeline; + GstRTSPMediaBin *mediabin; /* RTP session manager */ GstElement *rtpbin; @@ -125,15 +127,16 @@ GType gst_rtsp_session_get_type (void); GstRTSPSession * gst_rtsp_session_new (const gchar *sessionid); -GstRTSPSessionMedia * gst_rtsp_session_get_media (GstRTSPSession *sess, - GstRTSPMedia *media); -GstRTSPSessionStream * gst_rtsp_session_get_stream (GstRTSPSessionMedia *media, - guint idx); +GstRTSPSessionMedia * gst_rtsp_session_get_media (GstRTSPSession *sess, const gchar *location, + GstRTSPMediaFactory *factory); GstStateChangeReturn gst_rtsp_session_media_play (GstRTSPSessionMedia *media); GstStateChangeReturn gst_rtsp_session_media_pause (GstRTSPSessionMedia *media); GstStateChangeReturn gst_rtsp_session_media_stop (GstRTSPSessionMedia *media); +GstRTSPSessionStream * gst_rtsp_session_media_get_stream (GstRTSPSessionMedia *media, + guint idx); + GstRTSPTransport * gst_rtsp_session_stream_set_transport (GstRTSPSessionStream *stream, const gchar *destination, GstRTSPTransport *ct); -- 2.7.4