2 * Copyright (C) 2010 Igalia S.L
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
21 #include "GStreamerGWorld.h"
22 #if ENABLE(VIDEO) && USE(GSTREAMER)
24 #include "GOwnPtrGStreamer.h"
26 #include <gst/interfaces/xoverlay.h>
27 #include <gst/pbutils/pbutils.h>
31 #ifdef GDK_WINDOWING_X11
32 #include <gdk/gdkx.h> // for GDK_WINDOW_XID
40 gboolean gstGWorldSyncMessageCallback(GstBus* bus, GstMessage* message, gpointer data)
42 ASSERT(GST_MESSAGE_TYPE(message) == GST_MESSAGE_ELEMENT);
44 GStreamerGWorld* gstGWorld = static_cast<GStreamerGWorld*>(data);
46 if (gst_structure_has_name(message->structure, "prepare-xwindow-id")
47 || gst_structure_has_name(message->structure, "have-ns-view"))
48 gstGWorld->setWindowOverlay(message);
52 PassRefPtr<GStreamerGWorld> GStreamerGWorld::createGWorld(GstElement* pipeline)
54 return adoptRef(new GStreamerGWorld(pipeline));
57 GStreamerGWorld::GStreamerGWorld(GstElement* pipeline)
58 : m_pipeline(pipeline)
61 // XOverlay messages need to be handled synchronously.
62 GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(m_pipeline));
63 gst_bus_set_sync_handler(bus, gst_bus_sync_signal_handler, this);
64 g_signal_connect(bus, "sync-message::element", G_CALLBACK(gstGWorldSyncMessageCallback), this);
65 gst_object_unref(bus);
68 GStreamerGWorld::~GStreamerGWorld()
75 bool GStreamerGWorld::enterFullscreen()
81 m_videoWindow = PlatformVideoWindow::createWindow();
83 GstElement* platformVideoSink = gst_element_factory_make("autovideosink", "platformVideoSink");
84 GstElement* colorspace = gst_element_factory_make("ffmpegcolorspace", "colorspace");
85 GstElement* queue = gst_element_factory_make("queue", "queue");
86 GstElement* videoScale = gst_element_factory_make("videoscale", "videoScale");
88 // Get video sink bin and the tee inside.
89 GOwnPtr<GstElement> videoSink;
90 g_object_get(m_pipeline, "video-sink", &videoSink.outPtr(), NULL);
91 GstElement* tee = gst_bin_get_by_name(GST_BIN(videoSink.get()), "videoTee");
92 GstElement* valve = gst_bin_get_by_name(GST_BIN(videoSink.get()), "videoValve");
94 g_object_set(valve, "drop-probability", 1.0, NULL);
96 // Add and link a queue, ffmpegcolorspace, videoscale and sink in the bin.
97 gst_bin_add_many(GST_BIN(videoSink.get()), platformVideoSink, videoScale, colorspace, queue, NULL);
99 // Faster elements linking.
100 gst_element_link_pads_full(queue, "src", colorspace, "sink", GST_PAD_LINK_CHECK_NOTHING);
101 gst_element_link_pads_full(colorspace, "src", videoScale, "sink", GST_PAD_LINK_CHECK_NOTHING);
102 gst_element_link_pads_full(videoScale, "src", platformVideoSink, "sink", GST_PAD_LINK_CHECK_NOTHING);
104 // Link a new src pad from tee to queue.
105 GstPad* srcPad = gst_element_get_request_pad(tee, "src%d");
106 GstPad* sinkPad = gst_element_get_static_pad(queue, "sink");
107 gst_pad_link(srcPad, sinkPad);
108 gst_object_unref(GST_OBJECT(sinkPad));
110 m_dynamicPadName = gst_pad_get_name(srcPad);
112 // Roll new elements to pipeline state.
113 gst_element_sync_state_with_parent(queue);
114 gst_element_sync_state_with_parent(colorspace);
115 gst_element_sync_state_with_parent(videoScale);
116 gst_element_sync_state_with_parent(platformVideoSink);
118 gst_object_unref(tee);
120 // Query the current media segment informations and send them towards
121 // the new tee branch downstream.
123 GstQuery* query = gst_query_new_segment(GST_FORMAT_TIME);
124 gboolean queryResult = gst_element_query(m_pipeline, query);
127 gst_query_unref(query);
128 gst_object_unref(GST_OBJECT(srcPad));
134 if (!gst_element_query_position(m_pipeline, &format, &position))
138 gint64 startValue, stopValue;
139 gst_query_parse_segment(query, &rate, &format, &startValue, &stopValue);
141 GstEvent* event = gst_event_new_new_segment(FALSE, rate, format, startValue, stopValue, position);
142 gst_pad_push_event(srcPad, event);
144 gst_query_unref(query);
145 gst_object_unref(GST_OBJECT(srcPad));
149 void GStreamerGWorld::exitFullscreen()
151 if (!m_dynamicPadName)
154 // Get video sink bin and the elements to remove.
155 GOwnPtr<GstElement> videoSink;
156 g_object_get(m_pipeline, "video-sink", &videoSink.outPtr(), NULL);
157 GstElement* tee = gst_bin_get_by_name(GST_BIN(videoSink.get()), "videoTee");
158 GstElement* platformVideoSink = gst_bin_get_by_name(GST_BIN(videoSink.get()), "platformVideoSink");
159 GstElement* queue = gst_bin_get_by_name(GST_BIN(videoSink.get()), "queue");
160 GstElement* colorspace = gst_bin_get_by_name(GST_BIN(videoSink.get()), "colorspace");
161 GstElement* videoScale = gst_bin_get_by_name(GST_BIN(videoSink.get()), "videoScale");
163 GstElement* valve = gst_bin_get_by_name(GST_BIN(videoSink.get()), "videoValve");
165 g_object_set(valve, "drop-probability", 0.0, NULL);
167 // Get pads to unlink and remove.
168 GstPad* srcPad = gst_element_get_static_pad(tee, m_dynamicPadName);
169 GstPad* sinkPad = gst_element_get_static_pad(queue, "sink");
171 // Block data flow towards the pipeline branch to remove.
172 gst_pad_set_blocked(srcPad, true);
174 // Unlink and release request pad.
175 gst_pad_unlink(srcPad, sinkPad);
176 gst_element_release_request_pad(tee, srcPad);
177 gst_object_unref(GST_OBJECT(srcPad));
178 gst_object_unref(GST_OBJECT(sinkPad));
180 // Unlink, remove and cleanup queue, ffmpegcolorspace, videoScale and sink.
181 gst_element_unlink_many(queue, colorspace, videoScale, platformVideoSink, NULL);
182 gst_bin_remove_many(GST_BIN(videoSink.get()), queue, colorspace, videoScale, platformVideoSink, NULL);
183 gst_element_set_state(queue, GST_STATE_NULL);
184 gst_element_set_state(colorspace, GST_STATE_NULL);
185 gst_element_set_state(videoScale, GST_STATE_NULL);
186 gst_element_set_state(platformVideoSink, GST_STATE_NULL);
187 gst_object_unref(queue);
188 gst_object_unref(colorspace);
189 gst_object_unref(videoScale);
190 gst_object_unref(platformVideoSink);
192 gst_object_unref(tee);
193 m_dynamicPadName = 0;
196 void GStreamerGWorld::setWindowOverlay(GstMessage* message)
198 GstObject* sink = GST_MESSAGE_SRC(message);
200 if (!GST_IS_X_OVERLAY(sink))
203 if (g_object_class_find_property(G_OBJECT_GET_CLASS(sink), "force-aspect-ratio"))
204 g_object_set(sink, "force-aspect-ratio", TRUE, NULL);
207 m_videoWindow->prepareForOverlay(message);
209 // gst_x_overlay_set_window_handle was introduced in -plugins-base
210 // 0.10.31, just like the macro for checking the version.
211 #ifdef GST_CHECK_PLUGINS_BASE_VERSION
212 gst_x_overlay_set_window_handle(GST_X_OVERLAY(sink), m_videoWindow->videoWindowId());
214 gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(sink), m_videoWindow->videoWindowId());
220 #endif // USE(GSTREAMER)