qtmobility/plugins/multimedia/gstreamer/mediacapture/maemo/qgstreamercapturesession_maemo.cpp
changeset 4 90517678cc4f
child 11 06b8e2af4411
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qtmobility/plugins/multimedia/gstreamer/mediacapture/maemo/qgstreamercapturesession_maemo.cpp	Mon May 03 13:18:40 2010 +0300
@@ -0,0 +1,463 @@
+/****************************************************************************
+**
+** 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 "qgstreamercapturesession_maemo.h"
+#include "qgstreamerrecordercontrol_maemo.h"
+#include "qgstreamermediacontainercontrol_maemo.h"
+#include "qgstreameraudioencode_maemo.h"
+#include "qgstreamervideoencode_maemo.h"
+#include "qgstreamerbushelper.h"
+#include <qmediarecorder.h>
+#include <gst/gsttagsetter.h>
+#include <gst/gstversion.h>
+
+#include <QtCore/qdebug.h>
+#include <QCoreApplication>
+#include <QtCore/qmetaobject.h>
+
+#include <QtGui/qimage.h>
+
+#define gstRef(element) { gst_object_ref(GST_OBJECT(element)); gst_object_sink(GST_OBJECT(element)); }
+#define gstUnref(element) { if (element) { gst_object_unref(GST_OBJECT(element)); element = 0; } }
+
+#define PREVIEW_CAPS \
+    "video/x-raw-rgb, width = (int) 640, height = (int) 480"
+
+// Function prototypes
+static gboolean imgCaptured(GstElement *camera, const gchar *filename, gpointer user_data);
+
+QGstreamerCaptureSession::QGstreamerCaptureSession(QGstreamerCaptureSession::CaptureMode captureMode, QObject *parent)
+    :QObject(parent),
+     m_state(StoppedState),
+     m_pendingState(StoppedState),
+     m_waitingForEos(false),
+     m_pipelineMode(EmptyPipeline),
+     m_captureMode(captureMode),
+     m_audioInputFactory(0),
+     m_audioPreviewFactory(0),
+     m_videoInputFactory(0),
+     m_videoPreviewFactory(0),
+     m_pipeline(0),
+     m_videoSrc(0),
+     m_videoPreviewFactoryHasChanged(false),
+     m_audioSrc(0),
+     m_audioConvert(0),
+     m_capsFilter(0),
+     m_fileSink(0),
+     m_audioEncoder(0),
+     m_muxer(0)
+{
+    if (m_captureMode == AudioAndVideo) {
+        m_pipeline = gst_element_factory_make("camerabin", "camerabin");
+    } else if (m_captureMode & Audio) {
+        m_pipeline = gst_pipeline_new("audio-capture-pipeline");
+        m_audioSrc = gst_element_factory_make("pulsesrc", "pulsesrc");
+        m_audioConvert = gst_element_factory_make("audioconvert", "audioconvert");
+        m_capsFilter = gst_element_factory_make("capsfilter", "capsfilter-audio");
+        m_fileSink = gst_element_factory_make("filesink", "filesink");
+
+        if (!m_audioSrc || !m_audioConvert || !m_fileSink)
+            emit error(int(QMediaRecorder::ResourceError), QString("Element creation failed."));
+
+        gst_bin_add_many(GST_BIN(m_pipeline), m_audioSrc, m_audioConvert, m_fileSink, NULL);
+
+        if (!gst_element_link(m_audioSrc, m_audioConvert))
+            emit error(int(QMediaRecorder::ResourceError), QString("Element linking failed."));
+
+    }
+
+    gstRef(m_pipeline);
+
+    m_bus = gst_element_get_bus(m_pipeline);
+
+    m_busHelper = new QGstreamerBusHelper(m_bus, this);
+    m_busHelper->installSyncEventFilter(this);
+    connect(m_busHelper, SIGNAL(message(QGstreamerMessage)), SLOT(busMessage(QGstreamerMessage)));
+    m_audioEncodeControl = new QGstreamerAudioEncode(this);
+    m_videoEncodeControl = new QGstreamerVideoEncode(this);
+    m_recorderControl = new QGstreamerRecorderControl(this);
+    m_mediaContainerControl = new QGstreamerMediaContainerControl(this);
+}
+
+QGstreamerCaptureSession::~QGstreamerCaptureSession()
+{
+    if (m_pipeline) {
+        gst_element_set_state(m_pipeline, GST_STATE_NULL);
+        gst_element_get_state(m_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
+        gstUnref(m_pipeline);
+    }
+}
+
+void QGstreamerCaptureSession::setupCameraBin()
+{
+    GstState currentState = GST_STATE_PLAYING;
+    gst_element_get_state(m_pipeline, &currentState, 0, 0);
+    GstState previousState = currentState;
+
+    if (currentState != GST_STATE_NULL) {
+        gst_element_set_state(m_pipeline, GST_STATE_NULL);
+        gst_element_get_state(m_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
+    }
+
+    m_recorderControl->applySettings();
+
+    g_object_set(m_pipeline, "videosrc", buildVideoSrc(), NULL);
+    g_object_set(m_pipeline, "videoenc", m_videoEncodeControl->createEncoder(), NULL);
+    g_object_set(m_pipeline, "audioenc", m_audioEncodeControl->createEncoder(), NULL);
+    g_object_set(m_pipeline, "videomux",
+                 gst_element_factory_make(m_mediaContainerControl->formatElementName().constData(), NULL), NULL);
+
+    if (m_videoPreviewFactory) {
+        GstElement *preview = m_videoPreviewFactory->buildElement();
+        g_object_set(G_OBJECT(m_pipeline), "vfsink", preview, NULL);
+    }
+
+    gst_element_set_state(m_pipeline, GST_STATE_PLAYING);
+}
+
+#define REMOVE_ELEMENT(element) { if (element) {gst_bin_remove(GST_BIN(m_pipeline), element); element = 0;} }
+
+void QGstreamerCaptureSession::buildAudioEncodeBin()
+{    
+    REMOVE_ELEMENT(m_audioEncoder);
+    REMOVE_ELEMENT(m_muxer);
+
+    m_audioEncoder = m_audioEncodeControl->createEncoder();
+    m_muxer = gst_element_factory_make(m_mediaContainerControl->formatElementName().constData(), "muxer");
+
+    if (!m_audioEncoder || !m_muxer) {
+       emit error(int(QMediaRecorder::ResourceError), QString("Element creation failed"));
+       return;
+   }
+
+    gst_bin_add(GST_BIN(m_pipeline), m_audioEncoder);
+    gst_bin_add(GST_BIN(m_pipeline), m_muxer);
+
+    if (!gst_element_link_many(m_audioConvert, m_audioEncoder, m_muxer, m_fileSink, NULL)) {
+        emit error(int(QMediaRecorder::ResourceError), QString("Element linking failed"));
+        return;
+    }
+
+    g_object_set(G_OBJECT(m_fileSink), "location", m_sink.toString().toLocal8Bit().constData(), NULL);
+}
+
+GstElement *QGstreamerCaptureSession::buildVideoSrc()
+{
+    GstElement *videoSrc = 0;
+    if (m_videoInputFactory) {
+        videoSrc = m_videoInputFactory->buildElement();
+    } else {
+        videoSrc = gst_element_factory_make("videotestsrc", "video_test_src");
+    }
+
+    return videoSrc;
+}
+
+QUrl QGstreamerCaptureSession::outputLocation() const
+{
+    return m_sink;
+}
+
+bool QGstreamerCaptureSession::setOutputLocation(const QUrl& sink)
+{
+    m_sink = sink;
+    return true;
+}
+
+void QGstreamerCaptureSession::setAudioInput(QGstreamerElementFactory *audioInput)
+{
+    m_audioInputFactory = audioInput;
+}
+
+void QGstreamerCaptureSession::setAudioPreview(QGstreamerElementFactory *audioPreview)
+{
+    m_audioPreviewFactory = audioPreview;
+}
+
+void QGstreamerCaptureSession::setVideoInput(QGstreamerVideoInput *videoInput)
+{
+    m_videoInputFactory = videoInput;
+}
+
+void QGstreamerCaptureSession::setVideoPreview(QGstreamerElementFactory *videoPreview)
+{
+    m_videoPreviewFactory = videoPreview;
+    m_videoPreviewFactoryHasChanged = true;
+}
+
+QGstreamerCaptureSession::State QGstreamerCaptureSession::state() const
+{
+    return m_state;
+}
+
+void QGstreamerCaptureSession::setState(QGstreamerCaptureSession::State newState)
+{
+
+    if (newState == m_state)
+        return;
+
+    //qDebug() << "NewState: " << newState;
+    //qDebug() << "CurrentState: " << m_state;
+
+    switch (m_state) {
+        case PreviewState:
+            if (newState == StoppedState) {
+                m_state = StoppedState;
+                gst_element_set_state(m_pipeline, GST_STATE_NULL);
+                gst_element_get_state(m_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
+                emit stateChanged(StoppedState);
+            }
+            g_object_set(G_OBJECT(m_pipeline), "mode", 1, NULL);
+            m_state = StoppedState;
+            if (newState == RecordingState) {
+                setState(newState);
+            }
+            break;
+        case StoppedState:
+            m_state = newState;
+            if (newState == PreviewState) {
+
+                if (m_videoPreviewFactory && m_videoPreviewFactoryHasChanged) {
+                    m_videoPreviewFactoryHasChanged = false;
+                    GstElement *preview = m_videoPreviewFactory->buildElement();
+                    g_object_set(G_OBJECT(m_pipeline), "vfsink", preview, NULL);
+                }
+                gst_element_set_state(m_pipeline, GST_STATE_PLAYING);
+                g_object_set(G_OBJECT(m_pipeline), "mode", 0, NULL);
+            } else {
+                if (m_captureMode == AudioAndVideo) {
+                    setupCameraBin();
+                    g_object_set(G_OBJECT(m_pipeline), "filename", m_sink.toString().toLocal8Bit().constData(), NULL);
+                    g_object_set(G_OBJECT(m_pipeline), "mode", 1, NULL);
+                    g_signal_emit_by_name(m_pipeline, "user-start", 0);
+                } else if (m_captureMode & Audio) {
+                    buildAudioEncodeBin();
+                    gst_element_set_state(m_pipeline, GST_STATE_PLAYING);
+                }
+            }
+            break;
+        case PausedState:
+        case RecordingState:
+            if (newState == PausedState) {
+                g_signal_emit_by_name(m_pipeline, "user-pause", 0);
+                m_state = PausedState;
+            } else {
+                if (m_captureMode == AudioAndVideo)
+                    g_signal_emit_by_name(m_pipeline, "user-stop", 0);
+                else if (m_captureMode & Audio) {
+                    gst_element_send_event(m_pipeline, gst_event_new_eos());
+                    gst_element_set_state(m_pipeline, GST_STATE_NULL);
+                }
+
+                m_state = StoppedState;
+                if (newState == PreviewState)
+                    setState(newState);
+            }
+            break;
+    }
+}
+
+
+qint64 QGstreamerCaptureSession::duration() const
+{
+    GstFormat   format = GST_FORMAT_TIME;
+    gint64      duration = 0;
+
+    if ( m_pipeline && gst_element_query_position(m_pipeline, &format, &duration))
+        return duration / 1000000;
+    else
+        return 0;
+}
+
+void QGstreamerCaptureSession::setCaptureDevice(const QString &deviceName)
+{
+    m_captureDevice = deviceName;
+}
+
+void QGstreamerCaptureSession::setMetaData(const QMap<QByteArray, QVariant> &data)
+{
+    m_metaData = data;
+
+    if (m_pipeline) {
+        GstIterator *elements = gst_bin_iterate_all_by_interface(GST_BIN(m_pipeline), GST_TYPE_TAG_SETTER);
+        GstElement *element = 0;
+        while (gst_iterator_next(elements, (void**)&element) == GST_ITERATOR_OK) {
+            QMapIterator<QByteArray, QVariant> it(data);
+            while (it.hasNext()) {
+                it.next();
+                const QString tagName = it.key();
+                const QVariant tagValue = it.value();
+
+                switch(tagValue.type()) {
+                    case QVariant::String:
+                        gst_tag_setter_add_tags(GST_TAG_SETTER(element),
+                            GST_TAG_MERGE_REPLACE_ALL,
+                            tagName.toUtf8().constData(),
+                            tagValue.toString().toUtf8().constData(),
+                            NULL);
+                        break;
+                    case QVariant::Int:
+                    case QVariant::LongLong:
+                        gst_tag_setter_add_tags(GST_TAG_SETTER(element),
+                            GST_TAG_MERGE_REPLACE_ALL,
+                            tagName.toUtf8().constData(),
+                            tagValue.toInt(),
+                            NULL);
+                        break;
+                    case QVariant::Double:
+                        gst_tag_setter_add_tags(GST_TAG_SETTER(element),
+                            GST_TAG_MERGE_REPLACE_ALL,
+                            tagName.toUtf8().constData(),
+                            tagValue.toDouble(),
+                            NULL);
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+    }
+}
+
+bool QGstreamerCaptureSession::processSyncMessage(const QGstreamerMessage &message)
+{
+    GstMessage* gm = message.rawMessage();
+    const GstStructure *st;
+    const GValue *image;
+    GstBuffer *buffer = NULL;
+
+    if (gm && GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) {
+        if (gst_structure_has_name(gm->structure, "prepare-xwindow-id")) {
+            if (m_audioPreviewFactory)
+                m_audioPreviewFactory->prepareWinId();
+
+            if (m_videoPreviewFactory)
+                m_videoPreviewFactory->prepareWinId();
+
+            return true;
+        }
+    }
+
+    return false;
+}
+
+void QGstreamerCaptureSession::busMessage(const QGstreamerMessage &message)
+{
+    GstMessage* gm = message.rawMessage();
+
+    if (gm) {
+        if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ERROR) {
+            GError *err;
+            gchar *debug;
+            gst_message_parse_error (gm, &err, &debug);
+            emit error(int(QMediaRecorder::ResourceError),QString::fromUtf8(err->message));
+            g_error_free (err);
+            g_free (debug);
+        }
+
+        if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_pipeline)) {
+            switch (GST_MESSAGE_TYPE(gm))  {
+            case GST_MESSAGE_DURATION:
+                break;
+
+            case GST_MESSAGE_EOS:
+                if (m_waitingForEos)
+                    setState(m_pendingState);
+                break;
+
+            case GST_MESSAGE_STATE_CHANGED:
+                {
+
+                    GstState    oldState;
+                    GstState    newState;
+                    GstState    pending;
+
+                    gst_message_parse_state_changed(gm, &oldState, &newState, &pending);
+
+                    QStringList states;
+                    states << "GST_STATE_VOID_PENDING" <<  "GST_STATE_NULL" << "GST_STATE_READY" << "GST_STATE_PAUSED" << "GST_STATE_PLAYING";
+
+
+                    //qDebug() << QString("state changed: old: %1  new: %2  pending: %3") \
+                    //        .arg(states[oldState]) \
+                    //       .arg(states[newState]) \
+                    //        .arg(states[pending]);
+
+                    #define ENUM_NAME(c,e,v) (c::staticMetaObject.enumerator(c::staticMetaObject.indexOfEnumerator(e)).valueToKey((v)))
+
+                    //qDebug() << "Current session state:" << ENUM_NAME(QGstreamerCaptureSession,"State",m_state);
+                    //qDebug() << "Pending session state:" << ENUM_NAME(QGstreamerCaptureSession,"State",m_pendingState);
+
+
+                    switch (newState) {
+                    case GST_STATE_VOID_PENDING:
+                    case GST_STATE_NULL:
+                    case GST_STATE_READY:
+                        /*if (m_state != StoppedState && m_pendingState == StoppedState) {
+                            emit stateChanged(m_state = StoppedState);
+                        }*/
+                        break;
+                    case GST_STATE_PAUSED:
+                        if (m_state != PausedState && m_pendingState == PausedState)
+                            emit stateChanged(m_state = PausedState);
+
+                        if (m_pipelineMode == RecordingPipeline && !m_metaData.isEmpty())
+                            setMetaData(m_metaData);
+                        break;
+                    case GST_STATE_PLAYING:
+                        {
+                            if (m_state == PreviewState || m_state == RecordingState)
+                            {
+                                emit stateChanged(m_state);
+                            }
+
+                        }
+                        break;
+                    }
+                }
+                break;
+            default:
+                break;
+            }
+            //qDebug() << "New session state:" << ENUM_NAME(QGstreamerCaptureSession,"State",m_state);
+        }
+    }
+}