#include <gst/video/video.h>
#include <gst/interfaces/streamvolume.h>
#include <gst/interfaces/colorbalance.h>
+#include <gst/interfaces/xoverlay.h>
#include "gstplaysink.h"
#include "gststreamsynchronizer.h"
gboolean volume_changed; /* volume/mute changed while no audiochain */
gboolean mute_changed; /* ... has been created yet */
gint64 av_offset;
+
+ /* xoverlay proxy interface */
+ GstXOverlay *xoverlay_element; /* protected with LOCK */
+ gboolean xoverlay_handle_set;
+ guintptr xoverlay_handle;
+ gboolean xoverlay_render_rectangle_set;
+ gint xoverlay_x, xoverlay_y, xoverlay_width, xoverlay_height;
+ gboolean xoverlay_handle_events_set;
+ gboolean xoverlay_handle_events;
};
struct _GstPlaySinkClass
/* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
+static void gst_play_sink_implements_interface_init (gpointer g_iface,
+ gpointer g_iface_data);
+static void gst_play_sink_xoverlay_init (gpointer g_iface,
+ gpointer g_iface_data);
static void
_do_init (GType type)
{
+ static const GInterfaceInfo impl_info = {
+ gst_play_sink_implements_interface_init,
+ NULL, NULL
+ };
static const GInterfaceInfo svol_info = {
NULL, NULL, NULL
};
+ static const GInterfaceInfo xov_info = {
+ gst_play_sink_xoverlay_init,
+ NULL, NULL
+ };
+ g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE, &impl_info);
g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_info);
+ g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &xov_info);
}
G_DEFINE_TYPE_WITH_CODE (GstPlaySink, gst_play_sink, GST_TYPE_BIN,
g_param_spec_object ("audio-sink", "Audio Sink",
"the audio output element to use (NULL = default sink)",
GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
/**
* GstPlaySink:text-sink:
*
gst_object_ref_sink (bin);
gst_bin_add (bin, chain->sink);
+ /* Get the XOverlay element */
+ {
+ GstXOverlay *xoverlay = NULL;
+
+ GST_OBJECT_LOCK (playsink);
+ if (playsink->xoverlay_element)
+ gst_object_unref (playsink->xoverlay_element);
+ playsink->xoverlay_element =
+ GST_X_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
+ GST_TYPE_X_OVERLAY));
+ if (playsink->xoverlay_element)
+ xoverlay = GST_X_OVERLAY (gst_object_ref (playsink->xoverlay_element));
+ GST_OBJECT_UNLOCK (playsink);
+
+ if (xoverlay) {
+ if (playsink->xoverlay_handle_set)
+ gst_x_overlay_set_window_handle (xoverlay, playsink->xoverlay_handle);
+ if (playsink->xoverlay_handle_events_set)
+ gst_x_overlay_handle_events (xoverlay,
+ playsink->xoverlay_handle_events);
+ if (playsink->xoverlay_render_rectangle_set)
+ gst_x_overlay_set_render_rectangle (xoverlay,
+ playsink->xoverlay_x, playsink->xoverlay_y,
+ playsink->xoverlay_width, playsink->xoverlay_height);
+ gst_object_unref (xoverlay);
+ }
+ }
+
/* decouple decoder from sink, this improves playback quite a lot since the
* decoder can continue while the sink blocks for synchronisation. We don't
* need a lot of buffers as this consumes a lot of memory and we don't want
free_chain ((GstPlayChain *) chain);
return NULL;
}
+
link_failed:
{
GST_ELEMENT_ERROR (playsink, CORE, PAD,
if (ret == GST_STATE_CHANGE_FAILURE)
return FALSE;
- /* find ts-offset element */
+ /* Get the XOverlay element */
+ {
+ GstXOverlay *xoverlay = NULL;
+
+ GST_OBJECT_LOCK (playsink);
+ if (playsink->xoverlay_element)
+ gst_object_unref (playsink->xoverlay_element);
+ playsink->xoverlay_element =
+ GST_X_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
+ GST_TYPE_X_OVERLAY));
+ if (playsink->xoverlay_element)
+ xoverlay = GST_X_OVERLAY (gst_object_ref (playsink->xoverlay_element));
+ GST_OBJECT_UNLOCK (playsink);
+
+ if (xoverlay) {
+ if (playsink->xoverlay_handle_set)
+ gst_x_overlay_set_window_handle (xoverlay, playsink->xoverlay_handle);
+ if (playsink->xoverlay_handle_events_set)
+ gst_x_overlay_handle_events (xoverlay,
+ playsink->xoverlay_handle_events);
+ if (playsink->xoverlay_render_rectangle_set)
+ gst_x_overlay_set_render_rectangle (xoverlay,
+ playsink->xoverlay_x, playsink->xoverlay_y,
+ playsink->xoverlay_width, playsink->xoverlay_height);
+ gst_object_unref (xoverlay);
+ }
+ }
+ /* find ts-offset element */
gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
G_TYPE_INT64));
need_text = TRUE;
}
+ GST_OBJECT_LOCK (playsink);
+ if (playsink->xoverlay_element)
+ gst_object_unref (playsink->xoverlay_element);
+ playsink->xoverlay_element = NULL;
+ GST_OBJECT_UNLOCK (playsink);
+
if (((flags & GST_PLAY_FLAG_VIDEO)
|| (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
/* we have video and we are requested to show it */
GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
break;
}
+ case GST_MESSAGE_ELEMENT:{
+ if (gst_structure_has_name (message->structure, "prepare-xwindow-id")) {
+ GstXOverlay *xoverlay;
+
+ GST_OBJECT_LOCK (playsink);
+ if (playsink->xoverlay_element
+ && GST_OBJECT_CAST (playsink->xoverlay_element) !=
+ GST_MESSAGE_SRC (message)) {
+ gst_object_unref (playsink->xoverlay_element);
+ playsink->xoverlay_element = NULL;
+ }
+
+ if (!playsink->xoverlay_element)
+ playsink->xoverlay_element =
+ GST_X_OVERLAY (gst_object_ref (GST_MESSAGE_SRC (message)));
+ xoverlay = GST_X_OVERLAY (gst_object_ref (playsink->xoverlay_element));
+ GST_OBJECT_UNLOCK (playsink);
+
+ GST_DEBUG_OBJECT (playsink, "Got prepare-xwindow-id message");
+
+ if (playsink->xoverlay_handle_set)
+ gst_x_overlay_set_window_handle (playsink->xoverlay_element,
+ playsink->xoverlay_handle);
+ if (playsink->xoverlay_handle_events_set)
+ gst_x_overlay_handle_events (playsink->xoverlay_element,
+ playsink->xoverlay_handle_events);
+ if (playsink->xoverlay_render_rectangle_set)
+ gst_x_overlay_set_render_rectangle (playsink->xoverlay_element,
+ playsink->xoverlay_x, playsink->xoverlay_y,
+ playsink->xoverlay_width, playsink->xoverlay_height);
+
+ gst_object_unref (xoverlay);
+ gst_message_unref (message);
+ gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (playsink));
+ }
+ break;
+ }
default:
GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
break;
gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
{
gboolean res = TRUE;
-
if (playsink->textchain && playsink->textchain->sink) {
gst_event_ref (event);
if ((res = gst_element_send_event (playsink->textchain->chain.bin, event))) {
gboolean res = FALSE;
GstEventType event_type = GST_EVENT_TYPE (event);
GstPlaySink *playsink;
-
playsink = GST_PLAY_SINK_CAST (element);
-
switch (event_type) {
case GST_EVENT_SEEK:
GST_DEBUG_OBJECT (element, "Sending event to a sink");
guint64 amount;
gdouble rate;
gboolean flush, intermediate;
-
gst_event_parse_step (event, &format, &amount, &rate, &flush,
&intermediate);
-
if (format == GST_FORMAT_BUFFERS) {
/* for buffers, we will try to step video frames, for other formats we
* send the step to all sinks */
{
GstStateChangeReturn ret;
GstStateChangeReturn bret;
-
GstPlaySink *playsink;
-
playsink = GST_PLAY_SINK (element);
-
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
playsink->need_async_start = TRUE;
GstPad *opad =
GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
(playsink->audio_pad)));
-
if (gst_pad_is_blocked (opad)) {
gst_pad_set_blocked_async_full (opad, FALSE, sinkpad_blocked_cb,
gst_object_ref (playsink), (GDestroyNotify) gst_object_unref);
gst_object_unref (playsink->videochain->ts_offset);
playsink->videochain->ts_offset = NULL;
}
+
+ GST_OBJECT_LOCK (playsink);
+ if (playsink->xoverlay_element)
+ gst_object_unref (playsink->xoverlay_element);
+ playsink->xoverlay_element = NULL;
+ GST_OBJECT_UNLOCK (playsink);
+
ret = GST_STATE_CHANGE_SUCCESS;
break;
default:
if (playsink->textchain && playsink->textchain->sink)
gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
playsink->textchain->sink);
-
if (playsink->audio_sink != NULL)
gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
if (playsink->video_sink != NULL)
gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
if (playsink->text_sink != NULL)
gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
-
free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
playsink->videodeinterlacechain = NULL;
free_chain ((GstPlayChain *) playsink->videochain);
break;
}
return ret;
-
/* ERRORS */
activate_failed:
{
const GValue * value, GParamSpec * spec)
{
GstPlaySink *playsink = GST_PLAY_SINK (object);
-
switch (prop_id) {
case PROP_FLAGS:
gst_play_sink_set_flags (playsink, g_value_get_flags (value));
GValue * value, GParamSpec * spec)
{
GstPlaySink *playsink = GST_PLAY_SINK (object);
-
switch (prop_id) {
case PROP_FLAGS:
g_value_set_flags (value, gst_play_sink_get_flags (playsink));
}
}
+static void
+gst_play_sink_xoverlay_expose (GstXOverlay * overlay)
+{
+ GstPlaySink *playsink = GST_PLAY_SINK (overlay);
+ GstXOverlay *xoverlay;
+
+ GST_OBJECT_LOCK (playsink);
+ if (playsink->xoverlay_element)
+ xoverlay = GST_X_OVERLAY (gst_object_ref (playsink->xoverlay_element));
+ else
+ xoverlay = NULL;
+ GST_OBJECT_UNLOCK (playsink);
+
+ if (xoverlay) {
+ gst_x_overlay_expose (xoverlay);
+ gst_object_unref (xoverlay);
+ }
+}
+
+static void
+gst_play_sink_xoverlay_handle_events (GstXOverlay * overlay,
+ gboolean handle_events)
+{
+ GstPlaySink *playsink = GST_PLAY_SINK (overlay);
+ GstXOverlay *xoverlay;
+
+ GST_OBJECT_LOCK (playsink);
+ if (playsink->xoverlay_element)
+ xoverlay = GST_X_OVERLAY (gst_object_ref (playsink->xoverlay_element));
+ else
+ xoverlay = NULL;
+ GST_OBJECT_UNLOCK (playsink);
+
+ playsink->xoverlay_handle_events_set = TRUE;
+ playsink->xoverlay_handle_events = handle_events;
+
+ if (xoverlay) {
+ gst_x_overlay_handle_events (xoverlay, handle_events);
+ gst_object_unref (xoverlay);
+ }
+}
+
+static void
+gst_play_sink_xoverlay_set_render_rectangle (GstXOverlay * overlay, gint x,
+ gint y, gint width, gint height)
+{
+ GstPlaySink *playsink = GST_PLAY_SINK (overlay);
+ GstXOverlay *xoverlay;
+
+ GST_OBJECT_LOCK (playsink);
+ if (playsink->xoverlay_element)
+ xoverlay = GST_X_OVERLAY (gst_object_ref (playsink->xoverlay_element));
+ else
+ xoverlay = NULL;
+ GST_OBJECT_UNLOCK (playsink);
+
+ playsink->xoverlay_render_rectangle_set = TRUE;
+ playsink->xoverlay_x = x;
+ playsink->xoverlay_y = y;
+ playsink->xoverlay_width = width;
+ playsink->xoverlay_height = height;
+
+ if (xoverlay) {
+ gst_x_overlay_set_render_rectangle (xoverlay, x, y, width, height);
+ gst_object_unref (xoverlay);
+ }
+}
+
+static void
+gst_play_sink_xoverlay_set_window_handle (GstXOverlay * overlay,
+ guintptr handle)
+{
+ GstPlaySink *playsink = GST_PLAY_SINK (overlay);
+ GstXOverlay *xoverlay;
+
+ GST_OBJECT_LOCK (playsink);
+ if (playsink->xoverlay_element)
+ xoverlay = GST_X_OVERLAY (gst_object_ref (playsink->xoverlay_element));
+ else
+ xoverlay = NULL;
+ GST_OBJECT_UNLOCK (playsink);
+
+ playsink->xoverlay_handle_set = TRUE;
+ playsink->xoverlay_handle = handle;
+
+ if (xoverlay) {
+ gst_x_overlay_set_window_handle (xoverlay, handle);
+ gst_object_unref (xoverlay);
+ }
+}
+
+static void
+gst_play_sink_xoverlay_init (gpointer g_iface, gpointer g_iface_data)
+{
+ GstXOverlayClass *iface = (GstXOverlayClass *) g_iface;
+ iface->expose = gst_play_sink_xoverlay_expose;
+ iface->handle_events = gst_play_sink_xoverlay_handle_events;
+ iface->set_render_rectangle = gst_play_sink_xoverlay_set_render_rectangle;
+ iface->set_window_handle = gst_play_sink_xoverlay_set_window_handle;
+}
+
+static gboolean
+gst_play_sink_implements_interface_supported (GstImplementsInterface * iface,
+ GType type)
+{
+ if (type == GST_TYPE_X_OVERLAY || type == GST_TYPE_STREAM_VOLUME)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static void
+gst_play_sink_implements_interface_init (gpointer g_iface,
+ gpointer g_iface_data)
+{
+ GstImplementsInterfaceClass *iface = (GstImplementsInterfaceClass *) g_iface;
+ iface->supported = gst_play_sink_implements_interface_supported;
+}
+
gboolean
gst_play_sink_plugin_init (GstPlugin * plugin)
{
GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
-
return gst_element_register (plugin, "playsink", GST_RANK_NONE,
GST_TYPE_PLAY_SINK);
}