Home | History | Annotate | Download | only in gstreamer
      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