blob: 1cff8a4f6314718dd8aa27f80c2b1c1d99d6846a [file] [log] [blame]
/*
* 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;
}