rtsp: massive refactoring

Make GObjects from the remaining simple structures.
Remove GstRTSPSessionStream, it's not needed.
Rename GstRTSPMediaStream -> GstRTSPStream: It is shorter
Rename GstRTSPMediaTrans -> GstRTSPStreamTransport: It describes how
  a GstRTSPStream should be transported to a client.
Rename GstRTSPMediaFactory::get_element -> create_element because that
  more accurately describes what it does.
Make nice methods instead of poking in the structures.
Move some methods inside the relevant object source code.
Use GPtrArray to store objects instead of plain arrays, it is more
  natural and allows us to more easily clean up.
Move the allocation of udp ports to the Stream object. The Stream object
  contains the elements needed to stream the media to a client.
Improve the prepare and unprepare methods. Unprepare should now undo
  everything prepare did. Improve also async unprepare when doing EOS on
  shutdown. Make sure we always unprepare correctly.
This commit is contained in:
Wim Taymans 2012-10-25 21:29:58 +02:00
parent 0de6262dc4
commit de7c72dec2
17 changed files with 2036 additions and 1484 deletions

View file

@ -6,7 +6,10 @@ public_headers = \
rtsp-media-factory.h \
rtsp-media-factory-uri.h \
rtsp-media-mapping.h \
rtsp-stream.h \
rtsp-stream-transport.h \
rtsp-session.h \
rtsp-session-media.h \
rtsp-session-pool.h \
rtsp-client.h \
rtsp-server.h
@ -19,7 +22,10 @@ c_sources = \
rtsp-media-factory.c \
rtsp-media-factory-uri.c \
rtsp-media-mapping.c \
rtsp-stream.c \
rtsp-stream-transport.c \
rtsp-session.c \
rtsp-session-media.c \
rtsp-session-pool.c \
rtsp-client.c \
rtsp-server.c

View file

@ -69,7 +69,7 @@ static void gst_rtsp_client_finalize (GObject * obj);
static GstSDPMessage *create_sdp (GstRTSPClient * client, GstRTSPMedia * media);
static void client_session_finalized (GstRTSPClient * client,
GstRTSPSession * session);
static void unlink_session_streams (GstRTSPClient * client,
static void unlink_session_transports (GstRTSPClient * client,
GstRTSPSession * session, GstRTSPSessionMedia * media);
G_DEFINE_TYPE (GstRTSPClient, gst_rtsp_client, G_TYPE_OBJECT);
@ -184,7 +184,7 @@ client_unlink_session (GstRTSPClient * client, GstRTSPSession * session)
GstRTSPSessionMedia *media = g_list_first (session->medias)->data;
gst_rtsp_session_media_set_state (media, GST_STATE_NULL);
unlink_session_streams (client, session, media);
unlink_session_transports (client, session, media);
/* unmanage the media in the session. this will modify session->medias */
gst_rtsp_session_release_media (session, media);
}
@ -480,6 +480,7 @@ do_send_data (GstBuffer * buffer, guint8 channel, GstRTSPClient * client)
gst_rtsp_message_init_data (&message, channel);
/* FIXME, need some sort of iovec RTSPMessage here */
if (!gst_buffer_map (buffer, &map_info, GST_MAP_READ))
return FALSE;
@ -498,48 +499,49 @@ do_send_data (GstBuffer * buffer, guint8 channel, GstRTSPClient * client)
}
static void
link_stream (GstRTSPClient * client, GstRTSPSession * session,
GstRTSPSessionStream * stream)
link_transport (GstRTSPClient * client, GstRTSPSession * session,
GstRTSPStreamTransport * trans)
{
GST_DEBUG ("client %p: linking stream %p", client, stream);
gst_rtsp_session_stream_set_callbacks (stream, (GstRTSPSendFunc) do_send_data,
GST_DEBUG ("client %p: linking transport %p", client, trans);
gst_rtsp_stream_transport_set_callbacks (trans,
(GstRTSPSendFunc) do_send_data,
(GstRTSPSendFunc) do_send_data, client, NULL);
client->streams = g_list_prepend (client->streams, stream);
client->transports = g_list_prepend (client->transports, trans);
/* make sure our session can't expire */
gst_rtsp_session_prevent_expire (session);
}
static void
unlink_stream (GstRTSPClient * client, GstRTSPSession * session,
GstRTSPSessionStream * stream)
unlink_transport (GstRTSPClient * client, GstRTSPSession * session,
GstRTSPStreamTransport * trans)
{
GST_DEBUG ("client %p: unlinking stream %p", client, stream);
gst_rtsp_session_stream_set_callbacks (stream, NULL, NULL, NULL, NULL);
client->streams = g_list_remove (client->streams, stream);
GST_DEBUG ("client %p: unlinking transport %p", client, trans);
gst_rtsp_stream_transport_set_callbacks (trans, NULL, NULL, NULL, NULL);
client->transports = g_list_remove (client->transports, trans);
/* our session can now expire */
gst_rtsp_session_allow_expire (session);
}
static void
unlink_session_streams (GstRTSPClient * client, GstRTSPSession * session,
unlink_session_transports (GstRTSPClient * client, GstRTSPSession * session,
GstRTSPSessionMedia * media)
{
guint n_streams, i;
n_streams = gst_rtsp_media_n_streams (media->media);
for (i = 0; i < n_streams; i++) {
GstRTSPSessionStream *sstream;
GstRTSPStreamTransport *trans;
GstRTSPTransport *tr;
/* get the stream as configured in the session */
sstream = gst_rtsp_session_media_get_stream (media, i);
trans = gst_rtsp_session_media_get_transport (media, i);
/* get the transport, if there is no transport configured, skip this stream */
if (!(tr = sstream->trans.transport))
if (!(tr = trans->transport))
continue;
if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
/* for TCP, unlink the stream from the TCP connection of the client */
unlink_stream (client, session, sstream);
unlink_transport (client, session, trans);
}
}
}
@ -581,7 +583,7 @@ handle_teardown_request (GstRTSPClient * client, GstRTSPClientState * state)
state->sessmedia = media;
/* unlink the all TCP callbacks */
unlink_session_streams (client, session, media);
unlink_session_transports (client, session, media);
/* remove the session from the watched sessions */
g_object_weak_unref (G_OBJECT (session),
@ -722,7 +724,7 @@ handle_pause_request (GstRTSPClient * client, GstRTSPClientState * state)
goto invalid_state;
/* unlink the all TCP callbacks */
unlink_session_streams (client, session, media);
unlink_session_transports (client, session, media);
/* then pause sending */
gst_rtsp_session_media_set_state (media, GST_STATE_PAUSED);
@ -769,7 +771,6 @@ handle_play_request (GstRTSPClient * client, GstRTSPClientState * state)
GstRTSPStatusCode code;
GString *rtpinfo;
guint n_streams, i, infocount;
guint timestamp, seqnum;
gchar *str;
GstRTSPTimeRange *range;
GstRTSPResult res;
@ -805,44 +806,31 @@ handle_play_request (GstRTSPClient * client, GstRTSPClientState * state)
n_streams = gst_rtsp_media_n_streams (media->media);
for (i = 0, infocount = 0; i < n_streams; i++) {
GstRTSPSessionStream *sstream;
GstRTSPMediaStream *stream;
GstRTSPStreamTransport *trans;
GstRTSPTransport *tr;
GObjectClass *payobjclass;
gchar *uristr;
guint rtptime, seq;
/* get the stream as configured in the session */
sstream = gst_rtsp_session_media_get_stream (media, i);
trans = gst_rtsp_session_media_get_transport (media, i);
/* get the transport, if there is no transport configured, skip this stream */
if (!(tr = sstream->trans.transport)) {
if (!(tr = trans->transport)) {
GST_INFO ("stream %d is not configured", i);
continue;
}
if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
/* for TCP, link the stream to the TCP connection of the client */
link_stream (client, session, sstream);
link_transport (client, session, trans);
}
stream = sstream->media_stream;
payobjclass = G_OBJECT_GET_CLASS (stream->payloader);
if (g_object_class_find_property (payobjclass, "seqnum") &&
g_object_class_find_property (payobjclass, "timestamp")) {
GObject *payobj;
payobj = G_OBJECT (stream->payloader);
/* only add RTP-Info for streams with seqnum and timestamp */
g_object_get (payobj, "seqnum", &seqnum, "timestamp", &timestamp, NULL);
if (gst_rtsp_stream_get_rtpinfo (trans->stream, &rtptime, &seq)) {
if (infocount > 0)
g_string_append (rtpinfo, ", ");
uristr = gst_rtsp_url_get_request_uri (state->uri);
g_string_append_printf (rtpinfo, "url=%s/stream=%d;seq=%u;rtptime=%u",
uristr, i, seqnum, timestamp);
uristr, i, seq, rtptime);
g_free (uristr);
infocount++;
@ -944,7 +932,7 @@ handle_setup_request (GstRTSPClient * client, GstRTSPClientState * state)
GstRTSPLowerTrans supported;
GstRTSPStatusCode code;
GstRTSPSession *session;
GstRTSPSessionStream *stream;
GstRTSPStreamTransport *trans;
gchar *trans_str, *pos;
guint streamid;
GstRTSPSessionMedia *media;
@ -1086,14 +1074,14 @@ handle_setup_request (GstRTSPClient * client, GstRTSPClientState * state)
}
}
/* get a handle to the stream in the media */
if (!(stream = gst_rtsp_session_media_get_stream (media, streamid)))
goto no_stream;
/* get a handle to the transport of the media in this session */
if (!(trans = gst_rtsp_session_media_get_transport (media, streamid)))
goto no_stream_transport;
st = gst_rtsp_session_stream_set_transport (stream, ct);
st = gst_rtsp_stream_transport_set_transport (trans, ct);
/* configure keepalive for this transport */
gst_rtsp_session_stream_set_keepalive (stream,
gst_rtsp_stream_transport_set_keepalive (trans,
(GstRTSPKeepAliveFunc) do_keepalive, session, NULL);
/* serialize the server transport */
@ -1149,7 +1137,7 @@ invalid_blocksize:
gst_rtsp_transport_free (ct);
return FALSE;
}
no_stream:
no_stream_transport:
{
send_generic_response (client, GST_RTSP_STS_NOT_FOUND, state);
g_object_unref (session);
@ -1265,7 +1253,6 @@ handle_describe_request (GstRTSPClient * client, GstRTSPClientState * state)
if (!(media = find_media (client, state)))
goto no_media;
/* create an SDP for the media object on this client */
if (!(sdp = klass->create_sdp (client, media)))
goto no_sdp;
@ -1567,28 +1554,28 @@ handle_data (GstRTSPClient * client, GstRTSPMessage * message)
buffer = gst_buffer_new_wrapped (data, size);
handled = FALSE;
for (walk = client->streams; walk; walk = g_list_next (walk)) {
GstRTSPSessionStream *stream = (GstRTSPSessionStream *) walk->data;
GstRTSPMediaStream *mstream;
for (walk = client->transports; walk; walk = g_list_next (walk)) {
GstRTSPStreamTransport *trans = (GstRTSPStreamTransport *) walk->data;
GstRTSPStream *stream;
GstRTSPTransport *tr;
/* get the transport, if there is no transport configured, skip this stream */
if (!(tr = stream->trans.transport))
if (!(tr = trans->transport))
continue;
/* we also need a media stream */
if (!(mstream = stream->media_stream))
if (!(stream = trans->stream))
continue;
/* check for TCP transport */
if (tr->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
/* dispatch to the stream based on the channel number */
if (tr->interleaved.min == channel) {
gst_rtsp_media_stream_rtp (mstream, buffer);
gst_rtsp_stream_recv_rtp (stream, buffer);
handled = TRUE;
break;
} else if (tr->interleaved.max == channel) {
gst_rtsp_media_stream_rtcp (mstream, buffer);
gst_rtsp_stream_recv_rtcp (stream, buffer);
handled = TRUE;
break;
}

View file

@ -33,6 +33,7 @@ typedef struct _GstRTSPClientState GstRTSPClientState;
#include "rtsp-media.h"
#include "rtsp-media-mapping.h"
#include "rtsp-session-pool.h"
#include "rtsp-session-media.h"
#include "rtsp-auth.h"
#include "rtsp-sdp.h"
@ -58,7 +59,7 @@ typedef struct _GstRTSPClientState GstRTSPClientState;
*
* Information passed around containing the client state of a request.
*/
struct _GstRTSPClientState{
struct _GstRTSPClientState {
GstRTSPMessage *request;
GstRTSPUrl *uri;
GstRTSPMethod method;
@ -81,7 +82,7 @@ struct _GstRTSPClientState{
* @media_mapping: handle to the media mapping used by the client.
* @uri: cached uri
* @media: cached media
* @streams: a list of streams using @connection.
* @transports: a list of #GstRTSPStreamTransport using @connection.
* @sessions: a list of sessions managed by @connection.
*
* The client structure.
@ -104,7 +105,7 @@ struct _GstRTSPClient {
GstRTSPUrl *uri;
GstRTSPMedia *media;
GList *streams;
GList *transports;
GList *sessions;
};

View file

@ -66,7 +66,7 @@ static void gst_rtsp_media_factory_uri_set_property (GObject * object,
guint propid, const GValue * value, GParamSpec * pspec);
static void gst_rtsp_media_factory_uri_finalize (GObject * obj);
static GstElement *rtsp_media_factory_uri_get_element (GstRTSPMediaFactory *
static GstElement *rtsp_media_factory_uri_create_element (GstRTSPMediaFactory *
factory, const GstRTSPUrl * url);
G_DEFINE_TYPE (GstRTSPMediaFactoryURI, gst_rtsp_media_factory_uri,
@ -105,7 +105,7 @@ gst_rtsp_media_factory_uri_class_init (GstRTSPMediaFactoryURIClass * klass)
"Use the gstpay payloader to avoid decoding", DEFAULT_USE_GSTPAY,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
mediafactory_class->get_element = rtsp_media_factory_uri_get_element;
mediafactory_class->create_element = rtsp_media_factory_uri_create_element;
GST_DEBUG_CATEGORY_INIT (rtsp_media_factory_uri_debug, "rtspmediafactoryuri",
0, "GstRTSPMediaFactoryUri");
@ -528,7 +528,7 @@ no_more_pads_cb (GstElement * uribin, GstElement * element)
}
static GstElement *
rtsp_media_factory_uri_get_element (GstRTSPMediaFactory * factory,
rtsp_media_factory_uri_create_element (GstRTSPMediaFactory * factory,
const GstRTSPUrl * url)
{
GstElement *topbin, *element, *uribin;

View file

@ -58,7 +58,7 @@ static void gst_rtsp_media_factory_finalize (GObject * obj);
static gchar *default_gen_key (GstRTSPMediaFactory * factory,
const GstRTSPUrl * url);
static GstElement *default_get_element (GstRTSPMediaFactory * factory,
static GstElement *default_create_element (GstRTSPMediaFactory * factory,
const GstRTSPUrl * url);
static GstRTSPMedia *default_construct (GstRTSPMediaFactory * factory,
const GstRTSPUrl * url);
@ -139,7 +139,7 @@ gst_rtsp_media_factory_class_init (GstRTSPMediaFactoryClass * klass)
G_TYPE_NONE, 1, GST_TYPE_RTSP_MEDIA);
klass->gen_key = default_gen_key;
klass->get_element = default_get_element;
klass->create_element = default_create_element;
klass->construct = default_construct;
klass->configure = default_configure;
klass->create_pipeline = default_create_pipeline;
@ -578,13 +578,15 @@ media_unprepared (GstRTSPMedia * media, GstRTSPMediaFactory * factory)
* @factory: a #GstRTSPMediaFactory
* @url: the url used
*
* Prepare the media object and create its streams. Implementations
* Construct the media 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.
* One or more GstRTSPStream objects should be created from the result
* with gst_rtsp_media_create_stream ().
*
* After the media is constructed, it can be configured and then prepared
* with gst_rtsp_media_prepare ().
*
* Returns: (transfer full): a new #GstRTSPMedia if the media could be prepared.
*/
@ -596,6 +598,9 @@ gst_rtsp_media_factory_construct (GstRTSPMediaFactory * factory,
GstRTSPMedia *media;
GstRTSPMediaFactoryClass *klass;
g_return_val_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory), NULL);
g_return_val_if_fail (url != NULL, NULL);
klass = GST_RTSP_MEDIA_FACTORY_GET_CLASS (factory);
/* convert the url to a key for the hashtable. NULL return or a NULL function
@ -676,7 +681,7 @@ default_gen_key (GstRTSPMediaFactory * factory, const GstRTSPUrl * url)
}
static GstElement *
default_get_element (GstRTSPMediaFactory * factory, const GstRTSPUrl * url)
default_create_element (GstRTSPMediaFactory * factory, const GstRTSPUrl * url)
{
GstElement *element;
GError *error = NULL;
@ -718,63 +723,6 @@ parse_error:
}
}
/* try to find all the payloader elements, they should be named 'pay%d'. for
* each of the payloaders we will create a stream and collect the source pad. */
void
gst_rtsp_media_factory_collect_streams (GstRTSPMediaFactory * factory,
const GstRTSPUrl * url, GstRTSPMedia * media)
{
GstElement *element, *elem;
GstPad *pad;
gint i;
GstRTSPMediaStream *stream;
gboolean have_elem;
element = media->element;
have_elem = TRUE;
for (i = 0; have_elem; i++) {
gchar *name;
have_elem = FALSE;
name = g_strdup_printf ("pay%d", i);
if ((elem = gst_bin_get_by_name (GST_BIN (element), name))) {
/* create the stream */
stream = g_new0 (GstRTSPMediaStream, 1);
stream->payloader = elem;
GST_INFO ("found stream %d with payloader %p", i, elem);
pad = gst_element_get_static_pad (elem, "src");
/* ghost the pad of the payloader to the element */
stream->srcpad = gst_ghost_pad_new (name, pad);
g_object_unref (pad);
gst_pad_set_active (stream->srcpad, TRUE);
gst_element_add_pad (media->element, stream->srcpad);
gst_object_unref (elem);
/* add stream now */
g_array_append_val (media->streams, stream);
have_elem = TRUE;
}
g_free (name);
name = g_strdup_printf ("dynpay%d", i);
if ((elem = gst_bin_get_by_name (GST_BIN (element), name))) {
/* a stream that will dynamically create pads to provide RTP packets */
GST_INFO ("found dynamic element %d, %p", i, elem);
media->dynamic = g_list_prepend (media->dynamic, elem);
have_elem = TRUE;
}
g_free (name);
}
}
static GstRTSPMedia *
default_construct (GstRTSPMediaFactory * factory, const GstRTSPUrl * url)
{
@ -787,10 +735,7 @@ default_construct (GstRTSPMediaFactory * factory, const GstRTSPUrl * url)
if (!klass->create_pipeline)
goto no_create;
if (klass->get_element)
element = klass->get_element (factory, url);
else
element = NULL;
element = gst_rtsp_media_factory_create_element (factory, url);
if (element == NULL)
goto no_element;
@ -798,12 +743,12 @@ default_construct (GstRTSPMediaFactory * factory, const GstRTSPUrl * url)
media = gst_rtsp_media_new ();
media->element = element;
gst_rtsp_media_collect_streams (media);
media->pipeline = klass->create_pipeline (factory, media);
if (media->pipeline == NULL)
goto no_pipeline;
gst_rtsp_media_factory_collect_streams (factory, url, media);
return media;
/* ERRORS */
@ -879,16 +824,34 @@ default_configure (GstRTSPMediaFactory * factory, GstRTSPMedia * media)
}
/**
* gst_rtsp_media_factory_get_element:
* gst_rtsp_media_factory_create_element:
* @factory: a #GstRTSPMediaFactory
* @url: the url used
*
* Construct and return a #GstElement that is a #GstBin containing
* the elements to use for streaming the media.
*
* The bin should contain payloaders pay%d for each stream. The default
* implementation of this function returns the bin created from the
* launch parameter.
*
* Returns: (transfer floating) a new #GstElement.
*/
GstElement *
gst_rtsp_media_factory_get_element (GstRTSPMediaFactory * factory,
gst_rtsp_media_factory_create_element (GstRTSPMediaFactory * factory,
const GstRTSPUrl * url)
{
GstRTSPMediaFactoryClass *klass = GST_RTSP_MEDIA_FACTORY_GET_CLASS (factory);
return klass->get_element (factory, url);
GstRTSPMediaFactoryClass *klass;
GstElement *result;
g_return_val_if_fail (GST_IS_RTSP_MEDIA_FACTORY (factory), NULL);
klass = GST_RTSP_MEDIA_FACTORY_GET_CLASS (factory);
if (klass->create_element)
result = klass->create_element (factory, url);
else
result = NULL;
return result;
}

View file

@ -82,13 +82,13 @@ struct _GstRTSPMediaFactory {
* @gen_key: convert @url to a key for caching shared #GstRTSPMedia objects.
* The default implementation of this function will use the complete URL
* including the query parameters to return a key.
* @get_element: Construct and return a #GstElement that is a #GstBin containing
* @create_element: Construct and return a #GstElement that is a #GstBin containing
* the elements to use for streaming the media. The bin should contain
* payloaders pay%d for each stream. The default implementation of this
* function 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
* function calls create_element to retrieve an element and then looks for
* pay%d to create the streams.
* @configure: configure the media created with @construct. The default
* implementation will configure the 'shared' property of the media.
@ -104,7 +104,7 @@ struct _GstRTSPMediaFactoryClass {
gchar * (*gen_key) (GstRTSPMediaFactory *factory, const GstRTSPUrl *url);
GstElement * (*get_element) (GstRTSPMediaFactory *factory, const GstRTSPUrl *url);
GstElement * (*create_element) (GstRTSPMediaFactory *factory, const GstRTSPUrl *url);
GstRTSPMedia * (*construct) (GstRTSPMediaFactory *factory, const GstRTSPUrl *url);
void (*configure) (GstRTSPMediaFactory *factory, GstRTSPMedia *media);
GstElement * (*create_pipeline) (GstRTSPMediaFactory *factory, GstRTSPMedia *media);
@ -145,14 +145,11 @@ void gst_rtsp_media_factory_set_multicast_group (GstRTSPMediaFa
gchar * gst_rtsp_media_factory_get_multicast_group (GstRTSPMediaFactory * factory);
/* creating the media from the factory and a url */
GstRTSPMedia * gst_rtsp_media_factory_construct (GstRTSPMediaFactory *factory,
const GstRTSPUrl *url);
GstRTSPMedia * gst_rtsp_media_factory_construct (GstRTSPMediaFactory *factory,
const GstRTSPUrl *url);
void gst_rtsp_media_factory_collect_streams (GstRTSPMediaFactory *factory,
const GstRTSPUrl *url,
GstRTSPMedia *media);
GstElement * gst_rtsp_media_factory_get_element (GstRTSPMediaFactory *factory, const GstRTSPUrl *url);
GstElement * gst_rtsp_media_factory_create_element (GstRTSPMediaFactory *factory,
const GstRTSPUrl *url);
G_END_DECLS

File diff suppressed because it is too large Load diff

View file

@ -36,115 +36,17 @@ G_BEGIN_DECLS
#define GST_RTSP_MEDIA_CAST(obj) ((GstRTSPMedia*)(obj))
#define GST_RTSP_MEDIA_CLASS_CAST(klass) ((GstRTSPMediaClass*)(klass))
typedef struct _GstRTSPMediaStream GstRTSPMediaStream;
typedef struct _GstRTSPMedia GstRTSPMedia;
typedef struct _GstRTSPMediaClass GstRTSPMediaClass;
typedef struct _GstRTSPMediaTrans GstRTSPMediaTrans;
typedef gboolean (*GstRTSPSendFunc) (GstBuffer *buffer, guint8 channel, gpointer user_data);
typedef void (*GstRTSPKeepAliveFunc) (gpointer user_data);
/**
* GstRTSPMediaTrans:
* @idx: a stream index
* @send_rtp: callback for sending RTP messages
* @send_rtcp: callback for sending RTCP messages
* @send_rtp_list: callback for sending RTP messages
* @send_rtcp_list: callback for sending RTCP messages
* @user_data: user data passed in the callbacks
* @notify: free function for the user_data.
* @keep_alive: keep alive callback
* @ka_user_data: data passed to @keep_alive
* @ka_notify: called when @ka_user_data is freed
* @active: if we are actively sending
* @timeout: if we timed out
* @transport: a transport description
* @rtpsource: the receiver rtp source object
*
* A Transport description for stream @idx
*/
struct _GstRTSPMediaTrans {
guint idx;
GstRTSPSendFunc send_rtp;
GstRTSPSendFunc send_rtcp;
gpointer user_data;
GDestroyNotify notify;
GstRTSPKeepAliveFunc keep_alive;
gpointer ka_user_data;
GDestroyNotify ka_notify;
gboolean active;
gboolean timeout;
GstRTSPTransport *transport;
GObject *rtpsource;
};
#include "rtsp-stream.h"
#include "rtsp-auth.h"
/**
* GstRTSPMediaStream:
* @srcpad: the srcpad of the stream
* @payloader: the payloader of the format
* @prepared: if the stream is prepared for streaming
* @recv_rtp_sink: sinkpad for RTP buffers
* @recv_rtcp_sink: sinkpad for RTCP buffers
* @send_rtp_src: srcpad for RTP buffers
* @send_rtcp_src: srcpad for RTCP buffers
* @udpsrc: the udp source elements for RTP/RTCP
* @udpsink: the udp sink elements for RTP/RTCP
* @appsrc: the app source elements for RTP/RTCP
* @appsink: the app sink elements for RTP/RTCP
* @server_port: the server ports for this stream
* @caps_sig: the signal id for detecting caps
* @caps: the caps of the stream
* @tranports: the current transports being streamed
*
* The definition of a media stream. The streams are identified by @id.
*/
struct _GstRTSPMediaStream {
GstPad *srcpad;
GstElement *payloader;
gboolean prepared;
/* pads on the rtpbin */
GstPad *recv_rtcp_sink;
GstPad *recv_rtp_sink;
GstPad *send_rtp_sink;
GstPad *send_rtp_src;
GstPad *send_rtcp_src;
/* the RTPSession object */
GObject *session;
/* sinks used for sending and receiving RTP and RTCP, they share
* sockets */
GstElement *udpsrc[2];
GstElement *udpsink[2];
/* for TCP transport */
GstElement *appsrc[2];
GstElement *appqueue[2];
GstElement *appsink[2];
GstElement *tee[2];
GstElement *selector[2];
/* server ports for sending/receiving */
GstRTSPRange server_port;
/* the caps of the stream */
gulong caps_sig;
GstCaps *caps;
/* transports we stream to */
GList *transports;
};
/**
* GstRTSPMediaStatus:
* @GST_RTSP_MEDIA_STATUS_UNPREPARED: media pipeline not prerolled
* @GST_RTSP_MEDIA_STATUS_UNPREPARING: media pipeline is busy doing a clean
* shutdown.
* @GST_RTSP_MEDIA_STATUS_PREPARING: media pipeline is prerolling
* @GST_RTSP_MEDIA_STATUS_PREPARED: media pipeline is prerolled
* @GST_RTSP_MEDIA_STATUS_ERROR: media pipeline is in error
@ -152,10 +54,11 @@ struct _GstRTSPMediaStream {
* The state of the media pipeline.
*/
typedef enum {
GST_RTSP_MEDIA_STATUS_UNPREPARED = 0,
GST_RTSP_MEDIA_STATUS_PREPARING = 1,
GST_RTSP_MEDIA_STATUS_PREPARED = 2,
GST_RTSP_MEDIA_STATUS_ERROR = 3
GST_RTSP_MEDIA_STATUS_UNPREPARED = 0,
GST_RTSP_MEDIA_STATUS_UNPREPARING = 1,
GST_RTSP_MEDIA_STATUS_PREPARING = 2,
GST_RTSP_MEDIA_STATUS_PREPARED = 3,
GST_RTSP_MEDIA_STATUS_ERROR = 4
} GstRTSPMediaStatus;
/**
@ -168,10 +71,10 @@ typedef enum {
* @reused: if this media has been reused
* @is_ipv6: if this media is using ipv6
* @element: the data providing element
* @streams: the different streams provided by @element
* @streams: the different #GstRTSPStream provided by @element
* @dynamic: list of dynamic elements managed by @element
* @status: the status of the media pipeline
* @active: the number of active connections
* @n_active: the number of active connections
* @pipeline: the toplevel pipeline
* @fakesink: for making state changes async
* @source: the bus watch for pipeline messages.
@ -184,7 +87,7 @@ typedef enum {
* @range: the range of the media being streamed
*
* A class that contains the GStreamer element along with a list of
* #GstRTSPMediaStream objects that can produce data.
* #GstRTSPStream objects that can produce data.
*
* This object is usually created from a #GstRTSPMediaFactory.
*/
@ -205,11 +108,10 @@ struct _GstRTSPMedia {
gchar *multicast_group;
GstElement *element;
GArray *streams;
GPtrArray *streams;
GList *dynamic;
GstRTSPMediaStatus status;
gint active;
gboolean eos_pending;
gint n_active;
gboolean adding;
/* the pipeline for the media */
@ -251,15 +153,15 @@ struct _GstRTSPMediaClass {
GThread *thread;
/* vmethods */
gboolean (*handle_message) (GstRTSPMedia *media, GstMessage *message);
gboolean (*unprepare) (GstRTSPMedia *media);
void (*handle_mtu) (GstRTSPMedia *media, guint mtu);
gboolean (*handle_message) (GstRTSPMedia *media, GstMessage *message);
gboolean (*unprepare) (GstRTSPMedia *media);
void (*handle_mtu) (GstRTSPMedia *media, guint mtu);
/* signals */
gboolean (*prepared) (GstRTSPMedia *media);
gboolean (*unprepared) (GstRTSPMedia *media);
gboolean (*prepared) (GstRTSPMedia *media);
gboolean (*unprepared) (GstRTSPMedia *media);
gboolean (*new_state) (GstRTSPMedia *media, GstState state);
gboolean (*new_state) (GstRTSPMedia *media, GstState state);
};
GType gst_rtsp_media_get_type (void);
@ -294,24 +196,24 @@ gboolean gst_rtsp_media_prepare (GstRTSPMedia *media);
gboolean gst_rtsp_media_is_prepared (GstRTSPMedia *media);
gboolean gst_rtsp_media_unprepare (GstRTSPMedia *media);
/* creating streams */
void gst_rtsp_media_collect_streams (GstRTSPMedia *media);
GstRTSPStream * gst_rtsp_media_create_stream (GstRTSPMedia *media,
GstElement *payloader,
GstPad *srcpad);
/* dealing with the media */
guint gst_rtsp_media_n_streams (GstRTSPMedia *media);
GstRTSPMediaStream * gst_rtsp_media_get_stream (GstRTSPMedia *media, guint idx);
GstRTSPStream * gst_rtsp_media_get_stream (GstRTSPMedia *media, guint idx);
gboolean gst_rtsp_media_seek (GstRTSPMedia *media, GstRTSPTimeRange *range);
gchar * gst_rtsp_media_get_range_string (GstRTSPMedia *media, gboolean play);
GstFlowReturn gst_rtsp_media_stream_rtp (GstRTSPMediaStream *stream, GstBuffer *buffer);
GstFlowReturn gst_rtsp_media_stream_rtcp (GstRTSPMediaStream *stream, GstBuffer *buffer);
gboolean gst_rtsp_media_set_state (GstRTSPMedia *media, GstState state, GArray *transports);
void gst_rtsp_media_remove_elements (GstRTSPMedia *media);
gboolean gst_rtsp_media_set_state (GstRTSPMedia *media, GstState state,
GPtrArray *transports);
void gst_rtsp_media_handle_mtu (GstRTSPMedia *media, guint mtu);
void gst_rtsp_media_trans_cleanup (GstRTSPMediaTrans *trans);
G_END_DECLS
#endif /* __GST_RTSP_MEDIA_H__ */

View file

@ -48,7 +48,7 @@ gst_rtsp_sdp_from_media (GstSDPMessage * sdp, GstSDPInfo * info,
g_free (rangestr);
for (i = 0; i < n_streams; i++) {
GstRTSPMediaStream *stream;
GstRTSPStream *stream;
GstSDPMedia *smedia;
GstStructure *s;
const gchar *caps_str, *caps_enc, *caps_params;

View file

@ -0,0 +1,202 @@
/* 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-session.h"
#undef DEBUG
#define DEFAULT_TIMEOUT 60
enum
{
PROP_0,
PROP_LAST
};
GST_DEBUG_CATEGORY_STATIC (rtsp_session_media_debug);
#define GST_CAT_DEFAULT rtsp_session_media_debug
static void gst_rtsp_session_media_finalize (GObject * obj);
G_DEFINE_TYPE (GstRTSPSessionMedia, gst_rtsp_session_media, G_TYPE_OBJECT);
static void
gst_rtsp_session_media_class_init (GstRTSPSessionMediaClass * klass)
{
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = gst_rtsp_session_media_finalize;
GST_DEBUG_CATEGORY_INIT (rtsp_session_media_debug, "rtspsessionmedia", 0,
"GstRTSPSessionMedia");
}
static void
gst_rtsp_session_media_init (GstRTSPSessionMedia * media)
{
media->state = GST_RTSP_STATE_INIT;
}
static void
gst_rtsp_session_media_finalize (GObject * obj)
{
GstRTSPSessionMedia *media;
media = GST_RTSP_SESSION_MEDIA (obj);
GST_INFO ("free session media %p", media);
gst_rtsp_session_media_set_state (media, GST_STATE_NULL);
g_ptr_array_unref (media->transports);
gst_rtsp_url_free (media->url);
g_object_unref (media->media);
G_OBJECT_CLASS (gst_rtsp_session_media_parent_class)->finalize (obj);
}
static void
free_session_media (gpointer data)
{
if (data)
g_object_unref (data);
}
/**
* gst_rtsp_session_media_new:
* @url: the #GstRTSPUrl
* @media: the #GstRTSPMedia
*
* Create a new #GstRTPSessionMedia that manages the streams
* in @media for @url. @media should be prepared.
*
* Ownership is taken of @media.
*
* Returns: a new #GstRTSPSessionMedia.
*/
GstRTSPSessionMedia *
gst_rtsp_session_media_new (const GstRTSPUrl * url, GstRTSPMedia * media)
{
GstRTSPSessionMedia *result;
guint n_streams;
g_return_val_if_fail (url != NULL, NULL);
g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
g_return_val_if_fail (media->status == GST_RTSP_MEDIA_STATUS_PREPARED, NULL);
result = g_object_new (GST_TYPE_RTSP_SESSION_MEDIA, NULL);
result->url = gst_rtsp_url_copy ((GstRTSPUrl *) url);
result->media = media;
/* prealloc the streams now, filled with NULL */
n_streams = gst_rtsp_media_n_streams (media);
result->transports = g_ptr_array_new_full (n_streams, free_session_media);
g_ptr_array_set_size (result->transports, n_streams);
return result;
}
/**
* gst_rtsp_session_media_get_transport:
* @media: a #GstRTSPSessionMedia
* @idx: the stream index
*
* Get a previously created or create a new #GstRTSPStreamTransport at @idx.
*
* Returns: a #GstRTSPStreamTransport that is valid until the session of @media
* is unreffed.
*/
GstRTSPStreamTransport *
gst_rtsp_session_media_get_transport (GstRTSPSessionMedia * media, guint idx)
{
GstRTSPStreamTransport *result;
g_return_val_if_fail (GST_IS_RTSP_SESSION_MEDIA (media), NULL);
g_return_val_if_fail (media->media != NULL, NULL);
if (idx >= media->transports->len)
return NULL;
result = g_ptr_array_index (media->transports, idx);
if (result == NULL) {
GstRTSPStream *stream;
stream = gst_rtsp_media_get_stream (media->media, idx);
if (stream == NULL)
goto no_media;
result = gst_rtsp_stream_transport_new (stream);
g_ptr_array_index (media->transports, idx) = result;
}
return result;
/* ERRORS */
no_media:
{
return NULL;
}
}
/**
* gst_rtsp_session_media_alloc_channels:
* @media: a #GstRTSPSessionMedia
* @range: a #GstRTSPRange
*
* Fill @range with the next available min and max channels for
* interleaved transport.
*
* Returns: %TRUE on success.
*/
gboolean
gst_rtsp_session_media_alloc_channels (GstRTSPSessionMedia * media,
GstRTSPRange * range)
{
g_return_val_if_fail (GST_IS_RTSP_SESSION_MEDIA (media), FALSE);
range->min = media->counter++;
range->max = media->counter++;
return TRUE;
}
/**
* gst_rtsp_session_media_set_state:
* @media: a #GstRTSPSessionMedia
* @state: the new state
*
* Tell the media object @media to change to @state.
*
* Returns: %TRUE on success.
*/
gboolean
gst_rtsp_session_media_set_state (GstRTSPSessionMedia * media, GstState state)
{
gboolean ret;
g_return_val_if_fail (GST_IS_RTSP_SESSION_MEDIA (media), FALSE);
ret = gst_rtsp_media_set_state (media->media, state, media->transports);
return ret;
}

View file

@ -0,0 +1,86 @@
/* 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/gstrtsptransport.h>
#ifndef __GST_RTSP_SESSION_MEDIA_H__
#define __GST_RTSP_SESSION_MEDIA_H__
G_BEGIN_DECLS
#define GST_TYPE_RTSP_SESSION_MEDIA (gst_rtsp_session_media_get_type ())
#define GST_IS_RTSP_SESSION_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_SESSION_MEDIA))
#define GST_IS_RTSP_SESSION_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_SESSION_MEDIA))
#define GST_RTSP_SESSION_MEDIA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_SESSION_MEDIA, GstRTSPSessionMediaClass))
#define GST_RTSP_SESSION_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_SESSION_MEDIA, GstRTSPSessionMedia))
#define GST_RTSP_SESSION_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_SESSION_MEDIA, GstRTSPSessionMediaClass))
#define GST_RTSP_SESSION_MEDIA_CAST(obj) ((GstRTSPSessionMedia*)(obj))
#define GST_RTSP_SESSION_MEDIA_CLASS_CAST(klass) ((GstRTSPSessionMediaClass*)(klass))
typedef struct _GstRTSPSessionMedia GstRTSPSessionMedia;
typedef struct _GstRTSPSessionMediaClass GstRTSPSessionMediaClass;
/**
* GstRTSPSessionMedia:
* @url: the url of the media
* @media: the pipeline for the media
* @state: the server state
* @counter: counter for channels
* @transports: array of #GstRTSPStreamTransport with the configuration
* for the transport for each selected stream from @media.
*
* State of a client session regarding a specific media identified by uri.
*/
struct _GstRTSPSessionMedia
{
GObject parent;
GstRTSPUrl *url;
GstRTSPMedia *media;
GstRTSPState state;
guint counter;
GPtrArray *transports;
};
struct _GstRTSPSessionMediaClass
{
GObjectClass parent_class;
};
GType gst_rtsp_session_media_get_type (void);
GstRTSPSessionMedia * gst_rtsp_session_media_new (const GstRTSPUrl *url,
GstRTSPMedia *media);
/* control media */
gboolean gst_rtsp_session_media_set_state (GstRTSPSessionMedia *media,
GstState state);
/* get stream transport config */
GstRTSPStreamTransport * gst_rtsp_session_media_get_transport (GstRTSPSessionMedia *media,
guint idx);
gboolean gst_rtsp_session_media_alloc_channels (GstRTSPSessionMedia *media,
GstRTSPRange *range);
G_END_DECLS
#endif /* __GST_RTSP_SESSION_MEDIA_H__ */

View file

@ -76,51 +76,6 @@ gst_rtsp_session_init (GstRTSPSession * session)
gst_rtsp_session_touch (session);
}
static void
gst_rtsp_session_free_stream (GstRTSPSessionStream * stream)
{
GST_INFO ("free session stream %p", stream);
/* remove callbacks now */
gst_rtsp_session_stream_set_callbacks (stream, NULL, NULL, NULL, NULL);
gst_rtsp_session_stream_set_keepalive (stream, NULL, NULL, NULL);
gst_rtsp_media_trans_cleanup (&stream->trans);
g_free (stream);
}
static void
gst_rtsp_session_free_media (GstRTSPSessionMedia * media,
GstRTSPSession * session)
{
guint size, i;
size = media->streams->len;
GST_INFO ("free session media %p", media);
gst_rtsp_session_media_set_state (media, GST_STATE_NULL);
for (i = 0; i < size; i++) {
GstRTSPSessionStream *stream;
stream = g_array_index (media->streams, GstRTSPSessionStream *, i);
if (stream)
gst_rtsp_session_free_stream (stream);
}
g_array_free (media->streams, TRUE);
if (media->url)
gst_rtsp_url_free (media->url);
if (media->media)
g_object_unref (media->media);
g_free (media);
}
static void
gst_rtsp_session_finalize (GObject * obj)
{
@ -131,9 +86,7 @@ gst_rtsp_session_finalize (GObject * obj)
GST_INFO ("finalize session %p", session);
/* free all media */
g_list_foreach (session->medias, (GFunc) gst_rtsp_session_free_media,
session);
g_list_free (session->medias);
g_list_free_full (session->medias, g_object_unref);
/* free session id */
g_free (session->sessionid);
@ -196,24 +149,13 @@ gst_rtsp_session_manage_media (GstRTSPSession * sess, const GstRTSPUrl * uri,
GstRTSPMedia * media)
{
GstRTSPSessionMedia *result;
guint n_streams;
g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), NULL);
g_return_val_if_fail (uri != NULL, NULL);
g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
g_return_val_if_fail (media->status == GST_RTSP_MEDIA_STATUS_PREPARED, NULL);
result = g_new0 (GstRTSPSessionMedia, 1);
result->media = media;
result->url = gst_rtsp_url_copy ((GstRTSPUrl *) uri);
result->state = GST_RTSP_STATE_INIT;
/* prealloc the streams now, filled with NULL */
n_streams = gst_rtsp_media_n_streams (media);
result->streams =
g_array_sized_new (FALSE, TRUE, sizeof (GstRTSPSessionStream *),
n_streams);
g_array_set_size (result->streams, n_streams);
result = gst_rtsp_session_media_new (uri, media);
sess->medias = g_list_prepend (sess->medias, result);
@ -235,24 +177,15 @@ gboolean
gst_rtsp_session_release_media (GstRTSPSession * sess,
GstRTSPSessionMedia * media)
{
GList *walk, *next;
GList *find;
g_return_val_if_fail (GST_IS_RTSP_SESSION (sess), FALSE);
g_return_val_if_fail (media != NULL, FALSE);
for (walk = sess->medias; walk;) {
GstRTSPSessionMedia *find;
find = (GstRTSPSessionMedia *) walk->data;
next = g_list_next (walk);
if (find == media) {
sess->medias = g_list_delete_link (sess->medias, walk);
gst_rtsp_session_free_media (find, sess);
break;
}
walk = next;
find = g_list_find (sess->medias, media);
if (find) {
g_object_unref (find->data);
sess->medias = g_list_delete_link (sess->medias, find);
}
return (sess->medias != NULL);
}
@ -280,7 +213,7 @@ gst_rtsp_session_get_media (GstRTSPSession * sess, const GstRTSPUrl * url)
for (walk = sess->medias; walk; walk = g_list_next (walk)) {
result = (GstRTSPSessionMedia *) walk->data;
if (strcmp (result->url->abspath, url->abspath) == 0)
if (g_str_equal (result->url->abspath, url->abspath))
break;
result = NULL;
@ -288,61 +221,6 @@ gst_rtsp_session_get_media (GstRTSPSession * sess, const GstRTSPUrl * url)
return result;
}
/**
* gst_rtsp_session_media_get_stream:
* @media: a #GstRTSPSessionMedia
* @idx: the stream index
*
* Get a previously created or create a new #GstRTSPSessionStream at @idx.
*
* Returns: a #GstRTSPSessionStream that is valid until the session of @media
* is unreffed.
*/
GstRTSPSessionStream *
gst_rtsp_session_media_get_stream (GstRTSPSessionMedia * media, guint idx)
{
GstRTSPSessionStream *result;
g_return_val_if_fail (media != NULL, NULL);
g_return_val_if_fail (media->media != NULL, NULL);
if (idx >= media->streams->len)
return NULL;
result = g_array_index (media->streams, GstRTSPSessionStream *, idx);
if (result == NULL) {
GstRTSPMediaStream *media_stream;
media_stream = gst_rtsp_media_get_stream (media->media, idx);
if (media_stream == NULL)
goto no_media;
result = g_new0 (GstRTSPSessionStream, 1);
result->trans.idx = idx;
result->trans.transport = NULL;
result->media_stream = media_stream;
g_array_index (media->streams, GstRTSPSessionStream *, idx) = result;
}
return result;
/* ERRORS */
no_media:
{
return NULL;
}
}
gboolean
gst_rtsp_session_media_alloc_channels (GstRTSPSessionMedia * media,
GstRTSPRange * range)
{
range->min = media->counter++;
range->max = media->counter++;
return TRUE;
}
/**
* gst_rtsp_session_new:
*
@ -492,124 +370,3 @@ gst_rtsp_session_is_expired (GstRTSPSession * session, GTimeVal * now)
return res;
}
/**
* 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. This function takes ownership of the passed @ct.
*
* Returns: a server transport or NULL if something went wrong. Use
* gst_rtsp_transport_free () after usage.
*/
GstRTSPTransport *
gst_rtsp_session_stream_set_transport (GstRTSPSessionStream * stream,
GstRTSPTransport * ct)
{
GstRTSPTransport *st;
g_return_val_if_fail (stream != NULL, NULL);
g_return_val_if_fail (ct != NULL, NULL);
/* prepare the server transport */
gst_rtsp_transport_new (&st);
st->trans = ct->trans;
st->profile = ct->profile;
st->lower_transport = ct->lower_transport;
switch (st->lower_transport) {
case GST_RTSP_LOWER_TRANS_UDP:
st->client_port = ct->client_port;
st->server_port = stream->media_stream->server_port;
break;
case GST_RTSP_LOWER_TRANS_UDP_MCAST:
ct->port = st->port = stream->media_stream->server_port;
st->destination = g_strdup (ct->destination);
st->ttl = ct->ttl;
break;
case GST_RTSP_LOWER_TRANS_TCP:
st->interleaved = ct->interleaved;
default:
break;
}
if (stream->media_stream->session)
g_object_get (stream->media_stream->session, "internal-ssrc", &st->ssrc,
NULL);
/* keep track of the transports in the stream. */
if (stream->trans.transport)
gst_rtsp_transport_free (stream->trans.transport);
stream->trans.transport = ct;
return st;
}
/**
* gst_rtsp_session_stream_set_callbacks:
* @stream: a #GstRTSPSessionStream
* @send_rtp: (scope notified): a callback called when RTP should be sent
* @send_rtcp: (scope notified): a callback called when RTCP should be sent
* @user_data: user data passed to callbacks
* @notify: called with the user_data when no longer needed.
*
* Install callbacks that will be called when data for a stream should be sent
* to a client. This is usually used when sending RTP/RTCP over TCP.
*/
void
gst_rtsp_session_stream_set_callbacks (GstRTSPSessionStream * stream,
GstRTSPSendFunc send_rtp, GstRTSPSendFunc send_rtcp,
gpointer user_data, GDestroyNotify notify)
{
stream->trans.send_rtp = send_rtp;
stream->trans.send_rtcp = send_rtcp;
if (stream->trans.notify)
stream->trans.notify (stream->trans.user_data);
stream->trans.user_data = user_data;
stream->trans.notify = notify;
}
/**
* gst_rtsp_session_stream_set_keepalive:
* @stream: a #GstRTSPSessionStream
* @keep_alive: a callback called when the receiver is active
* @user_data: user data passed to callback
* @notify: called with the user_data when no longer needed.
*
* Install callbacks that will be called when RTCP packets are received from the
* receiver of @stream.
*/
void
gst_rtsp_session_stream_set_keepalive (GstRTSPSessionStream * stream,
GstRTSPKeepAliveFunc keep_alive, gpointer user_data, GDestroyNotify notify)
{
stream->trans.keep_alive = keep_alive;
if (stream->trans.ka_notify)
stream->trans.ka_notify (stream->trans.ka_user_data);
stream->trans.ka_user_data = user_data;
stream->trans.ka_notify = notify;
}
/**
* gst_rtsp_session_media_set_state:
* @media: a #GstRTSPSessionMedia
* @state: the new state
*
* Tell the media object @media to change to @state.
*
* Returns: %TRUE on success.
*/
gboolean
gst_rtsp_session_media_set_state (GstRTSPSessionMedia * media, GstState state)
{
gboolean ret;
g_return_val_if_fail (media != NULL, FALSE);
ret = gst_rtsp_media_set_state (media->media, state, media->streams);
return ret;
}

View file

@ -38,49 +38,8 @@ G_BEGIN_DECLS
typedef struct _GstRTSPSession GstRTSPSession;
typedef struct _GstRTSPSessionClass GstRTSPSessionClass;
typedef struct _GstRTSPSessionStream GstRTSPSessionStream;
typedef struct _GstRTSPSessionMedia GstRTSPSessionMedia;
#include "rtsp-media.h"
/**
* GstRTSPSessionStream:
* @trans: the media transport
* @media_stream: the controlled media stream
*
* Configuration of a stream. A stream is an audio or video stream related to a
* media.
*/
struct _GstRTSPSessionStream
{
GstRTSPMediaTrans trans;
/* the stream of the media */
GstRTSPMediaStream *media_stream;
};
/**
* GstRTSPSessionMedia:
*
* State of a client session regarding a specific media identified by uri.
*/
struct _GstRTSPSessionMedia
{
/* the url of the media */
GstRTSPUrl *url;
/* the pipeline for the media */
GstRTSPMedia *media;
/* the server state */
GstRTSPState state;
/* counter for channels */
guint counter;
/* configuration for the different streams */
GArray *streams;
};
#include "rtsp-session-media.h"
/**
* GstRTSPSession:
@ -138,28 +97,6 @@ gboolean gst_rtsp_session_release_media (GstRTSPSession *se
/* get media in a session */
GstRTSPSessionMedia * gst_rtsp_session_get_media (GstRTSPSession *sess,
const GstRTSPUrl *url);
/* control media */
gboolean gst_rtsp_session_media_set_state (GstRTSPSessionMedia *media, GstState state);
/* get stream config */
GstRTSPSessionStream * gst_rtsp_session_media_get_stream (GstRTSPSessionMedia *media,
guint idx);
gboolean gst_rtsp_session_media_alloc_channels (GstRTSPSessionMedia *media,
GstRTSPRange *range);
/* configure transport */
GstRTSPTransport * gst_rtsp_session_stream_set_transport (GstRTSPSessionStream *stream,
GstRTSPTransport *ct);
void gst_rtsp_session_stream_set_callbacks (GstRTSPSessionStream *stream,
GstRTSPSendFunc send_rtp,
GstRTSPSendFunc send_rtcp,
gpointer user_data,
GDestroyNotify notify);
void gst_rtsp_session_stream_set_keepalive (GstRTSPSessionStream *stream,
GstRTSPKeepAliveFunc keep_alive,
gpointer user_data,
GDestroyNotify notify);
G_END_DECLS

View file

@ -0,0 +1,202 @@
/* 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 <stdlib.h>
#include <gst/app/gstappsrc.h>
#include <gst/app/gstappsink.h>
#include "rtsp-stream-transport.h"
enum
{
PROP_0,
PROP_LAST
};
GST_DEBUG_CATEGORY_STATIC (rtsp_stream_transport_debug);
#define GST_CAT_DEFAULT rtsp_stream_transport_debug
static void gst_rtsp_stream_transport_finalize (GObject * obj);
G_DEFINE_TYPE (GstRTSPStreamTransport, gst_rtsp_stream_transport,
G_TYPE_OBJECT);
static void
gst_rtsp_stream_transport_class_init (GstRTSPStreamTransportClass * klass)
{
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = gst_rtsp_stream_transport_finalize;
GST_DEBUG_CATEGORY_INIT (rtsp_stream_transport_debug, "rtspmediatransport",
0, "GstRTSPStreamTransport");
}
static void
gst_rtsp_stream_transport_init (GstRTSPStreamTransport * trans)
{
}
static void
gst_rtsp_stream_transport_finalize (GObject * obj)
{
GstRTSPStreamTransport *trans;
trans = GST_RTSP_STREAM_TRANSPORT (obj);
/* remove callbacks now */
gst_rtsp_stream_transport_set_callbacks (trans, NULL, NULL, NULL, NULL);
gst_rtsp_stream_transport_set_keepalive (trans, NULL, NULL, NULL);
if (trans->transport)
gst_rtsp_transport_free (trans->transport);
#if 0
if (trans->rtpsource)
g_object_set_qdata (trans->rtpsource, ssrc_stream_map_key, NULL);
#endif
G_OBJECT_CLASS (gst_rtsp_stream_transport_parent_class)->finalize (obj);
}
/**
* gst_rtsp_stream_transport_new:
* @stream: a #GstRTSPStream
*
* Create a new #GstRTSPStreamTransport that can be used for
* @stream.
*
* Returns: a new #GstRTSPStreamTransport
*/
GstRTSPStreamTransport *
gst_rtsp_stream_transport_new (GstRTSPStream * stream)
{
GstRTSPStreamTransport *trans;
g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), NULL);
trans = g_object_new (GST_TYPE_RTSP_STREAM_TRANSPORT, NULL);
trans->stream = stream;
return trans;
}
/**
* gst_rtsp_stream_transport_set_callbacks:
* @trans: a #GstRTSPStreamTransport
* @send_rtp: (scope notified): a callback called when RTP should be sent
* @send_rtcp: (scope notified): a callback called when RTCP should be sent
* @user_data: user data passed to callbacks
* @notify: called with the user_data when no longer needed.
*
* Install callbacks that will be called when data for a stream should be sent
* to a client. This is usually used when sending RTP/RTCP over TCP.
*/
void
gst_rtsp_stream_transport_set_callbacks (GstRTSPStreamTransport * trans,
GstRTSPSendFunc send_rtp, GstRTSPSendFunc send_rtcp,
gpointer user_data, GDestroyNotify notify)
{
trans->send_rtp = send_rtp;
trans->send_rtcp = send_rtcp;
if (trans->notify)
trans->notify (trans->user_data);
trans->user_data = user_data;
trans->notify = notify;
}
/**
* gst_rtsp_stream_transport_set_keepalive:
* @trans: a #GstRTSPStreamTransport
* @keep_alive: a callback called when the receiver is active
* @user_data: user data passed to callback
* @notify: called with the user_data when no longer needed.
*
* Install callbacks that will be called when RTCP packets are received from the
* receiver of @trans.
*/
void
gst_rtsp_stream_transport_set_keepalive (GstRTSPStreamTransport * trans,
GstRTSPKeepAliveFunc keep_alive, gpointer user_data, GDestroyNotify notify)
{
trans->keep_alive = keep_alive;
if (trans->ka_notify)
trans->ka_notify (trans->ka_user_data);
trans->ka_user_data = user_data;
trans->ka_notify = notify;
}
/**
* gst_rtsp_stream_transport_set_transport:
* @trans: a #GstRTSPStreamTransport
* @ct: a client #GstRTSPTransport
*
* Set @ct as the client transport and create and return a matching server
* transport. This function takes ownership of the passed @ct.
*
* Returns: a server transport or NULL if something went wrong. Use
* gst_rtsp_transport_free () after usage.
*/
GstRTSPTransport *
gst_rtsp_stream_transport_set_transport (GstRTSPStreamTransport * trans,
GstRTSPTransport * ct)
{
GstRTSPTransport *st;
g_return_val_if_fail (GST_IS_RTSP_STREAM_TRANSPORT (trans), NULL);
g_return_val_if_fail (ct != NULL, NULL);
/* prepare the server transport */
gst_rtsp_transport_new (&st);
st->trans = ct->trans;
st->profile = ct->profile;
st->lower_transport = ct->lower_transport;
switch (st->lower_transport) {
case GST_RTSP_LOWER_TRANS_UDP:
st->client_port = ct->client_port;
st->server_port = trans->stream->server_port;
break;
case GST_RTSP_LOWER_TRANS_UDP_MCAST:
ct->port = st->port = trans->stream->server_port;
st->destination = g_strdup (ct->destination);
st->ttl = ct->ttl;
break;
case GST_RTSP_LOWER_TRANS_TCP:
st->interleaved = ct->interleaved;
default:
break;
}
if (trans->stream->session)
g_object_get (trans->stream->session, "internal-ssrc", &st->ssrc, NULL);
/* keep track of the transports in the stream. */
if (trans->transport)
gst_rtsp_transport_free (trans->transport);
trans->transport = ct;
return st;
}

View file

@ -0,0 +1,111 @@
/* 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/gstrtsprange.h>
#include <gst/rtsp/gstrtspurl.h>
#ifndef __GST_RTSP_STREAM_TRANSPORT_H__
#define __GST_RTSP_STREAM_TRANSPORT_H__
G_BEGIN_DECLS
/* types for the media */
#define GST_TYPE_RTSP_STREAM_TRANSPORT (gst_rtsp_stream_transport_get_type ())
#define GST_IS_RTSP_STREAM_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_STREAM_TRANSPORT))
#define GST_IS_RTSP_STREAM_TRANSPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_STREAM_TRANSPORT))
#define GST_RTSP_STREAM_TRANSPORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_STREAM_TRANSPORT, GstRTSPStreamTransportClass))
#define GST_RTSP_STREAM_TRANSPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_STREAM_TRANSPORT, GstRTSPStreamTransport))
#define GST_RTSP_STREAM_TRANSPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_STREAM_TRANSPORT, GstRTSPStreamTransportClass))
#define GST_RTSP_STREAM_TRANSPORT_CAST(obj) ((GstRTSPStreamTransport*)(obj))
#define GST_RTSP_STREAM_TRANSPORT_CLASS_CAST(klass) ((GstRTSPStreamTransportClass*)(klass))
typedef struct _GstRTSPStreamTransport GstRTSPStreamTransport;
typedef struct _GstRTSPStreamTransportClass GstRTSPStreamTransportClass;
#include "rtsp-stream.h"
typedef gboolean (*GstRTSPSendFunc) (GstBuffer *buffer, guint8 channel, gpointer user_data);
typedef void (*GstRTSPKeepAliveFunc) (gpointer user_data);
/**
* GstRTSPStreamTransport:
* @parent: parent instance
* @idx: the stream index
* @send_rtp: callback for sending RTP messages
* @send_rtcp: callback for sending RTCP messages
* @send_rtp_list: callback for sending RTP messages
* @send_rtcp_list: callback for sending RTCP messages
* @user_data: user data passed in the callbacks
* @notify: free function for the user_data.
* @keep_alive: keep alive callback
* @ka_user_data: data passed to @keep_alive
* @ka_notify: called when @ka_user_data is freed
* @active: if we are actively sending
* @timeout: if we timed out
* @transport: a transport description
* @rtpsource: the receiver rtp source object
*
* A Transport description for stream @idx
*/
struct _GstRTSPStreamTransport {
GObject parent;
GstRTSPStream *stream;
GstRTSPSendFunc send_rtp;
GstRTSPSendFunc send_rtcp;
gpointer user_data;
GDestroyNotify notify;
GstRTSPKeepAliveFunc keep_alive;
gpointer ka_user_data;
GDestroyNotify ka_notify;
gboolean active;
gboolean timeout;
GstRTSPTransport *transport;
GObject *rtpsource;
};
struct _GstRTSPStreamTransportClass {
GObjectClass parent_class;
};
GType gst_rtsp_stream_transport_get_type (void);
GstRTSPStreamTransport * gst_rtsp_stream_transport_new (GstRTSPStream *stream);
GstRTSPTransport * gst_rtsp_stream_transport_set_transport (GstRTSPStreamTransport *trans,
GstRTSPTransport * ct);
void gst_rtsp_stream_transport_set_callbacks (GstRTSPStreamTransport *trans,
GstRTSPSendFunc send_rtp,
GstRTSPSendFunc send_rtcp,
gpointer user_data,
GDestroyNotify notify);
void gst_rtsp_stream_transport_set_keepalive (GstRTSPStreamTransport *trans,
GstRTSPKeepAliveFunc keep_alive,
gpointer user_data,
GDestroyNotify notify);
G_END_DECLS
#endif /* __GST_RTSP_STREAM_TRANSPORT_H__ */

View file

@ -0,0 +1,971 @@
/* 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 <stdlib.h>
#include <gio/gio.h>
#include <gst/app/gstappsrc.h>
#include <gst/app/gstappsink.h>
#include "rtsp-stream.h"
enum
{
PROP_0,
PROP_LAST
};
GST_DEBUG_CATEGORY_STATIC (rtsp_stream_debug);
#define GST_CAT_DEFAULT rtsp_stream_debug
static GQuark ssrc_stream_map_key;
static void gst_rtsp_stream_finalize (GObject * obj);
G_DEFINE_TYPE (GstRTSPStream, gst_rtsp_stream, G_TYPE_OBJECT);
static void
gst_rtsp_stream_class_init (GstRTSPStreamClass * klass)
{
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = gst_rtsp_stream_finalize;
GST_DEBUG_CATEGORY_INIT (rtsp_stream_debug, "rtspstream", 0, "GstRTSPStream");
ssrc_stream_map_key = g_quark_from_static_string ("GstRTSPServer.stream");
}
static void
gst_rtsp_stream_init (GstRTSPStream * media)
{
}
static void
gst_rtsp_stream_finalize (GObject * obj)
{
GstRTSPStream *stream;
stream = GST_RTSP_STREAM (obj);
g_assert (!stream->is_joined);
gst_object_unref (stream->payloader);
gst_object_unref (stream->srcpad);
if (stream->session)
g_object_unref (stream->session);
if (stream->caps)
gst_caps_unref (stream->caps);
if (stream->send_rtp_sink)
gst_object_unref (stream->send_rtp_sink);
if (stream->send_rtp_src)
gst_object_unref (stream->send_rtp_src);
if (stream->send_rtcp_src)
gst_object_unref (stream->send_rtcp_src);
if (stream->recv_rtcp_sink)
gst_object_unref (stream->recv_rtcp_sink);
if (stream->recv_rtp_sink)
gst_object_unref (stream->recv_rtp_sink);
g_list_free (stream->transports);
G_OBJECT_CLASS (gst_rtsp_stream_parent_class)->finalize (obj);
}
/**
* gst_rtsp_stream_new:
* @idx: an index
* @srcpad: a #GstPad
* @payloader: a #GstElement
*
* Create a new media stream with index @idx that handles RTP data on
* @srcpad and has a payloader element @payloader.
*
* Returns: a new #GstRTSPStream
*/
GstRTSPStream *
gst_rtsp_stream_new (guint idx, GstElement * payloader, GstPad * srcpad)
{
GstRTSPStream *stream;
g_return_val_if_fail (GST_IS_ELEMENT (payloader), NULL);
g_return_val_if_fail (GST_IS_PAD (srcpad), NULL);
g_return_val_if_fail (GST_PAD_IS_SRC (srcpad), NULL);
stream = g_object_new (GST_TYPE_RTSP_STREAM, NULL);
stream->idx = idx;
stream->payloader = gst_object_ref (payloader);
stream->srcpad = gst_object_ref (srcpad);
return stream;
}
static gboolean
alloc_ports (GstRTSPStream * stream)
{
GstStateChangeReturn ret;
GstElement *udpsrc0, *udpsrc1;
GstElement *udpsink0, *udpsink1;
gint tmp_rtp, tmp_rtcp;
guint count;
gint rtpport, rtcpport;
GSocket *socket;
const gchar *host;
g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
udpsrc0 = NULL;
udpsrc1 = NULL;
udpsink0 = NULL;
udpsink1 = NULL;
count = 0;
/* Start with random port */
tmp_rtp = 0;
if (stream->is_ipv6)
host = "udp://[::0]";
else
host = "udp://0.0.0.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, host, NULL, 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, host, NULL, 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), "socket", &socket, NULL);
g_object_set (G_OBJECT (udpsink0), "socket", socket, NULL);
g_object_set (G_OBJECT (udpsink0), "close-socket", FALSE, NULL);
udpsink1 = gst_element_factory_make ("multiudpsink", NULL);
if (!udpsink1)
goto no_udp_protocol;
if (g_object_class_find_property (G_OBJECT_GET_CLASS (udpsink0),
"send-duplicates")) {
g_object_set (G_OBJECT (udpsink0), "send-duplicates", FALSE, NULL);
g_object_set (G_OBJECT (udpsink1), "send-duplicates", FALSE, NULL);
} else {
g_warning
("old multiudpsink version found without send-duplicates property");
}
if (g_object_class_find_property (G_OBJECT_GET_CLASS (udpsink0),
"buffer-size")) {
g_object_set (G_OBJECT (udpsink0), "buffer-size", stream->buffer_size,
NULL);
} else {
GST_WARNING ("multiudpsink version found without buffer-size property");
}
g_object_get (G_OBJECT (udpsrc1), "socket", &socket, NULL);
g_object_set (G_OBJECT (udpsink1), "socket", socket, NULL);
g_object_set (G_OBJECT (udpsink1), "close-socket", FALSE, NULL);
g_object_set (G_OBJECT (udpsink1), "sync", FALSE, NULL);
g_object_set (G_OBJECT (udpsink1), "async", FALSE, NULL);
g_object_set (G_OBJECT (udpsink0), "auto-multicast", FALSE, NULL);
g_object_set (G_OBJECT (udpsink0), "loop", FALSE, NULL);
g_object_set (G_OBJECT (udpsink1), "auto-multicast", FALSE, NULL);
g_object_set (G_OBJECT (udpsink1), "loop", FALSE, NULL);
/* we keep these elements, we will further configure them when the
* client told us to really use the UDP ports. */
stream->udpsrc[0] = udpsrc0;
stream->udpsrc[1] = udpsrc1;
stream->udpsink[0] = udpsink0;
stream->udpsink[1] = udpsink1;
stream->server_port.min = rtpport;
stream->server_port.max = rtcpport;
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;
}
}
/* executed from streaming thread */
static void
caps_notify (GstPad * pad, GParamSpec * unused, GstRTSPStream * stream)
{
GstCaps *newcaps, *oldcaps;
newcaps = gst_pad_get_current_caps (pad);
oldcaps = stream->caps;
stream->caps = newcaps;
if (oldcaps)
gst_caps_unref (oldcaps);
GST_INFO ("stream %p received caps %p, %" GST_PTR_FORMAT, stream, newcaps,
newcaps);
}
static void
dump_structure (const GstStructure * s)
{
gchar *sstr;
sstr = gst_structure_to_string (s);
GST_INFO ("structure: %s", sstr);
g_free (sstr);
}
static GstRTSPStreamTransport *
find_transport (GstRTSPStream * stream, const gchar * rtcp_from)
{
GList *walk;
GstRTSPStreamTransport *result = NULL;
const gchar *tmp;
gchar *dest;
guint port;
if (rtcp_from == NULL)
return NULL;
tmp = g_strrstr (rtcp_from, ":");
if (tmp == NULL)
return NULL;
port = atoi (tmp + 1);
dest = g_strndup (rtcp_from, tmp - rtcp_from);
GST_INFO ("finding %s:%d in %d transports", dest, port,
g_list_length (stream->transports));
for (walk = stream->transports; walk; walk = g_list_next (walk)) {
GstRTSPStreamTransport *trans = walk->data;
gint min, max;
min = trans->transport->client_port.min;
max = trans->transport->client_port.max;
if ((strcmp (trans->transport->destination, dest) == 0) && (min == port
|| max == port)) {
result = trans;
break;
}
}
g_free (dest);
return result;
}
static GstRTSPStreamTransport *
check_transport (GObject * source, GstRTSPStream * stream)
{
GstStructure *stats;
GstRTSPStreamTransport *trans;
/* see if we have a stream to match with the origin of the RTCP packet */
trans = g_object_get_qdata (source, ssrc_stream_map_key);
if (trans == NULL) {
g_object_get (source, "stats", &stats, NULL);
if (stats) {
const gchar *rtcp_from;
dump_structure (stats);
rtcp_from = gst_structure_get_string (stats, "rtcp-from");
if ((trans = find_transport (stream, rtcp_from))) {
GST_INFO ("%p: found transport %p for source %p", stream, trans,
source);
/* keep ref to the source */
trans->rtpsource = source;
g_object_set_qdata (source, ssrc_stream_map_key, trans);
}
gst_structure_free (stats);
}
}
return trans;
}
static void
on_new_ssrc (GObject * session, GObject * source, GstRTSPStream * stream)
{
GstRTSPStreamTransport *trans;
GST_INFO ("%p: new source %p", stream, source);
trans = check_transport (source, stream);
if (trans)
GST_INFO ("%p: source %p for transport %p", stream, source, trans);
}
static void
on_ssrc_sdes (GObject * session, GObject * source, GstRTSPStream * stream)
{
GST_INFO ("%p: new SDES %p", stream, source);
}
static void
on_ssrc_active (GObject * session, GObject * source, GstRTSPStream * stream)
{
GstRTSPStreamTransport *trans;
trans = check_transport (source, stream);
if (trans)
GST_INFO ("%p: source %p in transport %p is active", stream, source, trans);
if (trans && trans->keep_alive)
trans->keep_alive (trans->ka_user_data);
#ifdef DUMP_STATS
{
GstStructure *stats;
g_object_get (source, "stats", &stats, NULL);
if (stats) {
dump_structure (stats);
gst_structure_free (stats);
}
}
#endif
}
static void
on_bye_ssrc (GObject * session, GObject * source, GstRTSPStream * stream)
{
GST_INFO ("%p: source %p bye", stream, source);
}
static void
on_bye_timeout (GObject * session, GObject * source, GstRTSPStream * stream)
{
GstRTSPStreamTransport *trans;
GST_INFO ("%p: source %p bye timeout", stream, source);
if ((trans = g_object_get_qdata (source, ssrc_stream_map_key))) {
trans->rtpsource = NULL;
trans->timeout = TRUE;
}
}
static void
on_timeout (GObject * session, GObject * source, GstRTSPStream * stream)
{
GstRTSPStreamTransport *trans;
GST_INFO ("%p: source %p timeout", stream, source);
if ((trans = g_object_get_qdata (source, ssrc_stream_map_key))) {
trans->rtpsource = NULL;
trans->timeout = TRUE;
}
}
static GstFlowReturn
handle_new_sample (GstAppSink * sink, gpointer user_data)
{
GList *walk;
GstSample *sample;
GstBuffer *buffer;
GstRTSPStream *stream;
sample = gst_app_sink_pull_sample (sink);
if (!sample)
return GST_FLOW_OK;
stream = (GstRTSPStream *) user_data;
buffer = gst_sample_get_buffer (sample);
for (walk = stream->transports; walk; walk = g_list_next (walk)) {
GstRTSPStreamTransport *tr = (GstRTSPStreamTransport *) walk->data;
if (GST_ELEMENT_CAST (sink) == stream->appsink[0]) {
if (tr->send_rtp)
tr->send_rtp (buffer, tr->transport->interleaved.min, tr->user_data);
} else {
if (tr->send_rtcp)
tr->send_rtcp (buffer, tr->transport->interleaved.max, tr->user_data);
}
}
gst_sample_unref (sample);
return GST_FLOW_OK;
}
static GstAppSinkCallbacks sink_cb = {
NULL, /* not interested in EOS */
NULL, /* not interested in preroll samples */
handle_new_sample,
};
/**
* gst_rtsp_stream_join_bin:
* @stream: a #GstRTSPStream
* @bin: a #GstBin to join
* @rtpbin: a rtpbin element in @bin
*
* Join the #Gstbin @bin that contains the element @rtpbin.
*
* @stream will link to @rtpbin, which must be inside @bin.
*
* Returns: %TRUE on success.
*/
gboolean
gst_rtsp_stream_join_bin (GstRTSPStream * stream, GstBin * bin,
GstElement * rtpbin)
{
gint i, idx;
gchar *name;
GstPad *pad, *teepad, *queuepad, *selpad;
GstPadLinkReturn ret;
g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
g_return_val_if_fail (GST_IS_ELEMENT (rtpbin), FALSE);
idx = stream->idx;
if (stream->is_joined)
return TRUE;
GST_INFO ("stream %p joining bin", stream);
if (!alloc_ports (stream))
goto no_ports;
/* add the ports to the pipeline */
for (i = 0; i < 2; i++) {
gst_bin_add (bin, stream->udpsink[i]);
gst_bin_add (bin, stream->udpsrc[i]);
}
/* create elements for the TCP transfer */
for (i = 0; i < 2; i++) {
stream->appsrc[i] = gst_element_factory_make ("appsrc", NULL);
stream->appqueue[i] = gst_element_factory_make ("queue", NULL);
stream->appsink[i] = gst_element_factory_make ("appsink", NULL);
g_object_set (stream->appsink[i], "async", FALSE, "sync", FALSE, NULL);
g_object_set (stream->appsink[i], "emit-signals", FALSE, NULL);
gst_bin_add (bin, stream->appqueue[i]);
gst_bin_add (bin, stream->appsink[i]);
gst_bin_add (bin, stream->appsrc[i]);
gst_app_sink_set_callbacks (GST_APP_SINK_CAST (stream->appsink[i]),
&sink_cb, stream, NULL);
}
/* hook up the stream to the RTP session elements. */
name = g_strdup_printf ("send_rtp_sink_%u", idx);
stream->send_rtp_sink = gst_element_get_request_pad (rtpbin, name);
g_free (name);
name = g_strdup_printf ("send_rtp_src_%u", idx);
stream->send_rtp_src = gst_element_get_static_pad (rtpbin, name);
g_free (name);
name = g_strdup_printf ("send_rtcp_src_%u", idx);
stream->send_rtcp_src = gst_element_get_request_pad (rtpbin, name);
g_free (name);
name = g_strdup_printf ("recv_rtcp_sink_%u", idx);
stream->recv_rtcp_sink = gst_element_get_request_pad (rtpbin, name);
g_free (name);
name = g_strdup_printf ("recv_rtp_sink_%u", idx);
stream->recv_rtp_sink = gst_element_get_request_pad (rtpbin, name);
g_free (name);
/* get the session */
g_signal_emit_by_name (rtpbin, "get-internal-session", idx, &stream->session);
g_signal_connect (stream->session, "on-new-ssrc", (GCallback) on_new_ssrc,
stream);
g_signal_connect (stream->session, "on-ssrc-sdes", (GCallback) on_ssrc_sdes,
stream);
g_signal_connect (stream->session, "on-ssrc-active",
(GCallback) on_ssrc_active, stream);
g_signal_connect (stream->session, "on-bye-ssrc", (GCallback) on_bye_ssrc,
stream);
g_signal_connect (stream->session, "on-bye-timeout",
(GCallback) on_bye_timeout, stream);
g_signal_connect (stream->session, "on-timeout", (GCallback) on_timeout,
stream);
/* link the RTP pad to the session manager */
ret = gst_pad_link (stream->srcpad, stream->send_rtp_sink);
if (ret != GST_PAD_LINK_OK)
goto link_failed;
/* make tee for RTP and link to stream */
stream->tee[0] = gst_element_factory_make ("tee", NULL);
gst_bin_add (bin, stream->tee[0]);
pad = gst_element_get_static_pad (stream->tee[0], "sink");
gst_pad_link (stream->send_rtp_src, pad);
gst_object_unref (pad);
/* link RTP sink, we're pretty sure this will work. */
teepad = gst_element_get_request_pad (stream->tee[0], "src_%u");
pad = gst_element_get_static_pad (stream->udpsink[0], "sink");
gst_pad_link (teepad, pad);
gst_object_unref (pad);
gst_object_unref (teepad);
teepad = gst_element_get_request_pad (stream->tee[0], "src_%u");
pad = gst_element_get_static_pad (stream->appqueue[0], "sink");
gst_pad_link (teepad, pad);
gst_object_unref (pad);
gst_object_unref (teepad);
queuepad = gst_element_get_static_pad (stream->appqueue[0], "src");
pad = gst_element_get_static_pad (stream->appsink[0], "sink");
gst_pad_link (queuepad, pad);
gst_object_unref (pad);
gst_object_unref (queuepad);
/* make tee for RTCP */
stream->tee[1] = gst_element_factory_make ("tee", NULL);
gst_bin_add (bin, stream->tee[1]);
pad = gst_element_get_static_pad (stream->tee[1], "sink");
gst_pad_link (stream->send_rtcp_src, pad);
gst_object_unref (pad);
/* link RTCP elements */
teepad = gst_element_get_request_pad (stream->tee[1], "src_%u");
pad = gst_element_get_static_pad (stream->udpsink[1], "sink");
gst_pad_link (teepad, pad);
gst_object_unref (pad);
gst_object_unref (teepad);
teepad = gst_element_get_request_pad (stream->tee[1], "src_%u");
pad = gst_element_get_static_pad (stream->appqueue[1], "sink");
gst_pad_link (teepad, pad);
gst_object_unref (pad);
gst_object_unref (teepad);
queuepad = gst_element_get_static_pad (stream->appqueue[1], "src");
pad = gst_element_get_static_pad (stream->appsink[1], "sink");
gst_pad_link (queuepad, pad);
gst_object_unref (pad);
gst_object_unref (queuepad);
/* make selector for the RTP receivers */
stream->selector[0] = gst_element_factory_make ("funnel", NULL);
gst_bin_add (bin, stream->selector[0]);
pad = gst_element_get_static_pad (stream->selector[0], "src");
gst_pad_link (pad, stream->recv_rtp_sink);
gst_object_unref (pad);
selpad = gst_element_get_request_pad (stream->selector[0], "sink_%u");
pad = gst_element_get_static_pad (stream->udpsrc[0], "src");
gst_pad_link (pad, selpad);
gst_object_unref (pad);
gst_object_unref (selpad);
selpad = gst_element_get_request_pad (stream->selector[0], "sink_%u");
pad = gst_element_get_static_pad (stream->appsrc[0], "src");
gst_pad_link (pad, selpad);
gst_object_unref (pad);
gst_object_unref (selpad);
/* make selector for the RTCP receivers */
stream->selector[1] = gst_element_factory_make ("funnel", NULL);
gst_bin_add (bin, stream->selector[1]);
pad = gst_element_get_static_pad (stream->selector[1], "src");
gst_pad_link (pad, stream->recv_rtcp_sink);
gst_object_unref (pad);
selpad = gst_element_get_request_pad (stream->selector[1], "sink_%u");
pad = gst_element_get_static_pad (stream->udpsrc[1], "src");
gst_pad_link (pad, selpad);
gst_object_unref (pad);
gst_object_unref (selpad);
selpad = gst_element_get_request_pad (stream->selector[1], "sink_%u");
pad = gst_element_get_static_pad (stream->appsrc[1], "src");
gst_pad_link (pad, selpad);
gst_object_unref (pad);
gst_object_unref (selpad);
/* 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->is_joined = TRUE;
return TRUE;
/* ERRORS */
no_ports:
{
GST_WARNING ("failed to allocate ports %d", idx);
return FALSE;
}
link_failed:
{
GST_WARNING ("failed to link stream %d", idx);
return FALSE;
}
}
/**
* gst_rtsp_stream_leave_bin:
* @stream: a #GstRTSPStream
* @bin: a #GstBin
* @rtpbin: a rtpbin #GstElement
*
* Remove the elements of @stream from the bin
*
* Return: %TRUE on success.
*/
gboolean
gst_rtsp_stream_leave_bin (GstRTSPStream * stream, GstBin * bin,
GstElement * rtpbin)
{
gint i;
g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
g_return_val_if_fail (GST_IS_ELEMENT (rtpbin), FALSE);
if (!stream->is_joined)
return TRUE;
GST_INFO ("stream %p leaving bin", stream);
gst_pad_unlink (stream->srcpad, stream->send_rtp_sink);
g_signal_handler_disconnect (stream->send_rtp_sink, stream->caps_sig);
/* FIXME not entirely the opposite of join_bin */
for (i = 0; i < 2; i++) {
gst_bin_remove (bin, stream->udpsrc[i]);
gst_bin_remove (bin, stream->udpsink[i]);
gst_bin_remove (bin, stream->appsrc[i]);
gst_bin_remove (bin, stream->appsink[i]);
gst_bin_remove (bin, stream->appqueue[i]);
gst_bin_remove (bin, stream->tee[i]);
gst_bin_remove (bin, stream->selector[i]);
}
stream->is_joined = FALSE;
return TRUE;
}
/**
* gst_rtsp_stream_get_rtpinfo:
* @stream: a #GstRTSPStream
* @rtptime: result RTP timestamp
* @seq: result RTP seqnum
*
* Retrieve the current rtptime and seq. This is used to
* construct a RTPInfo reply header.
*
* Returns: %TRUE when rtptime and seq could be determined.
*/
gboolean
gst_rtsp_stream_get_rtpinfo (GstRTSPStream * stream,
guint * rtptime, guint * seq)
{
GObjectClass *payobjclass;
payobjclass = G_OBJECT_GET_CLASS (stream->payloader);
if (!g_object_class_find_property (payobjclass, "seqnum") ||
!g_object_class_find_property (payobjclass, "timestamp"))
return FALSE;
g_object_get (stream->payloader, "seqnum", seq, "timestamp", rtptime, NULL);
return TRUE;
}
/**
* gst_rtsp_stream_recv_rtp:
* @stream: a #GstRTSPStream
* @buffer: (transfer full): a #GstBuffer
*
* Handle an RTP buffer for the stream. This method is usually called when a
* message has been received from a client using the TCP transport.
*
* This function takes ownership of @buffer.
*
* Returns: a GstFlowReturn.
*/
GstFlowReturn
gst_rtsp_stream_recv_rtp (GstRTSPStream * stream, GstBuffer * buffer)
{
GstFlowReturn ret;
ret = gst_app_src_push_buffer (GST_APP_SRC_CAST (stream->appsrc[0]), buffer);
return ret;
}
/**
* gst_rtsp_stream_recv_rtcp:
* @stream: a #GstRTSPStream
* @buffer: (transfer full): a #GstBuffer
*
* Handle an RTCP buffer for the stream. This method is usually called when a
* message has been received from a client using the TCP transport.
*
* This function takes ownership of @buffer.
*
* Returns: a GstFlowReturn.
*/
GstFlowReturn
gst_rtsp_stream_recv_rtcp (GstRTSPStream * stream, GstBuffer * buffer)
{
GstFlowReturn ret;
ret = gst_app_src_push_buffer (GST_APP_SRC_CAST (stream->appsrc[1]), buffer);
return ret;
}
static gboolean
update_transport (GstRTSPStream * stream, GstRTSPStreamTransport * trans,
gboolean add)
{
GstRTSPTransport *tr;
gboolean updated;
updated = FALSE;
tr = trans->transport;
switch (tr->lower_transport) {
case GST_RTSP_LOWER_TRANS_UDP:
case GST_RTSP_LOWER_TRANS_UDP_MCAST:
{
gchar *dest;
gint min, max;
guint ttl = 0;
dest = tr->destination;
if (tr->lower_transport == GST_RTSP_LOWER_TRANS_UDP_MCAST) {
min = tr->port.min;
max = tr->port.max;
ttl = tr->ttl;
} else {
min = tr->client_port.min;
max = tr->client_port.max;
}
if (add && !trans->active) {
GST_INFO ("adding %s:%d-%d", dest, min, max);
g_signal_emit_by_name (stream->udpsink[0], "add", dest, min, NULL);
g_signal_emit_by_name (stream->udpsink[1], "add", dest, max, NULL);
if (ttl > 0) {
GST_INFO ("setting ttl-mc %d", ttl);
g_object_set (G_OBJECT (stream->udpsink[0]), "ttl-mc", ttl, NULL);
g_object_set (G_OBJECT (stream->udpsink[1]), "ttl-mc", ttl, NULL);
}
stream->transports = g_list_prepend (stream->transports, trans);
trans->active = TRUE;
updated = TRUE;
} else if (trans->active) {
GST_INFO ("removing %s:%d-%d", dest, min, max);
g_signal_emit_by_name (stream->udpsink[0], "remove", dest, min, NULL);
g_signal_emit_by_name (stream->udpsink[1], "remove", dest, max, NULL);
stream->transports = g_list_remove (stream->transports, trans);
trans->active = FALSE;
updated = TRUE;
}
break;
}
case GST_RTSP_LOWER_TRANS_TCP:
if (add && !trans->active) {
GST_INFO ("adding TCP %s", tr->destination);
stream->transports = g_list_prepend (stream->transports, trans);
trans->active = TRUE;
updated = TRUE;
} else if (trans->active) {
GST_INFO ("removing TCP %s", tr->destination);
stream->transports = g_list_remove (stream->transports, trans);
trans->active = FALSE;
updated = TRUE;
}
break;
default:
GST_INFO ("Unknown transport %d", tr->lower_transport);
break;
}
return updated;
}
/**
* gst_rtsp_stream_add_transport:
* @stream: a #GstRTSPStream
* @trans: a #GstRTSPStreamTransport
*
* Add the transport in @trans to @stream. The media of @stream will
* then also be send to the values configured in @trans.
*
* @trans must contain a valid #GstRTSPTransport.
*
* Returns: %TRUE if @trans was added
*/
gboolean
gst_rtsp_stream_add_transport (GstRTSPStream * stream,
GstRTSPStreamTransport * trans)
{
g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
g_return_val_if_fail (GST_IS_RTSP_STREAM_TRANSPORT (trans), FALSE);
g_return_val_if_fail (trans->transport != NULL, FALSE);
return update_transport (stream, trans, TRUE);
}
/**
* gst_rtsp_stream_remove_transport:
* @stream: a #GstRTSPStream
* @trans: a #GstRTSPStreamTransport
*
* Remove the transport in @trans from @stream. The media of @stream will
* not be sent to the values configured in @trans.
*
* Returns: %TRUE if @trans was removed
*/
gboolean
gst_rtsp_stream_remove_transport (GstRTSPStream * stream,
GstRTSPStreamTransport * trans)
{
g_return_val_if_fail (GST_IS_RTSP_STREAM (stream), FALSE);
g_return_val_if_fail (GST_IS_RTSP_STREAM_TRANSPORT (trans), FALSE);
g_return_val_if_fail (trans->transport != NULL, FALSE);
return update_transport (stream, trans, FALSE);
}

View file

@ -0,0 +1,142 @@
/* 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/gstrtsprange.h>
#include <gst/rtsp/gstrtspurl.h>
#ifndef __GST_RTSP_STREAM_H__
#define __GST_RTSP_STREAM_H__
G_BEGIN_DECLS
/* types for the media stream */
#define GST_TYPE_RTSP_STREAM (gst_rtsp_stream_get_type ())
#define GST_IS_RTSP_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_RTSP_STREAM))
#define GST_IS_RTSP_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_RTSP_STREAM))
#define GST_RTSP_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTSP_STREAM, GstRTSPStreamClass))
#define GST_RTSP_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_RTSP_STREAM, GstRTSPStream))
#define GST_RTSP_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_RTSP_STREAM, GstRTSPStreamClass))
#define GST_RTSP_STREAM_CAST(obj) ((GstRTSPStream*)(obj))
#define GST_RTSP_STREAM_CLASS_CAST(klass) ((GstRTSPStreamClass*)(klass))
typedef struct _GstRTSPStream GstRTSPStream;
typedef struct _GstRTSPStreamClass GstRTSPStreamClass;
#include "rtsp-stream-transport.h"
/**
* GstRTSPStream:
* @parent: the parent instance
* @idx: the stream index
* @srcpad: the srcpad of the stream
* @payloader: the payloader of the format
* @is_ipv6: should this stream be IPv6
* @buffer_size: the UDP buffer size
* @is_joined: if the stream is joined in a bin
* @recv_rtp_sink: sinkpad for RTP buffers
* @recv_rtcp_sink: sinkpad for RTCP buffers
* @send_rtp_src: srcpad for RTP buffers
* @send_rtcp_src: srcpad for RTCP buffers
* @udpsrc: the udp source elements for RTP/RTCP
* @udpsink: the udp sink elements for RTP/RTCP
* @appsrc: the app source elements for RTP/RTCP
* @appsink: the app sink elements for RTP/RTCP
* @server_port: the server ports for this stream
* @caps_sig: the signal id for detecting caps
* @caps: the caps of the stream
* @n_active: the number of active transports in @transports
* @tranports: list of #GstStreamTransport being streamed to
*
* The definition of a media stream. The streams are identified by @id.
*/
struct _GstRTSPStream {
GObject parent;
guint idx;
GstPad *srcpad;
GstElement *payloader;
gboolean is_ipv6;
guint buffer_size;
gboolean is_joined;
/* pads on the rtpbin */
GstPad *recv_rtcp_sink;
GstPad *recv_rtp_sink;
GstPad *send_rtp_sink;
GstPad *send_rtp_src;
GstPad *send_rtcp_src;
/* the RTPSession object */
GObject *session;
/* sinks used for sending and receiving RTP and RTCP, they share
* sockets */
GstElement *udpsrc[2];
GstElement *udpsink[2];
/* for TCP transport */
GstElement *appsrc[2];
GstElement *appqueue[2];
GstElement *appsink[2];
GstElement *tee[2];
GstElement *selector[2];
/* server ports for sending/receiving */
GstRTSPRange server_port;
/* the caps of the stream */
gulong caps_sig;
GstCaps *caps;
/* transports we stream to */
guint n_active;
GList *transports;
};
struct _GstRTSPStreamClass {
GObjectClass parent_class;
};
GType gst_rtsp_stream_get_type (void);
GstRTSPStream * gst_rtsp_stream_new (guint idx, GstElement *payloader,
GstPad *srcpad);
gboolean gst_rtsp_stream_join_bin (GstRTSPStream * stream,
GstBin *bin, GstElement *rtpbin);
gboolean gst_rtsp_stream_leave_bin (GstRTSPStream * stream,
GstBin *bin, GstElement *rtpbin);
gboolean gst_rtsp_stream_get_rtpinfo (GstRTSPStream * stream,
guint *rtptime, guint * seq);
GstFlowReturn gst_rtsp_stream_recv_rtp (GstRTSPStream *stream,
GstBuffer *buffer);
GstFlowReturn gst_rtsp_stream_recv_rtcp (GstRTSPStream *stream,
GstBuffer *buffer);
gboolean gst_rtsp_stream_add_transport (GstRTSPStream *stream,
GstRTSPStreamTransport *trans);
gboolean gst_rtsp_stream_remove_transport (GstRTSPStream *stream,
GstRTSPStreamTransport *trans);
G_END_DECLS
#endif /* __GST_RTSP_STREAM_H__ */