--- /dev/null
+TEMPLATE = app
+TARGET =
+DEPENDPATH += .
+INCLUDEPATH += .
+
+QT += opengl
+
+CONFIG += link_pkgconfig debug
+
+PKGCONFIG = gstreamer-0.10 gstreamer-interfaces-0.10 gstreamer-video-0.10 gstreamer-tag-0.10 \
+ gstreamer-pbutils-0.10
+
+HEADERS += qtcamconfig.h qtcamera.h qtcamscanner.h qtcamdevice.h qtcamviewfinder.h \
+ qtcammode.h qtcamgstreamermessagehandler.h qtcamgstreamermessagelistener.h \
+ qtcamgraphicsviewfinder.h qtcamviewfinderrenderer.h \
+ qtcamviewfinderrenderergeneric.h qtcamimagesettings.h qtcamvideosettings.h \
+ qtcamimagemode.h qtcamvideomode.h qtcammetadata.h
+
+SOURCES += qtcamconfig.cpp qtcamera.cpp qtcamscanner.cpp qtcamdevice.cpp qtcamviewfinder.cpp \
+ qtcammode.cpp qtcamgstreamermessagehandler.cpp qtcamgstreamermessagelistener.cpp \
+ qtcamgraphicsviewfinder.cpp qtcamviewfinderrenderer.cpp \
+ qtcamviewfinderrenderergeneric.cpp qtcamimagesettings.cpp qtcamvideosettings.cpp \
+ qtcamimagemode.cpp qtcamvideomode.cpp qtcammetadata.cpp
+
+HEADERS += qtcammode_p.h qtcamdevice_p.h
+
+SOURCES += main.cpp
+HEADERS += main.h
--- /dev/null
+#include <QApplication>
+#include <QDebug>
+#include "qtcamera.h"
+#include "qtcamdevice.h"
+#include <QWidget>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QGraphicsView>
+#include <QGraphicsScene>
+#include <QGLWidget>
+#include "main.h"
+#include "qtcamgraphicsviewfinder.h"
+#include <QPushButton>
+#include <QToolBar>
+#include <QAction>
+#include <QToolButton>
+#include <QActionGroup>
+#include "qtcamimagemode.h"
+#include "qtcamvideomode.h"
+#include <QPushButton>
+#include "qtcammetadata.h"
+#include <QDateTime>
+
+Camera::Camera(QWidget *parent) :
+ QWidget(parent),
+ m_cam(new QtCamera("camera.ini")),
+ m_dev(0), m_vf(0) {
+
+ setMinimumSize(700, 500);
+
+ QGraphicsView *v = new QGraphicsView;
+ v->setViewport(new QGLWidget);
+ v->setMinimumSize(640, 480);
+ m_scene = new QGraphicsScene;
+ v->setScene(m_scene);
+
+ // m_vf = new QtCamGraphicsViewfinder(m_cam->config());
+ // m_vf->resize(640, 480);
+ // s->addItem(m_vf);
+
+ QVBoxLayout *layout = new QVBoxLayout(this);
+ layout->setContentsMargins(0, 0, 0, 0);
+ layout->addWidget(v);
+
+ QToolBar *tools = new QToolBar(this);
+ layout->addWidget(tools);
+
+ QToolButton *devs = new QToolButton(this);
+ devs->setArrowType(Qt::DownArrow);
+ devs->setPopupMode(QToolButton::InstantPopup);
+ tools->addWidget(devs);
+
+ QActionGroup *group = new QActionGroup(devs);
+ QObject::connect(group, SIGNAL(triggered(QAction *)), this, SLOT(setDevice(QAction *)));
+
+ QList<QPair<QString, QVariant> > devices = m_cam->devices();
+ for (int x = 0; x < devices.size(); x++) {
+ QAction *a = group->addAction(devices[x].first);
+ a->setData(devices[x].second);
+ a->setCheckable(true);
+ devs->addAction(a);
+ }
+
+ tools->addSeparator();
+ tools->addAction(tr("Image"), this, SLOT(switchToImage()));
+ tools->addAction(tr("Video"), this, SLOT(switchToVideo()));
+
+ QList<QAction *> actions = group->actions();
+ if (!actions.isEmpty()) {
+ actions[0]->setChecked(true);
+ setDevice(actions[0]);
+ }
+
+ m_button = new QPushButton(tr("Capture"));
+ tools->addWidget(m_button);
+ QObject::connect(m_button, SIGNAL(clicked()), this, SLOT(buttonClicked()));
+
+ m_meta = new QtCamMetaData(this);
+ m_meta->setDevice(m_dev);
+}
+
+Camera::~Camera() {
+ if (m_dev) {
+ m_dev->stop();
+ }
+}
+
+void Camera::setDevice(QAction *action) {
+ QVariant id = action->data();
+
+ if (m_dev) {
+ m_dev->stop();
+ delete m_dev;
+ delete m_vf;
+ }
+
+ m_vf = new QtCamGraphicsViewfinder(m_cam->config());
+ m_vf->resize(640, 480);
+ m_scene->addItem(m_vf);
+ m_vf->show();
+
+ m_dev = m_cam->device(id);
+ m_dev->setViewfinder(m_vf);
+ m_dev->start();
+}
+
+void Camera::switchToImage() {
+ m_dev->imageMode()->activate();
+}
+
+void Camera::switchToVideo() {
+ m_dev->videoMode()->activate();
+}
+
+void Camera::buttonClicked() {
+ if (m_dev->activeMode() == m_dev->imageMode()) {
+ m_meta->reset();
+ m_meta->setArtist("Mohammed");
+ m_meta->setDateTime(QDateTime::currentDateTime());
+ m_dev->imageMode()->capture("/tmp/foo.jpg");
+ }
+ else if (m_dev->activeMode() == m_dev->videoMode()) {
+ if (m_dev->videoMode()->isRecording()) {
+ m_dev->videoMode()->stopRecording();
+ m_button->setText(tr("Capture"));
+ }
+ else {
+ m_meta->reset();
+ m_meta->setArtist("Mohammed");
+ m_meta->setDateTime(QDateTime::currentDateTime());
+ m_dev->videoMode()->startRecording("/tmp/foo.ogg");
+ m_button->setText(tr("Stop"));
+ }
+ }
+}
+
+int main(int argc, char *argv[]) {
+ QApplication app(argc, argv);
+ Camera c;
+ c.show();
+
+ return app.exec();
+}
--- /dev/null
+// -*- c++ -*-
+
+#ifndef MAIN_H
+#define MAIN_H
+
+#include <QWidget>
+
+class QtCamera;
+class QtCamDevice;
+class QtCamGraphicsViewfinder;
+class QPushButton;
+class QtCamMetaData;
+class QGraphicsScene;
+
+class Camera : public QWidget {
+ Q_OBJECT
+
+public:
+ Camera(QWidget *parent = 0);
+ ~Camera();
+
+private slots:
+ void setDevice(QAction *action);
+ void switchToImage();
+ void switchToVideo();
+ void buttonClicked();
+
+private:
+ QGraphicsScene *m_scene;
+ QtCamera *m_cam;
+ QtCamDevice *m_dev;
+ QtCamGraphicsViewfinder *m_vf;
+ QPushButton *m_button;
+ QtCamMetaData *m_meta;
+};
+
+#endif /* MAIN_H */
--- /dev/null
+#include "qtcamconfig.h"
+#include <QSettings>
+#include <QStringList>
+#include <QDebug>
+
+#define CONFIGURATION_FILE "/etc/qtcamera.ini"
+
+class QtCamConfigPrivate {
+public:
+ QString element(const QString& name) const {
+ return conf->value(QString("%1/element").arg(name)).toString();
+ }
+
+ QSize readResolution(const QString key) {
+ QList<QString> parts = conf->value(key).toString().trimmed().split("x");
+ return QSize(parts[0].toInt(), parts[1].toInt());
+ }
+
+ QPair<int, int> readFrameRate() {
+ QList<QString> parts = conf->value("fps").toString().trimmed().split("/");
+ return qMakePair<int, int>(parts[0].toInt(), parts[1].toInt());
+ }
+
+ QSettings *conf;
+
+ QList<QtCamImageSettings> imageSettings;
+ QList<QtCamVideoSettings> videoSettings;
+};
+
+QtCamConfig::QtCamConfig(QObject *parent) :
+ QObject(parent), d_ptr(new QtCamConfigPrivate) {
+
+ d_ptr->conf = new QSettings(CONFIGURATION_FILE, QSettings::IniFormat, this);
+}
+
+QtCamConfig::QtCamConfig(const QString& configPath, QObject *parent) :
+ QObject(parent), d_ptr(new QtCamConfigPrivate) {
+
+ d_ptr->conf = new QSettings(configPath, QSettings::IniFormat, this);
+
+ defaultImageSettings();
+}
+
+QtCamConfig::~QtCamConfig() {
+ delete d_ptr;
+}
+
+QString QtCamConfig::deviceScannerType() const {
+ return d_ptr->conf->value("devices/scanner").toString();
+}
+
+QString QtCamConfig::deviceScannerProperty() const {
+ return d_ptr->conf->value("devices/property").toString();
+}
+
+QString QtCamConfig::videoSource() const {
+ return d_ptr->element("video-source");
+}
+
+QString QtCamConfig::viewfinderSink() const {
+ return d_ptr->element("viewfinder-sink");
+}
+
+QString QtCamConfig::viewfinderRenderer() const {
+ return d_ptr->conf->value("viewfinder-sink/renderer").toString();
+}
+
+QString QtCamConfig::audioSource() const {
+ return d_ptr->element("audio-source");
+}
+
+QString QtCamConfig::wrapperVideoSource() const {
+ return d_ptr->element("wrapper-video-source");
+}
+
+QString QtCamConfig::wrapperVideoSourceProperty() const {
+ return d_ptr->conf->value("wrapper-video-source/property").toString();
+}
+
+QtCamImageSettings QtCamConfig::defaultImageSettings() {
+
+ QList<QtCamImageSettings> settings = imageSettings();
+
+ QString def = d_ptr->conf->value("image/default").toString();
+ foreach (const QtCamImageSettings& s, settings) {
+ if (s.id() == def) {
+ return s;
+ }
+ }
+
+ // This will crash if no presets have been shipped but you deserve paying
+ // the price of shipping a broken configuration file.
+ return settings[0];
+}
+
+QList<QtCamImageSettings> QtCamConfig::imageSettings() {
+ if (d_ptr->imageSettings.isEmpty()) {
+ QStringList presets = d_ptr->conf->value("image/presets").toStringList();
+ foreach (const QString& preset, presets) {
+ d_ptr->conf->beginGroup(preset);
+ QPair<int, int> fps = d_ptr->readFrameRate();
+ d_ptr->imageSettings <<
+ QtCamImageSettings(preset, d_ptr->conf->value("name").toString(),
+ d_ptr->readResolution("capture"),
+ d_ptr->readResolution("preview"),
+ d_ptr->readResolution("viewfinder"),
+ fps.first, fps.second);
+ d_ptr->conf->endGroup();
+ }
+ }
+
+ return d_ptr->imageSettings;
+}
+
+QtCamVideoSettings QtCamConfig::defaultVideoSettings() {
+ QList<QtCamVideoSettings> settings = videoSettings();
+
+ QString def = d_ptr->conf->value("video/default").toString();
+ foreach (const QtCamVideoSettings& s, settings) {
+ if (s.id() == def) {
+ return s;
+ }
+ }
+
+ // This will crash if no presets have been shipped but you deserve paying
+ // the price of shipping a broken configuration file.
+ return settings[0];
+}
+
+QList<QtCamVideoSettings> QtCamConfig::videoSettings() {
+ if (d_ptr->videoSettings.isEmpty()) {
+ QStringList presets = d_ptr->conf->value("video/presets").toStringList();
+ foreach (const QString& preset, presets) {
+ d_ptr->conf->beginGroup(preset);
+ QPair<int, int> fps = d_ptr->readFrameRate();
+ d_ptr->videoSettings <<
+ QtCamVideoSettings(preset, d_ptr->conf->value("name").toString(),
+ d_ptr->readResolution("capture"),
+ d_ptr->readResolution("preview"),
+ fps.first, fps.second);
+ d_ptr->conf->endGroup();
+ }
+ }
+
+ return d_ptr->videoSettings;
+}
+
+QString QtCamConfig::imageEncodingProfileName() const {
+ return d_ptr->conf->value("image/profile-name").toString();
+}
+
+QString QtCamConfig::imageEncodingProfilePath() const {
+ return d_ptr->conf->value("image/profile-path").toString();
+}
+
+QString QtCamConfig::videoEncodingProfileName() const {
+ return d_ptr->conf->value("video/profile-name").toString();
+}
+
+QString QtCamConfig::videoEncodingProfilePath() const {
+ return d_ptr->conf->value("video/profile-path").toString();
+}
+
+QString QtCamConfig::audioCaptureCaps() const {
+ return d_ptr->conf->value("audio-capture-caps/caps").toString();
+}
--- /dev/null
+// -*- c++ -*-
+
+#ifndef QT_CAM_CONFIG_H
+#define QT_CAM_CONFIG_H
+
+#include <QObject>
+#include "qtcamimagesettings.h"
+#include "qtcamvideosettings.h"
+
+#define SCANNER_TYPE_V4L2 "v4l2"
+#define SCANNER_TYPE_ENUM "enum"
+
+//#define RENDERER_TYPE_GL_SINK "glsink"
+//#define RENDERER_TYPE_X_OVERLAY "xoverlay"
+//#define RENDERER_TYPE_MEEGO "meego"
+#define RENDERER_TYPE_GENERIC "generic"
+class QtCamConfigPrivate;
+
+class QtCamConfig : public QObject {
+ Q_OBJECT
+
+public:
+ QtCamConfig(QObject *parent = 0);
+ QtCamConfig(const QString& configPath, QObject *parent = 0);
+
+ virtual ~QtCamConfig();
+
+ QString deviceScannerType() const;
+ QString deviceScannerProperty() const;
+
+ QString videoSource() const;
+ QString viewfinderSink() const;
+ QString viewfinderRenderer() const;
+ QString audioSource() const;
+ QString wrapperVideoSource() const;
+ QString wrapperVideoSourceProperty() const;
+
+ QtCamImageSettings defaultImageSettings();
+ QList<QtCamImageSettings> imageSettings();
+
+ QtCamVideoSettings defaultVideoSettings();
+ QList<QtCamVideoSettings> videoSettings();
+
+ QString imageEncodingProfileName() const;
+ QString imageEncodingProfilePath() const;
+
+ QString videoEncodingProfileName() const;
+ QString videoEncodingProfilePath() const;
+
+ QString audioCaptureCaps() const;
+
+private:
+ QtCamConfigPrivate *d_ptr;
+};
+
+#endif /* QT_CAM_CONFIG_H */
--- /dev/null
+#include "qtcamdevice.h"
+#include "qtcamviewfinder.h"
+#include "qtcamconfig.h"
+#include "qtcamdevice_p.h"
+#include <QDebug>
+#include <gst/gst.h>
+#include "qtcamgstreamermessagelistener.h"
+#include "qtcammode.h"
+#include "qtcamimagemode.h"
+#include "qtcamvideomode.h"
+
+QtCamDevice::QtCamDevice(QtCamConfig *config, const QString& name,
+ const QVariant& id, QObject *parent) :
+ QObject(parent), d_ptr(new QtCamDevicePrivate) {
+
+ d_ptr->q_ptr = this;
+ d_ptr->name = name;
+ d_ptr->id = id;
+ d_ptr->conf = config;
+
+ d_ptr->cameraBin = gst_element_factory_make("camerabin2", "QtCameraCameraBin");
+ if (!d_ptr->cameraBin) {
+ qCritical() << "Failed to create camerabin";
+ return;
+ }
+
+ d_ptr->createAndAddElement(d_ptr->conf->audioSource(), "audio-source", "QtCameraAudioSrc");
+ d_ptr->createAndAddVideoSource();
+
+ int flags =
+ 0x00000001 /* no-audio-conversion - Do not use audio conversion elements */
+ | 0x00000002 /* no-video-conversion - Do not use video conversion elements */
+ | 0x00000004 /* no-viewfinder-conversion - Do not use viewfinder conversion elements */
+ | 0x00000008; /* no-image-conversion - Do not use image conversion elements */
+
+ g_object_set(d_ptr->cameraBin, "flags", flags, NULL);
+
+ d_ptr->setAudioCaptureCaps();
+
+ // TODO: audio bitrate
+ // TODO: video bitrate
+ // TODO: filters
+ // TODO: capabilities
+ // TODO: custom properties for jifmux, mp4mux, audio encoder, video encoder, sink & video source
+ // color tune, scene modes
+ d_ptr->listener = new QtCamGStreamerMessageListener(gst_element_get_bus(d_ptr->cameraBin),
+ d_ptr, this);
+
+ QObject::connect(d_ptr->listener, SIGNAL(error(const QString&, int, const QString&)),
+ this, SLOT(_d_error(const QString&, int, const QString&)));
+ QObject::connect(d_ptr->listener, SIGNAL(started()), this, SIGNAL(started()));
+ QObject::connect(d_ptr->listener, SIGNAL(stopped()), this, SIGNAL(stopped()));
+
+ d_ptr->image = new QtCamImageMode(d_ptr, this);
+ d_ptr->video = new QtCamVideoMode(d_ptr, this);
+}
+
+QtCamDevice::~QtCamDevice() {
+ stop();
+
+ d_ptr->image->deactivate();
+ d_ptr->video->deactivate();
+
+ delete d_ptr->image; d_ptr->image = 0;
+ delete d_ptr->video; d_ptr->video = 0;
+ delete d_ptr; d_ptr = 0;
+}
+
+bool QtCamDevice::setViewfinder(QtCamViewfinder *viewfinder) {
+ if (isRunning()) {
+ qWarning() << "QtCamDevice: pipeline must be stopped before setting a viewfinder";
+ return false;
+ }
+
+ if (d_ptr->viewfinder == viewfinder) {
+ return true;
+ }
+
+ if (!viewfinder) {
+ qWarning() << "QtCamDevice: viewfinder cannot be unset.";
+ return false;
+ }
+
+ if (d_ptr->viewfinder) {
+ qWarning() << "QtCamDevice: viewfinder cannot be replaced.";
+ return false;
+ }
+
+ if (!viewfinder->setDevice(this)) {
+ return false;
+ }
+
+ d_ptr->viewfinder = viewfinder;
+
+ return true;
+}
+
+bool QtCamDevice::start() {
+ if (d_ptr->error) {
+ qWarning() << "Pipeline must be stopped first because of an error.";
+ return false;
+ }
+
+ if (!d_ptr->cameraBin) {
+ qWarning() << "Missing camerabin";
+ return false;
+ }
+
+ if (!d_ptr->viewfinder) {
+ qWarning() << "Viewfinder not set";
+ return false;
+ }
+
+ if (isRunning()) {
+ return true;
+ }
+
+ if (!d_ptr->active) {
+ d_ptr->image->activate();
+ }
+ else {
+ d_ptr->active->applySettings();
+ }
+
+ // Set sink.
+ if (!d_ptr->setViewfinderSink()) {
+ return false;
+ }
+
+ GstStateChangeReturn err = gst_element_set_state(d_ptr->cameraBin, GST_STATE_PLAYING);
+ if (err == GST_STATE_CHANGE_FAILURE) {
+ qWarning() << "Failed to start camera pipeline";
+ return false;
+ }
+
+ return true;
+}
+
+bool QtCamDevice::stop() {
+ if (!d_ptr->cameraBin) {
+ return true;
+ }
+
+ if (d_ptr->error) {
+ gst_element_set_state(d_ptr->cameraBin, GST_STATE_NULL);
+ d_ptr->error = false;
+ return true;
+ }
+
+ GstState state;
+ gst_element_get_state(d_ptr->cameraBin, &state, 0, GST_CLOCK_TIME_NONE);
+
+ if (state == GST_STATE_NULL) {
+ // Nothing to do.
+ return true;
+ }
+
+ if (!isIdle()) {
+ return false;
+ }
+
+ // First we go to ready:
+ GstStateChangeReturn st = gst_element_set_state(d_ptr->cameraBin, GST_STATE_READY);
+ if (st != GST_STATE_CHANGE_FAILURE) {
+ // Flush the bus:
+ d_ptr->listener->flushMessages();
+ }
+
+ // Now to NULL
+ gst_element_set_state(d_ptr->cameraBin, GST_STATE_NULL);
+
+ return true;
+}
+
+bool QtCamDevice::isRunning() {
+ if (!d_ptr->cameraBin) {
+ return false;
+ }
+
+ GstState state;
+ GstStateChangeReturn err = gst_element_get_state(d_ptr->cameraBin,
+ &state, 0, GST_CLOCK_TIME_NONE);
+
+ if (err == GST_STATE_CHANGE_FAILURE || state != GST_STATE_PLAYING) {
+ return false;
+ }
+
+ return true;
+}
+
+bool QtCamDevice::isIdle() {
+ if (!d_ptr->cameraBin) {
+ return true;
+ }
+
+ gboolean idle = FALSE;
+ g_object_get(d_ptr->cameraBin, "idle", &idle, NULL);
+
+ return idle == TRUE;
+}
+
+QtCamImageMode *QtCamDevice::imageMode() const {
+ return d_ptr->image;
+}
+
+QtCamVideoMode *QtCamDevice::videoMode() const {
+ return d_ptr->video;
+}
+
+QtCamMode *QtCamDevice::activeMode() const {
+ return d_ptr->active;
+}
+
+QString QtCamDevice::name() const {
+ return d_ptr->name;
+}
+
+QVariant QtCamDevice::id() const {
+ return d_ptr->id;
+}
+
+QtCamConfig *QtCamDevice::config() const {
+ return d_ptr->conf;
+}
+
+QtCamGStreamerMessageListener *QtCamDevice::listener() const {
+ return d_ptr->listener;
+}
+
+#include "moc_qtcamdevice.cpp"
--- /dev/null
+// -*- c++ -*-
+
+#ifndef QT_CAM_DEVICE_H
+#define QT_CAM_DEVICE_H
+
+#include <QObject>
+#include <QVariant>
+
+class QtCamDevicePrivate;
+class QtCamConfig;
+class QtCamViewfinder;
+class QtCamVideoMode;
+class QtCamImageMode;
+class QtCamMode;
+class QtCamGStreamerMessageListener;
+class QtCamMetaData;
+
+class QtCamDevice : public QObject {
+ Q_OBJECT
+
+public:
+ QtCamDevice(QtCamConfig *config, const QString& name, const QVariant& id, QObject *parent = 0);
+ ~QtCamDevice();
+
+ bool setViewfinder(QtCamViewfinder *viewfinder);
+ bool start();
+ bool stop();
+ bool isRunning();
+ bool isIdle();
+
+ QtCamImageMode *imageMode() const;
+ QtCamVideoMode *videoMode() const;
+
+ QtCamMode *activeMode() const;
+
+ QString name() const;
+ QVariant id() const;
+
+ QtCamConfig *config() const;
+ QtCamGStreamerMessageListener *listener() const;
+
+signals:
+ void error(const QString& message, int code, const QString& debug);
+ void started();
+ void stopped();
+
+private:
+ Q_PRIVATE_SLOT(d_ptr, void _d_error(const QString&, int, const QString&))
+ friend class QtCamMetaData;
+ QtCamDevicePrivate *d_ptr;
+};
+
+#endif /* QT_CAM_DEVICE_H */
--- /dev/null
+// -*- c++ -*-
+
+#ifndef QT_CAM_DEVICE_P_H
+#define QT_CAM_DEVICE_P_H
+
+#include <QDebug>
+#include <gst/gst.h>
+#include "qtcamconfig.h"
+#include "qtcamviewfinder.h"
+#include "qtcamdevice.h"
+
+class QtCamGStreamerMessageListener;
+class QtCamMode;
+class QtCamImageMode;
+class QtCamVideoMode;
+
+class QtCamDevicePrivate {
+public:
+ QtCamDevicePrivate() :
+ cameraBin(0),
+ videoSource(0),
+ wrapperVideoSource(0),
+ image(0),
+ video(0),
+ active(0),
+ viewfinder(0),
+ conf(0),
+ error(false) {
+
+ }
+
+ GstElement *createAndAddElement(const QString& elementName, const char *prop, const char *name) {
+ GstElement *elem = gst_element_factory_make(elementName.toAscii(), name);
+ if (!elem) {
+ qWarning() << "Failed to create" << elementName;
+ return 0;
+ }
+
+ g_object_set(cameraBin, prop, elem, NULL);
+
+ return elem;
+ }
+
+ void createAndAddVideoSource() {
+ GstElement *src, *wrapper;
+ QString wrapperSrc = conf->wrapperVideoSource();
+ QString prop = conf->wrapperVideoSourceProperty();
+
+ if (!prop.isEmpty() && !wrapperSrc.isEmpty()) {
+ wrapper = gst_element_factory_make(wrapperSrc.toAscii(), "QCameraWrapperVideoSrc");
+ if (!wrapper) {
+ qCritical() << "Failed to create wrapper source" << wrapperSrc;
+ return;
+ }
+ }
+
+ src = gst_element_factory_make(conf->videoSource().toAscii(),
+ "QtCameraVideoSrc");
+ if (!src) {
+ qCritical() << "Failed to create video source";
+ if (wrapper) {
+ gst_object_unref(wrapper);
+ }
+ return;
+ }
+
+ if (wrapper) {
+ g_object_set(wrapper, prop.toAscii(), src, NULL);
+ g_object_set(cameraBin, "camera-source", wrapper, NULL);
+ }
+
+ videoSource = src;
+ wrapperVideoSource = wrapper;
+
+ if (conf->deviceScannerType() == SCANNER_TYPE_ENUM) {
+ int dev = id.toInt();
+ g_object_set(src, conf->deviceScannerProperty().toAscii().data(), dev, NULL);
+ }
+ else {
+ QString dev = id.toString();
+ g_object_set(src, conf->deviceScannerProperty().toAscii().data(),
+ dev.toAscii().data(), NULL);
+ }
+ }
+
+ bool setViewfinderSink() {
+ GstElement *sink;
+ g_object_get(cameraBin, "viewfinder-sink", &sink, NULL);
+
+ if (sink) {
+ gst_object_unref(sink);
+ return true;
+ }
+
+ sink = viewfinder->sinkElement();
+ if (!sink) {
+ qCritical() << "Failed to create GStreamer sink element";
+ return false;
+ }
+
+ g_object_set(cameraBin, "viewfinder-sink", sink, NULL);
+
+ return true;
+ }
+
+ void _d_error(const QString& message, int code, const QString& debug) {
+ error = true;
+
+ QMetaObject::invokeMethod(q_ptr, "error", Q_ARG(QString, message),
+ Q_ARG(int, code), Q_ARG(QString, debug));
+ }
+
+ void setAudioCaptureCaps() {
+ QString captureCaps = conf->audioCaptureCaps();
+ if (!captureCaps.isEmpty()) {
+ GstCaps *caps = gst_caps_from_string(captureCaps.toAscii().data());
+ if (caps) {
+ g_object_set(cameraBin, "audio-capture-caps", caps, NULL);
+ gst_caps_unref(caps);
+ }
+ }
+ }
+
+ QString name;
+ QVariant id;
+
+ QtCamDevice *q_ptr;
+
+ GstElement *cameraBin;
+ GstElement *videoSource;
+ GstElement *wrapperVideoSource;
+
+ QtCamImageMode *image;
+ QtCamVideoMode *video;
+ QtCamMode *active;
+
+ QtCamViewfinder *viewfinder;
+ QtCamConfig *conf;
+ QtCamGStreamerMessageListener *listener;
+ bool error;
+};
+
+#endif /* QT_CAM_DEVICE_P_H */
--- /dev/null
+#include "qtcamera.h"
+#include "qtcamscanner.h"
+#include "qtcamconfig.h"
+#include "qtcamdevice.h"
+#include <gst/gst.h>
+
+class QtCameraPrivate {
+public:
+ QtCamConfig *conf;
+ QtCamScanner *scanner;
+};
+
+QtCamera::QtCamera(QObject *parent) :
+ QObject(parent), d_ptr(new QtCameraPrivate) {
+
+ gst_init(0, 0);
+
+ d_ptr->conf = new QtCamConfig(this);
+ d_ptr->scanner = new QtCamScanner(d_ptr->conf, this);
+
+ refreshDevices();
+}
+
+QtCamera::QtCamera(const QString& configPath, QObject *parent) :
+ QObject(parent), d_ptr(new QtCameraPrivate) {
+
+ gst_init(0, 0);
+
+ d_ptr->conf = new QtCamConfig(configPath, this);
+ d_ptr->scanner = new QtCamScanner(d_ptr->conf, this);
+
+ refreshDevices();
+}
+
+QtCamera::QtCamera(QtCamConfig *config, QObject *parent) :
+ QObject(parent), d_ptr(new QtCameraPrivate) {
+
+ gst_init(0, 0);
+
+ d_ptr->conf = config;
+ d_ptr->scanner = new QtCamScanner(d_ptr->conf, this);
+
+ refreshDevices();
+}
+
+QtCamera::~QtCamera() {
+ delete d_ptr; d_ptr = 0;
+
+ gst_deinit();
+}
+
+
+void QtCamera::refreshDevices() {
+ d_ptr->scanner->refresh();
+}
+
+QList<QPair<QString, QVariant> > QtCamera::devices() const {
+ return d_ptr->scanner->devices();
+}
+
+QtCamDevice *QtCamera::device(const QVariant& id, QObject *parent) {
+ QList<QPair<QString, QVariant> > devs = devices();
+
+ // G++ barfs with foreach and class templates.
+ typedef QPair<QString, QVariant> Dev;
+ foreach (const Dev& dev, devs) {
+ if (dev.second == id) {
+ return new QtCamDevice(d_ptr->conf, dev.first, dev.second, parent ? parent : this);
+ }
+ }
+
+ return 0;
+}
+
+QtCamConfig *QtCamera::config() const {
+ return d_ptr->conf;
+}
--- /dev/null
+// -*- c++ -*-
+
+#ifndef QT_CAMERA_H
+#define QT_CAMERA_H
+
+#include <QObject>
+#include <QPair>
+#include <QVariant>
+
+class QtCamConfig;
+class QtCameraPrivate;
+class QtCamDevice;
+
+class QtCamera : public QObject {
+ Q_OBJECT
+
+public:
+ QtCamera(QObject *parent = 0);
+ QtCamera(const QString& configPath, QObject *parent = 0);
+ QtCamera(QtCamConfig *config, QObject *parent = 0);
+
+ ~QtCamera();
+
+ void refreshDevices();
+
+ QList<QPair<QString, QVariant> > devices() const;
+
+ QtCamDevice *device(const QVariant& id, QObject *parent = 0);
+
+ QtCamConfig *config() const;
+
+private:
+ QtCameraPrivate *d_ptr;
+};
+
+#endif /* QT_CAMERA_H */
--- /dev/null
+#include "qtcamgraphicsviewfinder.h"
+#include "qtcamconfig.h"
+#include "qtcamdevice.h"
+#include <QDebug>
+#include "qtcamviewfinderrenderer.h"
+#include <QPainter>
+#include <QGraphicsSceneResizeEvent>
+
+class QtCamGraphicsViewfinderPrivate {
+public:
+ void ensureBackend() {
+ if (!renderer) {
+ renderer = QtCamViewfinderRenderer::create(conf, q_ptr);
+ renderer->resize(q_ptr->size());
+ QObject::connect(renderer, SIGNAL(updateRequested()), q_ptr, SLOT(updateRequested()));
+ }
+ }
+
+ void resetBackend() {
+ if (renderer) {
+ renderer->reset();
+ }
+ }
+
+ QtCamViewfinderRenderer *renderer;
+ QtCamConfig *conf;
+ QtCamDevice *dev;
+ QtCamGraphicsViewfinder *q_ptr;
+};
+
+QtCamGraphicsViewfinder::QtCamGraphicsViewfinder(QtCamConfig *config, QGraphicsItem *parent) :
+ QGraphicsWidget(parent), d_ptr(new QtCamGraphicsViewfinderPrivate) {
+ d_ptr->conf = config;
+ d_ptr->dev = 0;
+ d_ptr->renderer = 0;
+ d_ptr->q_ptr = this;
+}
+
+QtCamGraphicsViewfinder::~QtCamGraphicsViewfinder() {
+ delete d_ptr; d_ptr = 0;
+}
+
+GstElement *QtCamGraphicsViewfinder::sinkElement() {
+ d_ptr->ensureBackend();
+
+ return d_ptr->renderer->sinkElement();
+}
+
+bool QtCamGraphicsViewfinder::setDevice(QtCamDevice *device) {
+ if (device && d_ptr->dev == device) {
+ return true;
+ }
+
+ if (d_ptr->dev) {
+ qWarning() << "QtCamGraphicsViewfinder: viewfinder cannot be replaced.";
+ return false;
+ }
+
+ if (!device) {
+ qWarning() << "QtCamGraphicsViewfinder: viewfinder cannot be unset.";
+ return false;
+ }
+
+ // This is to break the loop.
+ d_ptr->dev = device;
+ if (!device->setViewfinder(this)) {
+ d_ptr->dev = 0;
+ return false;
+ }
+
+ d_ptr->resetBackend();
+
+ return true;
+}
+
+void QtCamGraphicsViewfinder::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
+ QWidget *widget) {
+ Q_UNUSED(widget);
+ Q_UNUSED(option);
+
+ painter->fillRect(boundingRect(), Qt::black);
+
+ if (!d_ptr->renderer) {
+ return;
+ }
+
+ d_ptr->renderer->paint(painter);
+}
+
+void QtCamGraphicsViewfinder::resizeEvent(QGraphicsSceneResizeEvent *event) {
+ QGraphicsWidget::resizeEvent(event);
+
+ if (!d_ptr->renderer) {
+ return;
+ }
+
+ d_ptr->renderer->resize(event->newSize());
+}
+
+void QtCamGraphicsViewfinder::updateRequested() {
+ update();
+}
--- /dev/null
+// -*- c++ -*-
+
+#ifndef QT_CAM_GRAPHICS_VIEWFINDER_H
+#define QT_CAM_GRAPHICS_VIEWFINDER_H
+
+#include "qtcamviewfinder.h"
+#include <QGraphicsWidget>
+
+class QtCamGraphicsViewfinderPrivate;
+class QtCamConfig;
+
+class QtCamGraphicsViewfinder : public QGraphicsWidget, public QtCamViewfinder {
+ Q_OBJECT
+
+public:
+ QtCamGraphicsViewfinder(QtCamConfig *config, QGraphicsItem *parent = 0);
+ virtual ~QtCamGraphicsViewfinder();
+
+ virtual GstElement *sinkElement();
+ virtual bool setDevice(QtCamDevice *device);
+
+ virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
+ QWidget *widget = 0);
+
+protected:
+ void resizeEvent(QGraphicsSceneResizeEvent *event);
+
+private slots:
+ void updateRequested();
+
+private:
+ QtCamGraphicsViewfinderPrivate *d_ptr;
+};
+
+#endif /* QT_CAM_GRAPHICS_VIEWFINDER_H */
--- /dev/null
+#include "qtcamgstreamermessagehandler.h"
+
+class QtCamGStreamerMessageHandlerPrivate {
+public:
+ QString name;
+};
+
+QtCamGStreamerMessageHandler::QtCamGStreamerMessageHandler(const QString& messageName,
+ QObject *parent) :
+ QObject(parent), d_ptr(new QtCamGStreamerMessageHandlerPrivate) {
+
+ d_ptr->name = messageName;
+}
+
+QtCamGStreamerMessageHandler::~QtCamGStreamerMessageHandler() {
+ delete d_ptr; d_ptr = 0;
+}
+
+QString QtCamGStreamerMessageHandler::messageName() const {
+ return d_ptr->name;
+}
+
+void QtCamGStreamerMessageHandler::handleMessage(GstMessage *message) {
+ emit messageSent(message);
+}
+
--- /dev/null
+// -*- c++ -*-
+
+#ifndef QT_CAM_GSTREAMER_MESSAGE_HANDLER_H
+#define QT_CAM_GSTREAMER_MESSAGE_HANDLER_H
+
+#include <QObject>
+#include <gst/gst.h>
+
+class QtCamGStreamerMessageHandlerPrivate;
+
+class QtCamGStreamerMessageHandler : public QObject {
+ Q_OBJECT
+
+public:
+ QtCamGStreamerMessageHandler(const QString& messageName, QObject *parent = 0);
+ virtual ~QtCamGStreamerMessageHandler();
+
+ QString messageName() const;
+
+ virtual void handleMessage(GstMessage *message);
+
+signals:
+ void messageSent(GstMessage *message);
+
+private:
+ QtCamGStreamerMessageHandlerPrivate *d_ptr;
+};
+
+#endif /* QT_CAM_GSTREAMER_MESSAGE_HANDLER_H */
--- /dev/null
+#include "qtcamgstreamermessagelistener.h"
+#include "qtcamgstreamermessagehandler.h"
+#include <QMultiMap>
+#include <QMutex>
+#include <QDebug>
+#include "qtcamdevice_p.h"
+
+class QtCamGStreamerMessageListenerPrivate {
+public:
+ QMultiMap<QString, QtCamGStreamerMessageHandler *> handlers;
+ QMultiMap<QString, QtCamGStreamerMessageHandler *> syncHandlers;
+
+ int handleMessage(GstMessage *message, QMultiMap<QString, QtCamGStreamerMessageHandler *>& map) {
+ const GstStructure *s = gst_message_get_structure(message);
+ if (!s) {
+ return 0;
+ }
+
+ QList<QtCamGStreamerMessageHandler *> list =
+ map.values(gst_structure_get_name(s));
+
+ foreach (QtCamGStreamerMessageHandler *handler, list) {
+ handler->handleMessage(message);
+ }
+
+ return list.size();
+ }
+
+ void handleMessage(GstMessage *message) {
+
+ switch (GST_MESSAGE_TYPE(message)) {
+ case GST_MESSAGE_ELEMENT:
+ handleMessage(message, handlers);
+ break;
+
+ case GST_MESSAGE_ERROR: {
+ GError *err = NULL;
+ gchar *debug;
+
+ gst_message_parse_error(message, &err, &debug);
+
+ QMetaObject::invokeMethod(q_ptr, "error", Q_ARG(QString, err->message),
+ Q_ARG(int, err->code), Q_ARG(QString, debug));
+
+ qDebug() << "Error" << err->message << ":" << debug;
+
+ g_error_free(err);
+ g_free(debug);
+ }
+ break;
+
+ case GST_MESSAGE_WARNING: {
+ GError *err = NULL;
+ gchar *debug;
+
+ gst_message_parse_warning(message, &err, &debug);
+
+ qDebug() << "Warning" << err->message << ":" << debug;
+
+ g_error_free(err);
+ g_free(debug);
+ }
+ break;
+
+ case GST_MESSAGE_INFO: {
+ GError *err = NULL;
+ gchar *debug;
+
+ gst_message_parse_info(message, &err, &debug);
+
+ qDebug() << "Info" << err->message << ":" << debug;
+
+ g_error_free(err);
+ g_free(debug);
+ }
+ break;
+
+ case GST_MESSAGE_STATE_CHANGED: {
+ if (GST_ELEMENT(GST_MESSAGE_SRC(message)) != dev->cameraBin) {
+ break;
+ }
+
+ GstState oldState, newState, pending;
+ gst_message_parse_state_changed(message, &oldState, &newState, &pending);
+ if (oldState == GST_STATE_PAUSED && newState == GST_STATE_PLAYING) {
+ QMetaObject::invokeMethod(q_ptr, "started");
+ }
+ else if (oldState == GST_STATE_PLAYING && newState == GST_STATE_PAUSED) {
+ QMetaObject::invokeMethod(q_ptr, "stopped");
+ }
+ }
+ break;
+
+ default:
+ // TODO: other types
+ break;
+ }
+ }
+
+ bool handleSyncMessage(GstMessage *message) {
+ QMutexLocker locker(&syncMutex);
+
+ if (handleMessage(message, syncHandlers) != 0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ void addHandler(QtCamGStreamerMessageHandler *handler,
+ QMultiMap<QString, QtCamGStreamerMessageHandler *>& map) {
+ if (!map.contains(handler->messageName(), handler)) {
+ map.insert(handler->messageName(), handler);
+ handler->setParent(q_ptr);
+ }
+ }
+
+ void removeHandler(QtCamGStreamerMessageHandler *handler,
+ QMultiMap<QString, QtCamGStreamerMessageHandler *>& map) {
+ map.remove(handler->messageName(), handler);
+ handler->setParent(0);
+ }
+
+ QMutex syncMutex;
+
+ GstBus *bus;
+
+ QtCamDevicePrivate *dev;
+
+ guint watchId;
+
+ QtCamGStreamerMessageListener *q_ptr;
+};
+
+gboolean async_handler(GstBus *bus, GstMessage *message, gpointer data)
+{
+ Q_UNUSED(bus);
+
+ QtCamGStreamerMessageListenerPrivate *d_ptr =
+ static_cast<QtCamGStreamerMessageListenerPrivate *>(data);
+
+ d_ptr->handleMessage(message);
+
+ // Call us again
+ return TRUE;
+}
+
+GstBusSyncReply sync_handler(GstBus *bus, GstMessage *message, gpointer data) {
+ Q_UNUSED(bus);
+
+ QtCamGStreamerMessageListenerPrivate *d_ptr =
+ static_cast<QtCamGStreamerMessageListenerPrivate *>(data);
+
+ if (d_ptr->handleSyncMessage(message)) {
+ gst_message_unref(message);
+ return GST_BUS_DROP;
+ }
+
+ return GST_BUS_PASS;
+}
+
+QtCamGStreamerMessageListener::QtCamGStreamerMessageListener(GstBus *bus,
+ QtCamDevicePrivate *d,
+ QObject *parent) :
+ QObject(parent), d_ptr(new QtCamGStreamerMessageListenerPrivate) {
+
+ d_ptr->dev = d;
+ d_ptr->bus = bus;
+ d_ptr->q_ptr = this;
+
+ d_ptr->watchId = gst_bus_add_watch(d_ptr->bus, async_handler, d_ptr);
+
+ gst_bus_set_sync_handler(d_ptr->bus, sync_handler, d_ptr);
+}
+
+QtCamGStreamerMessageListener::~QtCamGStreamerMessageListener() {
+ g_source_remove(d_ptr->watchId);
+ gst_bus_set_sync_handler(d_ptr->bus, NULL, NULL);
+
+ qDeleteAll(d_ptr->handlers);
+
+ d_ptr->syncMutex.lock();
+ qDeleteAll(d_ptr->syncHandlers);
+ d_ptr->syncMutex.unlock();
+
+ gst_object_unref(d_ptr->bus);
+
+ delete d_ptr; d_ptr = 0;
+}
+
+void QtCamGStreamerMessageListener::addHandler(QtCamGStreamerMessageHandler *handler) {
+ d_ptr->addHandler(handler, d_ptr->handlers);
+}
+
+void QtCamGStreamerMessageListener::removeHandler(QtCamGStreamerMessageHandler *handler) {
+ d_ptr->removeHandler(handler, d_ptr->handlers);
+}
+
+void QtCamGStreamerMessageListener::addSyncHandler(QtCamGStreamerMessageHandler *handler) {
+ QMutexLocker locker(&d_ptr->syncMutex);
+
+ d_ptr->addHandler(handler, d_ptr->syncHandlers);
+}
+
+void QtCamGStreamerMessageListener::removeSyncHandler(QtCamGStreamerMessageHandler *handler) {
+ QMutexLocker locker(&d_ptr->syncMutex);
+
+ d_ptr->removeHandler(handler, d_ptr->syncHandlers);
+}
+
+void QtCamGStreamerMessageListener::flushMessages() {
+ GstMessage *message = 0;
+
+ while ((message = gst_bus_pop(d_ptr->bus))) {
+ d_ptr->handleMessage(message);
+ gst_message_unref(message);
+ }
+}
--- /dev/null
+// -*- c++ -*-
+
+#ifndef QT_CAM_GSTREAMER_MESSAGE_LISTENER_H
+#define QT_CAM_GSTREAMER_MESSAGE_LISTENER_H
+
+#include <QObject>
+#include <gst/gst.h>
+
+class QtCamGStreamerMessageListenerPrivate;
+class QtCamGStreamerMessageHandler;
+class QtCamDevicePrivate;
+
+class QtCamGStreamerMessageListener : public QObject {
+ Q_OBJECT
+
+public:
+ QtCamGStreamerMessageListener(GstBus *bus, QtCamDevicePrivate *d, QObject *parent = 0);
+ ~QtCamGStreamerMessageListener();
+
+ void addHandler(QtCamGStreamerMessageHandler *handler);
+ void removeHandler(QtCamGStreamerMessageHandler *handler);
+ void addSyncHandler(QtCamGStreamerMessageHandler *handler);
+ void removeSyncHandler(QtCamGStreamerMessageHandler *handler);
+
+ void flushMessages();
+
+signals:
+ void error(const QString& message, int code, const QString& debug);
+ void started();
+ void stopped();
+
+private:
+ QtCamGStreamerMessageListenerPrivate *d_ptr;
+};
+
+#endif /* QT_CAM_GSTREAMER_MESSAGE_LISTENER_H */
--- /dev/null
+#include "qtcamimagemode.h"
+#include "qtcammode_p.h"
+#include "qtcamdevice_p.h"
+#include "qtcamimagesettings.h"
+#include "qtcamdevice.h"
+
+class QtCamImageModePrivate : public QtCamModePrivate {
+public:
+ QtCamImageModePrivate(QtCamDevicePrivate *dev) :
+ QtCamModePrivate(dev),
+ settings(dev->conf->defaultImageSettings()) {
+
+ }
+
+ ~QtCamImageModePrivate() {
+ }
+
+ bool wrapperIsReady() {
+ if (!dev->wrapperVideoSource) {
+ return false;
+ }
+
+ gboolean ready = FALSE;
+ g_object_get(dev->wrapperVideoSource, "ready-for-capture", &ready, NULL);
+
+ return ready == TRUE;
+ }
+
+ QtCamImageSettings settings;
+};
+
+QtCamImageMode::QtCamImageMode(QtCamDevicePrivate *d, QObject *parent) :
+ QtCamMode(new QtCamImageModePrivate(d), "mode-image", "image-done", parent) {
+
+ d_ptr = (QtCamImageModePrivate *)QtCamMode::d_ptr;
+
+ QString name = d_ptr->dev->conf->imageEncodingProfileName();
+ QString path = d_ptr->dev->conf->imageEncodingProfilePath();
+
+ if (!name.isEmpty() && !path.isEmpty()) {
+ GstEncodingProfile *profile = d_ptr->loadProfile(path, name);
+ if (profile) {
+ setProfile(profile);
+ }
+ }
+}
+
+QtCamImageMode::~QtCamImageMode() {
+
+}
+
+bool QtCamImageMode::canCapture() {
+ return QtCamMode::canCapture() && d_ptr->wrapperIsReady();
+}
+
+void QtCamImageMode::applySettings() {
+ setCaps("image-capture-caps", d_ptr->settings.captureResolution(),
+ d_ptr->settings.frameRate());
+ setCaps("viewfinder-caps", d_ptr->settings.viewfinderResolution(),
+ d_ptr->settings.frameRate());
+ setPreviewSize(d_ptr->settings.previewResolution());
+}
+
+void QtCamImageMode::start() {
+ // Nothing
+}
+
+void QtCamImageMode::stop() {
+ // Nothing
+}
+
+bool QtCamImageMode::capture(const QString& fileName) {
+ if (!canCapture()) {
+ return false;
+ }
+
+ setFileName(fileName);
+
+ g_object_set(d_ptr->dev->cameraBin, "location", fileName.toUtf8().data(), NULL);
+ g_signal_emit_by_name(d_ptr->dev->cameraBin, "start-capture", NULL);
+
+ return true;
+}
+
+bool QtCamImageMode::setSettings(const QtCamImageSettings& settings) {
+ d_ptr->settings = settings;
+
+ if (!d_ptr->dev->q_ptr->isIdle()) {
+ return false;
+ }
+
+ applySettings();
+
+ return true;
+}
+
+void QtCamImageMode::setProfile(GstEncodingProfile *profile) {
+ if (!d_ptr->dev->cameraBin) {
+ gst_encoding_profile_unref(profile);
+ return;
+ }
+
+ g_object_set(d_ptr->dev->cameraBin, "image-profile", profile, NULL);
+}
--- /dev/null
+// -*- c++ -*-
+
+#ifndef QT_CAM_IMAGE_MODE_H
+#define QT_CAM_IMAGE_MODE_H
+
+#include "qtcammode.h"
+#include <gst/pbutils/encoding-profile.h>
+
+class QtCamDevicePrivate;
+class QtCamImageModePrivate;
+class QtCamImageSettings;
+
+class QtCamImageMode : public QtCamMode {
+ Q_OBJECT
+
+public:
+ QtCamImageMode(QtCamDevicePrivate *d, QObject *parent = 0);
+ ~QtCamImageMode();
+
+ virtual bool canCapture();
+ virtual void applySettings();
+
+ bool capture(const QString& fileName);
+
+ bool setSettings(const QtCamImageSettings& settings);
+
+ void setProfile(GstEncodingProfile *profile);
+
+signals:
+ void imageSaved(const QString& fileName);
+
+protected:
+ virtual void start();
+ virtual void stop();
+
+private:
+ QtCamImageModePrivate *d_ptr;
+};
+
+#endif /* QT_CAM_IMAGE_MODE_H */
--- /dev/null
+#include "qtcamimagesettings.h"
+
+class QtCamImageSettingsPrivate {
+public:
+ QString id;
+ QString name;
+ QSize capture;
+ QSize preview;
+ QSize viewfinder;
+ int numerator;
+ int denominator;
+};
+
+QtCamImageSettings::QtCamImageSettings(const QString& id, const QString& name,
+ const QSize& capture, const QSize& preview,
+ const QSize& viewfinder,
+ int numerator, int denominator) :
+ d_ptr(new QtCamImageSettingsPrivate) {
+
+ d_ptr->id = id;
+ d_ptr->name = name;
+ d_ptr->capture = capture;
+ d_ptr->preview = preview;
+ d_ptr->viewfinder = viewfinder;
+ d_ptr->numerator = numerator;
+ d_ptr->denominator = denominator;
+}
+
+QtCamImageSettings::QtCamImageSettings(const QtCamImageSettings& other) :
+ d_ptr(new QtCamImageSettingsPrivate) {
+
+ d_ptr->id = other.d_ptr->id;
+ d_ptr->name = other.d_ptr->name;
+ d_ptr->capture = other.d_ptr->capture;
+ d_ptr->preview = other.d_ptr->preview;
+ d_ptr->viewfinder = other.d_ptr->viewfinder;
+ d_ptr->numerator = other.d_ptr->numerator;
+ d_ptr->denominator = other.d_ptr->denominator;
+}
+
+QtCamImageSettings::~QtCamImageSettings() {
+ delete d_ptr;
+}
+
+QtCamImageSettings& QtCamImageSettings::operator=(const QtCamImageSettings&
+ other) {
+ d_ptr->id = other.d_ptr->id;
+ d_ptr->name = other.d_ptr->name;
+ d_ptr->capture = other.d_ptr->capture;
+ d_ptr->preview = other.d_ptr->preview;
+ d_ptr->viewfinder = other.d_ptr->viewfinder;
+ d_ptr->numerator = other.d_ptr->numerator;
+ d_ptr->denominator = other.d_ptr->denominator;
+
+ return *this;
+}
+
+QString QtCamImageSettings::id() const {
+ return d_ptr->id;
+}
+
+QString QtCamImageSettings::name() const {
+ return d_ptr->name;
+}
+
+QSize QtCamImageSettings::captureResolution() const {
+ return d_ptr->capture;
+}
+
+QSize QtCamImageSettings::viewfinderResolution() const {
+ return d_ptr->viewfinder;
+}
+
+QSize QtCamImageSettings::previewResolution() const {
+ return d_ptr->preview;
+}
+
+QPair<int, int> QtCamImageSettings::frameRate() const {
+ return qMakePair<int, int>(d_ptr->numerator, d_ptr->denominator);
+}
--- /dev/null
+// -*- c++ -*-
+
+#ifndef QT_CAM_IMAGE_SETTINGS_H
+#define QT_CAM_IMAGE_SETTINGS_H
+
+#include <QSize>
+#include <QPair>
+#include <QString>
+
+class QtCamImageSettingsPrivate;
+
+class QtCamImageSettings {
+public:
+ QtCamImageSettings(const QString& id, const QString& name, const QSize& capture,
+ const QSize& preview, const QSize& viewfinder,
+ int numerator, int denominator);
+
+ QtCamImageSettings(const QtCamImageSettings& other);
+
+ ~QtCamImageSettings();
+
+ QtCamImageSettings& operator=(const QtCamImageSettings& other);
+
+ QString id() const;
+ QString name() const;
+ QSize captureResolution() const;
+ QSize viewfinderResolution() const;
+ QSize previewResolution() const;
+ QPair<int, int> frameRate() const;
+
+private:
+ QtCamImageSettingsPrivate *d_ptr;
+};
+
+#endif /* QT_CAM_IMAGE_SETTINGS_H */
--- /dev/null
+#include "qtcammetadata.h"
+#include <gst/gsttaglist.h>
+#include "qtcamdevice.h"
+#include "qtcamdevice_p.h"
+#include <QDebug>
+#include <QDate>
+#include <QTime>
+#include <QDateTime>
+
+const char *orientations[] = {
+ "rotate-0",
+ "rotate-90",
+ "rotate-180",
+ "rotate-270"
+};
+
+class QtCamMetaDataPrivate {
+public:
+ void addTag(const char *tag, const QString& value) {
+ if (!setter) {
+ return;
+ }
+
+ gst_tag_setter_add_tags(setter, GST_TAG_MERGE_REPLACE, tag, value.toUtf8().data(), NULL);
+ }
+
+ void addTag(const char *tag, double value) {
+ if (!setter) {
+ return;
+ }
+
+ gst_tag_setter_add_tags(setter, GST_TAG_MERGE_REPLACE, tag, value, NULL);
+ }
+
+ void addTag(const char *tag, GstDateTime *value) {
+ if (!setter) {
+ return;
+ }
+
+ gst_tag_setter_add_tags(setter, GST_TAG_MERGE_REPLACE, tag, value, NULL);
+ }
+
+ GstTagSetter *setter;
+};
+
+QtCamMetaData::QtCamMetaData(QObject *parent) :
+ QObject(parent), d_ptr(new QtCamMetaDataPrivate) {
+ d_ptr->setter = 0;
+}
+
+QtCamMetaData::~QtCamMetaData() {
+ setDevice(0);
+ delete d_ptr; d_ptr = 0;
+}
+
+void QtCamMetaData::setDevice(QtCamDevice *device) {
+ if (d_ptr->setter) {
+ gst_object_unref(d_ptr->setter);
+ d_ptr->setter = 0;
+ }
+
+ if (!device || !device->d_ptr->cameraBin) {
+ return;
+ }
+
+ if (!GST_IS_TAG_SETTER(device->d_ptr->cameraBin)) {
+ return;
+ }
+
+ d_ptr->setter = GST_TAG_SETTER(gst_object_ref(device->d_ptr->cameraBin));
+}
+
+void QtCamMetaData::setManufacturer(const QString& manufacturer) {
+ d_ptr->addTag(GST_TAG_DEVICE_MANUFACTURER, manufacturer);
+}
+
+void QtCamMetaData::setModel(const QString& model) {
+ d_ptr->addTag(GST_TAG_DEVICE_MODEL, model);
+}
+
+void QtCamMetaData::setCountry(const QString& country) {
+ d_ptr->addTag(GST_TAG_GEO_LOCATION_COUNTRY, country);
+}
+
+void QtCamMetaData::setCity(const QString& city) {
+ d_ptr->addTag(GST_TAG_GEO_LOCATION_CITY, city);
+}
+
+void QtCamMetaData::setSuburb(const QString& suburb) {
+ d_ptr->addTag(GST_TAG_GEO_LOCATION_SUBLOCATION, suburb);
+}
+
+void QtCamMetaData::setLongitude(double longitude) {
+ d_ptr->addTag(GST_TAG_GEO_LOCATION_LONGITUDE, longitude);
+}
+
+void QtCamMetaData::setLatitude(double latitude) {
+ d_ptr->addTag(GST_TAG_GEO_LOCATION_LATITUDE, latitude);
+}
+
+void QtCamMetaData::setElevation(double elevation) {
+ d_ptr->addTag(GST_TAG_GEO_LOCATION_ELEVATION, elevation);
+}
+
+void QtCamMetaData::setOrientation(Orientation orientation) {
+ int len = sizeof(orientations) / sizeof(orientations[0]);
+
+ if (orientation <= 0 || orientation >= len) {
+ orientation = Landscape;
+ }
+
+ d_ptr->addTag(GST_TAG_IMAGE_ORIENTATION, orientations[orientation]);
+}
+
+void QtCamMetaData::setArtist(const QString& artist) {
+ d_ptr->addTag(GST_TAG_ARTIST, artist);
+}
+
+void QtCamMetaData::setDateTime(const QDateTime& dateTime) {
+ QDate d = dateTime.date();
+ QTime t = dateTime.time();
+
+ int day = d.day();
+ int month = d.month();
+ int year = d.year();
+ int hour = t.hour();
+ int minute = t.minute();
+
+ // GstDateTime seconds expects microseconds to be there too :|
+ gdouble seconds = t.second();
+ seconds += t.msec()/(1000.0);
+
+ // Current UTC time. Created through string so that the link between
+ // current and utc is lost and secsTo returns non-zero values.
+ QDateTime utcTime = QDateTime::fromString(dateTime.toUTC().toString());
+ gfloat tzoffset = (utcTime.secsTo(dateTime)/3600.0);
+
+ GstDateTime *dt = gst_date_time_new(tzoffset, year, month, day,
+ hour, minute, seconds);
+
+ d_ptr->addTag(GST_TAG_DATE_TIME, dt);
+
+ gst_date_time_unref(dt);
+}
+
+void QtCamMetaData::setCaptureDirection(double direction) {
+ d_ptr->addTag(GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION, direction);
+}
+
+void QtCamMetaData::setHorizontalError(double error) {
+ d_ptr->addTag(GST_TAG_GEO_LOCATION_HORIZONTAL_ERROR, error);
+}
+
+void QtCamMetaData::reset() {
+ if (d_ptr->setter) {
+ gst_tag_setter_reset_tags(d_ptr->setter);
+ }
+}
--- /dev/null
+// -*- c++ -*-
+
+#ifndef QT_CAM_META_DATA_H
+#define QT_CAM_META_DATA_H
+
+#include <QObject>
+
+class QtCamMetaDataPrivate;
+class QDateTime;
+class QtCamDevice;
+
+class QtCamMetaData : public QObject {
+ Q_OBJECT
+
+public:
+
+ typedef enum {
+ Landscape = 0,
+ Portrait,
+ InvertedLandscape,
+ InvertedPortrait
+ } Orientation;
+
+ QtCamMetaData(QObject *parent = 0);
+ ~QtCamMetaData();
+
+ void setDevice(QtCamDevice *device);
+
+ void setManufacturer(const QString& manufacturer);
+ void setModel(const QString& model);
+ void setCountry(const QString& country);
+ void setCity(const QString& city);
+ void setSuburb(const QString& suburb);
+ void setLongitude(double longitude);
+ void setLatitude(double latitude);
+ void setElevation(double elevetion);
+ void setOrientation(Orientation orientation);
+ void setArtist(const QString& artist);
+ void setDateTime(const QDateTime& dateTime);
+ void setCaptureDirection(double direction);
+ void setHorizontalError(double error);
+
+ void reset();
+
+private:
+ QtCamMetaDataPrivate *d_ptr;
+};
+
+#endif /* QT_CAM_META_DATA_H */
--- /dev/null
+#include "qtcammode.h"
+#include "qtcammode_p.h"
+#include "qtcamdevice_p.h"
+#include "qtcamdevice.h"
+#include <QDebug>
+#include "qtcamgstreamermessagehandler.h"
+#include "qtcamgstreamermessagelistener.h"
+#include <gst/video/video.h>
+#include <QImage>
+
+#define PREVIEW_CAPS "video/x-raw-rgb, width = (int) %1, height = (int) %2, bpp = (int) 32, depth = (int) 24, red_mask = (int) 65280, green_mask = (int) 16711680, blue_mask = (int) -16777216"
+
+#define CAPS "video/x-raw-yuv, width = (int) %1, height = (int) %2, framerate = (fraction) %3/%4"
+
+class PreviewImageHandler : public QtCamGStreamerMessageHandler {
+public:
+ PreviewImageHandler(QtCamMode *m, QObject *parent = 0) :
+ QtCamGStreamerMessageHandler("preview-image", parent) {
+ mode = m;
+ }
+
+ virtual void handleMessage(GstMessage *message) {
+ const GstStructure *s = gst_message_get_structure(message);
+ if (!s) {
+ return;
+ }
+
+ const char *file = gst_structure_get_string(s, "location");
+ if (!file) {
+ return;
+ }
+
+ const GValue *val = gst_structure_get_value(s, "buffer");
+ if (!val) {
+ return;
+ }
+
+ GstBuffer *buffer = gst_value_get_buffer(val);
+ if (!buffer) {
+ return;
+ }
+
+ int width, height;
+ GstVideoFormat fmt;
+ if (!gst_video_format_parse_caps(buffer->caps, &fmt, &width, &height)) {
+ return;
+ }
+
+ if (fmt != GST_VIDEO_FORMAT_BGRx || width <= 0 || height <= 0) {
+ return;
+ }
+
+ QImage image(buffer->data, width, height, QImage::Format_RGB32);
+
+ // We need to copy because GStreamer will free the buffer after we return
+ // and since QImage doesn't copythe data by default we will end up with garbage.
+ // TODO: consider a QImage subclass that takes a GstBuffer reference
+ QImage cp = image.copy();
+
+ QString fileName = QString::fromUtf8(file);
+
+ QMetaObject::invokeMethod(mode, "previewAvailable",
+ Q_ARG(QImage, cp), Q_ARG(QString, fileName));
+ }
+
+ QtCamMode *mode;
+};
+
+class DoneHandler : public QtCamGStreamerMessageHandler {
+public:
+ DoneHandler(QtCamMode *m, const char *done, QObject *parent = 0) :
+ QtCamGStreamerMessageHandler(done, parent) {
+ mode = m;
+ }
+
+ virtual void handleMessage(GstMessage *message) {
+ const GstStructure *s = gst_message_get_structure(message);
+ if (gst_structure_has_field(s, "filename")) {
+ const char *str = gst_structure_get_string(s, "filename");
+ if (str) {
+ fileName = QString::fromUtf8(str);
+ }
+ }
+
+ QMetaObject::invokeMethod(mode, "saved", Q_ARG(QString, fileName));
+ }
+
+ QString fileName;
+ QtCamMode *mode;
+};
+
+QtCamMode::QtCamMode(QtCamModePrivate *d, const char *mode, const char *done, QObject *parent) :
+ QObject(parent), d_ptr(d) {
+
+ d_ptr->id = d_ptr->modeId(mode);
+ d_ptr->previewImageHandler = new PreviewImageHandler(this, this);
+ d_ptr->doneHandler = new DoneHandler(this, done, this);
+}
+
+QtCamMode::~QtCamMode() {
+ delete d_ptr; d_ptr = 0;
+}
+
+void QtCamMode::activate() {
+ if (!d_ptr->dev->cameraBin) {
+ return;
+ }
+
+ if (d_ptr->dev->active == this) {
+ return;
+ }
+
+ if (d_ptr->dev->active) {
+ d_ptr->dev->active->deactivate();
+ }
+
+ d_ptr->dev->active = this;
+
+ // TODO: check that we can actually do it. Perhaps the pipeline is busy.
+
+ g_object_set(d_ptr->dev->cameraBin, "mode", d_ptr->id, NULL);
+
+ d_ptr->dev->listener->addHandler(d_ptr->previewImageHandler);
+ d_ptr->dev->listener->addHandler(d_ptr->doneHandler);
+
+ start();
+
+ applySettings();
+}
+
+void QtCamMode::deactivate() {
+ if (d_ptr->dev->active != this) {
+ return;
+ }
+
+ d_ptr->dev->listener->removeHandler(d_ptr->previewImageHandler);
+ d_ptr->dev->listener->removeHandler(d_ptr->doneHandler);
+
+ d_ptr->previewImageHandler->setParent(this);
+ d_ptr->doneHandler->setParent(this);
+
+ stop();
+
+ d_ptr->dev->active = 0;
+}
+
+bool QtCamMode::canCapture() {
+ return d_ptr->dev->cameraBin && isActive() && d_ptr->dev->q_ptr->isRunning();
+}
+
+bool QtCamMode::isActive() {
+ return d_ptr->dev->active == this;
+}
+
+void QtCamMode::setCaps(const char *property, const QSize& resolution,
+ const QPair<int, int> frameRate) {
+
+ if (!d_ptr->dev->cameraBin) {
+ return;
+ }
+
+ // TODO: allow proceeding without specifying a frame rate (maybe we can calculate it ?)
+ if (frameRate.first <= 0 || frameRate.second <= 0) {
+ return;
+ }
+
+ if (resolution.width() <= 0 || resolution.width() <= 0) {
+ return;
+ }
+
+ QString capsString = QString(CAPS)
+ .arg(resolution.width()).arg(resolution.height())
+ .arg(frameRate.first).arg(frameRate.second);
+
+ GstCaps *caps = gst_caps_from_string(capsString.toAscii());
+
+ g_object_set(d_ptr->dev->cameraBin, property, caps, NULL);
+
+ gst_caps_unref(caps);
+}
+
+void QtCamMode::setPreviewSize(const QSize& size) {
+ if (!d_ptr->dev->cameraBin) {
+ return;
+ }
+
+ if (size.width() <= 0 && size.height() <= 0) {
+ g_object_set(d_ptr->dev->cameraBin, "preview-caps", NULL, "post-previews", FALSE, NULL);
+ }
+ else {
+ QString preview = QString(PREVIEW_CAPS).arg(size.width()).arg(size.height());
+
+ GstCaps *caps = gst_caps_from_string(preview.toAscii());
+
+ g_object_set(d_ptr->dev->cameraBin, "preview-caps", caps, "post-previews", TRUE, NULL);
+
+ gst_caps_unref(caps);
+ }
+}
+
+void QtCamMode::setFileName(const QString& fileName) {
+ d_ptr->doneHandler->fileName = fileName;
+}
--- /dev/null
+// -*- c++ -*-
+
+#ifndef QT_CAM_MODE_H
+#define QT_CAM_MODE_H
+
+#include <QObject>
+#include <QPair>
+
+class QtCamModePrivate;
+class QtCamDevicePrivate;
+class QSize;
+class QImage;
+
+class QtCamMode : public QObject {
+ Q_OBJECT
+
+public:
+ QtCamMode(QtCamModePrivate *d, const char *mode, const char *done, QObject *parent = 0);
+ virtual ~QtCamMode();
+
+ void activate();
+ void deactivate();
+
+ virtual bool canCapture();
+ bool isActive();
+
+ virtual void applySettings() = 0;
+
+signals:
+ void previewAvailable(const QImage& image, const QString& fileName);
+ void saved(const QString& fileName);
+
+protected:
+ virtual void start() = 0;
+ virtual void stop() = 0;
+
+ void setCaps(const char *property, const QSize& resolution, const QPair<int, int> frameRate);
+ void setPreviewSize(const QSize& size);
+
+ void setFileName(const QString& fileName);
+
+ QtCamModePrivate *d_ptr;
+};
+
+#endif /* QT_CAM_MODE_H */
--- /dev/null
+// -*- c++ -*-
+
+#ifndef QT_CAM_MODE_P_H
+#define QT_CAM_MODE_P_H
+
+#include <QSize>
+#include "qtcamdevice_p.h"
+#include <gst/pbutils/encoding-profile.h>
+#include <gst/pbutils/encoding-target.h>
+
+class QtCamDevicePrivate;
+class PreviewImageHandler;
+class DoneHandler;
+
+class QtCamModePrivate {
+public:
+ QtCamModePrivate(QtCamDevicePrivate *d) : id(-1), dev(d) {}
+ virtual ~QtCamModePrivate() {}
+
+ int modeId(const char *mode) {
+ if (!dev->cameraBin) {
+ return -1;
+ }
+
+ GParamSpec *pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(dev->cameraBin),
+ "mode");
+ if (!pspec) {
+ return -1;
+ }
+
+ if (!G_IS_PARAM_SPEC_ENUM(pspec)) {
+ return -1;
+ }
+
+ GParamSpecEnum *e = (GParamSpecEnum *)pspec;
+ GEnumClass *klass = e->enum_class;
+
+ for (unsigned x = 0; x < klass->n_values; x++) {
+ if (QLatin1String(mode) == QLatin1String(klass->values[x].value_nick)) {
+ return klass->values[x].value;
+ }
+ }
+
+ return -1;
+ }
+
+ GstEncodingProfile *loadProfile(const QString& path, const QString& name) {
+ GError *error = NULL;
+
+ GstEncodingTarget *target = gst_encoding_target_load_from_file(path.toUtf8().data(), &error);
+ if (!target) {
+ qCritical() << "Failed to load encoding target from" << path << error->message;
+ g_error_free(error);
+ return 0;
+ }
+
+ GstEncodingProfile *profile = gst_encoding_target_get_profile(target, name.toUtf8().data());
+ if (!profile) {
+ qCritical() << "Failed to load encoding profile from" << path;
+ gst_encoding_target_unref(target);
+ return 0;
+ }
+
+ gst_encoding_target_unref(target);
+
+ return profile;
+ }
+
+ int id;
+ QtCamDevicePrivate *dev;
+ PreviewImageHandler *previewImageHandler;
+ DoneHandler *doneHandler;
+};
+
+#endif /* QT_CAM_MODE_P_H */
--- /dev/null
+// -*- c++ -*-
+
+#ifndef QT_CAM_PREVIEW_HELPER_H
+#define QT_CAM_PREVIEW_HELPER_H
+
+#include <QObject>
+
+
+class QtCamPreviewHelper : public QObject {
+ Q_OBJECT
+
+public:
+ QtCamPreviewHelper(QObject *parent = 0);
+ ~QtCamPreviewHelper();
+
+ void enable();
+ void disable();
+};
+
+#endif /* QT_CAM_PREVIEW_HELPER_H */
--- /dev/null
+#include "qtcamscanner.h"
+#include "qtcamconfig.h"
+#include <QDir>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <linux/videodev2.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <gst/gst.h>
+
+class QtCamScannerPrivate {
+public:
+ void scanEnum();
+ void scanV4l2();
+
+ QtCamConfig *conf;
+ QList<QPair<QString, QVariant> > devices;
+};
+
+void QtCamScannerPrivate::scanEnum() {
+ // Too bad there's no way to get the values of an enum without creating the element :(
+ GstElement *elem = gst_element_factory_make(conf->videoSource().toAscii(), NULL);
+ if (!elem) {
+ return;
+ }
+
+ GParamSpec *spec = g_object_class_find_property(G_OBJECT_GET_CLASS(elem),
+ conf->deviceScannerProperty().toAscii());
+ if (!spec) {
+ gst_object_unref(elem);
+ return;
+ }
+
+ if (!G_IS_PARAM_SPEC_ENUM(spec)) {
+ gst_object_unref(elem);
+ return;
+ }
+
+ GParamSpecEnum *e = G_PARAM_SPEC_ENUM(spec);
+ // First add the default:
+ devices << qMakePair<QString, QVariant>(e->enum_class->values[e->default_value].value_name,
+ QByteArray::number(e->default_value));
+
+ for (int x = e->enum_class->minimum; x <= e->enum_class->maximum; x++) {
+ if (x != e->default_value) {
+ devices << qMakePair<QString, QVariant>(e->enum_class->values[x].value_name,
+ QByteArray::number(x));
+ }
+ }
+
+ gst_object_unref(elem);
+}
+
+void QtCamScannerPrivate::scanV4l2() {
+ QDir d("/dev/", "video?", QDir::Name | QDir::IgnoreCase, QDir::System);
+
+ QStringList entries = d.entryList();
+
+ foreach (const QString& dv, entries) {
+ QString dev = d.absoluteFilePath(dv);
+ struct v4l2_capability cap;
+ memset(&cap, 0x0, sizeof(cap));
+
+ int fd = open(dev.toLocal8Bit().data(), O_RDONLY);
+ if (fd == -1) {
+ continue;
+ }
+
+ if (ioctl(fd, VIDIOC_QUERYCAP, &cap) != 0) {
+ close(fd);
+ continue;
+ }
+
+ close(fd);
+
+ if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) {
+ devices << qMakePair<QString, QVariant>((char *)cap.card, dev.toLocal8Bit());
+ }
+ }
+}
+
+QtCamScanner::QtCamScanner(QtCamConfig *conf, QObject *parent) :
+ QObject(parent), d_ptr(new QtCamScannerPrivate) {
+
+ d_ptr->conf = conf;
+}
+
+QtCamScanner::~QtCamScanner() {
+ delete d_ptr; d_ptr = 0;
+}
+
+void QtCamScanner::refresh() {
+ d_ptr->devices.clear();
+
+ if (d_ptr->conf->deviceScannerType() == SCANNER_TYPE_ENUM) {
+ d_ptr->scanEnum();
+ }
+ else {
+ d_ptr->scanV4l2();
+ }
+}
+
+QList<QPair<QString, QVariant> > QtCamScanner::devices() const {
+ return d_ptr->devices;
+}
--- /dev/null
+// -*- c++ -*-
+
+#ifndef QT_CAM_SCANNER_H
+#define QT_CAM_SCANNER_H
+
+#include <QObject>
+#include <QPair>
+#include <QVariant>
+
+class QtCamConfig;
+class QtCamScannerPrivate;
+
+class QtCamScanner : public QObject {
+ Q_OBJECT
+
+public:
+ QtCamScanner(QtCamConfig *conf, QObject *parent = 0);
+ ~QtCamScanner();
+
+ void refresh();
+ QList<QPair<QString, QVariant> > devices() const;
+
+private:
+ QtCamScannerPrivate *d_ptr;
+};
+
+#endif /* QT_CAM_SCANNER_H */
--- /dev/null
+#include "qtcamvideomode.h"
+#include "qtcammode_p.h"
+#include <QDebug>
+#include "qtcamdevice_p.h"
+#include "qtcamdevice.h"
+#include "qtcamvideosettings.h"
+
+class QtCamVideoModePrivate : public QtCamModePrivate {
+public:
+ QtCamVideoModePrivate(QtCamDevicePrivate *dev) :
+ QtCamModePrivate(dev),
+ settings(dev->conf->defaultVideoSettings()) {
+
+ }
+
+ ~QtCamVideoModePrivate() {}
+
+ QtCamVideoSettings settings;
+};
+
+QtCamVideoMode::QtCamVideoMode(QtCamDevicePrivate *d, QObject *parent) :
+ QtCamMode(new QtCamVideoModePrivate(d), "mode-video", "video-done", parent) {
+
+ d_ptr = (QtCamVideoModePrivate *)QtCamMode::d_ptr;
+
+ QString name = d_ptr->dev->conf->videoEncodingProfileName();
+ QString path = d_ptr->dev->conf->videoEncodingProfilePath();
+
+ if (!name.isEmpty() && !path.isEmpty()) {
+ GstEncodingProfile *profile = d_ptr->loadProfile(path, name);
+ if (profile) {
+ setProfile(profile);
+ }
+ }
+}
+
+QtCamVideoMode::~QtCamVideoMode() {
+
+}
+
+bool QtCamVideoMode::canCapture() {
+ return d_ptr->dev->q_ptr->isIdle();
+}
+
+void QtCamVideoMode::applySettings() {
+ setCaps("video-capture-caps", d_ptr->settings.captureResolution(),
+ d_ptr->settings.frameRate());
+ setCaps("viewfinder-caps", d_ptr->settings.captureResolution(),
+ d_ptr->settings.frameRate());
+ setPreviewSize(d_ptr->settings.previewResolution());
+}
+
+void QtCamVideoMode::start() {
+ // Nothing
+}
+
+void QtCamVideoMode::stop() {
+ if (isRecording()) {
+ stopRecording();
+ }
+}
+
+bool QtCamVideoMode::isRecording() {
+ return !d_ptr->dev->q_ptr->isIdle();
+}
+
+bool QtCamVideoMode::startRecording(const QString& fileName) {
+ if (!canCapture() || isRecording()) {
+ return false;
+ }
+
+ setFileName(fileName);
+
+ g_object_set(d_ptr->dev->cameraBin, "location", fileName.toUtf8().data(), NULL);
+ g_signal_emit_by_name(d_ptr->dev->cameraBin, "start-capture", NULL);
+
+ return true;
+}
+
+bool QtCamVideoMode::stopRecording() {
+ if (isRecording()) {
+ g_signal_emit_by_name(d_ptr->dev->cameraBin, "stop-capture", NULL);
+ }
+
+ return true;
+}
+
+bool QtCamVideoMode::setSettings(const QtCamVideoSettings& settings) {
+ d_ptr->settings = settings;
+
+ if (isRecording()) {
+ return false;
+ }
+
+ applySettings();
+
+ return true;
+}
+
+
+void QtCamVideoMode::setProfile(GstEncodingProfile *profile) {
+ if (!d_ptr->dev->cameraBin) {
+ gst_encoding_profile_unref(profile);
+ return;
+ }
+
+ g_object_set(d_ptr->dev->cameraBin, "video-profile", profile, NULL);
+}
--- /dev/null
+// -*- c++ -*-
+
+#ifndef QT_CAM_VIDEO_MODE_H
+#define QT_CAM_VIDEO_MODE_H
+
+#include "qtcammode.h"
+#include <gst/pbutils/encoding-profile.h>
+
+class QtCamDevicePrivate;
+class QtCamVideoModePrivate;
+class QtCamVideoSettings;
+
+class QtCamVideoMode : public QtCamMode {
+ Q_OBJECT
+
+public:
+ QtCamVideoMode(QtCamDevicePrivate *d, QObject *parent = 0);
+ ~QtCamVideoMode();
+
+ virtual bool canCapture();
+ virtual void applySettings();
+
+ bool isRecording();
+ bool startRecording(const QString& fileName);
+ bool stopRecording();
+
+ bool setSettings(const QtCamVideoSettings& settings);
+
+ void setProfile(GstEncodingProfile *profile);
+
+protected:
+ virtual void start();
+ virtual void stop();
+
+private:
+ QtCamVideoModePrivate *d_ptr;
+};
+
+#endif /* QT_CAM_VIDEO_MODE_H */
--- /dev/null
+#include "qtcamvideosettings.h"
+
+class QtCamVideoSettingsPrivate {
+public:
+ QString id;
+ QString name;
+ QSize capture;
+ QSize preview;
+ int numerator;
+ int denominator;
+};
+
+QtCamVideoSettings::QtCamVideoSettings(const QString& id, const QString& name,
+ const QSize& capture, const QSize& preview,
+ int numerator, int denominator) :
+ d_ptr(new QtCamVideoSettingsPrivate) {
+
+ d_ptr->id = id;
+ d_ptr->name = name;
+ d_ptr->capture = capture;
+ d_ptr->preview = preview;
+ d_ptr->numerator = numerator;
+ d_ptr->denominator = denominator;
+}
+
+QtCamVideoSettings::QtCamVideoSettings(const QtCamVideoSettings& other) :
+ d_ptr(new QtCamVideoSettingsPrivate) {
+
+ d_ptr->id = other.d_ptr->id;
+ d_ptr->name = other.d_ptr->name;
+ d_ptr->capture = other.d_ptr->capture;
+ d_ptr->preview = other.d_ptr->preview;
+ d_ptr->numerator = other.d_ptr->numerator;
+ d_ptr->denominator = other.d_ptr->denominator;
+}
+
+QtCamVideoSettings::~QtCamVideoSettings() {
+ delete d_ptr;
+}
+
+QtCamVideoSettings& QtCamVideoSettings::operator=(const QtCamVideoSettings&
+ other) {
+ d_ptr->id = other.d_ptr->id;
+ d_ptr->name = other.d_ptr->name;
+ d_ptr->capture = other.d_ptr->capture;
+ d_ptr->preview = other.d_ptr->preview;
+ d_ptr->numerator = other.d_ptr->numerator;
+ d_ptr->denominator = other.d_ptr->denominator;
+
+ return *this;
+}
+
+QString QtCamVideoSettings::id() const {
+ return d_ptr->id;
+}
+
+QString QtCamVideoSettings::name() const {
+ return d_ptr->name;
+}
+
+QSize QtCamVideoSettings::captureResolution() const {
+ return d_ptr->capture;
+}
+
+QSize QtCamVideoSettings::previewResolution() const {
+ return d_ptr->preview;
+}
+
+QPair<int, int> QtCamVideoSettings::frameRate() const {
+ return qMakePair<int, int>(d_ptr->numerator, d_ptr->denominator);
+}
--- /dev/null
+// -*- c++ -*-
+
+#ifndef QT_CAM_VIDEO_SETTINGS_H
+#define QT_CAM_VIDEO_SETTINGS_H
+
+#include <QSize>
+#include <QPair>
+#include <QString>
+
+class QtCamVideoSettingsPrivate;
+
+class QtCamVideoSettings {
+public:
+ QtCamVideoSettings(const QString& id, const QString& name,
+ const QSize& capture, const QSize& preview,
+ int numerator, int denominator);
+
+ QtCamVideoSettings(const QtCamVideoSettings& other);
+
+ ~QtCamVideoSettings();
+
+ QtCamVideoSettings& operator=(const QtCamVideoSettings& other);
+
+ QString id() const;
+ QString name() const;
+ QSize captureResolution() const;
+ QSize previewResolution() const;
+ QPair<int, int> frameRate() const;
+
+private:
+ QtCamVideoSettingsPrivate *d_ptr;
+};
+
+#endif /* QT_CAM_VIDEO_SETTINGS_H */
--- /dev/null
+#include "qtcamviewfinder.h"
+
+QtCamViewfinder::QtCamViewfinder() {
+
+}
+
+QtCamViewfinder::~QtCamViewfinder() {
+
+}
--- /dev/null
+// -*- c++ -*-
+
+#ifndef QT_CAM_VIEWFINDER_H
+#define QT_CAM_VIEWFINDER_H
+
+#include <gst/gst.h>
+
+class QtCamDevice;
+
+class QtCamViewfinder {
+public:
+ QtCamViewfinder();
+ virtual ~QtCamViewfinder();
+
+ virtual GstElement *sinkElement() = 0;
+ virtual bool setDevice(QtCamDevice *device) = 0;
+};
+
+#endif /* QT_CAM_VIEWFINDER_H */
--- /dev/null
+#include "qtcamviewfinderrenderer.h"
+#include "qtcamconfig.h"
+#include <QMap>
+#include <QDebug>
+
+static QMap<QString, QMetaObject> _renderers;
+
+QtCamViewfinderRenderer::QtCamViewfinderRenderer(QtCamConfig *config, QObject *parent) :
+ QObject(parent) {
+
+ Q_UNUSED(config);
+}
+
+QtCamViewfinderRenderer::~QtCamViewfinderRenderer() {
+
+}
+
+QtCamViewfinderRenderer *QtCamViewfinderRenderer::create(QtCamConfig *config, QObject *parent) {
+ QString key = config->viewfinderRenderer();
+ if (!_renderers.contains(key)) {
+ qCritical() << "Unknown renderer" << key;
+ return 0;
+ }
+
+ QObject *obj = _renderers[key].newInstance(Q_ARG(QtCamConfig *, config),
+ Q_ARG(QObject *, parent));
+
+ if (!obj) {
+ qCritical() << "Failed to create renderer" << key;
+ return 0;
+ }
+
+ return dynamic_cast<QtCamViewfinderRenderer *>(obj);
+}
+
+int QtCamViewfinderRenderer::registerRenderer(const QString& key, const QMetaObject& meta) {
+ _renderers[key] = meta;
+ return _renderers.size() - 1;
+}
--- /dev/null
+// -*- c++ -*-
+
+#ifndef QT_CAM_VIEWFINDER_RENDERER_H
+#define QT_CAM_VIEWFINDER_RENDERER_H
+
+#include <QObject>
+#include <gst/gst.h>
+
+class QtCamConfig;
+class QMetaObject;
+class QPainter;
+class QSizeF;
+
+class QtCamViewfinderRenderer : public QObject {
+ Q_OBJECT
+
+public:
+ static QtCamViewfinderRenderer *create(QtCamConfig *config, QObject *parent = 0);
+ static int registerRenderer(const QString& key, const QMetaObject& meta);
+
+ virtual ~QtCamViewfinderRenderer();
+
+ virtual void paint(QPainter *painter) = 0;
+ virtual void resize(const QSizeF& size) = 0;
+ virtual void reset() = 0;
+ virtual GstElement *sinkElement() = 0;
+
+protected:
+ QtCamViewfinderRenderer(QtCamConfig *config, QObject *parent = 0);
+
+signals:
+ void updateRequested();
+};
+
+#define QT_CAM_VIEWFINDER_RENDERER(key, klass) \
+static int klass_seq = QtCamViewfinderRenderer::registerRenderer(key, klass::staticMetaObject)
+
+#endif /* QT_CAM_VIEWFINDER_RENDERER_H */
--- /dev/null
+#include "qtcamviewfinderrenderergeneric.h"
+#include <QDebug>
+#include <gst/video/video.h>
+#include <QPainter>
+
+QT_CAM_VIEWFINDER_RENDERER("generic", QtCamViewfinderRendererGeneric);
+
+#define CAPS "video/x-raw-rgb, bpp = (int) 32, depth = (int) 24, red_mask = (int) 65280, green_mask = (int) 16711680, blue_mask = (int) -16777216"
+
+// TODO: this needs to be debugged or rewritten. There are race conditions.
+QtCamViewfinderRendererGeneric::QtCamViewfinderRendererGeneric(QtCamConfig *config,
+ QObject *parent) :
+ QtCamViewfinderRenderer(config, parent), m_elem(0), m_sink(0) {
+
+}
+
+QtCamViewfinderRendererGeneric::~QtCamViewfinderRendererGeneric() {
+ m_mutex.lock();
+
+ if (m_elem) {
+ g_object_remove_toggle_ref(G_OBJECT(m_elem), (GToggleNotify)sink_notify, this);
+ m_elem = 0;
+
+ g_signal_handlers_disconnect_by_data(m_sink, this);
+ }
+
+ m_mutex.unlock();
+}
+
+void QtCamViewfinderRendererGeneric::paint(QPainter *painter) {
+ // TODO: scale and keep aspect ratio.
+ m_mutex.lock();
+
+ if (!m_image.isNull()) {
+ painter->drawImage(QRectF(QPointF(0, 0), m_size), m_image);
+ }
+
+ m_mutex.unlock();
+}
+
+void QtCamViewfinderRendererGeneric::resize(const QSizeF& size) {
+ m_size = size;
+}
+
+void QtCamViewfinderRendererGeneric::reset() {
+ m_image = QImage();
+}
+
+GstElement *QtCamViewfinderRendererGeneric::sinkElement() {
+ if (!m_elem) {
+ m_elem = gst_bin_new("QtCamViewfinderRendererGenericBin");
+ if (!m_elem) {
+ qCritical() << "Failed to create sink bin";
+ return 0;
+ }
+
+ GstElement *sink = gst_element_factory_make("fakesink", "QtCamViewfinderRendererGenericSink");
+ if (!sink) {
+ qCritical() << "Failed to create fakesink";
+ gst_object_unref(m_elem);
+ m_elem = 0;
+ return 0;
+ }
+
+ g_object_set(G_OBJECT(sink), "signal-handoffs", TRUE, "sync", TRUE, NULL);
+ g_signal_connect(sink, "handoff", G_CALLBACK(on_gst_buffer), this);
+
+ GstElement *csp = gst_element_factory_make("ffmpegcolorspace",
+ "QtCamViewfinderRendererGenericCsp");
+ if (!csp) {
+ qCritical() << "Failed to create ffmpegcolorspace";
+ gst_object_unref(sink);
+ gst_object_unref(m_elem);
+ m_elem = 0;
+ return 0;
+ }
+
+ GstElement *filter = gst_element_factory_make("capsfilter",
+ "QtCamViewfinderRendererGenericCapsFilter");
+ if (!filter) {
+ qCritical() << "Failed to create capsfilter";
+ gst_object_unref(sink);
+ gst_object_unref(csp);
+ gst_object_unref(m_elem);
+ m_elem = 0;
+ return 0;
+ }
+
+ GstCaps *caps = gst_caps_from_string(CAPS);
+ g_object_set(filter, "caps", caps, NULL);
+ gst_caps_unref(caps);
+
+ gst_bin_add_many(GST_BIN(m_elem), csp, filter, sink, NULL);
+ gst_element_link_many(csp, filter, sink, NULL);
+
+ GstPad *pad = gst_element_get_static_pad(csp, "sink");
+ gst_element_add_pad(m_elem, gst_ghost_pad_new("sink", pad));
+ gst_object_unref(pad);
+
+ g_object_add_toggle_ref(G_OBJECT(m_elem), (GToggleNotify)sink_notify, this);
+
+ m_sink = sink;
+ }
+
+ return m_elem;
+}
+
+void QtCamViewfinderRendererGeneric::on_gst_buffer(GstElement *element,
+ GstBuffer *buf, GstPad *pad,
+ QtCamViewfinderRendererGeneric *q) {
+
+ q->m_mutex.lock();
+
+ if (!q->m_elem) {
+ q->m_mutex.unlock();
+ return;
+ }
+
+ int width, height;
+
+ if (!gst_video_get_size(pad, &width, &height)) {
+ return;
+ }
+
+ QImage image(buf->data, width, height, QImage::Format_RGB32);
+
+ q->m_image = image.copy();
+
+ QMetaObject::invokeMethod(q, "updateRequested", Qt::QueuedConnection);
+
+ q->m_mutex.unlock();
+}
+
+void QtCamViewfinderRendererGeneric::sink_notify(QtCamViewfinderRendererGeneric *q,
+ GObject *object,
+ gboolean is_last_ref) {
+
+ if (is_last_ref) {
+ g_object_remove_toggle_ref(G_OBJECT(q->m_elem), (GToggleNotify)sink_notify, q);
+ q->m_elem = 0;
+ q->m_sink = 0;
+ }
+}
--- /dev/null
+// -*- c++ -*-
+
+#ifndef QT_CAM_VIEWFINDER_RENDERER_GENERIC_H
+#define QT_CAM_VIEWFINDER_RENDERER_GENERIC_H
+
+#include "qtcamviewfinderrenderer.h"
+#include <QImage>
+#include <QMutex>
+
+class QtCamViewfinderRendererGeneric : public QtCamViewfinderRenderer {
+ Q_OBJECT
+
+public:
+ Q_INVOKABLE QtCamViewfinderRendererGeneric(QtCamConfig *config, QObject *parent = 0);
+
+ ~QtCamViewfinderRendererGeneric();
+
+ virtual void paint(QPainter *painter);
+ virtual void resize(const QSizeF& size);
+ virtual void reset();
+ virtual GstElement *sinkElement();
+
+private:
+ static void on_gst_buffer(GstElement *element, GstBuffer *buf, GstPad *pad,
+ QtCamViewfinderRendererGeneric *q);
+
+ static void sink_notify(QtCamViewfinderRendererGeneric *q, GObject *object,
+ gboolean is_last_ref);
+
+ GstElement *m_elem;
+ GstElement *m_sink;
+ QImage m_image;
+ QMutex m_mutex;
+ QSizeF m_size;
+};
+
+#endif /* QT_CAM_VIEWFINDER_RENDERER_GENERIC_H */