Merge branch 'camera' of git://scm.dev.nokia.troll.no/qt/qtmultimediakit

Change-Id: Iec6f96e3713257d25f7b1e06c6da9c153b23800b
This commit is contained in:
Michael Goddard
2011-08-30 14:42:21 +10:00
58 changed files with 2121 additions and 977 deletions

View File

@@ -14,7 +14,8 @@ PKGCONFIG += \
gstreamer-base-0.10 \ gstreamer-base-0.10 \
gstreamer-interfaces-0.10 \ gstreamer-interfaces-0.10 \
gstreamer-audio-0.10 \ gstreamer-audio-0.10 \
gstreamer-video-0.10 gstreamer-video-0.10 \
gstreamer-pbutils-0.10
LIBS += -lgstphotography-0.10 LIBS += -lgstphotography-0.10

View File

@@ -43,6 +43,8 @@
#include <gst/interfaces/photography.h> #include <gst/interfaces/photography.h>
#include <gst/interfaces/photography-enumtypes.h> #include <gst/interfaces/photography-enumtypes.h>
#include <gst/pbutils/pbutils.h>
#include <gst/pbutils/encoding-profile.h>
int main(int argc, char** argv) int main(int argc, char** argv)
{ {

View File

@@ -136,8 +136,8 @@ FocusScope {
} }
ListElement { ListElement {
icon: "images/camera_white_balance_incandescent.png" icon: "images/camera_white_balance_incandescent.png"
value: Camera.WhiteBalanceIncandescent value: Camera.WhiteBalanceTungsten
text: "Incandescent" text: "Tungsten"
} }
ListElement { ListElement {
icon: "images/camera_white_balance_flourescent.png" icon: "images/camera_white_balance_flourescent.png"

View File

@@ -47,8 +47,8 @@
#include "qdeclarativemediametadata_p.h" #include "qdeclarativemediametadata_p.h"
#include "qdeclarativeaudio_p.h" #include "qdeclarativeaudio_p.h"
#include "qdeclarativevideooutput_p.h"
#if 0 #if 0
#include "qdeclarativevideo_p.h"
#include "qdeclarativecamera_p.h" #include "qdeclarativecamera_p.h"
#include "qdeclarativecamerapreviewprovider_p.h" #include "qdeclarativecamerapreviewprovider_p.h"
#endif #endif
@@ -67,9 +67,10 @@ public:
qmlRegisterType<QSoundEffect>(uri, 4, 0, "SoundEffect"); qmlRegisterType<QSoundEffect>(uri, 4, 0, "SoundEffect");
qmlRegisterType<QDeclarativeAudio>(uri, 4, 0, "Audio"); qmlRegisterType<QDeclarativeAudio>(uri, 4, 0, "Audio");
qmlRegisterType<QDeclarativeAudio>(uri, 4, 0, "MediaPlayer");
qmlRegisterType<QDeclarativeVideoOutput>(uri, 4, 0, "VideoOutput");
/* Disabled until ported to scenegraph */ /* Disabled until ported to scenegraph */
#if 0 #if 0
qmlRegisterType<QDeclarativeVideo>(uri, 4, 0, "Video");
qmlRegisterType<QDeclarativeCamera>(uri, 4, 0, "Camera"); qmlRegisterType<QDeclarativeCamera>(uri, 4, 0, "Camera");
#endif #endif
qmlRegisterType<QDeclarativeMediaMetaData>(); qmlRegisterType<QDeclarativeMediaMetaData>();

View File

@@ -12,11 +12,20 @@ HEADERS += \
qdeclarativeaudio_p.h \ qdeclarativeaudio_p.h \
qdeclarativemediabase_p.h \ qdeclarativemediabase_p.h \
qdeclarativemediametadata_p.h \ qdeclarativemediametadata_p.h \
qdeclarativevideooutput_p.h \
qsgvideonode_p.h \
qsgvideonode_i420.h \
qsgvideonode_rgb32.h \
SOURCES += \ SOURCES += \
multimedia.cpp \ multimedia.cpp \
qdeclarativeaudio.cpp \ qdeclarativeaudio.cpp \
qdeclarativemediabase.cpp \ qdeclarativemediabase.cpp \
qdeclarativevideooutput.cpp \
qsgvideonode.cpp \
qsgvideonode_i420.cpp \
qsgvideonode_rgb32.cpp \
disabled { disabled {
HEADERS += \ HEADERS += \

View File

@@ -305,6 +305,7 @@ QDeclarativeAudio::Error QDeclarativeAudio::error() const
void QDeclarativeAudio::classBegin() void QDeclarativeAudio::classBegin()
{ {
setObject(this); setObject(this);
emit mediaObjectChanged();
} }
void QDeclarativeAudio::componentComplete() void QDeclarativeAudio::componentComplete()

View File

@@ -84,6 +84,7 @@ class QDeclarativeAudio : public QObject, public QDeclarativeMediaBase, public Q
Q_PROPERTY(Error error READ error NOTIFY errorChanged) Q_PROPERTY(Error error READ error NOTIFY errorChanged)
Q_PROPERTY(QString errorString READ errorString NOTIFY errorChanged) Q_PROPERTY(QString errorString READ errorString NOTIFY errorChanged)
Q_PROPERTY(QDeclarativeMediaMetaData *metaData READ metaData CONSTANT) Q_PROPERTY(QDeclarativeMediaMetaData *metaData READ metaData CONSTANT)
Q_PROPERTY(QObject *mediaObject READ mediaObject NOTIFY mediaObjectChanged SCRIPTABLE false DESIGNABLE false)
Q_ENUMS(Status) Q_ENUMS(Status)
Q_ENUMS(Error) Q_ENUMS(Error)
Q_ENUMS(Loop) Q_ENUMS(Loop)
@@ -126,6 +127,8 @@ public:
void classBegin(); void classBegin();
void componentComplete(); void componentComplete();
QObject *mediaObject() { return m_mediaObject; }
public Q_SLOTS: public Q_SLOTS:
void play(); void play();
void pause(); void pause();
@@ -159,6 +162,8 @@ Q_SIGNALS:
void errorChanged(); void errorChanged();
void error(QDeclarativeAudio::Error error, const QString &errorString); void error(QDeclarativeAudio::Error error, const QString &errorString);
void mediaObjectChanged();
private Q_SLOTS: private Q_SLOTS:
void _q_error(int, const QString &); void _q_error(int, const QString &);

View File

@@ -0,0 +1,368 @@
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qdeclarativevideooutput_p.h"
#include "qsgvideonode_p.h"
#include "qsgvideonode_i420.h"
#include "qsgvideonode_rgb32.h"
#include <QtDeclarative/qsgitem.h>
#include <QtMultimediaKit/QAbstractVideoSurface>
#include <QtMultimediaKit/qmediaservice.h>
#include <QtMultimediaKit/qvideorenderercontrol.h>
#include <QtMultimediaKit/qvideosurfaceformat.h>
#include <QtCore/qmetaobject.h>
//#define DEBUG_VIDEOITEM
class QSGVideoItemSurface : public QAbstractVideoSurface
{
public:
QSGVideoItemSurface(QDeclarativeVideoOutput *item, QObject *parent = 0) :
QAbstractVideoSurface(parent),
m_item(item)
{
}
~QSGVideoItemSurface()
{
}
QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const
{
QList<QVideoFrame::PixelFormat> formats;
foreach (QSGVideoNodeFactory* factory, m_item->m_videoNodeFactories) {
formats.append(factory->supportedPixelFormats(handleType));
}
return formats;
}
bool start(const QVideoSurfaceFormat &format)
{
#ifdef DEBUG_VIDEOITEM
qDebug() << Q_FUNC_INFO << format;
#endif
if (!supportedPixelFormats(format.handleType()).contains(format.pixelFormat()))
return false;
return QAbstractVideoSurface::start(format);
}
virtual bool present(const QVideoFrame &frame)
{
if (!frame.isValid()) {
qWarning() << Q_FUNC_INFO << "I'm getting bad frames here...";
return false;
}
m_item->present(frame);
return true;
}
private:
QDeclarativeVideoOutput *m_item;
};
/*!
\qmlclass VideoOutput QDeclarativeVideoOutput
\brief The VideoOutput element allows you to render video or camera viewfinder.
\ingroup qml-multimedia
This element is part of the \bold{QtMultimediaKit 4.0} module.
\qml
import QtQuick 2.0
import Qt.multimediakit 4.0
Rectangle {
width: 800
height: 600
color: "black"
MediaPlayer {
id: player
source: "file://video.webm"
playing: true
}
VideoOutput {
id: videoOutput
source: player
anchors.fill: parent
}
}
\endqml
The VideoOutput item supports untransformed, stretched, and uniformly scaled video presentation.
For a description of stretched uniformly scaled presentation, see the \l fillMode property
description.
\sa MediaPlayer, Camera
*/
/*!
\internal
\class QDeclarativeVideoOutput
\brief The QDeclarativeVideoOutput class provides a video output item.
*/
QDeclarativeVideoOutput::QDeclarativeVideoOutput(QSGItem *parent) :
QSGItem(parent),
m_fillMode(PreserveAspectFit)
{
setFlag(ItemHasContents, true);
m_surface = new QSGVideoItemSurface(this);
connect(m_surface, SIGNAL(surfaceFormatChanged(QVideoSurfaceFormat)),
this, SLOT(_q_updateNativeSize(QVideoSurfaceFormat)), Qt::QueuedConnection);
m_videoNodeFactories.append(new QSGVideoNodeFactory_I420);
#ifndef QT_OPENGL_ES
m_videoNodeFactories.append(new QSGVideoNodeFactory_RGB32);
#endif
}
QDeclarativeVideoOutput::~QDeclarativeVideoOutput()
{
m_source.clear();
_q_updateMediaObject();
delete m_surface;
qDeleteAll(m_videoNodeFactories);
}
/*!
\qmlproperty variant VideoOutput::source
This property holds the source item providing the video frames like MediaPlayer or Camera.
*/
void QDeclarativeVideoOutput::setSource(QObject *source)
{
#ifdef DEBUG_VIDEOITEM
qDebug() << Q_FUNC_INFO << source;
#endif
if (source == m_source.data())
return;
if (m_source)
disconnect(0, m_source.data(), SLOT(_q_updateMediaObject()));
m_source = source;
if (m_source) {
const QMetaObject *metaObject = m_source.data()->metaObject();
const QMetaProperty mediaObjectProperty = metaObject->property(
metaObject->indexOfProperty("mediaObject"));
if (mediaObjectProperty.hasNotifySignal()) {
QMetaMethod method = mediaObjectProperty.notifySignal();
QMetaObject::connect(m_source.data(), method.methodIndex(),
this, this->metaObject()->indexOfSlot("updateMediaObject()"),
Qt::DirectConnection, 0);
}
}
_q_updateMediaObject();
emit sourceChanged();
}
void QDeclarativeVideoOutput::_q_updateMediaObject()
{
QMediaObject *mediaObject = 0;
if (m_source)
mediaObject = qobject_cast<QMediaObject*>(m_source.data()->property("mediaObject").value<QObject*>());
#ifdef DEBUG_VIDEOITEM
qDebug() << Q_FUNC_INFO << mediaObject;
#endif
if (m_mediaObject.data() == mediaObject)
return;
if (m_rendererControl) {
m_rendererControl.data()->setSurface(0);
m_service.data()->releaseControl(m_rendererControl.data());
}
m_mediaObject = mediaObject;
m_mediaObject.clear();
m_service.clear();
m_rendererControl.clear();
if (mediaObject) {
if (QMediaService *service = mediaObject->service()) {
if (QMediaControl *control = service->requestControl(QVideoRendererControl_iid)) {
if ((m_rendererControl = qobject_cast<QVideoRendererControl *>(control))) {
m_service = service;
m_mediaObject = mediaObject;
m_rendererControl.data()->setSurface(m_surface);
} else {
qWarning() << Q_FUNC_INFO << "Media service has no renderer control available";
service->releaseControl(control);
}
}
}
}
}
void QDeclarativeVideoOutput::present(const QVideoFrame &frame)
{
m_frame = frame;
update();
}
/*!
\qmlproperty enumeration VideoOutput::fillMode
Set this property to define how the video is scaled to fit the target area.
\list
\o Stretch - the video is scaled to fit.
\o PreserveAspectFit - the video is scaled uniformly to fit without cropping
\o PreserveAspectCrop - the video is scaled uniformly to fill, cropping if necessary
\endlist
The default fill mode is PreserveAspectFit.
*/
QDeclarativeVideoOutput::FillMode QDeclarativeVideoOutput::fillMode() const
{
return m_fillMode;
}
void QDeclarativeVideoOutput::setFillMode(FillMode mode)
{
if (mode == m_fillMode)
return;
m_fillMode = mode;
update();
emit fillModeChanged(mode);
}
void QDeclarativeVideoOutput::_q_updateNativeSize(const QVideoSurfaceFormat &format)
{
const QSize &size = format.sizeHint();
if (m_nativeSize != size) {
m_nativeSize = size;
setImplicitWidth(size.width());
setImplicitHeight(size.height());
}
}
void QDeclarativeVideoOutput::_q_updateGeometry()
{
QRectF rect(0, 0, width(), height());
if (m_nativeSize.isEmpty()) {
//this is necessary for item to receive the
//first paint event and configure video surface.
m_boundingRect = rect;
m_sourceRect = QRectF(0, 0, 1, 1);
} else if (m_fillMode == Stretch) {
m_boundingRect = rect;
m_sourceRect = QRectF(0, 0, 1, 1);
} else if (m_fillMode == PreserveAspectFit) {
QSizeF size = m_nativeSize;
size.scale(rect.size(), Qt::KeepAspectRatio);
m_boundingRect = QRectF(0, 0, size.width(), size.height());
m_boundingRect.moveCenter(rect.center());
m_sourceRect = QRectF(0, 0, 1, 1);
} else if (m_fillMode == PreserveAspectCrop) {
m_boundingRect = rect;
QSizeF size = rect.size();
size.scale(m_nativeSize, Qt::KeepAspectRatio);
m_sourceRect = QRectF(
0, 0, size.width() / m_nativeSize.width(), size.height() / m_nativeSize.height());
m_sourceRect.moveCenter(QPointF(0.5, 0.5));
}
}
QSGNode *QDeclarativeVideoOutput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
QSGVideoNode *videoNode = static_cast<QSGVideoNode *>(oldNode);
if (videoNode && videoNode->pixelFormat() != m_frame.pixelFormat()) {
#ifdef DEBUG_VIDEOITEM
qDebug() << "updatePaintNode: deleting old video node because frame format changed...";
#endif
delete videoNode;
videoNode = 0;
}
if (!m_frame.isValid()) {
#ifdef DEBUG_VIDEOITEM
qDebug() << "updatePaintNode: no frames yet... aborting...";
#endif
return 0;
}
if (videoNode == 0) {
foreach (QSGVideoNodeFactory* factory, m_videoNodeFactories) {
videoNode = factory->createNode(m_surface->surfaceFormat());
if (videoNode)
break;
}
}
if (videoNode == 0)
return 0;
_q_updateGeometry();
videoNode->setTexturedRectGeometry(m_boundingRect, m_sourceRect);
videoNode->setCurrentFrame(m_frame);
return videoNode;
}

View File

@@ -0,0 +1,115 @@
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QDECLARATIVEVIDEOOUTPUT_P_H
#define QDECLARATIVEVIDEOOUTPUT_P_H
#include <qsgitem.h>
#include <QtMultimediaKit/qvideoframe.h>
#include <QtMultimediaKit/qmediaobject.h>
#include <QtCore/qsharedpointer.h>
#include "qsgvideonode_p.h"
class QSGVideoItemSurface;
class QVideoRendererControl;
class QMediaService;
class QVideoSurfaceFormat;
class QDeclarativeVideoOutput : public QSGItem
{
Q_OBJECT
Q_DISABLE_COPY(QDeclarativeVideoOutput)
Q_PROPERTY(QObject* source READ source WRITE setSource NOTIFY sourceChanged)
Q_PROPERTY(FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged)
Q_ENUMS(FillMode)
public:
enum FillMode
{
Stretch = Qt::IgnoreAspectRatio,
PreserveAspectFit = Qt::KeepAspectRatio,
PreserveAspectCrop = Qt::KeepAspectRatioByExpanding
};
QDeclarativeVideoOutput(QSGItem *parent = 0);
~QDeclarativeVideoOutput();
QObject *source() const { return m_source.data(); }
void setSource(QObject *source);
FillMode fillMode() const;
void setFillMode(FillMode mode);
Q_SIGNALS:
void sourceChanged();
void fillModeChanged(QDeclarativeVideoOutput::FillMode);
protected:
QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
private Q_SLOTS:
void _q_updateMediaObject();
void _q_updateNativeSize(const QVideoSurfaceFormat&);
void _q_updateGeometry();
private:
void present(const QVideoFrame &frame);
friend class QSGVideoItemSurface;
QWeakPointer<QObject> m_source;
QWeakPointer<QMediaObject> m_mediaObject;
QWeakPointer<QMediaService> m_service;
QWeakPointer<QVideoRendererControl> m_rendererControl;
QList<QSGVideoNodeFactory*> m_videoNodeFactories;
QSGVideoItemSurface *m_surface;
QVideoFrame m_frame;
FillMode m_fillMode;
QSize m_nativeSize;
QRectF m_boundingRect;
QRectF m_sourceRect;
};
#endif // QDECLARATIVEVIDEOOUTPUT_H

View File

@@ -0,0 +1,67 @@
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qsgvideonode_p.h"
QSGVideoNode::QSGVideoNode()
{
}
void QSGVideoNode::setTexturedRectGeometry(const QRectF &rect, const QRectF &textureRect)
{
if (rect == m_rect && textureRect == m_textureRect)
return;
m_rect = rect;
m_textureRect = textureRect;
QSGGeometry *g = geometry();
if (g == 0) {
g = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4);
QSGGeometry::updateTexturedRectGeometry(g, rect, textureRect);
setGeometry(g);
} else {
QSGGeometry::updateTexturedRectGeometry(g, rect, textureRect);
}
markDirty(DirtyGeometry);
}

View File

@@ -0,0 +1,295 @@
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qsgvideonode_i420.h"
#include <QtDeclarative/qsgtexturematerial.h>
#include <QtDeclarative/qsgmaterial.h>
#include <QtOpenGL/qglshaderprogram.h>
QList<QVideoFrame::PixelFormat> QSGVideoNodeFactory_I420::supportedPixelFormats(
QAbstractVideoBuffer::HandleType handleType) const
{
QList<QVideoFrame::PixelFormat> formats;
if (handleType == QAbstractVideoBuffer::NoHandle)
formats << QVideoFrame::Format_YUV420P << QVideoFrame::Format_YV12;
return formats;
}
QSGVideoNode *QSGVideoNodeFactory_I420::createNode(const QVideoSurfaceFormat &format)
{
if (supportedPixelFormats(format.handleType()).contains(format.pixelFormat()))
return new QSGVideoNode_I420(format);
return 0;
}
class QSGVideoMaterialShader_YUV420 : public QSGMaterialShader
{
public:
void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
virtual char const *const *attributeNames() const {
static const char *names[] = {
"qt_VertexPosition",
"qt_VertexTexCoord",
0
};
return names;
}
protected:
virtual const char *vertexShader() const {
const char *shader =
"uniform highp mat4 qt_Matrix; \n"
"attribute highp vec4 qt_VertexPosition; \n"
"attribute highp vec2 qt_VertexTexCoord; \n"
"varying highp vec2 qt_TexCoord; \n"
"void main() { \n"
" qt_TexCoord = qt_VertexTexCoord; \n"
" gl_Position = qt_Matrix * qt_VertexPosition; \n"
"}";
return shader;
}
virtual const char *fragmentShader() const {
static const char *shader =
"uniform sampler2D yTexture;"
"uniform sampler2D uTexture;"
"uniform sampler2D vTexture;"
"uniform mediump mat4 colorMatrix;"
"uniform lowp float opacity;"
""
"varying highp vec2 qt_TexCoord;"
""
"void main()"
"{"
" mediump float Y = texture2D(yTexture, qt_TexCoord).r;"
" mediump float U = texture2D(uTexture, qt_TexCoord).r;"
" mediump float V = texture2D(vTexture, qt_TexCoord).r;"
" mediump vec4 color = vec4(Y, U, V, 1.);"
" gl_FragColor = colorMatrix * color * opacity;"
"}";
return shader;
}
virtual void initialize() {
m_id_matrix = program()->uniformLocation("qt_Matrix");
m_id_yTexture = program()->uniformLocation("yTexture");
m_id_uTexture = program()->uniformLocation("uTexture");
m_id_vTexture = program()->uniformLocation("vTexture");
m_id_colorMatrix = program()->uniformLocation("colorMatrix");
m_id_opacity = program()->uniformLocation("opacity");
}
int m_id_matrix;
int m_id_yTexture;
int m_id_uTexture;
int m_id_vTexture;
int m_id_colorMatrix;
int m_id_opacity;
};
class QSGVideoMaterial_YUV420 : public QSGMaterial
{
public:
QSGVideoMaterial_YUV420(const QVideoSurfaceFormat &format)
{
switch (format.yCbCrColorSpace()) {
case QVideoSurfaceFormat::YCbCr_JPEG:
colorMatrix = QMatrix4x4(
1.0, 0.000, 1.402, -0.701,
1.0, -0.344, -0.714, 0.529,
1.0, 1.772, 0.000, -0.886,
0.0, 0.000, 0.000, 1.0000);
break;
case QVideoSurfaceFormat::YCbCr_BT709:
case QVideoSurfaceFormat::YCbCr_xvYCC709:
colorMatrix = QMatrix4x4(
1.164, 0.000, 1.793, -0.5727,
1.164, -0.534, -0.213, 0.3007,
1.164, 2.115, 0.000, -1.1302,
0.0, 0.000, 0.000, 1.0000);
break;
default: //BT 601:
colorMatrix = QMatrix4x4(
1.164, 0.000, 1.596, -0.8708,
1.164, -0.392, -0.813, 0.5296,
1.164, 2.017, 0.000, -1.081,
0.0, 0.000, 0.000, 1.0000);
}
setFlag(Blending, false);
}
virtual QSGMaterialType *type() const {
static QSGMaterialType theType;
return &theType;
}
virtual QSGMaterialShader *createShader() const {
return new QSGVideoMaterialShader_YUV420;
}
virtual int compare(const QSGMaterial *other) const {
const QSGVideoMaterial_YUV420 *m = static_cast<const QSGVideoMaterial_YUV420 *>(other);
int d = idY - m->idY;
if (d)
return d;
else if ((d = idU - m->idU) != 0)
return d;
else
return idV - m->idV;
}
void updateBlending() {
setFlag(Blending, qFuzzyCompare(opacity, 1.0) ? false : true);
}
GLuint idY;
GLuint idU;
GLuint idV;
qreal opacity;
QMatrix4x4 colorMatrix;
};
QSGVideoNode_I420::QSGVideoNode_I420(const QVideoSurfaceFormat &format) :
m_width(0),
m_height(0),
m_format(format)
{
m_material = new QSGVideoMaterial_YUV420(format);
setMaterial(m_material);
m_material->opacity = 1;
}
QSGVideoNode_I420::~QSGVideoNode_I420()
{
if (m_width != 0 && m_height != 0)
glDeleteTextures(3, m_id);
}
void QSGVideoNode_I420::setCurrentFrame(const QVideoFrame &frame)
{
m_frame = frame;
m_frame.map(QAbstractVideoBuffer::ReadOnly);
int fw = frame.width();
int fh = frame.height();
// Frame has changed size, recreate textures...
if (fw != m_width || fh != m_height) {
if (m_width != 0 && m_height != 0)
glDeleteTextures(3, m_id);
glGenTextures(3, m_id);
m_width = fw;
m_height = fh;
m_material->idY = m_id[0];
m_material->idU = m_id[1];
m_material->idV = m_id[2];
}
const uchar *bits = frame.bits();
int bpl = frame.bytesPerLine();
int bpl2 = (bpl / 2 + 3) & ~3;
int offsetU = bpl * fh;
int offsetV = bpl * fh + bpl2 * fh / 2;
if (m_frame.pixelFormat() == QVideoFrame::Format_YV12)
qSwap(offsetU, offsetV);
bindTexture(m_id[0], GL_TEXTURE0, fw, fh, bits);
bindTexture(m_id[1], GL_TEXTURE1, fw/2, fh / 2, bits + offsetU);
bindTexture(m_id[2], GL_TEXTURE2, fw/2, fh / 2, bits + offsetV);
m_frame.unmap();
markDirty(DirtyMaterial);
}
void QSGVideoNode_I420::bindTexture(int id, int unit, int w, int h, const uchar *bits)
{
QGLFunctions *functions = QGLContext::currentContext()->functions();
functions->glActiveTexture(unit);
glBindTexture(GL_TEXTURE_2D, id);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, bits);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
void QSGVideoMaterialShader_YUV420::updateState(const RenderState &state,
QSGMaterial *newMaterial,
QSGMaterial *oldMaterial)
{
Q_UNUSED(oldMaterial);
QGLFunctions *functions = state.context()->functions();
QSGVideoMaterial_YUV420 *mat = static_cast<QSGVideoMaterial_YUV420 *>(newMaterial);
program()->setUniformValue(m_id_yTexture, 0);
program()->setUniformValue(m_id_uTexture, 1);
program()->setUniformValue(m_id_vTexture, 2);
functions->glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mat->idY);
functions->glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, mat->idU);
functions->glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, mat->idV);
program()->setUniformValue(m_id_colorMatrix, mat->colorMatrix);
if (state.isOpacityDirty()) {
mat->opacity = state.opacity();
program()->setUniformValue(m_id_opacity, GLfloat(mat->opacity));
}
if (state.isMatrixDirty())
program()->setUniformValue(m_id_matrix, state.combinedMatrix());
}

View File

@@ -0,0 +1,79 @@
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QSGVIDEONODE_I420_H
#define QSGVIDEONODE_I420_H
#include "qsgvideonode_p.h"
#include <QtMultimediaKit/qvideosurfaceformat.h>
class QSGVideoMaterial_YUV420;
class QSGVideoNode_I420 : public QSGVideoNode
{
public:
QSGVideoNode_I420(const QVideoSurfaceFormat &format);
~QSGVideoNode_I420();
virtual QVideoFrame::PixelFormat pixelFormat() const {
return m_format.pixelFormat();
}
void setCurrentFrame(const QVideoFrame &frame);
private:
void bindTexture(int id, int unit, int w, int h, const uchar *bits);
int m_width;
int m_height;
GLuint m_id[3];
QVideoSurfaceFormat m_format;
QSGVideoMaterial_YUV420 *m_material;
QVideoFrame m_frame;
};
class QSGVideoNodeFactory_I420 : public QSGVideoNodeFactory {
public:
QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const;
QSGVideoNode *createNode(const QVideoSurfaceFormat &format);
};
#endif // QSGVIDEONODE_I420_H

View File

@@ -0,0 +1,72 @@
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QSGVIDEONODE_P_H
#define QSGVIDEONODE_P_H
#include <QtDeclarative/qsgnode.h>
#include <QtMultimediaKit/qvideoframe.h>
#include <QtMultimediaKit/qvideosurfaceformat.h>
#include <QtOpenGL/qglfunctions.h>
class QSGVideoNode : public QSGGeometryNode
{
public:
QSGVideoNode();
virtual void setCurrentFrame(const QVideoFrame &frame) = 0;
virtual QVideoFrame::PixelFormat pixelFormat() const = 0;
void setTexturedRectGeometry(const QRectF &boundingRect, const QRectF &textureRect);
private:
QRectF m_rect;
QRectF m_textureRect;
};
class QSGVideoNodeFactory {
public:
virtual QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const = 0;
virtual QSGVideoNode *createNode(const QVideoSurfaceFormat &format) = 0;
};
#endif // QSGVIDEONODE_H

View File

@@ -0,0 +1,142 @@
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qsgvideonode_rgb32.h"
#include <QtDeclarative/qsgtexture.h>
QList<QVideoFrame::PixelFormat> QSGVideoNodeFactory_RGB32::supportedPixelFormats(
QAbstractVideoBuffer::HandleType handleType) const
{
QList<QVideoFrame::PixelFormat> formats;
if (handleType == QAbstractVideoBuffer::NoHandle)
formats.append(QVideoFrame::Format_RGB32);
return formats;
}
QSGVideoNode *QSGVideoNodeFactory_RGB32::createNode(const QVideoSurfaceFormat &format)
{
if (supportedPixelFormats(format.handleType()).contains(format.pixelFormat()))
return new QSGVideoNode_RGB32();
return 0;
}
class QSGVideoTexture_RGB32 : public QSGTexture
{
public:
QSGVideoTexture_RGB32();
int textureId() const { return m_textureId; }
QSize textureSize() const { return m_size; }
bool hasAlphaChannel() const { return false; }
bool hasMipmaps() const { return false; }
void setCurrentFrame(const QVideoFrame &frame) { m_frame = frame; }
//QRectF textureSubRect() const;
void bind();
private:
QVideoFrame m_frame;
GLuint m_textureId;
QSize m_size;
};
QSGVideoTexture_RGB32::QSGVideoTexture_RGB32()
: QSGTexture()
, m_textureId(0)
{
}
void QSGVideoTexture_RGB32::bind()
{
if (m_frame.isValid()) {
if (m_size != m_frame.size()) {
if (m_textureId)
glDeleteTextures(1, &m_textureId);
glGenTextures(1, &m_textureId);
m_size = m_frame.size();
}
if (m_frame.map(QAbstractVideoBuffer::ReadOnly)) {
QGLFunctions *functions = QGLContext::currentContext()->functions();
const uchar *bits = m_frame.bits();
functions->glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_textureId);
#ifdef QT_OPENGL_ES
qWarning() << "RGB video doesn't work on GL ES\n";
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
m_size.width(), m_size.height(),
0, GL_BGRA, GL_UNSIGNED_BYTE, bits);
#endif
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
m_frame.unmap();
}
m_frame = QVideoFrame();
updateBindOptions(true);
} else {
glBindTexture(GL_TEXTURE_2D, m_textureId);
updateBindOptions(false);
}
}
QSGVideoNode_RGB32::QSGVideoNode_RGB32()
{
setMaterial(&m_material);
m_texture = new QSGVideoTexture_RGB32();
m_material.setTexture(m_texture);
m_material.setFiltering(QSGTexture::Linear);
}
void QSGVideoNode_RGB32::setCurrentFrame(const QVideoFrame &frame)
{
m_texture->setCurrentFrame(frame);
markDirty(DirtyMaterial);
}

View File

@@ -0,0 +1,71 @@
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QSGVIDEONODE_RGB32_H
#define QSGVIDEONODE_RGB32_H
#include "qsgvideonode_p.h"
#include <QtDeclarative/qsgtexturematerial.h>
class QSGVideoTexture_RGB32;
class QSGVideoNode_RGB32 : public QSGVideoNode
{
public:
QSGVideoNode_RGB32();
void setCurrentFrame(const QVideoFrame &frame);
QVideoFrame::PixelFormat pixelFormat() const { return QVideoFrame::Format_RGB32; }
private:
QSGTextureMaterial m_material;
QSGVideoTexture_RGB32 *m_texture;
};
class QSGVideoNodeFactory_RGB32 : public QSGVideoNodeFactory {
public:
QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const;
QSGVideoNode *createNode(const QVideoSurfaceFormat &format);
};
#endif // QSGVIDEONODE_RGB32_H

View File

@@ -109,6 +109,7 @@ public:
void _q_error(int id, int error, const QString &errorString); void _q_error(int id, int error, const QString &errorString);
void _q_readyChanged(bool); void _q_readyChanged(bool);
void _q_serviceDestroyed();
void unsetError() { error = QCameraImageCapture::NoError; errorString.clear(); } void unsetError() { error = QCameraImageCapture::NoError; errorString.clear(); }
@@ -141,6 +142,14 @@ void QCameraImageCapturePrivate::_q_readyChanged(bool ready)
emit q->readyForCaptureChanged(ready); emit q->readyForCaptureChanged(ready);
} }
void QCameraImageCapturePrivate::_q_serviceDestroyed()
{
mediaObject = 0;
control = 0;
encoderControl = 0;
captureDestinationControl = 0;
bufferFormatControl = 0;
}
/*! /*!
Constructs a media recorder which records the media produced by \a mediaObject. Constructs a media recorder which records the media produced by \a mediaObject.
@@ -225,6 +234,8 @@ bool QCameraImageCapture::setMediaObject(QMediaObject *mediaObject)
service->releaseControl(d->captureDestinationControl); service->releaseControl(d->captureDestinationControl);
if (d->bufferFormatControl) if (d->bufferFormatControl)
service->releaseControl(d->bufferFormatControl); service->releaseControl(d->bufferFormatControl);
disconnect(service, SIGNAL(destroyed()), this, SLOT(_q_serviceDestroyed()));
} }
} }
@@ -269,6 +280,8 @@ bool QCameraImageCapture::setMediaObject(QMediaObject *mediaObject)
this, SIGNAL(bufferFormatChanged(QVideoFrame::PixelFormat))); this, SIGNAL(bufferFormatChanged(QVideoFrame::PixelFormat)));
} }
connect(service, SIGNAL(destroyed()), this, SLOT(_q_serviceDestroyed()));
return true; return true;
} }
} }

View File

@@ -145,6 +145,7 @@ private:
Q_DECLARE_PRIVATE(QCameraImageCapture) Q_DECLARE_PRIVATE(QCameraImageCapture)
Q_PRIVATE_SLOT(d_func(), void _q_error(int, int, const QString &)) Q_PRIVATE_SLOT(d_func(), void _q_error(int, int, const QString &))
Q_PRIVATE_SLOT(d_func(), void _q_readyChanged(bool)) Q_PRIVATE_SLOT(d_func(), void _q_readyChanged(bool))
Q_PRIVATE_SLOT(d_func(), void _q_serviceDestroyed())
}; };
Q_DECLARE_OPERATORS_FOR_FLAGS(QCameraImageCapture::CaptureDestinations) Q_DECLARE_OPERATORS_FOR_FLAGS(QCameraImageCapture::CaptureDestinations)

View File

@@ -343,7 +343,6 @@ void QCameraImageProcessing::setDenoisingLevel(int level)
\value WhiteBalanceShade Shade white balance mode. \value WhiteBalanceShade Shade white balance mode.
\value WhiteBalanceTungsten Tungsten white balance mode. \value WhiteBalanceTungsten Tungsten white balance mode.
\value WhiteBalanceFluorescent Fluorescent white balance mode. \value WhiteBalanceFluorescent Fluorescent white balance mode.
\value WhiteBalanceIncandescent Incandescent white balance mode.
\value WhiteBalanceFlash Flash white balance mode. \value WhiteBalanceFlash Flash white balance mode.
\value WhiteBalanceSunset Sunset white balance mode. \value WhiteBalanceSunset Sunset white balance mode.
\value WhiteBalanceVendor Vendor defined white balance mode. \value WhiteBalanceVendor Vendor defined white balance mode.

View File

@@ -71,9 +71,8 @@ public:
WhiteBalanceShade = 4, WhiteBalanceShade = 4,
WhiteBalanceTungsten = 5, WhiteBalanceTungsten = 5,
WhiteBalanceFluorescent = 6, WhiteBalanceFluorescent = 6,
WhiteBalanceIncandescent = 7, WhiteBalanceFlash = 7,
WhiteBalanceFlash = 8, WhiteBalanceSunset = 8,
WhiteBalanceSunset = 9,
WhiteBalanceVendor = 1000 WhiteBalanceVendor = 1000
}; };

View File

@@ -168,7 +168,12 @@ void QMediaRecorderPrivate::_q_error(int error, const QString &errorString)
void QMediaRecorderPrivate::_q_serviceDestroyed() void QMediaRecorderPrivate::_q_serviceDestroyed()
{ {
q_func()->setMediaObject(0); mediaObject = 0;
control = 0;
formatControl = 0;
audioControl = 0;
videoControl = 0;
metaDataControl = 0;
} }
void QMediaRecorderPrivate::_q_notify() void QMediaRecorderPrivate::_q_notify()

View File

@@ -41,68 +41,14 @@
#include "camerabinaudioencoder.h" #include "camerabinaudioencoder.h"
#include "camerabincontainer.h" #include "camerabincontainer.h"
#include "qgstcodecsinfo.h"
#include <QtCore/qdebug.h> #include <QtCore/qdebug.h>
CameraBinAudioEncoder::CameraBinAudioEncoder(QObject *parent) CameraBinAudioEncoder::CameraBinAudioEncoder(QObject *parent)
:QAudioEncoderControl(parent) :QAudioEncoderControl(parent),
m_codecs(QGstCodecsInfo::AudioEncoder)
{ {
QList<QByteArray> codecCandidates;
#if defined(Q_WS_MAEMO_6)
codecCandidates << "audio/AAC" << "audio/PCM" << "audio/AMR" << "audio/AMR-WB" << "audio/speex"
<< "audio/ADPCM" << "audio/iLBC" << "audio/vorbis" << "audio/mpeg" << "audio/FLAC";
m_elementNames["audio/AAC"] = "nokiaaacenc";
m_elementNames["audio/speex"] = "speexenc";
m_elementNames["audio/PCM"] = "audioresample";
m_elementNames["audio/AMR"] = "nokiaamrnbenc";
m_elementNames["audio/AMR-WB"] = "nokiaamrwbenc";
m_elementNames["audio/ADPCM"] = "nokiaadpcmenc";
m_elementNames["audio/iLBC"] = "nokiailbcenc";
m_elementNames["audio/vorbis"] = "vorbisenc";
m_elementNames["audio/FLAC"] = "flacenc";
m_elementNames["audio/mpeg"] = "ffenc_mp2";
#else
codecCandidates << "audio/mpeg" << "audio/vorbis" << "audio/speex" << "audio/GSM"
<< "audio/PCM" << "audio/AMR" << "audio/AMR-WB";
m_elementNames["audio/mpeg"] = "lamemp3enc";
m_elementNames["audio/vorbis"] = "vorbisenc";
m_elementNames["audio/speex"] = "speexenc";
m_elementNames["audio/GSM"] = "gsmenc";
m_elementNames["audio/PCM"] = "audioresample";
m_elementNames["audio/AMR"] = "amrnbenc";
m_elementNames["audio/AMR-WB"] = "amrwbenc";
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();
#endif
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,
CameraBinContainer::supportedStreamTypes(factory, GST_PAD_SRC));
gst_object_unref(GST_OBJECT(factory));
}
}
} }
CameraBinAudioEncoder::~CameraBinAudioEncoder() CameraBinAudioEncoder::~CameraBinAudioEncoder()
@@ -111,12 +57,12 @@ CameraBinAudioEncoder::~CameraBinAudioEncoder()
QStringList CameraBinAudioEncoder::supportedAudioCodecs() const QStringList CameraBinAudioEncoder::supportedAudioCodecs() const
{ {
return m_codecs; return m_codecs.supportedCodecs();
} }
QString CameraBinAudioEncoder::codecDescription(const QString &codecName) const QString CameraBinAudioEncoder::codecDescription(const QString &codecName) const
{ {
return m_codecDescriptions.value(codecName); return m_codecs.codecDescription(codecName);
} }
QStringList CameraBinAudioEncoder::supportedEncodingOptions(const QString &codec) const QStringList CameraBinAudioEncoder::supportedEncodingOptions(const QString &codec) const
@@ -165,129 +111,19 @@ void CameraBinAudioEncoder::resetActualSettings()
m_audioSettings = m_userSettings; m_audioSettings = m_userSettings;
} }
GstElement *CameraBinAudioEncoder::createEncoder() GstEncodingProfile *CameraBinAudioEncoder::createProfile()
{ {
QString codec = m_audioSettings.codec(); QString codec = m_audioSettings.codec();
QByteArray encoderElementName = m_elementNames.value(codec); GstCaps *caps;
GstElement *encoderElement = gst_element_factory_make(encoderElementName.constData(), NULL);
if (!encoderElement)
return 0;
GstBin * encoderBin = GST_BIN(gst_bin_new("audio-encoder-bin")); if (codec.isEmpty())
GstElement *capsFilter = gst_element_factory_make("capsfilter", NULL); caps = gst_caps_new_any();
else
gst_bin_add(encoderBin, capsFilter); caps = gst_caps_from_string(codec.toLatin1());
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);
g_object_set(G_OBJECT(capsFilter), "caps", caps, NULL);
}
if (encoderElement) {
if (m_audioSettings.encodingMode() == QtMultimediaKit::ConstantQualityEncoding) {
QtMultimediaKit::EncodingQuality qualityValue = m_audioSettings.quality();
if (encoderElementName == "lamemp3enc") {
g_object_set(G_OBJECT(encoderElement), "target", 0, NULL); //constant quality mode
qreal quality[] = {
10.0, //VeryLow
6.0, //Low
4.0, //Normal
2.0, //High
0.0 //VeryHigh
};
g_object_set(G_OBJECT(encoderElement), "quality", quality[qualityValue], NULL);
} else if (encoderElementName == "ffenc_mp2") {
int quality[] = {
8000, //VeryLow
64000, //Low
128000, //Normal
192000, //High
320000 //VeryHigh
};
g_object_set(G_OBJECT(encoderElement), "bitrate", 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) {
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);
} return (GstEncodingProfile *)gst_encoding_audio_profile_new(
caps,
NULL, //preset
QSet<QString> CameraBinAudioEncoder::supportedStreamTypes(const QString &codecName) const NULL, //restriction
{ 0); //presence
return m_streamTypes.value(codecName);
} }

View File

@@ -50,8 +50,11 @@ class CameraBinSession;
#include <QtCore/qset.h> #include <QtCore/qset.h>
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/pbutils/pbutils.h>
#include <gst/pbutils/encoding-profile.h>
#include <qaudioformat.h> #include <qaudioformat.h>
#include "qgstcodecsinfo.h"
QT_USE_NAMESPACE QT_USE_NAMESPACE
@@ -77,27 +80,19 @@ public:
QAudioEncoderSettings audioSettings() const; QAudioEncoderSettings audioSettings() const;
void setAudioSettings(const QAudioEncoderSettings&); void setAudioSettings(const QAudioEncoderSettings&);
GstElement *createEncoder();
QSet<QString> supportedStreamTypes(const QString &codecName) const;
void setActualAudioSettings(const QAudioEncoderSettings&); void setActualAudioSettings(const QAudioEncoderSettings&);
void resetActualSettings(); void resetActualSettings();
GstEncodingProfile *createProfile();
Q_SIGNALS: Q_SIGNALS:
void settingsChanged(); void settingsChanged();
private: private:
QStringList m_codecs; QGstCodecsInfo m_codecs;
QMap<QString,QByteArray> m_elementNames;
QMap<QString,QString> m_codecDescriptions;
QMap<QString,QStringList> m_codecOptions; QMap<QString,QStringList> m_codecOptions;
QMap<QString, QMap<QString, QVariant> > m_options; QMap<QString, QMap<QString, QVariant> > m_options;
QMap<QString, QSet<QString> > m_streamTypes;
QAudioEncoderSettings m_audioSettings; QAudioEncoderSettings m_audioSettings;
QAudioEncoderSettings m_userSettings; QAudioEncoderSettings m_userSettings;
}; };

View File

@@ -40,83 +40,90 @@
****************************************************************************/ ****************************************************************************/
#include "camerabincontainer.h" #include "camerabincontainer.h"
#include <QtCore/qregexp.h>
#include <QtCore/qdebug.h> #include <QtCore/qdebug.h>
CameraBinContainer::CameraBinContainer(QObject *parent) CameraBinContainer::CameraBinContainer(QObject *parent)
:QMediaContainerControl(parent) :QMediaContainerControl(parent),
m_supportedContainers(QGstCodecsInfo::Muxer)
{ {
QList<QByteArray> formatCandidates; //extension for containers hard to guess from mimetype
formatCandidates << "mp4" << "ogg" << "wav" << "amr" << "mkv" m_fileExtensions["video/x-matroska"] = "mkv";
<< "avi" << "3gp" << "3gp2" << "webm" << "mjpeg" << "asf" << "mov"; m_fileExtensions["video/quicktime"] = "mov";
m_fileExtensions["video/x-msvideo"] = "avi";
m_fileExtensions["video/msvideo"] = "avi";
m_fileExtensions["audio/mpeg"] = "mp3";
m_fileExtensions["application/x-shockwave-flash"] = "swf";
m_fileExtensions["application/x-pn-realmedia"] = "rm";
}
QMap<QString,QByteArray> elementNames; QStringList CameraBinContainer::supportedContainers() const
{
return m_supportedContainers.supportedCodecs();
}
elementNames.insertMulti("mp4", "ffmux_mp4"); QString CameraBinContainer::containerDescription(const QString &formatMimeType) const
elementNames.insertMulti("mp4", "hantromp4mux"); {
elementNames.insertMulti("mp4", "mp4mux"); return m_supportedContainers.codecDescription(formatMimeType);
elementNames.insert("ogg", "oggmux"); }
elementNames["wav"] = "wavenc";
elementNames["amr"] = "ffmux_amr";
elementNames["mkv"] = "matroskamux";
elementNames["avi"] = "avimux";
elementNames["3gp"] = "ffmux_3gp";
elementNames["3gp2"] = "ffmux_3g2";
elementNames["webm"] = "webmmux";
elementNames["mjpeg"] = "ffmux_mjpeg";
elementNames["asf"] = "ffmux_asf";
elementNames["mov"] = "qtmux";
QSet<QString> allTypes; QString CameraBinContainer::containerMimeType() const
{
return m_format;
}
foreach(const QByteArray &formatName, formatCandidates) { void CameraBinContainer::setContainerMimeType(const QString &formatMimeType)
foreach(const QByteArray &elementName, elementNames.values(formatName)) { {
GstElementFactory *factory = gst_element_factory_find(elementName.constData()); m_format = formatMimeType;
if (factory) {
m_supportedContainers.append(formatName);
const gchar *descr = gst_element_factory_get_description(factory);
m_containerDescriptions.insert(formatName, QString::fromUtf8(descr));
if (m_userFormat != formatMimeType) {
if (formatName == QByteArray("raw")) { m_userFormat = formatMimeType;
m_streamTypes.insert(formatName, allTypes); emit settingsChanged();
} else {
QSet<QString> types = supportedStreamTypes(factory, GST_PAD_SINK);
m_streamTypes.insert(formatName, types);
allTypes.unite(types);
}
gst_object_unref(GST_OBJECT(factory));
m_elementNames.insert(formatName, elementName);
break;
}
}
} }
} }
QSet<QString> CameraBinContainer::supportedStreamTypes(GstElementFactory *factory, GstPadDirection direction) void CameraBinContainer::setActualContainer(const QString &formatMimeType)
{ {
QSet<QString> types; m_format = formatMimeType;
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;
} }
void CameraBinContainer::resetActualContainer()
QSet<QString> CameraBinContainer::supportedStreamTypes(const QString &container) const
{ {
return m_streamTypes.value(container); m_format = m_userFormat;
}
GstEncodingContainerProfile *CameraBinContainer::createProfile()
{
GstCaps *caps;
if (m_format.isEmpty())
caps = gst_caps_new_any();
else
caps = gst_caps_from_string(m_format.toLatin1());
return (GstEncodingContainerProfile *)gst_encoding_container_profile_new(
"camerabin2_profile",
(gchar *)"custom camera profile",
caps,
NULL); //preset
}
/*!
Suggest file extension for current container mimetype.
*/
QString CameraBinContainer::suggestedFileExtension() const
{
QString format = m_format.left(m_format.indexOf(','));
QString extension = m_fileExtensions.value(format);
if (!extension.isEmpty() || format.isEmpty())
return extension;
QRegExp rx("[-/]([\\w]+)$");
if (rx.indexIn(format) != -1)
extension = rx.cap(1);
return extension;
} }

View File

@@ -48,6 +48,10 @@
#include <QtCore/qset.h> #include <QtCore/qset.h>
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/pbutils/pbutils.h>
#include <gst/pbutils/encoding-profile.h>
#include "qgstcodecsinfo.h"
QT_USE_NAMESPACE QT_USE_NAMESPACE
@@ -58,35 +62,18 @@ public:
CameraBinContainer(QObject *parent); CameraBinContainer(QObject *parent);
virtual ~CameraBinContainer() {} virtual ~CameraBinContainer() {}
virtual QStringList supportedContainers() const { return m_supportedContainers; } virtual QStringList supportedContainers() const;
virtual QString containerMimeType() const { return m_format; } virtual QString containerDescription(const QString &formatMimeType) const;
virtual void setContainerMimeType(const QString &formatMimeType)
{
m_format = formatMimeType;
if (m_userFormat != formatMimeType) { virtual QString containerMimeType() const;
m_userFormat = formatMimeType; virtual void setContainerMimeType(const QString &formatMimeType);
emit settingsChanged();
}
}
void setActualContainer(const QString &formatMimeType) void setActualContainer(const QString &formatMimeType);
{ void resetActualContainer();
m_format = formatMimeType;
}
void resetActualContainer() QString suggestedFileExtension() const;
{
m_format = m_userFormat;
}
virtual QString containerDescription(const QString &formatMimeType) const { return m_containerDescriptions.value(formatMimeType); } GstEncodingContainerProfile *createProfile();
QByteArray formatElementName() const { return m_elementNames.value(containerMimeType()); }
QSet<QString> supportedStreamTypes(const QString &container) const;
static QSet<QString> supportedStreamTypes(GstElementFactory *factory, GstPadDirection direction);
Q_SIGNALS: Q_SIGNALS:
void settingsChanged(); void settingsChanged();
@@ -94,10 +81,9 @@ Q_SIGNALS:
private: private:
QString m_format; // backend selected format, using m_userFormat QString m_format; // backend selected format, using m_userFormat
QString m_userFormat; QString m_userFormat;
QStringList m_supportedContainers; QMap<QString, QString> m_fileExtensions;
QMap<QString,QByteArray> m_elementNames;
QMap<QString, QString> m_containerDescriptions; QGstCodecsInfo m_supportedContainers;
QMap<QString, QSet<QString> > m_streamTypes;
}; };
#endif // CAMERABINMEDIACONTAINERCONTROL_H #endif // CAMERABINMEDIACONTAINERCONTROL_H

View File

@@ -75,14 +75,6 @@ CameraBinControl::CameraBinControl(CameraBinSession *session)
connect(m_session, SIGNAL(stateChanged(QCamera::State)), connect(m_session, SIGNAL(stateChanged(QCamera::State)),
this, SLOT(updateStatus())); this, SLOT(updateStatus()));
connect(m_session->audioEncodeControl(), SIGNAL(settingsChanged()),
SLOT(reloadLater()));
connect(m_session->videoEncodeControl(), SIGNAL(settingsChanged()),
SLOT(reloadLater()));
connect(m_session->mediaContainerControl(), SIGNAL(settingsChanged()),
SLOT(reloadLater()));
connect(m_session->imageEncodeControl(), SIGNAL(settingsChanged()),
SLOT(reloadLater()));
connect(m_session, SIGNAL(viewfinderChanged()), connect(m_session, SIGNAL(viewfinderChanged()),
SLOT(reloadLater())); SLOT(reloadLater()));
connect(m_session, SIGNAL(readyChanged(bool)), connect(m_session, SIGNAL(readyChanged(bool)),
@@ -115,7 +107,6 @@ void CameraBinControl::setCaptureMode(QCamera::CaptureMode mode)
{ {
if (m_session->captureMode() != mode) { if (m_session->captureMode() != mode) {
m_session->setCaptureMode(mode); m_session->setCaptureMode(mode);
reloadLater();
if (m_state == QCamera::ActiveState) { if (m_state == QCamera::ActiveState) {
m_resourcePolicy->setResourceSet( m_resourcePolicy->setResourceSet(

View File

@@ -48,7 +48,8 @@
#include <QtCore/qmetaobject.h> #include <QtCore/qmetaobject.h>
//#define CAMERABIN_DEBUG 1 //#define CAMERABIN_DEBUG 1
#define ENUM_NAME(c,e,v) (c::staticMetaObject.enumerator(c::staticMetaObject.indexOfEnumerator(e)).valueToKey((v))) #define ZOOM_PROPERTY "zoom"
#define MAX_ZOOM_PROPERTY "max-zoom"
CameraBinFocus::CameraBinFocus(CameraBinSession *session) CameraBinFocus::CameraBinFocus(CameraBinSession *session)
:QCameraFocusControl(session), :QCameraFocusControl(session),
@@ -59,8 +60,6 @@ CameraBinFocus::CameraBinFocus(CameraBinSession *session)
{ {
connect(m_session, SIGNAL(stateChanged(QCamera::State)), connect(m_session, SIGNAL(stateChanged(QCamera::State)),
this, SLOT(_q_handleCameraStateChange(QCamera::State))); this, SLOT(_q_handleCameraStateChange(QCamera::State)));
connect(m_session, SIGNAL(imageCaptured(int,QImage)),
this, SLOT(_q_handleCapturedImage()));
} }
CameraBinFocus::~CameraBinFocus() CameraBinFocus::~CameraBinFocus()
@@ -91,7 +90,9 @@ qreal CameraBinFocus::maximumOpticalZoom() const
qreal CameraBinFocus::maximumDigitalZoom() const qreal CameraBinFocus::maximumDigitalZoom() const
{ {
return 10; gfloat zoomFactor = 1.0;
g_object_get(GST_BIN(m_session->cameraBin()), MAX_ZOOM_PROPERTY, &zoomFactor, NULL);
return zoomFactor;
} }
qreal CameraBinFocus::opticalZoom() const qreal CameraBinFocus::opticalZoom() const
@@ -102,15 +103,15 @@ qreal CameraBinFocus::opticalZoom() const
qreal CameraBinFocus::digitalZoom() const qreal CameraBinFocus::digitalZoom() const
{ {
gfloat zoomFactor = 1.0; gfloat zoomFactor = 1.0;
g_object_get(GST_BIN(m_session->cameraBin()), "zoom", &zoomFactor, NULL); g_object_get(GST_BIN(m_session->cameraBin()), ZOOM_PROPERTY, &zoomFactor, NULL);
return zoomFactor; return zoomFactor;
} }
void CameraBinFocus::zoomTo(qreal optical, qreal digital) void CameraBinFocus::zoomTo(qreal optical, qreal digital)
{ {
Q_UNUSED(optical); Q_UNUSED(optical);
digital = qBound(qreal(1.0), digital, qreal(10.0)); digital = qBound(qreal(1.0), digital, maximumDigitalZoom());
g_object_set(GST_BIN(m_session->cameraBin()), "zoom", digital, NULL); g_object_set(GST_BIN(m_session->cameraBin()), ZOOM_PROPERTY, digital, NULL);
emit digitalZoomChanged(digital); emit digitalZoomChanged(digital);
} }
@@ -184,9 +185,9 @@ void CameraBinFocus::_q_setFocusStatus(QCamera::LockStatus status, QCamera::Lock
{ {
#ifdef CAMERABIN_DEBUG #ifdef CAMERABIN_DEBUG
qDebug() << Q_FUNC_INFO << "Current:" qDebug() << Q_FUNC_INFO << "Current:"
<< ENUM_NAME(QCamera, "LockStatus", m_focusStatus) << m_focusStatus
<< "New:" << "New:"
<< ENUM_NAME(QCamera, "LockStatus", status) << ENUM_NAME(QCamera, "LockChangeReason", reason); << status << reason;
#endif #endif
if (m_focusStatus != status) { if (m_focusStatus != status) {
@@ -211,10 +212,6 @@ void CameraBinFocus::_q_handleCameraStateChange(QCamera::State state)
_q_setFocusStatus(QCamera::Unlocked, QCamera::LockLost); _q_setFocusStatus(QCamera::Unlocked, QCamera::LockLost);
} }
void CameraBinFocus::_q_handleCapturedImage()
{
}
void CameraBinFocus::_q_startFocusing() void CameraBinFocus::_q_startFocusing()
{ {
_q_setFocusStatus(QCamera::Searching, QCamera::UserRequest); _q_setFocusStatus(QCamera::Searching, QCamera::UserRequest);

View File

@@ -92,7 +92,6 @@ public Q_SLOTS:
private Q_SLOTS: private Q_SLOTS:
void _q_setFocusStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason); void _q_setFocusStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason);
void _q_handleCameraStateChange(QCamera::State state); void _q_handleCameraStateChange(QCamera::State state);
void _q_handleCapturedImage();
private: private:
CameraBinSession *m_session; CameraBinSession *m_session;

View File

@@ -82,9 +82,8 @@ CameraBinImageCapture::CameraBinImageCapture(CameraBinSession *session)
connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(updateState())); connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(updateState()));
connect(m_session, SIGNAL(imageExposed(int)), this, SIGNAL(imageExposed(int))); 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(imageCaptured(int,QImage)), this, SIGNAL(imageCaptured(int,QImage)));
connect(m_session, SIGNAL(busMessage(QGstreamerMessage)), SLOT(handleBusMessage(QGstreamerMessage)));
g_signal_connect(G_OBJECT(m_session->cameraBin()), IMAGE_DONE_SIGNAL, G_CALLBACK(handleImageSaved), this); m_session->bus()->installMessageFilter(this);
} }
CameraBinImageCapture::~CameraBinImageCapture() CameraBinImageCapture::~CameraBinImageCapture()
@@ -127,39 +126,9 @@ void CameraBinImageCapture::updateState()
} }
} }
gboolean CameraBinImageCapture::handleImageSaved(GstElement *camera,
const gchar *filename,
CameraBinImageCapture *self)
{
#ifdef DEBUG_CAPTURE
qDebug() << "Image saved" << filename;
#endif
Q_UNUSED(camera);
if (self->m_session->captureDestinationControl()->captureDestination() & QCameraImageCapture::CaptureToFile) {
QMetaObject::invokeMethod(self, "imageSaved",
Qt::QueuedConnection,
Q_ARG(int, self->m_requestId),
Q_ARG(QString, QString::fromUtf8(filename)));
} else {
#ifdef DEBUG_CAPTURE
qDebug() << Q_FUNC_INFO << "Dropped saving file" << filename;
#endif
//camerabin creates an empty file when captured buffer is dropped,
//let's remove it
QFileInfo info(QString::fromUtf8(filename));
if (info.isFile() &&
info.filePath().startsWith("/home") &&
info.size() == 0) {
QFile(info.absoluteFilePath()).remove();
}
}
return true;
}
gboolean CameraBinImageCapture::metadataEventProbe(GstPad *pad, GstEvent *event, CameraBinImageCapture *self) gboolean CameraBinImageCapture::metadataEventProbe(GstPad *pad, GstEvent *event, CameraBinImageCapture *self)
{ {
Q_UNUSED(pad);
if (GST_EVENT_TYPE(event) == GST_EVENT_TAG) { if (GST_EVENT_TYPE(event) == GST_EVENT_TAG) {
GstTagList *gstTags; GstTagList *gstTags;
@@ -281,7 +250,7 @@ gboolean CameraBinImageCapture::jpegBufferProbe(GstPad *pad, GstBuffer *buffer,
return destination & QCameraImageCapture::CaptureToFile; return destination & QCameraImageCapture::CaptureToFile;
} }
void CameraBinImageCapture::handleBusMessage(const QGstreamerMessage &message) bool CameraBinImageCapture::processBusMessage(const QGstreamerMessage &message)
{ {
//Install metadata event and buffer probes //Install metadata event and buffer probes
@@ -298,7 +267,7 @@ void CameraBinImageCapture::handleBusMessage(const QGstreamerMessage &message)
if (newState == GST_STATE_READY) { if (newState == GST_STATE_READY) {
GstElement *element = GST_ELEMENT(GST_MESSAGE_SRC(gm)); GstElement *element = GST_ELEMENT(GST_MESSAGE_SRC(gm));
if (!element) if (!element)
return; return false;
QString elementName = QString::fromLatin1(gst_element_get_name(element)); QString elementName = QString::fromLatin1(gst_element_get_name(element));
if (elementName.contains("jpegenc") && element != m_jpegEncoderElement) { if (elementName.contains("jpegenc") && element != m_jpegEncoderElement) {
@@ -338,5 +307,33 @@ void CameraBinImageCapture::handleBusMessage(const QGstreamerMessage &message)
gst_object_unref(srcpad); gst_object_unref(srcpad);
} }
} }
} else if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) {
if (GST_MESSAGE_SRC(gm) == (GstObject *)m_session->cameraBin()) {
const GstStructure *structure = gst_message_get_structure(gm);
if (gst_structure_has_name (structure, "image-done")) {
const gchar *fileName = gst_structure_get_string (structure, "filename");
#ifdef DEBUG_CAPTURE
qDebug() << "Image saved" << fileName;
#endif
if (m_session->captureDestinationControl()->captureDestination() & QCameraImageCapture::CaptureToFile) {
emit imageSaved(m_requestId, QString::fromUtf8(fileName));
} else {
#ifdef DEBUG_CAPTURE
qDebug() << Q_FUNC_INFO << "Dropped saving file" << fileName;
#endif
//camerabin creates an empty file when captured buffer is dropped,
//let's remove it
QFileInfo info(QString::fromUtf8(fileName));
if (info.exists() && info.isFile() && info.size() == 0) {
QFile(info.absoluteFilePath()).remove();
}
}
}
}
} }
return false;
} }

View File

@@ -48,9 +48,10 @@
QT_USE_NAMESPACE QT_USE_NAMESPACE
class CameraBinImageCapture : public QCameraImageCaptureControl class CameraBinImageCapture : public QCameraImageCaptureControl, public QGstreamerBusMessageFilter
{ {
Q_OBJECT Q_OBJECT
Q_INTERFACES(QGstreamerBusMessageFilter)
public: public:
CameraBinImageCapture(CameraBinSession *session); CameraBinImageCapture(CameraBinSession *session);
virtual ~CameraBinImageCapture(); virtual ~CameraBinImageCapture();
@@ -62,15 +63,15 @@ public:
int capture(const QString &fileName); int capture(const QString &fileName);
void cancelCapture(); void cancelCapture();
bool processBusMessage(const QGstreamerMessage &message);
private slots: private slots:
void updateState(); void updateState();
void handleBusMessage(const QGstreamerMessage &message);
private: private:
static gboolean metadataEventProbe(GstPad *pad, GstEvent *event, CameraBinImageCapture *); static gboolean metadataEventProbe(GstPad *pad, GstEvent *event, CameraBinImageCapture *);
static gboolean uncompressedBufferProbe(GstPad *pad, GstBuffer *buffer, CameraBinImageCapture *); static gboolean uncompressedBufferProbe(GstPad *pad, GstBuffer *buffer, CameraBinImageCapture *);
static gboolean jpegBufferProbe(GstPad *pad, GstBuffer *buffer, CameraBinImageCapture *); static gboolean jpegBufferProbe(GstPad *pad, GstBuffer *buffer, CameraBinImageCapture *);
static gboolean handleImageSaved(GstElement *camera, const gchar *filename, CameraBinImageCapture *);
CameraBinSession *m_session; CameraBinSession *m_session;
bool m_ready; bool m_ready;

View File

@@ -45,6 +45,8 @@
#include "camerabincontainer.h" #include "camerabincontainer.h"
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <gst/pbutils/encoding-profile.h>
CameraBinRecorder::CameraBinRecorder(CameraBinSession *session) CameraBinRecorder::CameraBinRecorder(CameraBinSession *session)
:QMediaRecorderControl(session), :QMediaRecorderControl(session),
m_session(session), m_session(session),
@@ -92,10 +94,7 @@ qint64 CameraBinRecorder::duration() const
void CameraBinRecorder::record() void CameraBinRecorder::record()
{ {
if (m_session->state() == QCamera::ActiveState) { if (m_session->state() == QCamera::ActiveState) {
if (m_state == QMediaRecorder::PausedState) m_session->recordVideo();
m_session->resumeVideoRecording();
else
m_session->recordVideo();
emit stateChanged(m_state = QMediaRecorder::RecordingState); emit stateChanged(m_state = QMediaRecorder::RecordingState);
} else } else
emit error(QMediaRecorder::ResourceError, tr("Service has not been started")); emit error(QMediaRecorder::ResourceError, tr("Service has not been started"));
@@ -103,11 +102,7 @@ void CameraBinRecorder::record()
void CameraBinRecorder::pause() void CameraBinRecorder::pause()
{ {
if (m_session->state() == QCamera::ActiveState) { emit error(QMediaRecorder::ResourceError, tr("QMediaRecorder::pause() is not supported by camerabin2."));
m_session->pauseVideoRecording();
emit stateChanged(m_state = QMediaRecorder::PausedState);
} else
emit error(QMediaRecorder::ResourceError, tr("Service has not been started"));
} }
void CameraBinRecorder::stop() void CameraBinRecorder::stop()
@@ -118,100 +113,19 @@ void CameraBinRecorder::stop()
} }
} }
bool CameraBinRecorder::findCodecs()
{
//Check the codecs are compatible with container,
//and choose the compatible codecs/container if omitted
CameraBinAudioEncoder *audioEncodeControl = m_session->audioEncodeControl();
CameraBinVideoEncoder *videoEncodeControl = m_session->videoEncodeControl();
CameraBinContainer *mediaContainerControl = m_session->mediaContainerControl();
audioEncodeControl->resetActualSettings();
videoEncodeControl->resetActualSettings();
mediaContainerControl->resetActualContainer();
QStringList containerCandidates;
if (mediaContainerControl->containerMimeType().isEmpty())
containerCandidates = mediaContainerControl->supportedContainers();
else
containerCandidates << mediaContainerControl->containerMimeType();
QStringList audioCandidates;
QAudioEncoderSettings audioSettings = audioEncodeControl->audioSettings();
if (audioSettings.codec().isEmpty())
audioCandidates = audioEncodeControl->supportedAudioCodecs();
else
audioCandidates << audioSettings.codec();
QStringList videoCandidates;
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();
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;
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()) {
qWarning() << "Camera error: Not compatible codecs and container format.";
emit error(QMediaRecorder::FormatError, tr("Not compatible codecs and container format."));
return false;
} else {
mediaContainerControl->setActualContainer(container);
QAudioEncoderSettings audioSettings = audioEncodeControl->audioSettings();
audioSettings.setCodec(audioCodec);
audioEncodeControl->setActualAudioSettings(audioSettings);
QVideoEncoderSettings videoSettings = videoEncodeControl->videoSettings();
videoSettings.setCodec(videoCodec);
videoEncodeControl->setActualVideoSettings(videoSettings);
}
return true;
}
void CameraBinRecorder::applySettings() void CameraBinRecorder::applySettings()
{ {
findCodecs(); GstEncodingContainerProfile *containerProfile = m_session->mediaContainerControl()->createProfile();
if (containerProfile) {
GstEncodingProfile *audioProfile = m_session->audioEncodeControl()->createProfile();
GstEncodingProfile *videoProfile = m_session->videoEncodeControl()->createProfile();
gst_encoding_container_profile_add_profile(containerProfile, audioProfile);
gst_encoding_container_profile_add_profile(containerProfile, videoProfile);
}
g_object_set (G_OBJECT(m_session->cameraBin()), "video-profile", containerProfile, NULL);
} }
bool CameraBinRecorder::isMuted() const bool CameraBinRecorder::isMuted() const

View File

@@ -64,8 +64,6 @@ public:
bool isMuted() const; bool isMuted() const;
bool findCodecs();
void applySettings(); void applySettings();
public slots: public slots:

View File

@@ -160,17 +160,16 @@ QMediaControl *CameraBinService::requestControl(const char *name)
if (!m_videoOutput) { if (!m_videoOutput) {
if (qstrcmp(name, QVideoRendererControl_iid) == 0) { if (qstrcmp(name, QVideoRendererControl_iid) == 0) {
m_videoOutput = m_videoRenderer; m_videoOutput = m_videoRenderer;
m_captureSession->setViewfinder(m_videoRenderer);
} else if (qstrcmp(name, QVideoWindowControl_iid) == 0) { } else if (qstrcmp(name, QVideoWindowControl_iid) == 0) {
m_videoOutput = m_videoWindow; m_videoOutput = m_videoWindow;
m_captureSession->setViewfinder(m_videoWindow);
} else if (qstrcmp(name, QVideoWidgetControl_iid) == 0) { } else if (qstrcmp(name, QVideoWidgetControl_iid) == 0) {
m_captureSession->setViewfinder(m_videoWidgetControl);
m_videoOutput = m_videoWidgetControl; m_videoOutput = m_videoWidgetControl;
} }
if (m_videoOutput) if (m_videoOutput) {
m_captureSession->setViewfinder(m_videoOutput);
return m_videoOutput; return m_videoOutput;
}
} }
if (qstrcmp(name,QAudioEndpointSelector_iid) == 0) if (qstrcmp(name,QAudioEndpointSelector_iid) == 0)
@@ -238,7 +237,7 @@ void CameraBinService::releaseControl(QMediaControl *control)
bool CameraBinService::isCameraBinAvailable() bool CameraBinService::isCameraBinAvailable()
{ {
GstElementFactory *factory = gst_element_factory_find("camerabin"); GstElementFactory *factory = gst_element_factory_find("camerabin2");
if (factory) { if (factory) {
gst_object_unref(GST_OBJECT(factory)); gst_object_unref(GST_OBJECT(factory));
return true; return true;

View File

@@ -66,34 +66,30 @@
#include <QtGui/qimage.h> #include <QtGui/qimage.h>
//#define CAMERABIN_DEBUG 1 //#define CAMERABIN_DEBUG 1
//#define CAMERABIN_DEBUG_DUMP_BIN 1
#define ENUM_NAME(c,e,v) (c::staticMetaObject.enumerator(c::staticMetaObject.indexOfEnumerator(e)).valueToKey((v))) #define ENUM_NAME(c,e,v) (c::staticMetaObject.enumerator(c::staticMetaObject.indexOfEnumerator(e)).valueToKey((v)))
#define FILENAME_PROPERTY "filename" #define FILENAME_PROPERTY "location"
#define MODE_PROPERTY "mode" #define MODE_PROPERTY "mode"
#define MUTE_PROPERTY "mute" #define MUTE_PROPERTY "mute"
#define ZOOM_PROPERTY "zoom"
#define IMAGE_PP_PROPERTY "image-post-processing" #define IMAGE_PP_PROPERTY "image-post-processing"
#define IMAGE_ENCODER_PROPERTY "image-encoder" #define IMAGE_ENCODER_PROPERTY "image-encoder"
#define VIDEO_PP_PROPERTY "video-post-processing" #define VIDEO_PP_PROPERTY "video-post-processing"
#define VIDEO_ENCODER_PROPERTY "video-encoder"
#define AUDIO_ENCODER_PROPERTY "audio-encoder"
#define VIDEO_MUXER_PROPERTY "video-muxer"
#define VIEWFINDER_SINK_PROPERTY "viewfinder-sink" #define VIEWFINDER_SINK_PROPERTY "viewfinder-sink"
#define VIDEO_SOURCE_PROPERTY "video-source" #define CAMERA_SOURCE_PROPERTY "camera-source"
#define AUDIO_SOURCE_PROPERTY "audio-source" #define AUDIO_SOURCE_PROPERTY "audio-source"
#define VIDEO_SOURCE_CAPS_PROPERTY "video-source-caps" #define SUPPORTED_IMAGE_CAPTURE_CAPS_PROPERTY "image-capture-supported-caps"
#define SUPPORTED_VIDEO_CAPTURE_CAPS_PROPERTY "video-capture-supported-caps"
#define FILTER_CAPS_PROPERTY "filter-caps" #define FILTER_CAPS_PROPERTY "filter-caps"
#define PREVIEW_CAPS_PROPERTY "preview-caps" #define PREVIEW_CAPS_PROPERTY "preview-caps"
#define IMAGE_DONE_SIGNAL "image-done" #define CAPTURE_START "start-capture"
#define CAPTURE_START "capture-start" #define CAPTURE_STOP "stop-capture"
#define CAPTURE_STOP "capture-stop"
#define CAPTURE_PAUSE "capture-pause"
#define SET_VIDEO_RESOLUTION_FPS "set-video-resolution-fps" #define SET_VIDEO_RESOLUTION_FPS "set-video-resolution-fps"
#define SET_IMAGE_RESOLUTION "set-image-resolution" #define SET_IMAGE_RESOLUTION "set-image-resolution"
#define CAMERABIN_IMAGE_MODE 0 #define CAMERABIN_IMAGE_MODE 1
#define CAMERABIN_VIDEO_MODE 1 #define CAMERABIN_VIDEO_MODE 2
#define gstRef(element) { gst_object_ref(GST_OBJECT(element)); gst_object_sink(GST_OBJECT(element)); } #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; } } #define gstUnref(element) { if (element) { gst_object_unref(GST_OBJECT(element)); element = 0; } }
@@ -106,16 +102,16 @@
#define VIEWFINDER_RESOLUTION_16x9 QSize(800, 450) #define VIEWFINDER_RESOLUTION_16x9 QSize(800, 450)
//using GST_STATE_READY for QCamera::LoadedState //using GST_STATE_READY for QCamera::LoadedState
//doesn't work reliably at least with some webcams. //may not work reliably at least with some webcams.
#if defined(Q_WS_MAEMO_6)
#define USE_READY_STATE_ON_LOADED //#define USE_READY_STATE_ON_LOADED
#endif
CameraBinSession::CameraBinSession(QObject *parent) CameraBinSession::CameraBinSession(QObject *parent)
:QObject(parent), :QObject(parent),
m_recordingActive(false),
m_state(QCamera::UnloadedState), m_state(QCamera::UnloadedState),
m_pendingState(QCamera::UnloadedState), m_pendingState(QCamera::UnloadedState),
m_recordingActive(false),
m_pendingResolutionUpdate(false), m_pendingResolutionUpdate(false),
m_muted(false), m_muted(false),
m_busy(false), m_busy(false),
@@ -124,12 +120,10 @@ CameraBinSession::CameraBinSession(QObject *parent)
m_videoInputFactory(0), m_videoInputFactory(0),
m_viewfinder(0), m_viewfinder(0),
m_viewfinderInterface(0), m_viewfinderInterface(0),
m_pipeline(0),
m_videoSrc(0), m_videoSrc(0),
m_viewfinderElement(0), m_viewfinderElement(0),
m_viewfinderHasChanged(true), m_viewfinderHasChanged(true),
m_videoInputHasChanged(true), m_videoInputHasChanged(true),
m_sourceCaps(0),
m_audioSrc(0), m_audioSrc(0),
m_audioConvert(0), m_audioConvert(0),
m_capsFilter(0), m_capsFilter(0),
@@ -137,16 +131,15 @@ CameraBinSession::CameraBinSession(QObject *parent)
m_audioEncoder(0), m_audioEncoder(0),
m_muxer(0) m_muxer(0)
{ {
m_pipeline = gst_element_factory_make("camerabin", "camerabin"); m_camerabin = gst_element_factory_make("camerabin2", "camerabin2");
g_signal_connect(G_OBJECT(m_pipeline), "notify::idle", G_CALLBACK(updateBusyStatus), this); g_signal_connect(G_OBJECT(m_camerabin), "notify::idle", G_CALLBACK(updateBusyStatus), this);
gstRef(m_camerabin);
gstRef(m_pipeline); m_bus = gst_element_get_bus(m_camerabin);
m_bus = gst_element_get_bus(m_pipeline);
m_busHelper = new QGstreamerBusHelper(m_bus, this); m_busHelper = new QGstreamerBusHelper(m_bus, this);
m_busHelper->installSyncEventFilter(this); m_busHelper->installMessageFilter(this);
connect(m_busHelper, SIGNAL(message(QGstreamerMessage)), SLOT(handleBusMessage(QGstreamerMessage)));
m_audioEncodeControl = new CameraBinAudioEncoder(this); m_audioEncodeControl = new CameraBinAudioEncoder(this);
m_videoEncodeControl = new CameraBinVideoEncoder(this); m_videoEncodeControl = new CameraBinVideoEncoder(this);
m_imageEncodeControl = new CameraBinImageEncoder(this); m_imageEncodeControl = new CameraBinImageEncoder(this);
@@ -159,36 +152,40 @@ CameraBinSession::CameraBinSession(QObject *parent)
m_cameraLocksControl = new CameraBinLocks(this); m_cameraLocksControl = new CameraBinLocks(this);
m_captureDestinationControl = new CameraBinCaptureDestination(this); m_captureDestinationControl = new CameraBinCaptureDestination(this);
m_captureBufferFormatControl = new CameraBinCaptureBufferFormat(this); m_captureBufferFormatControl = new CameraBinCaptureBufferFormat(this);
//post image preview in RGB format
GstCaps *previewCaps = gst_caps_from_string("video/x-raw-rgb");
g_object_set(G_OBJECT(m_camerabin), PREVIEW_CAPS_PROPERTY, previewCaps, NULL);
gst_caps_unref(previewCaps);
} }
CameraBinSession::~CameraBinSession() CameraBinSession::~CameraBinSession()
{ {
if (m_pipeline) { if (m_camerabin) {
if (m_viewfinderInterface) if (m_viewfinderInterface)
m_viewfinderInterface->stopRenderer(); m_viewfinderInterface->stopRenderer();
gst_element_set_state(m_pipeline, GST_STATE_NULL); gst_element_set_state(m_camerabin, GST_STATE_NULL);
gst_element_get_state(m_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); gst_element_get_state(m_camerabin, NULL, NULL, GST_CLOCK_TIME_NONE);
gstUnref(m_pipeline); gstUnref(m_camerabin);
gstUnref(m_viewfinderElement); gstUnref(m_viewfinderElement);
} }
} }
GstPhotography *CameraBinSession::photography() GstPhotography *CameraBinSession::photography()
{ {
if (GST_IS_PHOTOGRAPHY(m_pipeline)) { if (GST_IS_PHOTOGRAPHY(m_camerabin)) {
return GST_PHOTOGRAPHY(m_pipeline); return GST_PHOTOGRAPHY(m_camerabin);
} }
if (!m_videoSrc) { if (!m_videoSrc) {
m_videoSrc = buildVideoSrc(); m_videoSrc = buildCameraSource();
if (m_videoSrc) if (m_videoSrc)
g_object_set(m_pipeline, VIDEO_SOURCE_PROPERTY, m_videoSrc, NULL); g_object_set(m_camerabin, CAMERA_SOURCE_PROPERTY, m_videoSrc, NULL);
else else
g_object_get(m_pipeline, VIDEO_SOURCE_PROPERTY, &m_videoSrc, NULL); g_object_get(m_camerabin, CAMERA_SOURCE_PROPERTY, &m_videoSrc, NULL);
updateVideoSourceCaps();
m_videoInputHasChanged = false; m_videoInputHasChanged = false;
} }
@@ -203,33 +200,19 @@ CameraBinSession::CameraRole CameraBinSession::cameraRole() const
return BackCamera; return BackCamera;
} }
/*
Configure camera during Loaded->Active states stansition.
*/
bool CameraBinSession::setupCameraBin() bool CameraBinSession::setupCameraBin()
{ {
if (m_captureMode == QCamera::CaptureStillImage) {
g_object_set(m_pipeline, MODE_PROPERTY, CAMERABIN_IMAGE_MODE, NULL);
}
if (m_captureMode == QCamera::CaptureVideo) {
g_object_set(m_pipeline, MODE_PROPERTY, CAMERABIN_VIDEO_MODE, NULL);
if (!m_recorderControl->findCodecs())
return false;
g_object_set(m_pipeline, VIDEO_ENCODER_PROPERTY, m_videoEncodeControl->createEncoder(), NULL);
g_object_set(m_pipeline, AUDIO_ENCODER_PROPERTY, m_audioEncodeControl->createEncoder(), NULL);
g_object_set(m_pipeline, VIDEO_MUXER_PROPERTY,
gst_element_factory_make(m_mediaContainerControl->formatElementName().constData(), NULL), NULL);
}
if (m_videoInputHasChanged) { if (m_videoInputHasChanged) {
m_videoSrc = buildVideoSrc(); m_videoSrc = buildCameraSource();
if (m_videoSrc) if (m_videoSrc)
g_object_set(m_pipeline, VIDEO_SOURCE_PROPERTY, m_videoSrc, NULL); g_object_set(m_camerabin, CAMERA_SOURCE_PROPERTY, m_videoSrc, NULL);
else else
g_object_get(m_pipeline, VIDEO_SOURCE_PROPERTY, &m_videoSrc, NULL); g_object_get(m_camerabin, CAMERA_SOURCE_PROPERTY, &m_videoSrc, NULL);
updateVideoSourceCaps();
m_videoInputHasChanged = false; m_videoInputHasChanged = false;
} }
@@ -248,25 +231,41 @@ bool CameraBinSession::setupCameraBin()
m_viewfinderElement = gst_element_factory_make("fakesink", NULL); m_viewfinderElement = gst_element_factory_make("fakesink", NULL);
} }
gst_object_ref(GST_OBJECT(m_viewfinderElement)); gst_object_ref(GST_OBJECT(m_viewfinderElement));
gst_element_set_state(m_pipeline, GST_STATE_NULL); gst_element_set_state(m_camerabin, GST_STATE_NULL);
g_object_set(G_OBJECT(m_pipeline), VIEWFINDER_SINK_PROPERTY, m_viewfinderElement, NULL); g_object_set(G_OBJECT(m_camerabin), VIEWFINDER_SINK_PROPERTY, m_viewfinderElement, NULL);
} }
GstCaps *previewCaps = gst_caps_from_string(PREVIEW_CAPS_4_3);
g_object_set(G_OBJECT(m_pipeline), PREVIEW_CAPS_PROPERTY, previewCaps, NULL);
gst_caps_unref(previewCaps);
return true; return true;
} }
void CameraBinSession::updateVideoSourceCaps() static GstCaps *resolutionToCaps(const QSize &resolution,
const QPair<int, int> &rate = qMakePair<int,int>(0,0))
{ {
if (m_sourceCaps) { if (resolution.isEmpty())
gst_caps_unref(m_sourceCaps); return gst_caps_new_any();
m_sourceCaps = 0;
}
g_object_get(G_OBJECT(m_pipeline), VIDEO_SOURCE_CAPS_PROPERTY, &m_sourceCaps, NULL); GstCaps *caps = 0;
if (rate.second > 0) {
caps = gst_caps_new_full(gst_structure_new("video/x-raw-yuv",
"width", G_TYPE_INT, resolution.width(),
"height", G_TYPE_INT, resolution.height(),
"framerate", GST_TYPE_FRACTION, rate.first, rate.second,
NULL),
gst_structure_new("video/x-raw-rgb",
"width", G_TYPE_INT, resolution.width(),
"height", G_TYPE_INT, resolution.height(),
"framerate", GST_TYPE_FRACTION, rate.first, rate.second,
NULL), NULL);
} else {
caps = gst_caps_new_full (gst_structure_new ("video/x-raw-yuv",
"width", G_TYPE_INT, resolution.width(),
"height", G_TYPE_INT, resolution.height(),
NULL),
gst_structure_new ("video/x-raw-rgb",
"width", G_TYPE_INT, resolution.width(),
"height", G_TYPE_INT, resolution.height(), NULL), NULL);
}
return caps;
} }
void CameraBinSession::setupCaptureResolution() void CameraBinSession::setupCaptureResolution()
@@ -276,7 +275,6 @@ void CameraBinSession::setupCaptureResolution()
//by default select the maximum supported resolution //by default select the maximum supported resolution
if (resolution.isEmpty()) { if (resolution.isEmpty()) {
updateVideoSourceCaps();
bool continuous = false; bool continuous = false;
QList<QSize> resolutions = supportedResolutions(qMakePair<int,int>(0,0), QList<QSize> resolutions = supportedResolutions(qMakePair<int,int>(0,0),
&continuous, &continuous,
@@ -285,17 +283,15 @@ void CameraBinSession::setupCaptureResolution()
resolution = resolutions.last(); resolution = resolutions.last();
} }
QString previewCapsString = PREVIEW_CAPS_4_3;
QSize viewfinderResolution = VIEWFINDER_RESOLUTION_4x3; QSize viewfinderResolution = VIEWFINDER_RESOLUTION_4x3;
if (!resolution.isEmpty()) { if (!resolution.isEmpty()) {
GstCaps *caps = resolutionToCaps(resolution);
#if CAMERABIN_DEBUG #if CAMERABIN_DEBUG
qDebug() << Q_FUNC_INFO << "set image resolution" << resolution; qDebug() << Q_FUNC_INFO << "set image resolution" << resolution << gst_caps_to_string(caps);
#endif #endif
g_signal_emit_by_name(G_OBJECT(m_pipeline), SET_IMAGE_RESOLUTION, resolution.width(), resolution.height(), NULL); g_object_set(m_camerabin, "image-capture-caps", caps, NULL);
gst_caps_unref(caps);
previewCapsString = QString("video/x-raw-rgb, width = (int) %1, height = (int) 480")
.arg(resolution.width()*480/resolution.height());
if (!resolution.isEmpty()) { if (!resolution.isEmpty()) {
qreal aspectRatio = qreal(resolution.width()) / resolution.height(); qreal aspectRatio = qreal(resolution.width()) / resolution.height();
@@ -308,35 +304,25 @@ void CameraBinSession::setupCaptureResolution()
} }
} }
GstCaps *previewCaps = gst_caps_from_string(previewCapsString.toLatin1());
g_object_set(G_OBJECT(m_pipeline), PREVIEW_CAPS_PROPERTY, previewCaps, NULL);
gst_caps_unref(previewCaps);
//on low res cameras the viewfinder resolution should not be bigger //on low res cameras the viewfinder resolution should not be bigger
//then capture resolution //then capture resolution
if (viewfinderResolution.width() > resolution.width()) if (viewfinderResolution.width() > resolution.width() && !resolution.isEmpty())
viewfinderResolution = resolution; viewfinderResolution = resolution;
GstCaps *viewfinderCaps = resolutionToCaps(viewfinderResolution);
#if CAMERABIN_DEBUG #if CAMERABIN_DEBUG
qDebug() << Q_FUNC_INFO << "set viewfinder resolution" << viewfinderResolution; qDebug() << "Set viewfinder resolution" << viewfinderResolution <<gst_caps_to_string(viewfinderCaps);
#endif #endif
g_signal_emit_by_name(G_OBJECT(m_pipeline), g_object_set(m_camerabin, "viewfinder-caps", viewfinderCaps, NULL);
SET_VIDEO_RESOLUTION_FPS, gst_caps_unref(viewfinderCaps);
viewfinderResolution.width(),
viewfinderResolution.height(),
0, // maximum framerate
1, // framerate denom
NULL);
} }
if (m_captureMode == QCamera::CaptureVideo) { if (m_captureMode == QCamera::CaptureVideo) {
QSize resolution = m_videoEncodeControl->videoSettings().resolution(); QSize resolution = m_videoEncodeControl->videoSettings().resolution();
qreal framerate = m_videoEncodeControl->videoSettings().frameRate(); //qreal framerate = m_videoEncodeControl->videoSettings().frameRate();
if (resolution.isEmpty()) { if (resolution.isEmpty()) {
//select the hightest supported resolution //select the hightest supported resolution
updateVideoSourceCaps();
bool continuous = false; bool continuous = false;
QList<QSize> resolutions = supportedResolutions(qMakePair<int,int>(0,0), QList<QSize> resolutions = supportedResolutions(qMakePair<int,int>(0,0),
&continuous, &continuous,
@@ -345,32 +331,27 @@ void CameraBinSession::setupCaptureResolution()
resolution = resolutions.last(); resolution = resolutions.last();
} }
if (!resolution.isEmpty() || framerate > 0) { GstCaps *caps = resolutionToCaps(resolution /*, framerate*/); //convert to rational
#if CAMERABIN_DEBUG #if CAMERABIN_DEBUG
qDebug() << Q_FUNC_INFO << "set video resolution" << resolution; qDebug() << Q_FUNC_INFO << "set video resolution" << resolution << gst_caps_to_string(caps);
#endif #endif
g_signal_emit_by_name(G_OBJECT(m_pipeline),
SET_VIDEO_RESOLUTION_FPS, g_object_set(m_camerabin, "video-capture-caps", caps, NULL);
resolution.width(), gst_caps_unref(caps);
resolution.height(),
0, //framerate nom == max rate
1, // framerate denom == max rate
NULL);
}
} }
} }
GstElement *CameraBinSession::buildVideoSrc() GstElement *CameraBinSession::buildCameraSource()
{ {
#if CAMERABIN_DEBUG
qDebug() << Q_FUNC_INFO;
#endif
GstElement *videoSrc = 0; GstElement *videoSrc = 0;
if (m_videoInputFactory) { if (m_videoInputFactory) {
videoSrc = m_videoInputFactory->buildElement(); videoSrc = m_videoInputFactory->buildElement();
} else { } else {
QList<QByteArray> candidates; QList<QByteArray> candidates;
candidates << "subdevsrc" candidates << "wrappercamerabinsrc";
<< "v4l2camsrc"
<< "v4l2src"
<< "autovideosrc";
QByteArray sourceElementName; QByteArray sourceElementName;
foreach(sourceElementName, candidates) { foreach(sourceElementName, candidates) {
@@ -389,7 +370,11 @@ GstElement *CameraBinSession::buildVideoSrc()
else else
g_object_set(G_OBJECT(videoSrc), "camera-device", 0, NULL); g_object_set(G_OBJECT(videoSrc), "camera-device", 0, NULL);
} else { } else {
g_object_set(G_OBJECT(videoSrc), "device", m_inputDevice.toLocal8Bit().constData(), NULL); if (g_object_class_find_property(G_OBJECT_GET_CLASS(videoSrc), "device"))
g_object_set(G_OBJECT(videoSrc),
"device",
m_inputDevice.toLocal8Bit().constData(),
NULL);
} }
} }
} }
@@ -405,9 +390,13 @@ void CameraBinSession::captureImage(int requestId, const QString &fileName)
m_requestId = requestId; m_requestId = requestId;
g_object_set(G_OBJECT(m_pipeline), FILENAME_PROPERTY, actualFileName.toLocal8Bit().constData(), NULL); #if CAMERABIN_DEBUG
qDebug() << Q_FUNC_INFO << m_requestId << fileName << "actual file name:" << actualFileName;
#endif
g_signal_emit_by_name(G_OBJECT(m_pipeline), CAPTURE_START, NULL); g_object_set(G_OBJECT(m_camerabin), FILENAME_PROPERTY, actualFileName.toLocal8Bit().constData(), NULL);
g_signal_emit_by_name(G_OBJECT(m_camerabin), CAPTURE_START, NULL);
m_imageFileName = actualFileName; m_imageFileName = actualFileName;
} }
@@ -418,10 +407,10 @@ void CameraBinSession::setCaptureMode(QCamera::CaptureMode mode)
switch (m_captureMode) { switch (m_captureMode) {
case QCamera::CaptureStillImage: case QCamera::CaptureStillImage:
g_object_set(m_pipeline, MODE_PROPERTY, CAMERABIN_IMAGE_MODE, NULL); g_object_set(m_camerabin, MODE_PROPERTY, CAMERABIN_IMAGE_MODE, NULL);
break; break;
case QCamera::CaptureVideo: case QCamera::CaptureVideo:
g_object_set(m_pipeline, MODE_PROPERTY, CAMERABIN_VIDEO_MODE, NULL); g_object_set(m_camerabin, MODE_PROPERTY, CAMERABIN_VIDEO_MODE, NULL);
break; break;
} }
} }
@@ -534,6 +523,8 @@ void CameraBinSession::setViewfinder(QObject *viewfinder)
this, SLOT(handleViewfinderChange())); this, SLOT(handleViewfinderChange()));
disconnect(m_viewfinder, SIGNAL(readyChanged(bool)), disconnect(m_viewfinder, SIGNAL(readyChanged(bool)),
this, SIGNAL(readyChanged(bool))); this, SIGNAL(readyChanged(bool)));
m_busHelper->removeMessageFilter(m_viewfinder);
} }
m_viewfinder = viewfinder; m_viewfinder = viewfinder;
@@ -544,6 +535,8 @@ void CameraBinSession::setViewfinder(QObject *viewfinder)
this, SLOT(handleViewfinderChange())); this, SLOT(handleViewfinderChange()));
connect(m_viewfinder, SIGNAL(readyChanged(bool)), connect(m_viewfinder, SIGNAL(readyChanged(bool)),
this, SIGNAL(readyChanged(bool))); this, SIGNAL(readyChanged(bool)));
m_busHelper->installMessageFilter(m_viewfinder);
} }
emit viewfinderChanged(); emit viewfinderChanged();
@@ -573,7 +566,7 @@ void CameraBinSession::setState(QCamera::State newState)
m_pendingState = newState; m_pendingState = newState;
#if CAMERABIN_DEBUG #if CAMERABIN_DEBUG
qDebug() << Q_FUNC_INFO << ENUM_NAME(QCamera, "State", newState); qDebug() << Q_FUNC_INFO << newState;
#endif #endif
switch (newState) { switch (newState) {
@@ -584,7 +577,7 @@ void CameraBinSession::setState(QCamera::State newState)
if (m_viewfinderInterface) if (m_viewfinderInterface)
m_viewfinderInterface->stopRenderer(); m_viewfinderInterface->stopRenderer();
gst_element_set_state(m_pipeline, GST_STATE_NULL); gst_element_set_state(m_camerabin, GST_STATE_NULL);
m_state = newState; m_state = newState;
if (m_busy) if (m_busy)
emit busyChanged(m_busy = false); emit busyChanged(m_busy = false);
@@ -599,19 +592,18 @@ void CameraBinSession::setState(QCamera::State newState)
if (m_viewfinderInterface) if (m_viewfinderInterface)
m_viewfinderInterface->stopRenderer(); m_viewfinderInterface->stopRenderer();
gst_element_set_state(m_pipeline, GST_STATE_NULL); gst_element_set_state(m_camerabin, GST_STATE_NULL);
m_videoSrc = buildVideoSrc(); m_videoSrc = buildCameraSource();
g_object_set(m_pipeline, VIDEO_SOURCE_PROPERTY, m_videoSrc, NULL); g_object_set(m_camerabin, CAMERA_SOURCE_PROPERTY, m_videoSrc, NULL);
updateVideoSourceCaps();
m_videoInputHasChanged = false; m_videoInputHasChanged = false;
} }
#ifdef USE_READY_STATE_ON_LOADED #ifdef USE_READY_STATE_ON_LOADED
gst_element_set_state(m_pipeline, GST_STATE_READY); gst_element_set_state(m_camerabin, GST_STATE_READY);
#else #else
m_state = QCamera::LoadedState; m_state = QCamera::LoadedState;
if (m_viewfinderInterface) if (m_viewfinderInterface)
m_viewfinderInterface->stopRenderer(); m_viewfinderInterface->stopRenderer();
gst_element_set_state(m_pipeline, GST_STATE_NULL); gst_element_set_state(m_camerabin, GST_STATE_NULL);
emit stateChanged(m_state); emit stateChanged(m_state);
#endif #endif
break; break;
@@ -619,15 +611,15 @@ void CameraBinSession::setState(QCamera::State newState)
if (setupCameraBin()) { if (setupCameraBin()) {
GstState binState = GST_STATE_NULL; GstState binState = GST_STATE_NULL;
GstState pending = GST_STATE_NULL; GstState pending = GST_STATE_NULL;
gst_element_get_state(m_pipeline, &binState, &pending, 0); gst_element_get_state(m_camerabin, &binState, &pending, 0);
if (pending == GST_STATE_VOID_PENDING && binState == GST_STATE_READY) { if (pending == GST_STATE_VOID_PENDING && binState == GST_STATE_READY) {
m_pendingResolutionUpdate = false; m_pendingResolutionUpdate = false;
setupCaptureResolution(); setupCaptureResolution();
gst_element_set_state(m_pipeline, GST_STATE_PLAYING); gst_element_set_state(m_camerabin, GST_STATE_PLAYING);
} else { } else {
m_pendingResolutionUpdate = true; m_pendingResolutionUpdate = true;
gst_element_set_state(m_pipeline, GST_STATE_READY); gst_element_set_state(m_camerabin, GST_STATE_READY);
} }
} }
} }
@@ -660,7 +652,7 @@ qint64 CameraBinSession::duration() const
GstFormat format = GST_FORMAT_TIME; GstFormat format = GST_FORMAT_TIME;
gint64 duration = 0; gint64 duration = 0;
if ( m_pipeline && gst_element_query_position(m_pipeline, &format, &duration)) if ( m_camerabin && gst_element_query_position(m_camerabin, &format, &duration))
return duration / 1000000; return duration / 1000000;
else else
return 0; return 0;
@@ -676,8 +668,8 @@ void CameraBinSession::setMuted(bool muted)
if (m_muted != muted) { if (m_muted != muted) {
m_muted = muted; m_muted = muted;
if (m_pipeline) if (m_camerabin)
g_object_set(G_OBJECT(m_pipeline), MUTE_PROPERTY, m_muted, NULL); g_object_set(G_OBJECT(m_camerabin), MUTE_PROPERTY, m_muted, NULL);
emit mutedChanged(m_muted); emit mutedChanged(m_muted);
} }
} }
@@ -691,8 +683,8 @@ void CameraBinSession::setMetaData(const QMap<QByteArray, QVariant> &data)
{ {
m_metaData = data; m_metaData = data;
if (m_pipeline) { if (m_camerabin) {
GstIterator *elements = gst_bin_iterate_all_by_interface(GST_BIN(m_pipeline), GST_TYPE_TAG_SETTER); GstIterator *elements = gst_bin_iterate_all_by_interface(GST_BIN(m_camerabin), GST_TYPE_TAG_SETTER);
GstElement *element = 0; GstElement *element = 0;
while (gst_iterator_next(elements, (void**)&element) == GST_ITERATOR_OK) { while (gst_iterator_next(elements, (void**)&element) == GST_ITERATOR_OK) {
QMapIterator<QByteArray, QVariant> it(data); QMapIterator<QByteArray, QVariant> it(data);
@@ -743,6 +735,7 @@ bool CameraBinSession::processSyncMessage(const QGstreamerMessage &message)
if (m_captureMode == QCamera::CaptureStillImage && if (m_captureMode == QCamera::CaptureStillImage &&
gst_structure_has_name(gm->structure, "preview-image")) { gst_structure_has_name(gm->structure, "preview-image")) {
st = gst_message_get_structure(gm); st = gst_message_get_structure(gm);
if (gst_structure_has_field_typed(st, "buffer", GST_TYPE_BUFFER)) { if (gst_structure_has_field_typed(st, "buffer", GST_TYPE_BUFFER)) {
image = gst_structure_get_value(st, "buffer"); image = gst_structure_get_value(st, "buffer");
if (image) { if (image) {
@@ -755,6 +748,9 @@ bool CameraBinSession::processSyncMessage(const QGstreamerMessage &message)
GstStructure *structure = gst_caps_get_structure(caps, 0); GstStructure *structure = gst_caps_get_structure(caps, 0);
gint width = 0; gint width = 0;
gint height = 0; gint height = 0;
#if CAMERABIN_DEBUG
qDebug() << "Preview caps:" << gst_structure_to_string(structure);
#endif
if (structure && if (structure &&
gst_structure_get_int(structure, "width", &width) && gst_structure_get_int(structure, "width", &width) &&
@@ -795,24 +791,14 @@ bool CameraBinSession::processSyncMessage(const QGstreamerMessage &message)
} }
} }
if (gst_structure_has_name(gm->structure, "prepare-xwindow-id")) {
if (m_viewfinderInterface)
m_viewfinderInterface->precessNewStream();
return true;
}
if (gst_structure_has_name(gm->structure, GST_PHOTOGRAPHY_AUTOFOCUS_DONE)) if (gst_structure_has_name(gm->structure, GST_PHOTOGRAPHY_AUTOFOCUS_DONE))
m_cameraFocusControl->handleFocusMessage(gm); m_cameraFocusControl->handleFocusMessage(gm);
if (m_viewfinderInterface && GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_viewfinderElement))
m_viewfinderInterface->handleSyncMessage(gm);
} }
return false; return false;
} }
void CameraBinSession::handleBusMessage(const QGstreamerMessage &message) bool CameraBinSession::processBusMessage(const QGstreamerMessage &message)
{ {
GstMessage* gm = message.rawMessage(); GstMessage* gm = message.rawMessage();
@@ -830,13 +816,20 @@ void CameraBinSession::handleBusMessage(const QGstreamerMessage &message)
} }
//only report error messager from camerabin //only report error messager from camerabin
if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_pipeline)) { if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_camerabin)) {
if (message.isEmpty()) if (message.isEmpty())
message = tr("Camera error"); message = tr("Camera error");
emit error(int(QMediaRecorder::ResourceError), message); emit error(int(QMediaRecorder::ResourceError), message);
} }
#ifdef CAMERABIN_DEBUG_DUMP_BIN
_gst_debug_bin_to_dot_file_with_ts(GST_BIN(m_camerabin),
GstDebugGraphDetails(GST_DEBUG_GRAPH_SHOW_ALL /* GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE | GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS | GST_DEBUG_GRAPH_SHOW_STATES*/),
"camerabin_error");
#endif
if (err) if (err)
g_error_free (err); g_error_free (err);
@@ -858,7 +851,7 @@ void CameraBinSession::handleBusMessage(const QGstreamerMessage &message)
g_free (debug); g_free (debug);
} }
if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_pipeline)) { if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_camerabin)) {
switch (GST_MESSAGE_TYPE(gm)) { switch (GST_MESSAGE_TYPE(gm)) {
case GST_MESSAGE_DURATION: case GST_MESSAGE_DURATION:
break; break;
@@ -884,6 +877,12 @@ void CameraBinSession::handleBusMessage(const QGstreamerMessage &message)
.arg(states[pending]); .arg(states[pending]);
#endif #endif
#ifdef CAMERABIN_DEBUG_DUMP_BIN
_gst_debug_bin_to_dot_file_with_ts(GST_BIN(m_camerabin),
GstDebugGraphDetails(GST_DEBUG_GRAPH_SHOW_ALL /*GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE | GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS | GST_DEBUG_GRAPH_SHOW_STATES*/),
"camerabin");
#endif
switch (newState) { switch (newState) {
case GST_STATE_VOID_PENDING: case GST_STATE_VOID_PENDING:
case GST_STATE_NULL: case GST_STATE_NULL:
@@ -894,7 +893,7 @@ void CameraBinSession::handleBusMessage(const QGstreamerMessage &message)
if (m_pendingResolutionUpdate) { if (m_pendingResolutionUpdate) {
m_pendingResolutionUpdate = false; m_pendingResolutionUpdate = false;
setupCaptureResolution(); setupCaptureResolution();
gst_element_set_state(m_pipeline, GST_STATE_PLAYING); gst_element_set_state(m_camerabin, GST_STATE_PLAYING);
} }
if (m_state != QCamera::LoadedState) if (m_state != QCamera::LoadedState)
emit stateChanged(m_state = QCamera::LoadedState); emit stateChanged(m_state = QCamera::LoadedState);
@@ -911,12 +910,9 @@ void CameraBinSession::handleBusMessage(const QGstreamerMessage &message)
} }
//qDebug() << "New session state:" << ENUM_NAME(CameraBinSession,"State",m_state); //qDebug() << "New session state:" << ENUM_NAME(CameraBinSession,"State",m_state);
} }
if (m_viewfinderInterface && GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_viewfinderElement))
m_viewfinderInterface->handleBusMessage(gm);
emit busMessage(message);
} }
return false;
} }
void CameraBinSession::recordVideo() void CameraBinSession::recordVideo()
@@ -924,31 +920,19 @@ void CameraBinSession::recordVideo()
m_recordingActive = true; m_recordingActive = true;
m_actualSink = m_sink; m_actualSink = m_sink;
if (m_actualSink.isEmpty()) { if (m_actualSink.isEmpty()) {
QString ext = m_mediaContainerControl->containerMimeType(); QString ext = m_mediaContainerControl->suggestedFileExtension();
m_actualSink = generateFileName("clip_", defaultDir(QCamera::CaptureVideo), ext); m_actualSink = generateFileName("clip_", defaultDir(QCamera::CaptureVideo), ext);
} }
g_object_set(G_OBJECT(m_pipeline), FILENAME_PROPERTY, m_actualSink.toEncoded().constData(), NULL); g_object_set(G_OBJECT(m_camerabin), FILENAME_PROPERTY, m_actualSink.toEncoded().constData(), NULL);
g_signal_emit_by_name(G_OBJECT(m_pipeline), CAPTURE_START, NULL); g_signal_emit_by_name(G_OBJECT(m_camerabin), CAPTURE_START, NULL);
}
void CameraBinSession::resumeVideoRecording()
{
m_recordingActive = true;
g_signal_emit_by_name(G_OBJECT(m_pipeline), CAPTURE_START, NULL);
}
void CameraBinSession::pauseVideoRecording()
{
g_signal_emit_by_name(G_OBJECT(m_pipeline), CAPTURE_PAUSE, NULL);
} }
void CameraBinSession::stopVideoRecording() void CameraBinSession::stopVideoRecording()
{ {
m_recordingActive = false; m_recordingActive = false;
g_signal_emit_by_name(G_OBJECT(m_pipeline), CAPTURE_STOP, NULL); g_signal_emit_by_name(G_OBJECT(m_camerabin), CAPTURE_STOP, NULL);
} }
//internal, only used by CameraBinSession::supportedFrameRates. //internal, only used by CameraBinSession::supportedFrameRates.
@@ -985,13 +969,18 @@ QList< QPair<int,int> > CameraBinSession::supportedFrameRates(const QSize &frame
{ {
QList< QPair<int,int> > res; QList< QPair<int,int> > res;
if (!m_sourceCaps) GstCaps *supportedCaps = 0;
g_object_get(G_OBJECT(m_camerabin),
SUPPORTED_VIDEO_CAPTURE_CAPS_PROPERTY,
&supportedCaps, NULL);
if (!supportedCaps)
return res; return res;
GstCaps *caps = 0; GstCaps *caps = 0;
if (frameSize.isEmpty()) { if (frameSize.isEmpty()) {
caps = gst_caps_copy(m_sourceCaps); caps = gst_caps_copy(supportedCaps);
} else { } else {
GstCaps *filter = gst_caps_new_full( GstCaps *filter = gst_caps_new_full(
gst_structure_new( gst_structure_new(
@@ -1008,12 +997,13 @@ QList< QPair<int,int> > CameraBinSession::supportedFrameRates(const QSize &frame
"height" , G_TYPE_INT, frameSize.height(), NULL), "height" , G_TYPE_INT, frameSize.height(), NULL),
NULL); NULL);
caps = gst_caps_intersect(m_sourceCaps, filter); caps = gst_caps_intersect(supportedCaps, filter);
gst_caps_unref(filter); gst_caps_unref(filter);
} }
gst_caps_unref(supportedCaps);
//simplify to the list of rates only: //simplify to the list of rates only:
gst_caps_make_writable(caps); caps = gst_caps_make_writable(caps);
for (uint i=0; i<gst_caps_get_size(caps); i++) { for (uint i=0; i<gst_caps_get_size(caps); i++) {
GstStructure *structure = gst_caps_get_structure(caps, i); GstStructure *structure = gst_caps_get_structure(caps, i);
gst_structure_set_name(structure, "video/x-raw-yuv"); gst_structure_set_name(structure, "video/x-raw-yuv");
@@ -1090,18 +1080,24 @@ QList<QSize> CameraBinSession::supportedResolutions(QPair<int,int> rate,
if (continuous) if (continuous)
*continuous = false; *continuous = false;
if (!m_sourceCaps) GstCaps *supportedCaps = 0;
g_object_get(G_OBJECT(m_camerabin),
(mode == QCamera::CaptureStillImage) ?
SUPPORTED_IMAGE_CAPTURE_CAPS_PROPERTY : SUPPORTED_VIDEO_CAPTURE_CAPS_PROPERTY,
&supportedCaps, NULL);
if (!supportedCaps)
return res; return res;
#if CAMERABIN_DEBUG #if CAMERABIN_DEBUG
qDebug() << "Source caps:" << gst_caps_to_string(m_sourceCaps); qDebug() << "Source caps:" << gst_caps_to_string(supportedCaps);
#endif #endif
GstCaps *caps = 0; GstCaps *caps = 0;
bool isContinuous = false; bool isContinuous = false;
if (rate.first <= 0 || rate.second <= 0) { if (rate.first <= 0 || rate.second <= 0) {
caps = gst_caps_copy(m_sourceCaps); caps = gst_caps_copy(supportedCaps);
} else { } else {
GstCaps *filter = gst_caps_new_full( GstCaps *filter = gst_caps_new_full(
gst_structure_new( gst_structure_new(
@@ -1115,12 +1111,13 @@ QList<QSize> CameraBinSession::supportedResolutions(QPair<int,int> rate,
"framerate" , GST_TYPE_FRACTION , rate.first, rate.second, NULL), "framerate" , GST_TYPE_FRACTION , rate.first, rate.second, NULL),
NULL); NULL);
caps = gst_caps_intersect(m_sourceCaps, filter); caps = gst_caps_intersect(supportedCaps, filter);
gst_caps_unref(filter); gst_caps_unref(filter);
} }
gst_caps_unref(supportedCaps);
//simplify to the list of resolutions only: //simplify to the list of resolutions only:
gst_caps_make_writable(caps); caps = gst_caps_make_writable(caps);
for (uint i=0; i<gst_caps_get_size(caps); i++) { for (uint i=0; i<gst_caps_get_size(caps); i++) {
GstStructure *structure = gst_caps_get_structure(caps, i); GstStructure *structure = gst_caps_get_structure(caps, i);
gst_structure_set_name(structure, "video/x-raw-yuv"); gst_structure_set_name(structure, "video/x-raw-yuv");
@@ -1184,16 +1181,6 @@ QList<QSize> CameraBinSession::supportedResolutions(QPair<int,int> rate,
<< QSize(2580, 1936); << QSize(2580, 1936);
QSize minSize = res.first(); QSize minSize = res.first();
QSize maxSize = res.last(); QSize maxSize = res.last();
#if defined(Q_WS_MAEMO_6)
if (cameraRole() == FrontCamera && maxSize.width() > 640)
maxSize = QSize(640, 480);
else if (mode == QCamera::CaptureVideo && maxSize.width() > 1280)
maxSize = QSize(1280, 720);
#else
Q_UNUSED(mode);
#endif
res.clear(); res.clear();
foreach (const QSize &candidate, commonSizes) { foreach (const QSize &candidate, commonSizes) {

View File

@@ -76,10 +76,13 @@ public:
virtual GstElement *buildElement() = 0; virtual GstElement *buildElement() = 0;
}; };
class CameraBinSession : public QObject, public QGstreamerSyncEventFilter class CameraBinSession : public QObject,
public QGstreamerBusMessageFilter,
public QGstreamerSyncMessageFilter
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(qint64 duration READ duration NOTIFY durationChanged) Q_PROPERTY(qint64 duration READ duration NOTIFY durationChanged)
Q_INTERFACES(QGstreamerBusMessageFilter QGstreamerSyncMessageFilter)
public: public:
enum CameraRole { enum CameraRole {
FrontCamera, // Secondary camera FrontCamera, // Secondary camera
@@ -90,7 +93,8 @@ public:
~CameraBinSession(); ~CameraBinSession();
GstPhotography *photography(); GstPhotography *photography();
GstElement *cameraBin() { return m_pipeline; } GstElement *cameraBin() { return m_camerabin; }
QGstreamerBusHelper *bus() { return m_busHelper; }
CameraRole cameraRole() const; CameraRole cameraRole() const;
@@ -139,13 +143,12 @@ public:
qint64 duration() const; qint64 duration() const;
void recordVideo(); void recordVideo();
void pauseVideoRecording();
void resumeVideoRecording();
void stopVideoRecording(); void stopVideoRecording();
bool isMuted() const; bool isMuted() const;
bool processSyncMessage(const QGstreamerMessage &message); bool processSyncMessage(const QGstreamerMessage &message);
bool processBusMessage(const QGstreamerMessage &message);
signals: signals:
void stateChanged(QCamera::State state); void stateChanged(QCamera::State state);
@@ -157,7 +160,6 @@ signals:
void viewfinderChanged(); void viewfinderChanged();
void readyChanged(bool); void readyChanged(bool);
void busyChanged(bool); void busyChanged(bool);
void busMessage(const QGstreamerMessage &message);
public slots: public slots:
void setDevice(const QString &device); void setDevice(const QString &device);
@@ -167,14 +169,12 @@ public slots:
void setMuted(bool); void setMuted(bool);
private slots: private slots:
void handleBusMessage(const QGstreamerMessage &message);
void handleViewfinderChange(); void handleViewfinderChange();
private: private:
bool setupCameraBin(); bool setupCameraBin();
void setupCaptureResolution(); void setupCaptureResolution();
void updateVideoSourceCaps(); GstElement *buildCameraSource();
GstElement *buildVideoSrc();
static void updateBusyStatus(GObject *o, GParamSpec *p, gpointer d); static void updateBusyStatus(GObject *o, GParamSpec *p, gpointer d);
QUrl m_sink; QUrl m_sink;
@@ -211,14 +211,12 @@ private:
QGstreamerBusHelper *m_busHelper; QGstreamerBusHelper *m_busHelper;
GstBus* m_bus; GstBus* m_bus;
GstElement *m_pipeline; GstElement *m_camerabin;
GstElement *m_videoSrc; GstElement *m_videoSrc;
GstElement *m_viewfinderElement; GstElement *m_viewfinderElement;
bool m_viewfinderHasChanged; bool m_viewfinderHasChanged;
bool m_videoInputHasChanged; bool m_videoInputHasChanged;
GstCaps *m_sourceCaps;
GstElement *m_audioSrc; GstElement *m_audioSrc;
GstElement *m_audioConvert; GstElement *m_audioConvert;
GstElement *m_capsFilter; GstElement *m_capsFilter;

View File

@@ -46,58 +46,10 @@
#include <QtCore/qdebug.h> #include <QtCore/qdebug.h>
CameraBinVideoEncoder::CameraBinVideoEncoder(CameraBinSession *session) CameraBinVideoEncoder::CameraBinVideoEncoder(CameraBinSession *session)
:QVideoEncoderControl(session), m_session(session) :QVideoEncoderControl(session),
m_session(session),
m_codecs(QGstCodecsInfo::VideoEncoder)
{ {
QList<QByteArray> codecCandidates;
#if defined(Q_WS_MAEMO_6)
codecCandidates << "video/mpeg4" << "video/h264" << "video/h263";
m_elementNames["video/h264"] = "dsph264enc";
m_elementNames["video/mpeg4"] = "dsphdmp4venc";
m_elementNames["video/h263"] = "dsph263enc";
QStringList options = QStringList() << "mode" << "keyframe-interval" << "max-bitrate" << "intra-refresh";
m_codecOptions["video/h264"] = options;
m_codecOptions["video/mpeg4"] = options;
m_codecOptions["video/h263"] = options;
#else
codecCandidates << "video/h264" << "video/xvid" << "video/mpeg4"
<< "video/mpeg1" << "video/mpeg2" << "video/theora"
<< "video/VP8" << "video/h261" << "video/mjpeg";
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_elementNames["video/mjpeg"] = "ffenc_mjpeg";
m_elementNames["video/VP8"] = "vp8enc";
m_elementNames["video/h261"] = "ffenc_h261";
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();
#endif
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,
CameraBinContainer::supportedStreamTypes(factory, GST_PAD_SRC));
gst_object_unref(GST_OBJECT(factory));
}
}
} }
CameraBinVideoEncoder::~CameraBinVideoEncoder() CameraBinVideoEncoder::~CameraBinVideoEncoder()
@@ -134,12 +86,12 @@ QList< qreal > CameraBinVideoEncoder::supportedFrameRates(const QVideoEncoderSet
QStringList CameraBinVideoEncoder::supportedVideoCodecs() const QStringList CameraBinVideoEncoder::supportedVideoCodecs() const
{ {
return m_codecs; return m_codecs.supportedCodecs();
} }
QString CameraBinVideoEncoder::videoCodecDescription(const QString &codecName) const QString CameraBinVideoEncoder::videoCodecDescription(const QString &codecName) const
{ {
return m_codecDescriptions.value(codecName); return m_codecs.codecDescription(codecName);
} }
QStringList CameraBinVideoEncoder::supportedEncodingOptions(const QString &codec) const QStringList CameraBinVideoEncoder::supportedEncodingOptions(const QString &codec) const
@@ -180,118 +132,6 @@ void CameraBinVideoEncoder::resetActualSettings()
m_videoSettings = m_userSettings; m_videoSettings = m_userSettings;
} }
GstElement *CameraBinVideoEncoder::createEncoder()
{
QString codec = m_videoSettings.codec();
QByteArray elementName = m_elementNames.value(codec);
GstElement *encoderElement = gst_element_factory_make( elementName.constData(), "video-encoder");
if (encoderElement) {
if (m_videoSettings.encodingMode() == QtMultimediaKit::ConstantQualityEncoding) {
QtMultimediaKit::EncodingQuality qualityValue = m_videoSettings.quality();
if (elementName == "x264enc") {
//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 (elementName == "xvidenc") {
//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 (elementName == "ffenc_mpeg4" ||
elementName == "ffenc_mpeg1video" ||
elementName == "ffenc_mpeg2video" ) {
//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 (elementName == "theoraenc") {
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 if (elementName == "dsph264enc" ||
elementName == "dspmp4venc" ||
elementName == "dsphdmp4venc" ||
elementName == "dsph263enc") {
//only bitrate parameter is supported
int qualityTable[] = {
1000000, //VeryLow
2000000, //Low
4000000, //Normal
8000000, //High
16000000 //VeryHigh
};
int bitrate = qualityTable[qualityValue];
g_object_set(G_OBJECT(encoderElement), "bitrate", bitrate, 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;
}
}
}
return encoderElement;
}
QPair<int,int> CameraBinVideoEncoder::rateAsRational(qreal frameRate) const QPair<int,int> CameraBinVideoEncoder::rateAsRational(qreal frameRate) const
{ {
@@ -324,8 +164,19 @@ QPair<int,int> CameraBinVideoEncoder::rateAsRational(qreal frameRate) const
return QPair<int,int>(); return QPair<int,int>();
} }
GstEncodingProfile *CameraBinVideoEncoder::createProfile()
QSet<QString> CameraBinVideoEncoder::supportedStreamTypes(const QString &codecName) const
{ {
return m_streamTypes.value(codecName); QString codec = m_videoSettings.codec();
GstCaps *caps;
if (codec.isEmpty())
caps = gst_caps_new_any();
else
caps = gst_caps_from_string(codec.toLatin1());
return (GstEncodingProfile *)gst_encoding_video_profile_new(
caps,
NULL, //preset
NULL, //restriction
0); //presence
} }

View File

@@ -50,6 +50,9 @@ class CameraBinSession;
#include <QtCore/qset.h> #include <QtCore/qset.h>
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/pbutils/pbutils.h>
#include <gst/pbutils/encoding-profile.h>
#include "qgstcodecsinfo.h"
QT_USE_NAMESPACE QT_USE_NAMESPACE
@@ -78,29 +81,23 @@ public:
QVariant encodingOption(const QString &codec, const QString &name) const; QVariant encodingOption(const QString &codec, const QString &name) const;
void setEncodingOption(const QString &codec, const QString &name, const QVariant &value); void setEncodingOption(const QString &codec, const QString &name, const QVariant &value);
GstElement *createEncoder();
QSet<QString> supportedStreamTypes(const QString &codecName) const;
void setActualVideoSettings(const QVideoEncoderSettings&); void setActualVideoSettings(const QVideoEncoderSettings&);
void resetActualSettings(); void resetActualSettings();
GstEncodingProfile *createProfile();
Q_SIGNALS: Q_SIGNALS:
void settingsChanged(); void settingsChanged();
private: private:
CameraBinSession *m_session; CameraBinSession *m_session;
QStringList m_codecs; QGstCodecsInfo m_codecs;
QMap<QString,QString> m_codecDescriptions;
QMap<QString,QByteArray> m_elementNames;
QMap<QString,QStringList> m_codecOptions; QMap<QString,QStringList> m_codecOptions;
QMap<QString, QMap<QString, QVariant> > m_options;
QVideoEncoderSettings m_videoSettings; // backend selected settings, using m_userSettings QVideoEncoderSettings m_videoSettings; // backend selected settings, using m_userSettings
QVideoEncoderSettings m_userSettings; QVideoEncoderSettings m_userSettings;
QMap<QString, QMap<QString, QVariant> > m_options;
QMap<QString, QSet<QString> > m_streamTypes;
}; };
#endif #endif

View File

@@ -21,7 +21,8 @@ PKGCONFIG += \
gstreamer-base-0.10 \ gstreamer-base-0.10 \
gstreamer-interfaces-0.10 \ gstreamer-interfaces-0.10 \
gstreamer-audio-0.10 \ gstreamer-audio-0.10 \
gstreamer-video-0.10 gstreamer-video-0.10 \
gstreamer-pbutils-0.10
maemo*:PKGCONFIG +=gstreamer-plugins-bad-0.10 maemo*:PKGCONFIG +=gstreamer-plugins-bad-0.10
contains(config_test_gstreamer_appsrc, yes): PKGCONFIG += gstreamer-app-0.10 contains(config_test_gstreamer_appsrc, yes): PKGCONFIG += gstreamer-app-0.10
@@ -53,6 +54,7 @@ HEADERS += \
qgstreamervideoinputdevicecontrol.h \ qgstreamervideoinputdevicecontrol.h \
gstvideoconnector.h \ gstvideoconnector.h \
qabstractgstbufferpool.h \ qabstractgstbufferpool.h \
qgstcodecsinfo.h \
qgstutils.h qgstutils.h
SOURCES += \ SOURCES += \
@@ -65,6 +67,7 @@ SOURCES += \
qgstvideobuffer.cpp \ qgstvideobuffer.cpp \
qvideosurfacegstsink.cpp \ qvideosurfacegstsink.cpp \
qgstreamervideoinputdevicecontrol.cpp \ qgstreamervideoinputdevicecontrol.cpp \
qgstcodecsinfo.cpp \
gstvideoconnector.c \ gstvideoconnector.c \
qgstutils.cpp qgstutils.cpp

View File

@@ -160,17 +160,16 @@ QMediaControl *QGstreamerCaptureService::requestControl(const char *name)
if (!m_videoOutput) { if (!m_videoOutput) {
if (qstrcmp(name, QVideoRendererControl_iid) == 0) { if (qstrcmp(name, QVideoRendererControl_iid) == 0) {
m_videoOutput = m_videoRenderer; m_videoOutput = m_videoRenderer;
m_captureSession->setVideoPreview(m_videoRenderer);
} else if (qstrcmp(name, QVideoWindowControl_iid) == 0) { } else if (qstrcmp(name, QVideoWindowControl_iid) == 0) {
m_videoOutput = m_videoWindow; m_videoOutput = m_videoWindow;
m_captureSession->setVideoPreview(m_videoWindow);
} else if (qstrcmp(name, QVideoWidgetControl_iid) == 0) { } else if (qstrcmp(name, QVideoWidgetControl_iid) == 0) {
m_captureSession->setVideoPreview(m_videoWidgetControl);
m_videoOutput = m_videoWidgetControl; m_videoOutput = m_videoWidgetControl;
} }
if (m_videoOutput) if (m_videoOutput) {
m_captureSession->setVideoPreview(m_videoOutput);
return m_videoOutput; return m_videoOutput;
}
} }
return 0; return 0;

View File

@@ -96,8 +96,8 @@ QGstreamerCaptureSession::QGstreamerCaptureSession(QGstreamerCaptureSession::Cap
m_bus = gst_element_get_bus(m_pipeline); m_bus = gst_element_get_bus(m_pipeline);
m_busHelper = new QGstreamerBusHelper(m_bus, this); m_busHelper = new QGstreamerBusHelper(m_bus, this);
m_busHelper->installSyncEventFilter(this); m_busHelper->installMessageFilter(this);
connect(m_busHelper, SIGNAL(message(QGstreamerMessage)), SLOT(busMessage(QGstreamerMessage)));
m_audioEncodeControl = new QGstreamerAudioEncode(this); m_audioEncodeControl = new QGstreamerAudioEncode(this);
m_videoEncodeControl = new QGstreamerVideoEncode(this); m_videoEncodeControl = new QGstreamerVideoEncode(this);
m_imageEncodeControl = new QGstreamerImageEncode(this); m_imageEncodeControl = new QGstreamerImageEncode(this);
@@ -735,6 +735,8 @@ void QGstreamerCaptureSession::setVideoPreview(QObject *viewfinder)
this, SIGNAL(viewfinderChanged())); this, SIGNAL(viewfinderChanged()));
disconnect(m_viewfinder, SIGNAL(readyChanged(bool)), disconnect(m_viewfinder, SIGNAL(readyChanged(bool)),
this, SIGNAL(readyChanged(bool))); this, SIGNAL(readyChanged(bool)));
m_busHelper->removeMessageFilter(m_viewfinder);
} }
m_viewfinder = viewfinder; m_viewfinder = viewfinder;
@@ -745,6 +747,8 @@ void QGstreamerCaptureSession::setVideoPreview(QObject *viewfinder)
this, SIGNAL(viewfinderChanged())); this, SIGNAL(viewfinderChanged()));
connect(m_viewfinder, SIGNAL(readyChanged(bool)), connect(m_viewfinder, SIGNAL(readyChanged(bool)),
this, SIGNAL(readyChanged(bool))); this, SIGNAL(readyChanged(bool)));
m_busHelper->installMessageFilter(m_viewfinder);
} }
emit viewfinderChanged(); emit viewfinderChanged();
@@ -917,29 +921,7 @@ void QGstreamerCaptureSession::setMetaData(const QMap<QByteArray, QVariant> &dat
} }
} }
bool QGstreamerCaptureSession::processSyncMessage(const QGstreamerMessage &message) bool QGstreamerCaptureSession::processBusMessage(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(); GstMessage* gm = message.rawMessage();
@@ -1027,11 +1009,8 @@ void QGstreamerCaptureSession::busMessage(const QGstreamerMessage &message)
} }
//qDebug() << "New session state:" << ENUM_NAME(QGstreamerCaptureSession,"State",m_state); //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);
} }
return false;
} }
void QGstreamerCaptureSession::setMuted(bool muted) void QGstreamerCaptureSession::setMuted(bool muted)

View File

@@ -76,12 +76,13 @@ public:
virtual QList<QSize> supportedResolutions(qreal frameRate = -1) const = 0; virtual QList<QSize> supportedResolutions(qreal frameRate = -1) const = 0;
}; };
class QGstreamerCaptureSession : public QObject, public QGstreamerSyncEventFilter class QGstreamerCaptureSession : public QObject, public QGstreamerBusMessageFilter
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(qint64 duration READ duration NOTIFY durationChanged) Q_PROPERTY(qint64 duration READ duration NOTIFY durationChanged)
Q_ENUMS(State) Q_ENUMS(State)
Q_ENUMS(CaptureMode) Q_ENUMS(CaptureMode)
Q_INTERFACES(QGstreamerBusMessageFilter)
public: public:
enum CaptureMode { Audio = 1, Video = 2, Image=4, AudioAndVideo = Audio | Video }; enum CaptureMode { Audio = 1, Video = 2, Image=4, AudioAndVideo = Audio | Video };
enum State { StoppedState, PreviewState, PausedState, RecordingState }; enum State { StoppedState, PreviewState, PausedState, RecordingState };
@@ -89,6 +90,8 @@ public:
QGstreamerCaptureSession(CaptureMode captureMode, QObject *parent); QGstreamerCaptureSession(CaptureMode captureMode, QObject *parent);
~QGstreamerCaptureSession(); ~QGstreamerCaptureSession();
QGstreamerBusHelper *bus() { return m_busHelper; }
CaptureMode captureMode() const { return m_captureMode; } CaptureMode captureMode() const { return m_captureMode; }
void setCaptureMode(CaptureMode); void setCaptureMode(CaptureMode);
@@ -122,7 +125,7 @@ public:
bool isReady() const; bool isReady() const;
bool processSyncMessage(const QGstreamerMessage &message); bool processBusMessage(const QGstreamerMessage &message);
signals: signals:
void stateChanged(QGstreamerCaptureSession::State state); void stateChanged(QGstreamerCaptureSession::State state);
@@ -144,9 +147,6 @@ public slots:
void setMetaData(const QMap<QByteArray, QVariant>&); void setMetaData(const QMap<QByteArray, QVariant>&);
void setMuted(bool); void setMuted(bool);
private slots:
void busMessage(const QGstreamerMessage &message);
private: private:
enum PipelineMode { EmptyPipeline, PreviewPipeline, RecordingPipeline, PreviewAndRecordingPipeline }; enum PipelineMode { EmptyPipeline, PreviewPipeline, RecordingPipeline, PreviewAndRecordingPipeline };

View File

@@ -155,8 +155,7 @@ QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent)
// Sort out messages // Sort out messages
m_bus = gst_element_get_bus(m_playbin); m_bus = gst_element_get_bus(m_playbin);
m_busHelper = new QGstreamerBusHelper(m_bus, this); m_busHelper = new QGstreamerBusHelper(m_bus, this);
connect(m_busHelper, SIGNAL(message(QGstreamerMessage)), SLOT(busMessage(QGstreamerMessage))); m_busHelper->installMessageFilter(this);
m_busHelper->installSyncEventFilter(this);
g_object_set(G_OBJECT(m_playbin), "video-sink", m_videoOutputBin, NULL); g_object_set(G_OBJECT(m_playbin), "video-sink", m_videoOutputBin, NULL);
@@ -188,6 +187,11 @@ QGstreamerPlayerSession::~QGstreamerPlayerSession()
} }
} }
GstElement *QGstreamerPlayerSession::playbin() const
{
return m_playbin;
}
#if defined(HAVE_GST_APPSRC) #if defined(HAVE_GST_APPSRC)
void QGstreamerPlayerSession::configureAppSrcElement(GObject* object, GObject *orig, GParamSpec *pspec, QGstreamerPlayerSession* self) void QGstreamerPlayerSession::configureAppSrcElement(GObject* object, GObject *orig, GParamSpec *pspec, QGstreamerPlayerSession* self)
{ {
@@ -444,16 +448,20 @@ void QGstreamerPlayerSession::setVideoRenderer(QObject *videoOutput)
this, SLOT(updateVideoRenderer())); this, SLOT(updateVideoRenderer()));
disconnect(m_videoOutput, SIGNAL(readyChanged(bool)), disconnect(m_videoOutput, SIGNAL(readyChanged(bool)),
this, SLOT(updateVideoRenderer())); this, SLOT(updateVideoRenderer()));
}
if (videoOutput) { m_busHelper->removeMessageFilter(m_videoOutput);
connect(videoOutput, SIGNAL(sinkChanged()),
this, SLOT(updateVideoRenderer()));
connect(videoOutput, SIGNAL(readyChanged(bool)),
this, SLOT(updateVideoRenderer()));
} }
m_videoOutput = videoOutput; m_videoOutput = videoOutput;
if (m_videoOutput) {
connect(m_videoOutput, SIGNAL(sinkChanged()),
this, SLOT(updateVideoRenderer()));
connect(m_videoOutput, SIGNAL(readyChanged(bool)),
this, SLOT(updateVideoRenderer()));
m_busHelper->installMessageFilter(m_videoOutput);
}
} }
QGstreamerVideoRendererInterface* renderer = qobject_cast<QGstreamerVideoRendererInterface*>(videoOutput); QGstreamerVideoRendererInterface* renderer = qobject_cast<QGstreamerVideoRendererInterface*>(videoOutput);
@@ -877,29 +885,9 @@ void QGstreamerPlayerSession::setSeekable(bool seekable)
} }
} }
bool QGstreamerPlayerSession::processSyncMessage(const QGstreamerMessage &message) bool QGstreamerPlayerSession::processBusMessage(const QGstreamerMessage &message)
{ {
GstMessage* gm = message.rawMessage(); GstMessage* gm = message.rawMessage();
if (gm && GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) {
if (m_renderer) {
if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_videoSink))
m_renderer->handleSyncMessage(gm);
if (gst_structure_has_name(gm->structure, "prepare-xwindow-id")) {
m_renderer->precessNewStream();
return true;
}
}
}
return false;
}
void QGstreamerPlayerSession::busMessage(const QGstreamerMessage &message)
{
GstMessage* gm = message.rawMessage();
if (gm) { if (gm) {
//tag message comes from elements inside playbin, not from playbin itself //tag message comes from elements inside playbin, not from playbin itself
if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_TAG) { if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_TAG) {
@@ -1111,19 +1099,6 @@ void QGstreamerPlayerSession::busMessage(const QGstreamerMessage &message)
default: default:
break; break;
} }
} else if (m_videoSink
&& m_renderer
&& GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_videoSink)) {
m_renderer->handleBusMessage(gm);
if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_STATE_CHANGED) {
GstState oldState;
GstState newState;
gst_message_parse_state_changed(gm, &oldState, &newState, 0);
if (oldState == GST_STATE_READY && newState == GST_STATE_PAUSED)
m_renderer->precessNewStream();
}
} else if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ERROR) { } else if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ERROR) {
GError *err; GError *err;
gchar *debug; gchar *debug;
@@ -1196,6 +1171,8 @@ void QGstreamerPlayerSession::busMessage(const QGstreamerMessage &message)
} }
} }
} }
return false;
} }
void QGstreamerPlayerSession::getStreamsInfo() void QGstreamerPlayerSession::getStreamsInfo()

View File

@@ -62,14 +62,19 @@ class QGstreamerVideoRendererInterface;
QT_USE_NAMESPACE QT_USE_NAMESPACE
class QGstreamerPlayerSession : public QObject, public QGstreamerSyncEventFilter class QGstreamerPlayerSession : public QObject,
public QGstreamerBusMessageFilter
{ {
Q_OBJECT Q_OBJECT
Q_INTERFACES(QGstreamerBusMessageFilter)
public: public:
QGstreamerPlayerSession(QObject *parent); QGstreamerPlayerSession(QObject *parent);
virtual ~QGstreamerPlayerSession(); virtual ~QGstreamerPlayerSession();
GstElement *playbin() const;
QGstreamerBusHelper *bus() const { return m_busHelper; }
QNetworkRequest request() const; QNetworkRequest request() const;
QMediaPlayer::State state() const { return m_state; } QMediaPlayer::State state() const { return m_state; }
@@ -105,7 +110,7 @@ public:
int activeStream(QMediaStreamsControl::StreamType streamType) const; int activeStream(QMediaStreamsControl::StreamType streamType) const;
void setActiveStream(QMediaStreamsControl::StreamType streamType, int streamNumber); void setActiveStream(QMediaStreamsControl::StreamType streamType, int streamNumber);
bool processSyncMessage(const QGstreamerMessage &message); bool processBusMessage(const QGstreamerMessage &message);
#if defined(HAVE_GST_APPSRC) #if defined(HAVE_GST_APPSRC)
QGstAppSrc *appsrc() const { return m_appSrc; } QGstAppSrc *appsrc() const { return m_appSrc; }
@@ -145,7 +150,6 @@ signals:
void playbackRateChanged(qreal); void playbackRateChanged(qreal);
private slots: private slots:
void busMessage(const QGstreamerMessage &message);
void getStreamsInfo(); void getStreamsInfo();
void setSeekable(bool); void setSeekable(bool);
void finishVideoOutputChange(); void finishVideoOutputChange();

View File

@@ -0,0 +1,182 @@
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qgstcodecsinfo.h"
#include <QtCore/qset.h>
#ifdef QMEDIA_GSTREAMER_CAMERABIN
#include <gst/pbutils/pbutils.h>
#include <gst/pbutils/encoding-profile.h>
#endif
QGstCodecsInfo::QGstCodecsInfo(QGstCodecsInfo::ElementType elementType)
{
#if GST_CHECK_VERSION(0,10,31)
GstElementFactoryListType gstElementType = 0;
switch (elementType) {
case AudioEncoder:
gstElementType = GST_ELEMENT_FACTORY_TYPE_AUDIO_ENCODER;
break;
case VideoEncoder:
gstElementType = GST_ELEMENT_FACTORY_TYPE_VIDEO_ENCODER;
break;
case Muxer:
gstElementType = GST_ELEMENT_FACTORY_TYPE_MUXER;
break;
}
GstCaps *allCaps = supportedElementCaps(gstElementType);
GstCaps *caps = gst_caps_new_empty();
uint codecsCount = gst_caps_get_size(allCaps);
for (uint i=0; i<codecsCount; i++) {
gst_caps_append_structure(caps, gst_caps_steal_structure(allCaps, 0));
gchar * capsString = gst_caps_to_string(caps);
QString codec = QLatin1String(capsString);
m_codecs.append(codec);
#ifdef QMEDIA_GSTREAMER_CAMERABIN
gchar *description = gst_pb_utils_get_codec_description(caps);
m_codecDescriptions.insert(codec, QString::fromUtf8(description));
if (description)
g_free(description);
#else
m_codecDescriptions.insert(codec, codec);
#endif
if (capsString)
g_free(capsString);
gst_caps_remove_structure(caps, 0);
}
#endif // GST_CHECK_VERSION(0,10,31)
}
QStringList QGstCodecsInfo::supportedCodecs() const
{
return m_codecs;
}
QString QGstCodecsInfo::codecDescription(const QString &codec) const
{
return m_codecDescriptions.value(codec);
}
#if GST_CHECK_VERSION(0,10,31)
/*!
List all supported caps for all installed elements of type \a elementType.
Caps are simplified to mime type and a few field necessary to distinguish
different codecs like mpegversion or layer.
*/
GstCaps* QGstCodecsInfo::supportedElementCaps(GstElementFactoryListType elementType,
GstRank minimumRank,
GstPadDirection padDirection)
{
GList *elements = gst_element_factory_list_get_elements(elementType, minimumRank);
GstCaps *res = gst_caps_new_empty();
QSet<QByteArray> fakeEncoderMimeTypes;
fakeEncoderMimeTypes << "unknown/unknown"
<< "audio/x-raw-int" << "audio/x-raw-float"
<< "video/x-raw-yuv" << "video/x-raw-rgb";
QSet<QByteArray> fieldsToAdd;
fieldsToAdd << "mpegversion" << "layer" << "layout" << "raversion"
<< "wmaversion" << "wmvversion" << "variant";
GList *element = elements;
while (element) {
GstElementFactory *factory = (GstElementFactory *)element->data;
element = element->next;
const GList *padTemplates = gst_element_factory_get_static_pad_templates(factory);
while (padTemplates) {
GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *)padTemplates->data;
padTemplates = padTemplates->next;
if (padTemplate->direction == padDirection) {
const GstCaps *caps = gst_static_caps_get(&padTemplate->static_caps);
for (uint i=0; i<gst_caps_get_size(caps); i++) {
const GstStructure *structure = gst_caps_get_structure(caps, i);
//skip "fake" encoders
if (fakeEncoderMimeTypes.contains(gst_structure_get_name(structure)))
continue;
GstStructure *newStructure = gst_structure_new(gst_structure_get_name(structure), NULL);
//add structure fields to distinguish between formats with similar mime types,
//like audio/mpeg
for (int j=0; j<gst_structure_n_fields(structure); j++) {
const gchar* fieldName = gst_structure_nth_field_name(structure, j);
if (fieldsToAdd.contains(fieldName)) {
const GValue *value = gst_structure_get_value(structure, fieldName);
GType valueType = G_VALUE_TYPE(value);
//don't add values of range type,
//gst_pb_utils_get_codec_description complains about not fixed caps
if (valueType != GST_TYPE_INT_RANGE && valueType != GST_TYPE_DOUBLE_RANGE &&
valueType != GST_TYPE_FRACTION_RANGE && valueType != GST_TYPE_LIST &&
valueType != GST_TYPE_ARRAY)
gst_structure_set_value(newStructure, fieldName, value);
}
}
gst_caps_merge_structure(res, newStructure);
}
}
}
}
gst_plugin_feature_list_free(elements);
return res;
}
#endif //GST_CHECK_VERSION(0,10,31)

View File

@@ -0,0 +1,72 @@
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QGSTCODECSINFO_H
#define QGSTCODECSINFO_H
#include <QtCore/qmap.h>
#include <QtCore/qstringlist.h>
#include <gst/gst.h>
class QGstCodecsInfo
{
public:
enum ElementType { AudioEncoder, VideoEncoder, Muxer };
QGstCodecsInfo(ElementType elementType);
QStringList supportedCodecs() const;
QString codecDescription(const QString &codec) const;
#if GST_CHECK_VERSION(0,10,31)
static GstCaps* supportedElementCaps(GstElementFactoryListType elementType,
GstRank minimumRank = GST_RANK_MARGINAL,
GstPadDirection padDirection = GST_PAD_SRC);
#endif
private:
QStringList m_codecs;
QMap<QString,QString> m_codecDescriptions;
};
#endif

View File

@@ -39,9 +39,10 @@
** **
****************************************************************************/ ****************************************************************************/
#include <QMap> #include <QtCore/qmap.h>
#include <QTimer> #include <QtCore/qtimer.h>
#include <QMutex> #include <QtCore/qmutex.h>
#include <QtCore/qlist.h>
#include "qgstreamerbushelper.h" #include "qgstreamerbushelper.h"
@@ -57,7 +58,6 @@ public:
setParent(helper); setParent(helper);
m_tag = gst_bus_add_watch_full(bus, 0, busCallback, this, NULL); m_tag = gst_bus_add_watch_full(bus, 0, busCallback, this, NULL);
m_helper = helper; m_helper = helper;
filter = 0;
} }
void removeWatch(QGstreamerBusHelper* helper) void removeWatch(QGstreamerBusHelper* helper)
@@ -75,7 +75,12 @@ private:
void processMessage(GstBus* bus, GstMessage* message) void processMessage(GstBus* bus, GstMessage* message)
{ {
Q_UNUSED(bus); Q_UNUSED(bus);
emit m_helper->message(message); QGstreamerMessage msg(message);
foreach (QGstreamerBusMessageFilter *filter, busFilters) {
if (filter->processBusMessage(msg))
break;
}
emit m_helper->message(msg);
} }
static gboolean busCallback(GstBus *bus, GstMessage *message, gpointer data) static gboolean busCallback(GstBus *bus, GstMessage *message, gpointer data)
@@ -89,8 +94,9 @@ private:
public: public:
GstBus* bus; GstBus* bus;
QGstreamerSyncEventFilter *filter;
QMutex filterMutex; QMutex filterMutex;
QList<QGstreamerSyncMessageFilter*> syncFilters;
QList<QGstreamerBusMessageFilter*> busFilters;
}; };
#else #else
@@ -131,7 +137,13 @@ private slots:
GstMessage* message; GstMessage* message;
while ((message = gst_bus_poll(it.value(), GST_MESSAGE_ANY, 0)) != 0) { while ((message = gst_bus_poll(it.value(), GST_MESSAGE_ANY, 0)) != 0) {
emit it.key()->message(message); QGstreamerMessage msg(message);
foreach (QGstreamerBusMessageFilter *filter, busFilters) {
if (filter->processBusMessage(msg))
break;
}
emit it.key()->message(msg);
gst_message_unref(message); gst_message_unref(message);
} }
@@ -153,8 +165,9 @@ private:
public: public:
GstBus* bus; GstBus* bus;
QGstreamerSyncEventFilter *filter;
QMutex filterMutex; QMutex filterMutex;
QList<QGstreamerSyncMessageFilter*> syncFilters;
QList<QGstreamerBusMessageFilter*> busFilters;
}; };
#endif #endif
@@ -164,12 +177,12 @@ static GstBusSyncReply syncGstBusFilter(GstBus* bus, GstMessage* message, QGstre
Q_UNUSED(bus); Q_UNUSED(bus);
QMutexLocker lock(&d->filterMutex); QMutexLocker lock(&d->filterMutex);
bool res = false; foreach (QGstreamerSyncMessageFilter *filter, d->syncFilters) {
if (filter->processSyncMessage(QGstreamerMessage(message)))
return GST_BUS_DROP;
}
if (d->filter) return GST_BUS_PASS;
res = d->filter->processSyncMessage(QGstreamerMessage(message));
return res ? GST_BUS_DROP : GST_BUS_PASS;
} }
@@ -194,10 +207,31 @@ QGstreamerBusHelper::~QGstreamerBusHelper()
gst_bus_set_sync_handler(d->bus,0,0); gst_bus_set_sync_handler(d->bus,0,0);
} }
void QGstreamerBusHelper::installSyncEventFilter(QGstreamerSyncEventFilter *filter) void QGstreamerBusHelper::installMessageFilter(QObject *filter)
{ {
QMutexLocker lock(&d->filterMutex); QGstreamerSyncMessageFilter *syncFilter = qobject_cast<QGstreamerSyncMessageFilter*>(filter);
d->filter = filter; if (syncFilter) {
QMutexLocker lock(&d->filterMutex);
if (!d->syncFilters.contains(syncFilter))
d->syncFilters.append(syncFilter);
}
QGstreamerBusMessageFilter *busFilter = qobject_cast<QGstreamerBusMessageFilter*>(filter);
if (busFilter && !d->busFilters.contains(busFilter))
d->busFilters.append(busFilter);
}
void QGstreamerBusHelper::removeMessageFilter(QObject *filter)
{
QGstreamerSyncMessageFilter *syncFilter = qobject_cast<QGstreamerSyncMessageFilter*>(filter);
if (syncFilter) {
QMutexLocker lock(&d->filterMutex);
d->syncFilters.removeAll(syncFilter);
}
QGstreamerBusMessageFilter *busFilter = qobject_cast<QGstreamerBusMessageFilter*>(filter);
if (busFilter)
d->busFilters.removeAll(busFilter);
} }
#include "qgstreamerbushelper.moc" #include "qgstreamerbushelper.moc"

View File

@@ -47,11 +47,23 @@
#include <qgstreamermessage.h> #include <qgstreamermessage.h>
#include <gst/gst.h> #include <gst/gst.h>
class QGstreamerSyncEventFilter { class QGstreamerSyncMessageFilter {
public: public:
//returns true if message was processed and should be dropped, false otherwise //returns true if message was processed and should be dropped, false otherwise
virtual bool processSyncMessage(const QGstreamerMessage &message) = 0; virtual bool processSyncMessage(const QGstreamerMessage &message) = 0;
}; };
#define QGstreamerSyncMessageFilter_iid "com.nokia.Qt.QGstreamerSyncMessageFilter/1.0"
Q_DECLARE_INTERFACE(QGstreamerSyncMessageFilter, QGstreamerSyncMessageFilter_iid)
class QGstreamerBusMessageFilter {
public:
//returns true if message was processed and should be dropped, false otherwise
virtual bool processBusMessage(const QGstreamerMessage &message) = 0;
};
#define QGstreamerBusMessageFilter_iid "com.nokia.Qt.QGstreamerBusMessageFilter/1.0"
Q_DECLARE_INTERFACE(QGstreamerBusMessageFilter, QGstreamerBusMessageFilter_iid)
class QGstreamerBusHelperPrivate; class QGstreamerBusHelperPrivate;
@@ -64,12 +76,12 @@ public:
QGstreamerBusHelper(GstBus* bus, QObject* parent = 0); QGstreamerBusHelper(GstBus* bus, QObject* parent = 0);
~QGstreamerBusHelper(); ~QGstreamerBusHelper();
void installSyncEventFilter(QGstreamerSyncEventFilter *filter); void installMessageFilter(QObject *filter);
void removeMessageFilter(QObject *filter);
signals: signals:
void message(QGstreamerMessage const& message); void message(QGstreamerMessage const& message);
private: private:
QGstreamerBusHelperPrivate* d; QGstreamerBusHelperPrivate* d;
}; };

View File

@@ -364,13 +364,16 @@ bool QGstreamerGLTextureRenderer::isReady() const
return m_surface->supportedPixelFormats(EGLImageTextureHandle).isEmpty(); return m_surface->supportedPixelFormats(EGLImageTextureHandle).isEmpty();
} }
void QGstreamerGLTextureRenderer::handleBusMessage(GstMessage* gm) bool QGstreamerGLTextureRenderer::processBusMessage(const QGstreamerMessage &message)
{ {
GstMessage* gm = message.rawMessage();
#ifdef GL_TEXTURE_SINK_DEBUG #ifdef GL_TEXTURE_SINK_DEBUG
qDebug() << Q_FUNC_INFO << GST_MESSAGE_TYPE_NAME(gm); qDebug() << Q_FUNC_INFO << GST_MESSAGE_TYPE_NAME(gm);
#endif #endif
if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_STATE_CHANGED) { if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_STATE_CHANGED &&
GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_videoSink)) {
GstState oldState; GstState oldState;
GstState newState; GstState newState;
gst_message_parse_state_changed(gm, &oldState, &newState, 0); gst_message_parse_state_changed(gm, &oldState, &newState, 0);
@@ -387,22 +390,20 @@ void QGstreamerGLTextureRenderer::handleBusMessage(GstMessage* gm)
updateNativeVideoSize(); updateNativeVideoSize();
} }
} }
return false;
} }
void QGstreamerGLTextureRenderer::handleSyncMessage(GstMessage* gm) bool QGstreamerGLTextureRenderer::processSyncMessage(const QGstreamerMessage &message)
{ {
GstMessage* gm = message.rawMessage();
if ((GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) &&
gst_structure_has_name(gm->structure, "prepare-xwindow-id") &&
m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) {
#ifdef GL_TEXTURE_SINK_DEBUG #ifdef GL_TEXTURE_SINK_DEBUG
qDebug() << Q_FUNC_INFO; qDebug() << Q_FUNC_INFO;
#endif #endif
if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT &&
gst_structure_has_name(gm->structure, "prepare-xwindow-id"))
precessNewStream();
}
void QGstreamerGLTextureRenderer::precessNewStream()
{
if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) {
GstXOverlay *overlay = GST_X_OVERLAY(m_videoSink); GstXOverlay *overlay = GST_X_OVERLAY(m_videoSink);
gst_x_overlay_set_xwindow_id(overlay, m_winId); gst_x_overlay_set_xwindow_id(overlay, m_winId);
@@ -417,7 +418,11 @@ void QGstreamerGLTextureRenderer::precessNewStream()
GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink");
m_bufferProbeId = gst_pad_add_buffer_probe(pad, G_CALLBACK(padBufferProbe), this); m_bufferProbeId = gst_pad_add_buffer_probe(pad, G_CALLBACK(padBufferProbe), this);
return true;
} }
return false;
} }
void QGstreamerGLTextureRenderer::stopRenderer() void QGstreamerGLTextureRenderer::stopRenderer()

View File

@@ -44,6 +44,7 @@
#include <qvideorenderercontrol.h> #include <qvideorenderercontrol.h>
#include "qvideosurfacegstsink.h" #include "qvideosurfacegstsink.h"
#include "qgstreamerbushelper.h"
#include "qgstreamervideorendererinterface.h" #include "qgstreamervideorendererinterface.h"
#include <QtGui/qcolor.h> #include <QtGui/qcolor.h>
@@ -54,10 +55,13 @@ QT_USE_NAMESPACE
class QGLContext; class QGLContext;
class QGstreamerGLTextureRenderer : public QVideoRendererControl, public QGstreamerVideoRendererInterface class QGstreamerGLTextureRenderer : public QVideoRendererControl,
public QGstreamerVideoRendererInterface,
public QGstreamerSyncMessageFilter,
public QGstreamerBusMessageFilter
{ {
Q_OBJECT Q_OBJECT
Q_INTERFACES(QGstreamerVideoRendererInterface) Q_INTERFACES(QGstreamerVideoRendererInterface QGstreamerSyncMessageFilter QGstreamerBusMessageFilter)
Q_PROPERTY(bool overlayEnabled READ overlayEnabled WRITE setOverlayEnabled) Q_PROPERTY(bool overlayEnabled READ overlayEnabled WRITE setOverlayEnabled)
Q_PROPERTY(qulonglong winId READ winId WRITE setWinId) Q_PROPERTY(qulonglong winId READ winId WRITE setWinId)
@@ -75,9 +79,8 @@ public:
GstElement *videoSink(); GstElement *videoSink();
bool isReady() const; bool isReady() const;
void handleBusMessage(GstMessage* gm); bool processBusMessage(const QGstreamerMessage &message);
void handleSyncMessage(GstMessage* gm); bool processSyncMessage(const QGstreamerMessage &message);
void precessNewStream();
void stopRenderer(); void stopRenderer();
int framebufferNumber() const; int framebufferNumber() const;

View File

@@ -61,7 +61,6 @@ public:
void setSurface(QAbstractVideoSurface *surface); void setSurface(QAbstractVideoSurface *surface);
GstElement *videoSink(); GstElement *videoSink();
void precessNewStream() {}
bool isReady() const { return m_surface != 0; } bool isReady() const { return m_surface != 0; }

View File

@@ -51,7 +51,6 @@ class QGstreamerVideoRendererInterface
public: public:
virtual ~QGstreamerVideoRendererInterface(); virtual ~QGstreamerVideoRendererInterface();
virtual GstElement *videoSink() = 0; virtual GstElement *videoSink() = 0;
virtual void precessNewStream() {}
//stopRenderer() is called when the renderer element is stopped. //stopRenderer() is called when the renderer element is stopped.
//it can be reimplemented when video renderer can't detect //it can be reimplemented when video renderer can't detect
@@ -62,10 +61,6 @@ public:
//(winId is known, //(winId is known,
virtual bool isReady() const { return true; } virtual bool isReady() const { return true; }
//video renderer may handle video sink specific gstreamer messages.
virtual void handleBusMessage(GstMessage*) {};
virtual void handleSyncMessage(GstMessage*) {};
//signals: //signals:
//void sinkChanged(); //void sinkChanged();
//void readyChanged(bool); //void readyChanged(bool);

View File

@@ -179,10 +179,36 @@ bool QGstreamerVideoWidgetControl::eventFilter(QObject *object, QEvent *e)
return false; return false;
} }
void QGstreamerVideoWidgetControl::precessNewStream() bool QGstreamerVideoWidgetControl::processSyncMessage(const QGstreamerMessage &message)
{ {
setOverlay(); GstMessage* gm = message.rawMessage();
QMetaObject::invokeMethod(this, "updateNativeVideoSize", Qt::QueuedConnection);
if (gm && (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) &&
gst_structure_has_name(gm->structure, "prepare-xwindow-id")) {
setOverlay();
QMetaObject::invokeMethod(this, "updateNativeVideoSize", Qt::QueuedConnection);
return true;
}
return false;
}
bool QGstreamerVideoWidgetControl::processBusMessage(const QGstreamerMessage &message)
{
GstMessage* gm = message.rawMessage();
if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_STATE_CHANGED &&
GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_videoSink)) {
GstState oldState;
GstState newState;
gst_message_parse_state_changed(gm, &oldState, &newState, 0);
if (oldState == GST_STATE_READY && newState == GST_STATE_PAUSED)
updateNativeVideoSize();
}
return false;
} }
void QGstreamerVideoWidgetControl::setOverlay() void QGstreamerVideoWidgetControl::setOverlay()

View File

@@ -45,6 +45,7 @@
#include <qvideowidgetcontrol.h> #include <qvideowidgetcontrol.h>
#include "qgstreamervideorendererinterface.h" #include "qgstreamervideorendererinterface.h"
#include "qgstreamerbushelper.h"
QT_USE_NAMESPACE QT_USE_NAMESPACE
@@ -53,15 +54,16 @@ class QGstreamerVideoWidget;
class QGstreamerVideoWidgetControl class QGstreamerVideoWidgetControl
: public QVideoWidgetControl : public QVideoWidgetControl
, public QGstreamerVideoRendererInterface , public QGstreamerVideoRendererInterface
, public QGstreamerSyncMessageFilter
, public QGstreamerBusMessageFilter
{ {
Q_OBJECT Q_OBJECT
Q_INTERFACES(QGstreamerVideoRendererInterface) Q_INTERFACES(QGstreamerVideoRendererInterface QGstreamerSyncMessageFilter QGstreamerBusMessageFilter)
public: public:
QGstreamerVideoWidgetControl(QObject *parent = 0); QGstreamerVideoWidgetControl(QObject *parent = 0);
virtual ~QGstreamerVideoWidgetControl(); virtual ~QGstreamerVideoWidgetControl();
GstElement *videoSink(); GstElement *videoSink();
void precessNewStream();
QWidget *videoWidget(); QWidget *videoWidget();
@@ -86,6 +88,8 @@ public:
void setOverlay(); void setOverlay();
bool eventFilter(QObject *object, QEvent *event); bool eventFilter(QObject *object, QEvent *event);
bool processSyncMessage(const QGstreamerMessage &message);
bool processBusMessage(const QGstreamerMessage &message);
public slots: public slots:
void updateNativeVideoSize(); void updateNativeVideoSize();

View File

@@ -115,14 +115,23 @@ void QGstreamerVideoWindow::setWinId(WId id)
emit readyChanged(false); emit readyChanged(false);
} }
void QGstreamerVideoWindow::precessNewStream() bool QGstreamerVideoWindow::processSyncMessage(const QGstreamerMessage &message)
{ {
if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { GstMessage* gm = message.rawMessage();
if ((GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) &&
gst_structure_has_name(gm->structure, "prepare-xwindow-id") &&
m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) {
gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(m_videoSink), m_windowId); gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(m_videoSink), m_windowId);
GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink");
m_bufferProbeId = gst_pad_add_buffer_probe(pad, G_CALLBACK(padBufferProbe), this); m_bufferProbeId = gst_pad_add_buffer_probe(pad, G_CALLBACK(padBufferProbe), this);
return true;
} }
return false;
} }
QRect QGstreamerVideoWindow::displayRect() const QRect QGstreamerVideoWindow::displayRect() const

View File

@@ -45,6 +45,7 @@
#include <qvideowindowcontrol.h> #include <qvideowindowcontrol.h>
#include "qgstreamervideorendererinterface.h" #include "qgstreamervideorendererinterface.h"
#include "qgstreamerbushelper.h"
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QAbstractVideoSurface; class QAbstractVideoSurface;
@@ -55,10 +56,12 @@ class QX11VideoSurface;
QT_USE_NAMESPACE QT_USE_NAMESPACE
class QGstreamerVideoWindow : public QVideoWindowControl, public QGstreamerVideoRendererInterface class QGstreamerVideoWindow : public QVideoWindowControl,
public QGstreamerVideoRendererInterface,
public QGstreamerSyncMessageFilter
{ {
Q_OBJECT Q_OBJECT
Q_INTERFACES(QGstreamerVideoRendererInterface) Q_INTERFACES(QGstreamerVideoRendererInterface QGstreamerSyncMessageFilter)
Q_PROPERTY(QColor colorKey READ colorKey WRITE setColorKey) Q_PROPERTY(QColor colorKey READ colorKey WRITE setColorKey)
Q_PROPERTY(bool autopaintColorKey READ autopaintColorKey WRITE setAutopaintColorKey) Q_PROPERTY(bool autopaintColorKey READ autopaintColorKey WRITE setAutopaintColorKey)
public: public:
@@ -103,7 +106,7 @@ public:
GstElement *videoSink(); GstElement *videoSink();
void precessNewStream(); bool processSyncMessage(const QGstreamerMessage &message);
bool isReady() const { return m_windowId != 0; } bool isReady() const { return m_windowId != 0; }
signals: signals:

View File

@@ -556,7 +556,7 @@ void tst_QCamera::testCameraWhiteBalance()
QSet<QCameraImageProcessing::WhiteBalanceMode> whiteBalanceModes; QSet<QCameraImageProcessing::WhiteBalanceMode> whiteBalanceModes;
whiteBalanceModes << QCameraImageProcessing::WhiteBalanceAuto; whiteBalanceModes << QCameraImageProcessing::WhiteBalanceAuto;
whiteBalanceModes << QCameraImageProcessing::WhiteBalanceFlash; whiteBalanceModes << QCameraImageProcessing::WhiteBalanceFlash;
whiteBalanceModes << QCameraImageProcessing::WhiteBalanceIncandescent; whiteBalanceModes << QCameraImageProcessing::WhiteBalanceTungsten;
MockCameraService service; MockCameraService service;
service.mockImageProcessingControl->setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceFlash); service.mockImageProcessingControl->setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceFlash);
@@ -574,11 +574,11 @@ void tst_QCamera::testCameraWhiteBalance()
QCOMPARE(cameraImageProcessing->whiteBalanceMode(), QCameraImageProcessing::WhiteBalanceFlash); QCOMPARE(cameraImageProcessing->whiteBalanceMode(), QCameraImageProcessing::WhiteBalanceFlash);
QVERIFY(camera.imageProcessing()->isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceAuto)); QVERIFY(camera.imageProcessing()->isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceAuto));
QVERIFY(camera.imageProcessing()->isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceFlash)); QVERIFY(camera.imageProcessing()->isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceFlash));
QVERIFY(camera.imageProcessing()->isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceIncandescent)); QVERIFY(camera.imageProcessing()->isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceTungsten));
QVERIFY(!camera.imageProcessing()->isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceCloudy)); QVERIFY(!camera.imageProcessing()->isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceCloudy));
cameraImageProcessing->setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceIncandescent); cameraImageProcessing->setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceTungsten);
QCOMPARE(cameraImageProcessing->whiteBalanceMode(), QCameraImageProcessing::WhiteBalanceIncandescent); QCOMPARE(cameraImageProcessing->whiteBalanceMode(), QCameraImageProcessing::WhiteBalanceTungsten);
cameraImageProcessing->setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceManual); cameraImageProcessing->setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceManual);
QCOMPARE(cameraImageProcessing->whiteBalanceMode(), QCameraImageProcessing::WhiteBalanceManual); QCOMPARE(cameraImageProcessing->whiteBalanceMode(), QCameraImageProcessing::WhiteBalanceManual);
@@ -1747,7 +1747,6 @@ void tst_QCamera::testEnumOfQCameraImageProcessing()
whiteBalanceModes << QCameraImageProcessing::WhiteBalanceShade; whiteBalanceModes << QCameraImageProcessing::WhiteBalanceShade;
whiteBalanceModes << QCameraImageProcessing::WhiteBalanceTungsten; whiteBalanceModes << QCameraImageProcessing::WhiteBalanceTungsten;
whiteBalanceModes << QCameraImageProcessing::WhiteBalanceFluorescent; whiteBalanceModes << QCameraImageProcessing::WhiteBalanceFluorescent;
whiteBalanceModes << QCameraImageProcessing::WhiteBalanceIncandescent;
whiteBalanceModes << QCameraImageProcessing::WhiteBalanceFlash; whiteBalanceModes << QCameraImageProcessing::WhiteBalanceFlash;
whiteBalanceModes << QCameraImageProcessing::WhiteBalanceSunset; whiteBalanceModes << QCameraImageProcessing::WhiteBalanceSunset;
whiteBalanceModes << QCameraImageProcessing::WhiteBalanceVendor; whiteBalanceModes << QCameraImageProcessing::WhiteBalanceVendor;
@@ -1783,10 +1782,6 @@ void tst_QCamera::testEnumOfQCameraImageProcessing()
QVERIFY(service.mockImageProcessingControl->isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceFluorescent)); QVERIFY(service.mockImageProcessingControl->isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceFluorescent));
QVERIFY(service.mockImageProcessingControl->whiteBalanceMode() == QCameraImageProcessing::WhiteBalanceFluorescent); QVERIFY(service.mockImageProcessingControl->whiteBalanceMode() == QCameraImageProcessing::WhiteBalanceFluorescent);
service.mockImageProcessingControl->setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceIncandescent);
QVERIFY(service.mockImageProcessingControl->isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceIncandescent));
QVERIFY(service.mockImageProcessingControl->whiteBalanceMode() == QCameraImageProcessing::WhiteBalanceIncandescent);
service.mockImageProcessingControl->setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceFlash); service.mockImageProcessingControl->setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceFlash);
QVERIFY(service.mockImageProcessingControl->isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceFlash)); QVERIFY(service.mockImageProcessingControl->isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceFlash));
QVERIFY(service.mockImageProcessingControl->whiteBalanceMode() == QCameraImageProcessing::WhiteBalanceFlash); QVERIFY(service.mockImageProcessingControl->whiteBalanceMode() == QCameraImageProcessing::WhiteBalanceFlash);

View File

@@ -107,6 +107,7 @@ public slots:
private slots: private slots:
void constructor(); void constructor();
void mediaObject(); void mediaObject();
void deleteMediaObject();
void isReadyForCapture(); void isReadyForCapture();
void capture(); void capture();
void cancelCapture(); void cancelCapture();
@@ -165,6 +166,29 @@ void tst_QCameraImageCapture::mediaObject()
QCOMPARE(medobj1, &camera1); QCOMPARE(medobj1, &camera1);
} }
void tst_QCameraImageCapture::deleteMediaObject()
{
MockMediaServiceProvider *provider = new MockMediaServiceProvider;
provider->service = new MockCameraService;
QCamera *camera = new QCamera(0, provider);
QCameraImageCapture *capture = new QCameraImageCapture(camera);
QVERIFY(capture->mediaObject() == camera);
QVERIFY(capture->isAvailable());
delete camera;
delete provider->service;
delete provider;
//capture should detach from camera
QVERIFY(capture->mediaObject() == 0);
QVERIFY(!capture->isAvailable());
capture->capture();
delete capture;
}
//MaemoAPI-1825:test isReadyForCapture //MaemoAPI-1825:test isReadyForCapture
void tst_QCameraImageCapture::isReadyForCapture() void tst_QCameraImageCapture::isReadyForCapture()
{ {

View File

@@ -165,6 +165,26 @@ void tst_QMediaRecorder::testNullControls()
QCOMPARE(spy.count(), 0); QCOMPARE(spy.count(), 0);
} }
void tst_QMediaRecorder::testDeleteMediaObject()
{
MockMediaRecorderControl *mock = new MockMediaRecorderControl(this);
MockMediaRecorderService *service = new MockMediaRecorderService(this, mock);
MockMediaObject *object = new MockMediaObject(this, service);
QMediaRecorder *capture = new QMediaRecorder(object);
QVERIFY(capture->mediaObject() == object);
QVERIFY(capture->isAvailable());
delete object;
delete service;
delete mock;
QVERIFY(capture->mediaObject() == 0);
QVERIFY(!capture->isAvailable());
delete capture;
}
void tst_QMediaRecorder::testError() void tst_QMediaRecorder::testError()
{ {
const QString errorString(QLatin1String("format error")); const QString errorString(QLatin1String("format error"));

View File

@@ -70,6 +70,7 @@ public slots:
private slots: private slots:
void testNullService(); void testNullService();
void testNullControls(); void testNullControls();
void testDeleteMediaObject();
void testError(); void testError();
void testSink(); void testSink();
void testRecord(); void testRecord();