diff options
Diffstat (limited to 'src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.cpp')
-rw-r--r-- | src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.cpp | 614 |
1 files changed, 0 insertions, 614 deletions
diff --git a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.cpp b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.cpp deleted file mode 100644 index d6b8ad759..000000000 --- a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.cpp +++ /dev/null @@ -1,614 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -//#define DEBUG_DECODER - -#include "qgstreameraudiodecodersession.h" -#include <private/qgstreamerbushelper_p.h> - -#include <private/qgstutils_p.h> - -#include <gst/gstvalue.h> -#include <gst/base/gstbasesrc.h> - -#include <QtCore/qdatetime.h> -#include <QtCore/qdebug.h> -#include <QtCore/qsize.h> -#include <QtCore/qtimer.h> -#include <QtCore/qdebug.h> -#include <QtCore/qdir.h> -#include <QtCore/qstandardpaths.h> -#include <QtCore/qurl.h> - -#define MAX_BUFFERS_IN_QUEUE 4 - -QT_BEGIN_NAMESPACE - -typedef enum { - GST_PLAY_FLAG_VIDEO = 0x00000001, - GST_PLAY_FLAG_AUDIO = 0x00000002, - GST_PLAY_FLAG_TEXT = 0x00000004, - GST_PLAY_FLAG_VIS = 0x00000008, - GST_PLAY_FLAG_SOFT_VOLUME = 0x00000010, - GST_PLAY_FLAG_NATIVE_AUDIO = 0x00000020, - GST_PLAY_FLAG_NATIVE_VIDEO = 0x00000040, - GST_PLAY_FLAG_DOWNLOAD = 0x00000080, - GST_PLAY_FLAG_BUFFERING = 0x000000100 -} GstPlayFlags; - -QGstreamerAudioDecoderSession::QGstreamerAudioDecoderSession(QObject *parent) - : QObject(parent), - m_state(QAudioDecoder::StoppedState), - m_pendingState(QAudioDecoder::StoppedState), - m_busHelper(0), - m_bus(0), - m_playbin(0), - m_outputBin(0), - m_audioConvert(0), - m_appSink(0), -#if QT_CONFIG(gstreamer_app) - m_appSrc(0), -#endif - mDevice(0), - m_buffersAvailable(0), - m_position(-1), - m_duration(-1), - m_durationQueries(0) -{ - // Create pipeline here - m_playbin = gst_element_factory_make(QT_GSTREAMER_PLAYBIN_ELEMENT_NAME, NULL); - - if (m_playbin != 0) { - // Sort out messages - m_bus = gst_element_get_bus(m_playbin); - m_busHelper = new QGstreamerBusHelper(m_bus, this); - m_busHelper->installMessageFilter(this); - - // Set the rest of the pipeline up - setAudioFlags(true); - - m_audioConvert = gst_element_factory_make("audioconvert", NULL); - - m_outputBin = gst_bin_new("audio-output-bin"); - gst_bin_add(GST_BIN(m_outputBin), m_audioConvert); - - // add ghostpad - GstPad *pad = gst_element_get_static_pad(m_audioConvert, "sink"); - Q_ASSERT(pad); - gst_element_add_pad(GST_ELEMENT(m_outputBin), gst_ghost_pad_new("sink", pad)); - gst_object_unref(GST_OBJECT(pad)); - - g_object_set(G_OBJECT(m_playbin), "audio-sink", m_outputBin, NULL); -#if QT_CONFIG(gstreamer_app) - g_signal_connect(G_OBJECT(m_playbin), "deep-notify::source", (GCallback) &QGstreamerAudioDecoderSession::configureAppSrcElement, (gpointer)this); -#endif - - // Set volume to 100% - gdouble volume = 1.0; - g_object_set(G_OBJECT(m_playbin), "volume", volume, NULL); - } -} - -QGstreamerAudioDecoderSession::~QGstreamerAudioDecoderSession() -{ - if (m_playbin) { - stop(); - - delete m_busHelper; -#if QT_CONFIG(gstreamer_app) - delete m_appSrc; -#endif - gst_object_unref(GST_OBJECT(m_bus)); - gst_object_unref(GST_OBJECT(m_playbin)); - } -} - -#if QT_CONFIG(gstreamer_app) -void QGstreamerAudioDecoderSession::configureAppSrcElement(GObject* object, GObject *orig, GParamSpec *pspec, QGstreamerAudioDecoderSession* self) -{ - Q_UNUSED(object); - Q_UNUSED(pspec); - - // In case we switch from appsrc to not - if (!self->appsrc()) - return; - - GstElement *appsrc; - g_object_get(orig, "source", &appsrc, NULL); - - if (!self->appsrc()->setup(appsrc)) - qWarning()<<"Could not setup appsrc element"; - - g_object_unref(G_OBJECT(appsrc)); -} -#endif - -bool QGstreamerAudioDecoderSession::processBusMessage(const QGstreamerMessage &message) -{ - GstMessage* gm = message.rawMessage(); - if (gm) { - if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_DURATION) { - updateDuration(); - } else if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_playbin)) { - switch (GST_MESSAGE_TYPE(gm)) { - case GST_MESSAGE_STATE_CHANGED: - { - GstState oldState; - GstState newState; - GstState pending; - - gst_message_parse_state_changed(gm, &oldState, &newState, &pending); - -#ifdef DEBUG_DECODER - 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]) << "internal" << m_state; -#endif - - QAudioDecoder::State prevState = m_state; - - switch (newState) { - case GST_STATE_VOID_PENDING: - case GST_STATE_NULL: - m_state = QAudioDecoder::StoppedState; - break; - case GST_STATE_READY: - m_state = QAudioDecoder::StoppedState; - break; - case GST_STATE_PLAYING: - m_state = QAudioDecoder::DecodingState; - break; - case GST_STATE_PAUSED: - m_state = QAudioDecoder::DecodingState; - - //gstreamer doesn't give a reliable indication the duration - //information is ready, GST_MESSAGE_DURATION is not sent by most elements - //the duration is queried up to 5 times with increasing delay - m_durationQueries = 5; - updateDuration(); - break; - } - - if (prevState != m_state) - emit stateChanged(m_state); - } - break; - - case GST_MESSAGE_EOS: - m_pendingState = m_state = QAudioDecoder::StoppedState; - emit finished(); - emit stateChanged(m_state); - break; - - case GST_MESSAGE_ERROR: { - GError *err; - gchar *debug; - gst_message_parse_error(gm, &err, &debug); - if (err->domain == GST_STREAM_ERROR && err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND) - processInvalidMedia(QAudioDecoder::FormatError, tr("Cannot play stream of type: <unknown>")); - else - processInvalidMedia(QAudioDecoder::ResourceError, QString::fromUtf8(err->message)); - qWarning() << "Error:" << QString::fromUtf8(err->message); - g_error_free(err); - g_free(debug); - } - break; - case GST_MESSAGE_WARNING: - { - GError *err; - gchar *debug; - gst_message_parse_warning (gm, &err, &debug); - qWarning() << "Warning:" << QString::fromUtf8(err->message); - g_error_free (err); - g_free (debug); - } - break; -#ifdef DEBUG_DECODER - case GST_MESSAGE_INFO: - { - GError *err; - gchar *debug; - gst_message_parse_info (gm, &err, &debug); - qDebug() << "Info:" << QString::fromUtf8(err->message); - g_error_free (err); - g_free (debug); - } - break; -#endif - default: - break; - } - } else if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ERROR) { - GError *err; - gchar *debug; - gst_message_parse_error(gm, &err, &debug); - QAudioDecoder::Error qerror = QAudioDecoder::ResourceError; - if (err->domain == GST_STREAM_ERROR) { - switch (err->code) { - case GST_STREAM_ERROR_DECRYPT: - case GST_STREAM_ERROR_DECRYPT_NOKEY: - qerror = QAudioDecoder::AccessDeniedError; - break; - case GST_STREAM_ERROR_FORMAT: - case GST_STREAM_ERROR_DEMUX: - case GST_STREAM_ERROR_DECODE: - case GST_STREAM_ERROR_WRONG_TYPE: - case GST_STREAM_ERROR_TYPE_NOT_FOUND: - case GST_STREAM_ERROR_CODEC_NOT_FOUND: - qerror = QAudioDecoder::FormatError; - break; - default: - break; - } - } else if (err->domain == GST_CORE_ERROR) { - switch (err->code) { - case GST_CORE_ERROR_MISSING_PLUGIN: - qerror = QAudioDecoder::FormatError; - break; - default: - break; - } - } - - processInvalidMedia(qerror, QString::fromUtf8(err->message)); - g_error_free(err); - g_free(debug); - } - } - - return false; -} - -QString QGstreamerAudioDecoderSession::sourceFilename() const -{ - return mSource; -} - -void QGstreamerAudioDecoderSession::setSourceFilename(const QString &fileName) -{ - stop(); - mDevice = 0; -#if QT_CONFIG(gstreamer_app) - if (m_appSrc) - m_appSrc->deleteLater(); - m_appSrc = 0; -#endif - - bool isSignalRequired = (mSource != fileName); - mSource = fileName; - if (isSignalRequired) - emit sourceChanged(); -} - -QIODevice *QGstreamerAudioDecoderSession::sourceDevice() const -{ - return mDevice; -} - -void QGstreamerAudioDecoderSession::setSourceDevice(QIODevice *device) -{ - stop(); - mSource.clear(); - bool isSignalRequired = (mDevice != device); - mDevice = device; - if (isSignalRequired) - emit sourceChanged(); -} - -void QGstreamerAudioDecoderSession::start() -{ - if (!m_playbin) { - processInvalidMedia(QAudioDecoder::ResourceError, "Playbin element is not valid"); - return; - } - - addAppSink(); - - if (!mSource.isEmpty()) { - g_object_set(G_OBJECT(m_playbin), "uri", QUrl::fromLocalFile(mSource).toEncoded().constData(), NULL); - } else if (mDevice) { -#if QT_CONFIG(gstreamer_app) - // make sure we can read from device - if (!mDevice->isOpen() || !mDevice->isReadable()) { - processInvalidMedia(QAudioDecoder::AccessDeniedError, "Unable to read from specified device"); - return; - } - - if (!m_appSrc) - m_appSrc = new QGstAppSrc(this); - m_appSrc->setStream(mDevice); - - g_object_set(G_OBJECT(m_playbin), "uri", "appsrc://", NULL); -#endif - } else { - return; - } - - // Set audio format - if (m_appSink) { - if (mFormat.isValid()) { - setAudioFlags(false); - GstCaps *caps = QGstUtils::capsForAudioFormat(mFormat); - gst_app_sink_set_caps(m_appSink, caps); - gst_caps_unref(caps); - } else { - // We want whatever the native audio format is - setAudioFlags(true); - gst_app_sink_set_caps(m_appSink, NULL); - } - } - - m_pendingState = QAudioDecoder::DecodingState; - if (gst_element_set_state(m_playbin, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { - qWarning() << "GStreamer; Unable to start decoding process"; - m_pendingState = m_state = QAudioDecoder::StoppedState; - - emit stateChanged(m_state); - } -} - -void QGstreamerAudioDecoderSession::stop() -{ - if (m_playbin) { - gst_element_set_state(m_playbin, GST_STATE_NULL); - removeAppSink(); - - QAudioDecoder::State oldState = m_state; - m_pendingState = m_state = QAudioDecoder::StoppedState; - - // GStreamer thread is stopped. Can safely access m_buffersAvailable - if (m_buffersAvailable != 0) { - m_buffersAvailable = 0; - emit bufferAvailableChanged(false); - } - - if (m_position != -1) { - m_position = -1; - emit positionChanged(m_position); - } - - if (m_duration != -1) { - m_duration = -1; - emit durationChanged(m_duration); - } - - if (oldState != m_state) - emit stateChanged(m_state); - } -} - -QAudioFormat QGstreamerAudioDecoderSession::audioFormat() const -{ - return mFormat; -} - -void QGstreamerAudioDecoderSession::setAudioFormat(const QAudioFormat &format) -{ - if (mFormat != format) { - mFormat = format; - emit formatChanged(mFormat); - } -} - -QAudioBuffer QGstreamerAudioDecoderSession::read() -{ - QAudioBuffer audioBuffer; - - int buffersAvailable; - { - QMutexLocker locker(&m_buffersMutex); - buffersAvailable = m_buffersAvailable; - - // need to decrement before pulling a buffer - // to make sure assert in QGstreamerAudioDecoderSession::new_buffer works - m_buffersAvailable--; - } - - - if (buffersAvailable) { - if (buffersAvailable == 1) - emit bufferAvailableChanged(false); - - const char* bufferData = 0; - int bufferSize = 0; - -#if GST_CHECK_VERSION(1,0,0) - GstSample *sample = gst_app_sink_pull_sample(m_appSink); - GstBuffer *buffer = gst_sample_get_buffer(sample); - GstMapInfo mapInfo; - gst_buffer_map(buffer, &mapInfo, GST_MAP_READ); - bufferData = (const char*)mapInfo.data; - bufferSize = mapInfo.size; - QAudioFormat format = QGstUtils::audioFormatForSample(sample); -#else - GstBuffer *buffer = gst_app_sink_pull_buffer(m_appSink); - bufferData = (const char*)buffer->data; - bufferSize = buffer->size; - QAudioFormat format = QGstUtils::audioFormatForBuffer(buffer); -#endif - - if (format.isValid()) { - // XXX At the moment we have to copy data from GstBuffer into QAudioBuffer. - // We could improve performance by implementing QAbstractAudioBuffer for GstBuffer. - qint64 position = getPositionFromBuffer(buffer); - audioBuffer = QAudioBuffer(QByteArray((const char*)bufferData, bufferSize), format, position); - position /= 1000; // convert to milliseconds - if (position != m_position) { - m_position = position; - emit positionChanged(m_position); - } - } -#if GST_CHECK_VERSION(1,0,0) - gst_buffer_unmap(buffer, &mapInfo); - gst_sample_unref(sample); -#else - gst_buffer_unref(buffer); -#endif - } - - return audioBuffer; -} - -bool QGstreamerAudioDecoderSession::bufferAvailable() const -{ - QMutexLocker locker(&m_buffersMutex); - return m_buffersAvailable > 0; -} - -qint64 QGstreamerAudioDecoderSession::position() const -{ - return m_position; -} - -qint64 QGstreamerAudioDecoderSession::duration() const -{ - return m_duration; -} - -void QGstreamerAudioDecoderSession::processInvalidMedia(QAudioDecoder::Error errorCode, const QString& errorString) -{ - stop(); - emit error(int(errorCode), errorString); -} - -GstFlowReturn QGstreamerAudioDecoderSession::new_sample(GstAppSink *, gpointer user_data) -{ - // "Note that the preroll buffer will also be returned as the first buffer when calling gst_app_sink_pull_buffer()." - QGstreamerAudioDecoderSession *session = reinterpret_cast<QGstreamerAudioDecoderSession*>(user_data); - - int buffersAvailable; - { - QMutexLocker locker(&session->m_buffersMutex); - buffersAvailable = session->m_buffersAvailable; - session->m_buffersAvailable++; - Q_ASSERT(session->m_buffersAvailable <= MAX_BUFFERS_IN_QUEUE); - } - - if (!buffersAvailable) - QMetaObject::invokeMethod(session, "bufferAvailableChanged", Qt::QueuedConnection, Q_ARG(bool, true)); - QMetaObject::invokeMethod(session, "bufferReady", Qt::QueuedConnection); - return GST_FLOW_OK; -} - -void QGstreamerAudioDecoderSession::setAudioFlags(bool wantNativeAudio) -{ - int flags = 0; - if (m_playbin) { - g_object_get(G_OBJECT(m_playbin), "flags", &flags, NULL); - // make sure not to use GST_PLAY_FLAG_NATIVE_AUDIO unless desired - // it prevents audio format conversion - flags &= ~(GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_NATIVE_VIDEO | GST_PLAY_FLAG_TEXT | GST_PLAY_FLAG_VIS | GST_PLAY_FLAG_NATIVE_AUDIO); - flags |= GST_PLAY_FLAG_AUDIO; - if (wantNativeAudio) - flags |= GST_PLAY_FLAG_NATIVE_AUDIO; - g_object_set(G_OBJECT(m_playbin), "flags", flags, NULL); - } -} - -void QGstreamerAudioDecoderSession::addAppSink() -{ - if (m_appSink) - return; - - m_appSink = (GstAppSink*)gst_element_factory_make("appsink", NULL); - - GstAppSinkCallbacks callbacks; - memset(&callbacks, 0, sizeof(callbacks)); -#if GST_CHECK_VERSION(1,0,0) - callbacks.new_sample = &new_sample; -#else - callbacks.new_buffer = &new_sample; -#endif - gst_app_sink_set_callbacks(m_appSink, &callbacks, this, NULL); - gst_app_sink_set_max_buffers(m_appSink, MAX_BUFFERS_IN_QUEUE); - gst_base_sink_set_sync(GST_BASE_SINK(m_appSink), FALSE); - - gst_bin_add(GST_BIN(m_outputBin), GST_ELEMENT(m_appSink)); - gst_element_link(m_audioConvert, GST_ELEMENT(m_appSink)); -} - -void QGstreamerAudioDecoderSession::removeAppSink() -{ - if (!m_appSink) - return; - - gst_element_unlink(m_audioConvert, GST_ELEMENT(m_appSink)); - gst_bin_remove(GST_BIN(m_outputBin), GST_ELEMENT(m_appSink)); - - m_appSink = 0; -} - -void QGstreamerAudioDecoderSession::updateDuration() -{ - gint64 gstDuration = 0; - int duration = -1; - - if (m_playbin && qt_gst_element_query_duration(m_playbin, GST_FORMAT_TIME, &gstDuration)) - duration = gstDuration / 1000000; - - if (m_duration != duration) { - m_duration = duration; - emit durationChanged(m_duration); - } - - if (m_duration > 0) - m_durationQueries = 0; - - if (m_durationQueries > 0) { - //increase delay between duration requests - int delay = 25 << (5 - m_durationQueries); - QTimer::singleShot(delay, this, SLOT(updateDuration())); - m_durationQueries--; - } -} - -qint64 QGstreamerAudioDecoderSession::getPositionFromBuffer(GstBuffer* buffer) -{ - qint64 position = GST_BUFFER_TIMESTAMP(buffer); - if (position >= 0) - position = position / G_GINT64_CONSTANT(1000); // microseconds - else - position = -1; - return position; -} - -QT_END_NAMESPACE |