diff options
author | Justin McPherson <justin.mcpherson@nokia.com> | 2010-01-19 02:16:42 (GMT) |
---|---|---|
committer | Justin McPherson <justin.mcpherson@nokia.com> | 2010-01-19 02:16:42 (GMT) |
commit | 07144fabe351f3a6021a2d213cfdba7ee55602a0 (patch) | |
tree | 66961e3e4ce73591dc8dc28697d7e4accbdf494c /src/plugins/mediaservices | |
parent | 29ae7af90a5bb86f9add481e8236b6ac91f22ad3 (diff) | |
download | Qt-07144fabe351f3a6021a2d213cfdba7ee55602a0.zip Qt-07144fabe351f3a6021a2d213cfdba7ee55602a0.tar.gz Qt-07144fabe351f3a6021a2d213cfdba7ee55602a0.tar.bz2 |
Multimedia; Add GStreamer backend
Diffstat (limited to 'src/plugins/mediaservices')
39 files changed, 5933 insertions, 0 deletions
diff --git a/src/plugins/mediaservices/gstreamer/gstreamer.pro b/src/plugins/mediaservices/gstreamer/gstreamer.pro new file mode 100644 index 0000000..e4e8d5a --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/gstreamer.pro @@ -0,0 +1,57 @@ +TARGET = gstengine +include(../../qpluginbase.pri) + +QT += multimedia + +unix:contains(QT_CONFIG, alsa) { + DEFINES += HAVE_ALSA + LIBS += -lasound +} + +LIBS += -lXv + +CONFIG += link_pkgconfig + +PKGCONFIG += \ + gstreamer-0.10 \ + gstreamer-base-0.10 \ + gstreamer-interfaces-0.10 \ + gstreamer-audio-0.10 \ + gstreamer-video-0.10 + +# Input +HEADERS += \ + qgstreamermessage.h \ + qgstreamerbushelper.h \ + qgstreamervideooutputcontrol.h \ + qgstreamervideorendererinterface.h \ + qgstreamervideowidget.h \ + qgstreamerserviceplugin.h \ +# qgstreameraudioinputendpointselector.h \ + qgstreamervideoinputdevicecontrol.h \ + qgstreamervideooverlay.h \ + qgstreamervideorenderer.h \ + qgstvideobuffer.h \ + qvideosurfacegstsink.h \ + qx11videosurface.h + +SOURCES += \ + qgstreamermessage.cpp \ + qgstreamerbushelper.cpp \ + qgstreamervideooutputcontrol.cpp \ + qgstreamervideorendererinterface.cpp \ + qgstreamervideowidget.cpp \ + qgstreamerserviceplugin.cpp \ +# qgstreameraudioinputendpointselector.cpp \ + qgstreamervideoinputdevicecontrol.cpp \ + qgstreamervideooverlay.cpp \ + qgstreamervideorenderer.cpp \ + qgstvideobuffer.cpp \ + qvideosurfacegstsink.cpp \ + qx11videosurface.cpp + +include(mediaplayer/mediaplayer.pri) + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/mediaservices +target.path = $$[QT_INSTALL_PLUGINS]/plugins/mediaservices +INSTALLS += target diff --git a/src/plugins/mediaservices/gstreamer/mediaplayer/mediaplayer.pri b/src/plugins/mediaservices/gstreamer/mediaplayer/mediaplayer.pri new file mode 100644 index 0000000..dda641c --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/mediaplayer/mediaplayer.pri @@ -0,0 +1,19 @@ +INCLUDEPATH += $$PWD + +DEFINES += QMEDIA_GSTREAMER_PLAYER + +HEADERS += \ + $$PWD/qgstreamerplayercontrol.h \ + $$PWD/qgstreamerplayerservice.h \ + $$PWD/qgstreamerplayersession.h \ +# $$PWD/qgstreamerstreamscontrol.h \ + $$PWD/qgstreamermetadataprovider.h + +SOURCES += \ + $$PWD/qgstreamerplayercontrol.cpp \ + $$PWD/qgstreamerplayerservice.cpp \ + $$PWD/qgstreamerplayersession.cpp \ +# $$PWD/qgstreamerstreamscontrol.cpp \ + $$PWD/qgstreamermetadataprovider.cpp + + diff --git a/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp new file mode 100644 index 0000000..c22e83c --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamermetadataprovider.h" +#include "qgstreamerplayersession.h" +#include <QDebug> + +#include <gst/gstversion.h> + +struct QGstreamerMetaDataKeyLookup +{ + QtMedia::MetaData key; + const char *token; +}; + +static const QGstreamerMetaDataKeyLookup qt_gstreamerMetaDataKeys[] = +{ + { QtMedia::Title, GST_TAG_TITLE }, + //{ QtMedia::SubTitle, 0 }, + //{ QtMedia::Author, 0 }, + { QtMedia::Comment, GST_TAG_COMMENT }, + { QtMedia::Description, GST_TAG_DESCRIPTION }, + //{ QtMedia::Category, 0 }, + { QtMedia::Genre, GST_TAG_GENRE }, + { QtMedia::Year, "year" }, + //{ QtMedia::UserRating, 0 }, + + { QtMedia::Language, GST_TAG_LANGUAGE_CODE }, + + { QtMedia::Publisher, GST_TAG_ORGANIZATION }, + { QtMedia::Copyright, GST_TAG_COPYRIGHT }, + //{ QtMedia::ParentalRating, 0 }, + //{ QtMedia::RatingOrganisation, 0 }, + + // Media + //{ QtMedia::Size, 0 }, + //{ QtMedia::MediaType, 0 }, + { QtMedia::Duration, GST_TAG_DURATION }, + + // Audio + { QtMedia::AudioBitRate, GST_TAG_BITRATE }, + { QtMedia::AudioCodec, GST_TAG_AUDIO_CODEC }, + //{ QtMedia::ChannelCount, 0 }, + //{ QtMedia::Frequency, 0 }, + + // Music + { QtMedia::AlbumTitle, GST_TAG_ALBUM }, + { QtMedia::AlbumArtist, GST_TAG_ARTIST}, + { QtMedia::ContributingArtist, GST_TAG_PERFORMER }, +#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 19) + { QtMedia::Composer, GST_TAG_COMPOSER }, +#endif + //{ QtMedia::Conductor, 0 }, + //{ QtMedia::Lyrics, 0 }, + //{ QtMedia::Mood, 0 }, + { QtMedia::TrackNumber, GST_TAG_TRACK_NUMBER }, + + //{ QtMedia::CoverArtUrlSmall, 0 }, + //{ QtMedia::CoverArtUrlLarge, 0 }, + + // Image/Video + //{ QtMedia::Resolution, 0 }, + //{ QtMedia::PixelAspectRatio, 0 }, + + // Video + //{ QtMedia::VideoFrameRate, 0 }, + //{ QtMedia::VideoBitRate, 0 }, + { QtMedia::VideoCodec, GST_TAG_VIDEO_CODEC }, + + //{ QtMedia::PosterUrl, 0 }, + + // Movie + //{ QtMedia::ChapterNumber, 0 }, + //{ QtMedia::Director, 0 }, + { QtMedia::LeadPerformer, GST_TAG_PERFORMER }, + //{ QtMedia::Writer, 0 }, + + // Photos + //{ QtMedia::CameraManufacturer, 0 }, + //{ QtMedia::CameraModel, 0 }, + //{ QtMedia::Event, 0 }, + //{ QtMedia::Subject, 0 } +}; + +QGstreamerMetaDataProvider::QGstreamerMetaDataProvider(QGstreamerPlayerSession *session, QObject *parent) + :QMetaDataControl(parent), m_session(session) +{ + connect(m_session, SIGNAL(tagsChanged()), SLOT(updateTags())); +} + +QGstreamerMetaDataProvider::~QGstreamerMetaDataProvider() +{ +} + +bool QGstreamerMetaDataProvider::isMetaDataAvailable() const +{ + return !m_session->tags().isEmpty(); +} + +bool QGstreamerMetaDataProvider::isWritable() const +{ + return false; +} + +QVariant QGstreamerMetaDataProvider::metaData(QtMedia::MetaData key) const +{ + static const int count = sizeof(qt_gstreamerMetaDataKeys) / sizeof(QGstreamerMetaDataKeyLookup); + + for (int i = 0; i < count; ++i) { + if (qt_gstreamerMetaDataKeys[i].key == key) { + return m_session->tags().value(QByteArray(qt_gstreamerMetaDataKeys[i].token)); + } + } + return QVariant(); +} + +void QGstreamerMetaDataProvider::setMetaData(QtMedia::MetaData key, QVariant const &value) +{ + Q_UNUSED(key); + Q_UNUSED(value); +} + +QList<QtMedia::MetaData> QGstreamerMetaDataProvider::availableMetaData() const +{ + static QMap<QByteArray, QtMedia::MetaData> keysMap; + if (keysMap.isEmpty()) { + const int count = sizeof(qt_gstreamerMetaDataKeys) / sizeof(QGstreamerMetaDataKeyLookup); + for (int i = 0; i < count; ++i) { + keysMap[QByteArray(qt_gstreamerMetaDataKeys[i].token)] = qt_gstreamerMetaDataKeys[i].key; + } + } + + QList<QtMedia::MetaData> res; + foreach (const QByteArray &key, m_session->tags().keys()) { + QtMedia::MetaData tag = keysMap.value(key, QtMedia::MetaData(-1)); + if (tag != -1) + res.append(tag); + } + + return res; +} + +QVariant QGstreamerMetaDataProvider::extendedMetaData(const QString &key) const +{ + return m_session->tags().value(key.toLatin1()); +} + +void QGstreamerMetaDataProvider::setExtendedMetaData(const QString &key, QVariant const &value) +{ + Q_UNUSED(key); + Q_UNUSED(value); +} + +QStringList QGstreamerMetaDataProvider::availableExtendedMetaData() const +{ + QStringList res; + foreach (const QByteArray &key, m_session->tags().keys()) + res.append(QString(key)); + + return res; +} + +void QGstreamerMetaDataProvider::updateTags() +{ + emit metaDataChanged(); +} diff --git a/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamermetadataprovider.h b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamermetadataprovider.h new file mode 100644 index 0000000..7e9af23 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamermetadataprovider.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERMETADATAPROVIDER_H +#define QGSTREAMERMETADATAPROVIDER_H + +#include <QtMultimedia/qmetadatacontrol.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGstreamerPlayerSession; + +class QGstreamerMetaDataProvider : public QMetaDataControl +{ + Q_OBJECT +public: + QGstreamerMetaDataProvider( QGstreamerPlayerSession *session, QObject *parent ); + virtual ~QGstreamerMetaDataProvider(); + + bool isMetaDataAvailable() const; + bool isWritable() const; + + QVariant metaData(QtMedia::MetaData key) const; + void setMetaData(QtMedia::MetaData key, const QVariant &value); + QList<QtMedia::MetaData> availableMetaData() const; + + QVariant extendedMetaData(const QString &key) const ; + void setExtendedMetaData(const QString &key, const QVariant &value); + QStringList availableExtendedMetaData() const; + +private slots: + void updateTags(); + +private: + QGstreamerPlayerSession *m_session; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGSTREAMERMETADATAPROVIDER_H diff --git a/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp new file mode 100644 index 0000000..730279a --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp @@ -0,0 +1,334 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamerplayercontrol.h" +#include "qgstreamerplayersession.h" + +#include <qmediaplaylistnavigator.h> + +#include <QtCore/qdir.h> +#include <QtCore/qsocketnotifier.h> +#include <QtCore/qurl.h> +#include <QtCore/qdebug.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +QGstreamerPlayerControl::QGstreamerPlayerControl(QGstreamerPlayerSession *session, QObject *parent) + : QMediaPlayerControl(parent) + , m_session(session) + , m_stream(0) + , m_fifoNotifier(0) + , m_fifoCanWrite(false) + , m_bufferSize(0) + , m_bufferOffset(0) +{ + m_fifoFd[0] = -1; + m_fifoFd[1] = -1; + + connect(m_session, SIGNAL(positionChanged(qint64)), + this, SIGNAL(positionChanged(qint64))); + connect(m_session, SIGNAL(durationChanged(qint64)), + this, SIGNAL(durationChanged(qint64))); + connect(m_session, SIGNAL(mutedStateChanged(bool)), + this, SIGNAL(mutedChanged(bool))); + connect(m_session, SIGNAL(volumeChanged(int)), + this, SIGNAL(volumeChanged(int))); + connect(m_session, SIGNAL(stateChanged(QMediaPlayer::State)), + this, SIGNAL(stateChanged(QMediaPlayer::State))); + connect(m_session, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), + this, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus))); + connect(m_session,SIGNAL(bufferingProgressChanged(int)), + this, SIGNAL(bufferStatusChanged(int))); + connect(m_session, SIGNAL(videoAvailableChanged(bool)), + this, SIGNAL(videoAvailableChanged(bool))); + connect(m_session, SIGNAL(seekableChanged(bool)), + this, SIGNAL(seekableChanged(bool))); +} + +QGstreamerPlayerControl::~QGstreamerPlayerControl() +{ + if (m_fifoFd[0] >= 0) { + ::close(m_fifoFd[0]); + ::close(m_fifoFd[1]); + m_fifoFd[0] = -1; + m_fifoFd[1] = -1; + } +} + +qint64 QGstreamerPlayerControl::position() const +{ + return m_session->position(); +} + +qint64 QGstreamerPlayerControl::duration() const +{ + return m_session->duration(); +} + +QMediaPlayer::State QGstreamerPlayerControl::state() const +{ + return m_session->state(); +} + +QMediaPlayer::MediaStatus QGstreamerPlayerControl::mediaStatus() const +{ + return m_session->mediaStatus(); +} + +int QGstreamerPlayerControl::bufferStatus() const +{ + return 100; +} + +int QGstreamerPlayerControl::volume() const +{ + return m_session->volume(); +} + +bool QGstreamerPlayerControl::isMuted() const +{ + return m_session->isMuted(); +} + +bool QGstreamerPlayerControl::isSeekable() const +{ + return m_session->isSeekable(); +} + +QMediaTimeRange QGstreamerPlayerControl::availablePlaybackRanges() const +{ + QMediaTimeRange ranges; + + if (m_session->isSeekable()) + ranges.addInterval(0, m_session->duration()); + + return ranges; +} + +qreal QGstreamerPlayerControl::playbackRate() const +{ + return m_session->playbackRate(); +} + +void QGstreamerPlayerControl::setPlaybackRate(qreal rate) +{ + m_session->setPlaybackRate(rate); +} + +void QGstreamerPlayerControl::setPosition(qint64 pos) +{ + m_session->seek(pos); +} + +void QGstreamerPlayerControl::play() +{ + m_session->play(); + + if (m_fifoFd[1] >= 0) { + m_fifoCanWrite = true; + + writeFifo(); + } +} + +void QGstreamerPlayerControl::pause() +{ + m_session->pause(); +} + +void QGstreamerPlayerControl::stop() +{ + m_session->stop(); +} + +void QGstreamerPlayerControl::setVolume(int volume) +{ + m_session->setVolume(volume); +} + +void QGstreamerPlayerControl::setMuted(bool muted) +{ + m_session->setMuted(muted); +} + +QMediaContent QGstreamerPlayerControl::media() const +{ + return m_currentResource; +} + +const QIODevice *QGstreamerPlayerControl::mediaStream() const +{ + return m_stream; +} + +void QGstreamerPlayerControl::setMedia(const QMediaContent &content, QIODevice *stream) +{ + m_session->stop(); + + if (m_stream) { + closeFifo(); + + disconnect(m_stream, SIGNAL(readyRead()), this, SLOT(writeFifo())); + m_stream = 0; + } + + m_currentResource = content; + m_stream = stream; + + QUrl url; + + if (m_stream) { + if (m_stream->isReadable() && openFifo()) { + url = QUrl(QString(QLatin1String("fd://%1")).arg(m_fifoFd[0])); + } + } else if (!content.isNull()) { + url = content.canonicalUrl(); + } + + m_session->load(url); + + emit mediaChanged(m_currentResource); +} + +void QGstreamerPlayerControl::setVideoOutput(QObject *output) +{ + m_session->setVideoRenderer(output); +} + +bool QGstreamerPlayerControl::isVideoAvailable() const +{ + return m_session->isVideoAvailable(); +} + +void QGstreamerPlayerControl::writeFifo() +{ + if (m_fifoCanWrite) { + qint64 bytesToRead = qMin<qint64>( + m_stream->bytesAvailable(), PIPE_BUF - m_bufferSize); + + if (bytesToRead > 0) { + int bytesRead = m_stream->read(&m_buffer[m_bufferOffset + m_bufferSize], bytesToRead); + + if (bytesRead > 0) + m_bufferSize += bytesRead; + } + + if (m_bufferSize > 0) { + int bytesWritten = ::write(m_fifoFd[1], &m_buffer[m_bufferOffset], size_t(m_bufferSize)); + + if (bytesWritten > 0) { + m_bufferOffset += bytesWritten; + m_bufferSize -= bytesWritten; + + if (m_bufferSize == 0) + m_bufferOffset = 0; + } else if (errno == EAGAIN) { + m_fifoCanWrite = false; + } else { + closeFifo(); + } + } + } + + m_fifoNotifier->setEnabled(m_stream->bytesAvailable() > 0); +} + +void QGstreamerPlayerControl::fifoReadyWrite(int socket) +{ + if (socket == m_fifoFd[1]) { + m_fifoCanWrite = true; + + writeFifo(); + } +} + +bool QGstreamerPlayerControl::openFifo() +{ + Q_ASSERT(m_fifoFd[0] < 0); + Q_ASSERT(m_fifoFd[1] < 0); + + if (::pipe(m_fifoFd) == 0) { + int flags = ::fcntl(m_fifoFd[1], F_GETFD); + + if (::fcntl(m_fifoFd[1], F_SETFD, flags | O_NONBLOCK) >= 0) { + m_fifoNotifier = new QSocketNotifier(m_fifoFd[1], QSocketNotifier::Write); + + connect(m_fifoNotifier, SIGNAL(activated(int)), this, SLOT(fifoReadyWrite(int))); + + return true; + } else { + qWarning("Failed to make pipe non blocking %d", errno); + + ::close(m_fifoFd[0]); + ::close(m_fifoFd[1]); + + m_fifoFd[0] = -1; + m_fifoFd[1] = -1; + + return false; + } + } else { + qWarning("Failed to create pipe %d", errno); + + return false; + } +} + +void QGstreamerPlayerControl::closeFifo() +{ + if (m_fifoFd[0] >= 0) { + delete m_fifoNotifier; + m_fifoNotifier = 0; + + ::close(m_fifoFd[0]); + ::close(m_fifoFd[1]); + m_fifoFd[0] = -1; + m_fifoFd[1] = -1; + + m_fifoCanWrite = false; + + m_bufferSize = 0; + m_bufferOffset = 0; + } +} diff --git a/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayercontrol.h b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayercontrol.h new file mode 100644 index 0000000..ec0253b --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayercontrol.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERPLAYERCONTROL_H +#define QGSTREAMERPLAYERCONTROL_H + +#include <QtCore/qobject.h> + +#include <QtMultimedia/qmediaplayercontrol.h> +#include <QtMultimedia/qmediaplayer.h> + +#include <limits.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QMediaPlaylist; +class QGstreamerPlayerSession; +class QGstreamerPlayerService; +class QMediaPlaylistNavigator; +class QSocketNotifier; + +class QGstreamerPlayerControl : public QMediaPlayerControl +{ + Q_OBJECT + +public: + QGstreamerPlayerControl(QGstreamerPlayerSession *session, QObject *parent = 0); + ~QGstreamerPlayerControl(); + + QMediaPlayer::State state() const; + QMediaPlayer::MediaStatus mediaStatus() const; + + qint64 position() const; + qint64 duration() const; + + int bufferStatus() const; + + int volume() const; + bool isMuted() const; + + bool isVideoAvailable() const; + void setVideoOutput(QObject *output); + + bool isSeekable() const; + QMediaTimeRange availablePlaybackRanges() const; + + qreal playbackRate() const; + void setPlaybackRate(qreal rate); + + QMediaContent media() const; + const QIODevice *mediaStream() const; + void setMedia(const QMediaContent&, QIODevice *); + +public Q_SLOTS: + void setPosition(qint64 pos); + + void play(); + void pause(); + void stop(); + + void setVolume(int volume); + void setMuted(bool muted); + +private Q_SLOTS: + void writeFifo(); + void fifoReadyWrite(int socket); + +private: + bool openFifo(); + void closeFifo(); + + QGstreamerPlayerSession *m_session; + QMediaContent m_currentResource; + QIODevice *m_stream; + QSocketNotifier *m_fifoNotifier; + int m_fifoFd[2]; + bool m_fifoCanWrite; + int m_bufferSize; + int m_bufferOffset; + char m_buffer[PIPE_BUF]; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayerservice.cpp b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayerservice.cpp new file mode 100644 index 0000000..0c83147 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayerservice.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qvariant.h> +#include <QtCore/qdebug.h> +#include <QtGui/qwidget.h> + +#include "qgstreamerplayerservice.h" +#include "qgstreamerplayercontrol.h" +#include "qgstreamerplayersession.h" +#include "qgstreamermetadataprovider.h" +#include "qgstreamervideooutputcontrol.h" + +#include "qgstreamervideooverlay.h" +#include "qgstreamervideorenderer.h" + +#include "qgstreamervideowidget.h" +//#include "qgstreamerstreamscontrol.h" + +#include <qmediaplaylistnavigator.h> +#include <qmediaplaylist.h> + + +QT_BEGIN_NAMESPACE + + +QGstreamerPlayerService::QGstreamerPlayerService(QObject *parent): + QMediaService(parent) +{ + m_session = new QGstreamerPlayerSession(this); + m_control = new QGstreamerPlayerControl(m_session, this); + m_metaData = new QGstreamerMetaDataProvider(m_session, this); + m_videoOutput = new QGstreamerVideoOutputControl(this); +// m_streamsControl = new QGstreamerStreamsControl(m_session,this); + + connect(m_videoOutput, SIGNAL(outputChanged(QVideoOutputControl::Output)), + this, SLOT(videoOutputChanged(QVideoOutputControl::Output))); + m_videoRenderer = new QGstreamerVideoRenderer(this); + m_videoWindow = new QGstreamerVideoOverlay(this); + m_videoWidget = new QGstreamerVideoWidgetControl(this); + + m_videoOutput->setAvailableOutputs(QList<QVideoOutputControl::Output>() + << QVideoOutputControl::RendererOutput + << QVideoOutputControl::WindowOutput + << QVideoOutputControl::WidgetOutput); +} + +QGstreamerPlayerService::~QGstreamerPlayerService() +{ +} + +QMediaControl *QGstreamerPlayerService::control(const char *name) const +{ + if (qstrcmp(name,QMediaPlayerControl_iid) == 0) + return m_control; + + if (qstrcmp(name,QMetaDataControl_iid) == 0) + return m_metaData; + +// if (qstrcmp(name,QMediaStreamsControl_iid) == 0) +// return m_streamsControl; + + if (qstrcmp(name, QVideoOutputControl_iid) == 0) + return m_videoOutput; + + if (qstrcmp(name, QVideoWidgetControl_iid) == 0) + return m_videoWidget; + + if (qstrcmp(name, QVideoRendererControl_iid) == 0) + return m_videoRenderer; + + if (qstrcmp(name, QVideoWindowControl_iid) == 0) + return m_videoWindow; + + return 0; +} + +void QGstreamerPlayerService::videoOutputChanged(QVideoOutputControl::Output output) +{ + switch (output) { + case QVideoOutputControl::NoOutput: + m_control->setVideoOutput(0); + break; + case QVideoOutputControl::RendererOutput: + m_control->setVideoOutput(m_videoRenderer); + break; + case QVideoOutputControl::WindowOutput: + m_control->setVideoOutput(m_videoWindow); + break; + case QVideoOutputControl::WidgetOutput: + m_control->setVideoOutput(m_videoWidget); + break; + default: + qWarning("Invalid video output selection"); + break; + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayerservice.h b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayerservice.h new file mode 100644 index 0000000..b27c2ae --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayerservice.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERPLAYERSERVICE_H +#define QGSTREAMERPLAYERSERVICE_H + +#include <QtCore/qobject.h> +#include <QtCore/qiodevice.h> + +#include <QtMultimedia/qmediaservice.h> + +#include "qgstreamervideooutputcontrol.h" + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QMediaMetaData; +class QMediaPlayerControl; +class QMediaPlaylist; +class QMediaPlaylistNavigator; +class QGstreamerMetaData; +class QGstreamerPlayerControl; +class QGstreamerPlayerSession; +class QGstreamerMetaDataProvider; +class QGstreamerStreamsControl; +class QGstreamerVideoRenderer; +class QGstreamerVideoOverlay; +class QGstreamerVideoWidgetControl; + + +class QGstreamerPlayerService : public QMediaService +{ + Q_OBJECT +public: + QGstreamerPlayerService(QObject *parent = 0); + ~QGstreamerPlayerService(); + + //void setVideoOutput(QObject *output); + + QMediaControl *control(const char *name) const; + +private slots: + void videoOutputChanged(QVideoOutputControl::Output output); + +private: + QGstreamerPlayerControl *m_control; + QGstreamerPlayerSession *m_session; + QGstreamerMetaDataProvider *m_metaData; + QGstreamerVideoOutputControl *m_videoOutput; + QGstreamerStreamsControl *m_streamsControl; + + QGstreamerVideoRenderer *m_videoRenderer; + QGstreamerVideoOverlay *m_videoWindow; + QGstreamerVideoWidgetControl *m_videoWidget; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayersession.cpp b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayersession.cpp new file mode 100644 index 0000000..baece67 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayersession.cpp @@ -0,0 +1,683 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamerplayersession.h" +#include "qgstreamerbushelper.h" + +#include "qgstreamervideorendererinterface.h" + +#include <gst/gstvalue.h> + +#include <QtCore/qdatetime.h> +#include <QtCore/qdebug.h> + +//#define USE_PLAYBIN2 + +QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent) + :QObject(parent), + m_state(QMediaPlayer::StoppedState), + m_mediaStatus(QMediaPlayer::UnknownMediaStatus), + m_busHelper(0), + m_playbin(0), + m_nullVideoOutput(0), + m_bus(0), + m_renderer(0), + m_volume(100), + m_playbackRate(1.0), + m_muted(false), + m_videoAvailable(false), + m_seekable(false), + m_lastPosition(0), + m_duration(-1) +{ + static bool initialized = false; + if (!initialized) { + initialized = true; + gst_init(NULL, NULL); + } + +#ifdef USE_PLAYBIN2 + m_playbin = gst_element_factory_make("playbin2", NULL); +#else + m_playbin = gst_element_factory_make("playbin", NULL); +#endif + + if (m_playbin != 0) { + // Sort out messages + m_bus = gst_element_get_bus(m_playbin); + m_busHelper = new QGstreamerBusHelper(m_bus, this); + connect(m_busHelper, SIGNAL(message(QGstreamerMessage)), SLOT(busMessage(QGstreamerMessage))); + m_busHelper->installSyncEventFilter(this); + + m_nullVideoOutput = gst_element_factory_make("fakesink", NULL); + g_object_set(G_OBJECT(m_playbin), "video-sink", m_nullVideoOutput, NULL); + + // Initial volume + double volume = 1.0; + g_object_get(G_OBJECT(m_playbin), "volume", &volume, NULL); + m_volume = int(volume*100); + } +} + +QGstreamerPlayerSession::~QGstreamerPlayerSession() +{ + if (m_playbin) { + stop(); + + delete m_busHelper; + gst_object_unref(GST_OBJECT(m_bus)); + gst_object_unref(GST_OBJECT(m_playbin)); + } +} + +void QGstreamerPlayerSession::load(const QUrl &url) +{ + m_url = url; + if (m_playbin) { + m_tags.clear(); + emit tagsChanged(); + + g_object_set(G_OBJECT(m_playbin), "uri", m_url.toString().toLocal8Bit().constData(), NULL); + +// if (!m_streamTypes.isEmpty()) { +// m_streamProperties.clear(); +// m_streamTypes.clear(); +// +// emit streamsChanged(); +// } + } +} + +qint64 QGstreamerPlayerSession::duration() const +{ + return m_duration; +} + +qint64 QGstreamerPlayerSession::position() const +{ + GstFormat format = GST_FORMAT_TIME; + gint64 position = 0; + + if ( m_playbin && gst_element_query_position(m_playbin, &format, &position)) + return position / 1000000; + else + return 0; +} + +qreal QGstreamerPlayerSession::playbackRate() const +{ + return m_playbackRate; +} + +void QGstreamerPlayerSession::setPlaybackRate(qreal rate) +{ + if (!qFuzzyCompare(m_playbackRate, rate)) { + m_playbackRate = rate; + if (m_playbin) { + gst_element_seek(m_playbin, rate, GST_FORMAT_TIME, + GstSeekFlags(GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SEGMENT), + GST_SEEK_TYPE_NONE,0, + GST_SEEK_TYPE_NONE,0 ); + } + } +} + + +//int QGstreamerPlayerSession::activeStream(QMediaStreamsControl::StreamType streamType) const +//{ +// int streamNumber = -1; +// if (m_playbin) { +// switch (streamType) { +// case QMediaStreamsControl::AudioStream: +// g_object_set(G_OBJECT(m_playbin), "current-audio", streamNumber, NULL); +// break; +// case QMediaStreamsControl::VideoStream: +// g_object_set(G_OBJECT(m_playbin), "current-video", streamNumber, NULL); +// break; +// case QMediaStreamsControl::SubPictureStream: +// g_object_set(G_OBJECT(m_playbin), "current-text", streamNumber, NULL); +// break; +// default: +// break; +// } +// } +// +//#ifdef USE_PLAYBIN2 +// streamNumber += m_playbin2StreamOffset.value(streamType,0); +//#endif +// +// return streamNumber; +//} + +//void QGstreamerPlayerSession::setActiveStream(QMediaStreamsControl::StreamType streamType, int streamNumber) +//{ +//#ifdef USE_PLAYBIN2 +// streamNumber -= m_playbin2StreamOffset.value(streamType,0); +//#endif +// +// if (m_playbin) { +// switch (streamType) { +// case QMediaStreamsControl::AudioStream: +// g_object_get(G_OBJECT(m_playbin), "current-audio", &streamNumber, NULL); +// break; +// case QMediaStreamsControl::VideoStream: +// g_object_get(G_OBJECT(m_playbin), "current-video", &streamNumber, NULL); +// break; +// case QMediaStreamsControl::SubPictureStream: +// g_object_get(G_OBJECT(m_playbin), "current-text", &streamNumber, NULL); +// break; +// default: +// break; +// } +// } +//} + + +bool QGstreamerPlayerSession::isBuffering() const +{ + return false; +} + +int QGstreamerPlayerSession::bufferingProgress() const +{ + return 0; +} + +int QGstreamerPlayerSession::volume() const +{ + return m_volume; +} + +bool QGstreamerPlayerSession::isMuted() const +{ + return m_muted; +} + +void QGstreamerPlayerSession::setVideoRenderer(QObject *videoOutput) +{ + m_renderer = qobject_cast<QGstreamerVideoRendererInterface*>(videoOutput); + if (m_renderer) + g_object_set(G_OBJECT(m_playbin), "video-sink", m_renderer->videoSink(), NULL); + else + g_object_set(G_OBJECT(m_playbin), "video-sink", m_nullVideoOutput, NULL); +} + +bool QGstreamerPlayerSession::isVideoAvailable() const +{ + return m_videoAvailable; +} + +bool QGstreamerPlayerSession::isSeekable() const +{ + return m_seekable; +} + +void QGstreamerPlayerSession::play() +{ + if (m_playbin) { + if (gst_element_set_state(m_playbin, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { + qWarning() << "GStreamer; Unable to play -" << m_url.toString(); + m_state = QMediaPlayer::StoppedState; + m_mediaStatus = QMediaPlayer::InvalidMedia; + + emit stateChanged(m_state); + emit mediaStatusChanged(m_mediaStatus); + } + } +} + +void QGstreamerPlayerSession::pause() +{ + if (m_playbin) + gst_element_set_state(m_playbin, GST_STATE_PAUSED); +} + +void QGstreamerPlayerSession::stop() +{ + if (m_playbin) { + gst_element_set_state(m_playbin, GST_STATE_NULL); + + //we have to do it here, since gstreamer will not emit bus messages any more + if (m_state != QMediaPlayer::StoppedState) + emit stateChanged(m_state = QMediaPlayer::StoppedState); + } +} + +void QGstreamerPlayerSession::seek(qint64 ms) +{ + if (m_playbin && m_state != QMediaPlayer::StoppedState) { + gint64 position = (gint64)ms * 1000000; + gst_element_seek_simple(m_playbin, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, position); + } +} + +void QGstreamerPlayerSession::setVolume(int volume) +{ + m_volume = volume; + emit volumeChanged(m_volume); + + if (!m_muted && m_playbin) + g_object_set(G_OBJECT(m_playbin), "volume", m_volume/100.0, NULL); + +} + +void QGstreamerPlayerSession::setMuted(bool muted) +{ + m_muted = muted; + + g_object_set(G_OBJECT(m_playbin), "volume", (m_muted ? 0 : m_volume/100.0), NULL); + + emit mutedStateChanged(m_muted); +} + +static void addTagToMap(const GstTagList *list, + const gchar *tag, + gpointer user_data) +{ + QMap<QByteArray, QVariant> *map = reinterpret_cast<QMap<QByteArray, QVariant>* >(user_data); + + GValue val; + val.g_type = 0; + gst_tag_list_copy_value(&val,list,tag); + + switch( G_VALUE_TYPE(&val) ) { + case G_TYPE_STRING: + { + const gchar *str_value = g_value_get_string(&val); + map->insert(QByteArray(tag), QString::fromUtf8(str_value)); + break; + } + case G_TYPE_INT: + map->insert(QByteArray(tag), g_value_get_int(&val)); + break; + case G_TYPE_UINT: + map->insert(QByteArray(tag), g_value_get_uint(&val)); + break; + case G_TYPE_LONG: + map->insert(QByteArray(tag), qint64(g_value_get_long(&val))); + break; + case G_TYPE_BOOLEAN: + map->insert(QByteArray(tag), g_value_get_boolean(&val)); + break; + case G_TYPE_CHAR: + map->insert(QByteArray(tag), g_value_get_char(&val)); + break; + case G_TYPE_DOUBLE: + map->insert(QByteArray(tag), g_value_get_double(&val)); + break; + default: + // GST_TYPE_DATE is a function, not a constant, so pull it out of the switch + if (G_VALUE_TYPE(&val) == GST_TYPE_DATE) { + const GDate *date = gst_value_get_date(&val); + if (g_date_valid(date)) { + int year = g_date_get_year(date); + int month = g_date_get_month(date); + int day = g_date_get_day(date); + map->insert(QByteArray(tag), QDate(year,month,day)); + if (!map->contains("year")) + map->insert("year", year); + } + } else if (G_VALUE_TYPE(&val) == GST_TYPE_FRACTION) { + int nom = gst_value_get_fraction_numerator(&val); + int denom = gst_value_get_fraction_denominator(&val); + + if (denom > 0) { + map->insert(QByteArray(tag), double(nom)/denom); + } + } + break; + } + + g_value_unset(&val); +} + +void QGstreamerPlayerSession::setSeekable(bool seekable) +{ + if (seekable != m_seekable) { + m_seekable = seekable; + emit seekableChanged(m_seekable); + } +} + +void QGstreamerPlayerSession::setMediaStatus(QMediaPlayer::MediaStatus status) +{ + if (m_mediaStatus != status) { + m_mediaStatus = status; + emit mediaStatusChanged(status); + } +} + +bool QGstreamerPlayerSession::processSyncMessage(const QGstreamerMessage &message) +{ + GstMessage* gm = message.rawMessage(); + + if (gm && + GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT && + gst_structure_has_name(gm->structure, "prepare-xwindow-id")) + { + if (m_renderer) + m_renderer->precessNewStream(); + return true; + } + + return false; +} + +void QGstreamerPlayerSession::busMessage(const QGstreamerMessage &message) +{ + GstMessage* gm = message.rawMessage(); + + if (gm == 0) { + // Null message, query current position + quint32 newPos = position(); + + if (newPos/1000 != m_lastPosition) { + m_lastPosition = newPos/1000; + emit positionChanged(newPos); + } + + } else { + //tag message comes from elements inside playbin, not from playbin itself + if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_TAG) { + //qDebug() << "tag message"; + GstTagList *tag_list; + gst_message_parse_tag(gm, &tag_list); + gst_tag_list_foreach(tag_list, addTagToMap, &m_tags); + + //qDebug() << m_tags; + + emit tagsChanged(); + } + + if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_playbin)) { + switch (GST_MESSAGE_TYPE(gm)) { + case GST_MESSAGE_DURATION: + break; + + case GST_MESSAGE_STATE_CHANGED: + { + GstState oldState; + GstState newState; + GstState pending; + + gst_message_parse_state_changed(gm, &oldState, &newState, &pending); + + /*QStringList states; + states << "GST_STATE_VOID_PENDING" << "GST_STATE_NULL" << "GST_STATE_READY" << "GST_STATE_PAUSED" << "GST_STATE_PLAYING"; + + qDebug() << QString("state changed: old: %1 new: %2 pending: %3") \ + .arg(states[oldState]) \ + .arg(states[newState]) \ + .arg(states[pending]);*/ + + switch (newState) { + case GST_STATE_VOID_PENDING: + case GST_STATE_NULL: + setMediaStatus(QMediaPlayer::UnknownMediaStatus); + setSeekable(false); + if (m_state != QMediaPlayer::StoppedState) + emit stateChanged(m_state = QMediaPlayer::StoppedState); + break; + case GST_STATE_READY: + setMediaStatus(QMediaPlayer::LoadedMedia); + setSeekable(false); + if (m_state != QMediaPlayer::StoppedState) + emit stateChanged(m_state = QMediaPlayer::StoppedState); + break; + case GST_STATE_PAUSED: + //don't emit state changes for intermediate states + if (m_state != QMediaPlayer::PausedState && pending == GST_STATE_VOID_PENDING) + emit stateChanged(m_state = QMediaPlayer::PausedState); + + setMediaStatus(QMediaPlayer::LoadedMedia); + + //check for seekable + if (oldState == GST_STATE_READY) { + /* + //gst_element_seek_simple doesn't work reliably here, have to find a better solution + + GstFormat format = GST_FORMAT_TIME; + gint64 position = 0; + bool seekable = false; + if (gst_element_query_position(m_playbin, &format, &position)) { + seekable = gst_element_seek_simple(m_playbin, format, GST_SEEK_FLAG_NONE, position); + } + + setSeekable(seekable); + */ + + setSeekable(true); + + if (!qFuzzyCompare(m_playbackRate, qreal(1.0))) + setPlaybackRate(m_playbackRate); + + if (m_renderer) + m_renderer->precessNewStream(); + + } + + + break; + case GST_STATE_PLAYING: + if (oldState == GST_STATE_PAUSED) + getStreamsInfo(); + + if (m_state != QMediaPlayer::PlayingState) + emit stateChanged(m_state = QMediaPlayer::PlayingState); + break; + } + } + break; + + case GST_MESSAGE_EOS: + if (m_state != QMediaPlayer::StoppedState) + emit stateChanged(m_state = QMediaPlayer::StoppedState); + + setMediaStatus(QMediaPlayer::EndOfMedia); + + emit playbackFinished(); + break; + + case GST_MESSAGE_TAG: + case GST_MESSAGE_STREAM_STATUS: + case GST_MESSAGE_UNKNOWN: + case GST_MESSAGE_ERROR: + case GST_MESSAGE_WARNING: + case GST_MESSAGE_INFO: + break; + case GST_MESSAGE_BUFFERING: + setMediaStatus(QMediaPlayer::BufferingMedia); + break; + case GST_MESSAGE_STATE_DIRTY: + case GST_MESSAGE_STEP_DONE: + case GST_MESSAGE_CLOCK_PROVIDE: + case GST_MESSAGE_CLOCK_LOST: + case GST_MESSAGE_NEW_CLOCK: + case GST_MESSAGE_STRUCTURE_CHANGE: + case GST_MESSAGE_APPLICATION: + case GST_MESSAGE_ELEMENT: + case GST_MESSAGE_SEGMENT_START: + case GST_MESSAGE_SEGMENT_DONE: + case GST_MESSAGE_LATENCY: +#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 13) + case GST_MESSAGE_ASYNC_START: + case GST_MESSAGE_ASYNC_DONE: +#if GST_VERSION_MICRO >= 23 + case GST_MESSAGE_REQUEST_STATE: +#endif +#endif + case GST_MESSAGE_ANY: + break; + } + } + } +} + +void QGstreamerPlayerSession::getStreamsInfo() +{ + GstFormat format = GST_FORMAT_TIME; + gint64 duration = 0; + + if (gst_element_query_duration(m_playbin, &format, &duration)) { + int newDuration = duration / 1000000; + if (m_duration != newDuration) { + m_duration = newDuration; + emit durationChanged(m_duration); + } + } + + //check if video is available: + bool haveVideo = false; +// m_streamProperties.clear(); +// m_streamTypes.clear(); + +#ifdef USE_PLAYBIN2 + gint audioStreamsCount = 0; + gint videoStreamsCount = 0; + gint textStreamsCount = 0; + + g_object_get(G_OBJECT(m_playbin), "n-audio", &audioStreamsCount, NULL); + g_object_get(G_OBJECT(m_playbin), "n-video", &videoStreamsCount, NULL); + g_object_get(G_OBJECT(m_playbin), "n-text", &textStreamsCount, NULL); + + m_playbin2StreamOffset[QMediaStreamsControl::AudioStream] = 0; + m_playbin2StreamOffset[QMediaStreamsControl::VideoStream] = audioStreamsCount; + m_playbin2StreamOffset[QMediaStreamsControl::SubPictureStream] = audioStreamsCount+videoStreamsCount; + + for (int i=0; i<audioStreamsCount; i++) + m_streamTypes.append(QMediaStreamsControl::AudioStream); + + for (int i=0; i<videoStreamsCount; i++) + m_streamTypes.append(QMediaStreamsControl::VideoStream); + + for (int i=0; i<textStreamsCount; i++) + m_streamTypes.append(QMediaStreamsControl::SubPictureStream); + + for (int i=0; i<m_streamTypes.count(); i++) { + QMediaStreamsControl::StreamType streamType = m_streamTypes[i]; + QMap<QtMedia::MetaData, QVariant> streamProperties; + + int streamIndex = i - m_playbin2StreamOffset[streamType]; + + GstTagList *tags = 0; + switch (streamType) { + case QMediaStreamsControl::AudioStream: + g_signal_emit_by_name(G_OBJECT(m_playbin), "get-audio-tags", streamIndex, &tags); + break; + case QMediaStreamsControl::VideoStream: + g_signal_emit_by_name(G_OBJECT(m_playbin), "get-video-tags", streamIndex, &tags); + break; + case QMediaStreamsControl::SubPictureStream: + g_signal_emit_by_name(G_OBJECT(m_playbin), "get-text-tags", streamIndex, &tags); + break; + default: + break; + } + + if (tags && gst_is_tag_list(tags)) { + gchar *languageCode = 0; + if (gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &languageCode)) + streamProperties[QtMedia::Language] = QString::fromUtf8(languageCode); + + //qDebug() << "language for setream" << i << QString::fromUtf8(languageCode); + g_free (languageCode); + } + + m_streamProperties.append(streamProperties); + } + +#else + enum { + GST_STREAM_TYPE_UNKNOWN, + GST_STREAM_TYPE_AUDIO, + GST_STREAM_TYPE_VIDEO, + GST_STREAM_TYPE_TEXT, + GST_STREAM_TYPE_SUBPICTURE, + GST_STREAM_TYPE_ELEMENT + }; + + GList* streamInfo; + g_object_get(G_OBJECT(m_playbin), "stream-info", &streamInfo, NULL); + + for (; streamInfo != 0; streamInfo = g_list_next(streamInfo)) { + gint type; + gchar *languageCode = 0; + + GObject* obj = G_OBJECT(streamInfo->data); + + g_object_get(obj, "type", &type, NULL); + g_object_get(obj, "language-code", &languageCode, NULL); + + if (type == GST_STREAM_TYPE_VIDEO) + haveVideo = true; + +// QMediaStreamsControl::StreamType streamType = QMediaStreamsControl::UnknownStream; +// +// switch (type) { +// case GST_STREAM_TYPE_VIDEO: +// streamType = QMediaStreamsControl::VideoStream; +// break; +// case GST_STREAM_TYPE_AUDIO: +// streamType = QMediaStreamsControl::AudioStream; +// break; +// case GST_STREAM_TYPE_SUBPICTURE: +// streamType = QMediaStreamsControl::SubPictureStream; +// break; +// default: +// streamType = QMediaStreamsControl::UnknownStream; +// break; +// } +// +// QMap<QtMedia::MetaData, QVariant> streamProperties; +// streamProperties[QtMedia::Language] = QString::fromUtf8(languageCode); +// +// m_streamProperties.append(streamProperties); +// m_streamTypes.append(streamType); + } +#endif + + if (haveVideo != m_videoAvailable) { + m_videoAvailable = haveVideo; + emit videoAvailableChanged(m_videoAvailable); + } + + emit streamsChanged(); +} diff --git a/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayersession.h b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayersession.h new file mode 100644 index 0000000..a0f5f1a --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerplayersession.h @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERPLAYERSESSION_H +#define QGSTREAMERPLAYERSESSION_H + +#include <QObject> +#include <QUrl> +#include "qgstreamerplayercontrol.h" +#include "qgstreamerbushelper.h" +#include <QtMultimedia/qmediaplayer.h> +//#include <qmediastreamscontrol.h> + +#include <gst/gst.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGstreamerBusHelper; +class QGstreamerMessage; +class QGstreamerVideoRendererInterface; + +class QGstreamerPlayerSession : public QObject, public QGstreamerSyncEventFilter +{ +Q_OBJECT + +public: + QGstreamerPlayerSession(QObject *parent); + virtual ~QGstreamerPlayerSession(); + + QUrl url() const; + + QMediaPlayer::State state() const { return m_state; } + QMediaPlayer::MediaStatus mediaStatus() const { return m_mediaStatus; } + + qint64 duration() const; + qint64 position() const; + + bool isBuffering() const; + + int bufferingProgress() const; + + int volume() const; + bool isMuted() const; + + void setVideoRenderer(QObject *renderer); + bool isVideoAvailable() const; + + bool isSeekable() const; + + qreal playbackRate() const; + void setPlaybackRate(qreal rate); + + QMap<QByteArray ,QVariant> tags() const { return m_tags; } + QMap<QtMedia::MetaData,QVariant> streamProperties(int streamNumber) const { return m_streamProperties[streamNumber]; } +// int streamCount() const { return m_streamProperties.count(); } +// QMediaStreamsControl::StreamType streamType(int streamNumber) { return m_streamTypes.value(streamNumber, QMediaStreamsControl::UnknownStream); } +// +// int activeStream(QMediaStreamsControl::StreamType streamType) const; +// void setActiveStream(QMediaStreamsControl::StreamType streamType, int streamNumber); + + bool processSyncMessage(const QGstreamerMessage &message); + +public slots: + void load(const QUrl &url); + + void play(); + void pause(); + void stop(); + + void seek(qint64 pos); + + void setVolume(int volume); + void setMuted(bool muted); + +signals: + void durationChanged(qint64 duration); + void positionChanged(qint64 position); + void stateChanged(QMediaPlayer::State state); + void mediaStatusChanged(QMediaPlayer::MediaStatus mediaStatus); + void volumeChanged(int volume); + void mutedStateChanged(bool muted); + void videoAvailableChanged(bool videoAvailable); + void bufferingChanged(bool buffering); + void bufferingProgressChanged(int percentFilled); + void playbackFinished(); + void tagsChanged(); + void streamsChanged(); + void seekableChanged(bool); + +private slots: + void busMessage(const QGstreamerMessage &message); + void getStreamsInfo(); + void setSeekable(bool); + +private: + void setMediaStatus(QMediaPlayer::MediaStatus); + + QUrl m_url; + QMediaPlayer::State m_state; + QMediaPlayer::MediaStatus m_mediaStatus; + QGstreamerBusHelper* m_busHelper; + GstElement* m_playbin; + GstElement* m_nullVideoOutput; + GstBus* m_bus; + QGstreamerVideoRendererInterface *m_renderer; + + QMap<QByteArray, QVariant> m_tags; + QList< QMap<QtMedia::MetaData,QVariant> > m_streamProperties; +// QList<QMediaStreamsControl::StreamType> m_streamTypes; +// QMap<QMediaStreamsControl::StreamType, int> m_playbin2StreamOffset; + + + int m_volume; + qreal m_playbackRate; + bool m_muted; + bool m_videoAvailable; + bool m_seekable; + + qint64 m_lastPosition; + qint64 m_duration; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGSTREAMERPLAYERSESSION_H diff --git a/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerstreamscontrol.cpp b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerstreamscontrol.cpp new file mode 100644 index 0000000..22c0b95 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerstreamscontrol.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamerstreamscontrol.h" +#include "qgstreamerplayersession.h" + +QGstreamerStreamsControl::QGstreamerStreamsControl(QGstreamerPlayerSession *session, QObject *parent) + :QMediaStreamsControl(parent), m_session(session) +{ + connect(m_session, SIGNAL(streamsChanged()), SIGNAL(streamsChanged())); +} + +QGstreamerStreamsControl::~QGstreamerStreamsControl() +{ +} + +int QGstreamerStreamsControl::streamCount() +{ + return m_session->streamCount(); +} + +QMediaStreamsControl::StreamType QGstreamerStreamsControl::streamType(int streamNumber) +{ + return m_session->streamType(streamNumber); +} + +QVariant QGstreamerStreamsControl::metaData(int streamNumber, QtMedia::MetaData key) +{ + return m_session->streamProperties(streamNumber).value(key); +} + +bool QGstreamerStreamsControl::isActive(int streamNumber) +{ + return streamNumber != -1 && streamNumber == m_session->activeStream(streamType(streamNumber)); +} + +void QGstreamerStreamsControl::setActive(int streamNumber, bool state) +{ + QMediaStreamsControl::StreamType type = m_session->streamType(streamNumber); + if (type == QMediaStreamsControl::UnknownStream) + return; + + if (state) + m_session->setActiveStream(type, streamNumber); + else { + //only one active stream of certain type is supported + if (m_session->activeStream(type) == streamNumber) + m_session->setActiveStream(type, -1); + } +} + diff --git a/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerstreamscontrol.h b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerstreamscontrol.h new file mode 100644 index 0000000..c115bce --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/mediaplayer/qgstreamerstreamscontrol.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERSTREAMSCONTROL_H +#define QGSTREAMERSTREAMSCONTROL_H + +#include <QtMultimedia/qmediastreamscontrol.h> + +QTM_USE_NAMESPACE + +class QGstreamerPlayerSession; + +class QGstreamerStreamsControl : public QMediaStreamsControl +{ + Q_OBJECT +public: + QGstreamerStreamsControl(QGstreamerPlayerSession *session, QObject *parent); + virtual ~QGstreamerStreamsControl(); + + virtual int streamCount(); + virtual StreamType streamType(int streamNumber); + + virtual QVariant metaData(int streamNumber, QtMedia::MetaData key); + + virtual bool isActive(int streamNumber); + virtual void setActive(int streamNumber, bool state); + +private: + QGstreamerPlayerSession *m_session; +}; + +#endif // QGSTREAMERSTREAMSCONTROL_H + diff --git a/src/plugins/mediaservices/gstreamer/qgstreameraudioinputendpointselector.cpp b/src/plugins/mediaservices/gstreamer/qgstreameraudioinputendpointselector.cpp new file mode 100644 index 0000000..ce6ab30 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreameraudioinputendpointselector.cpp @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreameraudioinputendpointselector.h" + +#include <QtGui/QIcon> +#include <QtCore/QDir> +#include <QtCore/QDebug> + +#include <gst/gst.h> + +#ifdef HAVE_ALSA +#include <alsa/asoundlib.h> +#endif + +QGstreamerAudioInputEndpointSelector::QGstreamerAudioInputEndpointSelector(QObject *parent) + :QAudioEndpointSelector(parent) +{ + update(); +} + +QGstreamerAudioInputEndpointSelector::~QGstreamerAudioInputEndpointSelector() +{ +} + +QList<QString> QGstreamerAudioInputEndpointSelector::availableEndpoints() const +{ + return m_names; +} + +QString QGstreamerAudioInputEndpointSelector::endpointDescription(const QString& name) const +{ + QString desc; + + for (int i = 0; i < m_names.size(); i++) { + if (m_names.at(i).compare(name) == 0) { + desc = m_descriptions.at(i); + break; + } + } + return desc; +} + +QString QGstreamerAudioInputEndpointSelector::defaultEndpoint() const +{ + if (m_names.size() > 0) + return m_names.at(0); + + return QString(); +} + +QString QGstreamerAudioInputEndpointSelector::activeEndpoint() const +{ + return m_audioInput; +} + +void QGstreamerAudioInputEndpointSelector::setActiveEndpoint(const QString& name) +{ + if (m_audioInput.compare(name) != 0) { + m_audioInput = name; + emit activeEndpointChanged(name); + } +} + +void QGstreamerAudioInputEndpointSelector::update() +{ + m_names.clear(); + m_descriptions.clear(); + updateAlsaDevices(); + updateOssDevices(); + updatePulseDevices(); + if (m_names.size() > 0) + m_audioInput = m_names.at(0); +} + +void QGstreamerAudioInputEndpointSelector::updateAlsaDevices() +{ +#ifdef HAVE_ALSA + void **hints, **n; + if (snd_device_name_hint(-1, "pcm", &hints) < 0) { + qWarning()<<"no alsa devices available"; + return; + } + n = hints; + + while (*n != NULL) { + char *name = snd_device_name_get_hint(*n, "NAME"); + char *descr = snd_device_name_get_hint(*n, "DESC"); + char *io = snd_device_name_get_hint(*n, "IOID"); + + if ((name != NULL) && (descr != NULL)) { + if ( io == NULL || qstrcmp(io,"Input") == 0 ) { + m_names.append(QLatin1String("alsa:")+QString::fromUtf8(name)); + m_descriptions.append(QString::fromUtf8(descr)); + } + } + + if (name != NULL) + free(name); + if (descr != NULL) + free(descr); + if (io != NULL) + free(io); + n++; + } + snd_device_name_free_hint(hints); +#endif +} + +void QGstreamerAudioInputEndpointSelector::updateOssDevices() +{ + QDir devDir("/dev"); + devDir.setFilter(QDir::System); +#ifndef QT_QWS_N810 + QFileInfoList entries = devDir.entryInfoList(QStringList() << "dsp*"); + foreach(const QFileInfo& entryInfo, entries) { + m_names.append(QLatin1String("oss:")+entryInfo.filePath()); + m_descriptions.append(QString("OSS device %1").arg(entryInfo.fileName())); + } +#else + m_names.append("dsppcm"); + m_descriptions.append("PCM audio input"); +#endif +} + +void QGstreamerAudioInputEndpointSelector::updatePulseDevices() +{ + GstElementFactory *factory = gst_element_factory_find("pulsesrc"); + if (factory) { + m_names.append("pulseaudio:"); + m_descriptions.append("PulseAudio device."); + gst_object_unref(GST_OBJECT(factory)); + } +} diff --git a/src/plugins/mediaservices/gstreamer/qgstreameraudioinputendpointselector.h b/src/plugins/mediaservices/gstreamer/qgstreameraudioinputendpointselector.h new file mode 100644 index 0000000..9b15939 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreameraudioinputendpointselector.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERAUDIOINPUTENDPOINTSELECTOR_H +#define QGSTREAMERAUDIOINPUTENDPOINTSELECTOR_H + +#include <QtMultimedia/qaudioendpointselector.h> +#include <QtCore/qstringlist.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGstreamerAudioInputEndpointSelector : public QAudioEndpointSelector +{ +Q_OBJECT +public: + QGstreamerAudioInputEndpointSelector(QObject *parent); + ~QGstreamerAudioInputEndpointSelector(); + + QList<QString> availableEndpoints() const; + QString endpointDescription(const QString& name) const; + QString defaultEndpoint() const; + QString activeEndpoint() const; + +public Q_SLOTS: + void setActiveEndpoint(const QString& name); + +private: + void update(); + void updateAlsaDevices(); + void updateOssDevices(); + void updatePulseDevices(); + + QString m_audioInput; + QList<QString> m_names; + QList<QString> m_descriptions; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGSTREAMERAUDIOINPUTENDPOINTSELECTOR_H diff --git a/src/plugins/mediaservices/gstreamer/qgstreamerbushelper.cpp b/src/plugins/mediaservices/gstreamer/qgstreamerbushelper.cpp new file mode 100644 index 0000000..0f09c6b --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamerbushelper.cpp @@ -0,0 +1,203 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QMap> +#include <QTimer> +#include <QMutex> + +#include "qgstreamerbushelper.h" + + +#ifndef QT_NO_GLIB +class QGstreamerBusHelperPrivate : public QObject +{ + Q_OBJECT + +public: + void addWatch(GstBus* bus, QGstreamerBusHelper* helper) + { + setParent(helper); + m_tag = gst_bus_add_watch_full(bus, 0, busCallback, this, NULL); + m_helper = helper; + filter = 0; + } + + void removeWatch(QGstreamerBusHelper* helper) + { + Q_UNUSED(helper); + g_source_remove(m_tag); + } + + static QGstreamerBusHelperPrivate* instance() + { + return new QGstreamerBusHelperPrivate; + } + +private: + void processMessage(GstBus* bus, GstMessage* message) + { + Q_UNUSED(bus); + emit m_helper->message(message); + } + + static gboolean busCallback(GstBus *bus, GstMessage *message, gpointer data) + { + reinterpret_cast<QGstreamerBusHelperPrivate*>(data)->processMessage(bus, message); + return TRUE; + } + + guint m_tag; + QGstreamerBusHelper* m_helper; + +public: + GstBus* bus; + QGstreamerSyncEventFilter *filter; + QMutex filterMutex; +}; + +#else + +class QGstreamerBusHelperPrivate : public QObject +{ + Q_OBJECT + typedef QMap<QGstreamerBusHelper*, GstBus*> HelperMap; + +public: + void addWatch(GstBus* bus, QGstreamerBusHelper* helper) + { + m_helperMap.insert(helper, bus); + + if (m_helperMap.size() == 1) + m_intervalTimer->start(); + } + + void removeWatch(QGstreamerBusHelper* helper) + { + m_helperMap.remove(helper); + + if (m_helperMap.size() == 0) + m_intervalTimer->stop(); + } + + static QGstreamerBusHelperPrivate* instance() + { + static QGstreamerBusHelperPrivate self; + + return &self; + } + +private slots: + void interval() + { + for (HelperMap::iterator it = m_helperMap.begin(); it != m_helperMap.end(); ++it) { + GstMessage* message; + + while ((message = gst_bus_poll(it.value(), GST_MESSAGE_ANY, 0)) != 0) { + emit it.key()->message(message); + gst_message_unref(message); + } + + emit it.key()->message(QGstreamerMessage()); + } + } + +private: + QGstreamerBusHelperPrivate() + { + m_intervalTimer = new QTimer(this); + m_intervalTimer->setInterval(250); + + connect(m_intervalTimer, SIGNAL(timeout()), SLOT(interval())); + } + + HelperMap m_helperMap; + QTimer* m_intervalTimer; + +public: + GstBus* bus; + QGstreamerSyncEventFilter *filter; + QMutex filterMutex; +}; +#endif + + +static GstBusSyncReply syncGstBusFilter(GstBus* bus, GstMessage* message, QGstreamerBusHelperPrivate *d) +{ + Q_UNUSED(bus); + QMutexLocker lock(&d->filterMutex); + + bool res = false; + + if (d->filter) + res = d->filter->processSyncMessage(QGstreamerMessage(message)); + + return res ? GST_BUS_DROP : GST_BUS_PASS; +} + + +/*! + \class gstreamer::QGstreamerBusHelper + \internal +*/ + +QGstreamerBusHelper::QGstreamerBusHelper(GstBus* bus, QObject* parent): + QObject(parent), + d(QGstreamerBusHelperPrivate::instance()) +{ + d->bus = bus; + d->addWatch(bus, this); + + gst_bus_set_sync_handler(bus, (GstBusSyncHandler)syncGstBusFilter, d); +} + +QGstreamerBusHelper::~QGstreamerBusHelper() +{ + d->removeWatch(this); + gst_bus_set_sync_handler(d->bus,0,0); +} + +void QGstreamerBusHelper::installSyncEventFilter(QGstreamerSyncEventFilter *filter) +{ + QMutexLocker lock(&d->filterMutex); + d->filter = filter; +} + +#include "qgstreamerbushelper.moc" diff --git a/src/plugins/mediaservices/gstreamer/qgstreamerbushelper.h b/src/plugins/mediaservices/gstreamer/qgstreamerbushelper.h new file mode 100644 index 0000000..471ca50 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamerbushelper.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERBUSHELPER_H +#define QGSTREAMERBUSHELPER_H + +#include <QObject> + +#include <qgstreamermessage.h> +#include <gst/gst.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGstreamerSyncEventFilter { +public: + //returns true if message was processed and should be dropped, false otherwise + virtual bool processSyncMessage(const QGstreamerMessage &message) = 0; +}; + +class QGstreamerBusHelperPrivate; + +class QGstreamerBusHelper : public QObject +{ + Q_OBJECT + friend class QGstreamerBusHelperPrivate; + +public: + QGstreamerBusHelper(GstBus* bus, QObject* parent = 0); + ~QGstreamerBusHelper(); + + void installSyncEventFilter(QGstreamerSyncEventFilter *filter); + +signals: + void message(QGstreamerMessage const& message); + + +private: + QGstreamerBusHelperPrivate* d; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/gstreamer/qgstreamermessage.cpp b/src/plugins/mediaservices/gstreamer/qgstreamermessage.cpp new file mode 100644 index 0000000..19cdfb1 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamermessage.cpp @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <gst/gst.h> + +#include "qgstreamermessage.h" + + +static int wuchi = qRegisterMetaType<QGstreamerMessage>(); + + +/*! + \class gstreamer::QGstreamerMessage + \internal +*/ + +QGstreamerMessage::QGstreamerMessage(): + m_message(0) +{ +} + +QGstreamerMessage::QGstreamerMessage(GstMessage* message): + m_message(message) +{ + gst_message_ref(m_message); +} + +QGstreamerMessage::QGstreamerMessage(QGstreamerMessage const& m): + m_message(m.m_message) +{ + gst_message_ref(m_message); +} + + +QGstreamerMessage::~QGstreamerMessage() +{ + if (m_message != 0) + gst_message_unref(m_message); +} + +GstMessage* QGstreamerMessage::rawMessage() const +{ + return m_message; +} + +QGstreamerMessage& QGstreamerMessage::operator=(QGstreamerMessage const& rhs) +{ + if (m_message != 0) + gst_message_unref(m_message); + + if ((m_message = rhs.m_message) != 0) + gst_message_ref(m_message); + + return *this; +} diff --git a/src/plugins/mediaservices/gstreamer/qgstreamermessage.h b/src/plugins/mediaservices/gstreamer/qgstreamermessage.h new file mode 100644 index 0000000..70f35f3 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamermessage.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERMESSAGE_H +#define QGSTREAMERMESSAGE_H + +#include <QMetaType> + +#include <gst/gst.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGstreamerMessage +{ +public: + QGstreamerMessage(); + QGstreamerMessage(GstMessage* message); + QGstreamerMessage(QGstreamerMessage const& m); + ~QGstreamerMessage(); + + GstMessage* rawMessage() const; + + QGstreamerMessage& operator=(QGstreamerMessage const& rhs); + +private: + GstMessage* m_message; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QGstreamerMessage); + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/gstreamer/qgstreamerserviceplugin.cpp b/src/plugins/mediaservices/gstreamer/qgstreamerserviceplugin.cpp new file mode 100644 index 0000000..e892e45 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamerserviceplugin.cpp @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qstring.h> +#include <QtCore/qdebug.h> +#include <QtGui/QIcon> +#include <QtCore/QDir> +#include <QtCore/QDebug> + +#include "qgstreamerserviceplugin.h" + +#ifdef QMEDIA_GSTREAMER_PLAYER +#include "qgstreamerplayerservice.h" +#endif +#ifdef QMEDIA_GSTREAMER_CAPTURE +#include "qgstreamercaptureservice.h" +#endif + +#include <QtMultimedia/qmediaserviceprovider.h> + +#include <linux/types.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/poll.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <linux/videodev2.h> + + +QT_BEGIN_NAMESPACE + + +QStringList QGstreamerServicePlugin::keys() const +{ + return QStringList() +#ifdef QMEDIA_GSTREAMER_PLAYER + << QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER) +#endif +#ifdef QMEDIA_GSTREAMER_CAPTURE + << QLatin1String(Q_MEDIASERVICE_AUDIOSOURCE) + << QLatin1String(Q_MEDIASERVICE_CAMERA) +#endif + ; +} + +QMediaService* QGstreamerServicePlugin::create(const QString &key) +{ +#ifdef QMEDIA_GSTREAMER_PLAYER + if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER)) + return new QGstreamerPlayerService; +#endif +#ifdef QMEDIA_GSTREAMER_CAPTURE + if (key == QLatin1String(Q_MEDIASERVICE_AUDIOSOURCE)) + return new QGstreamerCaptureService(key); + + if (key == QLatin1String(Q_MEDIASERVICE_CAMERA)) + return new QGstreamerCaptureService(key); +#endif + + qDebug() << "unsupported key:" << key; + return 0; +} + +void QGstreamerServicePlugin::release(QMediaService *service) +{ + delete service; +} + +QList<QByteArray> QGstreamerServicePlugin::devices(const QByteArray &service) const +{ + if (service == Q_MEDIASERVICE_CAMERA) { + if (m_cameraDevices.isEmpty()) + updateDevices(); + + return m_cameraDevices; + } + + return QList<QByteArray>(); +} + +QString QGstreamerServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device) +{ + if (service == Q_MEDIASERVICE_CAMERA) { + if (m_cameraDevices.isEmpty()) + updateDevices(); + + for (int i=0; i<m_cameraDevices.count(); i++) + if (m_cameraDevices[i] == device) + return m_cameraDescriptions[i]; + } + + return QString(); +} + +void QGstreamerServicePlugin::updateDevices() const +{ + m_cameraDevices.clear(); + m_cameraDescriptions.clear(); + + QDir devDir("/dev"); + devDir.setFilter(QDir::System); + + QFileInfoList entries = devDir.entryInfoList(QStringList() << "video*"); + + foreach( const QFileInfo &entryInfo, entries ) { + qDebug() << "Try" << entryInfo.filePath(); + + int fd = ::open(entryInfo.filePath().toLatin1().constData(), O_RDWR ); + if (fd == -1) + continue; + + bool isCamera = false; + + v4l2_input input; + memset(&input, 0, sizeof(input)); + for (; ::ioctl(fd, VIDIOC_ENUMINPUT, &input) >= 0; ++input.index) { + if(input.type == V4L2_INPUT_TYPE_CAMERA || input.type == 0) { + isCamera = ::ioctl(fd, VIDIOC_S_INPUT, input.index) != 0; + break; + } + } + + if (isCamera) { + // find out its driver "name" + QString name; + struct v4l2_capability vcap; + memset(&vcap, 0, sizeof(struct v4l2_capability)); + + if (ioctl(fd, VIDIOC_QUERYCAP, &vcap) != 0) + name = entryInfo.fileName(); + else + name = QString((const char*)vcap.card); + qDebug() << "found camera: " << name; + + m_cameraDevices.append(entryInfo.filePath().toLocal8Bit()); + m_cameraDescriptions.append(name); + } + ::close(fd); + } +} + +Q_EXPORT_PLUGIN2(gstengine, QGstreamerServicePlugin); + +QT_END_NAMESPACE diff --git a/src/plugins/mediaservices/gstreamer/qgstreamerserviceplugin.h b/src/plugins/mediaservices/gstreamer/qgstreamerserviceplugin.h new file mode 100644 index 0000000..33148ac --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamerserviceplugin.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QGSTREAMERSERVICEPLUGIN_H +#define QGSTREAMERSERVICEPLUGIN_H + +#include <QtMultimedia/qmediaserviceproviderplugin.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGstreamerServicePlugin : public QMediaServiceProviderPlugin, public QMediaServiceSupportedDevicesInterface +{ + Q_OBJECT + Q_INTERFACES(QMediaServiceSupportedDevicesInterface) +public: + QStringList keys() const; + QMediaService* create(QString const& key); + void release(QMediaService *service); + + QList<QByteArray> devices(const QByteArray &service) const; + QString deviceDescription(const QByteArray &service, const QByteArray &device); + +private: + void updateDevices() const; + + mutable QList<QByteArray> m_cameraDevices; + mutable QStringList m_cameraDescriptions; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGSTREAMERSERVICEPLUGIN_H diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideoinputdevicecontrol.cpp b/src/plugins/mediaservices/gstreamer/qgstreamervideoinputdevicecontrol.cpp new file mode 100644 index 0000000..5059409 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideoinputdevicecontrol.cpp @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamervideoinputdevicecontrol.h" + +#include <QtGui/QIcon> +#include <QtCore/QDir> +#include <QtCore/QDebug> + +#include <linux/types.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/poll.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <linux/videodev2.h> + +QGstreamerVideoInputDeviceControl::QGstreamerVideoInputDeviceControl(QObject *parent) + :QVideoDeviceControl(parent), m_selectedDevice(0) +{ + update(); +} + +QGstreamerVideoInputDeviceControl::~QGstreamerVideoInputDeviceControl() +{ +} + +int QGstreamerVideoInputDeviceControl::deviceCount() const +{ + return m_names.size(); +} + +QString QGstreamerVideoInputDeviceControl::deviceName(int index) const +{ + return m_names[index]; +} + +QString QGstreamerVideoInputDeviceControl::deviceDescription(int index) const +{ + return m_descriptions[index]; +} + +QIcon QGstreamerVideoInputDeviceControl::deviceIcon(int index) const +{ + Q_UNUSED(index); + return QIcon(); +} + +int QGstreamerVideoInputDeviceControl::defaultDevice() const +{ + return 0; +} + +int QGstreamerVideoInputDeviceControl::selectedDevice() const +{ + return m_selectedDevice; +} + + +void QGstreamerVideoInputDeviceControl::setSelectedDevice(int index) +{ + if (index != m_selectedDevice) { + m_selectedDevice = index; + emit selectedDeviceChanged(index); + emit selectedDeviceChanged(deviceName(index)); + } +} + + +void QGstreamerVideoInputDeviceControl::update() +{ + m_names.clear(); + m_descriptions.clear(); + + QDir devDir("/dev"); + devDir.setFilter(QDir::System); + + QFileInfoList entries = devDir.entryInfoList(QStringList() << "video*"); + + foreach( const QFileInfo &entryInfo, entries ) { + qDebug() << "Try" << entryInfo.filePath(); + + int fd = ::open(entryInfo.filePath().toLatin1().constData(), O_RDWR ); + if (fd == -1) + continue; + + bool isCamera = false; + + v4l2_input input; + memset(&input, 0, sizeof(input)); + for (; ::ioctl(fd, VIDIOC_ENUMINPUT, &input) >= 0; ++input.index) { + if(input.type == V4L2_INPUT_TYPE_CAMERA || input.type == 0) { + isCamera = ::ioctl(fd, VIDIOC_S_INPUT, input.index) != 0; + break; + } + } + + if (isCamera) { + // find out its driver "name" + QString name; + struct v4l2_capability vcap; + memset(&vcap, 0, sizeof(struct v4l2_capability)); + + if (ioctl(fd, VIDIOC_QUERYCAP, &vcap) != 0) + name = entryInfo.fileName(); + else + name = QString((const char*)vcap.card); + qDebug() << "found camera: " << name; + + m_names.append(entryInfo.filePath()); + m_descriptions.append(name); + } + ::close(fd); + } +} diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideoinputdevicecontrol.h b/src/plugins/mediaservices/gstreamer/qgstreamervideoinputdevicecontrol.h new file mode 100644 index 0000000..5cdc05f --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideoinputdevicecontrol.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEOINPUTDEVICECONTROL_H +#define QGSTREAMERVIDEOINPUTDEVICECONTROL_H + +#include <QtMultimedia/qvideodevicecontrol.h> +#include <QtCore/qstringlist.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QGstreamerVideoInputDeviceControl : public QVideoDeviceControl +{ +Q_OBJECT +public: + QGstreamerVideoInputDeviceControl(QObject *parent); + ~QGstreamerVideoInputDeviceControl(); + + int deviceCount() const; + + QString deviceName(int index) const; + QString deviceDescription(int index) const; + QIcon deviceIcon(int index) const; + + int defaultDevice() const; + int selectedDevice() const; + +public Q_SLOTS: + void setSelectedDevice(int index); + +private: + void update(); + + int m_selectedDevice; + QStringList m_names; + QStringList m_descriptions; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGSTREAMERAUDIOINPUTDEVICECONTROL_H diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideooutputcontrol.cpp b/src/plugins/mediaservices/gstreamer/qgstreamervideooutputcontrol.cpp new file mode 100644 index 0000000..10d446f --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideooutputcontrol.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamervideooutputcontrol.h" + +QGstreamerVideoOutputControl::QGstreamerVideoOutputControl(QObject *parent) + : QVideoOutputControl(parent) + , m_output(NoOutput) +{ +} + +QList<QVideoOutputControl::Output> QGstreamerVideoOutputControl::availableOutputs() const +{ + return m_outputs; +} + +void QGstreamerVideoOutputControl::setAvailableOutputs(const QList<Output> &outputs) +{ + emit availableOutputsChanged(m_outputs = outputs); +} + +QVideoOutputControl::Output QGstreamerVideoOutputControl::output() const +{ + return m_output; +} + +void QGstreamerVideoOutputControl::setOutput(Output output) +{ + if (!m_outputs.contains(output)) + output = NoOutput; + + if (m_output != output) + emit outputChanged(m_output = output); +} diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideooutputcontrol.h b/src/plugins/mediaservices/gstreamer/qgstreamervideooutputcontrol.h new file mode 100644 index 0000000..0d618f2 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideooutputcontrol.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEOOUTPUTCONTROL_H +#define QGSTREAMERVIDEOOUTPUTCONTROL_H + +#include <QtMultimedia/qvideooutputcontrol.h> + +#include <gst/gst.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGstreamerVideoRendererInterface +{ +public: + virtual ~QGstreamerVideoRendererInterface(); + virtual GstElement *videoSink() = 0; + virtual void precessNewStream() {} +}; + +class QGstreamerVideoOutputControl : public QVideoOutputControl +{ + Q_OBJECT +public: + QGstreamerVideoOutputControl(QObject *parent = 0); + + QList<Output> availableOutputs() const; + void setAvailableOutputs(const QList<Output> &outputs); + + Output output() const; + void setOutput(Output output); + +Q_SIGNALS: + void outputChanged(QVideoOutputControl::Output output); + +private: + QList<Output> m_outputs; + Output m_output; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideooverlay.cpp b/src/plugins/mediaservices/gstreamer/qgstreamervideooverlay.cpp new file mode 100644 index 0000000..8dbe3db --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideooverlay.cpp @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamervideooverlay.h" +#include "qvideosurfacegstsink.h" + +#include <QtMultimedia/qvideosurfaceformat.h> + +#include "qx11videosurface.h" + +QGstreamerVideoOverlay::QGstreamerVideoOverlay(QObject *parent) + : QVideoWindowControl(parent) + , m_surface(new QX11VideoSurface) + , m_videoSink(reinterpret_cast<GstElement*>(QVideoSurfaceGstSink::createSink(m_surface))) + , m_aspectRatioMode(QVideoWidget::KeepAspectRatio) + , m_fullScreen(false) +{ + if (m_videoSink) { + gst_object_ref(GST_OBJECT(m_videoSink)); //Take ownership + gst_object_sink(GST_OBJECT(m_videoSink)); + } + + connect(m_surface, SIGNAL(surfaceFormatChanged(QVideoSurfaceFormat)), + this, SLOT(surfaceFormatChanged())); +} + +QGstreamerVideoOverlay::~QGstreamerVideoOverlay() +{ + if (m_videoSink) + gst_object_unref(GST_OBJECT(m_videoSink)); + + delete m_surface; +} + +WId QGstreamerVideoOverlay::winId() const +{ + return m_surface->winId(); +} + +void QGstreamerVideoOverlay::setWinId(WId id) +{ + m_surface->setWinId(id); +} + +QRect QGstreamerVideoOverlay::displayRect() const +{ + return m_displayRect; +} + +void QGstreamerVideoOverlay::setDisplayRect(const QRect &rect) +{ + m_displayRect = rect; + + setScaledDisplayRect(); +} + +QVideoWidget::AspectRatioMode QGstreamerVideoOverlay::aspectRatioMode() const +{ + return m_aspectRatioMode; +} + +void QGstreamerVideoOverlay::setAspectRatioMode(QVideoWidget::AspectRatioMode mode) +{ + m_aspectRatioMode = mode; + + setScaledDisplayRect(); +} + +void QGstreamerVideoOverlay::repaint() +{ +} + +int QGstreamerVideoOverlay::brightness() const +{ + return m_surface->brightness(); +} + +void QGstreamerVideoOverlay::setBrightness(int brightness) +{ + m_surface->setBrightness(brightness); + + emit brightnessChanged(m_surface->brightness()); +} + +int QGstreamerVideoOverlay::contrast() const +{ + return m_surface->contrast(); +} + +void QGstreamerVideoOverlay::setContrast(int contrast) +{ + m_surface->setContrast(contrast); + + emit contrastChanged(m_surface->contrast()); +} + +int QGstreamerVideoOverlay::hue() const +{ + return m_surface->hue(); +} + +void QGstreamerVideoOverlay::setHue(int hue) +{ + m_surface->setHue(hue); + + emit hueChanged(m_surface->hue()); +} + +int QGstreamerVideoOverlay::saturation() const +{ + return m_surface->saturation(); +} + +void QGstreamerVideoOverlay::setSaturation(int saturation) +{ + m_surface->setSaturation(saturation); + + emit saturationChanged(m_surface->saturation()); +} + +bool QGstreamerVideoOverlay::isFullScreen() const +{ + return m_fullScreen; +} + +void QGstreamerVideoOverlay::setFullScreen(bool fullScreen) +{ + emit fullScreenChanged(m_fullScreen = fullScreen); +} + +QSize QGstreamerVideoOverlay::nativeSize() const +{ + return m_surface->surfaceFormat().sizeHint(); +} + +QAbstractVideoSurface *QGstreamerVideoOverlay::surface() const +{ + return m_surface; +} + +GstElement *QGstreamerVideoOverlay::videoSink() +{ + return m_videoSink; +} + +void QGstreamerVideoOverlay::surfaceFormatChanged() +{ + setScaledDisplayRect(); + + emit nativeSizeChanged(); +} + +void QGstreamerVideoOverlay::setScaledDisplayRect() +{ + switch (m_aspectRatioMode) { + case QVideoWidget::KeepAspectRatio: + { + QSize size = m_surface->surfaceFormat().viewport().size(); + + size.scale(m_displayRect.size(), Qt::KeepAspectRatio); + + QRect rect(QPoint(0, 0), size); + rect.moveCenter(m_displayRect.center()); + + m_surface->setDisplayRect(rect); + } + break; + case QVideoWidget::IgnoreAspectRatio: + m_surface->setDisplayRect(m_displayRect); + break; + }; +} diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideooverlay.h b/src/plugins/mediaservices/gstreamer/qgstreamervideooverlay.h new file mode 100644 index 0000000..518d8ce --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideooverlay.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEOOVERLAY_H +#define QGSTREAMERVIDEOOVERLAY_H + +#include <QtMultimedia/qvideowindowcontrol.h> + +#include "qgstreamervideorendererinterface.h" + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QAbstractVideoSurface; +class QX11VideoSurface; + +class QGstreamerVideoOverlay : public QVideoWindowControl, public QGstreamerVideoRendererInterface +{ + Q_OBJECT + Q_INTERFACES(QGstreamerVideoRendererInterface) +public: + QGstreamerVideoOverlay(QObject *parent = 0); + ~QGstreamerVideoOverlay(); + + WId winId() const; + void setWinId(WId id); + + QRect displayRect() const; + void setDisplayRect(const QRect &rect); + + bool isFullScreen() const; + void setFullScreen(bool fullScreen); + + QSize nativeSize() const; + + QVideoWidget::AspectRatioMode aspectRatioMode() const; + void setAspectRatioMode(QVideoWidget::AspectRatioMode mode); + + void repaint(); + + int brightness() const; + void setBrightness(int brightness); + + int contrast() const; + void setContrast(int contrast); + + int hue() const; + void setHue(int hue); + + int saturation() const; + void setSaturation(int saturation); + + QAbstractVideoSurface *surface() const; + + GstElement *videoSink(); + +private slots: + void surfaceFormatChanged(); + +private: + void setScaledDisplayRect(); + + QX11VideoSurface *m_surface; + GstElement *m_videoSink; + QVideoWidget::AspectRatioMode m_aspectRatioMode; + QRect m_displayRect; + bool m_fullScreen; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideorenderer.cpp b/src/plugins/mediaservices/gstreamer/qgstreamervideorenderer.cpp new file mode 100644 index 0000000..7e10fbf --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideorenderer.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamervideorenderer.h" +#include "qvideosurfacegstsink.h" + +#include <QEvent> +#include <QApplication> + +#include <gst/gst.h> + +QGstreamerVideoRenderer::QGstreamerVideoRenderer(QObject *parent) + :QVideoRendererControl(parent),m_videoSink(0) +{ +} + +QGstreamerVideoRenderer::~QGstreamerVideoRenderer() +{ + if (m_videoSink) + gst_object_unref(GST_OBJECT(m_videoSink)); +} + +GstElement *QGstreamerVideoRenderer::videoSink() +{ + if (!m_videoSink) { + m_videoSink = reinterpret_cast<GstElement*>(QVideoSurfaceGstSink::createSink(m_surface)); + gst_object_ref(GST_OBJECT(m_videoSink)); //Take ownership + gst_object_sink(GST_OBJECT(m_videoSink)); + } + + return m_videoSink; +} + + +QAbstractVideoSurface *QGstreamerVideoRenderer::surface() const +{ + return m_surface; +} + +void QGstreamerVideoRenderer::setSurface(QAbstractVideoSurface *surface) +{ + m_surface = surface; +} + diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideorenderer.h b/src/plugins/mediaservices/gstreamer/qgstreamervideorenderer.h new file mode 100644 index 0000000..f476730 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideorenderer.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEORENDERER_H +#define QGSTREAMERVIDEORENDERER_H + +#include <QtMultimedia/qvideorenderercontrol.h> +#include "qvideosurfacegstsink.h" + +#include "qgstreamervideorendererinterface.h" + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGstreamerVideoRenderer : public QVideoRendererControl, public QGstreamerVideoRendererInterface +{ + Q_OBJECT + Q_INTERFACES(QGstreamerVideoRendererInterface) +public: + QGstreamerVideoRenderer(QObject *parent = 0); + virtual ~QGstreamerVideoRenderer(); + + QAbstractVideoSurface *surface() const; + void setSurface(QAbstractVideoSurface *surface); + + GstElement *videoSink(); + void precessNewStream() {} + +private: + GstElement *m_videoSink; + QAbstractVideoSurface *m_surface; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGSTREAMERVIDEORENDRER_H diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideorendererinterface.cpp b/src/plugins/mediaservices/gstreamer/qgstreamervideorendererinterface.cpp new file mode 100644 index 0000000..57df692 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideorendererinterface.cpp @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamervideorendererinterface.h" + +QGstreamerVideoRendererInterface::~QGstreamerVideoRendererInterface() +{ +} diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideorendererinterface.h b/src/plugins/mediaservices/gstreamer/qgstreamervideorendererinterface.h new file mode 100644 index 0000000..2700965 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideorendererinterface.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEOOUTPUTCONTROL_H +#define QGSTREAMERVIDEOOUTPUTCONTROL_H + +#include <gst/gst.h> + +#include <QtCore/qobject.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGstreamerVideoRendererInterface +{ +public: + virtual ~QGstreamerVideoRendererInterface(); + virtual GstElement *videoSink() = 0; + virtual void precessNewStream() {} +}; + +QT_END_NAMESPACE + +#define QGstreamerVideoRendererInterface_iid "com.nokia.Qt.QGstreamerVideoRendererInterface/1.0" +Q_DECLARE_INTERFACE(QGstreamerVideoRendererInterface, QGstreamerVideoRendererInterface_iid) + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideowidget.cpp b/src/plugins/mediaservices/gstreamer/qgstreamervideowidget.cpp new file mode 100644 index 0000000..7b2ec14 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideowidget.cpp @@ -0,0 +1,322 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamervideowidget.h" + +#include <QtCore/qcoreevent.h> +#include <QtCore/qdebug.h> +#include <QtGui/qapplication.h> +#include <QtGui/qpainter.h> + +#include <X11/Xlib.h> +#include <gst/gst.h> +#include <gst/interfaces/xoverlay.h> +#include <gst/interfaces/propertyprobe.h> + +class QGstreamerVideoWidget : public QWidget +{ +public: + QGstreamerVideoWidget(QWidget *parent = 0) + :QWidget(parent) + { + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + QPalette palette; + palette.setColor(QPalette::Background, Qt::black); + setPalette(palette); + } + + virtual ~QGstreamerVideoWidget() {} + + QSize sizeHint() const + { + return m_nativeSize; + } + + void setNativeSize( const QSize &size) + { + if (size != m_nativeSize) { + m_nativeSize = size; + if (size.isEmpty()) + setMinimumSize(0,0); + else + setMinimumSize(160,120); + + updateGeometry(); + } + } + +protected: + void paintEvent(QPaintEvent *) + { + QPainter painter(this); + painter.fillRect(rect(), palette().background()); + } + + QSize m_nativeSize; +}; + +QGstreamerVideoWidgetControl::QGstreamerVideoWidgetControl(QObject *parent) + : QVideoWidgetControl(parent) + , m_videoSink(0) + , m_widget(new QGstreamerVideoWidget) + , m_fullScreen(false) +{ + m_widget->installEventFilter(this); + m_windowId = m_widget->winId(); + + m_videoSink = gst_element_factory_make ("xvimagesink", NULL); + if (m_videoSink) { + // Check if the xv sink is usable + if (gst_element_set_state(m_videoSink, GST_STATE_READY) != GST_STATE_CHANGE_SUCCESS) { + gst_object_unref(GST_OBJECT(m_videoSink)); + m_videoSink = 0; + } else { + gst_element_set_state(m_videoSink, GST_STATE_NULL); + + g_object_set(G_OBJECT(m_videoSink), "force-aspect-ratio", 1, (const char*)NULL); + } + } + + if (!m_videoSink) + m_videoSink = gst_element_factory_make ("ximagesink", NULL); + + gst_object_ref (GST_OBJECT (m_videoSink)); //Take ownership + gst_object_sink (GST_OBJECT (m_videoSink)); +} + +QGstreamerVideoWidgetControl::~QGstreamerVideoWidgetControl() +{ + if (m_videoSink) + gst_object_unref(GST_OBJECT(m_videoSink)); + + delete m_widget; +} + +GstElement *QGstreamerVideoWidgetControl::videoSink() +{ + return m_videoSink; +} + +bool QGstreamerVideoWidgetControl::eventFilter(QObject *object, QEvent *e) +{ + if (object == m_widget) { + if (e->type() == QEvent::ParentChange || e->type() == QEvent::Show) { + WId newWId = m_widget->winId(); + if (newWId != m_windowId) { + m_windowId = newWId; + // Even if we have created a winId at this point, other X applications + // need to be aware of it. + QApplication::syncX(); + setOverlay(); + } + } + + if (e->type() == QEvent::Show) { + // Setting these values ensures smooth resizing since it + // will prevent the system from clearing the background + m_widget->setAttribute(Qt::WA_NoSystemBackground, true); + m_widget->setAttribute(Qt::WA_PaintOnScreen, true); + } else if (e->type() == QEvent::Resize) { + // This is a workaround for missing background repaints + // when reducing window size + windowExposed(); + } + } + + return false; +} + +void QGstreamerVideoWidgetControl::precessNewStream() +{ + setOverlay(); + QMetaObject::invokeMethod(this, "updateNativeVideoSize", Qt::QueuedConnection); +} + +void QGstreamerVideoWidgetControl::setOverlay() +{ + if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { + gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(m_videoSink), m_windowId); + } +} + +void QGstreamerVideoWidgetControl::updateNativeVideoSize() +{ + if (m_videoSink) { + //find video native size to update video widget size hint + GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); + GstCaps *caps = gst_pad_get_negotiated_caps(pad); + + if (caps) { + GstStructure *str; + gint width, height; + + if ((str = gst_caps_get_structure (caps, 0))) { + if (gst_structure_get_int (str, "width", &width) && gst_structure_get_int (str, "height", &height)) { + gint aspectNum = 0; + gint aspectDenum = 0; + if (gst_structure_get_fraction(str, "pixel-aspect-ratio", &aspectNum, &aspectDenum)) { + if (aspectDenum > 0) + width = width*aspectNum/aspectDenum; + } + m_widget->setNativeSize(QSize(width, height)); + } + } + gst_caps_unref(caps); + } + } else { + m_widget->setNativeSize(QSize()); + } +} + + +void QGstreamerVideoWidgetControl::windowExposed() +{ + if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) + gst_x_overlay_expose(GST_X_OVERLAY(m_videoSink)); +} + +QWidget *QGstreamerVideoWidgetControl::videoWidget() +{ + return m_widget; +} + +QVideoWidget::AspectRatioMode QGstreamerVideoWidgetControl::aspectRatioMode() const +{ + return m_aspectRatioMode; +} + +void QGstreamerVideoWidgetControl::setAspectRatioMode(QVideoWidget::AspectRatioMode mode) +{ + if (m_videoSink) { + g_object_set(G_OBJECT(m_videoSink), + "force-aspect-ratio", + (mode == QVideoWidget::KeepAspectRatio), + (const char*)NULL); + } + + m_aspectRatioMode = mode; +} + +bool QGstreamerVideoWidgetControl::isFullScreen() const +{ + return m_fullScreen; +} + +void QGstreamerVideoWidgetControl::setFullScreen(bool fullScreen) +{ + emit fullScreenChanged(m_fullScreen = fullScreen); +} + +int QGstreamerVideoWidgetControl::brightness() const +{ + int brightness = 0; + + if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "brightness")) + g_object_get(G_OBJECT(m_videoSink), "brightness", &brightness, NULL); + + return brightness / 10; +} + +void QGstreamerVideoWidgetControl::setBrightness(int brightness) +{ + if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "brightness")) { + g_object_set(G_OBJECT(m_videoSink), "brightness", brightness * 10, NULL); + + emit brightnessChanged(brightness); + } +} + +int QGstreamerVideoWidgetControl::contrast() const +{ + int contrast = 0; + + if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "contrast")) + g_object_get(G_OBJECT(m_videoSink), "contrast", &contrast, NULL); + + return contrast / 10; +} + +void QGstreamerVideoWidgetControl::setContrast(int contrast) +{ + if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "contrast")) { + g_object_set(G_OBJECT(m_videoSink), "contrast", contrast * 10, NULL); + + emit contrastChanged(contrast); + } +} + +int QGstreamerVideoWidgetControl::hue() const +{ + int hue = 0; + + if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "hue")) + g_object_get(G_OBJECT(m_videoSink), "hue", &hue, NULL); + + return hue / 10; +} + +void QGstreamerVideoWidgetControl::setHue(int hue) +{ + if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "hue")) { + g_object_set(G_OBJECT(m_videoSink), "hue", hue * 10, NULL); + + emit hueChanged(hue); + } +} + +int QGstreamerVideoWidgetControl::saturation() const +{ + int saturation = 0; + + if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "saturation")) + g_object_get(G_OBJECT(m_videoSink), "saturation", &saturation, NULL); + + return saturation / 10; +} + +void QGstreamerVideoWidgetControl::setSaturation(int saturation) +{ + if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "saturation")) { + g_object_set(G_OBJECT(m_videoSink), "saturation", saturation * 10, NULL); + + emit saturationChanged(saturation); + } +} diff --git a/src/plugins/mediaservices/gstreamer/qgstreamervideowidget.h b/src/plugins/mediaservices/gstreamer/qgstreamervideowidget.h new file mode 100644 index 0000000..9edd1dd --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstreamervideowidget.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEOWIDGET_H +#define QGSTREAMERVIDEOWIDGET_H + +#include <QtMultimedia/qvideowidgetcontrol.h> + +#include "qgstreamervideorendererinterface.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGstreamerVideoWidget; + +class QGstreamerVideoWidgetControl + : public QVideoWidgetControl + , public QGstreamerVideoRendererInterface +{ + Q_OBJECT + Q_INTERFACES(QGstreamerVideoRendererInterface) +public: + QGstreamerVideoWidgetControl(QObject *parent = 0); + virtual ~QGstreamerVideoWidgetControl(); + + GstElement *videoSink(); + void precessNewStream(); + + QWidget *videoWidget(); + + QVideoWidget::AspectRatioMode aspectRatioMode() const; + void setAspectRatioMode(QVideoWidget::AspectRatioMode mode); + + bool isFullScreen() const; + void setFullScreen(bool fullScreen); + + int brightness() const; + void setBrightness(int brightness); + + int contrast() const; + void setContrast(int contrast); + + int hue() const; + void setHue(int hue); + + int saturation() const; + void setSaturation(int saturation); + + void setOverlay(); + + bool eventFilter(QObject *object, QEvent *event); + +public slots: + void updateNativeVideoSize(); + +private: + void windowExposed(); + + GstElement *m_videoSink; + QGstreamerVideoWidget *m_widget; + WId m_windowId; + QVideoWidget::AspectRatioMode m_aspectRatioMode; + bool m_fullScreen; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGSTREAMERVIDEOWIDGET_H diff --git a/src/plugins/mediaservices/gstreamer/qgstvideobuffer.cpp b/src/plugins/mediaservices/gstreamer/qgstvideobuffer.cpp new file mode 100644 index 0000000..80943b5 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstvideobuffer.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstvideobuffer.h" + + +QGstVideoBuffer::QGstVideoBuffer(GstBuffer *buffer, int bytesPerLine) + : QAbstractVideoBuffer(NoHandle) + , m_buffer(buffer) + , m_bytesPerLine(bytesPerLine) + , m_mode(NotMapped) +{ + gst_buffer_ref(m_buffer); +} + +QGstVideoBuffer::~QGstVideoBuffer() +{ + gst_buffer_unref(m_buffer); +} + + +QAbstractVideoBuffer::MapMode QGstVideoBuffer::mapMode() const +{ + return m_mode; +} + +uchar *QGstVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPerLine) +{ + if (mode != NotMapped && m_mode == NotMapped) { + if (numBytes) + *numBytes = m_buffer->size; + + if (bytesPerLine) + *bytesPerLine = m_bytesPerLine; + + m_mode = mode; + + return m_buffer->data; + } else { + return 0; + } +} +void QGstVideoBuffer::unmap() +{ + m_mode = NotMapped; +} + diff --git a/src/plugins/mediaservices/gstreamer/qgstvideobuffer.h b/src/plugins/mediaservices/gstreamer/qgstvideobuffer.h new file mode 100644 index 0000000..487832c --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qgstvideobuffer.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTVIDEOBUFFER_H +#define QGSTVIDEOBUFFER_H + +#include <QtMultimedia/QAbstractVideoBuffer> + +#include <gst/gst.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGstVideoBuffer : public QAbstractVideoBuffer +{ +public: + QGstVideoBuffer(GstBuffer *buffer, int bytesPerLine); + ~QGstVideoBuffer(); + + MapMode mapMode() const; + + uchar *map(MapMode mode, int *numBytes, int *bytesPerLine); + void unmap(); + +private: + GstBuffer *m_buffer; + int m_bytesPerLine; + MapMode m_mode; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/gstreamer/qvideosurfacegstsink.cpp b/src/plugins/mediaservices/gstreamer/qvideosurfacegstsink.cpp new file mode 100644 index 0000000..7c5805c --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qvideosurfacegstsink.cpp @@ -0,0 +1,553 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qvideosurfacegstsink.h" + +#include "qgstvideobuffer.h" + +#include <QtMultimedia/QAbstractVideoSurface> +#include <QtMultimedia/QVideoFrame> +#include <QDebug> +#include <QMap> +#include <QDebug> +#include <QThread> + +Q_DECLARE_METATYPE(QVideoSurfaceFormat) + +QVideoSurfaceGstDelegate::QVideoSurfaceGstDelegate(QAbstractVideoSurface *surface) + : m_surface(surface) + , m_renderReturn(GST_FLOW_ERROR) + , m_bytesPerLine(0) +{ + m_supportedPixelFormats = m_surface->supportedPixelFormats(); + + connect(m_surface, SIGNAL(supportedFormatsChanged()), this, SLOT(supportedFormatsChanged())); +} + +QList<QVideoFrame::PixelFormat> QVideoSurfaceGstDelegate::supportedPixelFormats() const +{ + QMutexLocker locker(const_cast<QMutex *>(&m_mutex)); + + return m_supportedPixelFormats; +} + +bool QVideoSurfaceGstDelegate::start(const QVideoSurfaceFormat &format, int bytesPerLine) +{ + QMutexLocker locker(&m_mutex); + + m_format = format; + m_bytesPerLine = bytesPerLine; + + if (QThread::currentThread() == thread()) { + m_started = !m_surface.isNull() ? m_surface->start(m_format) : false; + } else { + QMetaObject::invokeMethod(this, "queuedStart", Qt::QueuedConnection); + + m_setupCondition.wait(&m_mutex); + } + + return m_started; +} + +void QVideoSurfaceGstDelegate::stop() +{ + QMutexLocker locker(&m_mutex); + + if (QThread::currentThread() == thread()) { + if (!m_surface.isNull()) + m_surface->stop(); + } else { + QMetaObject::invokeMethod(this, "queuedStop", Qt::QueuedConnection); + + m_setupCondition.wait(&m_mutex); + } + + m_started = false; +} + +GstFlowReturn QVideoSurfaceGstDelegate::render(GstBuffer *buffer) +{ + QMutexLocker locker(&m_mutex); + + m_frame = QVideoFrame( + new QGstVideoBuffer(buffer, m_bytesPerLine), + m_format.frameSize(), + m_format.pixelFormat()); + + qint64 startTime = GST_BUFFER_TIMESTAMP(buffer); + + if (startTime >= 0) { + m_frame.setStartTime(startTime/G_GINT64_CONSTANT (1000000)); + + qint64 duration = GST_BUFFER_DURATION(buffer); + + if (duration >= 0) + m_frame.setEndTime((startTime + duration)/G_GINT64_CONSTANT (1000000)); + } + + QMetaObject::invokeMethod(this, "queuedRender", Qt::QueuedConnection); + + if (!m_renderCondition.wait(&m_mutex, 300)) { + m_frame = QVideoFrame(); + + return GST_FLOW_OK; + } else { + return m_renderReturn; + } +} + + +void QVideoSurfaceGstDelegate::queuedStart() +{ + QMutexLocker locker(&m_mutex); + + m_started = m_surface->start(m_format); + + m_setupCondition.wakeAll(); +} + +void QVideoSurfaceGstDelegate::queuedStop() +{ + QMutexLocker locker(&m_mutex); + + m_surface->stop(); + + m_setupCondition.wakeAll(); +} + +void QVideoSurfaceGstDelegate::queuedRender() +{ + QMutexLocker locker(&m_mutex); + + if (m_surface.isNull()) { + m_renderReturn = GST_FLOW_ERROR; + } else if (m_surface->present(m_frame)) { + m_renderReturn = GST_FLOW_OK; + } else { + switch (m_surface->error()) { + case QAbstractVideoSurface::NoError: + m_renderReturn = GST_FLOW_OK; + break; + case QAbstractVideoSurface::StoppedError: + m_renderReturn = GST_FLOW_NOT_NEGOTIATED; + break; + default: + m_renderReturn = GST_FLOW_ERROR; + break; + } + } + + m_renderCondition.wakeAll(); +} + +void QVideoSurfaceGstDelegate::supportedFormatsChanged() +{ + QMutexLocker locker(&m_mutex); + + m_supportedPixelFormats = m_surface->supportedPixelFormats(); +} + +struct YuvFormat +{ + QVideoFrame::PixelFormat pixelFormat; + guint32 fourcc; + int bitsPerPixel; +}; + +static const YuvFormat qt_yuvColorLookup[] = +{ + { QVideoFrame::Format_YUV420P, GST_MAKE_FOURCC('I','4','2','0'), 8 }, + { QVideoFrame::Format_YV12, GST_MAKE_FOURCC('Y','V','1','2'), 8 }, + { QVideoFrame::Format_UYVY, GST_MAKE_FOURCC('U','Y','V','Y'), 8 }, + { QVideoFrame::Format_YUYV, GST_MAKE_FOURCC('Y','U','Y','V'), 8 }, + { QVideoFrame::Format_YUYV, GST_MAKE_FOURCC('Y','U','Y','2'), 8 }, + { QVideoFrame::Format_NV12, GST_MAKE_FOURCC('N','V','1','2'), 8 }, + { QVideoFrame::Format_NV21, GST_MAKE_FOURCC('N','V','2','1'), 8 } +}; + +static int indexOfYuvColor(QVideoFrame::PixelFormat format) +{ + const int count = sizeof(qt_yuvColorLookup) / sizeof(YuvFormat); + + for (int i = 0; i < count; ++i) + if (qt_yuvColorLookup[i].pixelFormat == format) + return i; + return -1; +} + +static int indexOfYuvColor(guint32 fourcc) +{ + const int count = sizeof(YuvFormat) / sizeof(YuvFormat); + + for (int i = 0; i < count; ++i) + if (qt_yuvColorLookup[i].fourcc == fourcc) + return i; + return -1; +} + +struct RgbFormat +{ + QVideoFrame::PixelFormat pixelFormat; + int bitsPerPixel; + int depth; + int endianness; + int red; + int green; + int blue; + int alpha; +}; + +static const RgbFormat qt_rgbColorLookup[] = +{ + { QVideoFrame::Format_RGB32 , 32, 24, 4321, 0x0000FF00, 0x00FF0000, 0xFF000000, 0x00000000 }, + { QVideoFrame::Format_RGB32 , 32, 24, 1234, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000 }, + { QVideoFrame::Format_ARGB32, 32, 24, 4321, 0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF }, + { QVideoFrame::Format_ARGB32, 32, 24, 1234, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000 }, + { QVideoFrame::Format_RGB24 , 24, 24, 4321, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000 }, + { QVideoFrame::Format_RGB565, 16, 16, 1234, 0x0000F800, 0x000007E0, 0x0000001F, 0x00000000 } +}; + +static int indexOfRgbColor( + int bits, int depth, int endianness, int red, int green, int blue, int alpha) +{ + const int count = sizeof(qt_rgbColorLookup) / sizeof(RgbFormat); + + for (int i = 0; i < count; ++i) { + if (qt_rgbColorLookup[i].bitsPerPixel == bits + && qt_rgbColorLookup[i].depth == depth + && qt_rgbColorLookup[i].endianness == endianness + && qt_rgbColorLookup[i].red == red + && qt_rgbColorLookup[i].green == green + && qt_rgbColorLookup[i].blue == blue + && qt_rgbColorLookup[i].alpha == alpha) { + return i; + } + } + return -1; +} + +static GstVideoSinkClass *sink_parent_class; + +#define VO_SINK(s) QVideoSurfaceGstSink *sink(reinterpret_cast<QVideoSurfaceGstSink *>(s)) + +QVideoSurfaceGstSink *QVideoSurfaceGstSink::createSink(QAbstractVideoSurface *surface) +{ + QVideoSurfaceGstSink *sink = reinterpret_cast<QVideoSurfaceGstSink *>( + g_object_new(QVideoSurfaceGstSink::get_type(), 0)); + + sink->delegate = new QVideoSurfaceGstDelegate(surface); + + return sink; +} + +GType QVideoSurfaceGstSink::get_type() +{ + static GType type = 0; + + if (type == 0) { + static const GTypeInfo info = + { + sizeof(QVideoSurfaceGstSinkClass), // class_size + base_init, // base_init + NULL, // base_finalize + class_init, // class_init + NULL, // class_finalize + NULL, // class_data + sizeof(QVideoSurfaceGstSink), // instance_size + 0, // n_preallocs + instance_init, // instance_init + 0 // value_table + }; + + type = g_type_register_static( + GST_TYPE_VIDEO_SINK, "QVideoSurfaceGstSink", &info, GTypeFlags(0)); + } + + return type; +} + +void QVideoSurfaceGstSink::class_init(gpointer g_class, gpointer class_data) +{ + Q_UNUSED(class_data); + + sink_parent_class = reinterpret_cast<GstVideoSinkClass *>(g_type_class_peek_parent(g_class)); + + GstBaseSinkClass *base_sink_class = reinterpret_cast<GstBaseSinkClass *>(g_class); + base_sink_class->get_caps = QVideoSurfaceGstSink::get_caps; + base_sink_class->set_caps = QVideoSurfaceGstSink::set_caps; + // base_sink_class->buffer_alloc = QVideoSurfaceGstSink::buffer_alloc; // Not implemented. + base_sink_class->start = QVideoSurfaceGstSink::start; + base_sink_class->stop = QVideoSurfaceGstSink::stop; + // base_sink_class->unlock = QVideoSurfaceGstSink::unlock; // Not implemented. + // base_sink_class->event = QVideoSurfaceGstSink::event; // Not implemented. + base_sink_class->preroll = QVideoSurfaceGstSink::preroll; + base_sink_class->render = QVideoSurfaceGstSink::render; + + GstElementClass *element_class = reinterpret_cast<GstElementClass *>(g_class); + element_class->change_state = QVideoSurfaceGstSink::change_state; + + GObjectClass *object_class = reinterpret_cast<GObjectClass *>(g_class); + object_class->finalize = QVideoSurfaceGstSink::finalize; +} + +void QVideoSurfaceGstSink::base_init(gpointer g_class) +{ + static GstStaticPadTemplate sink_pad_template = GST_STATIC_PAD_TEMPLATE( + "sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS( + "video/x-raw-rgb, " + "framerate = (fraction) [ 0, MAX ], " + "width = (int) [ 1, MAX ], " + "height = (int) [ 1, MAX ]; " + "video/x-raw-yuv, " + "framerate = (fraction) [ 0, MAX ], " + "width = (int) [ 1, MAX ], " + "height = (int) [ 1, MAX ]")); + + gst_element_class_add_pad_template( + GST_ELEMENT_CLASS(g_class), gst_static_pad_template_get(&sink_pad_template)); +} + +void QVideoSurfaceGstSink::instance_init(GTypeInstance *instance, gpointer g_class) +{ + VO_SINK(instance); + + Q_UNUSED(g_class); + + sink->delegate = 0; +} + +void QVideoSurfaceGstSink::finalize(GObject *object) +{ + Q_UNUSED(object); +} + +GstStateChangeReturn QVideoSurfaceGstSink::change_state( + GstElement *element, GstStateChange transition) +{ + Q_UNUSED(element); + + return GST_ELEMENT_CLASS(sink_parent_class)->change_state( + element, transition); +} + +GstCaps *QVideoSurfaceGstSink::get_caps(GstBaseSink *base) +{ + VO_SINK(base); + + GstCaps *caps = gst_caps_new_empty(); + + foreach (QVideoFrame::PixelFormat format, sink->delegate->supportedPixelFormats()) { + int index = indexOfYuvColor(format); + + if (index != -1) { + gst_caps_append_structure(caps, gst_structure_new( + "video/x-raw-yuv", + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, INT_MAX, 1, + "width" , GST_TYPE_INT_RANGE, 1, INT_MAX, + "height" , GST_TYPE_INT_RANGE, 1, INT_MAX, + "format" , GST_TYPE_FOURCC, qt_yuvColorLookup[index].fourcc, + NULL)); + continue; + } + + const int count = sizeof(qt_rgbColorLookup) / sizeof(RgbFormat); + + for (int i = 0; i < count; ++i) { + if (qt_rgbColorLookup[i].pixelFormat == format) { + GstStructure *structure = gst_structure_new( + "video/x-raw-rgb", + "framerate" , GST_TYPE_FRACTION_RANGE, 0, 1, INT_MAX, 1, + "width" , GST_TYPE_INT_RANGE, 1, INT_MAX, + "height" , GST_TYPE_INT_RANGE, 1, INT_MAX, + "bpp" , G_TYPE_INT, qt_rgbColorLookup[i].bitsPerPixel, + "depth" , G_TYPE_INT, qt_rgbColorLookup[i].depth, + "endianness", G_TYPE_INT, qt_rgbColorLookup[i].endianness, + "red_mask" , G_TYPE_INT, qt_rgbColorLookup[i].red, + "green_mask", G_TYPE_INT, qt_rgbColorLookup[i].green, + "blue_mask" , G_TYPE_INT, qt_rgbColorLookup[i].blue, + NULL); + + if (qt_rgbColorLookup[i].alpha != 0) { + gst_structure_set( + structure, "alpha_mask", G_TYPE_INT, qt_rgbColorLookup[i].alpha, NULL); + } + gst_caps_append_structure(caps, structure); + } + } + } + + return caps; +} + +gboolean QVideoSurfaceGstSink::set_caps(GstBaseSink *base, GstCaps *caps) +{ + VO_SINK(base); + + if (!caps) { + sink->delegate->stop(); + + return TRUE; + } else { + const GstStructure *structure = gst_caps_get_structure(caps, 0); + + //qDebug() << gst_caps_to_string(caps); + + QVideoFrame::PixelFormat pixelFormat = QVideoFrame::Format_Invalid; + int bitsPerPixel = 0; + + QSize size; + gst_structure_get_int(structure, "width", &size.rwidth()); + gst_structure_get_int(structure, "height", &size.rheight()); + + if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-yuv") == 0) { + guint32 fourcc = 0; + gst_structure_get_fourcc(structure, "format", &fourcc); + + int index = indexOfYuvColor(fourcc); + if (index != -1) { + pixelFormat = qt_yuvColorLookup[index].pixelFormat; + bitsPerPixel = qt_yuvColorLookup[index].bitsPerPixel; + } + } else if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-rgb") == 0) { + int depth = 0; + int endianness = 0; + int red = 0; + int green = 0; + int blue = 0; + int alpha = 0; + + gst_structure_get_int(structure, "bpp", &bitsPerPixel); + gst_structure_get_int(structure, "depth", &depth); + gst_structure_get_int(structure, "endianness", &endianness); + gst_structure_get_int(structure, "red_mask", &red); + gst_structure_get_int(structure, "green_mask", &green); + gst_structure_get_int(structure, "blue_mask", &blue); + gst_structure_get_int(structure, "alpha_mask", &alpha); + + int index = indexOfRgbColor(bitsPerPixel, depth, endianness, red, green, blue, alpha); + + if (index != -1) + pixelFormat = qt_rgbColorLookup[index].pixelFormat; + } + + if (pixelFormat != QVideoFrame::Format_Invalid) { + QVideoSurfaceFormat format(size, pixelFormat); + + QPair<int, int> rate; + gst_structure_get_fraction(structure, "framerate", &rate.first, &rate.second); + + if (rate.second) + format.setFrameRate(qreal(rate.first)/rate.second); + + gint aspectNum = 0; + gint aspectDenum = 0; + if (gst_structure_get_fraction( + structure, "pixel-aspect-ratio", &aspectNum, &aspectDenum)) { + if (aspectDenum > 0) + format.setPixelAspectRatio(aspectNum, aspectDenum); + } + + int bytesPerLine = ((size.width() * bitsPerPixel / 8) + 3) & ~3; + + if (sink->delegate->start(format, bytesPerLine)) + return TRUE; + } + + } + + return FALSE; +} + +GstFlowReturn QVideoSurfaceGstSink::buffer_alloc( + GstBaseSink *base, guint64 offset, guint size, GstCaps *caps, GstBuffer **buffer) +{ + // Should implement this when the buffer pool situation is sorted. + Q_UNUSED(base); + Q_UNUSED(offset); + Q_UNUSED(size); + Q_UNUSED(caps); + Q_UNUSED(buffer); + + return GST_FLOW_NOT_SUPPORTED; +} + +gboolean QVideoSurfaceGstSink::start(GstBaseSink *base) +{ + Q_UNUSED(base); + + return TRUE; +} + +gboolean QVideoSurfaceGstSink::stop(GstBaseSink *base) +{ + Q_UNUSED(base); + + return TRUE; +} + +gboolean QVideoSurfaceGstSink::unlock(GstBaseSink *base) +{ + Q_UNUSED(base); + + return TRUE; +} + +gboolean QVideoSurfaceGstSink::event(GstBaseSink *base, GstEvent *event) +{ + Q_UNUSED(base); + Q_UNUSED(event); + + return TRUE; +} + +GstFlowReturn QVideoSurfaceGstSink::preroll(GstBaseSink *base, GstBuffer *buffer) +{ + VO_SINK(base); + + return sink->delegate->render(buffer); +} + +GstFlowReturn QVideoSurfaceGstSink::render(GstBaseSink *base, GstBuffer *buffer) +{ + VO_SINK(base); + return sink->delegate->render(buffer); +} + diff --git a/src/plugins/mediaservices/gstreamer/qvideosurfacegstsink.h b/src/plugins/mediaservices/gstreamer/qvideosurfacegstsink.h new file mode 100644 index 0000000..99d7007 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qvideosurfacegstsink.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef VIDEOSURFACEGSTSINK_H +#define VIDEOSURFACEGSTSINK_H + +#include <gst/video/gstvideosink.h> + +#include <QtCore/qlist.h> +#include <QtCore/qmutex.h> +#include <QtCore/qpointer.h> +#include <QtCore/qwaitcondition.h> +#include <QtMultimedia/qvideosurfaceformat.h> +#include <QtMultimedia/qvideoframe.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QAbstractVideoSurface; + +class QVideoSurfaceGstDelegate : public QObject +{ + Q_OBJECT +public: + QVideoSurfaceGstDelegate(QAbstractVideoSurface *surface); + + QList<QVideoFrame::PixelFormat> supportedPixelFormats() const; + + bool start(const QVideoSurfaceFormat &format, int bytesPerLine); + void stop(); + + GstFlowReturn render(GstBuffer *buffer); + +private slots: + void queuedStart(); + void queuedStop(); + void queuedRender(); + + void supportedFormatsChanged(); + +private: + QPointer<QAbstractVideoSurface> m_surface; + QList<QVideoFrame::PixelFormat> m_supportedPixelFormats; + QMutex m_mutex; + QWaitCondition m_setupCondition; + QWaitCondition m_renderCondition; + QVideoSurfaceFormat m_format; + QVideoFrame m_frame; + GstFlowReturn m_renderReturn; + int m_bytesPerLine; + bool m_started; +}; + +class QVideoSurfaceGstSink +{ +public: + GstVideoSink parent; + + static QVideoSurfaceGstSink *createSink(QAbstractVideoSurface *surface); + +private: + static GType get_type(); + static void class_init(gpointer g_class, gpointer class_data); + static void base_init(gpointer g_class); + static void instance_init(GTypeInstance *instance, gpointer g_class); + + static void finalize(GObject *object); + + static GstStateChangeReturn change_state(GstElement *element, GstStateChange transition); + + static GstCaps *get_caps(GstBaseSink *sink); + static gboolean set_caps(GstBaseSink *sink, GstCaps *caps); + + static GstFlowReturn buffer_alloc( + GstBaseSink *sink, guint64 offset, guint size, GstCaps *caps, GstBuffer **buffer); + + static gboolean start(GstBaseSink *sink); + static gboolean stop(GstBaseSink *sink); + + static gboolean unlock(GstBaseSink *sink); + + static gboolean event(GstBaseSink *sink, GstEvent *event); + static GstFlowReturn preroll(GstBaseSink *sink, GstBuffer *buffer); + static GstFlowReturn render(GstBaseSink *sink, GstBuffer *buffer); + +private: + QVideoSurfaceGstDelegate *delegate; +}; + + +class QVideoSurfaceGstSinkClass +{ +public: + GstVideoSinkClass parent_class; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/gstreamer/qx11videosurface.cpp b/src/plugins/mediaservices/gstreamer/qx11videosurface.cpp new file mode 100644 index 0000000..56c1405 --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qx11videosurface.cpp @@ -0,0 +1,471 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui/qx11info_x11.h> +#include <QtMultimedia/qvideosurfaceformat.h> + +#include "qx11videosurface.h" + +struct XvFormatRgb +{ + QVideoFrame::PixelFormat pixelFormat; + int bits_per_pixel; + int format; + int num_planes; + + int depth; + unsigned int red_mask; + unsigned int green_mask; + unsigned int blue_mask; + +}; + +bool operator ==(const XvImageFormatValues &format, const XvFormatRgb &rgb) +{ + return format.type == XvRGB + && format.bits_per_pixel == rgb.bits_per_pixel + && format.format == rgb.format + && format.num_planes == rgb.num_planes + && format.depth == rgb.depth + && format.red_mask == rgb.red_mask + && format.blue_mask == rgb.blue_mask; +} + +static const XvFormatRgb qt_xvRgbLookup[] = +{ + { QVideoFrame::Format_ARGB32, 32, XvPacked, 1, 32, 0x00FF0000, 0x0000FF00, 0x000000FF }, + { QVideoFrame::Format_RGB32 , 32, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF }, + { QVideoFrame::Format_RGB24 , 24, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF }, + { QVideoFrame::Format_RGB565, 16, XvPacked, 1, 16, 0x0000F800, 0x000007E0, 0x0000001F }, + { QVideoFrame::Format_BGRA32, 32, XvPacked, 1, 32, 0xFF000000, 0x00FF0000, 0x0000FF00 }, + { QVideoFrame::Format_BGR32 , 32, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF }, + { QVideoFrame::Format_BGR24 , 24, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF }, + { QVideoFrame::Format_BGR565, 16, XvPacked, 1, 16, 0x0000F800, 0x000007E0, 0x0000001F } +}; + +struct XvFormatYuv +{ + QVideoFrame::PixelFormat pixelFormat; + int bits_per_pixel; + int format; + int num_planes; + + unsigned int y_sample_bits; + unsigned int u_sample_bits; + unsigned int v_sample_bits; + unsigned int horz_y_period; + unsigned int horz_u_period; + unsigned int horz_v_period; + unsigned int vert_y_period; + unsigned int vert_u_period; + unsigned int vert_v_period; + char component_order[32]; +}; + +bool operator ==(const XvImageFormatValues &format, const XvFormatYuv &yuv) +{ + return format.type == XvYUV + && format.bits_per_pixel == yuv.bits_per_pixel + && format.format == yuv.format + && format.num_planes == yuv.num_planes + && format.y_sample_bits == yuv.y_sample_bits + && format.u_sample_bits == yuv.u_sample_bits + && format.v_sample_bits == yuv.v_sample_bits + && format.horz_y_period == yuv.horz_y_period + && format.horz_u_period == yuv.horz_u_period + && format.horz_v_period == yuv.horz_v_period + && format.horz_y_period == yuv.vert_y_period + && format.vert_u_period == yuv.vert_u_period + && format.vert_v_period == yuv.vert_v_period + && qstrncmp(format.component_order, yuv.component_order, 32) == 0; +} + +static const XvFormatYuv qt_xvYuvLookup[] = +{ + { QVideoFrame::Format_YUV444 , 24, XvPacked, 1, 8, 8, 8, 1, 1, 1, 1, 1, 1, "YUV" }, + { QVideoFrame::Format_YUV420P, 12, XvPlanar, 3, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YUV" }, + { QVideoFrame::Format_YV12 , 12, XvPlanar, 3, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YVU" }, + { QVideoFrame::Format_UYVY , 16, XvPacked, 1, 8, 8, 8, 1, 2, 2, 1, 1, 1, "UYVY" }, + { QVideoFrame::Format_YUYV , 16, XvPacked, 1, 8, 8, 8, 1, 2, 2, 1, 1, 1, "YUYV" }, + { QVideoFrame::Format_NV12 , 12, XvPlanar, 2, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YUV" }, + { QVideoFrame::Format_NV12 , 12, XvPlanar, 2, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YVU" }, + { QVideoFrame::Format_Y8 , 8 , XvPlanar, 1, 8, 0, 0, 1, 0, 0, 1, 0, 0, "Y" } +}; + +QX11VideoSurface::QX11VideoSurface(QObject *parent) + : QAbstractVideoSurface(parent) + , m_winId(0) + , m_portId(0) + , m_gc(0) + , m_image(0) +{ +} + +QX11VideoSurface::~QX11VideoSurface() +{ + if (m_gc) + XFreeGC(QX11Info::display(), m_gc); + + if (m_portId != 0) + XvUngrabPort(QX11Info::display(), m_portId, 0); +} + +WId QX11VideoSurface::winId() const +{ + return m_winId; +} + +void QX11VideoSurface::setWinId(WId id) +{ + if (id == m_winId) + return; + + if (m_image) + XFree(m_image); + + if (m_gc) { + XFreeGC(QX11Info::display(), m_gc); + m_gc = 0; + } + + if (m_portId != 0) + XvUngrabPort(QX11Info::display(), m_portId, 0); + + m_supportedPixelFormats.clear(); + m_formatIds.clear(); + + m_winId = id; + + if (m_winId && findPort()) { + querySupportedFormats(); + + m_gc = XCreateGC(QX11Info::display(), m_winId, 0, 0); + + if (m_image) { + m_image = 0; + + if (!start(surfaceFormat())) + QAbstractVideoSurface::stop(); + } + } else if (m_image) { + m_image = 0; + + QAbstractVideoSurface::stop(); + } + + emit supportedFormatsChanged(); +} + +QRect QX11VideoSurface::displayRect() const +{ + return m_displayRect; +} + +void QX11VideoSurface::setDisplayRect(const QRect &rect) +{ + m_displayRect = rect; +} + +int QX11VideoSurface::brightness() const +{ + return getAttribute("XV_BRIGHTNESS", m_brightnessRange.first, m_brightnessRange.second); +} + +void QX11VideoSurface::setBrightness(int brightness) +{ + setAttribute("XV_BRIGHTNESS", brightness, m_brightnessRange.first, m_brightnessRange.second); +} + +int QX11VideoSurface::contrast() const +{ + return getAttribute("XV_CONTRAST", m_contrastRange.first, m_contrastRange.second); +} + +void QX11VideoSurface::setContrast(int contrast) +{ + setAttribute("XV_CONTRAST", contrast, m_contrastRange.first, m_contrastRange.second); +} + +int QX11VideoSurface::hue() const +{ + return getAttribute("XV_HUE", m_hueRange.first, m_hueRange.second); +} + +void QX11VideoSurface::setHue(int hue) +{ + setAttribute("XV_HUE", hue, m_hueRange.first, m_hueRange.second); +} + +int QX11VideoSurface::saturation() const +{ + return getAttribute("XV_SATURATION", m_saturationRange.first, m_saturationRange.second); +} + +void QX11VideoSurface::setSaturation(int saturation) +{ + setAttribute("XV_SATURATION", saturation, m_saturationRange.first, m_saturationRange.second); +} + +int QX11VideoSurface::getAttribute(const char *attribute, int minimum, int maximum) const +{ + if (m_portId != 0) { + Display *display = QX11Info::display(); + + Atom atom = XInternAtom(display, attribute, True); + + int value = 0; + + XvGetPortAttribute(display, m_portId, atom, &value); + + return redistribute(value, minimum, maximum, -100, 100); + } else { + return 0; + } +} + +void QX11VideoSurface::setAttribute(const char *attribute, int value, int minimum, int maximum) +{ + if (m_portId != 0) { + Display *display = QX11Info::display(); + + Atom atom = XInternAtom(display, attribute, True); + + XvSetPortAttribute( + display, m_portId, atom, redistribute(value, -100, 100, minimum, maximum)); + } +} + +int QX11VideoSurface::redistribute( + int value, int fromLower, int fromUpper, int toLower, int toUpper) +{ + return fromUpper != fromLower + ? ((value - fromLower) * (toUpper - toLower) / (fromUpper - fromLower)) + toLower + : 0; +} + +QList<QVideoFrame::PixelFormat> QX11VideoSurface::supportedPixelFormats( + QAbstractVideoBuffer::HandleType handleType) const +{ + return handleType == QAbstractVideoBuffer::NoHandle + ? m_supportedPixelFormats + : QList<QVideoFrame::PixelFormat>(); +} + +bool QX11VideoSurface::start(const QVideoSurfaceFormat &format) +{ + if (m_image) + XFree(m_image); + + int xvFormatId = 0; + for (int i = 0; i < m_supportedPixelFormats.count(); ++i) { + if (m_supportedPixelFormats.at(i) == format.pixelFormat()) { + xvFormatId = m_formatIds.at(i); + break; + } + } + + if (xvFormatId == 0) { + setError(UnsupportedFormatError); + } else { + XvImage *image = XvCreateImage( + QX11Info::display(), + m_portId, + xvFormatId, + 0, + format.frameWidth(), + format.frameHeight()); + + if (!image) { + setError(ResourceError); + } else { + m_viewport = format.viewport(); + m_image = image; + + return QAbstractVideoSurface::start(format); + } + } + + if (m_image) { + m_image = 0; + + QAbstractVideoSurface::stop(); + } + + return false; +} + +void QX11VideoSurface::stop() +{ + if (m_image) { + XFree(m_image); + m_image = 0; + + QAbstractVideoSurface::stop(); + } +} + +bool QX11VideoSurface::present(const QVideoFrame &frame) +{ + if (!m_image) { + setError(StoppedError); + return false; + } else if (m_image->width != frame.width() || m_image->height != frame.height()) { + setError(IncorrectFormatError); + return false; + } else { + QVideoFrame frameCopy(frame); + + if (!frameCopy.map(QAbstractVideoBuffer::ReadOnly)) { + setError(IncorrectFormatError); + return false; + } else { + bool presented = false; + + if (m_image->data_size > frame.mappedBytes()) { + qWarning("Insufficient frame buffer size"); + setError(IncorrectFormatError); + } else if (m_image->num_planes > 0 && m_image->pitches[0] != frame.bytesPerLine()) { + qWarning("Incompatible frame pitches"); + setError(IncorrectFormatError); + } else { + m_image->data = reinterpret_cast<char *>(frameCopy.bits()); + + XvPutImage( + QX11Info::display(), + m_portId, + m_winId, + m_gc, + m_image, + m_viewport.x(), + m_viewport.y(), + m_viewport.width(), + m_viewport.height(), + m_displayRect.x(), + m_displayRect.y(), + m_displayRect.width(), + m_displayRect.height()); + + m_image->data = 0; + + presented = true; + } + + frameCopy.unmap(); + + return presented; + } + } +} + +bool QX11VideoSurface::findPort() +{ + unsigned int count = 0; + XvAdaptorInfo *adaptors = 0; + bool portFound = false; + + if (XvQueryAdaptors(QX11Info::display(), m_winId, &count, &adaptors) == Success) { + for (unsigned int i = 0; i < count && !portFound; ++i) { + if (adaptors[i].type & XvImageMask) { + m_portId = adaptors[i].base_id; + + for (unsigned int j = 0; j < adaptors[i].num_ports && !portFound; ++j, ++m_portId) + portFound = XvGrabPort(QX11Info::display(), m_portId, 0) == Success; + } + } + XvFreeAdaptorInfo(adaptors); + } + + return portFound; +} + +void QX11VideoSurface::querySupportedFormats() +{ + int count = 0; + if (XvImageFormatValues *imageFormats = XvListImageFormats( + QX11Info::display(), m_portId, &count)) { + const int rgbCount = sizeof(qt_xvRgbLookup) / sizeof(XvFormatRgb); + const int yuvCount = sizeof(qt_xvYuvLookup) / sizeof(XvFormatYuv); + + for (int i = 0; i < count; ++i) { + switch (imageFormats[i].type) { + case XvRGB: + for (int j = 0; j < rgbCount; ++j) { + if (imageFormats[i] == qt_xvRgbLookup[j]) { + m_supportedPixelFormats.append(qt_xvRgbLookup[j].pixelFormat); + m_formatIds.append(imageFormats[i].id); + break; + } + } + break; + case XvYUV: + for (int j = 0; j < yuvCount; ++j) { + if (imageFormats[i] == qt_xvYuvLookup[j]) { + m_supportedPixelFormats.append(qt_xvYuvLookup[j].pixelFormat); + m_formatIds.append(imageFormats[i].id); + break; + } + } + break; + } + } + XFree(imageFormats); + } + + m_brightnessRange = qMakePair(0, 0); + m_contrastRange = qMakePair(0, 0); + m_hueRange = qMakePair(0, 0); + m_saturationRange = qMakePair(0, 0); + + if (XvAttribute *attributes = XvQueryPortAttributes(QX11Info::display(), m_portId, &count)) { + for (int i = 0; i < count; ++i) { + if (qstrcmp(attributes[i].name, "XV_BRIGHTNESS") == 0) + m_brightnessRange = qMakePair(attributes[i].min_value, attributes[i].max_value); + else if (qstrcmp(attributes[i].name, "XV_CONTRAST") == 0) + m_contrastRange = qMakePair(attributes[i].min_value, attributes[i].max_value); + else if (qstrcmp(attributes[i].name, "XV_HUE") == 0) + m_hueRange = qMakePair(attributes[i].min_value, attributes[i].max_value); + else if (qstrcmp(attributes[i].name, "XV_SATURATION") == 0) + m_saturationRange = qMakePair(attributes[i].min_value, attributes[i].max_value); + } + + XFree(attributes); + } +} diff --git a/src/plugins/mediaservices/gstreamer/qx11videosurface.h b/src/plugins/mediaservices/gstreamer/qx11videosurface.h new file mode 100644 index 0000000..e69517a --- /dev/null +++ b/src/plugins/mediaservices/gstreamer/qx11videosurface.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QX11VIDEOSURFACE_H +#define QX11VIDEOSURFACE_H + +#include <QtGui/qwidget.h> +#include <QtMultimedia/qabstractvideosurface.h> + +#include <X11/Xlib.h> +#include <X11/extensions/Xv.h> +#include <X11/extensions/Xvlib.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QX11VideoSurface : public QAbstractVideoSurface +{ + Q_OBJECT +public: + QX11VideoSurface(QObject *parent = 0); + ~QX11VideoSurface(); + + WId winId() const; + void setWinId(WId id); + + QRect displayRect() const; + void setDisplayRect(const QRect &rect); + + int brightness() const; + void setBrightness(int brightness); + + int contrast() const; + void setContrast(int contrast); + + int hue() const; + void setHue(int hue); + + int saturation() const; + void setSaturation(int saturation); + + QList<QVideoFrame::PixelFormat> supportedPixelFormats( + QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const; + + bool start(const QVideoSurfaceFormat &format); + void stop(); + + bool present(const QVideoFrame &frame); + +private: + WId m_winId; + XvPortID m_portId; + GC m_gc; + XvImage *m_image; + QList<QVideoFrame::PixelFormat> m_supportedPixelFormats; + QVector<int> m_formatIds; + QRect m_viewport; + QRect m_displayRect; + QPair<int, int> m_brightnessRange; + QPair<int, int> m_contrastRange; + QPair<int, int> m_hueRange; + QPair<int, int> m_saturationRange; + + bool findPort(); + void querySupportedFormats(); + + int getAttribute(const char *attribute, int minimum, int maximum) const; + void setAttribute(const char *attribute, int value, int minimum, int maximum); + + static int redistribute(int value, int fromLower, int fromUpper, int toLower, int toUpper); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/mediaservices/mediaservices.pro b/src/plugins/mediaservices/mediaservices.pro index 03859bd..879d914 100644 --- a/src/plugins/mediaservices/mediaservices.pro +++ b/src/plugins/mediaservices/mediaservices.pro @@ -2,4 +2,15 @@ TEMPLATE = subdirs mac:SUBDIRS += qt7 +unix:!mac:!symbian { + TMP_GST_LIBS = \ + gstreamer-0.10 >= 0.10.19 \ + gstreamer-base-0.10 >= 0.10.19 \ + gstreamer-interfaces-0.10 >= 0.10.19 \ + gstreamer-audio-0.10 >= 0.10.19 \ + gstreamer-video-0.10 >= 0.10.19 + system(pkg-config --exists \'$${TMP_GST_LIBS}\' --print-errors): { + SUBDIRS += gstreamer + } +} |