diff options
Diffstat (limited to 'src/multimedia/platform/gstreamer/audio/qgstreameraudiodecoder.cpp')
-rw-r--r-- | src/multimedia/platform/gstreamer/audio/qgstreameraudiodecoder.cpp | 558 |
1 files changed, 0 insertions, 558 deletions
diff --git a/src/multimedia/platform/gstreamer/audio/qgstreameraudiodecoder.cpp b/src/multimedia/platform/gstreamer/audio/qgstreameraudiodecoder.cpp deleted file mode 100644 index dc44a1c38..000000000 --- a/src/multimedia/platform/gstreamer/audio/qgstreameraudiodecoder.cpp +++ /dev/null @@ -1,558 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2020 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 "qgstreameraudiodecoder_p.h" -#include "private/qgstreamermessage_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; - - - -QGstreamerAudioDecoder::QGstreamerAudioDecoder(QAudioDecoder *parent) - : QPlatformAudioDecoder(parent), - m_playbin(GST_PIPELINE_CAST(QGstElement("playbin", "playbin").element())) -{ - if (m_playbin.isNull()) { - // ### set error - return; - } - - // Sort out messages - m_playbin.installMessageFilter(this); - - // Set the rest of the pipeline up - setAudioFlags(true); - - m_audioConvert = QGstElement("audioconvert", "audioconvert"); - - m_outputBin = QGstBin("audio-output-bin"); - m_outputBin.add(m_audioConvert); - - // add ghostpad - m_outputBin.addGhostPad(m_audioConvert, "sink"); - - g_object_set(m_playbin.object(), "audio-sink", m_outputBin.element(), NULL); - g_signal_connect(m_playbin.object(), "deep-notify::source", (GCallback) &QGstreamerAudioDecoder::configureAppSrcElement, (gpointer)this); - - // Set volume to 100% - gdouble volume = 1.0; - m_playbin.set("volume", volume); -} - -QGstreamerAudioDecoder::~QGstreamerAudioDecoder() -{ - if (m_playbin.isNull()) - return; - - stop(); - -#if QT_CONFIG(gstreamer_app) - delete m_appSrc; -#endif -} - -#if QT_CONFIG(gstreamer_app) -void QGstreamerAudioDecoder::configureAppSrcElement(GObject* object, GObject *orig, GParamSpec *pspec, QGstreamerAudioDecoder *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); - - auto *qAppSrc = self->appsrc(); - qAppSrc->setExternalAppSrc(appsrc); - qAppSrc->setup(self->mDevice); - - g_object_unref(G_OBJECT(appsrc)); -} -#endif - -bool QGstreamerAudioDecoder::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) == m_playbin.object()) { - 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 - - bool isDecoding = false; - switch (newState) { - case GST_STATE_VOID_PENDING: - case GST_STATE_NULL: - case GST_STATE_READY: - break; - case GST_STATE_PLAYING: - isDecoding = true; - break; - case GST_STATE_PAUSED: - isDecoding = true; - - //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; - } - - setIsDecoding(isDecoding); - } - break; - - case GST_MESSAGE_EOS: - finished(); - 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; -} - -QUrl QGstreamerAudioDecoder::source() const -{ - return mSource; -} - -void QGstreamerAudioDecoder::setSource(const QUrl &fileName) -{ - stop(); - mDevice = nullptr; - delete m_appSrc; - m_appSrc = nullptr; - - bool isSignalRequired = (mSource != fileName); - mSource = fileName; - if (isSignalRequired) - emit sourceChanged(); -} - -QIODevice *QGstreamerAudioDecoder::sourceDevice() const -{ - return mDevice; -} - -void QGstreamerAudioDecoder::setSourceDevice(QIODevice *device) -{ - stop(); - mSource.clear(); - bool isSignalRequired = (mDevice != device); - mDevice = device; - if (isSignalRequired) - emit sourceChanged(); -} - -void QGstreamerAudioDecoder::start() -{ - if (m_playbin.isNull()) { - processInvalidMedia(QAudioDecoder::ResourceError, QLatin1String("Playbin element is not valid")); - return; - } - - addAppSink(); - - if (!mSource.isEmpty()) { - m_playbin.set("uri", mSource.toEncoded().constData()); - } else if (mDevice) { - // make sure we can read from device - if (!mDevice->isOpen() || !mDevice->isReadable()) { - processInvalidMedia(QAudioDecoder::AccessDeniedError, QLatin1String("Unable to read from specified device")); - return; - } - - if (!m_appSrc) - m_appSrc = new QGstAppSrc(this); - - m_playbin.set("uri", "appsrc://"); - } else { - return; - } - - // Set audio format - if (m_appSink) { - if (mFormat.isValid()) { - setAudioFlags(false); - QGstMutableCaps caps = QGstUtils::capsForAudioFormat(mFormat); - gst_app_sink_set_caps(m_appSink, caps.get()); - } else { - // We want whatever the native audio format is - setAudioFlags(true); - gst_app_sink_set_caps(m_appSink, nullptr); - } - } - - if (m_playbin.setState(GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { - qWarning() << "GStreamer; Unable to start decoding process"; - m_playbin.dumpGraph("failed"); - return; - } -} - -void QGstreamerAudioDecoder::stop() -{ - if (m_playbin.isNull()) - return; - - m_playbin.setState(GST_STATE_NULL); - removeAppSink(); - - // 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); - } - - setIsDecoding(false); -} - -QAudioFormat QGstreamerAudioDecoder::audioFormat() const -{ - return mFormat; -} - -void QGstreamerAudioDecoder::setAudioFormat(const QAudioFormat &format) -{ - if (mFormat != format) { - mFormat = format; - emit formatChanged(mFormat); - } -} - -QAudioBuffer QGstreamerAudioDecoder::read() -{ - QAudioBuffer audioBuffer; - - int buffersAvailable; - { - QMutexLocker locker(&m_buffersMutex); - buffersAvailable = m_buffersAvailable; - - // need to decrement before pulling a buffer - // to make sure assert in QGstreamerAudioDecoderControl::new_buffer works - m_buffersAvailable--; - } - - - if (buffersAvailable) { - if (buffersAvailable == 1) - emit bufferAvailableChanged(false); - - const char* bufferData = nullptr; - int bufferSize = 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); - - 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); - } - } - gst_buffer_unmap(buffer, &mapInfo); - gst_sample_unref(sample); - } - - return audioBuffer; -} - -bool QGstreamerAudioDecoder::bufferAvailable() const -{ - QMutexLocker locker(&m_buffersMutex); - return m_buffersAvailable > 0; -} - -qint64 QGstreamerAudioDecoder::position() const -{ - return m_position; -} - -qint64 QGstreamerAudioDecoder::duration() const -{ - return m_duration; -} - -void QGstreamerAudioDecoder::processInvalidMedia(QAudioDecoder::Error errorCode, const QString& errorString) -{ - stop(); - emit error(int(errorCode), errorString); -} - -GstFlowReturn QGstreamerAudioDecoder::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()." - QGstreamerAudioDecoder *decoder = reinterpret_cast<QGstreamerAudioDecoder*>(user_data); - - int buffersAvailable; - { - QMutexLocker locker(&decoder->m_buffersMutex); - buffersAvailable = decoder->m_buffersAvailable; - decoder->m_buffersAvailable++; - Q_ASSERT(decoder->m_buffersAvailable <= MAX_BUFFERS_IN_QUEUE); - } - - if (!buffersAvailable) - decoder->bufferAvailableChanged(true); - decoder->bufferReady(); - return GST_FLOW_OK; -} - -void QGstreamerAudioDecoder::setAudioFlags(bool wantNativeAudio) -{ - if (m_playbin.isNull()) - return; - - int flags = m_playbin.getInt("flags"); - // 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; - m_playbin.set("flags", flags); -} - -void QGstreamerAudioDecoder::addAppSink() -{ - if (m_appSink) - return; - - m_appSink = (GstAppSink*)gst_element_factory_make("appsink", nullptr); - - GstAppSinkCallbacks callbacks; - memset(&callbacks, 0, sizeof(callbacks)); - callbacks.new_sample = &new_sample; - gst_app_sink_set_callbacks(m_appSink, &callbacks, this, nullptr); - 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(m_outputBin.bin(), GST_ELEMENT(m_appSink)); - gst_element_link(m_audioConvert.element(), GST_ELEMENT(m_appSink)); -} - -void QGstreamerAudioDecoder::removeAppSink() -{ - if (!m_appSink) - return; - - gst_element_unlink(m_audioConvert.element(), GST_ELEMENT(m_appSink)); - gst_bin_remove(m_outputBin.bin(), GST_ELEMENT(m_appSink)); - - m_appSink = nullptr; -} - -void QGstreamerAudioDecoder::updateDuration() -{ - int duration = -1; - - if (!m_playbin.isNull()) - duration = m_playbin.duration() / 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 QGstreamerAudioDecoder::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 |