1 /*
2 * GStreamer
3 * Copyright (C) 2009 Julien Isorce <julien.isorce@gmail.com>
4 * Copyright (C) 2009 Andrey Nechypurenko <andreynech@gmail.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22 #include <gst/gl/gl.h>
23 #include "../gl-compat-defines.h"
24 #include "pipeline.h"
25
Pipeline(GstGLDisplay * display,GstGLContext * context,const QString & videoLocation,QObject * parent)26 Pipeline::Pipeline (GstGLDisplay *display,
27 GstGLContext * context, const QString & videoLocation, QObject * parent)
28 :
29 QObject (parent),
30 m_videoLocation (videoLocation),
31 m_loop (NULL),
32 m_bus (NULL),
33 m_pipeline (NULL)
34 {
35 this->display = display;
36 this->context = context;
37 this->configure ();
38 }
39
~Pipeline()40 Pipeline::~Pipeline ()
41 {
42 }
43
44 void
configure()45 Pipeline::configure ()
46 {
47
48 #ifdef Q_WS_WIN
49 m_loop = g_main_loop_new (NULL, FALSE);
50 #endif
51
52 if (m_videoLocation.isEmpty ()) {
53 qDebug ("No video file specified. Using video test source.");
54 m_pipeline =
55 GST_PIPELINE (gst_parse_launch
56 ("videotestsrc ! "
57 "video/x-raw, width=640, height=480, "
58 "framerate=(fraction)30/1 ! "
59 "glupload ! gleffects effect=5 ! fakesink sync=1", NULL));
60 } else {
61 QByteArray ba = m_videoLocation.toLocal8Bit ();
62 qDebug ("Loading video: %s", ba.data ());
63 gchar *pipeline = g_strdup_printf ("filesrc name=f ! "
64 "decodebin ! gleffects effect=5 ! " "fakesink sync=1");
65 m_pipeline = GST_PIPELINE (gst_parse_launch (pipeline, NULL));
66 GstElement *f = gst_bin_get_by_name (GST_BIN (m_pipeline), "f");
67 g_object_set (G_OBJECT (f), "location", ba.data (), NULL);
68 gst_object_unref (GST_OBJECT (f));
69 g_free (pipeline);
70 }
71
72 m_bus = gst_pipeline_get_bus (GST_PIPELINE (m_pipeline));
73 gst_bus_add_watch (m_bus, (GstBusFunc) bus_call, this);
74 gst_bus_enable_sync_message_emission (m_bus);
75 g_signal_connect (m_bus, "sync-message", G_CALLBACK (sync_bus_call), this);
76 gst_object_unref (m_bus);
77
78 gst_element_set_state (GST_ELEMENT (this->m_pipeline), GST_STATE_PAUSED);
79 GstState state = GST_STATE_PAUSED;
80 if (gst_element_get_state (GST_ELEMENT (this->m_pipeline),
81 &state, NULL, GST_CLOCK_TIME_NONE)
82 != GST_STATE_CHANGE_SUCCESS) {
83 qDebug ("failed to pause pipeline");
84 return;
85 }
86 }
87
88 void
start()89 Pipeline::start ()
90 {
91 // set a callback to retrieve the gst gl textures
92 GstElement *fakesink = gst_bin_get_by_name (GST_BIN (this->m_pipeline),
93 "fakesink0");
94 g_object_set (G_OBJECT (fakesink), "signal-handoffs", TRUE, NULL);
95 g_signal_connect (fakesink, "handoff", G_CALLBACK (on_gst_buffer), this);
96 gst_object_unref (fakesink);
97
98 GstStateChangeReturn ret =
99 gst_element_set_state (GST_ELEMENT (this->m_pipeline), GST_STATE_PLAYING);
100 if (ret == GST_STATE_CHANGE_FAILURE) {
101 qDebug ("Failed to start up pipeline!");
102
103 /* check if there is an error message with details on the bus */
104 GstMessage *msg = gst_bus_poll (this->m_bus, GST_MESSAGE_ERROR, 0);
105 if (msg) {
106 GError *err = NULL;
107 gst_message_parse_error (msg, &err, NULL);
108 qDebug ("ERROR: %s", err->message);
109 g_error_free (err);
110 gst_message_unref (msg);
111 }
112 return;
113 }
114 #ifdef Q_WS_WIN
115 g_main_loop_run (m_loop);
116 #endif
117 }
118
119 /* fakesink handoff callback */
120 void
on_gst_buffer(GstElement * element,GstBuffer * buf,GstPad * pad,Pipeline * p)121 Pipeline::on_gst_buffer (GstElement * element,
122 GstBuffer * buf, GstPad * pad, Pipeline * p)
123 {
124 Q_UNUSED (pad)
125 Q_UNUSED (element)
126
127 /* ref then push buffer to use it in qt */
128 gst_buffer_ref (buf);
129 p->queue_input_buf.put (buf);
130
131 if (p->queue_input_buf.size () > 3)
132 p->notifyNewFrame ();
133
134 /* pop then unref buffer we have finished to use in qt */
135 if (p->queue_output_buf.size () > 3) {
136 GstBuffer *buf_old = (p->queue_output_buf.get ());
137 if (buf_old)
138 gst_buffer_unref (buf_old);
139 }
140 }
141
142 void
stop()143 Pipeline::stop ()
144 {
145 #ifdef Q_WS_WIN
146 g_main_loop_quit (m_loop);
147 #else
148 emit stopRequested ();
149 #endif
150 }
151
152 void
unconfigure()153 Pipeline::unconfigure ()
154 {
155 gst_element_set_state (GST_ELEMENT (this->m_pipeline), GST_STATE_NULL);
156
157 GstBuffer *buf;
158 while (this->queue_input_buf.size ()) {
159 buf = (GstBuffer *) (this->queue_input_buf.get ());
160 gst_buffer_unref (buf);
161 }
162 while (this->queue_output_buf.size ()) {
163 buf = (GstBuffer *) (this->queue_output_buf.get ());
164 gst_buffer_unref (buf);
165 }
166
167 gst_object_unref (m_pipeline);
168 }
169
bus_call(GstBus * bus,GstMessage * msg,Pipeline * p)170 gboolean Pipeline::bus_call (GstBus * bus, GstMessage * msg, Pipeline * p)
171 {
172 Q_UNUSED (bus)
173
174 switch (GST_MESSAGE_TYPE (msg)) {
175 case GST_MESSAGE_EOS:
176 qDebug ("End-of-stream received. Stopping.");
177 p->stop ();
178 break;
179
180 case GST_MESSAGE_ERROR:
181 {
182 gchar *
183 debug = NULL;
184 GError *
185 err = NULL;
186 gst_message_parse_error (msg, &err, &debug);
187 qDebug ("Error: %s", err->message);
188 g_error_free (err);
189 if (debug) {
190 qDebug ("Debug deails: %s", debug);
191 g_free (debug);
192 }
193 p->stop ();
194 break;
195 }
196
197 default:
198 break;
199 }
200
201 return TRUE;
202 }
203
sync_bus_call(GstBus * bus,GstMessage * msg,Pipeline * p)204 gboolean Pipeline::sync_bus_call (GstBus * bus, GstMessage * msg, Pipeline * p)
205 {
206 switch (GST_MESSAGE_TYPE (msg)) {
207 case GST_MESSAGE_NEED_CONTEXT:
208 {
209 const gchar *
210 context_type;
211
212 gst_message_parse_context_type (msg, &context_type);
213 g_print ("got need context %s\n", context_type);
214
215 if (g_strcmp0 (context_type, GST_GL_DISPLAY_CONTEXT_TYPE) == 0) {
216 GstContext *display_context = gst_context_new (GST_GL_DISPLAY_CONTEXT_TYPE, TRUE);
217 gst_context_set_gl_display (display_context, p->display);
218 gst_element_set_context (GST_ELEMENT (msg->src), display_context);
219 } else if (g_strcmp0 (context_type, "gst.gl.app_context") == 0) {
220 GstContext *app_context = gst_context_new ("gst.gl.app_context", TRUE);
221 GstStructure *s = gst_context_writable_structure (app_context);
222 gst_structure_set (s, "context", GST_TYPE_GL_CONTEXT, p->context, NULL);
223 gst_element_set_context (GST_ELEMENT (msg->src), app_context);
224 }
225 break;
226 }
227 default:
228 break;
229 }
230 return FALSE;
231 }
232