summaryrefslogtreecommitdiffstats
path: root/src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp')
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp347
1 files changed, 347 insertions, 0 deletions
diff --git a/src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp b/src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp
new file mode 100644
index 000000000..3df1105bc
--- /dev/null
+++ b/src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp
@@ -0,0 +1,347 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "camerabinimagecapture.h"
+#include "camerabincapturedestination.h"
+#include "camerabincapturebufferformat.h"
+#include "camerabinsession.h"
+#include "qgstvideobuffer.h"
+#include "qvideosurfacegstsink.h"
+#include "qgstutils.h"
+#include <QtCore/qdebug.h>
+#include <QtCore/qbuffer.h>
+#include <QtGui/qimagereader.h>
+
+//#define DEBUG_CAPTURE
+
+#ifdef Q_WS_MAEMO_5
+#define IMAGE_DONE_SIGNAL "img-done"
+#else
+#define IMAGE_DONE_SIGNAL "image-done"
+#endif
+
+
+Q_DECLARE_METATYPE(QVideoFrame)
+Q_DECLARE_METATYPE(QtMultimediaKit::MetaData)
+
+namespace
+{
+class CameraRegisterMetaTypes
+{
+public:
+ CameraRegisterMetaTypes()
+ {
+ qRegisterMetaType<QVideoFrame>("QVideoFrame");
+ qRegisterMetaType<QtMultimediaKit::MetaData>("QtMultimediaKit::MetaData");
+ }
+} _registerCameraMetaTypes;
+}
+
+
+CameraBinImageCapture::CameraBinImageCapture(CameraBinSession *session)
+ :QCameraImageCaptureControl(session)
+ , m_session(session)
+ , m_ready(false)
+ , m_requestId(0)
+ , m_jpegEncoderElement(0)
+ , m_metadataMuxerElement(0)
+{
+ connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(updateState()));
+ connect(m_session, SIGNAL(imageExposed(int)), this, SIGNAL(imageExposed(int)));
+ connect(m_session, SIGNAL(imageCaptured(int,QImage)), this, SIGNAL(imageCaptured(int,QImage)));
+ connect(m_session, SIGNAL(busMessage(QGstreamerMessage)), SLOT(handleBusMessage(QGstreamerMessage)));
+
+ g_signal_connect(G_OBJECT(m_session->cameraBin()), IMAGE_DONE_SIGNAL, G_CALLBACK(handleImageSaved), this);
+}
+
+CameraBinImageCapture::~CameraBinImageCapture()
+{
+}
+
+bool CameraBinImageCapture::isReadyForCapture() const
+{
+ return m_ready;
+}
+
+int CameraBinImageCapture::capture(const QString &fileName)
+{
+ m_requestId++;
+
+ if (!m_ready) {
+ emit error(m_requestId, QCameraImageCapture::NotReadyError, tr("Camera not ready"));
+ return m_requestId;
+ }
+
+#ifdef DEBUG_CAPTURE
+ qDebug() << Q_FUNC_INFO << m_requestId << fileName;
+#endif
+ m_session->captureImage(m_requestId, fileName);
+ return m_requestId;
+}
+
+void CameraBinImageCapture::cancelCapture()
+{
+}
+
+void CameraBinImageCapture::updateState()
+{
+ bool ready = m_session->state() == QCamera::ActiveState;
+ if (m_ready != ready) {
+#ifdef DEBUG_CAPTURE
+ qDebug() << "readyForCaptureChanged" << ready;
+#endif
+ emit readyForCaptureChanged(m_ready = ready);
+ }
+}
+
+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)
+{
+
+ if (GST_EVENT_TYPE(event) == GST_EVENT_TAG) {
+ GstTagList *gstTags;
+ gst_event_parse_tag(event, &gstTags);
+ QMap<QByteArray, QVariant> extendedTags = QGstUtils::gstTagListToMap(gstTags);
+
+#ifdef DEBUG_CAPTURE
+ qDebug() << QString(gst_structure_to_string(gst_event_get_structure(event))).right(768);
+ qDebug() << "Capture event probe" << extendedTags;
+#endif
+
+ QMap<QtMultimediaKit::MetaData, QVariant> tags;
+ tags[QtMultimediaKit::ISOSpeedRatings] = extendedTags.value("capturing-iso-speed");
+ tags[QtMultimediaKit::DigitalZoomRatio] = extendedTags.value("capturing-digital-zoom-ratio");
+ tags[QtMultimediaKit::ExposureTime] = extendedTags.value("capturing-shutter-speed");
+ tags[QtMultimediaKit::WhiteBalance] = extendedTags.value("capturing-white-balance");
+ tags[QtMultimediaKit::Flash] = extendedTags.value("capturing-flash-fired");
+ tags[QtMultimediaKit::FocalLengthIn35mmFilm] = extendedTags.value("capturing-focal-length");
+ tags[QtMultimediaKit::MeteringMode] = extendedTags.value("capturing-metering-mode");
+ tags[QtMultimediaKit::ExposureMode] = extendedTags.value("capturing-exposure-mode");
+ tags[QtMultimediaKit::FNumber] = extendedTags.value("capturing-focal-ratio");
+ tags[QtMultimediaKit::ExposureMode] = extendedTags.value("capturing-exposure-mode");
+
+ QMapIterator<QtMultimediaKit::MetaData, QVariant> i(tags);
+ while (i.hasNext()) {
+ i.next();
+ if (i.value().isValid()) {
+ QMetaObject::invokeMethod(self, "imageMetadataAvailable",
+ Qt::QueuedConnection,
+ Q_ARG(int, self->m_requestId),
+ Q_ARG(QtMultimediaKit::MetaData, i.key()),
+ Q_ARG(QVariant, i.value()));
+ }
+ }
+ }
+
+ return true;
+}
+
+gboolean CameraBinImageCapture::uncompressedBufferProbe(GstPad *pad, GstBuffer *buffer, CameraBinImageCapture *self)
+{
+ Q_UNUSED(pad);
+ CameraBinSession *session = self->m_session;
+
+#ifdef DEBUG_CAPTURE
+ qDebug() << "Uncompressed buffer probe" << gst_caps_to_string(GST_BUFFER_CAPS(buffer));
+#endif
+
+ QCameraImageCapture::CaptureDestinations destination =
+ session->captureDestinationControl()->captureDestination();
+ QVideoFrame::PixelFormat format = session->captureBufferFormatControl()->bufferFormat();
+
+ if (destination & QCameraImageCapture::CaptureToBuffer) {
+ if (format != QVideoFrame::Format_Jpeg) {
+ GstCaps *caps = GST_BUFFER_CAPS(buffer);
+ int bytesPerLine = -1;
+ QVideoSurfaceFormat format = QVideoSurfaceGstSink::formatForCaps(caps, &bytesPerLine);
+#ifdef DEBUG_CAPTURE
+ qDebug() << "imageAvailable(uncompressed):" << format;
+#endif
+ QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer, bytesPerLine);
+
+ QVideoFrame frame(videoBuffer,
+ format.frameSize(),
+ format.pixelFormat());
+
+ QMetaObject::invokeMethod(self, "imageAvailable",
+ Qt::QueuedConnection,
+ Q_ARG(int, self->m_requestId),
+ Q_ARG(QVideoFrame, frame));
+ }
+ }
+
+ //keep the buffer if capture to file or jpeg buffer capture was reuqsted
+ bool keepBuffer = (destination & QCameraImageCapture::CaptureToFile) ||
+ ((destination & QCameraImageCapture::CaptureToBuffer) &&
+ format == QVideoFrame::Format_Jpeg);
+
+ return keepBuffer;
+}
+
+gboolean CameraBinImageCapture::jpegBufferProbe(GstPad *pad, GstBuffer *buffer, CameraBinImageCapture *self)
+{
+ Q_UNUSED(pad);
+ CameraBinSession *session = self->m_session;
+
+#ifdef DEBUG_CAPTURE
+ qDebug() << "Jpeg buffer probe" << gst_caps_to_string(GST_BUFFER_CAPS(buffer));
+#endif
+
+ QCameraImageCapture::CaptureDestinations destination =
+ session->captureDestinationControl()->captureDestination();
+
+ if ((destination & QCameraImageCapture::CaptureToBuffer) &&
+ session->captureBufferFormatControl()->bufferFormat() == QVideoFrame::Format_Jpeg) {
+ QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer,
+ -1); //bytesPerLine is not available for jpegs
+
+ QSize resolution = QGstUtils::capsCorrectedResolution(GST_BUFFER_CAPS(buffer));
+ //if resolution is not presented in caps, try to find it from encoded jpeg data:
+ if (resolution.isEmpty()) {
+ QBuffer data;
+ data.setData(reinterpret_cast<const char*>(GST_BUFFER_DATA(buffer)), GST_BUFFER_SIZE(buffer));
+ QImageReader reader(&data, "JPEG");
+ resolution = reader.size();
+ }
+
+ QVideoFrame frame(videoBuffer,
+ resolution,
+ QVideoFrame::Format_Jpeg);
+
+ QMetaObject::invokeMethod(self, "imageAvailable",
+ Qt::QueuedConnection,
+ Q_ARG(int, self->m_requestId),
+ Q_ARG(QVideoFrame, frame));
+ }
+
+ //drop the buffer if capture to file was disabled
+ return destination & QCameraImageCapture::CaptureToFile;
+}
+
+void CameraBinImageCapture::handleBusMessage(const QGstreamerMessage &message)
+{
+ //Install metadata event and buffer probes
+
+ //The image capture pipiline is built dynamically,
+ //it's necessary to wait until jpeg encoder is added to pipeline
+
+ GstMessage *gm = message.rawMessage();
+ if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_STATE_CHANGED) {
+ GstState oldState;
+ GstState newState;
+ GstState pending;
+ gst_message_parse_state_changed(gm, &oldState, &newState, &pending);
+
+ if (newState == GST_STATE_READY) {
+ GstElement *element = GST_ELEMENT(GST_MESSAGE_SRC(gm));
+ if (!element)
+ return;
+
+ QString elementName = QString::fromLatin1(gst_element_get_name(element));
+ if (elementName.contains("jpegenc") && element != m_jpegEncoderElement) {
+ m_jpegEncoderElement = element;
+ GstPad *sinkpad = gst_element_get_static_pad(element, "sink");
+
+ //metadata event probe is installed before jpeg encoder
+ //to emit metadata available signal as soon as possible.
+#ifdef DEBUG_CAPTURE
+ qDebug() << "install metadata probe";
+#endif
+ gst_pad_add_event_probe(sinkpad,
+ G_CALLBACK(CameraBinImageCapture::metadataEventProbe),
+ this);
+
+#ifdef DEBUG_CAPTURE
+ qDebug() << "install uncompressed buffer probe";
+#endif
+ gst_pad_add_buffer_probe(sinkpad,
+ G_CALLBACK(CameraBinImageCapture::uncompressedBufferProbe),
+ this);
+
+ gst_object_unref(sinkpad);
+ } else if ((elementName.contains("jifmux") || elementName.startsWith("metadatamux"))
+ && element != m_metadataMuxerElement) {
+ //Jpeg encoded buffer probe is added after jifmux/metadatamux
+ //element to ensure the resulting jpeg buffer contains capture metadata
+ m_metadataMuxerElement = element;
+
+ GstPad *srcpad = gst_element_get_static_pad(element, "src");
+#ifdef DEBUG_CAPTURE
+ qDebug() << "install jpeg buffer probe";
+#endif
+ gst_pad_add_buffer_probe(srcpad,
+ G_CALLBACK(CameraBinImageCapture::jpegBufferProbe),
+ this);
+ gst_object_unref(srcpad);
+ }
+ }
+ }
+}