TelepathyQt4: call/video-widget.cpp

| Home · All Classes · All Namespaces · Modules · Functions · Files

    /*
     * This file is part of TelepathyQt4
     *
     * Copyright © 2009 Collabora Ltd. <http://www.collabora.co.uk/>
     * Copyright © 2009 Nokia Corporation
     *
     * This library is free software; you can redistribute it and/or
     * modify it under the terms of the GNU Lesser General Public
     * License as published by the Free Software Foundation; either
     * version 2.1 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
     * Lesser General Public License for more details.
     *
     * You should have received a copy of the GNU Lesser 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 "video-widget.h"
    #include "_gen/video-widget.moc.hpp"

    #include <QApplication>
    #include <QDebug>
    #include <QPainter>
    #include <QThread>

    #include <gst/interfaces/xoverlay.h>
    #include <gst/farsight/fs-element-added-notifier.h>

    extern void qt_x11_set_global_double_buffer(bool);

    namespace Tp {

    struct VideoWidget::Private {
        Private(VideoWidget *parent, GstBus *bus);
        ~Private();

        static void onElementAdded(FsElementAddedNotifier *notifier,
                GstBin *bin, GstElement *element,
                Private *self);
        static void onSyncMessage(GstBus *bus, GstMessage *message,
                Private *self);

        VideoWidget *parent;
        GstBus *bus;
        FsElementAddedNotifier *notifier;
        GstElement *sink;
        GstElement *overlay;
    };

    VideoWidget::Private::Private(VideoWidget *parent, GstBus *bus)
        : parent(parent),
          bus((GstBus *) gst_object_ref(bus)),
          overlay(0)
    {
        notifier = fs_element_added_notifier_new();
        g_signal_connect(notifier, "element-added",
                G_CALLBACK(&VideoWidget::Private::onElementAdded),
                this);

        sink = gst_element_factory_make("autovideosink", NULL);
        gst_object_ref(sink);
        gst_object_sink(sink);

        fs_element_added_notifier_add(notifier, GST_BIN(sink));
        gst_bus_enable_sync_message_emission(bus);

        g_signal_connect(bus, "sync-message",
                G_CALLBACK(&VideoWidget::Private::onSyncMessage),
                this);
    }

    VideoWidget::Private::~Private()
    {
        g_object_unref(bus);
        g_object_unref(sink);
    }

    void VideoWidget::Private::onElementAdded(FsElementAddedNotifier *notifier,
            GstBin *bin, GstElement *element, VideoWidget::Private *self)
    {
        if (!self->overlay && GST_IS_X_OVERLAY(element)) {
            self->overlay = element;
            QMetaObject::invokeMethod(self->parent, "windowExposed", Qt::QueuedConnection);
        }

        if (g_object_class_find_property(G_OBJECT_GET_CLASS(element),
                    "force-aspect-ratio")) {
            g_object_set(G_OBJECT(element), "force-aspect-ratio", TRUE, NULL);
        }
    }

    void VideoWidget::Private::onSyncMessage(GstBus *bus, GstMessage *message,
            VideoWidget::Private *self)
    {
        if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_ELEMENT) {
            return;
        }

        if (GST_MESSAGE_SRC(message) != (GstObject *) self->overlay) {
            return;
        }

        const GstStructure *s = gst_message_get_structure (message);

        if (gst_structure_has_name(s, "prepare-xwindow-id") && self->overlay) {
            QMetaObject::invokeMethod(self->parent, "setOverlay", Qt::QueuedConnection);
        }
    }

    VideoWidget::VideoWidget(GstBus *bus, QWidget *parent)
        : QWidget(parent),
          mPriv(new Private(this, bus))
    {
        qt_x11_set_global_double_buffer(false);

        QPalette palette;
        palette.setColor(QPalette::Background, Qt::black);
        setPalette(palette);
        setAutoFillBackground(true);
    }

    VideoWidget::~VideoWidget()
    {
        delete mPriv;
    }

    GstElement *VideoWidget::element() const
    {
        return mPriv->sink;
    }

    bool VideoWidget::eventFilter(QEvent *ev)
    {
        if (ev->type() == QEvent::Show) {
            setAttribute(Qt::WA_NoSystemBackground, true);
            setAttribute(Qt::WA_PaintOnScreen, true);
            setOverlay();
        }
        return false;
    }

    void VideoWidget::setOverlay()
    {
        if (mPriv->overlay && GST_IS_X_OVERLAY(mPriv->overlay)) {
            WId windowId = winId();
            QApplication::syncX();
            gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(mPriv->overlay),
                    windowId);
        }
        windowExposed();
    }

    void VideoWidget::windowExposed()
    {
        QApplication::syncX();
        if (mPriv->overlay && GST_IS_X_OVERLAY(mPriv->overlay)) {
            gst_x_overlay_expose(GST_X_OVERLAY(mPriv->overlay));
        }
    }

    } // Tp