diff options
Diffstat (limited to 'src/plugins/gstreamer/gstvideoconnector.c')
-rw-r--r-- | src/plugins/gstreamer/gstvideoconnector.c | 421 |
1 files changed, 421 insertions, 0 deletions
diff --git a/src/plugins/gstreamer/gstvideoconnector.c b/src/plugins/gstreamer/gstvideoconnector.c new file mode 100644 index 000000000..55570873e --- /dev/null +++ b/src/plugins/gstreamer/gstvideoconnector.c @@ -0,0 +1,421 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "gstvideoconnector.h" + +/* signals */ +enum +{ + SIGNAL_RESEND_NEW_SEGMENT, + SIGNAL_CONNECTION_FAILED, + LAST_SIGNAL +}; +static guint gst_video_connector_signals[LAST_SIGNAL] = { 0 }; + + +GST_DEBUG_CATEGORY_STATIC (video_connector_debug); +#define GST_CAT_DEFAULT video_connector_debug + +static GstStaticPadTemplate gst_video_connector_sink_factory = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +static GstStaticPadTemplate gst_video_connector_src_factory = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +#define _do_init(bla) \ + GST_DEBUG_CATEGORY_INIT (video_connector_debug, \ + "video-connector", 0, "An identity like element for reconnecting video stream"); + +GST_BOILERPLATE_FULL (GstVideoConnector, gst_video_connector, GstElement, + GST_TYPE_ELEMENT, _do_init); + +static void gst_video_connector_dispose (GObject * object); +static GstFlowReturn gst_video_connector_chain (GstPad * pad, GstBuffer * buf); +static GstFlowReturn gst_video_connector_buffer_alloc (GstPad * pad, + guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf); +static GstStateChangeReturn gst_video_connector_change_state (GstElement * + element, GstStateChange transition); +static gboolean gst_video_connector_handle_sink_event (GstPad * pad, + GstEvent * event); +static gboolean gst_video_connector_new_buffer_probe(GstObject *pad, GstBuffer *buffer, guint * object); +static void gst_video_connector_resend_new_segment(GstElement * element, gboolean emitFailedSignal); +static gboolean gst_video_connector_setcaps (GstPad *pad, GstCaps *caps); +static GstCaps *gst_video_connector_getcaps (GstPad * pad); +static gboolean gst_video_connector_acceptcaps (GstPad * pad, GstCaps * caps); + +static void +gst_video_connector_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_set_details_simple (element_class, "Video Connector", + "Generic", + "An identity like element used for reconnecting video stream", + "Dmytro Poplavskiy <dmytro.poplavskiy@nokia.com>"); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_video_connector_sink_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_video_connector_src_factory)); +} + +static void +gst_video_connector_class_init (GstVideoConnectorClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->dispose = gst_video_connector_dispose; + gstelement_class->change_state = gst_video_connector_change_state; + klass->resend_new_segment = gst_video_connector_resend_new_segment; + + gst_video_connector_signals[SIGNAL_RESEND_NEW_SEGMENT] = + g_signal_new ("resend-new-segment", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GstVideoConnectorClass, resend_new_segment), NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); + + gst_video_connector_signals[SIGNAL_CONNECTION_FAILED] = + g_signal_new ("connection-failed", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); +} + +static void +gst_video_connector_init (GstVideoConnector *element, + GstVideoConnectorClass *g_class) +{ + element->sinkpad = + gst_pad_new_from_static_template (&gst_video_connector_sink_factory, + "sink"); + gst_pad_set_chain_function(element->sinkpad, + GST_DEBUG_FUNCPTR (gst_video_connector_chain)); + gst_pad_set_event_function(element->sinkpad, + GST_DEBUG_FUNCPTR (gst_video_connector_handle_sink_event)); + gst_pad_set_bufferalloc_function(element->sinkpad, + GST_DEBUG_FUNCPTR (gst_video_connector_buffer_alloc)); + gst_pad_set_setcaps_function(element->sinkpad, + GST_DEBUG_FUNCPTR (gst_video_connector_setcaps)); + gst_pad_set_getcaps_function(element->sinkpad, + GST_DEBUG_FUNCPTR(gst_video_connector_getcaps)); + gst_pad_set_acceptcaps_function(element->sinkpad, + GST_DEBUG_FUNCPTR(gst_video_connector_acceptcaps)); + + gst_element_add_pad (GST_ELEMENT (element), element->sinkpad); + + element->srcpad = + gst_pad_new_from_static_template (&gst_video_connector_src_factory, + "src"); + gst_pad_add_buffer_probe(element->srcpad, + G_CALLBACK(gst_video_connector_new_buffer_probe), element); + gst_element_add_pad (GST_ELEMENT (element), element->srcpad); + + element->relinked = FALSE; + element->failedSignalEmited = FALSE; + gst_segment_init (&element->segment, GST_FORMAT_TIME); + element->latest_buffer = NULL; +} + +static void +gst_video_connector_reset (GstVideoConnector * element) +{ + element->relinked = FALSE; + element->failedSignalEmited = FALSE; + if (element->latest_buffer != NULL) { + gst_buffer_unref (element->latest_buffer); + element->latest_buffer = NULL; + } + gst_segment_init (&element->segment, GST_FORMAT_UNDEFINED); +} + +static void +gst_video_connector_dispose (GObject * object) +{ + GstVideoConnector *element = GST_VIDEO_CONNECTOR (object); + + gst_video_connector_reset (element); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static GstFlowReturn +gst_video_connector_buffer_alloc (GstPad * pad, guint64 offset, guint size, + GstCaps * caps, GstBuffer ** buf) +{ + GstVideoConnector *element; + GstFlowReturn res = GST_FLOW_OK; + element = GST_VIDEO_CONNECTOR (GST_PAD_PARENT (pad)); + + if (!buf) + return GST_FLOW_ERROR; + *buf = NULL; + + GST_OBJECT_LOCK (element); + gst_object_ref(element->srcpad); + GST_OBJECT_UNLOCK (element); + + res = gst_pad_alloc_buffer(element->srcpad, offset, size, caps, buf); + gst_object_unref (element->srcpad); + + GST_DEBUG_OBJECT (element, "buffer alloc finished: %s", gst_flow_get_name (res)); + + return res; +} + +static gboolean +gst_video_connector_setcaps (GstPad *pad, GstCaps *caps) +{ + GstVideoConnector *element; + element = GST_VIDEO_CONNECTOR (GST_PAD_PARENT (pad)); + + /* forward-negotiate */ + gboolean res = gst_pad_set_caps(element->srcpad, caps); + + GST_DEBUG_OBJECT(element, "gst_video_connector_setcaps %s %i", gst_caps_to_string(caps), res); + + if (!res) { + //if set_caps failed, emit "connection-failed" signal + //so colorspace transformation elemnt can be inserted + GST_INFO_OBJECT(element, "gst_video_connector_setcaps failed, emit connection-failed signal"); + g_signal_emit(G_OBJECT(element), gst_video_connector_signals[SIGNAL_CONNECTION_FAILED], 0); + + return gst_pad_set_caps(element->srcpad, caps); + } + + return TRUE; +} + +static GstCaps *gst_video_connector_getcaps (GstPad * pad) +{ + GstVideoConnector *element; + element = GST_VIDEO_CONNECTOR (GST_PAD_PARENT (pad)); + +#if (GST_VERSION_MICRO > 25) + GstCaps *caps = gst_pad_peer_get_caps_reffed(element->srcpad); +#else + GstCaps *caps = gst_pad_peer_get_caps(element->srcpad); +#endif + + if (!caps) + caps = gst_caps_new_any(); + + return caps; +} + +static gboolean gst_video_connector_acceptcaps (GstPad * pad, GstCaps * caps) +{ + GstVideoConnector *element; + element = GST_VIDEO_CONNECTOR (GST_PAD_PARENT (pad)); + + return gst_pad_peer_accept_caps(element->srcpad, caps); +} + +static void +gst_video_connector_resend_new_segment(GstElement * element, gboolean emitFailedSignal) +{ + GST_INFO_OBJECT(element, "New segment requested, failed signal enabled: %i", emitFailedSignal); + GstVideoConnector *connector = GST_VIDEO_CONNECTOR(element); + connector->relinked = TRUE; + if (emitFailedSignal) + connector->failedSignalEmited = FALSE; +} + + +static gboolean gst_video_connector_new_buffer_probe(GstObject *pad, GstBuffer *buffer, guint * object) +{ + GstVideoConnector *element = GST_VIDEO_CONNECTOR (object); + + /* + If relinking is requested, the current buffer should be rejected and + the new segment + previous buffer should be pushed first + */ + + if (element->relinked) + GST_LOG_OBJECT(element, "rejected buffer because of new segment request"); + + return !element->relinked; +} + + +static GstFlowReturn +gst_video_connector_chain (GstPad * pad, GstBuffer * buf) +{ + GstFlowReturn res; + GstVideoConnector *element; + + element = GST_VIDEO_CONNECTOR (gst_pad_get_parent (pad)); + + do { + /* + Resend the segment message and last buffer to preroll the new sink. + Sinks can be changed multiple times while paused, + while loop allows to send the segment message and preroll + all of them with the same buffer. + */ + while (element->relinked) { + element->relinked = FALSE; + + gint64 pos = element->segment.last_stop; + + if (element->latest_buffer && GST_BUFFER_TIMESTAMP_IS_VALID(element->latest_buffer)) { + pos = GST_BUFFER_TIMESTAMP (element->latest_buffer); + } + + //push a new segment and last buffer + GstEvent *ev = gst_event_new_new_segment (TRUE, + element->segment.rate, + element->segment.format, + pos, //start + element->segment.stop, + pos); + + GST_DEBUG_OBJECT (element, "Pushing new segment event"); + if (!gst_pad_push_event (element->srcpad, ev)) { + GST_WARNING_OBJECT (element, + "Newsegment handling failed in %" GST_PTR_FORMAT, + element->srcpad); + } + + if (element->latest_buffer) { + GST_DEBUG_OBJECT (element, "Pushing latest buffer..."); + gst_buffer_ref(element->latest_buffer); + gst_pad_push(element->srcpad, element->latest_buffer); + } + } + + gst_buffer_ref(buf); + + //it's possible video sink is changed during gst_pad_push blocked by + //pad lock, in this case ( element->relinked == TRUE ) + //the buffer should be rejected by the buffer probe and + //the new segment + prev buffer should be sent before + + GST_LOG_OBJECT (element, "Pushing buffer..."); + res = gst_pad_push (element->srcpad, buf); + GST_LOG_OBJECT (element, "Pushed buffer: %s", gst_flow_get_name (res)); + + //if gst_pad_push failed give the service another chance, + //it may still work with the colorspace element added + if (!element->failedSignalEmited && res == GST_FLOW_NOT_NEGOTIATED) { + element->failedSignalEmited = TRUE; + GST_INFO_OBJECT(element, "gst_pad_push failed, emit connection-failed signal"); + g_signal_emit(G_OBJECT(element), gst_video_connector_signals[SIGNAL_CONNECTION_FAILED], 0); + } + + } while (element->relinked); + + + if (element->latest_buffer) { + gst_buffer_unref (element->latest_buffer); + element->latest_buffer = NULL; + } + + //don't save the last video buffer on maemo6 because of buffers shortage + //with omapxvsink +#ifndef Q_WS_MAEMO_6 + element->latest_buffer = gst_buffer_ref(buf); +#endif + + gst_buffer_unref(buf); + gst_object_unref (element); + + return res; +} + +static GstStateChangeReturn +gst_video_connector_change_state (GstElement * element, + GstStateChange transition) +{ + GstVideoConnector *connector; + GstStateChangeReturn result; + + connector = GST_VIDEO_CONNECTOR(element); + result = GST_ELEMENT_CLASS (parent_class)->change_state(element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_video_connector_reset (connector); + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + connector->relinked = FALSE; + break; + default: + break; + } + + return result; +} + +static gboolean +gst_video_connector_handle_sink_event (GstPad * pad, GstEvent * event) +{ + if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) { + GstVideoConnector *element = GST_VIDEO_CONNECTOR (gst_pad_get_parent (pad)); + + gboolean update; + GstFormat format; + gdouble rate, arate; + gint64 start, stop, time; + + gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, + &start, &stop, &time); + + GST_LOG_OBJECT (element, + "NEWSEGMENT update %d, rate %lf, applied rate %lf, " + "format %d, " "%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %" + G_GINT64_FORMAT, update, rate, arate, format, start, stop, time); + + gst_segment_set_newsegment_full (&element->segment, update, + rate, arate, format, start, stop, time); + + gst_object_unref (element); + } + + return gst_pad_event_default (pad, event); +} |