gst_rtsp_message_remove_header (response, GST_RTSP_HDR_SESSION, -1);
}
-#if 0
- gst_rtsp_connection_send (client->connection, response, &timeout);
-#endif
gst_rtsp_watch_queue_message (client->watch, response);
gst_rtsp_message_unset (response);
}
}
static gboolean
+do_send_data (GstBuffer *buffer, guint8 channel, GstRTSPClient *client)
+{
+ GstRTSPMessage message = { 0 };
+ guint8 *data;
+ guint size;
+
+ gst_rtsp_message_init_data (&message, channel);
+
+ data = GST_BUFFER_DATA (buffer);
+ size = GST_BUFFER_SIZE (buffer);
+ gst_rtsp_message_take_body (&message, data, size);
+
+ gst_rtsp_watch_queue_message (client->watch, &message);
+
+ gst_rtsp_message_steal_body (&message, &data, &size);
+ gst_rtsp_message_unset (&message);
+
+ return TRUE;
+}
+
+static void
+link_stream (GstRTSPClient *client, GstRTSPSessionStream *stream)
+{
+ gst_rtsp_session_stream_set_callbacks (stream, (GstRTSPSendFunc) do_send_data,
+ (GstRTSPSendFunc) do_send_data, client, NULL);
+ client->streams = g_list_prepend (client->streams, stream);
+}
+
+static void
+unlink_stream (GstRTSPClient *client, GstRTSPSessionStream *stream)
+{
+ gst_rtsp_session_stream_set_callbacks (stream, NULL,
+ NULL, client, g_object_unref);
+ client->streams = g_list_remove (client->streams, stream);
+}
+
+static void
+unlink_streams (GstRTSPClient *client)
+{
+ GList *walk;
+
+ for (walk = client->streams; walk; walk = g_list_next (walk)) {
+ GstRTSPSessionStream *stream = (GstRTSPSessionStream *) walk->data;
+
+ gst_rtsp_session_stream_set_callbacks (stream, NULL,
+ NULL, NULL, NULL);
+ }
+ g_list_free (client->streams);
+ client->streams = NULL;
+}
+
+static void
+unlink_session_streams (GstRTSPClient *client, GstRTSPSessionMedia *media)
+{
+ guint n_streams, i;
+
+ n_streams = gst_rtsp_media_n_streams (media->media);
+ for (i = 0; i < n_streams; i++) {
+ GstRTSPSessionStream *sstream;
+ GstRTSPTransport *tr;
+
+ /* get the stream as configured in the session */
+ sstream = gst_rtsp_session_media_get_stream (media, i);
+ /* get the transport, if there is no transport configured, skip this stream */
+ if (!(tr = sstream->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, sstream);
+ }
+ }
+}
+
+static gboolean
handle_teardown_request (GstRTSPClient *client, GstRTSPUrl *uri, GstRTSPSession *session, GstRTSPMessage *request)
{
GstRTSPSessionMedia *media;
if (!media)
goto not_found;
- gst_rtsp_session_media_stop (media);
+ /* unlink the all TCP callbacks */
+ unlink_session_streams (client, media);
+
+ gst_rtsp_session_media_set_state (media, GST_STATE_NULL);
/* unmanage the media in the session, returns false if all media session
* are torn down. */
media->state != GST_RTSP_STATE_RECORDING)
goto invalid_state;
- gst_rtsp_session_media_pause (media);
+ /* unlink the all TCP callbacks */
+ unlink_session_streams (client, media);
+
+ /* then pause sending */
+ gst_rtsp_session_media_set_state (media, GST_STATE_PAUSED);
/* construct the response now */
code = GST_RTSP_STS_OK;
n_streams = gst_rtsp_media_n_streams (media->media);
for (i = 0; i < n_streams; i++) {
+ GstRTSPSessionStream *sstream;
GstRTSPMediaStream *stream;
+ GstRTSPTransport *tr;
gchar *uristr;
- stream = gst_rtsp_media_get_stream (media->media, i);
+ /* get the stream as configured in the session */
+ sstream = gst_rtsp_session_media_get_stream (media, i);
+ /* get the transport, if there is no transport configured, skip this stream */
+ if (!(tr = sstream->trans.transport))
+ 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, sstream);
+ }
+
+ stream = sstream->media_stream;
g_object_get (G_OBJECT (stream->payloader), "seqnum", &seqnum, NULL);
g_object_get (G_OBJECT (stream->payloader), "timestamp", ×tamp, NULL);
send_response (client, session, &response);
/* start playing after sending the request */
- gst_rtsp_session_media_play (media);
+ gst_rtsp_session_media_set_state (media, GST_STATE_PLAYING);
media->state = GST_RTSP_STATE_PLAYING;
if (!(stream = gst_rtsp_session_media_get_stream (media, streamid)))
goto no_stream;
- /* setup the server transport from the client transport */
- if (ct->lower_transport == GST_RTSP_LOWER_TRANS_TCP) {
- ct->port.min = gst_rtsp_connection_get_readfd (client->connection);
- ct->port.max = gst_rtsp_connection_get_writefd (client->connection);
- }
-
st = gst_rtsp_session_stream_set_transport (stream, ct);
/* serialize the server transport */
gst_rtsp_message_dump (request);
#endif
+ g_message ("client %p: received a request", client);
+
gst_rtsp_message_parse_request (request, &method, &uristr, &version);
if (version != GST_RTSP_VERSION_1_0) {
}
}
+static void
+handle_data (GstRTSPClient *client, GstRTSPMessage *message)
+{
+ GstRTSPResult res;
+ guint8 channel;
+ GList *walk;
+ guint8 *data;
+ guint size;
+ GstBuffer *buffer;
+
+ /* find the stream for this message */
+ res = gst_rtsp_message_parse_data (message, &channel);
+ if (res != GST_RTSP_OK)
+ return;
+
+ gst_rtsp_message_steal_body (message, &data, &size);
+
+ buffer = gst_buffer_new ();
+ GST_BUFFER_DATA (buffer) = data;
+ GST_BUFFER_MALLOCDATA (buffer) = data;
+ GST_BUFFER_SIZE (buffer) = size;
+
+ for (walk = client->streams; walk; walk = g_list_next (walk)) {
+ GstRTSPSessionStream *stream = (GstRTSPSessionStream *) walk->data;
+ GstRTSPMediaStream *mstream;
+ GstRTSPTransport *tr;
+
+ /* get the transport, if there is no transport configured, skip this stream */
+ if (!(tr = stream->trans.transport))
+ continue;
+
+ /* we also need a media stream */
+ if (!(mstream = stream->media_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);
+ } else if (tr->interleaved.max == channel) {
+ gst_rtsp_media_stream_rtcp (mstream, buffer);
+ }
+ }
+ }
+ gst_buffer_unref (buffer);
+}
+
/**
* gst_rtsp_client_set_timeout:
* @client: a #GstRTSPClient
{
GstRTSPClient *client = GST_RTSP_CLIENT (user_data);
- g_message ("client %p: received a message", client);
-
switch (message->type) {
case GST_RTSP_MESSAGE_REQUEST:
handle_request (client, message);
case GST_RTSP_MESSAGE_RESPONSE:
break;
case GST_RTSP_MESSAGE_DATA:
+ handle_data (client, message);
break;
default:
break;
g_mutex_unlock (tunnels_lock);
}
+ /* remove all streams that are streaming over this client connection */
+ unlink_streams (client);
+
return GST_RTSP_OK;
}
gst_rtsp_media_init (GstRTSPMedia * media)
{
media->streams = g_array_new (FALSE, TRUE, sizeof (GstRTSPMediaStream *));
- media->complete = FALSE;
media->is_live = FALSE;
media->buffering = FALSE;
}
return res;
}
+/**
+ * gst_rtsp_media_stream_rtp:
+ * @stream: a #GstRTSPMediaStream
+ * @buffer: 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.
+ *
+ * Returns: a GstFlowReturn.
+ */
+GstFlowReturn
+gst_rtsp_media_stream_rtp (GstRTSPMediaStream *stream, GstBuffer *buffer)
+{
+ GstFlowReturn ret;
+
+ g_signal_emit_by_name (stream->appsrc[0], "push-buffer", buffer, &ret);
+
+ return ret;
+}
+
+/**
+ * gst_rtsp_media_stream_rtcp:
+ * @stream: a #GstRTSPMediaStream
+ * @buffer: 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.
+ *
+ * Returns: a GstFlowReturn.
+ */
+GstFlowReturn
+gst_rtsp_media_stream_rtcp (GstRTSPMediaStream *stream, GstBuffer *buffer)
+{
+ GstFlowReturn ret;
+
+ g_signal_emit_by_name (stream->appsrc[1], "push-buffer", buffer, &ret);
+
+ return GST_FLOW_ERROR;
+}
+
/* Allocate the udp ports and sockets */
static gboolean
alloc_udp_ports (GstRTSPMediaStream * stream)
g_message ("%p: source %p timeout", media, source);
}
+static void
+handle_new_buffer (GstElement *sink, GstRTSPMediaStream *stream)
+{
+ GList *walk;
+ GstBuffer *buffer;
+
+ g_signal_emit_by_name (sink, "pull-buffer", &buffer);
+ if (!buffer)
+ return;
+
+ for (walk = stream->transports; walk; walk = g_list_next (walk)) {
+ GstRTSPMediaTrans *tr = (GstRTSPMediaTrans *) walk->data;
+
+ if (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_buffer_unref (buffer);
+}
+
/* prepare the pipeline objects to handle @stream in @media */
static gboolean
setup_stream (GstRTSPMediaStream *stream, guint idx, GstRTSPMedia *media)
{
gchar *name;
- GstPad *pad;
+ GstPad *pad, *teepad, *selpad;
+ GstPadLinkReturn ret;
+ gint i;
+ GstElement *tee, *selector;
- alloc_udp_ports (stream);
+ /* allocate udp ports, we will have 4 of them, 2 for receiving RTP/RTCP and 2
+ * for sending RTP/RTCP. The sender and receiver ports are shared between the
+ * elements */
+ if (!alloc_udp_ports (stream))
+ return FALSE;
- gst_bin_add (GST_BIN_CAST (media->pipeline), stream->udpsink[0]);
- gst_bin_add (GST_BIN_CAST (media->pipeline), stream->udpsink[1]);
- gst_bin_add (GST_BIN_CAST (media->pipeline), stream->udpsrc[0]);
- gst_bin_add (GST_BIN_CAST (media->pipeline), stream->udpsrc[1]);
+ /* add the ports to the pipeline */
+ for (i = 0; i < 2; i++) {
+ gst_bin_add (GST_BIN_CAST (media->pipeline), stream->udpsink[i]);
+ gst_bin_add (GST_BIN_CAST (media->pipeline), 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->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", TRUE, NULL);
+ g_object_set (stream->appsink[i], "preroll-queue-len", 1, NULL);
+ gst_bin_add (GST_BIN_CAST (media->pipeline), stream->appsink[i]);
+ gst_bin_add (GST_BIN_CAST (media->pipeline), stream->appsrc[i]);
+ }
+ g_signal_connect (stream->appsink[0], "new-buffer",
+ (GCallback) handle_new_buffer, stream);
+ g_signal_connect (stream->appsink[1], "new-buffer",
+ (GCallback) handle_new_buffer, stream);
/* hook up the stream to the RTP session elements. */
name = g_strdup_printf ("send_rtp_sink_%d", idx);
media);
/* link the RTP pad to the session manager */
- gst_pad_link (stream->srcpad, stream->send_rtp_sink);
+ ret = gst_pad_link (stream->srcpad, stream->send_rtp_sink);
+ if (ret != GST_PAD_LINK_OK)
+ goto link_failed;
- /* link udp elements */
- pad = gst_element_get_static_pad (stream->udpsink[0], "sink");
+ /* make tee for RTP and link to stream */
+ tee = gst_element_factory_make ("tee", NULL);
+ gst_bin_add (GST_BIN_CAST (media->pipeline), tee);
+
+ pad = gst_element_get_static_pad (tee, "sink");
gst_pad_link (stream->send_rtp_src, pad);
gst_object_unref (pad);
- pad = gst_element_get_static_pad (stream->udpsink[1], "sink");
+
+ /* link RTP sink, we're pretty sure this will work. */
+ teepad = gst_element_get_request_pad (tee, "src%d");
+ 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 (tee, "src%d");
+ pad = gst_element_get_static_pad (stream->appsink[0], "sink");
+ gst_pad_link (teepad, pad);
+ gst_object_unref (pad);
+ gst_object_unref (teepad);
+
+ /* make tee for RTCP */
+ tee = gst_element_factory_make ("tee", NULL);
+ gst_bin_add (GST_BIN_CAST (media->pipeline), tee);
+
+ pad = gst_element_get_static_pad (tee, "sink");
gst_pad_link (stream->send_rtcp_src, pad);
gst_object_unref (pad);
- pad = gst_element_get_static_pad (stream->udpsrc[1], "src");
+
+ /* link RTCP elements */
+ teepad = gst_element_get_request_pad (tee, "src%d");
+ 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 (tee, "src%d");
+ pad = gst_element_get_static_pad (stream->appsink[1], "sink");
+ gst_pad_link (teepad, pad);
+ gst_object_unref (pad);
+ gst_object_unref (teepad);
+
+ /* make selector for the RTCP receivers */
+ selector = gst_element_factory_make ("input-selector", NULL);
+ g_object_set (selector, "select-all", TRUE, NULL);
+ gst_bin_add (GST_BIN_CAST (media->pipeline), selector);
+
+ pad = gst_element_get_static_pad (selector, "src");
gst_pad_link (pad, stream->recv_rtcp_sink);
gst_object_unref (pad);
+ selpad = gst_element_get_request_pad (selector, "sink%d");
+ 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 (selector, "sink%d");
+ 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);
stream->prepared = TRUE;
return TRUE;
+
+ /* ERRORS */
+link_failed:
+ {
+ g_warning ("failed to link stream %d", idx);
+ return FALSE;
+ }
}
static void
g_free (debug);
break;
}
+ case GST_MESSAGE_WARNING:
+ {
+ GError *gerror;
+ gchar *debug;
+
+ gst_message_parse_warning (message, &gerror, &debug);
+ g_warning ("%p: got warning %s (%s)", media, gerror->message, debug);
+ g_error_free (gerror);
+ g_free (debug);
+ break;
+ }
default:
g_message ("%p: got message type %s", media, gst_message_type_get_name (type));
break;
* Prepare @media for streaming. This function will create the pipeline and
* other objects to manage the streaming.
*
+ * It will preroll the pipeline and collect vital information about the streams
+ * such as the duration.
+ *
* Returns: %TRUE on success.
*/
gboolean
g_message ("live media %p", media);
media->is_live = TRUE;
ret = gst_element_set_state (media->pipeline, GST_STATE_PLAYING);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ goto state_failed;
break;
case GST_STATE_CHANGE_FAILURE:
- {
- unlock_streams (media);
goto state_failed;
- }
}
/* now wait for all pads to be prerolled */
ret = gst_element_get_state (media->pipeline, NULL, NULL, -1);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ goto state_failed;
/* and back to PAUSED for live pipelines */
ret = gst_element_set_state (media->pipeline, GST_STATE_PAUSED);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ goto state_failed;
/* collect stats about the media */
collect_media_stats (media);
/* ERRORS */
state_failed:
{
+ g_warning ("failed to preroll pipeline");
+ unlock_streams (media);
gst_element_set_state (media->pipeline, GST_STATE_NULL);
return FALSE;
}
}
/**
- * gst_rtsp_media_play:
+ * gst_rtsp_media_set_state:
* @media: a #GstRTSPMedia
+ * @state: the target state of the media
* @transports: a GArray of #GstRTSPMediaTrans pointers
*
- * Start playing @media for to the transports in @transports.
+ * Set the state of @media to @state and for the transports in @transports.
*
* Returns: %TRUE on success.
*/
gboolean
-gst_rtsp_media_play (GstRTSPMedia *media, GArray *transports)
+gst_rtsp_media_set_state (GstRTSPMedia *media, GstState state, GArray *transports)
{
gint i;
GstStateChangeReturn ret;
+ gboolean add, remove;
g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
g_return_val_if_fail (transports != NULL, FALSE);
g_return_val_if_fail (media->prepared, FALSE);
- if (media->target_state == GST_STATE_PLAYING)
- return TRUE;
+ /* NULL and READY are the same */
+ if (state == GST_STATE_READY)
+ state = GST_STATE_NULL;
- for (i = 0; i < transports->len; i++) {
- GstRTSPMediaTrans *tr;
- GstRTSPMediaStream *stream;
- GstRTSPTransport *trans;
-
- /* we need a non-NULL entry in the array */
- tr = g_array_index (transports, GstRTSPMediaTrans *, i);
- if (tr == NULL)
- continue;
-
- /* we need a transport */
- if (!(trans = tr->transport))
- continue;
+ if (media->target_state == state)
+ return TRUE;
- /* get the stream and add the destinations */
- stream = gst_rtsp_media_get_stream (media, tr->idx);
- switch (trans->lower_transport) {
- case GST_RTSP_LOWER_TRANS_UDP:
- case GST_RTSP_LOWER_TRANS_UDP_MCAST:
- g_message ("adding %s:%d-%d", trans->destination, trans->client_port.min, trans->client_port.max);
+ add = remove = FALSE;
- g_signal_emit_by_name (stream->udpsink[0], "add", trans->destination, trans->client_port.min, NULL);
- g_signal_emit_by_name (stream->udpsink[1], "add", trans->destination, trans->client_port.max, NULL);
- break;
- case GST_RTSP_LOWER_TRANS_TCP:
- g_message ("TCP transport not yet implemented");
- break;
- default:
- g_message ("Unknown transport %d", trans->lower_transport);
- break;
- }
+ switch (state) {
+ case GST_STATE_NULL:
+ case GST_STATE_PAUSED:
+ /* we're going from PLAYING to READY or NULL, remove */
+ if (media->target_state == GST_STATE_PLAYING)
+ remove = TRUE;
+ break;
+ case GST_STATE_PLAYING:
+ /* we're going to PLAYING, add */
+ add = TRUE;
+ break;
+ default:
+ break;
}
- g_message ("playing media %p", media);
- media->target_state = GST_STATE_PLAYING;
- ret = gst_element_set_state (media->pipeline, GST_STATE_PLAYING);
-
- return TRUE;
-}
-
-/**
- * gst_rtsp_media_pause:
- * @media: a #GstRTSPMedia
- * @transports: a array of #GstRTSPTransport pointers
- *
- * Pause playing @media for to the transports in @transports.
- *
- * Returns: %TRUE on success.
- */
-gboolean
-gst_rtsp_media_pause (GstRTSPMedia *media, GArray *transports)
-{
- gint i;
- GstStateChangeReturn ret;
-
- g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
- g_return_val_if_fail (transports != NULL, FALSE);
- g_return_val_if_fail (media->prepared, FALSE);
-
- if (media->target_state == GST_STATE_PAUSED)
- return TRUE;
-
for (i = 0; i < transports->len; i++) {
GstRTSPMediaTrans *tr;
GstRTSPMediaStream *stream;
/* get the stream and add the destinations */
stream = gst_rtsp_media_get_stream (media, tr->idx);
-
switch (trans->lower_transport) {
case GST_RTSP_LOWER_TRANS_UDP:
case GST_RTSP_LOWER_TRANS_UDP_MCAST:
- g_message ("removing %s:%d-%d", trans->destination, trans->client_port.min, trans->client_port.max);
-
- g_signal_emit_by_name (stream->udpsink[0], "remove", trans->destination, trans->client_port.min, NULL);
- g_signal_emit_by_name (stream->udpsink[1], "remove", trans->destination, trans->client_port.max, NULL);
+ if (add) {
+ g_message ("adding %s:%d-%d", trans->destination, trans->client_port.min, trans->client_port.max);
+ g_signal_emit_by_name (stream->udpsink[0], "add", trans->destination, trans->client_port.min, NULL);
+ g_signal_emit_by_name (stream->udpsink[1], "add", trans->destination, trans->client_port.max, NULL);
+ } else if (remove) {
+ g_message ("removing %s:%d-%d", trans->destination, trans->client_port.min, trans->client_port.max);
+ g_signal_emit_by_name (stream->udpsink[0], "remove", trans->destination, trans->client_port.min, NULL);
+ g_signal_emit_by_name (stream->udpsink[1], "remove", trans->destination, trans->client_port.max, NULL);
+ }
break;
case GST_RTSP_LOWER_TRANS_TCP:
- g_message ("TCP transport not yet implemented");
+ if (add) {
+ stream->transports = g_list_prepend (stream->transports, tr);
+ } else if (remove) {
+ stream->transports = g_list_remove (stream->transports, tr);
+ }
break;
default:
g_message ("Unknown transport %d", trans->lower_transport);
break;
}
}
- g_message ("pause media %p", media);
- media->target_state = GST_STATE_PAUSED;
- ret = gst_element_set_state (media->pipeline, GST_STATE_PAUSED);
-
- return TRUE;
-}
-
-/**
- * gst_rtsp_media_stream_stop:
- * @media: a #GstRTSPMedia
- * @transports: a GArray of #GstRTSPMediaTrans pointers
- *
- * Stop playing @media for to the transports in @transports.
- *
- * Returns: %TRUE on success.
- */
-gboolean
-gst_rtsp_media_stop (GstRTSPMedia *media, GArray *transports)
-{
- GstStateChangeReturn ret;
-
- g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
- g_return_val_if_fail (transports != NULL, FALSE);
- g_return_val_if_fail (media->prepared, FALSE);
-
- if (media->target_state == GST_STATE_NULL)
- return TRUE;
-
- gst_rtsp_media_pause (media, transports);
- g_message ("stop media %p", media);
- media->target_state = GST_STATE_NULL;
- ret = gst_element_set_state (media->pipeline, GST_STATE_NULL);
+ g_message ("state %s media %p", gst_element_state_get_name (state), media);
+ media->target_state = state;
+ ret = gst_element_set_state (media->pipeline, state);
return TRUE;
}
g_message ("free session media %p", media);
- gst_rtsp_session_media_stop (media);
+ gst_rtsp_session_media_set_state (media, GST_STATE_NULL);
for (i = 0; i < size; i++) {
GstRTSPSessionStream *stream;
st->profile = ct->profile;
st->lower_transport = ct->lower_transport;
st->client_port = ct->client_port;
+ st->interleaved = ct->interleaved;
+ st->server_port.min = stream->media_stream->server_port.min;
+ st->server_port.max = stream->media_stream->server_port.max;
- /* keep track of the transports */
+ /* keep track of the transports in the stream. */
if (stream->trans.transport)
gst_rtsp_transport_free (stream->trans.transport);
stream->trans.transport = ct;
- st->server_port.min = stream->media_stream->server_port.min;
- st->server_port.max = stream->media_stream->server_port.max;
-
return st;
}
/**
- * gst_rtsp_session_media_play:
- * @media: a #GstRTSPSessionMedia
- *
- * Tell the media object @media to start playing and streaming to the client.
- *
- * Returns: %TRUE on success.
- */
-gboolean
-gst_rtsp_session_media_play (GstRTSPSessionMedia *media)
-{
- gboolean ret;
-
- g_return_val_if_fail (media != NULL, FALSE);
-
- ret = gst_rtsp_media_play (media->media, media->streams);
-
- return ret;
-}
-
-/**
- * gst_rtsp_session_media_pause:
- * @media: a #GstRTSPSessionMedia
- *
- * Tell the media object @media to pause.
+ * gst_rtsp_session_stream_set_callbacks:
+ * @stream: a #GstRTSPSessionStream
+ * @send_rtp: a callback called when RTP should be send
+ * @send_rtcp: a callback called when RTCP should be send
+ * @user_data: user data passed to callbacks
+ * @notify: called with the user_data when no longer needed.
*
- * Returns: %TRUE on success.
+ * 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.
*/
-gboolean
-gst_rtsp_session_media_pause (GstRTSPSessionMedia *media)
+void
+gst_rtsp_session_stream_set_callbacks (GstRTSPSessionStream *stream,
+ GstRTSPSendFunc send_rtp, GstRTSPSendFunc send_rtcp, gpointer user_data,
+ GDestroyNotify notify)
{
- gboolean ret;
-
- g_return_val_if_fail (media != NULL, FALSE);
-
- ret = gst_rtsp_media_pause (media->media, media->streams);
-
- return ret;
+ 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_media_stop:
+ * gst_rtsp_session_media_set_state:
* @media: a #GstRTSPSessionMedia
+ * @state: the new state
*
- * Tell the media object @media to stop playing. After this call the media
- * cannot be played or paused anymore
+ * Tell the media object @media to change to @state.
*
* Returns: %TRUE on success.
*/
gboolean
-gst_rtsp_session_media_stop (GstRTSPSessionMedia *media)
+gst_rtsp_session_media_set_state (GstRTSPSessionMedia *media, GstState state)
{
gboolean ret;
g_return_val_if_fail (media != NULL, FALSE);
- ret = gst_rtsp_media_stop (media->media, media->streams);
+ ret = gst_rtsp_media_set_state (media->media, state, media->streams);
return ret;
}