qtmobility/plugins/multimedia/gstreamer/mediacapture/maemo/qgstreamercapturesession_maemo.cpp
changeset 4 90517678cc4f
child 11 06b8e2af4411
equal deleted inserted replaced
1:2b40d63a9c3d 4:90517678cc4f
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Mobility Components.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qgstreamercapturesession_maemo.h"
       
    43 #include "qgstreamerrecordercontrol_maemo.h"
       
    44 #include "qgstreamermediacontainercontrol_maemo.h"
       
    45 #include "qgstreameraudioencode_maemo.h"
       
    46 #include "qgstreamervideoencode_maemo.h"
       
    47 #include "qgstreamerbushelper.h"
       
    48 #include <qmediarecorder.h>
       
    49 #include <gst/gsttagsetter.h>
       
    50 #include <gst/gstversion.h>
       
    51 
       
    52 #include <QtCore/qdebug.h>
       
    53 #include <QCoreApplication>
       
    54 #include <QtCore/qmetaobject.h>
       
    55 
       
    56 #include <QtGui/qimage.h>
       
    57 
       
    58 #define gstRef(element) { gst_object_ref(GST_OBJECT(element)); gst_object_sink(GST_OBJECT(element)); }
       
    59 #define gstUnref(element) { if (element) { gst_object_unref(GST_OBJECT(element)); element = 0; } }
       
    60 
       
    61 #define PREVIEW_CAPS \
       
    62     "video/x-raw-rgb, width = (int) 640, height = (int) 480"
       
    63 
       
    64 // Function prototypes
       
    65 static gboolean imgCaptured(GstElement *camera, const gchar *filename, gpointer user_data);
       
    66 
       
    67 QGstreamerCaptureSession::QGstreamerCaptureSession(QGstreamerCaptureSession::CaptureMode captureMode, QObject *parent)
       
    68     :QObject(parent),
       
    69      m_state(StoppedState),
       
    70      m_pendingState(StoppedState),
       
    71      m_waitingForEos(false),
       
    72      m_pipelineMode(EmptyPipeline),
       
    73      m_captureMode(captureMode),
       
    74      m_audioInputFactory(0),
       
    75      m_audioPreviewFactory(0),
       
    76      m_videoInputFactory(0),
       
    77      m_videoPreviewFactory(0),
       
    78      m_pipeline(0),
       
    79      m_videoSrc(0),
       
    80      m_videoPreviewFactoryHasChanged(false),
       
    81      m_audioSrc(0),
       
    82      m_audioConvert(0),
       
    83      m_capsFilter(0),
       
    84      m_fileSink(0),
       
    85      m_audioEncoder(0),
       
    86      m_muxer(0)
       
    87 {
       
    88     if (m_captureMode == AudioAndVideo) {
       
    89         m_pipeline = gst_element_factory_make("camerabin", "camerabin");
       
    90     } else if (m_captureMode & Audio) {
       
    91         m_pipeline = gst_pipeline_new("audio-capture-pipeline");
       
    92         m_audioSrc = gst_element_factory_make("pulsesrc", "pulsesrc");
       
    93         m_audioConvert = gst_element_factory_make("audioconvert", "audioconvert");
       
    94         m_capsFilter = gst_element_factory_make("capsfilter", "capsfilter-audio");
       
    95         m_fileSink = gst_element_factory_make("filesink", "filesink");
       
    96 
       
    97         if (!m_audioSrc || !m_audioConvert || !m_fileSink)
       
    98             emit error(int(QMediaRecorder::ResourceError), QString("Element creation failed."));
       
    99 
       
   100         gst_bin_add_many(GST_BIN(m_pipeline), m_audioSrc, m_audioConvert, m_fileSink, NULL);
       
   101 
       
   102         if (!gst_element_link(m_audioSrc, m_audioConvert))
       
   103             emit error(int(QMediaRecorder::ResourceError), QString("Element linking failed."));
       
   104 
       
   105     }
       
   106 
       
   107     gstRef(m_pipeline);
       
   108 
       
   109     m_bus = gst_element_get_bus(m_pipeline);
       
   110 
       
   111     m_busHelper = new QGstreamerBusHelper(m_bus, this);
       
   112     m_busHelper->installSyncEventFilter(this);
       
   113     connect(m_busHelper, SIGNAL(message(QGstreamerMessage)), SLOT(busMessage(QGstreamerMessage)));
       
   114     m_audioEncodeControl = new QGstreamerAudioEncode(this);
       
   115     m_videoEncodeControl = new QGstreamerVideoEncode(this);
       
   116     m_recorderControl = new QGstreamerRecorderControl(this);
       
   117     m_mediaContainerControl = new QGstreamerMediaContainerControl(this);
       
   118 }
       
   119 
       
   120 QGstreamerCaptureSession::~QGstreamerCaptureSession()
       
   121 {
       
   122     if (m_pipeline) {
       
   123         gst_element_set_state(m_pipeline, GST_STATE_NULL);
       
   124         gst_element_get_state(m_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
       
   125         gstUnref(m_pipeline);
       
   126     }
       
   127 }
       
   128 
       
   129 void QGstreamerCaptureSession::setupCameraBin()
       
   130 {
       
   131     GstState currentState = GST_STATE_PLAYING;
       
   132     gst_element_get_state(m_pipeline, &currentState, 0, 0);
       
   133     GstState previousState = currentState;
       
   134 
       
   135     if (currentState != GST_STATE_NULL) {
       
   136         gst_element_set_state(m_pipeline, GST_STATE_NULL);
       
   137         gst_element_get_state(m_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
       
   138     }
       
   139 
       
   140     m_recorderControl->applySettings();
       
   141 
       
   142     g_object_set(m_pipeline, "videosrc", buildVideoSrc(), NULL);
       
   143     g_object_set(m_pipeline, "videoenc", m_videoEncodeControl->createEncoder(), NULL);
       
   144     g_object_set(m_pipeline, "audioenc", m_audioEncodeControl->createEncoder(), NULL);
       
   145     g_object_set(m_pipeline, "videomux",
       
   146                  gst_element_factory_make(m_mediaContainerControl->formatElementName().constData(), NULL), NULL);
       
   147 
       
   148     if (m_videoPreviewFactory) {
       
   149         GstElement *preview = m_videoPreviewFactory->buildElement();
       
   150         g_object_set(G_OBJECT(m_pipeline), "vfsink", preview, NULL);
       
   151     }
       
   152 
       
   153     gst_element_set_state(m_pipeline, GST_STATE_PLAYING);
       
   154 }
       
   155 
       
   156 #define REMOVE_ELEMENT(element) { if (element) {gst_bin_remove(GST_BIN(m_pipeline), element); element = 0;} }
       
   157 
       
   158 void QGstreamerCaptureSession::buildAudioEncodeBin()
       
   159 {    
       
   160     REMOVE_ELEMENT(m_audioEncoder);
       
   161     REMOVE_ELEMENT(m_muxer);
       
   162 
       
   163     m_audioEncoder = m_audioEncodeControl->createEncoder();
       
   164     m_muxer = gst_element_factory_make(m_mediaContainerControl->formatElementName().constData(), "muxer");
       
   165 
       
   166     if (!m_audioEncoder || !m_muxer) {
       
   167        emit error(int(QMediaRecorder::ResourceError), QString("Element creation failed"));
       
   168        return;
       
   169    }
       
   170 
       
   171     gst_bin_add(GST_BIN(m_pipeline), m_audioEncoder);
       
   172     gst_bin_add(GST_BIN(m_pipeline), m_muxer);
       
   173 
       
   174     if (!gst_element_link_many(m_audioConvert, m_audioEncoder, m_muxer, m_fileSink, NULL)) {
       
   175         emit error(int(QMediaRecorder::ResourceError), QString("Element linking failed"));
       
   176         return;
       
   177     }
       
   178 
       
   179     g_object_set(G_OBJECT(m_fileSink), "location", m_sink.toString().toLocal8Bit().constData(), NULL);
       
   180 }
       
   181 
       
   182 GstElement *QGstreamerCaptureSession::buildVideoSrc()
       
   183 {
       
   184     GstElement *videoSrc = 0;
       
   185     if (m_videoInputFactory) {
       
   186         videoSrc = m_videoInputFactory->buildElement();
       
   187     } else {
       
   188         videoSrc = gst_element_factory_make("videotestsrc", "video_test_src");
       
   189     }
       
   190 
       
   191     return videoSrc;
       
   192 }
       
   193 
       
   194 QUrl QGstreamerCaptureSession::outputLocation() const
       
   195 {
       
   196     return m_sink;
       
   197 }
       
   198 
       
   199 bool QGstreamerCaptureSession::setOutputLocation(const QUrl& sink)
       
   200 {
       
   201     m_sink = sink;
       
   202     return true;
       
   203 }
       
   204 
       
   205 void QGstreamerCaptureSession::setAudioInput(QGstreamerElementFactory *audioInput)
       
   206 {
       
   207     m_audioInputFactory = audioInput;
       
   208 }
       
   209 
       
   210 void QGstreamerCaptureSession::setAudioPreview(QGstreamerElementFactory *audioPreview)
       
   211 {
       
   212     m_audioPreviewFactory = audioPreview;
       
   213 }
       
   214 
       
   215 void QGstreamerCaptureSession::setVideoInput(QGstreamerVideoInput *videoInput)
       
   216 {
       
   217     m_videoInputFactory = videoInput;
       
   218 }
       
   219 
       
   220 void QGstreamerCaptureSession::setVideoPreview(QGstreamerElementFactory *videoPreview)
       
   221 {
       
   222     m_videoPreviewFactory = videoPreview;
       
   223     m_videoPreviewFactoryHasChanged = true;
       
   224 }
       
   225 
       
   226 QGstreamerCaptureSession::State QGstreamerCaptureSession::state() const
       
   227 {
       
   228     return m_state;
       
   229 }
       
   230 
       
   231 void QGstreamerCaptureSession::setState(QGstreamerCaptureSession::State newState)
       
   232 {
       
   233 
       
   234     if (newState == m_state)
       
   235         return;
       
   236 
       
   237     //qDebug() << "NewState: " << newState;
       
   238     //qDebug() << "CurrentState: " << m_state;
       
   239 
       
   240     switch (m_state) {
       
   241         case PreviewState:
       
   242             if (newState == StoppedState) {
       
   243                 m_state = StoppedState;
       
   244                 gst_element_set_state(m_pipeline, GST_STATE_NULL);
       
   245                 gst_element_get_state(m_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
       
   246                 emit stateChanged(StoppedState);
       
   247             }
       
   248             g_object_set(G_OBJECT(m_pipeline), "mode", 1, NULL);
       
   249             m_state = StoppedState;
       
   250             if (newState == RecordingState) {
       
   251                 setState(newState);
       
   252             }
       
   253             break;
       
   254         case StoppedState:
       
   255             m_state = newState;
       
   256             if (newState == PreviewState) {
       
   257 
       
   258                 if (m_videoPreviewFactory && m_videoPreviewFactoryHasChanged) {
       
   259                     m_videoPreviewFactoryHasChanged = false;
       
   260                     GstElement *preview = m_videoPreviewFactory->buildElement();
       
   261                     g_object_set(G_OBJECT(m_pipeline), "vfsink", preview, NULL);
       
   262                 }
       
   263                 gst_element_set_state(m_pipeline, GST_STATE_PLAYING);
       
   264                 g_object_set(G_OBJECT(m_pipeline), "mode", 0, NULL);
       
   265             } else {
       
   266                 if (m_captureMode == AudioAndVideo) {
       
   267                     setupCameraBin();
       
   268                     g_object_set(G_OBJECT(m_pipeline), "filename", m_sink.toString().toLocal8Bit().constData(), NULL);
       
   269                     g_object_set(G_OBJECT(m_pipeline), "mode", 1, NULL);
       
   270                     g_signal_emit_by_name(m_pipeline, "user-start", 0);
       
   271                 } else if (m_captureMode & Audio) {
       
   272                     buildAudioEncodeBin();
       
   273                     gst_element_set_state(m_pipeline, GST_STATE_PLAYING);
       
   274                 }
       
   275             }
       
   276             break;
       
   277         case PausedState:
       
   278         case RecordingState:
       
   279             if (newState == PausedState) {
       
   280                 g_signal_emit_by_name(m_pipeline, "user-pause", 0);
       
   281                 m_state = PausedState;
       
   282             } else {
       
   283                 if (m_captureMode == AudioAndVideo)
       
   284                     g_signal_emit_by_name(m_pipeline, "user-stop", 0);
       
   285                 else if (m_captureMode & Audio) {
       
   286                     gst_element_send_event(m_pipeline, gst_event_new_eos());
       
   287                     gst_element_set_state(m_pipeline, GST_STATE_NULL);
       
   288                 }
       
   289 
       
   290                 m_state = StoppedState;
       
   291                 if (newState == PreviewState)
       
   292                     setState(newState);
       
   293             }
       
   294             break;
       
   295     }
       
   296 }
       
   297 
       
   298 
       
   299 qint64 QGstreamerCaptureSession::duration() const
       
   300 {
       
   301     GstFormat   format = GST_FORMAT_TIME;
       
   302     gint64      duration = 0;
       
   303 
       
   304     if ( m_pipeline && gst_element_query_position(m_pipeline, &format, &duration))
       
   305         return duration / 1000000;
       
   306     else
       
   307         return 0;
       
   308 }
       
   309 
       
   310 void QGstreamerCaptureSession::setCaptureDevice(const QString &deviceName)
       
   311 {
       
   312     m_captureDevice = deviceName;
       
   313 }
       
   314 
       
   315 void QGstreamerCaptureSession::setMetaData(const QMap<QByteArray, QVariant> &data)
       
   316 {
       
   317     m_metaData = data;
       
   318 
       
   319     if (m_pipeline) {
       
   320         GstIterator *elements = gst_bin_iterate_all_by_interface(GST_BIN(m_pipeline), GST_TYPE_TAG_SETTER);
       
   321         GstElement *element = 0;
       
   322         while (gst_iterator_next(elements, (void**)&element) == GST_ITERATOR_OK) {
       
   323             QMapIterator<QByteArray, QVariant> it(data);
       
   324             while (it.hasNext()) {
       
   325                 it.next();
       
   326                 const QString tagName = it.key();
       
   327                 const QVariant tagValue = it.value();
       
   328 
       
   329                 switch(tagValue.type()) {
       
   330                     case QVariant::String:
       
   331                         gst_tag_setter_add_tags(GST_TAG_SETTER(element),
       
   332                             GST_TAG_MERGE_REPLACE_ALL,
       
   333                             tagName.toUtf8().constData(),
       
   334                             tagValue.toString().toUtf8().constData(),
       
   335                             NULL);
       
   336                         break;
       
   337                     case QVariant::Int:
       
   338                     case QVariant::LongLong:
       
   339                         gst_tag_setter_add_tags(GST_TAG_SETTER(element),
       
   340                             GST_TAG_MERGE_REPLACE_ALL,
       
   341                             tagName.toUtf8().constData(),
       
   342                             tagValue.toInt(),
       
   343                             NULL);
       
   344                         break;
       
   345                     case QVariant::Double:
       
   346                         gst_tag_setter_add_tags(GST_TAG_SETTER(element),
       
   347                             GST_TAG_MERGE_REPLACE_ALL,
       
   348                             tagName.toUtf8().constData(),
       
   349                             tagValue.toDouble(),
       
   350                             NULL);
       
   351                         break;
       
   352                     default:
       
   353                         break;
       
   354                 }
       
   355             }
       
   356         }
       
   357     }
       
   358 }
       
   359 
       
   360 bool QGstreamerCaptureSession::processSyncMessage(const QGstreamerMessage &message)
       
   361 {
       
   362     GstMessage* gm = message.rawMessage();
       
   363     const GstStructure *st;
       
   364     const GValue *image;
       
   365     GstBuffer *buffer = NULL;
       
   366 
       
   367     if (gm && GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) {
       
   368         if (gst_structure_has_name(gm->structure, "prepare-xwindow-id")) {
       
   369             if (m_audioPreviewFactory)
       
   370                 m_audioPreviewFactory->prepareWinId();
       
   371 
       
   372             if (m_videoPreviewFactory)
       
   373                 m_videoPreviewFactory->prepareWinId();
       
   374 
       
   375             return true;
       
   376         }
       
   377     }
       
   378 
       
   379     return false;
       
   380 }
       
   381 
       
   382 void QGstreamerCaptureSession::busMessage(const QGstreamerMessage &message)
       
   383 {
       
   384     GstMessage* gm = message.rawMessage();
       
   385 
       
   386     if (gm) {
       
   387         if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ERROR) {
       
   388             GError *err;
       
   389             gchar *debug;
       
   390             gst_message_parse_error (gm, &err, &debug);
       
   391             emit error(int(QMediaRecorder::ResourceError),QString::fromUtf8(err->message));
       
   392             g_error_free (err);
       
   393             g_free (debug);
       
   394         }
       
   395 
       
   396         if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_pipeline)) {
       
   397             switch (GST_MESSAGE_TYPE(gm))  {
       
   398             case GST_MESSAGE_DURATION:
       
   399                 break;
       
   400 
       
   401             case GST_MESSAGE_EOS:
       
   402                 if (m_waitingForEos)
       
   403                     setState(m_pendingState);
       
   404                 break;
       
   405 
       
   406             case GST_MESSAGE_STATE_CHANGED:
       
   407                 {
       
   408 
       
   409                     GstState    oldState;
       
   410                     GstState    newState;
       
   411                     GstState    pending;
       
   412 
       
   413                     gst_message_parse_state_changed(gm, &oldState, &newState, &pending);
       
   414 
       
   415                     QStringList states;
       
   416                     states << "GST_STATE_VOID_PENDING" <<  "GST_STATE_NULL" << "GST_STATE_READY" << "GST_STATE_PAUSED" << "GST_STATE_PLAYING";
       
   417 
       
   418 
       
   419                     //qDebug() << QString("state changed: old: %1  new: %2  pending: %3") \
       
   420                     //        .arg(states[oldState]) \
       
   421                     //       .arg(states[newState]) \
       
   422                     //        .arg(states[pending]);
       
   423 
       
   424                     #define ENUM_NAME(c,e,v) (c::staticMetaObject.enumerator(c::staticMetaObject.indexOfEnumerator(e)).valueToKey((v)))
       
   425 
       
   426                     //qDebug() << "Current session state:" << ENUM_NAME(QGstreamerCaptureSession,"State",m_state);
       
   427                     //qDebug() << "Pending session state:" << ENUM_NAME(QGstreamerCaptureSession,"State",m_pendingState);
       
   428 
       
   429 
       
   430                     switch (newState) {
       
   431                     case GST_STATE_VOID_PENDING:
       
   432                     case GST_STATE_NULL:
       
   433                     case GST_STATE_READY:
       
   434                         /*if (m_state != StoppedState && m_pendingState == StoppedState) {
       
   435                             emit stateChanged(m_state = StoppedState);
       
   436                         }*/
       
   437                         break;
       
   438                     case GST_STATE_PAUSED:
       
   439                         if (m_state != PausedState && m_pendingState == PausedState)
       
   440                             emit stateChanged(m_state = PausedState);
       
   441 
       
   442                         if (m_pipelineMode == RecordingPipeline && !m_metaData.isEmpty())
       
   443                             setMetaData(m_metaData);
       
   444                         break;
       
   445                     case GST_STATE_PLAYING:
       
   446                         {
       
   447                             if (m_state == PreviewState || m_state == RecordingState)
       
   448                             {
       
   449                                 emit stateChanged(m_state);
       
   450                             }
       
   451 
       
   452                         }
       
   453                         break;
       
   454                     }
       
   455                 }
       
   456                 break;
       
   457             default:
       
   458                 break;
       
   459             }
       
   460             //qDebug() << "New session state:" << ENUM_NAME(QGstreamerCaptureSession,"State",m_state);
       
   461         }
       
   462     }
       
   463 }