1 /* 2 * Copyright (C) 2010 Igalia S.L 3 * 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. 8 * 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. 13 * 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. 18 */ 19 20 #include "config.h" 21 #include "GStreamerGWorld.h" 22 #if USE(GSTREAMER) 23 24 #include "GOwnPtrGStreamer.h" 25 #include <gst/gst.h> 26 #include <gst/interfaces/xoverlay.h> 27 #include <gst/pbutils/pbutils.h> 28 29 #if PLATFORM(GTK) 30 #include <gtk/gtk.h> 31 #ifdef GDK_WINDOWING_X11 32 #include <gdk/gdkx.h> // for GDK_WINDOW_XID 33 #endif 34 #endif 35 36 using namespace std; 37 38 namespace WebCore { 39 40 gboolean gstGWorldSyncMessageCallback(GstBus* bus, GstMessage* message, gpointer data) 41 { 42 ASSERT(GST_MESSAGE_TYPE(message) == GST_MESSAGE_ELEMENT); 43 44 GStreamerGWorld* gstGWorld = static_cast<GStreamerGWorld*>(data); 45 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); 49 return TRUE; 50 } 51 52 PassRefPtr<GStreamerGWorld> GStreamerGWorld::createGWorld(GstElement* pipeline) 53 { 54 return adoptRef(new GStreamerGWorld(pipeline)); 55 } 56 57 GStreamerGWorld::GStreamerGWorld(GstElement* pipeline) 58 : m_pipeline(pipeline) 59 , m_dynamicPadName(0) 60 { 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); 66 } 67 68 GStreamerGWorld::~GStreamerGWorld() 69 { 70 exitFullscreen(); 71 72 m_pipeline = 0; 73 } 74 75 bool GStreamerGWorld::enterFullscreen() 76 { 77 if (m_dynamicPadName) 78 return false; 79 80 if (!m_videoWindow) 81 m_videoWindow = PlatformVideoWindow::createWindow(); 82 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"); 87 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"); 93 94 g_object_set(valve, "drop-probability", 1.0, NULL); 95 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); 98 #if GST_CHECK_VERSION(0, 10, 30) 99 // Faster elements linking, if possible. 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); 103 #else 104 gst_element_link_many(queue, colorspace, videoScale, platformVideoSink, NULL); 105 #endif 106 107 // Link a new src pad from tee to queue. 108 GstPad* srcPad = gst_element_get_request_pad(tee, "src%d"); 109 GstPad* sinkPad = gst_element_get_static_pad(queue, "sink"); 110 gst_pad_link(srcPad, sinkPad); 111 gst_object_unref(GST_OBJECT(sinkPad)); 112 113 m_dynamicPadName = gst_pad_get_name(srcPad); 114 115 // Roll new elements to pipeline state. 116 gst_element_sync_state_with_parent(queue); 117 gst_element_sync_state_with_parent(colorspace); 118 gst_element_sync_state_with_parent(videoScale); 119 gst_element_sync_state_with_parent(platformVideoSink); 120 121 gst_object_unref(tee); 122 123 // Query the current media segment informations and send them towards 124 // the new tee branch downstream. 125 126 GstQuery* query = gst_query_new_segment(GST_FORMAT_TIME); 127 gboolean queryResult = gst_element_query(m_pipeline, query); 128 129 #if GST_CHECK_VERSION(0, 10, 30) 130 if (!queryResult) { 131 gst_query_unref(query); 132 gst_object_unref(GST_OBJECT(srcPad)); 133 return true; 134 } 135 #else 136 // GStreamer < 0.10.30 doesn't set the query result correctly, so 137 // just ignore it to avoid a compilation warning. 138 // See https://bugzilla.gnome.org/show_bug.cgi?id=620490. 139 (void) queryResult; 140 #endif 141 142 GstFormat format; 143 gint64 position; 144 if (!gst_element_query_position(m_pipeline, &format, &position)) 145 position = 0; 146 147 gdouble rate; 148 gint64 startValue, stopValue; 149 gst_query_parse_segment(query, &rate, &format, &startValue, &stopValue); 150 151 GstEvent* event = gst_event_new_new_segment(FALSE, rate, format, startValue, stopValue, position); 152 gst_pad_push_event(srcPad, event); 153 154 gst_query_unref(query); 155 gst_object_unref(GST_OBJECT(srcPad)); 156 return true; 157 } 158 159 void GStreamerGWorld::exitFullscreen() 160 { 161 if (!m_dynamicPadName) 162 return; 163 164 // Get video sink bin and the elements to remove. 165 GOwnPtr<GstElement> videoSink; 166 g_object_get(m_pipeline, "video-sink", &videoSink.outPtr(), NULL); 167 GstElement* tee = gst_bin_get_by_name(GST_BIN(videoSink.get()), "videoTee"); 168 GstElement* platformVideoSink = gst_bin_get_by_name(GST_BIN(videoSink.get()), "platformVideoSink"); 169 GstElement* queue = gst_bin_get_by_name(GST_BIN(videoSink.get()), "queue"); 170 GstElement* colorspace = gst_bin_get_by_name(GST_BIN(videoSink.get()), "colorspace"); 171 GstElement* videoScale = gst_bin_get_by_name(GST_BIN(videoSink.get()), "videoScale"); 172 173 GstElement* valve = gst_bin_get_by_name(GST_BIN(videoSink.get()), "videoValve"); 174 175 g_object_set(valve, "drop-probability", 0.0, NULL); 176 177 // Get pads to unlink and remove. 178 GstPad* srcPad = gst_element_get_static_pad(tee, m_dynamicPadName); 179 GstPad* sinkPad = gst_element_get_static_pad(queue, "sink"); 180 181 // Unlink and release request pad. 182 gst_pad_unlink(srcPad, sinkPad); 183 gst_element_release_request_pad(tee, srcPad); 184 gst_object_unref(GST_OBJECT(srcPad)); 185 gst_object_unref(GST_OBJECT(sinkPad)); 186 187 // Unlink, remove and cleanup queue, ffmpegcolorspace, videoScale and sink. 188 gst_element_unlink_many(queue, colorspace, videoScale, platformVideoSink, NULL); 189 gst_bin_remove_many(GST_BIN(videoSink.get()), queue, colorspace, videoScale, platformVideoSink, NULL); 190 gst_element_set_state(queue, GST_STATE_NULL); 191 gst_element_set_state(colorspace, GST_STATE_NULL); 192 gst_element_set_state(videoScale, GST_STATE_NULL); 193 gst_element_set_state(platformVideoSink, GST_STATE_NULL); 194 gst_object_unref(queue); 195 gst_object_unref(colorspace); 196 gst_object_unref(videoScale); 197 gst_object_unref(platformVideoSink); 198 199 gst_object_unref(tee); 200 m_dynamicPadName = 0; 201 } 202 203 void GStreamerGWorld::setWindowOverlay(GstMessage* message) 204 { 205 GstObject* sink = GST_MESSAGE_SRC(message); 206 207 if (!GST_IS_X_OVERLAY(sink)) 208 return; 209 210 if (g_object_class_find_property(G_OBJECT_GET_CLASS(sink), "force-aspect-ratio")) 211 g_object_set(sink, "force-aspect-ratio", TRUE, NULL); 212 213 if (m_videoWindow) { 214 m_videoWindow->prepareForOverlay(message); 215 216 // gst_x_overlay_set_window_handle was introduced in -plugins-base 217 // 0.10.31, just like the macro for checking the version. 218 #ifdef GST_CHECK_PLUGINS_BASE_VERSION 219 gst_x_overlay_set_window_handle(GST_X_OVERLAY(sink), m_videoWindow->videoWindowId()); 220 #else 221 gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(sink), m_videoWindow->videoWindowId()); 222 #endif 223 } 224 } 225 226 } 227 #endif // USE(GSTREAMER) 228