diff -r 2b40d63a9c3d -r 90517678cc4f qtmobility/plugins/multimedia/gstreamer/mediacapture/maemo/qgstreamercapturesession_maemo.cpp --- /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 +#include +#include + +#include +#include +#include + +#include + +#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, ¤tState, 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 &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 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); + } + } +}