Commit d893c1dd authored by Benjamin Berg's avatar Benjamin Berg
Browse files

sinks: Add code to stop streaming cleanly and report client disconnect

parent 411a30e3
......@@ -27,9 +27,8 @@ struct _ScreencastDummyWFDSink
ScreencastSinkState state;
GCancellable *cancellable;
WfdServer *server;
guint server_source_id;
};
enum {
......@@ -47,6 +46,7 @@ enum {
static void screencast_dummy_wfd_sink_sink_iface_init (ScreencastSinkIface *iface);
static ScreencastSink * screencast_dummy_wfd_sink_sink_start_stream (ScreencastSink *sink);
static void screencast_dummy_wfd_sink_sink_stop_stream (ScreencastSink *sink);
G_DEFINE_TYPE_EXTENDED (ScreencastDummyWFDSink, screencast_dummy_wfd_sink, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (SCREENCAST_TYPE_SINK,
......@@ -96,12 +96,7 @@ screencast_dummy_wfd_sink_get_property (GObject *object,
void
screencast_dummy_wfd_sink_finalize (GObject *object)
{
ScreencastDummyWFDSink *sink = SCREENCAST_DUMMY_WFD_SINK (object);
g_cancellable_cancel (sink->cancellable);
g_clear_object (&sink->cancellable);
g_clear_object (&sink->server);
screencast_dummy_wfd_sink_sink_stop_stream (SCREENCAST_SINK (object));
G_OBJECT_CLASS (screencast_dummy_wfd_sink_parent_class)->finalize (object);
}
......@@ -124,7 +119,6 @@ static void
screencast_dummy_wfd_sink_init (ScreencastDummyWFDSink *sink)
{
sink->state = SCREENCAST_SINK_STATE_DISCONNECTED;
sink->cancellable = g_cancellable_new ();
}
/******************************************************************
......@@ -135,6 +129,7 @@ static void
screencast_dummy_wfd_sink_sink_iface_init (ScreencastSinkIface *iface)
{
iface->start_stream = screencast_dummy_wfd_sink_sink_start_stream;
iface->stop_stream = screencast_dummy_wfd_sink_sink_stop_stream;
}
static void
......@@ -146,6 +141,13 @@ play_request_cb (ScreencastDummyWFDSink *sink, GstRTSPContext *ctx, WfdClient *c
g_object_notify (G_OBJECT (sink), "state");
}
static void
closed_cb (ScreencastDummyWFDSink *sink, WfdClient *client)
{
/* Connection was closed, do a clean shutdown*/
screencast_dummy_wfd_sink_sink_stop_stream (SCREENCAST_SINK (sink));
}
static void
client_connected_cb (ScreencastDummyWFDSink *sink, WfdClient *client, WfdServer *server)
{
......@@ -161,6 +163,12 @@ client_connected_cb (ScreencastDummyWFDSink *sink, WfdClient *client, WfdServer
(GCallback) play_request_cb,
sink,
G_CONNECT_SWAPPED);
g_signal_connect_object (client,
"closed",
(GCallback) closed_cb,
sink,
G_CONNECT_SWAPPED);
}
static GstElement*
......@@ -178,13 +186,14 @@ screencast_dummy_wfd_sink_sink_start_stream (ScreencastSink *sink)
{
ScreencastDummyWFDSink *self = SCREENCAST_DUMMY_WFD_SINK (sink);
if (self->server)
g_signal_handlers_disconnect_by_data (self->server, self);
g_clear_object (&self->server);
g_return_val_if_fail (self->state == SCREENCAST_SINK_STATE_DISCONNECTED, NULL);
g_assert (self->server == NULL);
self->server = wfd_server_new ();
self->server_source_id = gst_rtsp_server_attach (GST_RTSP_SERVER (self->server), NULL);
if (gst_rtsp_server_attach (GST_RTSP_SERVER (self->server), NULL) < 0)
if (self->server_source_id < 0)
{
self->state = SCREENCAST_SINK_STATE_ERROR;
g_object_notify (G_OBJECT (self), "state");
......@@ -213,6 +222,31 @@ screencast_dummy_wfd_sink_sink_start_stream (ScreencastSink *sink)
return g_object_ref (sink);
}
void
screencast_dummy_wfd_sink_sink_stop_stream (ScreencastSink *sink)
{
ScreencastDummyWFDSink *self = SCREENCAST_DUMMY_WFD_SINK (sink);
if (self->server_source_id)
{
g_source_remove (self->server_source_id);
self->server_source_id = 0;
}
/* Needs to protect against recursion. */
if (self->server)
{
g_autoptr(WfdServer) server = NULL;
server = g_steal_pointer (&self->server);
g_signal_handlers_disconnect_by_data (server, self);
wfd_server_purge (server);
}
self->state = SCREENCAST_SINK_STATE_DISCONNECTED;
g_object_notify (G_OBJECT (self), "state");
}
ScreencastDummyWFDSink *
screencast_dummy_wfd_sink_new (void)
{
......
......@@ -41,6 +41,7 @@ enum {
static void screencast_meta_sink_sink_iface_init (ScreencastSinkIface *iface);
static ScreencastSink * screencast_meta_sink_sink_start_stream (ScreencastSink *sink);
static void screencast_meta_sink_sink_stop_stream (ScreencastSink *sink);
G_DEFINE_TYPE_EXTENDED (ScreencastMetaSink, screencast_meta_sink, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (SCREENCAST_TYPE_SINK,
......@@ -250,6 +251,7 @@ static void
screencast_meta_sink_sink_iface_init (ScreencastSinkIface *iface)
{
iface->start_stream = screencast_meta_sink_sink_start_stream;
iface->stop_stream = screencast_meta_sink_sink_stop_stream;
}
static ScreencastSink *
......@@ -262,6 +264,13 @@ screencast_meta_sink_sink_start_stream (ScreencastSink *sink)
return screencast_sink_start_stream (meta_sink->current_sink);
}
static void
screencast_meta_sink_sink_stop_stream (ScreencastSink *sink)
{
/* This must not happen. */
g_assert_not_reached ();
}
/******************************************************************
* ScreencastMetaSink public functions
******************************************************************/
......
......@@ -84,3 +84,17 @@ screencast_sink_start_stream (ScreencastSink *sink)
return iface->start_stream (sink);
}
/**
* screencast_sink_stop_stream
* @sink: the #ScreencastSink
*
* Stop any active streaming or connection attempt to this sink.
*/
void
screencast_sink_stop_stream (ScreencastSink *sink)
{
ScreencastSinkIface *iface = SCREENCAST_SINK_GET_IFACE (sink);
iface->stop_stream (sink);
}
......@@ -47,11 +47,13 @@ struct _ScreencastSinkIface
/*< public >*/
ScreencastSink * (* start_stream) (ScreencastSink *sink);
void (* stop_stream) (ScreencastSink *sink);
};
GType screencast_sink_get_type (void) G_GNUC_CONST;
ScreencastSink *screencast_sink_start_stream (ScreencastSink *sink);
void screencast_sink_stop_stream (ScreencastSink *sink);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ScreencastSink, g_object_unref)
......
......@@ -32,8 +32,10 @@ struct _ScreencastWFDP2PSink
NMClient *nm_client;
NMDevice *nm_device;
NMP2PPeer *nm_peer;
NMActiveConnection *nm_ac;
WfdServer *server;
guint server_source_id;
};
enum {
......@@ -51,6 +53,10 @@ enum {
static void screencast_wfd_p2p_sink_sink_iface_init (ScreencastSinkIface *iface);
static ScreencastSink * screencast_wfd_p2p_sink_sink_start_stream (ScreencastSink *sink);
static void screencast_wfd_p2p_sink_sink_stop_stream (ScreencastSink *sink);
static void screencast_wfd_p2p_sink_sink_stop_stream_int (ScreencastWFDP2PSink *self);
G_DEFINE_TYPE_EXTENDED (ScreencastWFDP2PSink, screencast_wfd_p2p_sink, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (SCREENCAST_TYPE_SINK,
......@@ -61,12 +67,30 @@ static GParamSpec * props[PROP_LAST] = { NULL, };
static void
peer_notify_cb (ScreencastWFDP2PSink *sink, GParamSpec *pspec, NMP2PPeer *peer)
peer_notify_cb (ScreencastWFDP2PSink *self, GParamSpec *pspec, NMP2PPeer *peer)
{
/* TODO: Assumes the display name may have changed.
* This is obviously overly agressive, on the other hand
* not really an issue. */
g_object_notify (G_OBJECT (sink), "display-name");
g_object_notify (G_OBJECT (self), "display-name");
}
static void
notify_active_connection_cb (ScreencastWFDP2PSink *self, GParamSpec *pspec, NMDevice *device)
{
if (!self->nm_ac)
return;
/* Nothing to do if it is still the correct connection. */
if (self->nm_ac == nm_device_get_active_connection (device))
return;
/* Our active connection is not active anymore ... */
g_clear_object (&self->nm_ac);
screencast_wfd_p2p_sink_sink_stop_stream_int (self);
self->state = SCREENCAST_SINK_STATE_ERROR;
g_object_notify (G_OBJECT (self), "state");
}
static void
......@@ -133,6 +157,12 @@ screencast_wfd_p2p_sink_set_property (GObject *object,
case PROP_CLIENT:
g_assert (sink->nm_client == NULL);
sink->nm_client = g_value_dup_object (value);
g_signal_connect_object (sink->nm_client,
"notify::" NM_DEVICE_ACTIVE_CONNECTION,
(GCallback) notify_active_connection_cb,
sink,
G_CONNECT_SWAPPED);
break;
case PROP_DEVICE:
......@@ -165,12 +195,12 @@ screencast_wfd_p2p_sink_finalize (GObject *object)
g_cancellable_cancel (sink->cancellable);
g_clear_object (&sink->cancellable);
screencast_wfd_p2p_sink_sink_stop_stream_int (sink);
g_clear_object (&sink->nm_client);
g_clear_object (&sink->nm_device);
g_clear_object (&sink->nm_peer);
g_clear_object (&sink->server);
G_OBJECT_CLASS (screencast_wfd_p2p_sink_parent_class)->finalize (object);
}
......@@ -224,6 +254,7 @@ static void
screencast_wfd_p2p_sink_sink_iface_init (ScreencastSinkIface *iface)
{
iface->start_stream = screencast_wfd_p2p_sink_sink_start_stream;
iface->stop_stream = screencast_wfd_p2p_sink_sink_stop_stream;
}
static void
......@@ -235,6 +266,13 @@ play_request_cb (ScreencastWFDP2PSink *sink, GstRTSPContext *ctx, WfdClient *cli
g_object_notify (G_OBJECT (sink), "state");
}
static void
closed_cb (ScreencastWFDP2PSink *sink, WfdClient *client)
{
/* Connection was closed, do a clean shutdown*/
screencast_wfd_p2p_sink_sink_stop_stream (SCREENCAST_SINK (sink));
}
static void
client_connected_cb (ScreencastWFDP2PSink *sink, WfdClient *client, WfdServer *server)
{
......@@ -250,6 +288,12 @@ client_connected_cb (ScreencastWFDP2PSink *sink, WfdClient *client, WfdServer *s
(GCallback) play_request_cb,
sink,
G_CONNECT_SWAPPED);
g_signal_connect_object (client,
"closed",
(GCallback) closed_cb,
sink,
G_CONNECT_SWAPPED);
}
static GstElement*
......@@ -268,12 +312,13 @@ p2p_connected (GObject *source_object,
gpointer user_data)
{
ScreencastWFDP2PSink *sink = NULL;
NMActiveConnection *ac = NULL;
g_autoptr(GError) error = NULL;
g_debug ("ScreencastWfdP2PSink: Got P2P connection");
if (!nm_client_add_and_activate_connection_options_finish (NM_CLIENT (source_object), res, &error))
ac = nm_client_add_and_activate_connection_options_finish (NM_CLIENT (source_object), res, &error);
if (!ac)
{
/* Operation was aborted */
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
......@@ -287,21 +332,20 @@ p2p_connected (GObject *source_object,
}
sink = SCREENCAST_WFD_P2P_SINK (user_data);
sink->nm_ac = ac;
if (sink->server)
g_signal_handlers_disconnect_by_data (sink->server, sink);
g_clear_object (&sink->server);
g_assert (sink->server == NULL);
sink->server = wfd_server_new ();
/*
XXX: Not yet implemented, but we should only bind on the P2P device
wfd_server_set_interface (GST_RTSP_SERVER (sink->server), nm_device_get_ip_iface (sink->nm_device));
* XXX: Not yet implemented, but we should only bind on the P2P device
* wfd_server_set_interface (GST_RTSP_SERVER (sink->server), nm_device_get_ip_iface (sink->nm_device));
*/
if (gst_rtsp_server_attach (GST_RTSP_SERVER (sink->server), NULL) < 0)
{
screencast_wfd_p2p_sink_sink_stop_stream_int (sink);
sink->state = SCREENCAST_SINK_STATE_ERROR;
g_object_notify (G_OBJECT (sink), "state");
g_clear_object (&sink->server);
return;
}
......@@ -351,6 +395,51 @@ screencast_wfd_p2p_sink_sink_start_stream (ScreencastSink *sink)
return g_object_ref (sink);
}
static void
screencast_wfd_p2p_sink_sink_stop_stream_int (ScreencastWFDP2PSink *self)
{
g_cancellable_cancel (self->cancellable);
g_clear_object (&self->cancellable);
self->cancellable = g_cancellable_new ();
/* Destroy the server that is streaming. */
if (self->server_source_id)
{
g_source_remove (self->server_source_id);
self->server_source_id = 0;
}
/* Needs to protect against recursion. */
if (self->server)
{
g_autoptr(WfdServer) server = NULL;
server = g_steal_pointer (&self->server);
g_signal_handlers_disconnect_by_data (server, self);
wfd_server_purge (server);
}
/* And disconnect our active connection.
* nm_ac will be unset if something else destroyed the connection already */
if (self->nm_ac)
{
nm_device_disconnect (self->nm_device, NULL, NULL);
g_clear_object (&self->nm_ac);
}
}
static void
screencast_wfd_p2p_sink_sink_stop_stream (ScreencastSink *sink)
{
ScreencastWFDP2PSink *self = SCREENCAST_WFD_P2P_SINK (sink);
screencast_wfd_p2p_sink_sink_stop_stream_int (self);
self->state = SCREENCAST_SINK_STATE_DISCONNECTED;
g_object_notify (G_OBJECT (self), "state");
}
/******************************************************************
* ScreencastWFDP2PSink public functions
******************************************************************/
......
......@@ -24,8 +24,6 @@ struct _WfdClient
WfdClientInitState init_state;
WfdParams *params;
WfdMedia *media;
};
G_DEFINE_TYPE (WfdClient, wfd_client, GST_TYPE_RTSP_CLIENT)
......@@ -204,8 +202,6 @@ wfd_client_configure_client_media (GstRTSPClient * client,
g_return_val_if_fail (self->params->selected_codec, FALSE);
g_return_val_if_fail (self->params->selected_resolution, FALSE);
self->media = WFD_MEDIA (media);
element = gst_rtsp_media_get_element (media);
wfd_configure_media_element (GST_BIN (element), self->params->selected_codec, self->params->selected_resolution);
......
......@@ -38,6 +38,8 @@ wfd_server_finalize (GObject *object)
{
WfdServer *self = (WfdServer *) object;
g_debug ("WfdServer: Finalize");
g_source_remove (self->clean_pool_source_id);
self->clean_pool_source_id = 0;
......@@ -200,3 +202,32 @@ wfd_server_init (WfdServer *self)
gst_rtsp_server_set_address (GST_RTSP_SERVER (self), "0.0.0.0");
gst_rtsp_server_set_service (GST_RTSP_SERVER (self), "7236");
}
static GstRTSPFilterResult
pool_filter_remove_cb (GstRTSPSessionPool *pool,
GstRTSPSession *session,
gpointer user_data)
{
return GST_RTSP_FILTER_REMOVE;
}
static GstRTSPFilterResult
client_filter_remove_cb (GstRTSPServer *server,
GstRTSPClient *client,
gpointer user_data)
{
return GST_RTSP_FILTER_REMOVE;
}
void
wfd_server_purge (WfdServer *self)
{
GstRTSPServer *server = GST_RTSP_SERVER (self);
GstRTSPSessionPool *session_pool;
session_pool = gst_rtsp_server_get_session_pool (server);
gst_rtsp_session_pool_filter (session_pool, pool_filter_remove_cb, NULL);
gst_rtsp_server_client_filter (server, client_filter_remove_cb, NULL);
}
......@@ -9,5 +9,6 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (WfdServer, wfd_server, WFD, SERVER, GstRTSPServer)
WfdServer * wfd_server_new (void);
void wfd_server_purge (WfdServer *self);
G_END_DECLS
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment