summaryrefslogtreecommitdiffstats
path: root/src/gsttools/gstvideoconnector.c
diff options
context:
space:
mode:
authorJonas Rabbe <jonas.rabbe@nokia.com>2012-03-22 11:04:03 +1000
committerQt by Nokia <qt-info@nokia.com>2012-04-27 09:22:09 +0200
commita3b6eabd45b452b98c5b7c45df8a332ad018b8f1 (patch)
tree9f3a3fd7bfd6c514a472ad0103037476da4c6e0f /src/gsttools/gstvideoconnector.c
parente44bcf0a384c15e7df593ec3f3cee30775f3f1ef (diff)
Split gstreamer plugin into smaller plugins providing fewer services
The gstreamer blob has been split into four plugins: audiodecoder, camerabin, mediacapture, and mediaplayer. Note: camerabin is still disabled because it is untested camerabin2 implementation. A new qmake configuration use_gstreamer_camera has been introduced and is needed for the mediacapture plugin to expose the camera service. This configuration has been disabled by default. Shared functionality has been moved to the internal gsttools library. Change-Id: Ifb2604f440cfa97513d39f5d7978766c88eaec45 Reviewed-by: Michael Goddard <michael.goddard@nokia.com>
Diffstat (limited to 'src/gsttools/gstvideoconnector.c')
-rw-r--r--src/gsttools/gstvideoconnector.c474
1 files changed, 474 insertions, 0 deletions
diff --git a/src/gsttools/gstvideoconnector.c b/src/gsttools/gstvideoconnector.c
new file mode 100644
index 000000000..c3cb2f430
--- /dev/null
+++ b/src/gsttools/gstvideoconnector.c
@@ -0,0 +1,474 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "gstvideoconnector_p.h"
+#include <unistd.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)
+{
+ (void) 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);
+}
+
+// "When this function returns anything else than GST_FLOW_OK,
+// the buffer allocation failed and buf does not contain valid data."
+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;
+
+ gboolean isFailed = FALSE;
+ while (1) {
+ GST_OBJECT_LOCK (element);
+ gst_object_ref(element->srcpad);
+ GST_OBJECT_UNLOCK (element);
+
+ // Check if downstream element is in NULL state
+ // and wait for up to 1 second for it to switch.
+ GstPad *peerPad = gst_pad_get_peer(element->srcpad);
+ if (peerPad) {
+ GstElement *parent = gst_pad_get_parent_element(peerPad);
+ gst_object_unref (peerPad);
+ if (parent) {
+ GstState state;
+ GstState pending;
+ int totalTimeout = 0;
+ // This seems to sleep for about 10ms usually.
+ while (totalTimeout < 1000000) {
+ gst_element_get_state(parent, &state, &pending, 0);
+ if (state != GST_STATE_NULL)
+ break;
+ usleep(5000);
+ totalTimeout += 5000;
+ }
+
+ gst_object_unref (parent);
+ if (state == GST_STATE_NULL) {
+ GST_DEBUG_OBJECT (element, "Downstream element is in NULL state");
+ // Downstream filter seems to be in the wrong state
+ return GST_FLOW_UNEXPECTED;
+ }
+ }
+ }
+
+ 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));
+
+ if (res == GST_FLOW_WRONG_STATE) {
+ // Just in case downstream filter is still somehow in the wrong state.
+ // Pipeline stalls if we report GST_FLOW_WRONG_STATE.
+ return GST_FLOW_UNEXPECTED;
+ }
+
+ if (res >= GST_FLOW_OK || isFailed == TRUE)
+ break;
+
+ //if gst_pad_alloc_buffer failed, emit "connection-failed" signal
+ //so colorspace transformation element can be inserted
+ GST_INFO_OBJECT(element, "gst_video_connector_buffer_alloc failed, emit connection-failed signal");
+ g_signal_emit(G_OBJECT(element), gst_video_connector_signals[SIGNAL_CONNECTION_FAILED], 0);
+ isFailed = TRUE;
+ }
+
+ 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 element 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)
+{
+ (void) pad;
+ (void) buffer;
+
+ 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);
+}