[Telepathy-commits] [telepathy-qt4/master] call example: Added VideoWidget.

Andre Moreira Magalhaes (andrunko) andre.magalhaes at collabora.co.uk
Tue Mar 17 02:13:14 PDT 2009


---
 examples/call/Makefile.am          |    9 +-
 examples/call/call-handler.cpp     |  113 +++++++++-
 examples/call/call-handler.h       |    8 +-
 examples/call/call-widget.cpp      |  449 ++++++++++++++++++++++++++----------
 examples/call/call-widget.h        |   52 +++-
 examples/call/call-window.cpp      |    6 +-
 examples/call/farsight-channel.cpp |   70 +++++-
 examples/call/farsight-channel.h   |    9 +-
 examples/call/main.cpp             |    4 +
 examples/call/video-widget.cpp     |  166 +++++++++++++
 examples/call/video-widget.h       |   57 +++++
 11 files changed, 792 insertions(+), 151 deletions(-)
 create mode 100644 examples/call/video-widget.cpp
 create mode 100644 examples/call/video-widget.h

diff --git a/examples/call/Makefile.am b/examples/call/Makefile.am
index b81af42..16204d9 100644
--- a/examples/call/Makefile.am
+++ b/examples/call/Makefile.am
@@ -1,5 +1,6 @@
 AM_CXXFLAGS = \
 	$(ERROR_CXXFLAGS) \
+	$(GST_CFLAGS) \
 	$(QTCORE_CFLAGS) \
 	$(QTGUI_CFLAGS) \
 	$(QTDBUS_CFLAGS) \
@@ -9,6 +10,7 @@ AM_CXXFLAGS = \
 noinst_PROGRAMS = call
 
 call_LDADD = \
+	$(GST_LIBS) \
 	$(QTGUI_LIBS) \
 	$(QTDBUS_LIBS) \
 	$(TP_FARSIGHT_LIBS) \
@@ -26,14 +28,17 @@ call_SOURCES = \
 	call-window.cpp \
 	call-window.h \
 	farsight-channel.cpp \
-	farsight-channel.h
+	farsight-channel.h \
+	video-widget.cpp \
+	video-widget.h
 
 nodist_call_SOURCES = \
 	_gen/call-handler.moc.hpp \
 	_gen/call-roster-widget.moc.hpp \
 	_gen/call-widget.moc.hpp \
 	_gen/call-window.moc.hpp \
-	_gen/farsight-channel.moc.hpp
+	_gen/farsight-channel.moc.hpp \
+	_gen/video-widget.moc.hpp
 
 BUILT_SOURCES = \
 	$(nodist_call_SOURCES)
diff --git a/examples/call/call-handler.cpp b/examples/call/call-handler.cpp
index 5a821cd..b2edfcd 100644
--- a/examples/call/call-handler.cpp
+++ b/examples/call/call-handler.cpp
@@ -24,9 +24,16 @@
 #include "call-widget.h"
 
 #include <TelepathyQt4/Client/Channel>
+#include <TelepathyQt4/Client/Connection>
 #include <TelepathyQt4/Client/Contact>
+#include <TelepathyQt4/Client/ContactManager>
+#include <TelepathyQt4/Client/PendingChannel>
+#include <TelepathyQt4/Client/PendingReady>
+#include <TelepathyQt4/Client/ReferencedHandles>
+#include <TelepathyQt4/Client/StreamedMediaChannel>
 
 #include <QDebug>
+#include <QMessageBox>
 
 using namespace Telepathy::Client;
 
@@ -44,7 +51,72 @@ CallHandler::~CallHandler()
 
 void CallHandler::addOutgoingCall(const QSharedPointer<Contact> &contact)
 {
-    CallWidget *call = new CallWidget(contact);
+    QVariantMap request;
+    request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"),
+                   TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA);
+    request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"),
+                   Telepathy::HandleTypeContact);
+    request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle"),
+                   contact->handle()[0]);
+
+    Connection *conn = contact->manager()->connection();
+    connect(conn->ensureChannel(request),
+            SIGNAL(finished(Telepathy::Client::PendingOperation*)),
+            SLOT(onOutgoingChannelCreated(Telepathy::Client::PendingOperation*)));
+}
+
+void CallHandler::addIncomingCall(StreamedMediaChannel *chan)
+{
+    mChannels.append(chan);
+    connect(chan->becomeReady(),
+            SIGNAL(finished(Telepathy::Client::PendingOperation*)),
+            SLOT(onIncomingChannelReady(Telepathy::Client::PendingOperation*)));
+}
+
+void CallHandler::onOutgoingChannelCreated(PendingOperation *op)
+{
+    if (op->isError()) {
+        qWarning() << "CallHandler::onOutgoingChannelCreated: channel cannot be created:" <<
+            op->errorName() << "-" << op->errorMessage();
+
+        QMessageBox msgBox;
+        msgBox.setText(QString("Unable to call (%1)").arg(op->errorMessage()));
+        msgBox.exec();
+        return;
+    }
+
+    PendingChannel *pc = qobject_cast<PendingChannel *>(op);
+
+    StreamedMediaChannel *chan = new StreamedMediaChannel(pc->connection(),
+            pc->objectPath(), pc->immutableProperties());
+    mChannels.append(chan);
+    connect(chan->becomeReady(),
+            SIGNAL(finished(Telepathy::Client::PendingOperation*)),
+            SLOT(onOutgoingChannelReady(Telepathy::Client::PendingOperation*)));
+}
+
+void CallHandler::onOutgoingChannelReady(PendingOperation *op)
+{
+    PendingReady *pr = qobject_cast<PendingReady *>(op);
+    StreamedMediaChannel *chan = qobject_cast<StreamedMediaChannel *>(pr->object());
+
+    if (op->isError()) {
+        qWarning() << "CallHandler::onOutgoingChannelReady: channel cannot become ready:" <<
+            op->errorName() << "-" << op->errorMessage();
+
+        mChannels.removeOne(chan);
+
+        QMessageBox msgBox;
+        msgBox.setText(QString("Unable to call (%1)").arg(op->errorMessage()));
+        msgBox.exec();
+        return;
+    }
+
+    ContactManager *cm = chan->connection()->contactManager();
+    QSharedPointer<Contact> contact = cm->lookupContactByHandle(chan->targetHandle());
+    Q_ASSERT(contact);
+
+    CallWidget *call = new CallWidget(chan, contact);
     mCalls.append(call);
     connect(call,
             SIGNAL(destroyed(QObject *)),
@@ -52,9 +124,37 @@ void CallHandler::addOutgoingCall(const QSharedPointer<Contact> &contact)
     call->show();
 }
 
-void CallHandler::addIncomingCall(const ChannelPtr &chan)
+void CallHandler::onIncomingChannelReady(PendingOperation *op)
 {
-    CallWidget *call = new CallWidget(chan);
+    PendingReady *pr = qobject_cast<PendingReady *>(op);
+    StreamedMediaChannel *chan = qobject_cast<StreamedMediaChannel *>(pr->object());
+
+    if (op->isError()) {
+        // ignore - channel cannot be ready
+        qWarning() << "CallHandler::onIncomingChannelReady: channel cannot become ready:" <<
+            op->errorName() << "-" << op->errorMessage();
+
+        mChannels.removeOne(chan);
+        return;
+    }
+
+    QSharedPointer<Contact> contact = chan->initiatorContact();
+
+    QMessageBox msgBox;
+    msgBox.setText("Incoming call");
+    msgBox.setInformativeText(QString("%1 is calling you, do you want to answer?").arg(contact->id()));
+    msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+    msgBox.setDefaultButton(QMessageBox::Yes);
+    int ret = msgBox.exec();
+
+    if (ret == QMessageBox::No) {
+        chan->requestClose();
+        return;
+    }
+
+    chan->acceptCall();
+
+    CallWidget *call = new CallWidget(chan, contact);
     mCalls.append(call);
     connect(call,
             SIGNAL(destroyed(QObject *)),
@@ -64,6 +164,9 @@ void CallHandler::addIncomingCall(const ChannelPtr &chan)
 
 void CallHandler::onCallTerminated(QObject *obj)
 {
-    // Why does obj comes 0x0?
-    mCalls.removeOne((CallWidget *) sender());
+    CallWidget *call = (CallWidget *) obj;
+    StreamedMediaChannel *chan = call->channel();
+    mCalls.removeOne(call);
+    mChannels.removeOne(chan);
+    delete chan;
 }
diff --git a/examples/call/call-handler.h b/examples/call/call-handler.h
index f50c81e..e202215 100644
--- a/examples/call/call-handler.h
+++ b/examples/call/call-handler.h
@@ -29,6 +29,8 @@
 namespace Telepathy {
 namespace Client {
 class Contact;
+class PendingOperation;
+class StreamedMediaChannel;
 }
 }
 
@@ -43,12 +45,16 @@ public:
     virtual ~CallHandler();
 
     void addOutgoingCall(const QSharedPointer<Telepathy::Client::Contact> &contact);
-    void addIncomingCall(const Telepathy::Client::ChannelPtr &chan);
+    void addIncomingCall(Telepathy::Client::StreamedMediaChannel *chan);
 
 private Q_SLOTS:
+    void onOutgoingChannelCreated(Telepathy::Client::PendingOperation *);
+    void onOutgoingChannelReady(Telepathy::Client::PendingOperation *);
+    void onIncomingChannelReady(Telepathy::Client::PendingOperation *);
     void onCallTerminated(QObject *);
 
 private:
+    QList<Telepathy::Client::StreamedMediaChannel *> mChannels;
     QList<CallWidget *> mCalls;
 };
 
diff --git a/examples/call/call-widget.cpp b/examples/call/call-widget.cpp
index 4a43540..284c96c 100644
--- a/examples/call/call-widget.cpp
+++ b/examples/call/call-widget.cpp
@@ -21,6 +21,8 @@
 #include "call-widget.h"
 #include "_gen/call-widget.moc.hpp"
 
+#include "video-widget.h"
+
 #include <TelepathyQt4/Client/Channel>
 #include <TelepathyQt4/Client/Connection>
 #include <TelepathyQt4/Client/Contact>
@@ -29,20 +31,24 @@
 #include <TelepathyQt4/Client/PendingReady>
 #include <TelepathyQt4/Client/StreamedMediaChannel>
 
-#include <QAction>
 #include <QDebug>
-#include <QLabel>
 #include <QHBoxLayout>
+#include <QLabel>
 #include <QPushButton>
-#include <QStackedWidget>
-#include <QVariantMap>
+#include <QStatusBar>
+#include <QVBoxLayout>
 
 using namespace Telepathy::Client;
 
-CallWidget::CallWidget(const QSharedPointer<Contact> &contact,
+CallWidget::CallWidget(StreamedMediaChannel *chan,
+        const QSharedPointer<Contact> &contact,
         QWidget *parent)
     : QWidget(parent),
-      mContact(contact)
+      mChan(chan),
+      mContact(contact),
+      mTfChan(new FarsightChannel(chan, this)),
+      mPmsAudio(0),
+      mPmsVideo(0)
 {
     setWindowTitle(QString("Call (%1)").arg(mContact->id()));
 
@@ -50,37 +56,16 @@ CallWidget::CallWidget(const QSharedPointer<Contact> &contact,
 
     setupGui();
 
-    QVariantMap request;
-    request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"),
-                   TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA);
-    request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"),
-                   Telepathy::HandleTypeContact);
-    request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle"),
-                   contact->handle()[0]);
-
-    Connection *conn = contact->manager()->connection();
-    connect(conn->ensureChannel(request),
-            SIGNAL(finished(Telepathy::Client::PendingOperation*)),
-            SLOT(onChannelCreated(Telepathy::Client::PendingOperation*)));
-}
-
-CallWidget::CallWidget(const ChannelPtr &chan,
-        QWidget *parent)
-    : QWidget(parent),
-      mChan(chan)
-{
-    setWindowTitle(QString("Call"));
-
-    setAttribute(Qt::WA_DeleteOnClose);
-
-    setupGui();
-
     connect(mChan->becomeReady(StreamedMediaChannel::FeatureStreams),
             SIGNAL(finished(Telepathy::Client::PendingOperation*)),
             SLOT(onChannelReady(Telepathy::Client::PendingOperation*)));
-    connect(mChan.data(),
-            SIGNAL(invalidated(DBusProxy *, const QString &, const QString &)),
-            SLOT(onChannelInvalidated(DBusProxy *, const QString &, const QString &)));
+    connect(mChan,
+            SIGNAL(invalidated(Telepathy::Client::DBusProxy *, const QString &, const QString &)),
+            SLOT(onChannelInvalidated(Telepathy::Client::DBusProxy *, const QString &, const QString &)));
+
+    connect(mTfChan,
+            SIGNAL(statusChanged(Telepathy::Client::FarsightChannel::Status)),
+            SLOT(onTfChannelStatusChanged(Telepathy::Client::FarsightChannel::Status)));
 }
 
 CallWidget::~CallWidget()
@@ -92,59 +77,93 @@ CallWidget::~CallWidget()
 
 void CallWidget::setupGui()
 {
-    QHBoxLayout *mainBox = new QHBoxLayout;
+    QVBoxLayout *mainBox = new QVBoxLayout;
 
-    mStack = new QStackedWidget();
+    // buttons
+    QHBoxLayout *btnBox = new QHBoxLayout;
 
-    mLblStatus = new QLabel("Initiating...");
-    mLblStatus->setAlignment(Qt::AlignCenter);
-    mStack->addWidget(mLblStatus);
+    mBtnHangup = new QPushButton("Hangup");
+    connect(mBtnHangup,
+            SIGNAL(clicked(bool)),
+            SLOT(onBtnHangupClicked()));
+    btnBox->addWidget(mBtnHangup);
 
-    QFrame *frame = new QFrame;
+    mBtnSendAudio = new QPushButton("Send Audio");
+    mBtnSendAudio->setCheckable(true);
+    mBtnSendAudio->setChecked(true);
+    mBtnSendAudio->setEnabled(false);
+    connect(mBtnSendAudio,
+            SIGNAL(toggled(bool)),
+            SLOT(onBtnSendAudioToggled(bool)));
+    btnBox->addWidget(mBtnSendAudio);
 
-    QHBoxLayout *hbox = new QHBoxLayout;
+    mBtnSendVideo = new QPushButton("Send Video");
+    mBtnSendVideo->setCheckable(true);
+    mBtnSendVideo->setChecked(false);
+    mBtnSendVideo->setEnabled(false);
+    connect(mBtnSendVideo,
+            SIGNAL(toggled(bool)),
+            SLOT(onBtnSendVideoToggled(bool)));
+    btnBox->addWidget(mBtnSendVideo);
 
-    mBtnAccept = new QPushButton("Call");
-    mBtnAccept->setEnabled(false);
-    connect(mBtnAccept,
-            SIGNAL(clicked(bool)),
-            SLOT(onBtnAcceptClicked()));
-    hbox->addWidget(mBtnAccept);
-    mBtnReject = new QPushButton("Terminate");
-    mBtnReject->setEnabled(false);
-    connect(mBtnReject,
-            SIGNAL(clicked(bool)),
-            SLOT(onBtnRejectClicked()));
-    hbox->addWidget(mBtnReject);
+    mainBox->addLayout(btnBox);
 
-    frame->setLayout(hbox);
+    // video
+    QHBoxLayout *videoBox = new QHBoxLayout;
 
-    mStack->addWidget(frame);
+    VideoWidget *videoWidget = mTfChan->videoWidget();
+    videoWidget->setMinimumSize(320, 240);
+    videoBox->addWidget(videoWidget);
 
-    mStack->setCurrentIndex(0);
+    QVBoxLayout *previewBox = new QVBoxLayout;
 
-    mainBox->addWidget(mStack);
+    VideoWidget *videoPreview = mTfChan->videoPreview();
+    videoPreview->setFixedSize(160, 120);
+    previewBox->addWidget(videoPreview);
 
-    setLayout(mainBox);
-}
+    previewBox->addStretch(1);
 
-void CallWidget::onChannelCreated(PendingOperation *op)
-{
-    if (op->isError()) {
-        qWarning() << "CallWidget::onChannelCreated: channel cannot be created:" <<
-            op->errorName() << "-" << op->errorMessage();
-        mLblStatus->setText("Unable to establish call");
-        mStack->setCurrentIndex(0);
-        return;
-    }
+    videoBox->addLayout(previewBox);
 
-    PendingChannel *pc = qobject_cast<PendingChannel *>(op);
+    mainBox->addLayout(videoBox);
 
-    mChan = pc->channel();
+    // stream status
+    QFrame *frame = new QFrame;
+    frame->setFrameStyle(QFrame::Box | QFrame::Sunken);
 
-    connect(mChan->becomeReady(StreamedMediaChannel::FeatureStreams),
-            SIGNAL(finished(Telepathy::Client::PendingOperation*)),
-            SLOT(onChannelReady(Telepathy::Client::PendingOperation*)));
+    QVBoxLayout *streamBox = new QVBoxLayout;
+
+    QLabel *lbl = new QLabel(QLatin1String("<b>Streams</b>"));
+    streamBox->addWidget(lbl);
+    streamBox->addSpacing(4);
+
+    lbl = new QLabel(QLatin1String("<b>Audio</b>"));
+    streamBox->addWidget(lbl);
+    mLblAudioDirection = new QLabel(QLatin1String("Direction: None"));
+    streamBox->addWidget(mLblAudioDirection);
+    mLblAudioState = new QLabel(QLatin1String("State: Disconnected"));
+    streamBox->addWidget(mLblAudioState);
+    streamBox->addSpacing(4);
+
+    lbl = new QLabel(QLatin1String("<b>Video</b>"));
+    streamBox->addWidget(lbl);
+    mLblVideoDirection = new QLabel(QLatin1String("Direction: None"));
+    streamBox->addWidget(mLblVideoDirection);
+    mLblVideoState = new QLabel(QLatin1String("State: Disconnected"));
+    streamBox->addWidget(mLblVideoState);
+
+    streamBox->addStretch(1);
+    frame->setLayout(streamBox);
+
+    videoBox->addSpacing(4);
+    frame->setLayout(streamBox);
+    videoBox->addWidget(frame);
+
+    // status bar
+    mStatusBar = new QStatusBar;
+    mainBox->addWidget(mStatusBar);
+
+    setLayout(mainBox);
 }
 
 void CallWidget::onChannelReady(PendingOperation *op)
@@ -152,37 +171,37 @@ void CallWidget::onChannelReady(PendingOperation *op)
     if (op->isError()) {
         qWarning() << "CallWidget::onChannelReady: channel cannot become ready:" <<
             op->errorName() << "-" << op->errorMessage();
-        mLblStatus->setText("Unable to establish call");
-        mStack->setCurrentIndex(0);
+        mChan->requestClose();
+        callEnded(QLatin1String("Unable to establish call"));
         return;
     }
 
-    mStack->setCurrentIndex(1);
-
-    StreamedMediaChannel *streamChan =
-        qobject_cast<StreamedMediaChannel *>(mChan.data());
-    if (streamChan->awaitingLocalAnswer()) {
-        mBtnAccept->setText("Accept");
-        mBtnAccept->setEnabled(true);
-        mBtnReject->setText("Reject");
-        mBtnReject->setEnabled(true);
-    } else {
-        mBtnAccept->setText("Call");
-        mBtnAccept->setEnabled(false);
-        mBtnReject->setText("Terminate");
-        mBtnReject->setEnabled(true);
-
-        PendingMediaStreams *pms =
-            streamChan->requestStream(mContact, Telepathy::MediaStreamTypeAudio);
-        connect(pms,
-                SIGNAL(finished(Telepathy::Client::PendingOperation*)),
-                SLOT(onStreamCreated(Telepathy::Client::PendingOperation*)));
+#if 1
+    MediaStreams streams = mChan->streams();
+    qDebug() << "CallWidget::onChannelReady: number of streams:" << streams.size();
+    qDebug() << " streams:";
+    foreach (const QSharedPointer<MediaStream> &stream, streams) {
+        qDebug() << "  " <<
+            (stream->type() == Telepathy::MediaStreamTypeAudio ? "Audio" : "Video");
+    }
+#endif
+
+    connect(mChan,
+            SIGNAL(streamAdded(const QSharedPointer<Telepathy::Client::MediaStream> &)),
+            SLOT(onStreamAdded(const QSharedPointer<Telepathy::Client::MediaStream> &)));
+
+    QSharedPointer<MediaStream> stream = streamForType(Telepathy::MediaStreamTypeAudio);
+    if (stream) {
+        connectStreamSignals(stream);
+        onStreamDirectionChanged(stream.data(), stream->direction(),
+                stream->pendingSend());
+        onStreamStateChanged(stream.data(), stream->state());
     }
 
-    mTfChan = new FarsightChannel(streamChan, this);
-    connect(mTfChan,
-            SIGNAL(statusChanged(Telepathy::Client::FarsightChannel::Status)),
-            SLOT(onTfChannelStatusChanged(Telepathy::Client::FarsightChannel::Status)));
+    mBtnSendAudio->setEnabled(true);
+    mBtnSendVideo->setEnabled(true);
+
+    onBtnSendAudioToggled(mBtnSendAudio->isChecked());
 }
 
 void CallWidget::onChannelInvalidated(DBusProxy *proxy,
@@ -190,45 +209,237 @@ void CallWidget::onChannelInvalidated(DBusProxy *proxy,
 {
     qDebug() << "CallWindow::onChannelInvalidated: channel became invalid:" <<
         errorName << "-" << errorMessage;
-    mLblStatus->setText("Call terminated");
-    mStack->setCurrentIndex(0);
+    callEnded(errorMessage);
 }
 
 void CallWidget::onStreamCreated(PendingOperation *op)
 {
+    if (op == mPmsAudio) {
+        mPmsAudio = 0;
+    } else if (op == mPmsVideo) {
+        mPmsVideo = 0;
+    }
+
     if (op->isError()) {
-        qWarning() << "CallWidget::onStreamCreated: unable to create audio stream:" <<
+        qWarning() << "CallWidget::onStreamCreated: unable to create stream:" <<
             op->errorName() << "-" << op->errorMessage();
-        mLblStatus->setText("Unable to establish call");
-        mStack->setCurrentIndex(0);
+
+        // we cannot create the stream for some reason, update buttons
+        if (!streamForType(Telepathy::MediaStreamTypeAudio)) {
+            mBtnSendAudio->setChecked(false);
+        }
+        if (!streamForType(Telepathy::MediaStreamTypeVideo)) {
+            mBtnSendVideo->setChecked(false);
+        }
         return;
     }
+
+    PendingMediaStreams *pms = qobject_cast<PendingMediaStreams *>(op);
+    Q_ASSERT(pms->streams().size() == 1);
+    QSharedPointer<MediaStream> stream = pms->streams().first();
+    connectStreamSignals(stream);
+    updateStreamDirection(stream);
+    onStreamDirectionChanged(stream.data(), stream->direction(),
+            stream->pendingSend());
+    onStreamStateChanged(stream.data(), stream->state());
 }
 
-void CallWidget::onTfChannelStatusChanged(FarsightChannel::Status status)
+void CallWidget::onStreamAdded(const QSharedPointer<MediaStream> &stream)
+{
+    connectStreamSignals(stream);
+    updateStreamDirection(stream);
+    onStreamDirectionChanged(stream.data(), stream->direction(),
+            stream->pendingSend());
+    onStreamStateChanged(stream.data(), stream->state());
+}
+
+void CallWidget::onStreamRemoved(MediaStream *stream)
+{
+    if (stream->type() == Telepathy::MediaStreamTypeAudio) {
+        mBtnSendAudio->setChecked(false);
+    } else if (stream->type() == Telepathy::MediaStreamTypeVideo) {
+        mBtnSendVideo->setChecked(false);
+    }
+}
+
+void CallWidget::onStreamDirectionChanged(MediaStream *stream,
+        Telepathy::MediaStreamDirection direction,
+        Telepathy::MediaStreamPendingSend pendingSend)
 {
-    if (status == FarsightChannel::StatusDisconnected) {
-        mLblStatus->setText("Call terminated");
-        mStack->setCurrentIndex(0);
+    qDebug() << "CallWidget::onStreamDirectionChanged: stream " <<
+        (stream->type() == Telepathy::MediaStreamTypeAudio ? "Audio" : "Video") <<
+        "direction changed to" << direction;
+
+    QLabel *lbl;
+    if (stream->type() == Telepathy::MediaStreamTypeAudio) {
+        lbl = mLblAudioDirection;
+    } else {
+        lbl = mLblVideoDirection;
+    }
+
+    if (direction == Telepathy::MediaStreamDirectionSend) {
+        lbl->setText(QLatin1String("Direction: Sending"));
+    } else if (direction == Telepathy::MediaStreamDirectionReceive) {
+        lbl->setText(QLatin1String("Direction: Receiving"));
+    } else if (direction == (Telepathy::MediaStreamDirectionSend | Telepathy::MediaStreamDirectionReceive)) {
+        lbl->setText(QLatin1String("Direction: Sending/Receiving"));
+    } else {
+        lbl->setText(QLatin1String("Direction: None"));
     }
 }
 
-void CallWidget::onBtnAcceptClicked()
+void CallWidget::onStreamStateChanged(MediaStream *stream,
+        Telepathy::MediaStreamState state)
 {
-    StreamedMediaChannel *streamChan =
-        qobject_cast<StreamedMediaChannel *>(mChan.data());
-    streamChan->acceptCall();
+    qDebug() << "CallWidget::onStreamStateChanged: stream " <<
+        (stream->type() == Telepathy::MediaStreamTypeAudio ? "Audio" : "Video") <<
+        "state changed to" << state;
 
-    mBtnAccept->setText("Call");
-    mBtnAccept->setEnabled(false);
-    mBtnReject->setText("Terminate");
-    mBtnReject->setEnabled(true);
+    QLabel *lbl;
+    if (stream->type() == Telepathy::MediaStreamTypeAudio) {
+        lbl = mLblAudioState;
+    } else {
+        lbl = mLblVideoState;
+    }
+
+    if (state == Telepathy::MediaStreamStateDisconnected) {
+        lbl->setText(QLatin1String("State: Disconnected"));
+    } else if (state == Telepathy::MediaStreamStateConnecting) {
+        lbl->setText(QLatin1String("State: Connecting"));
+    } else if (state == Telepathy::MediaStreamStateConnected) {
+        lbl->setText(QLatin1String("State: Connected"));
+    }
+}
+
+void CallWidget::onTfChannelStatusChanged(FarsightChannel::Status status)
+{
+    switch (status) {
+    case FarsightChannel::StatusConnecting:
+        mStatusBar->showMessage("Connecting...");
+        break;
+    case FarsightChannel::StatusConnected:
+        mStatusBar->showMessage("Connected");
+        break;
+    case FarsightChannel::StatusDisconnected:
+        mChan->requestClose();
+        callEnded("Call terminated");
+        break;
+    }
 }
 
-void CallWidget::onBtnRejectClicked()
+void CallWidget::onBtnHangupClicked()
 {
     mChan->requestClose();
+    callEnded("Call terminated");
+}
 
-    mLblStatus->setText("Call terminated");
-    mStack->setCurrentIndex(0);
+void CallWidget::onBtnSendAudioToggled(bool checked)
+{
+    QSharedPointer<MediaStream> stream =
+        streamForType(Telepathy::MediaStreamTypeAudio);
+    qDebug() << "CallWidget::onBtnSendAudioToggled: checked:" << checked;
+    if (!stream) {
+        if (mPmsAudio) {
+            return;
+        }
+        qDebug() << "CallWidget::onBtnSendAudioToggled: creating audio stream";
+        mPmsAudio = mChan->requestStream(mContact, Telepathy::MediaStreamTypeAudio);
+        connect(mPmsAudio,
+                SIGNAL(finished(Telepathy::Client::PendingOperation*)),
+                SLOT(onStreamCreated(Telepathy::Client::PendingOperation*)));
+    } else {
+        updateStreamDirection(stream);
+    }
+}
+
+void CallWidget::onBtnSendVideoToggled(bool checked)
+{
+    QSharedPointer<MediaStream> stream =
+        streamForType(Telepathy::MediaStreamTypeVideo);
+    qDebug() << "CallWidget::onBtnSendVideoToggled: checked:" << checked;
+    if (!stream) {
+        if (mPmsVideo) {
+            return;
+        }
+        qDebug() << "CallWidget::onBtnSendVideoToggled: creating video stream";
+        mPmsVideo = mChan->requestStream(mContact, Telepathy::MediaStreamTypeVideo);
+        connect(mPmsVideo,
+                SIGNAL(finished(Telepathy::Client::PendingOperation*)),
+                SLOT(onStreamCreated(Telepathy::Client::PendingOperation*)));
+    } else {
+        updateStreamDirection(stream);
+    }
+}
+
+QSharedPointer<MediaStream> CallWidget::streamForType(Telepathy::MediaStreamType type) const
+{
+    MediaStreams streams = mChan->streams();
+    foreach (const QSharedPointer<MediaStream> &stream, streams) {
+        if (stream->type() == type) {
+            return stream;
+        }
+    }
+    return QSharedPointer<MediaStream>();
+}
+
+void CallWidget::connectStreamSignals(const QSharedPointer<MediaStream> &stream)
+{
+    connect(stream.data(),
+            SIGNAL(removed(Telepathy::Client::MediaStream *)),
+            SLOT(onStreamRemoved(Telepathy::Client::MediaStream *)));
+    connect(stream.data(),
+            SIGNAL(directionChanged(Telepathy::Client::MediaStream *,
+                                    Telepathy::MediaStreamDirection,
+                                    Telepathy::MediaStreamPendingSend)),
+            SLOT(onStreamDirectionChanged(Telepathy::Client::MediaStream *,
+                                          Telepathy::MediaStreamDirection,
+                                          Telepathy::MediaStreamPendingSend)));
+    connect(stream.data(),
+            SIGNAL(stateChanged(Telepathy::Client::MediaStream *,
+                                Telepathy::MediaStreamState)),
+            SLOT(onStreamStateChanged(Telepathy::Client::MediaStream *,
+                                      Telepathy::MediaStreamState)));
+}
+
+void CallWidget::updateStreamDirection(const QSharedPointer<MediaStream> &stream)
+{
+    bool checked = false;
+    if (stream->type() == Telepathy::MediaStreamTypeAudio) {
+        checked = mBtnSendAudio->isChecked();
+    } else if (stream->type() == Telepathy::MediaStreamTypeVideo) {
+        checked = mBtnSendVideo->isChecked();
+    }
+
+    qDebug() << "CallWidget::updateStreamDirection: current stream direction:" <<
+        stream->direction() << "- checked:" << checked;
+
+    if (checked) {
+        if (!(stream->direction() & Telepathy::MediaStreamDirectionSend)) {
+            int dir = stream->direction() | Telepathy::MediaStreamDirectionSend;
+            stream->requestStreamDirection((Telepathy::MediaStreamDirection) dir);
+            qDebug() << "CallWidget::updateStreamDirection: start sending " <<
+                (stream->type() == Telepathy::MediaStreamTypeAudio ? "audio" : "video");
+        }
+    } else {
+        if (stream->direction() & Telepathy::MediaStreamDirectionSend) {
+            int dir = stream->direction() & ~Telepathy::MediaStreamDirectionSend;
+            qDebug() << "CallWidget::updateStreamDirection: stop sending " <<
+                (stream->type() == Telepathy::MediaStreamTypeAudio ? "audio" : "video");
+            stream->requestStreamDirection((Telepathy::MediaStreamDirection) dir);
+        }
+    }
+}
+
+void CallWidget::callEnded(const QString &message)
+{
+    mStatusBar->showMessage(message);
+    disconnect(mChan,
+               SIGNAL(invalidated(Telepathy::Client::DBusProxy *, const QString &, const QString &)),
+               this,
+               SLOT(onChannelInvalidated(Telepathy::Client::DBusProxy *, const QString &, const QString &)));
+    disconnect(mTfChan,
+               SIGNAL(statusChanged(Telepathy::Client::FarsightChannel::Status)),
+               this,
+               SLOT(onTfChannelStatusChanged(Telepathy::Client::FarsightChannel::Status)));
+    setEnabled(false);
 }
diff --git a/examples/call/call-widget.h b/examples/call/call-widget.h
index d283832..f6eef6c 100644
--- a/examples/call/call-widget.h
+++ b/examples/call/call-widget.h
@@ -25,6 +25,7 @@
 #include <QWidget>
 
 #include <TelepathyQt4/Client/Channel>
+#include <TelepathyQt4/Constants>
 
 #include "farsight-channel.h"
 
@@ -32,50 +33,75 @@ namespace Telepathy {
 namespace Client {
 class Contact;
 class DBusProxy;
+class MediaStream;
+class PendingMediaStreams;
 class PendingOperation;
+class StreamedMediaChannel;
 }
 }
 
 class QLabel;
 class QPushButton;
-class QStackedWidget;
+class QStatusBar;
 
 class CallWidget : public QWidget
 {
     Q_OBJECT
 
 public:
-    CallWidget(const QSharedPointer<Telepathy::Client::Contact> &contact,
-               QWidget *parent = 0);
-    CallWidget(const Telepathy::Client::ChannelPtr &chan,
+    CallWidget(Telepathy::Client::StreamedMediaChannel *channel,
+               const QSharedPointer<Telepathy::Client::Contact> &contact,
                QWidget *parent = 0);
     virtual ~CallWidget();
 
+    Telepathy::Client::StreamedMediaChannel *channel() const { return mChan; }
     QSharedPointer<Telepathy::Client::Contact> contact() const { return mContact; }
-    Telepathy::Client::ChannelPtr channel() const { return mChan; }
 
 private Q_SLOTS:
-    void onChannelCreated(Telepathy::Client::PendingOperation *);
     void onChannelReady(Telepathy::Client::PendingOperation *);
     void onChannelInvalidated(Telepathy::Client::DBusProxy *,
             const QString &, const QString &);
     void onStreamCreated(Telepathy::Client::PendingOperation *);
+    void onStreamAdded(const QSharedPointer<Telepathy::Client::MediaStream> &);
+    void onStreamRemoved(Telepathy::Client::MediaStream *);
+    void onStreamDirectionChanged(Telepathy::Client::MediaStream *,
+            Telepathy::MediaStreamDirection,
+            Telepathy::MediaStreamPendingSend);
+    void onStreamStateChanged(Telepathy::Client::MediaStream *,
+            Telepathy::MediaStreamState);
     void onTfChannelStatusChanged(Telepathy::Client::FarsightChannel::Status);
 
-    void onBtnAcceptClicked();
-    void onBtnRejectClicked();
+    void onBtnHangupClicked();
+    void onBtnSendAudioToggled(bool);
+    void onBtnSendVideoToggled(bool);
 
 private:
     void createActions();
     void setupGui();
 
-    Telepathy::Client::ChannelPtr mChan;
+    QSharedPointer<Telepathy::Client::MediaStream> streamForType(Telepathy::MediaStreamType type) const;
+    void connectStreamSignals(const QSharedPointer<Telepathy::Client::MediaStream> &stream);
+    void updateStreamDirection(const QSharedPointer<Telepathy::Client::MediaStream> &stream);
+
+    void callEnded(const QString &message);
+
+    Telepathy::Client::StreamedMediaChannel *mChan;
     QSharedPointer<Telepathy::Client::Contact> mContact;
     Telepathy::Client::FarsightChannel *mTfChan;
-    QPushButton *mBtnAccept;
-    QPushButton *mBtnReject;
-    QLabel *mLblStatus;
-    QStackedWidget *mStack;
+
+    Telepathy::Client::PendingMediaStreams *mPmsAudio;
+    Telepathy::Client::PendingMediaStreams *mPmsVideo;
+
+    QPushButton *mBtnHangup;
+    QPushButton *mBtnSendAudio;
+    QPushButton *mBtnSendVideo;
+
+    QLabel *mLblAudioDirection;
+    QLabel *mLblVideoDirection;
+    QLabel *mLblAudioState;
+    QLabel *mLblVideoState;
+
+    QStatusBar *mStatusBar;
 };
 
 #endif
diff --git a/examples/call/call-window.cpp b/examples/call/call-window.cpp
index a5c6058..1e3962f 100644
--- a/examples/call/call-window.cpp
+++ b/examples/call/call-window.cpp
@@ -120,6 +120,7 @@ void CallWindow::onConnectionConnected(Telepathy::Client::PendingOperation *op)
         Telepathy::CapabilityPair capability = {
             TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA,
             Telepathy::ChannelMediaCapabilityAudio |
+            Telepathy::ChannelMediaCapabilityVideo |
                 Telepathy::ChannelMediaCapabilityNATTraversalSTUN |
                 Telepathy::ChannelMediaCapabilityNATTraversalGTalkP2P
         };
@@ -159,10 +160,9 @@ void CallWindow::onNewChannels(const Telepathy::ChannelDetailsList &channels)
 
         if (channelType == TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA &&
             !requested) {
-            ChannelPtr channel = ChannelPtr(
-                    new StreamedMediaChannel(mConn.data(),
+            StreamedMediaChannel *channel = new StreamedMediaChannel(mConn.data(),
                         details.channel.path(),
-                        details.properties));
+                        details.properties);
             mCallHandler->addIncomingCall(channel);
             qDebug() << "CallWindow::onNewChannels: new call received";
         }
diff --git a/examples/call/farsight-channel.cpp b/examples/call/farsight-channel.cpp
index 1d4b8d3..d8d8fe8 100644
--- a/examples/call/farsight-channel.cpp
+++ b/examples/call/farsight-channel.cpp
@@ -22,6 +22,8 @@
 #include "farsight-channel.h"
 #include "_gen/farsight-channel.moc.hpp"
 
+#include "video-widget.h"
+
 #include <QDebug>
 
 #include <telepathy-farsight/channel.h>
@@ -61,6 +63,10 @@ struct FarsightChannel::Private
     GstElement *pipeline;
     GstElement *audioInput;
     GstElement *audioOutput;
+    GstElement *videoInput;
+    GstElement *videoTee;
+    VideoWidget *videoPreview;
+    VideoWidget *videoOutput;
 };
 
 FarsightChannel::Private::Private(FarsightChannel *parent,
@@ -72,7 +78,11 @@ FarsightChannel::Private::Private(FarsightChannel *parent,
       bus(0),
       pipeline(0),
       audioInput(0),
-      audioOutput(0)
+      audioOutput(0),
+      videoInput(0),
+      videoTee(0),
+      videoPreview(0),
+      videoOutput(0)
 {
     TpDBusDaemon *dbus = tp_dbus_daemon_dup(0);
     if (!dbus) {
@@ -128,13 +138,13 @@ FarsightChannel::Private::Private(FarsightChannel *parent,
     pipeline = gst_pipeline_new(NULL);
     bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
 
-    audioInput = gst_element_factory_make("gconfaudiosrc", NULL);
+    audioInput = gst_element_factory_make("autoaudiosrc", NULL);
     gst_object_ref(audioInput);
     gst_object_sink(audioInput);
 
-    audioOutput = gst_bin_new("bin");
+    audioOutput = gst_bin_new("audio-output-bin");
     GstElement *resample = gst_element_factory_make("audioresample", NULL);
-    GstElement *audioSink = gst_element_factory_make("gconfaudiosink", NULL);
+    GstElement *audioSink = gst_element_factory_make("autoaudiosink", NULL);
     gst_bin_add_many(GST_BIN(audioOutput), resample, audioSink, NULL);
     gst_element_link_many(resample, audioSink, NULL);
     GstPad *sink = gst_element_get_static_pad(resample, "sink");
@@ -144,6 +154,42 @@ FarsightChannel::Private::Private(FarsightChannel *parent,
     gst_object_ref(audioOutput);
     gst_object_sink(audioOutput);
 
+    videoInput = gst_bin_new("video-input-bin");
+    GstElement *scale = gst_element_factory_make("videoscale", NULL);
+    GstElement *rate = gst_element_factory_make("videorate", NULL);
+    GstElement *colorspace = gst_element_factory_make("ffmpegcolorspace", NULL);
+    GstElement *capsfilter = gst_element_factory_make("capsfilter", NULL);
+    GstCaps *caps = gst_caps_new_simple("video/x-raw-yuv",
+            "width", G_TYPE_INT, 320,
+            "height", G_TYPE_INT, 240,
+            NULL);
+    g_object_set(G_OBJECT(capsfilter), "caps", caps, NULL);
+    GstElement *videoSrc = gst_element_factory_make("autovideosrc", NULL);
+    // GstElement *videoSrc = gst_element_factory_make("videotestsrc", NULL);
+    gst_bin_add_many(GST_BIN(videoInput), videoSrc, scale, rate,
+            colorspace, capsfilter, NULL);
+    gst_element_link_many(videoSrc, scale, rate, colorspace, capsfilter, NULL);
+    GstPad *src = gst_element_get_static_pad(capsfilter, "src");
+    ghost = gst_ghost_pad_new("src", src);
+    Q_ASSERT(gst_element_add_pad(GST_ELEMENT(videoInput), ghost));
+    gst_object_unref(G_OBJECT(src));
+    gst_object_ref(audioInput);
+    gst_object_sink(audioInput);
+
+    videoTee = gst_element_factory_make("tee", NULL);
+    gst_object_ref(videoTee);
+    gst_object_sink(videoTee);
+
+    videoPreview = new VideoWidget(bus);
+    GstElement *videoPreviewElement = videoPreview->element();
+
+    gst_bin_add_many(GST_BIN(pipeline), videoInput, videoTee,
+            videoPreviewElement, NULL);
+    gst_element_link_many(videoInput, videoTee,
+            videoPreviewElement, NULL);
+
+    videoOutput = new VideoWidget(bus);
+
     gst_element_set_state(pipeline, GST_STATE_PLAYING);
 
     status = StatusConnecting;
@@ -229,7 +275,8 @@ void FarsightChannel::Private::onStreamCreated(TfChannel *tfChannel,
         gst_pad_link(pad, sink);
         break;
     case TP_MEDIA_STREAM_TYPE_VIDEO:
-        // TODO
+        pad = gst_element_get_request_pad(self->videoTee, "src%d");
+        gst_pad_link(pad, sink);
         break;
     default:
         Q_ASSERT(false);
@@ -254,8 +301,7 @@ void FarsightChannel::Private::onSrcPadAdded(TfStream *stream,
         g_object_ref(element);
         break;
     case TP_MEDIA_STREAM_TYPE_VIDEO:
-        // TODO
-        return;
+        element = self->videoOutput->element();
         break;
     default:
         Q_ASSERT(false);
@@ -289,5 +335,15 @@ FarsightChannel::~FarsightChannel()
     delete mPriv;
 }
 
+VideoWidget *FarsightChannel::videoPreview() const
+{
+    return mPriv->videoPreview;
+}
+
+VideoWidget *FarsightChannel::videoWidget() const
+{
+    return mPriv->videoOutput;
+}
+
 }
 }
diff --git a/examples/call/farsight-channel.h b/examples/call/farsight-channel.h
index bf68f8d..7de1f3e 100644
--- a/examples/call/farsight-channel.h
+++ b/examples/call/farsight-channel.h
@@ -23,6 +23,7 @@
 #define _TelepathyQt4_examples_call_farsight_channel_h_HEADER_GUARD_
 
 #include <QObject>
+#include <QMetaType>
 
 #include <telepathy-farsight/channel.h>
 
@@ -31,10 +32,12 @@ namespace Client {
 
 class Connection;
 class StreamedMediaChannel;
+class VideoWidget;
 
 class FarsightChannel : public QObject
 {
     Q_OBJECT
+    Q_ENUMS(Status)
 
 public:
     enum Status {
@@ -48,8 +51,10 @@ public:
 
     Status status() const;
 
+    VideoWidget *videoPreview() const;
+    VideoWidget *videoWidget() const;
+
     // TODO add a way to change input and output devices
-    //      add video support
 
 Q_SIGNALS:
     void statusChanged(Telepathy::Client::FarsightChannel::Status status);
@@ -63,4 +68,6 @@ private:
 }
 }
 
+Q_DECLARE_METATYPE(Telepathy::Client::FarsightChannel::Status)
+
 #endif
diff --git a/examples/call/main.cpp b/examples/call/main.cpp
index b78a140..1bec37c 100644
--- a/examples/call/main.cpp
+++ b/examples/call/main.cpp
@@ -9,6 +9,7 @@
 #include <QtGui>
 
 #include "call-window.h"
+#include "farsight-channel.h"
 
 int main(int argc, char **argv)
 {
@@ -22,9 +23,12 @@ int main(int argc, char **argv)
         return 1;
     }
 
+    app.setAttribute(Qt::AA_NativeWindows);
+
     Telepathy::registerTypes();
     Telepathy::enableDebug(true);
     Telepathy::enableWarnings(true);
+    qRegisterMetaType<Telepathy::Client::FarsightChannel::Status>();
 
     CallWindow w(argv[1], argv[2]);
     w.show();
diff --git a/examples/call/video-widget.cpp b/examples/call/video-widget.cpp
new file mode 100644
index 0000000..14c2da9
--- /dev/null
+++ b/examples/call/video-widget.cpp
@@ -0,0 +1,166 @@
+/*
+ * This file is part of TelepathyQt4
+ *
+ * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * 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 Telepathy {
+namespace Client {
+
+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));
+    }
+}
+
+}
+}
diff --git a/examples/call/video-widget.h b/examples/call/video-widget.h
new file mode 100644
index 0000000..de1b130
--- /dev/null
+++ b/examples/call/video-widget.h
@@ -0,0 +1,57 @@
+/*
+ * This file is part of TelepathyQt4
+ *
+ * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * 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
+ */
+
+#ifndef _TelepathyQt4_examples_call_video_widget_h_HEADER_GUARD_
+#define _TelepathyQt4_examples_call_video_widget_h_HEADER_GUARD_
+
+#include <QWidget>
+
+#include <gst/gst.h>
+
+namespace Telepathy {
+namespace Client {
+
+class VideoWidget : public QWidget
+{
+    Q_OBJECT
+
+public:
+    VideoWidget(GstBus *bus, QWidget *parent = 0);
+    virtual ~VideoWidget();
+
+    GstElement *element() const;
+
+protected:
+    bool eventFilter(QEvent *ev);
+
+private Q_SLOTS:
+    void setOverlay();
+    void windowExposed();
+
+private:
+    struct Private;
+    friend struct Private;
+    Private *mPriv;
+};
+
+} // Telepathy::Client
+} // Telepathy
+
+#endif
-- 
1.5.6.5




More information about the telepathy-commits mailing list