summaryrefslogtreecommitdiffstats
path: root/src/plugins/gstreamer/mediacapture
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/gstreamer/mediacapture')
-rw-r--r--src/plugins/gstreamer/mediacapture/mediacapture.pri27
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreameraudioencode.cpp292
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreameraudioencode.h97
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.cpp185
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.h98
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.cpp198
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.h75
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp185
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h96
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamercapturesession.cpp1051
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamercapturesession.h211
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.cpp98
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.h73
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamerimageencode.cpp90
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamerimageencode.h80
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.cpp135
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.h84
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp289
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.h91
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.cpp297
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.h83
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamervideoencode.cpp331
-rw-r--r--src/plugins/gstreamer/mediacapture/qgstreamervideoencode.h98
23 files changed, 4264 insertions, 0 deletions
diff --git a/src/plugins/gstreamer/mediacapture/mediacapture.pri b/src/plugins/gstreamer/mediacapture/mediacapture.pri
new file mode 100644
index 000000000..b7f7794f9
--- /dev/null
+++ b/src/plugins/gstreamer/mediacapture/mediacapture.pri
@@ -0,0 +1,27 @@
+INCLUDEPATH += $$PWD
+
+DEFINES += QMEDIA_GSTREAMER_CAPTURE
+
+HEADERS += $$PWD/qgstreamercaptureservice.h \
+ $$PWD/qgstreamercapturesession.h \
+ $$PWD/qgstreameraudioencode.h \
+ $$PWD/qgstreamervideoencode.h \
+ $$PWD/qgstreamerrecordercontrol.h \
+ $$PWD/qgstreamermediacontainercontrol.h \
+ $$PWD/qgstreamercameracontrol.h \
+ $$PWD/qgstreamerv4l2input.h \
+ $$PWD/qgstreamercapturemetadatacontrol.h \
+ $$PWD/qgstreamerimagecapturecontrol.h \
+ $$PWD/qgstreamerimageencode.h
+
+SOURCES += $$PWD/qgstreamercaptureservice.cpp \
+ $$PWD/qgstreamercapturesession.cpp \
+ $$PWD/qgstreameraudioencode.cpp \
+ $$PWD/qgstreamervideoencode.cpp \
+ $$PWD/qgstreamerrecordercontrol.cpp \
+ $$PWD/qgstreamermediacontainercontrol.cpp \
+ $$PWD/qgstreamercameracontrol.cpp \
+ $$PWD/qgstreamerv4l2input.cpp \
+ $$PWD/qgstreamercapturemetadatacontrol.cpp \
+ $$PWD/qgstreamerimagecapturecontrol.cpp \
+ $$PWD/qgstreamerimageencode.cpp
diff --git a/src/plugins/gstreamer/mediacapture/qgstreameraudioencode.cpp b/src/plugins/gstreamer/mediacapture/qgstreameraudioencode.cpp
new file mode 100644
index 000000000..e548393eb
--- /dev/null
+++ b/src/plugins/gstreamer/mediacapture/qgstreameraudioencode.cpp
@@ -0,0 +1,292 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 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 "qgstreameraudioencode.h"
+#include "qgstreamercapturesession.h"
+#include "qgstreamermediacontainercontrol.h"
+
+#include <QtCore/qdebug.h>
+
+#include <math.h>
+
+QGstreamerAudioEncode::QGstreamerAudioEncode(QObject *parent)
+ :QAudioEncoderControl(parent)
+{
+ QList<QByteArray> codecCandidates;
+
+#if defined(Q_WS_MAEMO_5)
+ codecCandidates << "audio/PCM"; //<< "audio/AMR" << "audio/AMR-WB" << "audio/speex";
+#elif defined(Q_WS_MAEMO_6)
+ codecCandidates << "audio/AAC" << "audio/mpeg" << "audio/vorbis" << "audio/speex" << "audio/GSM"
+ << "audio/PCM" << "audio/AMR" << "audio/AMR-WB" << "audio/FLAC";
+#else
+ codecCandidates << "audio/mpeg" << "audio/vorbis" << "audio/speex" << "audio/GSM"
+ << "audio/PCM" << "audio/AMR" << "audio/AMR-WB" << "audio/FLAC";
+#endif
+
+#if defined(Q_WS_MAEMO_5) || defined(Q_WS_MAEMO_6)
+ m_elementNames["audio/AMR"] = "nokiaamrnbenc";
+ m_elementNames["audio/AMR-WB"] = "nokiaamrwbenc";
+ m_elementNames["audio/AAC"] = "nokiaaacenc";
+#else
+ m_elementNames["audio/mpeg"] = "lamemp3enc";
+ m_elementNames["audio/AMR"] = "amrnbenc";
+ m_elementNames["audio/AMR-WB"] = "amrwbenc";
+#endif
+
+ m_elementNames["audio/vorbis"] = "vorbisenc";
+ m_elementNames["audio/speex"] = "speexenc";
+ m_elementNames["audio/PCM"] = "audioresample";
+ m_elementNames["audio/FLAC"] = "flacenc";
+ m_elementNames["audio/GSM"] = "gsmenc";
+
+ m_codecOptions["audio/vorbis"] = QStringList() << "min-bitrate" << "max-bitrate";
+ m_codecOptions["audio/mpeg"] = QStringList() << "mode";
+ m_codecOptions["audio/speex"] = QStringList() << "mode" << "vbr" << "vad" << "dtx";
+ m_codecOptions["audio/GSM"] = QStringList();
+ m_codecOptions["audio/PCM"] = QStringList();
+ m_codecOptions["audio/AMR"] = QStringList();
+ m_codecOptions["audio/AMR-WB"] = QStringList();
+
+ foreach( const QByteArray& codecName, codecCandidates ) {
+ QByteArray elementName = m_elementNames[codecName];
+ GstElementFactory *factory = gst_element_factory_find(elementName.constData());
+
+ if (factory) {
+ m_codecs.append(codecName);
+ const gchar *descr = gst_element_factory_get_description(factory);
+
+ if (codecName == QByteArray("audio/PCM"))
+ m_codecDescriptions.insert(codecName, tr("Raw PCM audio"));
+ else
+ m_codecDescriptions.insert(codecName, QString::fromUtf8(descr));
+
+ m_streamTypes.insert(codecName,
+ QGstreamerMediaContainerControl::supportedStreamTypes(factory, GST_PAD_SRC));
+
+ gst_object_unref(GST_OBJECT(factory));
+ }
+ }
+
+ //if (!m_codecs.isEmpty())
+ // m_audioSettings.setCodec(m_codecs[0]);
+}
+
+QGstreamerAudioEncode::~QGstreamerAudioEncode()
+{
+}
+
+QStringList QGstreamerAudioEncode::supportedAudioCodecs() const
+{
+ return m_codecs;
+}
+
+QString QGstreamerAudioEncode::codecDescription(const QString &codecName) const
+{
+ return m_codecDescriptions.value(codecName);
+}
+
+QStringList QGstreamerAudioEncode::supportedEncodingOptions(const QString &codec) const
+{
+ return m_codecOptions.value(codec);
+}
+
+QVariant QGstreamerAudioEncode::encodingOption(
+ const QString &codec, const QString &name) const
+{
+ return m_options[codec].value(name);
+}
+
+void QGstreamerAudioEncode::setEncodingOption(
+ const QString &codec, const QString &name, const QVariant &value)
+{
+ m_options[codec][name] = value;
+}
+
+QList<int> QGstreamerAudioEncode::supportedSampleRates(const QAudioEncoderSettings &, bool *) const
+{
+ //TODO check element caps to find actual values
+
+ return QList<int>();
+}
+
+QAudioEncoderSettings QGstreamerAudioEncode::audioSettings() const
+{
+ return m_audioSettings;
+}
+
+void QGstreamerAudioEncode::setAudioSettings(const QAudioEncoderSettings &settings)
+{
+ m_audioSettings = settings;
+}
+
+
+GstElement *QGstreamerAudioEncode::createEncoder()
+{
+ QString codec = m_audioSettings.codec();
+ GstElement *encoderElement = gst_element_factory_make(m_elementNames.value(codec).constData(), NULL);
+ if (!encoderElement)
+ return 0;
+
+ GstBin * encoderBin = GST_BIN(gst_bin_new("audio-encoder-bin"));
+
+ GstElement *capsFilter = gst_element_factory_make("capsfilter", NULL);
+
+ gst_bin_add(encoderBin, capsFilter);
+ gst_bin_add(encoderBin, encoderElement);
+ gst_element_link(capsFilter, encoderElement);
+
+ // add ghostpads
+ GstPad *pad = gst_element_get_static_pad(capsFilter, "sink");
+ gst_element_add_pad(GST_ELEMENT(encoderBin), gst_ghost_pad_new("sink", pad));
+ gst_object_unref(GST_OBJECT(pad));
+
+ pad = gst_element_get_static_pad(encoderElement, "src");
+ gst_element_add_pad(GST_ELEMENT(encoderBin), gst_ghost_pad_new("src", pad));
+ gst_object_unref(GST_OBJECT(pad));
+
+ if (m_audioSettings.sampleRate() > 0 || m_audioSettings.channelCount() > 0) {
+ GstCaps *caps = gst_caps_new_empty();
+ GstStructure *structure = gst_structure_new("audio/x-raw-int", NULL);
+
+ if (m_audioSettings.sampleRate() > 0)
+ gst_structure_set(structure, "rate", G_TYPE_INT, m_audioSettings.sampleRate(), NULL );
+
+ if (m_audioSettings.channelCount() > 0)
+ gst_structure_set(structure, "channels", G_TYPE_INT, m_audioSettings.channelCount(), NULL );
+
+ gst_caps_append_structure(caps,structure);
+
+ //qDebug() << "set caps filter:" << gst_caps_to_string(caps);
+
+ g_object_set(G_OBJECT(capsFilter), "caps", caps, NULL);
+ }
+
+ if (encoderElement) {
+ if (m_audioSettings.encodingMode() == QtMultimediaKit::ConstantQualityEncoding) {
+ QtMultimediaKit::EncodingQuality qualityValue = m_audioSettings.quality();
+
+ if (codec == QLatin1String("audio/vorbis")) {
+ double qualityTable[] = {
+ 0.1, //VeryLow
+ 0.3, //Low
+ 0.5, //Normal
+ 0.7, //High
+ 1.0 //VeryHigh
+ };
+ g_object_set(G_OBJECT(encoderElement), "quality", qualityTable[qualityValue], NULL);
+ } else if (codec == QLatin1String("audio/mpeg")) {
+ g_object_set(G_OBJECT(encoderElement), "target", 0, NULL); //constant quality mode
+ qreal quality[] = {
+ 1, //VeryLow
+ 3, //Low
+ 5, //Normal
+ 7, //High
+ 9 //VeryHigh
+ };
+ g_object_set(G_OBJECT(encoderElement), "quality", quality[qualityValue], NULL);
+ } else if (codec == QLatin1String("audio/speex")) {
+ //0-10 range with default 8
+ double qualityTable[] = {
+ 2, //VeryLow
+ 5, //Low
+ 8, //Normal
+ 9, //High
+ 10 //VeryHigh
+ };
+ g_object_set(G_OBJECT(encoderElement), "quality", qualityTable[qualityValue], NULL);
+ } else if (codec.startsWith("audio/AMR")) {
+ int band[] = {
+ 0, //VeryLow
+ 2, //Low
+ 4, //Normal
+ 6, //High
+ 7 //VeryHigh
+ };
+
+ g_object_set(G_OBJECT(encoderElement), "band-mode", band[qualityValue], NULL);
+ }
+ } else {
+ int bitrate = m_audioSettings.bitRate();
+ if (bitrate > 0) {
+ if (codec == QLatin1String("audio/mpeg")) {
+ g_object_set(G_OBJECT(encoderElement), "target", 1, NULL); //constant bitrate mode
+ }
+ g_object_set(G_OBJECT(encoderElement), "bitrate", bitrate, NULL);
+ }
+ }
+
+ QMap<QString, QVariant> options = m_options.value(codec);
+ QMapIterator<QString,QVariant> it(options);
+ while (it.hasNext()) {
+ it.next();
+ QString option = it.key();
+ QVariant value = it.value();
+
+ switch (value.type()) {
+ case QVariant::Int:
+ g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toInt(), NULL);
+ break;
+ case QVariant::Bool:
+ g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toBool(), NULL);
+ break;
+ case QVariant::Double:
+ g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toDouble(), NULL);
+ break;
+ case QVariant::String:
+ g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toString().toUtf8().constData(), NULL);
+ break;
+ default:
+ qWarning() << "unsupported option type:" << option << value;
+ break;
+ }
+
+ }
+ }
+
+ return GST_ELEMENT(encoderBin);
+}
+
+
+QSet<QString> QGstreamerAudioEncode::supportedStreamTypes(const QString &codecName) const
+{
+ return m_streamTypes.value(codecName);
+}
diff --git a/src/plugins/gstreamer/mediacapture/qgstreameraudioencode.h b/src/plugins/gstreamer/mediacapture/qgstreameraudioencode.h
new file mode 100644
index 000000000..e2c48234d
--- /dev/null
+++ b/src/plugins/gstreamer/mediacapture/qgstreameraudioencode.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 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 QGSTREAMERAUDIOENCODE_H
+#define QGSTREAMERAUDIOENCODE_H
+
+#include <qaudioencodercontrol.h>
+class QGstreamerCaptureSession;
+
+#include <QtCore/qstringlist.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qset.h>
+
+#include <gst/gst.h>
+
+#include <qaudioformat.h>
+
+QT_USE_NAMESPACE
+
+class QGstreamerAudioEncode : public QAudioEncoderControl
+{
+ Q_OBJECT
+public:
+ QGstreamerAudioEncode(QObject *parent);
+ virtual ~QGstreamerAudioEncode();
+
+ QStringList supportedAudioCodecs() const;
+ QString codecDescription(const QString &codecName) const;
+
+ QStringList supportedEncodingOptions(const QString &codec) const;
+ QVariant encodingOption(const QString &codec, const QString &name) const;
+ void setEncodingOption(const QString &codec, const QString &name, const QVariant &value);
+
+ QList<int> supportedSampleRates(const QAudioEncoderSettings &settings = QAudioEncoderSettings(),
+ bool *isContinuous = 0) const;
+ QList<int> supportedChannelCounts(const QAudioEncoderSettings &settings = QAudioEncoderSettings()) const;
+ QList<int> supportedSampleSizes(const QAudioEncoderSettings &settings = QAudioEncoderSettings()) const;
+
+ QAudioEncoderSettings audioSettings() const;
+ void setAudioSettings(const QAudioEncoderSettings&);
+
+ GstElement *createEncoder();
+
+ QSet<QString> supportedStreamTypes(const QString &codecName) const;
+
+private:
+ QStringList m_codecs;
+ QMap<QString,QByteArray> m_elementNames;
+ QMap<QString,QString> m_codecDescriptions;
+ QMap<QString,QStringList> m_codecOptions;
+
+ QMap<QString, QMap<QString, QVariant> > m_options;
+
+ QMap<QString, QSet<QString> > m_streamTypes;
+
+ QAudioEncoderSettings m_audioSettings;
+};
+
+#endif
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.cpp b/src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.cpp
new file mode 100644
index 000000000..927a827dd
--- /dev/null
+++ b/src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.cpp
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** 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 "qgstreamercameracontrol.h"
+#include "qgstreamerimageencode.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qfile.h>
+
+
+QGstreamerCameraControl::QGstreamerCameraControl(QGstreamerCaptureSession *session)
+ :QCameraControl(session),
+ m_captureMode(QCamera::CaptureStillImage),
+ m_session(session),
+ m_state(QCamera::UnloadedState),
+ m_status(QCamera::UnloadedStatus),
+ m_reloadPending(false)
+
+{
+ connect(m_session, SIGNAL(stateChanged(QGstreamerCaptureSession::State)),
+ this, SLOT(updateStatus()));
+
+ connect(m_session->imageEncodeControl(), SIGNAL(settingsChanged()),
+ SLOT(reloadLater()));
+ connect(m_session, SIGNAL(viewfinderChanged()),
+ SLOT(reloadLater()));
+ connect(m_session, SIGNAL(readyChanged(bool)),
+ SLOT(reloadLater()));
+}
+
+QGstreamerCameraControl::~QGstreamerCameraControl()
+{
+}
+
+void QGstreamerCameraControl::setCaptureMode(QCamera::CaptureMode mode)
+{
+ if (m_captureMode == mode)
+ return;
+
+ switch (mode) {
+ case QCamera::CaptureStillImage:
+ m_session->setCaptureMode(QGstreamerCaptureSession::Image);
+ break;
+ case QCamera::CaptureVideo:
+ m_session->setCaptureMode(QGstreamerCaptureSession::AudioAndVideo);
+ break;
+ }
+
+ emit captureModeChanged(mode);
+ updateStatus();
+ reloadLater();
+}
+
+void QGstreamerCameraControl::setState(QCamera::State state)
+{
+ if (m_state == state)
+ return;
+
+ m_state = state;
+ switch (state) {
+ case QCamera::UnloadedState:
+ case QCamera::LoadedState:
+ m_session->setState(QGstreamerCaptureSession::StoppedState);
+ break;
+ case QCamera::ActiveState:
+ //postpone changing to Active if the session is nor ready yet
+ if (m_session->isReady()) {
+ m_session->setState(QGstreamerCaptureSession::PreviewState);
+ } else {
+#ifdef CAMEABIN_DEBUG
+ qDebug() << "Camera session is not ready yet, postpone activating";
+#endif
+ }
+ break;
+ default:
+ emit error(QCamera::NotSupportedFeatureError, tr("State not supported."));
+ }
+
+ updateStatus();
+ emit stateChanged(m_state);
+}
+
+QCamera::State QGstreamerCameraControl::state() const
+{
+ return m_state;
+}
+
+void QGstreamerCameraControl::updateStatus()
+{
+ QCamera::Status oldStatus = m_status;
+
+ switch (m_state) {
+ case QCamera::UnloadedState:
+ m_status = QCamera::UnloadedStatus;
+ break;
+ case QCamera::LoadedState:
+ m_status = QCamera::LoadedStatus;
+ break;
+ case QCamera::ActiveState:
+ if (m_session->state() == QGstreamerCaptureSession::StoppedState)
+ m_status = QCamera::StartingStatus;
+ else
+ m_status = QCamera::ActiveStatus;
+ break;
+ }
+
+ if (oldStatus != m_status) {
+ //qDebug() << "Status changed:" << m_status;
+ emit statusChanged(m_status);
+ }
+}
+
+void QGstreamerCameraControl::reloadLater()
+{
+ //qDebug() << "reload pipeline requested";
+ if (!m_reloadPending && m_state == QCamera::ActiveState) {
+ m_reloadPending = true;
+ m_session->setState(QGstreamerCaptureSession::StoppedState);
+ QMetaObject::invokeMethod(this, "reloadPipeline", Qt::QueuedConnection);
+ }
+}
+
+void QGstreamerCameraControl::reloadPipeline()
+{
+ //qDebug() << "reload pipeline";
+ if (m_reloadPending) {
+ m_reloadPending = false;
+ if (m_state == QCamera::ActiveState && m_session->isReady()) {
+ m_session->setState(QGstreamerCaptureSession::PreviewState);
+ }
+ }
+}
+
+bool QGstreamerCameraControl::canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const
+{
+ Q_UNUSED(status);
+
+ switch (changeType) {
+ case QCameraControl::CaptureMode:
+ case QCameraControl::ImageEncodingSettings:
+ case QCameraControl::VideoEncodingSettings:
+ case QCameraControl::Viewfinder:
+ return true;
+ default:
+ return false;
+ }
+}
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.h b/src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.h
new file mode 100644
index 000000000..c07ffcd19
--- /dev/null
+++ b/src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** 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 QGSTREAMERCAMERACONTROL_H
+#define QGSTREAMERCAMERACONTROL_H
+
+#include <QHash>
+#include <qcameracontrol.h>
+#include "qgstreamercapturesession.h"
+
+QT_USE_NAMESPACE
+QT_USE_NAMESPACE
+
+class QGstreamerCameraControl : public QCameraControl
+{
+ Q_OBJECT
+public:
+ QGstreamerCameraControl( QGstreamerCaptureSession *session );
+ virtual ~QGstreamerCameraControl();
+
+ bool isValid() const { return true; }
+
+ QCamera::State state() const;
+ void setState(QCamera::State state);
+
+ QCamera::Status status() const { return m_status; }
+
+ QCamera::CaptureMode captureMode() const { return m_captureMode; }
+ void setCaptureMode(QCamera::CaptureMode mode);
+
+ bool isCaptureModeSupported(QCamera::CaptureMode mode) const
+ {
+ return mode == QCamera::CaptureStillImage || mode == QCamera::CaptureVideo;
+ }
+
+ QCamera::LockTypes supportedLocks() const
+ {
+ return QCamera::NoLock;
+ }
+
+ bool canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const;
+
+public slots:
+ void reloadLater();
+
+private slots:
+ void updateStatus();
+ void reloadPipeline();
+
+
+private:
+ QCamera::CaptureMode m_captureMode;
+ QGstreamerCaptureSession *m_session;
+ QCamera::State m_state;
+ QCamera::Status m_status;
+ bool m_reloadPending;
+};
+
+#endif // QGSTREAMERCAMERACONTROL_H
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.cpp b/src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.cpp
new file mode 100644
index 000000000..b0e589dc8
--- /dev/null
+++ b/src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.cpp
@@ -0,0 +1,198 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 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 "qgstreamercapturemetadatacontrol.h"
+
+#include <gst/gst.h>
+#include <gst/gstversion.h>
+
+struct QGstreamerMetaDataKeyLookup
+{
+ QtMultimediaKit::MetaData key;
+ const char *token;
+};
+
+static const QGstreamerMetaDataKeyLookup qt_gstreamerMetaDataKeys[] =
+{
+ { QtMultimediaKit::Title, GST_TAG_TITLE },
+ //{ QtMultimediaKit::SubTitle, 0 },
+ //{ QtMultimediaKit::Author, 0 },
+ { QtMultimediaKit::Comment, GST_TAG_COMMENT },
+ { QtMultimediaKit::Description, GST_TAG_DESCRIPTION },
+ //{ QtMultimediaKit::Category, 0 },
+ { QtMultimediaKit::Genre, GST_TAG_GENRE },
+ //{ QtMultimediaKit::Year, 0 },
+ //{ QtMultimediaKit::UserRating, 0 },
+
+ { QtMultimediaKit::Language, GST_TAG_LANGUAGE_CODE },
+
+ { QtMultimediaKit::Publisher, GST_TAG_ORGANIZATION },
+ { QtMultimediaKit::Copyright, GST_TAG_COPYRIGHT },
+ //{ QtMultimediaKit::ParentalRating, 0 },
+ //{ QtMultimediaKit::RatingOrganisation, 0 },
+
+ // Media
+ //{ QtMultimediaKit::Size, 0 },
+ //{ QtMultimediaKit::MediaType, 0 },
+ { QtMultimediaKit::Duration, GST_TAG_DURATION },
+
+ // Audio
+ { QtMultimediaKit::AudioBitRate, GST_TAG_BITRATE },
+ { QtMultimediaKit::AudioCodec, GST_TAG_AUDIO_CODEC },
+ //{ QtMultimediaKit::ChannelCount, 0 },
+ //{ QtMultimediaKit::SampleRate, 0 },
+
+ // Music
+ { QtMultimediaKit::AlbumTitle, GST_TAG_ALBUM },
+ { QtMultimediaKit::AlbumArtist, GST_TAG_ARTIST},
+ { QtMultimediaKit::ContributingArtist, GST_TAG_PERFORMER },
+#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 19)
+ { QtMultimediaKit::Composer, GST_TAG_COMPOSER },
+#endif
+ //{ QtMultimediaKit::Conductor, 0 },
+ //{ QtMultimediaKit::Lyrics, 0 },
+ //{ QtMultimediaKit::Mood, 0 },
+ { QtMultimediaKit::TrackNumber, GST_TAG_TRACK_NUMBER },
+
+ //{ QtMultimediaKit::CoverArtUrlSmall, 0 },
+ //{ QtMultimediaKit::CoverArtUrlLarge, 0 },
+
+ // Image/Video
+ //{ QtMultimediaKit::Resolution, 0 },
+ //{ QtMultimediaKit::PixelAspectRatio, 0 },
+
+ // Video
+ //{ QtMultimediaKit::VideoFrameRate, 0 },
+ //{ QtMultimediaKit::VideoBitRate, 0 },
+ { QtMultimediaKit::VideoCodec, GST_TAG_VIDEO_CODEC },
+
+ //{ QtMultimediaKit::PosterUrl, 0 },
+
+ // Movie
+ //{ QtMultimediaKit::ChapterNumber, 0 },
+ //{ QtMultimediaKit::Director, 0 },
+ { QtMultimediaKit::LeadPerformer, GST_TAG_PERFORMER },
+ //{ QtMultimediaKit::Writer, 0 },
+
+ // Photos
+ //{ QtMultimediaKit::CameraManufacturer, 0 },
+ //{ QtMultimediaKit::CameraModel, 0 },
+ //{ QtMultimediaKit::Event, 0 },
+ //{ QtMultimediaKit::Subject, 0 }
+};
+
+QGstreamerCaptureMetaDataControl::QGstreamerCaptureMetaDataControl(QObject *parent)
+ :QMetaDataWriterControl(parent)
+{
+}
+
+QVariant QGstreamerCaptureMetaDataControl::metaData(QtMultimediaKit::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) {
+ const char *name = qt_gstreamerMetaDataKeys[i].token;
+
+ return m_values.value(QByteArray::fromRawData(name, qstrlen(name)));
+ }
+ }
+ return QVariant();
+}
+
+void QGstreamerCaptureMetaDataControl::setMetaData(QtMultimediaKit::MetaData key, const QVariant &value)
+{
+ static const int count = sizeof(qt_gstreamerMetaDataKeys) / sizeof(QGstreamerMetaDataKeyLookup);
+
+ for (int i = 0; i < count; ++i) {
+ if (qt_gstreamerMetaDataKeys[i].key == key) {
+ const char *name = qt_gstreamerMetaDataKeys[i].token;
+
+ m_values.insert(QByteArray::fromRawData(name, qstrlen(name)), value);
+
+ emit QMetaDataWriterControl::metaDataChanged();
+ emit metaDataChanged(m_values);
+
+ return;
+ }
+ }
+}
+
+QList<QtMultimediaKit::MetaData> QGstreamerCaptureMetaDataControl::availableMetaData() const
+{
+ static QMap<QByteArray, QtMultimediaKit::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<QtMultimediaKit::MetaData> res;
+ foreach (const QByteArray &key, m_values.keys()) {
+ QtMultimediaKit::MetaData tag = keysMap.value(key, QtMultimediaKit::MetaData(-1));
+ if (tag != -1)
+ res.append(tag);
+ }
+
+ return res;
+}
+
+QVariant QGstreamerCaptureMetaDataControl::extendedMetaData(QString const &name) const
+{
+ return m_values.value(name.toLatin1());
+}
+
+void QGstreamerCaptureMetaDataControl::setExtendedMetaData(QString const &name, QVariant const &value)
+{
+ m_values.insert(name.toLatin1(), value);
+ emit QMetaDataWriterControl::metaDataChanged();
+ emit metaDataChanged(m_values);
+}
+
+QStringList QGstreamerCaptureMetaDataControl::availableExtendedMetaData() const
+{
+ QStringList res;
+ foreach (const QByteArray &key, m_values.keys())
+ res.append(QString(key));
+
+ return res;
+}
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.h b/src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.h
new file mode 100644
index 000000000..e859da486
--- /dev/null
+++ b/src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 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 QGSTREAMERCAPTUREMETADATACONTROL_H
+#define QGSTREAMERCAPTUREMETADATACONTROL_H
+
+#include <qmetadatawritercontrol.h>
+
+QT_USE_NAMESPACE
+
+class QGstreamerCaptureMetaDataControl : public QMetaDataWriterControl
+{
+ Q_OBJECT
+public:
+ QGstreamerCaptureMetaDataControl(QObject *parent);
+ virtual ~QGstreamerCaptureMetaDataControl() {};
+
+
+ bool isMetaDataAvailable() const { return true; }
+ bool isWritable() const { return true; }
+
+ QVariant metaData(QtMultimediaKit::MetaData key) const;
+ void setMetaData(QtMultimediaKit::MetaData key, const QVariant &value);
+ QList<QtMultimediaKit::MetaData> availableMetaData() const;
+
+ QVariant extendedMetaData(QString const &name) const;
+ void setExtendedMetaData(QString const &name, QVariant const &value);
+ QStringList availableExtendedMetaData() const;
+
+Q_SIGNALS:
+ void metaDataChanged(const QMap<QByteArray, QVariant>&);
+
+private:
+ QMap<QByteArray, QVariant> m_values;
+};
+
+#endif // QGSTREAMERCAPTUREMETADATACONTROL_H
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp
new file mode 100644
index 000000000..97310d199
--- /dev/null
+++ b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 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 "qgstreamercaptureservice.h"
+#include "qgstreamercapturesession.h"
+#include "qgstreamerrecordercontrol.h"
+#include "qgstreamermediacontainercontrol.h"
+#include "qgstreameraudioencode.h"
+#include "qgstreamervideoencode.h"
+#include "qgstreamerimageencode.h"
+#include "qgstreamerbushelper.h"
+#include "qgstreamercameracontrol.h"
+#include "qgstreamerv4l2input.h"
+#include "qgstreamercapturemetadatacontrol.h"
+
+#include "qgstreameraudioinputendpointselector.h"
+#include "qgstreamervideoinputdevicecontrol.h"
+#include "qgstreamerimagecapturecontrol.h"
+
+#include "qgstreamervideooverlay.h"
+#include "qgstreamervideorenderer.h"
+
+#include "qgstreamervideowidget.h"
+
+#include <qmediaserviceprovider.h>
+
+
+QGstreamerCaptureService::QGstreamerCaptureService(const QString &service, QObject *parent):
+ QMediaService(parent)
+{
+ m_captureSession = 0;
+ m_cameraControl = 0;
+ m_metaDataControl = 0;
+
+ m_videoInput = 0;
+ m_audioInputEndpointSelector = 0;
+ m_videoInputDevice = 0;
+
+ m_videoOutput = 0;
+ m_videoRenderer = 0;
+ m_videoWindow = 0;
+ m_videoWidgetControl = 0;
+ m_imageCaptureControl = 0;
+
+ if (service == Q_MEDIASERVICE_AUDIOSOURCE) {
+ m_captureSession = new QGstreamerCaptureSession(QGstreamerCaptureSession::Audio, this);
+ }
+
+ if (service == Q_MEDIASERVICE_CAMERA) {
+ m_captureSession = new QGstreamerCaptureSession(QGstreamerCaptureSession::AudioAndVideo, this);
+ m_cameraControl = new QGstreamerCameraControl(m_captureSession);
+ m_videoInput = new QGstreamerV4L2Input(this);
+ m_captureSession->setVideoInput(m_videoInput);
+ m_videoInputDevice = new QGstreamerVideoInputDeviceControl(this);
+
+ connect(m_videoInputDevice, SIGNAL(selectedDeviceChanged(QString)),
+ m_videoInput, SLOT(setDevice(QString)));
+
+ if (m_videoInputDevice->deviceCount())
+ m_videoInput->setDevice(m_videoInputDevice->deviceName(m_videoInputDevice->selectedDevice()));
+
+ m_videoRenderer = new QGstreamerVideoRenderer(this);
+
+#if defined(Q_WS_X11) && !defined(QT_NO_XVIDEO)
+ m_videoWindow = new QGstreamerVideoOverlay(this);
+ m_videoWidgetControl = new QGstreamerVideoWidgetControl(this);
+#endif
+ m_imageCaptureControl = new QGstreamerImageCaptureControl(m_captureSession);
+ }
+
+ m_audioInputEndpointSelector = new QGstreamerAudioInputEndpointSelector(this);
+ connect(m_audioInputEndpointSelector, SIGNAL(activeEndpointChanged(QString)), m_captureSession, SLOT(setCaptureDevice(QString)));
+
+ if (m_captureSession && m_audioInputEndpointSelector->availableEndpoints().size() > 0)
+ m_captureSession->setCaptureDevice(m_audioInputEndpointSelector->defaultEndpoint());
+
+ m_metaDataControl = new QGstreamerCaptureMetaDataControl(this);
+ connect(m_metaDataControl, SIGNAL(metaDataChanged(QMap<QByteArray,QVariant>)),
+ m_captureSession, SLOT(setMetaData(QMap<QByteArray,QVariant>)));
+}
+
+QGstreamerCaptureService::~QGstreamerCaptureService()
+{
+}
+
+QMediaControl *QGstreamerCaptureService::requestControl(const char *name)
+{
+ if (!m_captureSession)
+ return 0;
+
+ if (qstrcmp(name,QAudioEndpointSelector_iid) == 0)
+ return m_audioInputEndpointSelector;
+
+ if (qstrcmp(name,QVideoDeviceControl_iid) == 0)
+ return m_videoInputDevice;
+
+ if (qstrcmp(name,QMediaRecorderControl_iid) == 0)
+ return m_captureSession->recorderControl();
+
+ if (qstrcmp(name,QAudioEncoderControl_iid) == 0)
+ return m_captureSession->audioEncodeControl();
+
+ if (qstrcmp(name,QVideoEncoderControl_iid) == 0)
+ return m_captureSession->videoEncodeControl();
+
+ if (qstrcmp(name,QImageEncoderControl_iid) == 0)
+ return m_captureSession->imageEncodeControl();
+
+
+ if (qstrcmp(name,QMediaContainerControl_iid) == 0)
+ return m_captureSession->mediaContainerControl();
+
+ if (qstrcmp(name,QCameraControl_iid) == 0)
+ return m_cameraControl;
+
+ if (qstrcmp(name,QMetaDataWriterControl_iid) == 0)
+ return m_metaDataControl;
+
+ if (qstrcmp(name, QCameraImageCaptureControl_iid) == 0)
+ return m_imageCaptureControl;
+
+ if (!m_videoOutput) {
+ if (qstrcmp(name, QVideoRendererControl_iid) == 0) {
+ m_videoOutput = m_videoRenderer;
+ m_captureSession->setVideoPreview(m_videoRenderer);
+ } else if (qstrcmp(name, QVideoWindowControl_iid) == 0) {
+ m_videoOutput = m_videoWindow;
+ m_captureSession->setVideoPreview(m_videoWindow);
+ } else if (qstrcmp(name, QVideoWidgetControl_iid) == 0) {
+ m_captureSession->setVideoPreview(m_videoWidgetControl);
+ m_videoOutput = m_videoWidgetControl;
+ }
+
+ if (m_videoOutput)
+ return m_videoOutput;
+ }
+
+ return 0;
+}
+
+void QGstreamerCaptureService::releaseControl(QMediaControl *control)
+{
+ if (control && control == m_videoOutput) {
+ m_videoOutput = 0;
+ m_captureSession->setVideoPreview(0);
+ }
+}
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h
new file mode 100644
index 000000000..09b275c35
--- /dev/null
+++ b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 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 QGSTREAMERCAPTURESERVICE_H
+#define QGSTREAMERCAPTURESERVICE_H
+
+#include <qmediaservice.h>
+#include <qmediacontrol.h>
+
+#include <gst/gst.h>
+QT_BEGIN_NAMESPACE
+class QAudioEndpointSelector;
+class QVideoDeviceControl;
+QT_END_NAMESPACE
+
+class QGstreamerCaptureSession;
+class QGstreamerCameraControl;
+class QGstreamerMessage;
+class QGstreamerBusHelper;
+class QGstreamerVideoRenderer;
+class QGstreamerVideoOverlay;
+class QGstreamerVideoWidgetControl;
+class QGstreamerElementFactory;
+class QGstreamerCaptureMetaDataControl;
+class QGstreamerImageCaptureControl;
+class QGstreamerV4L2Input;
+
+class QGstreamerCaptureService : public QMediaService
+{
+ Q_OBJECT
+
+public:
+ QGstreamerCaptureService(const QString &service, QObject *parent = 0);
+ virtual ~QGstreamerCaptureService();
+
+ QMediaControl *requestControl(const char *name);
+ void releaseControl(QMediaControl *);
+
+private:
+ void setAudioPreview(GstElement*);
+
+ QGstreamerCaptureSession *m_captureSession;
+ QGstreamerCameraControl *m_cameraControl;
+ QGstreamerV4L2Input *m_videoInput;
+ QGstreamerCaptureMetaDataControl *m_metaDataControl;
+
+ QAudioEndpointSelector *m_audioInputEndpointSelector;
+ QVideoDeviceControl *m_videoInputDevice;
+
+ QMediaControl *m_videoOutput;
+
+ QGstreamerVideoRenderer *m_videoRenderer;
+ QMediaControl *m_videoWindow;
+ QMediaControl *m_videoWidgetControl;
+ QGstreamerImageCaptureControl *m_imageCaptureControl;
+};
+
+#endif // QGSTREAMERCAPTURESERVICE_H
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.cpp b/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.cpp
new file mode 100644
index 000000000..f8e73c7af
--- /dev/null
+++ b/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.cpp
@@ -0,0 +1,1051 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 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 "qgstreamercapturesession.h"
+#include "qgstreamerrecordercontrol.h"
+#include "qgstreamermediacontainercontrol.h"
+#include "qgstreamervideorendererinterface.h"
+#include "qgstreameraudioencode.h"
+#include "qgstreamervideoencode.h"
+#include "qgstreamerimageencode.h"
+#include "qgstreamerbushelper.h"
+#include <qmediarecorder.h>
+
+#include <gst/gsttagsetter.h>
+#include <gst/gstversion.h>
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qset.h>
+#include <QCoreApplication>
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qfile.h>
+
+#include <QtGui/qimage.h>
+
+#define gstRef(element) { gst_object_ref(GST_OBJECT(element)); gst_object_sink(GST_OBJECT(element)); }
+#define gstUnref(element) { if (element) { gst_object_unref(GST_OBJECT(element)); element = 0; } }
+
+QGstreamerCaptureSession::QGstreamerCaptureSession(QGstreamerCaptureSession::CaptureMode captureMode, QObject *parent)
+ :QObject(parent),
+ m_state(StoppedState),
+ m_pendingState(StoppedState),
+ m_waitingForEos(false),
+ m_pipelineMode(EmptyPipeline),
+ m_captureMode(captureMode),
+ m_audioInputFactory(0),
+ m_audioPreviewFactory(0),
+ m_videoInputFactory(0),
+ m_viewfinder(0),
+ m_viewfinderInterface(0),
+ m_audioSrc(0),
+ m_audioTee(0),
+ m_audioPreviewQueue(0),
+ m_audioPreview(0),
+ m_audioVolume(0),
+ m_muted(false),
+ m_videoSrc(0),
+ m_videoTee(0),
+ m_videoPreviewQueue(0),
+ m_videoPreview(0),
+ m_imageCaptureBin(0),
+ m_encodeBin(0),
+ m_passImage(false),
+ m_passPrerollImage(false)
+{
+ m_pipeline = gst_pipeline_new("media-capture-pipeline");
+ gstRef(m_pipeline);
+
+ m_bus = gst_element_get_bus(m_pipeline);
+ m_busHelper = new QGstreamerBusHelper(m_bus, this);
+ m_busHelper->installSyncEventFilter(this);
+ connect(m_busHelper, SIGNAL(message(QGstreamerMessage)), SLOT(busMessage(QGstreamerMessage)));
+ m_audioEncodeControl = new QGstreamerAudioEncode(this);
+ m_videoEncodeControl = new QGstreamerVideoEncode(this);
+ m_imageEncodeControl = new QGstreamerImageEncode(this);
+ m_recorderControl = new QGstreamerRecorderControl(this);
+ m_mediaContainerControl = new QGstreamerMediaContainerControl(this);
+
+ setState(StoppedState);
+}
+
+QGstreamerCaptureSession::~QGstreamerCaptureSession()
+{
+ setState(StoppedState);
+ gst_element_set_state(m_pipeline, GST_STATE_NULL);
+ gst_object_unref(GST_OBJECT(m_pipeline));
+}
+
+void QGstreamerCaptureSession::setCaptureMode(CaptureMode mode)
+{
+ m_captureMode = mode;
+}
+
+GstElement *QGstreamerCaptureSession::buildEncodeBin()
+{
+ GstElement *encodeBin = gst_bin_new("encode-bin");
+
+ GstElement *muxer = gst_element_factory_make( m_mediaContainerControl->formatElementName().constData(), "muxer");
+ if (!muxer) {
+ qWarning() << "Could not create a media muxer element:" << m_mediaContainerControl->formatElementName();
+ gst_object_unref(encodeBin);
+ return 0;
+ }
+
+ GstElement *fileSink = gst_element_factory_make("filesink", "filesink");
+ g_object_set(G_OBJECT(fileSink), "location", m_sink.toString().toLocal8Bit().constData(), NULL);
+ gst_bin_add_many(GST_BIN(encodeBin), muxer, fileSink, NULL);
+
+ if (!gst_element_link(muxer, fileSink)) {
+ gst_object_unref(encodeBin);
+ return 0;
+ }
+
+ if (m_captureMode & Audio) {
+ GstElement *audioConvert = gst_element_factory_make("audioconvert", "audioconvert");
+ GstElement *audioQueue = gst_element_factory_make("queue", "audio-encode-queue");
+ m_audioVolume = gst_element_factory_make("volume", "volume");
+ gst_bin_add_many(GST_BIN(encodeBin), audioConvert, audioQueue, m_audioVolume, NULL);
+
+ GstElement *audioEncoder = m_audioEncodeControl->createEncoder();
+ if (!audioEncoder) {
+ gst_object_unref(encodeBin);
+ qWarning() << "Could not create an audio encoder element:" << m_audioEncodeControl->audioSettings().codec();
+ return 0;
+ }
+
+ gst_bin_add(GST_BIN(encodeBin), audioEncoder);
+
+ if (!gst_element_link_many(audioConvert, audioQueue, m_audioVolume, audioEncoder, muxer, NULL)) {
+ gst_object_unref(encodeBin);
+ return 0;
+ }
+
+ g_object_set(G_OBJECT(m_audioVolume), "volume", (m_muted ? 0.0 : 1.0), NULL);
+
+ // add ghostpads
+ GstPad *pad = gst_element_get_static_pad(audioConvert, "sink");
+ gst_element_add_pad(GST_ELEMENT(encodeBin), gst_ghost_pad_new("audiosink", pad));
+ gst_object_unref(GST_OBJECT(pad));
+ }
+
+ if (m_captureMode & Video) {
+ GstElement *videoQueue = gst_element_factory_make("queue", "video-encode-queue");
+ GstElement *colorspace = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace-encoder");
+ GstElement *videoscale = gst_element_factory_make("videoscale","videoscale-encoder");
+ gst_bin_add_many(GST_BIN(encodeBin), videoQueue, colorspace, videoscale, NULL);
+
+ GstElement *videoEncoder = m_videoEncodeControl->createEncoder();
+ if (!videoEncoder) {
+ gst_object_unref(encodeBin);
+ qWarning() << "Could not create a video encoder element:" << m_videoEncodeControl->videoSettings().codec();
+ return 0;
+ }
+
+ gst_bin_add(GST_BIN(encodeBin), videoEncoder);
+
+ if (!gst_element_link_many(videoQueue, colorspace, videoscale, videoEncoder, muxer, NULL)) {
+ gst_object_unref(encodeBin);
+ return 0;
+ }
+
+ // add ghostpads
+ GstPad *pad = gst_element_get_static_pad(videoQueue, "sink");
+ gst_element_add_pad(GST_ELEMENT(encodeBin), gst_ghost_pad_new("videosink", pad));
+ gst_object_unref(GST_OBJECT(pad));
+ }
+
+ return encodeBin;
+}
+
+GstElement *QGstreamerCaptureSession::buildAudioSrc()
+{
+ GstElement *audioSrc = 0;
+ if (m_audioInputFactory)
+ audioSrc = m_audioInputFactory->buildElement();
+ else {
+
+#if defined(Q_WS_MAEMO_5) || defined(Q_WS_MAEMO_6)
+ audioSrc = gst_element_factory_make("pulsesrc", "audio_src");
+#elif defined(QT_QWS_N810)
+ audioSrc = gst_element_factory_make("dsppcmsrc", "audio_src");
+#else
+ QString elementName = "alsasrc";
+ QString device;
+
+ if (m_captureDevice.startsWith("alsa:")) {
+ device = m_captureDevice.mid(QString("alsa:").length());
+ } else if (m_captureDevice.startsWith("oss:")) {
+ elementName = "osssrc";
+ device = m_captureDevice.mid(QString("oss:").length());
+ } else if (m_captureDevice.startsWith("pulseaudio:")) {
+ elementName = "pulsesrc";
+ } else {
+ elementName = "autoaudiosrc";
+ }
+
+ audioSrc = gst_element_factory_make(elementName.toAscii().constData(), "audio_src");
+ if (audioSrc && !device.isEmpty())
+ g_object_set(G_OBJECT(audioSrc), "device", device.toLocal8Bit().constData(), NULL);
+#endif
+ }
+
+ if (!audioSrc) {
+ emit error(int(QMediaRecorder::ResourceError), tr("Could not create an audio source element"));
+ audioSrc = gst_element_factory_make("fakesrc", NULL);
+ }
+
+ return audioSrc;
+}
+
+GstElement *QGstreamerCaptureSession::buildAudioPreview()
+{
+ GstElement *previewElement = 0;
+
+ if (m_audioPreviewFactory) {
+ previewElement = m_audioPreviewFactory->buildElement();
+ } else {
+
+
+#if 1
+ previewElement = gst_element_factory_make("fakesink", "audio-preview");
+#else
+ GstElement *bin = gst_bin_new("audio-preview-bin");
+ GstElement *visual = gst_element_factory_make("libvisual_lv_scope", "audio-preview");
+ GstElement *sink = gst_element_factory_make("ximagesink", NULL);
+ gst_bin_add_many(GST_BIN(bin), visual, sink, NULL);
+ gst_element_link_many(visual,sink, NULL);
+
+
+ // add ghostpads
+ GstPad *pad = gst_element_get_static_pad(visual, "sink");
+ Q_ASSERT(pad);
+ gst_element_add_pad(GST_ELEMENT(bin), gst_ghost_pad_new("audiosink", pad));
+ gst_object_unref(GST_OBJECT(pad));
+
+ previewElement = bin;
+#endif
+ }
+
+ return previewElement;
+}
+
+GstElement *QGstreamerCaptureSession::buildVideoSrc()
+{
+ GstElement *videoSrc = 0;
+ if (m_videoInputFactory) {
+ videoSrc = m_videoInputFactory->buildElement();
+ } else {
+ videoSrc = gst_element_factory_make("videotestsrc", "video_test_src");
+ //videoSrc = gst_element_factory_make("v4l2src", "video_test_src");
+ }
+
+ return videoSrc;
+}
+
+GstElement *QGstreamerCaptureSession::buildVideoPreview()
+{
+ GstElement *previewElement = 0;
+
+ if (m_viewfinderInterface) {
+ GstElement *bin = gst_bin_new("video-preview-bin");
+ GstElement *colorspace = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace-preview");
+ GstElement *capsFilter = gst_element_factory_make("capsfilter", "capsfilter-video-preview");
+ GstElement *preview = m_viewfinderInterface->videoSink();
+
+ gst_bin_add_many(GST_BIN(bin), colorspace, capsFilter, preview, NULL);
+ gst_element_link(colorspace,capsFilter);
+ gst_element_link(capsFilter,preview);
+
+ QSize resolution;
+ qreal frameRate = 0;
+
+ if (m_captureMode & Video) {
+ QVideoEncoderSettings videoSettings = m_videoEncodeControl->videoSettings();
+ resolution = videoSettings.resolution();
+ frameRate = videoSettings.frameRate();
+ } else if (m_captureMode & Image) {
+ resolution = m_imageEncodeControl->imageSettings().resolution();
+ }
+
+ if (!resolution.isEmpty() || frameRate > 0.001) {
+ GstCaps *caps = gst_caps_new_empty();
+ QStringList structureTypes;
+ structureTypes << "video/x-raw-yuv" << "video/x-raw-rgb";
+
+ foreach(const QString &structureType, structureTypes) {
+ GstStructure *structure = gst_structure_new(structureType.toAscii().constData(), NULL);
+
+ if (!resolution.isEmpty()) {
+ gst_structure_set(structure, "width", G_TYPE_INT, resolution.width(), NULL);
+ gst_structure_set(structure, "height", G_TYPE_INT, resolution.height(), NULL);
+ }
+
+ if (frameRate > 0.001) {
+ QPair<int,int> rate = m_videoEncodeControl->rateAsRational();
+
+ //qDebug() << "frame rate:" << num << denum;
+
+ gst_structure_set(structure, "framerate", GST_TYPE_FRACTION, rate.first, rate.second, NULL);
+ }
+
+ gst_caps_append_structure(caps,structure);
+ }
+
+ //qDebug() << "set video preview caps filter:" << gst_caps_to_string(caps);
+
+ g_object_set(G_OBJECT(capsFilter), "caps", caps, NULL);
+
+ }
+
+ // add ghostpads
+ GstPad *pad = gst_element_get_static_pad(colorspace, "sink");
+ Q_ASSERT(pad);
+ gst_element_add_pad(GST_ELEMENT(bin), gst_ghost_pad_new("videosink", pad));
+ gst_object_unref(GST_OBJECT(pad));
+
+ previewElement = bin;
+ } else {
+#if 1
+ previewElement = gst_element_factory_make("fakesink", "video-preview");
+#else
+ GstElement *bin = gst_bin_new("video-preview-bin");
+ GstElement *colorspace = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace-preview");
+ GstElement *preview = gst_element_factory_make("ximagesink", "video-preview");
+ gst_bin_add_many(GST_BIN(bin), colorspace, preview, NULL);
+ gst_element_link(colorspace,preview);
+
+ // add ghostpads
+ GstPad *pad = gst_element_get_static_pad(colorspace, "sink");
+ Q_ASSERT(pad);
+ gst_element_add_pad(GST_ELEMENT(bin), gst_ghost_pad_new("videosink", pad));
+ gst_object_unref(GST_OBJECT(pad));
+
+ previewElement = bin;
+#endif
+ }
+
+ return previewElement;
+}
+
+
+static gboolean passImageFilter(GstElement *element,
+ GstBuffer *buffer,
+ void *appdata)
+{
+ Q_UNUSED(element);
+ Q_UNUSED(buffer);
+
+ QGstreamerCaptureSession *session = (QGstreamerCaptureSession *)appdata;
+ if (session->m_passImage || session->m_passPrerollImage) {
+ session->m_passImage = false;
+
+ if (session->m_passPrerollImage) {
+ session->m_passPrerollImage = false;
+ return TRUE;
+ }
+ session->m_passPrerollImage = false;
+
+ QImage img;
+
+ GstCaps *caps = gst_buffer_get_caps(buffer);
+ if (caps) {
+ GstStructure *structure = gst_caps_get_structure (caps, 0);
+ gint width = 0;
+ gint height = 0;
+
+ if (structure &&
+ gst_structure_get_int(structure, "width", &width) &&
+ gst_structure_get_int(structure, "height", &height) &&
+ width > 0 && height > 0) {
+ if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-yuv") == 0) {
+ guint32 fourcc = 0;
+ gst_structure_get_fourcc(structure, "format", &fourcc);
+
+ if (fourcc == GST_MAKE_FOURCC('I','4','2','0')) {
+ img = QImage(width/2, height/2, QImage::Format_RGB32);
+
+ const uchar *data = (const uchar *)buffer->data;
+
+ for (int y=0; y<height; y+=2) {
+ const uchar *yLine = data + y*width;
+ const uchar *uLine = data + width*height + y*width/4;
+ const uchar *vLine = data + width*height*5/4 + y*width/4;
+
+ for (int x=0; x<width; x+=2) {
+ const qreal Y = 1.164*(yLine[x]-16);
+ const int U = uLine[x/2]-128;
+ const int V = vLine[x/2]-128;
+
+ int b = qBound(0, int(Y + 2.018*U), 255);
+ int g = qBound(0, int(Y - 0.813*V - 0.391*U), 255);
+ int r = qBound(0, int(Y + 1.596*V), 255);
+
+ img.setPixel(x/2,y/2,qRgb(r,g,b));
+ }
+ }
+ }
+
+ } else if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-rgb") == 0) {
+ QImage::Format format = QImage::Format_Invalid;
+ int bpp = 0;
+ gst_structure_get_int(structure, "bpp", &bpp);
+
+ if (bpp == 24)
+ format = QImage::Format_RGB888;
+ else if (bpp == 32)
+ format = QImage::Format_RGB32;
+
+ if (format != QImage::Format_Invalid) {
+ img = QImage((const uchar *)buffer->data,
+ width,
+ height,
+ format);
+ img.bits(); //detach
+ }
+ }
+ }
+ gst_caps_unref(caps);
+ }
+
+ static int exposedSignalIndex = session->metaObject()->indexOfSignal("imageExposed(int)");
+ session->metaObject()->method(exposedSignalIndex).invoke(session,
+ Qt::QueuedConnection,
+ Q_ARG(int,session->m_imageRequestId));
+
+ static int capturedSignalIndex = session->metaObject()->indexOfSignal("imageCaptured(int,QImage)");
+ session->metaObject()->method(capturedSignalIndex).invoke(session,
+ Qt::QueuedConnection,
+ Q_ARG(int,session->m_imageRequestId),
+ Q_ARG(QImage,img));
+
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+static gboolean saveImageFilter(GstElement *element,
+ GstBuffer *buffer,
+ GstPad *pad,
+ void *appdata)
+{
+ Q_UNUSED(element);
+ Q_UNUSED(pad);
+ QGstreamerCaptureSession *session = (QGstreamerCaptureSession *)appdata;
+
+ QString fileName = session->m_imageFileName;
+
+ if (!fileName.isEmpty()) {
+ QFile f(fileName);
+ if (f.open(QFile::WriteOnly)) {
+ f.write((const char *)buffer->data, buffer->size);
+ f.close();
+
+ static int signalIndex = session->metaObject()->indexOfSignal("imageSaved(int,QString)");
+ session->metaObject()->method(signalIndex).invoke(session,
+ Qt::QueuedConnection,
+ Q_ARG(int,session->m_imageRequestId),
+ Q_ARG(QString,fileName));
+ }
+ }
+
+ return TRUE;
+}
+
+GstElement *QGstreamerCaptureSession::buildImageCapture()
+{
+ GstElement *bin = gst_bin_new("image-capture-bin");
+ GstElement *queue = gst_element_factory_make("queue", "queue-image-capture");
+ GstElement *colorspace = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace-image-capture");
+ GstElement *encoder = gst_element_factory_make("jpegenc", "image-encoder");
+ GstElement *sink = gst_element_factory_make("fakesink","sink-image-capture");
+
+ GstPad *pad = gst_element_get_static_pad(queue, "src");
+ Q_ASSERT(pad);
+ gst_pad_add_buffer_probe(pad, G_CALLBACK(passImageFilter), this);
+
+ g_object_set(G_OBJECT(sink), "signal-handoffs", TRUE, NULL);
+ g_signal_connect(G_OBJECT(sink), "handoff",
+ G_CALLBACK(saveImageFilter), this);
+
+ gst_bin_add_many(GST_BIN(bin), queue, colorspace, encoder, sink, NULL);
+ gst_element_link_many(queue, colorspace, encoder, sink, NULL);
+
+ // add ghostpads
+ pad = gst_element_get_static_pad(queue, "sink");
+ Q_ASSERT(pad);
+ gst_element_add_pad(GST_ELEMENT(bin), gst_ghost_pad_new("imagesink", pad));
+ gst_object_unref(GST_OBJECT(pad));
+
+ m_passImage = false;
+ m_passPrerollImage = true;
+ m_imageFileName = QString();
+
+ return bin;
+}
+
+void QGstreamerCaptureSession::captureImage(int requestId, const QString &fileName)
+{
+ m_imageRequestId = requestId;
+ m_imageFileName = fileName;
+ m_passImage = true;
+}
+
+
+#define REMOVE_ELEMENT(element) { if (element) {gst_bin_remove(GST_BIN(m_pipeline), element); element = 0;} }
+
+bool QGstreamerCaptureSession::rebuildGraph(QGstreamerCaptureSession::PipelineMode newMode)
+{
+ REMOVE_ELEMENT(m_audioSrc);
+ REMOVE_ELEMENT(m_audioPreview);
+ REMOVE_ELEMENT(m_audioPreviewQueue);
+ REMOVE_ELEMENT(m_audioTee);
+ REMOVE_ELEMENT(m_videoSrc);
+ REMOVE_ELEMENT(m_videoPreview);
+ REMOVE_ELEMENT(m_videoPreviewQueue);
+ REMOVE_ELEMENT(m_videoTee);
+ REMOVE_ELEMENT(m_encodeBin);
+ REMOVE_ELEMENT(m_imageCaptureBin);
+ m_audioVolume = 0;
+
+ bool ok = true;
+
+ switch (newMode) {
+ case EmptyPipeline:
+ break;
+ case PreviewPipeline:
+ if (m_captureMode & Audio) {
+ m_audioSrc = buildAudioSrc();
+ m_audioPreview = buildAudioPreview();
+
+ ok &= m_audioSrc && m_audioPreview;
+
+ if (ok) {
+ gst_bin_add_many(GST_BIN(m_pipeline), m_audioSrc, m_audioPreview, NULL);
+ ok &= gst_element_link(m_audioSrc, m_audioPreview);
+ }
+ }
+ if (m_captureMode & Video || m_captureMode & Image) {
+ m_videoSrc = buildVideoSrc();
+ m_videoTee = gst_element_factory_make("tee", "video-preview-tee");
+ m_videoPreviewQueue = gst_element_factory_make("queue", "video-preview-queue");
+ m_videoPreview = buildVideoPreview();
+ m_imageCaptureBin = buildImageCapture();
+
+ ok &= m_videoSrc && m_videoTee && m_videoPreviewQueue && m_videoPreview && m_imageCaptureBin;
+
+ if (ok) {
+ gst_bin_add_many(GST_BIN(m_pipeline), m_videoSrc, m_videoTee,
+ m_videoPreviewQueue, m_videoPreview,
+ m_imageCaptureBin, NULL);
+
+ ok &= gst_element_link(m_videoSrc, m_videoTee);
+ ok &= gst_element_link(m_videoTee, m_videoPreviewQueue);
+ ok &= gst_element_link(m_videoPreviewQueue, m_videoPreview);
+ ok &= gst_element_link(m_videoTee, m_imageCaptureBin);
+ }
+ }
+ break;
+ case RecordingPipeline:
+ m_encodeBin = buildEncodeBin();
+ gst_bin_add(GST_BIN(m_pipeline), m_encodeBin);
+
+ if (m_captureMode & Audio) {
+ m_audioSrc = buildAudioSrc();
+ ok &= m_audioSrc != 0;
+
+ gst_bin_add(GST_BIN(m_pipeline), m_audioSrc);
+ ok &= gst_element_link(m_audioSrc, m_encodeBin);
+ }
+
+ if (m_captureMode & Video) {
+ m_videoSrc = buildVideoSrc();
+ ok &= m_videoSrc != 0;
+
+ gst_bin_add(GST_BIN(m_pipeline), m_videoSrc);
+ ok &= gst_element_link(m_videoSrc, m_encodeBin);
+ }
+
+ if (!m_metaData.isEmpty())
+ setMetaData(m_metaData);
+
+ break;
+ case PreviewAndRecordingPipeline:
+ m_encodeBin = buildEncodeBin();
+ if (m_encodeBin)
+ gst_bin_add(GST_BIN(m_pipeline), m_encodeBin);
+
+ ok &= m_encodeBin != 0;
+
+ if (ok && m_captureMode & Audio) {
+ m_audioSrc = buildAudioSrc();
+ m_audioPreview = buildAudioPreview();
+ m_audioTee = gst_element_factory_make("tee", NULL);
+ m_audioPreviewQueue = gst_element_factory_make("queue", NULL);
+
+ ok &= m_audioSrc && m_audioPreview && m_audioTee && m_audioPreviewQueue;
+
+ if (ok) {
+ gst_bin_add_many(GST_BIN(m_pipeline), m_audioSrc, m_audioTee,
+ m_audioPreviewQueue, m_audioPreview, NULL);
+ ok &= gst_element_link(m_audioSrc, m_audioTee);
+ ok &= gst_element_link(m_audioTee, m_audioPreviewQueue);
+ ok &= gst_element_link(m_audioPreviewQueue, m_audioPreview);
+ ok &= gst_element_link(m_audioTee, m_encodeBin);
+ }
+ }
+
+ if (ok && (m_captureMode & Video || m_captureMode & Image)) {
+ m_videoSrc = buildVideoSrc();
+ m_videoPreview = buildVideoPreview();
+ m_videoTee = gst_element_factory_make("tee", NULL);
+ m_videoPreviewQueue = gst_element_factory_make("queue", NULL);
+
+ ok &= m_videoSrc && m_videoPreview && m_videoTee && m_videoPreviewQueue;
+
+ if (ok) {
+ gst_bin_add_many(GST_BIN(m_pipeline), m_videoSrc, m_videoTee,
+ m_videoPreviewQueue, m_videoPreview, NULL);
+ ok &= gst_element_link(m_videoSrc, m_videoTee);
+ ok &= gst_element_link(m_videoTee, m_videoPreviewQueue);
+ ok &= gst_element_link(m_videoPreviewQueue, m_videoPreview);
+ }
+
+ if (ok && (m_captureMode & Video))
+ ok &= gst_element_link(m_videoTee, m_encodeBin);
+ }
+
+ if (!m_metaData.isEmpty())
+ setMetaData(m_metaData);
+
+
+ break;
+ }
+
+ if (!ok) {
+ emit error(int(QMediaRecorder::FormatError),tr("Failed to build media capture pipeline."));
+ }
+
+ dumpGraph( QString("rebuild_graph_%1_%2").arg(m_pipelineMode).arg(newMode) );
+ if (m_encodeBin) {
+ QString fileName = QString("rebuild_graph_encode_%1_%2").arg(m_pipelineMode).arg(newMode);
+#if !(GST_DISABLE_GST_DEBUG) && (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 19)
+ _gst_debug_bin_to_dot_file(GST_BIN(m_encodeBin), GST_DEBUG_GRAPH_SHOW_ALL, fileName.toAscii());
+#endif
+ }
+
+ if (ok) {
+ m_pipelineMode = newMode;
+ } else {
+ m_pipelineMode = EmptyPipeline;
+
+ REMOVE_ELEMENT(m_audioSrc);
+ REMOVE_ELEMENT(m_audioPreview);
+ REMOVE_ELEMENT(m_audioPreviewQueue);
+ REMOVE_ELEMENT(m_audioTee);
+ REMOVE_ELEMENT(m_videoSrc);
+ REMOVE_ELEMENT(m_videoPreview);
+ REMOVE_ELEMENT(m_videoPreviewQueue);
+ REMOVE_ELEMENT(m_videoTee);
+ REMOVE_ELEMENT(m_encodeBin);
+ }
+
+ return ok;
+}
+
+void QGstreamerCaptureSession::dumpGraph(const QString &fileName)
+{
+#if !(GST_DISABLE_GST_DEBUG) && (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 19)
+ _gst_debug_bin_to_dot_file(GST_BIN(m_pipeline),
+ GstDebugGraphDetails(/*GST_DEBUG_GRAPH_SHOW_ALL |*/ GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE | GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS | GST_DEBUG_GRAPH_SHOW_STATES),
+ fileName.toAscii());
+#endif
+}
+
+QUrl QGstreamerCaptureSession::outputLocation() const
+{
+ return m_sink;
+}
+
+bool QGstreamerCaptureSession::setOutputLocation(const QUrl& sink)
+{
+ m_sink = sink;
+ return true;
+}
+
+void QGstreamerCaptureSession::setAudioInput(QGstreamerElementFactory *audioInput)
+{
+ m_audioInputFactory = audioInput;
+}
+
+void QGstreamerCaptureSession::setAudioPreview(QGstreamerElementFactory *audioPreview)
+{
+ m_audioPreviewFactory = audioPreview;
+}
+
+void QGstreamerCaptureSession::setVideoInput(QGstreamerVideoInput *videoInput)
+{
+ m_videoInputFactory = videoInput;
+}
+
+void QGstreamerCaptureSession::setVideoPreview(QObject *viewfinder)
+{
+ m_viewfinderInterface = qobject_cast<QGstreamerVideoRendererInterface*>(viewfinder);
+ if (!m_viewfinderInterface)
+ viewfinder = 0;
+
+ if (m_viewfinder != viewfinder) {
+ bool oldReady = isReady();
+
+ if (m_viewfinder) {
+ disconnect(m_viewfinder, SIGNAL(sinkChanged()),
+ this, SIGNAL(viewfinderChanged()));
+ disconnect(m_viewfinder, SIGNAL(readyChanged(bool)),
+ this, SIGNAL(readyChanged(bool)));
+ }
+
+ m_viewfinder = viewfinder;
+ //m_viewfinderHasChanged = true;
+
+ if (m_viewfinder) {
+ connect(m_viewfinder, SIGNAL(sinkChanged()),
+ this, SIGNAL(viewfinderChanged()));
+ connect(m_viewfinder, SIGNAL(readyChanged(bool)),
+ this, SIGNAL(readyChanged(bool)));
+ }
+
+ emit viewfinderChanged();
+ if (oldReady != isReady())
+ emit readyChanged(isReady());
+ }
+}
+
+bool QGstreamerCaptureSession::isReady() const
+{
+ return m_viewfinderInterface != 0 && m_viewfinderInterface->isReady();
+}
+
+QGstreamerCaptureSession::State QGstreamerCaptureSession::state() const
+{
+ return m_state;
+}
+
+void QGstreamerCaptureSession::waitForStopped()
+{
+ GstState state = GST_STATE_PLAYING;
+ gst_element_get_state(m_pipeline, &state, 0, 0);
+
+ while (state != GST_STATE_NULL) {
+ qApp->processEvents();
+ gst_element_get_state(m_pipeline, &state, 0, 0);
+ }
+}
+
+void QGstreamerCaptureSession::setState(QGstreamerCaptureSession::State newState)
+{
+ if (newState == m_pendingState && !m_waitingForEos)
+ return;
+
+ m_pendingState = newState;
+
+ PipelineMode newMode = EmptyPipeline;
+
+ switch (newState) {
+ case PausedState:
+ case RecordingState:
+ newMode = PreviewAndRecordingPipeline;
+ break;
+ case PreviewState:
+ newMode = PreviewPipeline;
+ break;
+ case StoppedState:
+ newMode = EmptyPipeline;
+ break;
+ }
+
+ if (newMode != m_pipelineMode) {
+ if (m_pipelineMode == PreviewAndRecordingPipeline) {
+ if (!m_waitingForEos) {
+ m_waitingForEos = true;
+ //qDebug() << "Waiting for EOS";
+ //with live sources it's necessary to send EOS even to pipeline
+ //before going to STOPPED state
+ gst_element_send_event(m_pipeline, gst_event_new_eos());
+ // Unless gstreamer is in GST_STATE_PLAYING our EOS message will not be received.
+ gst_element_set_state(m_pipeline, GST_STATE_PLAYING);
+
+ return;
+ } else {
+ m_waitingForEos = false;
+ //qDebug() << "EOS received";
+ }
+ }
+
+ //select suitable default codecs/containers, if necessary
+ m_recorderControl->applySettings();
+
+ gst_element_set_state(m_pipeline, GST_STATE_NULL);
+
+ //It would be better to do this async. but
+ //gstreamer doesn't notify about pipeline went to NULL state
+ waitForStopped();
+ if (!rebuildGraph(newMode)) {
+ m_pendingState = StoppedState;
+ m_state = StoppedState;
+ emit stateChanged(StoppedState);
+
+ return;
+ }
+ }
+
+ switch (newState) {
+ case PausedState:
+ gst_element_set_state(m_pipeline, GST_STATE_PAUSED);
+ break;
+ case RecordingState:
+ case PreviewState:
+ gst_element_set_state(m_pipeline, GST_STATE_PLAYING);
+ break;
+ case StoppedState:
+ gst_element_set_state(m_pipeline, GST_STATE_NULL);
+ }
+
+ //we have to do it here, since gstreamer will not emit bus messages any more
+ if (newState == StoppedState) {
+ m_state = StoppedState;
+ emit stateChanged(StoppedState);
+ }
+}
+
+
+qint64 QGstreamerCaptureSession::duration() const
+{
+ GstFormat format = GST_FORMAT_TIME;
+ gint64 duration = 0;
+
+ if ( m_encodeBin && gst_element_query_position(m_encodeBin, &format, &duration))
+ return duration / 1000000;
+ else
+ return 0;
+}
+
+void QGstreamerCaptureSession::setCaptureDevice(const QString &deviceName)
+{
+ m_captureDevice = deviceName;
+}
+
+void QGstreamerCaptureSession::setMetaData(const QMap<QByteArray, QVariant> &data)
+{
+ //qDebug() << "QGstreamerCaptureSession::setMetaData" << data;
+ m_metaData = data;
+
+ if (m_encodeBin) {
+ GstIterator *elements = gst_bin_iterate_all_by_interface(GST_BIN(m_encodeBin), GST_TYPE_TAG_SETTER);
+ GstElement *element = 0;
+ while (gst_iterator_next(elements, (void**)&element) == GST_ITERATOR_OK) {
+ //qDebug() << "found element with tag setter interface:" << gst_element_get_name(element);
+ QMapIterator<QByteArray, QVariant> it(data);
+ while (it.hasNext()) {
+ it.next();
+ const QString tagName = it.key();
+ const QVariant tagValue = it.value();
+
+
+ switch(tagValue.type()) {
+ case QVariant::String:
+ gst_tag_setter_add_tags(GST_TAG_SETTER(element),
+ GST_TAG_MERGE_REPLACE_ALL,
+ tagName.toUtf8().constData(),
+ tagValue.toString().toUtf8().constData(),
+ NULL);
+ break;
+ case QVariant::Int:
+ case QVariant::LongLong:
+ gst_tag_setter_add_tags(GST_TAG_SETTER(element),
+ GST_TAG_MERGE_REPLACE_ALL,
+ tagName.toUtf8().constData(),
+ tagValue.toInt(),
+ NULL);
+ break;
+ case QVariant::Double:
+ gst_tag_setter_add_tags(GST_TAG_SETTER(element),
+ GST_TAG_MERGE_REPLACE_ALL,
+ tagName.toUtf8().constData(),
+ tagValue.toDouble(),
+ NULL);
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ }
+ }
+}
+
+bool QGstreamerCaptureSession::processSyncMessage(const QGstreamerMessage &message)
+{
+ GstMessage* gm = message.rawMessage();
+
+ if (gm && GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) {
+ if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_videoPreview))
+ m_viewfinderInterface->handleSyncMessage(gm);
+
+ if (gst_structure_has_name(gm->structure, "prepare-xwindow-id")) {
+ if (m_audioPreviewFactory)
+ m_audioPreviewFactory->prepareWinId();
+
+ if (m_viewfinderInterface)
+ m_viewfinderInterface->precessNewStream();
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void QGstreamerCaptureSession::busMessage(const QGstreamerMessage &message)
+{
+ GstMessage* gm = message.rawMessage();
+
+ if (gm) {
+ if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ERROR) {
+ GError *err;
+ gchar *debug;
+ gst_message_parse_error (gm, &err, &debug);
+ emit error(int(QMediaRecorder::ResourceError),QString::fromUtf8(err->message));
+ g_error_free (err);
+ g_free (debug);
+ }
+
+ if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_pipeline)) {
+ switch (GST_MESSAGE_TYPE(gm)) {
+ case GST_MESSAGE_DURATION:
+ break;
+
+ case GST_MESSAGE_EOS:
+ if (m_waitingForEos)
+ setState(m_pendingState);
+ 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]);
+
+ #define ENUM_NAME(c,e,v) (c::staticMetaObject.enumerator(c::staticMetaObject.indexOfEnumerator(e)).valueToKey((v)))
+
+ qDebug() << "Current session state:" << ENUM_NAME(QGstreamerCaptureSession,"State",m_state);
+ qDebug() << "Pending session state:" << ENUM_NAME(QGstreamerCaptureSession,"State",m_pendingState);
+ */
+
+ switch (newState) {
+ case GST_STATE_VOID_PENDING:
+ case GST_STATE_NULL:
+ case GST_STATE_READY:
+ if (m_state != StoppedState && m_pendingState == StoppedState) {
+ emit stateChanged(m_state = StoppedState);
+ dumpGraph("stopped");
+ }
+ break;
+ case GST_STATE_PAUSED:
+ if (m_state != PausedState && m_pendingState == PausedState)
+ emit stateChanged(m_state = PausedState);
+ dumpGraph("paused");
+
+ if (m_pipelineMode == RecordingPipeline && !m_metaData.isEmpty())
+ setMetaData(m_metaData);
+ break;
+ case GST_STATE_PLAYING:
+ {
+ if ((m_pendingState == PreviewState || m_pendingState == RecordingState) &&
+ m_state != m_pendingState)
+ {
+ m_state = m_pendingState;
+ emit stateChanged(m_state);
+ }
+
+ if (m_pipelineMode == PreviewPipeline)
+ dumpGraph("preview");
+ else
+ dumpGraph("recording");
+ }
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ //qDebug() << "New session state:" << ENUM_NAME(QGstreamerCaptureSession,"State",m_state);
+ }
+
+ if (m_videoPreview && m_viewfinderInterface &&
+ GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_videoPreview))
+ m_viewfinderInterface->handleBusMessage(gm);
+ }
+}
+
+void QGstreamerCaptureSession::setMuted(bool muted)
+{
+ if (m_muted != muted) {
+ m_muted = muted;
+ if (m_audioVolume)
+ g_object_set(G_OBJECT(m_audioVolume), "volume", (m_muted ? 0.0 : 1.0), NULL);
+ emit mutedChanged(muted);
+ }
+}
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.h b/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.h
new file mode 100644
index 000000000..f04a49be2
--- /dev/null
+++ b/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.h
@@ -0,0 +1,211 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 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 QGSTREAMERCAPTURESESSION_H
+#define QGSTREAMERCAPTURESESSION_H
+
+#include <qmediarecordercontrol.h>
+#include <qmediarecorder.h>
+
+#include <QtCore/qurl.h>
+
+#include <gst/gst.h>
+
+#include "qgstreamerbushelper.h"
+
+QT_USE_NAMESPACE
+
+class QGstreamerMessage;
+class QGstreamerBusHelper;
+class QGstreamerAudioEncode;
+class QGstreamerVideoEncode;
+class QGstreamerImageEncode;
+class QGstreamerRecorderControl;
+class QGstreamerMediaContainerControl;
+class QGstreamerVideoRendererInterface;
+
+class QGstreamerElementFactory
+{
+public:
+ virtual GstElement *buildElement() = 0;
+ virtual void prepareWinId() {}
+};
+
+class QGstreamerVideoInput : public QGstreamerElementFactory
+{
+public:
+ virtual QList<qreal> supportedFrameRates(const QSize &frameSize = QSize()) const = 0;
+ virtual QList<QSize> supportedResolutions(qreal frameRate = -1) const = 0;
+};
+
+class QGstreamerCaptureSession : public QObject, public QGstreamerSyncEventFilter
+{
+ Q_OBJECT
+ Q_PROPERTY(qint64 duration READ duration NOTIFY durationChanged)
+ Q_ENUMS(State)
+ Q_ENUMS(CaptureMode)
+public:
+ enum CaptureMode { Audio = 1, Video = 2, Image=4, AudioAndVideo = Audio | Video };
+ enum State { StoppedState, PreviewState, PausedState, RecordingState };
+
+ QGstreamerCaptureSession(CaptureMode captureMode, QObject *parent);
+ ~QGstreamerCaptureSession();
+
+ CaptureMode captureMode() const { return m_captureMode; }
+ void setCaptureMode(CaptureMode);
+
+ QUrl outputLocation() const;
+ bool setOutputLocation(const QUrl& sink);
+
+ QGstreamerAudioEncode *audioEncodeControl() const { return m_audioEncodeControl; }
+ QGstreamerVideoEncode *videoEncodeControl() const { return m_videoEncodeControl; }
+ QGstreamerImageEncode *imageEncodeControl() const { return m_imageEncodeControl; }
+
+ QGstreamerRecorderControl *recorderControl() const { return m_recorderControl; }
+ QGstreamerMediaContainerControl *mediaContainerControl() const { return m_mediaContainerControl; }
+
+ QGstreamerElementFactory *audioInput() const { return m_audioInputFactory; }
+ void setAudioInput(QGstreamerElementFactory *audioInput);
+
+ QGstreamerElementFactory *audioPreview() const { return m_audioPreviewFactory; }
+ void setAudioPreview(QGstreamerElementFactory *audioPreview);
+
+ QGstreamerVideoInput *videoInput() const { return m_videoInputFactory; }
+ void setVideoInput(QGstreamerVideoInput *videoInput);
+
+ QObject *videoPreview() const { return m_viewfinder; }
+ void setVideoPreview(QObject *viewfinder);
+
+ void captureImage(int requestId, const QString &fileName);
+
+ State state() const;
+ qint64 duration() const;
+ bool isMuted() const { return m_muted; }
+
+ bool isReady() const;
+
+ bool processSyncMessage(const QGstreamerMessage &message);
+
+signals:
+ void stateChanged(QGstreamerCaptureSession::State state);
+ void durationChanged(qint64 duration);
+ void error(int error, const QString &errorString);
+ void imageExposed(int requestId);
+ void imageCaptured(int requestId, const QImage &img);
+ void imageSaved(int requestId, const QString &path);
+ void mutedChanged(bool);
+ void readyChanged(bool);
+ void viewfinderChanged();
+
+public slots:
+ void setState(QGstreamerCaptureSession::State);
+ void setCaptureDevice(const QString &deviceName);
+
+ void dumpGraph(const QString &fileName);
+
+ void setMetaData(const QMap<QByteArray, QVariant>&);
+ void setMuted(bool);
+
+private slots:
+ void busMessage(const QGstreamerMessage &message);
+
+private:
+ enum PipelineMode { EmptyPipeline, PreviewPipeline, RecordingPipeline, PreviewAndRecordingPipeline };
+
+ GstElement *buildEncodeBin();
+ GstElement *buildAudioSrc();
+ GstElement *buildAudioPreview();
+ GstElement *buildVideoSrc();
+ GstElement *buildVideoPreview();
+ GstElement *buildImageCapture();
+
+ void waitForStopped();
+ bool rebuildGraph(QGstreamerCaptureSession::PipelineMode newMode);
+
+ QUrl m_sink;
+ QString m_captureDevice;
+ State m_state;
+ State m_pendingState;
+ bool m_waitingForEos;
+ PipelineMode m_pipelineMode;
+ QGstreamerCaptureSession::CaptureMode m_captureMode;
+ QMap<QByteArray, QVariant> m_metaData;
+
+ QGstreamerElementFactory *m_audioInputFactory;
+ QGstreamerElementFactory *m_audioPreviewFactory;
+ QGstreamerVideoInput *m_videoInputFactory;
+ QObject *m_viewfinder;
+ QGstreamerVideoRendererInterface *m_viewfinderInterface;
+
+ QGstreamerAudioEncode *m_audioEncodeControl;
+ QGstreamerVideoEncode *m_videoEncodeControl;
+ QGstreamerImageEncode *m_imageEncodeControl;
+ QGstreamerRecorderControl *m_recorderControl;
+ QGstreamerMediaContainerControl *m_mediaContainerControl;
+
+ QGstreamerBusHelper *m_busHelper;
+ GstBus* m_bus;
+ GstElement *m_pipeline;
+
+ GstElement *m_audioSrc;
+ GstElement *m_audioTee;
+ GstElement *m_audioPreviewQueue;
+ GstElement *m_audioPreview;
+ GstElement *m_audioVolume;
+ bool m_muted;
+
+ GstElement *m_videoSrc;
+ GstElement *m_videoTee;
+ GstElement *m_videoPreviewQueue;
+ GstElement *m_videoPreview;
+
+ GstElement *m_imageCaptureBin;
+
+ GstElement *m_encodeBin;
+
+public:
+ bool m_passImage;
+ bool m_passPrerollImage;
+ QString m_imageFileName;
+ int m_imageRequestId;
+};
+
+#endif // QGSTREAMERCAPTURESESSION_H
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.cpp b/src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.cpp
new file mode 100644
index 000000000..99c3b1665
--- /dev/null
+++ b/src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.cpp
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** 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 "qgstreamerimagecapturecontrol.h"
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+
+QGstreamerImageCaptureControl::QGstreamerImageCaptureControl(QGstreamerCaptureSession *session)
+ :QCameraImageCaptureControl(session), m_session(session), m_ready(false), m_lastId(0)
+{
+ connect(m_session, SIGNAL(stateChanged(QGstreamerCaptureSession::State)), SLOT(updateState()));
+ connect(m_session, SIGNAL(imageExposed(int)), this, SIGNAL(imageExposed(int)));
+ connect(m_session, SIGNAL(imageCaptured(int,QImage)), this, SIGNAL(imageCaptured(int,QImage)));
+ connect(m_session, SIGNAL(imageSaved(int,QString)), this, SIGNAL(imageSaved(int,QString)));
+}
+
+QGstreamerImageCaptureControl::~QGstreamerImageCaptureControl()
+{
+}
+
+bool QGstreamerImageCaptureControl::isReadyForCapture() const
+{
+ return m_ready;
+}
+
+int QGstreamerImageCaptureControl::capture(const QString &fileName)
+{
+ QString path = fileName;
+ if (path.isEmpty()) {
+ int lastImage = 0;
+ QDir outputDir = QDir::currentPath();
+ foreach(QString fileName, outputDir.entryList(QStringList() << "img_*.jpg")) {
+ int imgNumber = fileName.mid(4, fileName.size()-8).toInt();
+ lastImage = qMax(lastImage, imgNumber);
+ }
+
+ path = QString("img_%1.jpg").arg(lastImage+1,
+ 4, //fieldWidth
+ 10,
+ QLatin1Char('0'));
+ }
+ m_lastId++;
+
+ m_session->captureImage(m_lastId, path);
+
+ return m_lastId;
+}
+
+void QGstreamerImageCaptureControl::cancelCapture()
+{
+
+}
+
+void QGstreamerImageCaptureControl::updateState()
+{
+ bool ready = m_session->state() == QGstreamerCaptureSession::PreviewState;
+ if (m_ready != ready) {
+ emit readyForCaptureChanged(m_ready = ready);
+ }
+}
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.h b/src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.h
new file mode 100644
index 000000000..898162440
--- /dev/null
+++ b/src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** 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 QGSTREAMERIMAGECAPTURECONTROL_H
+#define QGSTREAMERIMAGECAPTURECONTROL_H
+
+#include <qcameraimagecapturecontrol.h>
+#include "qgstreamercapturesession.h"
+QT_USE_NAMESPACE
+
+class QGstreamerImageCaptureControl : public QCameraImageCaptureControl
+{
+ Q_OBJECT
+public:
+ QGstreamerImageCaptureControl(QGstreamerCaptureSession *session);
+ virtual ~QGstreamerImageCaptureControl();
+
+ QCameraImageCapture::DriveMode driveMode() const { return QCameraImageCapture::SingleImageCapture; }
+ void setDriveMode(QCameraImageCapture::DriveMode) {}
+
+ bool isReadyForCapture() const;
+ int capture(const QString &fileName);
+ void cancelCapture();
+
+private slots:
+ void updateState();
+
+private:
+ QGstreamerCaptureSession *m_session;
+ bool m_ready;
+ int m_lastId;
+};
+
+#endif // QGSTREAMERCAPTURECORNTROL_H
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerimageencode.cpp b/src/plugins/gstreamer/mediacapture/qgstreamerimageencode.cpp
new file mode 100644
index 000000000..5aef97794
--- /dev/null
+++ b/src/plugins/gstreamer/mediacapture/qgstreamerimageencode.cpp
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** 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 "qgstreamerimageencode.h"
+#include "qgstreamercapturesession.h"
+
+#include <QtCore/qdebug.h>
+
+#include <math.h>
+
+QGstreamerImageEncode::QGstreamerImageEncode(QGstreamerCaptureSession *session)
+ :QImageEncoderControl(session), m_session(session)
+{
+}
+
+QGstreamerImageEncode::~QGstreamerImageEncode()
+{
+}
+
+QList<QSize> QGstreamerImageEncode::supportedResolutions(const QImageEncoderSettings &, bool *continuous) const
+{
+ if (continuous)
+ *continuous = m_session->videoInput() != 0;
+
+ return m_session->videoInput() ? m_session->videoInput()->supportedResolutions() : QList<QSize>();
+}
+
+QStringList QGstreamerImageEncode::supportedImageCodecs() const
+{
+ return QStringList() << "jpeg";
+}
+
+QString QGstreamerImageEncode::imageCodecDescription(const QString &codecName) const
+{
+ if (codecName == "jpeg")
+ return tr("JPEG image encoder");
+
+ return QString();
+}
+
+QImageEncoderSettings QGstreamerImageEncode::imageSettings() const
+{
+ return m_settings;
+}
+
+void QGstreamerImageEncode::setImageSettings(const QImageEncoderSettings &settings)
+{
+ if (m_settings != settings) {
+ m_settings = settings;
+ emit settingsChanged();
+ }
+}
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerimageencode.h b/src/plugins/gstreamer/mediacapture/qgstreamerimageencode.h
new file mode 100644
index 000000000..b66ba75f8
--- /dev/null
+++ b/src/plugins/gstreamer/mediacapture/qgstreamerimageencode.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** 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 QGSTREAMERIMAGEENCODE_H
+#define QGSTREAMERIMAGEENCODE_H
+
+class QGstreamerCaptureSession;
+
+#include <qimageencodercontrol.h>
+
+#include <QtCore/qstringlist.h>
+#include <QtCore/qmap.h>
+
+#include <gst/gst.h>
+QT_USE_NAMESPACE
+
+class QGstreamerImageEncode : public QImageEncoderControl
+{
+ Q_OBJECT
+public:
+ QGstreamerImageEncode(QGstreamerCaptureSession *session);
+ virtual ~QGstreamerImageEncode();
+
+ QList<QSize> supportedResolutions(const QImageEncoderSettings &settings = QImageEncoderSettings(),
+ bool *continuous = 0) const;
+
+ QStringList supportedImageCodecs() const;
+ QString imageCodecDescription(const QString &codecName) const;
+
+ QImageEncoderSettings imageSettings() const;
+ void setImageSettings(const QImageEncoderSettings &settings);
+
+Q_SIGNALS:
+ void settingsChanged();
+
+private:
+ QImageEncoderSettings m_settings;
+
+ QGstreamerCaptureSession *m_session;
+};
+
+#endif
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.cpp b/src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.cpp
new file mode 100644
index 000000000..34c43e3ac
--- /dev/null
+++ b/src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.cpp
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 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 "qgstreamermediacontainercontrol.h"
+
+
+#include <QtCore/qdebug.h>
+
+QGstreamerMediaContainerControl::QGstreamerMediaContainerControl(QObject *parent)
+ :QMediaContainerControl(parent)
+{
+ QList<QByteArray> formatCandidates;
+ formatCandidates << "matroska" << "ogg" << "mp4" << "wav" << "quicktime" << "avi" << "3gpp";
+ formatCandidates << "flv" << "amr" << "asf" << "dv" << "gif";
+ formatCandidates << "mpeg" << "vob" << "mpegts" << "3g2" << "3gp";
+ formatCandidates << "raw";
+
+ m_elementNames["matroska"] = "matroskamux";
+ m_elementNames["ogg"] = "oggmux";
+ m_elementNames["mp4"] = "ffmux_mp4";
+ m_elementNames["quicktime"] = "ffmux_mov";
+ m_elementNames["avi"] = "avimux";
+ m_elementNames["3gpp"] = "gppmux";
+ m_elementNames["flv"] = "flvmux";
+ m_elementNames["wav"] = "wavenc";
+ m_elementNames["amr"] = "ffmux_amr";
+ m_elementNames["asf"] = "ffmux_asf";
+ m_elementNames["dv"] = "ffmux_dv";
+ m_elementNames["gif"] = "ffmux_gif";
+ m_elementNames["mpeg"] = "ffmux_mpeg";
+ m_elementNames["vob"] = "ffmux_vob";
+ m_elementNames["mpegts"] = "ffmux_mpegts";
+ m_elementNames["3g2"] = "ffmux_3g2";
+ m_elementNames["3gp"] = "ffmux_3gp";
+ m_elementNames["raw"] = "identity";
+
+ m_containerExtensions["matroska"] = "mkv";
+ m_containerExtensions["quicktime"] = "mov";
+ m_containerExtensions["mpegts"] = "m2t";
+ m_containerExtensions["mpeg"] = "mpg";
+
+ QSet<QString> allTypes;
+
+ foreach( const QByteArray& formatName, formatCandidates ) {
+ QByteArray elementName = m_elementNames[formatName];
+ GstElementFactory *factory = gst_element_factory_find(elementName.constData());
+ if (factory) {
+ m_supportedContainers.append(formatName);
+ const gchar *descr = gst_element_factory_get_description(factory);
+ m_containerDescriptions.insert(formatName, QString::fromUtf8(descr));
+
+
+ if (formatName == QByteArray("raw")) {
+ m_streamTypes.insert(formatName, allTypes);
+ } else {
+ QSet<QString> types = supportedStreamTypes(factory, GST_PAD_SINK);
+ m_streamTypes.insert(formatName, types);
+ allTypes.unite(types);
+ }
+
+ gst_object_unref(GST_OBJECT(factory));
+ }
+ }
+
+ //if (!m_supportedContainers.isEmpty())
+ // setContainerMimeType(m_supportedContainers[0]);
+}
+
+QSet<QString> QGstreamerMediaContainerControl::supportedStreamTypes(GstElementFactory *factory, GstPadDirection direction)
+{
+ QSet<QString> types;
+ const GList *pads = gst_element_factory_get_static_pad_templates(factory);
+ for (const GList *pad = pads; pad; pad = g_list_next(pad)) {
+ GstStaticPadTemplate *templ = (GstStaticPadTemplate*)pad->data;
+ if (templ->direction == direction) {
+ GstCaps *caps = gst_static_caps_get(&templ->static_caps);
+ for (uint i=0; i<gst_caps_get_size(caps); i++) {
+ GstStructure *structure = gst_caps_get_structure(caps, i);
+ types.insert( QString::fromUtf8(gst_structure_get_name(structure)) );
+ }
+ gst_caps_unref(caps);
+ }
+ }
+
+ return types;
+}
+
+
+QSet<QString> QGstreamerMediaContainerControl::supportedStreamTypes(const QString &container) const
+{
+ return m_streamTypes.value(container);
+}
+
+QString QGstreamerMediaContainerControl::containerExtension() const
+{
+ return m_containerExtensions.value(m_format, m_format);
+}
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.h b/src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.h
new file mode 100644
index 000000000..57e0a5bf1
--- /dev/null
+++ b/src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 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 QGSTREAMERMEDIACONTAINERCONTROL_H
+#define QGSTREAMERMEDIACONTAINERCONTROL_H
+
+#include <qmediacontainercontrol.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qset.h>
+
+#include <gst/gst.h>
+
+QT_USE_NAMESPACE
+
+class QGstreamerMediaContainerControl : public QMediaContainerControl
+{
+Q_OBJECT
+public:
+ QGstreamerMediaContainerControl(QObject *parent);
+ virtual ~QGstreamerMediaContainerControl() {};
+
+ virtual QStringList supportedContainers() const { return m_supportedContainers; }
+ virtual QString containerMimeType() const { return m_format; }
+ virtual void setContainerMimeType(const QString &formatMimeType) { m_format = formatMimeType; }
+
+ virtual QString containerDescription(const QString &formatMimeType) const { return m_containerDescriptions.value(formatMimeType); }
+
+ QByteArray formatElementName() const { return m_elementNames.value(containerMimeType()); }
+
+ QSet<QString> supportedStreamTypes(const QString &container) const;
+
+ static QSet<QString> supportedStreamTypes(GstElementFactory *factory, GstPadDirection direction);
+
+ QString containerExtension() const;
+
+private:
+ QString m_format;
+ QStringList m_supportedContainers;
+ QMap<QString,QByteArray> m_elementNames;
+ QMap<QString, QString> m_containerDescriptions;
+ QMap<QString, QString> m_containerExtensions;
+ QMap<QString, QSet<QString> > m_streamTypes;
+};
+
+#endif // QGSTREAMERMEDIACONTAINERCONTROL_H
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp b/src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp
new file mode 100644
index 000000000..24b28dead
--- /dev/null
+++ b/src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp
@@ -0,0 +1,289 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 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 "qgstreamerrecordercontrol.h"
+#include "qgstreameraudioencode.h"
+#include "qgstreamervideoencode.h"
+#include "qgstreamermediacontainercontrol.h"
+#include <QtCore/QDebug>
+
+QGstreamerRecorderControl::QGstreamerRecorderControl(QGstreamerCaptureSession *session)
+ :QMediaRecorderControl(session), m_session(session), m_state(QMediaRecorder::StoppedState)
+{
+ connect(m_session, SIGNAL(stateChanged(QGstreamerCaptureSession::State)), SLOT(updateState()));
+ connect(m_session, SIGNAL(error(int,QString)), SIGNAL(error(int,QString)));
+ connect(m_session, SIGNAL(durationChanged(qint64)), SIGNAL(durationChanged(qint64)));
+ connect(m_session, SIGNAL(mutedChanged(bool)), SIGNAL(mutedChanged(bool)));
+ m_hasPreviewState = m_session->captureMode() != QGstreamerCaptureSession::Audio;
+}
+
+QGstreamerRecorderControl::~QGstreamerRecorderControl()
+{
+}
+
+QUrl QGstreamerRecorderControl::outputLocation() const
+{
+ return m_session->outputLocation();
+}
+
+bool QGstreamerRecorderControl::setOutputLocation(const QUrl &sink)
+{
+ m_outputLocation = sink;
+ m_session->setOutputLocation(sink);
+ return true;
+}
+
+
+QMediaRecorder::State QGstreamerRecorderControl::state() const
+{
+ switch ( m_session->state() ) {
+ case QGstreamerCaptureSession::RecordingState:
+ return QMediaRecorder::RecordingState;
+ case QGstreamerCaptureSession::PausedState:
+ return QMediaRecorder::PausedState;
+ case QGstreamerCaptureSession::PreviewState:
+ case QGstreamerCaptureSession::StoppedState:
+ return QMediaRecorder::StoppedState;
+ }
+
+ return QMediaRecorder::StoppedState;
+
+}
+
+void QGstreamerRecorderControl::updateState()
+{
+ QMediaRecorder::State newState = state();
+ if (m_state != newState) {
+ m_state = newState;
+ emit stateChanged(m_state);
+ }
+}
+
+qint64 QGstreamerRecorderControl::duration() const
+{
+ return m_session->duration();
+}
+
+void QGstreamerRecorderControl::record()
+{
+ if (m_outputLocation.isEmpty()) {
+ QString container = m_session->mediaContainerControl()->containerExtension();
+ if (container.isEmpty())
+ container = "raw";
+
+ m_session->setOutputLocation(QUrl(generateFileName(defaultDir(), container)));
+ }
+
+ m_session->dumpGraph("before-record");
+ if (!m_hasPreviewState || m_session->state() != QGstreamerCaptureSession::StoppedState) {
+ m_session->setState(QGstreamerCaptureSession::RecordingState);
+ } else
+ emit error(QMediaRecorder::ResourceError, tr("Service has not been started"));
+
+ m_session->dumpGraph("after-record");
+}
+
+void QGstreamerRecorderControl::pause()
+{
+ m_session->dumpGraph("before-pause");
+ if (!m_hasPreviewState || m_session->state() != QGstreamerCaptureSession::StoppedState) {
+ m_session->setState(QGstreamerCaptureSession::PausedState);
+ } else
+ emit error(QMediaRecorder::ResourceError, tr("Service has not been started"));
+}
+
+void QGstreamerRecorderControl::stop()
+{
+ if (!m_hasPreviewState) {
+ m_session->setState(QGstreamerCaptureSession::StoppedState);
+ } else {
+ if (m_session->state() != QGstreamerCaptureSession::StoppedState)
+ m_session->setState(QGstreamerCaptureSession::PreviewState);
+ }
+}
+
+void QGstreamerRecorderControl::applySettings()
+{
+ //Check the codecs are compatible with container,
+ //and choose the compatible codecs/container if omitted
+ QGstreamerAudioEncode *audioEncodeControl = m_session->audioEncodeControl();
+ QGstreamerVideoEncode *videoEncodeControl = m_session->videoEncodeControl();
+ QGstreamerMediaContainerControl *mediaContainerControl = m_session->mediaContainerControl();
+
+ bool needAudio = m_session->captureMode() & QGstreamerCaptureSession::Audio;
+ bool needVideo = m_session->captureMode() & QGstreamerCaptureSession::Video;
+
+ QStringList containerCandidates;
+ if (mediaContainerControl->containerMimeType().isEmpty())
+ containerCandidates = mediaContainerControl->supportedContainers();
+ else
+ containerCandidates << mediaContainerControl->containerMimeType();
+
+
+ QStringList audioCandidates;
+ if (needAudio) {
+ QAudioEncoderSettings audioSettings = audioEncodeControl->audioSettings();
+ if (audioSettings.codec().isEmpty())
+ audioCandidates = audioEncodeControl->supportedAudioCodecs();
+ else
+ audioCandidates << audioSettings.codec();
+ }
+
+ QStringList videoCandidates;
+ if (needVideo) {
+ QVideoEncoderSettings videoSettings = videoEncodeControl->videoSettings();
+ if (videoSettings.codec().isEmpty())
+ videoCandidates = videoEncodeControl->supportedVideoCodecs();
+ else
+ videoCandidates << videoSettings.codec();
+ }
+
+ QString container;
+ QString audioCodec;
+ QString videoCodec;
+
+ foreach (const QString &containerCandidate, containerCandidates) {
+ QSet<QString> supportedTypes = mediaContainerControl->supportedStreamTypes(containerCandidate);
+
+ audioCodec.clear();
+ videoCodec.clear();
+
+ if (needAudio) {
+ bool found = false;
+ foreach (const QString &audioCandidate, audioCandidates) {
+ QSet<QString> audioTypes = audioEncodeControl->supportedStreamTypes(audioCandidate);
+ if (!audioTypes.intersect(supportedTypes).isEmpty()) {
+ found = true;
+ audioCodec = audioCandidate;
+ break;
+ }
+ }
+ if (!found)
+ continue;
+ }
+
+ if (needVideo) {
+ bool found = false;
+ foreach (const QString &videoCandidate, videoCandidates) {
+ QSet<QString> videoTypes = videoEncodeControl->supportedStreamTypes(videoCandidate);
+ if (!videoTypes.intersect(supportedTypes).isEmpty()) {
+ found = true;
+ videoCodec = videoCandidate;
+ break;
+ }
+ }
+ if (!found)
+ continue;
+ }
+
+ container = containerCandidate;
+ break;
+ }
+
+ if (container.isEmpty()) {
+ emit error(QMediaRecorder::FormatError, tr("Not compatible codecs and container format."));
+ } else {
+ mediaContainerControl->setContainerMimeType(container);
+
+ if (needAudio) {
+ QAudioEncoderSettings audioSettings = audioEncodeControl->audioSettings();
+ audioSettings.setCodec(audioCodec);
+ audioEncodeControl->setAudioSettings(audioSettings);
+ }
+
+ if (needVideo) {
+ QVideoEncoderSettings videoSettings = videoEncodeControl->videoSettings();
+ videoSettings.setCodec(videoCodec);
+ videoEncodeControl->setVideoSettings(videoSettings);
+ }
+ }
+}
+
+
+bool QGstreamerRecorderControl::isMuted() const
+{
+ return m_session->isMuted();
+}
+
+void QGstreamerRecorderControl::setMuted(bool muted)
+{
+ m_session->setMuted(muted);
+}
+
+QDir QGstreamerRecorderControl::defaultDir() const
+{
+ QStringList dirCandidates;
+
+#if defined(Q_WS_MAEMO_5) || defined(Q_WS_MAEMO_6)
+ dirCandidates << QLatin1String("/home/user/MyDocs");
+#endif
+
+ dirCandidates << QDir::home().filePath("Documents");
+ dirCandidates << QDir::home().filePath("My Documents");
+ dirCandidates << QDir::homePath();
+ dirCandidates << QDir::currentPath();
+ dirCandidates << QDir::tempPath();
+
+ foreach (const QString &path, dirCandidates) {
+ QDir dir(path);
+ if (dir.exists() && QFileInfo(path).isWritable())
+ return dir;
+ }
+
+ return QDir();
+}
+
+QString QGstreamerRecorderControl::generateFileName(const QDir &dir, const QString &ext) const
+{
+
+ int lastClip = 0;
+ foreach(QString fileName, dir.entryList(QStringList() << QString("clip_*.%1").arg(ext))) {
+ int imgNumber = fileName.mid(5, fileName.size()-6-ext.length()).toInt();
+ lastClip = qMax(lastClip, imgNumber);
+ }
+
+ QString name = QString("clip_%1.%2").arg(lastClip+1,
+ 4, //fieldWidth
+ 10,
+ QLatin1Char('0')).arg(ext);
+
+ return dir.absoluteFilePath(name);
+}
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.h b/src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.h
new file mode 100644
index 000000000..c252f1543
--- /dev/null
+++ b/src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 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 QGSTREAMERRECORDERCONTROL_H
+#define QGSTREAMERRECORDERCONTROL_H
+
+#include <QtCore/QDir>
+
+#include <qmediarecordercontrol.h>
+#include "qgstreamercapturesession.h"
+
+QT_USE_NAMESPACE
+
+class QGstreamerRecorderControl : public QMediaRecorderControl
+{
+ Q_OBJECT
+
+public:
+ QGstreamerRecorderControl(QGstreamerCaptureSession *session);
+ virtual ~QGstreamerRecorderControl();
+
+ QUrl outputLocation() const;
+ bool setOutputLocation(const QUrl &sink);
+
+ QMediaRecorder::State state() const;
+
+ qint64 duration() const;
+
+ bool isMuted() const;
+
+ void applySettings();
+
+public slots:
+ void record();
+ void pause();
+ void stop();
+ void setMuted(bool);
+
+private slots:
+ void updateState();
+
+private:
+ QDir defaultDir() const;
+ QString generateFileName(const QDir &dir, const QString &ext) const;
+
+ QUrl m_outputLocation;
+ QGstreamerCaptureSession *m_session;
+ QMediaRecorder::State m_state;
+ bool m_hasPreviewState;
+};
+
+#endif // QGSTREAMERCAPTURECORNTROL_H
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.cpp b/src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.cpp
new file mode 100644
index 000000000..70907b2dd
--- /dev/null
+++ b/src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.cpp
@@ -0,0 +1,297 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 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 "qgstreamerv4l2input.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qfile.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
+static inline uint qHash(const QSize& key) { return uint(key.width()*256+key.height()); }
+
+static bool operator<(const QSize &s1, const QSize s2)
+{
+ return s1.width()*s1.height() < s2.width()*s2.height();
+}
+QT_END_NAMESPACE
+
+QGstreamerV4L2Input::QGstreamerV4L2Input(QObject *parent)
+ :QObject(parent)
+{
+}
+
+QGstreamerV4L2Input::~QGstreamerV4L2Input()
+{
+}
+
+GstElement *QGstreamerV4L2Input::buildElement()
+{
+#ifndef Q_WS_MAEMO_5
+ GstElement *camera = gst_element_factory_make("v4l2src", "camera_source");
+#else
+ GstElement *camera = gst_element_factory_make("v4l2camsrc", "camera_source");
+#endif
+ if (camera && !m_device.isEmpty() )
+ g_object_set(G_OBJECT(camera), "device", m_device.constData(), NULL);
+
+ return camera;
+}
+
+void QGstreamerV4L2Input::setDevice(const QByteArray &newDevice)
+{
+ if (m_device != newDevice) {
+ m_device = newDevice;
+ updateSupportedResolutions(newDevice);
+ }
+}
+
+void QGstreamerV4L2Input::setDevice(const QString &device)
+{
+ setDevice(QFile::encodeName(device));
+}
+
+void QGstreamerV4L2Input::updateSupportedResolutions(const QByteArray &device)
+{
+ m_frameRates.clear();
+ m_resolutions.clear();
+ m_ratesByResolution.clear();
+
+ QSet<QSize> allResolutions;
+ QSet<int> allFrameRates;
+
+ QFile f(device);
+
+ if (!f.open(QFile::ReadOnly))
+ return;
+
+ int fd = f.handle();
+
+ //get the list of formats:
+ QList<quint32> supportedFormats;
+
+ {
+ v4l2_fmtdesc fmt;
+ memset(&fmt, 0, sizeof(v4l2_fmtdesc));
+
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ int sanity = 0;
+
+ for (fmt.index = 0;; fmt.index++) {
+ if (sanity++ > 8)
+ break;
+ if( ::ioctl(fd, VIDIOC_ENUM_FMT, &fmt) == -1) {
+ if(errno == EINVAL)
+ break;
+ }
+ supportedFormats.append(fmt.pixelformat);
+ }
+ }
+
+ QList<QSize> commonSizes;
+ commonSizes << QSize(128, 96)
+ <<QSize(160,120)
+ <<QSize(176, 144)
+ <<QSize(320, 240)
+ <<QSize(352, 288)
+ <<QSize(640, 480)
+ <<QSize(1024, 768)
+ <<QSize(1280, 1024)
+ <<QSize(1600, 1200)
+ <<QSize(1920, 1200)
+ <<QSize(2048, 1536)
+ <<QSize(2560, 1600)
+ <<QSize(2580, 1936);
+
+ QList<int> commonRates;
+ commonRates << 05*1000 << 75*1000 << 10*1000 << 15*1000 << 20*1000
+ << 24*1000 << 25*1000 << 30*1000 << 50*1000 << 60*1000;
+
+
+ //get the list of resolutions:
+
+ foreach (quint32 format, supportedFormats) {
+ struct v4l2_frmsizeenum formatSize;
+ memset(&formatSize, 0, sizeof(formatSize));
+ formatSize.pixel_format = format;
+
+ QList<QSize> sizeList;
+
+ if (0) {
+ char formatStr[5];
+ memcpy(formatStr, &format, 4);
+ formatStr[4] = 0;
+ //qDebug() << "trying format" << formatStr;
+ }
+
+ for (int i=0;;i++) {
+ formatSize.index = i;
+ if (ioctl (fd, VIDIOC_ENUM_FRAMESIZES, &formatSize) < 0)
+ break;
+
+ if (formatSize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
+ sizeList.append(QSize(formatSize.discrete.width, formatSize.discrete.height));
+ } else {
+
+ foreach (const QSize& candidate, commonSizes) {
+ if (candidate.width() <= (int)formatSize.stepwise.max_width &&
+ candidate.height() >= (int)formatSize.stepwise.min_width &&
+ candidate.width() % formatSize.stepwise.step_width == 0 &&
+ candidate.height() <= (int)formatSize.stepwise.max_height &&
+ candidate.height() >= (int)formatSize.stepwise.min_height &&
+ candidate.height() % formatSize.stepwise.step_height == 0) {
+ sizeList.append(candidate);
+ }
+ }
+
+ if (!sizeList.contains(QSize(formatSize.stepwise.min_width, formatSize.stepwise.min_height)))
+ sizeList.prepend(QSize(formatSize.stepwise.min_width, formatSize.stepwise.min_height));
+
+ if (!sizeList.contains(QSize(formatSize.stepwise.max_width, formatSize.stepwise.max_height)))
+ sizeList.append(QSize(formatSize.stepwise.max_width, formatSize.stepwise.max_height));
+
+ break; //stepwise values are returned only for index 0
+ }
+
+ }
+
+ //and frameRates for each resolution.
+
+ foreach (const QSize &s, sizeList) {
+ allResolutions.insert(s);
+
+ struct v4l2_frmivalenum formatInterval;
+ memset(&formatInterval, 0, sizeof(formatInterval));
+ formatInterval.pixel_format = format;
+ formatInterval.width = s.width();
+ formatInterval.height = s.height();
+
+ QList<int> frameRates; //in 1/1000 of fps
+
+ for (int i=0; ; i++) {
+ formatInterval.index = i;
+
+ if (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &formatInterval) < 0)
+ break;
+
+ if (formatInterval.type == V4L2_FRMIVAL_TYPE_DISCRETE) {
+ //converts seconds to fps*1000
+ if (formatInterval.discrete.numerator)
+ frameRates.append(qRound(formatInterval.discrete.denominator*1000.0 / formatInterval.discrete.numerator));
+ } else {
+ if (formatInterval.stepwise.min.numerator == 0 ||
+ formatInterval.stepwise.max.numerator == 0) {
+ qWarning() << "received invalid frame interval";
+ break;
+ }
+
+
+ int minRate = qRound(formatInterval.stepwise.min.denominator*1000.0 /
+ formatInterval.stepwise.min.numerator);
+
+ int maxRate = qRound(formatInterval.stepwise.max.denominator*1000.0 /
+ formatInterval.stepwise.max.numerator);
+
+
+ foreach (int candidate, commonRates) {
+ if (candidate >= minRate && candidate <= maxRate)
+ frameRates.append(candidate);
+ }
+
+ if (!frameRates.contains(minRate))
+ frameRates.prepend(minRate);
+
+ if (!frameRates.contains(maxRate))
+ frameRates.append(maxRate);
+
+ break; //stepwise values are returned only for index 0
+ }
+ }
+ allFrameRates.unite(frameRates.toSet());
+ m_ratesByResolution[s].unite(frameRates.toSet());
+ }
+ }
+
+ f.close();
+
+ foreach(int rate, allFrameRates) {
+ m_frameRates.append(rate/1000.0);
+ }
+
+ qSort(m_frameRates);
+
+ m_resolutions = allResolutions.toList();
+ qSort(m_resolutions);
+
+ //qDebug() << "frame rates:" << m_frameRates;
+ //qDebug() << "resolutions:" << m_resolutions;
+}
+
+
+QList<qreal> QGstreamerV4L2Input::supportedFrameRates(const QSize &frameSize) const
+{
+ if (frameSize.isEmpty())
+ return m_frameRates;
+ else {
+ QList<qreal> res;
+ foreach(int rate, m_ratesByResolution[frameSize]) {
+ res.append(rate/1000.0);
+ }
+ return res;
+ }
+}
+
+QList<QSize> QGstreamerV4L2Input::supportedResolutions(qreal frameRate) const
+{
+ Q_UNUSED(frameRate);
+ return m_resolutions;
+}
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.h b/src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.h
new file mode 100644
index 000000000..5dbdd5453
--- /dev/null
+++ b/src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 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 QGSTREAMERV4L2INPUT_H
+#define QGSTREAMERV4L2INPUT_H
+
+#include <QtCore/qhash.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qsize.h>
+#include "qgstreamercapturesession.h"
+
+QT_USE_NAMESPACE
+
+class QGstreamerV4L2Input : public QObject, public QGstreamerVideoInput
+{
+ Q_OBJECT
+public:
+ QGstreamerV4L2Input(QObject *parent = 0);
+ virtual ~QGstreamerV4L2Input();
+
+ GstElement *buildElement();
+
+ QList<qreal> supportedFrameRates(const QSize &frameSize = QSize()) const;
+ QList<QSize> supportedResolutions(qreal frameRate = -1) const;
+
+ QByteArray device() const;
+
+public slots:
+ void setDevice(const QByteArray &device);
+ void setDevice(const QString &device);
+
+private:
+ void updateSupportedResolutions(const QByteArray &device);
+
+ QList<qreal> m_frameRates;
+ QList<QSize> m_resolutions;
+
+ QHash<QSize, QSet<int> > m_ratesByResolution;
+
+ QByteArray m_device;
+};
+
+#endif // QGSTREAMERV4L2INPUT_H
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.cpp b/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.cpp
new file mode 100644
index 000000000..a491f14ae
--- /dev/null
+++ b/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.cpp
@@ -0,0 +1,331 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 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 "qgstreamervideoencode.h"
+#include "qgstreamercapturesession.h"
+#include "qgstreamermediacontainercontrol.h"
+
+#include <QtCore/qdebug.h>
+
+#include <math.h>
+
+QGstreamerVideoEncode::QGstreamerVideoEncode(QGstreamerCaptureSession *session)
+ :QVideoEncoderControl(session), m_session(session)
+{
+ QList<QByteArray> codecCandidates;
+ codecCandidates << "video/h264" << "video/xvid" << "video/mpeg4" << "video/mpeg1" << "video/mpeg2" << "video/theora";
+
+ m_elementNames["video/h264"] = "x264enc";
+ m_elementNames["video/xvid"] = "xvidenc";
+ m_elementNames["video/mpeg4"] = "ffenc_mpeg4";
+ m_elementNames["video/mpeg1"] = "ffenc_mpeg1video";
+ m_elementNames["video/mpeg2"] = "ffenc_mpeg2video";
+ m_elementNames["video/theora"] = "theoraenc";
+
+ m_codecOptions["video/h264"] = QStringList() << "quantizer";
+ m_codecOptions["video/xvid"] = QStringList() << "quantizer" << "profile";
+ m_codecOptions["video/mpeg4"] = QStringList() << "quantizer";
+ m_codecOptions["video/mpeg1"] = QStringList() << "quantizer";
+ m_codecOptions["video/mpeg2"] = QStringList() << "quantizer";
+ m_codecOptions["video/theora"] = QStringList();
+
+ foreach( const QByteArray& codecName, codecCandidates ) {
+ QByteArray elementName = m_elementNames[codecName];
+ GstElementFactory *factory = gst_element_factory_find(elementName.constData());
+ if (factory) {
+ m_codecs.append(codecName);
+ const gchar *descr = gst_element_factory_get_description(factory);
+ m_codecDescriptions.insert(codecName, QString::fromUtf8(descr));
+
+ m_streamTypes.insert(codecName,
+ QGstreamerMediaContainerControl::supportedStreamTypes(factory, GST_PAD_SRC));
+
+ gst_object_unref(GST_OBJECT(factory));
+ }
+ }
+
+ //if (!m_codecs.isEmpty())
+ // m_videoSettings.setCodec(m_codecs[0]);
+}
+
+QGstreamerVideoEncode::~QGstreamerVideoEncode()
+{
+}
+
+QList<QSize> QGstreamerVideoEncode::supportedResolutions(const QVideoEncoderSettings &, bool *continuous) const
+{
+ if (continuous)
+ *continuous = m_session->videoInput() != 0;
+
+ return m_session->videoInput() ? m_session->videoInput()->supportedResolutions() : QList<QSize>();
+}
+
+QList< qreal > QGstreamerVideoEncode::supportedFrameRates(const QVideoEncoderSettings &, bool *continuous) const
+{
+ if (continuous)
+ *continuous = false;
+
+ return m_session->videoInput() ? m_session->videoInput()->supportedFrameRates() : QList<qreal>();
+}
+
+QStringList QGstreamerVideoEncode::supportedVideoCodecs() const
+{
+ return m_codecs;
+}
+
+QString QGstreamerVideoEncode::videoCodecDescription(const QString &codecName) const
+{
+ return m_codecDescriptions.value(codecName);
+}
+
+QStringList QGstreamerVideoEncode::supportedEncodingOptions(const QString &codec) const
+{
+ return m_codecOptions.value(codec);
+}
+
+QVariant QGstreamerVideoEncode::encodingOption(const QString &codec, const QString &name) const
+{
+ return m_options[codec].value(name);
+}
+
+void QGstreamerVideoEncode::setEncodingOption(
+ const QString &codec, const QString &name, const QVariant &value)
+{
+ m_options[codec][name] = value;
+}
+
+QVideoEncoderSettings QGstreamerVideoEncode::videoSettings() const
+{
+ return m_videoSettings;
+}
+
+void QGstreamerVideoEncode::setVideoSettings(const QVideoEncoderSettings &settings)
+{
+ m_videoSettings = settings;
+}
+
+GstElement *QGstreamerVideoEncode::createEncoder()
+{
+ QString codec = m_videoSettings.codec();
+ //qDebug() << "create encoder for video codec" << codec;
+ GstElement *encoderElement = gst_element_factory_make( m_elementNames.value(codec).constData(), "video-encoder");
+ if (!encoderElement)
+ return 0;
+
+ GstBin *encoderBin = GST_BIN(gst_bin_new("video-encoder-bin"));
+
+ GstElement *capsFilter = gst_element_factory_make("capsfilter", "capsfilter-video");
+ gst_bin_add(encoderBin, capsFilter);
+
+ GstElement *colorspace = gst_element_factory_make("ffmpegcolorspace", NULL);
+ gst_bin_add(encoderBin, colorspace);
+ gst_bin_add(encoderBin, encoderElement);
+
+ gst_element_link_many(capsFilter, colorspace, encoderElement, NULL);
+
+ // add ghostpads
+ GstPad *pad = gst_element_get_static_pad(capsFilter, "sink");
+ gst_element_add_pad(GST_ELEMENT(encoderBin), gst_ghost_pad_new("sink", pad));
+ gst_object_unref(GST_OBJECT(pad));
+
+ pad = gst_element_get_static_pad(encoderElement, "src");
+ gst_element_add_pad(GST_ELEMENT(encoderBin), gst_ghost_pad_new("src", pad));
+ gst_object_unref(GST_OBJECT(pad));
+
+ if (encoderElement) {
+ if (m_videoSettings.encodingMode() == QtMultimediaKit::ConstantQualityEncoding) {
+ QtMultimediaKit::EncodingQuality qualityValue = m_videoSettings.quality();
+
+ if (codec == QLatin1String("video/h264")) {
+ //constant quantizer mode
+ g_object_set(G_OBJECT(encoderElement), "pass", 4, NULL);
+ int qualityTable[] = {
+ 50, //VeryLow
+ 35, //Low
+ 21, //Normal
+ 15, //High
+ 8 //VeryHigh
+ };
+ g_object_set(G_OBJECT(encoderElement), "quantizer", qualityTable[qualityValue], NULL);
+ } else if (codec == QLatin1String("video/xvid")) {
+ //constant quantizer mode
+ g_object_set(G_OBJECT(encoderElement), "pass", 3, NULL);
+ int qualityTable[] = {
+ 32, //VeryLow
+ 12, //Low
+ 5, //Normal
+ 3, //High
+ 2 //VeryHigh
+ };
+ int quant = qualityTable[qualityValue];
+ g_object_set(G_OBJECT(encoderElement), "quantizer", quant, NULL);
+ } else if (codec == QLatin1String("video/mpeg4") ||
+ codec == QLatin1String("video/mpeg1") ||
+ codec == QLatin1String("video/mpeg2") ) {
+ //constant quantizer mode
+ g_object_set(G_OBJECT(encoderElement), "pass", 2, NULL);
+ //quant from 1 to 30, default ~3
+ double qualityTable[] = {
+ 20, //VeryLow
+ 8.0, //Low
+ 3.0, //Normal
+ 2.5, //High
+ 2.0 //VeryHigh
+ };
+ double quant = qualityTable[qualityValue];
+ g_object_set(G_OBJECT(encoderElement), "quantizer", quant, NULL);
+ } else if (codec == QLatin1String("video/theora")) {
+ int qualityTable[] = {
+ 8, //VeryLow
+ 16, //Low
+ 32, //Normal
+ 45, //High
+ 60 //VeryHigh
+ };
+ //quality from 0 to 63
+ int quality = qualityTable[qualityValue];
+ g_object_set(G_OBJECT(encoderElement), "quality", quality, NULL);
+ }
+ } else {
+ int bitrate = m_videoSettings.bitRate();
+ if (bitrate > 0) {
+ g_object_set(G_OBJECT(encoderElement), "bitrate", bitrate, NULL);
+ }
+ }
+
+ QMap<QString,QVariant> options = m_options.value(codec);
+ QMapIterator<QString,QVariant> it(options);
+ while (it.hasNext()) {
+ it.next();
+ QString option = it.key();
+ QVariant value = it.value();
+
+ switch (value.type()) {
+ case QVariant::Int:
+ g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toInt(), NULL);
+ break;
+ case QVariant::Bool:
+ g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toBool(), NULL);
+ break;
+ case QVariant::Double:
+ g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toDouble(), NULL);
+ break;
+ case QVariant::String:
+ g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toString().toUtf8().constData(), NULL);
+ break;
+ default:
+ qWarning() << "unsupported option type:" << option << value;
+ break;
+ }
+
+ }
+ }
+
+ if (!m_videoSettings.resolution().isEmpty() || m_videoSettings.frameRate() > 0.001) {
+ GstCaps *caps = gst_caps_new_empty();
+ QStringList structureTypes;
+ structureTypes << "video/x-raw-yuv" << "video/x-raw-rgb";
+
+ foreach(const QString &structureType, structureTypes) {
+ GstStructure *structure = gst_structure_new(structureType.toAscii().constData(), NULL);
+
+ if (!m_videoSettings.resolution().isEmpty()) {
+ gst_structure_set(structure, "width", G_TYPE_INT, m_videoSettings.resolution().width(), NULL);
+ gst_structure_set(structure, "height", G_TYPE_INT, m_videoSettings.resolution().height(), NULL);
+ }
+
+ if (m_videoSettings.frameRate() > 0.001) {
+ QPair<int,int> rate = rateAsRational();
+
+ //qDebug() << "frame rate:" << num << denum;
+
+ gst_structure_set(structure, "framerate", GST_TYPE_FRACTION, rate.first, rate.second, NULL);
+ }
+
+ gst_caps_append_structure(caps,structure);
+ }
+
+ //qDebug() << "set video caps filter:" << gst_caps_to_string(caps);
+
+ g_object_set(G_OBJECT(capsFilter), "caps", caps, NULL);
+ }
+
+ return GST_ELEMENT(encoderBin);
+}
+
+QPair<int,int> QGstreamerVideoEncode::rateAsRational() const
+{
+ qreal frameRate = m_videoSettings.frameRate();
+
+ if (frameRate > 0.001) {
+ //convert to rational number
+ QList<int> denumCandidates;
+ denumCandidates << 1 << 2 << 3 << 5 << 10 << 1001 << 1000;
+
+ qreal error = 1.0;
+ int num = 1;
+ int denum = 1;
+
+ foreach (int curDenum, denumCandidates) {
+ int curNum = qRound(frameRate*curDenum);
+ qreal curError = qAbs(qreal(curNum)/curDenum - frameRate);
+
+ if (curError < error) {
+ error = curError;
+ num = curNum;
+ denum = curDenum;
+ }
+
+ if (curError < 1e-8)
+ break;
+ }
+
+ return QPair<int,int>(num,denum);
+ }
+
+ return QPair<int,int>();
+}
+
+
+QSet<QString> QGstreamerVideoEncode::supportedStreamTypes(const QString &codecName) const
+{
+ return m_streamTypes.value(codecName);
+}
diff --git a/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.h b/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.h
new file mode 100644
index 000000000..c042efd25
--- /dev/null
+++ b/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 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 QGSTREAMERVIDEOENCODE_H
+#define QGSTREAMERVIDEOENCODE_H
+
+#include <qvideoencodercontrol.h>
+class QGstreamerCaptureSession;
+
+#include <QtCore/qstringlist.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qset.h>
+
+#include <gst/gst.h>
+
+QT_USE_NAMESPACE
+
+class QGstreamerVideoEncode : public QVideoEncoderControl
+{
+ Q_OBJECT
+public:
+ QGstreamerVideoEncode(QGstreamerCaptureSession *session);
+ virtual ~QGstreamerVideoEncode();
+
+ QList<QSize> supportedResolutions(const QVideoEncoderSettings &settings = QVideoEncoderSettings(),
+ bool *continuous = 0) const;
+
+ QList< qreal > supportedFrameRates(const QVideoEncoderSettings &settings = QVideoEncoderSettings(),
+ bool *continuous = 0) const;
+
+ QPair<int,int> rateAsRational() const;
+
+ QStringList supportedVideoCodecs() const;
+ QString videoCodecDescription(const QString &codecName) const;
+
+ QVideoEncoderSettings videoSettings() const;
+ void setVideoSettings(const QVideoEncoderSettings &settings);
+
+ QStringList supportedEncodingOptions(const QString &codec) const;
+ QVariant encodingOption(const QString &codec, const QString &name) const;
+ void setEncodingOption(const QString &codec, const QString &name, const QVariant &value);
+
+ GstElement *createEncoder();
+
+ QSet<QString> supportedStreamTypes(const QString &codecName) const;
+
+private:
+ QGstreamerCaptureSession *m_session;
+
+ QStringList m_codecs;
+ QMap<QString,QString> m_codecDescriptions;
+ QMap<QString,QByteArray> m_elementNames;
+ QMap<QString,QStringList> m_codecOptions;
+
+ QVideoEncoderSettings m_videoSettings;
+ QMap<QString, QMap<QString, QVariant> > m_options;
+ QMap<QString, QSet<QString> > m_streamTypes;
+};
+
+#endif