| /* |
| * GStreamer |
| * Copyright (C) 2009 Julien Isorce <julien.isorce@gmail.com> |
| * Copyright (C) 2009 Andrey Nechypurenko <andreynech@gmail.com> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public |
| * License along with this library; if not, write to the |
| * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "pipeline.h" |
| |
| |
| Pipeline::Pipeline (GstGLDisplay *display, |
| GstGLContext * context, const QString & videoLocation, QObject * parent) |
| : |
| QObject (parent), |
| m_videoLocation (videoLocation), |
| m_loop (NULL), |
| m_bus (NULL), |
| m_pipeline (NULL) |
| { |
| this->display = display; |
| this->context = context; |
| this->configure (); |
| } |
| |
| Pipeline::~Pipeline () |
| { |
| } |
| |
| void |
| Pipeline::configure () |
| { |
| |
| #ifdef Q_WS_WIN |
| m_loop = g_main_loop_new (NULL, FALSE); |
| #endif |
| |
| if (m_videoLocation.isEmpty ()) { |
| qDebug ("No video file specified. Using video test source."); |
| m_pipeline = |
| GST_PIPELINE (gst_parse_launch |
| ("videotestsrc ! " |
| "video/x-raw, width=640, height=480, " |
| "framerate=(fraction)30/1 ! " |
| "glupload ! gleffects effect=5 ! fakesink sync=1", NULL)); |
| } else { |
| QByteArray ba = m_videoLocation.toLocal8Bit (); |
| qDebug ("Loading video: %s", ba.data ()); |
| gchar *pipeline = g_strdup_printf ("filesrc name=f ! " |
| "decodebin ! gleffects effect=5 ! " "fakesink sync=1"); |
| m_pipeline = GST_PIPELINE (gst_parse_launch (pipeline, NULL)); |
| GstElement *f = gst_bin_get_by_name (GST_BIN (m_pipeline), "f"); |
| g_object_set (G_OBJECT (f), "location", ba.data (), NULL); |
| gst_object_unref (GST_OBJECT (f)); |
| g_free (pipeline); |
| } |
| |
| m_bus = gst_pipeline_get_bus (GST_PIPELINE (m_pipeline)); |
| gst_bus_add_watch (m_bus, (GstBusFunc) bus_call, this); |
| gst_bus_enable_sync_message_emission (m_bus); |
| g_signal_connect (m_bus, "sync-message", G_CALLBACK (sync_bus_call), this); |
| gst_object_unref (m_bus); |
| |
| gst_element_set_state (GST_ELEMENT (this->m_pipeline), GST_STATE_PAUSED); |
| GstState state = GST_STATE_PAUSED; |
| if (gst_element_get_state (GST_ELEMENT (this->m_pipeline), |
| &state, NULL, GST_CLOCK_TIME_NONE) |
| != GST_STATE_CHANGE_SUCCESS) { |
| qDebug ("failed to pause pipeline"); |
| return; |
| } |
| } |
| |
| void |
| Pipeline::start () |
| { |
| // set a callback to retrieve the gst gl textures |
| GstElement *fakesink = gst_bin_get_by_name (GST_BIN (this->m_pipeline), |
| "fakesink0"); |
| g_object_set (G_OBJECT (fakesink), "signal-handoffs", TRUE, NULL); |
| g_signal_connect (fakesink, "handoff", G_CALLBACK (on_gst_buffer), this); |
| gst_object_unref (fakesink); |
| |
| GstStateChangeReturn ret = |
| gst_element_set_state (GST_ELEMENT (this->m_pipeline), GST_STATE_PLAYING); |
| if (ret == GST_STATE_CHANGE_FAILURE) { |
| qDebug ("Failed to start up pipeline!"); |
| |
| /* check if there is an error message with details on the bus */ |
| GstMessage *msg = gst_bus_poll (this->m_bus, GST_MESSAGE_ERROR, 0); |
| if (msg) { |
| GError *err = NULL; |
| gst_message_parse_error (msg, &err, NULL); |
| qDebug ("ERROR: %s", err->message); |
| g_error_free (err); |
| gst_message_unref (msg); |
| } |
| return; |
| } |
| #ifdef Q_WS_WIN |
| g_main_loop_run (m_loop); |
| #endif |
| } |
| |
| /* fakesink handoff callback */ |
| void |
| Pipeline::on_gst_buffer (GstElement * element, |
| GstBuffer * buf, GstPad * pad, Pipeline * p) |
| { |
| Q_UNUSED (pad) |
| Q_UNUSED (element) |
| |
| /* ref then push buffer to use it in qt */ |
| gst_buffer_ref (buf); |
| p->queue_input_buf.put (buf); |
| |
| if (p->queue_input_buf.size () > 3) |
| p->notifyNewFrame (); |
| |
| /* pop then unref buffer we have finished to use in qt */ |
| if (p->queue_output_buf.size () > 3) { |
| GstBuffer *buf_old = (p->queue_output_buf.get ()); |
| if (buf_old) |
| gst_buffer_unref (buf_old); |
| } |
| } |
| |
| void |
| Pipeline::stop () |
| { |
| #ifdef Q_WS_WIN |
| g_main_loop_quit (m_loop); |
| #else |
| emit stopRequested (); |
| #endif |
| } |
| |
| void |
| Pipeline::unconfigure () |
| { |
| gst_element_set_state (GST_ELEMENT (this->m_pipeline), GST_STATE_NULL); |
| |
| GstBuffer *buf; |
| while (this->queue_input_buf.size ()) { |
| buf = (GstBuffer *) (this->queue_input_buf.get ()); |
| gst_buffer_unref (buf); |
| } |
| while (this->queue_output_buf.size ()) { |
| buf = (GstBuffer *) (this->queue_output_buf.get ()); |
| gst_buffer_unref (buf); |
| } |
| |
| gst_object_unref (m_pipeline); |
| } |
| |
| gboolean Pipeline::bus_call (GstBus * bus, GstMessage * msg, Pipeline * p) |
| { |
| Q_UNUSED (bus) |
| |
| switch (GST_MESSAGE_TYPE (msg)) { |
| case GST_MESSAGE_EOS: |
| qDebug ("End-of-stream received. Stopping."); |
| p->stop (); |
| break; |
| |
| case GST_MESSAGE_ERROR: |
| { |
| gchar * |
| debug = NULL; |
| GError * |
| err = NULL; |
| gst_message_parse_error (msg, &err, &debug); |
| qDebug ("Error: %s", err->message); |
| g_error_free (err); |
| if (debug) { |
| qDebug ("Debug deails: %s", debug); |
| g_free (debug); |
| } |
| p->stop (); |
| break; |
| } |
| |
| default: |
| break; |
| } |
| |
| return TRUE; |
| } |
| |
| gboolean Pipeline::sync_bus_call (GstBus * bus, GstMessage * msg, Pipeline * p) |
| { |
| switch (GST_MESSAGE_TYPE (msg)) { |
| case GST_MESSAGE_NEED_CONTEXT: |
| { |
| const gchar * |
| context_type; |
| |
| gst_message_parse_context_type (msg, &context_type); |
| g_print ("got need context %s\n", context_type); |
| |
| if (g_strcmp0 (context_type, GST_GL_DISPLAY_CONTEXT_TYPE) == 0) { |
| GstContext *display_context = gst_context_new (GST_GL_DISPLAY_CONTEXT_TYPE, TRUE); |
| gst_context_set_gl_display (display_context, p->display); |
| gst_element_set_context (GST_ELEMENT (msg->src), display_context); |
| } else if (g_strcmp0 (context_type, "gst.gl.app_context") == 0) { |
| GstContext *app_context = gst_context_new ("gst.gl.app_context", TRUE); |
| GstStructure *s = gst_context_writable_structure (app_context); |
| gst_structure_set (s, "context", GST_TYPE_GL_CONTEXT, p->context, NULL); |
| gst_element_set_context (GST_ELEMENT (msg->src), app_context); |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| return FALSE; |
| } |