diff options
Diffstat (limited to 'src/multimedia/platform')
338 files changed, 1310 insertions, 77010 deletions
diff --git a/src/multimedia/platform/alsa/qalsaaudiodevice.cpp b/src/multimedia/platform/alsa/qalsaaudiodevice.cpp deleted file mode 100644 index ed0aa0030..000000000 --- a/src/multimedia/platform/alsa/qalsaaudiodevice.cpp +++ /dev/null @@ -1,123 +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$ -** -****************************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of other Qt classes. This header file may change from version to -// version without notice, or even be removed. -// -// INTERNAL USE ONLY: Do NOT use for any other purpose. -// - -#include "qalsaaudiodevice_p.h" - -#include <alsa/version.h> - -QT_BEGIN_NAMESPACE - -QAlsaAudioDeviceInfo::QAlsaAudioDeviceInfo(const QByteArray &dev, const QString &desc, QAudioDevice::Mode mode) - : QAudioDevicePrivate(dev, mode) -{ - description = desc; - - checkSurround(); - - minimumChannelCount = 1; - maximumChannelCount = 2; - if (surround71) - maximumChannelCount = 8; - else if (surround40) - maximumChannelCount = 4; - else if (surround51) - maximumChannelCount = 6; - - minimumSampleRate = 8000; - maximumSampleRate = 48000; - - supportedSampleFormats << QAudioFormat::UInt8 << QAudioFormat::Int16 << QAudioFormat::Int32 << QAudioFormat::Float; -} - -QAlsaAudioDeviceInfo::~QAlsaAudioDeviceInfo() -{ -} - -void QAlsaAudioDeviceInfo::checkSurround() -{ - surround40 = false; - surround51 = false; - surround71 = false; - - void **hints, **n; - char *name, *descr, *io; - - if(snd_device_name_hint(-1, "pcm", &hints) < 0) - return; - - n = hints; - - while (*n != NULL) { - name = snd_device_name_get_hint(*n, "NAME"); - descr = snd_device_name_get_hint(*n, "DESC"); - io = snd_device_name_get_hint(*n, "IOID"); - if((name != NULL) && (descr != NULL)) { - QString deviceName = QLatin1String(name); - if (mode == QAudioDevice::Output) { - if(deviceName.contains(QLatin1String("surround40"))) - surround40 = true; - if(deviceName.contains(QLatin1String("surround51"))) - surround51 = true; - if(deviceName.contains(QLatin1String("surround71"))) - surround71 = true; - } - } - if(name != NULL) - free(name); - if(descr != NULL) - free(descr); - if(io != NULL) - free(io); - ++n; - } - snd_device_name_free_hint(hints); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/alsa/qalsaaudiodevice_p.h b/src/multimedia/platform/alsa/qalsaaudiodevice_p.h deleted file mode 100644 index 4f7bc5757..000000000 --- a/src/multimedia/platform/alsa/qalsaaudiodevice_p.h +++ /dev/null @@ -1,85 +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$ -** -****************************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of other Qt classes. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - - -#ifndef QALSAAUDIODEVICEINFO_H -#define QALSAAUDIODEVICEINFO_H - -#include <alsa/asoundlib.h> - -#include <QtCore/qbytearray.h> -#include <QtCore/qstringlist.h> -#include <QtCore/qlist.h> -#include <QtCore/qdebug.h> - -#include <QtMultimedia/qaudio.h> -#include <private/qaudiodevice_p.h> -#include <private/qaudiosystem_p.h> - -QT_BEGIN_NAMESPACE - - -class QAlsaAudioDeviceInfo : public QAudioDevicePrivate -{ -public: - QAlsaAudioDeviceInfo(const QByteArray &dev, const QString &description, QAudioDevice::Mode mode); - ~QAlsaAudioDeviceInfo(); - -private: - void checkSurround(); - bool surround40; - bool surround51; - bool surround71; -}; - -QT_END_NAMESPACE - - -#endif // QALSAAUDIODEVICEINFO_H diff --git a/src/multimedia/platform/alsa/qalsaaudiosink.cpp b/src/multimedia/platform/alsa/qalsaaudiosink.cpp deleted file mode 100644 index 7157e9508..000000000 --- a/src/multimedia/platform/alsa/qalsaaudiosink.cpp +++ /dev/null @@ -1,749 +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$ -** -****************************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of other Qt classes. This header file may change from version to -// version without notice, or even be removed. -// -// INTERNAL USE ONLY: Do NOT use for any other purpose. -// - -#include <QtCore/qcoreapplication.h> -#include <QtCore/qvarlengtharray.h> -#include <QtMultimedia/private/qaudiohelpers_p.h> -#include "qalsaaudiosink_p.h" -#include "qalsaaudiodevice_p.h" -#include <QLoggingCategory> - -QT_BEGIN_NAMESPACE - -Q_LOGGING_CATEGORY(lcAlsaOutput, "qt.multimedia.alsa.output") -//#define DEBUG_AUDIO 1 - -QAlsaAudioSink::QAlsaAudioSink(const QByteArray &device) -{ - bytesAvailable = 0; - handle = 0; - access = SND_PCM_ACCESS_RW_INTERLEAVED; - pcmformat = SND_PCM_FORMAT_S16; - buffer_frames = 0; - period_frames = 0; - buffer_size = 0; - period_size = 0; - buffer_time = 100000; - period_time = 20000; - totalTimeValue = 0; - audioBuffer = 0; - errorState = QAudio::NoError; - deviceState = QAudio::StoppedState; - audioSource = 0; - pullMode = true; - resuming = false; - opened = false; - - m_volume = 1.0f; - - m_device = device; - - timer = new QTimer(this); - connect(timer,SIGNAL(timeout()),SLOT(userFeed())); -} - -QAlsaAudioSink::~QAlsaAudioSink() -{ - close(); - disconnect(timer, SIGNAL(timeout())); - QCoreApplication::processEvents(); - delete timer; -} - -void QAlsaAudioSink::setVolume(qreal vol) -{ - m_volume = vol; -} - -qreal QAlsaAudioSink::volume() const -{ - return m_volume; -} - -QAudio::Error QAlsaAudioSink::error() const -{ - return errorState; -} - -QAudio::State QAlsaAudioSink::state() const -{ - return deviceState; -} - -int QAlsaAudioSink::xrun_recovery(int err) -{ - int count = 0; - bool reset = false; - - // ESTRPIPE is not available in all OSes where ALSA is available - int estrpipe = EIO; -#ifdef ESTRPIPE - estrpipe = ESTRPIPE; -#endif - - if(err == -EPIPE) { - errorState = QAudio::UnderrunError; - emit errorChanged(errorState); - err = snd_pcm_prepare(handle); - if(err < 0) - reset = true; - - } else if ((err == -estrpipe)||(err == -EIO)) { - errorState = QAudio::IOError; - emit errorChanged(errorState); - while((err = snd_pcm_resume(handle)) == -EAGAIN){ - usleep(100); - count++; - if(count > 5) { - reset = true; - break; - } - } - if(err < 0) { - err = snd_pcm_prepare(handle); - if(err < 0) - reset = true; - } - } - if(reset) { - close(); - open(); - snd_pcm_prepare(handle); - return 0; - } - return err; -} - -int QAlsaAudioSink::setFormat() -{ - snd_pcm_format_t pcmformat = SND_PCM_FORMAT_UNKNOWN; - - switch (settings.sampleFormat()) { - case QAudioFormat::UInt8: - pcmformat = SND_PCM_FORMAT_U8; - break; - case QAudioFormat::Int16: - if constexpr (QSysInfo::ByteOrder == QSysInfo::BigEndian) - pcmformat = SND_PCM_FORMAT_S16_LE; - else - pcmformat = SND_PCM_FORMAT_S16_BE; - break; - case QAudioFormat::Int32: - if constexpr (QSysInfo::ByteOrder == QSysInfo::BigEndian) - pcmformat = SND_PCM_FORMAT_S32_LE; - else - pcmformat = SND_PCM_FORMAT_S32_BE; - break; - case QAudioFormat::Float: - if constexpr (QSysInfo::ByteOrder == QSysInfo::BigEndian) - pcmformat = SND_PCM_FORMAT_FLOAT_LE; - else - pcmformat = SND_PCM_FORMAT_FLOAT_BE; - default: - break; - } - - return pcmformat != SND_PCM_FORMAT_UNKNOWN - ? snd_pcm_hw_params_set_format( handle, hwparams, pcmformat) - : -1; -} - -void QAlsaAudioSink::start(QIODevice* device) -{ - if(deviceState != QAudio::StoppedState) - deviceState = QAudio::StoppedState; - - errorState = QAudio::NoError; - - // Handle change of mode - if(audioSource && !pullMode) { - delete audioSource; - audioSource = 0; - } - - close(); - - pullMode = true; - audioSource = device; - - deviceState = QAudio::ActiveState; - - open(); - - emit stateChanged(deviceState); -} - -QIODevice* QAlsaAudioSink::start() -{ - if(deviceState != QAudio::StoppedState) - deviceState = QAudio::StoppedState; - - errorState = QAudio::NoError; - - // Handle change of mode - if(audioSource && !pullMode) { - delete audioSource; - audioSource = 0; - } - - close(); - - audioSource = new AlsaOutputPrivate(this); - audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered); - pullMode = false; - - deviceState = QAudio::IdleState; - - open(); - - emit stateChanged(deviceState); - - return audioSource; -} - -void QAlsaAudioSink::stop() -{ - if(deviceState == QAudio::StoppedState) - return; - errorState = QAudio::NoError; - deviceState = QAudio::StoppedState; - close(); - emit stateChanged(deviceState); -} - -bool QAlsaAudioSink::open() -{ - if(opened) - return true; - -#ifdef DEBUG_AUDIO - QTime now(QTime::currentTime()); - qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; -#endif - timeStamp.restart(); - elapsedTimeOffset = 0; - - int dir; - int err = 0; - int count=0; - unsigned int sampleRate=settings.sampleRate(); - - if (!settings.isValid()) { - qWarning("QAudioSink: open error, invalid format."); - } else if (settings.sampleRate() <= 0) { - qWarning("QAudioSink: open error, invalid sample rate (%d).", - settings.sampleRate()); - } else { - err = -1; - } - - if (err == 0) { - errorState = QAudio::OpenError; - deviceState = QAudio::StoppedState; - emit errorChanged(errorState); - return false; - } - - // Step 1: try and open the device - while((count < 5) && (err < 0)) { - err=snd_pcm_open(&handle, m_device.constData(),SND_PCM_STREAM_PLAYBACK,0); - if(err < 0) - count++; - } - if (( err < 0)||(handle == 0)) { - errorState = QAudio::OpenError; - emit errorChanged(errorState); - deviceState = QAudio::StoppedState; - return false; - } - snd_pcm_nonblock( handle, 0 ); - - // Step 2: Set the desired HW parameters. - snd_pcm_hw_params_alloca( &hwparams ); - - bool fatal = false; - QString errMessage; - unsigned int chunks = 8; - - err = snd_pcm_hw_params_any( handle, hwparams ); - if ( err < 0 ) { - fatal = true; - errMessage = QString::fromLatin1("QAudioSink: snd_pcm_hw_params_any: err = %1").arg(err); - } - if ( !fatal ) { - err = snd_pcm_hw_params_set_rate_resample( handle, hwparams, 1 ); - if ( err < 0 ) { - fatal = true; - errMessage = QString::fromLatin1("QAudioSink: snd_pcm_hw_params_set_rate_resample: err = %1").arg(err); - } - } - if ( !fatal ) { - err = snd_pcm_hw_params_set_access( handle, hwparams, access ); - if ( err < 0 ) { - fatal = true; - errMessage = QString::fromLatin1("QAudioSink: snd_pcm_hw_params_set_access: err = %1").arg(err); - } - } - if ( !fatal ) { - err = setFormat(); - if ( err < 0 ) { - fatal = true; - errMessage = QString::fromLatin1("QAudioSink: snd_pcm_hw_params_set_format: err = %1").arg(err); - } - } - if ( !fatal ) { - err = snd_pcm_hw_params_set_channels( handle, hwparams, (unsigned int)settings.channelCount() ); - if ( err < 0 ) { - fatal = true; - errMessage = QString::fromLatin1("QAudioSink: snd_pcm_hw_params_set_channels: err = %1").arg(err); - } - } - if ( !fatal ) { - err = snd_pcm_hw_params_set_rate_near( handle, hwparams, &sampleRate, 0 ); - if ( err < 0 ) { - fatal = true; - errMessage = QString::fromLatin1("QAudioSink: snd_pcm_hw_params_set_rate_near: err = %1").arg(err); - } - } - if ( !fatal ) { - unsigned int maxBufferTime = 0; - unsigned int minBufferTime = 0; - unsigned int maxPeriodTime = 0; - unsigned int minPeriodTime = 0; - - err = snd_pcm_hw_params_get_buffer_time_max(hwparams, &maxBufferTime, &dir); - if ( err >= 0) - err = snd_pcm_hw_params_get_buffer_time_min(hwparams, &minBufferTime, &dir); - if ( err >= 0) - err = snd_pcm_hw_params_get_period_time_max(hwparams, &maxPeriodTime, &dir); - if ( err >= 0) - err = snd_pcm_hw_params_get_period_time_min(hwparams, &minPeriodTime, &dir); - - if ( err < 0 ) { - fatal = true; - errMessage = QString::fromLatin1("QAudioSink: buffer/period min and max: err = %1").arg(err); - } else { - static unsigned user_buffer_time = qEnvironmentVariableIntValue("QT_ALSA_OUTPUT_BUFFER_TIME"); - static unsigned user_period_time = qEnvironmentVariableIntValue("QT_ALSA_OUTPUT_PERIOD_TIME"); - const bool outOfRange = maxBufferTime < buffer_time || buffer_time < minBufferTime || maxPeriodTime < period_time || minPeriodTime > period_time; - if (outOfRange || user_period_time || user_buffer_time) { - period_time = user_period_time ? user_period_time : minPeriodTime; - if (!user_buffer_time) { - chunks = maxBufferTime / period_time; - buffer_time = period_time * chunks; - } else { - buffer_time = user_buffer_time; - chunks = buffer_time / period_time; - } - } - qCDebug(lcAlsaOutput) << "buffer time: [" << minBufferTime << "-" << maxBufferTime << "] =" << buffer_time; - qCDebug(lcAlsaOutput) << "period time: [" << minPeriodTime << "-" << maxPeriodTime << "] =" << period_time; - qCDebug(lcAlsaOutput) << "chunks =" << chunks; - } - } - if ( !fatal ) { - err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir); - if ( err < 0 ) { - fatal = true; - errMessage = QString::fromLatin1("QAudioSink: snd_pcm_hw_params_set_buffer_time_near: err = %1").arg(err); - } - } - if ( !fatal ) { - err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, &dir); - if ( err < 0 ) { - fatal = true; - errMessage = QString::fromLatin1("QAudioSink: snd_pcm_hw_params_set_period_time_near: err = %1").arg(err); - } - } - if ( !fatal ) { - err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &chunks, &dir); - if ( err < 0 ) { - fatal = true; - errMessage = QString::fromLatin1("QAudioSink: snd_pcm_hw_params_set_periods_near: err = %1").arg(err); - } - } - if ( !fatal ) { - err = snd_pcm_hw_params(handle, hwparams); - if ( err < 0 ) { - fatal = true; - errMessage = QString::fromLatin1("QAudioSink: snd_pcm_hw_params: err = %1").arg(err); - } - } - if( err < 0) { - qWarning()<<errMessage; - errorState = QAudio::OpenError; - emit errorChanged(errorState); - deviceState = QAudio::StoppedState; - return false; - } - snd_pcm_hw_params_get_buffer_size(hwparams,&buffer_frames); - buffer_size = snd_pcm_frames_to_bytes(handle,buffer_frames); - snd_pcm_hw_params_get_period_size(hwparams,&period_frames, &dir); - period_size = snd_pcm_frames_to_bytes(handle,period_frames); - snd_pcm_hw_params_get_buffer_time(hwparams,&buffer_time, &dir); - snd_pcm_hw_params_get_period_time(hwparams,&period_time, &dir); - - // Step 3: Set the desired SW parameters. - snd_pcm_sw_params_t *swparams; - snd_pcm_sw_params_alloca(&swparams); - snd_pcm_sw_params_current(handle, swparams); - snd_pcm_sw_params_set_start_threshold(handle,swparams,period_frames); - snd_pcm_sw_params_set_stop_threshold(handle,swparams,buffer_frames); - snd_pcm_sw_params_set_avail_min(handle, swparams,period_frames); - snd_pcm_sw_params(handle, swparams); - - // Step 4: Prepare audio - if(audioBuffer == 0) - audioBuffer = new char[snd_pcm_frames_to_bytes(handle,buffer_frames)]; - snd_pcm_prepare( handle ); - snd_pcm_start(handle); - - // Step 5: Setup timer - bytesAvailable = bytesFree(); - - // Step 6: Start audio processing - timer->start(period_time/1000); - - timeStamp.restart(); - elapsedTimeOffset = 0; - errorState = QAudio::NoError; - totalTimeValue = 0; - opened = true; - - return true; -} - -void QAlsaAudioSink::close() -{ - timer->stop(); - - if ( handle ) { - snd_pcm_drain( handle ); - snd_pcm_close( handle ); - handle = 0; - delete [] audioBuffer; - audioBuffer=0; - } - if(!pullMode && audioSource) { - delete audioSource; - audioSource = 0; - } - opened = false; -} - -qsizetype QAlsaAudioSink::bytesFree() const -{ - if(resuming) - return period_size; - - if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState) - return 0; - - int frames = snd_pcm_avail_update(handle); - if (frames == -EPIPE) { - // Try and handle buffer underrun - int err = snd_pcm_recover(handle, frames, 0); - if (err < 0) - return 0; - else - frames = snd_pcm_avail_update(handle); - } else if (frames < 0) { - return 0; - } - - if ((int)frames > (int)buffer_frames) - frames = buffer_frames; - - return snd_pcm_frames_to_bytes(handle, frames); -} - -qint64 QAlsaAudioSink::write( const char *data, qint64 len ) -{ - // Write out some audio data - if ( !handle ) - return 0; -#ifdef DEBUG_AUDIO - qDebug()<<"frames to write out = "<< - snd_pcm_bytes_to_frames( handle, (int)len )<<" ("<<len<<") bytes"; -#endif - int frames, err; - int space = bytesFree(); - - if (!space) - return 0; - - if (len < space) - space = len; - - frames = snd_pcm_bytes_to_frames(handle, space); - - if (m_volume < 1.0f) { - QVarLengthArray<char, 4096> out(space); - QAudioHelperInternal::qMultiplySamples(m_volume, settings, data, out.data(), space); - err = snd_pcm_writei(handle, out.constData(), frames); - } else { - err = snd_pcm_writei(handle, data, frames); - } - - if(err > 0) { - totalTimeValue += err; - resuming = false; - errorState = QAudio::NoError; - if (deviceState != QAudio::ActiveState) { - deviceState = QAudio::ActiveState; - emit stateChanged(deviceState); - } - return snd_pcm_frames_to_bytes( handle, err ); - } else - err = xrun_recovery(err); - - if(err < 0) { - close(); - errorState = QAudio::FatalError; - emit errorChanged(errorState); - deviceState = QAudio::StoppedState; - emit stateChanged(deviceState); - } - return 0; -} - -void QAlsaAudioSink::setBufferSize(qsizetype value) -{ - if(deviceState == QAudio::StoppedState) - buffer_size = value; -} - -qsizetype QAlsaAudioSink::bufferSize() const -{ - return buffer_size; -} - -qint64 QAlsaAudioSink::processedUSecs() const -{ - return qint64(1000000) * totalTimeValue / settings.sampleRate(); -} - -void QAlsaAudioSink::resume() -{ - if(deviceState == QAudio::SuspendedState) { - int err = 0; - - if(handle) { - err = snd_pcm_prepare( handle ); - if(err < 0) - xrun_recovery(err); - - err = snd_pcm_start(handle); - if(err < 0) - xrun_recovery(err); - - bytesAvailable = (int)snd_pcm_frames_to_bytes(handle, buffer_frames); - } - resuming = true; - - deviceState = pullMode ? QAudio::ActiveState : QAudio::IdleState; - - errorState = QAudio::NoError; - timer->start(period_time/1000); - emit stateChanged(deviceState); - } -} - -void QAlsaAudioSink::setFormat(const QAudioFormat& fmt) -{ - if (deviceState == QAudio::StoppedState) - settings = fmt; -} - -QAudioFormat QAlsaAudioSink::format() const -{ - return settings; -} - -void QAlsaAudioSink::suspend() -{ - if(deviceState == QAudio::ActiveState || deviceState == QAudio::IdleState || resuming) { - snd_pcm_drain(handle); - timer->stop(); - deviceState = QAudio::SuspendedState; - errorState = QAudio::NoError; - emit stateChanged(deviceState); - } -} - -void QAlsaAudioSink::userFeed() -{ - if(deviceState == QAudio::StoppedState || deviceState == QAudio::SuspendedState) - return; -#ifdef DEBUG_AUDIO - QTime now(QTime::currentTime()); - qDebug()<<now.second()<<"s "<<now.msec()<<"ms :userFeed() OUT"; -#endif - if(deviceState == QAudio::IdleState) - bytesAvailable = bytesFree(); - - deviceReady(); -} - -bool QAlsaAudioSink::deviceReady() -{ - if(pullMode) { - int l = 0; - int chunks = bytesAvailable/period_size; - if(chunks==0) { - bytesAvailable = bytesFree(); - return false; - } -#ifdef DEBUG_AUDIO - qDebug()<<"deviceReady() avail="<<bytesAvailable<<" bytes, period size="<<period_size<<" bytes"; - qDebug()<<"deviceReady() no. of chunks that can fit ="<<chunks<<", chunks in bytes ="<<period_size*chunks; -#endif - int input = period_frames*chunks; - if(input > (int)buffer_frames) - input = buffer_frames; - l = audioSource->read(audioBuffer,snd_pcm_frames_to_bytes(handle, input)); - - // reading can take a while and stream may have been stopped - if (!handle) - return false; - - if(l > 0) { - // Got some data to output - if (deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState) - return true; - qint64 bytesWritten = write(audioBuffer,l); - if (bytesWritten != l) - audioSource->seek(audioSource->pos()-(l-bytesWritten)); - bytesAvailable = bytesFree(); - - } else if(l == 0) { - // Did not get any data to output - bytesAvailable = bytesFree(); - if(bytesAvailable > snd_pcm_frames_to_bytes(handle, buffer_frames-period_frames)) { - // Underrun - if (deviceState != QAudio::IdleState) { - errorState = QAudio::UnderrunError; - emit errorChanged(errorState); - deviceState = QAudio::IdleState; - emit stateChanged(deviceState); - } - } - - } else if(l < 0) { - close(); - deviceState = QAudio::StoppedState; - errorState = QAudio::IOError; - emit errorChanged(errorState); - emit stateChanged(deviceState); - } - } else { - bytesAvailable = bytesFree(); - if(bytesAvailable > snd_pcm_frames_to_bytes(handle, buffer_frames-period_frames)) { - // Underrun - if (deviceState != QAudio::IdleState) { - errorState = QAudio::UnderrunError; - emit errorChanged(errorState); - deviceState = QAudio::IdleState; - emit stateChanged(deviceState); - } - } - } - - if(deviceState != QAudio::ActiveState) - return true; - - return true; -} - -void QAlsaAudioSink::reset() -{ - if(handle) - snd_pcm_reset(handle); - - stop(); -} - -AlsaOutputPrivate::AlsaOutputPrivate(QAlsaAudioSink* audio) -{ - audioDevice = qobject_cast<QAlsaAudioSink*>(audio); -} - -AlsaOutputPrivate::~AlsaOutputPrivate() {} - -qint64 AlsaOutputPrivate::readData( char* data, qint64 len) -{ - Q_UNUSED(data); - Q_UNUSED(len); - - return 0; -} - -qint64 AlsaOutputPrivate::writeData(const char* data, qint64 len) -{ - int retry = 0; - qint64 written = 0; - if((audioDevice->deviceState == QAudio::ActiveState) - ||(audioDevice->deviceState == QAudio::IdleState)) { - while(written < len) { - int chunk = audioDevice->write(data+written,(len-written)); - if(chunk <= 0) - retry++; - written+=chunk; - if(retry > 10) - return written; - } - } - return written; - -} - -QT_END_NAMESPACE - -#include "moc_qalsaaudiosink_p.cpp" diff --git a/src/multimedia/platform/alsa/qalsaaudiosink_p.h b/src/multimedia/platform/alsa/qalsaaudiosink_p.h deleted file mode 100644 index 9c6da2557..000000000 --- a/src/multimedia/platform/alsa/qalsaaudiosink_p.h +++ /dev/null @@ -1,157 +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$ -** -****************************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of other Qt classes. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#ifndef QAUDIOOUTPUTALSA_H -#define QAUDIOOUTPUTALSA_H - -#include <alsa/asoundlib.h> - -#include <QtCore/qfile.h> -#include <QtCore/qdebug.h> -#include <QtCore/qtimer.h> -#include <QtCore/qstring.h> -#include <QtCore/qstringlist.h> -#include <QtCore/qelapsedtimer.h> -#include <QtCore/qiodevice.h> - -#include <QtMultimedia/qaudio.h> -#include <QtMultimedia/qaudiodevice.h> -#include <private/qaudiosystem_p.h> - -QT_BEGIN_NAMESPACE - -class QAlsaAudioSink : public QPlatformAudioSink -{ - friend class AlsaOutputPrivate; - Q_OBJECT -public: - QAlsaAudioSink(const QByteArray &device); - ~QAlsaAudioSink(); - - qint64 write( const char *data, qint64 len ); - - void start(QIODevice* device) override; - QIODevice* start() override; - void stop() override; - void reset() override; - void suspend() override; - void resume() override; - qsizetype bytesFree() const override; - void setBufferSize(qsizetype value) override; - qsizetype bufferSize() const override; - qint64 processedUSecs() const override; - QAudio::Error error() const override; - QAudio::State state() const override; - void setFormat(const QAudioFormat& fmt) override; - QAudioFormat format() const override; - void setVolume(qreal) override; - qreal volume() const override; - - - QIODevice* audioSource; - QAudioFormat settings; - QAudio::Error errorState; - QAudio::State deviceState; - -private slots: - void userFeed(); - bool deviceReady(); - -signals: - void processMore(); - -private: - bool opened; - bool pullMode; - bool resuming; - int buffer_size; - int period_size; - qint64 totalTimeValue; - unsigned int buffer_time; - unsigned int period_time; - snd_pcm_uframes_t buffer_frames; - snd_pcm_uframes_t period_frames; - int xrun_recovery(int err); - - int setFormat(); - bool open(); - void close(); - - QTimer* timer; - QByteArray m_device; - int bytesAvailable; - qint64 elapsedTimeOffset; - char* audioBuffer; - snd_pcm_t* handle; - snd_pcm_access_t access; - snd_pcm_format_t pcmformat; - snd_pcm_hw_params_t *hwparams; - qreal m_volume; -}; - -class AlsaOutputPrivate : public QIODevice -{ - friend class QAlsaAudioSink; - Q_OBJECT -public: - AlsaOutputPrivate(QAlsaAudioSink* audio); - ~AlsaOutputPrivate(); - - qint64 readData( char* data, qint64 len) override; - qint64 writeData(const char* data, qint64 len) override; - -private: - QAlsaAudioSink *audioDevice; -}; - -QT_END_NAMESPACE - - -#endif diff --git a/src/multimedia/platform/alsa/qalsaaudiosource.cpp b/src/multimedia/platform/alsa/qalsaaudiosource.cpp deleted file mode 100644 index 19c85542e..000000000 --- a/src/multimedia/platform/alsa/qalsaaudiosource.cpp +++ /dev/null @@ -1,805 +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$ -** -****************************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of other Qt classes. This header file may change from version to -// version without notice, or even be removed. -// -// INTERNAL USE ONLY: Do NOT use for any other purpose. -// - -#include <QtCore/qcoreapplication.h> -#include <QtCore/qvarlengtharray.h> -#include <QtMultimedia/private/qaudiohelpers_p.h> -#include "qalsaaudiosource_p.h" -#include "qalsaaudiodevice_p.h" - -QT_BEGIN_NAMESPACE - -//#define DEBUG_AUDIO 1 - -QAlsaAudioSource::QAlsaAudioSource(const QByteArray &device) -{ - bytesAvailable = 0; - handle = 0; - access = SND_PCM_ACCESS_RW_INTERLEAVED; - pcmformat = SND_PCM_FORMAT_S16; - buffer_size = 0; - period_size = 0; - buffer_time = 100000; - period_time = 20000; - totalTimeValue = 0; - errorState = QAudio::NoError; - deviceState = QAudio::StoppedState; - audioSource = 0; - pullMode = true; - resuming = false; - - m_volume = 1.0f; - - m_device = device; - - timer = new QTimer(this); - connect(timer,SIGNAL(timeout()),SLOT(userFeed())); -} - -QAlsaAudioSource::~QAlsaAudioSource() -{ - close(); - disconnect(timer, SIGNAL(timeout())); - QCoreApplication::processEvents(); - delete timer; -} - -void QAlsaAudioSource::setVolume(qreal vol) -{ - m_volume = vol; -} - -qreal QAlsaAudioSource::volume() const -{ - return m_volume; -} - -QAudio::Error QAlsaAudioSource::error() const -{ - return errorState; -} - -QAudio::State QAlsaAudioSource::state() const -{ - return deviceState; -} - -void QAlsaAudioSource::setFormat(const QAudioFormat& fmt) -{ - if (deviceState == QAudio::StoppedState) - settings = fmt; -} - -QAudioFormat QAlsaAudioSource::format() const -{ - return settings; -} - -int QAlsaAudioSource::xrun_recovery(int err) -{ - int count = 0; - bool reset = false; - - // ESTRPIPE is not available in all OSes where ALSA is available - int estrpipe = EIO; -#ifdef ESTRPIPE - estrpipe = ESTRPIPE; -#endif - - if(err == -EPIPE) { - errorState = QAudio::UnderrunError; - err = snd_pcm_prepare(handle); - if(err < 0) - reset = true; - else { - bytesAvailable = checkBytesReady(); - if (bytesAvailable <= 0) - reset = true; - } - } else if ((err == -estrpipe)||(err == -EIO)) { - errorState = QAudio::IOError; - while((err = snd_pcm_resume(handle)) == -EAGAIN){ - usleep(100); - count++; - if(count > 5) { - reset = true; - break; - } - } - if(err < 0) { - err = snd_pcm_prepare(handle); - if(err < 0) - reset = true; - } - } - if(reset) { - close(); - open(); - snd_pcm_prepare(handle); - return 0; - } - return err; -} - -int QAlsaAudioSource::setFormat() -{ - snd_pcm_format_t pcmformat = SND_PCM_FORMAT_UNKNOWN; - - switch (settings.sampleFormat()) { - case QAudioFormat::UInt8: - pcmformat = SND_PCM_FORMAT_U8; - break; - case QAudioFormat::Int16: - if constexpr (QSysInfo::ByteOrder == QSysInfo::BigEndian) - pcmformat = SND_PCM_FORMAT_S16_LE; - else - pcmformat = SND_PCM_FORMAT_S16_BE; - break; - case QAudioFormat::Int32: - if constexpr (QSysInfo::ByteOrder == QSysInfo::BigEndian) - pcmformat = SND_PCM_FORMAT_S32_LE; - else - pcmformat = SND_PCM_FORMAT_S32_BE; - break; - case QAudioFormat::Float: - if constexpr (QSysInfo::ByteOrder == QSysInfo::BigEndian) - pcmformat = SND_PCM_FORMAT_FLOAT_LE; - else - pcmformat = SND_PCM_FORMAT_FLOAT_BE; - default: - break; - } - - return pcmformat != SND_PCM_FORMAT_UNKNOWN - ? snd_pcm_hw_params_set_format( handle, hwparams, pcmformat) - : -1; -} - -void QAlsaAudioSource::start(QIODevice* device) -{ - if(deviceState != QAudio::StoppedState) - close(); - - if(!pullMode && audioSource) - delete audioSource; - - pullMode = true; - audioSource = device; - - deviceState = QAudio::ActiveState; - - if( !open() ) - return; - - emit stateChanged(deviceState); -} - -QIODevice* QAlsaAudioSource::start() -{ - if(deviceState != QAudio::StoppedState) - close(); - - if(!pullMode && audioSource) - delete audioSource; - - pullMode = false; - audioSource = new AlsaInputPrivate(this); - audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered); - - deviceState = QAudio::IdleState; - - if( !open() ) - return 0; - - emit stateChanged(deviceState); - - return audioSource; -} - -void QAlsaAudioSource::stop() -{ - if(deviceState == QAudio::StoppedState) - return; - - deviceState = QAudio::StoppedState; - - close(); - emit stateChanged(deviceState); -} - -bool QAlsaAudioSource::open() -{ -#ifdef DEBUG_AUDIO - QTime now(QTime::currentTime()); - qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; -#endif - elapsedTimeOffset = 0; - - int dir; - int err = 0; - int count=0; - unsigned int sampleRate=settings.sampleRate(); - - if (!settings.isValid()) { - qWarning("QAudioSource: open error, invalid format."); - } else if (settings.sampleRate() <= 0) { - qWarning("QAudioSource: open error, invalid sample rate (%d).", - settings.sampleRate()); - } else { - err = -1; - } - - if (err == 0) { - errorState = QAudio::OpenError; - deviceState = QAudio::StoppedState; - emit errorChanged(errorState); - return false; - } - - - // Step 1: try and open the device - while((count < 5) && (err < 0)) { - err = snd_pcm_open(&handle, m_device.constData(), SND_PCM_STREAM_CAPTURE,0); - if(err < 0) - count++; - } - if (( err < 0)||(handle == 0)) { - errorState = QAudio::OpenError; - deviceState = QAudio::StoppedState; - emit stateChanged(deviceState); - return false; - } - snd_pcm_nonblock( handle, 0 ); - - // Step 2: Set the desired HW parameters. - snd_pcm_hw_params_alloca( &hwparams ); - - bool fatal = false; - QString errMessage; - unsigned int chunks = 8; - - err = snd_pcm_hw_params_any( handle, hwparams ); - if ( err < 0 ) { - fatal = true; - errMessage = QString::fromLatin1("QAudioSource: snd_pcm_hw_params_any: err = %1").arg(err); - } - if ( !fatal ) { - err = snd_pcm_hw_params_set_rate_resample( handle, hwparams, 1 ); - if ( err < 0 ) { - fatal = true; - errMessage = QString::fromLatin1("QAudioSource: snd_pcm_hw_params_set_rate_resample: err = %1").arg(err); - } - } - if ( !fatal ) { - err = snd_pcm_hw_params_set_access( handle, hwparams, access ); - if ( err < 0 ) { - fatal = true; - errMessage = QString::fromLatin1("QAudioSource: snd_pcm_hw_params_set_access: err = %1").arg(err); - } - } - if ( !fatal ) { - err = setFormat(); - if ( err < 0 ) { - fatal = true; - errMessage = QString::fromLatin1("QAudioSource: snd_pcm_hw_params_set_format: err = %1").arg(err); - } - } - if ( !fatal ) { - err = snd_pcm_hw_params_set_channels( handle, hwparams, (unsigned int)settings.channelCount() ); - if ( err < 0 ) { - fatal = true; - errMessage = QString::fromLatin1("QAudioSource: snd_pcm_hw_params_set_channels: err = %1").arg(err); - } - } - if ( !fatal ) { - err = snd_pcm_hw_params_set_rate_near( handle, hwparams, &sampleRate, 0 ); - if ( err < 0 ) { - fatal = true; - errMessage = QString::fromLatin1("QAudioSource: snd_pcm_hw_params_set_rate_near: err = %1").arg(err); - } - } - if ( !fatal ) { - err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir); - if ( err < 0 ) { - fatal = true; - errMessage = QString::fromLatin1("QAudioSource: snd_pcm_hw_params_set_buffer_time_near: err = %1").arg(err); - } - } - if ( !fatal ) { - err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, &dir); - if ( err < 0 ) { - fatal = true; - errMessage = QString::fromLatin1("QAudioSource: snd_pcm_hw_params_set_period_time_near: err = %1").arg(err); - } - } - if ( !fatal ) { - err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &chunks, &dir); - if ( err < 0 ) { - fatal = true; - errMessage = QString::fromLatin1("QAudioSource: snd_pcm_hw_params_set_periods_near: err = %1").arg(err); - } - } - if ( !fatal ) { - err = snd_pcm_hw_params(handle, hwparams); - if ( err < 0 ) { - fatal = true; - errMessage = QString::fromLatin1("QAudioSource: snd_pcm_hw_params: err = %1").arg(err); - } - } - if( err < 0) { - qWarning()<<errMessage; - errorState = QAudio::OpenError; - deviceState = QAudio::StoppedState; - emit stateChanged(deviceState); - return false; - } - snd_pcm_hw_params_get_buffer_size(hwparams,&buffer_frames); - buffer_size = snd_pcm_frames_to_bytes(handle,buffer_frames); - snd_pcm_hw_params_get_period_size(hwparams,&period_frames, &dir); - period_size = snd_pcm_frames_to_bytes(handle,period_frames); - snd_pcm_hw_params_get_buffer_time(hwparams,&buffer_time, &dir); - snd_pcm_hw_params_get_period_time(hwparams,&period_time, &dir); - - // Step 3: Set the desired SW parameters. - snd_pcm_sw_params_t *swparams; - snd_pcm_sw_params_alloca(&swparams); - snd_pcm_sw_params_current(handle, swparams); - snd_pcm_sw_params_set_start_threshold(handle,swparams,period_frames); - snd_pcm_sw_params_set_stop_threshold(handle,swparams,buffer_frames); - snd_pcm_sw_params_set_avail_min(handle, swparams,period_frames); - snd_pcm_sw_params(handle, swparams); - - // Step 4: Prepare audio - ringBuffer.resize(buffer_size); - snd_pcm_prepare( handle ); - snd_pcm_start(handle); - - // Step 5: Setup timer - bytesAvailable = checkBytesReady(); - - if(pullMode) - connect(audioSource,SIGNAL(readyRead()),this,SLOT(userFeed())); - - // Step 6: Start audio processing - chunks = buffer_size/period_size; - timer->start(period_time*chunks/2000); - - errorState = QAudio::NoError; - - totalTimeValue = 0; - - return true; -} - -void QAlsaAudioSource::close() -{ - timer->stop(); - - if ( handle ) { - snd_pcm_drop( handle ); - snd_pcm_close( handle ); - handle = 0; - } -} - -int QAlsaAudioSource::checkBytesReady() -{ - if(resuming) - bytesAvailable = period_size; - else if(deviceState != QAudio::ActiveState - && deviceState != QAudio::IdleState) - bytesAvailable = 0; - else { - int frames = snd_pcm_avail_update(handle); - if (frames < 0) { - bytesAvailable = frames; - } else { - if((int)frames > (int)buffer_frames) - frames = buffer_frames; - bytesAvailable = snd_pcm_frames_to_bytes(handle, frames); - } - } - return bytesAvailable; -} - -int QAlsaAudioSource::bytesReady() const -{ - return qMax(bytesAvailable, 0); -} - -qint64 QAlsaAudioSource::read(char* data, qint64 len) -{ - // Read in some audio data and write it to QIODevice, pull mode - if ( !handle ) - return 0; - - int bytesRead = 0; - int bytesInRingbufferBeforeRead = ringBuffer.bytesOfDataInBuffer(); - - if (ringBuffer.bytesOfDataInBuffer() < len) { - - // bytesAvaiable is saved as a side effect of checkBytesReady(). - int bytesToRead = checkBytesReady(); - - if (bytesToRead < 0) { - // bytesAvailable as negative is error code, try to recover from it. - xrun_recovery(bytesToRead); - bytesToRead = checkBytesReady(); - if (bytesToRead < 0) { - // recovery failed must stop and set error. - close(); - errorState = QAudio::IOError; - deviceState = QAudio::StoppedState; - emit stateChanged(deviceState); - return 0; - } - } - - bytesToRead = qMin<qint64>(len, bytesToRead); - bytesToRead = qMin<qint64>(ringBuffer.freeBytes(), bytesToRead); - bytesToRead -= bytesToRead % period_size; - - int count=0; - int err = 0; - QVarLengthArray<char, 4096> buffer(bytesToRead); - while(count < 5 && bytesToRead > 0) { - int chunks = bytesToRead / period_size; - int frames = chunks * period_frames; - if (frames > (int)buffer_frames) - frames = buffer_frames; - - int readFrames = snd_pcm_readi(handle, buffer.data(), frames); - bytesRead = snd_pcm_frames_to_bytes(handle, readFrames); - if (m_volume < 1.0f) - QAudioHelperInternal::qMultiplySamples(m_volume, settings, - buffer.constData(), - buffer.data(), bytesRead); - - if (readFrames >= 0) { - ringBuffer.write(buffer.data(), bytesRead); -#ifdef DEBUG_AUDIO - qDebug() << QString::fromLatin1("read in bytes = %1 (frames=%2)").arg(bytesRead).arg(readFrames).toLatin1().constData(); -#endif - break; - } else if((readFrames == -EAGAIN) || (readFrames == -EINTR)) { - errorState = QAudio::IOError; - err = 0; - break; - } else { - if(readFrames == -EPIPE) { - errorState = QAudio::UnderrunError; - err = snd_pcm_prepare(handle); -#ifdef ESTRPIPE - } else if(readFrames == -ESTRPIPE) { - err = snd_pcm_prepare(handle); -#endif - } - if(err != 0) break; - } - count++; - } - - } - - bytesRead += bytesInRingbufferBeforeRead; - - if (bytesRead > 0) { - // got some send it onward -#ifdef DEBUG_AUDIO - qDebug() << "frames to write to QIODevice = " << - snd_pcm_bytes_to_frames( handle, (int)bytesRead ) << " (" << bytesRead << ") bytes"; -#endif - if (deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState) - return 0; - - if (pullMode) { - qint64 l = 0; - qint64 bytesWritten = 0; - while (ringBuffer.bytesOfDataInBuffer() > 0) { - l = audioSource->write(ringBuffer.availableData(), ringBuffer.availableDataBlockSize()); - if (l > 0) { - ringBuffer.readBytes(l); - bytesWritten += l; - } else { - break; - } - } - - if (l < 0) { - close(); - errorState = QAudio::IOError; - deviceState = QAudio::StoppedState; - emit stateChanged(deviceState); - } else if (l == 0 && bytesWritten == 0) { - if (deviceState != QAudio::IdleState) { - errorState = QAudio::NoError; - deviceState = QAudio::IdleState; - emit stateChanged(deviceState); - } - } else { - bytesAvailable -= bytesWritten; - totalTimeValue += bytesWritten; - resuming = false; - if (deviceState != QAudio::ActiveState) { - errorState = QAudio::NoError; - deviceState = QAudio::ActiveState; - emit stateChanged(deviceState); - } - } - - return bytesWritten; - } else { - while (ringBuffer.bytesOfDataInBuffer() > 0) { - int size = ringBuffer.availableDataBlockSize(); - memcpy(data, ringBuffer.availableData(), size); - data += size; - ringBuffer.readBytes(size); - } - - bytesAvailable -= bytesRead; - totalTimeValue += bytesRead; - resuming = false; - if (deviceState != QAudio::ActiveState) { - errorState = QAudio::NoError; - deviceState = QAudio::ActiveState; - emit stateChanged(deviceState); - } - - return bytesRead; - } - } - - return 0; -} - -void QAlsaAudioSource::resume() -{ - if(deviceState == QAudio::SuspendedState) { - int err = 0; - - if(handle) { - err = snd_pcm_prepare( handle ); - if(err < 0) - xrun_recovery(err); - - err = snd_pcm_start(handle); - if(err < 0) - xrun_recovery(err); - - bytesAvailable = buffer_size; - } - resuming = true; - deviceState = QAudio::ActiveState; - int chunks = buffer_size/period_size; - timer->start(period_time*chunks/2000); - emit stateChanged(deviceState); - } -} - -void QAlsaAudioSource::setBufferSize(int value) -{ - buffer_size = value; -} - -int QAlsaAudioSource::bufferSize() const -{ - return buffer_size; -} - -qint64 QAlsaAudioSource::processedUSecs() const -{ - qint64 result = qint64(1000000) * totalTimeValue / - settings.bytesPerFrame() / - settings.sampleRate(); - - return result; -} - -void QAlsaAudioSource::suspend() -{ - if(deviceState == QAudio::ActiveState||resuming) { - snd_pcm_drain(handle); - timer->stop(); - deviceState = QAudio::SuspendedState; - emit stateChanged(deviceState); - } -} - -void QAlsaAudioSource::userFeed() -{ - if(deviceState == QAudio::StoppedState || deviceState == QAudio::SuspendedState) - return; -#ifdef DEBUG_AUDIO - QTime now(QTime::currentTime()); - qDebug()<<now.second()<<"s "<<now.msec()<<"ms :userFeed() IN"; -#endif - deviceReady(); -} - -bool QAlsaAudioSource::deviceReady() -{ - if(pullMode) { - // reads some audio data and writes it to QIODevice - read(0, buffer_size); - } else { - // emits readyRead() so user will call read() on QIODevice to get some audio data - AlsaInputPrivate* a = qobject_cast<AlsaInputPrivate*>(audioSource); - a->trigger(); - } - bytesAvailable = checkBytesReady(); - - if(deviceState != QAudio::ActiveState) - return true; - - if (bytesAvailable < 0) { - // bytesAvailable as negative is error code, try to recover from it. - xrun_recovery(bytesAvailable); - bytesAvailable = checkBytesReady(); - if (bytesAvailable < 0) { - // recovery failed must stop and set error. - close(); - errorState = QAudio::IOError; - deviceState = QAudio::StoppedState; - emit stateChanged(deviceState); - return 0; - } - } - - return true; -} - -void QAlsaAudioSource::reset() -{ - if(handle) - snd_pcm_reset(handle); - stop(); - bytesAvailable = 0; -} - -void QAlsaAudioSource::drain() -{ - if(handle) - snd_pcm_drain(handle); -} - -AlsaInputPrivate::AlsaInputPrivate(QAlsaAudioSource* audio) -{ - audioDevice = qobject_cast<QAlsaAudioSource*>(audio); -} - -AlsaInputPrivate::~AlsaInputPrivate() -{ -} - -qint64 AlsaInputPrivate::readData( char* data, qint64 len) -{ - return audioDevice->read(data,len); -} - -qint64 AlsaInputPrivate::writeData(const char* data, qint64 len) -{ - Q_UNUSED(data); - Q_UNUSED(len); - return 0; -} - -void AlsaInputPrivate::trigger() -{ - emit readyRead(); -} - -RingBuffer::RingBuffer() : - m_head(0), - m_tail(0) -{ -} - -void RingBuffer::resize(int size) -{ - m_data.resize(size); -} - -int RingBuffer::bytesOfDataInBuffer() const -{ - if (m_head < m_tail) - return m_tail - m_head; - else if (m_tail < m_head) - return m_data.size() + m_tail - m_head; - else - return 0; -} - -int RingBuffer::freeBytes() const -{ - if (m_head > m_tail) - return m_head - m_tail - 1; - else if (m_tail > m_head) - return m_data.size() - m_tail + m_head - 1; - else - return m_data.size() - 1; -} - -const char *RingBuffer::availableData() const -{ - return (m_data.constData() + m_head); -} - -int RingBuffer::availableDataBlockSize() const -{ - if (m_head > m_tail) - return m_data.size() - m_head; - else if (m_tail > m_head) - return m_tail - m_head; - else - return 0; -} - -void RingBuffer::readBytes(int bytes) -{ - m_head = (m_head + bytes) % m_data.size(); -} - -void RingBuffer::write(char *data, int len) -{ - if (m_tail + len < m_data.size()) { - memcpy(m_data.data() + m_tail, data, len); - m_tail += len; - } else { - int bytesUntilEnd = m_data.size() - m_tail; - memcpy(m_data.data() + m_tail, data, bytesUntilEnd); - if (len - bytesUntilEnd > 0) - memcpy(m_data.data(), data + bytesUntilEnd, len - bytesUntilEnd); - m_tail = len - bytesUntilEnd; - } -} - -QT_END_NAMESPACE - -#include "moc_qalsaaudiosource_p.cpp" diff --git a/src/multimedia/platform/alsa/qalsaaudiosource_p.h b/src/multimedia/platform/alsa/qalsaaudiosource_p.h deleted file mode 100644 index 95df8008b..000000000 --- a/src/multimedia/platform/alsa/qalsaaudiosource_p.h +++ /dev/null @@ -1,178 +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$ -** -****************************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of other Qt classes. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - - -#ifndef QAUDIOINPUTALSA_H -#define QAUDIOINPUTALSA_H - -#include <alsa/asoundlib.h> - -#include <QtCore/qfile.h> -#include <QtCore/qdebug.h> -#include <QtCore/qtimer.h> -#include <QtCore/qstring.h> -#include <QtCore/qstringlist.h> -#include <QtCore/qelapsedtimer.h> -#include <QtCore/qiodevice.h> - -#include <QtMultimedia/qaudio.h> -#include <QtMultimedia/qaudiodevice.h> -#include <private/qaudiosystem_p.h> - -QT_BEGIN_NAMESPACE - - -class AlsaInputPrivate; - -class RingBuffer -{ -public: - RingBuffer(); - - void resize(int size); - - int bytesOfDataInBuffer() const; - int freeBytes() const; - - const char *availableData() const; - int availableDataBlockSize() const; - void readBytes(int bytes); - - void write(char *data, int len); - -private: - int m_head; - int m_tail; - - QByteArray m_data; -}; - -class QAlsaAudioSource : public QPlatformAudioSource -{ - Q_OBJECT -public: - QAlsaAudioSource(const QByteArray &device); - ~QAlsaAudioSource(); - - qint64 read(char* data, qint64 len); - - void start(QIODevice* device) override; - QIODevice* start() override; - void stop() override; - void reset() override; - void suspend() override; - void resume() override; - qsizetype bytesReady() const override; - void setBufferSize(qsizetype value) override; - qsizetype bufferSize() const override; - qint64 processedUSecs() const override; - QAudio::Error error() const override; - QAudio::State state() const override; - void setFormat(const QAudioFormat& fmt) override; - QAudioFormat format() const override; - void setVolume(qreal) override; - qreal volume() const override; - bool resuming; - snd_pcm_t* handle; - qint64 totalTimeValue; - QIODevice* audioSource; - QAudioFormat settings; - QAudio::Error errorState; - QAudio::State deviceState; - -private slots: - void userFeed(); - bool deviceReady(); - -private: - int checkBytesReady(); - int xrun_recovery(int err); - int setFormat(); - bool open(); - void close(); - void drain(); - - QTimer* timer; - qint64 elapsedTimeOffset; - RingBuffer ringBuffer; - int bytesAvailable; - QByteArray m_device; - bool pullMode; - int buffer_size; - int period_size; - unsigned int buffer_time; - unsigned int period_time; - snd_pcm_uframes_t buffer_frames; - snd_pcm_uframes_t period_frames; - snd_pcm_access_t access; - snd_pcm_format_t pcmformat; - snd_pcm_hw_params_t *hwparams; - qreal m_volume; -}; - -class AlsaInputPrivate : public QIODevice -{ - Q_OBJECT -public: - AlsaInputPrivate(QAlsaAudioSource* audio); - ~AlsaInputPrivate(); - - qint64 readData( char* data, qint64 len) override; - qint64 writeData(const char* data, qint64 len) override; - - void trigger(); -private: - QAlsaAudioSource *audioDevice; -}; - -QT_END_NAMESPACE - - -#endif diff --git a/src/multimedia/platform/alsa/qalsaintegration.cpp b/src/multimedia/platform/alsa/qalsaintegration.cpp deleted file mode 100644 index bfbc40c6a..000000000 --- a/src/multimedia/platform/alsa/qalsaintegration.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qalsaintegration_p.h" -#include "qalsamediadevices_p.h" - -QT_BEGIN_NAMESPACE - -QAlsaIntegration::QAlsaIntegration() -{ -} - -QAlsaIntegration::~QAlsaIntegration() -{ - delete m_devices; -} - -QPlatformMediaDevices *QAlsaIntegration::devices() -{ - if (!m_devices) - m_devices = new QAlsaMediaDevices(); - return m_devices; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/alsa/qalsaintegration_p.h b/src/multimedia/platform/alsa/qalsaintegration_p.h deleted file mode 100644 index a3b7315b3..000000000 --- a/src/multimedia/platform/alsa/qalsaintegration_p.h +++ /dev/null @@ -1,73 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QALSAINTEGRATION_H -#define QALSAINTEGRATION_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediaintegration_p.h> - -QT_BEGIN_NAMESPACE - -class QAlsaMediaDevices; - -class QAlsaIntegration : public QPlatformMediaIntegration -{ -public: - QAlsaIntegration(); - ~QAlsaIntegration(); - - QPlatformMediaDevices *devices() override; - - QAlsaMediaDevices *m_devices = nullptr; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/alsa/qalsamediadevices.cpp b/src/multimedia/platform/alsa/qalsamediadevices.cpp deleted file mode 100644 index e2561e66a..000000000 --- a/src/multimedia/platform/alsa/qalsamediadevices.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qalsamediadevices_p.h" -#include "qmediadevices.h" -#include "qcameradevice_p.h" - -#include "private/qalsaaudiosource_p.h" -#include "private/qalsaaudiosink_p.h" -#include "private/qalsaaudiodevice_p.h" - -#include <alsa/asoundlib.h> - -QT_BEGIN_NAMESPACE - -QAlsaMediaDevices::QAlsaMediaDevices() - : QPlatformMediaDevices() -{ -} - -static QList<QAudioDevice> availableDevices(QAudioDevice::Mode mode) -{ - QList<QAudioDevice> devices; - - QByteArray filter; - - // Create a list of all current audio devices that support mode - void **hints, **n; - char *name, *descr, *io; - - if(snd_device_name_hint(-1, "pcm", &hints) < 0) { - qWarning() << "no alsa devices available"; - return devices; - } - n = hints; - - if(mode == QAudioDevice::Input) { - filter = "Input"; - } else { - filter = "Output"; - } - - while (*n != NULL) { - name = snd_device_name_get_hint(*n, "NAME"); - if (name != 0 && qstrcmp(name, "null") != 0) { - descr = snd_device_name_get_hint(*n, "DESC"); - io = snd_device_name_get_hint(*n, "IOID"); - - if ((descr != NULL) && ((io == NULL) || (io == filter))) { - auto *infop = new QAlsaAudioDeviceInfo(name, QString::fromUtf8(descr), mode); - devices.append(infop->create()); - if (strcmp(name, "default") == 0) - infop->isDefault = true; - } - - free(descr); - free(io); - } - free(name); - ++n; - } - snd_device_name_free_hint(hints); - - return devices; -} - -QList<QAudioDevice> QAlsaMediaDevices::audioInputs() const -{ - return availableDevices(QAudioDevice::Input); -} - -QList<QAudioDevice> QAlsaMediaDevices::audioOutputs() const -{ - return availableDevices(QAudioDevice::Output); -} - -QList<QCameraDevice> QAlsaMediaDevices::videoInputs() const -{ - return {}; -} - -QPlatformAudioSource *QAlsaMediaDevices::createAudioSource(const QAudioDevice &deviceInfo) -{ - return new QAlsaAudioSource(deviceInfo.id()); -} - -QPlatformAudioSink *QAlsaMediaDevices::createAudioSink(const QAudioDevice &deviceInfo) -{ - return new QAlsaAudioSink(deviceInfo.id()); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/alsa/qalsamediadevices_p.h b/src/multimedia/platform/alsa/qalsamediadevices_p.h deleted file mode 100644 index 54df9c851..000000000 --- a/src/multimedia/platform/alsa/qalsamediadevices_p.h +++ /dev/null @@ -1,76 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QALSAMEDIADEVICES_H -#define QALSAMEDIADEVICES_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediadevices_p.h> -#include <qset.h> -#include <qaudio.h> - -QT_BEGIN_NAMESPACE - -class QAlsaEngine; - -class QAlsaMediaDevices : public QPlatformMediaDevices -{ -public: - QAlsaMediaDevices(); - - QList<QAudioDevice> audioInputs() const override; - QList<QAudioDevice> audioOutputs() const override; - QList<QCameraDevice> videoInputs() const override; - QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo) override; - QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo) override; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/android/audio/qandroidaudiodecoder.cpp b/src/multimedia/platform/android/audio/qandroidaudiodecoder.cpp deleted file mode 100644 index 718492118..000000000 --- a/src/multimedia/platform/android/audio/qandroidaudiodecoder.cpp +++ /dev/null @@ -1,436 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ -#include "qandroidaudiodecoder_p.h" - -#include <QtCore/qcoreapplication.h> -#include <QtCore/qjniobject.h> -#include <QtCore/qjnienvironment.h> -#include <QtCore/private/qandroidextras_p.h> -#include <QTimer> -#include <QFile> -#include <QDir> - -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> - -QT_BEGIN_NAMESPACE - -static const char tempFile[] = "encoded.tmp"; -static const char tempPath[] = "/storage/emulated/0/data/local/tmp/audiodecoder/"; -constexpr int dequeueTimeout = 5000; -Q_LOGGING_CATEGORY(adLogger, "QAndroidAudioDecoder") - -Decoder::Decoder() - : m_format(AMediaFormat_new()) -{} - -Decoder::~Decoder() -{ - if (m_codec) { - AMediaCodec_delete(m_codec); - m_codec = nullptr; - } - - if (m_extractor) { - AMediaExtractor_delete(m_extractor); - m_extractor = nullptr; - } - - if (m_format) { - AMediaFormat_delete(m_format); - m_format = nullptr; - } -} - -void Decoder::stop() -{ - const media_status_t err = AMediaCodec_stop(m_codec); - if (err != AMEDIA_OK) - qCWarning(adLogger) << "stop() error: " << err; -} - -void Decoder::setSource(const QUrl &source) -{ - if (!m_extractor) - m_extractor = AMediaExtractor_new(); - - int fd = -1; - if (source.path().contains(QLatin1String("content"))) { - fd = QJniObject::callStaticMethod<jint>("org/qtproject/qt/android/QtNative", - "openFdForContentUrl", - "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I", - QNativeInterface::QAndroidApplication::context(), - QJniObject::fromString(source.path()).object(), - QJniObject::fromString(QLatin1String("r")).object()); - } else { - fd = open(source.path().toStdString().c_str(), O_RDONLY); - } - - if (fd < 0) { - emit error(QAudioDecoder::ResourceError, tr("Invalid fileDescriptor for source.")); - return; - } - const int size = QFile(source.toString()).size(); - media_status_t status = AMediaExtractor_setDataSourceFd(m_extractor, fd, 0, - size > 0 ? size : LONG_MAX); - close(fd); - - if (status != AMEDIA_OK) { - if (m_extractor) { - AMediaExtractor_delete(m_extractor); - m_extractor = nullptr; - } - emit error(QAudioDecoder::ResourceError, tr("Setting source for Audio Decoder failed.")); - } -} - -void Decoder::createDecoder() -{ - // get encoded format for decoder - m_format = AMediaExtractor_getTrackFormat(m_extractor, 0); - - const char *mime; - if (!AMediaFormat_getString(m_format, AMEDIAFORMAT_KEY_MIME, &mime)) { - if (m_extractor) { - AMediaExtractor_delete(m_extractor); - m_extractor = nullptr; - } - emit error(QAudioDecoder::FormatError, tr("Format not supported by Audio Decoder.")); - - return; - } - - // get audio duration from source - int64_t durationUs; - AMediaFormat_getInt64(m_format, AMEDIAFORMAT_KEY_DURATION, &durationUs); - emit durationChanged(durationUs / 1000); - - // set default output audio format from input file - if (!m_outputFormat.isValid()) { - int32_t sampleRate; - AMediaFormat_getInt32(m_format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &sampleRate); - m_outputFormat.setSampleRate(sampleRate); - int32_t channelCount; - AMediaFormat_getInt32(m_format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &channelCount); - m_outputFormat.setChannelCount(channelCount); - m_outputFormat.setSampleFormat(QAudioFormat::Int16); - } - - m_codec = AMediaCodec_createDecoderByType(mime); -} - -void Decoder::doDecode() -{ - if (!m_extractor) { - emit error(QAudioDecoder::ResourceError, tr("Cannot decode, source not set.")); - return; - } - - createDecoder(); - - media_status_t status = AMediaCodec_configure(m_codec, m_format, nullptr /* surface */, - nullptr /* crypto */, 0); - - if (status != AMEDIA_OK) { - emit error(QAudioDecoder::ResourceError, tr("Audio Decoder failed configuration.")); - return; - } - - status = AMediaCodec_start(m_codec); - if (status != AMEDIA_OK) { - emit error(QAudioDecoder::ResourceError, tr("Audio Decoder failed to start.")); - return; - } - - AMediaExtractor_selectTrack(m_extractor, 0); - - m_inputEOS = false; - while (!m_inputEOS) { - // handle input buffer - const ssize_t bufferIdx = AMediaCodec_dequeueInputBuffer(m_codec, dequeueTimeout); - - if (bufferIdx >= 0) { - size_t bufferSize = {}; - uint8_t *buffer = AMediaCodec_getInputBuffer(m_codec, bufferIdx, &bufferSize); - const int sample = AMediaExtractor_readSampleData(m_extractor, buffer, bufferSize); - if (sample < 0) { - m_inputEOS = true; - break; - } - - const int64_t presentationTimeUs = AMediaExtractor_getSampleTime(m_extractor); - AMediaCodec_queueInputBuffer(m_codec, bufferIdx, 0, sample, presentationTimeUs, - m_inputEOS ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0); - AMediaExtractor_advance(m_extractor); - - // handle output buffer - AMediaCodecBufferInfo info; - ssize_t idx = AMediaCodec_dequeueOutputBuffer(m_codec, &info, dequeueTimeout); - if (idx >= 0) { - if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) - break; - - if (info.size > 0) { - size_t bufferSize; - const uint8_t *bufferData = AMediaCodec_getOutputBuffer(m_codec, idx, - &bufferSize); - const QByteArray data((const char*)(bufferData + info.offset), info.size); - auto audioBuffer = QAudioBuffer(data, m_outputFormat, presentationTimeUs); - if (presentationTimeUs > 0) - emit positionChanged(std::move(audioBuffer), presentationTimeUs / 1000); - AMediaCodec_releaseOutputBuffer(m_codec, idx, false); - } - } else { - // The outputIndex doubles as a status return if its value is < 0 - switch (idx) { - case AMEDIACODEC_INFO_TRY_AGAIN_LATER: - qCWarning(adLogger) << "dequeueOutputBuffer() status: try again later"; - break; - case AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED: - qCWarning(adLogger) << "dequeueOutputBuffer() status: output buffers changed"; - break; - case AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED: - m_format = AMediaCodec_getOutputFormat(m_codec); - qCWarning(adLogger) << "dequeueOutputBuffer() status: outputFormat changed"; - break; - } - } - } else { - qCWarning(adLogger) << "dequeueInputBuffer() status: invalid buffer idx " << bufferIdx; - } - } - - emit finished(); -} - -QAndroidAudioDecoder::QAndroidAudioDecoder(QAudioDecoder *parent) - : QPlatformAudioDecoder(parent), - m_decoder(new Decoder()) -{ - connect(m_decoder, &Decoder::positionChanged, this, &QAndroidAudioDecoder::positionChanged); - connect(m_decoder, &Decoder::durationChanged, this, &QAndroidAudioDecoder::durationChanged); - connect(m_decoder, &Decoder::error, this, &QAndroidAudioDecoder::error); - connect(m_decoder, &Decoder::finished, this, &QAndroidAudioDecoder::finished); -} - -QAndroidAudioDecoder::~QAndroidAudioDecoder() -{ - m_decoder->thread()->exit(); - m_decoder->deleteLater(); -} - -void QAndroidAudioDecoder::setSource(const QUrl &fileName) -{ - if (!requestPermissions()) - return; - - if (isDecoding()) - return; - - m_device = nullptr; - error(QAudioDecoder::NoError, QStringLiteral("")); - - if (m_source != fileName) { - m_source = fileName; - m_decoder->setSource(m_source); - sourceChanged(); - } -} - -void QAndroidAudioDecoder::setSourceDevice(QIODevice *device) -{ - if (isDecoding()) - return; - - m_source.clear(); - if (m_device != device) { - m_device = device; - - if (!requestPermissions()) - return; - - sourceChanged(); - } -} - -void QAndroidAudioDecoder::start() -{ - if (isDecoding()) - return; - - setIsDecoding(true); - m_position = -1; - - QThread *threadDecoder = new QThread(this); - m_decoder->moveToThread(threadDecoder); - threadDecoder->start(); - decode(); -} - -void QAndroidAudioDecoder::stop() -{ - if (!isDecoding()) - return; - - m_decoder->stop(); - - QMutexLocker locker(&m_buffersMutex); - m_position = -1; - m_audioBuffer.clear(); - locker.unlock(); - setIsDecoding(false); -} - -QAudioBuffer QAndroidAudioDecoder::read() -{ - QMutexLocker locker(&m_buffersMutex); - if (m_buffersAvailable && !m_audioBuffer.isEmpty()) { - --m_buffersAvailable; - return m_audioBuffer.takeFirst(); - } - - // no buffers available - return {}; -} - -bool QAndroidAudioDecoder::bufferAvailable() const -{ - QMutexLocker locker(&m_buffersMutex); - return m_buffersAvailable; -} - -qint64 QAndroidAudioDecoder::position() const -{ - QMutexLocker locker(&m_buffersMutex); - return m_position; -} - -qint64 QAndroidAudioDecoder::duration() const -{ - QMutexLocker locker(&m_buffersMutex); - return m_duration; -} - -void QAndroidAudioDecoder::positionChanged(QAudioBuffer audioBuffer, qint64 position) -{ - QMutexLocker locker(&m_buffersMutex); - m_audioBuffer.append(audioBuffer); - m_position = position; - m_buffersAvailable++; - locker.unlock(); - emit bufferReady(); - emit QPlatformAudioDecoder::positionChanged(position); -} - -void QAndroidAudioDecoder::durationChanged(qint64 duration) -{ - QMutexLocker locker(&m_buffersMutex); - m_duration = duration; - locker.unlock(); - emit QPlatformAudioDecoder::durationChanged(duration); -} - -void QAndroidAudioDecoder::error(const QAudioDecoder::Error err, const QString &errorString) -{ - emit QPlatformAudioDecoder::error(err, errorString); -} - -void QAndroidAudioDecoder::finished() -{ - stop(); - // remove temp file when decoding is finished - QFile(QString::fromUtf8(tempPath).append(QString::fromUtf8(tempFile))).remove(); - emit QPlatformAudioDecoder::finished(); -} - -bool QAndroidAudioDecoder::requestPermissions() -{ - const auto writeRes = QtAndroidPrivate::requestPermission(QtAndroidPrivate::Storage); - if (writeRes.result() == QtAndroidPrivate::Authorized) - return true; - - return false; -} - -void QAndroidAudioDecoder::decode() -{ - if (m_device) { - connect(m_device, &QIODevice::readyRead, this, &QAndroidAudioDecoder::readDevice); - if (m_device->bytesAvailable()) - readDevice(); - } else { - QTimer::singleShot(0, m_decoder, &Decoder::doDecode); - } -} - -bool QAndroidAudioDecoder::createTempFile() -{ - QFile file = QFile(QString::fromUtf8(tempPath).append(QString::fromUtf8(tempFile))); - if (!QDir().mkpath(QString::fromUtf8(tempPath)) || !file.open(QIODevice::WriteOnly)) { - emit error(QAudioDecoder::ResourceError, - QString::fromUtf8("Error while creating or opening tmp file")); - return false; - } - - QDataStream out; - out.setDevice(&file); - out << m_deviceBuffer; - file.close(); - - m_deviceBuffer.clear(); - m_decoder->setSource(file.fileName()); - - return true; -} - -void QAndroidAudioDecoder::readDevice() { - m_deviceBuffer.append(m_device->readAll()); - if (m_device->atEnd()) { - disconnect(m_device, &QIODevice::readyRead, this, &QAndroidAudioDecoder::readDevice); - if (!createTempFile()) { - m_deviceBuffer.clear(); - stop(); - return; - } - QTimer::singleShot(0, m_decoder, &Decoder::doDecode); - } -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/audio/qandroidaudiodecoder_p.h b/src/multimedia/platform/android/audio/qandroidaudiodecoder_p.h deleted file mode 100644 index d6e1bf32e..000000000 --- a/src/multimedia/platform/android/audio/qandroidaudiodecoder_p.h +++ /dev/null @@ -1,151 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QANDROIDAUDIODECODER_P_H -#define QANDROIDAUDIODECODER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// -#include "private/qplatformaudiodecoder_p.h" - -#include <QtCore/qurl.h> -#include <QtCore/qmutex.h> -#include <QThread> - -#include "media/NdkMediaCodec.h" -#include "media/NdkMediaExtractor.h" -#include "media/NdkMediaFormat.h" -#include "media/NdkMediaError.h" - - -QT_USE_NAMESPACE - -class Decoder : public QObject -{ - Q_OBJECT -public: - Decoder(); - ~Decoder(); - -public slots: - void setSource(const QUrl &source); - void doDecode(); - void stop(); - -signals: - void positionChanged(const QAudioBuffer &buffer, qint64 position); - void durationChanged(const qint64 duration); - void error(const QAudioDecoder::Error error, const QString &errorString); - void finished(); - -private: - void createDecoder(); - - AMediaCodec *m_codec = nullptr; - AMediaExtractor *m_extractor = nullptr; - AMediaFormat *m_format = nullptr; - - QAudioFormat m_outputFormat; - bool m_inputEOS; -}; - - -class QAndroidAudioDecoder : public QPlatformAudioDecoder -{ - Q_OBJECT -public: - QAndroidAudioDecoder(QAudioDecoder *parent); - virtual ~QAndroidAudioDecoder(); - - QUrl source() const override { return m_source; } - void setSource(const QUrl &fileName) override; - - QIODevice *sourceDevice() const override { return m_device; } - void setSourceDevice(QIODevice *device) override; - - void start() override; - void stop() override; - - QAudioFormat audioFormat() const override { return {}; } - void setAudioFormat(const QAudioFormat &/*format*/) override {} - - QAudioBuffer read() override; - bool bufferAvailable() const override; - - qint64 position() const override; - qint64 duration() const override; - -private slots: - void positionChanged(QAudioBuffer audioBuffer, qint64 position); - void durationChanged(qint64 duration); - void error(const QAudioDecoder::Error error, const QString &errorString); - void readDevice(); - void finished(); - -private: - bool requestPermissions(); - bool createTempFile(); - void decode(); - - QIODevice *m_device = nullptr; - Decoder *m_decoder; - - QList<QAudioBuffer> m_audioBuffer; - QUrl m_source; - - mutable QMutex m_buffersMutex; - qint64 m_position = -1; - qint64 m_duration = -1; - long long m_presentationTimeUs = 0; - int m_buffersAvailable = 0; - - QByteArray m_deviceBuffer; -}; - -QT_END_NAMESPACE - -#endif // QANDROIDAUDIODECODER_P_H diff --git a/src/multimedia/platform/android/audio/qandroidaudiodevice.cpp b/src/multimedia/platform/android/audio/qandroidaudiodevice.cpp deleted file mode 100644 index fcd979a5a..000000000 --- a/src/multimedia/platform/android/audio/qandroidaudiodevice.cpp +++ /dev/null @@ -1,75 +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$ -** -****************************************************************************/ - -#include "qandroidaudiodevice_p.h" - -#include "qopenslesengine_p.h" - -QT_BEGIN_NAMESPACE - -QOpenSLESDeviceInfo::QOpenSLESDeviceInfo(const QByteArray &device, const QString &desc, QAudioDevice::Mode mode) - : QAudioDevicePrivate(device, mode), - m_engine(QOpenSLESEngine::instance()) -{ - description = desc; - - auto channels = m_engine->supportedChannelCounts(mode); - if (channels.size()) { - minimumChannelCount = channels.first(); - maximumChannelCount = channels.last(); - } - - auto sampleRates = m_engine->supportedSampleRates(mode); - if (sampleRates.size()) { - minimumSampleRate = sampleRates.first(); - maximumSampleRate = sampleRates.last(); - } - if (mode == QAudioDevice::Input) - supportedSampleFormats.append(QAudioFormat::UInt8); - supportedSampleFormats.append(QAudioFormat::Int16); - - preferredFormat.setChannelCount(2); - preferredFormat.setSampleRate(48000); - QAudioFormat::SampleFormat f = QAudioFormat::Int16; - if (!supportedSampleFormats.contains(f)) - f = supportedSampleFormats.value(0, QAudioFormat::Unknown); - preferredFormat.setSampleFormat(f); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/audio/qandroidaudiodevice_p.h b/src/multimedia/platform/android/audio/qandroidaudiodevice_p.h deleted file mode 100644 index 81b85c9fd..000000000 --- a/src/multimedia/platform/android/audio/qandroidaudiodevice_p.h +++ /dev/null @@ -1,73 +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$ -** -****************************************************************************/ - -#ifndef QOPENSLESDEVICEINFO_H -#define QOPENSLESDEVICEINFO_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qaudiosystem_p.h> -#include <private/qaudiodevice_p.h> - -QT_BEGIN_NAMESPACE - -class QOpenSLESEngine; - -class QOpenSLESDeviceInfo : public QAudioDevicePrivate -{ -public: - QOpenSLESDeviceInfo(const QByteArray &device, const QString &desc, QAudioDevice::Mode mode); - ~QOpenSLESDeviceInfo() {} - -private: - QOpenSLESEngine *m_engine; -}; - -QT_END_NAMESPACE - -#endif // QOPENSLESDEVICEINFO_H diff --git a/src/multimedia/platform/android/audio/qandroidaudiosink.cpp b/src/multimedia/platform/android/audio/qandroidaudiosink.cpp deleted file mode 100644 index 1a0b622a3..000000000 --- a/src/multimedia/platform/android/audio/qandroidaudiosink.cpp +++ /dev/null @@ -1,636 +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$ -** -****************************************************************************/ - -#include "qandroidaudiosink_p.h" -#include "qopenslesengine_p.h" -#include <QDebug> -#include <qmath.h> -#include <qmediadevices.h> - -#ifdef ANDROID -#include <SLES/OpenSLES_Android.h> -#include <SLES/OpenSLES_AndroidConfiguration.h> -#endif // ANDROID - -#define BUFFER_COUNT 2 - -QT_BEGIN_NAMESPACE - -static inline void openSlDebugInfo() -{ - const QAudioFormat &format = QMediaDevices::defaultAudioOutput().preferredFormat(); - qDebug() << "======= OpenSL ES Device info =======" - << "\nSupports low-latency playback: " << (QOpenSLESEngine::supportsLowLatency() ? "YES" : "NO") - << "\nPreferred sample rate: " << QOpenSLESEngine::getOutputValue(QOpenSLESEngine::SampleRate, -1) - << "\nFrames per buffer: " << QOpenSLESEngine::getOutputValue(QOpenSLESEngine::FramesPerBuffer, -1) - << "\nPreferred Format: " << format - << "\nLow-latency buffer size: " << QOpenSLESEngine::getLowLatencyBufferSize(format) - << "\nDefault buffer size: " << QOpenSLESEngine::getDefaultBufferSize(format); -} - -QAndroidAudioSink::QAndroidAudioSink(const QByteArray &device) - : m_deviceName(device), - m_state(QAudio::StoppedState), - m_error(QAudio::NoError), - m_outputMixObject(nullptr), - m_playerObject(nullptr), - m_playItf(nullptr), - m_volumeItf(nullptr), - m_bufferQueueItf(nullptr), - m_audioSource(nullptr), - m_buffers(nullptr), - m_volume(1.0), - m_pullMode(false), - m_nextBuffer(0), - m_bufferSize(0), - m_elapsedTime(0), - m_processedBytes(0), - m_availableBuffers(BUFFER_COUNT), - m_eventMask(SL_PLAYEVENT_HEADATEND), - m_startRequiresInit(true) -{ -#ifndef ANDROID - m_streamType = -1; -#else - m_streamType = SL_ANDROID_STREAM_MEDIA; -#endif // ANDROID -} - -QAndroidAudioSink::~QAndroidAudioSink() -{ - destroyPlayer(); -} - -QAudio::Error QAndroidAudioSink::error() const -{ - return m_error; -} - -QAudio::State QAndroidAudioSink::state() const -{ - return m_state; -} - -void QAndroidAudioSink::start(QIODevice *device) -{ - Q_ASSERT(device); - - if (m_state != QAudio::StoppedState) - stop(); - - if (!preparePlayer()) - return; - - m_pullMode = true; - m_audioSource = device; - m_nextBuffer = 0; - m_processedBytes = 0; - m_availableBuffers = BUFFER_COUNT; - setState(QAudio::ActiveState); - setError(QAudio::NoError); - - // Attempt to fill buffers first. - for (int i = 0; i != BUFFER_COUNT; ++i) { - const int index = i * m_bufferSize; - const qint64 readSize = m_audioSource->read(m_buffers + index, m_bufferSize); - if (readSize && SL_RESULT_SUCCESS != (*m_bufferQueueItf)->Enqueue(m_bufferQueueItf, - m_buffers + index, - readSize)) { - setError(QAudio::FatalError); - destroyPlayer(); - return; - } - m_processedBytes += readSize; - } - - if (m_processedBytes < 1) - onEOSEvent(); - - // Change the state to playing. - // We need to do this after filling the buffers or processedBytes might get corrupted. - startPlayer(); -} - -QIODevice *QAndroidAudioSink::start() -{ - if (m_state != QAudio::StoppedState) - stop(); - - if (!preparePlayer()) - return nullptr; - - m_pullMode = false; - m_processedBytes = 0; - m_availableBuffers = BUFFER_COUNT; - m_audioSource = new SLIODevicePrivate(this); - m_audioSource->open(QIODevice::WriteOnly | QIODevice::Unbuffered); - - // Change the state to playing - startPlayer(); - - setState(QAudio::IdleState); - return m_audioSource; -} - -void QAndroidAudioSink::stop() -{ - if (m_state == QAudio::StoppedState) - return; - - stopPlayer(); - setError(QAudio::NoError); -} - -qsizetype QAndroidAudioSink::bytesFree() const -{ - if (m_state != QAudio::ActiveState && m_state != QAudio::IdleState) - return 0; - - return m_availableBuffers.loadAcquire() ? m_bufferSize : 0; -} - -void QAndroidAudioSink::setBufferSize(qsizetype value) -{ - if (m_state != QAudio::StoppedState) - return; - - m_startRequiresInit = true; - m_bufferSize = value; -} - -qsizetype QAndroidAudioSink::bufferSize() const -{ - return m_bufferSize; -} - -qint64 QAndroidAudioSink::processedUSecs() const -{ - if (m_state == QAudio::IdleState || m_state == QAudio::SuspendedState) - return m_format.durationForBytes(m_processedBytes); - - SLmillisecond processMSec = 0; - if (m_playItf) - (*m_playItf)->GetPosition(m_playItf, &processMSec); - - return processMSec * 1000; -} - -void QAndroidAudioSink::resume() -{ - if (m_state != QAudio::SuspendedState) - return; - - if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING)) { - setError(QAudio::FatalError); - destroyPlayer(); - return; - } - - setState(m_pullMode ? QAudio::ActiveState : QAudio::IdleState); - setError(QAudio::NoError); -} - -void QAndroidAudioSink::setFormat(const QAudioFormat &format) -{ - m_startRequiresInit = true; - m_format = format; -} - -QAudioFormat QAndroidAudioSink::format() const -{ - return m_format; -} - -void QAndroidAudioSink::suspend() -{ - if (m_state != QAudio::ActiveState && m_state != QAudio::IdleState) - return; - - if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PAUSED)) { - setError(QAudio::FatalError); - destroyPlayer(); - return; - } - - setState(QAudio::SuspendedState); - setError(QAudio::NoError); -} - -void QAndroidAudioSink::reset() -{ - destroyPlayer(); -} - -void QAndroidAudioSink::setVolume(qreal vol) -{ - m_volume = qBound(qreal(0.0), vol, qreal(1.0)); - const SLmillibel newVolume = adjustVolume(m_volume); - if (m_volumeItf && SL_RESULT_SUCCESS != (*m_volumeItf)->SetVolumeLevel(m_volumeItf, newVolume)) - qWarning() << "Unable to change volume"; -} - -qreal QAndroidAudioSink::volume() const -{ - return m_volume; -} - -void QAndroidAudioSink::onEOSEvent() -{ - if (m_state != QAudio::ActiveState) - return; - - SLBufferQueueState state; - if (SL_RESULT_SUCCESS != (*m_bufferQueueItf)->GetState(m_bufferQueueItf, &state)) - return; - - if (state.count > 0) - return; - - setState(QAudio::IdleState); - setError(QAudio::UnderrunError); -} - -void QAndroidAudioSink::onBytesProcessed(qint64 bytes) -{ - m_processedBytes += bytes; -} - -void QAndroidAudioSink::bufferAvailable(quint32 count, quint32 playIndex) -{ - Q_UNUSED(count); - Q_UNUSED(playIndex); - - if (m_state == QAudio::StoppedState) - return; - - if (!m_pullMode) { // We're in push mode. - // Signal that there is a new open slot in the buffer and return - const int val = m_availableBuffers.fetchAndAddRelease(1) + 1; - if (val == BUFFER_COUNT) - QMetaObject::invokeMethod(this, "onEOSEvent", Qt::QueuedConnection); - - return; - } - - // We're in pull mode. - const int index = m_nextBuffer * m_bufferSize; - const qint64 readSize = m_audioSource->read(m_buffers + index, m_bufferSize); - - if (readSize < 1) { - QMetaObject::invokeMethod(this, "onEOSEvent", Qt::QueuedConnection); - return; - } - - - if (SL_RESULT_SUCCESS != (*m_bufferQueueItf)->Enqueue(m_bufferQueueItf, - m_buffers + index, - readSize)) { - setError(QAudio::FatalError); - destroyPlayer(); - return; - } - - m_nextBuffer = (m_nextBuffer + 1) % BUFFER_COUNT; - QMetaObject::invokeMethod(this, "onBytesProcessed", Qt::QueuedConnection, Q_ARG(qint64, readSize)); -} - -void QAndroidAudioSink::playCallback(SLPlayItf player, void *ctx, SLuint32 event) -{ - Q_UNUSED(player); - QAndroidAudioSink *audioOutput = reinterpret_cast<QAndroidAudioSink *>(ctx); - if (event & SL_PLAYEVENT_HEADATEND) - QMetaObject::invokeMethod(audioOutput, "onEOSEvent", Qt::QueuedConnection); -} - -void QAndroidAudioSink::bufferQueueCallback(SLBufferQueueItf bufferQueue, void *ctx) -{ - SLBufferQueueState state; - (*bufferQueue)->GetState(bufferQueue, &state); - QAndroidAudioSink *audioOutput = reinterpret_cast<QAndroidAudioSink *>(ctx); - audioOutput->bufferAvailable(state.count, state.playIndex); -} - -bool QAndroidAudioSink::preparePlayer() -{ - if (m_startRequiresInit) - destroyPlayer(); - else - return true; - - SLEngineItf engine = QOpenSLESEngine::instance()->slEngine(); - if (!engine) { - qWarning() << "No engine"; - setError(QAudio::FatalError); - return false; - } - - SLDataLocator_BufferQueue bufferQueueLocator = { SL_DATALOCATOR_BUFFERQUEUE, BUFFER_COUNT }; - SLDataFormat_PCM pcmFormat = QOpenSLESEngine::audioFormatToSLFormatPCM(m_format); - - SLDataSource audioSrc = { &bufferQueueLocator, &pcmFormat }; - - // OutputMix - if (SL_RESULT_SUCCESS != (*engine)->CreateOutputMix(engine, - &m_outputMixObject, - 0, - nullptr, - nullptr)) { - qWarning() << "Unable to create output mix"; - setError(QAudio::FatalError); - return false; - } - - if (SL_RESULT_SUCCESS != (*m_outputMixObject)->Realize(m_outputMixObject, SL_BOOLEAN_FALSE)) { - qWarning() << "Unable to initialize output mix"; - setError(QAudio::FatalError); - return false; - } - - SLDataLocator_OutputMix outputMixLocator = { SL_DATALOCATOR_OUTPUTMIX, m_outputMixObject }; - SLDataSink audioSink = { &outputMixLocator, nullptr }; - -#ifndef ANDROID - const int iids = 2; - const SLInterfaceID ids[iids] = { SL_IID_BUFFERQUEUE, SL_IID_VOLUME }; - const SLboolean req[iids] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE }; -#else - const int iids = 3; - const SLInterfaceID ids[iids] = { SL_IID_BUFFERQUEUE, - SL_IID_VOLUME, - SL_IID_ANDROIDCONFIGURATION }; - const SLboolean req[iids] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE }; -#endif // ANDROID - - // AudioPlayer - if (SL_RESULT_SUCCESS != (*engine)->CreateAudioPlayer(engine, - &m_playerObject, - &audioSrc, - &audioSink, - iids, - ids, - req)) { - qWarning() << "Unable to create AudioPlayer"; - setError(QAudio::OpenError); - return false; - } - -#ifdef ANDROID - // Set profile/category - SLAndroidConfigurationItf playerConfig; - if (SL_RESULT_SUCCESS == (*m_playerObject)->GetInterface(m_playerObject, - SL_IID_ANDROIDCONFIGURATION, - &playerConfig)) { - (*playerConfig)->SetConfiguration(playerConfig, - SL_ANDROID_KEY_STREAM_TYPE, - &m_streamType, - sizeof(SLint32)); - } -#endif // ANDROID - - if (SL_RESULT_SUCCESS != (*m_playerObject)->Realize(m_playerObject, SL_BOOLEAN_FALSE)) { - qWarning() << "Unable to initialize AudioPlayer"; - setError(QAudio::OpenError); - return false; - } - - // Buffer interface - if (SL_RESULT_SUCCESS != (*m_playerObject)->GetInterface(m_playerObject, - SL_IID_BUFFERQUEUE, - &m_bufferQueueItf)) { - setError(QAudio::FatalError); - return false; - } - - if (SL_RESULT_SUCCESS != (*m_bufferQueueItf)->RegisterCallback(m_bufferQueueItf, - bufferQueueCallback, - this)) { - setError(QAudio::FatalError); - return false; - } - - // Play interface - if (SL_RESULT_SUCCESS != (*m_playerObject)->GetInterface(m_playerObject, - SL_IID_PLAY, - &m_playItf)) { - setError(QAudio::FatalError); - return false; - } - - if (SL_RESULT_SUCCESS != (*m_playItf)->RegisterCallback(m_playItf, playCallback, this)) { - setError(QAudio::FatalError); - return false; - } - - if (SL_RESULT_SUCCESS != (*m_playItf)->SetCallbackEventsMask(m_playItf, m_eventMask)) { - setError(QAudio::FatalError); - return false; - } - - // Volume interface - if (SL_RESULT_SUCCESS != (*m_playerObject)->GetInterface(m_playerObject, - SL_IID_VOLUME, - &m_volumeItf)) { - setError(QAudio::FatalError); - return false; - } - - setVolume(m_volume); - - const int lowLatencyBufferSize = QOpenSLESEngine::getLowLatencyBufferSize(m_format); - const int defaultBufferSize = QOpenSLESEngine::getDefaultBufferSize(m_format); - - if (defaultBufferSize <= 0) { - qWarning() << "Unable to get minimum buffer size, returned" << defaultBufferSize; - setError(QAudio::FatalError); - return false; - } - - // Buffer size - if (m_bufferSize <= 0) { - m_bufferSize = defaultBufferSize; - } else if (QOpenSLESEngine::supportsLowLatency()) { - if (m_bufferSize < lowLatencyBufferSize) - m_bufferSize = lowLatencyBufferSize; - } else if (m_bufferSize < defaultBufferSize) { - m_bufferSize = defaultBufferSize; - } - - if (!m_buffers) - m_buffers = new char[BUFFER_COUNT * m_bufferSize]; - - setError(QAudio::NoError); - m_startRequiresInit = false; - - return true; -} - -void QAndroidAudioSink::destroyPlayer() -{ - if (m_state != QAudio::StoppedState) - stopPlayer(); - - if (m_playerObject) { - (*m_playerObject)->Destroy(m_playerObject); - m_playerObject = nullptr; - } - - if (m_outputMixObject) { - (*m_outputMixObject)->Destroy(m_outputMixObject); - m_outputMixObject = nullptr; - } - - if (!m_pullMode && m_audioSource) { - m_audioSource->close(); - delete m_audioSource; - m_audioSource = nullptr; - } - - delete [] m_buffers; - m_buffers = nullptr; - m_processedBytes = 0; - m_nextBuffer = 0; - m_availableBuffers.storeRelease(BUFFER_COUNT); - m_playItf = nullptr; - m_volumeItf = nullptr; - m_bufferQueueItf = nullptr; - m_startRequiresInit = true; -} - -void QAndroidAudioSink::stopPlayer() -{ - setState(QAudio::StoppedState); - - if (m_audioSource && !m_pullMode) { - m_audioSource->close(); - delete m_audioSource; - m_audioSource = nullptr; - } - - // We need to change the state manually... - if (m_playItf) - (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_STOPPED); - - if (m_bufferQueueItf && SL_RESULT_SUCCESS != (*m_bufferQueueItf)->Clear(m_bufferQueueItf)) - qWarning() << "Unable to clear buffer"; -} - -void QAndroidAudioSink::startPlayer() -{ - if (QOpenSLESEngine::printDebugInfo()) - openSlDebugInfo(); - - if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING)) { - setError(QAudio::FatalError); - destroyPlayer(); - } -} - -qint64 QAndroidAudioSink::writeData(const char *data, qint64 len) -{ - if (!len) - return 0; - - if (len > m_bufferSize) - len = m_bufferSize; - - // Acquire one slot in the buffer - const int before = m_availableBuffers.fetchAndAddAcquire(-1); - - // If there where no vacant slots, then we just overdrew the buffer account... - if (before < 1) { - m_availableBuffers.fetchAndAddRelease(1); - return 0; - } - - const int index = m_nextBuffer * m_bufferSize; - ::memcpy(m_buffers + index, data, len); - const SLuint32 res = (*m_bufferQueueItf)->Enqueue(m_bufferQueueItf, - m_buffers + index, - len); - - // If we where unable to enqueue a new buffer, give back the acquired slot. - if (res == SL_RESULT_BUFFER_INSUFFICIENT) { - m_availableBuffers.fetchAndAddRelease(1); - return 0; - } - - if (res != SL_RESULT_SUCCESS) { - setError(QAudio::FatalError); - destroyPlayer(); - return -1; - } - - m_processedBytes += len; - setState(QAudio::ActiveState); - setError(QAudio::NoError); - m_nextBuffer = (m_nextBuffer + 1) % BUFFER_COUNT; - - return len; -} - -inline void QAndroidAudioSink::setState(QAudio::State state) -{ - if (m_state == state) - return; - - m_state = state; - Q_EMIT stateChanged(m_state); -} - -inline void QAndroidAudioSink::setError(QAudio::Error error) -{ - if (m_error == error) - return; - - m_error = error; - Q_EMIT errorChanged(m_error); -} - -inline SLmillibel QAndroidAudioSink::adjustVolume(qreal vol) -{ - if (qFuzzyIsNull(vol)) - return SL_MILLIBEL_MIN; - - if (qFuzzyCompare(vol, qreal(1.0))) - return 0; - - return QAudio::convertVolume(vol, QAudio::LinearVolumeScale, QAudio::DecibelVolumeScale) * 100; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/audio/qandroidaudiosink_p.h b/src/multimedia/platform/android/audio/qandroidaudiosink_p.h deleted file mode 100644 index 40b964dc2..000000000 --- a/src/multimedia/platform/android/audio/qandroidaudiosink_p.h +++ /dev/null @@ -1,158 +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$ -** -****************************************************************************/ - -#ifndef QOPENSLESAUDIOOUTPUT_H -#define QOPENSLESAUDIOOUTPUT_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qaudiosystem_p.h> -#include <SLES/OpenSLES.h> -#include <qbytearray.h> -#include <qmap.h> -#include <QElapsedTimer> -#include <QIODevice> - -QT_BEGIN_NAMESPACE - -class QAndroidAudioSink : public QPlatformAudioSink -{ - Q_OBJECT - -public: - QAndroidAudioSink(const QByteArray &device); - ~QAndroidAudioSink(); - - void start(QIODevice *device) override; - QIODevice *start() override; - void stop() override; - void reset() override; - void suspend() override; - void resume() override; - qsizetype bytesFree() const override; - void setBufferSize(qsizetype value) override; - qsizetype bufferSize() const override; - qint64 processedUSecs() const override; - QAudio::Error error() const override; - QAudio::State state() const override; - void setFormat(const QAudioFormat &format) override; - QAudioFormat format() const override; - - void setVolume(qreal volume) override; - qreal volume() const override; - -private: - friend class SLIODevicePrivate; - - Q_INVOKABLE void onEOSEvent(); - Q_INVOKABLE void onBytesProcessed(qint64 bytes); - void bufferAvailable(quint32 count, quint32 playIndex); - - static void playCallback(SLPlayItf playItf, void *ctx, SLuint32 event); - static void bufferQueueCallback(SLBufferQueueItf bufferQueue, void *ctx); - - bool preparePlayer(); - void destroyPlayer(); - void stopPlayer(); - void startPlayer(); - qint64 writeData(const char *data, qint64 len); - - void setState(QAudio::State state); - void setError(QAudio::Error error); - - SLmillibel adjustVolume(qreal vol); - - QByteArray m_deviceName; - QAudio::State m_state; - QAudio::Error m_error; - SLObjectItf m_outputMixObject; - SLObjectItf m_playerObject; - SLPlayItf m_playItf; - SLVolumeItf m_volumeItf; - SLBufferQueueItf m_bufferQueueItf; - QIODevice *m_audioSource; - char *m_buffers; - qreal m_volume; - bool m_pullMode; - int m_nextBuffer; - int m_bufferSize; - qint64 m_elapsedTime; - qint64 m_processedBytes; - QAtomicInt m_availableBuffers; - SLuint32 m_eventMask; - bool m_startRequiresInit; - - qint32 m_streamType; - QAudioFormat m_format; -}; - -class SLIODevicePrivate : public QIODevice -{ - Q_OBJECT - -public: - inline SLIODevicePrivate(QAndroidAudioSink *audio) : m_audioDevice(audio) {} - inline ~SLIODevicePrivate() override {} - -protected: - inline qint64 readData(char *, qint64) override { return 0; } - inline qint64 writeData(const char *data, qint64 len) override; - -private: - QAndroidAudioSink *m_audioDevice; -}; - -qint64 SLIODevicePrivate::writeData(const char *data, qint64 len) -{ - Q_ASSERT(m_audioDevice); - return m_audioDevice->writeData(data, len); -} - -QT_END_NAMESPACE - -#endif // QOPENSLESAUDIOOUTPUT_H diff --git a/src/multimedia/platform/android/audio/qandroidaudiosource.cpp b/src/multimedia/platform/android/audio/qandroidaudiosource.cpp deleted file mode 100644 index c7eaf57ad..000000000 --- a/src/multimedia/platform/android/audio/qandroidaudiosource.cpp +++ /dev/null @@ -1,505 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qandroidaudiosource_p.h" - -#include "qopenslesengine_p.h" -#include <private/qaudiohelpers_p.h> -#include <QtCore/private/qandroidextras_p.h> -#include <qbuffer.h> -#include <qdebug.h> - -#ifdef ANDROID -#include <SLES/OpenSLES_AndroidConfiguration.h> -#include <QtCore/qcoreapplication.h> -#endif - -QT_BEGIN_NAMESPACE - -#define NUM_BUFFERS 2 -#define DEFAULT_PERIOD_TIME_MS 50 -#define MINIMUM_PERIOD_TIME_MS 5 - -#ifdef ANDROID -static bool hasRecordingPermission() -{ - if (QNativeInterface::QAndroidApplication::sdkVersion() < 23) - return true; - - const QtAndroidPrivate::PermissionType key(QtAndroidPrivate::Microphone); - // Permission already granted? - if (QtAndroidPrivate::checkPermission(key).result() == QtAndroidPrivate::Authorized) - return true; - - if (QtAndroidPrivate::requestPermission(key).result() != QtAndroidPrivate::Authorized) { - qDebug("Microphone permission denied by user!"); - return false; - } - - return true; -} - -static void bufferQueueCallback(SLAndroidSimpleBufferQueueItf, void *context) -#else -static void bufferQueueCallback(SLBufferQueueItf, void *context) -#endif -{ - // Process buffer in main thread - QMetaObject::invokeMethod(reinterpret_cast<QAndroidAudioSource*>(context), "processBuffer"); -} - -QAndroidAudioSource::QAndroidAudioSource(const QByteArray &device) - : m_device(device) - , m_engine(QOpenSLESEngine::instance()) - , m_recorderObject(0) - , m_recorder(0) - , m_bufferQueue(0) - , m_pullMode(true) - , m_processedBytes(0) - , m_audioSource(0) - , m_bufferIODevice(0) - , m_errorState(QAudio::NoError) - , m_deviceState(QAudio::StoppedState) - , m_lastNotifyTime(0) - , m_volume(1.0) - , m_bufferSize(0) - , m_buffers(new QByteArray[NUM_BUFFERS]) - , m_currentBuffer(0) -{ -#ifdef ANDROID - if (qstrcmp(device, QT_ANDROID_PRESET_CAMCORDER) == 0) - m_recorderPreset = SL_ANDROID_RECORDING_PRESET_CAMCORDER; - else if (qstrcmp(device, QT_ANDROID_PRESET_VOICE_RECOGNITION) == 0) - m_recorderPreset = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION; - else if (qstrcmp(device, QT_ANDROID_PRESET_VOICE_COMMUNICATION) == 0) - m_recorderPreset = SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION; - else - m_recorderPreset = SL_ANDROID_RECORDING_PRESET_GENERIC; -#endif -} - -QAndroidAudioSource::~QAndroidAudioSource() -{ - if (m_recorderObject) - (*m_recorderObject)->Destroy(m_recorderObject); - delete[] m_buffers; -} - -QAudio::Error QAndroidAudioSource::error() const -{ - return m_errorState; -} - -QAudio::State QAndroidAudioSource::state() const -{ - return m_deviceState; -} - -void QAndroidAudioSource::setFormat(const QAudioFormat &format) -{ - if (m_deviceState == QAudio::StoppedState) - m_format = format; -} - -QAudioFormat QAndroidAudioSource::format() const -{ - return m_format; -} - -void QAndroidAudioSource::start(QIODevice *device) -{ - if (m_deviceState != QAudio::StoppedState) - stopRecording(); - - if (!m_pullMode && m_bufferIODevice) { - m_bufferIODevice->close(); - delete m_bufferIODevice; - m_bufferIODevice = 0; - } - - m_pullMode = true; - m_audioSource = device; - - if (startRecording()) { - m_deviceState = QAudio::ActiveState; - } else { - m_deviceState = QAudio::StoppedState; - Q_EMIT errorChanged(m_errorState); - } - - Q_EMIT stateChanged(m_deviceState); -} - -QIODevice *QAndroidAudioSource::start() -{ - if (m_deviceState != QAudio::StoppedState) - stopRecording(); - - m_audioSource = 0; - - if (!m_pullMode && m_bufferIODevice) { - m_bufferIODevice->close(); - delete m_bufferIODevice; - } - - m_pullMode = false; - m_pushBuffer.clear(); - m_bufferIODevice = new QBuffer(&m_pushBuffer); - m_bufferIODevice->open(QIODevice::ReadOnly); - - if (startRecording()) { - m_deviceState = QAudio::IdleState; - } else { - m_deviceState = QAudio::StoppedState; - Q_EMIT errorChanged(m_errorState); - m_bufferIODevice->close(); - delete m_bufferIODevice; - m_bufferIODevice = 0; - } - - Q_EMIT stateChanged(m_deviceState); - return m_bufferIODevice; -} - -bool QAndroidAudioSource::startRecording() -{ - if (!hasRecordingPermission()) - return false; - - m_processedBytes = 0; - m_lastNotifyTime = 0; - - SLresult result; - - // configure audio source - SLDataLocator_IODevice loc_dev = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, - SL_DEFAULTDEVICEID_AUDIOINPUT, NULL }; - SLDataSource audioSrc = { &loc_dev, NULL }; - - // configure audio sink -#ifdef ANDROID - SLDataLocator_AndroidSimpleBufferQueue loc_bq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, - NUM_BUFFERS }; -#else - SLDataLocator_BufferQueue loc_bq = { SL_DATALOCATOR_BUFFERQUEUE, NUM_BUFFERS }; -#endif - - SLDataFormat_PCM format_pcm = QOpenSLESEngine::audioFormatToSLFormatPCM(m_format); - SLDataSink audioSnk = { &loc_bq, &format_pcm }; - - // create audio recorder - // (requires the RECORD_AUDIO permission) -#ifdef ANDROID - const SLInterfaceID id[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION }; - const SLboolean req[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE }; -#else - const SLInterfaceID id[1] = { SL_IID_BUFFERQUEUE }; - const SLboolean req[1] = { SL_BOOLEAN_TRUE }; -#endif - - result = (*m_engine->slEngine())->CreateAudioRecorder(m_engine->slEngine(), &m_recorderObject, - &audioSrc, &audioSnk, - sizeof(req) / sizeof(SLboolean), id, req); - if (result != SL_RESULT_SUCCESS) { - m_errorState = QAudio::OpenError; - return false; - } - -#ifdef ANDROID - // configure recorder source - SLAndroidConfigurationItf configItf; - result = (*m_recorderObject)->GetInterface(m_recorderObject, SL_IID_ANDROIDCONFIGURATION, - &configItf); - if (result != SL_RESULT_SUCCESS) { - m_errorState = QAudio::OpenError; - return false; - } - - result = (*configItf)->SetConfiguration(configItf, SL_ANDROID_KEY_RECORDING_PRESET, - &m_recorderPreset, sizeof(SLuint32)); - - SLuint32 presetValue = SL_ANDROID_RECORDING_PRESET_NONE; - SLuint32 presetSize = 2*sizeof(SLuint32); // intentionally too big - result = (*configItf)->GetConfiguration(configItf, SL_ANDROID_KEY_RECORDING_PRESET, - &presetSize, (void*)&presetValue); - - if (result != SL_RESULT_SUCCESS || presetValue == SL_ANDROID_RECORDING_PRESET_NONE) { - m_errorState = QAudio::OpenError; - return false; - } -#endif - - // realize the audio recorder - result = (*m_recorderObject)->Realize(m_recorderObject, SL_BOOLEAN_FALSE); - if (result != SL_RESULT_SUCCESS) { - m_errorState = QAudio::OpenError; - return false; - } - - // get the record interface - result = (*m_recorderObject)->GetInterface(m_recorderObject, SL_IID_RECORD, &m_recorder); - if (result != SL_RESULT_SUCCESS) { - m_errorState = QAudio::FatalError; - return false; - } - - // get the buffer queue interface -#ifdef ANDROID - SLInterfaceID bufferqueueItfID = SL_IID_ANDROIDSIMPLEBUFFERQUEUE; -#else - SLInterfaceID bufferqueueItfID = SL_IID_BUFFERQUEUE; -#endif - result = (*m_recorderObject)->GetInterface(m_recorderObject, bufferqueueItfID, &m_bufferQueue); - if (result != SL_RESULT_SUCCESS) { - m_errorState = QAudio::FatalError; - return false; - } - - // register callback on the buffer queue - result = (*m_bufferQueue)->RegisterCallback(m_bufferQueue, bufferQueueCallback, this); - if (result != SL_RESULT_SUCCESS) { - m_errorState = QAudio::FatalError; - return false; - } - - if (m_bufferSize <= 0) { - m_bufferSize = m_format.bytesForDuration(DEFAULT_PERIOD_TIME_MS * 1000); - } else { - int minimumBufSize = m_format.bytesForDuration(MINIMUM_PERIOD_TIME_MS * 1000); - if (m_bufferSize < minimumBufSize) - m_bufferSize = minimumBufSize; - } - - // enqueue empty buffers to be filled by the recorder - for (int i = 0; i < NUM_BUFFERS; ++i) { - m_buffers[i].resize(m_bufferSize); - - result = (*m_bufferQueue)->Enqueue(m_bufferQueue, m_buffers[i].data(), m_bufferSize); - if (result != SL_RESULT_SUCCESS) { - m_errorState = QAudio::FatalError; - return false; - } - } - - // start recording - result = (*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_RECORDING); - if (result != SL_RESULT_SUCCESS) { - m_errorState = QAudio::FatalError; - return false; - } - - m_errorState = QAudio::NoError; - - return true; -} - -void QAndroidAudioSource::stop() -{ - if (m_deviceState == QAudio::StoppedState) - return; - - m_deviceState = QAudio::StoppedState; - - stopRecording(); - - m_errorState = QAudio::NoError; - Q_EMIT stateChanged(m_deviceState); -} - -void QAndroidAudioSource::stopRecording() -{ - flushBuffers(); - - (*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_STOPPED); - (*m_bufferQueue)->Clear(m_bufferQueue); - - (*m_recorderObject)->Destroy(m_recorderObject); - m_recorderObject = 0; - - for (int i = 0; i < NUM_BUFFERS; ++i) - m_buffers[i].clear(); - m_currentBuffer = 0; - - if (!m_pullMode && m_bufferIODevice) { - m_bufferIODevice->close(); - delete m_bufferIODevice; - m_bufferIODevice = 0; - m_pushBuffer.clear(); - } -} - -void QAndroidAudioSource::suspend() -{ - if (m_deviceState == QAudio::ActiveState) { - m_deviceState = QAudio::SuspendedState; - emit stateChanged(m_deviceState); - - (*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_PAUSED); - } -} - -void QAndroidAudioSource::resume() -{ - if (m_deviceState == QAudio::SuspendedState || m_deviceState == QAudio::IdleState) { - (*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_RECORDING); - - m_deviceState = QAudio::ActiveState; - emit stateChanged(m_deviceState); - } -} - -void QAndroidAudioSource::processBuffer() -{ - if (m_deviceState == QAudio::StoppedState || m_deviceState == QAudio::SuspendedState) - return; - - if (m_deviceState != QAudio::ActiveState) { - m_errorState = QAudio::NoError; - m_deviceState = QAudio::ActiveState; - emit stateChanged(m_deviceState); - } - - QByteArray *processedBuffer = &m_buffers[m_currentBuffer]; - writeDataToDevice(processedBuffer->constData(), processedBuffer->size()); - - // Re-enqueue the buffer - SLresult result = (*m_bufferQueue)->Enqueue(m_bufferQueue, - processedBuffer->data(), - processedBuffer->size()); - - m_currentBuffer = (m_currentBuffer + 1) % NUM_BUFFERS; - - // If the buffer queue is empty (shouldn't happen), stop recording. -#ifdef ANDROID - SLAndroidSimpleBufferQueueState state; -#else - SLBufferQueueState state; -#endif - result = (*m_bufferQueue)->GetState(m_bufferQueue, &state); - if (result != SL_RESULT_SUCCESS || state.count == 0) { - stop(); - m_errorState = QAudio::FatalError; - Q_EMIT errorChanged(m_errorState); - } -} - -void QAndroidAudioSource::writeDataToDevice(const char *data, int size) -{ - m_processedBytes += size; - - QByteArray outData; - - // Apply volume - if (m_volume < 1.0f) { - outData.resize(size); - QAudioHelperInternal::qMultiplySamples(m_volume, m_format, data, outData.data(), size); - } else { - outData.append(data, size); - } - - if (m_pullMode) { - // write buffer to the QIODevice - if (m_audioSource->write(outData) < 0) { - stop(); - m_errorState = QAudio::IOError; - Q_EMIT errorChanged(m_errorState); - } - } else { - // emits readyRead() so user will call read() on QIODevice to get some audio data - if (m_bufferIODevice != 0) { - m_pushBuffer.append(outData); - Q_EMIT m_bufferIODevice->readyRead(); - } - } -} - -void QAndroidAudioSource::flushBuffers() -{ - SLmillisecond recorderPos; - (*m_recorder)->GetPosition(m_recorder, &recorderPos); - qint64 devicePos = processedUSecs(); - - qint64 delta = recorderPos * 1000 - devicePos; - - if (delta > 0) { - const int writeSize = qMin(m_buffers[m_currentBuffer].size(), - m_format.bytesForDuration(delta)); - writeDataToDevice(m_buffers[m_currentBuffer].constData(), writeSize); - } -} - -qsizetype QAndroidAudioSource::bytesReady() const -{ - if (m_deviceState == QAudio::ActiveState || m_deviceState == QAudio::SuspendedState) - return m_bufferIODevice ? m_bufferIODevice->bytesAvailable() : m_bufferSize; - - return 0; -} - -void QAndroidAudioSource::setBufferSize(qsizetype value) -{ - m_bufferSize = value; -} - -qsizetype QAndroidAudioSource::bufferSize() const -{ - return m_bufferSize; -} - -qint64 QAndroidAudioSource::processedUSecs() const -{ - return m_format.durationForBytes(m_processedBytes); -} - -void QAndroidAudioSource::setVolume(qreal vol) -{ - m_volume = vol; -} - -qreal QAndroidAudioSource::volume() const -{ - return m_volume; -} - -void QAndroidAudioSource::reset() -{ - stop(); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/audio/qandroidaudiosource_p.h b/src/multimedia/platform/android/audio/qandroidaudiosource_p.h deleted file mode 100644 index 8a4621b2c..000000000 --- a/src/multimedia/platform/android/audio/qandroidaudiosource_p.h +++ /dev/null @@ -1,137 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QOPENSLESAUDIOINPUT_H -#define QOPENSLESAUDIOINPUT_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qaudiosystem_p.h> -#include <QElapsedTimer> -#include <SLES/OpenSLES.h> - -#ifdef ANDROID -#include <SLES/OpenSLES_Android.h> - -#define QT_ANDROID_PRESET_MIC "mic" -#define QT_ANDROID_PRESET_CAMCORDER "camcorder" -#define QT_ANDROID_PRESET_VOICE_RECOGNITION "voicerecognition" -#define QT_ANDROID_PRESET_VOICE_COMMUNICATION "voicecommunication" - -#endif - -QT_BEGIN_NAMESPACE - -class QOpenSLESEngine; -class QIODevice; -class QBuffer; - -class QAndroidAudioSource : public QPlatformAudioSource -{ - Q_OBJECT - -public: - QAndroidAudioSource(const QByteArray &device); - ~QAndroidAudioSource(); - - void start(QIODevice *device); - QIODevice *start(); - void stop(); - void reset(); - void suspend(); - void resume(); - qsizetype bytesReady() const; - void setBufferSize(qsizetype value); - qsizetype bufferSize() const; - qint64 processedUSecs() const; - QAudio::Error error() const; - QAudio::State state() const; - void setFormat(const QAudioFormat &format); - QAudioFormat format() const; - - void setVolume(qreal volume); - qreal volume() const; - -public Q_SLOTS: - void processBuffer(); - -private: - bool startRecording(); - void stopRecording(); - void writeDataToDevice(const char *data, int size); - void flushBuffers(); - - QByteArray m_device; - QOpenSLESEngine *m_engine; - SLObjectItf m_recorderObject; - SLRecordItf m_recorder; -#ifdef ANDROID - SLuint32 m_recorderPreset; - SLAndroidSimpleBufferQueueItf m_bufferQueue; -#else - SLBufferQueueItf m_bufferQueue; -#endif - - bool m_pullMode; - qint64 m_processedBytes; - QIODevice *m_audioSource; - QBuffer *m_bufferIODevice; - QByteArray m_pushBuffer; - QAudioFormat m_format; - QAudio::Error m_errorState; - QAudio::State m_deviceState; - qint64 m_lastNotifyTime; - qreal m_volume; - int m_bufferSize; - QByteArray *m_buffers; - int m_currentBuffer; -}; - -QT_END_NAMESPACE - -#endif // QOPENSLESAUDIOINPUT_H diff --git a/src/multimedia/platform/android/audio/qopenslesengine.cpp b/src/multimedia/platform/android/audio/qopenslesengine.cpp deleted file mode 100644 index 7d207a369..000000000 --- a/src/multimedia/platform/android/audio/qopenslesengine.cpp +++ /dev/null @@ -1,368 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qopenslesengine_p.h" - -#include "qandroidaudiosource_p.h" -#include "qandroidaudiodevice_p.h" - -#include <QtCore/qjniobject.h> -#include <QtCore/private/qandroidextras_p.h> -#include <qdebug.h> - -#define MINIMUM_PERIOD_TIME_MS 5 -#define DEFAULT_PERIOD_TIME_MS 50 - -#define CheckError(message) if (result != SL_RESULT_SUCCESS) { qWarning(message); return; } - -Q_GLOBAL_STATIC(QOpenSLESEngine, openslesEngine); - -QOpenSLESEngine::QOpenSLESEngine() - : m_engineObject(0) - , m_engine(0) - , m_checkedInputFormats(false) -{ - SLresult result; - - result = slCreateEngine(&m_engineObject, 0, 0, 0, 0, 0); - CheckError("Failed to create engine"); - - result = (*m_engineObject)->Realize(m_engineObject, SL_BOOLEAN_FALSE); - CheckError("Failed to realize engine"); - - result = (*m_engineObject)->GetInterface(m_engineObject, SL_IID_ENGINE, &m_engine); - CheckError("Failed to get engine interface"); -} - -QOpenSLESEngine::~QOpenSLESEngine() -{ - if (m_engineObject) - (*m_engineObject)->Destroy(m_engineObject); -} - -QOpenSLESEngine *QOpenSLESEngine::instance() -{ - return openslesEngine(); -} - -SLDataFormat_PCM QOpenSLESEngine::audioFormatToSLFormatPCM(const QAudioFormat &format) -{ - SLDataFormat_PCM format_pcm; - format_pcm.formatType = SL_DATAFORMAT_PCM; - format_pcm.numChannels = format.channelCount(); - format_pcm.samplesPerSec = format.sampleRate() * 1000; - format_pcm.bitsPerSample = format.bytesPerSample() * 8; - format_pcm.containerSize = format.bytesPerSample() * 8; - format_pcm.channelMask = (format.channelCount() == 1 ? - SL_SPEAKER_FRONT_CENTER : - SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT); - format_pcm.endianness = (QSysInfo::ByteOrder == QSysInfo::LittleEndian ? - SL_BYTEORDER_LITTLEENDIAN : - SL_BYTEORDER_BIGENDIAN); - return format_pcm; - -} - -QList<QAudioDevice> QOpenSLESEngine::availableDevices(QAudioDevice::Mode mode) -{ - QList<QAudioDevice> devices; - QJniObject devs; - if (mode == QAudioDevice::Input) { - devs = QJniObject::callStaticObjectMethod( - "org/qtproject/qt/android/multimedia/QtAudioDeviceManager", - "getAudioInputDevices", - "()[Ljava/lang/String;"); - } else if (mode == QAudioDevice::Output) { - devs = QJniObject::callStaticObjectMethod( - "org/qtproject/qt/android/multimedia/QtAudioDeviceManager", - "getAudioOutputDevices", - "()[Ljava/lang/String;"); - } - if (devs.isValid()) { - QJniEnvironment env; - jobjectArray devsArray = static_cast<jobjectArray>(devs.object()); - const jint size = env->GetArrayLength(devsArray); - for (int i = 0; i < size; ++i) { - QString val = QJniObject(env->GetObjectArrayElement(devsArray, i)).toString(); - int pos = val.indexOf(QStringLiteral(":")); - devices << (new QOpenSLESDeviceInfo( - val.left(pos).toUtf8(), val.mid(pos+1), mode))->create(); - } - } - return devices; -} - -static bool hasRecordPermission() -{ - const auto recordPerm = QtAndroidPrivate::checkPermission(QtAndroidPrivate::Microphone); - return recordPerm.result() == QtAndroidPrivate::Authorized; -} - -QList<int> QOpenSLESEngine::supportedChannelCounts(QAudioDevice::Mode mode) const -{ - if (mode == QAudioDevice::Input && hasRecordPermission()) { - if (!m_checkedInputFormats) - const_cast<QOpenSLESEngine *>(this)->checkSupportedInputFormats(); - return m_supportedInputChannelCounts; - } else { - return QList<int>() << 1 << 2; - } -} - -QList<int> QOpenSLESEngine::supportedSampleRates(QAudioDevice::Mode mode) const -{ - if (mode == QAudioDevice::Input && hasRecordPermission()) { - if (!m_checkedInputFormats) - const_cast<QOpenSLESEngine *>(this)->checkSupportedInputFormats(); - return m_supportedInputSampleRates; - } else { - return QList<int>() << 8000 << 11025 << 12000 << 16000 << 22050 << 24000 - << 32000 << 44100 << 48000 << 64000 << 88200 << 96000 << 192000; - } -} - -int QOpenSLESEngine::getOutputValue(QOpenSLESEngine::OutputValue type, int defaultValue) -{ - static int sampleRate = 0; - static int framesPerBuffer = 0; - - if (type == FramesPerBuffer && framesPerBuffer != 0) - return framesPerBuffer; - - if (type == SampleRate && sampleRate != 0) - return sampleRate; - - QJniObject ctx(QNativeInterface::QAndroidApplication::context()); - if (!ctx.isValid()) - return defaultValue; - - - QJniObject audioServiceString = ctx.getStaticObjectField("android/content/Context", - "AUDIO_SERVICE", - "Ljava/lang/String;"); - QJniObject am = ctx.callObjectMethod("getSystemService", - "(Ljava/lang/String;)Ljava/lang/Object;", - audioServiceString.object()); - if (!am.isValid()) - return defaultValue; - - auto sampleRateField = QJniObject::getStaticObjectField("android/media/AudioManager", - "PROPERTY_OUTPUT_SAMPLE_RATE", - "Ljava/lang/String;"); - auto framesPerBufferField = QJniObject::getStaticObjectField( - "android/media/AudioManager", - "PROPERTY_OUTPUT_FRAMES_PER_BUFFER", - "Ljava/lang/String;"); - - auto sampleRateString = am.callObjectMethod("getProperty", - "(Ljava/lang/String;)Ljava/lang/String;", - sampleRateField.object()); - auto framesPerBufferString = am.callObjectMethod("getProperty", - "(Ljava/lang/String;)Ljava/lang/String;", - framesPerBufferField.object()); - - if (!sampleRateString.isValid() || !framesPerBufferString.isValid()) - return defaultValue; - - framesPerBuffer = framesPerBufferString.toString().toInt(); - sampleRate = sampleRateString.toString().toInt(); - - if (type == FramesPerBuffer) - return framesPerBuffer; - - if (type == SampleRate) - return sampleRate; - - return defaultValue; -} - -int QOpenSLESEngine::getDefaultBufferSize(const QAudioFormat &format) -{ - if (!format.isValid()) - return 0; - - const int channelConfig = [&format]() -> int - { - if (format.channelCount() == 1) - return 4; /* MONO */ - else if (format.channelCount() == 2) - return 12; /* STEREO */ - else if (format.channelCount() > 2) - return 1052; /* SURROUND */ - else - return 1; /* DEFAULT */ - }(); - - const int audioFormat = [&format]() -> int - { - const int sdkVersion = QNativeInterface::QAndroidApplication::sdkVersion(); - if (format.sampleFormat() == QAudioFormat::Float && sdkVersion >= 21) - return 4; /* PCM_FLOAT */ - else if (format.sampleFormat() == QAudioFormat::UInt8) - return 3; /* PCM_8BIT */ - else if (format.sampleFormat() == QAudioFormat::Int16) - return 2; /* PCM_16BIT*/ - else - return 1; /* DEFAULT */ - }(); - - const int sampleRate = format.sampleRate(); - const int minBufferSize = QJniObject::callStaticMethod<jint>("android/media/AudioTrack", - "getMinBufferSize", - "(III)I", - sampleRate, - channelConfig, - audioFormat); - return minBufferSize > 0 ? minBufferSize : format.bytesForDuration(DEFAULT_PERIOD_TIME_MS); -} - -int QOpenSLESEngine::getLowLatencyBufferSize(const QAudioFormat &format) -{ - return format.bytesForFrames(QOpenSLESEngine::getOutputValue(QOpenSLESEngine::FramesPerBuffer, - format.framesForDuration(MINIMUM_PERIOD_TIME_MS))); -} - -bool QOpenSLESEngine::supportsLowLatency() -{ - static int isSupported = -1; - - if (isSupported != -1) - return (isSupported == 1); - - QJniObject ctx(QNativeInterface::QAndroidApplication::context()); - if (!ctx.isValid()) - return false; - - QJniObject pm = ctx.callObjectMethod("getPackageManager", "()Landroid/content/pm/PackageManager;"); - if (!pm.isValid()) - return false; - - QJniObject audioFeatureField = QJniObject::getStaticObjectField( - "android/content/pm/PackageManager", - "FEATURE_AUDIO_LOW_LATENCY", - "Ljava/lang/String;"); - if (!audioFeatureField.isValid()) - return false; - - isSupported = pm.callMethod<jboolean>("hasSystemFeature", - "(Ljava/lang/String;)Z", - audioFeatureField.object()); - return (isSupported == 1); -} - -bool QOpenSLESEngine::printDebugInfo() -{ - return qEnvironmentVariableIsSet("QT_OPENSL_INFO"); -} - -void QOpenSLESEngine::checkSupportedInputFormats() -{ - m_supportedInputChannelCounts = QList<int>() << 1; - m_supportedInputSampleRates.clear(); - - SLAndroidDataFormat_PCM_EX defaultFormat; - defaultFormat.formatType = SL_DATAFORMAT_PCM; - defaultFormat.numChannels = 1; - defaultFormat.sampleRate = SL_SAMPLINGRATE_44_1; - defaultFormat.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_32; - defaultFormat.containerSize = SL_PCMSAMPLEFORMAT_FIXED_32; - defaultFormat.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT; - defaultFormat.channelMask = SL_ANDROID_MAKE_INDEXED_CHANNEL_MASK(SL_SPEAKER_FRONT_CENTER); - defaultFormat.endianness = SL_BYTEORDER_LITTLEENDIAN; - - const SLuint32 rates[13] = { SL_SAMPLINGRATE_8, - SL_SAMPLINGRATE_11_025, - SL_SAMPLINGRATE_12, - SL_SAMPLINGRATE_16, - SL_SAMPLINGRATE_22_05, - SL_SAMPLINGRATE_24, - SL_SAMPLINGRATE_32, - SL_SAMPLINGRATE_44_1, - SL_SAMPLINGRATE_48, - SL_SAMPLINGRATE_64, - SL_SAMPLINGRATE_88_2, - SL_SAMPLINGRATE_96, - SL_SAMPLINGRATE_192 }; - - - // Test sampling rates - for (size_t i = 0 ; i < std::size(rates); ++i) { - SLAndroidDataFormat_PCM_EX format = defaultFormat; - format.sampleRate = rates[i]; - - if (inputFormatIsSupported(format)) - m_supportedInputSampleRates.append(rates[i] / 1000); - - } - - // Test if stereo is supported - { - SLAndroidDataFormat_PCM_EX format = defaultFormat; - format.numChannels = 2; - format.channelMask = SL_ANDROID_MAKE_INDEXED_CHANNEL_MASK(SL_SPEAKER_FRONT_LEFT - | SL_SPEAKER_FRONT_RIGHT); - if (inputFormatIsSupported(format)) - m_supportedInputChannelCounts.append(2); - } - - m_checkedInputFormats = true; -} - -bool QOpenSLESEngine::inputFormatIsSupported(SLAndroidDataFormat_PCM_EX format) -{ - SLresult result; - SLObjectItf recorder = 0; - SLDataLocator_IODevice loc_dev = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, - SL_DEFAULTDEVICEID_AUDIOINPUT, NULL }; - SLDataSource audioSrc = { &loc_dev, NULL }; - - SLDataLocator_AndroidSimpleBufferQueue loc_bq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1 }; - SLDataSink audioSnk = { &loc_bq, &format }; - - result = (*m_engine)->CreateAudioRecorder(m_engine, &recorder, &audioSrc, &audioSnk, 0, 0, 0); - if (result == SL_RESULT_SUCCESS) - result = (*recorder)->Realize(recorder, false); - - if (result == SL_RESULT_SUCCESS) { - (*recorder)->Destroy(recorder); - return true; - } - - return false; -} diff --git a/src/multimedia/platform/android/audio/qopenslesengine_p.h b/src/multimedia/platform/android/audio/qopenslesengine_p.h deleted file mode 100644 index 36e994fb2..000000000 --- a/src/multimedia/platform/android/audio/qopenslesengine_p.h +++ /dev/null @@ -1,100 +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$ -** -****************************************************************************/ - -#ifndef QOPENSLESENGINE_H -#define QOPENSLESENGINE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qglobal.h> -#include <qaudio.h> -#include <qlist.h> -#include <qaudioformat.h> -#include <qaudiodevice.h> -#include <SLES/OpenSLES_Android.h> - -QT_BEGIN_NAMESPACE - -class QOpenSLESEngine -{ -public: - enum OutputValue { FramesPerBuffer, SampleRate }; - - QOpenSLESEngine(); - ~QOpenSLESEngine(); - - static QOpenSLESEngine *instance(); - - SLEngineItf slEngine() const { return m_engine; } - - static SLDataFormat_PCM audioFormatToSLFormatPCM(const QAudioFormat &format); - - static QList<QAudioDevice> availableDevices(QAudioDevice::Mode mode); - QList<int> supportedChannelCounts(QAudioDevice::Mode mode) const; - QList<int> supportedSampleRates(QAudioDevice::Mode mode) const; - - static int getOutputValue(OutputValue type, int defaultValue = 0); - static int getDefaultBufferSize(const QAudioFormat &format); - static int getLowLatencyBufferSize(const QAudioFormat &format); - static bool supportsLowLatency(); - static bool printDebugInfo(); - -private: - void checkSupportedInputFormats(); - bool inputFormatIsSupported(SLAndroidDataFormat_PCM_EX format); - SLObjectItf m_engineObject; - SLEngineItf m_engine; - - QList<int> m_supportedInputChannelCounts; - QList<int> m_supportedInputSampleRates; - bool m_checkedInputFormats; -}; - -QT_END_NAMESPACE - -#endif // QOPENSLESENGINE_H diff --git a/src/multimedia/platform/android/common/qandroidaudiooutput_p.h b/src/multimedia/platform/android/common/qandroidaudiooutput_p.h deleted file mode 100644 index e17f158fc..000000000 --- a/src/multimedia/platform/android/common/qandroidaudiooutput_p.h +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ -#ifndef QANDROIDAUDIOOUTPUT_H -#define QANDROIDAUDIOOUTPUT_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformaudiooutput_p.h> - -QT_BEGIN_NAMESPACE - -class Q_MULTIMEDIA_EXPORT QAndroidAudioOutput : public QPlatformAudioOutput -{ -public: - QAndroidAudioOutput(QAudioOutput *qq) : QPlatformAudioOutput(qq) {} -}; - -QT_END_NAMESPACE - - -#endif // QANDROIDAUDIOOUTPUT_H diff --git a/src/multimedia/platform/android/common/qandroidglobal_p.h b/src/multimedia/platform/android/common/qandroidglobal_p.h deleted file mode 100644 index 45bd22ffb..000000000 --- a/src/multimedia/platform/android/common/qandroidglobal_p.h +++ /dev/null @@ -1,64 +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$ -** -****************************************************************************/ - -#ifndef QANDROIDGLOBAL_H -#define QANDROIDGLOBAL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qtmultimediaglobal_p.h> -#include <QtCore/qglobal.h> -#include <QtCore/qloggingcategory.h> - -QT_BEGIN_NAMESPACE - -Q_DECLARE_LOGGING_CATEGORY(qtAndroidMediaPlugin) - -QT_END_NAMESPACE - -#endif // QANDROIDGLOBAL_H diff --git a/src/multimedia/platform/android/common/qandroidmultimediautils.cpp b/src/multimedia/platform/android/common/qandroidmultimediautils.cpp deleted file mode 100644 index 75f012f07..000000000 --- a/src/multimedia/platform/android/common/qandroidmultimediautils.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qandroidmultimediautils_p.h" -#include "qandroidglobal_p.h" - -#include <qlist.h> -#include <QtCore/qcoreapplication.h> -#include <QtCore/private/qandroidextras_p.h> - -QT_BEGIN_NAMESPACE - -int qt_findClosestValue(const QList<int> &list, int value) -{ - if (list.size() < 2) - return 0; - - int begin = 0; - int end = list.size() - 1; - int pivot = begin + (end - begin) / 2; - int v = list.at(pivot); - - while (end - begin > 1) { - if (value == v) - return pivot; - - if (value > v) - begin = pivot; - else - end = pivot; - - pivot = begin + (end - begin) / 2; - v = list.at(pivot); - } - - return value - v >= list.at(pivot + 1) - value ? pivot + 1 : pivot; -} - -bool qt_sizeLessThan(const QSize &s1, const QSize &s2) -{ - return s1.width() * s1.height() < s2.width() * s2.height(); -} - -QVideoFrameFormat::PixelFormat qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat f) -{ - switch (f) { - case AndroidCamera::NV21: - return QVideoFrameFormat::Format_NV21; - case AndroidCamera::YV12: - return QVideoFrameFormat::Format_YV12; - case AndroidCamera::YUY2: - return QVideoFrameFormat::Format_YUYV; - case AndroidCamera::JPEG: - return QVideoFrameFormat::Format_Jpeg; - default: - return QVideoFrameFormat::Format_Invalid; - } -} - -AndroidCamera::ImageFormat qt_androidImageFormatFromPixelFormat(QVideoFrameFormat::PixelFormat f) -{ - switch (f) { - case QVideoFrameFormat::Format_NV21: - return AndroidCamera::NV21; - case QVideoFrameFormat::Format_YV12: - return AndroidCamera::YV12; - case QVideoFrameFormat::Format_YUYV: - return AndroidCamera::YUY2; - case QVideoFrameFormat::Format_Jpeg: - return AndroidCamera::JPEG; - default: - return AndroidCamera::UnknownImageFormat; - } -} - -static bool androidRequestPermission(QtAndroidPrivate::PermissionType key) -{ - if (QNativeInterface::QAndroidApplication::sdkVersion() < 23) - return true; - - // Permission already granted? - if (QtAndroidPrivate::checkPermission(key).result() == QtAndroidPrivate::Authorized) - return true; - - if (QtAndroidPrivate::requestPermission(key).result() != QtAndroidPrivate::Authorized) - return false; - - return true; -} - -static bool androidCheckPermission(QtAndroidPrivate::PermissionType key) -{ - if (QNativeInterface::QAndroidApplication::sdkVersion() < 23) - return true; - - // Permission already granted? - return (QtAndroidPrivate::checkPermission(key).result() == QtAndroidPrivate::Authorized); -} - -bool qt_androidCheckCameraPermission() -{ - return androidCheckPermission(QtAndroidPrivate::Camera); -} - -bool qt_androidCheckMicrophonePermission() -{ - return androidCheckPermission(QtAndroidPrivate::Microphone); -} - -bool qt_androidRequestCameraPermission() -{ - if (!androidRequestPermission(QtAndroidPrivate::Camera)) { - qCDebug(qtAndroidMediaPlugin, "Camera permission denied by user!"); - return false; - } - - return true; -} - -bool qt_androidRequestRecordingPermission() -{ - if (!androidRequestPermission(QtAndroidPrivate::Microphone)) { - qCDebug(qtAndroidMediaPlugin, "Microphone permission denied by user!"); - return false; - } - - return true; -} - -bool qt_androidRequestWriteStoragePermission() -{ - if (!androidRequestPermission(QtAndroidPrivate::Storage)) { - qCDebug(qtAndroidMediaPlugin, "Storage permission denied by user!"); - return false; - } - - return true; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/common/qandroidmultimediautils_p.h b/src/multimedia/platform/android/common/qandroidmultimediautils_p.h deleted file mode 100644 index 5bc187da3..000000000 --- a/src/multimedia/platform/android/common/qandroidmultimediautils_p.h +++ /dev/null @@ -1,78 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QANDROIDMULTIMEDIAUTILS_H -#define QANDROIDMULTIMEDIAUTILS_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qglobal.h> -#include <qsize.h> -#include "androidcamera_p.h" - -QT_BEGIN_NAMESPACE - -// return the index of the closest value to <value> in <list> -// (binary search) -int qt_findClosestValue(const QList<int> &list, int value); - -bool qt_sizeLessThan(const QSize &s1, const QSize &s2); - -QVideoFrameFormat::PixelFormat qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat f); -AndroidCamera::ImageFormat qt_androidImageFormatFromPixelFormat(QVideoFrameFormat::PixelFormat f); - -bool qt_androidRequestCameraPermission(); -bool qt_androidRequestRecordingPermission(); -bool qt_androidRequestWriteStoragePermission(); - -bool qt_androidCheckCameraPermission(); -bool qt_androidCheckMicrophonePermission(); - -QT_END_NAMESPACE - -#endif // QANDROIDMULTIMEDIAUTILS_H diff --git a/src/multimedia/platform/android/common/qandroidvideooutput.cpp b/src/multimedia/platform/android/common/qandroidvideooutput.cpp deleted file mode 100644 index d0f3203de..000000000 --- a/src/multimedia/platform/android/common/qandroidvideooutput.cpp +++ /dev/null @@ -1,465 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qandroidvideooutput_p.h" - -#include "androidsurfacetexture_p.h" -#include <qvideosink.h> -#include "private/qabstractvideobuffer_p.h" -#include "private/qplatformvideosink_p.h" -#include <QVideoFrameFormat> -#include <QFile> -#include <QtGui/private/qrhigles2_p.h> -#include <QOpenGLContext> -#include <QPainter> -#include <QPainterPath> -#include <QMutexLocker> -#include <QTextLayout> -#include <QTextFormat> - -QT_BEGIN_NAMESPACE - -void GraphicsResourceDeleter::deleteResourcesHelper(const QList<QRhiResource *> &res) -{ - qDeleteAll(res); -} - -void GraphicsResourceDeleter::deleteRhiHelper(QRhi *rhi, QOffscreenSurface *surf) -{ - delete rhi; - delete surf; -} - -void GraphicsResourceDeleter::deleteThisHelper() -{ - delete this; -} - -bool AndroidTextureVideoBuffer::updateReadbackFrame() -{ - // Even though the texture was updated in a previous call, we need to re-check - // that this has not become a stale buffer, e.g., if the output size changed or - // has since became invalid. - if (!m_output->m_nativeSize.isValid()) - return false; - - // Size changed - if (m_output->m_nativeSize != m_size) - return false; - - // In the unlikely event that we don't have a valid fbo, but have a valid size, - // force an update. - const bool forceUpdate = !m_output->m_readbackTex; - if (m_textureUpdated && !forceUpdate) - return true; - - // update the video texture (called from the render thread) - return (m_textureUpdated = m_output->renderAndReadbackFrame()); -} - -QAbstractVideoBuffer::MapData AndroidTextureVideoBuffer::map(QVideoFrame::MapMode mode) -{ - MapData mapData; - if (m_mapMode == QVideoFrame::NotMapped && mode == QVideoFrame::ReadOnly && updateReadbackFrame()) { - m_mapMode = mode; - m_image = m_output->m_readbackImage; - mapData.nPlanes = 1; - mapData.bytesPerLine[0] = m_image.bytesPerLine(); - mapData.size[0] = static_cast<int>(m_image.sizeInBytes()); - mapData.data[0] = m_image.bits(); - } - return mapData; -} - -static QMatrix4x4 extTransformMatrix(AndroidSurfaceTexture *surfaceTexture) -{ - QMatrix4x4 m = surfaceTexture->getTransformMatrix(); - // flip it back, see - // http://androidxref.com/9.0.0_r3/xref/frameworks/native/libs/gui/GLConsumer.cpp#866 - // (NB our matrix ctor takes row major) - static const QMatrix4x4 flipV(1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, -1.0f, 0.0f, 1.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f); - m *= flipV; - return m; -} - -quint64 AndroidTextureVideoBuffer::textureHandle(int plane) const -{ - if (plane != 0 || !rhi || !m_output->m_nativeSize.isValid()) - return 0; - - m_output->ensureExternalTexture(rhi); - m_output->m_surfaceTexture->updateTexImage(); - m_externalMatrix = extTransformMatrix(m_output->m_surfaceTexture); - return m_output->m_externalTex->nativeTexture().object; -} - -QAndroidTextureVideoOutput::QAndroidTextureVideoOutput(QObject *parent) : QAndroidVideoOutput(parent) { } - -QAndroidTextureVideoOutput::~QAndroidTextureVideoOutput() -{ - clearSurfaceTexture(); - - if (m_graphicsDeleter) { // Make sure all of these are deleted on the render thread. - m_graphicsDeleter->deleteResources({ - m_externalTex, - m_readbackSrc, - m_readbackTex, - m_readbackVBuf, - m_readbackUBuf, - m_externalTexSampler, - m_readbackSrb, - m_readbackRenderTarget, - m_readbackRpDesc, - m_readbackPs - }); - - m_graphicsDeleter->deleteRhi(m_readbackRhi, m_readbackRhiFallbackSurface); - m_graphicsDeleter->deleteThis(); - } -} - -void QAndroidTextureVideoOutput::setSubtitle(const QString &subtitle) -{ - if (!m_sink) - return; - auto *sink = m_sink->platformVideoSink(); - sink->setSubtitleText(subtitle); -} - -QVideoSink *QAndroidTextureVideoOutput::surface() const -{ - return m_sink; -} - -void QAndroidTextureVideoOutput::setSurface(QVideoSink *surface) -{ - if (surface == m_sink) - return; - - m_sink = surface; -} - -bool QAndroidTextureVideoOutput::isReady() -{ - return true; -} - -void QAndroidTextureVideoOutput::initSurfaceTexture() -{ - if (m_surfaceTexture) - return; - - if (!m_sink) - return; - - QMutexLocker locker(&m_mutex); - - m_surfaceTexture = new AndroidSurfaceTexture(m_externalTex ? m_externalTex->nativeTexture().object : 0); - - if (m_surfaceTexture->surfaceTexture() != 0) { - connect(m_surfaceTexture, &AndroidSurfaceTexture::frameAvailable, - this, &QAndroidTextureVideoOutput::onFrameAvailable); - } else { - delete m_surfaceTexture; - m_surfaceTexture = nullptr; - if (m_graphicsDeleter) - m_graphicsDeleter->deleteResources({ m_externalTex }); - m_externalTex = nullptr; - } -} - -void QAndroidTextureVideoOutput::clearSurfaceTexture() -{ - QMutexLocker locker(&m_mutex); - if (m_surfaceTexture) { - delete m_surfaceTexture; - m_surfaceTexture = nullptr; - } - - // Also reset the attached texture - if (m_graphicsDeleter) - m_graphicsDeleter->deleteResources({ m_externalTex }); - m_externalTex = nullptr; -} - -AndroidSurfaceTexture *QAndroidTextureVideoOutput::surfaceTexture() -{ - initSurfaceTexture(); - return m_surfaceTexture; -} - -void QAndroidTextureVideoOutput::setVideoSize(const QSize &size) -{ - QMutexLocker locker(&m_mutex); - if (m_nativeSize == size) - return; - - stop(); - - m_nativeSize = size; -} - -void QAndroidTextureVideoOutput::stop() -{ - m_nativeSize = QSize(); -} - -void QAndroidTextureVideoOutput::reset() -{ - // flush pending frame - if (m_sink) - m_sink->platformVideoSink()->setVideoFrame(QVideoFrame()); - - clearSurfaceTexture(); -} - -void QAndroidTextureVideoOutput::onFrameAvailable() -{ - if (!m_nativeSize.isValid() || !m_sink) - return; - - QRhi *rhi = m_sink ? m_sink->rhi() : nullptr; - auto *buffer = new AndroidTextureVideoBuffer(rhi, this, m_nativeSize); - const QVideoFrameFormat::PixelFormat format = rhi ? QVideoFrameFormat::Format_SamplerExternalOES - : QVideoFrameFormat::Format_RGBA8888; - QVideoFrame frame(buffer, QVideoFrameFormat(m_nativeSize, format)); - m_sink->platformVideoSink()->setVideoFrame(frame); -} - -static const float g_quad[] = { - -1.f, -1.f, 0.f, 0.f, - -1.f, 1.f, 0.f, 1.f, - 1.f, 1.f, 1.f, 1.f, - 1.f, -1.f, 1.f, 0.f -}; - -static QShader getShader(const QString &name) -{ - QFile f(name); - if (f.open(QIODevice::ReadOnly)) - return QShader::fromSerialized(f.readAll()); - - return QShader(); -} - -bool QAndroidTextureVideoOutput::renderAndReadbackFrame() -{ - QMutexLocker locker(&m_mutex); - - if (!m_nativeSize.isValid() || !m_surfaceTexture) - return false; - - if (!m_readbackRhi) { - QRhi *sinkRhi = m_sink ? m_sink->rhi() : nullptr; - if (sinkRhi && sinkRhi->backend() == QRhi::OpenGLES2) { - // There is an rhi from the sink, e.g. VideoOutput. We lack the necessary - // insight to use that directly, so create our own a QRhi that just wraps the - // same QOpenGLContext. - sinkRhi->finish(); - QRhiGles2NativeHandles h = *static_cast<const QRhiGles2NativeHandles *>(sinkRhi->nativeHandles()); - m_readbackRhiFallbackSurface = QRhiGles2InitParams::newFallbackSurface(h.context->format()); - QRhiGles2InitParams initParams; - initParams.format = h.context->format(); - initParams.fallbackSurface = m_readbackRhiFallbackSurface; - m_readbackRhi = QRhi::create(QRhi::OpenGLES2, &initParams, {}, &h); - } else { - // No rhi from the sink, e.g. QVideoWidget. - // We will fire up our own QRhi with its own QOpenGLContext. - m_readbackRhiFallbackSurface = QRhiGles2InitParams::newFallbackSurface({}); - QRhiGles2InitParams initParams; - initParams.fallbackSurface = m_readbackRhiFallbackSurface; - m_readbackRhi = QRhi::create(QRhi::OpenGLES2, &initParams); - } - } - - if (!m_readbackRhi) { - qWarning("Failed to create QRhi for video frame readback"); - return false; - } - - QRhiCommandBuffer *cb = nullptr; - if (m_readbackRhi->beginOffscreenFrame(&cb) != QRhi::FrameOpSuccess) - return false; - - if (!m_readbackTex || m_readbackTex->pixelSize() != m_nativeSize) { - delete m_readbackRenderTarget; - delete m_readbackRpDesc; - delete m_readbackTex; - m_readbackTex = m_readbackRhi->newTexture(QRhiTexture::RGBA8, m_nativeSize, 1, QRhiTexture::RenderTarget); - if (!m_readbackTex->create()) { - qWarning("Failed to create readback texture"); - return false; - } - m_readbackRenderTarget = m_readbackRhi->newTextureRenderTarget({ { m_readbackTex } }); - m_readbackRpDesc = m_readbackRenderTarget->newCompatibleRenderPassDescriptor(); - m_readbackRenderTarget->setRenderPassDescriptor(m_readbackRpDesc); - m_readbackRenderTarget->create(); - } - - m_readbackRhi->makeThreadLocalNativeContextCurrent(); - ensureExternalTexture(m_readbackRhi); - m_surfaceTexture->updateTexImage(); - - // The only purpose of m_readbackSrc is to be nice and have a QRhiTexture that belongs - // to m_readbackRhi, not the sink's rhi if there is one. The underlying native object - // (and the rhi's OpenGL context) are the same regardless. - if (!m_readbackSrc) - m_readbackSrc = m_readbackRhi->newTexture(QRhiTexture::RGBA8, m_nativeSize, 1, QRhiTexture::ExternalOES); - - // Keep the object the same (therefore all references to m_readbackSrc in - // the srb or other objects stay valid all the time), just call createFrom - // if the native external texture changes. - const quint64 texId = m_externalTex->nativeTexture().object; - if (m_readbackSrc->nativeTexture().object != texId) - m_readbackSrc->createFrom({ texId, 0 }); - - QRhiResourceUpdateBatch *rub = nullptr; - if (!m_readbackVBuf) { - m_readbackVBuf = m_readbackRhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(g_quad)); - m_readbackVBuf->create(); - if (!rub) - rub = m_readbackRhi->nextResourceUpdateBatch(); - rub->uploadStaticBuffer(m_readbackVBuf, g_quad); - } - - if (!m_readbackUBuf) { - m_readbackUBuf = m_readbackRhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64 + 64 + 4 + 4); - m_readbackUBuf->create(); - } - - if (!m_externalTexSampler) { - m_externalTexSampler = m_readbackRhi->newSampler(QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None, - QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge); - m_externalTexSampler->create(); - } - - if (!m_readbackSrb) { - m_readbackSrb = m_readbackRhi->newShaderResourceBindings(); - m_readbackSrb->setBindings({ - QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, m_readbackUBuf), - QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, m_readbackSrc, m_externalTexSampler) - }); - m_readbackSrb->create(); - } - - if (!m_readbackPs) { - m_readbackPs = m_readbackRhi->newGraphicsPipeline(); - m_readbackPs->setTopology(QRhiGraphicsPipeline::TriangleFan); - QShader vs = getShader(QStringLiteral(":/qt-project.org/multimedia/shaders/externalsampler.vert.qsb")); - Q_ASSERT(vs.isValid()); - QShader fs = getShader(QStringLiteral(":/qt-project.org/multimedia/shaders/externalsampler.frag.qsb")); - Q_ASSERT(fs.isValid()); - m_readbackPs->setShaderStages({ - { QRhiShaderStage::Vertex, vs }, - { QRhiShaderStage::Fragment, fs } - }); - QRhiVertexInputLayout inputLayout; - inputLayout.setBindings({ - { 4 * sizeof(float) } - }); - inputLayout.setAttributes({ - { 0, 0, QRhiVertexInputAttribute::Float2, 0 }, - { 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) } - }); - m_readbackPs->setVertexInputLayout(inputLayout); - m_readbackPs->setShaderResourceBindings(m_readbackSrb); - m_readbackPs->setRenderPassDescriptor(m_readbackRpDesc); - m_readbackPs->create(); - } - - QMatrix4x4 identity; - char *p = m_readbackUBuf->beginFullDynamicBufferUpdateForCurrentFrame(); - memcpy(p, identity.constData(), 64); - QMatrix4x4 extMatrix = extTransformMatrix(m_surfaceTexture); - memcpy(p + 64, extMatrix.constData(), 64); - float opacity = 1.0f; - memcpy(p + 64 + 64, &opacity, 4); - m_readbackUBuf->endFullDynamicBufferUpdateForCurrentFrame(); - - cb->beginPass(m_readbackRenderTarget, Qt::transparent, { 1.0f, 0 }, rub); - cb->setGraphicsPipeline(m_readbackPs); - cb->setViewport(QRhiViewport(0, 0, m_nativeSize.width(), m_nativeSize.height())); - cb->setShaderResources(); - const QRhiCommandBuffer::VertexInput vbufBinding(m_readbackVBuf, 0); - cb->setVertexInput(0, 1, &vbufBinding); - cb->draw(4); - - QRhiReadbackDescription readDesc(m_readbackTex); - QRhiReadbackResult readResult; - bool readCompleted = false; - // invoked at latest in the endOffscreenFrame() below - readResult.completed = [&readCompleted] { readCompleted = true; }; - rub = m_readbackRhi->nextResourceUpdateBatch(); - rub->readBackTexture(readDesc, &readResult); - - cb->endPass(rub); - - m_readbackRhi->endOffscreenFrame(); - - if (!readCompleted) - return false; - - // implicit sharing, keep the data alive - m_readbackImageData = readResult.data; - // the QImage does not own the data - m_readbackImage = QImage(reinterpret_cast<const uchar *>(m_readbackImageData.constData()), - readResult.pixelSize.width(), readResult.pixelSize.height(), - QImage::Format_ARGB32_Premultiplied); - - return true; -} - -void QAndroidTextureVideoOutput::ensureExternalTexture(QRhi *rhi) -{ - if (!m_graphicsDeleter) - m_graphicsDeleter = new GraphicsResourceDeleter; - - if (!m_externalTex) { - m_surfaceTexture->detachFromGLContext(); - m_externalTex = rhi->newTexture(QRhiTexture::RGBA8, m_nativeSize, 1, QRhiTexture::ExternalOES); - if (!m_externalTex->create()) - qWarning("Failed to create native texture object"); - m_surfaceTexture->attachToGLContext(m_externalTex->nativeTexture().object); - } -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/common/qandroidvideooutput_p.h b/src/multimedia/platform/android/common/qandroidvideooutput_p.h deleted file mode 100644 index fc85d74fc..000000000 --- a/src/multimedia/platform/android/common/qandroidvideooutput_p.h +++ /dev/null @@ -1,213 +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$ -** -****************************************************************************/ - -#ifndef QANDROIDVIDEOOUTPUT_H -#define QANDROIDVIDEOOUTPUT_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qobject.h> -#include <qsize.h> -#include <qmutex.h> -#include <qreadwritelock.h> -#include <private/qabstractvideobuffer_p.h> -#include <qmatrix4x4.h> -#include <QtGui/private/qrhi_p.h> -#include <QtGui/qoffscreensurface.h> -#include <QPixmap> - -QT_BEGIN_NAMESPACE - -class AndroidSurfaceTexture; -class AndroidSurfaceHolder; -class QVideoSink; - -class QAndroidVideoOutput : public QObject -{ - Q_OBJECT -public: - virtual ~QAndroidVideoOutput() { } - - virtual AndroidSurfaceTexture *surfaceTexture() { return 0; } - virtual AndroidSurfaceHolder *surfaceHolder() { return 0; } - - virtual bool isReady() { return true; } - - virtual void setVideoSize(const QSize &) { } - virtual void stop() { } - virtual void reset() { } - -Q_SIGNALS: - void readyChanged(bool); - -protected: - QAndroidVideoOutput(QObject *parent) : QObject(parent) { } -}; - -class GraphicsResourceDeleter : public QObject -{ - Q_OBJECT -public: - void deleteResources(const QList<QRhiResource *> &res) { QMetaObject::invokeMethod(this, "deleteResourcesHelper", Qt::AutoConnection, Q_ARG(QList<QRhiResource*>, res)); } - void deleteRhi(QRhi *rhi, QOffscreenSurface *surf) { QMetaObject::invokeMethod(this, "deleteRhiHelper", Qt::AutoConnection, Q_ARG(QRhi*, rhi), Q_ARG(QOffscreenSurface*, surf)); } - void deleteThis() { QMetaObject::invokeMethod(this, "deleteThisHelper"); } - -private: - Q_INVOKABLE void deleteResourcesHelper(const QList<QRhiResource *> &res); - Q_INVOKABLE void deleteRhiHelper(QRhi *rhi, QOffscreenSurface *surf); - Q_INVOKABLE void deleteThisHelper(); -}; - -class QAndroidTextureVideoOutput : public QAndroidVideoOutput -{ - Q_OBJECT -public: - explicit QAndroidTextureVideoOutput(QObject *parent = 0); - ~QAndroidTextureVideoOutput() override; - - QVideoSink *surface() const; - void setSurface(QVideoSink *surface); - - AndroidSurfaceTexture *surfaceTexture() override; - - bool isReady() override; - void setVideoSize(const QSize &) override; - void stop() override; - void reset() override; - - void setSubtitle(const QString &subtitle); -private Q_SLOTS: - void onFrameAvailable(); - -private: - void initSurfaceTexture(); - bool renderAndReadbackFrame(); - void ensureExternalTexture(QRhi *rhi); - - QMutex m_mutex; - QReadWriteLock m_subtitleLock; - - void clearSurfaceTexture(); - - QVideoSink *m_sink = nullptr; - QSize m_nativeSize; - - AndroidSurfaceTexture *m_surfaceTexture = nullptr; - - QRhiTexture *m_externalTex = nullptr; - - QRhi *m_readbackRhi = nullptr; - QOffscreenSurface *m_readbackRhiFallbackSurface = nullptr; - QRhiTexture *m_readbackSrc = nullptr; - QRhiTexture *m_readbackTex = nullptr; - QRhiBuffer *m_readbackVBuf = nullptr; - QRhiBuffer *m_readbackUBuf = nullptr; - QRhiSampler *m_externalTexSampler = nullptr; - QRhiShaderResourceBindings *m_readbackSrb = nullptr; - QRhiTextureRenderTarget *m_readbackRenderTarget = nullptr; - QRhiRenderPassDescriptor *m_readbackRpDesc = nullptr; - QRhiGraphicsPipeline *m_readbackPs = nullptr; - - QImage m_readbackImage; - QByteArray m_readbackImageData; - - QString m_subtitleText; - QPixmap m_subtitlePixmap; - - GraphicsResourceDeleter *m_graphicsDeleter = nullptr; - - friend class AndroidTextureVideoBuffer; -}; - - -class AndroidTextureVideoBuffer : public QAbstractVideoBuffer -{ -public: - AndroidTextureVideoBuffer(QRhi *rhi, QAndroidTextureVideoOutput *output, const QSize &size) - : QAbstractVideoBuffer(rhi ? QVideoFrame::RhiTextureHandle : QVideoFrame::NoHandle, rhi) - , m_output(output) - , m_size(size) - { - } - - virtual ~AndroidTextureVideoBuffer() {} - - QVideoFrame::MapMode mapMode() const override { return m_mapMode; } - - MapData map(QVideoFrame::MapMode mode) override; - - void unmap() override - { - m_image = QImage(); - m_mapMode = QVideoFrame::NotMapped; - } - - quint64 textureHandle(int plane) const override; - - QMatrix4x4 externalTextureMatrix() const - { - return m_externalMatrix; - } - -private: - bool updateReadbackFrame(); - - QVideoFrame::MapMode m_mapMode = QVideoFrame::NotMapped; - QAndroidTextureVideoOutput *m_output = nullptr; - QImage m_image; - QSize m_size; - mutable QMatrix4x4 m_externalMatrix; - bool m_textureUpdated = false; -}; - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(QList<QRhiResource *>) -Q_DECLARE_METATYPE(QRhi*) - -#endif // QANDROIDVIDEOOUTPUT_H diff --git a/src/multimedia/platform/android/common/qandroidvideosink.cpp b/src/multimedia/platform/android/common/qandroidvideosink.cpp deleted file mode 100644 index 7cc0fefe4..000000000 --- a/src/multimedia/platform/android/common/qandroidvideosink.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qandroidvideosink_p.h" -#include <QtGui/private/qrhi_p.h> - -#include <QtCore/qdebug.h> - -#include <QtCore/qloggingcategory.h> - -QT_BEGIN_NAMESPACE - -Q_LOGGING_CATEGORY(qLcMediaVideoSink, "qt.multimedia.videosink") - -QAndroidVideoSink::QAndroidVideoSink(QVideoSink *parent) - : QPlatformVideoSink(parent) -{ -} - -QAndroidVideoSink::~QAndroidVideoSink() -{ -} - -void QAndroidVideoSink::setRhi(QRhi *rhi) -{ - if (rhi && rhi->backend() != QRhi::OpenGLES2) - rhi = nullptr; - if (m_rhi == rhi) - return; - - m_rhi = rhi; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/common/qandroidvideosink_p.h b/src/multimedia/platform/android/common/qandroidvideosink_p.h deleted file mode 100644 index d653234f1..000000000 --- a/src/multimedia/platform/android/common/qandroidvideosink_p.h +++ /dev/null @@ -1,77 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QANDROIDVIDEOSINK_P_H -#define QANDROIDVIDEOSINK_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qtmultimediaglobal_p.h> -#include <private/qplatformvideosink_p.h> - -#include <qvideosink.h> - -QT_BEGIN_NAMESPACE - -class QAndroidVideoSink - : public QPlatformVideoSink -{ - Q_OBJECT -public: - explicit QAndroidVideoSink(QVideoSink *parent = 0); - ~QAndroidVideoSink(); - - void setRhi(QRhi *rhi) override; - -private: - QRhi *m_rhi = nullptr; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/android/mediacapture/qandroidcamera.cpp b/src/multimedia/platform/android/mediacapture/qandroidcamera.cpp deleted file mode 100644 index 3bcc93564..000000000 --- a/src/multimedia/platform/android/mediacapture/qandroidcamera.cpp +++ /dev/null @@ -1,590 +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$ -** -****************************************************************************/ - -#include "qandroidcamera_p.h" -#include "qandroidcamerasession_p.h" -#include "qandroidcapturesession_p.h" -#include "qandroidmediacapturesession_p.h" -#include <qmediadevices.h> -#include <qcameradevice.h> -#include <qtimer.h> -#include "qandroidmultimediautils_p.h" - -QT_BEGIN_NAMESPACE - -QAndroidCamera::QAndroidCamera(QCamera *camera) - : QPlatformCamera(camera) -{ - Q_ASSERT(camera); -} - -QAndroidCamera::~QAndroidCamera() -{ -} - -void QAndroidCamera::setActive(bool active) -{ - if (m_cameraSession) - m_cameraSession->setActive(active); -} - -bool QAndroidCamera::isActive() const -{ - return m_cameraSession ? m_cameraSession->isActive() : false; -} - -void QAndroidCamera::setCamera(const QCameraDevice &camera) -{ - m_cameraDev = camera; - - if (m_cameraSession) { - int id = 0; - auto cameras = QMediaDevices::videoInputs(); - for (int i = 0; i < cameras.size(); ++i) { - if (cameras.at(i) == camera) { - id = i; - break; - } - } - if (id != m_cameraSession->getSelectedCameraId()) { - m_cameraSession->setSelectedCameraId(id); - reactivateCameraSession(); - } - } -} - -void QAndroidCamera::reactivateCameraSession() -{ - if (m_cameraSession->isActive()) { - if (m_service->captureSession() && - m_service->captureSession()->state() == QMediaRecorder::RecordingState) { - m_service->captureSession()->stop(); - qWarning() << "Changing camera during recording not supported"; - } - m_cameraSession->setActive(false); - m_cameraSession->setActive(true); - } -} - -bool QAndroidCamera::setCameraFormat(const QCameraFormat &format) -{ - m_cameraFormat = format; - - if (m_cameraSession) - m_cameraSession->setCameraFormat(m_cameraFormat); - - return true; -} - -void QAndroidCamera::setCaptureSession(QPlatformMediaCaptureSession *session) -{ - QAndroidMediaCaptureSession *captureSession = static_cast<QAndroidMediaCaptureSession *>(session); - if (m_service == captureSession) - return; - - m_service = captureSession; - if (!m_service) { - disconnect(m_cameraSession,nullptr,this,nullptr); - m_cameraSession = nullptr; - return; - } - - m_cameraSession = m_service->cameraSession(); - Q_ASSERT(m_cameraSession); - if (!m_cameraFormat.isNull()) - m_cameraSession->setCameraFormat(m_cameraFormat); - - setCamera(m_cameraDev); - - connect(m_cameraSession, &QAndroidCameraSession::activeChanged, this, &QAndroidCamera::activeChanged); - connect(m_cameraSession, &QAndroidCameraSession::error, this, &QAndroidCamera::error); - connect(m_cameraSession, &QAndroidCameraSession::opened, this, &QAndroidCamera::onCameraOpened); -} - -void QAndroidCamera::setFocusMode(QCamera::FocusMode mode) -{ - if (!m_cameraSession || !m_cameraSession->camera()) - return; - - if (isFocusModeSupported(mode)) { - QString focusMode; - - switch (mode) { - case QCamera::FocusModeHyperfocal: - focusMode = QLatin1String("edof"); - break; - case QCamera::FocusModeInfinity: // not 100%, but close - focusMode = QLatin1String("infinity"); - break; - case QCamera::FocusModeManual: - focusMode = QLatin1String("fixed"); - break; - case QCamera::FocusModeAutoNear: - focusMode = QLatin1String("macro"); - break; - case QCamera::FocusModeAuto: - case QCamera::FocusModeAutoFar: - if (1) { // ###? - focusMode = QLatin1String("continuous-video"); - } else { - focusMode = QLatin1String("continuous-picture"); - } - break; - } - - m_cameraSession->camera()->setFocusMode(focusMode); - - // reset focus position - m_cameraSession->camera()->cancelAutoFocus(); - - focusModeChanged(mode); - } -} - -bool QAndroidCamera::isFocusModeSupported(QCamera::FocusMode mode) const -{ - return (m_cameraSession && m_cameraSession->camera()) ? m_supportedFocusModes.contains(mode) : false; -} - -void QAndroidCamera::onCameraOpened() -{ - Q_ASSERT(m_cameraSession); - connect(m_cameraSession->camera(), &AndroidCamera::previewSizeChanged, this, &QAndroidCamera::setCameraFocusArea); - - m_supportedFocusModes.clear(); - m_continuousPictureFocusSupported = false; - m_continuousVideoFocusSupported = false; - m_focusPointSupported = false; - - QStringList focusModes = m_cameraSession->camera()->getSupportedFocusModes(); - for (int i = 0; i < focusModes.size(); ++i) { - const QString &focusMode = focusModes.at(i); - if (focusMode == QLatin1String("continuous-picture")) { - m_supportedFocusModes << QCamera::FocusModeAuto; - m_continuousPictureFocusSupported = true; - } else if (focusMode == QLatin1String("continuous-video")) { - m_supportedFocusModes << QCamera::FocusModeAuto; - m_continuousVideoFocusSupported = true; - } else if (focusMode == QLatin1String("edof")) { - m_supportedFocusModes << QCamera::FocusModeHyperfocal; - } else if (focusMode == QLatin1String("fixed")) { - m_supportedFocusModes << QCamera::FocusModeManual; - } else if (focusMode == QLatin1String("infinity")) { - m_supportedFocusModes << QCamera::FocusModeInfinity; - } else if (focusMode == QLatin1String("macro")) { - m_supportedFocusModes << QCamera::FocusModeAutoNear; - } - } - - if (m_cameraSession->camera()->getMaxNumFocusAreas() > 0) - m_focusPointSupported = true; - - auto m = focusMode(); - if (!m_supportedFocusModes.contains(m)) - m = QCamera::FocusModeAuto; - - setFocusMode(m); - setCustomFocusPoint(focusPoint()); - - if (m_cameraSession->camera()->isZoomSupported()) { - m_zoomRatios = m_cameraSession->camera()->getZoomRatios(); - qreal maxZoom = m_zoomRatios.last() / qreal(100); - if (m_maximumZoom != maxZoom) { - m_maximumZoom = maxZoom; - } - zoomTo(1, -1); - } else { - m_zoomRatios.clear(); - m_maximumZoom = 1.0; - } - - m_minExposureCompensationIndex = m_cameraSession->camera()->getMinExposureCompensation(); - m_maxExposureCompensationIndex = m_cameraSession->camera()->getMaxExposureCompensation(); - m_exposureCompensationStep = m_cameraSession->camera()->getExposureCompensationStep(); - exposureCompensationRangeChanged(m_minExposureCompensationIndex*m_exposureCompensationStep, - m_maxExposureCompensationIndex*m_exposureCompensationStep); - - m_supportedExposureModes.clear(); - QStringList sceneModes = m_cameraSession->camera()->getSupportedSceneModes(); - if (!sceneModes.isEmpty()) { - for (int i = 0; i < sceneModes.size(); ++i) { - const QString &sceneMode = sceneModes.at(i); - if (sceneMode == QLatin1String("auto")) - m_supportedExposureModes << QCamera::ExposureAuto; - else if (sceneMode == QLatin1String("beach")) - m_supportedExposureModes << QCamera::ExposureBeach; - else if (sceneMode == QLatin1String("night")) - m_supportedExposureModes << QCamera::ExposureNight; - else if (sceneMode == QLatin1String("portrait")) - m_supportedExposureModes << QCamera::ExposurePortrait; - else if (sceneMode == QLatin1String("snow")) - m_supportedExposureModes << QCamera::ExposureSnow; - else if (sceneMode == QLatin1String("sports")) - m_supportedExposureModes << QCamera::ExposureSports; - else if (sceneMode == QLatin1String("action")) - m_supportedExposureModes << QCamera::ExposureAction; - else if (sceneMode == QLatin1String("landscape")) - m_supportedExposureModes << QCamera::ExposureLandscape; - else if (sceneMode == QLatin1String("night-portrait")) - m_supportedExposureModes << QCamera::ExposureNightPortrait; - else if (sceneMode == QLatin1String("theatre")) - m_supportedExposureModes << QCamera::ExposureTheatre; - else if (sceneMode == QLatin1String("sunset")) - m_supportedExposureModes << QCamera::ExposureSunset; - else if (sceneMode == QLatin1String("steadyphoto")) - m_supportedExposureModes << QCamera::ExposureSteadyPhoto; - else if (sceneMode == QLatin1String("fireworks")) - m_supportedExposureModes << QCamera::ExposureFireworks; - else if (sceneMode == QLatin1String("party")) - m_supportedExposureModes << QCamera::ExposureParty; - else if (sceneMode == QLatin1String("candlelight")) - m_supportedExposureModes << QCamera::ExposureCandlelight; - else if (sceneMode == QLatin1String("barcode")) - m_supportedExposureModes << QCamera::ExposureBarcode; - } - } - - setExposureCompensation(exposureCompensation()); - setExposureMode(exposureMode()); - - isFlashSupported = false; - isFlashAutoSupported = false; - isTorchSupported = false; - - QStringList flashModes = m_cameraSession->camera()->getSupportedFlashModes(); - for (int i = 0; i < flashModes.size(); ++i) { - const QString &flashMode = flashModes.at(i); - if (flashMode == QLatin1String("auto")) - isFlashAutoSupported = true; - else if (flashMode == QLatin1String("on")) - isFlashSupported = true; - else if (flashMode == QLatin1String("torch")) - isTorchSupported = true; - } - - setFlashMode(flashMode()); - - m_supportedWhiteBalanceModes.clear(); - QStringList whiteBalanceModes = m_cameraSession->camera()->getSupportedWhiteBalance(); - for (int i = 0; i < whiteBalanceModes.size(); ++i) { - const QString &wb = whiteBalanceModes.at(i); - if (wb == QLatin1String("auto")) { - m_supportedWhiteBalanceModes.insert(QCamera::WhiteBalanceAuto, - QStringLiteral("auto")); - } else if (wb == QLatin1String("cloudy-daylight")) { - m_supportedWhiteBalanceModes.insert(QCamera::WhiteBalanceCloudy, - QStringLiteral("cloudy-daylight")); - } else if (wb == QLatin1String("daylight")) { - m_supportedWhiteBalanceModes.insert(QCamera::WhiteBalanceSunlight, - QStringLiteral("daylight")); - } else if (wb == QLatin1String("fluorescent")) { - m_supportedWhiteBalanceModes.insert(QCamera::WhiteBalanceFluorescent, - QStringLiteral("fluorescent")); - } else if (wb == QLatin1String("incandescent")) { - m_supportedWhiteBalanceModes.insert(QCamera::WhiteBalanceTungsten, - QStringLiteral("incandescent")); - } else if (wb == QLatin1String("shade")) { - m_supportedWhiteBalanceModes.insert(QCamera::WhiteBalanceShade, - QStringLiteral("shade")); - } else if (wb == QLatin1String("twilight")) { - m_supportedWhiteBalanceModes.insert(QCamera::WhiteBalanceSunset, - QStringLiteral("twilight")); - } else if (wb == QLatin1String("warm-fluorescent")) { - m_supportedWhiteBalanceModes.insert(QCamera::WhiteBalanceFlash, - QStringLiteral("warm-fluorescent")); - } - } - -} - -//void QAndroidCameraFocusControl::onCameraCaptureModeChanged() -//{ -// if (m_cameraSession->camera() && m_focusMode == QCamera::FocusModeAudio) { -// QString focusMode; -// if ((m_cameraSession->captureMode().testFlag(QCamera::CaptureVideo) && m_continuousVideoFocusSupported) -// || !m_continuousPictureFocusSupported) { -// focusMode = QLatin1String("continuous-video"); -// } else { -// focusMode = QLatin1String("continuous-picture"); -// } -// m_cameraSession->camera()->setFocusMode(focusMode); -// m_cameraSession->camera()->cancelAutoFocus(); -// } -//} - -static QRect adjustedArea(const QRectF &area) -{ - // Qt maps focus points in the range (0.0, 0.0) -> (1.0, 1.0) - // Android maps focus points in the range (-1000, -1000) -> (1000, 1000) - // Converts an area in Qt coordinates to Android coordinates - return QRect(-1000 + qRound(area.x() * 2000), - -1000 + qRound(area.y() * 2000), - qRound(area.width() * 2000), - qRound(area.height() * 2000)) - .intersected(QRect(-1000, -1000, 2000, 2000)); -} - -void QAndroidCamera::setCameraFocusArea() -{ - if (!m_cameraSession) - return; - - QList<QRect> areas; - auto focusPoint = customFocusPoint(); - if (QRectF(0., 0., 1., 1.).contains(focusPoint)) { - // in FocusPointAuto mode, leave the area list empty - // to let the driver choose the focus point. - QSize viewportSize = m_cameraSession->camera()->previewSize(); - - if (!viewportSize.isValid()) - return; - - // Set up a 50x50 pixel focus area around the focal point - QSizeF focusSize(50.f / viewportSize.width(), 50.f / viewportSize.height()); - float x = qBound(qreal(0), - focusPoint.x() - (focusSize.width() / 2), - 1.f - focusSize.width()); - float y = qBound(qreal(0), - focusPoint.y() - (focusSize.height() / 2), - 1.f - focusSize.height()); - - QRectF area(QPointF(x, y), focusSize); - - areas.append(adjustedArea(area)); - } - m_cameraSession->camera()->setFocusAreas(areas); -} - -void QAndroidCamera::zoomTo(float factor, float rate) -{ - Q_UNUSED(rate); - - if (zoomFactor() == factor) - return; - - if (!m_cameraSession || !m_cameraSession->camera()) - return; - - factor = qBound(qreal(1), factor, maxZoomFactor()); - int validZoomIndex = qt_findClosestValue(m_zoomRatios, qRound(factor * 100)); - float newZoom = m_zoomRatios.at(validZoomIndex) / qreal(100); - m_cameraSession->camera()->setZoom(validZoomIndex); - zoomFactorChanged(newZoom); -} - -void QAndroidCamera::setFlashMode(QCamera::FlashMode mode) -{ - if (!m_cameraSession || !m_cameraSession->camera()) - return; - - if (!isFlashModeSupported(mode)) - return; - - QString flashMode; - if (mode == QCamera::FlashAuto) - flashMode = QLatin1String("auto"); - else if (mode == QCamera::FlashOn) - flashMode = QLatin1String("on"); - else // FlashOff - flashMode = QLatin1String("off"); - - m_cameraSession->camera()->setFlashMode(flashMode); - flashModeChanged(mode); -} - -bool QAndroidCamera::isFlashModeSupported(QCamera::FlashMode mode) const -{ - if (!m_cameraSession || !m_cameraSession->camera()) - return false; - switch (mode) { - case QCamera::FlashOff: - return true; - case QCamera::FlashOn: - return isFlashSupported; - case QCamera::FlashAuto: - return isFlashAutoSupported; - } -} - -bool QAndroidCamera::isFlashReady() const -{ - // Android doesn't have an API for that - return true; -} - -void QAndroidCamera::setTorchMode(QCamera::TorchMode mode) -{ - if (!m_cameraSession) - return; - auto *camera = m_cameraSession->camera(); - if (!camera || !isTorchSupported || mode == QCamera::TorchAuto) - return; - - if (mode == QCamera::TorchOn) { - camera->setFlashMode(QLatin1String("torch")); - } else if (mode == QCamera::TorchOff) { - // if torch was enabled, it first needs to be turned off before restoring the flash mode - camera->setFlashMode(QLatin1String("off")); - setFlashMode(flashMode()); - } - torchModeChanged(mode); -} - -bool QAndroidCamera::isTorchModeSupported(QCamera::TorchMode mode) const -{ - if (!m_cameraSession || !m_cameraSession->camera()) - return false; - switch (mode) { - case QCamera::TorchOff: - return true; - case QCamera::TorchOn: - return isTorchSupported; - case QCamera::TorchAuto: - return false; - } -} - -void QAndroidCamera::setExposureMode(QCamera::ExposureMode mode) -{ - if (exposureMode() == mode) - return; - - if (!m_cameraSession || !m_cameraSession->camera()) - return; - - if (!m_supportedExposureModes.contains(mode)) - return; - - QString sceneMode; - switch (mode) { - case QCamera::ExposureAuto: - sceneMode = QLatin1String("auto"); - break; - case QCamera::ExposureSports: - sceneMode = QLatin1String("sports"); - break; - case QCamera::ExposurePortrait: - sceneMode = QLatin1String("portrait"); - break; - case QCamera::ExposureBeach: - sceneMode = QLatin1String("beach"); - break; - case QCamera::ExposureSnow: - sceneMode = QLatin1String("snow"); - break; - case QCamera::ExposureNight: - sceneMode = QLatin1String("night"); - break; - case QCamera::ExposureAction: - sceneMode = QLatin1String("action"); - break; - case QCamera::ExposureLandscape: - sceneMode = QLatin1String("landscape"); - break; - case QCamera::ExposureNightPortrait: - sceneMode = QLatin1String("night-portrait"); - break; - case QCamera::ExposureTheatre: - sceneMode = QLatin1String("theatre"); - break; - case QCamera::ExposureSunset: - sceneMode = QLatin1String("sunset"); - break; - case QCamera::ExposureSteadyPhoto: - sceneMode = QLatin1String("steadyphoto"); - break; - case QCamera::ExposureFireworks: - sceneMode = QLatin1String("fireworks"); - break; - case QCamera::ExposureParty: - sceneMode = QLatin1String("party"); - break; - case QCamera::ExposureCandlelight: - sceneMode = QLatin1String("candlelight"); - break; - case QCamera::ExposureBarcode: - sceneMode = QLatin1String("barcode"); - break; - default: - sceneMode = QLatin1String("auto"); - mode = QCamera::ExposureAuto; - break; - } - - m_cameraSession->camera()->setSceneMode(sceneMode); - exposureModeChanged(mode); -} - -bool QAndroidCamera::isExposureModeSupported(QCamera::ExposureMode mode) const -{ - return m_supportedExposureModes.contains(mode); -} - -void QAndroidCamera::setExposureCompensation(float bias) -{ - if (exposureCompensation() == bias || !m_cameraSession || !m_cameraSession->camera()) - return; - - int biasIndex = qRound(bias / m_exposureCompensationStep); - biasIndex = qBound(m_minExposureCompensationIndex, biasIndex, m_maxExposureCompensationIndex); - float comp = biasIndex * m_exposureCompensationStep; - m_cameraSession->camera()->setExposureCompensation(biasIndex); - exposureCompensationChanged(comp); -} - -bool QAndroidCamera::isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const -{ - return m_supportedWhiteBalanceModes.contains(mode); -} - -void QAndroidCamera::setWhiteBalanceMode(QCamera::WhiteBalanceMode mode) -{ - if (!m_cameraSession) - return; - auto *camera = m_cameraSession->camera(); - if (!camera) - return; - QString wb = m_supportedWhiteBalanceModes.value(mode, QString()); - if (!wb.isEmpty()) { - camera->setWhiteBalance(wb); - whiteBalanceModeChanged(mode); - } -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/mediacapture/qandroidcamera_p.h b/src/multimedia/platform/android/mediacapture/qandroidcamera_p.h deleted file mode 100644 index e97368698..000000000 --- a/src/multimedia/platform/android/mediacapture/qandroidcamera_p.h +++ /dev/null @@ -1,133 +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$ -** -****************************************************************************/ - - -#ifndef QANDROIDCAMERACONTROL_H -#define QANDROIDCAMERACONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformcamera_p.h> - -QT_BEGIN_NAMESPACE - -class QAndroidCameraSession; -class QAndroidCameraVideoRendererControl; -class QAndroidMediaCaptureSession; - -class QAndroidCamera : public QPlatformCamera -{ - Q_OBJECT -public: - explicit QAndroidCamera(QCamera *camera); - virtual ~QAndroidCamera(); - - bool isActive() const override; - void setActive(bool active) override; - - void setCamera(const QCameraDevice &camera) override; - bool setCameraFormat(const QCameraFormat &format) override; - - void setCaptureSession(QPlatformMediaCaptureSession *session) override; - - void setFocusMode(QCamera::FocusMode mode) override; - bool isFocusModeSupported(QCamera::FocusMode mode) const override; - - void zoomTo(float factor, float rate) override; - - void setFlashMode(QCamera::FlashMode mode) override; - bool isFlashModeSupported(QCamera::FlashMode mode) const override; - bool isFlashReady() const override; - - void setTorchMode(QCamera::TorchMode mode) override; - bool isTorchModeSupported(QCamera::TorchMode mode) const override; - - void setExposureMode(QCamera::ExposureMode mode) override; - bool isExposureModeSupported(QCamera::ExposureMode mode) const override; - - void setExposureCompensation(float bias) override; - - bool isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const override; - void setWhiteBalanceMode(QCamera::WhiteBalanceMode mode) override; - -private Q_SLOTS: - void onCameraOpened(); - void setCameraFocusArea(); - -private: - void reactivateCameraSession(); - - QAndroidCameraSession *m_cameraSession = nullptr; - QAndroidMediaCaptureSession *m_service = nullptr; - - QList<QCamera::FocusMode> m_supportedFocusModes; - bool m_continuousPictureFocusSupported = false; - bool m_continuousVideoFocusSupported = false; - bool m_focusPointSupported = false; - - float m_maximumZoom; - QList<int> m_zoomRatios; - - QList<QCamera::ExposureMode> m_supportedExposureModes; - int m_minExposureCompensationIndex; - int m_maxExposureCompensationIndex; - qreal m_exposureCompensationStep; - - bool isFlashSupported = false; - bool isFlashAutoSupported = false; - bool isTorchSupported = false; - QCameraDevice m_cameraDev; - - QMap<QCamera::WhiteBalanceMode, QString> m_supportedWhiteBalanceModes; - QCameraFormat m_cameraFormat; -}; - - -QT_END_NAMESPACE - -#endif // QANDROIDCAMERACONTROL_H diff --git a/src/multimedia/platform/android/mediacapture/qandroidcamerasession.cpp b/src/multimedia/platform/android/mediacapture/qandroidcamerasession.cpp deleted file mode 100644 index 38b819352..000000000 --- a/src/multimedia/platform/android/mediacapture/qandroidcamerasession.cpp +++ /dev/null @@ -1,794 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Copyright (C) 2016 Ruslan Baratov -** 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$ -** -****************************************************************************/ - -#include "qandroidcamerasession_p.h" - -#include "androidcamera_p.h" -#include "androidmultimediautils_p.h" -#include "qandroidvideooutput_p.h" -#include "qandroidmultimediautils_p.h" -#include <qvideosink.h> -#include <QtConcurrent/qtconcurrentrun.h> -#include <qfile.h> -#include <qguiapplication.h> -#include <qscreen.h> -#include <qdebug.h> -#include <qvideoframe.h> -#include <private/qplatformimagecapture_p.h> -#include <private/qmemoryvideobuffer_p.h> -#include <private/qcameradevice_p.h> -#include <private/qmediastoragelocation_p.h> - -QT_BEGIN_NAMESPACE - -Q_GLOBAL_STATIC(QList<QCameraDevice>, g_availableCameras) - -QAndroidCameraSession::QAndroidCameraSession(QObject *parent) - : QObject(parent) - , m_selectedCamera(0) - , m_camera(0) - , m_videoOutput(0) - , m_savedState(-1) - , m_previewStarted(false) - , m_lastImageCaptureId(0) - , m_readyForCapture(false) - , m_currentImageCaptureId(-1) - , m_previewCallback(0) - , m_keepActive(false) -{ - if (qApp) { - connect(qApp, &QGuiApplication::applicationStateChanged, - this, &QAndroidCameraSession::onApplicationStateChanged); - - auto screen = qApp->primaryScreen(); - if (screen) { - connect(screen, &QScreen::orientationChanged, this, - &QAndroidCameraSession::updateOrientation); - enableRotation(); - } - } -} - -QAndroidCameraSession::~QAndroidCameraSession() -{ - close(); -} - -//void QAndroidCameraSession::setCaptureMode(QCamera::CaptureModes mode) -//{ -// if (m_captureMode == mode || !isCaptureModeSupported(mode)) -// return; - -// m_captureMode = mode; -// emit captureModeChanged(m_captureMode); - -// if (m_previewStarted && m_captureMode.testFlag(QCamera::CaptureStillImage)) -// applyResolution(m_actualImageSettings.resolution()); -//} - -void QAndroidCameraSession::setActive(bool active) -{ - if (m_active == active) - return; - - m_active = active; - - // If the application is inactive, the camera shouldn't be started. Save the desired state - // instead and it will be set when the application becomes active. - if (qApp->applicationState() == Qt::ApplicationActive) - setActiveHelper(active); - else - m_savedState = active; - - emit activeChanged(m_active); -} - -void QAndroidCameraSession::setActiveHelper(bool active) -{ - if (!active) { - stopPreview(); - close(); - } else { - if (!m_camera && !open()) { - emit error(QCamera::CameraError, QStringLiteral("Failed to open camera")); - return; - } - startPreview(); - } -} - -void QAndroidCameraSession::updateAvailableCameras() -{ - g_availableCameras->clear(); - - const int numCameras = AndroidCamera::getNumberOfCameras(); - for (int i = 0; i < numCameras; ++i) { - QCameraDevicePrivate *info = new QCameraDevicePrivate; - AndroidCamera::getCameraInfo(i, info); - - if (!info->id.isEmpty()) { - AndroidCamera::getSupportedFormats(i, info->videoFormats); - // Add supported picture sizes to the camera info - AndroidCamera *camera = AndroidCamera::open(i); - if (camera) - info->photoResolutions = camera->getSupportedPictureSizes(); - delete camera; - g_availableCameras->append(info->create()); - } - } -} - -const QList<QCameraDevice> &QAndroidCameraSession::availableCameras() -{ - if (g_availableCameras->isEmpty()) - updateAvailableCameras(); - - return *g_availableCameras; -} - -bool QAndroidCameraSession::open() -{ - close(); - - m_camera = AndroidCamera::open(m_selectedCamera); - - if (m_camera) { - connect(m_camera, &AndroidCamera::pictureExposed, - this, &QAndroidCameraSession::onCameraPictureExposed); - connect(m_camera, &AndroidCamera::lastPreviewFrameFetched, - this, &QAndroidCameraSession::onLastPreviewFrameFetched, - Qt::DirectConnection); - connect(m_camera, &AndroidCamera::newPreviewFrame, - this, &QAndroidCameraSession::onNewPreviewFrame, - Qt::DirectConnection); - connect(m_camera, &AndroidCamera::pictureCaptured, - this, &QAndroidCameraSession::onCameraPictureCaptured); - connect(m_camera, &AndroidCamera::previewStarted, - this, &QAndroidCameraSession::onCameraPreviewStarted); - connect(m_camera, &AndroidCamera::previewStopped, - this, &QAndroidCameraSession::onCameraPreviewStopped); - connect(m_camera, &AndroidCamera::previewFailedToStart, - this, &QAndroidCameraSession::onCameraPreviewFailedToStart); - connect(m_camera, &AndroidCamera::takePictureFailed, - this, &QAndroidCameraSession::onCameraTakePictureFailed); - - if (m_camera->getPreviewFormat() != AndroidCamera::NV21) - m_camera->setPreviewFormat(AndroidCamera::NV21); - - m_camera->notifyNewFrames(m_previewCallback); - - emit opened(); - setActive(true); - } - - return m_camera != 0; -} - -void QAndroidCameraSession::close() -{ - if (!m_camera) - return; - - stopPreview(); - - m_readyForCapture = false; - m_currentImageCaptureId = -1; - m_currentImageCaptureFileName.clear(); - m_actualImageSettings = m_requestedImageSettings; - - m_camera->release(); - delete m_camera; - m_camera = 0; - - setActive(false); -} - -void QAndroidCameraSession::setVideoOutput(QAndroidVideoOutput *output) -{ - if (m_videoOutput) { - m_videoOutput->stop(); - m_videoOutput->reset(); - } - - if (output) { - m_videoOutput = output; - if (m_videoOutput->isReady()) { - onVideoOutputReady(true); - } else { - connect(m_videoOutput, &QAndroidVideoOutput::readyChanged, - this, &QAndroidCameraSession::onVideoOutputReady); - } - } else { - m_videoOutput = 0; - } -} - -void QAndroidCameraSession::setCameraFormat(const QCameraFormat &format) -{ - m_requestedFpsRange.min = format.minFrameRate(); - m_requestedFpsRange.max = format.maxFrameRate(); - m_requestedPixelFromat = AndroidCamera::AndroidImageFormatFromQtPixelFormat(format.pixelFormat()); - - m_requestedImageSettings.setResolution(format.resolution()); - m_actualImageSettings.setResolution(format.resolution()); - if (m_readyForCapture) - applyResolution(m_actualImageSettings.resolution()); -} - -void QAndroidCameraSession::applyResolution(const QSize &captureSize, bool restartPreview) -{ - if (!m_camera) - return; - - const QSize currentViewfinderResolution = m_camera->previewSize(); - const AndroidCamera::ImageFormat currentPreviewFormat = m_camera->getPreviewFormat(); - const AndroidCamera::FpsRange currentFpsRange = m_camera->getPreviewFpsRange(); - - // -- adjust resolution - QSize adjustedViewfinderResolution; - const bool validCaptureSize = captureSize.width() > 0 && captureSize.height() > 0; - if (validCaptureSize - && m_camera->getPreferredPreviewSizeForVideo().isEmpty()) { - // According to the Android doc, if getPreferredPreviewSizeForVideo() returns null, it means - // the preview size cannot be different from the capture size - adjustedViewfinderResolution = captureSize; - } else { - qreal captureAspectRatio = 0; - if (validCaptureSize) - captureAspectRatio = qreal(captureSize.width()) / qreal(captureSize.height()); - - const QList<QSize> previewSizes = m_camera->getSupportedPreviewSizes(); - - if (validCaptureSize) { - // search for viewfinder resolution with the same aspect ratio - qreal minAspectDiff = 1; - QSize closestResolution; - for (int i = previewSizes.count() - 1; i >= 0; --i) { - const QSize &size = previewSizes.at(i); - const qreal sizeAspect = qreal(size.width()) / size.height(); - if (qFuzzyCompare(captureAspectRatio, sizeAspect)) { - adjustedViewfinderResolution = size; - break; - } else if (minAspectDiff > qAbs(sizeAspect - captureAspectRatio)) { - closestResolution = size; - minAspectDiff = qAbs(sizeAspect - captureAspectRatio); - } - } - if (!adjustedViewfinderResolution.isValid()) { - qWarning("Cannot find a viewfinder resolution matching the capture aspect ratio."); - if (closestResolution.isValid()) { - adjustedViewfinderResolution = closestResolution; - qWarning("Using closest viewfinder resolution."); - } else { - return; - } - } - } else { - adjustedViewfinderResolution = previewSizes.last(); - } - } - - // -- adjust pixel format - - AndroidCamera::ImageFormat adjustedPreviewFormat = m_requestedPixelFromat; - if (adjustedPreviewFormat == AndroidCamera::UnknownImageFormat) - adjustedPreviewFormat = AndroidCamera::NV21; - - // -- adjust FPS - - AndroidCamera::FpsRange adjustedFps = m_requestedFpsRange;; - if (adjustedFps.min == 0 || adjustedFps.max == 0) - adjustedFps = currentFpsRange; - - // -- Set values on camera - - if (currentViewfinderResolution != adjustedViewfinderResolution - || currentPreviewFormat != adjustedPreviewFormat - || currentFpsRange.min != adjustedFps.min - || currentFpsRange.max != adjustedFps.max) { - - if (m_videoOutput) { - // fix the resolution of output based on the orientation - QSize outputResolution = adjustedViewfinderResolution; - const int rotation = currentCameraRotation(); - if (rotation == 90 || rotation == 270) - outputResolution.transpose(); - m_videoOutput->setVideoSize(outputResolution); - } - - // if preview is started, we have to stop it first before changing its size - if (m_previewStarted && restartPreview) - m_camera->stopPreview(); - - m_camera->setPreviewSize(adjustedViewfinderResolution); - m_camera->setPreviewFormat(adjustedPreviewFormat); - m_camera->setPreviewFpsRange(adjustedFps); - - // restart preview - if (m_previewStarted && restartPreview) - m_camera->startPreview(); - } -} - -QList<QSize> QAndroidCameraSession::getSupportedPreviewSizes() const -{ - return m_camera ? m_camera->getSupportedPreviewSizes() : QList<QSize>(); -} - -QList<QVideoFrameFormat::PixelFormat> QAndroidCameraSession::getSupportedPixelFormats() const -{ - QList<QVideoFrameFormat::PixelFormat> formats; - - if (!m_camera) - return formats; - - const QList<AndroidCamera::ImageFormat> nativeFormats = m_camera->getSupportedPreviewFormats(); - - formats.reserve(nativeFormats.size()); - - for (AndroidCamera::ImageFormat nativeFormat : nativeFormats) { - QVideoFrameFormat::PixelFormat format = AndroidCamera::QtPixelFormatFromAndroidImageFormat(nativeFormat); - if (format != QVideoFrameFormat::Format_Invalid) - formats.append(format); - } - - return formats; -} - -QList<AndroidCamera::FpsRange> QAndroidCameraSession::getSupportedPreviewFpsRange() const -{ - return m_camera ? m_camera->getSupportedPreviewFpsRange() : QList<AndroidCamera::FpsRange>(); -} - - -bool QAndroidCameraSession::startPreview() -{ - if (!m_camera || !m_videoOutput) - return false; - - if (m_previewStarted) - return true; - - if (!m_videoOutput->isReady()) - return true; // delay starting until the video output is ready - - Q_ASSERT(m_videoOutput->surfaceTexture() || m_videoOutput->surfaceHolder()); - - if ((m_videoOutput->surfaceTexture() && !m_camera->setPreviewTexture(m_videoOutput->surfaceTexture())) - || (m_videoOutput->surfaceHolder() && !m_camera->setPreviewDisplay(m_videoOutput->surfaceHolder()))) - return false; - - applyImageSettings(); - applyResolution(m_actualImageSettings.resolution()); - - AndroidMultimediaUtils::enableOrientationListener(true); - - updateOrientation(); - - m_camera->startPreview(); - m_previewStarted = true; - - return true; -} - -void QAndroidCameraSession::stopPreview() -{ - if (!m_camera || !m_previewStarted) - return; - - AndroidMultimediaUtils::enableOrientationListener(false); - - m_camera->stopPreview(); - m_camera->setPreviewSize(QSize()); - m_camera->setPreviewTexture(0); - m_camera->setPreviewDisplay(0); - - if (m_videoOutput) { - m_videoOutput->stop(); - m_videoOutput->reset(); - } - m_previewStarted = false; -} - -void QAndroidCameraSession::setImageSettings(const QImageEncoderSettings &settings) -{ - if (m_requestedImageSettings == settings) - return; - - m_requestedImageSettings = m_actualImageSettings = settings; - - applyImageSettings(); - - if (m_readyForCapture) - applyResolution(m_actualImageSettings.resolution()); -} - -void QAndroidCameraSession::enableRotation() -{ - m_rotationEnabled = true; -} - -void QAndroidCameraSession::disableRotation() -{ - m_rotationEnabled = false; -} - -void QAndroidCameraSession::updateOrientation() -{ - if (!m_camera || !m_rotationEnabled) - return; - - m_camera->setDisplayOrientation(currentCameraRotation()); - applyResolution(m_actualImageSettings.resolution()); -} - - -int QAndroidCameraSession::currentCameraRotation() const -{ - if (!m_camera) - return 0; - - auto screen = QGuiApplication::primaryScreen(); - auto screenOrientation = screen->orientation(); - if (screenOrientation == Qt::PrimaryOrientation) - screenOrientation = screen->primaryOrientation(); - - int deviceOrientation = 0; - switch (screenOrientation) { - case Qt::PrimaryOrientation: - case Qt::PortraitOrientation: - break; - case Qt::LandscapeOrientation: - deviceOrientation = 90; - break; - case Qt::InvertedPortraitOrientation: - deviceOrientation = 180; - break; - case Qt::InvertedLandscapeOrientation: - deviceOrientation = 270; - break; - } - - int nativeCameraOrientation = m_camera->getNativeOrientation(); - - int rotation; - // subtract natural camera orientation and physical device orientation - if (m_camera->getFacing() == AndroidCamera::CameraFacingFront) { - rotation = (nativeCameraOrientation + deviceOrientation) % 360; - rotation = (360 - rotation) % 360; // compensate the mirror - } else { // back-facing camera - rotation = (nativeCameraOrientation - deviceOrientation + 360) % 360; - } - return rotation; -} - -void QAndroidCameraSession::setPreviewFormat(AndroidCamera::ImageFormat format) -{ - if (format == AndroidCamera::UnknownImageFormat) - return; - - m_camera->setPreviewFormat(format); -} - -void QAndroidCameraSession::setPreviewCallback(PreviewCallback *callback) -{ - m_videoFrameCallbackMutex.lock(); - m_previewCallback = callback; - if (m_camera) - m_camera->notifyNewFrames(m_previewCallback); - m_videoFrameCallbackMutex.unlock(); -} - -void QAndroidCameraSession::applyImageSettings() -{ - if (!m_camera) - return; - - // only supported format right now. - m_actualImageSettings.setFormat(QImageCapture::JPEG); - - const QSize requestedResolution = m_requestedImageSettings.resolution(); - const QList<QSize> supportedResolutions = m_camera->getSupportedPictureSizes(); - if (!requestedResolution.isValid()) { - // use the highest supported one - m_actualImageSettings.setResolution(supportedResolutions.last()); - } else if (!supportedResolutions.contains(requestedResolution)) { - // if the requested resolution is not supported, find the closest one - int reqPixelCount = requestedResolution.width() * requestedResolution.height(); - QList<int> supportedPixelCounts; - for (int i = 0; i < supportedResolutions.size(); ++i) { - const QSize &s = supportedResolutions.at(i); - supportedPixelCounts.append(s.width() * s.height()); - } - int closestIndex = qt_findClosestValue(supportedPixelCounts, reqPixelCount); - m_actualImageSettings.setResolution(supportedResolutions.at(closestIndex)); - } - m_camera->setPictureSize(m_actualImageSettings.resolution()); - - int jpegQuality = 100; - switch (m_requestedImageSettings.quality()) { - case QImageCapture::VeryLowQuality: - jpegQuality = 20; - break; - case QImageCapture::LowQuality: - jpegQuality = 40; - break; - case QImageCapture::NormalQuality: - jpegQuality = 60; - break; - case QImageCapture::HighQuality: - jpegQuality = 80; - break; - case QImageCapture::VeryHighQuality: - jpegQuality = 100; - break; - } - m_camera->setJpegQuality(jpegQuality); -} - -bool QAndroidCameraSession::isReadyForCapture() const -{ - return isActive() && m_readyForCapture; -} - -void QAndroidCameraSession::setReadyForCapture(bool ready) -{ - if (m_readyForCapture == ready) - return; - - m_readyForCapture = ready; - emit readyForCaptureChanged(ready); -} - -int QAndroidCameraSession::capture(const QString &fileName) -{ - ++m_lastImageCaptureId; - - if (!isReadyForCapture()) { - emit imageCaptureError(m_lastImageCaptureId, QImageCapture::NotReadyError, - QPlatformImageCapture::msgCameraNotReady()); - return m_lastImageCaptureId; - } - - setReadyForCapture(false); - - m_currentImageCaptureId = m_lastImageCaptureId; - m_currentImageCaptureFileName = fileName; - - applyImageSettings(); - applyResolution(m_actualImageSettings.resolution()); - - // adjust picture rotation depending on the device orientation - m_camera->setRotation(currentCameraRotation()); - - m_camera->takePicture(); - - return m_lastImageCaptureId; -} - -void QAndroidCameraSession::onCameraTakePictureFailed() -{ - emit imageCaptureError(m_currentImageCaptureId, QImageCapture::ResourceError, - tr("Failed to capture image")); - - // Preview needs to be restarted and the preview call back must be setup again - m_camera->startPreview(); -} - -void QAndroidCameraSession::onCameraPictureExposed() -{ - if (!m_camera) - return; - - emit imageExposed(m_currentImageCaptureId); - m_camera->fetchLastPreviewFrame(); -} - -void QAndroidCameraSession::onLastPreviewFrameFetched(const QVideoFrame &frame) -{ - if (!m_camera) - return; - - (void) QtConcurrent::run(&QAndroidCameraSession::processPreviewImage, this, - m_currentImageCaptureId, - frame, - m_camera->getRotation()); -} - -void QAndroidCameraSession::processPreviewImage(int id, const QVideoFrame &frame, int rotation) -{ - // Preview display of front-facing cameras is flipped horizontally, but the frame data - // we get here is not. Flip it ourselves if the camera is front-facing to match what the user - // sees on the viewfinder. - QTransform transform; - if (m_camera->getFacing() == AndroidCamera::CameraFacingFront) - transform.scale(-1, 1); - transform.rotate(rotation); - - emit imageCaptured(id, frame.toImage().transformed(transform)); -} - -void QAndroidCameraSession::onNewPreviewFrame(const QVideoFrame &frame) -{ - if (!m_camera) - return; - - m_videoFrameCallbackMutex.lock(); - - if (m_previewCallback) - m_previewCallback->onFrameAvailable(frame); - - m_videoFrameCallbackMutex.unlock(); -} - -void QAndroidCameraSession::onCameraPictureCaptured(const QByteArray &data) -{ - // Loading and saving the captured image can be slow, do it in a separate thread - (void) QtConcurrent::run(&QAndroidCameraSession::processCapturedImage, this, - m_currentImageCaptureId, - data, - m_actualImageSettings.resolution(), - /* captureToBuffer = */ false, - m_currentImageCaptureFileName); - - // Preview needs to be restarted after taking a picture - if (m_camera) - m_camera->startPreview(); -} - -void QAndroidCameraSession::onCameraPreviewStarted() -{ - setReadyForCapture(true); -} - -void QAndroidCameraSession::onCameraPreviewFailedToStart() -{ - if (isActive()) { - Q_EMIT error(QCamera::CameraError, tr("Camera preview failed to start.")); - - AndroidMultimediaUtils::enableOrientationListener(false); - m_camera->setPreviewSize(QSize()); - m_camera->setPreviewTexture(0); - if (m_videoOutput) { - m_videoOutput->stop(); - m_videoOutput->reset(); - } - m_previewStarted = false; - - setActive(false); - setReadyForCapture(false); - } -} - -void QAndroidCameraSession::onCameraPreviewStopped() -{ - if (!m_previewStarted) - setActive(false); - setReadyForCapture(false); -} - -void QAndroidCameraSession::processCapturedImage(int id, - const QByteArray &data, - const QSize &resolution, - bool captureToBuffer, - const QString &fileName) -{ - - - if (!captureToBuffer) { - const QString actualFileName = QMediaStorageLocation::generateFileName(fileName, QStandardPaths::PicturesLocation, QLatin1String("jpg")); - - QFile file(actualFileName); - if (file.open(QFile::WriteOnly)) { - if (file.write(data) == data.size()) { - // if the picture is saved into the standard picture location, register it - // with the Android media scanner so it appears immediately in apps - // such as the gallery. - if (fileName.isEmpty() || QFileInfo(fileName).isRelative()) - AndroidMultimediaUtils::registerMediaFile(actualFileName); - - emit imageSaved(id, actualFileName); - } else { - emit imageCaptureError(id, QImageCapture::OutOfSpaceError, file.errorString()); - } - } else { - const QString errorMessage = tr("Could not open destination file: %1").arg(actualFileName); - emit imageCaptureError(id, QImageCapture::ResourceError, errorMessage); - } - } else { - QVideoFrame frame(new QMemoryVideoBuffer(data, -1), QVideoFrameFormat(resolution, QVideoFrameFormat::Format_Jpeg)); - emit imageAvailable(id, frame); - } -} - -void QAndroidCameraSession::onVideoOutputReady(bool ready) -{ - if (ready && m_active) - startPreview(); -} - -void QAndroidCameraSession::onApplicationStateChanged(Qt::ApplicationState state) -{ - switch (state) { - case Qt::ApplicationInactive: - if (!m_keepActive && m_active) { - m_savedState = m_active; - close(); - setActive(false); - } - break; - case Qt::ApplicationActive: - if (m_savedState != -1) { - setActiveHelper(m_savedState); - m_savedState = -1; - } - break; - default: - break; - } -} - -bool QAndroidCameraSession::requestCameraPermission() -{ - m_keepActive = true; - const bool result = qt_androidRequestCameraPermission(); - m_keepActive = false; - return result; -} - -void QAndroidCameraSession::setVideoSink(QVideoSink *sink) -{ - if (m_sink == sink) - return; - - m_sink = sink; - - if (m_sink) { - delete m_textureOutput; - m_textureOutput = nullptr; - - m_textureOutput = new QAndroidTextureVideoOutput(this); - m_textureOutput->setSurface(m_sink); - } - - setVideoOutput(m_textureOutput); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/mediacapture/qandroidcamerasession_p.h b/src/multimedia/platform/android/mediacapture/qandroidcamerasession_p.h deleted file mode 100644 index fc3efef26..000000000 --- a/src/multimedia/platform/android/mediacapture/qandroidcamerasession_p.h +++ /dev/null @@ -1,196 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Ruslan Baratov -** 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$ -** -****************************************************************************/ - -#ifndef QANDROIDCAMERASESSION_H -#define QANDROIDCAMERASESSION_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qcamera.h> -#include <QImageCapture> -#include <QSet> -#include <QMutex> -#include <private/qplatformimagecapture_p.h> -#include "androidcamera_p.h" - -QT_BEGIN_NAMESPACE - -class QAndroidVideoOutput; -class QAndroidTextureVideoOutput ; -class QVideoSink; - -class QAndroidCameraSession : public QObject -{ - Q_OBJECT -public: - explicit QAndroidCameraSession(QObject *parent = 0); - ~QAndroidCameraSession(); - - static const QList<QCameraDevice> &availableCameras(); - - void setSelectedCameraId(int cameraId) { m_selectedCamera = cameraId; } - int getSelectedCameraId() { return m_selectedCamera; } - AndroidCamera *camera() const { return m_camera; } - - bool isActive() const { return m_active; } - void setActive(bool active); - - void applyResolution(const QSize &captureSize = QSize(), bool restartPreview = true); - - QAndroidVideoOutput *videoOutput() const { return m_videoOutput; } - void setVideoOutput(QAndroidVideoOutput *output); - - void setCameraFormat(const QCameraFormat &format); - - QList<QSize> getSupportedPreviewSizes() const; - QList<QVideoFrameFormat::PixelFormat> getSupportedPixelFormats() const; - QList<AndroidCamera::FpsRange> getSupportedPreviewFpsRange() const; - - QImageEncoderSettings imageSettings() const { return m_actualImageSettings; } - void setImageSettings(const QImageEncoderSettings &settings); - - bool isReadyForCapture() const; - void setReadyForCapture(bool ready); - int capture(const QString &fileName); - - int currentCameraRotation() const; - - void setPreviewFormat(AndroidCamera::ImageFormat format); - - struct PreviewCallback - { - virtual void onFrameAvailable(const QVideoFrame &frame) = 0; - }; - void setPreviewCallback(PreviewCallback *callback); - bool requestCameraPermission(); - - void setVideoSink(QVideoSink *surface); - - void disableRotation(); - void enableRotation(); - -Q_SIGNALS: - void activeChanged(bool); - void error(int error, const QString &errorString); - void opened(); - - void readyForCaptureChanged(bool); - void imageExposed(int id); - void imageCaptured(int id, const QImage &preview); - void imageMetadataAvailable(int id, const QMediaMetaData &key); - void imageAvailable(int id, const QVideoFrame &buffer); - void imageSaved(int id, const QString &fileName); - void imageCaptureError(int id, int error, const QString &errorString); - -private Q_SLOTS: - void onVideoOutputReady(bool ready); - void updateOrientation(); - - void onApplicationStateChanged(Qt::ApplicationState state); - - void onCameraTakePictureFailed(); - void onCameraPictureExposed(); - void onCameraPictureCaptured(const QByteArray &data); - void onLastPreviewFrameFetched(const QVideoFrame &frame); - void onNewPreviewFrame(const QVideoFrame &frame); - void onCameraPreviewStarted(); - void onCameraPreviewFailedToStart(); - void onCameraPreviewStopped(); - -private: - static void updateAvailableCameras(); - - bool open(); - void close(); - - bool startPreview(); - void stopPreview(); - - void applyImageSettings(); - - void processPreviewImage(int id, const QVideoFrame &frame, int rotation); - void processCapturedImage(int id, - const QByteArray &data, - const QSize &resolution, - bool captureToBuffer, - const QString &fileName); - - void setActiveHelper(bool active); - - int m_selectedCamera; - AndroidCamera *m_camera; - QAndroidVideoOutput *m_videoOutput; - - bool m_active = false; - int m_savedState = -1; - bool m_previewStarted; - - bool m_rotationEnabled = false; - - QVideoSink *m_sink = nullptr; - QAndroidTextureVideoOutput *m_textureOutput = nullptr; - - QImageEncoderSettings m_requestedImageSettings; - QImageEncoderSettings m_actualImageSettings; - AndroidCamera::FpsRange m_requestedFpsRange; - AndroidCamera::ImageFormat m_requestedPixelFromat; - - int m_lastImageCaptureId; - bool m_readyForCapture; - int m_currentImageCaptureId; - QString m_currentImageCaptureFileName; - - QMutex m_videoFrameCallbackMutex; - PreviewCallback *m_previewCallback; - bool m_keepActive; -}; - -QT_END_NAMESPACE - -#endif // QANDROIDCAMERASESSION_H diff --git a/src/multimedia/platform/android/mediacapture/qandroidcapturesession.cpp b/src/multimedia/platform/android/mediacapture/qandroidcapturesession.cpp deleted file mode 100644 index 913a75ee9..000000000 --- a/src/multimedia/platform/android/mediacapture/qandroidcapturesession.cpp +++ /dev/null @@ -1,472 +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$ -** -****************************************************************************/ - -#include "qandroidcapturesession_p.h" - -#include "androidcamera_p.h" -#include "qandroidcamerasession_p.h" -#include "androidmediaplayer_p.h" -#include "androidmultimediautils_p.h" -#include "qandroidmultimediautils_p.h" -#include "qandroidvideooutput_p.h" -#include "qandroidglobal_p.h" -#include <private/qplatformaudioinput_p.h> -#include <private/qplatformaudiooutput_p.h> -#include <private/qmediarecorder_p.h> -#include <private/qmediastoragelocation_p.h> -#include <QtCore/qmimetype.h> - -#include <algorithm> - -QT_BEGIN_NAMESPACE - -QAndroidCaptureSession::QAndroidCaptureSession() - : QObject() - , m_mediaRecorder(0) - , m_cameraSession(0) - , m_duration(0) - , m_state(QMediaRecorder::StoppedState) - , m_outputFormat(AndroidMediaRecorder::DefaultOutputFormat) - , m_audioEncoder(AndroidMediaRecorder::DefaultAudioEncoder) - , m_videoEncoder(AndroidMediaRecorder::DefaultVideoEncoder) -{ - m_notifyTimer.setInterval(1000); - connect(&m_notifyTimer, &QTimer::timeout, this, &QAndroidCaptureSession::updateDuration); -} - -QAndroidCaptureSession::~QAndroidCaptureSession() -{ - stop(); - delete m_mediaRecorder; -} - -void QAndroidCaptureSession::setCameraSession(QAndroidCameraSession *cameraSession) -{ - if (m_cameraSession) { - disconnect(m_connOpenCamera); - disconnect(m_connActiveChangedCamera); - } - - m_cameraSession = cameraSession; - if (m_cameraSession) { - m_connOpenCamera = connect(cameraSession, &QAndroidCameraSession::opened, - this, &QAndroidCaptureSession::onCameraOpened); - m_connActiveChangedCamera = connect(cameraSession, &QAndroidCameraSession::activeChanged, - this, [this](bool isActive) { - if (!isActive) - stop(); - }); - - // It requests permission on setAudioInput instead of on start because asking for - // permission can pause the activity and android can release the surface referenced - // by camera crashing the app when mediaRecorder tries to use it - // TODO: https://bugreports.qt.io/browse/QTBUG-96346 - m_cameraSession->requestCameraPermission(); - } -} - -void QAndroidCaptureSession::setAudioInput(QPlatformAudioInput *input) -{ - m_audioInput = input; - - // It requests permission on setAudioInput instead of on start because asking for - // permission can pause the activity and android can release the surface referenced - // by camera crashing the app when mediaRecorder tries to use it - // TODO: https://bugreports.qt.io/browse/QTBUG-96346 - qt_androidRequestRecordingPermission(); -} - -void QAndroidCaptureSession::setAudioOutput(QPlatformAudioOutput *output) -{ - if (m_audioOutput == output) - return; - - m_audioOutput = output; - - if (m_audioOutput) - AndroidMediaPlayer::setAudioOutput(m_audioOutput->device.id()); -} - -QMediaRecorder::RecorderState QAndroidCaptureSession::state() const -{ - return m_state; -} - -void QAndroidCaptureSession::start(QMediaEncoderSettings &settings, const QUrl &outputLocation) -{ - if (m_state == QMediaRecorder::RecordingState) - return; - - if (m_mediaRecorder) { - m_mediaRecorder->release(); - delete m_mediaRecorder; - m_mediaRecorder = nullptr; - } - - if (!m_cameraSession && !m_audioInput) { - Q_EMIT error(QMediaRecorder::ResourceError, QLatin1String("No devices are set")); - return; - } - - applySettings(settings); - - m_mediaRecorder = new AndroidMediaRecorder; - connect(m_mediaRecorder, &AndroidMediaRecorder::error, this, &QAndroidCaptureSession::onError); - connect(m_mediaRecorder, &AndroidMediaRecorder::info, this, &QAndroidCaptureSession::onInfo); - - // Set audio/video sources - if (m_cameraSession) { - if (!qt_androidCheckCameraPermission()) { - Q_EMIT error(QMediaRecorder::ResourceError, QLatin1String("Camera permission denied.")); - return; - } - - m_cameraSession->camera()->stopPreviewSynchronous(); - m_cameraSession->applyResolution(settings.videoResolution(), false); - m_cameraSession->camera()->unlock(); - m_mediaRecorder->setCamera(m_cameraSession->camera()); - m_mediaRecorder->setAudioSource(AndroidMediaRecorder::Camcorder); - m_mediaRecorder->setVideoSource(AndroidMediaRecorder::Camera); - } - - if (m_audioInput) { - if (!qt_androidCheckMicrophonePermission()) { - Q_EMIT error(QMediaRecorder::ResourceError, - QLatin1String("Microphone permission denied.")); - return; - } - - m_mediaRecorder->setAudioInput(m_audioInput->device.id()); - if (!m_mediaRecorder->isAudioSourceSet()) - m_mediaRecorder->setAudioSource(AndroidMediaRecorder::DefaultAudioSource); - } - - // Set output format - m_mediaRecorder->setOutputFormat(m_outputFormat); - - // Set audio encoder settings - m_mediaRecorder->setAudioChannels(settings.audioChannelCount()); - m_mediaRecorder->setAudioEncodingBitRate(settings.audioBitRate()); - m_mediaRecorder->setAudioSamplingRate(settings.audioSampleRate()); - m_mediaRecorder->setAudioEncoder(m_audioEncoder); - - // Set video encoder settings - if (m_cameraSession) { - m_mediaRecorder->setVideoSize(settings.videoResolution()); - m_mediaRecorder->setVideoFrameRate(qRound(settings.videoFrameRate())); - m_mediaRecorder->setVideoEncodingBitRate(settings.videoBitRate()); - m_mediaRecorder->setVideoEncoder(m_videoEncoder); - - m_mediaRecorder->setOrientationHint(m_cameraSession->currentCameraRotation()); - } - - QString extension = settings.mimeType().preferredSuffix(); - - // Set output file - auto location = outputLocation.toString(QUrl::PreferLocalFile); - auto filePath = QMediaStorageLocation::generateFileName( - location, m_cameraSession ? QStandardPaths::MoviesLocation : QStandardPaths::MusicLocation, extension); - - m_usedOutputLocation = QUrl::fromLocalFile(filePath); - m_outputLocationIsStandard = location.isEmpty() || QFileInfo(location).isRelative(); - m_mediaRecorder->setOutputFile(filePath); - - // Even though the Android doc explicitly says that calling MediaRecorder.setPreviewDisplay() - // is not necessary when the Camera already has a Surface, it doesn't actually work on some - // devices. For example on the Samsung Galaxy Tab 2, the camera server dies after prepare() - // and start() if MediaRecorder.setPreviewDispaly() is not called. - if (m_cameraSession) { - // When using a SurfaceTexture, we need to pass a new one to the MediaRecorder, not the same - // one that is set on the Camera or it will crash, hence the reset(). - m_cameraSession->videoOutput()->reset(); - if (m_cameraSession->videoOutput()->surfaceTexture()) - m_mediaRecorder->setSurfaceTexture(m_cameraSession->videoOutput()->surfaceTexture()); - else if (m_cameraSession->videoOutput()->surfaceHolder()) - m_mediaRecorder->setSurfaceHolder(m_cameraSession->videoOutput()->surfaceHolder()); - - m_cameraSession->disableRotation(); - } - - if (!m_mediaRecorder->prepare()) { - emit error(QMediaRecorder::FormatError, QLatin1String("Unable to prepare the media recorder.")); - if (m_cameraSession) - restartViewfinder(); - return; - } - - if (!m_mediaRecorder->start()) { - emit error(QMediaRecorder::FormatError, - QMediaRecorderPrivate::msgFailedStartRecording()); - if (m_cameraSession) - restartViewfinder(); - return; - } - - m_elapsedTime.start(); - m_notifyTimer.start(); - updateDuration(); - - if (m_cameraSession) { - m_cameraSession->setReadyForCapture(false); - - // Preview frame callback is cleared when setting up the camera with the media recorder. - // We need to reset it. - m_cameraSession->camera()->setupPreviewFrameCallback(); - } - - m_state = QMediaRecorder::RecordingState; - emit stateChanged(m_state); -} - -void QAndroidCaptureSession::stop(bool error) -{ - if (m_state == QMediaRecorder::StoppedState || m_mediaRecorder == 0) - return; - - m_mediaRecorder->stop(); - m_notifyTimer.stop(); - updateDuration(); - m_elapsedTime.invalidate(); - m_mediaRecorder->release(); - delete m_mediaRecorder; - m_mediaRecorder = 0; - - if (m_cameraSession && m_cameraSession->isActive()) { - // Viewport needs to be restarted after recording - restartViewfinder(); - } - - if (!error) { - // if the media is saved into the standard media location, register it - // with the Android media scanner so it appears immediately in apps - // such as the gallery. - if (m_outputLocationIsStandard) - AndroidMultimediaUtils::registerMediaFile(m_usedOutputLocation.toLocalFile()); - - emit actualLocationChanged(m_usedOutputLocation); - } - - m_state = QMediaRecorder::StoppedState; - emit stateChanged(m_state); -} - -qint64 QAndroidCaptureSession::duration() const -{ - return m_duration; -} - -void QAndroidCaptureSession::applySettings(QMediaEncoderSettings &settings) -{ - // container settings - auto fileFormat = settings.mediaFormat().fileFormat(); - if (!m_cameraSession && fileFormat == QMediaFormat::AAC) { - m_outputFormat = AndroidMediaRecorder::AAC_ADTS; - } else if (fileFormat == QMediaFormat::Ogg) { - m_outputFormat = AndroidMediaRecorder::OGG; - } else if (fileFormat == QMediaFormat::WebM) { - m_outputFormat = AndroidMediaRecorder::WEBM; -// } else if (fileFormat == QLatin1String("3gp")) { -// m_outputFormat = AndroidMediaRecorder::THREE_GPP; - } else { - // fallback to MP4 - m_outputFormat = AndroidMediaRecorder::MPEG_4; - } - - // audio settings - if (settings.audioChannelCount() <= 0) - settings.setAudioChannelCount(m_defaultSettings.audioChannels); - if (settings.audioBitRate() <= 0) - settings.setAudioBitRate(m_defaultSettings.audioBitRate); - if (settings.audioSampleRate() <= 0) - settings.setAudioSampleRate(m_defaultSettings.audioSampleRate); - - if (settings.audioCodec() == QMediaFormat::AudioCodec::AAC) - m_audioEncoder = AndroidMediaRecorder::AAC; - else if (settings.audioCodec() == QMediaFormat::AudioCodec::Opus) - m_audioEncoder = AndroidMediaRecorder::OPUS; - else if (settings.audioCodec() == QMediaFormat::AudioCodec::Vorbis) - m_audioEncoder = AndroidMediaRecorder::VORBIS; - else - m_audioEncoder = m_defaultSettings.audioEncoder; - - - // video settings - if (m_cameraSession && m_cameraSession->camera()) { - if (settings.videoResolution().isEmpty()) { - settings.setVideoResolution(m_defaultSettings.videoResolution); - } else if (!m_supportedResolutions.contains(settings.videoResolution())) { - // if the requested resolution is not supported, find the closest one - QSize reqSize = settings.videoResolution(); - int reqPixelCount = reqSize.width() * reqSize.height(); - QList<int> supportedPixelCounts; - for (int i = 0; i < m_supportedResolutions.size(); ++i) { - const QSize &s = m_supportedResolutions.at(i); - supportedPixelCounts.append(s.width() * s.height()); - } - int closestIndex = qt_findClosestValue(supportedPixelCounts, reqPixelCount); - settings.setVideoResolution(m_supportedResolutions.at(closestIndex)); - } - - if (settings.videoFrameRate() <= 0) - settings.setVideoFrameRate(m_defaultSettings.videoFrameRate); - if (settings.videoBitRate() <= 0) - settings.setVideoBitRate(m_defaultSettings.videoBitRate); - - if (settings.videoCodec() == QMediaFormat::VideoCodec::H264) - m_videoEncoder = AndroidMediaRecorder::H264; - else if (settings.videoCodec() == QMediaFormat::VideoCodec::H265) - m_videoEncoder = AndroidMediaRecorder::HEVC; - else if (settings.videoCodec() == QMediaFormat::VideoCodec::MPEG4) - m_videoEncoder = AndroidMediaRecorder::MPEG_4_SP; - else - m_videoEncoder = m_defaultSettings.videoEncoder; - - } -} - -void QAndroidCaptureSession::restartViewfinder() -{ - if (!m_cameraSession) - return; - m_cameraSession->camera()->reconnect(); - - // This is not necessary on most devices, but it crashes on some if we don't stop the - // preview and reset the preview display on the camera when recording is over. - m_cameraSession->camera()->stopPreviewSynchronous(); - m_cameraSession->videoOutput()->reset(); - if (m_cameraSession->videoOutput()->surfaceTexture()) - m_cameraSession->camera()->setPreviewTexture(m_cameraSession->videoOutput()->surfaceTexture()); - else if (m_cameraSession->videoOutput()->surfaceHolder()) - m_cameraSession->camera()->setPreviewDisplay(m_cameraSession->videoOutput()->surfaceHolder()); - - m_cameraSession->camera()->startPreview(); - m_cameraSession->setReadyForCapture(true); - m_cameraSession->enableRotation(); -} - -void QAndroidCaptureSession::updateDuration() -{ - if (m_elapsedTime.isValid()) - m_duration = m_elapsedTime.elapsed(); - - emit durationChanged(m_duration); -} - -void QAndroidCaptureSession::onCameraOpened() -{ - m_supportedResolutions.clear(); - m_supportedFramerates.clear(); - - // get supported resolutions from predefined profiles - for (int i = 0; i < 8; ++i) { - CaptureProfile profile = getProfile(i); - if (!profile.isNull) { - if (i == AndroidCamcorderProfile::QUALITY_HIGH) - m_defaultSettings = profile; - - if (!m_supportedResolutions.contains(profile.videoResolution)) - m_supportedResolutions.append(profile.videoResolution); - if (!m_supportedFramerates.contains(profile.videoFrameRate)) - m_supportedFramerates.append(profile.videoFrameRate); - } - } - - std::sort(m_supportedResolutions.begin(), m_supportedResolutions.end(), qt_sizeLessThan); - std::sort(m_supportedFramerates.begin(), m_supportedFramerates.end()); -} - -QAndroidCaptureSession::CaptureProfile QAndroidCaptureSession::getProfile(int id) -{ - CaptureProfile profile; - const bool hasProfile = AndroidCamcorderProfile::hasProfile(m_cameraSession->camera()->cameraId(), - AndroidCamcorderProfile::Quality(id)); - - if (hasProfile) { - AndroidCamcorderProfile camProfile = AndroidCamcorderProfile::get(m_cameraSession->camera()->cameraId(), - AndroidCamcorderProfile::Quality(id)); - - profile.outputFormat = AndroidMediaRecorder::OutputFormat(camProfile.getValue(AndroidCamcorderProfile::fileFormat)); - profile.audioEncoder = AndroidMediaRecorder::AudioEncoder(camProfile.getValue(AndroidCamcorderProfile::audioCodec)); - profile.audioBitRate = camProfile.getValue(AndroidCamcorderProfile::audioBitRate); - profile.audioChannels = camProfile.getValue(AndroidCamcorderProfile::audioChannels); - profile.audioSampleRate = camProfile.getValue(AndroidCamcorderProfile::audioSampleRate); - profile.videoEncoder = AndroidMediaRecorder::VideoEncoder(camProfile.getValue(AndroidCamcorderProfile::videoCodec)); - profile.videoBitRate = camProfile.getValue(AndroidCamcorderProfile::videoBitRate); - profile.videoFrameRate = camProfile.getValue(AndroidCamcorderProfile::videoFrameRate); - profile.videoResolution = QSize(camProfile.getValue(AndroidCamcorderProfile::videoFrameWidth), - camProfile.getValue(AndroidCamcorderProfile::videoFrameHeight)); - - if (profile.outputFormat == AndroidMediaRecorder::MPEG_4) - profile.outputFileExtension = QStringLiteral("mp4"); - else if (profile.outputFormat == AndroidMediaRecorder::THREE_GPP) - profile.outputFileExtension = QStringLiteral("3gp"); - else if (profile.outputFormat == AndroidMediaRecorder::AMR_NB_Format) - profile.outputFileExtension = QStringLiteral("amr"); - else if (profile.outputFormat == AndroidMediaRecorder::AMR_WB_Format) - profile.outputFileExtension = QStringLiteral("awb"); - - profile.isNull = false; - } - - return profile; -} - -void QAndroidCaptureSession::onError(int what, int extra) -{ - Q_UNUSED(what); - Q_UNUSED(extra); - stop(true); - emit error(QMediaRecorder::ResourceError, QLatin1String("Unknown error.")); -} - -void QAndroidCaptureSession::onInfo(int what, int extra) -{ - Q_UNUSED(extra); - if (what == 800) { - // MEDIA_RECORDER_INFO_MAX_DURATION_REACHED - stop(); - emit error(QMediaRecorder::OutOfSpaceError, QLatin1String("Maximum duration reached.")); - } else if (what == 801) { - // MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED - stop(); - emit error(QMediaRecorder::OutOfSpaceError, QLatin1String("Maximum file size reached.")); - } -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/mediacapture/qandroidcapturesession_p.h b/src/multimedia/platform/android/mediacapture/qandroidcapturesession_p.h deleted file mode 100644 index 16b3c8481..000000000 --- a/src/multimedia/platform/android/mediacapture/qandroidcapturesession_p.h +++ /dev/null @@ -1,188 +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$ -** -****************************************************************************/ - -#ifndef QANDROIDCAPTURESESSION_H -#define QANDROIDCAPTURESESSION_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qobject.h> -#include <qmediarecorder.h> -#include <qurl.h> -#include <qelapsedtimer.h> -#include <qtimer.h> -#include "androidmediarecorder_p.h" -#include "qandroidmediaencoder_p.h" - -QT_BEGIN_NAMESPACE - -class QAudioInput; -class QAndroidCameraSession; - -class QAndroidCaptureSession : public QObject -{ - Q_OBJECT -public: - explicit QAndroidCaptureSession(); - ~QAndroidCaptureSession(); - - QList<QSize> supportedResolutions() const { return m_supportedResolutions; } - QList<qreal> supportedFrameRates() const { return m_supportedFramerates; } - - void setCameraSession(QAndroidCameraSession *cameraSession = 0); - void setAudioInput(QPlatformAudioInput *input); - void setAudioOutput(QPlatformAudioOutput *output); - - QMediaRecorder::RecorderState state() const; - - void start(QMediaEncoderSettings &settings, const QUrl &outputLocation); - void stop(bool error = false); - - qint64 duration() const; - - QMediaEncoderSettings encoderSettings() { return m_encoderSettings; } - - void setMediaEncoder(QAndroidMediaEncoder *encoder) { m_mediaEncoder = encoder; } - - void stateChanged(QMediaRecorder::RecorderState state) { - if (m_mediaEncoder) - m_mediaEncoder->stateChanged(state); - } - void durationChanged(qint64 position) - { - if (m_mediaEncoder) - m_mediaEncoder->durationChanged(position); - } - void actualLocationChanged(const QUrl &location) - { - if (m_mediaEncoder) - m_mediaEncoder->actualLocationChanged(location); - } - void error(int error, const QString &errorString) - { - if (m_mediaEncoder) - m_mediaEncoder->error(QMediaRecorder::Error(error), errorString); - } - -private Q_SLOTS: - void updateDuration(); - void onCameraOpened(); - - void onError(int what, int extra); - void onInfo(int what, int extra); - -private: - void applySettings(QMediaEncoderSettings &settings); - - struct CaptureProfile { - AndroidMediaRecorder::OutputFormat outputFormat; - QString outputFileExtension; - - AndroidMediaRecorder::AudioEncoder audioEncoder; - int audioBitRate; - int audioChannels; - int audioSampleRate; - - AndroidMediaRecorder::VideoEncoder videoEncoder; - int videoBitRate; - int videoFrameRate; - QSize videoResolution; - - bool isNull; - - CaptureProfile() - : outputFormat(AndroidMediaRecorder::MPEG_4) - , outputFileExtension(QLatin1String("mp4")) - , audioEncoder(AndroidMediaRecorder::DefaultAudioEncoder) - , audioBitRate(128000) - , audioChannels(2) - , audioSampleRate(44100) - , videoEncoder(AndroidMediaRecorder::DefaultVideoEncoder) - , videoBitRate(1) - , videoFrameRate(-1) - , videoResolution(1280, 720) - , isNull(true) - { } - }; - - CaptureProfile getProfile(int id); - - void restartViewfinder(); - - QAndroidMediaEncoder *m_mediaEncoder = nullptr; - AndroidMediaRecorder *m_mediaRecorder; - QAndroidCameraSession *m_cameraSession; - - QPlatformAudioInput *m_audioInput = nullptr; - QPlatformAudioOutput *m_audioOutput = nullptr; - - QElapsedTimer m_elapsedTime; - QTimer m_notifyTimer; - qint64 m_duration; - - QMediaRecorder::RecorderState m_state; - QUrl m_usedOutputLocation; - bool m_outputLocationIsStandard = false; - - CaptureProfile m_defaultSettings; - - QMediaEncoderSettings m_encoderSettings; - AndroidMediaRecorder::OutputFormat m_outputFormat; - AndroidMediaRecorder::AudioEncoder m_audioEncoder; - AndroidMediaRecorder::VideoEncoder m_videoEncoder; - - QList<QSize> m_supportedResolutions; - QList<qreal> m_supportedFramerates; - - QMetaObject::Connection m_connOpenCamera; - QMetaObject::Connection m_connActiveChangedCamera; -}; - -QT_END_NAMESPACE - -#endif // QANDROIDCAPTURESESSION_H diff --git a/src/multimedia/platform/android/mediacapture/qandroidimagecapture.cpp b/src/multimedia/platform/android/mediacapture/qandroidimagecapture.cpp deleted file mode 100644 index 32e787290..000000000 --- a/src/multimedia/platform/android/mediacapture/qandroidimagecapture.cpp +++ /dev/null @@ -1,113 +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$ -** -****************************************************************************/ - -#include "qandroidimagecapture_p.h" - -#include "qandroidcamerasession_p.h" -#include "qandroidmediacapturesession_p.h" - -QT_BEGIN_NAMESPACE - -QAndroidImageCapture::QAndroidImageCapture(QImageCapture *parent) - : QPlatformImageCapture(parent) -{ -} - -bool QAndroidImageCapture::isReadyForCapture() const -{ - return m_session->isReadyForCapture(); -} - -int QAndroidImageCapture::capture(const QString &fileName) -{ - return m_session->capture(fileName); -} - -int QAndroidImageCapture::captureToBuffer() -{ - // ### implement me! - const QLatin1String errorMessage("Capturing to buffer not supported."); - QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, - Q_ARG(int, -1), - Q_ARG(int, QImageCapture::NotSupportedFeatureError), - Q_ARG(QString, errorMessage)); - return -1; -} - -QImageEncoderSettings QAndroidImageCapture::imageSettings() const -{ - return m_session->imageSettings(); -} - -void QAndroidImageCapture::setImageSettings(const QImageEncoderSettings &settings) -{ - m_session->setImageSettings(settings); -} - -void QAndroidImageCapture::setCaptureSession(QPlatformMediaCaptureSession *session) -{ - QAndroidMediaCaptureSession *captureSession = static_cast<QAndroidMediaCaptureSession *>(session); - if (m_service == captureSession) - return; - - m_service = captureSession; - if (!m_service) { - disconnect(m_session, nullptr, this, nullptr); - return; - } - - m_session = m_service->cameraSession(); - Q_ASSERT(m_session); - - connect(m_session, &QAndroidCameraSession::readyForCaptureChanged, - this, &QAndroidImageCapture::readyForCaptureChanged); - connect(m_session, &QAndroidCameraSession::imageExposed, - this, &QAndroidImageCapture::imageExposed); - connect(m_session, &QAndroidCameraSession::imageCaptured, - this, &QAndroidImageCapture::imageCaptured); - connect(m_session, &QAndroidCameraSession::imageMetadataAvailable, - this, &QAndroidImageCapture::imageMetadataAvailable); - connect(m_session, &QAndroidCameraSession::imageAvailable, - this, &QAndroidImageCapture::imageAvailable); - connect(m_session, &QAndroidCameraSession::imageSaved, - this, &QAndroidImageCapture::imageSaved); - connect(m_session, &QAndroidCameraSession::imageCaptureError, - this, &QAndroidImageCapture::error); -} -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/mediacapture/qandroidimagecapture_p.h b/src/multimedia/platform/android/mediacapture/qandroidimagecapture_p.h deleted file mode 100644 index 83d15445f..000000000 --- a/src/multimedia/platform/android/mediacapture/qandroidimagecapture_p.h +++ /dev/null @@ -1,84 +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$ -** -****************************************************************************/ - -#ifndef QANDROIDCAMERAIMAGECAPTURECONTROL_H -#define QANDROIDCAMERAIMAGECAPTURECONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformimagecapture_p.h> - -QT_BEGIN_NAMESPACE - -class QAndroidCameraSession; -class QAndroidMediaCaptureSession; - -class QAndroidImageCapture : public QPlatformImageCapture -{ - Q_OBJECT -public: - explicit QAndroidImageCapture(QImageCapture *parent = nullptr); - - bool isReadyForCapture() const override; - - int capture(const QString &fileName) override; - int captureToBuffer() override; - - QImageEncoderSettings imageSettings() const override; - void setImageSettings(const QImageEncoderSettings &settings) override; - - void setCaptureSession(QPlatformMediaCaptureSession *session); - -private: - QAndroidCameraSession *m_session; - QAndroidMediaCaptureSession *m_service; -}; - -QT_END_NAMESPACE - -#endif // QANDROIDCAMERAIMAGECAPTURECONTROL_H diff --git a/src/multimedia/platform/android/mediacapture/qandroidmediacapturesession.cpp b/src/multimedia/platform/android/mediacapture/qandroidmediacapturesession.cpp deleted file mode 100644 index 94207c8a9..000000000 --- a/src/multimedia/platform/android/mediacapture/qandroidmediacapturesession.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Ruslan Baratov -** 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$ -** -****************************************************************************/ - -#include "qandroidmediacapturesession_p.h" - -#include "qandroidmediaencoder_p.h" -#include "qandroidcapturesession_p.h" -#include "qandroidcamera_p.h" -#include "qandroidcamerasession_p.h" -#include "qandroidimagecapture_p.h" -#include "qmediadevices.h" -#include "qaudiodevice.h" - -QT_BEGIN_NAMESPACE - -QAndroidMediaCaptureSession::QAndroidMediaCaptureSession() - : m_captureSession(new QAndroidCaptureSession()) - , m_cameraSession(new QAndroidCameraSession()) -{ -} - -QAndroidMediaCaptureSession::~QAndroidMediaCaptureSession() -{ - delete m_encoder; - delete m_captureSession; - delete m_cameraControl; - delete m_imageCaptureControl; - delete m_cameraSession; -} - -QPlatformCamera *QAndroidMediaCaptureSession::camera() -{ - return m_cameraControl; -} - -void QAndroidMediaCaptureSession::setCamera(QPlatformCamera *camera) -{ - if (camera) { - m_captureSession->setCameraSession(m_cameraSession); - } else { - m_captureSession->setCameraSession(nullptr); - } - - QAndroidCamera *control = static_cast<QAndroidCamera *>(camera); - if (m_cameraControl == control) - return; - - if (m_cameraControl) - m_cameraControl->setCaptureSession(nullptr); - - m_cameraControl = control; - if (m_cameraControl) - m_cameraControl->setCaptureSession(this); - - emit cameraChanged(); -} - -QPlatformImageCapture *QAndroidMediaCaptureSession::imageCapture() -{ - return m_imageCaptureControl; -} - -void QAndroidMediaCaptureSession::setImageCapture(QPlatformImageCapture *imageCapture) -{ - QAndroidImageCapture *control = static_cast<QAndroidImageCapture *>(imageCapture); - if (m_imageCaptureControl == control) - return; - - if (m_imageCaptureControl) - m_imageCaptureControl->setCaptureSession(nullptr); - - m_imageCaptureControl = control; - if (m_imageCaptureControl) - m_imageCaptureControl->setCaptureSession(this); -} - -QPlatformMediaEncoder *QAndroidMediaCaptureSession::mediaEncoder() -{ - return m_encoder; -} - -void QAndroidMediaCaptureSession::setMediaEncoder(QPlatformMediaEncoder *encoder) -{ - QAndroidMediaEncoder *control = static_cast<QAndroidMediaEncoder *>(encoder); - - if (m_encoder == control) - return; - - if (m_encoder) - m_encoder->setCaptureSession(nullptr); - - m_encoder = control; - if (m_encoder) - m_encoder->setCaptureSession(this); - - emit encoderChanged(); - -} - -void QAndroidMediaCaptureSession::setAudioInput(QPlatformAudioInput *input) -{ - m_captureSession->setAudioInput(input); -} - -void QAndroidMediaCaptureSession::setAudioOutput(QPlatformAudioOutput *output) -{ - m_captureSession->setAudioOutput(output); -} - -void QAndroidMediaCaptureSession::setVideoPreview(QVideoSink *sink) -{ - m_cameraSession->setVideoSink(sink); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/mediacapture/qandroidmediacapturesession_p.h b/src/multimedia/platform/android/mediacapture/qandroidmediacapturesession_p.h deleted file mode 100644 index 667fbba7a..000000000 --- a/src/multimedia/platform/android/mediacapture/qandroidmediacapturesession_p.h +++ /dev/null @@ -1,102 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Ruslan Baratov -** 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$ -** -****************************************************************************/ - -#ifndef QANDROIDCAPTURESERVICE_H -#define QANDROIDCAPTURESERVICE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediacapture_p.h> -#include <private/qplatformmediaintegration_p.h> - -QT_BEGIN_NAMESPACE - -class QAndroidMediaEncoder; -class QAndroidCaptureSession; -class QAndroidCamera; -class QAndroidCameraSession; -class QAndroidImageCapture; - -class QAndroidMediaCaptureSession : public QPlatformMediaCaptureSession -{ - Q_OBJECT - -public: - explicit QAndroidMediaCaptureSession(); - virtual ~QAndroidMediaCaptureSession(); - - QPlatformCamera *camera() override; - void setCamera(QPlatformCamera *camera) override; - - QPlatformImageCapture *imageCapture() override; - void setImageCapture(QPlatformImageCapture *imageCapture) override; - - QPlatformMediaEncoder *mediaEncoder() override; - void setMediaEncoder(QPlatformMediaEncoder *encoder) override; - - void setAudioInput(QPlatformAudioInput *input) override; - - void setVideoPreview(QVideoSink *sink) override; - - void setAudioOutput(QPlatformAudioOutput *output) override; - - QAndroidCaptureSession *captureSession() const { return m_captureSession; } - QAndroidCameraSession *cameraSession() const { return m_cameraSession; } - -private: - QAndroidMediaEncoder *m_encoder = nullptr; - QAndroidCaptureSession *m_captureSession = nullptr; - QAndroidCamera *m_cameraControl = nullptr; - QAndroidCameraSession *m_cameraSession = nullptr; - QAndroidImageCapture *m_imageCaptureControl = nullptr; -}; - -QT_END_NAMESPACE - -#endif // QANDROIDCAPTURESERVICE_H diff --git a/src/multimedia/platform/android/mediacapture/qandroidmediaencoder.cpp b/src/multimedia/platform/android/mediacapture/qandroidmediaencoder.cpp deleted file mode 100644 index 5e7ae990d..000000000 --- a/src/multimedia/platform/android/mediacapture/qandroidmediaencoder.cpp +++ /dev/null @@ -1,108 +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$ -** -****************************************************************************/ - -#include "qandroidmediaencoder_p.h" -#include "qandroidmultimediautils_p.h" -#include "qandroidcapturesession_p.h" -#include "qandroidmediacapturesession_p.h" - -QT_BEGIN_NAMESPACE - -QAndroidMediaEncoder::QAndroidMediaEncoder(QMediaRecorder *parent) - : QPlatformMediaEncoder(parent) -{ -} - -bool QAndroidMediaEncoder::isLocationWritable(const QUrl &location) const -{ - return location.isValid() - && (location.isLocalFile() || location.isRelative()); -} - -QMediaRecorder::RecorderState QAndroidMediaEncoder::state() const -{ - return m_session ? m_session->state() : QMediaRecorder::StoppedState; -} - -qint64 QAndroidMediaEncoder::duration() const -{ - return m_session ? m_session->duration() : 0; - -} - -void QAndroidMediaEncoder::record(QMediaEncoderSettings &settings) -{ - if (m_session) - m_session->start(settings, outputLocation()); -} - -void QAndroidMediaEncoder::stop() -{ - if (m_session) - m_session->stop(); -} - -void QAndroidMediaEncoder::setOutputLocation(const QUrl &location) -{ - if (location.isLocalFile()) { - qt_androidRequestWriteStoragePermission(); - } - QPlatformMediaEncoder::setOutputLocation(location); -} - -void QAndroidMediaEncoder::setCaptureSession(QPlatformMediaCaptureSession *session) -{ - QAndroidMediaCaptureSession *captureSession = static_cast<QAndroidMediaCaptureSession *>(session); - if (m_service == captureSession) - return; - - if (m_service) - stop(); - if (m_session) - m_session->setMediaEncoder(nullptr); - - m_service = captureSession; - if (!m_service) - return; - m_session = m_service->captureSession(); - Q_ASSERT(m_session); - m_session->setMediaEncoder(this); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/mediacapture/qandroidmediaencoder_p.h b/src/multimedia/platform/android/mediacapture/qandroidmediaencoder_p.h deleted file mode 100644 index 6c6160ca9..000000000 --- a/src/multimedia/platform/android/mediacapture/qandroidmediaencoder_p.h +++ /dev/null @@ -1,86 +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$ -** -****************************************************************************/ - -#ifndef QANDROIDMEDIAENCODER_H -#define QANDROIDMEDIAENCODER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediaencoder_p.h> -#include <private/qplatformmediacapture_p.h> - -QT_BEGIN_NAMESPACE - -class QAndroidCaptureSession; -class QAndroidMediaCaptureSession; - -class QAndroidMediaEncoder : public QPlatformMediaEncoder -{ -public: - explicit QAndroidMediaEncoder(QMediaRecorder *parent); - - bool isLocationWritable(const QUrl &location) const override; - QMediaRecorder::RecorderState state() const override; - qint64 duration() const override; - - void setCaptureSession(QPlatformMediaCaptureSession *session); - - void setOutputLocation(const QUrl &location) override; - void record(QMediaEncoderSettings &settings) override; - void stop() override; - -private: - friend class QAndroidCaptureSession; - - QAndroidCaptureSession *m_session = nullptr; - QAndroidMediaCaptureSession *m_service = nullptr; -}; - -QT_END_NAMESPACE - -#endif // QANDROIDMEDIAENCODER_H diff --git a/src/multimedia/platform/android/mediaplayer/qandroidmediaplayer.cpp b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayer.cpp deleted file mode 100644 index 8ae49715e..000000000 --- a/src/multimedia/platform/android/mediaplayer/qandroidmediaplayer.cpp +++ /dev/null @@ -1,983 +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$ -** -****************************************************************************/ - -#include "qandroidmediaplayer_p.h" -#include "androidmediaplayer_p.h" -#include "qandroidvideooutput_p.h" -#include "qandroidmetadata_p.h" -#include "qandroidaudiooutput_p.h" -#include "qaudiooutput.h" - -QT_BEGIN_NAMESPACE - -Q_LOGGING_CATEGORY(lcMediaPlayer, "qt.multimedia.mediaplayer.android") - -class StateChangeNotifier -{ -public: - StateChangeNotifier(QAndroidMediaPlayer *mp) - : mControl(mp) - , mPreviousState(mp->state()) - , mPreviousMediaStatus(mp->mediaStatus()) - { - ++mControl->mActiveStateChangeNotifiers; - } - - ~StateChangeNotifier() - { - if (--mControl->mActiveStateChangeNotifiers) - return; - - if (mPreviousMediaStatus != mControl->mediaStatus()) - Q_EMIT mControl->mediaStatusChanged(mControl->mediaStatus()); - - if (mPreviousState != mControl->state()) - Q_EMIT mControl->stateChanged(mControl->state()); - } - -private: - QAndroidMediaPlayer *mControl; - QMediaPlayer::PlaybackState mPreviousState; - QMediaPlayer::MediaStatus mPreviousMediaStatus; -}; - -QAndroidMediaPlayer::QAndroidMediaPlayer(QMediaPlayer *parent) - : QPlatformMediaPlayer(parent), - mMediaPlayer(new AndroidMediaPlayer), - mState(AndroidMediaPlayer::Uninitialized) -{ - connect(mMediaPlayer, &AndroidMediaPlayer::bufferingChanged, this, - &QAndroidMediaPlayer::onBufferingChanged); - connect(mMediaPlayer, &AndroidMediaPlayer::info, this, &QAndroidMediaPlayer::onInfo); - connect(mMediaPlayer, &AndroidMediaPlayer::error, this, &QAndroidMediaPlayer::onError); - connect(mMediaPlayer, &AndroidMediaPlayer::stateChanged, this, - &QAndroidMediaPlayer::onStateChanged); - connect(mMediaPlayer, &AndroidMediaPlayer::videoSizeChanged, this, - &QAndroidMediaPlayer::onVideoSizeChanged); - connect(mMediaPlayer, &AndroidMediaPlayer::progressChanged, this, - &QAndroidMediaPlayer::positionChanged); - connect(mMediaPlayer, &AndroidMediaPlayer::durationChanged, this, - &QAndroidMediaPlayer::durationChanged); - connect(mMediaPlayer, &AndroidMediaPlayer::tracksInfoChanged, this, - &QAndroidMediaPlayer::updateTrackInfo); -} - -QAndroidMediaPlayer::~QAndroidMediaPlayer() -{ - mMediaPlayer->release(); - delete mMediaPlayer; -} - -qint64 QAndroidMediaPlayer::duration() const -{ - if ((mState & (AndroidMediaPlayer::Prepared - | AndroidMediaPlayer::Started - | AndroidMediaPlayer::Paused - | AndroidMediaPlayer::Stopped - | AndroidMediaPlayer::PlaybackCompleted)) == 0) { - return 0; - } - - return mMediaPlayer->getDuration(); -} - -qint64 QAndroidMediaPlayer::position() const -{ - if (mediaStatus() == QMediaPlayer::EndOfMedia) - return duration(); - - if ((mState & (AndroidMediaPlayer::Prepared - | AndroidMediaPlayer::Started - | AndroidMediaPlayer::Paused - | AndroidMediaPlayer::PlaybackCompleted))) { - return mMediaPlayer->getCurrentPosition(); - } - - return (mPendingPosition == -1) ? 0 : mPendingPosition; -} - -void QAndroidMediaPlayer::setPosition(qint64 position) -{ - if (!isSeekable()) - return; - - const int seekPosition = (position > INT_MAX) ? INT_MAX : position; - - if (seekPosition == this->position()) - return; - - StateChangeNotifier notifier(this); - - if (mediaStatus() == QMediaPlayer::EndOfMedia) - setMediaStatus(QMediaPlayer::LoadedMedia); - - if ((mState & (AndroidMediaPlayer::Prepared - | AndroidMediaPlayer::Started - | AndroidMediaPlayer::Paused - | AndroidMediaPlayer::PlaybackCompleted)) == 0) { - mPendingPosition = seekPosition; - } else { - mMediaPlayer->seekTo(seekPosition); - - if (mPendingPosition != -1) { - mPendingPosition = -1; - } - } - - Q_EMIT positionChanged(seekPosition); -} - -void QAndroidMediaPlayer::setVolume(float volume) -{ - if ((mState & (AndroidMediaPlayer::Idle - | AndroidMediaPlayer::Initialized - | AndroidMediaPlayer::Stopped - | AndroidMediaPlayer::Prepared - | AndroidMediaPlayer::Started - | AndroidMediaPlayer::Paused - | AndroidMediaPlayer::PlaybackCompleted)) == 0) { - mPendingVolume = volume; - return; - } - - mMediaPlayer->setVolume(qRound(volume*100.)); - mPendingVolume = -1; -} - -void QAndroidMediaPlayer::setMuted(bool muted) -{ - if ((mState & (AndroidMediaPlayer::Idle - | AndroidMediaPlayer::Initialized - | AndroidMediaPlayer::Stopped - | AndroidMediaPlayer::Prepared - | AndroidMediaPlayer::Started - | AndroidMediaPlayer::Paused - | AndroidMediaPlayer::PlaybackCompleted)) == 0) { - mPendingMute = muted; - return; - } - - mMediaPlayer->setMuted(muted); - mPendingMute = -1; -} - -QMediaMetaData QAndroidMediaPlayer::metaData() const -{ - return QAndroidMetaData::extractMetadata(mMediaContent); -} - -float QAndroidMediaPlayer::bufferProgress() const -{ - return mBufferFilled ? 1. : 0; -} - -bool QAndroidMediaPlayer::isAudioAvailable() const -{ - return mAudioAvailable; -} - -bool QAndroidMediaPlayer::isVideoAvailable() const -{ - return mVideoAvailable; -} - -QMediaTimeRange QAndroidMediaPlayer::availablePlaybackRanges() const -{ - return mAvailablePlaybackRange; -} - -void QAndroidMediaPlayer::updateAvailablePlaybackRanges() -{ - if (mBuffering) { - const qint64 pos = position(); - const qint64 end = (duration() / 100) * mBufferPercent; - mAvailablePlaybackRange.addInterval(pos, end); - } else if (isSeekable()) { - mAvailablePlaybackRange = QMediaTimeRange(0, duration()); - } else { - mAvailablePlaybackRange = QMediaTimeRange(); - } - -// #### Q_EMIT availablePlaybackRangesChanged(mAvailablePlaybackRange); -} - -qreal QAndroidMediaPlayer::playbackRate() const -{ - if (mHasPendingPlaybackRate || - (mState & (AndroidMediaPlayer::Initialized - | AndroidMediaPlayer::Prepared - | AndroidMediaPlayer::Started - | AndroidMediaPlayer::Paused - | AndroidMediaPlayer::PlaybackCompleted - | AndroidMediaPlayer::Error)) == 0) { - return mPendingPlaybackRate; - } - - return mMediaPlayer->playbackRate(); -} - -void QAndroidMediaPlayer::setPlaybackRate(qreal rate) -{ - if ((mState & (AndroidMediaPlayer::Initialized - | AndroidMediaPlayer::Prepared - | AndroidMediaPlayer::Started - | AndroidMediaPlayer::Paused - | AndroidMediaPlayer::PlaybackCompleted - | AndroidMediaPlayer::Error)) == 0) { - if (mPendingPlaybackRate != rate) { - mPendingPlaybackRate = rate; - mHasPendingPlaybackRate = true; - Q_EMIT playbackRateChanged(rate); - } - return; - } - - bool succeeded = mMediaPlayer->setPlaybackRate(rate); - - if (mHasPendingPlaybackRate) { - mHasPendingPlaybackRate = false; - mPendingPlaybackRate = qreal(1.0); - if (!succeeded) - Q_EMIT playbackRateChanged(playbackRate()); - } else if (succeeded) { - Q_EMIT playbackRateChanged(rate); - } -} - -QUrl QAndroidMediaPlayer::media() const -{ - return mMediaContent; -} - -const QIODevice *QAndroidMediaPlayer::mediaStream() const -{ - return mMediaStream; -} - -void QAndroidMediaPlayer::setMedia(const QUrl &mediaContent, - QIODevice *stream) -{ - StateChangeNotifier notifier(this); - - mReloadingMedia = (mMediaContent == mediaContent) && !mPendingSetMedia; - - if (!mReloadingMedia) { - mMediaContent = mediaContent; - mMediaStream = stream; - } - - if (mediaContent.isEmpty()) { - setMediaStatus(QMediaPlayer::NoMedia); - } else { - if (mVideoOutput && !mVideoOutput->isReady()) { - // if a video output is set but the video texture is not ready, delay loading the media - // since it can cause problems on some hardware - mPendingSetMedia = true; - return; - } - - if (mVideoSize.isValid() && mVideoOutput) - mVideoOutput->setVideoSize(mVideoSize); - - if ((mMediaPlayer->display() == 0) && mVideoOutput) - mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture()); - mMediaPlayer->setDataSource(QNetworkRequest(mediaContent)); - mMediaPlayer->prepareAsync(); - } - - resetBufferingProgress(); - - mReloadingMedia = false; -} - -void QAndroidMediaPlayer::setVideoSink(QVideoSink *sink) -{ - if (m_videoSink == sink) - return; - - m_videoSink = sink; - - if (!m_videoSink) { - return; - } - - if (mVideoOutput) { - delete mVideoOutput; - mVideoOutput = nullptr; - mMediaPlayer->setDisplay(nullptr); - } - - mVideoOutput = new QAndroidTextureVideoOutput(this); - connect(mVideoOutput, &QAndroidTextureVideoOutput::readyChanged, this, - &QAndroidMediaPlayer::onVideoOutputReady); - connect(mMediaPlayer, &AndroidMediaPlayer::timedTextChanged, mVideoOutput, - &QAndroidTextureVideoOutput::setSubtitle); - - mVideoOutput->setSurface(sink); - - if (mVideoOutput->isReady()) - mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture()); -} - -void QAndroidMediaPlayer::setAudioOutput(QPlatformAudioOutput *output) -{ - if (m_audioOutput == output) - return; - if (m_audioOutput) - m_audioOutput->q->disconnect(this); - m_audioOutput = static_cast<QAndroidAudioOutput *>(output); - if (m_audioOutput) { - connect(m_audioOutput->q, &QAudioOutput::deviceChanged, this, &QAndroidMediaPlayer::updateAudioDevice); - connect(m_audioOutput->q, &QAudioOutput::volumeChanged, this, &QAndroidMediaPlayer::setVolume); - connect(m_audioOutput->q, &QAudioOutput::mutedChanged, this, &QAndroidMediaPlayer::setMuted); - updateAudioDevice(); - } -} - -void QAndroidMediaPlayer::updateAudioDevice() -{ - if (m_audioOutput) - mMediaPlayer->setAudioOutput(m_audioOutput->device.id()); -} - -void QAndroidMediaPlayer::play() -{ - StateChangeNotifier notifier(this); - - // We need to prepare the mediaplayer again. - if ((mState & AndroidMediaPlayer::Stopped) && !mMediaContent.isEmpty()) { - setMedia(mMediaContent, mMediaStream); - } - - if (!mMediaContent.isEmpty()) - stateChanged(QMediaPlayer::PlayingState); - - if ((mState & (AndroidMediaPlayer::Prepared - | AndroidMediaPlayer::Started - | AndroidMediaPlayer::Paused - | AndroidMediaPlayer::PlaybackCompleted)) == 0) { - mPendingState = QMediaPlayer::PlayingState; - return; - } - - mMediaPlayer->play(); -} - -void QAndroidMediaPlayer::pause() -{ - StateChangeNotifier notifier(this); - - stateChanged(QMediaPlayer::PausedState); - - if ((mState & (AndroidMediaPlayer::Started - | AndroidMediaPlayer::Paused - | AndroidMediaPlayer::PlaybackCompleted)) == 0) { - mPendingState = QMediaPlayer::PausedState; - return; - } - - mMediaPlayer->pause(); -} - -void QAndroidMediaPlayer::stop() -{ - StateChangeNotifier notifier(this); - - stateChanged(QMediaPlayer::StoppedState); - - if ((mState & (AndroidMediaPlayer::Prepared - | AndroidMediaPlayer::Started - | AndroidMediaPlayer::Stopped - | AndroidMediaPlayer::Paused - | AndroidMediaPlayer::PlaybackCompleted)) == 0) { - if ((mState & (AndroidMediaPlayer::Idle | AndroidMediaPlayer::Uninitialized | AndroidMediaPlayer::Error)) == 0) - mPendingState = QMediaPlayer::StoppedState; - return; - } - - mMediaPlayer->stop(); -} - -bool QAndroidMediaPlayer::isSeekable() const -{ - return true; -} - -void QAndroidMediaPlayer::onInfo(qint32 what, qint32 extra) -{ - StateChangeNotifier notifier(this); - - Q_UNUSED(extra); - switch (what) { - case AndroidMediaPlayer::MEDIA_INFO_UNKNOWN: - break; - case AndroidMediaPlayer::MEDIA_INFO_VIDEO_TRACK_LAGGING: - // IGNORE - break; - case AndroidMediaPlayer::MEDIA_INFO_VIDEO_RENDERING_START: - break; - case AndroidMediaPlayer::MEDIA_INFO_BUFFERING_START: - mPendingState = state(); - stateChanged(QMediaPlayer::PausedState); - setMediaStatus(QMediaPlayer::StalledMedia); - break; - case AndroidMediaPlayer::MEDIA_INFO_BUFFERING_END: - if (state() != QMediaPlayer::StoppedState) - flushPendingStates(); - break; - case AndroidMediaPlayer::MEDIA_INFO_BAD_INTERLEAVING: - break; - case AndroidMediaPlayer::MEDIA_INFO_NOT_SEEKABLE: - seekableChanged(false); - break; - case AndroidMediaPlayer::MEDIA_INFO_METADATA_UPDATE: - Q_EMIT metaDataChanged(); - break; - } -} - -void QAndroidMediaPlayer::onError(qint32 what, qint32 extra) -{ - StateChangeNotifier notifier(this); - - QString errorString; - QMediaPlayer::Error error = QMediaPlayer::ResourceError; - - switch (what) { - case AndroidMediaPlayer::MEDIA_ERROR_UNKNOWN: - errorString = QLatin1String("Error:"); - break; - case AndroidMediaPlayer::MEDIA_ERROR_SERVER_DIED: - errorString = QLatin1String("Error: Server died"); - error = QMediaPlayer::ResourceError; - break; - case AndroidMediaPlayer::MEDIA_ERROR_INVALID_STATE: - errorString = QLatin1String("Error: Invalid state"); - error = QMediaPlayer::ResourceError; - break; - } - - switch (extra) { - case AndroidMediaPlayer::MEDIA_ERROR_IO: // Network OR file error - errorString += QLatin1String(" (I/O operation failed)"); - error = QMediaPlayer::NetworkError; - setMediaStatus(QMediaPlayer::InvalidMedia); - break; - case AndroidMediaPlayer::MEDIA_ERROR_MALFORMED: - errorString += QLatin1String(" (Malformed bitstream)"); - error = QMediaPlayer::FormatError; - setMediaStatus(QMediaPlayer::InvalidMedia); - break; - case AndroidMediaPlayer::MEDIA_ERROR_UNSUPPORTED: - errorString += QLatin1String(" (Unsupported media)"); - error = QMediaPlayer::FormatError; - setMediaStatus(QMediaPlayer::InvalidMedia); - break; - case AndroidMediaPlayer::MEDIA_ERROR_TIMED_OUT: - errorString += QLatin1String(" (Timed out)"); - break; - case AndroidMediaPlayer::MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK: - errorString += QLatin1String(" (Unable to start progressive playback')"); - error = QMediaPlayer::FormatError; - setMediaStatus(QMediaPlayer::InvalidMedia); - break; - case AndroidMediaPlayer::MEDIA_ERROR_BAD_THINGS_ARE_GOING_TO_HAPPEN: - errorString += QLatin1String(" (Unknown error/Insufficient resources)"); - error = QMediaPlayer::ResourceError; - break; - } - - Q_EMIT QPlatformMediaPlayer::error(error, errorString); -} - -void QAndroidMediaPlayer::onBufferingChanged(qint32 percent) -{ - StateChangeNotifier notifier(this); - - mBuffering = percent != 100; - mBufferPercent = percent; - - updateAvailablePlaybackRanges(); - - if (state() != QMediaPlayer::StoppedState) - setMediaStatus(mBuffering ? QMediaPlayer::BufferingMedia : QMediaPlayer::BufferedMedia); -} - -void QAndroidMediaPlayer::onVideoSizeChanged(qint32 width, qint32 height) -{ - QSize newSize(width, height); - - if (width == 0 || height == 0 || newSize == mVideoSize) - return; - - setVideoAvailable(true); - mVideoSize = newSize; - - if (mVideoOutput) - mVideoOutput->setVideoSize(mVideoSize); -} - -void QAndroidMediaPlayer::onStateChanged(qint32 state) -{ - // If reloading, don't report state changes unless the new state is Prepared or Error. - if ((mState & AndroidMediaPlayer::Stopped) - && (state & (AndroidMediaPlayer::Prepared | AndroidMediaPlayer::Error | AndroidMediaPlayer::Uninitialized)) == 0) { - return; - } - - StateChangeNotifier notifier(this); - - mState = state; - switch (mState) { - case AndroidMediaPlayer::Idle: - break; - case AndroidMediaPlayer::Initialized: - break; - case AndroidMediaPlayer::Preparing: - if (!mReloadingMedia) - setMediaStatus(QMediaPlayer::LoadingMedia); - break; - case AndroidMediaPlayer::Prepared: - setMediaStatus(QMediaPlayer::LoadedMedia); - if (mBuffering) { - setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia - : QMediaPlayer::BufferingMedia); - } else { - onBufferingChanged(100); - } - Q_EMIT metaDataChanged(); - setAudioAvailable(true); - flushPendingStates(); - break; - case AndroidMediaPlayer::Started: - stateChanged(QMediaPlayer::PlayingState); - if (mBuffering) { - setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia - : QMediaPlayer::BufferingMedia); - } else { - setMediaStatus(QMediaPlayer::BufferedMedia); - } - Q_EMIT positionChanged(position()); - break; - case AndroidMediaPlayer::Paused: - stateChanged(QMediaPlayer::PausedState); - break; - case AndroidMediaPlayer::Error: - stateChanged(QMediaPlayer::StoppedState); - setMediaStatus(QMediaPlayer::InvalidMedia); - mMediaPlayer->release(); - Q_EMIT positionChanged(0); - break; - case AndroidMediaPlayer::Stopped: - stateChanged(QMediaPlayer::StoppedState); - setMediaStatus(QMediaPlayer::LoadedMedia); - Q_EMIT positionChanged(0); - break; - case AndroidMediaPlayer::PlaybackCompleted: - stateChanged(QMediaPlayer::StoppedState); - setMediaStatus(QMediaPlayer::EndOfMedia); - break; - case AndroidMediaPlayer::Uninitialized: - // reset some properties (unless we reload the same media) - if (!mReloadingMedia) { - resetBufferingProgress(); - mPendingPosition = -1; - mPendingSetMedia = false; - mPendingState = -1; - - Q_EMIT durationChanged(0); - Q_EMIT positionChanged(0); - - setAudioAvailable(false); - setVideoAvailable(false); - seekableChanged(true); - } - break; - default: - break; - } - - if ((mState & (AndroidMediaPlayer::Stopped | AndroidMediaPlayer::Uninitialized)) != 0) { - mMediaPlayer->setDisplay(0); - if (mVideoOutput) { - mVideoOutput->stop(); - mVideoOutput->reset(); - } - } -} - -int QAndroidMediaPlayer::trackCount(TrackType trackType) -{ - if (!mTracksMetadata.contains(trackType)) - return -1; - - auto tracks = mTracksMetadata.value(trackType); - return tracks.count(); -} - -QMediaMetaData QAndroidMediaPlayer::trackMetaData(TrackType trackType, int streamNumber) -{ - if (!mTracksMetadata.contains(trackType)) - return QMediaMetaData(); - - auto tracks = mTracksMetadata.value(trackType); - if (tracks.count() < streamNumber) - return QMediaMetaData(); - - QAndroidMetaData trackInfo = tracks.at(streamNumber); - return static_cast<QMediaMetaData>(trackInfo); -} - -QPlatformMediaPlayer::TrackType convertTrackType(AndroidMediaPlayer::TrackType type) -{ - switch (type) { - case AndroidMediaPlayer::TrackType::Video: - return QPlatformMediaPlayer::TrackType::VideoStream; - case AndroidMediaPlayer::TrackType::Audio: - return QPlatformMediaPlayer::TrackType::AudioStream; - case AndroidMediaPlayer::TrackType::TimedText: - return QPlatformMediaPlayer::TrackType::SubtitleStream; - case AndroidMediaPlayer::TrackType::Subtitle: - return QPlatformMediaPlayer::TrackType::SubtitleStream; - case AndroidMediaPlayer::TrackType::Unknown: - case AndroidMediaPlayer::TrackType::Metadata: - return QPlatformMediaPlayer::TrackType::NTrackTypes; - } - - return QPlatformMediaPlayer::TrackType::NTrackTypes; -} - -int QAndroidMediaPlayer::convertTrackNumber(int androidTrackNumber) -{ - int trackNumber = androidTrackNumber; - - int videoTrackCount = trackCount(QPlatformMediaPlayer::TrackType::VideoStream); - if (trackNumber <= videoTrackCount) - return trackNumber; - - trackNumber = trackNumber - videoTrackCount; - - int audioTrackCount = trackCount(QPlatformMediaPlayer::TrackType::AudioStream); - if (trackNumber <= audioTrackCount) - return trackNumber; - - trackNumber = trackNumber - audioTrackCount; - - auto subtitleTracks = mTracksMetadata.value(QPlatformMediaPlayer::TrackType::SubtitleStream); - int timedTextCount = 0; - int subtitleTextCount = 0; - for (const auto &track : subtitleTracks) { - if (track.androidTrackType() == 3) // 3 == TimedText - timedTextCount++; - - if (track.androidTrackType() == 4) // 4 == Subtitle - subtitleTextCount++; - } - - if (trackNumber <= timedTextCount) - return trackNumber; - - trackNumber = trackNumber - timedTextCount; - - if (trackNumber <= subtitleTextCount) - return trackNumber; - - return -1; -} - -int QAndroidMediaPlayer::activeTrack(TrackType trackType) -{ - int androidTrackNumber = -1; - - switch (trackType) { - case QPlatformMediaPlayer::TrackType::VideoStream: { - if (!mIsVideoTrackEnabled) - return -1; - androidTrackNumber = mMediaPlayer->activeTrack(AndroidMediaPlayer::TrackType::Video); - } - case QPlatformMediaPlayer::TrackType::AudioStream: { - if (!mIsAudioTrackEnabled) - return -1; - - androidTrackNumber = mMediaPlayer->activeTrack(AndroidMediaPlayer::TrackType::Audio); - } - case QPlatformMediaPlayer::TrackType::SubtitleStream: { - int timedTextSelectedTrack = - mMediaPlayer->activeTrack(AndroidMediaPlayer::TrackType::TimedText); - - if (timedTextSelectedTrack > -1) { - androidTrackNumber = timedTextSelectedTrack; - break; - } - - int subtitleSelectedTrack = - mMediaPlayer->activeTrack(AndroidMediaPlayer::TrackType::Subtitle); - if (subtitleSelectedTrack > -1) { - androidTrackNumber = subtitleSelectedTrack; - break; - } - - return -1; - } - case QPlatformMediaPlayer::TrackType::NTrackTypes: - return -1; - } - - return convertTrackNumber(androidTrackNumber); -} - -void QAndroidMediaPlayer::disableTrack(TrackType trackType) -{ - const auto track = activeTrack(trackType); - - switch (trackType) { - case VideoStream: { - if (track > -1) { - mMediaPlayer->setDisplay(nullptr); - mIsVideoTrackEnabled = false; - } - break; - } - case AudioStream: { - if (track > -1) { - mMediaPlayer->setMuted(true); - mMediaPlayer->blockAudio(); - mIsAudioTrackEnabled = false; - } - break; - } - case SubtitleStream: { - // subtitles and timedtext tracks can be selected at the same time so deselect both - int subtitleSelectedTrack = - mMediaPlayer->activeTrack(AndroidMediaPlayer::TrackType::Subtitle); - if (subtitleSelectedTrack > -1) - mMediaPlayer->deselectTrack(subtitleSelectedTrack); - - int timedTextSelectedTrack = - mMediaPlayer->activeTrack(AndroidMediaPlayer::TrackType::TimedText); - if (timedTextSelectedTrack > -1) - mMediaPlayer->deselectTrack(timedTextSelectedTrack); - - break; - } - case NTrackTypes: - break; - } -} - -void QAndroidMediaPlayer::setActiveTrack(TrackType trackType, int streamNumber) -{ - - if (!mTracksMetadata.contains(trackType)) { - qCWarning(lcMediaPlayer) - << "Trying to set a active track which type has no available tracks."; - return; - } - - const auto &tracks = mTracksMetadata.value(trackType); - if (streamNumber > tracks.count()) { - qCWarning(lcMediaPlayer) << "Trying to set a active track that does not exist."; - return; - } - - // in case of < 0 deselect tracktype - if (streamNumber < 0) { - disableTrack(trackType); - return; - } - - const auto currentTrack = activeTrack(trackType); - if (streamNumber == currentTrack) { - return; - } - - if (trackType == TrackType::VideoStream && !mIsVideoTrackEnabled) { - // enable video stream - mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture()); - mIsVideoTrackEnabled = true; - } - - if (trackType == TrackType::AudioStream && !mIsAudioTrackEnabled) { - // enable audio stream - mMediaPlayer->unblockAudio(); - mMediaPlayer->setMuted(false); - mIsAudioTrackEnabled = true; - } - - if (trackType == TrackType::SubtitleStream) { - // subtitles and timedtext tracks can be selected at the same time so deselect both before - // selecting a new one - disableTrack(TrackType::SubtitleStream); - } - - const auto &trackInfo = tracks.at(streamNumber); - const auto &trackNumber = trackInfo.androidTrackNumber(); - mMediaPlayer->selectTrack(trackNumber); -} - -void QAndroidMediaPlayer::positionChanged(qint64 position) -{ - QPlatformMediaPlayer::positionChanged(position); -} - -void QAndroidMediaPlayer::durationChanged(qint64 duration) -{ - QPlatformMediaPlayer::durationChanged(duration); -} - -void QAndroidMediaPlayer::onVideoOutputReady(bool ready) -{ - if ((mMediaPlayer->display() == 0) && mVideoOutput && ready) - mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture()); - - flushPendingStates(); -} - -void QAndroidMediaPlayer::setMediaStatus(QMediaPlayer::MediaStatus status) -{ - mediaStatusChanged(status); - - if (status == QMediaPlayer::NoMedia || status == QMediaPlayer::InvalidMedia) - Q_EMIT durationChanged(0); - - if (status == QMediaPlayer::EndOfMedia) - Q_EMIT positionChanged(position()); - - updateBufferStatus(); -} - -void QAndroidMediaPlayer::setAudioAvailable(bool available) -{ - if (mAudioAvailable == available) - return; - - mAudioAvailable = available; - Q_EMIT audioAvailableChanged(mAudioAvailable); -} - -void QAndroidMediaPlayer::setVideoAvailable(bool available) -{ - if (mVideoAvailable == available) - return; - - if (!available) - mVideoSize = QSize(); - - mVideoAvailable = available; - Q_EMIT videoAvailableChanged(mVideoAvailable); -} - -void QAndroidMediaPlayer::resetBufferingProgress() -{ - mBuffering = false; - mBufferPercent = 0; - mAvailablePlaybackRange = QMediaTimeRange(); -} - -void QAndroidMediaPlayer::flushPendingStates() -{ - if (mPendingSetMedia) { - setMedia(mMediaContent, 0); - mPendingSetMedia = false; - return; - } - - const int newState = mPendingState; - mPendingState = -1; - - if (mPendingPosition != -1) - setPosition(mPendingPosition); - if (mPendingVolume >= 0) - setVolume(mPendingVolume); - if (mPendingMute != -1) - setMuted((mPendingMute == 1)); - if (mHasPendingPlaybackRate) - setPlaybackRate(mPendingPlaybackRate); - - switch (newState) { - case QMediaPlayer::PlayingState: - play(); - break; - case QMediaPlayer::PausedState: - pause(); - break; - case QMediaPlayer::StoppedState: - stop(); - break; - default: - break; - } -} - -void QAndroidMediaPlayer::updateBufferStatus() -{ - const auto &status = mediaStatus(); - bool bufferFilled = (status == QMediaPlayer::BufferedMedia || status == QMediaPlayer::BufferingMedia); - - if (mBufferFilled != bufferFilled) { - mBufferFilled = bufferFilled; - Q_EMIT bufferProgressChanged(bufferProgress()); - } -} - -void QAndroidMediaPlayer::updateTrackInfo() -{ - const auto &androidTracksInfo = mMediaPlayer->tracksInfo(); - - // prepare mTracksMetadata - mTracksMetadata[TrackType::VideoStream] = QList<QAndroidMetaData>(); - mTracksMetadata[TrackType::AudioStream] = QList<QAndroidMetaData>(); - mTracksMetadata[TrackType::SubtitleStream] = QList<QAndroidMetaData>(); - mTracksMetadata[TrackType::NTrackTypes] = QList<QAndroidMetaData>(); - - for (const auto &androidTrackInfo : androidTracksInfo) { - - const auto &mediaPlayerType = convertTrackType(androidTrackInfo.trackType); - auto &tracks = mTracksMetadata[mediaPlayerType]; - - const QAndroidMetaData metadata(mediaPlayerType, androidTrackInfo.trackType, - androidTrackInfo.trackNumber, androidTrackInfo.mimeType, - androidTrackInfo.language); - tracks.append(metadata); - } - - emit tracksChanged(); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/mediaplayer/qandroidmediaplayer_p.h b/src/multimedia/platform/android/mediaplayer/qandroidmediaplayer_p.h deleted file mode 100644 index b8e187a08..000000000 --- a/src/multimedia/platform/android/mediaplayer/qandroidmediaplayer_p.h +++ /dev/null @@ -1,164 +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$ -** -****************************************************************************/ - -#ifndef QANDROIDMEDIAPLAYERCONTROL_H -#define QANDROIDMEDIAPLAYERCONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qglobal.h> -#include <private/qplatformmediaplayer_p.h> -#include <private/qandroidmetadata_p.h> -#include <qsize.h> -#include <qurl.h> - -QT_BEGIN_NAMESPACE - -class AndroidMediaPlayer; -class QAndroidTextureVideoOutput; -class QAndroidMediaPlayerVideoRendererControl; -class QAndroidAudioOutput; - -class QAndroidMediaPlayer : public QObject, public QPlatformMediaPlayer -{ - Q_OBJECT - -public: - explicit QAndroidMediaPlayer(QMediaPlayer *parent = 0); - ~QAndroidMediaPlayer() override; - - qint64 duration() const override; - qint64 position() const override; - float bufferProgress() const override; - bool isAudioAvailable() const override; - bool isVideoAvailable() const override; - QMediaTimeRange availablePlaybackRanges() const override; - qreal playbackRate() const override; - void setPlaybackRate(qreal rate) override; - QUrl media() const override; - const QIODevice *mediaStream() const override; - void setMedia(const QUrl &mediaContent, QIODevice *stream) override; - - QMediaMetaData metaData() const override; - - void setVideoSink(QVideoSink *surface) override; - - void setAudioOutput(QPlatformAudioOutput *output) override; - void updateAudioDevice(); - - void setPosition(qint64 position) override; - void play() override; - void pause() override; - void stop() override; - - bool isSeekable() const override; - - int trackCount(TrackType trackType) override; - QMediaMetaData trackMetaData(TrackType trackType, int streamNumber) override; - int activeTrack(TrackType trackType) override; - void setActiveTrack(TrackType trackType, int streamNumber) override; - -private Q_SLOTS: - void setVolume(float volume); - void setMuted(bool muted); - void onVideoOutputReady(bool ready); - void onError(qint32 what, qint32 extra); - void onInfo(qint32 what, qint32 extra); - void onBufferingChanged(qint32 percent); - void onVideoSizeChanged(qint32 width, qint32 height); - void onStateChanged(qint32 state); - void positionChanged(qint64 position); - void durationChanged(qint64 duration); - -private: - AndroidMediaPlayer *mMediaPlayer = nullptr; - QAndroidAudioOutput *m_audioOutput = nullptr; - QUrl mMediaContent; - QIODevice *mMediaStream = nullptr; - QAndroidTextureVideoOutput *mVideoOutput = nullptr; - QVideoSink *m_videoSink = nullptr; - int mBufferPercent = -1; - bool mBufferFilled = false; - bool mAudioAvailable = false; - bool mVideoAvailable = false; - QSize mVideoSize; - bool mBuffering = false; - QMediaTimeRange mAvailablePlaybackRange; - int mState; - int mPendingState = -1; - qint64 mPendingPosition = -1; - bool mPendingSetMedia = false; - float mPendingVolume = -1; - int mPendingMute = -1; - bool mReloadingMedia = false; - int mActiveStateChangeNotifiers = 0; - qreal mPendingPlaybackRate = 1.; - bool mHasPendingPlaybackRate = false; // we need this because the rate can theoretically be negative - QMap<TrackType, QList<QAndroidMetaData>> mTracksMetadata; - - bool mIsVideoTrackEnabled = true; - bool mIsAudioTrackEnabled = true; - - void setMediaStatus(QMediaPlayer::MediaStatus status); - void setAudioAvailable(bool available); - void setVideoAvailable(bool available); - void updateAvailablePlaybackRanges(); - void resetBufferingProgress(); - void flushPendingStates(); - void updateBufferStatus(); - void updateTrackInfo(); - void setSubtitle(QString subtitle); - void disableTrack(TrackType trackType); - - int convertTrackNumber(int androidTrackNumber); - friend class StateChangeNotifier; -}; - -QT_END_NAMESPACE - -#endif // QANDROIDMEDIAPLAYERCONTROL_H diff --git a/src/multimedia/platform/android/mediaplayer/qandroidmetadata.cpp b/src/multimedia/platform/android/mediaplayer/qandroidmetadata.cpp deleted file mode 100644 index 059b62b52..000000000 --- a/src/multimedia/platform/android/mediaplayer/qandroidmetadata.cpp +++ /dev/null @@ -1,202 +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$ -** -****************************************************************************/ - -#include "qandroidmetadata_p.h" - -#include "androidmediametadataretriever_p.h" -#include <QtMultimedia/qmediametadata.h> -#include <qsize.h> -#include <QDate> -#include <QtCore/qlist.h> -#include <QtConcurrent/qtconcurrentrun.h> - -QT_BEGIN_NAMESPACE - -// Genre name ordered by ID -// see: http://id3.org/id3v2.3.0#Appendix_A_-_Genre_List_from_ID3v1 -static const char* qt_ID3GenreNames[] = -{ - "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz", - "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", - "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", - "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", - "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass", "Soul", "Punk", - "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", - "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", - "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", - "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal", - "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk", - "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival", "Celtic", - "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", - "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", - "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", - "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad", - "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock", "Drum Solo", "A capella", - "Euro-House", "Dance Hall" -}; - -QMediaMetaData QAndroidMetaData::extractMetadata(const QUrl &url) -{ - QMediaMetaData metadata; - - if (!url.isEmpty()) { - AndroidMediaMetadataRetriever retriever; - if (!retriever.setDataSource(url)) - return metadata; - - QString mimeType = retriever.extractMetadata(AndroidMediaMetadataRetriever::MimeType); - if (!mimeType.isNull()) - metadata.insert(QMediaMetaData::MediaType, mimeType); - - bool isVideo = !retriever.extractMetadata(AndroidMediaMetadataRetriever::HasVideo).isNull() - || mimeType.startsWith(QStringLiteral("video")); - - QString string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Album); - if (!string.isNull()) - metadata.insert(QMediaMetaData::AlbumTitle, string); - - string = retriever.extractMetadata(AndroidMediaMetadataRetriever::AlbumArtist); - if (!string.isNull()) - metadata.insert(QMediaMetaData::AlbumArtist, string); - - string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Artist); - if (!string.isNull()) { - metadata.insert(isVideo ? QMediaMetaData::LeadPerformer - : QMediaMetaData::ContributingArtist, - string.split(QLatin1Char('/'), Qt::SkipEmptyParts)); - } - - string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Author); - if (!string.isNull()) - metadata.insert(QMediaMetaData::Author, string.split(QLatin1Char('/'), Qt::SkipEmptyParts)); - - string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Bitrate); - if (!string.isNull()) { - metadata.insert(isVideo ? QMediaMetaData::VideoBitRate - : QMediaMetaData::AudioBitRate, - string.toInt()); - } - - string = retriever.extractMetadata(AndroidMediaMetadataRetriever::CDTrackNumber); - if (!string.isNull()) - metadata.insert(QMediaMetaData::TrackNumber, string.toInt()); - - string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Composer); - if (!string.isNull()) - metadata.insert(QMediaMetaData::Composer, string.split(QLatin1Char('/'), Qt::SkipEmptyParts)); - - string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Date); - if (!string.isNull()) - metadata.insert(QMediaMetaData::Date, QDateTime::fromString(string, QStringLiteral("yyyyMMddTHHmmss.zzzZ")).date()); - - string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Duration); - if (!string.isNull()) - metadata.insert(QMediaMetaData::Duration, string.toLongLong()); - - string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Genre); - if (!string.isNull()) { - // The genre can be returned as an ID3v2 id, get the name for it in that case - if (string.startsWith(QLatin1Char('(')) && string.endsWith(QLatin1Char(')'))) { - bool ok = false; - const int genreId = QStringView{string}.mid(1, string.length() - 2).toInt(&ok); - if (ok && genreId >= 0 && genreId <= 125) - string = QLatin1String(qt_ID3GenreNames[genreId]); - } - metadata.insert(QMediaMetaData::Genre, string); - } - - string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Title); - if (!string.isNull()) - metadata.insert(QMediaMetaData::Title, string); - - string = retriever.extractMetadata(AndroidMediaMetadataRetriever::VideoHeight); - if (!string.isNull()) { - const int height = string.toInt(); - const int width = retriever.extractMetadata(AndroidMediaMetadataRetriever::VideoWidth).toInt(); - metadata.insert(QMediaMetaData::Resolution, QSize(width, height)); - } - -// string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Writer); -// if (!string.isNull()) -// metadata.insert(QMediaMetaData::Writer, string.split('/', Qt::SkipEmptyParts)); - - } - - return metadata; -} - -QLocale::Language getLocaleLanguage(const QString &language) -{ - // undefined language or uncoded language - if (language == QLatin1String("und") || language == QStringLiteral("mis")) - return QLocale::AnyLanguage; - - QLocale locale(language); - if (locale != QLocale::c()) - return locale.language(); - - return QLocale::codeToLanguage(language.left(2)); -} - -QAndroidMetaData::QAndroidMetaData(int trackType, int androidTrackType, int androidTrackNumber, - const QString &mimeType, const QString &language) - : mTrackType(trackType), - mAndroidTrackType(androidTrackType), - mAndroidTrackNumber(androidTrackNumber) -{ - insert(QMediaMetaData::MediaType, mimeType); - insert(QMediaMetaData::Language, getLocaleLanguage(language)); -} - -int QAndroidMetaData::trackType() const -{ - return mTrackType; -} - -int QAndroidMetaData::androidTrackType() const -{ - return mAndroidTrackType; -} - -int QAndroidMetaData::androidTrackNumber() const -{ - return mAndroidTrackNumber; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/mediaplayer/qandroidmetadata_p.h b/src/multimedia/platform/android/mediaplayer/qandroidmetadata_p.h deleted file mode 100644 index 812edb062..000000000 --- a/src/multimedia/platform/android/mediaplayer/qandroidmetadata_p.h +++ /dev/null @@ -1,83 +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$ -** -****************************************************************************/ - -#ifndef QANDROIDMETADATA_H -#define QANDROIDMETADATA_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qmediametadata.h> -#include <qurl.h> -#include <QMutex> -#include <QVariant> - -QT_BEGIN_NAMESPACE - -class AndroidMediaMetadataRetriever; - -class QAndroidMetaData : public QMediaMetaData -{ -public: - static QMediaMetaData extractMetadata(const QUrl &url); - - QAndroidMetaData(int trackType, int androidTrackType, int androidTrackNumber, - const QString &mimeType, const QString &language); - - int trackType() const; - int androidTrackType() const; - int androidTrackNumber() const; - -private: - int mTrackType; - int mAndroidTrackType; - int mAndroidTrackNumber; -}; - -QT_END_NAMESPACE - -#endif // QANDROIDMETADATA_H diff --git a/src/multimedia/platform/android/qandroidformatsinfo.cpp b/src/multimedia/platform/android/qandroidformatsinfo.cpp deleted file mode 100644 index c45610b69..000000000 --- a/src/multimedia/platform/android/qandroidformatsinfo.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qandroidformatsinfo_p.h" - -#include <qcoreapplication.h> - -QT_BEGIN_NAMESPACE - -QAndroidFormatInfo::QAndroidFormatInfo() -{ - // Audio/Video/Image formats with their decoder/encoder information is documented at - // https://developer.android.com/guide/topics/media/media-formats - decoders = { - { QMediaFormat::AAC, { QMediaFormat::AudioCodec::AAC }, {} }, - { QMediaFormat::MP3, { QMediaFormat::AudioCodec::MP3}, {} }, - { QMediaFormat::Ogg, { QMediaFormat::AudioCodec::Opus, QMediaFormat::AudioCodec::Vorbis }, - {} }, - { QMediaFormat::FLAC, { QMediaFormat::AudioCodec::FLAC }, {} }, - { QMediaFormat::Mpeg4Audio, { QMediaFormat::AudioCodec::AAC, QMediaFormat::AudioCodec::FLAC, - QMediaFormat::AudioCodec::MP3, QMediaFormat::AudioCodec::Vorbis}, - {} }, - { QMediaFormat::MPEG4, { QMediaFormat::AudioCodec::MP3, QMediaFormat::AudioCodec::AAC, - QMediaFormat::AudioCodec::FLAC, QMediaFormat::AudioCodec::Vorbis }, - { QMediaFormat::VideoCodec::H264, QMediaFormat::VideoCodec::H265, - QMediaFormat::VideoCodec::AV1 } }, - { QMediaFormat::Matroska, { QMediaFormat::AudioCodec::MP3, QMediaFormat::AudioCodec::Opus, - QMediaFormat::AudioCodec::Vorbis }, - { QMediaFormat::VideoCodec::VP8, QMediaFormat::VideoCodec::VP9, - QMediaFormat::VideoCodec::H264, QMediaFormat::VideoCodec::H265, - QMediaFormat::VideoCodec::AV1} }, - { QMediaFormat::WebM, { QMediaFormat::AudioCodec::Opus, QMediaFormat::AudioCodec::Vorbis }, - { QMediaFormat::VideoCodec::VP8, QMediaFormat::VideoCodec::VP9} } - }; - - // MP3 encoders doesn't seem to be supported by the default Android SDK - encoders = { - { QMediaFormat::AAC, { QMediaFormat::AudioCodec::AAC }, {} }, - { QMediaFormat::MP3, {}, {} }, - { QMediaFormat::FLAC, { QMediaFormat::AudioCodec::FLAC }, {} }, - { QMediaFormat::Mpeg4Audio, {QMediaFormat::AudioCodec::AAC, QMediaFormat::AudioCodec::FLAC}, - {} }, - { QMediaFormat::MPEG4, { QMediaFormat::AudioCodec::AAC, QMediaFormat::AudioCodec::FLAC }, - { QMediaFormat::VideoCodec::H264 } } - }; - - // Opus encoder available only for Android 10+ - if (QNativeInterface::QAndroidApplication::sdkVersion() >= 29) { - encoders.append({ QMediaFormat::Ogg, { QMediaFormat::AudioCodec::Opus }, {} }); - encoders.append({ QMediaFormat::Matroska, { QMediaFormat::AudioCodec::MP3, - QMediaFormat::AudioCodec::Opus }, - { QMediaFormat::VideoCodec::VP8, QMediaFormat::VideoCodec::H264 } }); - encoders.append({ QMediaFormat::WebM, { QMediaFormat::AudioCodec::Opus }, - { QMediaFormat::VideoCodec::VP8 } }); - } else { - encoders.append({ QMediaFormat::Ogg, {}, {} }); - encoders.append({ QMediaFormat::Matroska, { QMediaFormat::AudioCodec::MP3 }, - { QMediaFormat::VideoCodec::VP8, QMediaFormat::VideoCodec::H264 } }); - encoders.append({ QMediaFormat::WebM, {}, { QMediaFormat::VideoCodec::VP8 } }); - } - - imageFormats << QImageCapture::JPEG << QImageCapture::PNG << QImageCapture::WebP; -} - -QAndroidFormatInfo::~QAndroidFormatInfo() -{ - -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/qandroidformatsinfo_p.h b/src/multimedia/platform/android/qandroidformatsinfo_p.h deleted file mode 100644 index 8810bb863..000000000 --- a/src/multimedia/platform/android/qandroidformatsinfo_p.h +++ /dev/null @@ -1,67 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QANDROIDFORMATINFO_H -#define QANDROIDFORMATINFO_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediaformatinfo_p.h> - -QT_BEGIN_NAMESPACE - -class QAndroidFormatInfo : public QPlatformMediaFormatInfo -{ -public: - QAndroidFormatInfo(); - ~QAndroidFormatInfo(); -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/android/qandroidintegration.cpp b/src/multimedia/platform/android/qandroidintegration.cpp deleted file mode 100644 index 5299275fa..000000000 --- a/src/multimedia/platform/android/qandroidintegration.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qandroidintegration_p.h" -#include "qandroidmediadevices_p.h" -#include "private/qandroidglobal_p.h" -#include "private/qandroidmediacapturesession_p.h" -#include "private/androidmediaplayer_p.h" -#include "private/qandroidcamerasession_p.h" -#include "private/androidsurfacetexture_p.h" -#include "private/androidsurfaceview_p.h" -#include "private/androidcamera_p.h" -#include "private/qandroidcamera_p.h" -#include "private/qandroidimagecapture_p.h" -#include "private/qandroidmediaencoder_p.h" -#include "private/androidmediarecorder_p.h" -#include "private/qandroidformatsinfo_p.h" -#include "private/qandroidmediaplayer_p.h" -#include "private/qandroidaudiooutput_p.h" -#include "private/qandroidvideosink_p.h" -#include "private/qandroidaudiodecoder_p.h" - -QT_BEGIN_NAMESPACE - -Q_LOGGING_CATEGORY(qtAndroidMediaPlugin, "qt.multimedia.android") - -QAndroidIntegration::QAndroidIntegration() -{ - -} - -QAndroidIntegration::~QAndroidIntegration() -{ - delete m_devices; - delete m_formatInfo; -} - -QPlatformMediaDevices *QAndroidIntegration::devices() -{ - if (!m_devices) - m_devices = new QAndroidMediaDevices(); - return m_devices; -} - -QPlatformAudioDecoder *QAndroidIntegration::createAudioDecoder(QAudioDecoder *decoder) -{ - return new QAndroidAudioDecoder(decoder); -} - -QPlatformMediaFormatInfo *QAndroidIntegration::formatInfo() -{ - if (!m_formatInfo) - m_formatInfo = new QAndroidFormatInfo(); - return m_formatInfo; - -} - -QPlatformMediaCaptureSession *QAndroidIntegration::createCaptureSession() -{ - return new QAndroidMediaCaptureSession(); -} - -QPlatformMediaPlayer *QAndroidIntegration::createPlayer(QMediaPlayer *player) -{ - return new QAndroidMediaPlayer(player); -} - -QPlatformCamera *QAndroidIntegration::createCamera(QCamera *camera) -{ - return new QAndroidCamera(camera); -} - -QPlatformMediaEncoder *QAndroidIntegration::createEncoder(QMediaRecorder *encoder) -{ - return new QAndroidMediaEncoder(encoder); -} - -QPlatformImageCapture *QAndroidIntegration::createImageCapture(QImageCapture *imageCapture) -{ - return new QAndroidImageCapture(imageCapture); -} - -QPlatformAudioOutput *QAndroidIntegration::createAudioOutput(QAudioOutput *q) -{ - return new QAndroidAudioOutput(q); -} - -QPlatformVideoSink *QAndroidIntegration::createVideoSink(QVideoSink *sink) -{ - return new QAndroidVideoSink(sink); -} - -Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/) -{ - static bool initialized = false; - if (initialized) - return JNI_VERSION_1_6; - initialized = true; - - QT_USE_NAMESPACE - typedef union { - JNIEnv *nativeEnvironment; - void *venv; - } UnionJNIEnvToVoid; - - UnionJNIEnvToVoid uenv; - uenv.venv = NULL; - - if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK) - return JNI_ERR; - - if (!AndroidMediaPlayer::registerNativeMethods() - || !AndroidCamera::registerNativeMethods() - || !AndroidMediaRecorder::registerNativeMethods() - || !AndroidSurfaceHolder::registerNativeMethods() - || !QAndroidMediaDevices::registerNativeMethods()) { - return JNI_ERR; - } - - AndroidSurfaceTexture::registerNativeMethods(); - - return JNI_VERSION_1_6; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/qandroidintegration_p.h b/src/multimedia/platform/android/qandroidintegration_p.h deleted file mode 100644 index a56881988..000000000 --- a/src/multimedia/platform/android/qandroidintegration_p.h +++ /dev/null @@ -1,86 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QANDROIDINTEGRATION_H -#define QANDROIDINTEGRATION_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediaintegration_p.h> - -QT_BEGIN_NAMESPACE - -class QAndroidMediaDevices; - -class QAndroidIntegration : public QPlatformMediaIntegration -{ -public: - QAndroidIntegration(); - ~QAndroidIntegration(); - - QPlatformMediaDevices *devices() override; - QPlatformMediaFormatInfo *formatInfo() override; - - QPlatformAudioDecoder *createAudioDecoder(QAudioDecoder *decoder) override; - QPlatformMediaCaptureSession *createCaptureSession() override; - QPlatformMediaPlayer *createPlayer(QMediaPlayer *player) override; - QPlatformCamera *createCamera(QCamera *camera) override; - QPlatformMediaEncoder *createEncoder(QMediaRecorder *encoder) override; - QPlatformImageCapture *createImageCapture(QImageCapture *imageCapture) override; - - QPlatformAudioOutput *createAudioOutput(QAudioOutput *q) override; - - QPlatformVideoSink *createVideoSink(QVideoSink *) override; - - QAndroidMediaDevices *m_devices = nullptr; - QPlatformMediaFormatInfo *m_formatInfo = nullptr; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/android/qandroidmediadevices.cpp b/src/multimedia/platform/android/qandroidmediadevices.cpp deleted file mode 100644 index 6737c46c6..000000000 --- a/src/multimedia/platform/android/qandroidmediadevices.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qandroidmediadevices_p.h" -#include "qmediadevices.h" -#include "qcameradevice_p.h" - -#include "private/qandroidaudiosource_p.h" -#include "private/qandroidaudiosink_p.h" -#include "private/qandroidaudiodevice_p.h" -#include "private/qopenslesengine_p.h" -#include "private/qplatformmediaintegration_p.h" -#include "private/qandroidcamerasession_p.h" - -QT_BEGIN_NAMESPACE - -QAndroidMediaDevices::QAndroidMediaDevices() - : QPlatformMediaDevices() -{ -} - -QList<QAudioDevice> QAndroidMediaDevices::audioInputs() const -{ - return QOpenSLESEngine::availableDevices(QAudioDevice::Input); -} - -QList<QAudioDevice> QAndroidMediaDevices::audioOutputs() const -{ - return QOpenSLESEngine::availableDevices(QAudioDevice::Output); -} - -QList<QCameraDevice> QAndroidMediaDevices::videoInputs() const -{ - return QAndroidCameraSession::availableCameras(); -} - -QPlatformAudioSource *QAndroidMediaDevices::createAudioSource(const QAudioDevice &deviceInfo) -{ - return new QAndroidAudioSource(deviceInfo.id()); -} - -QPlatformAudioSink *QAndroidMediaDevices::createAudioSink(const QAudioDevice &deviceInfo) -{ - return new QAndroidAudioSink(deviceInfo.id()); -} - -void QAndroidMediaDevices::forwardAudioOutputsChanged() -{ - audioOutputsChanged(); -} - -void QAndroidMediaDevices::forwardAudioInputsChanged() -{ - audioInputsChanged(); -} - -static void onAudioInputDevicesUpdated(JNIEnv */*env*/, jobject /*thiz*/) -{ - static_cast<QAndroidMediaDevices*>( - QPlatformMediaIntegration::instance()->devices())->forwardAudioInputsChanged(); -} - -static void onAudioOutputDevicesUpdated(JNIEnv */*env*/, jobject /*thiz*/) -{ - static_cast<QAndroidMediaDevices*>( - QPlatformMediaIntegration::instance()->devices())->forwardAudioOutputsChanged(); -} - -bool QAndroidMediaDevices::registerNativeMethods() -{ - static const JNINativeMethod methods[] = { - {"onAudioInputDevicesUpdated","()V",(void*)onAudioInputDevicesUpdated}, - {"onAudioOutputDevicesUpdated", "()V",(void*)onAudioOutputDevicesUpdated} - }; - const int size = std::size(methods); - return QJniEnvironment().registerNativeMethods( - "org/qtproject/qt/android/multimedia/QtAudioDeviceManager", methods, size); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/qandroidmediadevices_p.h b/src/multimedia/platform/android/qandroidmediadevices_p.h deleted file mode 100644 index 734678aad..000000000 --- a/src/multimedia/platform/android/qandroidmediadevices_p.h +++ /dev/null @@ -1,77 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QANDROIDMEDIADEVICES_H -#define QANDROIDMEDIADEVICES_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediadevices_p.h> -#include <qaudio.h> - -QT_BEGIN_NAMESPACE - -class QAndroidMediaDevices : public QPlatformMediaDevices -{ -public: - QAndroidMediaDevices(); - - QList<QAudioDevice> audioInputs() const override; - QList<QAudioDevice> audioOutputs() const override; - QList<QCameraDevice> videoInputs() const override; - QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo) override; - QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo) override; - - void forwardAudioOutputsChanged(); - void forwardAudioInputsChanged(); - static bool registerNativeMethods(); -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/android/wrappers/jni/androidcamera.cpp b/src/multimedia/platform/android/wrappers/jni/androidcamera.cpp deleted file mode 100644 index e40921f38..000000000 --- a/src/multimedia/platform/android/wrappers/jni/androidcamera.cpp +++ /dev/null @@ -1,1747 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Copyright (C) 2016 Ruslan Baratov -** 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$ -** -****************************************************************************/ - -#include "androidcamera_p.h" -#include "androidsurfacetexture_p.h" -#include "androidsurfaceview_p.h" -#include "qandroidmultimediautils_p.h" -#include "qandroidglobal_p.h" - -#include <qstringlist.h> -#include <qdebug.h> -#include <QtCore/qthread.h> -#include <QtCore/qreadwritelock.h> -#include <QtCore/qmutex.h> -#include <QtMultimedia/private/qmemoryvideobuffer_p.h> -#include <QtCore/qcoreapplication.h> - -#include <mutex> - -QT_BEGIN_NAMESPACE - -static const char QtCameraListenerClassName[] = "org/qtproject/qt/android/multimedia/QtCameraListener"; - -typedef QHash<int, AndroidCamera *> CameraMap; -Q_GLOBAL_STATIC(CameraMap, cameras) -Q_GLOBAL_STATIC(QReadWriteLock, rwLock) - -static QRect areaToRect(jobject areaObj) -{ - QJniObject area(areaObj); - QJniObject rect = area.getObjectField("rect", "Landroid/graphics/Rect;"); - - return QRect(rect.getField<jint>("left"), - rect.getField<jint>("top"), - rect.callMethod<jint>("width"), - rect.callMethod<jint>("height")); -} - -static QJniObject rectToArea(const QRect &rect) -{ - QJniObject jrect("android/graphics/Rect", - "(IIII)V", - rect.left(), rect.top(), rect.right(), rect.bottom()); - - QJniObject area("android/hardware/Camera$Area", - "(Landroid/graphics/Rect;I)V", - jrect.object(), 500); - - return area; -} - -// native method for QtCameraLisener.java -static void notifyAutoFocusComplete(JNIEnv* , jobject, int id, jboolean success) -{ - QReadLocker locker(rwLock); - const auto it = cameras->constFind(id); - if (Q_UNLIKELY(it == cameras->cend())) - return; - - Q_EMIT (*it)->autoFocusComplete(success); -} - -static void notifyPictureExposed(JNIEnv* , jobject, int id) -{ - QReadLocker locker(rwLock); - const auto it = cameras->constFind(id); - if (Q_UNLIKELY(it == cameras->cend())) - return; - - Q_EMIT (*it)->pictureExposed(); -} - -static void notifyPictureCaptured(JNIEnv *env, jobject, int id, jbyteArray data) -{ - QReadLocker locker(rwLock); - const auto it = cameras->constFind(id); - if (Q_UNLIKELY(it == cameras->cend())) - return; - - const int arrayLength = env->GetArrayLength(data); - QByteArray bytes(arrayLength, Qt::Uninitialized); - env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data()); - Q_EMIT (*it)->pictureCaptured(bytes); -} - -static void notifyNewPreviewFrame(JNIEnv *env, jobject, int id, jbyteArray data, - int width, int height, int format, int bpl) -{ - QReadLocker locker(rwLock); - const auto it = cameras->constFind(id); - if (Q_UNLIKELY(it == cameras->cend())) - return; - - const int arrayLength = env->GetArrayLength(data); - if (arrayLength == 0) - return; - - QByteArray bytes(arrayLength, Qt::Uninitialized); - env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data()); - - QVideoFrame frame(new QMemoryVideoBuffer(bytes, bpl), - QVideoFrameFormat(QSize(width, height), - qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat(format)))); - - Q_EMIT (*it)->newPreviewFrame(frame); -} - -static void notifyFrameAvailable(JNIEnv *, jobject, int id) -{ - QReadLocker locker(rwLock); - const auto it = cameras->constFind(id); - if (Q_UNLIKELY(it == cameras->cend())) - return; - - (*it)->fetchLastPreviewFrame(); -} - -class AndroidCameraPrivate : public QObject -{ - Q_OBJECT -public: - AndroidCameraPrivate(); - ~AndroidCameraPrivate(); - - Q_INVOKABLE bool init(int cameraId); - - Q_INVOKABLE void release(); - Q_INVOKABLE bool lock(); - Q_INVOKABLE bool unlock(); - Q_INVOKABLE bool reconnect(); - - Q_INVOKABLE AndroidCamera::CameraFacing getFacing(); - Q_INVOKABLE int getNativeOrientation(); - - Q_INVOKABLE QSize getPreferredPreviewSizeForVideo(); - Q_INVOKABLE QList<QSize> getSupportedPreviewSizes(); - static QList<QSize> getSupportedPreviewSizes(QJniObject ¶meters); - - Q_INVOKABLE QList<AndroidCamera::FpsRange> getSupportedPreviewFpsRange(); - - Q_INVOKABLE AndroidCamera::FpsRange getPreviewFpsRange(); - static AndroidCamera::FpsRange getPreviewFpsRange(QJniObject ¶meters); - Q_INVOKABLE void setPreviewFpsRange(int min, int max); - - Q_INVOKABLE AndroidCamera::ImageFormat getPreviewFormat(); - Q_INVOKABLE void setPreviewFormat(AndroidCamera::ImageFormat fmt); - Q_INVOKABLE QList<AndroidCamera::ImageFormat> getSupportedPreviewFormats(); - static QList<AndroidCamera::ImageFormat> getSupportedPreviewFormats(QJniObject ¶meters); - - Q_INVOKABLE QSize previewSize() const { return m_previewSize; } - Q_INVOKABLE QSize getPreviewSize(); - Q_INVOKABLE void updatePreviewSize(); - Q_INVOKABLE bool setPreviewTexture(void *surfaceTexture); - Q_INVOKABLE bool setPreviewDisplay(void *surfaceHolder); - Q_INVOKABLE void setDisplayOrientation(int degrees); - - Q_INVOKABLE bool isZoomSupported(); - Q_INVOKABLE int getMaxZoom(); - Q_INVOKABLE QList<int> getZoomRatios(); - Q_INVOKABLE int getZoom(); - Q_INVOKABLE void setZoom(int value); - - Q_INVOKABLE QString getFlashMode(); - Q_INVOKABLE void setFlashMode(const QString &value); - - Q_INVOKABLE QString getFocusMode(); - Q_INVOKABLE void setFocusMode(const QString &value); - - Q_INVOKABLE int getMaxNumFocusAreas(); - Q_INVOKABLE QList<QRect> getFocusAreas(); - Q_INVOKABLE void setFocusAreas(const QList<QRect> &areas); - - Q_INVOKABLE void autoFocus(); - Q_INVOKABLE void cancelAutoFocus(); - - Q_INVOKABLE bool isAutoExposureLockSupported(); - Q_INVOKABLE bool getAutoExposureLock(); - Q_INVOKABLE void setAutoExposureLock(bool toggle); - - Q_INVOKABLE bool isAutoWhiteBalanceLockSupported(); - Q_INVOKABLE bool getAutoWhiteBalanceLock(); - Q_INVOKABLE void setAutoWhiteBalanceLock(bool toggle); - - Q_INVOKABLE int getExposureCompensation(); - Q_INVOKABLE void setExposureCompensation(int value); - Q_INVOKABLE float getExposureCompensationStep(); - Q_INVOKABLE int getMinExposureCompensation(); - Q_INVOKABLE int getMaxExposureCompensation(); - - Q_INVOKABLE QString getSceneMode(); - Q_INVOKABLE void setSceneMode(const QString &value); - - Q_INVOKABLE QString getWhiteBalance(); - Q_INVOKABLE void setWhiteBalance(const QString &value); - - Q_INVOKABLE void updateRotation(); - - Q_INVOKABLE QList<QSize> getSupportedPictureSizes(); - Q_INVOKABLE void setPictureSize(const QSize &size); - Q_INVOKABLE void setJpegQuality(int quality); - - Q_INVOKABLE void startPreview(); - Q_INVOKABLE void stopPreview(); - - Q_INVOKABLE void takePicture(); - - Q_INVOKABLE void setupPreviewFrameCallback(); - Q_INVOKABLE void notifyNewFrames(bool notify); - Q_INVOKABLE void fetchLastPreviewFrame(); - - Q_INVOKABLE void applyParameters(); - - Q_INVOKABLE QStringList callParametersStringListMethod(const QByteArray &methodName); - - int m_cameraId; - QRecursiveMutex m_parametersMutex; - QSize m_previewSize; - int m_rotation; - QJniObject m_info; - QJniObject m_parameters; - QJniObject m_camera; - QJniObject m_cameraListener; - -Q_SIGNALS: - void previewSizeChanged(); - void previewStarted(); - void previewFailedToStart(); - void previewStopped(); - - void autoFocusStarted(); - - void whiteBalanceChanged(); - - void takePictureFailed(); - - void lastPreviewFrameFetched(const QVideoFrame &frame); -}; - -AndroidCamera::AndroidCamera(AndroidCameraPrivate *d, QThread *worker) - : QObject(), - d_ptr(d), - m_worker(worker) - -{ - connect(d, &AndroidCameraPrivate::previewSizeChanged, this, &AndroidCamera::previewSizeChanged); - connect(d, &AndroidCameraPrivate::previewStarted, this, &AndroidCamera::previewStarted); - connect(d, &AndroidCameraPrivate::previewFailedToStart, this, &AndroidCamera::previewFailedToStart); - connect(d, &AndroidCameraPrivate::previewStopped, this, &AndroidCamera::previewStopped); - connect(d, &AndroidCameraPrivate::autoFocusStarted, this, &AndroidCamera::autoFocusStarted); - connect(d, &AndroidCameraPrivate::whiteBalanceChanged, this, &AndroidCamera::whiteBalanceChanged); - connect(d, &AndroidCameraPrivate::takePictureFailed, this, &AndroidCamera::takePictureFailed); - connect(d, &AndroidCameraPrivate::lastPreviewFrameFetched, this, &AndroidCamera::lastPreviewFrameFetched); -} - -AndroidCamera::~AndroidCamera() -{ - Q_D(AndroidCamera); - if (d->m_camera.isValid()) { - release(); - QWriteLocker locker(rwLock); - cameras->remove(cameraId()); - } - - m_worker->exit(); - m_worker->wait(5000); -} - -AndroidCamera *AndroidCamera::open(int cameraId) -{ - if (!qt_androidRequestCameraPermission()) - return nullptr; - - AndroidCameraPrivate *d = new AndroidCameraPrivate(); - QThread *worker = new QThread; - worker->start(); - d->moveToThread(worker); - connect(worker, &QThread::finished, d, &AndroidCameraPrivate::deleteLater); - bool ok = true; - QMetaObject::invokeMethod(d, "init", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok), Q_ARG(int, cameraId)); - if (!ok) { - worker->quit(); - worker->wait(5000); - delete worker; - return 0; - } - - AndroidCamera *q = new AndroidCamera(d, worker); - QWriteLocker locker(rwLock); - cameras->insert(cameraId, q); - - return q; -} - -int AndroidCamera::cameraId() const -{ - Q_D(const AndroidCamera); - return d->m_cameraId; -} - -bool AndroidCamera::lock() -{ - Q_D(AndroidCamera); - bool ok = true; - QMetaObject::invokeMethod(d, "lock", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok)); - return ok; -} - -bool AndroidCamera::unlock() -{ - Q_D(AndroidCamera); - bool ok = true; - QMetaObject::invokeMethod(d, "unlock", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok)); - return ok; -} - -bool AndroidCamera::reconnect() -{ - Q_D(AndroidCamera); - bool ok = true; - QMetaObject::invokeMethod(d, "reconnect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ok)); - return ok; -} - -void AndroidCamera::release() -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "release", Qt::BlockingQueuedConnection); -} - -AndroidCamera::CameraFacing AndroidCamera::getFacing() -{ - Q_D(AndroidCamera); - return d->getFacing(); -} - -int AndroidCamera::getNativeOrientation() -{ - Q_D(AndroidCamera); - return d->getNativeOrientation(); -} - -QSize AndroidCamera::getPreferredPreviewSizeForVideo() -{ - Q_D(AndroidCamera); - return d->getPreferredPreviewSizeForVideo(); -} - -QList<QSize> AndroidCamera::getSupportedPreviewSizes() -{ - Q_D(AndroidCamera); - return d->getSupportedPreviewSizes(); -} - -QList<AndroidCamera::FpsRange> AndroidCamera::getSupportedPreviewFpsRange() -{ - Q_D(AndroidCamera); - return d->getSupportedPreviewFpsRange(); -} - -AndroidCamera::FpsRange AndroidCamera::getPreviewFpsRange() -{ - Q_D(AndroidCamera); - return d->getPreviewFpsRange(); -} - -void AndroidCamera::setPreviewFpsRange(FpsRange range) -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "setPreviewFpsRange", Q_ARG(int, range.min), Q_ARG(int, range.max)); -} - -AndroidCamera::ImageFormat AndroidCamera::getPreviewFormat() -{ - Q_D(AndroidCamera); - return d->getPreviewFormat(); -} - -void AndroidCamera::setPreviewFormat(ImageFormat fmt) -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "setPreviewFormat", Q_ARG(AndroidCamera::ImageFormat, fmt)); -} - -QList<AndroidCamera::ImageFormat> AndroidCamera::getSupportedPreviewFormats() -{ - Q_D(AndroidCamera); - return d->getSupportedPreviewFormats(); -} - -QSize AndroidCamera::previewSize() const -{ - Q_D(const AndroidCamera); - return d->m_previewSize; -} - -QSize AndroidCamera::actualPreviewSize() -{ - Q_D(AndroidCamera); - return d->getPreviewSize(); -} - -void AndroidCamera::setPreviewSize(const QSize &size) -{ - Q_D(AndroidCamera); - d->m_parametersMutex.lock(); - bool areParametersValid = d->m_parameters.isValid(); - d->m_parametersMutex.unlock(); - if (!areParametersValid) - return; - - d->m_previewSize = size; - QMetaObject::invokeMethod(d, "updatePreviewSize"); -} - -bool AndroidCamera::setPreviewTexture(AndroidSurfaceTexture *surfaceTexture) -{ - Q_D(AndroidCamera); - bool ok = true; - QMetaObject::invokeMethod(d, - "setPreviewTexture", - Qt::BlockingQueuedConnection, - Q_RETURN_ARG(bool, ok), - Q_ARG(void *, surfaceTexture ? surfaceTexture->surfaceTexture() : 0)); - return ok; -} - -bool AndroidCamera::setPreviewDisplay(AndroidSurfaceHolder *surfaceHolder) -{ - Q_D(AndroidCamera); - bool ok = true; - QMetaObject::invokeMethod(d, - "setPreviewDisplay", - Qt::BlockingQueuedConnection, - Q_RETURN_ARG(bool, ok), - Q_ARG(void *, surfaceHolder ? surfaceHolder->surfaceHolder() : 0)); - return ok; -} - -void AndroidCamera::setDisplayOrientation(int degrees) -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "setDisplayOrientation", Qt::QueuedConnection, Q_ARG(int, degrees)); -} - -bool AndroidCamera::isZoomSupported() -{ - Q_D(AndroidCamera); - return d->isZoomSupported(); -} - -int AndroidCamera::getMaxZoom() -{ - Q_D(AndroidCamera); - return d->getMaxZoom(); -} - -QList<int> AndroidCamera::getZoomRatios() -{ - Q_D(AndroidCamera); - return d->getZoomRatios(); -} - -int AndroidCamera::getZoom() -{ - Q_D(AndroidCamera); - return d->getZoom(); -} - -void AndroidCamera::setZoom(int value) -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "setZoom", Q_ARG(int, value)); -} - -QStringList AndroidCamera::getSupportedFlashModes() -{ - Q_D(AndroidCamera); - return d->callParametersStringListMethod("getSupportedFlashModes"); -} - -QString AndroidCamera::getFlashMode() -{ - Q_D(AndroidCamera); - return d->getFlashMode(); -} - -void AndroidCamera::setFlashMode(const QString &value) -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "setFlashMode", Q_ARG(QString, value)); -} - -QStringList AndroidCamera::getSupportedFocusModes() -{ - Q_D(AndroidCamera); - return d->callParametersStringListMethod("getSupportedFocusModes"); -} - -QString AndroidCamera::getFocusMode() -{ - Q_D(AndroidCamera); - return d->getFocusMode(); -} - -void AndroidCamera::setFocusMode(const QString &value) -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "setFocusMode", Q_ARG(QString, value)); -} - -int AndroidCamera::getMaxNumFocusAreas() -{ - Q_D(AndroidCamera); - return d->getMaxNumFocusAreas(); -} - -QList<QRect> AndroidCamera::getFocusAreas() -{ - Q_D(AndroidCamera); - return d->getFocusAreas(); -} - -void AndroidCamera::setFocusAreas(const QList<QRect> &areas) -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "setFocusAreas", Q_ARG(QList<QRect>, areas)); -} - -void AndroidCamera::autoFocus() -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "autoFocus"); -} - -void AndroidCamera::cancelAutoFocus() -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "cancelAutoFocus", Qt::QueuedConnection); -} - -bool AndroidCamera::isAutoExposureLockSupported() -{ - Q_D(AndroidCamera); - return d->isAutoExposureLockSupported(); -} - -bool AndroidCamera::getAutoExposureLock() -{ - Q_D(AndroidCamera); - return d->getAutoExposureLock(); -} - -void AndroidCamera::setAutoExposureLock(bool toggle) -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "setAutoExposureLock", Q_ARG(bool, toggle)); -} - -bool AndroidCamera::isAutoWhiteBalanceLockSupported() -{ - Q_D(AndroidCamera); - return d->isAutoWhiteBalanceLockSupported(); -} - -bool AndroidCamera::getAutoWhiteBalanceLock() -{ - Q_D(AndroidCamera); - return d->getAutoWhiteBalanceLock(); -} - -void AndroidCamera::setAutoWhiteBalanceLock(bool toggle) -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "setAutoWhiteBalanceLock", Q_ARG(bool, toggle)); -} - -int AndroidCamera::getExposureCompensation() -{ - Q_D(AndroidCamera); - return d->getExposureCompensation(); -} - -void AndroidCamera::setExposureCompensation(int value) -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "setExposureCompensation", Q_ARG(int, value)); -} - -float AndroidCamera::getExposureCompensationStep() -{ - Q_D(AndroidCamera); - return d->getExposureCompensationStep(); -} - -int AndroidCamera::getMinExposureCompensation() -{ - Q_D(AndroidCamera); - return d->getMinExposureCompensation(); -} - -int AndroidCamera::getMaxExposureCompensation() -{ - Q_D(AndroidCamera); - return d->getMaxExposureCompensation(); -} - -QStringList AndroidCamera::getSupportedSceneModes() -{ - Q_D(AndroidCamera); - return d->callParametersStringListMethod("getSupportedSceneModes"); -} - -QString AndroidCamera::getSceneMode() -{ - Q_D(AndroidCamera); - return d->getSceneMode(); -} - -void AndroidCamera::setSceneMode(const QString &value) -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "setSceneMode", Q_ARG(QString, value)); -} - -QStringList AndroidCamera::getSupportedWhiteBalance() -{ - Q_D(AndroidCamera); - return d->callParametersStringListMethod("getSupportedWhiteBalance"); -} - -QString AndroidCamera::getWhiteBalance() -{ - Q_D(AndroidCamera); - return d->getWhiteBalance(); -} - -void AndroidCamera::setWhiteBalance(const QString &value) -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "setWhiteBalance", Q_ARG(QString, value)); -} - -void AndroidCamera::setRotation(int rotation) -{ - Q_D(AndroidCamera); - //We need to do it here and not in worker class because we cache rotation - d->m_parametersMutex.lock(); - bool areParametersValid = d->m_parameters.isValid(); - d->m_parametersMutex.unlock(); - if (!areParametersValid) - return; - - d->m_rotation = rotation; - QMetaObject::invokeMethod(d, "updateRotation"); -} - -int AndroidCamera::getRotation() const -{ - Q_D(const AndroidCamera); - return d->m_rotation; -} - -QList<QSize> AndroidCamera::getSupportedPictureSizes() -{ - Q_D(AndroidCamera); - return d->getSupportedPictureSizes(); -} - -void AndroidCamera::setPictureSize(const QSize &size) -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "setPictureSize", Q_ARG(QSize, size)); -} - -void AndroidCamera::setJpegQuality(int quality) -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "setJpegQuality", Q_ARG(int, quality)); -} - -void AndroidCamera::takePicture() -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "takePicture", Qt::BlockingQueuedConnection); -} - -void AndroidCamera::setupPreviewFrameCallback() -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "setupPreviewFrameCallback"); -} - -void AndroidCamera::notifyNewFrames(bool notify) -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "notifyNewFrames", Q_ARG(bool, notify)); -} - -void AndroidCamera::fetchLastPreviewFrame() -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "fetchLastPreviewFrame"); -} - -QJniObject AndroidCamera::getCameraObject() -{ - Q_D(AndroidCamera); - return d->m_camera; -} - -int AndroidCamera::getNumberOfCameras() -{ - if (!qt_androidRequestCameraPermission()) - return 0; - - return QJniObject::callStaticMethod<jint>("android/hardware/Camera", - "getNumberOfCameras"); -} - -void AndroidCamera::getCameraInfo(int id, QCameraDevicePrivate *info) -{ - Q_ASSERT(info); - - QJniObject cameraInfo("android/hardware/Camera$CameraInfo"); - QJniObject::callStaticMethod<void>("android/hardware/Camera", - "getCameraInfo", - "(ILandroid/hardware/Camera$CameraInfo;)V", - id, cameraInfo.object()); - - AndroidCamera::CameraFacing facing = AndroidCamera::CameraFacing(cameraInfo.getField<jint>("facing")); - // The orientation provided by Android is counter-clockwise, we need it clockwise - info->orientation = (360 - cameraInfo.getField<jint>("orientation")) % 360; - - switch (facing) { - case AndroidCamera::CameraFacingBack: - info->id = QByteArray("back"); - info->description = QStringLiteral("Rear-facing camera"); - info->position = QCameraDevice::BackFace; - info->isDefault = true; - break; - case AndroidCamera::CameraFacingFront: - info->id = QByteArray("front"); - info->description = QStringLiteral("Front-facing camera"); - info->position = QCameraDevice::FrontFace; - break; - default: - break; - } -} - -QVideoFrameFormat::PixelFormat AndroidCamera::QtPixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat format) -{ - switch (format) { - case AndroidCamera::NV21: - return QVideoFrameFormat::Format_NV21; - case AndroidCamera::YUY2: - return QVideoFrameFormat::Format_YUYV; - case AndroidCamera::JPEG: - return QVideoFrameFormat::Format_Jpeg; - case AndroidCamera::YV12: - return QVideoFrameFormat::Format_YV12; - default: - return QVideoFrameFormat::Format_Invalid; - } -} - -AndroidCamera::ImageFormat AndroidCamera::AndroidImageFormatFromQtPixelFormat(QVideoFrameFormat::PixelFormat format) -{ - switch (format) { - case QVideoFrameFormat::Format_NV21: - return AndroidCamera::NV21; - case QVideoFrameFormat::Format_YUYV: - return AndroidCamera::YUY2; - case QVideoFrameFormat::Format_Jpeg: - return AndroidCamera::JPEG; - case QVideoFrameFormat::Format_YV12: - return AndroidCamera::YV12; - default: - return AndroidCamera::UnknownImageFormat; - } -} - -void AndroidCamera::getSupportedFormats(int id, QList<QCameraFormat> &formats) -{ - QJniObject camera = QJniObject::callStaticObjectMethod("android/hardware/Camera", - "open", - "(I)Landroid/hardware/Camera;", - id); - if (!camera.isValid()) - return; - - QJniObject cameraParams = camera.callObjectMethod("getParameters", - "()Landroid/hardware/Camera$Parameters;"); - if (!cameraParams.isValid()) { - camera.callMethod<void>("release"); - return; - } - AndroidCamera::FpsRange range = AndroidCameraPrivate::getPreviewFpsRange(cameraParams); - - for (const auto &previewSize : AndroidCameraPrivate::getSupportedPreviewSizes(cameraParams)) - { - for (const auto &previewFormat : AndroidCameraPrivate::getSupportedPreviewFormats(cameraParams)) - { - QCameraFormatPrivate * format = new QCameraFormatPrivate(); - format->pixelFormat = QtPixelFormatFromAndroidImageFormat(previewFormat); - format->resolution = previewSize; - format->minFrameRate = range.min; - format->maxFrameRate = range.max; - formats.append(format->create()); - } - } - - camera.callMethod<void>("release"); -} - -void AndroidCamera::startPreview() -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "startPreview"); -} - -void AndroidCamera::stopPreview() -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "stopPreview"); -} - -void AndroidCamera::stopPreviewSynchronous() -{ - Q_D(AndroidCamera); - QMetaObject::invokeMethod(d, "stopPreview", Qt::BlockingQueuedConnection); -} - -AndroidCameraPrivate::AndroidCameraPrivate() - : QObject() -{ -} - -AndroidCameraPrivate::~AndroidCameraPrivate() -{ -} - -static qint32 s_activeCameras = 0; - -bool AndroidCameraPrivate::init(int cameraId) -{ - m_cameraId = cameraId; - QJniEnvironment env; - - const bool opened = s_activeCameras & (1 << cameraId); - if (opened) - return false; - - m_camera = QJniObject::callStaticObjectMethod("android/hardware/Camera", - "open", - "(I)Landroid/hardware/Camera;", - cameraId); - if (!m_camera.isValid()) - return false; - - m_cameraListener = QJniObject(QtCameraListenerClassName, "(I)V", m_cameraId); - m_info = QJniObject("android/hardware/Camera$CameraInfo"); - m_camera.callStaticMethod<void>("android/hardware/Camera", - "getCameraInfo", - "(ILandroid/hardware/Camera$CameraInfo;)V", - cameraId, - m_info.object()); - - QJniObject params = m_camera.callObjectMethod("getParameters", - "()Landroid/hardware/Camera$Parameters;"); - m_parameters = QJniObject(params); - s_activeCameras |= 1 << cameraId; - - return true; -} - -void AndroidCameraPrivate::release() -{ - m_previewSize = QSize(); - m_parametersMutex.lock(); - m_parameters = QJniObject(); - m_parametersMutex.unlock(); - if (m_camera.isValid()) { - m_camera.callMethod<void>("release"); - s_activeCameras &= ~(1 << m_cameraId); - } -} - -bool AndroidCameraPrivate::lock() -{ - QJniEnvironment env; - auto methodId = env->GetMethodID(m_camera.objectClass(), "lock", "()V"); - env->CallVoidMethod(m_camera.object(), methodId); - - if (env.checkAndClearExceptions()) - return false; - return true; -} - -bool AndroidCameraPrivate::unlock() -{ - QJniEnvironment env; - auto methodId = env->GetMethodID(m_camera.objectClass(), "unlock", "()V"); - env->CallVoidMethod(m_camera.object(), methodId); - - if (env.checkAndClearExceptions()) - return false; - return true; -} - -bool AndroidCameraPrivate::reconnect() -{ - QJniEnvironment env; - auto methodId = env->GetMethodID(m_camera.objectClass(), "reconnect", "()V"); - env->CallVoidMethod(m_camera.object(), methodId); - - if (env.checkAndClearExceptions()) - return false; - return true; -} - -AndroidCamera::CameraFacing AndroidCameraPrivate::getFacing() -{ - return AndroidCamera::CameraFacing(m_info.getField<jint>("facing")); -} - -int AndroidCameraPrivate::getNativeOrientation() -{ - return m_info.getField<jint>("orientation"); -} - -QSize AndroidCameraPrivate::getPreferredPreviewSizeForVideo() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return QSize(); - - QJniObject size = m_parameters.callObjectMethod("getPreferredPreviewSizeForVideo", - "()Landroid/hardware/Camera$Size;"); - - if (!size.isValid()) - return QSize(); - - return QSize(size.getField<jint>("width"), size.getField<jint>("height")); -} - -QList<QSize> AndroidCameraPrivate::getSupportedPreviewSizes() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - return getSupportedPreviewSizes(m_parameters); -} - -QList<QSize> AndroidCameraPrivate::getSupportedPreviewSizes(QJniObject ¶meters) -{ - QList<QSize> list; - - if (parameters.isValid()) { - QJniObject sizeList = parameters.callObjectMethod("getSupportedPreviewSizes", - "()Ljava/util/List;"); - int count = sizeList.callMethod<jint>("size"); - for (int i = 0; i < count; ++i) { - QJniObject size = sizeList.callObjectMethod("get", - "(I)Ljava/lang/Object;", - i); - list.append(QSize(size.getField<jint>("width"), size.getField<jint>("height"))); - } - - std::sort(list.begin(), list.end(), qt_sizeLessThan); - } - - return list; -} - -QList<AndroidCamera::FpsRange> AndroidCameraPrivate::getSupportedPreviewFpsRange() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - QJniEnvironment env; - - QList<AndroidCamera::FpsRange> rangeList; - - if (m_parameters.isValid()) { - QJniObject rangeListNative = m_parameters.callObjectMethod("getSupportedPreviewFpsRange", - "()Ljava/util/List;"); - int count = rangeListNative.callMethod<jint>("size"); - - rangeList.reserve(count); - - for (int i = 0; i < count; ++i) { - QJniObject range = rangeListNative.callObjectMethod("get", - "(I)Ljava/lang/Object;", - i); - - jintArray jRange = static_cast<jintArray>(range.object()); - jint* rangeArray = env->GetIntArrayElements(jRange, 0); - - AndroidCamera::FpsRange fpsRange; - - fpsRange.min = rangeArray[0]; - fpsRange.max = rangeArray[1]; - - env->ReleaseIntArrayElements(jRange, rangeArray, 0); - - rangeList << fpsRange; - } - } - - return rangeList; -} - -AndroidCamera::FpsRange AndroidCameraPrivate::getPreviewFpsRange() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - return getPreviewFpsRange(m_parameters); -} - -AndroidCamera::FpsRange AndroidCameraPrivate::getPreviewFpsRange(QJniObject ¶meters) -{ - QJniEnvironment env; - - AndroidCamera::FpsRange range; - - if (!parameters.isValid()) - return range; - - jintArray jRangeArray = env->NewIntArray(2); - parameters.callMethod<void>("getPreviewFpsRange", "([I)V", jRangeArray); - - jint* jRangeElements = env->GetIntArrayElements(jRangeArray, 0); - - range.min = jRangeElements[0]; - range.max = jRangeElements[1]; - - env->ReleaseIntArrayElements(jRangeArray, jRangeElements, 0); - env->DeleteLocalRef(jRangeArray); - - return range; -} - -void AndroidCameraPrivate::setPreviewFpsRange(int min, int max) -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return; - - QJniEnvironment env; - m_parameters.callMethod<void>("setPreviewFpsRange", "(II)V", min, max); -} - -AndroidCamera::ImageFormat AndroidCameraPrivate::getPreviewFormat() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return AndroidCamera::UnknownImageFormat; - - return AndroidCamera::ImageFormat(m_parameters.callMethod<jint>("getPreviewFormat")); -} - -void AndroidCameraPrivate::setPreviewFormat(AndroidCamera::ImageFormat fmt) -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return; - - m_parameters.callMethod<void>("setPreviewFormat", "(I)V", jint(fmt)); - applyParameters(); -} - -QList<AndroidCamera::ImageFormat> AndroidCameraPrivate::getSupportedPreviewFormats() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - return getSupportedPreviewFormats(m_parameters); -} - -QList<AndroidCamera::ImageFormat> AndroidCameraPrivate::getSupportedPreviewFormats(QJniObject ¶meters) -{ - QList<AndroidCamera::ImageFormat> list; - - if (parameters.isValid()) { - QJniObject formatList = parameters.callObjectMethod("getSupportedPreviewFormats", - "()Ljava/util/List;"); - int count = formatList.callMethod<jint>("size"); - for (int i = 0; i < count; ++i) { - QJniObject format = formatList.callObjectMethod("get", - "(I)Ljava/lang/Object;", - i); - list.append(AndroidCamera::ImageFormat(format.callMethod<jint>("intValue"))); - } - } - - return list; -} - -QSize AndroidCameraPrivate::getPreviewSize() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return QSize(); - - QJniObject size = m_parameters.callObjectMethod("getPreviewSize", - "()Landroid/hardware/Camera$Size;"); - - if (!size.isValid()) - return QSize(); - - return QSize(size.getField<jint>("width"), size.getField<jint>("height")); -} - -void AndroidCameraPrivate::updatePreviewSize() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (m_previewSize.isValid()) { - m_parameters.callMethod<void>("setPreviewSize", "(II)V", m_previewSize.width(), m_previewSize.height()); - applyParameters(); - } - - emit previewSizeChanged(); -} - -bool AndroidCameraPrivate::setPreviewTexture(void *surfaceTexture) -{ - QJniEnvironment env; - auto methodId = env->GetMethodID(m_camera.objectClass(), "setPreviewTexture", - "(Landroid/graphics/SurfaceTexture;)V"); - env->CallVoidMethod(m_camera.object(), methodId, static_cast<jobject>(surfaceTexture)); - - if (env.checkAndClearExceptions()) - return false; - return true; -} - -bool AndroidCameraPrivate::setPreviewDisplay(void *surfaceHolder) -{ - QJniEnvironment env; - auto methodId = env->GetMethodID(m_camera.objectClass(), "setPreviewDisplay", - "(Landroid/view/SurfaceHolder;)V"); - env->CallVoidMethod(m_camera.object(), methodId, static_cast<jobject>(surfaceHolder)); - - if (env.checkAndClearExceptions()) - return false; - return true; -} - -void AndroidCameraPrivate::setDisplayOrientation(int degrees) -{ - m_camera.callMethod<void>("setDisplayOrientation", "(I)V", degrees); -} - -bool AndroidCameraPrivate::isZoomSupported() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return false; - - return m_parameters.callMethod<jboolean>("isZoomSupported"); -} - -int AndroidCameraPrivate::getMaxZoom() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return 0; - - return m_parameters.callMethod<jint>("getMaxZoom"); -} - -QList<int> AndroidCameraPrivate::getZoomRatios() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - QList<int> ratios; - - if (m_parameters.isValid()) { - QJniObject ratioList = m_parameters.callObjectMethod("getZoomRatios", - "()Ljava/util/List;"); - int count = ratioList.callMethod<jint>("size"); - for (int i = 0; i < count; ++i) { - QJniObject zoomRatio = ratioList.callObjectMethod("get", - "(I)Ljava/lang/Object;", - i); - - ratios.append(zoomRatio.callMethod<jint>("intValue")); - } - } - - return ratios; -} - -int AndroidCameraPrivate::getZoom() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return 0; - - return m_parameters.callMethod<jint>("getZoom"); -} - -void AndroidCameraPrivate::setZoom(int value) -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return; - - m_parameters.callMethod<void>("setZoom", "(I)V", value); - applyParameters(); -} - -QString AndroidCameraPrivate::getFlashMode() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - QString value; - - if (m_parameters.isValid()) { - QJniObject flashMode = m_parameters.callObjectMethod("getFlashMode", - "()Ljava/lang/String;"); - if (flashMode.isValid()) - value = flashMode.toString(); - } - - return value; -} - -void AndroidCameraPrivate::setFlashMode(const QString &value) -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return; - - m_parameters.callMethod<void>("setFlashMode", - "(Ljava/lang/String;)V", - QJniObject::fromString(value).object()); - applyParameters(); -} - -QString AndroidCameraPrivate::getFocusMode() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - QString value; - - if (m_parameters.isValid()) { - QJniObject focusMode = m_parameters.callObjectMethod("getFocusMode", - "()Ljava/lang/String;"); - if (focusMode.isValid()) - value = focusMode.toString(); - } - - return value; -} - -void AndroidCameraPrivate::setFocusMode(const QString &value) -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return; - - m_parameters.callMethod<void>("setFocusMode", - "(Ljava/lang/String;)V", - QJniObject::fromString(value).object()); - applyParameters(); -} - -int AndroidCameraPrivate::getMaxNumFocusAreas() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return 0; - - return m_parameters.callMethod<jint>("getMaxNumFocusAreas"); -} - -QList<QRect> AndroidCameraPrivate::getFocusAreas() -{ - QList<QRect> areas; - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (m_parameters.isValid()) { - QJniObject list = m_parameters.callObjectMethod("getFocusAreas", - "()Ljava/util/List;"); - - if (list.isValid()) { - int count = list.callMethod<jint>("size"); - for (int i = 0; i < count; ++i) { - QJniObject area = list.callObjectMethod("get", - "(I)Ljava/lang/Object;", - i); - - areas.append(areaToRect(area.object())); - } - } - } - - return areas; -} - -void AndroidCameraPrivate::setFocusAreas(const QList<QRect> &areas) -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid() || areas.isEmpty()) - return; - - QJniObject list; - - if (!areas.isEmpty()) { - QJniEnvironment env; - QJniObject arrayList("java/util/ArrayList", "(I)V", areas.size()); - for (int i = 0; i < areas.size(); ++i) { - arrayList.callMethod<jboolean>("add", - "(Ljava/lang/Object;)Z", - rectToArea(areas.at(i)).object()); - } - list = arrayList; - } - - m_parameters.callMethod<void>("setFocusAreas", "(Ljava/util/List;)V", list.object()); - - applyParameters(); -} - -void AndroidCameraPrivate::autoFocus() -{ - QJniEnvironment env; - auto methodId = env->GetMethodID(m_camera.objectClass(), "autoFocus", - "(Landroid/hardware/Camera$AutoFocusCallback;)V"); - env->CallVoidMethod(m_camera.object(), methodId, m_cameraListener.object()); - - if (!env.checkAndClearExceptions()) - emit autoFocusStarted(); -} - -void AndroidCameraPrivate::cancelAutoFocus() -{ - QJniEnvironment env; - m_camera.callMethod<void>("cancelAutoFocus"); -} - -bool AndroidCameraPrivate::isAutoExposureLockSupported() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return false; - - return m_parameters.callMethod<jboolean>("isAutoExposureLockSupported"); -} - -bool AndroidCameraPrivate::getAutoExposureLock() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return false; - - return m_parameters.callMethod<jboolean>("getAutoExposureLock"); -} - -void AndroidCameraPrivate::setAutoExposureLock(bool toggle) -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return; - - m_parameters.callMethod<void>("setAutoExposureLock", "(Z)V", toggle); - applyParameters(); -} - -bool AndroidCameraPrivate::isAutoWhiteBalanceLockSupported() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return false; - - return m_parameters.callMethod<jboolean>("isAutoWhiteBalanceLockSupported"); -} - -bool AndroidCameraPrivate::getAutoWhiteBalanceLock() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return false; - - return m_parameters.callMethod<jboolean>("getAutoWhiteBalanceLock"); -} - -void AndroidCameraPrivate::setAutoWhiteBalanceLock(bool toggle) -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return; - - m_parameters.callMethod<void>("setAutoWhiteBalanceLock", "(Z)V", toggle); - applyParameters(); -} - -int AndroidCameraPrivate::getExposureCompensation() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return 0; - - return m_parameters.callMethod<jint>("getExposureCompensation"); -} - -void AndroidCameraPrivate::setExposureCompensation(int value) -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return; - - m_parameters.callMethod<void>("setExposureCompensation", "(I)V", value); - applyParameters(); -} - -float AndroidCameraPrivate::getExposureCompensationStep() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return 0; - - return m_parameters.callMethod<jfloat>("getExposureCompensationStep"); -} - -int AndroidCameraPrivate::getMinExposureCompensation() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return 0; - - return m_parameters.callMethod<jint>("getMinExposureCompensation"); -} - -int AndroidCameraPrivate::getMaxExposureCompensation() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return 0; - - return m_parameters.callMethod<jint>("getMaxExposureCompensation"); -} - -QString AndroidCameraPrivate::getSceneMode() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - QString value; - - if (m_parameters.isValid()) { - QJniObject sceneMode = m_parameters.callObjectMethod("getSceneMode", - "()Ljava/lang/String;"); - if (sceneMode.isValid()) - value = sceneMode.toString(); - } - - return value; -} - -void AndroidCameraPrivate::setSceneMode(const QString &value) -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return; - - m_parameters.callMethod<void>("setSceneMode", - "(Ljava/lang/String;)V", - QJniObject::fromString(value).object()); - applyParameters(); -} - -QString AndroidCameraPrivate::getWhiteBalance() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - QString value; - - if (m_parameters.isValid()) { - QJniObject wb = m_parameters.callObjectMethod("getWhiteBalance", - "()Ljava/lang/String;"); - if (wb.isValid()) - value = wb.toString(); - } - - return value; -} - -void AndroidCameraPrivate::setWhiteBalance(const QString &value) -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return; - - m_parameters.callMethod<void>("setWhiteBalance", - "(Ljava/lang/String;)V", - QJniObject::fromString(value).object()); - applyParameters(); - - emit whiteBalanceChanged(); -} - -void AndroidCameraPrivate::updateRotation() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - m_parameters.callMethod<void>("setRotation", "(I)V", m_rotation); - applyParameters(); -} - -QList<QSize> AndroidCameraPrivate::getSupportedPictureSizes() -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - QList<QSize> list; - - if (m_parameters.isValid()) { - QJniObject sizeList = m_parameters.callObjectMethod("getSupportedPictureSizes", - "()Ljava/util/List;"); - int count = sizeList.callMethod<jint>("size"); - for (int i = 0; i < count; ++i) { - QJniObject size = sizeList.callObjectMethod("get", - "(I)Ljava/lang/Object;", - i); - list.append(QSize(size.getField<jint>("width"), size.getField<jint>("height"))); - } - - std::sort(list.begin(), list.end(), qt_sizeLessThan); - } - - return list; -} - -void AndroidCameraPrivate::setPictureSize(const QSize &size) -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return; - - m_parameters.callMethod<void>("setPictureSize", "(II)V", size.width(), size.height()); - applyParameters(); -} - -void AndroidCameraPrivate::setJpegQuality(int quality) -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - if (!m_parameters.isValid()) - return; - - m_parameters.callMethod<void>("setJpegQuality", "(I)V", quality); - applyParameters(); -} - -void AndroidCameraPrivate::startPreview() -{ - setupPreviewFrameCallback(); - - QJniEnvironment env; - auto methodId = env->GetMethodID(m_camera.objectClass(), "startPreview", "()V"); - env->CallVoidMethod(m_camera.object(), methodId); - - if (env.checkAndClearExceptions()) - emit previewFailedToStart(); - else - emit previewStarted(); -} - -void AndroidCameraPrivate::stopPreview() -{ - // cancel any pending new frame notification - m_cameraListener.callMethod<void>("notifyWhenFrameAvailable", "(Z)V", false); - m_camera.callMethod<void>("stopPreview"); - emit previewStopped(); -} - -void AndroidCameraPrivate::takePicture() -{ - // We must clear the preview callback before calling takePicture(), otherwise the call will - // block and the camera server will be frozen until the next device restart... - // That problem only happens on some devices and on the emulator - m_cameraListener.callMethod<void>("clearPreviewCallback", "(Landroid/hardware/Camera;)V", m_camera.object()); - - QJniEnvironment env; - auto methodId = env->GetMethodID(m_camera.objectClass(), "takePicture", - "(Landroid/hardware/Camera$ShutterCallback;" - "Landroid/hardware/Camera$PictureCallback;" - "Landroid/hardware/Camera$PictureCallback;)V"); - env->CallVoidMethod(m_camera.object(), methodId, m_cameraListener.object(), - jobject(0), m_cameraListener.object()); - - if (env.checkAndClearExceptions()) - emit takePictureFailed(); -} - -void AndroidCameraPrivate::setupPreviewFrameCallback() -{ - m_cameraListener.callMethod<void>("setupPreviewCallback", "(Landroid/hardware/Camera;)V", m_camera.object()); -} - -void AndroidCameraPrivate::notifyNewFrames(bool notify) -{ - m_cameraListener.callMethod<void>("notifyNewFrames", "(Z)V", notify); -} - -void AndroidCameraPrivate::fetchLastPreviewFrame() -{ - QJniEnvironment env; - QJniObject data = m_cameraListener.callObjectMethod("lastPreviewBuffer", "()[B"); - - if (!data.isValid()) { - // If there's no buffer received yet, retry when the next one arrives - m_cameraListener.callMethod<void>("notifyWhenFrameAvailable", "(Z)V", true); - return; - } - - const int arrayLength = env->GetArrayLength(static_cast<jbyteArray>(data.object())); - if (arrayLength == 0) - return; - - QByteArray bytes(arrayLength, Qt::Uninitialized); - env->GetByteArrayRegion(static_cast<jbyteArray>(data.object()), - 0, - arrayLength, - reinterpret_cast<jbyte *>(bytes.data())); - - const int width = m_cameraListener.callMethod<jint>("previewWidth"); - const int height = m_cameraListener.callMethod<jint>("previewHeight"); - const int format = m_cameraListener.callMethod<jint>("previewFormat"); - const int bpl = m_cameraListener.callMethod<jint>("previewBytesPerLine"); - - QVideoFrame frame(new QMemoryVideoBuffer(bytes, bpl), - QVideoFrameFormat(QSize(width, height), - qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat(format)))); - - emit lastPreviewFrameFetched(frame); -} - -void AndroidCameraPrivate::applyParameters() -{ - QJniEnvironment env; - m_camera.callMethod<void>("setParameters", - "(Landroid/hardware/Camera$Parameters;)V", - m_parameters.object()); -} - -QStringList AndroidCameraPrivate::callParametersStringListMethod(const QByteArray &methodName) -{ - const std::lock_guard<QRecursiveMutex> locker(m_parametersMutex); - - QStringList stringList; - - if (m_parameters.isValid()) { - QJniObject list = m_parameters.callObjectMethod(methodName.constData(), - "()Ljava/util/List;"); - - if (list.isValid()) { - int count = list.callMethod<jint>("size"); - for (int i = 0; i < count; ++i) { - QJniObject string = list.callObjectMethod("get", - "(I)Ljava/lang/Object;", - i); - stringList.append(string.toString()); - } - } - } - - return stringList; -} - -bool AndroidCamera::registerNativeMethods() -{ - static const JNINativeMethod methods[] = { - {"notifyAutoFocusComplete", "(IZ)V", (void *)notifyAutoFocusComplete}, - {"notifyPictureExposed", "(I)V", (void *)notifyPictureExposed}, - {"notifyPictureCaptured", "(I[B)V", (void *)notifyPictureCaptured}, - {"notifyNewPreviewFrame", "(I[BIIII)V", (void *)notifyNewPreviewFrame}, - {"notifyFrameAvailable", "(I)V", (void *)notifyFrameAvailable} - }; - - const int size = std::size(methods); - return QJniEnvironment().registerNativeMethods(QtCameraListenerClassName, methods, size); -} - -QT_END_NAMESPACE - -#include "androidcamera.moc" diff --git a/src/multimedia/platform/android/wrappers/jni/androidcamera_p.h b/src/multimedia/platform/android/wrappers/jni/androidcamera_p.h deleted file mode 100644 index 94c2d3c46..000000000 --- a/src/multimedia/platform/android/wrappers/jni/androidcamera_p.h +++ /dev/null @@ -1,242 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Copyright (C) 2016 Ruslan Baratov -** 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$ -** -****************************************************************************/ - -#ifndef ANDROIDCAMERA_H -#define ANDROIDCAMERA_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qobject.h> -#include <qsize.h> -#include <qrect.h> -#include <QtMultimedia/qcamera.h> -#include <QtCore/qjniobject.h> -#include <private/qcameradevice_p.h> - -QT_BEGIN_NAMESPACE - -class QThread; - -class AndroidCameraPrivate; -class AndroidSurfaceTexture; -class AndroidSurfaceHolder; - -class AndroidCamera : public QObject -{ - Q_OBJECT -public: - enum CameraFacing { - CameraFacingBack = 0, - CameraFacingFront = 1 - }; - Q_ENUM(CameraFacing) - - enum ImageFormat { // same values as in android.graphics.ImageFormat Java class - UnknownImageFormat = 0, - RGB565 = 4, - NV16 = 16, - NV21 = 17, - YUY2 = 20, - JPEG = 256, - YV12 = 842094169 - }; - Q_ENUM(ImageFormat) - - // http://developer.android.com/reference/android/hardware/Camera.Parameters.html#getSupportedPreviewFpsRange%28%29 - // "The values are multiplied by 1000 and represented in integers" - struct FpsRange { - int min; - int max; - - FpsRange(): min(0), max(0) {} - - qreal getMinReal() const { return min / 1000.0; } - qreal getMaxReal() const { return max / 1000.0; } - - static FpsRange makeFromQReal(qreal min, qreal max) - { - FpsRange range; - range.min = static_cast<int>(min * 1000.0); - range.max = static_cast<int>(max * 1000.0); - return range; - } - }; - - ~AndroidCamera(); - - static AndroidCamera *open(int cameraId); - - int cameraId() const; - - bool lock(); - bool unlock(); - bool reconnect(); - void release(); - - CameraFacing getFacing(); - int getNativeOrientation(); - - QSize getPreferredPreviewSizeForVideo(); - QList<QSize> getSupportedPreviewSizes(); - - QList<FpsRange> getSupportedPreviewFpsRange(); - - FpsRange getPreviewFpsRange(); - void setPreviewFpsRange(FpsRange); - - ImageFormat getPreviewFormat(); - void setPreviewFormat(ImageFormat fmt); - QList<ImageFormat> getSupportedPreviewFormats(); - - QSize previewSize() const; - QSize actualPreviewSize(); - void setPreviewSize(const QSize &size); - bool setPreviewTexture(AndroidSurfaceTexture *surfaceTexture); - bool setPreviewDisplay(AndroidSurfaceHolder *surfaceHolder); - void setDisplayOrientation(int degrees); - - bool isZoomSupported(); - int getMaxZoom(); - QList<int> getZoomRatios(); - int getZoom(); - void setZoom(int value); - - QStringList getSupportedFlashModes(); - QString getFlashMode(); - void setFlashMode(const QString &value); - - QStringList getSupportedFocusModes(); - QString getFocusMode(); - void setFocusMode(const QString &value); - - int getMaxNumFocusAreas(); - QList<QRect> getFocusAreas(); - void setFocusAreas(const QList<QRect> &areas); - - void autoFocus(); - void cancelAutoFocus(); - - bool isAutoExposureLockSupported(); - bool getAutoExposureLock(); - void setAutoExposureLock(bool toggle); - - bool isAutoWhiteBalanceLockSupported(); - bool getAutoWhiteBalanceLock(); - void setAutoWhiteBalanceLock(bool toggle); - - int getExposureCompensation(); - void setExposureCompensation(int value); - float getExposureCompensationStep(); - int getMinExposureCompensation(); - int getMaxExposureCompensation(); - - QStringList getSupportedSceneModes(); - QString getSceneMode(); - void setSceneMode(const QString &value); - - QStringList getSupportedWhiteBalance(); - QString getWhiteBalance(); - void setWhiteBalance(const QString &value); - - void setRotation(int rotation); - int getRotation() const; - - QList<QSize> getSupportedPictureSizes(); - void setPictureSize(const QSize &size); - void setJpegQuality(int quality); - - void startPreview(); - void stopPreview(); - void stopPreviewSynchronous(); - - void takePicture(); - - void setupPreviewFrameCallback(); - void notifyNewFrames(bool notify); - void fetchLastPreviewFrame(); - QJniObject getCameraObject(); - - static int getNumberOfCameras(); - static void getCameraInfo(int id, QCameraDevicePrivate *info); - static QVideoFrameFormat::PixelFormat QtPixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat); - static AndroidCamera::ImageFormat AndroidImageFormatFromQtPixelFormat(QVideoFrameFormat::PixelFormat); - static void getSupportedFormats(int id, QList<QCameraFormat> &formats); - static bool requestCameraPermission(); - - static bool registerNativeMethods(); -Q_SIGNALS: - void previewSizeChanged(); - void previewStarted(); - void previewFailedToStart(); - void previewStopped(); - - void autoFocusStarted(); - void autoFocusComplete(bool success); - - void whiteBalanceChanged(); - - void takePictureFailed(); - void pictureExposed(); - void pictureCaptured(const QByteArray &data); - void lastPreviewFrameFetched(const QVideoFrame &frame); - void newPreviewFrame(const QVideoFrame &frame); - -private: - AndroidCamera(AndroidCameraPrivate *d, QThread *worker); - - Q_DECLARE_PRIVATE(AndroidCamera) - AndroidCameraPrivate *d_ptr; - QScopedPointer<QThread> m_worker; -}; - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(AndroidCamera::ImageFormat) - -#endif // ANDROIDCAMERA_H diff --git a/src/multimedia/platform/android/wrappers/jni/androidmediametadataretriever.cpp b/src/multimedia/platform/android/wrappers/jni/androidmediametadataretriever.cpp deleted file mode 100644 index 733717ae7..000000000 --- a/src/multimedia/platform/android/wrappers/jni/androidmediametadataretriever.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "androidmediametadataretriever_p.h" - -#include <QtCore/QUrl> -#include <qdebug.h> -#include <QtCore/qcoreapplication.h> - -QT_BEGIN_NAMESPACE - -AndroidMediaMetadataRetriever::AndroidMediaMetadataRetriever() -{ - m_metadataRetriever = QJniObject("android/media/MediaMetadataRetriever"); -} - -AndroidMediaMetadataRetriever::~AndroidMediaMetadataRetriever() -{ - release(); -} - -QString AndroidMediaMetadataRetriever::extractMetadata(MetadataKey key) -{ - QString value; - - QJniObject metadata = m_metadataRetriever.callObjectMethod("extractMetadata", - "(I)Ljava/lang/String;", - jint(key)); - if (metadata.isValid()) - value = metadata.toString(); - - return value; -} - -void AndroidMediaMetadataRetriever::release() -{ - if (!m_metadataRetriever.isValid()) - return; - - m_metadataRetriever.callMethod<void>("release"); -} - -bool AndroidMediaMetadataRetriever::setDataSource(const QUrl &url) -{ - if (!m_metadataRetriever.isValid()) - return false; - - QJniEnvironment env; - if (url.isLocalFile()) { // also includes qrc files (copied to a temp file by QMediaPlayer) - QJniObject string = QJniObject::fromString(url.path()); - QJniObject fileInputStream("java/io/FileInputStream", - "(Ljava/lang/String;)V", - string.object()); - - if (!fileInputStream.isValid()) - return false; - - QJniObject fd = fileInputStream.callObjectMethod("getFD", - "()Ljava/io/FileDescriptor;"); - if (!fd.isValid()) { - fileInputStream.callMethod<void>("close"); - return false; - } - - auto methodId = env->GetMethodID(m_metadataRetriever.objectClass(), "setDataSource", - "(Ljava/io/FileDescriptor;)V"); - env->CallVoidMethod(m_metadataRetriever.object(), methodId, fd.object()); - bool ok = !env.checkAndClearExceptions(); - fileInputStream.callMethod<void>("close"); - if (!ok) - return false; - } else if (url.scheme() == QLatin1String("assets")) { - QJniObject string = QJniObject::fromString(url.path().mid(1)); // remove first '/' - QJniObject activity(QNativeInterface::QAndroidApplication::context()); - QJniObject assetManager = activity.callObjectMethod("getAssets", - "()Landroid/content/res/AssetManager;"); - QJniObject assetFd = assetManager.callObjectMethod("openFd", - "(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;", - string.object()); - if (!assetFd.isValid()) - return false; - - QJniObject fd = assetFd.callObjectMethod("getFileDescriptor", - "()Ljava/io/FileDescriptor;"); - if (!fd.isValid()) { - assetFd.callMethod<void>("close"); - return false; - } - - auto methodId = env->GetMethodID(m_metadataRetriever.objectClass(), "setDataSource", - "(Ljava/io/FileDescriptor;JJ)V"); - env->CallVoidMethod(m_metadataRetriever.object(), methodId, - fd.object(), - assetFd.callMethod<jlong>("getStartOffset"), - assetFd.callMethod<jlong>("getLength")); - bool ok = !env.checkAndClearExceptions(); - assetFd.callMethod<void>("close"); - - if (!ok) - return false; - } else if (url.scheme() != QLatin1String("content")) { - // On API levels >= 14, only setDataSource(String, Map<String, String>) accepts remote media - QJniObject string = QJniObject::fromString(url.toString(QUrl::FullyEncoded)); - QJniObject hash("java/util/HashMap"); - - auto methodId = env->GetMethodID(m_metadataRetriever.objectClass(), "setDataSource", - "(Ljava/lang/String;Ljava/util/Map;)V"); - env->CallVoidMethod(m_metadataRetriever.object(), methodId, - string.object(), hash.object()); - if (env.checkAndClearExceptions()) - return false; - } else { - // While on API levels < 14, only setDataSource(Context, Uri) is available and works for - // remote media... - QJniObject string = QJniObject::fromString(url.toString(QUrl::FullyEncoded)); - QJniObject uri = m_metadataRetriever.callStaticObjectMethod( - "android/net/Uri", - "parse", - "(Ljava/lang/String;)Landroid/net/Uri;", - string.object()); - if (!uri.isValid()) - return false; - - auto methodId = env->GetMethodID(m_metadataRetriever.objectClass(), "setDataSource", - "(Landroid/content/Context;Landroid/net/Uri;)V"); - env->CallVoidMethod(m_metadataRetriever.object(), methodId, - QNativeInterface::QAndroidApplication::context(), uri.object()); - if (env.checkAndClearExceptions()) - return false; - } - - return true; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/wrappers/jni/androidmediametadataretriever_p.h b/src/multimedia/platform/android/wrappers/jni/androidmediametadataretriever_p.h deleted file mode 100644 index 69727bbd3..000000000 --- a/src/multimedia/platform/android/wrappers/jni/androidmediametadataretriever_p.h +++ /dev/null @@ -1,102 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef ANDROIDMEDIAMETADATARETRIEVER_H -#define ANDROIDMEDIAMETADATARETRIEVER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/private/qglobal_p.h> -#include <QtCore/qurl.h> -#include <QtCore/qjniobject.h> - -QT_BEGIN_NAMESPACE - -class AndroidMediaMetadataRetriever -{ -public: - enum MetadataKey { - Album = 1, - AlbumArtist = 13, - Artist = 2, - Author = 3, - Bitrate = 20, - CDTrackNumber = 0, - Compilation = 15, - Composer = 4, - Date = 5, - DiscNumber = 14, - Duration = 9, - Genre = 6, - HasAudio = 16, - HasVideo = 17, - Location = 23, - MimeType = 12, - NumTracks = 10, - Title = 7, - VideoHeight = 19, - VideoWidth = 18, - VideoRotation = 24, - Writer = 11, - Year = 8 - }; - - AndroidMediaMetadataRetriever(); - ~AndroidMediaMetadataRetriever(); - - QString extractMetadata(MetadataKey key); - bool setDataSource(const QUrl &url); - -private: - void release(); - QJniObject m_metadataRetriever; -}; - -QT_END_NAMESPACE - -#endif // ANDROIDMEDIAMETADATARETRIEVER_H diff --git a/src/multimedia/platform/android/wrappers/jni/androidmediaplayer.cpp b/src/multimedia/platform/android/wrappers/jni/androidmediaplayer.cpp deleted file mode 100644 index f28622d6c..000000000 --- a/src/multimedia/platform/android/wrappers/jni/androidmediaplayer.cpp +++ /dev/null @@ -1,581 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "androidmediaplayer_p.h" -#include "androidsurfacetexture_p.h" - -#include <QList> -#include <QReadWriteLock> -#include <QString> -#include <QtCore/qcoreapplication.h> - -static const char QtAndroidMediaPlayerClassName[] = "org/qtproject/qt/android/multimedia/QtAndroidMediaPlayer"; -typedef QList<AndroidMediaPlayer *> MediaPlayerList; -Q_GLOBAL_STATIC(MediaPlayerList, mediaPlayers) -Q_GLOBAL_STATIC(QReadWriteLock, rwLock) - -QT_BEGIN_NAMESPACE - -Q_LOGGING_CATEGORY(lcAudio, "qt.multimedia.audio") - -AndroidMediaPlayer::AndroidMediaPlayer() - : QObject() -{ - QWriteLocker locker(rwLock); - auto context = QNativeInterface::QAndroidApplication::context(); - const jlong id = reinterpret_cast<jlong>(this); - mMediaPlayer = QJniObject(QtAndroidMediaPlayerClassName, - "(Landroid/content/Context;J)V", - context, - id); - mediaPlayers->append(this); -} - -AndroidMediaPlayer::~AndroidMediaPlayer() -{ - QWriteLocker locker(rwLock); - const int i = mediaPlayers->indexOf(this); - Q_ASSERT(i != -1); - mediaPlayers->remove(i); -} - -void AndroidMediaPlayer::release() -{ - mMediaPlayer.callMethod<void>("release"); -} - -void AndroidMediaPlayer::reset() -{ - mMediaPlayer.callMethod<void>("reset"); -} - -int AndroidMediaPlayer::getCurrentPosition() -{ - return mMediaPlayer.callMethod<jint>("getCurrentPosition"); -} - -int AndroidMediaPlayer::getDuration() -{ - return mMediaPlayer.callMethod<jint>("getDuration"); -} - -bool AndroidMediaPlayer::isPlaying() -{ - return mMediaPlayer.callMethod<jboolean>("isPlaying"); -} - -int AndroidMediaPlayer::volume() -{ - return mMediaPlayer.callMethod<jint>("getVolume"); -} - -bool AndroidMediaPlayer::isMuted() -{ - return mMediaPlayer.callMethod<jboolean>("isMuted"); -} - -qreal AndroidMediaPlayer::playbackRate() -{ - qreal rate(1.0); - - if (QNativeInterface::QAndroidApplication::sdkVersion() < 23) - return rate; - - QJniObject player = mMediaPlayer.callObjectMethod("getMediaPlayerHandle", - "()Landroid/media/MediaPlayer;"); - if (player.isValid()) { - QJniObject playbackParams = player.callObjectMethod("getPlaybackParams", - "()Landroid/media/PlaybackParams;"); - if (playbackParams.isValid()) { - QJniEnvironment env; - auto methodId = env->GetMethodID(playbackParams.objectClass(), "getSpeed", "()F"); - const qreal speed = env->CallFloatMethod(playbackParams.object(), methodId); - if (!env.checkAndClearExceptions()) - rate = speed; - } - } - - return rate; -} - -jobject AndroidMediaPlayer::display() -{ - return mMediaPlayer.callObjectMethod("display", "()Landroid/view/SurfaceHolder;").object(); -} - -AndroidMediaPlayer::TrackInfo convertTrackInfo(int streamNumber, QJniObject androidTrackInfo) -{ - const QLatin1String unknownMimeType("application/octet-stream"); - const QLatin1String undefinedLanguage("und"); - - if (!androidTrackInfo.isValid()) - return { streamNumber, AndroidMediaPlayer::TrackType::Unknown, undefinedLanguage, - unknownMimeType }; - - QJniEnvironment env; - auto methodId = env->GetMethodID(androidTrackInfo.objectClass(), "getType", "()I"); - const jint type = env->CallIntMethod(androidTrackInfo.object(), methodId); - if (env.checkAndClearExceptions()) - return { streamNumber, AndroidMediaPlayer::TrackType::Unknown, undefinedLanguage, - unknownMimeType }; - - if (type < 0 || type > 5) { - return { streamNumber, AndroidMediaPlayer::TrackType::Unknown, undefinedLanguage, - unknownMimeType }; - } - - AndroidMediaPlayer::TrackType trackType = static_cast<AndroidMediaPlayer::TrackType>(type); - - auto languageObject = androidTrackInfo.callObjectMethod("getLanguage", "()Ljava/lang/String;"); - QString language = languageObject.isValid() ? languageObject.toString() : undefinedLanguage; - - auto mimeTypeObject = androidTrackInfo.callObjectMethod("getMime", "()Ljava/lang/String;"); - QString mimeType = mimeTypeObject.isValid() ? mimeTypeObject.toString() : unknownMimeType; - - return { streamNumber, trackType, language, mimeType }; -} - -QList<AndroidMediaPlayer::TrackInfo> AndroidMediaPlayer::tracksInfo() -{ - auto androidTracksInfoObject = mMediaPlayer.callObjectMethod( - "getAllTrackInfo", - "()[Lorg/qtproject/qt/android/multimedia/QtAndroidMediaPlayer$TrackInfo;"); - - if (!androidTracksInfoObject.isValid()) - return QList<AndroidMediaPlayer::TrackInfo>(); - - auto androidTracksInfo = androidTracksInfoObject.object<jobjectArray>(); - if (!androidTracksInfo) - return QList<AndroidMediaPlayer::TrackInfo>(); - - QJniEnvironment environment; - auto numberofTracks = environment->GetArrayLength(androidTracksInfo); - - QList<AndroidMediaPlayer::TrackInfo> tracksInformation; - - for (int index = 0; index < numberofTracks; index++) { - auto androidTrackInformation = environment->GetObjectArrayElement(androidTracksInfo, index); - - if (environment.checkAndClearExceptions()) { - continue; - } - - auto trackInfo = convertTrackInfo(index, androidTrackInformation); - tracksInformation.insert(index, trackInfo); - - environment->DeleteLocalRef(androidTrackInformation); - } - - return tracksInformation; -} - -int AndroidMediaPlayer::activeTrack(TrackType androidTrackType) -{ - int type = static_cast<int>(androidTrackType); - return mMediaPlayer.callMethod<jint>("getSelectedTrack", "(I)I", type); -} - -void AndroidMediaPlayer::deselectTrack(int trackNumber) -{ - mMediaPlayer.callMethod<void>("deselectTrack", "(I)V", trackNumber); -} - -void AndroidMediaPlayer::selectTrack(int trackNumber) -{ - mMediaPlayer.callMethod<void>("selectTrack", "(I)V", trackNumber); -} - -void AndroidMediaPlayer::play() -{ - mMediaPlayer.callMethod<void>("start"); -} - -void AndroidMediaPlayer::pause() -{ - mMediaPlayer.callMethod<void>("pause"); -} - -void AndroidMediaPlayer::stop() -{ - mMediaPlayer.callMethod<void>("stop"); -} - -void AndroidMediaPlayer::seekTo(qint32 msec) -{ - mMediaPlayer.callMethod<void>("seekTo", "(I)V", jint(msec)); -} - -void AndroidMediaPlayer::setMuted(bool mute) -{ - if (mAudioBlocked) - return; - - mMediaPlayer.callMethod<void>("mute", "(Z)V", jboolean(mute)); -} - -void AndroidMediaPlayer::setDataSource(const QNetworkRequest &request) -{ - QJniObject string = QJniObject::fromString(request.url().toString(QUrl::FullyEncoded)); - - mMediaPlayer.callMethod<void>("initHeaders", "()V"); - for (auto &header : request.rawHeaderList()) { - auto value = request.rawHeader(header); - mMediaPlayer.callMethod<void>("setHeader", "(Ljava/lang/String;Ljava/lang/String;)V", - QJniObject::fromString(QLatin1String(header)).object(), - QJniObject::fromString(QLatin1String(value)).object()); - } - - mMediaPlayer.callMethod<void>("setDataSource", "(Ljava/lang/String;)V", string.object()); -} - -void AndroidMediaPlayer::prepareAsync() -{ - mMediaPlayer.callMethod<void>("prepareAsync"); -} - -void AndroidMediaPlayer::setVolume(int volume) -{ - if (mAudioBlocked) - return; - - mMediaPlayer.callMethod<void>("setVolume", "(I)V", jint(volume)); -} - -void AndroidMediaPlayer::blockAudio() -{ - mAudioBlocked = true; -} - -void AndroidMediaPlayer::unblockAudio() -{ - mAudioBlocked = false; -} - -bool AndroidMediaPlayer::setPlaybackRate(qreal rate) -{ - if (QNativeInterface::QAndroidApplication::sdkVersion() < 23) { - qWarning() << "Setting the playback rate on a media player requires" - << "Android 6.0 (API level 23) or later"; - return false; - } - - QJniObject player = mMediaPlayer.callObjectMethod("getMediaPlayerHandle", - "()Landroid/media/MediaPlayer;"); - if (player.isValid()) { - QJniObject playbackParams = player.callObjectMethod("getPlaybackParams", - "()Landroid/media/PlaybackParams;"); - if (playbackParams.isValid()) { - playbackParams.callObjectMethod("setSpeed", "(F)Landroid/media/PlaybackParams;", - jfloat(rate)); - // pitch can only be > 0 - if (!qFuzzyIsNull(rate)) - playbackParams.callObjectMethod("setPitch", "(F)Landroid/media/PlaybackParams;", - jfloat(qAbs(rate))); - - QJniEnvironment env; - auto methodId = env->GetMethodID(player.objectClass(), "setPlaybackParams", - "(Landroid/media/PlaybackParams;)V"); - env->CallVoidMethod(player.object(), methodId, playbackParams.object()); - - if (env.checkAndClearExceptions()) { - qWarning() << "Invalid playback rate" << rate; - return false; - } else { - return true; - } - } - } - - return false; -} - -void AndroidMediaPlayer::setDisplay(AndroidSurfaceTexture *surfaceTexture) -{ - mMediaPlayer.callMethod<void>("setDisplay", - "(Landroid/view/SurfaceHolder;)V", - surfaceTexture ? surfaceTexture->surfaceHolder() : 0); -} - -bool AndroidMediaPlayer::setAudioOutput(const QByteArray &deviceId) -{ - const bool ret = QJniObject::callStaticMethod<jboolean>( - "org/qtproject/qt/android/multimedia/QtAudioDeviceManager", - "setAudioOutput", - "(I)Z", - deviceId.toInt()); - - if (!ret) - qCWarning(lcAudio) << "Output device not set"; - - return ret; -} - -#if 0 -void AndroidMediaPlayer::setAudioRole(QAudio::Role role) -{ - QString r; - switch (role) { - case QAudio::MusicRole: - r = QLatin1String("CONTENT_TYPE_MUSIC"); - break; - case QAudio::VideoRole: - r = QLatin1String("CONTENT_TYPE_MOVIE"); - break; - case QAudio::VoiceCommunicationRole: - r = QLatin1String("USAGE_VOICE_COMMUNICATION"); - break; - case QAudio::AlarmRole: - r = QLatin1String("USAGE_ALARM"); - break; - case QAudio::NotificationRole: - r = QLatin1String("USAGE_NOTIFICATION"); - break; - case QAudio::RingtoneRole: - r = QLatin1String("USAGE_NOTIFICATION_RINGTONE"); - break; - case QAudio::AccessibilityRole: - r = QLatin1String("USAGE_ASSISTANCE_ACCESSIBILITY"); - break; - case QAudio::SonificationRole: - r = QLatin1String("CONTENT_TYPE_SONIFICATION"); - break; - case QAudio::GameRole: - r = QLatin1String("USAGE_GAME"); - break; - default: - return; - } - - int type = 0; // CONTENT_TYPE_UNKNOWN - int usage = 0; // USAGE_UNKNOWN - - if (r == QLatin1String("CONTENT_TYPE_MOVIE")) - type = 3; - else if (r == QLatin1String("CONTENT_TYPE_MUSIC")) - type = 2; - else if (r == QLatin1String("CONTENT_TYPE_SONIFICATION")) - type = 4; - else if (r == QLatin1String("CONTENT_TYPE_SPEECH")) - type = 1; - else if (r == QLatin1String("USAGE_ALARM")) - usage = 4; - else if (r == QLatin1String("USAGE_ASSISTANCE_ACCESSIBILITY")) - usage = 11; - else if (r == QLatin1String("USAGE_ASSISTANCE_NAVIGATION_GUIDANCE")) - usage = 12; - else if (r == QLatin1String("USAGE_ASSISTANCE_SONIFICATION")) - usage = 13; - else if (r == QLatin1String("USAGE_ASSISTANT")) - usage = 16; - else if (r == QLatin1String("USAGE_GAME")) - usage = 14; - else if (r == QLatin1String("USAGE_MEDIA")) - usage = 1; - else if (r == QLatin1String("USAGE_NOTIFICATION")) - usage = 5; - else if (r == QLatin1String("USAGE_NOTIFICATION_COMMUNICATION_DELAYED")) - usage = 9; - else if (r == QLatin1String("USAGE_NOTIFICATION_COMMUNICATION_INSTANT")) - usage = 8; - else if (r == QLatin1String("USAGE_NOTIFICATION_COMMUNICATION_REQUEST")) - usage = 7; - else if (r == QLatin1String("USAGE_NOTIFICATION_EVENT")) - usage = 10; - else if (r == QLatin1String("USAGE_NOTIFICATION_RINGTONE")) - usage = 6; - else if (r == QLatin1String("USAGE_VOICE_COMMUNICATION")) - usage = 2; - else if (r == QLatin1String("USAGE_VOICE_COMMUNICATION_SIGNALLING")) - usage = 3; - - mMediaPlayer.callMethod<void>("setAudioAttributes", "(II)V", jint(type), jint(usage)); -} -#endif - -static void onErrorNative(JNIEnv *env, jobject thiz, jint what, jint extra, jlong id) -{ - Q_UNUSED(env); - Q_UNUSED(thiz); - QReadLocker locker(rwLock); - const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id)); - if (Q_UNLIKELY(i == -1)) - return; - - Q_EMIT (*mediaPlayers)[i]->error(what, extra); -} - -static void onBufferingUpdateNative(JNIEnv *env, jobject thiz, jint percent, jlong id) -{ - Q_UNUSED(env); - Q_UNUSED(thiz); - QReadLocker locker(rwLock); - const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id)); - if (Q_UNLIKELY(i == -1)) - return; - - Q_EMIT (*mediaPlayers)[i]->bufferingChanged(percent); -} - -static void onProgressUpdateNative(JNIEnv *env, jobject thiz, jint progress, jlong id) -{ - Q_UNUSED(env); - Q_UNUSED(thiz); - QReadLocker locker(rwLock); - const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id)); - if (Q_UNLIKELY(i == -1)) - return; - - Q_EMIT (*mediaPlayers)[i]->progressChanged(progress); -} - -static void onDurationChangedNative(JNIEnv *env, jobject thiz, jint duration, jlong id) -{ - Q_UNUSED(env); - Q_UNUSED(thiz); - QReadLocker locker(rwLock); - const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id)); - if (Q_UNLIKELY(i == -1)) - return; - - Q_EMIT (*mediaPlayers)[i]->durationChanged(duration); -} - -static void onInfoNative(JNIEnv *env, jobject thiz, jint what, jint extra, jlong id) -{ - Q_UNUSED(env); - Q_UNUSED(thiz); - QReadLocker locker(rwLock); - const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id)); - if (Q_UNLIKELY(i == -1)) - return; - - Q_EMIT (*mediaPlayers)[i]->info(what, extra); -} - -static void onStateChangedNative(JNIEnv *env, jobject thiz, jint state, jlong id) -{ - Q_UNUSED(env); - Q_UNUSED(thiz); - QReadLocker locker(rwLock); - const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id)); - if (Q_UNLIKELY(i == -1)) - return; - - Q_EMIT (*mediaPlayers)[i]->stateChanged(state); -} - -static void onVideoSizeChangedNative(JNIEnv *env, - jobject thiz, - jint width, - jint height, - jlong id) -{ - Q_UNUSED(env); - Q_UNUSED(thiz); - QReadLocker locker(rwLock); - const int i = mediaPlayers->indexOf(reinterpret_cast<AndroidMediaPlayer *>(id)); - if (Q_UNLIKELY(i == -1)) - return; - - Q_EMIT (*mediaPlayers)[i]->videoSizeChanged(width, height); -} - -static AndroidMediaPlayer *getMediaPlayer(jlong ptr) -{ - auto mediaplayer = reinterpret_cast<AndroidMediaPlayer *>(ptr); - if (!mediaplayer || !mediaPlayers->contains(mediaplayer)) - return nullptr; - - return mediaplayer; -} - -static void onTrackInfoChangedNative(JNIEnv *env, jobject thiz, jlong ptr) -{ - Q_UNUSED(env); - Q_UNUSED(thiz); - - QReadLocker locker(rwLock); - auto mediaplayer = getMediaPlayer(ptr); - if (!mediaplayer) - return; - - emit mediaplayer->tracksInfoChanged(); -} - -static void onTimedTextChangedNative(JNIEnv *env, jobject thiz, jstring timedText, jint time, - jlong ptr) -{ - Q_UNUSED(env); - Q_UNUSED(thiz); - Q_UNUSED(time); - - QReadLocker locker(rwLock); - - auto mediaplayer = getMediaPlayer(ptr); - if (!mediaplayer) - return; - - QString subtitleText; - if (timedText != nullptr) - subtitleText = QString::fromUtf8(env->GetStringUTFChars(timedText, 0)); - - emit mediaplayer->timedTextChanged(subtitleText); -} - -bool AndroidMediaPlayer::registerNativeMethods() -{ - static const JNINativeMethod methods[] = { - { "onErrorNative", "(IIJ)V", reinterpret_cast<void *>(onErrorNative) }, - { "onBufferingUpdateNative", "(IJ)V", reinterpret_cast<void *>(onBufferingUpdateNative) }, - { "onProgressUpdateNative", "(IJ)V", reinterpret_cast<void *>(onProgressUpdateNative) }, - { "onDurationChangedNative", "(IJ)V", reinterpret_cast<void *>(onDurationChangedNative) }, - { "onInfoNative", "(IIJ)V", reinterpret_cast<void *>(onInfoNative) }, - { "onVideoSizeChangedNative", "(IIJ)V", - reinterpret_cast<void *>(onVideoSizeChangedNative) }, - { "onStateChangedNative", "(IJ)V", reinterpret_cast<void *>(onStateChangedNative) }, - { "onTrackInfoChangedNative", "(J)V", reinterpret_cast<void *>(onTrackInfoChangedNative) }, - { "onTimedTextChangedNative", "(Ljava/lang/String;IJ)V", - reinterpret_cast<void *>(onTimedTextChangedNative) } - }; - - const int size = std::size(methods); - return QJniEnvironment().registerNativeMethods(QtAndroidMediaPlayerClassName, methods, size); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/wrappers/jni/androidmediaplayer_p.h b/src/multimedia/platform/android/wrappers/jni/androidmediaplayer_p.h deleted file mode 100644 index d5cf07f9c..000000000 --- a/src/multimedia/platform/android/wrappers/jni/androidmediaplayer_p.h +++ /dev/null @@ -1,169 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef ANDROIDMEDIAPLAYER_H -#define ANDROIDMEDIAPLAYER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QObject> -#include <QNetworkRequest> -#include <QtCore/qjniobject.h> -#include <QAudio> - -QT_BEGIN_NAMESPACE - -class AndroidSurfaceTexture; - -class AndroidMediaPlayer : public QObject -{ - Q_OBJECT -public: - AndroidMediaPlayer(); - ~AndroidMediaPlayer(); - - enum MediaError - { - // What - MEDIA_ERROR_UNKNOWN = 1, - MEDIA_ERROR_SERVER_DIED = 100, - MEDIA_ERROR_INVALID_STATE = -38, // Undocumented - // Extra - MEDIA_ERROR_IO = -1004, - MEDIA_ERROR_MALFORMED = -1007, - MEDIA_ERROR_UNSUPPORTED = -1010, - MEDIA_ERROR_TIMED_OUT = -110, - MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200, - MEDIA_ERROR_BAD_THINGS_ARE_GOING_TO_HAPPEN = -2147483648 // Undocumented - }; - - enum MediaInfo - { - MEDIA_INFO_UNKNOWN = 1, - MEDIA_INFO_VIDEO_TRACK_LAGGING = 700, - MEDIA_INFO_VIDEO_RENDERING_START = 3, - MEDIA_INFO_BUFFERING_START = 701, - MEDIA_INFO_BUFFERING_END = 702, - MEDIA_INFO_BAD_INTERLEAVING = 800, - MEDIA_INFO_NOT_SEEKABLE = 801, - MEDIA_INFO_METADATA_UPDATE = 802 - }; - - enum MediaPlayerState { - Uninitialized = 0x1, /* End */ - Idle = 0x2, - Preparing = 0x4, - Prepared = 0x8, - Initialized = 0x10, - Started = 0x20, - Stopped = 0x40, - Paused = 0x80, - PlaybackCompleted = 0x100, - Error = 0x200 - }; - - enum TrackType { Unknown = 0, Video, Audio, TimedText, Subtitle, Metadata }; - - struct TrackInfo - { - int trackNumber; - TrackType trackType; - QString language; - QString mimeType; - }; - - void release(); - void reset(); - - int getCurrentPosition(); - int getDuration(); - bool isPlaying(); - int volume(); - bool isMuted(); - qreal playbackRate(); - jobject display(); - - void play(); - void pause(); - void stop(); - void seekTo(qint32 msec); - void setMuted(bool mute); - void setDataSource(const QNetworkRequest &request); - void prepareAsync(); - void setVolume(int volume); - bool setPlaybackRate(qreal rate); - void setDisplay(AndroidSurfaceTexture *surfaceTexture); - static bool setAudioOutput(const QByteArray &deviceId); - QList<TrackInfo> tracksInfo(); - int activeTrack(TrackType trackType); - void deselectTrack(int trackNumber); - void selectTrack(int trackNumber); - - static bool registerNativeMethods(); - - void blockAudio(); - void unblockAudio(); -Q_SIGNALS: - void error(qint32 what, qint32 extra); - void bufferingChanged(qint32 percent); - void durationChanged(qint64 duration); - void progressChanged(qint64 progress); - void stateChanged(qint32 state); - void info(qint32 what, qint32 extra); - void videoSizeChanged(qint32 width, qint32 height); - void timedTextChanged(QString text); - void tracksInfoChanged(); - -private: - QJniObject mMediaPlayer; - bool mAudioBlocked = false; -}; - -QT_END_NAMESPACE - -#endif // ANDROIDMEDIAPLAYER_H diff --git a/src/multimedia/platform/android/wrappers/jni/androidmediarecorder.cpp b/src/multimedia/platform/android/wrappers/jni/androidmediarecorder.cpp deleted file mode 100644 index c5cdadbfd..000000000 --- a/src/multimedia/platform/android/wrappers/jni/androidmediarecorder.cpp +++ /dev/null @@ -1,344 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "androidmediarecorder_p.h" - -#include "androidcamera_p.h" -#include "androidsurfacetexture_p.h" -#include "androidsurfaceview_p.h" -#include "qandroidglobal_p.h" -#include "qandroidmultimediautils_p.h" -#include <qmap.h> -#include <QtCore/qlogging.h> - -QT_BEGIN_NAMESPACE - -Q_LOGGING_CATEGORY(lcMediaRecorder, "qt.multimedia.mediarecorder.android") - -typedef QMap<QString, QJniObject> CamcorderProfiles; -Q_GLOBAL_STATIC(CamcorderProfiles, g_camcorderProfiles) - -static QString profileKey() -{ - return QStringLiteral("%1-%2"); -} - -bool AndroidCamcorderProfile::hasProfile(jint cameraId, Quality quality) -{ - if (g_camcorderProfiles->contains(profileKey().arg(cameraId).arg(quality))) - return true; - - return QJniObject::callStaticMethod<jboolean>("android/media/CamcorderProfile", - "hasProfile", - "(II)Z", - cameraId, - quality); -} - -AndroidCamcorderProfile AndroidCamcorderProfile::get(jint cameraId, Quality quality) -{ - const QString key = profileKey().arg(cameraId).arg(quality); - QMap<QString, QJniObject>::const_iterator it = g_camcorderProfiles->constFind(key); - - if (it != g_camcorderProfiles->constEnd()) - return AndroidCamcorderProfile(*it); - - QJniObject camProfile = QJniObject::callStaticObjectMethod("android/media/CamcorderProfile", - "get", - "(II)Landroid/media/CamcorderProfile;", - cameraId, - quality); - - return AndroidCamcorderProfile((*g_camcorderProfiles)[key] = camProfile); -} - -int AndroidCamcorderProfile::getValue(AndroidCamcorderProfile::Field field) const -{ - switch (field) { - case audioBitRate: - return m_camcorderProfile.getField<jint>("audioBitRate"); - case audioChannels: - return m_camcorderProfile.getField<jint>("audioChannels"); - case audioCodec: - return m_camcorderProfile.getField<jint>("audioCodec"); - case audioSampleRate: - return m_camcorderProfile.getField<jint>("audioSampleRate"); - case duration: - return m_camcorderProfile.getField<jint>("duration"); - case fileFormat: - return m_camcorderProfile.getField<jint>("fileFormat"); - case quality: - return m_camcorderProfile.getField<jint>("quality"); - case videoBitRate: - return m_camcorderProfile.getField<jint>("videoBitRate"); - case videoCodec: - return m_camcorderProfile.getField<jint>("videoCodec"); - case videoFrameHeight: - return m_camcorderProfile.getField<jint>("videoFrameHeight"); - case videoFrameRate: - return m_camcorderProfile.getField<jint>("videoFrameRate"); - case videoFrameWidth: - return m_camcorderProfile.getField<jint>("videoFrameWidth"); - } - - return 0; -} - -AndroidCamcorderProfile::AndroidCamcorderProfile(const QJniObject &camcorderProfile) -{ - m_camcorderProfile = camcorderProfile; -} - -static const char QtMediaRecorderListenerClassName[] = - "org/qtproject/qt/android/multimedia/QtMediaRecorderListener"; -typedef QMap<jlong, AndroidMediaRecorder*> MediaRecorderMap; -Q_GLOBAL_STATIC(MediaRecorderMap, mediaRecorders) - -static void notifyError(JNIEnv* , jobject, jlong id, jint what, jint extra) -{ - AndroidMediaRecorder *obj = mediaRecorders->value(id, 0); - if (obj) - emit obj->error(what, extra); -} - -static void notifyInfo(JNIEnv* , jobject, jlong id, jint what, jint extra) -{ - AndroidMediaRecorder *obj = mediaRecorders->value(id, 0); - if (obj) - emit obj->info(what, extra); -} - -AndroidMediaRecorder::AndroidMediaRecorder() - : QObject() - , m_id(reinterpret_cast<jlong>(this)) -{ - m_mediaRecorder = QJniObject("android/media/MediaRecorder"); - if (m_mediaRecorder.isValid()) { - QJniObject listener(QtMediaRecorderListenerClassName, "(J)V", m_id); - m_mediaRecorder.callMethod<void>("setOnErrorListener", - "(Landroid/media/MediaRecorder$OnErrorListener;)V", - listener.object()); - m_mediaRecorder.callMethod<void>("setOnInfoListener", - "(Landroid/media/MediaRecorder$OnInfoListener;)V", - listener.object()); - mediaRecorders->insert(m_id, this); - } -} - -AndroidMediaRecorder::~AndroidMediaRecorder() -{ - mediaRecorders->remove(m_id); -} - -void AndroidMediaRecorder::release() -{ - m_mediaRecorder.callMethod<void>("release"); -} - -bool AndroidMediaRecorder::prepare() -{ - QJniEnvironment env; - auto methodId = env->GetMethodID(m_mediaRecorder.objectClass(), "prepare", "()V"); - env->CallVoidMethod(m_mediaRecorder.object(), methodId); - - if (env.checkAndClearExceptions()) - return false; - return true; -} - -void AndroidMediaRecorder::reset() -{ - m_mediaRecorder.callMethod<void>("reset"); - m_isAudioSourceSet = false; // Now setAudioSource can be used again. -} - -bool AndroidMediaRecorder::start() -{ - QJniEnvironment env; - auto methodId = env->GetMethodID(m_mediaRecorder.objectClass(), "start", "()V"); - env->CallVoidMethod(m_mediaRecorder.object(), methodId); - - if (env.checkAndClearExceptions()) - return false; - return true; -} - -void AndroidMediaRecorder::stop() -{ - m_mediaRecorder.callMethod<void>("stop"); -} - -void AndroidMediaRecorder::setAudioChannels(int numChannels) -{ - m_mediaRecorder.callMethod<void>("setAudioChannels", "(I)V", numChannels); -} - -void AndroidMediaRecorder::setAudioEncoder(AudioEncoder encoder) -{ - QJniEnvironment env; - m_mediaRecorder.callMethod<void>("setAudioEncoder", "(I)V", int(encoder)); -} - -void AndroidMediaRecorder::setAudioEncodingBitRate(int bitRate) -{ - m_mediaRecorder.callMethod<void>("setAudioEncodingBitRate", "(I)V", bitRate); -} - -void AndroidMediaRecorder::setAudioSamplingRate(int samplingRate) -{ - m_mediaRecorder.callMethod<void>("setAudioSamplingRate", "(I)V", samplingRate); -} - -void AndroidMediaRecorder::setAudioSource(AudioSource source) -{ - if (!m_isAudioSourceSet) { - QJniEnvironment env; - auto methodId = env->GetMethodID(m_mediaRecorder.objectClass(), "setAudioSource", "(I)V"); - env->CallVoidMethod(m_mediaRecorder.object(), methodId, source); - if (!env.checkAndClearExceptions()) - m_isAudioSourceSet = true; - } else { - qCWarning(lcMediaRecorder) << "Audio source already set. Not setting a new source."; - } -} - -bool AndroidMediaRecorder::isAudioSourceSet() const -{ - return m_isAudioSourceSet; -} - -bool AndroidMediaRecorder::setAudioInput(const QByteArray &id) -{ - const bool ret = QJniObject::callStaticMethod<jboolean>( - "org/qtproject/qt/android/multimedia/QtAudioDeviceManager", - "setAudioInput", - "(Landroid/media/MediaRecorder;I)Z", - m_mediaRecorder.object(), - id.toInt()); - if (!ret) - qCWarning(lcMediaRecorder) << "No default input device was set."; - - return ret; -} - -void AndroidMediaRecorder::setCamera(AndroidCamera *camera) -{ - QJniObject cam = camera->getCameraObject(); - m_mediaRecorder.callMethod<void>("setCamera", "(Landroid/hardware/Camera;)V", cam.object()); -} - -void AndroidMediaRecorder::setVideoEncoder(VideoEncoder encoder) -{ - m_mediaRecorder.callMethod<void>("setVideoEncoder", "(I)V", int(encoder)); -} - -void AndroidMediaRecorder::setVideoEncodingBitRate(int bitRate) -{ - m_mediaRecorder.callMethod<void>("setVideoEncodingBitRate", "(I)V", bitRate); -} - -void AndroidMediaRecorder::setVideoFrameRate(int rate) -{ - m_mediaRecorder.callMethod<void>("setVideoFrameRate", "(I)V", rate); -} - -void AndroidMediaRecorder::setVideoSize(const QSize &size) -{ - m_mediaRecorder.callMethod<void>("setVideoSize", "(II)V", size.width(), size.height()); -} - -void AndroidMediaRecorder::setVideoSource(VideoSource source) -{ - m_mediaRecorder.callMethod<void>("setVideoSource", "(I)V", int(source)); -} - -void AndroidMediaRecorder::setOrientationHint(int degrees) -{ - m_mediaRecorder.callMethod<void>("setOrientationHint", "(I)V", degrees); -} - -void AndroidMediaRecorder::setOutputFormat(OutputFormat format) -{ - QJniEnvironment env; - auto methodId = env->GetMethodID(m_mediaRecorder.objectClass(), "setOutputFormat", "(I)V"); - env->CallVoidMethod(m_mediaRecorder.object(), methodId, format); - // setAudioSource cannot be set after outputFormat is set. - if (!env.checkAndClearExceptions()) - m_isAudioSourceSet = true; -} - -void AndroidMediaRecorder::setOutputFile(const QString &path) -{ - m_mediaRecorder.callMethod<void>("setOutputFile", - "(Ljava/lang/String;)V", - QJniObject::fromString(path).object()); -} - -void AndroidMediaRecorder::setSurfaceTexture(AndroidSurfaceTexture *texture) -{ - m_mediaRecorder.callMethod<void>("setPreviewDisplay", - "(Landroid/view/Surface;)V", - texture->surface()); -} - -void AndroidMediaRecorder::setSurfaceHolder(AndroidSurfaceHolder *holder) -{ - QJniObject surfaceHolder(holder->surfaceHolder()); - QJniObject surface = surfaceHolder.callObjectMethod("getSurface", - "()Landroid/view/Surface;"); - if (!surface.isValid()) - return; - - m_mediaRecorder.callMethod<void>("setPreviewDisplay", - "(Landroid/view/Surface;)V", - surface.object()); -} - -bool AndroidMediaRecorder::registerNativeMethods() -{ - static const JNINativeMethod methods[] = { - {"notifyError", "(JII)V", (void *)notifyError}, - {"notifyInfo", "(JII)V", (void *)notifyInfo} - }; - - const int size = std::size(methods); - return QJniEnvironment().registerNativeMethods(QtMediaRecorderListenerClassName, methods, size); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/wrappers/jni/androidmediarecorder_p.h b/src/multimedia/platform/android/wrappers/jni/androidmediarecorder_p.h deleted file mode 100644 index b0f65dd1d..000000000 --- a/src/multimedia/platform/android/wrappers/jni/androidmediarecorder_p.h +++ /dev/null @@ -1,196 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef ANDROIDMEDIARECORDER_H -#define ANDROIDMEDIARECORDER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qobject.h> -#include <QtCore/qjniobject.h> -#include <qsize.h> - -QT_BEGIN_NAMESPACE - -class AndroidCamera; -class AndroidSurfaceTexture; -class AndroidSurfaceHolder; - -class AndroidCamcorderProfile -{ -public: - enum Quality { // Needs to match CamcorderProfile - QUALITY_LOW, - QUALITY_HIGH, - QUALITY_QCIF, - QUALITY_CIF, - QUALITY_480P, - QUALITY_720P, - QUALITY_1080P, - QUALITY_QVGA - }; - - enum Field { - audioBitRate, - audioChannels, - audioCodec, - audioSampleRate, - duration, - fileFormat, - quality, - videoBitRate, - videoCodec, - videoFrameHeight, - videoFrameRate, - videoFrameWidth - }; - - static bool hasProfile(jint cameraId, Quality quality); - static AndroidCamcorderProfile get(jint cameraId, Quality quality); - int getValue(Field field) const; - -private: - AndroidCamcorderProfile(const QJniObject &camcorderProfile); - QJniObject m_camcorderProfile; -}; - -class AndroidMediaRecorder : public QObject -{ - Q_OBJECT -public: - enum AudioEncoder { - DefaultAudioEncoder = 0, - AMR_NB_Encoder = 1, - AMR_WB_Encoder = 2, - AAC = 3, - OPUS = 7, - VORBIS = 6 - }; - - enum AudioSource { - DefaultAudioSource = 0, - Mic = 1, - VoiceUplink = 2, - VoiceDownlink = 3, - VoiceCall = 4, - Camcorder = 5, - VoiceRecognition = 6 - }; - - enum VideoEncoder { - DefaultVideoEncoder = 0, - H263 = 1, - H264 = 2, - MPEG_4_SP = 3, - HEVC = 5 - }; - - enum VideoSource { - DefaultVideoSource = 0, - Camera = 1 - }; - - enum OutputFormat { - DefaultOutputFormat = 0, - THREE_GPP = 1, - MPEG_4 = 2, - AMR_NB_Format = 3, - AMR_WB_Format = 4, - AAC_ADTS = 6, - OGG = 11, - WEBM = 9 - }; - - AndroidMediaRecorder(); - ~AndroidMediaRecorder(); - - void release(); - bool prepare(); - void reset(); - - bool start(); - void stop(); - - void setAudioChannels(int numChannels); - void setAudioEncoder(AudioEncoder encoder); - void setAudioEncodingBitRate(int bitRate); - void setAudioSamplingRate(int samplingRate); - void setAudioSource(AudioSource source); - bool isAudioSourceSet() const; - bool setAudioInput(const QByteArray &id); - - void setCamera(AndroidCamera *camera); - void setVideoEncoder(VideoEncoder encoder); - void setVideoEncodingBitRate(int bitRate); - void setVideoFrameRate(int rate); - void setVideoSize(const QSize &size); - void setVideoSource(VideoSource source); - - void setOrientationHint(int degrees); - - void setOutputFormat(OutputFormat format); - void setOutputFile(const QString &path); - - void setSurfaceTexture(AndroidSurfaceTexture *texture); - void setSurfaceHolder(AndroidSurfaceHolder *holder); - - static bool registerNativeMethods(); - -Q_SIGNALS: - void error(int what, int extra); - void info(int what, int extra); - -private: - jlong m_id; - QJniObject m_mediaRecorder; - bool m_isAudioSourceSet = false; -}; - -QT_END_NAMESPACE - -#endif // ANDROIDMEDIARECORDER_H diff --git a/src/multimedia/platform/android/wrappers/jni/androidmultimediautils.cpp b/src/multimedia/platform/android/wrappers/jni/androidmultimediautils.cpp deleted file mode 100644 index 1fe482f30..000000000 --- a/src/multimedia/platform/android/wrappers/jni/androidmultimediautils.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "androidmultimediautils_p.h" - -#include <QtCore/qjniobject.h> - -QT_BEGIN_NAMESPACE - - -void AndroidMultimediaUtils::enableOrientationListener(bool enable) -{ - QJniObject::callStaticMethod<void>("org/qtproject/qt/android/multimedia/QtMultimediaUtils", - "enableOrientationListener", - "(Z)V", - enable); -} - -int AndroidMultimediaUtils::getDeviceOrientation() -{ - return QJniObject::callStaticMethod<jint>("org/qtproject/qt/android/multimedia/QtMultimediaUtils", - "getDeviceOrientation"); -} - -QString AndroidMultimediaUtils::getDefaultMediaDirectory(MediaType type) -{ - QJniObject path = QJniObject::callStaticObjectMethod( - "org/qtproject/qt/android/multimedia/QtMultimediaUtils", - "getDefaultMediaDirectory", - "(I)Ljava/lang/String;", - jint(type)); - return path.toString(); -} - -void AndroidMultimediaUtils::registerMediaFile(const QString &file) -{ - QJniObject::callStaticMethod<void>("org/qtproject/qt/android/multimedia/QtMultimediaUtils", - "registerMediaFile", - "(Ljava/lang/String;)V", - QJniObject::fromString(file).object()); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/wrappers/jni/androidmultimediautils_p.h b/src/multimedia/platform/android/wrappers/jni/androidmultimediautils_p.h deleted file mode 100644 index 8a1fd7328..000000000 --- a/src/multimedia/platform/android/wrappers/jni/androidmultimediautils_p.h +++ /dev/null @@ -1,76 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef ANDROIDMULTIMEDIAUTILS_H -#define ANDROIDMULTIMEDIAUTILS_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qobject.h> - -QT_BEGIN_NAMESPACE - -class AndroidMultimediaUtils -{ -public: - enum MediaType { - Music = 0, - Movies = 1, - DCIM = 2, - Sounds = 3 - }; - - static void enableOrientationListener(bool enable); - static int getDeviceOrientation(); - static QString getDefaultMediaDirectory(MediaType type); - static void registerMediaFile(const QString &file); -}; - -QT_END_NAMESPACE - -#endif // ANDROIDMULTIMEDIAUTILS_H diff --git a/src/multimedia/platform/android/wrappers/jni/androidsurfacetexture.cpp b/src/multimedia/platform/android/wrappers/jni/androidsurfacetexture.cpp deleted file mode 100644 index d3a5b56ff..000000000 --- a/src/multimedia/platform/android/wrappers/jni/androidsurfacetexture.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "androidsurfacetexture_p.h" -#include <QtCore/qmutex.h> -#include <QtCore/qcoreapplication.h> - -QT_BEGIN_NAMESPACE - -static const char QtSurfaceTextureListenerClassName[] = "org/qtproject/qt/android/multimedia/QtSurfaceTextureListener"; -typedef QList<jlong> SurfaceTextures; -Q_GLOBAL_STATIC(SurfaceTextures, g_surfaceTextures); -Q_GLOBAL_STATIC(QMutex, g_textureMutex); - -// native method for QtSurfaceTexture.java -static void notifyFrameAvailable(JNIEnv* , jobject, jlong id) -{ - const QMutexLocker lock(g_textureMutex()); - const int idx = g_surfaceTextures->indexOf(id); - if (idx == -1) - return; - - AndroidSurfaceTexture *obj = reinterpret_cast<AndroidSurfaceTexture *>(g_surfaceTextures->at(idx)); - if (obj) - Q_EMIT obj->frameAvailable(); -} - -AndroidSurfaceTexture::AndroidSurfaceTexture(quint32 texName) - : QObject() -{ - Q_STATIC_ASSERT(sizeof (jlong) >= sizeof (void *)); - m_surfaceTexture = QJniObject("android/graphics/SurfaceTexture", "(I)V", jint(texName)); - - if (!m_surfaceTexture.isValid()) - return; - - const QMutexLocker lock(g_textureMutex()); - g_surfaceTextures->append(jlong(this)); - QJniObject listener(QtSurfaceTextureListenerClassName, "(J)V", jlong(this)); - setOnFrameAvailableListener(listener); -} - -AndroidSurfaceTexture::~AndroidSurfaceTexture() -{ - if (m_surface.isValid()) - m_surface.callMethod<void>("release"); - - if (m_surfaceTexture.isValid()) { - release(); - const QMutexLocker lock(g_textureMutex()); - const int idx = g_surfaceTextures->indexOf(jlong(this)); - if (idx != -1) - g_surfaceTextures->remove(idx); - } -} - -QMatrix4x4 AndroidSurfaceTexture::getTransformMatrix() -{ - QMatrix4x4 matrix; - if (!m_surfaceTexture.isValid()) - return matrix; - - QJniEnvironment env; - jfloatArray array = env->NewFloatArray(16); - m_surfaceTexture.callMethod<void>("getTransformMatrix", "([F)V", array); - env->GetFloatArrayRegion(array, 0, 16, matrix.data()); - env->DeleteLocalRef(array); - - return matrix; -} - -void AndroidSurfaceTexture::release() -{ - m_surfaceTexture.callMethod<void>("release"); -} - -void AndroidSurfaceTexture::updateTexImage() -{ - if (!m_surfaceTexture.isValid()) - return; - - m_surfaceTexture.callMethod<void>("updateTexImage"); -} - -jobject AndroidSurfaceTexture::surfaceTexture() -{ - return m_surfaceTexture.object(); -} - -jobject AndroidSurfaceTexture::surface() -{ - if (!m_surface.isValid()) { - m_surface = QJniObject("android/view/Surface", - "(Landroid/graphics/SurfaceTexture;)V", - m_surfaceTexture.object()); - } - - return m_surface.object(); -} - -jobject AndroidSurfaceTexture::surfaceHolder() -{ - if (!m_surfaceHolder.isValid()) { - m_surfaceHolder = QJniObject("org/qtproject/qt/android/multimedia/QtSurfaceTextureHolder", - "(Landroid/view/Surface;)V", - surface()); - } - - return m_surfaceHolder.object(); -} - -void AndroidSurfaceTexture::attachToGLContext(quint32 texName) -{ - if (!m_surfaceTexture.isValid()) - return; - - m_surfaceTexture.callMethod<void>("attachToGLContext", "(I)V", texName); -} - -void AndroidSurfaceTexture::detachFromGLContext() -{ - if (!m_surfaceTexture.isValid()) - return; - - m_surfaceTexture.callMethod<void>("detachFromGLContext"); -} - -bool AndroidSurfaceTexture::registerNativeMethods() -{ - static const JNINativeMethod methods[] = { - {"notifyFrameAvailable", "(J)V", (void *)notifyFrameAvailable} - }; - const int size = std::size(methods); - if (QJniEnvironment().registerNativeMethods(QtSurfaceTextureListenerClassName, methods, size)) - return false; - - return true; -} - -void AndroidSurfaceTexture::setOnFrameAvailableListener(const QJniObject &listener) -{ - m_surfaceTexture.callMethod<void>("setOnFrameAvailableListener", - "(Landroid/graphics/SurfaceTexture$OnFrameAvailableListener;)V", - listener.object()); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/wrappers/jni/androidsurfacetexture_p.h b/src/multimedia/platform/android/wrappers/jni/androidsurfacetexture_p.h deleted file mode 100644 index d31df972b..000000000 --- a/src/multimedia/platform/android/wrappers/jni/androidsurfacetexture_p.h +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef ANDROIDSURFACETEXTURE_H -#define ANDROIDSURFACETEXTURE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qobject.h> -#include <QtCore/qjniobject.h> - -#include <QMatrix4x4> - -QT_BEGIN_NAMESPACE - -class AndroidSurfaceTexture : public QObject -{ - Q_OBJECT -public: - explicit AndroidSurfaceTexture(quint32 texName); - ~AndroidSurfaceTexture(); - - jobject surfaceTexture(); - jobject surface(); - jobject surfaceHolder(); - inline bool isValid() const { return m_surfaceTexture.isValid(); } - - QMatrix4x4 getTransformMatrix(); - void release(); // API level 14 - void updateTexImage(); - - void attachToGLContext(quint32 texName); // API level 16 - void detachFromGLContext(); // API level 16 - - static bool registerNativeMethods(); - -Q_SIGNALS: - void frameAvailable(); - -private: - void setOnFrameAvailableListener(const QJniObject &listener); - - QJniObject m_surfaceTexture; - QJniObject m_surface; - QJniObject m_surfaceHolder; -}; - -QT_END_NAMESPACE - -#endif // ANDROIDSURFACETEXTURE_H diff --git a/src/multimedia/platform/android/wrappers/jni/androidsurfaceview.cpp b/src/multimedia/platform/android/wrappers/jni/androidsurfaceview.cpp deleted file mode 100644 index 33d15a91b..000000000 --- a/src/multimedia/platform/android/wrappers/jni/androidsurfaceview.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "androidsurfaceview_p.h" - -#include <QtCore/qcoreapplication.h> -#include <QtCore/qdebug.h> -#include <QtCore/qlist.h> -#include <QtCore/qmutex.h> -#include <QtGui/qwindow.h> - -QT_BEGIN_NAMESPACE - -static const char QtSurfaceHolderCallbackClassName[] = "org/qtproject/qt/android/multimedia/QtSurfaceHolderCallback"; -typedef QList<AndroidSurfaceHolder *> SurfaceHolders; -Q_GLOBAL_STATIC(SurfaceHolders, surfaceHolders) -Q_GLOBAL_STATIC(QMutex, shLock) - -AndroidSurfaceHolder::AndroidSurfaceHolder(QJniObject object) - : m_surfaceHolder(object) - , m_surfaceCreated(false) -{ - if (!m_surfaceHolder.isValid()) - return; - - { - QMutexLocker locker(shLock()); - surfaceHolders->append(this); - } - - QJniObject callback(QtSurfaceHolderCallbackClassName, "(J)V", reinterpret_cast<jlong>(this)); - m_surfaceHolder.callMethod<void>("addCallback", - "(Landroid/view/SurfaceHolder$Callback;)V", - callback.object()); -} - -AndroidSurfaceHolder::~AndroidSurfaceHolder() -{ - QMutexLocker locker(shLock()); - const int i = surfaceHolders->indexOf(this); - if (Q_UNLIKELY(i == -1)) - return; - - surfaceHolders->remove(i); -} - -jobject AndroidSurfaceHolder::surfaceHolder() const -{ - return m_surfaceHolder.object(); -} - -bool AndroidSurfaceHolder::isSurfaceCreated() const -{ - QMutexLocker locker(shLock()); - return m_surfaceCreated; -} - -void AndroidSurfaceHolder::handleSurfaceCreated(JNIEnv*, jobject, jlong id) -{ - QMutexLocker locker(shLock()); - const int i = surfaceHolders->indexOf(reinterpret_cast<AndroidSurfaceHolder *>(id)); - if (Q_UNLIKELY(i == -1)) - return; - - (*surfaceHolders)[i]->m_surfaceCreated = true; - Q_EMIT (*surfaceHolders)[i]->surfaceCreated(); -} - -void AndroidSurfaceHolder::handleSurfaceDestroyed(JNIEnv*, jobject, jlong id) -{ - QMutexLocker locker(shLock()); - const int i = surfaceHolders->indexOf(reinterpret_cast<AndroidSurfaceHolder *>(id)); - if (Q_UNLIKELY(i == -1)) - return; - - (*surfaceHolders)[i]->m_surfaceCreated = false; -} - -bool AndroidSurfaceHolder::registerNativeMethods() -{ - static const JNINativeMethod methods[] = { - {"notifySurfaceCreated", "(J)V", (void *)AndroidSurfaceHolder::handleSurfaceCreated}, - {"notifySurfaceDestroyed", "(J)V", (void *)AndroidSurfaceHolder::handleSurfaceDestroyed} - }; - - const int size = std::size(methods); - return QJniEnvironment().registerNativeMethods(QtSurfaceHolderCallbackClassName, methods, size); -} - -AndroidSurfaceView::AndroidSurfaceView() - : m_window(0) - , m_surfaceHolder(0) - , m_pendingVisible(-1) -{ - QNativeInterface::QAndroidApplication::runOnAndroidMainThread([this] { - m_surfaceView = QJniObject("android/view/SurfaceView", - "(Landroid/content/Context;)V", - QNativeInterface::QAndroidApplication::context()); - }).waitForFinished(); - - Q_ASSERT(m_surfaceView.isValid()); - - QJniObject holder = m_surfaceView.callObjectMethod("getHolder", - "()Landroid/view/SurfaceHolder;"); - if (!holder.isValid()) { - m_surfaceView = QJniObject(); - } else { - m_surfaceHolder = new AndroidSurfaceHolder(holder); - connect(m_surfaceHolder, &AndroidSurfaceHolder::surfaceCreated, - this, &AndroidSurfaceView::surfaceCreated); - { // Lock now to avoid a race with handleSurfaceCreated() - QMutexLocker locker(shLock()); - m_window = QWindow::fromWinId(WId(m_surfaceView.object())); - - if (m_pendingVisible != -1) - m_window->setVisible(m_pendingVisible); - if (m_pendingGeometry.isValid()) - m_window->setGeometry(m_pendingGeometry); - } - } -} - -AndroidSurfaceView::~AndroidSurfaceView() -{ - delete m_surfaceHolder; - delete m_window; -} - -AndroidSurfaceHolder *AndroidSurfaceView::holder() const -{ - return m_surfaceHolder; -} - -void AndroidSurfaceView::setVisible(bool v) -{ - if (m_window) - m_window->setVisible(v); - else - m_pendingVisible = int(v); -} - -void AndroidSurfaceView::setGeometry(int x, int y, int width, int height) -{ - if (m_window) - m_window->setGeometry(x, y, width, height); - else - m_pendingGeometry = QRect(x, y, width, height); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/android/wrappers/jni/androidsurfaceview_p.h b/src/multimedia/platform/android/wrappers/jni/androidsurfaceview_p.h deleted file mode 100644 index cd0badc34..000000000 --- a/src/multimedia/platform/android/wrappers/jni/androidsurfaceview_p.h +++ /dev/null @@ -1,113 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef ANDROIDSURFACEVIEW_H -#define ANDROIDSURFACEVIEW_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qjniobject.h> -#include <qrect.h> -#include <QtCore/qrunnable.h> - -QT_BEGIN_NAMESPACE - -class QWindow; - -class AndroidSurfaceHolder : public QObject -{ - Q_OBJECT -public: - ~AndroidSurfaceHolder(); - - jobject surfaceHolder() const; - bool isSurfaceCreated() const; - - static bool registerNativeMethods(); - -Q_SIGNALS: - void surfaceCreated(); - -private: - AndroidSurfaceHolder(QJniObject object); - - static void handleSurfaceCreated(JNIEnv*, jobject, jlong id); - static void handleSurfaceDestroyed(JNIEnv*, jobject, jlong id); - - QJniObject m_surfaceHolder; - bool m_surfaceCreated; - - friend class AndroidSurfaceView; -}; - -class AndroidSurfaceView : public QObject -{ - Q_OBJECT -public: - AndroidSurfaceView(); - ~AndroidSurfaceView(); - - AndroidSurfaceHolder *holder() const; - - void setVisible(bool v); - void setGeometry(int x, int y, int width, int height); - -Q_SIGNALS: - void surfaceCreated(); - -private: - QJniObject m_surfaceView; - QWindow *m_window; - AndroidSurfaceHolder *m_surfaceHolder; - int m_pendingVisible; - QRect m_pendingGeometry; -}; - -QT_END_NAMESPACE - -#endif // ANDROIDSURFACEVIEW_H diff --git a/src/multimedia/platform/darwin/audio/avfaudiodecoder.mm b/src/multimedia/platform/darwin/audio/avfaudiodecoder.mm deleted file mode 100644 index 079c4b39e..000000000 --- a/src/multimedia/platform/darwin/audio/avfaudiodecoder.mm +++ /dev/null @@ -1,529 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "avfaudiodecoder_p.h" - -#include <QtCore/qmutex.h> -#include <QtCore/qiodevice.h> -#include <QMimeDatabase> -#include "qcoreaudioutils_p.h" - -#include <AVFoundation/AVFoundation.h> - -#define MAX_BUFFERS_IN_QUEUE 10 - -QT_USE_NAMESPACE - -@interface AVFResourceReaderDelegate : NSObject <AVAssetResourceLoaderDelegate> -{ - AVFAudioDecoder *m_decoder; - QMutex m_mutex; -} - --(void)handleNextSampleBuffer:(CMSampleBufferRef)sampleBuffer; - --(BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader - shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest; - -@end - -@implementation AVFResourceReaderDelegate - --(id)initWithDecoder: (AVFAudioDecoder *)decoder { - if (!(self = [super init])) - return nil; - - m_decoder = decoder; - - return self; -} - --(void)dealloc { - m_decoder = nil; - [super dealloc]; -} - --(void)handleNextSampleBuffer:(CMSampleBufferRef)sampleBuffer -{ - if (!sampleBuffer) - return; - - // Check format - CMFormatDescriptionRef formatDescription = CMSampleBufferGetFormatDescription(sampleBuffer); - if (!formatDescription) - return; - const AudioStreamBasicDescription* const asbd = CMAudioFormatDescriptionGetStreamBasicDescription(formatDescription); - QAudioFormat qtFormat = CoreAudioUtils::toQAudioFormat(*asbd); - if (qtFormat.sampleFormat() == QAudioFormat::Unknown && asbd->mBitsPerChannel == 8) - qtFormat.setSampleFormat(QAudioFormat::UInt8); - if (!qtFormat.isValid()) - return; - - // Get the required size to allocate to audioBufferList - size_t audioBufferListSize = 0; - OSStatus err = CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampleBuffer, - &audioBufferListSize, - NULL, - 0, - NULL, - NULL, - kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, - NULL); - if (err != noErr) - return; - - CMBlockBufferRef blockBuffer = NULL; - AudioBufferList* audioBufferList = (AudioBufferList*) malloc(audioBufferListSize); - // This ensures the buffers placed in audioBufferList are contiguous - err = CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampleBuffer, - NULL, - audioBufferList, - audioBufferListSize, - NULL, - NULL, - kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, - &blockBuffer); - if (err != noErr) { - free(audioBufferList); - return; - } - - QByteArray abuf; - for (UInt32 i = 0; i < audioBufferList->mNumberBuffers; i++) - { - AudioBuffer audioBuffer = audioBufferList->mBuffers[i]; - abuf.push_back(QByteArray((const char*)audioBuffer.mData, audioBuffer.mDataByteSize)); - } - - free(audioBufferList); - CFRelease(blockBuffer); - - CMTime sampleStartTime = (CMSampleBufferGetPresentationTimeStamp(sampleBuffer)); - float sampleStartTimeSecs = CMTimeGetSeconds(sampleStartTime); - - QAudioBuffer audioBuffer; - audioBuffer = QAudioBuffer(abuf, qtFormat, qint64(sampleStartTimeSecs * 1000000)); - if (!audioBuffer.isValid()) - return; - - emit m_decoder->newAudioBuffer(audioBuffer); -} - --(BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader - shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest -{ - Q_UNUSED(resourceLoader); - - if (![loadingRequest.request.URL.scheme isEqualToString:@"iodevice"]) - return NO; - - QMutexLocker locker(&m_mutex); - - QIODevice *device = m_decoder->sourceDevice(); - if (!device) - return NO; - - device->seek(loadingRequest.dataRequest.requestedOffset); - if (loadingRequest.contentInformationRequest) { - loadingRequest.contentInformationRequest.contentLength = device->size(); - loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES; - } - - if (loadingRequest.dataRequest) { - NSInteger requestedLength = loadingRequest.dataRequest.requestedLength; - int maxBytes = qMin(32 * 1024, int(requestedLength)); - char buffer[maxBytes]; - NSInteger submitted = 0; - while (submitted < requestedLength) { - qint64 len = device->read(buffer, maxBytes); - if (len < 1) - break; - - [loadingRequest.dataRequest respondWithData:[NSData dataWithBytes:buffer length:len]]; - submitted += len; - } - - // Finish loading even if not all bytes submitted. - [loadingRequest finishLoading]; - } - - return YES; -} - -@end - -namespace { - -NSDictionary *av_audio_settings_for_format(const QAudioFormat &format) -{ - float sampleRate = format.sampleRate(); - int nChannels = format.channelCount(); - int sampleSize = format.bytesPerSample() * 8; - BOOL isFloat = format.sampleFormat() == QAudioFormat::Float; - - NSDictionary *audioSettings = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithInt:kAudioFormatLinearPCM], AVFormatIDKey, - [NSNumber numberWithFloat:sampleRate], AVSampleRateKey, - [NSNumber numberWithInt:nChannels], AVNumberOfChannelsKey, - [NSNumber numberWithInt:sampleSize], AVLinearPCMBitDepthKey, - [NSNumber numberWithBool:isFloat], AVLinearPCMIsFloatKey, - [NSNumber numberWithBool:NO], AVLinearPCMIsNonInterleaved, - [NSNumber numberWithBool:NO], AVLinearPCMIsBigEndianKey, - nil]; - - return audioSettings; -} - -QAudioFormat qt_format_for_audio_track(AVAssetTrack *track) -{ - QAudioFormat format; - CMFormatDescriptionRef desc = (__bridge CMFormatDescriptionRef)track.formatDescriptions[0]; - const AudioStreamBasicDescription* const asbd = - CMAudioFormatDescriptionGetStreamBasicDescription(desc); - format = CoreAudioUtils::toQAudioFormat(*asbd); - // AudioStreamBasicDescription's mBitsPerChannel is 0 for compressed formats - // In this case set default Int16 sample format - if (asbd->mBitsPerChannel == 0) - format.setSampleFormat(QAudioFormat::Int16); - return format; -} - -} - -AVFAudioDecoder::AVFAudioDecoder(QAudioDecoder *parent) - : QPlatformAudioDecoder(parent) -{ - m_readingQueue = dispatch_queue_create("reader_queue", DISPATCH_QUEUE_SERIAL); - m_decodingQueue = dispatch_queue_create("decoder_queue", DISPATCH_QUEUE_SERIAL); - - m_readerDelegate = [[AVFResourceReaderDelegate alloc] initWithDecoder:this]; - - connect(this, &AVFAudioDecoder::readyToRead, this, &AVFAudioDecoder::startReading); - connect(this, &AVFAudioDecoder::newAudioBuffer, this, &AVFAudioDecoder::handleNewAudioBuffer); -} - -AVFAudioDecoder::~AVFAudioDecoder() -{ - stop(); - - [m_readerOutput release]; - m_readerOutput = nil; - - [m_reader release]; - m_reader = nil; - - [m_readerDelegate release]; - m_readerDelegate = nil; - - [m_asset release]; - m_asset = nil; - - if (m_readingQueue) - dispatch_release(m_readingQueue); - if (m_decodingQueue) - dispatch_release(m_decodingQueue); -} - -QUrl AVFAudioDecoder::source() const -{ - return m_source; -} - -void AVFAudioDecoder::setSource(const QUrl &fileName) -{ - if (!m_device && m_source == fileName) - return; - - stop(); - m_device = nullptr; - [m_asset release]; - m_asset = nil; - - m_source = fileName; - - if (!m_source.isEmpty()) { - NSURL *nsURL = m_source.toNSURL(); - m_asset = [[AVURLAsset alloc] initWithURL:nsURL options:nil]; - } - - emit sourceChanged(); -} - -QIODevice *AVFAudioDecoder::sourceDevice() const -{ - return m_device; -} - -void AVFAudioDecoder::setSourceDevice(QIODevice *device) -{ - if (m_device == device && m_source.isEmpty()) - return; - - stop(); - m_source.clear(); - [m_asset release]; - m_asset = nil; - - m_device = device; - - if (m_device) { - const QString ext = QMimeDatabase().mimeTypeForData(m_device).preferredSuffix(); - const QString url = "iodevice:///iodevice." + ext; - NSString *urlString = url.toNSString(); - NSURL *nsURL = [NSURL URLWithString:urlString]; - - m_asset = [[AVURLAsset alloc] initWithURL:nsURL options:nil]; - [m_asset.resourceLoader setDelegate:m_readerDelegate queue:m_readingQueue]; - - m_loadingSource = true; - } - - emit sourceChanged(); -} - -void AVFAudioDecoder::start() -{ - Q_ASSERT(!m_buffersAvailable); - if (isDecoding()) - return; - - if (m_position != -1) { - m_position = -1; - emit positionChanged(-1); - } - - if (m_device && (!m_device->isOpen() || !m_device->isReadable())) { - processInvalidMedia(QAudioDecoder::AccessDeniedError, tr("Unable to read from specified device")); - return; - } - - [m_asset loadValuesAsynchronouslyForKeys:@[@"tracks"] completionHandler: - ^{ - dispatch_async(m_readingQueue, - ^{ - NSError *error = nil; - AVKeyValueStatus status = [m_asset statusOfValueForKey:@"tracks" error:&error]; - if (status != AVKeyValueStatusLoaded) { - if (status == AVKeyValueStatusFailed) { - if (error.domain == NSURLErrorDomain) - processInvalidMedia(QAudioDecoder::ResourceError, QString::fromNSString(error.localizedDescription)); - else - processInvalidMedia(QAudioDecoder::FormatError, tr("Could not load media source's tracks")); - } - return; - } - initAssetReader(); - }); - } - ]; - - if (m_device && m_loadingSource) { - setIsDecoding(true); - return; - } -} - -void AVFAudioDecoder::stop() -{ - m_cachedBuffers.clear(); - - if (m_reader) - [m_reader cancelReading]; - - 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 AVFAudioDecoder::audioFormat() const -{ - return m_format; -} - -void AVFAudioDecoder::setAudioFormat(const QAudioFormat &format) -{ - if (m_format != format) { - m_format = format; - emit formatChanged(m_format); - } -} - -QAudioBuffer AVFAudioDecoder::read() -{ - if (!m_buffersAvailable) - return QAudioBuffer(); - - Q_ASSERT(m_cachedBuffers.size() > 0); - QAudioBuffer buffer = m_cachedBuffers.takeFirst(); - - m_position = qint64(buffer.startTime() / 1000); - emit positionChanged(m_position); - - m_buffersAvailable--; - if (!m_buffersAvailable) - emit bufferAvailableChanged(false); - return buffer; -} - -bool AVFAudioDecoder::bufferAvailable() const -{ - return m_buffersAvailable > 0; -} - -qint64 AVFAudioDecoder::position() const -{ - return m_position; -} - -qint64 AVFAudioDecoder::duration() const -{ - return m_duration; -} - -void AVFAudioDecoder::processInvalidMedia(QAudioDecoder::Error errorCode, const QString& errorString) -{ - stop(); - emit error(int(errorCode), errorString); -} - -void AVFAudioDecoder::initAssetReader() -{ - if (!m_asset) - return; - - NSArray<AVAssetTrack *> *tracks = [m_asset tracksWithMediaType:AVMediaTypeAudio]; - if (!tracks.count) { - processInvalidMedia(QAudioDecoder::FormatError, tr("No audio tracks found")); - return; - } - AVAssetTrack *track = [tracks objectAtIndex:0]; - - // Set format - QAudioFormat format; - if (m_format.isValid()) { - format = m_format; - } else { - format = qt_format_for_audio_track(track); - if (!format.isValid()) - { - processInvalidMedia(QAudioDecoder::FormatError, tr("Unsupported source format")); - return; - } - } - - // Set duration - qint64 duration = CMTimeGetSeconds(track.timeRange.duration) * 1000; - if (m_duration != duration) { - m_duration = duration; - emit durationChanged(m_duration); - } - - // Initialize asset reader and output - [m_reader release]; - m_reader = nil; - [m_readerOutput release]; - m_readerOutput = nil; - - NSError *error = nil; - NSDictionary *audioSettings = av_audio_settings_for_format(format); - m_readerOutput = [[AVAssetReaderTrackOutput alloc] initWithTrack:track outputSettings:audioSettings]; - m_reader = [[AVAssetReader alloc] initWithAsset:m_asset error:&error]; - if (error) { - processInvalidMedia(QAudioDecoder::ResourceError, QString::fromNSString(error.localizedDescription)); - return; - } - if (![m_reader canAddOutput:m_readerOutput]) { - processInvalidMedia(QAudioDecoder::ResourceError, tr("Failed to add asset reader output")); - return; - } - [m_reader addOutput:m_readerOutput]; - - emit readyToRead(); -} - -void AVFAudioDecoder::startReading() -{ - m_loadingSource = false; - - // Prepares the receiver for obtaining sample buffers from the asset. - if (!m_reader || ![m_reader startReading]) { - processInvalidMedia(QAudioDecoder::ResourceError, tr("Could not start reading")); - return; - } - - setIsDecoding(true); - - // Since copyNextSampleBuffer is synchronous, submit it to an async dispatch queue - // to run in a separate thread. Call the handleNextSampleBuffer "callback" on another - // thread when new audio sample is read. - dispatch_async(m_readingQueue, ^{ - CMSampleBufferRef sampleBuffer; - while ((sampleBuffer = [m_readerOutput copyNextSampleBuffer])) { - dispatch_async(m_decodingQueue, ^{ - if (CMSampleBufferDataIsReady(sampleBuffer)) - [m_readerDelegate handleNextSampleBuffer:sampleBuffer]; - CFRelease(sampleBuffer); - }); - } - if (m_reader.status == AVAssetReaderStatusCompleted) - emit finished(); - }); -} - -void AVFAudioDecoder::handleNewAudioBuffer(QAudioBuffer buffer) -{ - Q_ASSERT(m_cachedBuffers.size() <= MAX_BUFFERS_IN_QUEUE); - m_cachedBuffers.push_back(buffer); - - m_buffersAvailable++; - Q_ASSERT(m_buffersAvailable <= MAX_BUFFERS_IN_QUEUE); - - emit bufferAvailableChanged(true); - emit bufferReady(); -} diff --git a/src/multimedia/platform/darwin/audio/avfaudiodecoder_p.h b/src/multimedia/platform/darwin/audio/avfaudiodecoder_p.h deleted file mode 100644 index 73a8cbd38..000000000 --- a/src/multimedia/platform/darwin/audio/avfaudiodecoder_p.h +++ /dev/null @@ -1,130 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef AVFAUDIODECODER_H -#define AVFAUDIODECODER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtMultimedia/private/qtmultimediaglobal_p.h> -#include <QObject> -#include <QtCore/qurl.h> - -#include "private/qplatformaudiodecoder_p.h" -#include "qaudiodecoder.h" - -#include <dispatch/dispatch.h> - -Q_FORWARD_DECLARE_OBJC_CLASS(AVURLAsset); -Q_FORWARD_DECLARE_OBJC_CLASS(AVAssetReader); -Q_FORWARD_DECLARE_OBJC_CLASS(AVAssetReaderTrackOutput); -Q_FORWARD_DECLARE_OBJC_CLASS(AVFResourceReaderDelegate); - -QT_BEGIN_NAMESPACE - -class AVFAudioDecoder : public QPlatformAudioDecoder -{ - Q_OBJECT - -public: - AVFAudioDecoder(QAudioDecoder *parent); - virtual ~AVFAudioDecoder(); - - QUrl source() const override; - void setSource(const QUrl &fileName) override; - - QIODevice *sourceDevice() const override; - void setSourceDevice(QIODevice *device) override; - - void start() override; - void stop() override; - - QAudioFormat audioFormat() const override; - void setAudioFormat(const QAudioFormat &format) override; - - QAudioBuffer read() override; - bool bufferAvailable() const override; - - qint64 position() const override; - qint64 duration() const override; - -private slots: - void handleNewAudioBuffer(QAudioBuffer); - void startReading(); - -signals: - void newAudioBuffer(QAudioBuffer); - void readyToRead(); - -private: - void processInvalidMedia(QAudioDecoder::Error errorCode, const QString& errorString); - void initAssetReader(); - - QUrl m_source; - QIODevice *m_device = nullptr; - QAudioFormat m_format; - - int m_buffersAvailable = 0; - QList<QAudioBuffer> m_cachedBuffers; - - qint64 m_position = -1; - qint64 m_duration = -1; - - bool m_loadingSource = false; - - AVURLAsset *m_asset = nullptr; - AVAssetReader *m_reader = nullptr; - AVAssetReaderTrackOutput *m_readerOutput = nullptr; - AVFResourceReaderDelegate *m_readerDelegate = nullptr; - dispatch_queue_t m_readingQueue; - dispatch_queue_t m_decodingQueue; -}; - -QT_END_NAMESPACE - -#endif // AVFAUDIODECODER_H diff --git a/src/multimedia/platform/darwin/audio/qcoreaudiosessionmanager.mm b/src/multimedia/platform/darwin/audio/qcoreaudiosessionmanager.mm deleted file mode 100644 index 264935d74..000000000 --- a/src/multimedia/platform/darwin/audio/qcoreaudiosessionmanager.mm +++ /dev/null @@ -1,473 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). -** 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$ -** -****************************************************************************/ - -#include "qcoreaudiosessionmanager_p.h" -#import <AVFoundation/AVAudioSession.h> -#import <Foundation/Foundation.h> - -QT_BEGIN_NAMESPACE - -@interface CoreAudioSessionObserver : NSObject -{ - CoreAudioSessionManager *m_sessionManager; - AVAudioSession *m_audioSession; -} - -@property (readonly, getter=sessionManager) CoreAudioSessionManager *m_sessionManager; -@property (readonly, getter=audioSession) AVAudioSession *m_audioSession; - --(CoreAudioSessionObserver *)initWithAudioSessionManager:(CoreAudioSessionManager *)sessionManager; - --(BOOL)activateAudio; --(BOOL)deactivateAudio; - -//Notification handlers --(void)audioSessionInterruption:(NSNotification *)notification; --(void)audioSessionRouteChange:(NSNotification *)notification; --(void)audioSessionMediaServicesWereReset:(NSNotification *)notification; - -@end //interface CoreAudioSessionObserver - -@implementation CoreAudioSessionObserver - -@synthesize m_sessionManager, m_audioSession; - --(CoreAudioSessionObserver *)initWithAudioSessionManager:(CoreAudioSessionManager *)sessionManager -{ - if (!(self = [super init])) - return nil; - - self->m_sessionManager = sessionManager; - self->m_audioSession = [AVAudioSession sharedInstance]; - - //Set up observers - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(audioSessionInterruption:) - name:AVAudioSessionInterruptionNotification - object:self->m_audioSession]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(audioSessionMediaServicesWereReset:) - name:AVAudioSessionMediaServicesWereResetNotification - object:self->m_audioSession]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(audioSessionRouteChange:) - name:AVAudioSessionRouteChangeNotification - object:self->m_audioSession]; - - return self; -} - --(void)dealloc -{ -#ifdef QT_DEBUG_COREAUDIO - qDebug() << Q_FUNC_INFO; -#endif - - [[NSNotificationCenter defaultCenter] removeObserver:self - name:AVAudioSessionInterruptionNotification - object:self->m_audioSession]; - [[NSNotificationCenter defaultCenter] removeObserver:self - name:AVAudioSessionMediaServicesWereResetNotification - object:self->m_audioSession]; - [[NSNotificationCenter defaultCenter] removeObserver:self - name:AVAudioSessionRouteChangeNotification - object:self->m_audioSession]; - - [super dealloc]; -} - --(BOOL)activateAudio -{ - NSError *error = nil; - BOOL success = [self->m_audioSession setActive:YES error:&error]; - if (![self->m_audioSession setActive:YES error:&error]) { -#ifdef QT_DEBUG_COREAUDIO - qDebug("audio session activation failed: %s", [[error localizedDescription] UTF8String]); - } else { - qDebug("audio session activated"); -#endif - } - - return success; -} - --(BOOL)deactivateAudio -{ - NSError *error = nil; - BOOL success = [m_audioSession setActive:NO error:&error]; -#ifdef QT_DEBUG_COREAUDIO - if (!success) { - qDebug("%s", [[error localizedDescription] UTF8String]); - } -#endif - return success; -} - --(void)audioSessionInterruption:(NSNotification *)notification -{ - NSNumber *type = [[notification userInfo] valueForKey:AVAudioSessionInterruptionTypeKey]; - if ([type intValue] == AVAudioSessionInterruptionTypeBegan) { -#ifdef QT_DEBUG_COREAUDIO - qDebug("audioSession Interuption begain"); -#endif - } else if ([type intValue] == AVAudioSessionInterruptionTypeEnded) { -#ifdef QT_DEBUG_COREAUDIO - qDebug("audioSession Interuption ended"); -#endif - NSNumber *option = [[notification userInfo] valueForKey:AVAudioSessionInterruptionOptionKey]; - if ([option intValue] == AVAudioSessionInterruptionOptionShouldResume) { -#ifdef QT_DEBUG_COREAUDIO - qDebug("audioSession is active and immediately ready to be used."); -#endif - } else { - [self activateAudio]; - } - } -} - --(void)audioSessionMediaServicesWereReset:(NSNotification *)notification -{ - Q_UNUSED(notification); -#ifdef QT_DEBUG_COREAUDIO - qDebug("audioSession Media Services were reset"); -#endif - //Reactivate audio when this occurs - [self activateAudio]; -} - --(void)audioSessionRouteChange:(NSNotification *)notification -{ - NSNumber *reason = [[notification userInfo] valueForKey:AVAudioSessionRouteChangeReasonKey]; - NSUInteger reasonEnum = [reason intValue]; - - if (reasonEnum == AVAudioSessionRouteChangeReasonUnknown) { -#ifdef QT_DEBUG_COREAUDIO - qDebug("audioSession route changed. reason: unknown"); -#endif - } else if (reasonEnum == AVAudioSessionRouteChangeReasonNewDeviceAvailable) { -#ifdef QT_DEBUG_COREAUDIO - qDebug("audioSession route changed. reason: new device available"); -#endif - } else if (reasonEnum == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) { -#ifdef QT_DEBUG_COREAUDIO - qDebug("audioSession route changed. reason: old device unavailable"); -#endif - } else if (reasonEnum == AVAudioSessionRouteChangeReasonCategoryChange) { -#ifdef QT_DEBUG_COREAUDIO - qDebug("audioSession route changed. reason: category changed"); -#endif - } else if (reasonEnum == AVAudioSessionRouteChangeReasonOverride) { -#ifdef QT_DEBUG_COREAUDIO - qDebug("audioSession route changed. reason: override"); -#endif - } else if (reasonEnum == AVAudioSessionRouteChangeReasonWakeFromSleep) { -#ifdef QT_DEBUG_COREAUDIO - qDebug("audioSession route changed. reason: woken from sleep"); -#endif - } else if (reasonEnum == AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory) { -#ifdef QT_DEBUG_COREAUDIO - qDebug("audioSession route changed. reason: no suitable route for category"); -#endif - } - - m_sessionManager->devicesAvailableChanged(); -} - -@end //implementation CoreAudioSessionObserver - -CoreAudioSessionManager::CoreAudioSessionManager() : - QObject(0) -{ - m_sessionObserver = [[CoreAudioSessionObserver alloc] initWithAudioSessionManager:this]; -} - -CoreAudioSessionManager::~CoreAudioSessionManager() -{ -#ifdef QT_DEBUG_COREAUDIO - qDebug() << Q_FUNC_INFO; -#endif - [m_sessionObserver release]; -} - - -CoreAudioSessionManager &CoreAudioSessionManager::instance() -{ - static CoreAudioSessionManager instance; - return instance; -} - -bool CoreAudioSessionManager::setActive(bool active) -{ - if (active) { - return [m_sessionObserver activateAudio]; - } else { - return [m_sessionObserver deactivateAudio]; - } -} - -bool CoreAudioSessionManager::setCategory(CoreAudioSessionManager::AudioSessionCategorys category, CoreAudioSessionManager::AudioSessionCategoryOptions options) -{ - NSString *targetCategory = nil; - - switch (category) { - case CoreAudioSessionManager::Ambient: - targetCategory = AVAudioSessionCategoryAmbient; - break; - case CoreAudioSessionManager::SoloAmbient: - targetCategory = AVAudioSessionCategorySoloAmbient; - break; - case CoreAudioSessionManager::Playback: - targetCategory = AVAudioSessionCategoryPlayback; - break; - case CoreAudioSessionManager::Record: - targetCategory = AVAudioSessionCategoryRecord; - break; - case CoreAudioSessionManager::PlayAndRecord: - targetCategory = AVAudioSessionCategoryPlayAndRecord; - break; - case CoreAudioSessionManager::AudioProcessing: -#ifndef Q_OS_TVOS - targetCategory = AVAudioSessionCategoryAudioProcessing; -#endif - break; - case CoreAudioSessionManager::MultiRoute: - targetCategory = AVAudioSessionCategoryMultiRoute; - break; - } - - if (targetCategory == nil) - return false; - - return [[m_sessionObserver audioSession] setCategory:targetCategory - withOptions:(AVAudioSessionCategoryOptions)options - error:nil]; -} - -bool CoreAudioSessionManager::setMode(CoreAudioSessionManager::AudioSessionModes mode) -{ - NSString *targetMode = nil; - switch (mode) { - case CoreAudioSessionManager::Default: - targetMode = AVAudioSessionModeDefault; - break; - case CoreAudioSessionManager::VoiceChat: - targetMode = AVAudioSessionModeVoiceChat; - break; - case CoreAudioSessionManager::GameChat: - targetMode = AVAudioSessionModeGameChat; - break; - case CoreAudioSessionManager::VideoRecording: - targetMode = AVAudioSessionModeVideoRecording; - break; - case CoreAudioSessionManager::Measurement: - targetMode = AVAudioSessionModeMeasurement; - break; - case CoreAudioSessionManager::MoviePlayback: - targetMode = AVAudioSessionModeMoviePlayback; - break; - } - - if (targetMode == nil) - return false; - - return [[m_sessionObserver audioSession] setMode:targetMode error:nil]; - -} - -CoreAudioSessionManager::AudioSessionCategorys CoreAudioSessionManager::category() -{ - NSString *category = [[m_sessionObserver audioSession] category]; - AudioSessionCategorys localCategory = Ambient; - - if (category == AVAudioSessionCategoryAmbient) { - localCategory = Ambient; - } else if (category == AVAudioSessionCategorySoloAmbient) { - localCategory = SoloAmbient; - } else if (category == AVAudioSessionCategoryPlayback) { - localCategory = Playback; - } else if (category == AVAudioSessionCategoryRecord) { - localCategory = Record; - } else if (category == AVAudioSessionCategoryPlayAndRecord) { - localCategory = PlayAndRecord; -#ifndef Q_OS_TVOS - } else if (category == AVAudioSessionCategoryAudioProcessing) { - localCategory = AudioProcessing; -#endif - } else if (category == AVAudioSessionCategoryMultiRoute) { - localCategory = MultiRoute; - } - - return localCategory; -} - -CoreAudioSessionManager::AudioSessionModes CoreAudioSessionManager::mode() -{ - NSString *mode = [[m_sessionObserver audioSession] mode]; - AudioSessionModes localMode = Default; - - if (mode == AVAudioSessionModeDefault) { - localMode = Default; - } else if (mode == AVAudioSessionModeVoiceChat) { - localMode = VoiceChat; - } else if (mode == AVAudioSessionModeGameChat) { - localMode = GameChat; - } else if (mode == AVAudioSessionModeVideoRecording) { - localMode = VideoRecording; - } else if (mode == AVAudioSessionModeMeasurement) { - localMode = Measurement; - } else if (mode == AVAudioSessionModeMoviePlayback) { - localMode = MoviePlayback; - } - - return localMode; -} - -QList<QByteArray> CoreAudioSessionManager::inputDevices() -{ - //TODO: Add support for USB input devices - //Right now the default behavior on iOS is to have only one input route - //at a time. - QList<QByteArray> inputDevices; - inputDevices << "default"; - return inputDevices; -} - -QList<QByteArray> CoreAudioSessionManager::outputDevices() -{ - //TODO: Add support for USB output devices - //Right now the default behavior on iOS is to have only one output route - //at a time. - QList<QByteArray> outputDevices; - outputDevices << "default"; - return outputDevices; -} - -float CoreAudioSessionManager::currentIOBufferDuration() -{ - return [[m_sessionObserver audioSession] IOBufferDuration]; -} - -float CoreAudioSessionManager::preferredSampleRate() -{ - return [[m_sessionObserver audioSession] preferredSampleRate]; -} - -#ifdef QT_DEBUG_COREAUDIO -QDebug operator<<(QDebug dbg, CoreAudioSessionManager::AudioSessionCategorys category) -{ - QDebug output = dbg.nospace(); - switch (category) { - case CoreAudioSessionManager::Ambient: - output << "AudioSessionCategoryAmbient"; - break; - case CoreAudioSessionManager::SoloAmbient: - output << "AudioSessionCategorySoloAmbient"; - break; - case CoreAudioSessionManager::Playback: - output << "AudioSessionCategoryPlayback"; - break; - case CoreAudioSessionManager::Record: - output << "AudioSessionCategoryRecord"; - break; - case CoreAudioSessionManager::PlayAndRecord: - output << "AudioSessionCategoryPlayAndRecord"; - break; - case CoreAudioSessionManager::AudioProcessing: - output << "AudioSessionCategoryAudioProcessing"; - break; - case CoreAudioSessionManager::MultiRoute: - output << "AudioSessionCategoryMultiRoute"; - break; - } - return output; -} - -QDebug operator<<(QDebug dbg, CoreAudioSessionManager::AudioSessionCategoryOptions option) -{ - QDebug output = dbg.nospace(); - switch (option) { - case CoreAudioSessionManager::None: - output << "AudioSessionCategoryOptionNone"; - break; - case CoreAudioSessionManager::MixWithOthers: - output << "AudioSessionCategoryOptionMixWithOthers"; - break; - case CoreAudioSessionManager::DuckOthers: - output << "AudioSessionCategoryOptionDuckOthers"; - break; - case CoreAudioSessionManager::AllowBluetooth: - output << "AudioSessionCategoryOptionAllowBluetooth"; - break; - case CoreAudioSessionManager::DefaultToSpeaker: - output << "AudioSessionCategoryOptionDefaultToSpeaker"; - break; - } - return output; -} - -QDebug operator<<(QDebug dbg, CoreAudioSessionManager::AudioSessionModes mode) -{ - QDebug output = dbg.nospace(); - switch (mode) { - case CoreAudioSessionManager::Default: - output << "AudioSessionModeDefault"; - break; - case CoreAudioSessionManager::VoiceChat: - output << "AudioSessionModeVoiceChat"; - break; - case CoreAudioSessionManager::GameChat: - output << "AudioSessionModeGameChat"; - break; - case CoreAudioSessionManager::VideoRecording: - output << "AudioSessionModeVideoRecording"; - break; - case CoreAudioSessionManager::Measurement: - output << "AudioSessionModeMeasurement"; - break; - case CoreAudioSessionManager::MoviePlayback: - output << "AudioSessionModeMoviePlayback"; - break; - } - return output; -} -#endif - -QT_END_NAMESPACE - -#include "moc_qcoreaudiosessionmanager_p.cpp" diff --git a/src/multimedia/platform/darwin/audio/qcoreaudiosessionmanager_p.h b/src/multimedia/platform/darwin/audio/qcoreaudiosessionmanager_p.h deleted file mode 100644 index 7b2a5294f..000000000 --- a/src/multimedia/platform/darwin/audio/qcoreaudiosessionmanager_p.h +++ /dev/null @@ -1,131 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins 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$ -** -****************************************************************************/ - -#ifndef IOSAUDIOSESSIONMANAGER_H -#define IOSAUDIOSESSIONMANAGER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QObject> -#ifdef QT_DEBUG_COREAUDIO -# include <QtCore/QDebug> -#endif - -@class CoreAudioSessionObserver; - -QT_BEGIN_NAMESPACE - -class CoreAudioSessionManager : public QObject -{ - Q_OBJECT -public: - enum AudioSessionCategorys { - Ambient, - SoloAmbient, - Playback, - Record, - PlayAndRecord, - AudioProcessing, - MultiRoute - }; - enum AudioSessionCategoryOptions { - None = 0, - MixWithOthers = 1, - DuckOthers = 2, - AllowBluetooth = 4, - DefaultToSpeaker = 8 - }; - enum AudioSessionModes { - Default, - VoiceChat, - GameChat, - VideoRecording, - Measurement, - MoviePlayback - }; - - static CoreAudioSessionManager& instance(); - - bool setActive(bool active); - bool setCategory(AudioSessionCategorys category, AudioSessionCategoryOptions options = None); - bool setMode(AudioSessionModes mode); - - AudioSessionCategorys category(); - AudioSessionModes mode(); - - QList<QByteArray> inputDevices(); - QList<QByteArray> outputDevices(); - - float currentIOBufferDuration(); - float preferredSampleRate(); - -signals: - void activeChanged(); - void categoryChanged(); - void modeChanged(); - void routeChanged(); - void devicesAvailableChanged(); - -private: - CoreAudioSessionManager(); - ~CoreAudioSessionManager(); - CoreAudioSessionManager(CoreAudioSessionManager const ©); - CoreAudioSessionManager& operator =(CoreAudioSessionManager const ©); - - CoreAudioSessionObserver *m_sessionObserver; -}; - -#ifdef QT_DEBUG_COREAUDIO -QDebug operator <<(QDebug dbg, CoreAudioSessionManager::AudioSessionCategorys category); -QDebug operator <<(QDebug dbg, CoreAudioSessionManager::AudioSessionCategoryOptions option); -QDebug operator <<(QDebug dbg, CoreAudioSessionManager::AudioSessionModes mode); -#endif - -QT_END_NAMESPACE - -#endif // IOSAUDIOSESSIONMANAGER_H diff --git a/src/multimedia/platform/darwin/audio/qcoreaudioutils.mm b/src/multimedia/platform/darwin/audio/qcoreaudioutils.mm deleted file mode 100644 index 8faa353c7..000000000 --- a/src/multimedia/platform/darwin/audio/qcoreaudioutils.mm +++ /dev/null @@ -1,219 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins 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$ -** -****************************************************************************/ - -#include "qcoreaudioutils_p.h" -#include <mach/mach_time.h> - -QT_BEGIN_NAMESPACE - -double CoreAudioUtils::sFrequency = 0.0; -bool CoreAudioUtils::sIsInitialized = false; - -void CoreAudioUtils::initialize() -{ - struct mach_timebase_info timeBaseInfo; - mach_timebase_info(&timeBaseInfo); - sFrequency = static_cast<double>(timeBaseInfo.denom) / static_cast<double>(timeBaseInfo.numer); - sFrequency *= 1000000000.0; - - sIsInitialized = true; -} - - -quint64 CoreAudioUtils::currentTime() -{ - return mach_absolute_time(); -} - -double CoreAudioUtils::frequency() -{ - if (!sIsInitialized) - initialize(); - return sFrequency; -} - -QAudioFormat CoreAudioUtils::toQAudioFormat(AudioStreamBasicDescription const& sf) -{ - QAudioFormat audioFormat; - // all Darwin HW is little endian, we ignore those formats - if ((sf.mFormatFlags & kAudioFormatFlagIsBigEndian) != 0 && QSysInfo::ByteOrder != QSysInfo::LittleEndian) - return audioFormat; - - // filter out the formats we're interested in - QAudioFormat::SampleFormat format = QAudioFormat::Unknown; - switch (sf.mBitsPerChannel) { - case 8: - if ((sf.mFormatFlags & kAudioFormatFlagIsSignedInteger) == 0) - format = QAudioFormat::UInt8; - break; - case 16: - if ((sf.mFormatFlags & kAudioFormatFlagIsSignedInteger) != 0) - format = QAudioFormat::Int16; - break; - case 32: - if ((sf.mFormatFlags & kAudioFormatFlagIsSignedInteger) != 0) - format = QAudioFormat::Int32; - else if ((sf.mFormatFlags & kAudioFormatFlagIsFloat) != 0) - format = QAudioFormat::Float; - break; - default: - break; - } - - audioFormat.setSampleFormat(format); - audioFormat.setSampleRate(sf.mSampleRate); - audioFormat.setChannelCount(sf.mChannelsPerFrame); - - return audioFormat; -} - -AudioStreamBasicDescription CoreAudioUtils::toAudioStreamBasicDescription(QAudioFormat const& audioFormat) -{ - AudioStreamBasicDescription sf; - - sf.mFormatFlags = kAudioFormatFlagIsPacked; - sf.mSampleRate = audioFormat.sampleRate(); - sf.mFramesPerPacket = 1; - sf.mChannelsPerFrame = audioFormat.channelCount(); - sf.mBitsPerChannel = audioFormat.bytesPerSample() * 8; - sf.mBytesPerFrame = audioFormat.bytesPerFrame(); - sf.mBytesPerPacket = sf.mFramesPerPacket * sf.mBytesPerFrame; - sf.mFormatID = kAudioFormatLinearPCM; - - switch (audioFormat.sampleFormat()) { - case QAudioFormat::Int16: - case QAudioFormat::Int32: - sf.mFormatFlags |= kAudioFormatFlagIsSignedInteger; - break; - case QAudioFormat::Float: - sf.mFormatFlags |= kAudioFormatFlagIsFloat; - break; - case QAudioFormat::UInt8: - /* default */ - case QAudioFormat::Unknown: - case QAudioFormat::NSampleFormats: - break; - } - - return sf; -} - -// QAudioRingBuffer -CoreAudioRingBuffer::CoreAudioRingBuffer(int bufferSize): - m_bufferSize(bufferSize) -{ - m_buffer = new char[m_bufferSize]; - reset(); -} - -CoreAudioRingBuffer::~CoreAudioRingBuffer() -{ - delete[] m_buffer; -} - -CoreAudioRingBuffer::Region CoreAudioRingBuffer::acquireReadRegion(int size) -{ - const int used = m_bufferUsed.fetchAndAddAcquire(0); - - if (used > 0) { - const int readSize = qMin(size, qMin(m_bufferSize - m_readPos, used)); - - return readSize > 0 ? Region(m_buffer + m_readPos, readSize) : Region(0, 0); - } - - return Region(0, 0); -} - -void CoreAudioRingBuffer::releaseReadRegion(const CoreAudioRingBuffer::Region ®ion) -{ - m_readPos = (m_readPos + region.second) % m_bufferSize; - - m_bufferUsed.fetchAndAddRelease(-region.second); -} - -CoreAudioRingBuffer::Region CoreAudioRingBuffer::acquireWriteRegion(int size) -{ - const int free = m_bufferSize - m_bufferUsed.fetchAndAddAcquire(0); - - Region output; - - if (free > 0) { - const int writeSize = qMin(size, qMin(m_bufferSize - m_writePos, free)); - output = writeSize > 0 ? Region(m_buffer + m_writePos, writeSize) : Region(0, 0); - } else { - output = Region(0, 0); - } -#ifdef QT_DEBUG_COREAUDIO - qDebug("acquireWriteRegion(%d) free: %d returning Region(%p, %d)", size, free, output.first, output.second); -#endif - return output; -} -void CoreAudioRingBuffer::releaseWriteRegion(const CoreAudioRingBuffer::Region ®ion) -{ - m_writePos = (m_writePos + region.second) % m_bufferSize; - - m_bufferUsed.fetchAndAddRelease(region.second); -#ifdef QT_DEBUG_COREAUDIO - qDebug("releaseWriteRegion(%p,%d): m_writePos:%d", region.first, region.second, m_writePos); -#endif -} - -int CoreAudioRingBuffer::used() const -{ - return m_bufferUsed.loadRelaxed(); -} - -int CoreAudioRingBuffer::free() const -{ - return m_bufferSize - m_bufferUsed.loadRelaxed(); -} - -int CoreAudioRingBuffer::size() const -{ - return m_bufferSize; -} - -void CoreAudioRingBuffer::reset() -{ - m_readPos = 0; - m_writePos = 0; - m_bufferUsed.storeRelaxed(0); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/darwin/audio/qcoreaudioutils_p.h b/src/multimedia/platform/darwin/audio/qcoreaudioutils_p.h deleted file mode 100644 index 4f9d5d327..000000000 --- a/src/multimedia/platform/darwin/audio/qcoreaudioutils_p.h +++ /dev/null @@ -1,104 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins 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$ -** -****************************************************************************/ - -#ifndef IOSAUDIOUTILS_H -#define IOSAUDIOUTILS_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <CoreAudio/CoreAudioTypes.h> - -#include <QtMultimedia/QAudioFormat> -#include <QtCore/qglobal.h> - -QT_BEGIN_NAMESPACE - -class CoreAudioUtils -{ -public: - static quint64 currentTime(); - static double frequency(); - static QAudioFormat toQAudioFormat(const AudioStreamBasicDescription& streamFormat); - static AudioStreamBasicDescription toAudioStreamBasicDescription(QAudioFormat const& audioFormat); - -private: - static void initialize(); - static double sFrequency; - static bool sIsInitialized; -}; - -class CoreAudioRingBuffer -{ -public: - typedef QPair<char*, int> Region; - - CoreAudioRingBuffer(int bufferSize); - ~CoreAudioRingBuffer(); - - Region acquireReadRegion(int size); - void releaseReadRegion(Region const& region); - Region acquireWriteRegion(int size); - void releaseWriteRegion(Region const& region); - - int used() const; - int free() const; - int size() const; - - void reset(); - -private: - int m_bufferSize; - int m_readPos; - int m_writePos; - char* m_buffer; - QAtomicInt m_bufferUsed; -}; - -QT_END_NAMESPACE - -#endif // IOSAUDIOUTILS_H diff --git a/src/multimedia/platform/darwin/audio/qdarwinaudiodevice.mm b/src/multimedia/platform/darwin/audio/qdarwinaudiodevice.mm deleted file mode 100644 index 8fe6f1a2c..000000000 --- a/src/multimedia/platform/darwin/audio/qdarwinaudiodevice.mm +++ /dev/null @@ -1,153 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). -** 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$ -** -****************************************************************************/ - -#include "qdarwinaudiodevice_p.h" -#include "qcoreaudioutils_p.h" -#include <private/qcore_mac_p.h> - -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) -# include "qcoreaudiosessionmanager_p.h" -#endif - -#include <QtCore/QDataStream> -#include <QtCore/QDebug> -#include <QtCore/QSet> -#include <QIODevice> - -QT_BEGIN_NAMESPACE - -#if defined(Q_OS_MACOS) - QCoreAudioDeviceInfo::QCoreAudioDeviceInfo(AudioDeviceID id, const QByteArray &device, QAudioDevice::Mode mode) - : QAudioDevicePrivate(device, mode), - m_deviceId(id) -#else - QCoreAudioDeviceInfo::QCoreAudioDeviceInfo(const QByteArray &device, QAudioDevice::Mode mode) - : QAudioDevicePrivate(device, mode) -#endif - { - preferredFormat = determinePreferredFormat(); - description = getDescription(); - minimumSampleRate = 1; - maximumSampleRate = 96000; - minimumChannelCount = 1; - maximumChannelCount = 16; - supportedSampleFormats << QAudioFormat::UInt8 << QAudioFormat::Int16 << QAudioFormat::Int32 << QAudioFormat::Float; - } - - -QAudioFormat QCoreAudioDeviceInfo::determinePreferredFormat() const -{ - QAudioFormat format; - -#if defined(Q_OS_MACOS) - UInt32 propSize = 0; - AudioObjectPropertyScope audioDevicePropertyScope = mode == QAudioDevice::Input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; - AudioObjectPropertyAddress audioDevicePropertyStreamsAddress = { kAudioDevicePropertyStreams, - audioDevicePropertyScope, - kAudioObjectPropertyElementMaster }; - - if (AudioObjectGetPropertyDataSize(m_deviceId, &audioDevicePropertyStreamsAddress, 0, NULL, &propSize) == noErr) { - - const int sc = propSize / sizeof(AudioStreamID); - - if (sc > 0) { - AudioStreamID* streams = new AudioStreamID[sc]; - - if (AudioObjectGetPropertyData(m_deviceId, &audioDevicePropertyStreamsAddress, 0, NULL, &propSize, streams) == noErr) { - - AudioObjectPropertyAddress audioDevicePhysicalFormatPropertyAddress = { kAudioStreamPropertyPhysicalFormat, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - - for (int i = 0; i < sc; ++i) { - if (AudioObjectGetPropertyDataSize(streams[i], &audioDevicePhysicalFormatPropertyAddress, 0, NULL, &propSize) == noErr) { - AudioStreamBasicDescription sf; - - if (AudioObjectGetPropertyData(streams[i], &audioDevicePhysicalFormatPropertyAddress, 0, NULL, &propSize, &sf) == noErr) { - format = CoreAudioUtils::toQAudioFormat(sf); - break; - } else { - qWarning() << "QAudioDevice: Unable to find perferedFormat for stream"; - } - } else { - qWarning() << "QAudioDevice: Unable to find size of perferedFormat for stream"; - } - } - } - - delete[] streams; - } - } - if (!format.isValid()) -#endif - { - format.setSampleRate(44100); - format.setSampleFormat(QAudioFormat::Int16); - format.setChannelCount(mode == QAudioDevice::Input ? 1 : 2); - } - - return format; -} - - -QString QCoreAudioDeviceInfo::getDescription() const -{ -#ifdef Q_OS_MACOS - CFStringRef name; - UInt32 size = sizeof(CFStringRef); - AudioObjectPropertyScope audioPropertyScope = mode == QAudioDevice::Input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; - - AudioObjectPropertyAddress audioDeviceNamePropertyAddress = { kAudioObjectPropertyName, - audioPropertyScope, - kAudioObjectPropertyElementMaster }; - - if (AudioObjectGetPropertyData(m_deviceId, &audioDeviceNamePropertyAddress, 0, NULL, &size, &name) != noErr) { - qWarning() << "QAudioDevice: Unable to find device description"; - return QString(); - } - - QString s = QString::fromCFString(name); - CFRelease(name); - return s; -#else - return QString::fromUtf8(id); -#endif -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/darwin/audio/qdarwinaudiodevice_p.h b/src/multimedia/platform/darwin/audio/qdarwinaudiodevice_p.h deleted file mode 100644 index 8a1ad502d..000000000 --- a/src/multimedia/platform/darwin/audio/qdarwinaudiodevice_p.h +++ /dev/null @@ -1,87 +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$ -** -****************************************************************************/ -#ifndef IOSAUDIODEVICEINFO_H -#define IOSAUDIODEVICEINFO_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qaudiosystem_p.h> -#include <private/qaudiodevice_p.h> - -#if defined(Q_OS_MACOS) -# include <CoreAudio/CoreAudio.h> -#endif - -QT_BEGIN_NAMESPACE - -class QCoreAudioDeviceInfo : public QAudioDevicePrivate -{ -public: -#if defined(Q_OS_MACOS) - QCoreAudioDeviceInfo(AudioDeviceID id, const QByteArray &device, QAudioDevice::Mode mode); -#else - QCoreAudioDeviceInfo(const QByteArray &device, QAudioDevice::Mode mode); -#endif - ~QCoreAudioDeviceInfo() {} - - bool isFormatSupported(const QAudioFormat &format) const; - -#if defined(Q_OS_MACOS) - AudioDeviceID deviceID() const { return m_deviceId; } -#endif -private: - QAudioFormat determinePreferredFormat() const; - QString getDescription() const; -#if defined(Q_OS_MACOS) - AudioDeviceID m_deviceId; -#endif -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/darwin/audio/qdarwinaudiosink.mm b/src/multimedia/platform/darwin/audio/qdarwinaudiosink.mm deleted file mode 100644 index 5fd5afbdf..000000000 --- a/src/multimedia/platform/darwin/audio/qdarwinaudiosink.mm +++ /dev/null @@ -1,691 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). -** 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$ -** -****************************************************************************/ -#include "qdarwinaudiosink_p.h" -#include "qcoreaudiosessionmanager_p.h" -#include "qdarwinaudiodevice_p.h" -#include "qcoreaudioutils_p.h" -#include <private/qdarwinmediadevices_p.h> -#include <qmediadevices.h> - -#include <QtCore/QDataStream> -#include <QtCore/QTimer> -#include <QtCore/QDebug> - -#include <AudioUnit/AudioUnit.h> -#include <AudioToolbox/AudioToolbox.h> -#if defined(Q_OS_OSX) -# include <AudioUnit/AudioComponent.h> -#endif - -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) -# include <QtMultimedia/private/qaudiohelpers_p.h> -#endif - -QT_BEGIN_NAMESPACE - -QDarwinAudioSinkBuffer::QDarwinAudioSinkBuffer(int bufferSize, int maxPeriodSize, const QAudioFormat &audioFormat) - : m_deviceError(false) - , m_maxPeriodSize(maxPeriodSize) - , m_device(0) -{ - m_buffer = new CoreAudioRingBuffer(bufferSize + (bufferSize % maxPeriodSize == 0 ? 0 : maxPeriodSize - (bufferSize % maxPeriodSize))); - m_bytesPerFrame = audioFormat.bytesPerFrame(); - m_periodTime = m_maxPeriodSize / m_bytesPerFrame * 1000 / audioFormat.sampleRate(); - - m_fillTimer = new QTimer(this); - connect(m_fillTimer, SIGNAL(timeout()), SLOT(fillBuffer())); -} - -QDarwinAudioSinkBuffer::~QDarwinAudioSinkBuffer() -{ - delete m_buffer; -} - -qint64 QDarwinAudioSinkBuffer::readFrames(char *data, qint64 maxFrames) -{ - bool wecan = true; - qint64 framesRead = 0; - - while (wecan && framesRead < maxFrames) { - CoreAudioRingBuffer::Region region = m_buffer->acquireReadRegion((maxFrames - framesRead) * m_bytesPerFrame); - - if (region.second > 0) { - // Ensure that we only read whole frames. - region.second -= region.second % m_bytesPerFrame; - - if (region.second > 0) { - memcpy(data + (framesRead * m_bytesPerFrame), region.first, region.second); - framesRead += region.second / m_bytesPerFrame; - } else - wecan = false; // If there is only a partial frame left we should exit. - } - else - wecan = false; - - m_buffer->releaseReadRegion(region); - } - - if (framesRead == 0 && m_deviceError) - framesRead = -1; - - return framesRead; -} - -qint64 QDarwinAudioSinkBuffer::writeBytes(const char *data, qint64 maxSize) -{ - bool wecan = true; - qint64 bytesWritten = 0; - - maxSize -= maxSize % m_bytesPerFrame; - while (wecan && bytesWritten < maxSize) { - CoreAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(maxSize - bytesWritten); - - if (region.second > 0) { - memcpy(region.first, data + bytesWritten, region.second); - bytesWritten += region.second; - } - else - wecan = false; - - m_buffer->releaseWriteRegion(region); - } - - if (bytesWritten > 0) - emit readyRead(); - - return bytesWritten; -} - -int QDarwinAudioSinkBuffer::available() const -{ - return m_buffer->free(); -} - -void QDarwinAudioSinkBuffer::reset() -{ - m_buffer->reset(); - m_device = 0; - m_deviceError = false; -} - -void QDarwinAudioSinkBuffer::setPrefetchDevice(QIODevice *device) -{ - if (m_device != device) { - m_device = device; - if (m_device != 0) - fillBuffer(); - } -} - -void QDarwinAudioSinkBuffer::startFillTimer() -{ - if (m_device != 0) - m_fillTimer->start(m_buffer->size() / 2 / m_maxPeriodSize * m_periodTime); -} - -void QDarwinAudioSinkBuffer::stopFillTimer() -{ - m_fillTimer->stop(); -} - -void QDarwinAudioSinkBuffer::fillBuffer() -{ - const int free = m_buffer->free(); - const int writeSize = free - (free % m_maxPeriodSize); - - if (writeSize > 0) { - bool wecan = true; - int filled = 0; - - while (!m_deviceError && wecan && filled < writeSize) { - CoreAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(writeSize - filled); - - if (region.second > 0) { - region.second = m_device->read(region.first, region.second); - if (region.second > 0) - filled += region.second; - else if (region.second == 0) - wecan = false; - else if (region.second < 0) { - m_fillTimer->stop(); - region.second = 0; - m_deviceError = true; - } - } - else - wecan = false; - - m_buffer->releaseWriteRegion(region); - } - - if (filled > 0) - emit readyRead(); - } -} - -QDarwinAudioSinkDevice::QDarwinAudioSinkDevice(QDarwinAudioSinkBuffer *audioBuffer, QObject *parent) - : QIODevice(parent) - , m_audioBuffer(audioBuffer) -{ - open(QIODevice::WriteOnly | QIODevice::Unbuffered); -} - -qint64 QDarwinAudioSinkDevice::readData(char *data, qint64 len) -{ - Q_UNUSED(data); - Q_UNUSED(len); - - return 0; -} - -qint64 QDarwinAudioSinkDevice::writeData(const char *data, qint64 len) -{ - return m_audioBuffer->writeBytes(data, len); -} - -QDarwinAudioSink::QDarwinAudioSink(const QAudioDevice &device) - : m_audioDeviceInfo(device) -{ - QAudioDevice di = device; - if (di.isNull()) - di = QMediaDevices::defaultAudioOutput(); -#if defined(Q_OS_MACOS) - const QCoreAudioDeviceInfo *info = static_cast<const QCoreAudioDeviceInfo *>(di.handle()); - Q_ASSERT(info); - m_audioDeviceId = info->deviceID(); -#endif - m_device = di.id(); - - m_clockFrequency = CoreAudioUtils::frequency() / 1000; - m_audioThreadState.storeRelaxed(Stopped); -} - -QDarwinAudioSink::~QDarwinAudioSink() -{ - close(); -} - -void QDarwinAudioSink::start(QIODevice *device) -{ - QIODevice* op = device; - - if (!m_audioDeviceInfo.isFormatSupported(m_audioFormat) || !open()) { - m_stateCode = QAudio::StoppedState; - m_errorCode = QAudio::OpenError; - return; - } - - reset(); - m_audioBuffer->reset(); - m_audioBuffer->setPrefetchDevice(op); - - if (op == 0) { - op = m_audioIO; - m_stateCode = QAudio::IdleState; - } - else - m_stateCode = QAudio::ActiveState; - - // Start - m_pullMode = true; - m_errorCode = QAudio::NoError; - m_totalFrames = 0; - - if (m_stateCode == QAudio::ActiveState) - audioThreadStart(); - - emit stateChanged(m_stateCode); -} - -QIODevice *QDarwinAudioSink::start() -{ - if (!m_audioDeviceInfo.isFormatSupported(m_audioFormat) || !open()) { - m_stateCode = QAudio::StoppedState; - m_errorCode = QAudio::OpenError; - return m_audioIO; - } - - reset(); - m_audioBuffer->reset(); - m_audioBuffer->setPrefetchDevice(0); - - m_stateCode = QAudio::IdleState; - - // Start - m_pullMode = false; - m_errorCode = QAudio::NoError; - m_totalFrames = 0; - - emit stateChanged(m_stateCode); - - return m_audioIO; -} - -void QDarwinAudioSink::stop() -{ - QMutexLocker lock(&m_mutex); - if (m_stateCode != QAudio::StoppedState) { - audioThreadDrain(); - - m_stateCode = QAudio::StoppedState; - m_errorCode = QAudio::NoError; - emit stateChanged(m_stateCode); - } -} - -void QDarwinAudioSink::reset() -{ - QMutexLocker lock(&m_mutex); - if (m_stateCode != QAudio::StoppedState) { - audioThreadStop(); - - m_stateCode = QAudio::StoppedState; - m_errorCode = QAudio::NoError; - emit stateChanged(m_stateCode); - } -} - -void QDarwinAudioSink::suspend() -{ - QMutexLocker lock(&m_mutex); - if (m_stateCode == QAudio::ActiveState || m_stateCode == QAudio::IdleState) { - audioThreadStop(); - - m_stateCode = QAudio::SuspendedState; - m_errorCode = QAudio::NoError; - emit stateChanged(m_stateCode); - } -} - -void QDarwinAudioSink::resume() -{ - QMutexLocker lock(&m_mutex); - if (m_stateCode == QAudio::SuspendedState) { - audioThreadStart(); - - m_stateCode = m_pullMode ? QAudio::ActiveState : QAudio::IdleState; - m_errorCode = QAudio::NoError; - emit stateChanged(m_stateCode); - } -} - -qsizetype QDarwinAudioSink::bytesFree() const -{ - return m_audioBuffer->available(); -} - -void QDarwinAudioSink::setBufferSize(qsizetype value) -{ - if (m_stateCode == QAudio::StoppedState) - m_internalBufferSize = value; -} - -qsizetype QDarwinAudioSink::bufferSize() const -{ - return m_internalBufferSize; -} - -qint64 QDarwinAudioSink::processedUSecs() const -{ - return m_totalFrames * 1000000 / m_audioFormat.sampleRate(); -} - -QAudio::Error QDarwinAudioSink::error() const -{ - return m_errorCode; -} - -QAudio::State QDarwinAudioSink::state() const -{ - return m_stateCode; -} - -void QDarwinAudioSink::setFormat(const QAudioFormat &format) -{ - if (m_stateCode == QAudio::StoppedState) - m_audioFormat = format; -} - -QAudioFormat QDarwinAudioSink::format() const -{ - return m_audioFormat; -} - -void QDarwinAudioSink::setVolume(qreal volume) -{ - m_cachedVolume = qBound(qreal(0.0), volume, qreal(1.0)); - if (!m_isOpen) - return; - -#if defined(Q_OS_OSX) - //on OS X the volume can be set directly on the AudioUnit - if (AudioUnitSetParameter(m_audioUnit, - kHALOutputParam_Volume, - kAudioUnitScope_Global, - 0 /* bus */, - m_cachedVolume, - 0) == noErr) - m_volume = m_cachedVolume; -#endif -} - -qreal QDarwinAudioSink::volume() const -{ - return m_cachedVolume; -} - -void QDarwinAudioSink::deviceStopped() -{ - emit stateChanged(m_stateCode); -} - -void QDarwinAudioSink::inputReady() -{ - QMutexLocker lock(&m_mutex); - if (m_stateCode == QAudio::IdleState) { - audioThreadStart(); - - m_stateCode = QAudio::ActiveState; - m_errorCode = QAudio::NoError; - - emit stateChanged(m_stateCode); - } -} - -OSStatus QDarwinAudioSink::renderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) -{ - Q_UNUSED(ioActionFlags); - Q_UNUSED(inTimeStamp); - Q_UNUSED(inBusNumber); - Q_UNUSED(inNumberFrames); - - QDarwinAudioSink* d = static_cast<QDarwinAudioSink*>(inRefCon); - - const int threadState = d->m_audioThreadState.fetchAndAddAcquire(0); - if (threadState == Stopped) { - ioData->mBuffers[0].mDataByteSize = 0; - d->audioDeviceStop(); - } - else { - const UInt32 bytesPerFrame = d->m_streamFormat.mBytesPerFrame; - qint64 framesRead; - - framesRead = d->m_audioBuffer->readFrames((char*)ioData->mBuffers[0].mData, - ioData->mBuffers[0].mDataByteSize / bytesPerFrame); - - if (framesRead > 0) { - ioData->mBuffers[0].mDataByteSize = framesRead * bytesPerFrame; - d->m_totalFrames += framesRead; - -#if defined(Q_OS_MACOS) - // If playback is already stopped. - if (threadState != Running) { - qreal oldVolume = d->m_cachedVolume; - // Decrease volume smoothly. - d->setVolume(d->m_volume / 2); - d->m_cachedVolume = oldVolume; - } -#elif defined(Q_OS_IOS) || defined(Q_OS_TVOS) - // on iOS we have to adjust the sound volume ourselves - if (!qFuzzyCompare(d->m_cachedVolume, qreal(1.0f))) { - QAudioHelperInternal::qMultiplySamples(d->m_cachedVolume, - d->m_audioFormat, - ioData->mBuffers[0].mData, /* input */ - ioData->mBuffers[0].mData, /* output */ - ioData->mBuffers[0].mDataByteSize); - } -#endif - - } - else { - ioData->mBuffers[0].mDataByteSize = 0; - if (framesRead == 0) { - if (threadState == Draining) - d->audioDeviceStop(); - else - d->audioDeviceIdle(); - } - else - d->audioDeviceError(); - } - } - - return noErr; -} - -bool QDarwinAudioSink::open() -{ -#if defined(Q_OS_IOS) - // Set default category to Ambient (implies MixWithOthers). This makes sure audio stops playing - // if the screen is locked or if the Silent switch is toggled. - CoreAudioSessionManager::instance().setCategory(CoreAudioSessionManager::Ambient, CoreAudioSessionManager::None); - CoreAudioSessionManager::instance().setActive(true); -#endif - - if (m_errorCode != QAudio::NoError) - return false; - - if (m_isOpen) { - setVolume(m_cachedVolume); - return true; - } - - AudioComponentDescription componentDescription; - componentDescription.componentType = kAudioUnitType_Output; -#if defined(Q_OS_OSX) - componentDescription.componentSubType = kAudioUnitSubType_HALOutput; -#else - componentDescription.componentSubType = kAudioUnitSubType_RemoteIO; -#endif - componentDescription.componentManufacturer = kAudioUnitManufacturer_Apple; - componentDescription.componentFlags = 0; - componentDescription.componentFlagsMask = 0; - - AudioComponent component = AudioComponentFindNext(0, &componentDescription); - if (component == 0) { - qWarning() << "QAudioOutput: Failed to find Output component"; - return false; - } - - if (AudioComponentInstanceNew(component, &m_audioUnit) != noErr) { - qWarning() << "QAudioOutput: Unable to Open Output Component"; - return false; - } - - // register callback - AURenderCallbackStruct callback; - callback.inputProc = renderCallback; - callback.inputProcRefCon = this; - - if (AudioUnitSetProperty(m_audioUnit, - kAudioUnitProperty_SetRenderCallback, - kAudioUnitScope_Global, - 0, - &callback, - sizeof(callback)) != noErr) { - qWarning() << "QAudioOutput: Failed to set AudioUnit callback"; - return false; - } - -#if defined(Q_OS_OSX) - //Set Audio Device - if (AudioUnitSetProperty(m_audioUnit, - kAudioOutputUnitProperty_CurrentDevice, - kAudioUnitScope_Global, - 0, - &m_audioDeviceId, - sizeof(m_audioDeviceId)) != noErr) { - qWarning() << "QAudioOutput: Unable to use configured device"; - return false; - } -#endif - - // Set stream format - m_streamFormat = CoreAudioUtils::toAudioStreamBasicDescription(m_audioFormat); - - UInt32 size = sizeof(m_streamFormat); - if (AudioUnitSetProperty(m_audioUnit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, - 0, - &m_streamFormat, - size) != noErr) { - qWarning() << "QAudioOutput: Unable to Set Stream information"; - return false; - } - - // Allocate buffer - UInt32 numberOfFrames = 0; -#if defined(Q_OS_OSX) - size = sizeof(UInt32); - if (AudioUnitGetProperty(m_audioUnit, - kAudioDevicePropertyBufferFrameSize, - kAudioUnitScope_Global, - 0, - &numberOfFrames, - &size) != noErr) { - qWarning() << "QAudioSource: Failed to get audio period size"; - return false; - } -#else //iOS - Float32 bufferSize = CoreAudioSessionManager::instance().currentIOBufferDuration(); - bufferSize *= m_streamFormat.mSampleRate; - numberOfFrames = bufferSize; -#endif - - m_periodSizeBytes = numberOfFrames * m_streamFormat.mBytesPerFrame; - if (m_internalBufferSize < m_periodSizeBytes * 2) - m_internalBufferSize = m_periodSizeBytes * 2; - else - m_internalBufferSize -= m_internalBufferSize % m_streamFormat.mBytesPerFrame; - - m_audioBuffer = new QDarwinAudioSinkBuffer(m_internalBufferSize, m_periodSizeBytes, m_audioFormat); - connect(m_audioBuffer, SIGNAL(readyRead()), SLOT(inputReady())); //Pull - - m_audioIO = new QDarwinAudioSinkDevice(m_audioBuffer, this); - - //Init - if (AudioUnitInitialize(m_audioUnit)) { - qWarning() << "QAudioOutput: Failed to initialize AudioUnit"; - return false; - } - - m_isOpen = true; - - setVolume(m_cachedVolume); - - return true; -} - -void QDarwinAudioSink::close() -{ - if (m_audioUnit != 0) { - AudioOutputUnitStop(m_audioUnit); - AudioUnitUninitialize(m_audioUnit); - AudioComponentInstanceDispose(m_audioUnit); - } - - delete m_audioBuffer; -} - -void QDarwinAudioSink::audioThreadStart() -{ - startTimers(); - m_audioThreadState.storeRelaxed(Running); - AudioOutputUnitStart(m_audioUnit); -} - -void QDarwinAudioSink::audioThreadStop() -{ - stopTimers(); - if (m_audioThreadState.testAndSetAcquire(Running, Stopped)) - m_threadFinished.wait(&m_mutex, 500); -} - -void QDarwinAudioSink::audioThreadDrain() -{ - stopTimers(); - if (m_audioThreadState.testAndSetAcquire(Running, Draining)) - m_threadFinished.wait(&m_mutex, 500); -} - -void QDarwinAudioSink::audioDeviceStop() -{ - AudioOutputUnitStop(m_audioUnit); - m_audioThreadState.storeRelaxed(Stopped); - m_threadFinished.wakeOne(); -} - -void QDarwinAudioSink::audioDeviceIdle() -{ - if (m_stateCode == QAudio::ActiveState) { - QMutexLocker lock(&m_mutex); - audioDeviceStop(); - - m_errorCode = QAudio::UnderrunError; - m_stateCode = QAudio::IdleState; - QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection); - } -} - -void QDarwinAudioSink::audioDeviceError() -{ - if (m_stateCode == QAudio::ActiveState) { - QMutexLocker lock(&m_mutex); - audioDeviceStop(); - - m_errorCode = QAudio::IOError; - m_stateCode = QAudio::StoppedState; - QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection); - } -} - -void QDarwinAudioSink::startTimers() -{ - m_audioBuffer->startFillTimer(); -} - -void QDarwinAudioSink::stopTimers() -{ - m_audioBuffer->stopFillTimer(); -} - -QT_END_NAMESPACE - -#include "moc_qdarwinaudiosink_p.cpp" diff --git a/src/multimedia/platform/darwin/audio/qdarwinaudiosink_p.h b/src/multimedia/platform/darwin/audio/qdarwinaudiosink_p.h deleted file mode 100644 index bb2c6507f..000000000 --- a/src/multimedia/platform/darwin/audio/qdarwinaudiosink_p.h +++ /dev/null @@ -1,212 +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$ -** -****************************************************************************/ -#ifndef IOSAUDIOOUTPUT_H -#define IOSAUDIOOUTPUT_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qaudiosystem_p.h> - -#if defined(Q_OS_OSX) -# include <CoreAudio/CoreAudio.h> -#endif -#include <AudioUnit/AudioUnit.h> -#include <CoreAudio/CoreAudioTypes.h> - -#include <QtCore/QIODevice> -#include <QtCore/QWaitCondition> -#include <QtCore/QMutex> -#include <private/qdarwinaudiodevice_p.h> - -QT_BEGIN_NAMESPACE - -class QDarwinAudioSinkBuffer; -class QTimer; -class QCoreAudioDeviceInfo; -class CoreAudioRingBuffer; - -class QDarwinAudioSinkBuffer : public QObject -{ - Q_OBJECT - -public: - QDarwinAudioSinkBuffer(int bufferSize, int maxPeriodSize, QAudioFormat const& audioFormat); - ~QDarwinAudioSinkBuffer(); - - qint64 readFrames(char *data, qint64 maxFrames); - qint64 writeBytes(const char *data, qint64 maxSize); - - int available() const; - void reset(); - - void setPrefetchDevice(QIODevice *device); - - void startFillTimer(); - void stopFillTimer(); - -signals: - void readyRead(); - -private slots: - void fillBuffer(); - -private: - bool m_deviceError; - int m_maxPeriodSize; - int m_bytesPerFrame; - int m_periodTime; - QIODevice *m_device; - QTimer *m_fillTimer; - CoreAudioRingBuffer *m_buffer; -}; - -class QDarwinAudioSinkDevice : public QIODevice -{ -public: - QDarwinAudioSinkDevice(QDarwinAudioSinkBuffer *audioBuffer, QObject *parent); - - qint64 readData(char *data, qint64 len); - qint64 writeData(const char *data, qint64 len); - - bool isSequential() const { return true; } - -private: - QDarwinAudioSinkBuffer *m_audioBuffer; -}; - - -class QDarwinAudioSink : public QPlatformAudioSink -{ - Q_OBJECT - -public: - QDarwinAudioSink(const QAudioDevice &device); - ~QDarwinAudioSink(); - - void start(QIODevice *device); - QIODevice *start(); - void stop(); - void reset(); - void suspend(); - void resume(); - qsizetype bytesFree() const; - void setBufferSize(qsizetype value); - qsizetype bufferSize() const; - qint64 processedUSecs() const; - QAudio::Error error() const; - QAudio::State state() const; - void setFormat(const QAudioFormat &format); - QAudioFormat format() const; - - void setVolume(qreal volume); - qreal volume() const; - -private slots: - void deviceStopped(); - void inputReady(); - -private: - enum { - Running, - Draining, - Stopped - }; - - static OSStatus renderCallback(void *inRefCon, - AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList *ioData); - - bool open(); - void close(); - void audioThreadStart(); - void audioThreadStop(); - void audioThreadDrain(); - void audioDeviceStop(); - void audioDeviceIdle(); - void audioDeviceError(); - - void startTimers(); - void stopTimers(); - - QAudioDevice m_audioDeviceInfo; - QByteArray m_device; - - static constexpr int DEFAULT_BUFFER_SIZE = 8 * 1024; - - bool m_isOpen = false; - int m_internalBufferSize = DEFAULT_BUFFER_SIZE; - int m_periodSizeBytes = 0; - qint64 m_totalFrames = 0; - QAudioFormat m_audioFormat; - QIODevice *m_audioIO = nullptr; -#if defined(Q_OS_MACOS) - AudioDeviceID m_audioDeviceId; -#endif - AudioUnit m_audioUnit = 0; - Float64 m_clockFrequency = 0; - AudioStreamBasicDescription m_streamFormat; - QDarwinAudioSinkBuffer *m_audioBuffer = nullptr; - QAtomicInt m_audioThreadState; - QWaitCondition m_threadFinished; - QMutex m_mutex; - qreal m_cachedVolume = 1.; -#if defined(Q_OS_MACOS) - qreal m_volume = 1.; -#endif - bool m_pullMode = false; - - QAudio::Error m_errorCode = QAudio::NoError; - QAudio::State m_stateCode = QAudio::StoppedState; -}; - -QT_END_NAMESPACE - -#endif // IOSAUDIOOUTPUT_H diff --git a/src/multimedia/platform/darwin/audio/qdarwinaudiosource.mm b/src/multimedia/platform/darwin/audio/qdarwinaudiosource.mm deleted file mode 100644 index 17754518b..000000000 --- a/src/multimedia/platform/darwin/audio/qdarwinaudiosource.mm +++ /dev/null @@ -1,977 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). -** 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$ -** -****************************************************************************/ -#include "qdarwinaudiosource_p.h" -#include "qcoreaudiosessionmanager_p.h" -#include "qdarwinaudiodevice_p.h" -#include "qcoreaudioutils_p.h" -#include "private/qdarwinmediadevices_p.h" -#include <qmediadevices.h> - -#if defined(Q_OS_OSX) -# include <AudioUnit/AudioComponent.h> -#endif - -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) -# include "qcoreaudiosessionmanager_p.h" -#endif - -#include <QtMultimedia/private/qaudiohelpers_p.h> -#include <QtCore/QDataStream> -#include <QtCore/QDebug> - -QT_BEGIN_NAMESPACE - -static const int DEFAULT_BUFFER_SIZE = 4 * 1024; - -QCoreAudioBufferList::QCoreAudioBufferList(const AudioStreamBasicDescription &streamFormat) - : m_owner(false) - , m_streamDescription(streamFormat) -{ - const bool isInterleaved = (m_streamDescription.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; - const int numberOfBuffers = isInterleaved ? 1 : m_streamDescription.mChannelsPerFrame; - - m_dataSize = 0; - - m_bufferList = reinterpret_cast<AudioBufferList*>(malloc(sizeof(AudioBufferList) + - (sizeof(AudioBuffer) * numberOfBuffers))); - - m_bufferList->mNumberBuffers = numberOfBuffers; - for (int i = 0; i < numberOfBuffers; ++i) { - m_bufferList->mBuffers[i].mNumberChannels = isInterleaved ? numberOfBuffers : 1; - m_bufferList->mBuffers[i].mDataByteSize = 0; - m_bufferList->mBuffers[i].mData = 0; - } -} - -QCoreAudioBufferList::QCoreAudioBufferList(const AudioStreamBasicDescription &streamFormat, char *buffer, int bufferSize) - : m_owner(false) - , m_streamDescription(streamFormat) - , m_bufferList(0) -{ - m_dataSize = bufferSize; - - m_bufferList = reinterpret_cast<AudioBufferList*>(malloc(sizeof(AudioBufferList) + sizeof(AudioBuffer))); - - m_bufferList->mNumberBuffers = 1; - m_bufferList->mBuffers[0].mNumberChannels = 1; - m_bufferList->mBuffers[0].mDataByteSize = m_dataSize; - m_bufferList->mBuffers[0].mData = buffer; -} - -QCoreAudioBufferList::QCoreAudioBufferList(const AudioStreamBasicDescription &streamFormat, int framesToBuffer) - : m_owner(true) - , m_streamDescription(streamFormat) - , m_bufferList(0) -{ - const bool isInterleaved = (m_streamDescription.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; - const int numberOfBuffers = isInterleaved ? 1 : m_streamDescription.mChannelsPerFrame; - - m_dataSize = framesToBuffer * m_streamDescription.mBytesPerFrame; - - m_bufferList = reinterpret_cast<AudioBufferList*>(malloc(sizeof(AudioBufferList) + - (sizeof(AudioBuffer) * numberOfBuffers))); - m_bufferList->mNumberBuffers = numberOfBuffers; - for (int i = 0; i < numberOfBuffers; ++i) { - m_bufferList->mBuffers[i].mNumberChannels = isInterleaved ? numberOfBuffers : 1; - m_bufferList->mBuffers[i].mDataByteSize = m_dataSize; - m_bufferList->mBuffers[i].mData = malloc(m_dataSize); - } -} - -QCoreAudioBufferList::~QCoreAudioBufferList() -{ - if (m_owner) { - for (UInt32 i = 0; i < m_bufferList->mNumberBuffers; ++i) - free(m_bufferList->mBuffers[i].mData); - } - - free(m_bufferList); -} - -char *QCoreAudioBufferList::data(int buffer) const -{ - return static_cast<char*>(m_bufferList->mBuffers[buffer].mData); -} - -qint64 QCoreAudioBufferList::bufferSize(int buffer) const -{ - return m_bufferList->mBuffers[buffer].mDataByteSize; -} - -int QCoreAudioBufferList::frameCount(int buffer) const -{ - return m_bufferList->mBuffers[buffer].mDataByteSize / m_streamDescription.mBytesPerFrame; -} - -int QCoreAudioBufferList::packetCount(int buffer) const -{ - return m_bufferList->mBuffers[buffer].mDataByteSize / m_streamDescription.mBytesPerPacket; -} - -int QCoreAudioBufferList::packetSize() const -{ - return m_streamDescription.mBytesPerPacket; -} - -void QCoreAudioBufferList::reset() -{ - for (UInt32 i = 0; i < m_bufferList->mNumberBuffers; ++i) { - m_bufferList->mBuffers[i].mDataByteSize = m_dataSize; - m_bufferList->mBuffers[i].mData = 0; - } -} - -QCoreAudioPacketFeeder::QCoreAudioPacketFeeder(QCoreAudioBufferList *abl) - : m_audioBufferList(abl) -{ - m_totalPackets = m_audioBufferList->packetCount(); - m_position = 0; -} - -bool QCoreAudioPacketFeeder::feed(AudioBufferList &dst, UInt32 &packetCount) -{ - if (m_position == m_totalPackets) { - dst.mBuffers[0].mDataByteSize = 0; - packetCount = 0; - return false; - } - - if (m_totalPackets - m_position < packetCount) - packetCount = m_totalPackets - m_position; - - dst.mBuffers[0].mDataByteSize = packetCount * m_audioBufferList->packetSize(); - dst.mBuffers[0].mData = m_audioBufferList->data() + (m_position * m_audioBufferList->packetSize()); - - m_position += packetCount; - - return true; -} - -bool QCoreAudioPacketFeeder::empty() const -{ - return m_position == m_totalPackets; -} - -QDarwinAudioSourceBuffer::QDarwinAudioSourceBuffer(int bufferSize, int maxPeriodSize, const AudioStreamBasicDescription &inputFormat, const AudioStreamBasicDescription &outputFormat, QObject *parent) - : QObject(parent) - , m_deviceError(false) - , m_device(0) - , m_audioConverter(0) - , m_inputFormat(inputFormat) - , m_outputFormat(outputFormat) - , m_volume(qreal(1.0f)) -{ - m_maxPeriodSize = maxPeriodSize; - m_periodTime = m_maxPeriodSize / m_outputFormat.mBytesPerFrame * 1000 / m_outputFormat.mSampleRate; - - m_buffer = new CoreAudioRingBuffer(bufferSize); - - m_inputBufferList = new QCoreAudioBufferList(m_inputFormat); - - m_flushTimer = new QTimer(this); - connect(m_flushTimer, SIGNAL(timeout()), SLOT(flushBuffer())); - - if (CoreAudioUtils::toQAudioFormat(inputFormat) != CoreAudioUtils::toQAudioFormat(outputFormat)) { - if (AudioConverterNew(&m_inputFormat, &m_outputFormat, &m_audioConverter) != noErr) { - qWarning() << "QAudioSource: Unable to create an Audio Converter"; - m_audioConverter = 0; - } - } - - m_qFormat = CoreAudioUtils::toQAudioFormat(inputFormat); // we adjust volume before conversion -} - -QDarwinAudioSourceBuffer::~QDarwinAudioSourceBuffer() -{ - delete m_buffer; -} - -qreal QDarwinAudioSourceBuffer::volume() const -{ - return m_volume; -} - -void QDarwinAudioSourceBuffer::setVolume(qreal v) -{ - m_volume = v; -} - -qint64 QDarwinAudioSourceBuffer::renderFromDevice(AudioUnit audioUnit, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames) -{ - const bool pullMode = m_device == 0; - - OSStatus err; - qint64 framesRendered = 0; - - m_inputBufferList->reset(); - err = AudioUnitRender(audioUnit, - ioActionFlags, - inTimeStamp, - inBusNumber, - inNumberFrames, - m_inputBufferList->audioBufferList()); - - // adjust volume, if necessary - if (!qFuzzyCompare(m_volume, qreal(1.0f))) { - QAudioHelperInternal::qMultiplySamples(m_volume, - m_qFormat, - m_inputBufferList->data(), /* input */ - m_inputBufferList->data(), /* output */ - m_inputBufferList->bufferSize()); - } - - if (m_audioConverter != 0) { - QCoreAudioPacketFeeder feeder(m_inputBufferList); - - int copied = 0; - const int available = m_buffer->free(); - - while (err == noErr && !feeder.empty()) { - CoreAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(available - copied); - - if (region.second == 0) - break; - - AudioBufferList output; - output.mNumberBuffers = 1; - output.mBuffers[0].mNumberChannels = 1; - output.mBuffers[0].mDataByteSize = region.second; - output.mBuffers[0].mData = region.first; - - UInt32 packetSize = region.second / m_outputFormat.mBytesPerPacket; - err = AudioConverterFillComplexBuffer(m_audioConverter, - converterCallback, - &feeder, - &packetSize, - &output, - 0); - region.second = output.mBuffers[0].mDataByteSize; - copied += region.second; - - m_buffer->releaseWriteRegion(region); - } - - framesRendered += copied / m_outputFormat.mBytesPerFrame; - } - else { - const int available = m_inputBufferList->bufferSize(); - bool wecan = true; - int copied = 0; - - while (wecan && copied < available) { - CoreAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(available - copied); - - if (region.second > 0) { - memcpy(region.first, m_inputBufferList->data() + copied, region.second); - copied += region.second; - } - else - wecan = false; - - m_buffer->releaseWriteRegion(region); - } - - framesRendered = copied / m_outputFormat.mBytesPerFrame; - } - - if (pullMode && framesRendered > 0) - emit readyRead(); - - return framesRendered; -} - -qint64 QDarwinAudioSourceBuffer::readBytes(char *data, qint64 len) -{ - bool wecan = true; - qint64 bytesCopied = 0; - - len -= len % m_maxPeriodSize; - while (wecan && bytesCopied < len) { - CoreAudioRingBuffer::Region region = m_buffer->acquireReadRegion(len - bytesCopied); - - if (region.second > 0) { - memcpy(data + bytesCopied, region.first, region.second); - bytesCopied += region.second; - } - else - wecan = false; - - m_buffer->releaseReadRegion(region); - } - - return bytesCopied; -} - -void QDarwinAudioSourceBuffer::setFlushDevice(QIODevice *device) -{ - if (m_device != device) - m_device = device; -} - -void QDarwinAudioSourceBuffer::startFlushTimer() -{ - if (m_device != 0) { - // We use the period time for the timer, since that's - // around the buffer size (pre conversion >.>) - m_flushTimer->start(qMax(1, m_periodTime)); - } -} - -void QDarwinAudioSourceBuffer::stopFlushTimer() -{ - m_flushTimer->stop(); -} - -void QDarwinAudioSourceBuffer::flush(bool all) -{ - if (m_device == 0) - return; - - const int used = m_buffer->used(); - const int readSize = all ? used : used - (used % m_maxPeriodSize); - - if (readSize > 0) { - bool wecan = true; - int flushed = 0; - - while (!m_deviceError && wecan && flushed < readSize) { - CoreAudioRingBuffer::Region region = m_buffer->acquireReadRegion(readSize - flushed); - - if (region.second > 0) { - int bytesWritten = m_device->write(region.first, region.second); - if (bytesWritten < 0) { - stopFlushTimer(); - m_deviceError = true; - } - else { - region.second = bytesWritten; - flushed += bytesWritten; - wecan = bytesWritten != 0; - } - } - else - wecan = false; - - m_buffer->releaseReadRegion(region); - } - } -} - -void QDarwinAudioSourceBuffer::reset() -{ - m_buffer->reset(); - m_deviceError = false; -} - -int QDarwinAudioSourceBuffer::available() const -{ - return m_buffer->free(); -} - -int QDarwinAudioSourceBuffer::used() const -{ - return m_buffer->used(); -} - -void QDarwinAudioSourceBuffer::flushBuffer() -{ - flush(); -} - -OSStatus QDarwinAudioSourceBuffer::converterCallback(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData) -{ - Q_UNUSED(inAudioConverter); - Q_UNUSED(outDataPacketDescription); - - QCoreAudioPacketFeeder* feeder = static_cast<QCoreAudioPacketFeeder*>(inUserData); - - if (!feeder->feed(*ioData, *ioNumberDataPackets)) - return as_empty; - - return noErr; -} - -QDarwinAudioSourceDevice::QDarwinAudioSourceDevice(QDarwinAudioSourceBuffer *audioBuffer, QObject *parent) - : QIODevice(parent) - , m_audioBuffer(audioBuffer) -{ - open(QIODevice::ReadOnly | QIODevice::Unbuffered); - connect(m_audioBuffer, SIGNAL(readyRead()), SIGNAL(readyRead())); -} - -qint64 QDarwinAudioSourceDevice::readData(char *data, qint64 len) -{ - return m_audioBuffer->readBytes(data, len); -} - -qint64 QDarwinAudioSourceDevice::writeData(const char *data, qint64 len) -{ - Q_UNUSED(data); - Q_UNUSED(len); - - return 0; -} - -QDarwinAudioSource::QDarwinAudioSource(const QAudioDevice &device) - : m_audioDeviceInfo(device) - , m_isOpen(false) - , m_internalBufferSize(DEFAULT_BUFFER_SIZE) - , m_totalFrames(0) - , m_audioUnit(0) - , m_clockFrequency(CoreAudioUtils::frequency() / 1000) - , m_errorCode(QAudio::NoError) - , m_stateCode(QAudio::StoppedState) - , m_audioBuffer(nullptr) - , m_volume(1.0) -{ - QAudioDevice di = device; - if (di.isNull()) - di = QMediaDevices::defaultAudioInput(); -#if defined(Q_OS_MACOS) - const QCoreAudioDeviceInfo *info = static_cast<const QCoreAudioDeviceInfo *>(di.handle()); - Q_ASSERT(info); - m_audioDeviceId = info->deviceID(); -#endif - m_device = di.id(); -} - - -QDarwinAudioSource::~QDarwinAudioSource() -{ - close(); -} - -bool QDarwinAudioSource::open() -{ -#if defined(Q_OS_IOS) - CoreAudioSessionManager::instance().setCategory(CoreAudioSessionManager::PlayAndRecord, CoreAudioSessionManager::MixWithOthers); - CoreAudioSessionManager::instance().setActive(true); -#endif - - if (m_isOpen) - return true; - - UInt32 size = 0; - - AudioComponentDescription componentDescription; - componentDescription.componentType = kAudioUnitType_Output; -#if defined(Q_OS_OSX) - componentDescription.componentSubType = kAudioUnitSubType_HALOutput; -#else - componentDescription.componentSubType = kAudioUnitSubType_RemoteIO; -#endif - componentDescription.componentManufacturer = kAudioUnitManufacturer_Apple; - componentDescription.componentFlags = 0; - componentDescription.componentFlagsMask = 0; - - AudioComponent component = AudioComponentFindNext(0, &componentDescription); - if (component == 0) { - qWarning() << "QAudioSource: Failed to find Output component"; - return false; - } - - if (AudioComponentInstanceNew(component, &m_audioUnit) != noErr) { - qWarning() << "QAudioSource: Unable to Open Output Component"; - return false; - } - - // Set mode - // switch to input mode - UInt32 enable = 1; - if (AudioUnitSetProperty(m_audioUnit, - kAudioOutputUnitProperty_EnableIO, - kAudioUnitScope_Input, - 1, - &enable, - sizeof(enable)) != noErr) { - qWarning() << "QAudioSource: Unable to switch to input mode (Enable Input)"; - return false; - } - - enable = 0; - if (AudioUnitSetProperty(m_audioUnit, - kAudioOutputUnitProperty_EnableIO, - kAudioUnitScope_Output, - 0, - &enable, - sizeof(enable)) != noErr) { - qWarning() << "QAudioSource: Unable to switch to input mode (Disable output)"; - return false; - } - - // register callback - AURenderCallbackStruct callback; - callback.inputProc = inputCallback; - callback.inputProcRefCon = this; - - if (AudioUnitSetProperty(m_audioUnit, - kAudioOutputUnitProperty_SetInputCallback, - kAudioUnitScope_Global, - 0, - &callback, - sizeof(callback)) != noErr) { - qWarning() << "QAudioSource: Failed to set AudioUnit callback"; - return false; - } - -#if defined(Q_OS_OSX) - //Set Audio Device - if (AudioUnitSetProperty(m_audioUnit, - kAudioOutputUnitProperty_CurrentDevice, - kAudioUnitScope_Global, - 0, - &m_audioDeviceId, - sizeof(m_audioDeviceId)) != noErr) { - qWarning() << "QAudioSource: Unable to use configured device"; - return false; - } -#endif - - //set format - m_streamFormat = CoreAudioUtils::toAudioStreamBasicDescription(m_audioFormat); - -#if defined(Q_OS_OSX) - if (m_audioFormat == m_audioDeviceInfo.preferredFormat()) { -#endif - - m_deviceFormat = m_streamFormat; - AudioUnitSetProperty(m_audioUnit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Output, - 1, - &m_deviceFormat, - sizeof(m_deviceFormat)); -#if defined(Q_OS_OSX) - } else { - size = sizeof(m_deviceFormat); - if (AudioUnitGetProperty(m_audioUnit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, - 1, - &m_deviceFormat, - &size) != noErr) { - qWarning() << "QAudioSource: Unable to retrieve device format"; - return false; - } - - if (AudioUnitSetProperty(m_audioUnit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Output, - 1, - &m_deviceFormat, - sizeof(m_deviceFormat)) != noErr) { - qWarning() << "QAudioSource: Unable to set device format"; - return false; - } - } -#endif - - //setup buffers - UInt32 numberOfFrames; -#if defined(Q_OS_OSX) - size = sizeof(UInt32); - if (AudioUnitGetProperty(m_audioUnit, - kAudioDevicePropertyBufferFrameSize, - kAudioUnitScope_Global, - 0, - &numberOfFrames, - &size) != noErr) { - qWarning() << "QAudioSource: Failed to get audio period size"; - return false; - } - //BUG: numberOfFrames gets ignored after this point - - AudioValueRange bufferRange; - size = sizeof(AudioValueRange); - - if (AudioUnitGetProperty(m_audioUnit, - kAudioDevicePropertyBufferFrameSizeRange, - kAudioUnitScope_Global, - 0, - &bufferRange, - &size) != noErr) { - qWarning() << "QAudioSource: Failed to get audio period size range"; - return false; - } - - // See if the requested buffer size is permissible - numberOfFrames = qBound((UInt32)bufferRange.mMinimum, m_internalBufferSize / m_streamFormat.mBytesPerFrame, (UInt32)bufferRange.mMaximum); - - // Set it back - if (AudioUnitSetProperty(m_audioUnit, - kAudioDevicePropertyBufferFrameSize, - kAudioUnitScope_Global, - 0, - &numberOfFrames, - sizeof(UInt32)) != noErr) { - qWarning() << "QAudioSource: Failed to set audio buffer size"; - return false; - } -#else //iOS - Float32 bufferSize = CoreAudioSessionManager::instance().currentIOBufferDuration(); - bufferSize *= m_streamFormat.mSampleRate; - numberOfFrames = bufferSize; -#endif - - // Now allocate a few buffers to be safe. - m_periodSizeBytes = m_internalBufferSize = numberOfFrames * m_streamFormat.mBytesPerFrame; - - { - QMutexLocker lock(m_audioBuffer); - m_audioBuffer = new QDarwinAudioSourceBuffer(m_internalBufferSize * 4, - m_periodSizeBytes, - m_deviceFormat, - m_streamFormat, - this); - - m_audioBuffer->setVolume(m_volume); - } - m_audioIO = new QDarwinAudioSourceDevice(m_audioBuffer, this); - - // Init - if (AudioUnitInitialize(m_audioUnit) != noErr) { - qWarning() << "QAudioSource: Failed to initialize AudioUnit"; - return false; - } - - m_isOpen = true; - - return m_isOpen; - -} - -void QDarwinAudioSource::close() -{ - stop(); - if (m_audioUnit != 0) { - AudioOutputUnitStop(m_audioUnit); - AudioUnitUninitialize(m_audioUnit); - AudioComponentInstanceDispose(m_audioUnit); - } - - delete m_audioBuffer; - m_audioBuffer = nullptr; - m_isOpen = false; -} - -void QDarwinAudioSource::start(QIODevice *device) -{ - QIODevice* op = device; - - if (!m_audioDeviceInfo.isFormatSupported(m_audioFormat) || !open()) { - m_stateCode = QAudio::StoppedState; - m_errorCode = QAudio::OpenError; - return; - } - - reset(); - { - QMutexLocker lock(m_audioBuffer); - m_audioBuffer->reset(); - m_audioBuffer->setFlushDevice(op); - } - - if (op == 0) - op = m_audioIO; - - // Start - m_totalFrames = 0; - - m_stateCode = QAudio::IdleState; - m_errorCode = QAudio::NoError; - emit stateChanged(m_stateCode); - - audioThreadStart(); -} - - -QIODevice *QDarwinAudioSource::start() -{ - QIODevice* op = 0; - - if (!m_audioDeviceInfo.isFormatSupported(m_audioFormat) || !open()) { - m_stateCode = QAudio::StoppedState; - m_errorCode = QAudio::OpenError; - return m_audioIO; - } - - reset(); - { - QMutexLocker lock(m_audioBuffer); - m_audioBuffer->reset(); - m_audioBuffer->setFlushDevice(op); - } - - if (op == 0) - op = m_audioIO; - - // Start - m_totalFrames = 0; - - m_stateCode = QAudio::IdleState; - m_errorCode = QAudio::NoError; - emit stateChanged(m_stateCode); - - audioThreadStart(); - - return op; -} - - -void QDarwinAudioSource::stop() -{ - QMutexLocker lock(m_audioBuffer); - if (m_stateCode != QAudio::StoppedState) { - audioThreadStop(); - m_audioBuffer->flush(true); - - m_errorCode = QAudio::NoError; - m_stateCode = QAudio::StoppedState; - QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, m_stateCode)); - } -} - - -void QDarwinAudioSource::reset() -{ - QMutexLocker lock(m_audioBuffer); - if (m_stateCode != QAudio::StoppedState) { - audioThreadStop(); - - m_errorCode = QAudio::NoError; - m_stateCode = QAudio::StoppedState; - m_audioBuffer->reset(); - QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, m_stateCode)); - } -} - - -void QDarwinAudioSource::suspend() -{ - QMutexLocker lock(m_audioBuffer); - if (m_stateCode == QAudio::ActiveState || m_stateCode == QAudio::IdleState) { - audioThreadStop(); - - m_errorCode = QAudio::NoError; - m_stateCode = QAudio::SuspendedState; - QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, m_stateCode)); - } -} - - -void QDarwinAudioSource::resume() -{ - QMutexLocker lock(m_audioBuffer); - if (m_stateCode == QAudio::SuspendedState) { - audioThreadStart(); - - m_errorCode = QAudio::NoError; - m_stateCode = QAudio::ActiveState; - QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, m_stateCode)); - } -} - - -qsizetype QDarwinAudioSource::bytesReady() const -{ - QMutexLocker lock(m_audioBuffer); - if (!m_audioBuffer) - return 0; - return m_audioBuffer->used(); -} - -void QDarwinAudioSource::setBufferSize(qsizetype value) -{ - m_internalBufferSize = value; -} - - -qsizetype QDarwinAudioSource::bufferSize() const -{ - return m_internalBufferSize; -} - -qint64 QDarwinAudioSource::processedUSecs() const -{ - return m_totalFrames * 1000000 / m_audioFormat.sampleRate(); -} - -QAudio::Error QDarwinAudioSource::error() const -{ - return m_errorCode; -} - - -QAudio::State QDarwinAudioSource::state() const -{ - return m_stateCode; -} - - -void QDarwinAudioSource::setFormat(const QAudioFormat &format) -{ - if (m_stateCode == QAudio::StoppedState) - m_audioFormat = format; -} - - -QAudioFormat QDarwinAudioSource::format() const -{ - return m_audioFormat; -} - - -void QDarwinAudioSource::setVolume(qreal volume) -{ - QMutexLocker lock(m_audioBuffer); - m_volume = volume; - if (m_audioBuffer) - m_audioBuffer->setVolume(m_volume); -} - - -qreal QDarwinAudioSource::volume() const -{ - return m_volume; -} - -void QDarwinAudioSource::deviceStoppped() -{ - stopTimers(); - emit stateChanged(m_stateCode); -} - -void QDarwinAudioSource::audioThreadStart() -{ - startTimers(); - m_audioThreadState.storeRelaxed(Running); - AudioOutputUnitStart(m_audioUnit); -} - -void QDarwinAudioSource::audioThreadStop() -{ - stopTimers(); - if (m_audioThreadState.testAndSetAcquire(Running, Stopped)) - m_audioBuffer->wait(); -} - -void QDarwinAudioSource::audioDeviceStop() -{ - AudioOutputUnitStop(m_audioUnit); - m_audioThreadState.storeRelaxed(Stopped); - m_audioBuffer->wake(); -} - -void QDarwinAudioSource::audioDeviceActive() -{ - if (m_stateCode == QAudio::IdleState) { - QMutexLocker lock(m_audioBuffer); - m_stateCode = QAudio::ActiveState; - emit stateChanged(m_stateCode); - } -} - -void QDarwinAudioSource::audioDeviceFull() -{ - if (m_stateCode == QAudio::ActiveState) { - QMutexLocker lock(m_audioBuffer); - m_errorCode = QAudio::UnderrunError; - m_stateCode = QAudio::IdleState; - emit stateChanged(m_stateCode); - } -} - -void QDarwinAudioSource::audioDeviceError() -{ - if (m_stateCode == QAudio::ActiveState) { - QMutexLocker lock(m_audioBuffer); - audioDeviceStop(); - - m_errorCode = QAudio::IOError; - m_stateCode = QAudio::StoppedState; - QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection); - } -} - -void QDarwinAudioSource::startTimers() -{ - m_audioBuffer->startFlushTimer(); -} - -void QDarwinAudioSource::stopTimers() -{ - m_audioBuffer->stopFlushTimer(); -} - -OSStatus QDarwinAudioSource::inputCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) -{ - Q_UNUSED(ioData); - - QDarwinAudioSource* d = static_cast<QDarwinAudioSource*>(inRefCon); - - const int threadState = d->m_audioThreadState.loadAcquire(); - if (threadState == Stopped) - d->audioDeviceStop(); - else { - qint64 framesWritten; - - { - QMutexLocker locker(d->m_audioBuffer); - framesWritten = d->m_audioBuffer->renderFromDevice(d->m_audioUnit, - ioActionFlags, - inTimeStamp, - inBusNumber, - inNumberFrames); - } - - if (framesWritten > 0) { - d->m_totalFrames += framesWritten; - d->audioDeviceActive(); - } else if (framesWritten == 0) - d->audioDeviceFull(); - else if (framesWritten < 0) - d->audioDeviceError(); - } - - return noErr; -} - -QT_END_NAMESPACE - -#include "moc_qdarwinaudiosource_p.cpp" diff --git a/src/multimedia/platform/darwin/audio/qdarwinaudiosource_p.h b/src/multimedia/platform/darwin/audio/qdarwinaudiosource_p.h deleted file mode 100644 index 6a14b6a55..000000000 --- a/src/multimedia/platform/darwin/audio/qdarwinaudiosource_p.h +++ /dev/null @@ -1,280 +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$ -** -****************************************************************************/ -#ifndef IOSAUDIOINPUT_H -#define IOSAUDIOINPUT_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qaudiosystem_p.h> -#include <private/qdarwinaudiodevice_p.h> - -#include <AudioUnit/AudioUnit.h> -#include <CoreAudio/CoreAudioTypes.h> -#include <AudioToolbox/AudioToolbox.h> - -#include <QtCore/QIODevice> -#include <QtCore/QWaitCondition> -#include <QtCore/QMutex> -#include <QtCore/QTimer> - -QT_BEGIN_NAMESPACE - -class CoreAudioRingBuffer; -class QCoreAudioPacketFeeder; -class QDarwinAudioSourceBuffer; -class QDarwinAudioSourceDevice; - -class QCoreAudioBufferList -{ -public: - QCoreAudioBufferList(AudioStreamBasicDescription const& streamFormat); - QCoreAudioBufferList(AudioStreamBasicDescription const& streamFormat, char *buffer, int bufferSize); - QCoreAudioBufferList(AudioStreamBasicDescription const& streamFormat, int framesToBuffer); - - ~QCoreAudioBufferList(); - - AudioBufferList* audioBufferList() const { return m_bufferList; } - char *data(int buffer = 0) const; - qint64 bufferSize(int buffer = 0) const; - int frameCount(int buffer = 0) const; - int packetCount(int buffer = 0) const; - int packetSize() const; - void reset(); - -private: - bool m_owner; - int m_dataSize; - AudioStreamBasicDescription m_streamDescription; - AudioBufferList *m_bufferList; -}; - -class QCoreAudioPacketFeeder -{ -public: - QCoreAudioPacketFeeder(QCoreAudioBufferList *abl); - - bool feed(AudioBufferList& dst, UInt32& packetCount); - bool empty() const; - -private: - UInt32 m_totalPackets; - UInt32 m_position; - QCoreAudioBufferList *m_audioBufferList; -}; - -class QDarwinAudioSourceBuffer : public QObject -{ - Q_OBJECT - -public: - QDarwinAudioSourceBuffer(int bufferSize, - int maxPeriodSize, - AudioStreamBasicDescription const& inputFormat, - AudioStreamBasicDescription const& outputFormat, - QObject *parent); - - ~QDarwinAudioSourceBuffer(); - - qreal volume() const; - void setVolume(qreal v); - - qint64 renderFromDevice(AudioUnit audioUnit, - AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames); - - qint64 readBytes(char *data, qint64 len); - - void setFlushDevice(QIODevice *device); - - void startFlushTimer(); - void stopFlushTimer(); - - void flush(bool all = false); - void reset(); - int available() const; - int used() const; - - void lock() { m_mutex.lock(); } - void unlock() { m_mutex.unlock(); } - - void wait() { m_threadFinished.wait(&m_mutex); } - void wake() { m_threadFinished.wakeOne(); } - -signals: - void readyRead(); - -private slots: - void flushBuffer(); - -private: - QMutex m_mutex; - QWaitCondition m_threadFinished; - - bool m_deviceError; - int m_maxPeriodSize; - int m_periodTime; - QIODevice *m_device; - QTimer *m_flushTimer; - CoreAudioRingBuffer *m_buffer; - QCoreAudioBufferList *m_inputBufferList; - AudioConverterRef m_audioConverter; - AudioStreamBasicDescription m_inputFormat; - AudioStreamBasicDescription m_outputFormat; - QAudioFormat m_qFormat; - qreal m_volume; - - const static OSStatus as_empty = 'qtem'; - - // Converter callback - static OSStatus converterCallback(AudioConverterRef inAudioConverter, - UInt32 *ioNumberDataPackets, - AudioBufferList *ioData, - AudioStreamPacketDescription **outDataPacketDescription, - void *inUserData); -}; - -class QDarwinAudioSourceDevice : public QIODevice -{ - Q_OBJECT - -public: - QDarwinAudioSourceDevice(QDarwinAudioSourceBuffer *audioBuffer, QObject *parent); - - qint64 readData(char *data, qint64 len); - qint64 writeData(const char *data, qint64 len); - - bool isSequential() const { return true; } - -private: - QDarwinAudioSourceBuffer *m_audioBuffer; -}; - -class QDarwinAudioSource : public QPlatformAudioSource -{ - Q_OBJECT - -public: - QDarwinAudioSource(const QAudioDevice &device); - ~QDarwinAudioSource(); - - void start(QIODevice *device); - QIODevice *start(); - void stop(); - void reset(); - void suspend(); - void resume(); - qsizetype bytesReady() const; - void setBufferSize(qsizetype value); - qsizetype bufferSize() const; - qint64 processedUSecs() const; - QAudio::Error error() const; - QAudio::State state() const; - void setFormat(const QAudioFormat &format); - QAudioFormat format() const; - - void setVolume(qreal volume); - qreal volume() const; - -private slots: - void deviceStoppped(); - -private: - enum { - Running, - Stopped - }; - - bool open(); - void close(); - - void audioThreadStart(); - void audioThreadStop(); - - void audioDeviceStop(); - void audioDeviceActive(); - void audioDeviceFull(); - void audioDeviceError(); - - void startTimers(); - void stopTimers(); - - // Input callback - static OSStatus inputCallback(void *inRefCon, - AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList *ioData); - - QAudioDevice m_audioDeviceInfo; - QByteArray m_device; - bool m_isOpen; - int m_periodSizeBytes; - int m_internalBufferSize; - qint64 m_totalFrames; - QAudioFormat m_audioFormat; - QIODevice *m_audioIO; - AudioUnit m_audioUnit; -#if defined(Q_OS_OSX) - AudioDeviceID m_audioDeviceId; -#endif - Float64 m_clockFrequency; - QAudio::Error m_errorCode; - QAudio::State m_stateCode; - QDarwinAudioSourceBuffer *m_audioBuffer; - QAtomicInt m_audioThreadState; - AudioStreamBasicDescription m_streamFormat; - AudioStreamBasicDescription m_deviceFormat; - qreal m_volume; -}; - -QT_END_NAMESPACE - -#endif // IOSAUDIOINPUT_H diff --git a/src/multimedia/platform/darwin/avfvideobuffer.mm b/src/multimedia/platform/darwin/avfvideobuffer.mm deleted file mode 100644 index 67cd42d50..000000000 --- a/src/multimedia/platform/darwin/avfvideobuffer.mm +++ /dev/null @@ -1,315 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). -** 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$ -** -****************************************************************************/ - -#include "avfvideobuffer_p.h" -#include <private/qrhi_p.h> -#include <private/qrhimetal_p.h> -#include <private/qrhigles2_p.h> -#include <CoreVideo/CVMetalTexture.h> -#include <CoreVideo/CVMetalTextureCache.h> -#include <QtGui/qopenglcontext.h> - -#include <private/qvideotexturehelper_p.h> - -#import <AVFoundation/AVFoundation.h> -#import <Metal/Metal.h> - -QT_USE_NAMESPACE - -AVFVideoBuffer::AVFVideoBuffer(AVFVideoSinkInterface *sink, CVImageBufferRef buffer) - : QAbstractVideoBuffer(sink->rhi() ? QVideoFrame::RhiTextureHandle : QVideoFrame::NoHandle, sink->rhi()), - sink(sink), - m_buffer(buffer) -{ -// m_type = QVideoFrame::NoHandle; -// qDebug() << "RHI" << rhi; - CVPixelBufferRetain(m_buffer); - m_pixelFormat = fromCVVideoPixelFormat(CVPixelBufferGetPixelFormatType(m_buffer)); -} - -AVFVideoBuffer::~AVFVideoBuffer() -{ - AVFVideoBuffer::unmap(); - for (int i = 0; i < 3; ++i) - if (cvMetalTexture[i]) - CFRelease(cvMetalTexture[i]); -#if defined(Q_OS_MACOS) - if (cvOpenGLTexture) - CVOpenGLTextureRelease(cvOpenGLTexture); -#elif defined(Q_OS_IOS) - if (cvOpenGLESTexture) - CFRelease(cvOpenGLESTexture); -#endif - CVPixelBufferRelease(m_buffer); -} - -AVFVideoBuffer::MapData AVFVideoBuffer::map(QVideoFrame::MapMode mode) -{ - MapData mapData; - - if (m_mode == QVideoFrame::NotMapped) { - CVPixelBufferLockBaseAddress(m_buffer, mode == QVideoFrame::ReadOnly - ? kCVPixelBufferLock_ReadOnly - : 0); - m_mode = mode; - } - - mapData.nPlanes = CVPixelBufferGetPlaneCount(m_buffer); - Q_ASSERT(mapData.nPlanes <= 3); - - if (!mapData.nPlanes) { - // single plane - mapData.bytesPerLine[0] = CVPixelBufferGetBytesPerRow(m_buffer); - mapData.data[0] = static_cast<uchar*>(CVPixelBufferGetBaseAddress(m_buffer)); - mapData.size[0] = CVPixelBufferGetDataSize(m_buffer); - mapData.nPlanes = mapData.data[0] ? 1 : 0; - return mapData; - } - - // For a bi-planar or tri-planar format we have to set the parameters correctly: - for (int i = 0; i < mapData.nPlanes; ++i) { - mapData.bytesPerLine[i] = CVPixelBufferGetBytesPerRowOfPlane(m_buffer, i); - mapData.size[i] = mapData.bytesPerLine[i]*CVPixelBufferGetHeightOfPlane(m_buffer, i); - mapData.data[i] = static_cast<uchar*>(CVPixelBufferGetBaseAddressOfPlane(m_buffer, i)); - } - - return mapData; -} - -void AVFVideoBuffer::unmap() -{ - if (m_mode != QVideoFrame::NotMapped) { - CVPixelBufferUnlockBaseAddress(m_buffer, m_mode == QVideoFrame::ReadOnly - ? kCVPixelBufferLock_ReadOnly - : 0); - m_mode = QVideoFrame::NotMapped; - } -} - -static MTLPixelFormat rhiTextureFormatToMetalFormat(QRhiTexture::Format f) -{ - switch (f) { - default: - case QRhiTexture::UnknownFormat: - return MTLPixelFormatInvalid; - case QRhiTexture::RGBA8: - return MTLPixelFormatRGBA8Unorm; - case QRhiTexture::BGRA8: - return MTLPixelFormatBGRA8Unorm; - case QRhiTexture::R8: - return MTLPixelFormatR8Unorm; - case QRhiTexture::RG8: - return MTLPixelFormatRG8Unorm; - case QRhiTexture::R16: - return MTLPixelFormatR16Unorm; - case QRhiTexture::RG16: - return MTLPixelFormatRG16Unorm; - - case QRhiTexture::RGBA16F: - return MTLPixelFormatRGBA16Float; - case QRhiTexture::RGBA32F: - return MTLPixelFormatRGBA32Float; - case QRhiTexture::R16F: - return MTLPixelFormatR16Float; - case QRhiTexture::R32F: - return MTLPixelFormatR32Float; - } -} - - -quint64 AVFVideoBuffer::textureHandle(int plane) const -{ - auto *textureDescription = QVideoTextureHelper::textureDescription(m_pixelFormat); - int bufferPlanes = CVPixelBufferGetPlaneCount(m_buffer); -// qDebug() << "texture handle" << plane << rhi << (rhi->backend() == QRhi::Metal) << bufferPlanes; - if (plane > 0 && plane >= bufferPlanes) - return 0; - if (!rhi) - return 0; - if (rhi->backend() == QRhi::Metal) { - if (!cvMetalTexture[plane]) { - size_t width = CVPixelBufferGetWidth(m_buffer); - size_t height = CVPixelBufferGetHeight(m_buffer); - width = textureDescription->widthForPlane(width, plane); - height = textureDescription->heightForPlane(height, plane); - - // Create a CoreVideo pixel buffer backed Metal texture image from the texture cache. - auto ret = CVMetalTextureCacheCreateTextureFromImage( - kCFAllocatorDefault, - sink->cvMetalTextureCache, - m_buffer, nil, - rhiTextureFormatToMetalFormat(textureDescription->textureFormat[plane]), - width, height, - plane, - &cvMetalTexture[plane]); - - if (ret != kCVReturnSuccess) - qWarning() << "texture creation failed" << ret; -// auto t = CVMetalTextureGetTexture(cvMetalTexture[plane]); -// qDebug() << " metal texture is" << quint64(cvMetalTexture[plane]) << width << height; -// qDebug() << " " << t.iosurfacePlane << t.pixelFormat << t.width << t.height; - } - - // Get a Metal texture using the CoreVideo Metal texture reference. -// qDebug() << " -> " << quint64(CVMetalTextureGetTexture(cvMetalTexture[plane])); - return cvMetalTexture[plane] ? quint64(CVMetalTextureGetTexture(cvMetalTexture[plane])) : 0; - } else if (rhi->backend() == QRhi::OpenGLES2) { -#ifdef Q_OS_MACOS - CVOpenGLTextureCacheFlush(sink->cvOpenGLTextureCache, 0); - CVReturn cvret; - // Create a CVPixelBuffer-backed OpenGL texture image from the texture cache. - cvret = CVOpenGLTextureCacheCreateTextureFromImage( - kCFAllocatorDefault, - sink->cvOpenGLTextureCache, - m_buffer, - nil, - &cvOpenGLTexture); - - Q_ASSERT(CVOpenGLTextureGetTarget(cvOpenGLTexture) == GL_TEXTURE_RECTANGLE); - // Get an OpenGL texture name from the CVPixelBuffer-backed OpenGL texture image. - return CVOpenGLTextureGetName(cvOpenGLTexture); -#endif -#ifdef Q_OS_IOS - CVOpenGLESTextureCacheFlush(sink->cvOpenGLESTextureCache, 0); - CVReturn cvret; - // Create a CVPixelBuffer-backed OpenGL texture image from the texture cache. - cvret = CVOpenGLESTextureCacheCreateTextureFromImage( - kCFAllocatorDefault, - sink->cvOpenGLESTextureCache, - m_buffer, - nil, - GL_TEXTURE_2D, - GL_RGBA, - CVPixelBufferGetWidth(m_buffer), - CVPixelBufferGetHeight(m_buffer), - GL_RGBA, - GL_UNSIGNED_BYTE, - 0, - &cvOpenGLESTexture); - - // Get an OpenGL texture name from the CVPixelBuffer-backed OpenGL texture image. - return CVOpenGLESTextureGetName(cvOpenGLESTexture); -#endif - } - return 0; -} - - -QVideoFrameFormat::PixelFormat AVFVideoBuffer::fromCVVideoPixelFormat(unsigned avPixelFormat) const -{ -#ifdef Q_OS_MACOS - if (sink->rhi() && sink->rhi()->backend() == QRhi::OpenGLES2) { - if (avPixelFormat == kCVPixelFormatType_32BGRA) - return QVideoFrameFormat::Format_SamplerRect; - else - qWarning() << "Accelerated macOS OpenGL video supports BGRA only, got CV pixel format" << avPixelFormat; - } -#endif - return fromCVPixelFormat(avPixelFormat); -} - -QVideoFrameFormat::PixelFormat AVFVideoBuffer::fromCVPixelFormat(unsigned avPixelFormat) -{ - switch (avPixelFormat) { - case kCVPixelFormatType_32ARGB: - return QVideoFrameFormat::Format_ARGB8888; - case kCVPixelFormatType_32BGRA: - return QVideoFrameFormat::Format_BGRA8888; - case kCVPixelFormatType_420YpCbCr8Planar: - case kCVPixelFormatType_420YpCbCr8PlanarFullRange: - return QVideoFrameFormat::Format_YUV420P; - case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: - case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange: - return QVideoFrameFormat::Format_NV12; - case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange: - case kCVPixelFormatType_420YpCbCr10BiPlanarFullRange: - return QVideoFrameFormat::Format_P010; - case kCVPixelFormatType_422YpCbCr8: - return QVideoFrameFormat::Format_UYVY; - case kCVPixelFormatType_422YpCbCr8_yuvs: - return QVideoFrameFormat::Format_YUYV; - case kCVPixelFormatType_OneComponent8: - return QVideoFrameFormat::Format_Y8; - case q_kCVPixelFormatType_OneComponent16: - return QVideoFrameFormat::Format_Y16; - - case kCMVideoCodecType_JPEG: - case kCMVideoCodecType_JPEG_OpenDML: - return QVideoFrameFormat::Format_Jpeg; - default: - return QVideoFrameFormat::Format_Invalid; - } -} - -bool AVFVideoBuffer::toCVPixelFormat(QVideoFrameFormat::PixelFormat qtFormat, unsigned &conv) -{ - switch (qtFormat) { - case QVideoFrameFormat::Format_ARGB8888: - conv = kCVPixelFormatType_32ARGB; - break; - case QVideoFrameFormat::Format_BGRA8888: - conv = kCVPixelFormatType_32BGRA; - break; - case QVideoFrameFormat::Format_YUV420P: - conv = kCVPixelFormatType_420YpCbCr8PlanarFullRange; - break; - case QVideoFrameFormat::Format_NV12: - conv = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange; - break; - case QVideoFrameFormat::Format_P010: - conv = kCVPixelFormatType_420YpCbCr10BiPlanarFullRange; - break; - case QVideoFrameFormat::Format_UYVY: - conv = kCVPixelFormatType_422YpCbCr8; - break; - case QVideoFrameFormat::Format_YUYV: - conv = kCVPixelFormatType_422YpCbCr8_yuvs; - break; - case QVideoFrameFormat::Format_Y8: - conv = kCVPixelFormatType_OneComponent8; - break; - case QVideoFrameFormat::Format_Y16: - conv = q_kCVPixelFormatType_OneComponent16; - break; - default: - return false; - } - - return true; -} diff --git a/src/multimedia/platform/darwin/avfvideobuffer_p.h b/src/multimedia/platform/darwin/avfvideobuffer_p.h deleted file mode 100644 index 61590115b..000000000 --- a/src/multimedia/platform/darwin/avfvideobuffer_p.h +++ /dev/null @@ -1,112 +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$ -** -****************************************************************************/ - -#ifndef AVFVIDEOBUFFER_H -#define AVFVIDEOBUFFER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtMultimedia/qvideoframe.h> -#include <private/qabstractvideobuffer_p.h> - -#include <QtCore/qobject.h> -#include <QtCore/qmutex.h> -#include <private/avfvideosink_p.h> - -#include <CoreVideo/CVBase.h> -#include <CoreVideo/CVPixelBuffer.h> -#include <CoreVideo/CVImageBuffer.h> - -#import "Metal/Metal.h" -#import "MetalKit/MetalKit.h" - -enum { - // macOS 10.14 doesn't define this pixel format yet - q_kCVPixelFormatType_OneComponent16 = 'L016' -}; - -QT_BEGIN_NAMESPACE - -struct AVFMetalTexture; -class AVFVideoBuffer : public QAbstractVideoBuffer -{ -public: - AVFVideoBuffer(AVFVideoSinkInterface *sink, CVImageBufferRef buffer); - ~AVFVideoBuffer(); - - QVideoFrameFormat::PixelFormat fromCVVideoPixelFormat(unsigned avPixelFormat) const; - - static QVideoFrameFormat::PixelFormat fromCVPixelFormat(unsigned avPixelFormat); - static bool toCVPixelFormat(QVideoFrameFormat::PixelFormat qtFormat, unsigned &conv); - - - QVideoFrame::MapMode mapMode() const { return m_mode; } - MapData map(QVideoFrame::MapMode mode); - void unmap(); - - virtual quint64 textureHandle(int plane) const; - -private: - AVFVideoSinkInterface *sink = nullptr; - - mutable CVMetalTextureRef cvMetalTexture[3] = {}; - -#if defined(Q_OS_MACOS) - mutable CVOpenGLTextureRef cvOpenGLTexture = nullptr; -#elif defined(Q_OS_IOS) - mutable CVOpenGLESTextureRef cvOpenGLESTexture = nullptr; -#endif - - CVImageBufferRef m_buffer = nullptr; - QVideoFrame::MapMode m_mode = QVideoFrame::NotMapped; - QVideoFrameFormat::PixelFormat m_pixelFormat = QVideoFrameFormat::Format_Invalid; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/darwin/avfvideosink.mm b/src/multimedia/platform/darwin/avfvideosink.mm deleted file mode 100644 index ac4367140..000000000 --- a/src/multimedia/platform/darwin/avfvideosink.mm +++ /dev/null @@ -1,212 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). -** 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$ -** -****************************************************************************/ - -#include "avfvideosink_p.h" - -#include <private/qrhi_p.h> -#include <private/qrhimetal_p.h> -#include <private/qrhigles2_p.h> -#include <QtGui/qopenglcontext.h> - -#include <AVFoundation/AVFoundation.h> -#import <QuartzCore/CATransaction.h> - -#if QT_HAS_INCLUDE(<AppKit/AppKit.h>) -#include <AppKit/AppKit.h> -#endif - -#if QT_HAS_INCLUDE(<UIKit/UIKit.h>) -#include <UIKit/UIKit.h> -#endif - -QT_USE_NAMESPACE - -AVFVideoSink::AVFVideoSink(QVideoSink *parent) - : QPlatformVideoSink(parent) -{ -} - -AVFVideoSink::~AVFVideoSink() -{ -} - -void AVFVideoSink::setRhi(QRhi *rhi) -{ - if (m_rhi == rhi) - return; - m_rhi = rhi; - if (m_interface) - m_interface->setRhi(rhi); -} - -void AVFVideoSink::setNativeSize(QSize size) -{ - if (size == nativeSize()) - return; - QPlatformVideoSink::setNativeSize(size); - if (m_interface) - m_interface->nativeSizeChanged(); -} - -void AVFVideoSink::setVideoSinkInterface(AVFVideoSinkInterface *interface) -{ - m_interface = interface; - if (m_interface) - m_interface->setRhi(m_rhi); -} - -AVFVideoSinkInterface::~AVFVideoSinkInterface() -{ - if (m_layer) - [m_layer release]; - freeTextureCaches(); -} - -void AVFVideoSinkInterface::freeTextureCaches() -{ - if (cvMetalTextureCache) - CFRelease(cvMetalTextureCache); - cvMetalTextureCache = nullptr; -#if defined(Q_OS_MACOS) - if (cvOpenGLTextureCache) - CFRelease(cvOpenGLTextureCache); - cvOpenGLTextureCache = nullptr; -#elif defined(Q_OS_IOS) - if (cvOpenGLESTextureCache) - CFRelease(cvOpenGLESTextureCache); - cvOpenGLESTextureCache = nullptr; -#endif -} - -void AVFVideoSinkInterface::setVideoSink(AVFVideoSink *sink) -{ - if (sink == m_sink) - return; - - m_sink = sink; - if (m_sink) { - m_sink->setVideoSinkInterface(this); - reconfigure(); - } -} - -void AVFVideoSinkInterface::setRhi(QRhi *rhi) -{ - if (m_rhi == rhi) - return; - freeTextureCaches(); - m_rhi = rhi; - - if (!rhi) - return; - if (rhi->backend() == QRhi::Metal) { - const auto *metal = static_cast<const QRhiMetalNativeHandles *>(rhi->nativeHandles()); - - // Create a Metal Core Video texture cache from the pixel buffer. - Q_ASSERT(!cvMetalTextureCache); - if (CVMetalTextureCacheCreate( - kCFAllocatorDefault, - nil, - (id<MTLDevice>)metal->dev, - nil, - &cvMetalTextureCache) != kCVReturnSuccess) { - qWarning() << "Metal texture cache creation failed"; - m_rhi = nullptr; - } - } else if (rhi->backend() == QRhi::OpenGLES2) { -#ifdef Q_OS_MACOS - const auto *gl = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles()); - - auto nsGLContext = gl->context->nativeInterface<QNativeInterface::QCocoaGLContext>()->nativeContext(); - auto nsGLPixelFormat = nsGLContext.pixelFormat.CGLPixelFormatObj; - - // Create an OpenGL CoreVideo texture cache from the pixel buffer. - if (CVOpenGLTextureCacheCreate( - kCFAllocatorDefault, - nullptr, - reinterpret_cast<CGLContextObj>(nsGLContext.CGLContextObj), - nsGLPixelFormat, - nil, - &cvOpenGLTextureCache)) { - qWarning() << "OpenGL texture cache creation failed"; - m_rhi = nullptr; - } -#endif -#ifdef Q_OS_IOS - // Create an OpenGL CoreVideo texture cache from the pixel buffer. - if (CVOpenGLESTextureCacheCreate( - kCFAllocatorDefault, - nullptr, - [EAGLContext currentContext], - nullptr, - &cvOpenGLESTextureCache)) { - qWarning() << "OpenGL texture cache creation failed"; - m_rhi = nullptr; - } -#endif - } -} - -void AVFVideoSinkInterface::setLayer(CALayer *layer) -{ - if (layer == m_layer) - return; - - if (m_layer) - [m_layer release]; - - m_layer = layer; - if (m_layer) - [m_layer retain]; - - reconfigure(); -} - -void AVFVideoSinkInterface::updateLayerBounds() -{ - if (!m_layer) - return; - [CATransaction begin]; - [CATransaction setDisableActions: YES]; // disable animation/flicks - m_layer.frame = QRectF(0, 0, nativeSize().width(), nativeSize().height()).toCGRect(); - m_layer.bounds = m_layer.frame; - [CATransaction commit]; -} - -#include "moc_avfvideosink_p.cpp" diff --git a/src/multimedia/platform/darwin/avfvideosink_p.h b/src/multimedia/platform/darwin/avfvideosink_p.h deleted file mode 100644 index d25efdfc7..000000000 --- a/src/multimedia/platform/darwin/avfvideosink_p.h +++ /dev/null @@ -1,129 +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$ -** -****************************************************************************/ - -#ifndef AVFVIDEOWINDOWCONTROL_H -#define AVFVIDEOWINDOWCONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "private/qplatformvideosink_p.h" - -Q_FORWARD_DECLARE_OBJC_CLASS(CALayer); -Q_FORWARD_DECLARE_OBJC_CLASS(AVPlayerLayer); -Q_FORWARD_DECLARE_OBJC_CLASS(AVCaptureVideoPreviewLayer); - -#include <CoreVideo/CVBase.h> -#include <CoreVideo/CVPixelBuffer.h> -#include <CoreVideo/CVImageBuffer.h> - -#import "Metal/Metal.h" -#import "MetalKit/MetalKit.h" - -QT_BEGIN_NAMESPACE - -class AVFVideoSinkInterface; - -class AVFVideoSink : public QPlatformVideoSink -{ - Q_OBJECT - -public: - AVFVideoSink(QVideoSink *parent = nullptr); - virtual ~AVFVideoSink(); - - // QPlatformVideoSink interface -public: - void setRhi(QRhi *rhi) override; - - void setNativeSize(QSize size); - - void setVideoSinkInterface(AVFVideoSinkInterface *interface); - -private: - AVFVideoSinkInterface *m_interface = nullptr; - QRhi *m_rhi = nullptr; -}; - -class AVFVideoSinkInterface -{ -public: - ~AVFVideoSinkInterface(); - - void setVideoSink(AVFVideoSink *sink); - - - virtual void reconfigure() = 0; - virtual void setRhi(QRhi *); - QRhi *rhi() const { return m_rhi; } - - virtual void setLayer(CALayer *layer); - - void updateLayerBounds(); - void nativeSizeChanged() { updateLayerBounds(); } - QSize nativeSize() const { return m_sink->nativeSize(); } - - CVMetalTextureCacheRef cvMetalTextureCache = nullptr; -#if defined(Q_OS_MACOS) - CVOpenGLTextureCacheRef cvOpenGLTextureCache = nullptr; -#elif defined(Q_OS_IOS) - CVOpenGLESTextureCacheRef cvOpenGLESTextureCache = nullptr; -#endif -private: - void freeTextureCaches(); - -protected: - - AVFVideoSink *m_sink = nullptr; - QRhi *m_rhi = nullptr; - CALayer *m_layer = nullptr; -}; - - -QT_END_NAMESPACE - -#endif // AVFVIDEOWINDOWCONTROL_H diff --git a/src/multimedia/platform/darwin/camera/avfaudiopreviewdelegate.mm b/src/multimedia/platform/darwin/camera/avfaudiopreviewdelegate.mm deleted file mode 100644 index 27261b8a0..000000000 --- a/src/multimedia/platform/darwin/camera/avfaudiopreviewdelegate.mm +++ /dev/null @@ -1,134 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "avfcamerasession_p.h" -#include "avfaudiopreviewdelegate_p.h" - -QT_USE_NAMESPACE - -@implementation AVFAudioPreviewDelegate -{ -@private - AVSampleBufferAudioRenderer *m_audioRenderer; - AVFCameraSession *m_session; - AVSampleBufferRenderSynchronizer *m_audioBufferSynchronizer; - dispatch_queue_t m_audioPreviewQueue; -} - -- (id)init -{ - if (self = [super init]) { - m_session = nil; - m_audioBufferSynchronizer = [[AVSampleBufferRenderSynchronizer alloc] init]; - m_audioRenderer = [[AVSampleBufferAudioRenderer alloc] init]; - [m_audioBufferSynchronizer addRenderer:m_audioRenderer]; - return self; - } - return nil; -} - -- (void)captureOutput:(AVCaptureOutput *)captureOutput - didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer - fromConnection:(AVCaptureConnection *)connection -{ - Q_UNUSED(connection); - Q_ASSERT(m_session); - - if (!CMSampleBufferDataIsReady(sampleBuffer)) { - qWarning() << Q_FUNC_INFO << "sample buffer is not ready, skipping."; - return; - } - - CFRetain(sampleBuffer); - - dispatch_async(m_audioPreviewQueue, ^{ - [self renderAudioSampleBuffer:sampleBuffer]; - CFRelease(sampleBuffer); - }); -} - -- (void)renderAudioSampleBuffer:(CMSampleBufferRef)sampleBuffer -{ - Q_ASSERT(sampleBuffer); - Q_ASSERT(m_session); - - if (m_audioBufferSynchronizer && m_audioRenderer) { - [m_audioRenderer enqueueSampleBuffer:sampleBuffer]; - if (m_audioBufferSynchronizer.rate == 0) - [m_audioBufferSynchronizer setRate:1 time:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)]; - } -} - -- (void)resetAudioPreviewDelegate -{ - [m_session->audioOutput() setSampleBufferDelegate:self queue:m_audioPreviewQueue]; -} - -- (void)setupWithCaptureSession: (AVFCameraSession*)session - audioOutputDevice: (NSString*)deviceId -{ - m_session = session; - - m_audioPreviewQueue = dispatch_queue_create("audio-preview-queue", nullptr); - [m_session->audioOutput() setSampleBufferDelegate:self queue:m_audioPreviewQueue]; -#ifdef Q_OS_MACOS - m_audioRenderer.audioOutputDeviceUniqueID = deviceId; -#endif -} - -- (void)setVolume: (float)volume -{ - m_audioRenderer.volume = volume; -} - -- (void)setMuted: (bool)muted -{ - m_audioRenderer.muted = muted; -} - --(void)dealloc { - m_session = nil; - [m_audioRenderer release]; - [m_audioBufferSynchronizer release]; - dispatch_release(m_audioPreviewQueue); - - [super dealloc]; -} - -@end diff --git a/src/multimedia/platform/darwin/camera/avfaudiopreviewdelegate_p.h b/src/multimedia/platform/darwin/camera/avfaudiopreviewdelegate_p.h deleted file mode 100644 index e993637dd..000000000 --- a/src/multimedia/platform/darwin/camera/avfaudiopreviewdelegate_p.h +++ /dev/null @@ -1,76 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef AVFAUDIOPREVIEWDELEGATE_P_H -#define AVFAUDIOPREVIEWDELEGATE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qglobal.h> - -#include <AVFoundation/AVFoundation.h> - -QT_BEGIN_NAMESPACE - -class AVFCameraSession; - -QT_END_NAMESPACE - -@interface AVFAudioPreviewDelegate : NSObject<AVCaptureAudioDataOutputSampleBufferDelegate> - -- (id)init; -- (void)setupWithCaptureSession: (AVFCameraSession*)session - audioOutputDevice: (NSString*)deviceId; -- (void)renderAudioSampleBuffer:(CMSampleBufferRef)sampleBuffer; -- (void)resetAudioPreviewDelegate; -- (void)setVolume: (float)volume; -- (void)setMuted: (bool)muted; - -@end - -#endif // AVFAUDIOPREVIEWDELEGATE_P_H diff --git a/src/multimedia/platform/darwin/camera/avfcamera.mm b/src/multimedia/platform/darwin/camera/avfcamera.mm deleted file mode 100644 index ede3c49eb..000000000 --- a/src/multimedia/platform/darwin/camera/avfcamera.mm +++ /dev/null @@ -1,1004 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). -** 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$ -** -****************************************************************************/ - -#include "avfcameradebug_p.h" -#include "avfcamera_p.h" -#include "avfcamerasession_p.h" -#include "avfcameraservice_p.h" -#include "avfcamerautility_p.h" -#include "avfcamerarenderer_p.h" -#include <qmediacapturesession.h> - -QT_USE_NAMESPACE - - -namespace { - -// All these methods to work with exposure/ISO/SS in custom mode do not support macOS. - -#ifdef Q_OS_IOS - -// Misc. helpers to check values/ranges: - -bool qt_check_ISO_conversion(float isoValue) -{ - if (isoValue >= std::numeric_limits<int>::max()) - return false; - if (isoValue <= std::numeric_limits<int>::min()) - return false; - return true; -} - -bool qt_check_ISO_range(AVCaptureDeviceFormat *format) -{ - // Qt is using int for ISO, AVFoundation - float. It looks like the ISO range - // at the moment can be represented by int (it's max - min > 100, etc.). - Q_ASSERT(format); - if (format.maxISO - format.minISO < 1.) { - // ISO is in some strange units? - return false; - } - - return qt_check_ISO_conversion(format.minISO) - && qt_check_ISO_conversion(format.maxISO); -} - -bool qt_check_exposure_duration(AVCaptureDevice *captureDevice, CMTime duration) -{ - Q_ASSERT(captureDevice); - - AVCaptureDeviceFormat *activeFormat = captureDevice.activeFormat; - if (!activeFormat) { - qDebugCamera() << Q_FUNC_INFO << "failed to obtain capture device format"; - return false; - } - - return CMTimeCompare(duration, activeFormat.minExposureDuration) != -1 - && CMTimeCompare(activeFormat.maxExposureDuration, duration) != -1; -} - -bool qt_check_ISO_value(AVCaptureDevice *captureDevice, int newISO) -{ - Q_ASSERT(captureDevice); - - AVCaptureDeviceFormat *activeFormat = captureDevice.activeFormat; - if (!activeFormat) { - qDebugCamera() << Q_FUNC_INFO << "failed to obtain capture device format"; - return false; - } - - return !(newISO < activeFormat.minISO || newISO > activeFormat.maxISO); -} - -bool qt_exposure_duration_equal(AVCaptureDevice *captureDevice, qreal qDuration) -{ - Q_ASSERT(captureDevice); - const CMTime avDuration = CMTimeMakeWithSeconds(qDuration, captureDevice.exposureDuration.timescale); - return !CMTimeCompare(avDuration, captureDevice.exposureDuration); -} - -bool qt_iso_equal(AVCaptureDevice *captureDevice, int iso) -{ - Q_ASSERT(captureDevice); - return qFuzzyCompare(float(iso), captureDevice.ISO); -} - -bool qt_exposure_bias_equal(AVCaptureDevice *captureDevice, qreal bias) -{ - Q_ASSERT(captureDevice); - return qFuzzyCompare(bias, qreal(captureDevice.exposureTargetBias)); -} - -// Converters: - -bool qt_convert_exposure_mode(AVCaptureDevice *captureDevice, QCamera::ExposureMode mode, - AVCaptureExposureMode &avMode) -{ - // Test if mode supported and convert. - Q_ASSERT(captureDevice); - - if (mode == QCamera::ExposureAuto) { - if ([captureDevice isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) { - avMode = AVCaptureExposureModeContinuousAutoExposure; - return true; - } - } - - if (mode == QCamera::ExposureManual) { - if ([captureDevice isExposureModeSupported:AVCaptureExposureModeCustom]) { - avMode = AVCaptureExposureModeCustom; - return true; - } - } - - return false; -} - -#endif // defined(Q_OS_IOS) - -} // Unnamed namespace. - - -AVFCamera::AVFCamera(QCamera *camera) - : QPlatformCamera(camera) -{ - Q_ASSERT(camera); -} - -AVFCamera::~AVFCamera() -{ -} - -bool AVFCamera::isActive() const -{ - return m_active; -} - -void AVFCamera::setActive(bool active) -{ - if (m_active == active) - return; - if (m_cameraDevice.isNull() && active) - return; - - m_active = active; - if (m_session) - m_session->setActive(active); - - if (active) - updateCameraConfiguration(); - Q_EMIT activeChanged(m_active); -} - -void AVFCamera::setCamera(const QCameraDevice &camera) -{ - if (m_cameraDevice == camera) - return; - m_cameraDevice = camera; - if (m_session) - m_session->setActiveCamera(camera); - setCameraFormat({}); -} - -bool AVFCamera::setCameraFormat(const QCameraFormat &format) -{ - if (!format.isNull() && !m_cameraDevice.videoFormats().contains(format)) - return false; - - m_cameraFormat = format.isNull() ? findBestCameraFormat(m_cameraDevice) : format; - - if (m_session) - m_session->setCameraFormat(m_cameraFormat); - - return true; -} - -void AVFCamera::setCaptureSession(QPlatformMediaCaptureSession *session) -{ - AVFCameraService *captureSession = static_cast<AVFCameraService *>(session); - if (m_service == captureSession) - return; - - if (m_session) { - m_session->disconnect(this); - m_session->setActiveCamera({}); - m_session->setCameraFormat({}); - } - - m_service = captureSession; - if (!m_service) { - m_session = nullptr; - return; - } - - m_session = m_service->session(); - Q_ASSERT(m_session); - - m_session->setActiveCamera(m_cameraDevice); - m_session->setCameraFormat(m_cameraFormat); - m_session->setActive(m_active); -} - -AVCaptureConnection *AVFCamera::videoConnection() const -{ - if (!m_session || !m_session->videoOutput() || !m_session->videoOutput()->videoDataOutput()) - return nil; - - return [m_session->videoOutput()->videoDataOutput() connectionWithMediaType:AVMediaTypeVideo]; -} - -AVCaptureDevice *AVFCamera::device() const -{ - AVCaptureDevice *device = nullptr; - QByteArray deviceId = m_cameraDevice.id(); - if (!deviceId.isEmpty()) { - device = [AVCaptureDevice deviceWithUniqueID: - [NSString stringWithUTF8String: - deviceId.constData()]]; - } - return device; -} - -#ifdef Q_OS_IOS -namespace -{ - -bool qt_focus_mode_supported(QCamera::FocusMode mode) -{ - // Check if QCamera::FocusMode has counterpart in AVFoundation. - - // AVFoundation has 'Manual', 'Auto' and 'Continuous', - // where 'Manual' is actually 'Locked' + writable property 'lensPosition'. - return mode == QCamera::FocusModeAuto - || mode == QCamera::FocusModeManual; -} - -AVCaptureFocusMode avf_focus_mode(QCamera::FocusMode requestedMode) -{ - switch (requestedMode) { - case QCamera::FocusModeHyperfocal: - case QCamera::FocusModeInfinity: - case QCamera::FocusModeManual: - return AVCaptureFocusModeLocked; - default: - return AVCaptureFocusModeContinuousAutoFocus; - } - -} - -} -#endif - -void AVFCamera::setFocusMode(QCamera::FocusMode mode) -{ -#ifdef Q_OS_IOS - if (focusMode() == mode) - return; - - AVCaptureDevice *captureDevice = device(); - if (!captureDevice) { - if (qt_focus_mode_supported(mode)) { - focusModeChanged(mode); - } else { - qDebugCamera() << Q_FUNC_INFO - << "focus mode not supported"; - } - return; - } - - if (isFocusModeSupported(mode)) { - const AVFConfigurationLock lock(captureDevice); - if (!lock) { - qDebugCamera() << Q_FUNC_INFO - << "failed to lock for configuration"; - return; - } - - captureDevice.focusMode = avf_focus_mode(mode); - } else { - qDebugCamera() << Q_FUNC_INFO << "focus mode not supported"; - return; - } - - Q_EMIT focusModeChanged(mode); -#else - Q_UNUSED(mode); -#endif -} - -bool AVFCamera::isFocusModeSupported(QCamera::FocusMode mode) const -{ -#ifdef Q_OS_IOS - AVCaptureDevice *captureDevice = device(); - if (captureDevice) { - AVCaptureFocusMode avMode = avf_focus_mode(mode); - switch (mode) { - case QCamera::FocusModeAuto: - case QCamera::FocusModeHyperfocal: - case QCamera::FocusModeInfinity: - case QCamera::FocusModeManual: - return [captureDevice isFocusModeSupported:avMode]; - case QCamera::FocusModeAutoNear: - Q_FALLTHROUGH(); - case QCamera::FocusModeAutoFar: - return captureDevice.autoFocusRangeRestrictionSupported - && [captureDevice isFocusModeSupported:avMode]; - } - } -#endif - return mode == QCamera::FocusModeAuto; // stupid builtin webcam doesn't do any focus handling, but hey it's usually focused :) -} - -void AVFCamera::setCustomFocusPoint(const QPointF &point) -{ - if (customFocusPoint() == point) - return; - - if (!QRectF(0.f, 0.f, 1.f, 1.f).contains(point)) { - // ### release custom focus point, tell the camera to focus where it wants... - qDebugCamera() << Q_FUNC_INFO << "invalid focus point (out of range)"; - return; - } - - AVCaptureDevice *captureDevice = device(); - if (!captureDevice) - return; - - if ([captureDevice isFocusPointOfInterestSupported]) { - const AVFConfigurationLock lock(captureDevice); - if (!lock) { - qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration"; - return; - } - - const CGPoint focusPOI = CGPointMake(point.x(), point.y()); - [captureDevice setFocusPointOfInterest:focusPOI]; - if (focusMode() != QCamera::FocusModeAuto) - [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus]; - - customFocusPointChanged(point); - } -} - -void AVFCamera::setFocusDistance(float d) -{ -#ifdef Q_OS_IOS - AVCaptureDevice *captureDevice = device(); - if (!captureDevice) - return; - - if (captureDevice.lockingFocusWithCustomLensPositionSupported) { - qDebugCamera() << Q_FUNC_INFO << "Setting custom focus distance not supported\n"; - return; - } - - { - AVFConfigurationLock lock(captureDevice); - if (!lock) { - qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration"; - return; - } - [captureDevice setFocusModeLockedWithLensPosition:d completionHandler:nil]; - } - focusDistanceChanged(d); -#else - Q_UNUSED(d); -#endif -} - -void AVFCamera::updateCameraConfiguration() -{ - AVCaptureDevice *captureDevice = device(); - if (!captureDevice) { - qDebugCamera() << Q_FUNC_INFO << "capture device is nil in 'active' state"; - return; - } - - const AVFConfigurationLock lock(captureDevice); - if (!lock) { - qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration"; - return; - } - - if ([captureDevice isFocusPointOfInterestSupported]) { - auto point = customFocusPoint(); - const CGPoint focusPOI = CGPointMake(point.x(), point.y()); - [captureDevice setFocusPointOfInterest:focusPOI]; - } - -#ifdef Q_OS_IOS - if (focusMode() != QCamera::FocusModeAuto) { - const AVCaptureFocusMode avMode = avf_focus_mode(focusMode()); - if (captureDevice.focusMode != avMode) { - if ([captureDevice isFocusModeSupported:avMode]) { - [captureDevice setFocusMode:avMode]; - } else { - qDebugCamera() << Q_FUNC_INFO << "focus mode not supported"; - } - } - } - - if (!captureDevice.activeFormat) { - qDebugCamera() << Q_FUNC_INFO << "camera state is active, but active format is nil"; - return; - } - - minimumZoomFactorChanged(captureDevice.minAvailableVideoZoomFactor); - maximumZoomFactorChanged(captureDevice.maxAvailableVideoZoomFactor); - - captureDevice.videoZoomFactor = zoomFactor(); - - CMTime newDuration = AVCaptureExposureDurationCurrent; - bool setCustomMode = false; - - float exposureTime = manualExposureTime(); - if (exposureTime > 0 - && !qt_exposure_duration_equal(captureDevice, exposureTime)) { - newDuration = CMTimeMakeWithSeconds(exposureTime, captureDevice.exposureDuration.timescale); - if (!qt_check_exposure_duration(captureDevice, newDuration)) { - qDebugCamera() << Q_FUNC_INFO << "requested exposure duration is out of range"; - return; - } - setCustomMode = true; - } - - float newISO = AVCaptureISOCurrent; - int iso = manualIsoSensitivity(); - if (iso > 0 && !qt_iso_equal(captureDevice, iso)) { - newISO = iso; - if (!qt_check_ISO_value(captureDevice, newISO)) { - qDebugCamera() << Q_FUNC_INFO << "requested ISO value is out of range"; - return; - } - setCustomMode = true; - } - - float bias = exposureCompensation(); - if (bias != 0 && !qt_exposure_bias_equal(captureDevice, bias)) { - // TODO: mixed fpns. - if (bias < captureDevice.minExposureTargetBias || bias > captureDevice.maxExposureTargetBias) { - qDebugCamera() << Q_FUNC_INFO << "exposure compensation value is" - << "out of range"; - return; - } - [captureDevice setExposureTargetBias:bias completionHandler:nil]; - } - - // Setting shutter speed (exposure duration) or ISO values - // also reset exposure mode into Custom. With this settings - // we ignore any attempts to set exposure mode. - - if (setCustomMode) { - [captureDevice setExposureModeCustomWithDuration:newDuration - ISO:newISO - completionHandler:nil]; - return; - } - - QCamera::ExposureMode qtMode = exposureMode(); - AVCaptureExposureMode avMode = AVCaptureExposureModeContinuousAutoExposure; - if (!qt_convert_exposure_mode(captureDevice, qtMode, avMode)) { - qDebugCamera() << Q_FUNC_INFO << "requested exposure mode is not supported"; - return; - } - - captureDevice.exposureMode = avMode; -#endif - - isFlashSupported = isFlashAutoSupported = false; - isTorchSupported = isTorchAutoSupported = false; - - if (captureDevice.hasFlash) { - if ([captureDevice isFlashModeSupported:AVCaptureFlashModeOn]) - isFlashSupported = true; - if ([captureDevice isFlashModeSupported:AVCaptureFlashModeAuto]) - isFlashAutoSupported = true; - } - - if (captureDevice.hasTorch) { - if ([captureDevice isTorchModeSupported:AVCaptureTorchModeOn]) - isTorchSupported = true; - if ([captureDevice isTorchModeSupported:AVCaptureTorchModeAuto]) - isTorchAutoSupported = true; - } - - applyFlashSettings(); - flashReadyChanged(isFlashSupported); -} - -void AVFCamera::updateCameraProperties() -{ - QCamera::Features features; - AVCaptureDevice *captureDevice = device(); - -#ifdef Q_OS_IOS - features = QCamera::Feature::ColorTemperature | QCamera::Feature::ExposureCompensation | - QCamera::Feature::IsoSensitivity | QCamera::Feature::ManualExposureTime; - - if (captureDevice && [captureDevice isLockingFocusWithCustomLensPositionSupported]) - features |= QCamera::Feature::FocusDistance; -#endif - - if (captureDevice && [captureDevice isFocusPointOfInterestSupported]) - features |= QCamera::Feature::CustomFocusPoint; - - supportedFeaturesChanged(features); -} - -void AVFCamera::zoomTo(float factor, float rate) -{ - Q_UNUSED(factor); - Q_UNUSED(rate); - -#ifdef Q_OS_IOS - if (zoomFactor() == factor) - return; - - AVCaptureDevice *captureDevice = device(); - if (!captureDevice || !captureDevice.activeFormat) - return; - - factor = qBound(captureDevice.minAvailableVideoZoomFactor, factor, captureDevice.maxAvailableVideoZoomFactor); - - const AVFConfigurationLock lock(captureDevice); - if (!lock) { - qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration"; - return; - } - - if (rate < 0) - captureDevice.videoZoomFactor = factor; - else - [AVCaptureDevice rampToVideoZoomFactor:factor withRate:rate]; -#endif -} - -void AVFCamera::setFlashMode(QCamera::FlashMode mode) -{ - if (flashMode() == mode) - return; - - if (isActive() && !isFlashModeSupported(mode)) { - qDebugCamera() << Q_FUNC_INFO << "unsupported mode" << mode; - return; - } - - flashModeChanged(mode); - - if (!isActive()) - return; - - applyFlashSettings(); -} - -bool AVFCamera::isFlashModeSupported(QCamera::FlashMode mode) const -{ - if (mode == QCamera::FlashOff) - return true; - else if (mode == QCamera::FlashOn) - return isFlashSupported; - else //if (mode == QCamera::FlashAuto) - return isFlashAutoSupported; -} - -bool AVFCamera::isFlashReady() const -{ - if (!isActive()) - return false; - - AVCaptureDevice *captureDevice = device(); - if (!captureDevice) - return false; - - if (!captureDevice.hasFlash) - return false; - - if (!isFlashModeSupported(flashMode())) - return false; - -#ifdef Q_OS_IOS - // AVCaptureDevice's docs: - // "The flash may become unavailable if, for example, - // the device overheats and needs to cool off." - return [captureDevice isFlashAvailable]; -#endif - - return true; -} - -void AVFCamera::setTorchMode(QCamera::TorchMode mode) -{ - if (torchMode() == mode) - return; - - if (isActive() && !isTorchModeSupported(mode)) { - qDebugCamera() << Q_FUNC_INFO << "unsupported torch mode" << mode; - return; - } - - torchModeChanged(mode); - - if (!isActive()) - return; - - applyFlashSettings(); -} - -bool AVFCamera::isTorchModeSupported(QCamera::TorchMode mode) const -{ - if (mode == QCamera::TorchOff) - return true; - else if (mode == QCamera::TorchOn) - return isTorchSupported; - else //if (mode == QCamera::TorchAuto) - return isTorchAutoSupported; -} - -void AVFCamera::setExposureMode(QCamera::ExposureMode qtMode) -{ -#ifdef Q_OS_IOS - if (qtMode != QCamera::ExposureAuto && qtMode != QCamera::ExposureManual) { - qDebugCamera() << Q_FUNC_INFO << "exposure mode not supported"; - return; - } - - AVCaptureDevice *captureDevice = device(); - if (!captureDevice) { - exposureModeChanged(qtMode); - return; - } - - AVCaptureExposureMode avMode = AVCaptureExposureModeContinuousAutoExposure; - if (!qt_convert_exposure_mode(captureDevice, qtMode, avMode)) { - qDebugCamera() << Q_FUNC_INFO << "exposure mode not supported"; - return; - } - - const AVFConfigurationLock lock(captureDevice); - if (!lock) { - qDebugCamera() << Q_FUNC_INFO << "failed to lock a capture device" - << "for configuration"; - return; - } - - [captureDevice setExposureMode:avMode]; - exposureModeChanged(qtMode); -#else - Q_UNUSED(qtMode); -#endif -} - -bool AVFCamera::isExposureModeSupported(QCamera::ExposureMode mode) const -{ - if (mode == QCamera::ExposureAuto) - return true; - if (mode != QCamera::ExposureManual) - return false; - - if (@available(macOS 10.15, *)) { - AVCaptureDevice *captureDevice = device(); - return captureDevice && [captureDevice isExposureModeSupported:AVCaptureExposureModeCustom]; - } - - return false; -} - -void AVFCamera::applyFlashSettings() -{ - Q_ASSERT(isActive()); - - AVCaptureDevice *captureDevice = device(); - if (!captureDevice) { - qDebugCamera() << Q_FUNC_INFO << "no capture device found"; - return; - } - - - const AVFConfigurationLock lock(captureDevice); - - if (captureDevice.hasFlash) { - auto mode = flashMode(); - if (mode == QCamera::FlashOff) { - captureDevice.flashMode = AVCaptureFlashModeOff; - } else { -#ifdef Q_OS_IOS - if (![captureDevice isFlashAvailable]) { - qDebugCamera() << Q_FUNC_INFO << "flash is not available at the moment"; - return; - } -#endif - if (mode == QCamera::FlashOn) - captureDevice.flashMode = AVCaptureFlashModeOn; - else if (mode == QCamera::FlashAuto) - captureDevice.flashMode = AVCaptureFlashModeAuto; - } - } - - if (captureDevice.hasTorch) { - auto mode = torchMode(); - if (mode == QCamera::TorchOff) { - captureDevice.torchMode = AVCaptureTorchModeOff; - } else { -#ifdef Q_OS_IOS - if (![captureDevice isTorchAvailable]) { - qDebugCamera() << Q_FUNC_INFO << "torch is not available at the moment"; - return; - } -#endif - if (mode == QCamera::TorchOn) - captureDevice.torchMode = AVCaptureTorchModeOn; - else if (mode == QCamera::TorchAuto) - captureDevice.torchMode = AVCaptureTorchModeAuto; - } - } -} - - -void AVFCamera::setExposureCompensation(float bias) -{ -#ifdef Q_OS_IOS - AVCaptureDevice *captureDevice = device(); - if (!captureDevice) { - exposureCompensationChanged(bias); - return; - } - - bias = qBound(captureDevice.minExposureTargetBias, bias, captureDevice.maxExposureTargetBias); - - const AVFConfigurationLock lock(captureDevice); - if (!lock) { - qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration"; - return; - } - - [captureDevice setExposureTargetBias:bias completionHandler:nil]; - exposureCompensationChanged(bias); -#else - Q_UNUSED(bias); -#endif -} - -void AVFCamera::setManualExposureTime(float value) -{ -#ifdef Q_OS_IOS - if (value < 0) { - setExposureMode(QCamera::ExposureAuto); - return; - } - - AVCaptureDevice *captureDevice = device(); - if (!captureDevice) { - exposureTimeChanged(value); - return; - } - - const CMTime newDuration = CMTimeMakeWithSeconds(value, captureDevice.exposureDuration.timescale); - if (!qt_check_exposure_duration(captureDevice, newDuration)) { - qDebugCamera() << Q_FUNC_INFO << "shutter speed value is out of range"; - return; - } - - const AVFConfigurationLock lock(captureDevice); - if (!lock) { - qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration"; - return; - } - - // Setting the shutter speed (exposure duration in Apple's terms, - // since there is no shutter actually) will also reset - // exposure mode into custom mode. - [captureDevice setExposureModeCustomWithDuration:newDuration - ISO:AVCaptureISOCurrent - completionHandler:nil]; - - exposureTimeChanged(value); - -#else - Q_UNUSED(value); -#endif -} - -float AVFCamera::exposureTime() const -{ -#ifdef Q_OS_IOS - AVCaptureDevice *captureDevice = device(); - if (!captureDevice) - return -1.; - auto duration = captureDevice.exposureDuration; - return CMTimeGetSeconds(duration); -#else - return -1; -#endif -} - -#ifdef Q_OS_IOS -namespace { - -void avf_convert_white_balance_mode(QCamera::WhiteBalanceMode qtMode, - AVCaptureWhiteBalanceMode &avMode) -{ - if (qtMode == QCamera::WhiteBalanceAuto) - avMode = AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance; - else - avMode = AVCaptureWhiteBalanceModeLocked; -} - -bool avf_set_white_balance_mode(AVCaptureDevice *captureDevice, - AVCaptureWhiteBalanceMode avMode) -{ - Q_ASSERT(captureDevice); - - const bool lock = [captureDevice lockForConfiguration:nil]; - if (!lock) { - qDebug() << "Failed to lock a capture device for configuration\n"; - return false; - } - - captureDevice.whiteBalanceMode = avMode; - [captureDevice unlockForConfiguration]; - return true; -} - -bool avf_convert_temp_and_tint_to_wb_gains(AVCaptureDevice *captureDevice, - float temp, float tint, AVCaptureWhiteBalanceGains &wbGains) -{ - Q_ASSERT(captureDevice); - - AVCaptureWhiteBalanceTemperatureAndTintValues wbTTValues = { - .temperature = temp, - .tint = tint - }; - wbGains = [captureDevice deviceWhiteBalanceGainsForTemperatureAndTintValues:wbTTValues]; - - if (wbGains.redGain >= 1.0 && wbGains.redGain <= captureDevice.maxWhiteBalanceGain - && wbGains.greenGain >= 1.0 && wbGains.greenGain <= captureDevice.maxWhiteBalanceGain - && wbGains.blueGain >= 1.0 && wbGains.blueGain <= captureDevice.maxWhiteBalanceGain) - return true; - - return false; -} - -bool avf_set_white_balance_gains(AVCaptureDevice *captureDevice, - AVCaptureWhiteBalanceGains wbGains) -{ - const bool lock = [captureDevice lockForConfiguration:nil]; - if (!lock) { - qDebug() << "Failed to lock a capture device for configuration\n"; - return false; - } - - [captureDevice setWhiteBalanceModeLockedWithDeviceWhiteBalanceGains:wbGains - completionHandler:nil]; - [captureDevice unlockForConfiguration]; - return true; -} - -} - -bool AVFCamera::isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const -{ - if (mode == QCamera::WhiteBalanceAuto) - return true; - AVCaptureDevice *captureDevice = device(); - if (!captureDevice) - return false; - return [captureDevice isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeLocked]; -} - -void AVFCamera::setWhiteBalanceMode(QCamera::WhiteBalanceMode mode) -{ - if (!isWhiteBalanceModeSupported(mode)) - return; - - AVCaptureDevice *captureDevice = device(); - Q_ASSERT(captureDevice); - - const AVFConfigurationLock lock(captureDevice); - if (!lock) { - qDebugCamera() << Q_FUNC_INFO << "failed to lock a capture device" - << "for configuration"; - return; - } - - AVCaptureWhiteBalanceMode avMode; - avf_convert_white_balance_mode(mode, avMode); - avf_set_white_balance_mode(captureDevice, avMode); - - if (mode == QCamera::WhiteBalanceAuto || mode == QCamera::WhiteBalanceManual) { - whiteBalanceModeChanged(mode); - return; - } - - const int colorTemp = colorTemperatureForWhiteBalance(mode); - AVCaptureWhiteBalanceGains wbGains; - if (avf_convert_temp_and_tint_to_wb_gains(captureDevice, colorTemp, 0., wbGains) - && avf_set_white_balance_gains(captureDevice, wbGains)) - whiteBalanceModeChanged(mode); -} - -void AVFCamera::setColorTemperature(int colorTemp) -{ - if (colorTemp == 0) { - colorTemperatureChanged(colorTemp); - return; - } - - AVCaptureDevice *captureDevice = device(); - if (!captureDevice || ![captureDevice isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeLocked]) - return; - - const AVFConfigurationLock lock(captureDevice); - if (!lock) { - qDebugCamera() << Q_FUNC_INFO << "failed to lock a capture device" - << "for configuration"; - return; - } - - AVCaptureWhiteBalanceGains wbGains; - if (avf_convert_temp_and_tint_to_wb_gains(captureDevice, colorTemp, 0., wbGains) - && avf_set_white_balance_gains(captureDevice, wbGains)) - colorTemperatureChanged(colorTemp); -} -#endif - -void AVFCamera::setManualIsoSensitivity(int value) -{ -#ifdef Q_OS_IOS - if (value < 0) { - setExposureMode(QCamera::ExposureAuto); - return; - } - - AVCaptureDevice *captureDevice = device(); - if (!captureDevice) { - isoSensitivityChanged(value); - return; - } - - if (!qt_check_ISO_value(captureDevice, value)) { - qDebugCamera() << Q_FUNC_INFO << "ISO value is out of range"; - return; - } - - const AVFConfigurationLock lock(captureDevice); - if (!lock) { - qDebugCamera() << Q_FUNC_INFO << "failed to lock a capture device" - << "for configuration"; - return; - } - - // Setting the ISO will also reset - // exposure mode to the custom mode. - [captureDevice setExposureModeCustomWithDuration:AVCaptureExposureDurationCurrent - ISO:value - completionHandler:nil]; - - isoSensitivityChanged(value); -#else - Q_UNUSED(value); -#endif -} - -int AVFCamera::isoSensitivity() const -{ - return manualIsoSensitivity(); -} - - -#include "moc_avfcamera_p.cpp" diff --git a/src/multimedia/platform/darwin/camera/avfcamera_p.h b/src/multimedia/platform/darwin/camera/avfcamera_p.h deleted file mode 100644 index 950d0131b..000000000 --- a/src/multimedia/platform/darwin/camera/avfcamera_p.h +++ /dev/null @@ -1,139 +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$ -** -****************************************************************************/ - -#ifndef AVFCAMERA_H -#define AVFCAMERA_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qobject.h> - -#include <private/qplatformcamera_p.h> - -Q_FORWARD_DECLARE_OBJC_CLASS(AVCaptureDeviceFormat); -Q_FORWARD_DECLARE_OBJC_CLASS(AVCaptureConnection); -Q_FORWARD_DECLARE_OBJC_CLASS(AVCaptureDevice); - -QT_BEGIN_NAMESPACE - -class AVFCameraSession; -class AVFCameraService; -class AVFCameraSession; - -class AVFCamera : public QPlatformCamera -{ -Q_OBJECT -public: - AVFCamera(QCamera *camera); - ~AVFCamera(); - - bool isActive() const override; - void setActive(bool activce) override; - - void setCamera(const QCameraDevice &camera) override; - bool setCameraFormat(const QCameraFormat &format) override; - - void setCaptureSession(QPlatformMediaCaptureSession *) override; - - void setFocusMode(QCamera::FocusMode mode) override; - bool isFocusModeSupported(QCamera::FocusMode mode) const override; - - void setCustomFocusPoint(const QPointF &point) override; - - void setFocusDistance(float d) override; - void zoomTo(float factor, float rate) override; - - void setFlashMode(QCamera::FlashMode mode) override; - bool isFlashModeSupported(QCamera::FlashMode mode) const override; - bool isFlashReady() const override; - - void setTorchMode(QCamera::TorchMode mode) override; - bool isTorchModeSupported(QCamera::TorchMode mode) const override; - - void setExposureMode(QCamera::ExposureMode) override; - bool isExposureModeSupported(QCamera::ExposureMode mode) const override; - - void setExposureCompensation(float bias) override; - void setManualIsoSensitivity(int value) override; - virtual int isoSensitivity() const override; - void setManualExposureTime(float value) override; - virtual float exposureTime() const override; - -#ifdef Q_OS_IOS - // not supported on macOS - bool isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const override; - void setWhiteBalanceMode(QCamera::WhiteBalanceMode /*mode*/) override; - void setColorTemperature(int /*temperature*/) override; -#endif - - AVCaptureConnection *videoConnection() const; - AVCaptureDevice *device() const; - -private: - void updateCameraConfiguration(); - void updateCameraProperties(); - void applyFlashSettings(); - - friend class AVFCameraSession; - AVFCameraService *m_service = nullptr; - AVFCameraSession *m_session = nullptr; - - QCameraDevice m_cameraDevice; - QCameraFormat m_cameraFormat; - - bool m_active = false; - - bool isFlashSupported = false; - bool isFlashAutoSupported = false; - bool isTorchSupported = false; - bool isTorchAutoSupported = false; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/darwin/camera/avfcameradebug_p.h b/src/multimedia/platform/darwin/camera/avfcameradebug_p.h deleted file mode 100644 index 616e53d99..000000000 --- a/src/multimedia/platform/darwin/camera/avfcameradebug_p.h +++ /dev/null @@ -1,68 +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$ -** -****************************************************************************/ - -#ifndef AVFDEBUG_H -#define AVFDEBUG_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qtmultimediaglobal.h" - -#include <QtCore/qdebug.h> - -QT_USE_NAMESPACE - -//#define AVF_DEBUG_CAMERA - -#ifdef AVF_DEBUG_CAMERA -#define qDebugCamera qDebug -#else -#define qDebugCamera QT_NO_QDEBUG_MACRO -#endif - -#endif diff --git a/src/multimedia/platform/darwin/camera/avfcamerarenderer.mm b/src/multimedia/platform/darwin/camera/avfcamerarenderer.mm deleted file mode 100644 index 016ee3333..000000000 --- a/src/multimedia/platform/darwin/camera/avfcamerarenderer.mm +++ /dev/null @@ -1,298 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). -** 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$ -** -****************************************************************************/ - -#include "private/qabstractvideobuffer_p.h" -#include "avfcamerarenderer_p.h" -#include "avfcamerasession_p.h" -#include "avfcameraservice_p.h" -#include "avfcameradebug_p.h" -#include "avfcamera_p.h" -#include <private/avfvideosink_p.h> -#include <private/avfvideobuffer_p.h> -#include "qvideosink.h" - -#import <AVFoundation/AVFoundation.h> - -#ifdef Q_OS_IOS -#include <QtGui/qopengl.h> -#endif - -#include <private/qabstractvideobuffer_p.h> - -#include <QtMultimedia/qvideoframeformat.h> - -QT_USE_NAMESPACE - -@interface AVFCaptureFramesDelegate : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate> - -- (AVFCaptureFramesDelegate *) initWithRenderer:(AVFCameraRenderer*)renderer; - -- (void) captureOutput:(AVCaptureOutput *)captureOutput - didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer - fromConnection:(AVCaptureConnection *)connection; - -@end - -@implementation AVFCaptureFramesDelegate -{ -@private - AVFCameraRenderer *m_renderer; -} - -- (AVFCaptureFramesDelegate *) initWithRenderer:(AVFCameraRenderer*)renderer -{ - if (!(self = [super init])) - return nil; - - self->m_renderer = renderer; - return self; -} - -- (void)captureOutput:(AVCaptureOutput *)captureOutput - didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer - fromConnection:(AVCaptureConnection *)connection -{ - Q_UNUSED(connection); - Q_UNUSED(captureOutput); - - // NB: on iOS captureOutput/connection can be nil (when recording a video - - // avfmediaassetwriter). - - CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); - - int width = CVPixelBufferGetWidth(imageBuffer); - int height = CVPixelBufferGetHeight(imageBuffer); - QVideoFrameFormat::PixelFormat format = - AVFVideoBuffer::fromCVPixelFormat(CVPixelBufferGetPixelFormatType(imageBuffer)); - if (format == QVideoFrameFormat::Format_Invalid) - return; - - QVideoFrame frame(new AVFVideoBuffer(m_renderer, imageBuffer), - QVideoFrameFormat(QSize(width, height), format)); - - m_renderer->syncHandleViewfinderFrame(frame); -} - -@end - -AVFCameraRenderer::AVFCameraRenderer(QObject *parent) - : QObject(parent) -{ - m_viewfinderFramesDelegate = [[AVFCaptureFramesDelegate alloc] initWithRenderer:this]; - connect(&m_orientationHandler, &QVideoOutputOrientationHandler::orientationChanged, - this, &AVFCameraRenderer::deviceOrientationChanged); -} - -AVFCameraRenderer::~AVFCameraRenderer() -{ - [m_cameraSession->captureSession() removeOutput:m_videoDataOutput]; - [m_viewfinderFramesDelegate release]; - if (m_delegateQueue) - dispatch_release(m_delegateQueue); -#ifdef Q_OS_IOS - if (m_textureCache) - CFRelease(m_textureCache); -#endif -} - -void AVFCameraRenderer::reconfigure() -{ - QMutexLocker lock(&m_vfMutex); - - // ### This is a hack, need to use a reliable way to determine the size and not use the preview layer - if (m_layer) - m_sink->setNativeSize(QSize(m_layer.bounds.size.width, m_layer.bounds.size.height)); - nativeSizeChanged(); - deviceOrientationChanged(); -} - -void AVFCameraRenderer::configureAVCaptureSession(AVFCameraSession *cameraSession) -{ - m_cameraSession = cameraSession; - connect(m_cameraSession, SIGNAL(readyToConfigureConnections()), - this, SLOT(updateCaptureConnection())); - - m_needsHorizontalMirroring = false; - - m_videoDataOutput = [[[AVCaptureVideoDataOutput alloc] init] autorelease]; - - // Configure video output - m_delegateQueue = dispatch_queue_create("vf_queue", nullptr); - [m_videoDataOutput - setSampleBufferDelegate:m_viewfinderFramesDelegate - queue:m_delegateQueue]; - - [m_cameraSession->captureSession() addOutput:m_videoDataOutput]; -} - -void AVFCameraRenderer::updateCaptureConnection() -{ - AVCaptureConnection *connection = [m_videoDataOutput connectionWithMediaType:AVMediaTypeVideo]; - if (connection == nil || !m_cameraSession->videoCaptureDevice()) - return; - - // Frames of front-facing cameras should be mirrored horizontally (it's the default when using - // AVCaptureVideoPreviewLayer but not with AVCaptureVideoDataOutput) - if (connection.isVideoMirroringSupported) - connection.videoMirrored = m_cameraSession->videoCaptureDevice().position == AVCaptureDevicePositionFront; - - // If the connection does't support mirroring, we'll have to do it ourselves - m_needsHorizontalMirroring = !connection.isVideoMirrored - && m_cameraSession->videoCaptureDevice().position == AVCaptureDevicePositionFront; - - deviceOrientationChanged(); -} - -void AVFCameraRenderer::deviceOrientationChanged(int angle) -{ - AVCaptureConnection *connection = [m_videoDataOutput connectionWithMediaType:AVMediaTypeVideo]; - if (connection == nil || !m_cameraSession->videoCaptureDevice()) - return; - - if (!connection.supportsVideoOrientation) - return; - - if (angle < 0) - angle = m_orientationHandler.currentOrientation(); - - AVCaptureVideoOrientation orientation = AVCaptureVideoOrientationPortrait; - switch (angle) { - default: - break; - case 90: - orientation = AVCaptureVideoOrientationLandscapeRight; - break; - case 180: - // this keeps the last orientation, don't do anything - return; - case 270: - orientation = AVCaptureVideoOrientationLandscapeLeft; - break; - } - - connection.videoOrientation = orientation; -} - -//can be called from non main thread -void AVFCameraRenderer::syncHandleViewfinderFrame(const QVideoFrame &frame) -{ - Q_EMIT newViewfinderFrame(frame); - - QMutexLocker lock(&m_vfMutex); - - if (!m_lastViewfinderFrame.isValid()) { - static QMetaMethod handleViewfinderFrameSlot = metaObject()->method( - metaObject()->indexOfMethod("handleViewfinderFrame()")); - - handleViewfinderFrameSlot.invoke(this, Qt::QueuedConnection); - } - - m_lastViewfinderFrame = frame; -} - -AVCaptureVideoDataOutput *AVFCameraRenderer::videoDataOutput() const -{ - return m_videoDataOutput; -} - -AVFCaptureFramesDelegate *AVFCameraRenderer::captureDelegate() const -{ - return m_viewfinderFramesDelegate; -} - -void AVFCameraRenderer::resetCaptureDelegate() const -{ - [m_videoDataOutput setSampleBufferDelegate:m_viewfinderFramesDelegate queue:m_delegateQueue]; -} - -void AVFCameraRenderer::handleViewfinderFrame() -{ - QVideoFrame frame; - { - QMutexLocker lock(&m_vfMutex); - frame = m_lastViewfinderFrame; - m_lastViewfinderFrame = QVideoFrame(); - } - - if (m_sink && frame.isValid()) { - // ### pass format to surface - QVideoFrameFormat format = frame.surfaceFormat(); - if (m_needsHorizontalMirroring) - format.setMirrored(true); - - m_sink->setVideoFrame(frame); - } -} - -void AVFCameraRenderer::setPixelFormat(const QVideoFrameFormat::PixelFormat pixelFormat) -{ - // Default to 32ARGB/32BGRA pixel formats on the viewfinder, in case the requested - // format can't be used (shouldn't happen unless the developers sets a wrong camera - // format on the camera). - unsigned avPixelFormat = kCVPixelFormatType_32ARGB; -#ifdef Q_OS_IOS - avPixelFormat = kCVPixelFormatType_32BGRA; -#endif - if (!AVFVideoBuffer::toCVPixelFormat(pixelFormat, avPixelFormat)) - qWarning() << "QCamera::setCameraFormat: couldn't convert requested pixel format, using ARGB32"; - qDebug() << "setPixelFormat" << pixelFormat << Qt::hex << avPixelFormat; - - bool isSupported = false; - NSArray *supportedPixelFormats = m_videoDataOutput.availableVideoCVPixelFormatTypes; - for (NSNumber *currentPixelFormat in supportedPixelFormats) - { - if ([currentPixelFormat unsignedIntValue] == avPixelFormat) { - isSupported = true; - break; - } - } - - if (isSupported) { - NSDictionary* outputSettings = @{ - (NSString *)kCVPixelBufferPixelFormatTypeKey: [NSNumber numberWithUnsignedInt:avPixelFormat], - (NSString *)kCVPixelBufferMetalCompatibilityKey: @true - }; - m_videoDataOutput.videoSettings = outputSettings; - } else { - qWarning() << "QCamera::setCameraFormat: requested pixel format not supported. Did you use a camera format from another camera?"; - } -} - -#include "moc_avfcamerarenderer_p.cpp" - diff --git a/src/multimedia/platform/darwin/camera/avfcamerarenderer_p.h b/src/multimedia/platform/darwin/camera/avfcamerarenderer_p.h deleted file mode 100644 index 9455f593d..000000000 --- a/src/multimedia/platform/darwin/camera/avfcamerarenderer_p.h +++ /dev/null @@ -1,129 +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$ -** -****************************************************************************/ - -#ifndef AVFCAMERARENDERER_H -#define AVFCAMERARENDERER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qobject.h> -#include <QtMultimedia/qvideoframe.h> -#include <QtCore/qmutex.h> -#include <private/avfvideosink_p.h> -#include <private/qvideooutputorientationhandler_p.h> - -#include <CoreVideo/CVBase.h> -#include <CoreVideo/CVPixelBuffer.h> -#include <CoreVideo/CVImageBuffer.h> -#ifdef Q_OS_IOS -#include <CoreVideo/CVOpenGLESTexture.h> -#include <CoreVideo/CVOpenGLESTextureCache.h> -#endif - -#include <dispatch/dispatch.h> - -Q_FORWARD_DECLARE_OBJC_CLASS(AVFCaptureFramesDelegate); -Q_FORWARD_DECLARE_OBJC_CLASS(AVCaptureVideoDataOutput); - -QT_BEGIN_NAMESPACE - -class AVFCameraSession; -class AVFCameraService; -class AVFCameraRenderer; -class AVFVideoSink; - -class AVFCameraRenderer : public QObject, public AVFVideoSinkInterface -{ -Q_OBJECT -public: - AVFCameraRenderer(QObject *parent = nullptr); - ~AVFCameraRenderer(); - - void reconfigure() override; - - void configureAVCaptureSession(AVFCameraSession *cameraSession); - void syncHandleViewfinderFrame(const QVideoFrame &frame); - - AVCaptureVideoDataOutput *videoDataOutput() const; - - AVFCaptureFramesDelegate *captureDelegate() const; - void resetCaptureDelegate() const; - - void setPixelFormat(const QVideoFrameFormat::PixelFormat format); - -Q_SIGNALS: - void newViewfinderFrame(const QVideoFrame &frame); - -private Q_SLOTS: - void handleViewfinderFrame(); - void updateCaptureConnection(); -public Q_SLOTS: - void deviceOrientationChanged(int angle = -1); - -private: - AVFCaptureFramesDelegate *m_viewfinderFramesDelegate = nullptr; - AVFCameraSession *m_cameraSession = nullptr; - AVCaptureVideoDataOutput *m_videoDataOutput = nullptr; - - bool m_needsHorizontalMirroring = false; - -#ifdef Q_OS_IOS - CVOpenGLESTextureCacheRef m_textureCache = nullptr; -#endif - - QVideoFrame m_lastViewfinderFrame; - QMutex m_vfMutex; - dispatch_queue_t m_delegateQueue; - QVideoOutputOrientationHandler m_orientationHandler; - - friend class CVImageVideoBuffer; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/darwin/camera/avfcameraservice.mm b/src/multimedia/platform/darwin/camera/avfcameraservice.mm deleted file mode 100644 index 2d37a4117..000000000 --- a/src/multimedia/platform/darwin/camera/avfcameraservice.mm +++ /dev/null @@ -1,201 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). -** 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$ -** -****************************************************************************/ - -#include <QtCore/qvariant.h> -#include <QtCore/qdebug.h> - -#include "avfcameraservice_p.h" -#include "avfcamera_p.h" -#include "avfcamerasession_p.h" -#include "avfimagecapture_p.h" -#include "avfcamerarenderer_p.h" -#include "avfimagecapture_p.h" -#include "avfmediaencoder_p.h" -#include <qmediadevices.h> -#include <private/qplatformaudioinput_p.h> -#include <private/qplatformaudiooutput_p.h> -#include <qaudioinput.h> -#include <qaudiooutput.h> - -QT_USE_NAMESPACE - -AVFCameraService::AVFCameraService() -{ - m_session = new AVFCameraSession(this); -} - -AVFCameraService::~AVFCameraService() -{ - if (m_session) - delete m_session; -} - -QPlatformCamera *AVFCameraService::camera() -{ - return m_cameraControl; -} - -void AVFCameraService::setCamera(QPlatformCamera *camera) -{ - AVFCamera *control = static_cast<AVFCamera *>(camera); - if (m_cameraControl == control) - return; - - if (m_cameraControl) - m_cameraControl->setCaptureSession(nullptr); - - m_cameraControl = control; - emit cameraChanged(); - - if (m_cameraControl) - m_cameraControl->setCaptureSession(this); -} - -QPlatformImageCapture *AVFCameraService::imageCapture() -{ - return m_imageCaptureControl; -} - -void AVFCameraService::setImageCapture(QPlatformImageCapture *imageCapture) -{ - AVFImageCapture *control = static_cast<AVFImageCapture *>(imageCapture); - if (m_imageCaptureControl == control) - return; - - if (m_imageCaptureControl) - m_imageCaptureControl->setCaptureSession(nullptr); - - m_imageCaptureControl = control; - if (m_imageCaptureControl) - m_imageCaptureControl->setCaptureSession(this); -} - -QPlatformMediaEncoder *AVFCameraService::mediaEncoder() -{ - return m_encoder; -} - -void AVFCameraService::setMediaEncoder(QPlatformMediaEncoder *encoder) -{ - AVFMediaEncoder *control = static_cast<AVFMediaEncoder *>(encoder); - if (m_encoder == control) - return; - - if (m_encoder) - m_encoder->setCaptureSession(nullptr); - - m_encoder = control; - if (m_encoder) - m_encoder->setCaptureSession(this); - - emit encoderChanged(); -} - -void AVFCameraService::setAudioInput(QPlatformAudioInput *input) -{ - if (m_audioInput == input) - return; - if (m_audioInput) - m_audioInput->q->disconnect(this); - - m_audioInput = input; - - if (input) { - connect(m_audioInput->q, &QAudioInput::destroyed, this, &AVFCameraService::audioInputDestroyed); - connect(m_audioInput->q, &QAudioInput::deviceChanged, this, &AVFCameraService::audioInputChanged); - connect(m_audioInput->q, &QAudioInput::mutedChanged, this, &AVFCameraService::setAudioInputMuted); - connect(m_audioInput->q, &QAudioInput::volumeChanged, this, &AVFCameraService::setAudioInputVolume); - } - audioInputChanged(); -} - -void AVFCameraService::setAudioOutput(QPlatformAudioOutput *output) -{ - if (m_audioOutput == output) - return; - if (m_audioOutput) - m_audioOutput->q->disconnect(this); - - m_audioOutput = output; - - if (m_audioOutput) { - connect(m_audioOutput->q, &QAudioOutput::destroyed, this, &AVFCameraService::audioOutputDestroyed); - connect(m_audioOutput->q, &QAudioOutput::deviceChanged, this, &AVFCameraService::audioOutputChanged); - connect(m_audioOutput->q, &QAudioOutput::mutedChanged, this, &AVFCameraService::setAudioOutputMuted); - connect(m_audioOutput->q, &QAudioOutput::volumeChanged, this, &AVFCameraService::setAudioOutputVolume); - } - audioOutputChanged(); -} - -void AVFCameraService::audioInputChanged() -{ - m_session->updateAudioInput(); -} - -void AVFCameraService::audioOutputChanged() -{ - m_session->updateAudioOutput(); -} - -void AVFCameraService::setAudioInputMuted(bool muted) -{ - m_session->setAudioInputMuted(muted); -} - -void AVFCameraService::setAudioInputVolume(float volume) -{ - m_session->setAudioInputVolume(volume); -} - -void AVFCameraService::setAudioOutputMuted(bool muted) -{ - m_session->setAudioOutputMuted(muted); -} - -void AVFCameraService::setAudioOutputVolume(float volume) -{ - m_session->setAudioOutputVolume(volume); -} - -void AVFCameraService::setVideoPreview(QVideoSink *sink) -{ - m_session->setVideoSink(sink); -} - -#include "moc_avfcameraservice_p.cpp" diff --git a/src/multimedia/platform/darwin/camera/avfcameraservice_p.h b/src/multimedia/platform/darwin/camera/avfcameraservice_p.h deleted file mode 100644 index 462c957b4..000000000 --- a/src/multimedia/platform/darwin/camera/avfcameraservice_p.h +++ /dev/null @@ -1,120 +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$ -** -****************************************************************************/ - -#ifndef AVFCAMERASERVICE_H -#define AVFCAMERASERVICE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qobject.h> -#include <QtCore/qset.h> -#include <private/qplatformmediacapture_p.h> - -Q_FORWARD_DECLARE_OBJC_CLASS(AVCaptureDevice); - -QT_BEGIN_NAMESPACE -class QPlatformCamera; -class QPlatformMediaEncoder; -class AVFCamera; -class AVFImageCapture; -class AVFCameraSession; -class AVFMediaEncoder; - -class AVFCameraService : public QPlatformMediaCaptureSession -{ - Q_OBJECT -public: - AVFCameraService(); - ~AVFCameraService(); - - QPlatformCamera *camera() override; - void setCamera(QPlatformCamera *camera) override; - - QPlatformImageCapture *imageCapture() override; - void setImageCapture(QPlatformImageCapture *imageCapture) override; - - QPlatformMediaEncoder *mediaEncoder() override; - void setMediaEncoder(QPlatformMediaEncoder *encoder) override; - - void setAudioInput(QPlatformAudioInput *) override; - void setAudioOutput(QPlatformAudioOutput *) override; - - void setVideoPreview(QVideoSink *sink) override; - - AVFCameraSession *session() const { return m_session; } - AVFCamera *avfCameraControl() const { return m_cameraControl; } - AVFMediaEncoder *recorderControl() const { return m_encoder; } - AVFImageCapture *avfImageCaptureControl() const { return m_imageCaptureControl; } - - QPlatformAudioInput *audioInput() { return m_audioInput; } - QPlatformAudioOutput *audioOutput() { return m_audioOutput; } - -public Q_SLOTS: - void audioInputDestroyed() { setAudioInput(nullptr); } - void audioInputChanged(); - void audioOutputDestroyed() { setAudioOutput(nullptr); } - void audioOutputChanged(); - - void setAudioInputMuted(bool muted); - void setAudioInputVolume(float volume); - void setAudioOutputMuted(bool muted); - void setAudioOutputVolume(float volume); - -private: - QPlatformAudioInput *m_audioInput = nullptr; - QPlatformAudioOutput *m_audioOutput = nullptr; - - AVFCameraSession *m_session = nullptr; - AVFCamera *m_cameraControl = nullptr; - AVFMediaEncoder *m_encoder = nullptr; - AVFImageCapture *m_imageCaptureControl = nullptr; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/darwin/camera/avfcamerasession.mm b/src/multimedia/platform/darwin/camera/avfcamerasession.mm deleted file mode 100644 index 4a6d4ddeb..000000000 --- a/src/multimedia/platform/darwin/camera/avfcamerasession.mm +++ /dev/null @@ -1,521 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). -** 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$ -** -****************************************************************************/ - -#include "avfcameradebug_p.h" -#include "avfcamerasession_p.h" -#include "avfcameraservice_p.h" -#include "avfcamera_p.h" -#include "avfcamerarenderer_p.h" -#include "avfimagecapture_p.h" -#include "avfmediaencoder_p.h" -#include "avfcamerautility_p.h" -#include <private/avfvideosink_p.h> - -#include <CoreFoundation/CoreFoundation.h> -#include <Foundation/Foundation.h> - -#include <QtCore/qdatetime.h> -#include <QtCore/qurl.h> -#include <QtCore/qelapsedtimer.h> - -#include <private/qplatformaudioinput_p.h> -#include <private/qplatformaudiooutput_p.h> - -#include <QtCore/qdebug.h> - -QT_USE_NAMESPACE - -@interface AVFCameraSessionObserver : NSObject - -- (AVFCameraSessionObserver *) initWithCameraSession:(AVFCameraSession*)session; -- (void) processRuntimeError:(NSNotification *)notification; -- (void) processSessionStarted:(NSNotification *)notification; -- (void) processSessionStopped:(NSNotification *)notification; - -@end - -@implementation AVFCameraSessionObserver -{ -@private - AVFCameraSession *m_session; - AVCaptureSession *m_captureSession; -} - -- (AVFCameraSessionObserver *) initWithCameraSession:(AVFCameraSession*)session -{ - if (!(self = [super init])) - return nil; - - self->m_session = session; - self->m_captureSession = session->captureSession(); - - [m_captureSession retain]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(processRuntimeError:) - name:AVCaptureSessionRuntimeErrorNotification - object:m_captureSession]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(processSessionStarted:) - name:AVCaptureSessionDidStartRunningNotification - object:m_captureSession]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(processSessionStopped:) - name:AVCaptureSessionDidStopRunningNotification - object:m_captureSession]; - - return self; -} - -- (void) dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self - name:AVCaptureSessionRuntimeErrorNotification - object:m_captureSession]; - - [[NSNotificationCenter defaultCenter] removeObserver:self - name:AVCaptureSessionDidStartRunningNotification - object:m_captureSession]; - - [[NSNotificationCenter defaultCenter] removeObserver:self - name:AVCaptureSessionDidStopRunningNotification - object:m_captureSession]; - [m_captureSession release]; - [super dealloc]; -} - -- (void) processRuntimeError:(NSNotification *)notification -{ - Q_UNUSED(notification); - QMetaObject::invokeMethod(m_session, "processRuntimeError", Qt::AutoConnection); -} - -- (void) processSessionStarted:(NSNotification *)notification -{ - Q_UNUSED(notification); - QMetaObject::invokeMethod(m_session, "processSessionStarted", Qt::AutoConnection); -} - -- (void) processSessionStopped:(NSNotification *)notification -{ - Q_UNUSED(notification); - QMetaObject::invokeMethod(m_session, "processSessionStopped", Qt::AutoConnection); -} - -@end - -AVFCameraSession::AVFCameraSession(AVFCameraService *service, QObject *parent) - : QObject(parent) - , m_service(service) - , m_defaultCodec(0) -{ - m_captureSession = [[AVCaptureSession alloc] init]; - m_observer = [[AVFCameraSessionObserver alloc] initWithCameraSession:this]; -} - -AVFCameraSession::~AVFCameraSession() -{ - if (m_videoInput) { - [m_captureSession removeInput:m_videoInput]; - [m_videoInput release]; - } - - if (m_audioInput) { - [m_captureSession removeInput:m_audioInput]; - [m_audioInput release]; - } - - if (m_audioOutput) { - [m_captureSession removeOutput:m_audioOutput]; - [m_audioOutput release]; - } - - if (m_videoOutput) - delete m_videoOutput; - - [m_observer release]; - [m_captureSession release]; -} - -void AVFCameraSession::setActiveCamera(const QCameraDevice &info) -{ - if (m_activeCameraDevice != info) { - m_activeCameraDevice = info; - - auto recorder = m_service->recorderControl(); - if (recorder && recorder->state() == QMediaRecorder::RecordingState) - recorder->toggleRecord(false); - - [m_captureSession beginConfiguration]; - - attachVideoInputDevice(); - if (!m_activeCameraDevice.isNull() && !m_videoOutput) { - setVideoOutput(new AVFCameraRenderer(this)); - connect(m_videoOutput, &AVFCameraRenderer::newViewfinderFrame, - this, &AVFCameraSession::newViewfinderFrame); - updateVideoOutput(); - } - m_videoOutput->deviceOrientationChanged(); - - [m_captureSession commitConfiguration]; - - if (recorder && recorder->state() == QMediaRecorder::RecordingState) - recorder->toggleRecord(true); - } -} - -void AVFCameraSession::setCameraFormat(const QCameraFormat &format) -{ - if (m_cameraFormat == format) - return; - m_cameraFormat = format; - - AVCaptureDevice *captureDevice = videoCaptureDevice(); - if (!captureDevice) - return; - - AVCaptureDeviceFormat *newFormat = qt_convert_to_capture_device_format(captureDevice, format); - if (newFormat) { - qt_set_active_format(captureDevice, newFormat, false); - if (m_videoOutput) - m_videoOutput->setPixelFormat(format.pixelFormat()); - } -} - -void AVFCameraSession::setVideoOutput(AVFCameraRenderer *output) -{ - if (m_videoOutput == output) - return; - - delete m_videoOutput; - m_videoOutput = output; - if (output) - output->configureAVCaptureSession(this); -} - -void AVFCameraSession::addAudioCapture() -{ - if (!m_audioOutput) { - m_audioOutput = [[AVCaptureAudioDataOutput alloc] init]; - if (m_audioOutput && [m_captureSession canAddOutput:m_audioOutput]) { - [m_captureSession addOutput:m_audioOutput]; - } else { - qWarning() << Q_FUNC_INFO << "failed to add audio output"; - } - } -} - -AVCaptureDevice *AVFCameraSession::videoCaptureDevice() const -{ - if (m_videoInput) - return m_videoInput.device; - - return nullptr; -} - -AVCaptureDevice *AVFCameraSession::audioCaptureDevice() const -{ - if (m_audioInput) - return m_audioInput.device; - - return nullptr; -} - -void AVFCameraSession::setAudioInputVolume(float volume) -{ - m_inputVolume = volume; - - if (m_inputMuted) - volume = 0.0; - -#ifdef Q_OS_MACOS - AVCaptureConnection *audioInputConnection = [m_audioOutput connectionWithMediaType:AVMediaTypeAudio]; - NSArray<AVCaptureAudioChannel *> *audioChannels = audioInputConnection.audioChannels; - if (audioChannels) { - for (AVCaptureAudioChannel *channel in audioChannels) { - channel.volume = volume; - } - } -#endif -} - -void AVFCameraSession::setAudioInputMuted(bool muted) -{ - m_inputMuted = muted; - setAudioInputVolume(m_inputVolume); -} - -void AVFCameraSession::setAudioOutputVolume(float volume) -{ - if (m_audioPreviewDelegate) - [m_audioPreviewDelegate setVolume:volume]; -} - -void AVFCameraSession::setAudioOutputMuted(bool muted) -{ - if (m_audioPreviewDelegate) - [m_audioPreviewDelegate setMuted:muted]; -} - -bool AVFCameraSession::isActive() const -{ - return m_active; -} - -void AVFCameraSession::setActive(bool active) -{ - if (m_active == active) - return; - - m_active = active; - - qDebugCamera() << Q_FUNC_INFO << m_active << " -> " << active; - - if (active) { - if (!m_activeCameraDevice.isNull()) { - Q_EMIT readyToConfigureConnections(); - m_defaultCodec = 0; - defaultCodec(); - } - - applyImageEncoderSettings(); - - // According to the doc, the capture device must be locked before - // startRunning to prevent the format we set to be overridden by the - // session preset. - [videoCaptureDevice() lockForConfiguration:nil]; - [m_captureSession startRunning]; - [videoCaptureDevice() unlockForConfiguration]; - } else { - [m_captureSession stopRunning]; - } -} - -void AVFCameraSession::processRuntimeError() -{ - qWarning() << tr("Runtime camera error"); - m_active = false; - Q_EMIT error(QCamera::CameraError, tr("Runtime camera error")); -} - -void AVFCameraSession::processSessionStarted() -{ - qDebugCamera() << Q_FUNC_INFO; - if (!m_active) { - m_active = true; - Q_EMIT activeChanged(m_active); - } -} - -void AVFCameraSession::processSessionStopped() -{ - qDebugCamera() << Q_FUNC_INFO; - if (m_active) { - m_active = false; - Q_EMIT activeChanged(m_active); - } -} - -AVCaptureDevice *AVFCameraSession::createVideoCaptureDevice() -{ - AVCaptureDevice *device = nullptr; - - QByteArray deviceId = m_activeCameraDevice.id(); - if (!deviceId.isEmpty()) { - device = [AVCaptureDevice deviceWithUniqueID: - [NSString stringWithUTF8String: - deviceId.constData()]]; - } - - return device; -} - -AVCaptureDevice *AVFCameraSession::createAudioCaptureDevice() -{ - AVCaptureDevice *device = nullptr; - - QByteArray deviceId = m_service->audioInput() ? m_service->audioInput()->device.id() - : QByteArray(); - if (!deviceId.isEmpty()) - device = [AVCaptureDevice deviceWithUniqueID: [NSString stringWithUTF8String:deviceId.constData()]]; - - return device; -} - -void AVFCameraSession::attachVideoInputDevice() -{ - if (m_videoInput) { - [m_captureSession removeInput:m_videoInput]; - [m_videoInput release]; - m_videoInput = nullptr; - } - - AVCaptureDevice *videoDevice = createVideoCaptureDevice(); - if (videoDevice) { - NSError *error = nil; - m_videoInput = [AVCaptureDeviceInput - deviceInputWithDevice:videoDevice - error:&error]; - - if (!m_videoInput) { - qWarning() << "Failed to create video device input"; - } else { - if ([m_captureSession canAddInput:m_videoInput]) { - [m_videoInput retain]; - [m_captureSession addInput:m_videoInput]; - } else { - qWarning() << "Failed to connect video device input"; - m_activeCameraDevice = QCameraDevice(); - } - } - } else { - m_activeCameraDevice = QCameraDevice(); - } -} - -void AVFCameraSession::attachAudioInputDevice() -{ - if (m_audioInput) { - [m_captureSession removeInput:m_audioInput]; - [m_audioInput release]; - m_audioInput = nullptr; - } - - AVCaptureDevice *audioDevice = createAudioCaptureDevice(); - if (audioDevice) { - NSError *error = nil; - m_audioInput = [AVCaptureDeviceInput - deviceInputWithDevice:audioDevice - error:&error]; - - if (!m_audioInput) { - qWarning() << "Failed to create audio device input"; - } else { - if ([m_captureSession canAddInput:m_audioInput]) { - [m_audioInput retain]; - [m_captureSession addInput:m_audioInput]; - } else { - qWarning() << "Failed to connect audio device input"; - } - } - } -} - -bool AVFCameraSession::applyImageEncoderSettings() -{ - if (AVFImageCapture *control = m_service->avfImageCaptureControl()) - return control->applySettings(); - - return false; -} - -FourCharCode AVFCameraSession::defaultCodec() -{ - if (!m_defaultCodec) { - if (AVCaptureDevice *device = videoCaptureDevice()) { - AVCaptureDeviceFormat *format = device.activeFormat; - if (!format || !format.formatDescription) - return m_defaultCodec; - m_defaultCodec = CMVideoFormatDescriptionGetCodecType(format.formatDescription); - } - } - return m_defaultCodec; -} - -void AVFCameraSession::setVideoSink(QVideoSink *sink) -{ - auto *videoSink = sink ? static_cast<AVFVideoSink *>(sink->platformVideoSink()) : nullptr; - - if (m_videoSink == videoSink) - return; - - m_videoSink = videoSink; - - updateVideoOutput(); -} - -void AVFCameraSession::updateAudioInput() -{ - auto recorder = m_service->recorderControl(); - if (recorder && recorder->state() == QMediaRecorder::RecordingState) - recorder->toggleRecord(false); - - [m_captureSession beginConfiguration]; - if (m_audioOutput) { - AVCaptureConnection *lastConnection = [m_audioOutput connectionWithMediaType:AVMediaTypeAudio]; - [m_captureSession removeConnection:lastConnection]; - } - attachAudioInputDevice(); - if (m_audioInput) - addAudioCapture(); - [m_captureSession commitConfiguration]; - - if (recorder && recorder->state() == QMediaRecorder::RecordingState) - recorder->toggleRecord(true); -} - -void AVFCameraSession::updateAudioOutput() -{ - QByteArray deviceId = m_service->audioOutput() - ? m_service->audioOutput()->device.id() - : QByteArray(); - - [m_audioPreviewDelegate release]; - m_audioPreviewDelegate = nil; - if (!deviceId.isEmpty()) { - m_audioPreviewDelegate = [[AVFAudioPreviewDelegate alloc] init]; - [m_audioPreviewDelegate setupWithCaptureSession:this - audioOutputDevice:[NSString stringWithUTF8String: - deviceId.constData()]]; - } -} - -void AVFCameraSession::updateVideoOutput() -{ - if (m_videoOutput) { - m_videoOutput->setVideoSink(m_videoSink); - if (m_videoSink) { - AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:m_captureSession]; - m_videoOutput->setLayer(previewLayer); - } - } -} - -#include "moc_avfcamerasession_p.cpp" diff --git a/src/multimedia/platform/darwin/camera/avfcamerasession_p.h b/src/multimedia/platform/darwin/camera/avfcamerasession_p.h deleted file mode 100644 index e461b809c..000000000 --- a/src/multimedia/platform/darwin/camera/avfcamerasession_p.h +++ /dev/null @@ -1,162 +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$ -** -****************************************************************************/ - -#ifndef AVFCAMERASESSION_H -#define AVFCAMERASESSION_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qmutex.h> -#include <QtMultimedia/qcamera.h> -#include <QVideoFrame> -#include <qcameradevice.h> -#include "avfaudiopreviewdelegate_p.h" - -#import <AVFoundation/AVFoundation.h> - -@class AVFCameraSessionObserver; - -QT_BEGIN_NAMESPACE - -class AVFCamera; -class AVFCameraService; -class AVFCameraRenderer; -class AVFVideoSink; -class QVideoSink; - -class AVFCameraSession : public QObject -{ - Q_OBJECT -public: - AVFCameraSession(AVFCameraService *service, QObject *parent = nullptr); - ~AVFCameraSession(); - - QCameraDevice activecameraDevice() const { return m_activeCameraDevice; } - void setActiveCamera(const QCameraDevice &info); - - void setCameraFormat(const QCameraFormat &format); - - AVFCameraRenderer *videoOutput() const { return m_videoOutput; } - AVCaptureAudioDataOutput *audioOutput() const { return m_audioOutput; } - AVFAudioPreviewDelegate *audioPreviewDelegate() const { return m_audioPreviewDelegate; } - - - AVCaptureSession *captureSession() const { return m_captureSession; } - AVCaptureDevice *videoCaptureDevice() const; - AVCaptureDevice *audioCaptureDevice() const; - - bool isActive() const; - - FourCharCode defaultCodec(); - - AVCaptureDeviceInput *videoInput() const { return m_videoInput; } - AVCaptureDeviceInput *audioInput() const { return m_audioInput; } - - void setVideoSink(QVideoSink *sink); - - void updateAudioInput(); - void updateAudioOutput(); - -public Q_SLOTS: - void setActive(bool active); - - void setAudioInputVolume(float volume); - void setAudioInputMuted(bool muted); - void setAudioOutputMuted(bool muted); - void setAudioOutputVolume(float volume); - - void processRuntimeError(); - void processSessionStarted(); - void processSessionStopped(); - -Q_SIGNALS: - void readyToConfigureConnections(); - void activeChanged(bool); - void error(int error, const QString &errorString); - void newViewfinderFrame(const QVideoFrame &frame); - -private: - void setVideoOutput(AVFCameraRenderer *output); - void updateVideoOutput(); - - void addAudioCapture(); - - AVCaptureDevice *createVideoCaptureDevice(); - AVCaptureDevice *createAudioCaptureDevice(); - void attachVideoInputDevice(); - void attachAudioInputDevice(); - - bool applyImageEncoderSettings(); - - QCameraDevice m_activeCameraDevice; - QCameraFormat m_cameraFormat; - - AVFCameraService *m_service; - AVCaptureSession *m_captureSession; - AVFCameraSessionObserver *m_observer; - - AVFCameraRenderer *m_videoOutput = nullptr; - AVFVideoSink *m_videoSink = nullptr; - - AVCaptureDeviceInput *m_videoInput = nullptr; - AVCaptureDeviceInput *m_audioInput = nullptr; - - AVCaptureAudioDataOutput *m_audioOutput = nullptr; - AVFAudioPreviewDelegate *m_audioPreviewDelegate = nullptr; - - bool m_active = false; - - float m_inputVolume = 1.0; - bool m_inputMuted = false; - - FourCharCode m_defaultCodec; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/darwin/camera/avfcamerautility.mm b/src/multimedia/platform/darwin/camera/avfcamerautility.mm deleted file mode 100644 index 444162523..000000000 --- a/src/multimedia/platform/darwin/camera/avfcamerautility.mm +++ /dev/null @@ -1,714 +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$ -** -****************************************************************************/ - -#include "avfcamerautility_p.h" -#include "avfcameradebug_p.h" - -#include <QtCore/qvector.h> -#include <QtCore/qpair.h> -#include <private/qmultimediautils_p.h> -#include "private/avfvideobuffer_p.h" - -#include <functional> -#include <algorithm> -#include <limits> -#include <tuple> - -QT_BEGIN_NAMESPACE - -AVFPSRange qt_connection_framerates(AVCaptureConnection *videoConnection) -{ - Q_ASSERT(videoConnection); - - AVFPSRange newRange; - // "The value in the videoMinFrameDuration is equivalent to the reciprocal - // of the maximum framerate, the value in the videoMaxFrameDuration is equivalent - // to the reciprocal of the minimum framerate." - if (videoConnection.supportsVideoMinFrameDuration) { - const CMTime cmMin = videoConnection.videoMinFrameDuration; - if (CMTimeCompare(cmMin, kCMTimeInvalid)) { // Has some non-default value: - if (const Float64 minSeconds = CMTimeGetSeconds(cmMin)) - newRange.second = 1. / minSeconds; - } - } - - if (videoConnection.supportsVideoMaxFrameDuration) { - const CMTime cmMax = videoConnection.videoMaxFrameDuration; - if (CMTimeCompare(cmMax, kCMTimeInvalid)) { - if (const Float64 maxSeconds = CMTimeGetSeconds(cmMax)) - newRange.first = 1. / maxSeconds; - } - } - - return newRange; -} - -namespace { - -inline bool qt_area_sane(const QSize &size) -{ - return !size.isNull() && size.isValid() - && std::numeric_limits<int>::max() / size.width() >= size.height(); -} - -template <template <typename...> class Comp> // std::less or std::greater (or std::equal_to) -struct ByResolution -{ - bool operator() (AVCaptureDeviceFormat *f1, AVCaptureDeviceFormat *f2)const - { - Q_ASSERT(f1 && f2); - const QSize r1(qt_device_format_resolution(f1)); - const QSize r2(qt_device_format_resolution(f2)); - // use std::tuple for lexicograpical sorting: - const Comp<std::tuple<int, int>> op = {}; - return op(std::make_tuple(r1.width(), r1.height()), - std::make_tuple(r2.width(), r2.height())); - } -}; - -struct FormatHasNoFPSRange : std::unary_function<AVCaptureDeviceFormat *, bool> -{ - bool operator() (AVCaptureDeviceFormat *format) - { - Q_ASSERT(format); - return !format.videoSupportedFrameRateRanges || !format.videoSupportedFrameRateRanges.count; - } -}; - -Float64 qt_find_min_framerate_distance(AVCaptureDeviceFormat *format, Float64 fps) -{ - Q_ASSERT(format && format.videoSupportedFrameRateRanges - && format.videoSupportedFrameRateRanges.count); - - AVFrameRateRange *range = [format.videoSupportedFrameRateRanges objectAtIndex:0]; - Float64 distance = qAbs(range.maxFrameRate - fps); - for (NSUInteger i = 1, e = format.videoSupportedFrameRateRanges.count; i < e; ++i) { - range = [format.videoSupportedFrameRateRanges objectAtIndex:i]; - distance = qMin(distance, qAbs(range.maxFrameRate - fps)); - } - - return distance; -} - -} // Unnamed namespace. - -AVCaptureDeviceFormat *qt_convert_to_capture_device_format(AVCaptureDevice *captureDevice, - const QCameraFormat &cameraFormat) -{ - AVCaptureDeviceFormat *newFormat = nil; - NSArray<AVCaptureDeviceFormat *> *formats = captureDevice.formats; - for (AVCaptureDeviceFormat *format in formats) { - CMFormatDescriptionRef formatDesc = format.formatDescription; - CMVideoDimensions dim = CMVideoFormatDescriptionGetDimensions(formatDesc); - FourCharCode formatCodec = CMVideoFormatDescriptionGetCodecType(formatDesc); - if (AVFVideoBuffer::fromCVPixelFormat(formatCodec) == cameraFormat.pixelFormat() - && cameraFormat.resolution().width() == dim.width - && cameraFormat.resolution().height() == dim.height) { - for (AVFrameRateRange *frameRateRange in format.videoSupportedFrameRateRanges) { - if (frameRateRange.minFrameRate >= cameraFormat.minFrameRate() - && frameRateRange.maxFrameRate <= cameraFormat.maxFrameRate()) { - newFormat = format; - break; - } - } - } - if (newFormat) - break; - } - return newFormat; -} - -QVector<AVCaptureDeviceFormat *> qt_unique_device_formats(AVCaptureDevice *captureDevice, FourCharCode filter) -{ - // 'filter' is the format we prefer if we have duplicates. - Q_ASSERT(captureDevice); - - QVector<AVCaptureDeviceFormat *> formats; - - if (!captureDevice.formats || !captureDevice.formats.count) - return formats; - - formats.reserve(captureDevice.formats.count); - for (AVCaptureDeviceFormat *format in captureDevice.formats) { - const QSize resolution(qt_device_format_resolution(format)); - if (resolution.isNull() || !resolution.isValid()) - continue; - formats << format; - } - - if (!formats.size()) - return formats; - - std::sort(formats.begin(), formats.end(), ByResolution<std::less>()); - - QSize size(qt_device_format_resolution(formats[0])); - FourCharCode codec = CMVideoFormatDescriptionGetCodecType(formats[0].formatDescription); - int last = 0; - for (int i = 1; i < formats.size(); ++i) { - const QSize nextSize(qt_device_format_resolution(formats[i])); - if (nextSize == size) { - if (codec == filter) - continue; - formats[last] = formats[i]; - } else { - ++last; - formats[last] = formats[i]; - size = nextSize; - } - codec = CMVideoFormatDescriptionGetCodecType(formats[i].formatDescription); - } - formats.resize(last + 1); - - return formats; -} - -QSize qt_device_format_resolution(AVCaptureDeviceFormat *format) -{ - if (!format || !format.formatDescription) - return QSize(); - - const CMVideoDimensions res = CMVideoFormatDescriptionGetDimensions(format.formatDescription); - return QSize(res.width, res.height); -} - -QSize qt_device_format_high_resolution(AVCaptureDeviceFormat *format) -{ - Q_ASSERT(format); - QSize res; -#if defined(Q_OS_IOS) - const CMVideoDimensions hrDim(format.highResolutionStillImageDimensions); - res.setWidth(hrDim.width); - res.setHeight(hrDim.height); -#endif - return res; -} - -QVector<AVFPSRange> qt_device_format_framerates(AVCaptureDeviceFormat *format) -{ - Q_ASSERT(format); - - QVector<AVFPSRange> qtRanges; - - if (!format.videoSupportedFrameRateRanges || !format.videoSupportedFrameRateRanges.count) - return qtRanges; - - qtRanges.reserve(format.videoSupportedFrameRateRanges.count); - for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) - qtRanges << AVFPSRange(range.minFrameRate, range.maxFrameRate); - - return qtRanges; -} - -QSize qt_device_format_pixel_aspect_ratio(AVCaptureDeviceFormat *format) -{ - Q_ASSERT(format); - - if (!format.formatDescription) { - qDebugCamera() << Q_FUNC_INFO << "no format description found"; - return QSize(); - } - - const CMVideoDimensions res = CMVideoFormatDescriptionGetDimensions(format.formatDescription); - const CGSize resPAR = CMVideoFormatDescriptionGetPresentationDimensions(format.formatDescription, true, false); - - if (qAbs(resPAR.width - res.width) < 1.) { - // "Pixel aspect ratio is used to adjust the width, leaving the height alone." - return QSize(1, 1); - } - - if (!res.width || !resPAR.width) - return QSize(); - - int n, d; - qt_real_to_fraction(resPAR.width > res.width - ? res.width / qreal(resPAR.width) - : resPAR.width / qreal(res.width), - &n, &d); - - return QSize(n, d); -} - -AVCaptureDeviceFormat *qt_find_best_resolution_match(AVCaptureDevice *captureDevice, - const QSize &request, - FourCharCode filter, - bool stillImage) -{ - Q_ASSERT(captureDevice); - Q_ASSERT(!request.isNull() && request.isValid()); - - if (!captureDevice.formats || !captureDevice.formats.count) - return nullptr; - - QVector<AVCaptureDeviceFormat *> formats(qt_unique_device_formats(captureDevice, filter)); - - for (int i = 0; i < formats.size(); ++i) { - AVCaptureDeviceFormat *format = formats[i]; - if (qt_device_format_resolution(format) == request) - return format; - // iOS only (still images). - if (stillImage && qt_device_format_high_resolution(format) == request) - return format; - } - - if (!qt_area_sane(request)) - return nullptr; - - typedef QPair<QSize, AVCaptureDeviceFormat *> FormatPair; - - QVector<FormatPair> pairs; // default|HR sizes - pairs.reserve(formats.size()); - - for (int i = 0; i < formats.size(); ++i) { - AVCaptureDeviceFormat *format = formats[i]; - const QSize res(qt_device_format_resolution(format)); - if (!res.isNull() && res.isValid() && qt_area_sane(res)) - pairs << FormatPair(res, format); - const QSize highRes(qt_device_format_high_resolution(format)); - if (stillImage && !highRes.isNull() && highRes.isValid() && qt_area_sane(highRes)) - pairs << FormatPair(highRes, format); - } - - if (!pairs.size()) - return nullptr; - - AVCaptureDeviceFormat *best = pairs[0].second; - QSize next(pairs[0].first); - int wDiff = qAbs(request.width() - next.width()); - int hDiff = qAbs(request.height() - next.height()); - const int area = request.width() * request.height(); - int areaDiff = qAbs(area - next.width() * next.height()); - for (int i = 1; i < pairs.size(); ++i) { - next = pairs[i].first; - const int newWDiff = qAbs(next.width() - request.width()); - const int newHDiff = qAbs(next.height() - request.height()); - const int newAreaDiff = qAbs(area - next.width() * next.height()); - - if ((newWDiff < wDiff && newHDiff < hDiff) - || ((newWDiff <= wDiff || newHDiff <= hDiff) && newAreaDiff <= areaDiff)) { - wDiff = newWDiff; - hDiff = newHDiff; - best = pairs[i].second; - areaDiff = newAreaDiff; - } - } - - return best; -} - -AVCaptureDeviceFormat *qt_find_best_framerate_match(AVCaptureDevice *captureDevice, - FourCharCode filter, - Float64 fps) -{ - Q_ASSERT(captureDevice); - Q_ASSERT(fps > 0.); - - const qreal epsilon = 0.1; - - QVector<AVCaptureDeviceFormat *>sorted(qt_unique_device_formats(captureDevice, filter)); - // Sort formats by their resolution in decreasing order: - std::sort(sorted.begin(), sorted.end(), ByResolution<std::greater>()); - // We can use only formats with framerate ranges: - sorted.erase(std::remove_if(sorted.begin(), sorted.end(), FormatHasNoFPSRange()), sorted.end()); - - if (!sorted.size()) - return nil; - - for (int i = 0; i < sorted.size(); ++i) { - AVCaptureDeviceFormat *format = sorted[i]; - for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) { - if (range.maxFrameRate - range.minFrameRate < epsilon) { - // On OS X ranges are points (built-in camera). - if (qAbs(fps - range.maxFrameRate) < epsilon) - return format; - } - - if (fps >= range.minFrameRate && fps <= range.maxFrameRate) - return format; - } - } - - Float64 distance = qt_find_min_framerate_distance(sorted[0], fps); - AVCaptureDeviceFormat *match = sorted[0]; - for (int i = 1; i < sorted.size(); ++i) { - const Float64 newDistance = qt_find_min_framerate_distance(sorted[i], fps); - if (newDistance < distance) { - distance = newDistance; - match = sorted[i]; - } - } - - return match; -} - -AVFrameRateRange *qt_find_supported_framerate_range(AVCaptureDeviceFormat *format, Float64 fps) -{ - Q_ASSERT(format && format.videoSupportedFrameRateRanges - && format.videoSupportedFrameRateRanges.count); - - const qreal epsilon = 0.1; - - for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) { - if (range.maxFrameRate - range.minFrameRate < epsilon) { - // On OS X ranges are points (built-in camera). - if (qAbs(fps - range.maxFrameRate) < epsilon) - return range; - } - - if (fps >= range.minFrameRate && fps <= range.maxFrameRate) - return range; - } - - AVFrameRateRange *match = [format.videoSupportedFrameRateRanges objectAtIndex:0]; - Float64 distance = qAbs(match.maxFrameRate - fps); - for (NSUInteger i = 1, e = format.videoSupportedFrameRateRanges.count; i < e; ++i) { - AVFrameRateRange *range = [format.videoSupportedFrameRateRanges objectAtIndex:i]; - const Float64 newDistance = qAbs(range.maxFrameRate - fps); - if (newDistance < distance) { - distance = newDistance; - match = range; - } - } - - return match; -} - -bool qt_format_supports_framerate(AVCaptureDeviceFormat *format, qreal fps) -{ - if (format && fps > qreal(0)) { - const qreal epsilon = 0.1; - for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) { - if (fps >= range.minFrameRate - epsilon && fps <= range.maxFrameRate + epsilon) - return true; - } - } - - return false; -} - -bool qt_formats_are_equal(AVCaptureDeviceFormat *f1, AVCaptureDeviceFormat *f2) -{ - if (f1 == f2) - return true; - - if (![f1.mediaType isEqualToString:f2.mediaType]) - return false; - - return CMFormatDescriptionEqual(f1.formatDescription, f2.formatDescription); -} - -bool qt_set_active_format(AVCaptureDevice *captureDevice, AVCaptureDeviceFormat *format, bool preserveFps) -{ - static bool firstSet = true; - - if (!captureDevice || !format) - return false; - - if (qt_formats_are_equal(captureDevice.activeFormat, format)) { - if (firstSet) { - // The capture device format is persistent. The first time we set a format, report that - // it changed even if the formats are the same. - // This prevents the session from resetting the format to the default value. - firstSet = false; - return true; - } - return false; - } - - firstSet = false; - - const AVFConfigurationLock lock(captureDevice); - if (!lock) { - qWarning("Failed to set active format (lock failed)"); - return false; - } - - // Changing the activeFormat resets the frame rate. - AVFPSRange fps; - if (preserveFps) - fps = qt_current_framerates(captureDevice, nil); - - captureDevice.activeFormat = format; - - if (preserveFps) - qt_set_framerate_limits(captureDevice, nil, fps.first, fps.second); - - return true; -} - -void qt_set_framerate_limits(AVCaptureConnection *videoConnection, qreal minFPS, qreal maxFPS) -{ - Q_ASSERT(videoConnection); - - if (minFPS < 0. || maxFPS < 0. || (maxFPS && maxFPS < minFPS)) { - qDebugCamera() << Q_FUNC_INFO << "invalid framerates (min, max):" - << minFPS << maxFPS; - return; - } - - CMTime minDuration = kCMTimeInvalid; - if (maxFPS > 0.) { - if (!videoConnection.supportsVideoMinFrameDuration) - qDebugCamera() << Q_FUNC_INFO << "maximum framerate is not supported"; - else - minDuration = CMTimeMake(1, maxFPS); - } - if (videoConnection.supportsVideoMinFrameDuration) - videoConnection.videoMinFrameDuration = minDuration; - - CMTime maxDuration = kCMTimeInvalid; - if (minFPS > 0.) { - if (!videoConnection.supportsVideoMaxFrameDuration) - qDebugCamera() << Q_FUNC_INFO << "minimum framerate is not supported"; - else - maxDuration = CMTimeMake(1, minFPS); - } - if (videoConnection.supportsVideoMaxFrameDuration) - videoConnection.videoMaxFrameDuration = maxDuration; -} - -CMTime qt_adjusted_frame_duration(AVFrameRateRange *range, qreal fps) -{ - Q_ASSERT(range); - Q_ASSERT(fps > 0.); - - if (range.maxFrameRate - range.minFrameRate < 0.1) { - // Can happen on OS X. - return range.minFrameDuration; - } - - if (fps <= range.minFrameRate) - return range.maxFrameDuration; - if (fps >= range.maxFrameRate) - return range.minFrameDuration; - - int n, d; - qt_real_to_fraction(1. / fps, &n, &d); - return CMTimeMake(n, d); -} - -void qt_set_framerate_limits(AVCaptureDevice *captureDevice, qreal minFPS, qreal maxFPS) -{ - Q_ASSERT(captureDevice); - if (!captureDevice.activeFormat) { - qDebugCamera() << Q_FUNC_INFO << "no active capture device format"; - return; - } - - if (minFPS < 0. || maxFPS < 0. || (maxFPS && maxFPS < minFPS)) { - qDebugCamera() << Q_FUNC_INFO << "invalid framerates (min, max):" - << minFPS << maxFPS; - return; - } - - CMTime minFrameDuration = kCMTimeInvalid; - CMTime maxFrameDuration = kCMTimeInvalid; - if (maxFPS || minFPS) { - AVFrameRateRange *range = qt_find_supported_framerate_range(captureDevice.activeFormat, - maxFPS ? maxFPS : minFPS); - if (!range) { - qDebugCamera() << Q_FUNC_INFO << "no framerate range found, (min, max):" - << minFPS << maxFPS; - return; - } - - if (maxFPS) - minFrameDuration = qt_adjusted_frame_duration(range, maxFPS); - if (minFPS) - maxFrameDuration = qt_adjusted_frame_duration(range, minFPS); - } - - const AVFConfigurationLock lock(captureDevice); - if (!lock) { - qDebugCamera() << Q_FUNC_INFO << "failed to lock for configuration"; - return; - } - - // While Apple's docs say kCMTimeInvalid will end in default - // settings for this format, kCMTimeInvalid on OS X ends with a runtime - // exception: - // "The activeVideoMinFrameDuration passed is not supported by the device." - // Instead, use the first item in the supported frame rates. -#ifdef Q_OS_IOS - [captureDevice setActiveVideoMinFrameDuration:minFrameDuration]; - [captureDevice setActiveVideoMaxFrameDuration:maxFrameDuration]; -#elif defined(Q_OS_MACOS) - if (CMTimeCompare(minFrameDuration, kCMTimeInvalid) == 0 - && CMTimeCompare(maxFrameDuration, kCMTimeInvalid) == 0) { - AVFrameRateRange *range = captureDevice.activeFormat.videoSupportedFrameRateRanges.firstObject; - minFrameDuration = range.minFrameDuration; - maxFrameDuration = range.maxFrameDuration; - } - - if (CMTimeCompare(minFrameDuration, kCMTimeInvalid)) - [captureDevice setActiveVideoMinFrameDuration:minFrameDuration]; - - if (CMTimeCompare(maxFrameDuration, kCMTimeInvalid)) - [captureDevice setActiveVideoMaxFrameDuration:maxFrameDuration]; -#endif // Q_OS_MACOS -} - -void qt_set_framerate_limits(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection, - qreal minFPS, qreal maxFPS) -{ - Q_UNUSED(videoConnection); - Q_ASSERT(captureDevice); - qt_set_framerate_limits(captureDevice, minFPS, maxFPS); -} - -AVFPSRange qt_current_framerates(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection) -{ - Q_UNUSED(videoConnection); - Q_ASSERT(captureDevice); - - AVFPSRange fps; - const CMTime minDuration = captureDevice.activeVideoMinFrameDuration; - if (CMTimeCompare(minDuration, kCMTimeInvalid)) { - if (const Float64 minSeconds = CMTimeGetSeconds(minDuration)) - fps.second = 1. / minSeconds; // Max FPS = 1 / MinDuration. - } - - const CMTime maxDuration = captureDevice.activeVideoMaxFrameDuration; - if (CMTimeCompare(maxDuration, kCMTimeInvalid)) { - if (const Float64 maxSeconds = CMTimeGetSeconds(maxDuration)) - fps.first = 1. / maxSeconds; // Min FPS = 1 / MaxDuration. - } - - return fps; -} - -QList<AudioValueRange> qt_supported_sample_rates_for_format(int codecId) -{ - QList<AudioValueRange> result; - UInt32 format = codecId; - UInt32 size; - OSStatus err = AudioFormatGetPropertyInfo( - kAudioFormatProperty_AvailableEncodeSampleRates, - sizeof(format), - &format, - &size); - - if (err != noErr) - return result; - - UInt32 numRanges = size / sizeof(AudioValueRange); - AudioValueRange sampleRanges[numRanges]; - - err = AudioFormatGetProperty(kAudioFormatProperty_AvailableEncodeSampleRates, - sizeof(format), - &format, - &size, - sampleRanges); - if (err != noErr) - return result; - - for (UInt32 i = 0; i < numRanges; i++) - result << sampleRanges[i]; - - return result; -} - -QList<AudioValueRange> qt_supported_bit_rates_for_format(int codecId) -{ - QList<AudioValueRange> result; - UInt32 format = codecId; - UInt32 size; - OSStatus err = AudioFormatGetPropertyInfo( - kAudioFormatProperty_AvailableEncodeBitRates, - sizeof(format), - &format, - &size); - - if (err != noErr) - return result; - - UInt32 numRanges = size / sizeof(AudioValueRange); - AudioValueRange bitRanges[numRanges]; - - err = AudioFormatGetProperty(kAudioFormatProperty_AvailableEncodeBitRates, - sizeof(format), - &format, - &size, - bitRanges); - if (err != noErr) - return result; - - for (UInt32 i = 0; i < numRanges; i++) - result << bitRanges[i]; - - return result; -} - -std::optional<QList<UInt32>> qt_supported_channel_counts_for_format(int codecId) -{ - QList<UInt32> result; - AudioStreamBasicDescription sf = {}; - sf.mFormatID = codecId; - UInt32 size; - OSStatus err = AudioFormatGetPropertyInfo( - kAudioFormatProperty_AvailableEncodeNumberChannels, - sizeof(sf), - &sf, - &size); - - if (err != noErr) - return result; - - // From Apple's docs: - // A value of 0xFFFFFFFF indicates that any number of channels may be encoded. - if (int(size) == -1) - return std::nullopt; - - UInt32 numCounts = size / sizeof(UInt32); - UInt32 channelCounts[numCounts]; - - err = AudioFormatGetProperty(kAudioFormatProperty_AvailableEncodeNumberChannels, - sizeof(sf), - &sf, - &size, - channelCounts); - if (err != noErr) - return result; - - for (UInt32 i = 0; i < numCounts; i++) - result << channelCounts[i]; - - return result; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/darwin/camera/avfcamerautility_p.h b/src/multimedia/platform/darwin/camera/avfcamerautility_p.h deleted file mode 100644 index 26a023be9..000000000 --- a/src/multimedia/platform/darwin/camera/avfcamerautility_p.h +++ /dev/null @@ -1,201 +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$ -** -****************************************************************************/ - -#ifndef AVFCAMERAUTILITY_H -#define AVFCAMERAUTILITY_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qglobal.h> -#include <QtCore/qdebug.h> -#include <QtCore/qlist.h> -#include <QtCore/qpair.h> -#include <QtCore/qsize.h> - -#include "qcameradevice.h" - -#include <CoreAudio/CoreAudioTypes.h> - -#include <AVFoundation/AVFoundation.h> - -// In case we have SDK below 10.7/7.0: -@class AVCaptureDeviceFormat; - -QT_BEGIN_NAMESPACE - -class AVFConfigurationLock -{ -public: - explicit AVFConfigurationLock(AVCaptureDevice *captureDevice) - : m_captureDevice(captureDevice), - m_locked(false) - { - Q_ASSERT(m_captureDevice); - NSError *error = nil; - m_locked = [m_captureDevice lockForConfiguration:&error]; - } - - ~AVFConfigurationLock() - { - if (m_locked) - [m_captureDevice unlockForConfiguration]; - } - - operator bool() const - { - return m_locked; - } - -private: - Q_DISABLE_COPY(AVFConfigurationLock) - - AVCaptureDevice *m_captureDevice; - bool m_locked; -}; - -struct AVFObjectDeleter { - static void cleanup(NSObject *obj) - { - if (obj) - [obj release]; - } -}; - -template<class T> -class AVFScopedPointer : public QScopedPointer<NSObject, AVFObjectDeleter> -{ -public: - AVFScopedPointer() {} - explicit AVFScopedPointer(T *ptr) : QScopedPointer(ptr) {} - operator T*() const - { - // Quite handy operator to enable Obj-C messages: [ptr someMethod]; - return data(); - } - - T *data() const - { - return static_cast<T *>(QScopedPointer::data()); - } - - T *take() - { - return static_cast<T *>(QScopedPointer::take()); - } -}; - -template<> -class AVFScopedPointer<dispatch_queue_t> -{ -public: - AVFScopedPointer() : m_queue(nullptr) {} - explicit AVFScopedPointer(dispatch_queue_t q) : m_queue(q) {} - - ~AVFScopedPointer() - { - if (m_queue) - dispatch_release(m_queue); - } - - operator dispatch_queue_t() const - { - // Quite handy operator to enable Obj-C messages: [ptr someMethod]; - return m_queue; - } - - dispatch_queue_t data() const - { - return m_queue; - } - - void reset(dispatch_queue_t q = nullptr) - { - if (m_queue) - dispatch_release(m_queue); - m_queue = q; - } - -private: - dispatch_queue_t m_queue; - - Q_DISABLE_COPY(AVFScopedPointer) -}; - -typedef QPair<qreal, qreal> AVFPSRange; -AVFPSRange qt_connection_framerates(AVCaptureConnection *videoConnection); - -AVCaptureDeviceFormat *qt_convert_to_capture_device_format(AVCaptureDevice *captureDevice, - const QCameraFormat &format); -QList<AVCaptureDeviceFormat *> qt_unique_device_formats(AVCaptureDevice *captureDevice, - FourCharCode preferredFormat); -QSize qt_device_format_resolution(AVCaptureDeviceFormat *format); -QSize qt_device_format_high_resolution(AVCaptureDeviceFormat *format); -QSize qt_device_format_pixel_aspect_ratio(AVCaptureDeviceFormat *format); -QList<AVFPSRange> qt_device_format_framerates(AVCaptureDeviceFormat *format); -AVCaptureDeviceFormat *qt_find_best_resolution_match(AVCaptureDevice *captureDevice, const QSize &res, - FourCharCode preferredFormat, bool stillImage = true); -AVCaptureDeviceFormat *qt_find_best_framerate_match(AVCaptureDevice *captureDevice, - FourCharCode preferredFormat, - Float64 fps); -AVFrameRateRange *qt_find_supported_framerate_range(AVCaptureDeviceFormat *format, Float64 fps); -bool qt_format_supports_framerate(AVCaptureDeviceFormat *format, qreal fps); - -bool qt_formats_are_equal(AVCaptureDeviceFormat *f1, AVCaptureDeviceFormat *f2); -bool qt_set_active_format(AVCaptureDevice *captureDevice, AVCaptureDeviceFormat *format, bool preserveFps); - -AVFPSRange qt_current_framerates(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection); -void qt_set_framerate_limits(AVCaptureDevice *captureDevice, AVCaptureConnection *videoConnection, - qreal minFPS, qreal maxFPS); - -QList<AudioValueRange> qt_supported_sample_rates_for_format(int codecId); -QList<AudioValueRange> qt_supported_bit_rates_for_format(int codecId); -std::optional<QList<UInt32>> qt_supported_channel_counts_for_format(int codecId); - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/darwin/camera/avfimagecapture.mm b/src/multimedia/platform/darwin/camera/avfimagecapture.mm deleted file mode 100644 index a397fe418..000000000 --- a/src/multimedia/platform/darwin/camera/avfimagecapture.mm +++ /dev/null @@ -1,416 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). -** 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$ -** -****************************************************************************/ - -#include "avfcameradebug_p.h" -#include "avfimagecapture_p.h" -#include "avfcameraservice_p.h" -#include "avfcamerautility_p.h" -#include "avfcamera_p.h" -#include "avfcamerasession_p.h" -#include "avfcamerarenderer_p.h" -#include "qmediastoragelocation_p.h" -#include <private/qplatformimagecapture_p.h> -#include <private/qmemoryvideobuffer_p.h> - -#include <QtCore/qurl.h> -#include <QtCore/qfile.h> -#include <QtCore/qbuffer.h> -#include <QtConcurrent/qtconcurrentrun.h> -#include <QtGui/qimagereader.h> - -#import <AVFoundation/AVFoundation.h> - -QT_USE_NAMESPACE - -AVFImageCapture::AVFImageCapture(QImageCapture *parent) - : QPlatformImageCapture(parent) -{ - m_stillImageOutput = [[AVCaptureStillImageOutput alloc] init]; - - NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys: - AVVideoCodecTypeJPEG, AVVideoCodecKey, nil]; - - [m_stillImageOutput setOutputSettings:outputSettings]; - [outputSettings release]; -} - -AVFImageCapture::~AVFImageCapture() -{ - [m_stillImageOutput release]; -} - -bool AVFImageCapture::isReadyForCapture() const -{ - return m_cameraControl && m_videoConnection && m_cameraControl->isActive(); -} - -void AVFImageCapture::updateReadyStatus() -{ - if (m_ready != isReadyForCapture()) { - m_ready = !m_ready; - qDebugCamera() << "ReadyToCapture status changed:" << m_ready; - Q_EMIT readyForCaptureChanged(m_ready); - } -} - -int AVFImageCapture::doCapture(const QString &actualFileName) -{ - if (!m_session) { - QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, - Q_ARG(int, m_lastCaptureId), - Q_ARG(int, QImageCapture::ResourceError), - Q_ARG(QString, QPlatformImageCapture::msgImageCaptureNotSet())); - return m_lastCaptureId; - } - if (!isReadyForCapture()) { - QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, - Q_ARG(int, m_lastCaptureId), - Q_ARG(int, QImageCapture::NotReadyError), - Q_ARG(QString, QPlatformImageCapture::msgCameraNotReady())); - return m_lastCaptureId; - } - m_lastCaptureId++; - - bool captureToBuffer = actualFileName.isEmpty(); - - CaptureRequest request = { m_lastCaptureId, QSharedPointer<QSemaphore>::create()}; - m_requestsMutex.lock(); - m_captureRequests.enqueue(request); - m_requestsMutex.unlock(); - - QString fileName(actualFileName); - - [m_stillImageOutput captureStillImageAsynchronouslyFromConnection:m_videoConnection - completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) { - - if (error) { - QStringList messageParts; - messageParts << QString::fromUtf8([[error localizedDescription] UTF8String]); - messageParts << QString::fromUtf8([[error localizedFailureReason] UTF8String]); - messageParts << QString::fromUtf8([[error localizedRecoverySuggestion] UTF8String]); - - QString errorMessage = messageParts.join(QChar(u' ')); - qDebugCamera() << "Image capture failed:" << errorMessage; - - QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, - Q_ARG(int, request.captureId), - Q_ARG(int, QImageCapture::ResourceError), - Q_ARG(QString, errorMessage)); - return; - } - - // Wait for the preview to be generated before saving the JPEG (but only - // if we have AVFCameraRenderer attached). - // It is possible to stop camera immediately after trying to capture an - // image; this can result in a blocked callback's thread, waiting for a - // new viewfinder's frame to arrive/semaphore to be released. It is also - // unspecified on which thread this callback gets executed, (probably it's - // not the same thread that initiated a capture and stopped the camera), - // so we cannot reliably check the camera's status. Instead, we wait - // with a timeout and treat a failure to acquire a semaphore as an error. - if (!m_session->videoOutput() || request.previewReady->tryAcquire(1, 1000)) { - qDebugCamera() << "Image capture completed"; - - NSData *nsJpgData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; - QByteArray jpgData = QByteArray::fromRawData((const char *)[nsJpgData bytes], [nsJpgData length]); - - if (captureToBuffer) { - QBuffer data(&jpgData); - QImageReader reader(&data, "JPEG"); - QSize size = reader.size(); - QVideoFrame frame(new QMemoryVideoBuffer(QByteArray(jpgData.constData(), jpgData.size()), -1), - QVideoFrameFormat(size, QVideoFrameFormat::Format_Jpeg)); - QMetaObject::invokeMethod(this, "imageAvailable", Qt::QueuedConnection, - Q_ARG(int, request.captureId), - Q_ARG(QVideoFrame, frame)); - } else { - QFile f(fileName); - if (f.open(QFile::WriteOnly)) { - if (f.write(jpgData) != -1) { - QMetaObject::invokeMethod(this, "imageSaved", Qt::QueuedConnection, - Q_ARG(int, request.captureId), - Q_ARG(QString, fileName)); - } else { - QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, - Q_ARG(int, request.captureId), - Q_ARG(int, QImageCapture::OutOfSpaceError), - Q_ARG(QString, f.errorString())); - } - } else { - QString errorMessage = tr("Could not open destination file:\n%1").arg(fileName); - QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, - Q_ARG(int, request.captureId), - Q_ARG(int, QImageCapture::ResourceError), - Q_ARG(QString, errorMessage)); - } - } - } else { - const QLatin1String errorMessage("Image capture failed: timed out waiting" - " for a preview frame."); - qDebugCamera() << errorMessage; - QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, - Q_ARG(int, request.captureId), - Q_ARG(int, QImageCapture::ResourceError), - Q_ARG(QString, errorMessage)); - } - }]; - - return request.captureId; -} - -int AVFImageCapture::capture(const QString &fileName) -{ - auto actualFileName = QMediaStorageLocation::generateFileName(fileName, QStandardPaths::PicturesLocation, QLatin1String("jpg")); - - qDebugCamera() << "Capture image to" << actualFileName; - return doCapture(actualFileName); -} - -int AVFImageCapture::captureToBuffer() -{ - return doCapture(QString()); -} - -void AVFImageCapture::onNewViewfinderFrame(const QVideoFrame &frame) -{ - QMutexLocker locker(&m_requestsMutex); - - if (m_captureRequests.isEmpty()) - return; - - CaptureRequest request = m_captureRequests.dequeue(); - Q_EMIT imageExposed(request.captureId); - - (void) QtConcurrent::run(&AVFImageCapture::makeCapturePreview, this, - request, - frame, - 0 /* rotation */); -} - -void AVFImageCapture::onCameraChanged() -{ - if (m_service) - m_cameraControl = static_cast<AVFCamera *>(m_service->camera()); - else - m_cameraControl = nullptr; - - if (m_cameraControl) - connect(m_cameraControl, SIGNAL(activeChanged(bool)), this, SLOT(updateReadyStatus())); -} - -void AVFImageCapture::makeCapturePreview(CaptureRequest request, - const QVideoFrame &frame, - int rotation) -{ - QTransform transform; - transform.rotate(rotation); - - Q_EMIT imageCaptured(request.captureId, frame.toImage().transformed(transform)); - - request.previewReady->release(); -} - -void AVFImageCapture::updateCaptureConnection() -{ - if (m_session && m_session->videoCaptureDevice()) { - qDebugCamera() << Q_FUNC_INFO; - AVCaptureSession *captureSession = m_session->captureSession(); - - if (![captureSession.outputs containsObject:m_stillImageOutput]) { - if ([captureSession canAddOutput:m_stillImageOutput]) { - [captureSession beginConfiguration]; - // Lock the video capture device to make sure the active format is not reset - const AVFConfigurationLock lock(m_session->videoCaptureDevice()); - [captureSession addOutput:m_stillImageOutput]; - m_videoConnection = [m_stillImageOutput connectionWithMediaType:AVMediaTypeVideo]; - [captureSession commitConfiguration]; - updateReadyStatus(); - } - } else { - m_videoConnection = [m_stillImageOutput connectionWithMediaType:AVMediaTypeVideo]; - } - } -} - - -QImageEncoderSettings AVFImageCapture::imageSettings() const -{ - QImageEncoderSettings settings; - - if (!videoCaptureDeviceIsValid()) - return settings; - - AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice(); - if (!captureDevice.activeFormat) { - qDebugCamera() << Q_FUNC_INFO << "no active format"; - return settings; - } - - QSize res(qt_device_format_resolution(captureDevice.activeFormat)); -#ifdef Q_OS_IOS - if (!m_service->avfImageCaptureControl() || !m_service->avfImageCaptureControl()->stillImageOutput()) { - qDebugCamera() << Q_FUNC_INFO << "no still image output"; - return settings; - } - - AVCaptureStillImageOutput *stillImageOutput = m_service->avfImageCaptureControl()->stillImageOutput(); - if (stillImageOutput.highResolutionStillImageOutputEnabled) - res = qt_device_format_high_resolution(captureDevice.activeFormat); -#endif - if (res.isNull() || !res.isValid()) { - qDebugCamera() << Q_FUNC_INFO << "failed to exctract the image resolution"; - return settings; - } - - settings.setResolution(res); - settings.setFormat(QImageCapture::JPEG); - - return settings; -} - -void AVFImageCapture::setImageSettings(const QImageEncoderSettings &settings) -{ - if (m_settings == settings) - return; - - m_settings = settings; - applySettings(); -} - -bool AVFImageCapture::applySettings() -{ - if (!videoCaptureDeviceIsValid()) - return false; - - AVFCameraSession *session = m_service->session(); - if (!session) - return false; - - if (!m_service->imageCapture() - || !m_service->avfImageCaptureControl()->stillImageOutput()) { - qDebugCamera() << Q_FUNC_INFO << "no still image output"; - return false; - } - - if (m_settings.format() != QImageCapture::UnspecifiedFormat && m_settings.format() != QImageCapture::JPEG) { - qDebugCamera() << Q_FUNC_INFO << "unsupported format:" << m_settings.format(); - return false; - } - - QSize res(m_settings.resolution()); - if (res.isNull()) { - qDebugCamera() << Q_FUNC_INFO << "invalid resolution:" << res; - return false; - } - - if (!res.isValid()) { - // Invalid == default value. - // Here we could choose the best format available, but - // activeFormat is already equal to 'preset high' by default, - // which is good enough, otherwise we can end in some format with low framerates. - return false; - } - - bool activeFormatChanged = false; - - AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice(); - AVCaptureDeviceFormat *match = qt_find_best_resolution_match(captureDevice, res, - m_service->session()->defaultCodec()); - - if (!match) { - qDebugCamera() << Q_FUNC_INFO << "unsupported resolution:" << res; - return false; - } - - activeFormatChanged = qt_set_active_format(captureDevice, match, true); - -#ifdef Q_OS_IOS - AVCaptureStillImageOutput *imageOutput = m_service->avfImageCaptureControl()->stillImageOutput(); - if (res == qt_device_format_high_resolution(captureDevice.activeFormat)) - imageOutput.highResolutionStillImageOutputEnabled = YES; - else - imageOutput.highResolutionStillImageOutputEnabled = NO; -#endif - - return activeFormatChanged; -} - -void AVFImageCapture::setCaptureSession(QPlatformMediaCaptureSession *session) -{ - AVFCameraService *captureSession = static_cast<AVFCameraService *>(session); - if (m_service == captureSession) - return; - - m_service = captureSession; - if (!m_service) { - m_session->disconnect(this); - if (m_cameraControl) - m_cameraControl->disconnect(this); - m_session = nullptr; - m_cameraControl = nullptr; - m_videoConnection = nil; - return; - } - - m_session = m_service->session(); - Q_ASSERT(m_session); - - connect(m_service, &AVFCameraService::cameraChanged, this, &AVFImageCapture::onCameraChanged); - connect(m_session, SIGNAL(readyToConfigureConnections()), SLOT(updateCaptureConnection())); - connect(m_session, &AVFCameraSession::newViewfinderFrame, - this, &AVFImageCapture::onNewViewfinderFrame); - - updateCaptureConnection(); - updateReadyStatus(); - onCameraChanged(); -} - -bool AVFImageCapture::videoCaptureDeviceIsValid() const -{ - if (!m_service || !m_service->session() || !m_service->session()->videoCaptureDevice()) - return false; - - AVCaptureDevice *captureDevice = m_service->session()->videoCaptureDevice(); - if (!captureDevice.formats || !captureDevice.formats.count) - return false; - - return true; -} - -#include "moc_avfimagecapture_p.cpp" diff --git a/src/multimedia/platform/darwin/camera/avfimagecapture_p.h b/src/multimedia/platform/darwin/camera/avfimagecapture_p.h deleted file mode 100644 index ef11bd70f..000000000 --- a/src/multimedia/platform/darwin/camera/avfimagecapture_p.h +++ /dev/null @@ -1,117 +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$ -** -****************************************************************************/ - -#ifndef AVFCAMERAIMAGECAPTURE_H -#define AVFCAMERAIMAGECAPTURE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#import <AVFoundation/AVFoundation.h> - -#include <QtCore/qqueue.h> -#include <QtCore/qsemaphore.h> -#include <QtCore/qsharedpointer.h> -#include <private/qplatformimagecapture_p.h> -#include "avfcamerasession_p.h" - -QT_BEGIN_NAMESPACE - -class AVFImageCapture : public QPlatformImageCapture -{ -Q_OBJECT -public: - struct CaptureRequest { - int captureId; - QSharedPointer<QSemaphore> previewReady; - }; - - AVFImageCapture(QImageCapture *parent = nullptr); - ~AVFImageCapture(); - - bool isReadyForCapture() const override; - - AVCaptureStillImageOutput *stillImageOutput() const {return m_stillImageOutput;} - - int doCapture(const QString &fileName); - int capture(const QString &fileName) override; - int captureToBuffer() override; - - QImageEncoderSettings imageSettings() const override; - void setImageSettings(const QImageEncoderSettings &settings) override; - bool applySettings(); - - void setCaptureSession(QPlatformMediaCaptureSession *session); - -private Q_SLOTS: - void updateCaptureConnection(); - void updateReadyStatus(); - void onNewViewfinderFrame(const QVideoFrame &frame); - void onCameraChanged(); - -private: - void makeCapturePreview(CaptureRequest request, const QVideoFrame &frame, int rotation); - bool videoCaptureDeviceIsValid() const; - - AVFCameraService *m_service = nullptr; - AVFCameraSession *m_session = nullptr; - AVFCamera *m_cameraControl = nullptr; - bool m_ready = false; - int m_lastCaptureId = 0; - AVCaptureStillImageOutput *m_stillImageOutput; - AVCaptureConnection *m_videoConnection = nullptr; - - QMutex m_requestsMutex; - QQueue<CaptureRequest> m_captureRequests; - QImageEncoderSettings m_settings; -}; - -Q_DECLARE_TYPEINFO(AVFImageCapture::CaptureRequest, Q_PRIMITIVE_TYPE); - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/darwin/camera/avfmediaassetwriter.mm b/src/multimedia/platform/darwin/camera/avfmediaassetwriter.mm deleted file mode 100644 index ed90dbd07..000000000 --- a/src/multimedia/platform/darwin/camera/avfmediaassetwriter.mm +++ /dev/null @@ -1,579 +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$ -** -****************************************************************************/ - -#include "avfmediaencoder_p.h" -#include "avfcamerarenderer_p.h" -#include "avfmediaassetwriter_p.h" -#include "avfcameraservice_p.h" -#include "avfcamerasession_p.h" -#include "avfcameradebug_p.h" -#include <private/qdarwinformatsinfo_p.h> -#include <private/avfmetadata_p.h> - -#include <QtCore/qmetaobject.h> -#include <QtCore/qatomic.h> - -QT_USE_NAMESPACE - -namespace { - -bool qt_capture_session_isValid(AVFCameraService *service) -{ - if (!service || !service->session()) - return false; - - AVFCameraSession *session = service->session(); - if (!session->captureSession()) - return false; - - return true; -} - -enum WriterState -{ - WriterStateIdle, - WriterStateActive, - WriterStatePaused, - WriterStateAborted -}; - -using AVFAtomicInt64 = QAtomicInteger<qint64>; - -} // unnamed namespace - -@interface QT_MANGLE_NAMESPACE(AVFMediaAssetWriter) (PrivateAPI) -- (bool)addWriterInputs; -- (void)setQueues; -- (void)updateDuration:(CMTime)newTimeStamp; -- (CMSampleBufferRef)adjustTime:(CMSampleBufferRef)sample by:(CMTime)offset; -@end - -@implementation QT_MANGLE_NAMESPACE(AVFMediaAssetWriter) -{ -@private - AVFCameraService *m_service; - - AVFScopedPointer<AVAssetWriterInput> m_cameraWriterInput; - AVFScopedPointer<AVAssetWriterInput> m_audioWriterInput; - - // Queue to write sample buffers: - AVFScopedPointer<dispatch_queue_t> m_writerQueue; - // High priority serial queue for video output: - AVFScopedPointer<dispatch_queue_t> m_videoQueue; - // Serial queue for audio output: - AVFScopedPointer<dispatch_queue_t> m_audioQueue; - - AVFScopedPointer<AVAssetWriter> m_assetWriter; - - AVFMediaEncoder *m_delegate; - - bool m_setStartTime; - - QAtomicInt m_state; - - bool m_writeFirstAudioBuffer; - - CMTime m_startTime; - CMTime m_lastTimeStamp; - CMTime m_lastVideoTimestamp; - CMTime m_lastAudioTimestamp; - CMTime m_timeOffset; - bool m_adjustTime; - - NSDictionary *m_audioSettings; - NSDictionary *m_videoSettings; - - AVFAtomicInt64 m_durationInMs; -} - -- (id)initWithDelegate:(AVFMediaEncoder *)delegate -{ - Q_ASSERT(delegate); - - if (self = [super init]) { - m_delegate = delegate; - m_setStartTime = true; - m_state.storeRelaxed(WriterStateIdle); - m_startTime = kCMTimeInvalid; - m_lastTimeStamp = kCMTimeInvalid; - m_lastAudioTimestamp = kCMTimeInvalid; - m_lastVideoTimestamp = kCMTimeInvalid; - m_timeOffset = kCMTimeInvalid; - m_adjustTime = false; - m_durationInMs.storeRelaxed(0); - m_audioSettings = nil; - m_videoSettings = nil; - m_writeFirstAudioBuffer = false; - } - - return self; -} - -- (bool)setupWithFileURL:(NSURL *)fileURL - cameraService:(AVFCameraService *)service - audioSettings:(NSDictionary *)audioSettings - videoSettings:(NSDictionary *)videoSettings - fileFormat:(QMediaFormat::FileFormat)fileFormat - transform:(CGAffineTransform)transform -{ - Q_ASSERT(fileURL); - - if (!qt_capture_session_isValid(service)) { - qDebugCamera() << Q_FUNC_INFO << "invalid capture session"; - return false; - } - - m_service = service; - m_audioSettings = audioSettings; - m_videoSettings = videoSettings; - - AVFCameraSession *session = m_service->session(); - - m_writerQueue.reset(dispatch_queue_create("asset-writer-queue", DISPATCH_QUEUE_SERIAL)); - if (!m_writerQueue) { - qDebugCamera() << Q_FUNC_INFO << "failed to create an asset writer's queue"; - return false; - } - - if (session->videoOutput() && session->videoOutput()->videoDataOutput()) { - m_videoQueue.reset(dispatch_queue_create("video-output-queue", DISPATCH_QUEUE_SERIAL)); - if (!m_videoQueue) { - qDebugCamera() << Q_FUNC_INFO << "failed to create video queue"; - return false; - } - dispatch_set_target_queue(m_videoQueue, dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)); - } - - m_audioQueue.reset(dispatch_queue_create("audio-output-queue", DISPATCH_QUEUE_SERIAL)); - if (!m_audioQueue) { - qDebugCamera() << Q_FUNC_INFO << "failed to create audio queue"; - if (!m_videoQueue) - return false; - // But we still can write video! - } - - auto fileType = QDarwinFormatInfo::avFileTypeForContainerFormat(fileFormat); - m_assetWriter.reset([[AVAssetWriter alloc] initWithURL:fileURL - fileType:fileType - error:nil]); - if (!m_assetWriter) { - qDebugCamera() << Q_FUNC_INFO << "failed to create asset writer"; - return false; - } - - bool audioCaptureOn = false; - if (m_audioQueue) - audioCaptureOn = session->audioOutput() != nil; - - if (!m_videoQueue) - m_writeFirstAudioBuffer = true; - - if (![self addWriterInputs]) { - m_assetWriter.reset(); - return false; - } - - if (m_cameraWriterInput) - m_cameraWriterInput.data().transform = transform; - - [self setMetaData:fileType]; - - // Ready to start ... - return true; -} - -- (void)setMetaData:(AVFileType)fileType -{ - m_assetWriter.data().metadata = AVFMetaData::toAVMetadataForFormat(m_delegate->metaData(), fileType); -} - -- (void)start -{ - [self setQueues]; - - m_setStartTime = true; - - m_state.storeRelease(WriterStateActive); - - [m_assetWriter startWriting]; - AVCaptureSession *session = m_service->session()->captureSession(); - if (!session.running) - [session startRunning]; -} - -- (void)stop -{ - if (m_state.loadAcquire() != WriterStateActive && m_state.loadAcquire() != WriterStatePaused) - return; - if ([m_assetWriter status] != AVAssetWriterStatusWriting) - return; - - // Do this here so that - - // 1. '-abort' should not try calling finishWriting again and - // 2. async block (see below) will know if recorder control was deleted - // before the block's execution: - m_state.storeRelease(WriterStateIdle); - // Now, since we have to ensure no sample buffers are - // appended after a call to finishWriting, we must - // ensure writer's queue sees this change in m_state - // _before_ we call finishWriting: - dispatch_sync(m_writerQueue, ^{}); - // Done, but now we also want to prevent video queue - // from updating our viewfinder: - if (m_videoQueue) - dispatch_sync(m_videoQueue, ^{}); - - // Now we're safe to stop: - [m_assetWriter finishWritingWithCompletionHandler:^{ - // This block is async, so by the time it's executed, - // it's possible that render control was deleted already ... - if (m_state.loadAcquire() == WriterStateAborted) - return; - - AVCaptureSession *session = m_service->session()->captureSession(); - if (session.running) - [session stopRunning]; - QMetaObject::invokeMethod(m_delegate, "assetWriterFinished", Qt::QueuedConnection); - }]; -} - -- (void)abort -{ - // -abort is to be called from recorder control's dtor. - - if (m_state.fetchAndStoreRelease(WriterStateAborted) != WriterStateActive) { - // Not recording, nothing to stop. - return; - } - - // From Apple's docs: - // "To guarantee that all sample buffers are successfully written, - // you must ensure that all calls to appendSampleBuffer: and - // appendPixelBuffer:withPresentationTime: have returned before - // invoking this method." - // - // The only way we can ensure this is: - dispatch_sync(m_writerQueue, ^{}); - // At this point next block (if any) on the writer's queue - // will see m_state preventing it from any further processing. - if (m_videoQueue) - dispatch_sync(m_videoQueue, ^{}); - // After this point video queue will not try to modify our - // viewfider, so we're safe to delete now. - - [m_assetWriter finishWritingWithCompletionHandler:^{ - }]; -} - -- (void)pause -{ - if (m_state.loadAcquire() != WriterStateActive) - return; - if ([m_assetWriter status] != AVAssetWriterStatusWriting) - return; - - m_state.storeRelease(WriterStatePaused); - m_adjustTime = true; -} - -- (void)resume -{ - if (m_state.loadAcquire() != WriterStatePaused) - return; - if ([m_assetWriter status] != AVAssetWriterStatusWriting) - return; - - m_state.storeRelease(WriterStateActive); -} - -- (void)setStartTimeFrom:(CMSampleBufferRef)sampleBuffer -{ - // Writer's queue only. - Q_ASSERT(m_setStartTime); - Q_ASSERT(sampleBuffer); - - if (m_state.loadAcquire() != WriterStateActive) - return; - - QMetaObject::invokeMethod(m_delegate, "assetWriterStarted", Qt::QueuedConnection); - - m_durationInMs.storeRelease(0); - m_startTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer); - m_lastTimeStamp = m_startTime; - [m_assetWriter startSessionAtSourceTime:m_startTime]; - m_setStartTime = false; -} - -- (CMSampleBufferRef)adjustTime:(CMSampleBufferRef)sample by:(CMTime)offset -{ - CMItemCount count; - CMSampleBufferGetSampleTimingInfoArray(sample, 0, nil, &count); - CMSampleTimingInfo* timingInfo = (CMSampleTimingInfo*) malloc(sizeof(CMSampleTimingInfo) * count); - CMSampleBufferGetSampleTimingInfoArray(sample, count, timingInfo, &count); - for (CMItemCount i = 0; i < count; i++) - { - timingInfo[i].decodeTimeStamp = CMTimeSubtract(timingInfo[i].decodeTimeStamp, offset); - timingInfo[i].presentationTimeStamp = CMTimeSubtract(timingInfo[i].presentationTimeStamp, offset); - } - CMSampleBufferRef updatedBuffer; - CMSampleBufferCreateCopyWithNewTiming(kCFAllocatorDefault, sample, count, timingInfo, &updatedBuffer); - free(timingInfo); - return updatedBuffer; -} - -- (void)writeVideoSampleBuffer:(CMSampleBufferRef)sampleBuffer -{ - // This code is executed only on a writer's queue. - Q_ASSERT(sampleBuffer); - - if (m_state.loadAcquire() == WriterStateActive) { - if (m_setStartTime) - [self setStartTimeFrom:sampleBuffer]; - - if (m_cameraWriterInput.data().readyForMoreMediaData) { - [self updateDuration:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)]; - [m_cameraWriterInput appendSampleBuffer:sampleBuffer]; - } - } -} - -- (void)writeAudioSampleBuffer:(CMSampleBufferRef)sampleBuffer -{ - Q_ASSERT(sampleBuffer); - - // This code is executed only on a writer's queue. - if (m_state.loadAcquire() == WriterStateActive) { - if (m_setStartTime) - [self setStartTimeFrom:sampleBuffer]; - - if (m_audioWriterInput.data().readyForMoreMediaData) { - [self updateDuration:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)]; - [m_audioWriterInput appendSampleBuffer:sampleBuffer]; - } - } -} - -- (void)captureOutput:(AVCaptureOutput *)captureOutput - didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer - fromConnection:(AVCaptureConnection *)connection -{ - Q_UNUSED(connection); - Q_ASSERT(m_service && m_service->session()); - - if (m_state.loadAcquire() != WriterStateActive && m_state.loadAcquire() != WriterStatePaused) - return; - - if (!CMSampleBufferDataIsReady(sampleBuffer)) { - qWarning() << Q_FUNC_INFO << "sample buffer is not ready, skipping."; - return; - } - - CFRetain(sampleBuffer); - - bool isVideoBuffer = true; - isVideoBuffer = (captureOutput != m_service->session()->audioOutput()); - if (isVideoBuffer) { - // Find renderercontrol's delegate and invoke its method to - // show updated viewfinder's frame. - if (m_service->session()->videoOutput()) { - NSObject<AVCaptureVideoDataOutputSampleBufferDelegate> *vfDelegate = - (NSObject<AVCaptureVideoDataOutputSampleBufferDelegate> *)m_service->session()->videoOutput()->captureDelegate(); - if (vfDelegate) { - AVCaptureOutput *output = nil; - AVCaptureConnection *connection = nil; - [vfDelegate captureOutput:output didOutputSampleBuffer:sampleBuffer fromConnection:connection]; - } - } - } else { - if (m_service->session()->audioOutput()) { - NSObject<AVCaptureAudioDataOutputSampleBufferDelegate> *audioPreviewDelegate = - (NSObject<AVCaptureAudioDataOutputSampleBufferDelegate> *)m_service->session()->audioPreviewDelegate(); - if (audioPreviewDelegate) { - AVCaptureOutput *output = nil; - AVCaptureConnection *connection = nil; - [audioPreviewDelegate captureOutput:output didOutputSampleBuffer:sampleBuffer fromConnection:connection]; - } - } - } - - if (m_state.loadAcquire() != WriterStateActive) { - CFRelease(sampleBuffer); - return; - } - - if (m_adjustTime) { - CMTime currentTimestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer); - CMTime lastTimestamp = isVideoBuffer ? m_lastVideoTimestamp : m_lastAudioTimestamp; - - if (!CMTIME_IS_INVALID(lastTimestamp)) { - if (!CMTIME_IS_INVALID(m_timeOffset)) - currentTimestamp = CMTimeSubtract(currentTimestamp, m_timeOffset); - - CMTime pauseDuration = CMTimeSubtract(currentTimestamp, lastTimestamp); - - if (m_timeOffset.value == 0) - m_timeOffset = pauseDuration; - else - m_timeOffset = CMTimeAdd(m_timeOffset, pauseDuration); - } - m_lastVideoTimestamp = kCMTimeInvalid; - m_adjustTime = false; - } - - if (m_timeOffset.value > 0) { - CFRelease(sampleBuffer); - sampleBuffer = [self adjustTime:sampleBuffer by:m_timeOffset]; - } - - CMTime currentTimestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer); - CMTime currentDuration = CMSampleBufferGetDuration(sampleBuffer); - if (currentDuration.value > 0) - currentTimestamp = CMTimeAdd(currentTimestamp, currentDuration); - - if (isVideoBuffer) - { - m_lastVideoTimestamp = currentTimestamp; - dispatch_async(m_writerQueue, ^{ - [self writeVideoSampleBuffer:sampleBuffer]; - m_writeFirstAudioBuffer = true; - CFRelease(sampleBuffer); - }); - } else if (m_writeFirstAudioBuffer) { - m_lastAudioTimestamp = currentTimestamp; - dispatch_async(m_writerQueue, ^{ - [self writeAudioSampleBuffer:sampleBuffer]; - CFRelease(sampleBuffer); - }); - } -} - -- (bool)addWriterInputs -{ - Q_ASSERT(m_service && m_service->session()); - Q_ASSERT(m_assetWriter.data()); - - AVFCameraSession *session = m_service->session(); - - if (m_videoQueue) - { - Q_ASSERT(session->videoOutput() && session->videoOutput()->videoDataOutput()); - m_cameraWriterInput.reset([[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo - outputSettings:m_videoSettings - sourceFormatHint:session->videoCaptureDevice().activeFormat.formatDescription]); - - if (!m_cameraWriterInput) { - qDebugCamera() << Q_FUNC_INFO << "failed to create camera writer input"; - return false; - } - if ([m_assetWriter canAddInput:m_cameraWriterInput]) { - [m_assetWriter addInput:m_cameraWriterInput]; - } else { - qDebugCamera() << Q_FUNC_INFO << "failed to add camera writer input"; - m_cameraWriterInput.reset(); - return false; - } - - m_cameraWriterInput.data().expectsMediaDataInRealTime = YES; - } - - if (session->audioOutput()) { - CMFormatDescriptionRef sourceFormat = session->audioCaptureDevice() - ? session->audioCaptureDevice().activeFormat.formatDescription - : 0; - m_audioWriterInput.reset([[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio - outputSettings:m_audioSettings - sourceFormatHint:sourceFormat]); - if (!m_audioWriterInput) { - qWarning() << Q_FUNC_INFO << "failed to create audio writer input"; - // But we still can record video. - if (!m_cameraWriterInput) - return false; - } else if ([m_assetWriter canAddInput:m_audioWriterInput]) { - [m_assetWriter addInput:m_audioWriterInput]; - m_audioWriterInput.data().expectsMediaDataInRealTime = YES; - } else { - qWarning() << Q_FUNC_INFO << "failed to add audio writer input"; - m_audioWriterInput.reset(); - if (!m_cameraWriterInput) - return false; - // We can (still) write video though ... - } - } - - return true; -} - -- (void)setQueues -{ - Q_ASSERT(m_service && m_service->session()); - if (m_videoQueue) { - Q_ASSERT(m_service->session()->videoOutput() - && m_service->session()->videoOutput()->videoDataOutput()); - [m_service->session()->videoOutput()->videoDataOutput() setSampleBufferDelegate:self queue:m_videoQueue]; - } - - if (m_service->session()->audioOutput()) { - Q_ASSERT(m_audioQueue); - [m_service->session()->audioOutput() setSampleBufferDelegate:self queue:m_audioQueue]; - } -} - -- (void)updateDuration:(CMTime)newTimeStamp -{ - Q_ASSERT(CMTimeCompare(m_startTime, kCMTimeInvalid)); - Q_ASSERT(CMTimeCompare(m_lastTimeStamp, kCMTimeInvalid)); - if (CMTimeCompare(newTimeStamp, m_lastTimeStamp) > 0) { - - const CMTime duration = CMTimeSubtract(newTimeStamp, m_startTime); - if (!CMTimeCompare(duration, kCMTimeInvalid)) - return; - - m_durationInMs.storeRelease(CMTimeGetSeconds(duration) * 1000); - m_lastTimeStamp = newTimeStamp; - - m_delegate->updateDuration([self durationInMs]); - } -} - -- (qint64)durationInMs -{ - return m_durationInMs.loadAcquire(); -} - -@end diff --git a/src/multimedia/platform/darwin/camera/avfmediaassetwriter_p.h b/src/multimedia/platform/darwin/camera/avfmediaassetwriter_p.h deleted file mode 100644 index 6691b66a1..000000000 --- a/src/multimedia/platform/darwin/camera/avfmediaassetwriter_p.h +++ /dev/null @@ -1,90 +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$ -** -****************************************************************************/ - -#ifndef AVFMEDIAASSETWRITER_H -#define AVFMEDIAASSETWRITER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "avfcamerautility_p.h" -#include "qmediaformat.h" - -#include <QtCore/qglobal.h> - -#include <AVFoundation/AVFoundation.h> - -QT_BEGIN_NAMESPACE - -class AVFMediaEncoder; -class AVFCameraService; - -QT_END_NAMESPACE - -@interface QT_MANGLE_NAMESPACE(AVFMediaAssetWriter) : NSObject<AVCaptureVideoDataOutputSampleBufferDelegate, - AVCaptureAudioDataOutputSampleBufferDelegate> -- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(AVFMediaEncoder) *)delegate; - -- (bool)setupWithFileURL:(NSURL *)fileURL - cameraService:(QT_PREPEND_NAMESPACE(AVFCameraService) *)service - audioSettings:(NSDictionary *)audioSettings - videoSettings:(NSDictionary *)videoSettings - fileFormat:(QMediaFormat::FileFormat)fileFormat - transform:(CGAffineTransform)transform; - -// This to be called from the recorder control's thread: -- (void)start; -- (void)stop; -- (void)pause; -- (void)resume; -// This to be called from the recorder control's dtor: -- (void)abort; -- (qint64)durationInMs; - -@end - -#endif // AVFMEDIAASSETWRITER_H diff --git a/src/multimedia/platform/darwin/camera/avfmediaencoder.mm b/src/multimedia/platform/darwin/camera/avfmediaencoder.mm deleted file mode 100644 index 83e9c6d3d..000000000 --- a/src/multimedia/platform/darwin/camera/avfmediaencoder.mm +++ /dev/null @@ -1,649 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). -** 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$ -** -****************************************************************************/ - - -#include "avfmediaencoder_p.h" -#include "avfcamerarenderer_p.h" -#include "avfcamerasession_p.h" -#include "avfcamera_p.h" -#include "avfcameraservice_p.h" -#include "avfcameradebug_p.h" -#include "avfcamerautility_p.h" -#include "qaudiodevice.h" - -#include "qmediadevices.h" -#include "qmediastoragelocation_p.h" -#include "private/qmediarecorder_p.h" -#include "private/qdarwinformatsinfo_p.h" -#include "private/qplatformaudiooutput_p.h" - -#include <QtCore/qmath.h> -#include <QtCore/qdebug.h> -#include <QtCore/qmimetype.h> - -QT_USE_NAMESPACE - -namespace { - -bool qt_is_writable_file_URL(NSURL *fileURL) -{ - Q_ASSERT(fileURL); - - if (![fileURL isFileURL]) - return false; - - if (NSString *path = [[fileURL path] stringByExpandingTildeInPath]) { - return [[NSFileManager defaultManager] - isWritableFileAtPath:[path stringByDeletingLastPathComponent]]; - } - - return false; -} - -bool qt_file_exists(NSURL *fileURL) -{ - Q_ASSERT(fileURL); - - if (NSString *path = [[fileURL path] stringByExpandingTildeInPath]) - return [[NSFileManager defaultManager] fileExistsAtPath:path]; - - return false; -} - -} - -AVFMediaEncoder::AVFMediaEncoder(QMediaRecorder *parent) - : QObject(parent) - , QPlatformMediaEncoder(parent) - , m_state(QMediaRecorder::StoppedState) - , m_duration(0) - , m_audioSettings(nil) - , m_videoSettings(nil) - //, m_restoreFPS(-1, -1) -{ - m_writer.reset([[QT_MANGLE_NAMESPACE(AVFMediaAssetWriter) alloc] initWithDelegate:this]); - if (!m_writer) { - qDebugCamera() << Q_FUNC_INFO << "failed to create an asset writer"; - return; - } -} - -AVFMediaEncoder::~AVFMediaEncoder() -{ - [m_writer abort]; - - if (m_audioSettings) - [m_audioSettings release]; - if (m_videoSettings) - [m_videoSettings release]; -} - -bool AVFMediaEncoder::isLocationWritable(const QUrl &location) const -{ - return location.scheme() == QLatin1String("file") || location.scheme().isEmpty(); -} - -QMediaRecorder::RecorderState AVFMediaEncoder::state() const -{ - return m_state; -} - -qint64 AVFMediaEncoder::duration() const -{ - return m_duration; -} - -void AVFMediaEncoder::updateDuration(qint64 duration) -{ - m_duration = duration; - durationChanged(m_duration); -} - -static NSDictionary *avfAudioSettings(const QMediaEncoderSettings &encoderSettings) -{ - NSMutableDictionary *settings = [NSMutableDictionary dictionary]; - - int codecId = QDarwinFormatInfo::audioFormatForCodec(encoderSettings.mediaFormat().audioCodec()); - [settings setObject:[NSNumber numberWithInt:codecId] forKey:AVFormatIDKey]; - - // Setting AVEncoderQualityKey is not allowed when format ID is alac or lpcm - if (codecId != kAudioFormatAppleLossless && codecId != kAudioFormatLinearPCM - && encoderSettings.encodingMode() == QMediaRecorder::ConstantQualityEncoding) { - int quality; - switch (encoderSettings.quality()) { - case QMediaRecorder::VeryLowQuality: - quality = AVAudioQualityMin; - break; - case QMediaRecorder::LowQuality: - quality = AVAudioQualityLow; - break; - case QMediaRecorder::HighQuality: - quality = AVAudioQualityHigh; - break; - case QMediaRecorder::VeryHighQuality: - quality = AVAudioQualityMax; - break; - case QMediaRecorder::NormalQuality: - default: - quality = AVAudioQualityMedium; - break; - } - [settings setObject:[NSNumber numberWithInt:quality] forKey:AVEncoderAudioQualityKey]; - } else { - bool isBitRateSupported = false; - int bitRate = encoderSettings.audioBitRate(); - if (bitRate > 0) { - QList<AudioValueRange> bitRates = qt_supported_bit_rates_for_format(codecId); - for (int i = 0; i < bitRates.count(); i++) { - if (bitRate >= bitRates[i].mMinimum && - bitRate <= bitRates[i].mMaximum) { - isBitRateSupported = true; - break; - } - } - if (isBitRateSupported) - [settings setObject:[NSNumber numberWithInt:encoderSettings.audioBitRate()] - forKey:AVEncoderBitRateKey]; - } - } - - int sampleRate = encoderSettings.audioSampleRate(); - bool isSampleRateSupported = false; - if (sampleRate >= 8000 && sampleRate <= 192000) { - QList<AudioValueRange> sampleRates = qt_supported_sample_rates_for_format(codecId); - for (int i = 0; i < sampleRates.count(); i++) { - if (sampleRate >= sampleRates[i].mMinimum && sampleRate <= sampleRates[i].mMaximum) { - isSampleRateSupported = true; - break; - } - } - } - - int channelCount = encoderSettings.audioChannelCount(); - bool isChannelCountSupported = false; - if (channelCount > 0) { - std::optional<QList<UInt32>> channelCounts = qt_supported_channel_counts_for_format(codecId); - // An std::nullopt result indicates that - // any number of channels can be encoded. - if (channelCounts == std::nullopt) { - isChannelCountSupported = true; - } else { - for (int i = 0; i < channelCounts.value().count(); i++) { - if ((UInt32)channelCount == channelCounts.value()[i]) { - isChannelCountSupported = true; - break; - } - } - } - } - -#ifdef Q_OS_IOS - // Some keys are mandatory only on iOS - if (!isSampleRateSupported) { - sampleRate = 44100; - isSampleRateSupported = true; - } - if (!isChannelCountSupported) { - channelCount = 2; - isChannelCountSupported = true; - } - - if (codecId == kAudioFormatAppleLossless) - [settings setObject:[NSNumber numberWithInt:24] forKey:AVEncoderBitDepthHintKey]; - - if (codecId == kAudioFormatLinearPCM) { - [settings setObject:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey]; - [settings setObject:[NSNumber numberWithInt:NO] forKey:AVLinearPCMIsBigEndianKey]; - [settings setObject:[NSNumber numberWithInt:NO] forKey:AVLinearPCMIsFloatKey]; - [settings setObject:[NSNumber numberWithInt:NO] forKey:AVLinearPCMIsNonInterleaved]; - } -#endif - if (isSampleRateSupported) - [settings setObject:[NSNumber numberWithInt:sampleRate] forKey:AVSampleRateKey]; - if (isChannelCountSupported) - [settings setObject:[NSNumber numberWithInt:channelCount] forKey:AVNumberOfChannelsKey]; - - return settings; -} - -NSDictionary *avfVideoSettings(QMediaEncoderSettings &encoderSettings, AVCaptureDevice *device, AVCaptureConnection *connection, QSize nativeSize) -{ - if (!device) - return nil; - - - // ### re-add needFpsChange -// AVFPSRange currentFps = qt_current_framerates(device, connection); - - NSMutableDictionary *videoSettings = [NSMutableDictionary dictionary]; - - // -- Codec - - // AVVideoCodecKey is the only mandatory key - auto codec = encoderSettings.mediaFormat().videoCodec(); - NSString *c = QDarwinFormatInfo::videoFormatForCodec(codec); - [videoSettings setObject:c forKey:AVVideoCodecKey]; - [c release]; - - // -- Resolution - - int w = encoderSettings.videoResolution().width(); - int h = encoderSettings.videoResolution().height(); - - if (AVCaptureDeviceFormat *currentFormat = device.activeFormat) { - CMFormatDescriptionRef formatDesc = currentFormat.formatDescription; - CMVideoDimensions dim = CMVideoFormatDescriptionGetDimensions(formatDesc); - FourCharCode formatCodec = CMVideoFormatDescriptionGetCodecType(formatDesc); - - // We have to change the device's activeFormat in 3 cases: - // - the requested recording resolution is higher than the current device resolution - // - the requested recording resolution has a different aspect ratio than the current device aspect ratio - // - the requested frame rate is not available for the current device format - AVCaptureDeviceFormat *newFormat = nil; - if ((w <= 0 || h <= 0) - && encoderSettings.videoFrameRate() > 0 - && !qt_format_supports_framerate(currentFormat, encoderSettings.videoFrameRate())) { - - newFormat = qt_find_best_framerate_match(device, - formatCodec, - encoderSettings.videoFrameRate()); - - } else if (w > 0 && h > 0) { - AVCaptureDeviceFormat *f = qt_find_best_resolution_match(device, - encoderSettings.videoResolution(), - formatCodec); - - if (f) { - CMVideoDimensions d = CMVideoFormatDescriptionGetDimensions(f.formatDescription); - qreal fAspectRatio = qreal(d.width) / d.height; - - if (w > dim.width || h > dim.height - || qAbs((qreal(dim.width) / dim.height) - fAspectRatio) > 0.01) { - newFormat = f; - } - } - } - - if (qt_set_active_format(device, newFormat, false /*### !needFpsChange*/)) { - formatDesc = newFormat.formatDescription; - dim = CMVideoFormatDescriptionGetDimensions(formatDesc); - } - - if (w > 0 && h > 0) { - // Make sure the recording resolution has the same aspect ratio as the device's - // current resolution - qreal deviceAspectRatio = qreal(dim.width) / dim.height; - qreal recAspectRatio = qreal(w) / h; - if (qAbs(deviceAspectRatio - recAspectRatio) > 0.01) { - if (recAspectRatio > deviceAspectRatio) - w = qRound(h * deviceAspectRatio); - else - h = qRound(w / deviceAspectRatio); - } - - // recording resolution can't be higher than the device's active resolution - w = qMin(w, dim.width); - h = qMin(h, dim.height); - } - } - - if (w > 0 && h > 0) { - // Width and height must be divisible by 2 - w += w & 1; - h += h & 1; - - bool isPortrait = nativeSize.width() < nativeSize.height(); - // Make sure the video has the right aspect ratio - if (isPortrait && h < w) - qSwap(w, h); - else if (!isPortrait && w < h) - qSwap(w, h); - - encoderSettings.setVideoResolution(QSize(w, h)); - } else { - w = nativeSize.width(); - h = nativeSize.height(); - encoderSettings.setVideoResolution(nativeSize); - } - [videoSettings setObject:[NSNumber numberWithInt:w] forKey:AVVideoWidthKey]; - [videoSettings setObject:[NSNumber numberWithInt:h] forKey:AVVideoHeightKey]; - - // -- FPS - - if (true /*needFpsChange*/) { - const qreal fps = encoderSettings.videoFrameRate(); - qt_set_framerate_limits(device, connection, fps, fps); - } - encoderSettings.setVideoFrameRate(qt_current_framerates(device, connection).second); - - // -- Codec Settings - - NSMutableDictionary *codecProperties = [NSMutableDictionary dictionary]; - int bitrate = -1; - float quality = -1.f; - - if (encoderSettings.encodingMode() == QMediaRecorder::ConstantQualityEncoding) { - if (encoderSettings.quality() != QMediaRecorder::NormalQuality) { - if (codec != QMediaFormat::VideoCodec::MotionJPEG) { - qWarning("ConstantQualityEncoding is not supported for MotionJPEG"); - } else { - switch (encoderSettings.quality()) { - case QMediaRecorder::VeryLowQuality: - quality = 0.f; - break; - case QMediaRecorder::LowQuality: - quality = 0.25f; - break; - case QMediaRecorder::HighQuality: - quality = 0.75f; - break; - case QMediaRecorder::VeryHighQuality: - quality = 1.f; - break; - default: - quality = -1.f; // NormalQuality, let the system decide - break; - } - } - } - } else if (encoderSettings.encodingMode() == QMediaRecorder::AverageBitRateEncoding){ - if (codec != QMediaFormat::VideoCodec::H264 && codec != QMediaFormat::VideoCodec::H265) - qWarning() << "AverageBitRateEncoding is not supported for codec" << QMediaFormat::videoCodecName(codec); - else - bitrate = encoderSettings.videoBitRate(); - } else { - qWarning("Encoding mode is not supported"); - } - - if (bitrate != -1) - [codecProperties setObject:[NSNumber numberWithInt:bitrate] forKey:AVVideoAverageBitRateKey]; - if (quality != -1.f) - [codecProperties setObject:[NSNumber numberWithFloat:quality] forKey:AVVideoQualityKey]; - - [videoSettings setObject:codecProperties forKey:AVVideoCompressionPropertiesKey]; - - return videoSettings; -} - -void AVFMediaEncoder::applySettings(QMediaEncoderSettings &settings) -{ - AVFCameraSession *session = m_service->session(); - - // audio settings - m_audioSettings = avfAudioSettings(settings); - if (m_audioSettings) - [m_audioSettings retain]; - - // video settings - AVCaptureDevice *device = session->videoCaptureDevice(); - if (!device) - return; - const AVFConfigurationLock lock(device); // prevents activeFormat from being overridden - AVCaptureConnection *conn = [session->videoOutput()->videoDataOutput() connectionWithMediaType:AVMediaTypeVideo]; - auto nativeSize = session->videoOutput()->nativeSize(); - m_videoSettings = avfVideoSettings(settings, device, conn, nativeSize); - if (m_videoSettings) - [m_videoSettings retain]; -} - -void AVFMediaEncoder::unapplySettings() -{ - if (m_audioSettings) { - [m_audioSettings release]; - m_audioSettings = nil; - } - if (m_videoSettings) { - [m_videoSettings release]; - m_videoSettings = nil; - } -} - -void AVFMediaEncoder::setMetaData(const QMediaMetaData &metaData) -{ - m_metaData = metaData; -} - -QMediaMetaData AVFMediaEncoder::metaData() const -{ - return m_metaData; -} - -void AVFMediaEncoder::setCaptureSession(QPlatformMediaCaptureSession *session) -{ - AVFCameraService *captureSession = static_cast<AVFCameraService *>(session); - if (m_service == captureSession) - return; - - if (m_service) - stop(); - - m_service = captureSession; - if (!m_service) - return; - - connect(m_service, &AVFCameraService::cameraChanged, this, &AVFMediaEncoder::onCameraChanged); - onCameraChanged(); -} - -void AVFMediaEncoder::record(QMediaEncoderSettings &settings) -{ - if (!m_service || !m_service->session()) { - qWarning() << Q_FUNC_INFO << "Encoder is not set to a capture session"; - return; - } - - if (!m_writer) { - qDebugCamera() << Q_FUNC_INFO << "Invalid recorder"; - return; - } - - if (QMediaRecorder::RecordingState == m_state) - return; - - m_service->session()->setActive(true); - const bool audioOnly = settings.videoCodec() == QMediaFormat::VideoCodec::Unspecified; - AVCaptureSession *session = m_service->session()->captureSession(); - float rotation = 0; - - if (!audioOnly) { - AVFCamera *cameraControl = m_service->avfCameraControl(); - if (!cameraControl || !cameraControl->isActive()) { - qDebugCamera() << Q_FUNC_INFO << "can not start record while camera is not active"; - Q_EMIT error(QMediaRecorder::ResourceError, - QMediaRecorderPrivate::msgFailedStartRecording()); - return; - } - } - - const QString path(outputLocation().scheme() == QLatin1String("file") ? - outputLocation().path() : outputLocation().toString()); - const QUrl fileURL(QUrl::fromLocalFile(QMediaStorageLocation::generateFileName(path, - audioOnly ? QStandardPaths::MusicLocation : QStandardPaths::MoviesLocation, - settings.mimeType().preferredSuffix()))); - - NSURL *nsFileURL = fileURL.toNSURL(); - if (!nsFileURL) { - qWarning() << Q_FUNC_INFO << "invalid output URL:" << fileURL; - Q_EMIT error(QMediaRecorder::ResourceError, tr("Invalid output file URL")); - return; - } - if (!qt_is_writable_file_URL(nsFileURL)) { - qWarning() << Q_FUNC_INFO << "invalid output URL:" << fileURL - << "(the location is not writable)"; - Q_EMIT error(QMediaRecorder::ResourceError, tr("Non-writeable file location")); - return; - } - if (qt_file_exists(nsFileURL)) { - // We test for/handle this error here since AWAssetWriter will raise an - // Objective-C exception, which is not good at all. - qWarning() << Q_FUNC_INFO << "invalid output URL:" << fileURL - << "(file already exists)"; - Q_EMIT error(QMediaRecorder::ResourceError, tr("File already exists")); - return; - } - - applySettings(settings); - - QVideoOutputOrientationHandler::setIsRecording(true); - - // We stop session now so that no more frames for renderer's queue - // generated, will restart in assetWriterStarted. - [session stopRunning]; - - if ([m_writer setupWithFileURL:nsFileURL - cameraService:m_service - audioSettings:m_audioSettings - videoSettings:m_videoSettings - fileFormat:settings.fileFormat() - transform:CGAffineTransformMakeRotation(qDegreesToRadians(rotation))]) { - - m_state = QMediaRecorder::RecordingState; - - Q_EMIT actualLocationChanged(fileURL); - Q_EMIT stateChanged(m_state); - - // Apple recommends to call startRunning and do all - // setup on a special queue, and that's what we had - // initially (dispatch_async to writerQueue). Unfortunately, - // writer's queue is not the only queue/thread that can - // access/modify the session, and as a result we have - // all possible data/race-conditions with Obj-C exceptions - // at best and something worse in general. - // Now we try to only modify session on the same thread. - [m_writer start]; - } else { - [session startRunning]; - Q_EMIT error(QMediaRecorder::FormatError, - QMediaRecorderPrivate::msgFailedStartRecording()); - } -} - -void AVFMediaEncoder::pause() -{ - if (!m_service || !m_service->session() || state() != QMediaRecorder::RecordingState) - return; - - toggleRecord(false); - m_state = QMediaRecorder::PausedState; - stateChanged(m_state); -} - -void AVFMediaEncoder::resume() -{ - if (!m_service || !m_service->session() || state() != QMediaRecorder::PausedState) - return; - - toggleRecord(true); - m_state = QMediaRecorder::RecordingState; - stateChanged(m_state); -} - -void AVFMediaEncoder::stop() -{ - if (m_state != QMediaRecorder::StoppedState) { - // Do not check the camera status, we can stop if we started. - stopWriter(); - } - QVideoOutputOrientationHandler::setIsRecording(false); -} - - -void AVFMediaEncoder::toggleRecord(bool enable) -{ - if (!m_service || !m_service->session()) - return; - - if (!enable) - [m_writer pause]; - else - [m_writer resume]; -} - -void AVFMediaEncoder::assetWriterStarted() -{ -} - -void AVFMediaEncoder::assetWriterFinished() -{ - Q_ASSERT(m_service && m_service->session()); - AVFCameraSession *session = m_service->session(); - - const QMediaRecorder::RecorderState lastState = m_state; - - unapplySettings(); - - if (session->videoOutput()) { - session->videoOutput()->resetCaptureDelegate(); - } - if (session->audioPreviewDelegate()) { - [session->audioPreviewDelegate() resetAudioPreviewDelegate]; - } - [session->captureSession() startRunning]; - - m_state = QMediaRecorder::StoppedState; - if (m_state != lastState) - Q_EMIT stateChanged(m_state); -} - -void AVFMediaEncoder::onCameraChanged() -{ - if (m_service && m_service->avfCameraControl()) { - AVFCamera *cameraControl = m_service->avfCameraControl(); - connect(cameraControl, SIGNAL(activeChanged(bool)), - SLOT(cameraActiveChanged(bool))); - } -} - -void AVFMediaEncoder::cameraActiveChanged(bool active) -{ - Q_ASSERT(m_service); - AVFCamera *cameraControl = m_service->avfCameraControl(); - Q_ASSERT(cameraControl); - - if (!active) { - return stopWriter(); - } -} - -void AVFMediaEncoder::stopWriter() -{ - [m_writer stop]; -} - -#include "moc_avfmediaencoder_p.cpp" diff --git a/src/multimedia/platform/darwin/camera/avfmediaencoder_p.h b/src/multimedia/platform/darwin/camera/avfmediaencoder_p.h deleted file mode 100644 index 9e80c4c13..000000000 --- a/src/multimedia/platform/darwin/camera/avfmediaencoder_p.h +++ /dev/null @@ -1,131 +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$ -** -****************************************************************************/ - -#ifndef AVFMEDIAENCODER_H -#define AVFMEDIAENCODER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "avfmediaassetwriter_p.h" -#include "avfcamerautility_p.h" -#include "qaudiodevice.h" - -#include <private/qplatformmediaencoder_p.h> -#include <private/qplatformmediacapture_p.h> -#include <QtMultimedia/qmediametadata.h> - -#include <QtCore/qglobal.h> -#include <QtCore/qurl.h> - -#include <AVFoundation/AVFoundation.h> - -QT_BEGIN_NAMESPACE - -class AVFCameraService; -class QString; -class QUrl; - -class AVFMediaEncoder : public QObject, public QPlatformMediaEncoder -{ - Q_OBJECT -public: - AVFMediaEncoder(QMediaRecorder *parent); - ~AVFMediaEncoder() override; - - bool isLocationWritable(const QUrl &location) const override; - - QMediaRecorder::RecorderState state() const override; - - qint64 duration() const override; - - void record(QMediaEncoderSettings &settings) override; - void pause() override; - void resume() override; - void stop() override; - - void setMetaData(const QMediaMetaData &) override; - QMediaMetaData metaData() const override; - - AVFCameraService *cameraService() const { return m_service; } - - void setCaptureSession(QPlatformMediaCaptureSession *session); - - void updateDuration(qint64 duration); - - void toggleRecord(bool enable); - -private: - void applySettings(QMediaEncoderSettings &settings); - void unapplySettings(); - - Q_INVOKABLE void assetWriterStarted(); - Q_INVOKABLE void assetWriterFinished(); - -private Q_SLOTS: - void onCameraChanged(); - void cameraActiveChanged(bool); - -private: - void stopWriter(); - - AVFCameraService *m_service = nullptr; - AVFScopedPointer<QT_MANGLE_NAMESPACE(AVFMediaAssetWriter)> m_writer; - - QMediaRecorder::RecorderState m_state; - - QMediaMetaData m_metaData; - - qint64 m_duration; - - NSDictionary *m_audioSettings; - NSDictionary *m_videoSettings; -}; - -QT_END_NAMESPACE - -#endif // AVFMEDIAENCODER_H diff --git a/src/multimedia/platform/darwin/common/avfmetadata.mm b/src/multimedia/platform/darwin/common/avfmetadata.mm deleted file mode 100644 index ea95bcea6..000000000 --- a/src/multimedia/platform/darwin/common/avfmetadata.mm +++ /dev/null @@ -1,398 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). -** 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$ -** -****************************************************************************/ - -#include "avfmetadata_p.h" -#include <private/qdarwinformatsinfo_p.h> - -#include <QtCore/qbuffer.h> -#include <QtCore/qiodevice.h> -#include <QtCore/qdatetime.h> -#include <QtCore/qlocale.h> -#include <QtCore/qurl.h> -#include <QImage> - -#if QT_HAS_INCLUDE(<AppKit/AppKit.h>) -#include <AppKit/AppKit.h> -#endif - -#include <CoreFoundation/CoreFoundation.h> - -QT_USE_NAMESPACE - -struct AVMetadataIDs { - AVMetadataIdentifier common; - AVMetadataIdentifier iTunes; - AVMetadataIdentifier quickTime; - AVMetadataIdentifier ID3; - AVMetadataIdentifier quickTimeUserData; - AVMetadataIdentifier isoUserData; -}; - -const AVMetadataIDs keyToAVMetaDataID[] = { - // Title - { AVMetadataCommonIdentifierTitle, AVMetadataIdentifieriTunesMetadataSongName, - AVMetadataIdentifierQuickTimeMetadataTitle, - AVMetadataIdentifierID3MetadataTitleDescription, - nil, AVMetadata3GPUserDataKeyTitle }, - // Author - { AVMetadataCommonIdentifierAuthor,AVMetadataIdentifieriTunesMetadataAuthor, - AVMetadataIdentifierQuickTimeMetadataAuthor, nil, - AVMetadataQuickTimeUserDataKeyAuthor, AVMetadata3GPUserDataKeyAuthor }, - // Comment - { nil, AVMetadataIdentifieriTunesMetadataUserComment, - AVMetadataIdentifierQuickTimeMetadataComment, AVMetadataIdentifierID3MetadataComments, - AVMetadataQuickTimeUserDataKeyComment, nil }, - // Description - { AVMetadataCommonIdentifierDescription,AVMetadataIdentifieriTunesMetadataDescription, - AVMetadataIdentifierQuickTimeMetadataDescription, nil, - AVMetadataQuickTimeUserDataKeyDescription, AVMetadata3GPUserDataKeyDescription }, - // Genre - { nil, AVMetadataIdentifieriTunesMetadataUserGenre, - AVMetadataIdentifierQuickTimeMetadataGenre, nil, - AVMetadataQuickTimeUserDataKeyGenre, AVMetadata3GPUserDataKeyGenre }, - // Date - { AVMetadataCommonIdentifierCreationDate, AVMetadataIdentifieriTunesMetadataReleaseDate, - AVMetadataIdentifierQuickTimeMetadataCreationDate, AVMetadataIdentifierID3MetadataDate, - AVMetadataQuickTimeUserDataKeyCreationDate, AVMetadataISOUserDataKeyDate }, - // Language - { AVMetadataCommonIdentifierLanguage, nil, nil, AVMetadataIdentifierID3MetadataLanguage, nil, nil }, - // Publisher - { AVMetadataCommonIdentifierPublisher, AVMetadataIdentifieriTunesMetadataPublisher, - AVMetadataIdentifierQuickTimeMetadataPublisher, AVMetadataIdentifierID3MetadataPublisher, nil, nil }, - // Copyright - { AVMetadataCommonIdentifierCopyrights, AVMetadataIdentifieriTunesMetadataCopyright, - AVMetadataIdentifierQuickTimeMetadataCopyright, AVMetadataIdentifierID3MetadataCopyright, - AVMetadataQuickTimeUserDataKeyCopyright, AVMetadataISOUserDataKeyCopyright }, - // Url - { nil, nil, nil, AVMetadataIdentifierID3MetadataOfficialAudioSourceWebpage, nil, nil }, - // Duration - { nil, nil, nil, AVMetadataIdentifierID3MetadataLength, nil, nil }, - // MediaType - { AVMetadataCommonIdentifierType, nil, nil, AVMetadataIdentifierID3MetadataContentType, nil, nil }, - // FileFormat - { nil, nil, nil, AVMetadataIdentifierID3MetadataFileType, nil, nil }, - // AudioBitRate - { nil, nil, nil, nil, nil, nil }, - // AudioCodec - { nil, nil, nil, nil, nil, nil }, - // VideoBitRate - { nil, nil, nil, nil, nil, nil }, - // VideoCodec - { nil, nil, nil, nil, nil, nil }, - // VideoFrameRate - { nil, nil, AVMetadataIdentifierQuickTimeMetadataCameraFrameReadoutTime, nil, nil, nil }, - // AlbumTitle - { AVMetadataCommonIdentifierAlbumName, AVMetadataIdentifieriTunesMetadataAlbum, - AVMetadataIdentifierQuickTimeMetadataAlbum, AVMetadataIdentifierID3MetadataAlbumTitle, - AVMetadataQuickTimeUserDataKeyAlbum, AVMetadata3GPUserDataKeyAlbumAndTrack }, - // AlbumArtist - { nil, AVMetadataIdentifieriTunesMetadataAlbumArtist, nil, nil, - AVMetadataQuickTimeUserDataKeyArtist, AVMetadata3GPUserDataKeyPerformer }, - // ContributingArtist - { AVMetadataCommonIdentifierArtist, AVMetadataIdentifieriTunesMetadataArtist, - AVMetadataIdentifierQuickTimeMetadataArtist, nil, nil, nil }, - // TrackNumber - { nil, AVMetadataIdentifieriTunesMetadataTrackNumber, - nil, AVMetadataIdentifierID3MetadataTrackNumber, nil, nil }, - // Composer - { nil, AVMetadataIdentifieriTunesMetadataComposer, - AVMetadataIdentifierQuickTimeMetadataComposer, AVMetadataIdentifierID3MetadataComposer, nil, nil }, - // LeadPerformer - { nil, AVMetadataIdentifieriTunesMetadataPerformer, - AVMetadataIdentifierQuickTimeMetadataPerformer, AVMetadataIdentifierID3MetadataLeadPerformer, nil, nil }, - // ThumbnailImage - { nil, nil, nil, AVMetadataIdentifierID3MetadataAttachedPicture, nil, nil }, - // CoverArtImage - { AVMetadataCommonIdentifierArtwork, AVMetadataIdentifieriTunesMetadataCoverArt, - AVMetadataIdentifierQuickTimeMetadataArtwork, nil, nil, nil }, - // Orientation - { nil, nil, AVMetadataIdentifierQuickTimeMetadataDirectionFacing, nil, nil, nil }, - // Resolution - { nil, nil, nil, nil, nil, nil } -}; - -static AVMetadataIdentifier toIdentifier(QMediaMetaData::Key key, AVMetadataKeySpace keySpace) -{ - static_assert(sizeof(keyToAVMetaDataID)/sizeof(AVMetadataIDs) == QMediaMetaData::Key::Resolution + 1); - - AVMetadataIdentifier identifier = nil; - if ([keySpace isEqualToString:AVMetadataKeySpaceiTunes]) { - identifier = keyToAVMetaDataID[key].iTunes; - } else if ([keySpace isEqualToString:AVMetadataKeySpaceID3]) { - identifier = keyToAVMetaDataID[key].ID3; - } else if ([keySpace isEqualToString:AVMetadataKeySpaceQuickTimeMetadata]) { - identifier = keyToAVMetaDataID[key].quickTime; - } else { - identifier = keyToAVMetaDataID[key].common; - } - return identifier; -} - -static std::optional<QMediaMetaData::Key> toKey(AVMetadataItem *item) -{ - static_assert(sizeof(keyToAVMetaDataID)/sizeof(AVMetadataIDs) == QMediaMetaData::Key::Resolution + 1); - - // The item identifier may be different than the ones we support, - // so check by common key first, as it will get the metadata - // irrespective of the format. - AVMetadataKey commonKey = item.commonKey; - if (commonKey.length != 0) { - if ([commonKey isEqualToString:AVMetadataCommonKeyTitle]) { - return QMediaMetaData::Title; - } else if ([commonKey isEqualToString:AVMetadataCommonKeyDescription]) { - return QMediaMetaData::Description; - } else if ([commonKey isEqualToString:AVMetadataCommonKeyPublisher]) { - return QMediaMetaData::Publisher; - } else if ([commonKey isEqualToString:AVMetadataCommonKeyCreationDate]) { - return QMediaMetaData::Date; - } else if ([commonKey isEqualToString:AVMetadataCommonKeyType]) { - return QMediaMetaData::MediaType; - } else if ([commonKey isEqualToString:AVMetadataCommonKeyLanguage]) { - return QMediaMetaData::Language; - } else if ([commonKey isEqualToString:AVMetadataCommonKeyCopyrights]) { - return QMediaMetaData::Copyright; - } else if ([commonKey isEqualToString:AVMetadataCommonKeyAlbumName]) { - return QMediaMetaData::AlbumTitle; - } else if ([commonKey isEqualToString:AVMetadataCommonKeyAuthor]) { - return QMediaMetaData::Author; - } else if ([commonKey isEqualToString:AVMetadataCommonKeyArtist]) { - return QMediaMetaData::ContributingArtist; - } - } - - // Check by identifier if no common key found - // No need to check for the common keySpace since there's no common key - enum keySpaces { iTunes, QuickTime, QuickTimeUserData, IsoUserData, ID3, Other } itemKeySpace; - itemKeySpace = Other; - AVMetadataKeySpace keySpace = [item keySpace]; - AVMetadataIdentifier identifier = [item identifier]; - - if ([keySpace isEqualToString:AVMetadataKeySpaceiTunes]) { - itemKeySpace = iTunes; - } else if ([keySpace isEqualToString:AVMetadataKeySpaceQuickTimeMetadata]) { - itemKeySpace = QuickTime; - } else if ([keySpace isEqualToString:AVMetadataKeySpaceQuickTimeUserData]) { - itemKeySpace = QuickTimeUserData; - } else if ([keySpace isEqualToString:AVMetadataKeySpaceISOUserData]) { - itemKeySpace = IsoUserData; - } else if (([keySpace isEqualToString:AVMetadataKeySpaceID3])) { - itemKeySpace = ID3; - } - - for (int key = 0; key < QMediaMetaData::Resolution + 1; key++) { - AVMetadataIdentifier idForKey = nil; - switch (itemKeySpace) { - case iTunes: - idForKey = keyToAVMetaDataID[key].iTunes; - break; - case QuickTime: - idForKey = keyToAVMetaDataID[key].quickTime; - break; - case ID3: - idForKey = keyToAVMetaDataID[key].ID3; - break; - case QuickTimeUserData: - idForKey = keyToAVMetaDataID[key].quickTimeUserData; - break; - case IsoUserData: - idForKey = keyToAVMetaDataID[key].isoUserData; - break; - default: - break; - } - - if ([identifier isEqualToString:idForKey]) - return QMediaMetaData::Key(key); - } - - return std::nullopt; -} - -static QMediaMetaData fromAVMetadata(NSArray *metadataItems) -{ - QMediaMetaData metadata; - - for (AVMetadataItem* item in metadataItems) { - auto key = toKey(item); - if (!key) - continue; - - const QString value = QString::fromNSString([item stringValue]); - if (!value.isNull()) - metadata.insert(*key, value); - } - return metadata; -} - -QMediaMetaData AVFMetaData::fromAsset(AVAsset *asset) -{ -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO; -#endif - QMediaMetaData metadata = fromAVMetadata([asset metadata]); - - // add duration - const CMTime time = [asset duration]; - const qint64 duration = static_cast<qint64>(float(time.value) / float(time.timescale) * 1000.0f); - metadata.insert(QMediaMetaData::Duration, duration); - - return metadata; -} - -QMediaMetaData AVFMetaData::fromAssetTrack(AVAssetTrack *asset) -{ - QMediaMetaData metadata = fromAVMetadata([asset metadata]); - if (metadata.value(QMediaMetaData::Language).isNull()) { - auto *languageCode = asset.languageCode; - if (languageCode) { - // languageCode is encoded as ISO 639-2, which QLocale does not handle. - // Convert it to 639-1 first. - auto id = CFLocaleCreateCanonicalLanguageIdentifierFromString(kCFAllocatorDefault, - (__bridge CFStringRef)languageCode); - QString lang = QString::fromCFString(id); - CFRelease(id); - metadata.insert(QMediaMetaData::Language, QLocale::codeToLanguage(lang)); - } - } - return metadata; -} - -static AVMutableMetadataItem *setAVMetadataItemForKey(QMediaMetaData::Key key, const QVariant &value, - AVMetadataKeySpace keySpace = AVMetadataKeySpaceCommon) -{ - AVMetadataIdentifier identifier = toIdentifier(key, keySpace); - if (!identifier.length) - return nil; - - AVMutableMetadataItem *item = [AVMutableMetadataItem metadataItem]; - item.keySpace = keySpace; - item.identifier = identifier; - - switch (key) { - case QMediaMetaData::ThumbnailImage: - case QMediaMetaData::CoverArtImage: { -#if defined(Q_OS_MACOS) - QImage img = value.value<QImage>(); - if (!img.isNull()) { - QByteArray arr; - QBuffer buffer(&arr); - buffer.open(QIODevice::WriteOnly); - img.save(&buffer); - NSData *data = arr.toNSData(); - NSImage *nsImg = [[NSImage alloc] initWithData:data]; - item.value = nsImg; - [nsImg release]; - } -#endif - break; - } - case QMediaMetaData::FileFormat: { - QMediaFormat::FileFormat qtFormat = value.value<QMediaFormat::FileFormat>(); - AVFileType avFormat = QDarwinFormatInfo::avFileTypeForContainerFormat(qtFormat); - item.value = avFormat; - break; - } - case QMediaMetaData::Language: { - QString lang = QLocale::languageToCode(value.value<QLocale::Language>()); - if (!lang.isEmpty()) - item.value = lang.toNSString(); - break; - } - default: { - switch (value.typeId()) { - case QMetaType::QString: { - item.value = value.toString().toNSString(); - break; - } - case QMetaType::Int: { - item.value = [NSNumber numberWithInt:value.toInt()]; - break; - } - case QMetaType::LongLong: { - item.value = [NSNumber numberWithLongLong:value.toLongLong()]; - break; - } - case QMetaType::Double: { - item.value = [NSNumber numberWithDouble:value.toDouble()]; - break; - } - case QMetaType::QDate: - case QMetaType::QDateTime: { - item.value = value.toDateTime().toNSDate(); - break; - } - case QMetaType::QUrl: { - item.value = value.toUrl().toNSURL(); - break; - } - default: - break; - } - } - } - - return item; -} - -NSMutableArray<AVMetadataItem *> *AVFMetaData::toAVMetadataForFormat(QMediaMetaData metadata, AVFileType format) -{ - NSMutableArray<AVMetadataKeySpace> *keySpaces = [NSMutableArray<AVMetadataKeySpace> array]; - if (format == AVFileTypeAppleM4A) { - [keySpaces addObject:AVMetadataKeySpaceiTunes]; - } else if (format == AVFileTypeMPEGLayer3) { - [keySpaces addObject:AVMetadataKeySpaceID3]; - [keySpaces addObject:AVMetadataKeySpaceiTunes]; - } else if (format == AVFileTypeQuickTimeMovie) { - [keySpaces addObject:AVMetadataKeySpaceQuickTimeMetadata]; - } else { - [keySpaces addObject:AVMetadataKeySpaceCommon]; - } - NSMutableArray<AVMetadataItem *> *avMetaDataArr = [NSMutableArray array]; - for (const auto &key : metadata.keys()) { - for (NSUInteger i = 0; i < [keySpaces count]; i++) { - const QVariant &value = metadata.value(key); - // set format-specific metadata - AVMetadataItem *item = setAVMetadataItemForKey(key, value, keySpaces[i]); - if (item) - [avMetaDataArr addObject:item]; - } - } - return avMetaDataArr; -} - diff --git a/src/multimedia/platform/darwin/common/avfmetadata_p.h b/src/multimedia/platform/darwin/common/avfmetadata_p.h deleted file mode 100644 index e983be531..000000000 --- a/src/multimedia/platform/darwin/common/avfmetadata_p.h +++ /dev/null @@ -1,73 +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$ -** -****************************************************************************/ - -#ifndef AVFMEDIAPLAYERMETADATACONTROL_H -#define AVFMEDIAPLAYERMETADATACONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtMultimedia/QMediaMetaData> -#include <QtCore/qvariant.h> - -#import <AVFoundation/AVFoundation.h> - -QT_BEGIN_NAMESPACE - -class AVFMediaPlayer; - -class AVFMetaData -{ -public: - static QMediaMetaData fromAsset(AVAsset *asset); - static QMediaMetaData fromAssetTrack(AVAssetTrack *asset); - static NSMutableArray<AVMetadataItem *> *toAVMetadataForFormat(QMediaMetaData metaData, AVFileType format); -}; - -QT_END_NAMESPACE - -#endif // AVFMEDIAPLAYERMETADATACONTROL_H diff --git a/src/multimedia/platform/darwin/mediaplayer/avfdisplaylink.mm b/src/multimedia/platform/darwin/mediaplayer/avfdisplaylink.mm deleted file mode 100644 index 64b625f0e..000000000 --- a/src/multimedia/platform/darwin/mediaplayer/avfdisplaylink.mm +++ /dev/null @@ -1,241 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). -** 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$ -** -****************************************************************************/ - -#include "avfdisplaylink_p.h" -#include <QtCore/qcoreapplication.h> - -#ifdef QT_DEBUG_AVF -#include <QtCore/qdebug.h> -#endif - -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) -#import <QuartzCore/CADisplayLink.h> -#import <Foundation/NSRunLoop.h> -#define _m_displayLink static_cast<DisplayLinkObserver*>(m_displayLink) -#else -#endif - -QT_USE_NAMESPACE - -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) -@interface DisplayLinkObserver : NSObject - -- (void)start; -- (void)stop; -- (void)displayLinkNotification:(CADisplayLink *)sender; - -@end - -@implementation DisplayLinkObserver -{ - AVFDisplayLink *m_avfDisplayLink; - CADisplayLink *m_displayLink; -} - -- (id)initWithAVFDisplayLink:(AVFDisplayLink *)link -{ - self = [super init]; - - if (self) { - m_avfDisplayLink = link; - m_displayLink = [[CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkNotification:)] retain]; - } - - return self; -} - -- (void) dealloc -{ - if (m_displayLink) { - [m_displayLink release]; - m_displayLink = nullptr; - } - - [super dealloc]; -} - -- (void)start -{ - [m_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; -} - -- (void)stop -{ - [m_displayLink removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; -} - -- (void)displayLinkNotification:(CADisplayLink *)sender -{ - Q_UNUSED(sender); - m_avfDisplayLink->displayLinkEvent(nullptr); -} - -@end -#else -static CVReturn CVDisplayLinkCallback(CVDisplayLinkRef displayLink, - const CVTimeStamp *inNow, - const CVTimeStamp *inOutputTime, - CVOptionFlags flagsIn, - CVOptionFlags *flagsOut, - void *displayLinkContext) -{ - Q_UNUSED(displayLink); - Q_UNUSED(inNow); - Q_UNUSED(flagsIn); - Q_UNUSED(flagsOut); - - AVFDisplayLink *link = (AVFDisplayLink *)displayLinkContext; - - link->displayLinkEvent(inOutputTime); - return kCVReturnSuccess; -} -#endif - -AVFDisplayLink::AVFDisplayLink(QObject *parent) - : QObject(parent) - , m_displayLink(nullptr) - , m_pendingDisplayLinkEvent(false) - , m_isActive(false) -{ -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) - m_displayLink = [[DisplayLinkObserver alloc] initWithAVFDisplayLink:this]; -#else - // create display link for the main display - CVDisplayLinkCreateWithCGDisplay(kCGDirectMainDisplay, &m_displayLink); - if (m_displayLink) { - // set the current display of a display link. - CVDisplayLinkSetCurrentCGDisplay(m_displayLink, kCGDirectMainDisplay); - - // set the renderer output callback function - CVDisplayLinkSetOutputCallback(m_displayLink, &CVDisplayLinkCallback, this); - } -#endif -} - -AVFDisplayLink::~AVFDisplayLink() -{ -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO; -#endif - - if (m_displayLink) { - stop(); -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) - [_m_displayLink release]; -#else - CVDisplayLinkRelease(m_displayLink); -#endif - m_displayLink = nullptr; - } -} - -bool AVFDisplayLink::isValid() const -{ - return m_displayLink != nullptr; -} - -bool AVFDisplayLink::isActive() const -{ - return m_isActive; -} - -void AVFDisplayLink::start() -{ - if (m_displayLink && !m_isActive) { -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) - [_m_displayLink start]; -#else - CVDisplayLinkStart(m_displayLink); -#endif - m_isActive = true; - } -} - -void AVFDisplayLink::stop() -{ - if (m_displayLink && m_isActive) { -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) - [_m_displayLink stop]; -#else - CVDisplayLinkStop(m_displayLink); -#endif - m_isActive = false; - } -} - -void AVFDisplayLink::displayLinkEvent(const CVTimeStamp *ts) -{ - // This function is called from a - // thread != gui thread. So we post the event. - // But we need to make sure that we don't post faster - // than the event loop can eat: - m_displayLinkMutex.lock(); - bool pending = m_pendingDisplayLinkEvent; - m_pendingDisplayLinkEvent = true; -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) - Q_UNUSED(ts); - memset(&m_frameTimeStamp, 0, sizeof(CVTimeStamp)); -#else - m_frameTimeStamp = *ts; -#endif - m_displayLinkMutex.unlock(); - - if (!pending) - qApp->postEvent(this, new QEvent(QEvent::User), Qt::HighEventPriority); -} - -bool AVFDisplayLink::event(QEvent *event) -{ - switch (event->type()){ - case QEvent::User: { - m_displayLinkMutex.lock(); - m_pendingDisplayLinkEvent = false; - CVTimeStamp ts = m_frameTimeStamp; - m_displayLinkMutex.unlock(); - - Q_EMIT tick(ts); - - return false; - } - break; - default: - break; - } - return QObject::event(event); -} diff --git a/src/multimedia/platform/darwin/mediaplayer/avfdisplaylink_p.h b/src/multimedia/platform/darwin/mediaplayer/avfdisplaylink_p.h deleted file mode 100644 index 6b95e1e07..000000000 --- a/src/multimedia/platform/darwin/mediaplayer/avfdisplaylink_p.h +++ /dev/null @@ -1,101 +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$ -** -****************************************************************************/ - -#ifndef AVFDISPLAYLINK_H -#define AVFDISPLAYLINK_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qobject.h> -#include <QtCore/qmutex.h> - -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) -#include <CoreVideo/CVBase.h> -#else -#include <QuartzCore/CVDisplayLink.h> -#endif - -QT_BEGIN_NAMESPACE - -class AVFDisplayLink : public QObject -{ - Q_OBJECT -public: - explicit AVFDisplayLink(QObject *parent = nullptr); - virtual ~AVFDisplayLink(); - bool isValid() const; - bool isActive() const; - -public Q_SLOTS: - void start(); - void stop(); - -Q_SIGNALS: - void tick(const CVTimeStamp &ts); - -public: - void displayLinkEvent(const CVTimeStamp *); - -protected: - virtual bool event(QEvent *) override; - -private: -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) - void *m_displayLink; -#else - CVDisplayLinkRef m_displayLink; -#endif - QMutex m_displayLinkMutex; - bool m_pendingDisplayLinkEvent; - bool m_isActive; - CVTimeStamp m_frameTimeStamp; -}; - -QT_END_NAMESPACE - -#endif // AVFDISPLAYLINK_H diff --git a/src/multimedia/platform/darwin/mediaplayer/avfmediaplayer.mm b/src/multimedia/platform/darwin/mediaplayer/avfmediaplayer.mm deleted file mode 100644 index 2cd61a42e..000000000 --- a/src/multimedia/platform/darwin/mediaplayer/avfmediaplayer.mm +++ /dev/null @@ -1,1161 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). -** 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$ -** -****************************************************************************/ - -#include "avfmediaplayer_p.h" -#include "avfmediaplayer_p.h" -#include "avfvideorenderercontrol_p.h" -#include <private/avfvideosink_p.h> -#include <private/avfmetadata_p.h> - -#include "qaudiooutput.h" -#include "qplatformaudiooutput_p.h" - -#include <qpointer.h> -#include <QFileInfo> - -#import <AVFoundation/AVFoundation.h> - -QT_USE_NAMESPACE - -//AVAsset Keys -static NSString* const AVF_TRACKS_KEY = @"tracks"; -static NSString* const AVF_PLAYABLE_KEY = @"playable"; - -//AVPlayerItem keys -static NSString* const AVF_STATUS_KEY = @"status"; -static NSString* const AVF_BUFFER_LIKELY_KEEP_UP_KEY = @"playbackLikelyToKeepUp"; - -//AVPlayer keys -static NSString* const AVF_RATE_KEY = @"rate"; -static NSString* const AVF_CURRENT_ITEM_KEY = @"currentItem"; -static NSString* const AVF_CURRENT_ITEM_DURATION_KEY = @"currentItem.duration"; - -static void *AVFMediaPlayerObserverRateObservationContext = &AVFMediaPlayerObserverRateObservationContext; -static void *AVFMediaPlayerObserverStatusObservationContext = &AVFMediaPlayerObserverStatusObservationContext; -static void *AVFMediaPlayerObserverPresentationSizeContext = &AVFMediaPlayerObserverPresentationSizeContext; -static void *AVFMediaPlayerObserverBufferLikelyToKeepUpContext = &AVFMediaPlayerObserverBufferLikelyToKeepUpContext; -static void *AVFMediaPlayerObserverTracksContext = &AVFMediaPlayerObserverTracksContext; -static void *AVFMediaPlayerObserverCurrentItemObservationContext = &AVFMediaPlayerObserverCurrentItemObservationContext; -static void *AVFMediaPlayerObserverCurrentItemDurationObservationContext = &AVFMediaPlayerObserverCurrentItemDurationObservationContext; - -@interface AVFMediaPlayerObserver : NSObject<AVAssetResourceLoaderDelegate> - -@property (readonly, getter=player) AVPlayer* m_player; -@property (readonly, getter=playerItem) AVPlayerItem* m_playerItem; -@property (readonly, getter=playerLayer) AVPlayerLayer* m_playerLayer; -@property (readonly, getter=session) AVFMediaPlayer* m_session; -@property (retain) AVPlayerItemTrack *videoTrack; - -- (AVFMediaPlayerObserver *) initWithMediaPlayerSession:(AVFMediaPlayer *)session; -- (void) setURL:(NSURL *)url mimeType:(NSString *)mimeType; -- (void) unloadMedia; -- (void) prepareToPlayAsset:(AVURLAsset *)asset withKeys:(NSArray *)requestedKeys; -- (void) assetFailedToPrepareForPlayback:(NSError *)error; -- (void) playerItemDidReachEnd:(NSNotification *)notification; -- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object - change:(NSDictionary *)change context:(void *)context; -- (void) detatchSession; -- (void) dealloc; -- (BOOL) resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest; -@end - -@implementation AVFMediaPlayerObserver -{ -@private - AVFMediaPlayer *m_session; - AVPlayer *m_player; - AVPlayerItem *m_playerItem; - AVPlayerLayer *m_playerLayer; - NSURL *m_URL; - BOOL m_bufferIsLikelyToKeepUp; - NSData *m_data; - NSString *m_mimeType; -} - -@synthesize m_player, m_playerItem, m_playerLayer, m_session; - -- (AVFMediaPlayerObserver *) initWithMediaPlayerSession:(AVFMediaPlayer *)session -{ - if (!(self = [super init])) - return nil; - - m_session = session; - m_bufferIsLikelyToKeepUp = FALSE; - - m_playerLayer = [AVPlayerLayer playerLayerWithPlayer:nil]; - [m_playerLayer retain]; - m_playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; - m_playerLayer.anchorPoint = CGPointMake(0.0f, 0.0f); - return self; -} - -- (void) setURL:(NSURL *)url mimeType:(NSString *)mimeType -{ - [m_mimeType release]; - m_mimeType = [mimeType retain]; - - if (m_URL != url) - { - [m_URL release]; - m_URL = [url copy]; - - //Create an asset for inspection of a resource referenced by a given URL. - //Load the values for the asset keys "tracks", "playable". - - // use __block to avoid maintaining strong references on variables captured by the - // following block callback - __block AVURLAsset *asset = [[AVURLAsset URLAssetWithURL:m_URL options:nil] retain]; - [asset.resourceLoader setDelegate:self queue:dispatch_get_main_queue()]; - - __block NSArray *requestedKeys = [[NSArray arrayWithObjects:AVF_TRACKS_KEY, AVF_PLAYABLE_KEY, nil] retain]; - - __block AVFMediaPlayerObserver *blockSelf = self; - QPointer<AVFMediaPlayer> session(m_session); - - // Tells the asset to load the values of any of the specified keys that are not already loaded. - [asset loadValuesAsynchronouslyForKeys:requestedKeys completionHandler: - ^{ - dispatch_async( dispatch_get_main_queue(), - ^{ - if (session) - [blockSelf prepareToPlayAsset:asset withKeys:requestedKeys]; - [asset release]; - [requestedKeys release]; - }); - }]; - } -} - -- (void) unloadMedia -{ - if (m_playerItem) { - [m_playerItem removeObserver:self forKeyPath:@"presentationSize"]; - [m_playerItem removeObserver:self forKeyPath:AVF_STATUS_KEY]; - [m_playerItem removeObserver:self forKeyPath:AVF_BUFFER_LIKELY_KEEP_UP_KEY]; - [m_playerItem removeObserver:self forKeyPath:AVF_TRACKS_KEY]; - - [[NSNotificationCenter defaultCenter] removeObserver:self - name:AVPlayerItemDidPlayToEndTimeNotification - object:m_playerItem]; - m_playerItem = 0; - } - if (m_player) { - [m_player setRate:0.0]; - [m_player removeObserver:self forKeyPath:AVF_CURRENT_ITEM_DURATION_KEY]; - [m_player removeObserver:self forKeyPath:AVF_CURRENT_ITEM_KEY]; - [m_player removeObserver:self forKeyPath:AVF_RATE_KEY]; - [m_player release]; - m_player = 0; - } - if (m_playerLayer) - m_playerLayer.player = nil; -#if defined(Q_OS_IOS) - [[AVAudioSession sharedInstance] setActive:NO error:nil]; -#endif -} - -- (void) prepareToPlayAsset:(AVURLAsset *)asset - withKeys:(NSArray *)requestedKeys -{ - //Make sure that the value of each key has loaded successfully. - for (NSString *thisKey in requestedKeys) - { - NSError *error = nil; - AVKeyValueStatus keyStatus = [asset statusOfValueForKey:thisKey error:&error]; -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO << [thisKey UTF8String] << " status: " << keyStatus; -#endif - if (keyStatus == AVKeyValueStatusFailed) - { - [self assetFailedToPrepareForPlayback:error]; - return; - } - } - - //Use the AVAsset playable property to detect whether the asset can be played. -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO << "isPlayable: " << [asset isPlayable]; -#endif - if (!asset.playable) - { - //Generate an error describing the failure. - NSString *localizedDescription = NSLocalizedString(@"Item cannot be played", @"Item cannot be played description"); - NSString *localizedFailureReason = NSLocalizedString(@"The assets tracks were loaded, but could not be made playable.", @"Item cannot be played failure reason"); - NSDictionary *errorDict = [NSDictionary dictionaryWithObjectsAndKeys: - localizedDescription, NSLocalizedDescriptionKey, - localizedFailureReason, NSLocalizedFailureReasonErrorKey, - nil]; - NSError *assetCannotBePlayedError = [NSError errorWithDomain:@"StitchedStreamPlayer" code:0 userInfo:errorDict]; - - [self assetFailedToPrepareForPlayback:assetCannotBePlayedError]; - - return; - } - - //At this point we're ready to set up for playback of the asset. - //Stop observing our prior AVPlayerItem, if we have one. - if (m_playerItem) - { - //Remove existing player item key value observers and notifications. - [self unloadMedia]; - } - - //Create a new instance of AVPlayerItem from the now successfully loaded AVAsset. - m_playerItem = [AVPlayerItem playerItemWithAsset:asset]; - - //Observe the player item "status" key to determine when it is ready to play. - [m_playerItem addObserver:self - forKeyPath:AVF_STATUS_KEY - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:AVFMediaPlayerObserverStatusObservationContext]; - - [m_playerItem addObserver:self - forKeyPath:@"presentationSize" - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:AVFMediaPlayerObserverPresentationSizeContext]; - - [m_playerItem addObserver:self - forKeyPath:AVF_BUFFER_LIKELY_KEEP_UP_KEY - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:AVFMediaPlayerObserverBufferLikelyToKeepUpContext]; - - [m_playerItem addObserver:self - forKeyPath:AVF_TRACKS_KEY - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:AVFMediaPlayerObserverTracksContext]; - - //When the player item has played to its end time we'll toggle - //the movie controller Pause button to be the Play button - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(playerItemDidReachEnd:) - name:AVPlayerItemDidPlayToEndTimeNotification - object:m_playerItem]; - - //Get a new AVPlayer initialized to play the specified player item. - m_player = [AVPlayer playerWithPlayerItem:m_playerItem]; - [m_player retain]; - - //Set the initial volume on new player object - if (self.session) { - auto *audioOutput = m_session->m_audioOutput; - m_player.volume = (audioOutput ? audioOutput->volume : 1.); - m_player.muted = (audioOutput ? audioOutput->muted : true); - } - - //Assign the output layer to the new player - m_playerLayer.player = m_player; - - //Observe the AVPlayer "currentItem" property to find out when any - //AVPlayer replaceCurrentItemWithPlayerItem: replacement will/did - //occur. - [m_player addObserver:self - forKeyPath:AVF_CURRENT_ITEM_KEY - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:AVFMediaPlayerObserverCurrentItemObservationContext]; - - //Observe the AVPlayer "rate" property to update the scrubber control. - [m_player addObserver:self - forKeyPath:AVF_RATE_KEY - options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew - context:AVFMediaPlayerObserverRateObservationContext]; - - //Observe the duration for getting the buffer state - [m_player addObserver:self - forKeyPath:AVF_CURRENT_ITEM_DURATION_KEY - options:0 - context:AVFMediaPlayerObserverCurrentItemDurationObservationContext]; -#if defined(Q_OS_IOS) - [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil]; - [[AVAudioSession sharedInstance] setActive:YES error:nil]; -#endif -} - --(void) assetFailedToPrepareForPlayback:(NSError *)error -{ - Q_UNUSED(error); - QMetaObject::invokeMethod(m_session, "processMediaLoadError", Qt::AutoConnection); -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO; - qDebug() << [[error localizedDescription] UTF8String]; - qDebug() << [[error localizedFailureReason] UTF8String]; - qDebug() << [[error localizedRecoverySuggestion] UTF8String]; -#endif -} - -- (void) playerItemDidReachEnd:(NSNotification *)notification -{ - Q_UNUSED(notification); - if (self.session) - QMetaObject::invokeMethod(m_session, "processEOS", Qt::AutoConnection); -} - -- (void) observeValueForKeyPath:(NSString*) path - ofObject:(id)object - change:(NSDictionary*)change - context:(void*)context -{ - //AVPlayerItem "status" property value observer. - if (context == AVFMediaPlayerObserverStatusObservationContext) - { - AVPlayerStatus status = (AVPlayerStatus)[[change objectForKey:NSKeyValueChangeNewKey] integerValue]; - switch (status) - { - //Indicates that the status of the player is not yet known because - //it has not tried to load new media resources for playback - case AVPlayerStatusUnknown: - { - //QMetaObject::invokeMethod(m_session, "processLoadStateChange", Qt::AutoConnection); - } - break; - - case AVPlayerStatusReadyToPlay: - { - //Once the AVPlayerItem becomes ready to play, i.e. - //[playerItem status] == AVPlayerItemStatusReadyToPlay, - //its duration can be fetched from the item. - if (self.session) - QMetaObject::invokeMethod(m_session, "processLoadStateChange", Qt::AutoConnection); - } - break; - - case AVPlayerStatusFailed: - { - AVPlayerItem *playerItem = static_cast<AVPlayerItem*>(object); - [self assetFailedToPrepareForPlayback:playerItem.error]; - - if (self.session) - QMetaObject::invokeMethod(m_session, "processLoadStateFailure", Qt::AutoConnection); - } - break; - } - } else if (context == AVFMediaPlayerObserverPresentationSizeContext) { - QSize size(m_playerItem.presentationSize.width, m_playerItem.presentationSize.height); - QMetaObject::invokeMethod(m_session, "nativeSizeChanged", Qt::AutoConnection, Q_ARG(QSize, size)); - } else if (context == AVFMediaPlayerObserverBufferLikelyToKeepUpContext) - { - const bool isPlaybackLikelyToKeepUp = [m_playerItem isPlaybackLikelyToKeepUp]; - if (isPlaybackLikelyToKeepUp != m_bufferIsLikelyToKeepUp) { - m_bufferIsLikelyToKeepUp = isPlaybackLikelyToKeepUp; - QMetaObject::invokeMethod(m_session, "processBufferStateChange", Qt::AutoConnection, - Q_ARG(int, isPlaybackLikelyToKeepUp ? 100 : 0)); - } - } - else if (context == AVFMediaPlayerObserverTracksContext) - { - QMetaObject::invokeMethod(m_session, "updateTracks", Qt::AutoConnection); - } - //AVPlayer "rate" property value observer. - else if (context == AVFMediaPlayerObserverRateObservationContext) - { - //QMetaObject::invokeMethod(m_session, "setPlaybackRate", Qt::AutoConnection, Q_ARG(qreal, [m_player rate])); - } - //AVPlayer "currentItem" property observer. - //Called when the AVPlayer replaceCurrentItemWithPlayerItem: - //replacement will/did occur. - else if (context == AVFMediaPlayerObserverCurrentItemObservationContext) - { - AVPlayerItem *newPlayerItem = [change objectForKey:NSKeyValueChangeNewKey]; - if (m_playerItem != newPlayerItem) - m_playerItem = newPlayerItem; - } - else if (context == AVFMediaPlayerObserverCurrentItemDurationObservationContext) - { - const CMTime time = [m_playerItem duration]; - const qint64 duration = static_cast<qint64>(float(time.value) / float(time.timescale) * 1000.0f); - if (self.session) - QMetaObject::invokeMethod(m_session, "processDurationChange", Qt::AutoConnection, Q_ARG(qint64, duration)); - } - else - { - [super observeValueForKeyPath:path ofObject:object change:change context:context]; - } -} - -- (void) detatchSession -{ -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO; -#endif - m_session = 0; -} - -- (void) dealloc -{ -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO; -#endif - [self unloadMedia]; - - if (m_URL) { - [m_URL release]; - } - - [m_mimeType release]; - [m_playerLayer release]; - [super dealloc]; -} - -- (BOOL) resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest -{ - Q_UNUSED(resourceLoader); - - if (![loadingRequest.request.URL.scheme isEqualToString:@"iodevice"]) - return NO; - - QIODevice *device = m_session->mediaStream(); - if (!device) - return NO; - - device->seek(loadingRequest.dataRequest.requestedOffset); - if (loadingRequest.contentInformationRequest) { - loadingRequest.contentInformationRequest.contentType = m_mimeType; - loadingRequest.contentInformationRequest.contentLength = device->size(); - loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES; - } - - if (loadingRequest.dataRequest) { - NSInteger requestedLength = loadingRequest.dataRequest.requestedLength; - int maxBytes = qMin(32 * 1064, int(requestedLength)); - char buffer[maxBytes]; - NSInteger submitted = 0; - while (submitted < requestedLength) { - qint64 len = device->read(buffer, maxBytes); - if (len < 1) - break; - - [loadingRequest.dataRequest respondWithData:[NSData dataWithBytes:buffer length:len]]; - submitted += len; - } - - // Finish loading even if not all bytes submitted. - [loadingRequest finishLoading]; - } - - return YES; -} -@end - -AVFMediaPlayer::AVFMediaPlayer(QMediaPlayer *player) - : QObject(player) - , QPlatformMediaPlayer(player) - , m_state(QMediaPlayer::StoppedState) - , m_mediaStatus(QMediaPlayer::NoMedia) - , m_mediaStream(nullptr) - , m_tryingAsync(false) - , m_rate(1.0) - , m_requestedPosition(-1) - , m_duration(0) - , m_bufferProgress(0) - , m_videoAvailable(false) - , m_audioAvailable(false) - , m_seekable(false) -{ - m_observer = [[AVFMediaPlayerObserver alloc] initWithMediaPlayerSession:this]; - connect(&m_playbackTimer, &QTimer::timeout, this, &AVFMediaPlayer::processPositionChange); - setVideoOutput(new AVFVideoRendererControl(this)); -} - -AVFMediaPlayer::~AVFMediaPlayer() -{ -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO; -#endif - //Detatch the session from the sessionObserver (which could still be alive trying to communicate with this session). - [m_observer detatchSession]; - [static_cast<AVFMediaPlayerObserver*>(m_observer) release]; -} - -void AVFMediaPlayer::setVideoSink(QVideoSink *sink) -{ - m_videoSink = sink ? static_cast<AVFVideoSink *>(sink->platformVideoSink()): nullptr; - m_videoOutput->setVideoSink(m_videoSink); -} - -void AVFMediaPlayer::setVideoOutput(AVFVideoRendererControl *output) -{ -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO << output; -#endif - - if (m_videoOutput == output) - return; - - //Set the current output layer to null to stop rendering - if (m_videoOutput) { - m_videoOutput->setLayer(nullptr); - } - - m_videoOutput = output; - - if (m_videoOutput && m_state != QMediaPlayer::StoppedState) - m_videoOutput->setLayer([static_cast<AVFMediaPlayerObserver*>(m_observer) playerLayer]); -} - -AVAsset *AVFMediaPlayer::currentAssetHandle() -{ -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO; -#endif - AVAsset *currentAsset = [[static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem] asset]; - return currentAsset; -} - -QMediaPlayer::PlaybackState AVFMediaPlayer::state() const -{ - return m_state; -} - -QMediaPlayer::MediaStatus AVFMediaPlayer::mediaStatus() const -{ - return m_mediaStatus; -} - -QUrl AVFMediaPlayer::media() const -{ - return m_resources; -} - -QIODevice *AVFMediaPlayer::mediaStream() const -{ - return m_mediaStream; -} - -static void setURL(AVFMediaPlayerObserver *observer, const QByteArray &url, const QString &mimeType = QString()) -{ - NSString *urlString = [NSString stringWithUTF8String:url.constData()]; - NSURL *nsurl = [NSURL URLWithString:urlString]; - [observer setURL:nsurl mimeType:[NSString stringWithUTF8String:mimeType.toLatin1().constData()]]; -} - -static void setStreamURL(AVFMediaPlayerObserver *observer, const QByteArray &url) -{ - setURL(observer, QByteArrayLiteral("iodevice://") + url, QFileInfo(QString::fromUtf8(url)).suffix()); -} - -void AVFMediaPlayer::setMedia(const QUrl &content, QIODevice *stream) -{ -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO << content.request().url(); -#endif - - [static_cast<AVFMediaPlayerObserver*>(m_observer) unloadMedia]; - - m_resources = content; - resetStream(stream); - - setAudioAvailable(false); - setVideoAvailable(false); - setSeekable(false); - m_requestedPosition = -1; - Q_EMIT positionChanged(position()); - if (m_duration != 0) { - m_duration = 0; - Q_EMIT durationChanged(0); - } - if (!m_metaData.isEmpty()) { - m_metaData.clear(); - metaDataChanged(); - } - - const QMediaPlayer::MediaStatus oldMediaStatus = m_mediaStatus; - const QMediaPlayer::PlaybackState oldState = m_state; - - if (!m_mediaStream && content.isEmpty()) { - m_mediaStatus = QMediaPlayer::NoMedia; - if (m_mediaStatus != oldMediaStatus) - Q_EMIT mediaStatusChanged(m_mediaStatus); - - m_state = QMediaPlayer::StoppedState; - if (m_state != oldState) - Q_EMIT stateChanged(m_state); - - return; - } - - m_mediaStatus = QMediaPlayer::LoadingMedia; - if (m_mediaStatus != oldMediaStatus) - Q_EMIT mediaStatusChanged(m_mediaStatus); - - if (m_mediaStream) { - // If there is a data, try to load it, - // otherwise wait for readyRead. - if (m_mediaStream->size()) - setStreamURL(m_observer, m_resources.toEncoded()); - } else { - //Load AVURLAsset - //initialize asset using content's URL - setURL(m_observer, m_resources.toEncoded()); - } - - m_state = QMediaPlayer::StoppedState; - if (m_state != oldState) - Q_EMIT stateChanged(m_state); -} - -qint64 AVFMediaPlayer::position() const -{ - AVPlayerItem *playerItem = [static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem]; - - if (!playerItem) - return m_requestedPosition != -1 ? m_requestedPosition : 0; - - CMTime time = [playerItem currentTime]; - return static_cast<quint64>(float(time.value) / float(time.timescale) * 1000.0f); -} - -qint64 AVFMediaPlayer::duration() const -{ -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO; -#endif - return m_duration; -} - -float AVFMediaPlayer::bufferProgress() const -{ -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO; -#endif - return m_bufferProgress/100.; -} - -void AVFMediaPlayer::setAudioAvailable(bool available) -{ - if (m_audioAvailable == available) - return; - - m_audioAvailable = available; - Q_EMIT audioAvailableChanged(available); -} - -bool AVFMediaPlayer::isAudioAvailable() const -{ - return m_audioAvailable; -} - -void AVFMediaPlayer::setVideoAvailable(bool available) -{ - if (m_videoAvailable == available) - return; - - m_videoAvailable = available; - Q_EMIT videoAvailableChanged(available); -} - -bool AVFMediaPlayer::isVideoAvailable() const -{ - return m_videoAvailable; -} - -bool AVFMediaPlayer::isSeekable() const -{ - return m_seekable; -} - -void AVFMediaPlayer::setSeekable(bool seekable) -{ - if (m_seekable == seekable) - return; - - m_seekable = seekable; - Q_EMIT seekableChanged(seekable); -} - -QMediaTimeRange AVFMediaPlayer::availablePlaybackRanges() const -{ - AVPlayerItem *playerItem = [static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem]; - - if (playerItem) { - QMediaTimeRange timeRanges; - - NSArray *ranges = [playerItem loadedTimeRanges]; - for (NSValue *timeRange in ranges) { - CMTimeRange currentTimeRange = [timeRange CMTimeRangeValue]; - qint64 startTime = qint64(float(currentTimeRange.start.value) / currentTimeRange.start.timescale * 1000.0); - timeRanges.addInterval(startTime, startTime + qint64(float(currentTimeRange.duration.value) / currentTimeRange.duration.timescale * 1000.0)); - } - if (!timeRanges.isEmpty()) - return timeRanges; - } - return QMediaTimeRange(0, duration()); -} - -qreal AVFMediaPlayer::playbackRate() const -{ - return m_rate; -} - -void AVFMediaPlayer::setAudioOutput(QPlatformAudioOutput *output) -{ - if (m_audioOutput == output) - return; - if (m_audioOutput) - m_audioOutput->q->disconnect(this); - m_audioOutput = output; - if (m_audioOutput) { - connect(m_audioOutput->q, &QAudioOutput::deviceChanged, this, &AVFMediaPlayer::audioOutputChanged); - connect(m_audioOutput->q, &QAudioOutput::volumeChanged, this, &AVFMediaPlayer::setVolume); - connect(m_audioOutput->q, &QAudioOutput::mutedChanged, this, &AVFMediaPlayer::setMuted); - //connect(m_audioOutput->q, &QAudioOutput::audioRoleChanged, this, &AVFMediaPlayer::setAudioRole); - } - audioOutputChanged(); - setMuted(m_audioOutput ? m_audioOutput->muted : true); - setVolume(m_audioOutput ? m_audioOutput->volume : 1.); -} - -QMediaMetaData AVFMediaPlayer::metaData() const -{ - return m_metaData; -} - -void AVFMediaPlayer::setPlaybackRate(qreal rate) -{ -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO << rate; -#endif - - if (qFuzzyCompare(m_rate, rate)) - return; - - m_rate = rate; - - AVPlayer *player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player]; - if (player && m_state == QMediaPlayer::PlayingState) - [player setRate:m_rate]; - - Q_EMIT playbackRateChanged(m_rate); -} - -void AVFMediaPlayer::setPosition(qint64 pos) -{ -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO << pos; -#endif - - if (pos == position()) - return; - - AVPlayerItem *playerItem = [static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem]; - if (!playerItem) { - m_requestedPosition = pos; - Q_EMIT positionChanged(m_requestedPosition); - return; - } - - if (!isSeekable()) { - if (m_requestedPosition != -1) { - m_requestedPosition = -1; - Q_EMIT positionChanged(position()); - } - return; - } - - pos = qMax(qint64(0), pos); - if (duration() > 0) - pos = qMin(pos, duration()); - - CMTime newTime = [playerItem currentTime]; - newTime.value = (pos / 1000.0f) * newTime.timescale; - [playerItem seekToTime:newTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:nil]; - - Q_EMIT positionChanged(pos); - - // Reset media status if the current status is EndOfMedia - if (m_mediaStatus == QMediaPlayer::EndOfMedia) { - QMediaPlayer::MediaStatus newMediaStatus = (m_state == QMediaPlayer::PausedState) ? QMediaPlayer::BufferedMedia - : QMediaPlayer::LoadedMedia; - Q_EMIT mediaStatusChanged((m_mediaStatus = newMediaStatus)); - } -} - -void AVFMediaPlayer::play() -{ -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO << "currently: " << m_state; -#endif - - if (m_mediaStatus == QMediaPlayer::NoMedia || m_mediaStatus == QMediaPlayer::InvalidMedia) - return; - - if (m_state == QMediaPlayer::PlayingState) - return; - - if (m_videoOutput && m_videoSink) - m_videoOutput->setLayer([static_cast<AVFMediaPlayerObserver*>(m_observer) playerLayer]); - - // Reset media status if the current status is EndOfMedia - if (m_mediaStatus == QMediaPlayer::EndOfMedia) - setPosition(0); - - if (m_mediaStatus == QMediaPlayer::LoadedMedia || m_mediaStatus == QMediaPlayer::BufferedMedia) { - // Setting the rate starts playback - [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate]; - } - - m_state = QMediaPlayer::PlayingState; - processLoadStateChange(); - - Q_EMIT stateChanged(m_state); - m_playbackTimer.start(100); -} - -void AVFMediaPlayer::pause() -{ -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO << "currently: " << m_state; -#endif - - if (m_mediaStatus == QMediaPlayer::NoMedia) - return; - - if (m_state == QMediaPlayer::PausedState) - return; - - m_state = QMediaPlayer::PausedState; - - if (m_videoOutput && m_videoSink) - m_videoOutput->setLayer([static_cast<AVFMediaPlayerObserver*>(m_observer) playerLayer]); - - [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] pause]; - - // Reset media status if the current status is EndOfMedia - if (m_mediaStatus == QMediaPlayer::EndOfMedia) - setPosition(0); - - Q_EMIT positionChanged(position()); - Q_EMIT stateChanged(m_state); - m_playbackTimer.stop(); -} - -void AVFMediaPlayer::stop() -{ -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO << "currently: " << m_state; -#endif - - if (m_state == QMediaPlayer::StoppedState) - return; - - // AVPlayer doesn't have stop(), only pause() and play(). - [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] pause]; - setPosition(0); - - if (m_videoOutput) - m_videoOutput->setLayer(nullptr); - - if (m_mediaStatus == QMediaPlayer::BufferedMedia) - Q_EMIT mediaStatusChanged((m_mediaStatus = QMediaPlayer::LoadedMedia)); - - Q_EMIT stateChanged((m_state = QMediaPlayer::StoppedState)); - m_playbackTimer.stop(); -} - -void AVFMediaPlayer::setVolume(float volume) -{ -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO << volume; -#endif - - AVPlayer *player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player]; - if (player) - player.volume = volume; -} - -void AVFMediaPlayer::setMuted(bool muted) -{ -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO << muted; -#endif - - AVPlayer *player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player]; - if (player) - player.muted = muted; -} - -void AVFMediaPlayer::audioOutputChanged() -{ -#ifdef Q_OS_MACOS - AVPlayer *player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player]; - if (!m_audioOutput || m_audioOutput->device.id().isEmpty()) { - player.audioOutputDeviceUniqueID = nil; - if (!m_audioOutput) - player.muted = true; - } else { - NSString *str = QString::fromUtf8(m_audioOutput->device.id()).toNSString(); - player.audioOutputDeviceUniqueID = str; - } -#endif -} - -void AVFMediaPlayer::processEOS() -{ - //AVPlayerItem has reached end of track/stream -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO; -#endif - Q_EMIT positionChanged(position()); - m_mediaStatus = QMediaPlayer::EndOfMedia; - m_state = QMediaPlayer::StoppedState; - - if (m_videoOutput) - m_videoOutput->setLayer(nullptr); - - Q_EMIT mediaStatusChanged(m_mediaStatus); - Q_EMIT stateChanged(m_state); -} - -void AVFMediaPlayer::processLoadStateChange(QMediaPlayer::PlaybackState newState) -{ - AVPlayerStatus currentStatus = [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] status]; - -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO << currentStatus << ", " << m_mediaStatus << ", " << newState; -#endif - - if (m_mediaStatus == QMediaPlayer::NoMedia) - return; - - if (currentStatus == AVPlayerStatusReadyToPlay) { - - QMediaPlayer::MediaStatus newStatus = m_mediaStatus; - - AVPlayerItem *playerItem = [m_observer playerItem]; - - // get the meta data - m_metaData = AVFMetaData::fromAsset(playerItem.asset); - Q_EMIT metaDataChanged(); - updateTracks(); - - if (playerItem) { - setSeekable([[playerItem seekableTimeRanges] count] > 0); - - // Get the native size of the video, and reset the bounds of the player layer - AVPlayerLayer *playerLayer = [m_observer playerLayer]; - if (m_observer.videoTrack && playerLayer) { - if (!playerLayer.bounds.size.width || !playerLayer.bounds.size.height) { - playerLayer.bounds = CGRectMake(0.0f, 0.0f, - m_observer.videoTrack.assetTrack.naturalSize.width, - m_observer.videoTrack.assetTrack.naturalSize.height); - } - } - - if (m_requestedPosition != -1) { - setPosition(m_requestedPosition); - m_requestedPosition = -1; - } - } - - newStatus = (newState != QMediaPlayer::StoppedState) ? QMediaPlayer::BufferedMedia - : QMediaPlayer::LoadedMedia; - - if (newStatus != m_mediaStatus) - Q_EMIT mediaStatusChanged((m_mediaStatus = newStatus)); - - } - - if (newState == QMediaPlayer::PlayingState && [static_cast<AVFMediaPlayerObserver*>(m_observer) player]) { - // Setting the rate is enough to start playback, no need to call play() - [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate]; - m_playbackTimer.start(); - } -} - - -void AVFMediaPlayer::processLoadStateChange() -{ - processLoadStateChange(m_state); -} - - -void AVFMediaPlayer::processLoadStateFailure() -{ - Q_EMIT stateChanged((m_state = QMediaPlayer::StoppedState)); -} - -void AVFMediaPlayer::processBufferStateChange(int bufferProgress) -{ - if (bufferProgress == m_bufferProgress) - return; - - auto status = m_mediaStatus; - // Buffered -> unbuffered. - if (!bufferProgress) { - status = QMediaPlayer::StalledMedia; - } else if (status == QMediaPlayer::StalledMedia) { - status = QMediaPlayer::BufferedMedia; - // Resume playback. - if (m_state == QMediaPlayer::PlayingState) { - [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate]; - m_playbackTimer.start(); - } - } - - if (m_mediaStatus != status) - Q_EMIT mediaStatusChanged(m_mediaStatus = status); - - m_bufferProgress = bufferProgress; - Q_EMIT bufferProgressChanged(bufferProgress/100.); -} - -void AVFMediaPlayer::processDurationChange(qint64 duration) -{ - if (duration == m_duration) - return; - - m_duration = duration; - Q_EMIT durationChanged(duration); -} - -void AVFMediaPlayer::processPositionChange() -{ - if (m_state == QMediaPlayer::StoppedState) - return; - - Q_EMIT positionChanged(position()); -} - -void AVFMediaPlayer::processMediaLoadError() -{ - if (m_requestedPosition != -1) { - m_requestedPosition = -1; - Q_EMIT positionChanged(position()); - } - - Q_EMIT mediaStatusChanged((m_mediaStatus = QMediaPlayer::InvalidMedia)); - - Q_EMIT error(QMediaPlayer::FormatError, tr("Failed to load media")); -} - -void AVFMediaPlayer::streamReady() -{ - setStreamURL(m_observer, m_resources.toEncoded()); -} - -void AVFMediaPlayer::streamDestroyed() -{ - resetStream(nullptr); -} - -void AVFMediaPlayer::updateTracks() -{ - for (int i = 0; i < QPlatformMediaPlayer::NTrackTypes; ++i) { - tracks[i].clear(); - nativeTracks[i].clear(); - } - AVPlayerItem *playerItem = [m_observer playerItem]; - if (playerItem) { - // Check each track for audio and video content - NSArray *tracks = playerItem.tracks; - for (AVPlayerItemTrack *track in tracks) { - AVAssetTrack *assetTrack = track.assetTrack; - if (assetTrack) { - int qtTrack = -1; - if ([assetTrack.mediaType isEqualToString:AVMediaTypeAudio]) { - qtTrack = QPlatformMediaPlayer::AudioStream; - setAudioAvailable(true); - } else if ([assetTrack.mediaType isEqualToString:AVMediaTypeVideo]) { - qtTrack = QPlatformMediaPlayer::VideoStream; - setVideoAvailable(true); - if (!m_observer.videoTrack) - m_observer.videoTrack = track; - } - else if ([assetTrack.mediaType isEqualToString:AVMediaTypeSubtitle]) { - qtTrack = QPlatformMediaPlayer::SubtitleStream; - } - if (qtTrack != -1) { - QMediaMetaData metaData = AVFMetaData::fromAssetTrack(assetTrack); - this->tracks[qtTrack].append(metaData); - nativeTracks[qtTrack].append(track); - } - } - } - } - Q_EMIT tracksChanged(); -} - -void AVFMediaPlayer::setActiveTrack(QPlatformMediaPlayer::TrackType type, int index) -{ - const auto &t = nativeTracks[type]; - for (int i = 0; i < t.count(); ++i) - t.at(i).enabled = (i == index); -} - -int AVFMediaPlayer::activeTrack(QPlatformMediaPlayer::TrackType type) -{ - const auto &t = nativeTracks[type]; - for (int i = 0; i < t.count(); ++i) - if (t.at(i).enabled) - return i; - return -1; -} - -int AVFMediaPlayer::trackCount(QPlatformMediaPlayer::TrackType type) -{ - return nativeTracks[type].count(); -} - -QMediaMetaData AVFMediaPlayer::trackMetaData(QPlatformMediaPlayer::TrackType type, int trackNumber) -{ - const auto &t = tracks[type]; - if (trackNumber < 0 || trackNumber >= t.count()) - return QMediaMetaData(); - return t.at(trackNumber); -} - -void AVFMediaPlayer::resetStream(QIODevice *stream) -{ - if (m_mediaStream) { - disconnect(m_mediaStream, &QIODevice::readyRead, this, &AVFMediaPlayer::streamReady); - disconnect(m_mediaStream, &QIODevice::destroyed, this, &AVFMediaPlayer::streamDestroyed); - } - - m_mediaStream = stream; - - if (m_mediaStream) { - connect(m_mediaStream, &QIODevice::readyRead, this, &AVFMediaPlayer::streamReady); - connect(m_mediaStream, &QIODevice::destroyed, this, &AVFMediaPlayer::streamDestroyed); - } -} - -void AVFMediaPlayer::nativeSizeChanged(QSize size) -{ - if (!m_videoSink) - return; - m_videoSink->setNativeSize(size); -} diff --git a/src/multimedia/platform/darwin/mediaplayer/avfmediaplayer_p.h b/src/multimedia/platform/darwin/mediaplayer/avfmediaplayer_p.h deleted file mode 100644 index 4bd082c1c..000000000 --- a/src/multimedia/platform/darwin/mediaplayer/avfmediaplayer_p.h +++ /dev/null @@ -1,180 +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$ -** -****************************************************************************/ - -#ifndef AVFMEDIAPLAYER_H -#define AVFMEDIAPLAYER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/QObject> -#include <QtCore/QByteArray> -#include <QtCore/QSet> -#include <QtCore/QResource> -#include <QtCore/QUrl> -#include <QtCore/QTimer> - -#include <private/qplatformmediaplayer_p.h> -#include <QtMultimedia/QMediaPlayer> - -Q_FORWARD_DECLARE_OBJC_CLASS(AVAsset); -Q_FORWARD_DECLARE_OBJC_CLASS(AVPlayerItemTrack); -Q_FORWARD_DECLARE_OBJC_CLASS(AVFMediaPlayerObserver); - -QT_BEGIN_NAMESPACE - -class AVFMediaPlayer; -class AVFVideoRendererControl; -class AVFVideoSink; - -class AVFMediaPlayer : public QObject, public QPlatformMediaPlayer -{ - Q_OBJECT -public: - AVFMediaPlayer(QMediaPlayer *parent); - virtual ~AVFMediaPlayer(); - - void setVideoSink(QVideoSink *sink) override; - void setVideoOutput(AVFVideoRendererControl *output); - AVAsset *currentAssetHandle(); - - QMediaPlayer::PlaybackState state() const override; - QMediaPlayer::MediaStatus mediaStatus() const override; - - QUrl media() const override; - QIODevice *mediaStream() const override; - void setMedia(const QUrl &content, QIODevice *stream) override; - - qint64 position() const override; - qint64 duration() const override; - - float bufferProgress() const override; - - bool isAudioAvailable() const override; - bool isVideoAvailable() const override; - - bool isSeekable() const override; - QMediaTimeRange availablePlaybackRanges() const override; - - qreal playbackRate() const override; - - void setAudioOutput(QPlatformAudioOutput *output) override; - QPlatformAudioOutput *m_audioOutput = nullptr; - - QMediaMetaData metaData() const override; - -public Q_SLOTS: - void setPlaybackRate(qreal rate) override; - void nativeSizeChanged(QSize size); - - void setPosition(qint64 pos) override; - - void play() override; - void pause() override; - void stop() override; - - void setVolume(float volume); - void setMuted(bool muted); - void audioOutputChanged(); - - void processEOS(); - void processLoadStateChange(QMediaPlayer::PlaybackState newState); - void processPositionChange(); - void processMediaLoadError(); - - void processLoadStateChange(); - void processLoadStateFailure(); - - void processBufferStateChange(int bufferProgress); - - void processDurationChange(qint64 duration); - - void streamReady(); - void streamDestroyed(); - void updateTracks(); - void setActiveTrack(QPlatformMediaPlayer::TrackType type, int index) override; - int activeTrack(QPlatformMediaPlayer::TrackType type) override; - int trackCount(TrackType) override; - QMediaMetaData trackMetaData(TrackType type, int trackNumber) override; - -public: - QList<QMediaMetaData> tracks[QPlatformMediaPlayer::NTrackTypes]; - QList<AVPlayerItemTrack *> nativeTracks[QPlatformMediaPlayer::NTrackTypes]; - -private: - void setAudioAvailable(bool available); - void setVideoAvailable(bool available); - void setSeekable(bool seekable); - void resetStream(QIODevice *stream = nullptr); - - AVFVideoRendererControl *m_videoOutput = nullptr; - AVFVideoSink *m_videoSink = nullptr; - - QMediaPlayer::PlaybackState m_state; - QMediaPlayer::MediaStatus m_mediaStatus; - QIODevice *m_mediaStream; - QUrl m_resources; - QMediaMetaData m_metaData; - - bool m_tryingAsync; - qreal m_rate; - qint64 m_requestedPosition; - - qint64 m_duration; - int m_bufferProgress; - bool m_videoAvailable; - bool m_audioAvailable; - bool m_seekable; - - AVFMediaPlayerObserver *m_observer; - - QTimer m_playbackTimer; -}; - -QT_END_NAMESPACE - -#endif // AVFMEDIAPLAYER_H diff --git a/src/multimedia/platform/darwin/mediaplayer/avfvideorenderercontrol.mm b/src/multimedia/platform/darwin/mediaplayer/avfvideorenderercontrol.mm deleted file mode 100644 index 1b3985688..000000000 --- a/src/multimedia/platform/darwin/mediaplayer/avfvideorenderercontrol.mm +++ /dev/null @@ -1,272 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). -** 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$ -** -****************************************************************************/ - -#include "avfvideorenderercontrol_p.h" -#include "avfdisplaylink_p.h" -#include <private/avfvideobuffer_p.h> - -#include <private/qabstractvideobuffer_p.h> -#include <QtMultimedia/qvideoframeformat.h> - -#include <private/avfvideosink_p.h> -#include <QtGui/private/qrhi_p.h> - -#include <QtCore/qdebug.h> - -#import <AVFoundation/AVFoundation.h> -#include <CoreVideo/CVPixelBuffer.h> -#include <CoreVideo/CVImageBuffer.h> - -QT_USE_NAMESPACE - -@interface SubtitleDelegate : NSObject <AVPlayerItemLegibleOutputPushDelegate> -{ - AVFVideoRendererControl *m_renderer; -} - -- (void)legibleOutput:(AVPlayerItemLegibleOutput *)output - didOutputAttributedStrings:(NSArray<NSAttributedString *> *)strings - nativeSampleBuffers:(NSArray *)nativeSamples - forItemTime:(CMTime)itemTime; - -@end - -@implementation SubtitleDelegate - --(id)initWithRenderer: (AVFVideoRendererControl *)renderer -{ - if (!(self = [super init])) - return nil; - - m_renderer = renderer; - - return self; -} - -- (void)legibleOutput:(AVPlayerItemLegibleOutput *)output - didOutputAttributedStrings:(NSArray<NSAttributedString *> *)strings - nativeSampleBuffers:(NSArray *)nativeSamples - forItemTime:(CMTime)itemTime -{ - QString text; - for (NSAttributedString *s : strings) { - if (!text.isEmpty()) - text += QChar::LineSeparator; - text += QString::fromNSString(s.string); - } - m_renderer->setSubtitleText(text); -} - -@end - - -AVFVideoRendererControl::AVFVideoRendererControl(QObject *parent) - : QObject(parent) -{ - m_displayLink = new AVFDisplayLink(this); - connect(m_displayLink, SIGNAL(tick(CVTimeStamp)), SLOT(updateVideoFrame(CVTimeStamp))); -} - -AVFVideoRendererControl::~AVFVideoRendererControl() -{ -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO; -#endif - m_displayLink->stop(); - if (m_videoOutput) - [m_videoOutput release]; - if (m_subtitleOutput) - [m_subtitleOutput release]; - if (m_subtitleDelegate) - [m_subtitleDelegate release]; -} - -void AVFVideoRendererControl::reconfigure() -{ -#ifdef QT_DEBUG_AVF - qDebug() << "reconfigure"; -#endif - if (!m_layer) { - m_displayLink->stop(); - return; - } - - QMutexLocker locker(&m_mutex); - - m_displayLink->start(); - - nativeSizeChanged(); -} - -void AVFVideoRendererControl::setLayer(CALayer *layer) -{ - if (m_layer == layer) - return; - - AVPlayerLayer *plLayer = playerLayer(); - if (plLayer) { - if (m_videoOutput) - [[[plLayer player] currentItem] removeOutput:m_videoOutput]; - - if (m_subtitleOutput) - [[[plLayer player] currentItem] removeOutput:m_subtitleOutput]; - } - - if (!layer && m_sink) - m_sink->setVideoFrame(QVideoFrame()); - - AVFVideoSinkInterface::setLayer(layer); -} - -void AVFVideoRendererControl::updateVideoFrame(const CVTimeStamp &ts) -{ - Q_UNUSED(ts); - - if (!m_sink) - return; - - if (!m_layer) - return; - - auto *layer = playerLayer(); - if (!layer.readyForDisplay) - return; - nativeSizeChanged(); - - QVideoFrame frame; - size_t width, height; - CVPixelBufferRef pixelBuffer = copyPixelBufferFromLayer(width, height); - if (!pixelBuffer) - return; - AVFVideoBuffer *buffer = new AVFVideoBuffer(this, pixelBuffer); - auto fmt = buffer->fromCVVideoPixelFormat(CVPixelBufferGetPixelFormatType(pixelBuffer)); -// qDebug() << "Got pixelbuffer with format" << fmt << Qt::hex << CVPixelBufferGetPixelFormatType(pixelBuffer); - CVPixelBufferRelease(pixelBuffer); - - QVideoFrameFormat format(QSize(width, height), fmt); - - frame = QVideoFrame(buffer, format); - m_sink->setVideoFrame(frame); -} - -static NSDictionary* const AVF_OUTPUT_SETTINGS = @{ - (NSString *)kCVPixelBufferPixelFormatTypeKey: @[ - @(kCVPixelFormatType_32BGRA), - @(kCVPixelFormatType_32RGBA), - @(kCVPixelFormatType_422YpCbCr8), - @(kCVPixelFormatType_422YpCbCr8_yuvs), - @(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange), - @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange), - @(kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange), - @(kCVPixelFormatType_420YpCbCr10BiPlanarFullRange), - @(kCVPixelFormatType_OneComponent8), - @(q_kCVPixelFormatType_OneComponent16), - @(kCVPixelFormatType_420YpCbCr8Planar), - @(kCVPixelFormatType_420YpCbCr8PlanarFullRange) - ], - (NSString *)kCVPixelBufferMetalCompatibilityKey: @true -}; - -// The OpengGL texture cache can apparently only handle single plane formats, so lets simply restrict to BGRA -static NSDictionary* const AVF_OUTPUT_SETTINGS_OPENGL = @{ - (NSString *)kCVPixelBufferPixelFormatTypeKey: @[ - @(kCVPixelFormatType_32BGRA), - ], - (NSString *)kCVPixelBufferOpenGLCompatibilityKey: @true -}; - -CVPixelBufferRef AVFVideoRendererControl::copyPixelBufferFromLayer(size_t& width, size_t& height) -{ - AVPlayerLayer *layer = playerLayer(); - //Is layer valid - if (!layer) { -#ifdef QT_DEBUG_AVF - qWarning("copyPixelBufferFromLayer: invalid layer"); -#endif - return nullptr; - } - - AVPlayerItem * item = [[layer player] currentItem]; - - if (!m_videoOutput) { - auto *settings = (m_rhi && m_rhi->backend() == QRhi::OpenGLES2) ? AVF_OUTPUT_SETTINGS_OPENGL : AVF_OUTPUT_SETTINGS; - m_videoOutput = [[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:settings]; - [m_videoOutput setDelegate:nil queue:nil]; - } - if (!m_subtitleOutput) { - m_subtitleOutput = [[AVPlayerItemLegibleOutput alloc] init]; - m_subtitleDelegate = [[SubtitleDelegate alloc] initWithRenderer:this]; - [m_subtitleOutput setDelegate:m_subtitleDelegate queue:dispatch_get_main_queue()]; - } - if (![item.outputs containsObject:m_videoOutput]) - [item addOutput:m_videoOutput]; - if (![item.outputs containsObject:m_subtitleOutput]) - [item addOutput:m_subtitleOutput]; - - CFTimeInterval currentCAFrameTime = CACurrentMediaTime(); - CMTime currentCMFrameTime = [m_videoOutput itemTimeForHostTime:currentCAFrameTime]; - // happens when buffering / loading - if (CMTimeCompare(currentCMFrameTime, kCMTimeZero) < 0) { - return nullptr; - } - - if (![m_videoOutput hasNewPixelBufferForItemTime:currentCMFrameTime]) - return nullptr; - - CVPixelBufferRef pixelBuffer = [m_videoOutput copyPixelBufferForItemTime:currentCMFrameTime - itemTimeForDisplay:nil]; - if (!pixelBuffer) { -#ifdef QT_DEBUG_AVF - qWarning("copyPixelBufferForItemTime returned nil"); - CMTimeShow(currentCMFrameTime); -#endif - return nullptr; - } - - width = CVPixelBufferGetWidth(pixelBuffer); - height = CVPixelBufferGetHeight(pixelBuffer); -// auto f = CVPixelBufferGetPixelFormatType(pixelBuffer); -// char fmt[5]; -// memcpy(fmt, &f, 4); -// fmt[4] = 0; -// qDebug() << "copyPixelBuffer" << f << fmt << width << height; - return pixelBuffer; -} - -#include "moc_avfvideorenderercontrol_p.cpp" diff --git a/src/multimedia/platform/darwin/mediaplayer/avfvideorenderercontrol_p.h b/src/multimedia/platform/darwin/mediaplayer/avfvideorenderercontrol_p.h deleted file mode 100644 index ce0ec0738..000000000 --- a/src/multimedia/platform/darwin/mediaplayer/avfvideorenderercontrol_p.h +++ /dev/null @@ -1,103 +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$ -** -****************************************************************************/ - -#ifndef AVFVIDEORENDERERCONTROL_H -#define AVFVIDEORENDERERCONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/QObject> -#include <QtCore/QMutex> -#include <QtCore/QSize> - -#include <private/avfvideosink_p.h> - -#include <CoreVideo/CVBase.h> -#include <CoreVideo/CVPixelBuffer.h> - -Q_FORWARD_DECLARE_OBJC_CLASS(CALayer); -Q_FORWARD_DECLARE_OBJC_CLASS(AVPlayerItemVideoOutput); -Q_FORWARD_DECLARE_OBJC_CLASS(AVPlayerItemLegibleOutput); -Q_FORWARD_DECLARE_OBJC_CLASS(SubtitleDelegate); - -QT_BEGIN_NAMESPACE - -class AVFDisplayLink; - -class AVFVideoRendererControl : public QObject, public AVFVideoSinkInterface -{ - Q_OBJECT -public: - explicit AVFVideoRendererControl(QObject *parent = nullptr); - virtual ~AVFVideoRendererControl(); - - // AVFVideoSinkInterface - void reconfigure() override; - void setLayer(CALayer *layer) override; - - void setSubtitleText(const QString &subtitle) - { - m_sink->setSubtitleText(subtitle); - } -private Q_SLOTS: - void updateVideoFrame(const CVTimeStamp &ts); - -private: - AVPlayerLayer *playerLayer() const { return static_cast<AVPlayerLayer *>(m_layer); } - CVPixelBufferRef copyPixelBufferFromLayer(size_t& width, size_t& height); - - QMutex m_mutex; - AVFDisplayLink *m_displayLink = nullptr; - AVPlayerItemVideoOutput *m_videoOutput = nullptr; - AVPlayerItemLegibleOutput *m_subtitleOutput = nullptr; - SubtitleDelegate *m_subtitleDelegate = nullptr; -}; - -QT_END_NAMESPACE - -#endif // AVFVIDEORENDERERCONTROL_H diff --git a/src/multimedia/platform/darwin/qdarwinformatsinfo.mm b/src/multimedia/platform/darwin/qdarwinformatsinfo.mm deleted file mode 100644 index 9d27a843d..000000000 --- a/src/multimedia/platform/darwin/qdarwinformatsinfo.mm +++ /dev/null @@ -1,246 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qdarwinformatsinfo_p.h" -#include <AVFoundation/AVFoundation.h> -#include <qdebug.h> - -QT_BEGIN_NAMESPACE - -static struct { - const char *name; - QMediaFormat::FileFormat value; -} mediaContainerMap[] = { - { "video/x-ms-asf", QMediaFormat::WMV }, - { "video/avi", QMediaFormat::AVI }, - { "video/x-matroska", QMediaFormat::Matroska }, - { "video/mp4", QMediaFormat::MPEG4 }, - { "video/quicktime", QMediaFormat::QuickTime }, - { "video/ogg", QMediaFormat::Ogg }, - { "audio/mp3", QMediaFormat::MP3 }, - { nullptr, QMediaFormat::UnspecifiedFormat } -}; - -static struct { - const char *name; - QMediaFormat::VideoCodec value; -} videoCodecMap[] = { - // See CMVideoCodecType for the four character code names of codecs - { "; codecs=\"mp1v\"", QMediaFormat::VideoCodec::MPEG1 }, - { "; codecs=\"mp2v\"", QMediaFormat::VideoCodec::MPEG2 }, - { "; codecs=\"mp4v\"", QMediaFormat::VideoCodec::MPEG4 }, - { "; codecs=\"avc1\"", QMediaFormat::VideoCodec::H264 }, - { "; codecs=\"hvc1\"", QMediaFormat::VideoCodec::H265 }, - { "; codecs=\"vp09\"", QMediaFormat::VideoCodec::VP9 }, - { "; codecs=\"av01\"", QMediaFormat::VideoCodec::AV1 }, // ### ???? - { "; codecs=\"jpeg\"", QMediaFormat::VideoCodec::MotionJPEG }, - { nullptr, QMediaFormat::VideoCodec::Unspecified } -}; - -static struct { - const char *name; - QMediaFormat::AudioCodec value; -} audioCodecMap[] = { - // AudioFile.h - // ### The next two entries do not work, probably because they contain non a space and period and AVFoundation doesn't like that - // We know they are supported on all Apple platforms, so we'll add them manually below -// { "; codecs=\".mp3\"", QMediaFormat::AudioCodec::MP3 }, -// { "; codecs=\"aac \"", QMediaFormat::AudioCodec::AAC }, - { "; codecs=\"ac-3\"", QMediaFormat::AudioCodec::AC3 }, - { "; codecs=\"ec-3\"", QMediaFormat::AudioCodec::EAC3 }, - { "; codecs=\"flac\"", QMediaFormat::AudioCodec::FLAC }, - { "; codecs=\"alac\"", QMediaFormat::AudioCodec::ALAC }, - { "; codecs=\"opus\"", QMediaFormat::AudioCodec::Opus }, - { nullptr, QMediaFormat::AudioCodec::Unspecified }, -}; - -QDarwinFormatInfo::QDarwinFormatInfo() -{ - auto avtypes = [AVURLAsset audiovisualMIMETypes]; - for (AVFileType filetype in avtypes) { - auto *m = mediaContainerMap; - while (m->name) { - if (strcmp(filetype.UTF8String, m->name)) { - ++m; - continue; - } - - QList<QMediaFormat::VideoCodec> video; - QList<QMediaFormat::AudioCodec> audio; - - auto *v = videoCodecMap; - while (v->name) { - QByteArray extendedMimetype = m->name; - extendedMimetype += v->name; - if ([AVURLAsset isPlayableExtendedMIMEType:[NSString stringWithUTF8String:extendedMimetype.constData()]]) - video << v->value; - ++v; - } - - auto *a = audioCodecMap; - while (a->name) { - QByteArray extendedMimetype = m->name; - extendedMimetype += a->name; - if ([AVURLAsset isPlayableExtendedMIMEType:[NSString stringWithUTF8String:extendedMimetype.constData()]]) - audio << a->value; - ++a; - } - // Added manually, see comment in the list above - if (m->value <= QMediaFormat::AAC) - audio << QMediaFormat::AudioCodec::AAC; - if (m->value < QMediaFormat::AAC || m->value == QMediaFormat::MP3) - audio << QMediaFormat::AudioCodec::MP3; - - decoders << CodecMap{ m->value, audio, video }; - ++m; - } - } - - // seems AVFoundation only supports those for encoding - encoders = { - { QMediaFormat::MPEG4, - { QMediaFormat::AudioCodec::AAC, QMediaFormat::AudioCodec::ALAC }, - { QMediaFormat::VideoCodec::H264, QMediaFormat::VideoCodec::H265, QMediaFormat::VideoCodec::MotionJPEG } }, - { QMediaFormat::QuickTime, - { QMediaFormat::AudioCodec::AAC, QMediaFormat::AudioCodec::ALAC }, - { QMediaFormat::VideoCodec::H264, QMediaFormat::VideoCodec::H265, QMediaFormat::VideoCodec::MotionJPEG } }, - { QMediaFormat::Mpeg4Audio, - { QMediaFormat::AudioCodec::AAC }, - {} }, - { QMediaFormat::Wave, - { QMediaFormat::AudioCodec::Wave }, - {} }, - }; - - // ### - imageFormats << QImageCapture::JPEG; -} - -QDarwinFormatInfo::~QDarwinFormatInfo() -{ -} - -int QDarwinFormatInfo::audioFormatForCodec(QMediaFormat::AudioCodec codec) -{ - int codecId = kAudioFormatMPEG4AAC; - switch (codec) { - case QMediaFormat::AudioCodec::Unspecified: - case QMediaFormat::AudioCodec::DolbyTrueHD: - case QMediaFormat::AudioCodec::Vorbis: - case QMediaFormat::AudioCodec::WMA: - // Unsupported, shouldn't happen. Fall back to AAC - case QMediaFormat::AudioCodec::AAC: - codecId = kAudioFormatMPEG4AAC; - break; - case QMediaFormat::AudioCodec::MP3: - codecId = kAudioFormatMPEGLayer3; - break; - case QMediaFormat::AudioCodec::AC3: - codecId = kAudioFormatAC3; - break; - case QMediaFormat::AudioCodec::EAC3: - codecId = kAudioFormatEnhancedAC3; - break; - case QMediaFormat::AudioCodec::FLAC: - codecId = kAudioFormatFLAC; - break; - case QMediaFormat::AudioCodec::ALAC: - codecId = kAudioFormatAppleLossless; - break; - case QMediaFormat::AudioCodec::Opus: - codecId = kAudioFormatOpus; - break; - case QMediaFormat::AudioCodec::Wave: - codecId = kAudioFormatLinearPCM; - } - return codecId; -} - -NSString *QDarwinFormatInfo::videoFormatForCodec(QMediaFormat::VideoCodec codec) -{ - const char *c = "hvc1"; // fallback is H265 - switch (codec) { - case QMediaFormat::VideoCodec::Unspecified: - case QMediaFormat::VideoCodec::VP8: - case QMediaFormat::VideoCodec::H265: - case QMediaFormat::VideoCodec::AV1: - case QMediaFormat::VideoCodec::Theora: - case QMediaFormat::VideoCodec::WMV: - break; - - case QMediaFormat::VideoCodec::MPEG1: - c = "mp1v"; - break; - case QMediaFormat::VideoCodec::MPEG2: - c = "mp2v"; - break; - case QMediaFormat::VideoCodec::MPEG4: - c = "mp4v"; - break; - case QMediaFormat::VideoCodec::H264: - c = "avc1"; - break; - case QMediaFormat::VideoCodec::VP9: - c = "vp09"; - break; - case QMediaFormat::VideoCodec::MotionJPEG: - c = "jpeg"; - } - return [NSString stringWithUTF8String:c]; -} - -NSString *QDarwinFormatInfo::avFileTypeForContainerFormat(QMediaFormat::FileFormat container) -{ - switch (container) { - case QMediaFormat::MPEG4: - return AVFileTypeMPEG4; - case QMediaFormat::QuickTime: - return AVFileTypeQuickTimeMovie; - case QMediaFormat::MP3: - return AVFileTypeMPEGLayer3; - case QMediaFormat::Mpeg4Audio: - return AVFileTypeAppleM4A; - case QMediaFormat::Wave: - return AVFileTypeWAVE; - default: - return AVFileTypeQuickTimeMovie; - } -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/darwin/qdarwinformatsinfo_p.h b/src/multimedia/platform/darwin/qdarwinformatsinfo_p.h deleted file mode 100644 index 79e33b6f4..000000000 --- a/src/multimedia/platform/darwin/qdarwinformatsinfo_p.h +++ /dev/null @@ -1,74 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QDARWINFORMATINFO_H -#define QDARWINFORMATINFO_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediaformatinfo_p.h> -#include <qlist.h> - -QT_BEGIN_NAMESPACE - -class QDarwinMediaDevices; - -class QDarwinFormatInfo : public QPlatformMediaFormatInfo -{ -public: - QDarwinFormatInfo(); - ~QDarwinFormatInfo(); - - static int audioFormatForCodec(QMediaFormat::AudioCodec codec); - static NSString *videoFormatForCodec(QMediaFormat::VideoCodec codec); - static NSString *avFileTypeForContainerFormat(QMediaFormat::FileFormat fileType); -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/darwin/qdarwinintegration.mm b/src/multimedia/platform/darwin/qdarwinintegration.mm deleted file mode 100644 index 372ce9e81..000000000 --- a/src/multimedia/platform/darwin/qdarwinintegration.mm +++ /dev/null @@ -1,113 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qdarwinintegration_p.h" -#include "qdarwinmediadevices_p.h" -#include <private/avfmediaplayer_p.h> -#include <private/avfcameraservice_p.h> -#include <private/avfcamera_p.h> -#include <private/avfimagecapture_p.h> -#include <private/avfmediaencoder_p.h> -#include <private/qdarwinformatsinfo_p.h> -#include <private/avfvideosink_p.h> -#include <private/avfaudiodecoder_p.h> - -QT_BEGIN_NAMESPACE - -QDarwinIntegration::QDarwinIntegration() -{ - -} - -QDarwinIntegration::~QDarwinIntegration() -{ - delete m_devices; - delete m_formatInfo; -} - -QPlatformMediaDevices *QDarwinIntegration::devices() -{ - if (!m_devices) - m_devices = new QDarwinMediaDevices(); - return m_devices; -} - -QPlatformMediaFormatInfo *QDarwinIntegration::formatInfo() -{ - if (!m_formatInfo) - m_formatInfo = new QDarwinFormatInfo(); - return m_formatInfo; -} - -QPlatformAudioDecoder *QDarwinIntegration::createAudioDecoder(QAudioDecoder *decoder) -{ - return new AVFAudioDecoder(decoder); -} - -QPlatformMediaCaptureSession *QDarwinIntegration::createCaptureSession() -{ - return new AVFCameraService; -} - -QPlatformMediaPlayer *QDarwinIntegration::createPlayer(QMediaPlayer *player) -{ - return new AVFMediaPlayer(player); -} - -QPlatformCamera *QDarwinIntegration::createCamera(QCamera *camera) -{ - return new AVFCamera(camera); -} - -QPlatformMediaEncoder *QDarwinIntegration::createEncoder(QMediaRecorder *encoder) -{ - return new AVFMediaEncoder(encoder); -} - -QPlatformImageCapture *QDarwinIntegration::createImageCapture(QImageCapture *imageCapture) -{ - return new AVFImageCapture(imageCapture); -} - -QPlatformVideoSink *QDarwinIntegration::createVideoSink(QVideoSink *sink) -{ - return new AVFVideoSink(sink); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/darwin/qdarwinintegration_p.h b/src/multimedia/platform/darwin/qdarwinintegration_p.h deleted file mode 100644 index 946300761..000000000 --- a/src/multimedia/platform/darwin/qdarwinintegration_p.h +++ /dev/null @@ -1,84 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QDARWININTEGRATION_H -#define QDARWININTEGRATION_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediaintegration_p.h> - -QT_BEGIN_NAMESPACE - -class QDarwinMediaDevices; - -class QDarwinIntegration : public QPlatformMediaIntegration -{ -public: - QDarwinIntegration(); - ~QDarwinIntegration(); - - QPlatformMediaDevices *devices() override; - QPlatformMediaFormatInfo *formatInfo() override; - - QPlatformAudioDecoder *createAudioDecoder(QAudioDecoder *) override; - QPlatformMediaCaptureSession *createCaptureSession() override; - QPlatformMediaPlayer *createPlayer(QMediaPlayer *player) override; - QPlatformCamera *createCamera(QCamera *camera) override; - QPlatformMediaEncoder *createEncoder(QMediaRecorder *) override; - QPlatformImageCapture *createImageCapture(QImageCapture *) override; - - QPlatformVideoSink *createVideoSink(QVideoSink *) override; - - QDarwinMediaDevices *m_devices = nullptr; - QPlatformMediaFormatInfo *m_formatInfo = nullptr; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/darwin/qdarwinmediadevices.mm b/src/multimedia/platform/darwin/qdarwinmediadevices.mm deleted file mode 100644 index b7bb6e7f9..000000000 --- a/src/multimedia/platform/darwin/qdarwinmediadevices.mm +++ /dev/null @@ -1,348 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qdarwinmediadevices_p.h" -#include "qmediadevices.h" -#include "qcameradevice_p.h" -#include "qaudiodevice_p.h" -#include "private/qdarwinaudiodevice_p.h" -#include "private/qdarwinaudiosource_p.h" -#include "private/qdarwinaudiosink_p.h" -#include "private/avfcamera_p.h" -#include "private/avfcamerautility_p.h" -#include "private/avfvideobuffer_p.h" - -#include <CoreVideo/CoreVideo.h> -#import <AVFoundation/AVFoundation.h> - -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) -#include "private/qcoreaudiosessionmanager_p.h" -#endif - -QT_BEGIN_NAMESPACE - -#if defined(Q_OS_MACOS) -AudioDeviceID defaultAudioDevice(QAudioDevice::Mode mode) -{ - AudioDeviceID audioDevice; - UInt32 size = sizeof(audioDevice); - const AudioObjectPropertySelector selector = (mode == QAudioDevice::Output) ? kAudioHardwarePropertyDefaultOutputDevice - : kAudioHardwarePropertyDefaultInputDevice; - AudioObjectPropertyAddress defaultDevicePropertyAddress = { selector, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - - if (AudioObjectGetPropertyData(kAudioObjectSystemObject, - &defaultDevicePropertyAddress, - 0, NULL, &size, &audioDevice) != noErr) { - qWarning("QAudioDevice: Unable to find default %s device", (mode == QAudioDevice::Output) ? "output" : "input"); - return 0; - } - - return audioDevice; -} - -static QByteArray uniqueId(AudioDeviceID device, QAudioDevice::Mode mode) -{ - CFStringRef name; - UInt32 size = sizeof(CFStringRef); - - AudioObjectPropertyScope audioPropertyScope = mode == QAudioDevice::Input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; - - AudioObjectPropertyAddress audioDeviceNamePropertyAddress = { kAudioDevicePropertyDeviceUID, - audioPropertyScope, - kAudioObjectPropertyElementMaster }; - - if (AudioObjectGetPropertyData(device, &audioDeviceNamePropertyAddress, 0, NULL, &size, &name) != noErr) { - qWarning() << "QAudioDevice: Unable to get device UID"; - return QByteArray(); - } - - QString s = QString::fromCFString(name); - CFRelease(name); - return s.toUtf8(); -} - -QList<QAudioDevice> availableAudioDevices(QAudioDevice::Mode mode) -{ - - QList<QAudioDevice> devices; - - AudioDeviceID defaultDevice = defaultAudioDevice(mode); - if (defaultDevice != 0) - devices << (new QCoreAudioDeviceInfo(defaultDevice, uniqueId(defaultDevice, mode), mode))->create(); - - UInt32 propSize = 0; - AudioObjectPropertyAddress audioDevicesPropertyAddress = { kAudioHardwarePropertyDevices, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - - if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, - &audioDevicesPropertyAddress, - 0, NULL, &propSize) == noErr) { - - const int dc = propSize / sizeof(AudioDeviceID); - - if (dc > 0) { - AudioDeviceID* audioDevices = new AudioDeviceID[dc]; - - if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &audioDevicesPropertyAddress, 0, NULL, &propSize, audioDevices) == noErr) { - for (int i = 0; i < dc; ++i) { - if (audioDevices[i] == defaultDevice) - continue; - - AudioStreamBasicDescription sf; - UInt32 size = sizeof(AudioStreamBasicDescription); - AudioObjectPropertyAddress audioDeviceStreamFormatPropertyAddress = { kAudioDevicePropertyStreamFormat, - (mode == QAudioDevice::Input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput), - kAudioObjectPropertyElementMaster }; - - if (AudioObjectGetPropertyData(audioDevices[i], &audioDeviceStreamFormatPropertyAddress, 0, NULL, &size, &sf) == noErr) - devices << (new QCoreAudioDeviceInfo(audioDevices[i], uniqueId(audioDevices[i], mode), mode))->create(); - } - } - - delete[] audioDevices; - } - } - - return devices; -} - -static OSStatus -audioDeviceChangeListener(AudioObjectID, UInt32, const AudioObjectPropertyAddress*, void* ptr) -{ - QDarwinMediaDevices *m = static_cast<QDarwinMediaDevices *>(ptr); - m->updateAudioDevices(); - return 0; -} -#endif - - -QDarwinMediaDevices::QDarwinMediaDevices() - : QPlatformMediaDevices() -{ - NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; - m_deviceConnectedObserver = [notificationCenter addObserverForName:AVCaptureDeviceWasConnectedNotification - object:nil - queue:[NSOperationQueue mainQueue] - usingBlock:^(NSNotification *) { - this->updateCameraDevices(); - this->updateAudioDevices(); - }]; - - m_deviceDisconnectedObserver = [notificationCenter addObserverForName:AVCaptureDeviceWasDisconnectedNotification - object:nil - queue:[NSOperationQueue mainQueue] - usingBlock:^(NSNotification *) { - this->updateCameraDevices(); - this->updateAudioDevices(); - }]; - -#ifdef Q_OS_MACOS - OSStatus err = noErr; - AudioObjectPropertyAddress *audioDevicesAddress = new AudioObjectPropertyAddress{ kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; - m_audioDevicesProperty = audioDevicesAddress; - err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, audioDevicesAddress, audioDeviceChangeListener, this); - if (err) - qDebug("error on AudioObjectAddPropertyListener"); -#else - // ### This should use the audio session manager -#endif - updateCameraDevices(); - updateAudioDevices(); -} - - -QDarwinMediaDevices::~QDarwinMediaDevices() -{ - NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter]; - [notificationCenter removeObserver:(id)m_deviceConnectedObserver]; - [notificationCenter removeObserver:(id)m_deviceDisconnectedObserver]; - -#ifdef Q_OS_MACOS - AudioObjectRemovePropertyListener(kAudioObjectSystemObject, (AudioObjectPropertyAddress *)m_audioDevicesProperty, audioDeviceChangeListener, this); -#endif -} - -QList<QAudioDevice> QDarwinMediaDevices::audioInputs() const -{ -#ifdef Q_OS_IOS - // TODO: Support Bluetooth and USB devices - QList<QAudioDevice> devices; - AVCaptureDeviceDiscoverySession *captureDeviceDiscoverySession = [AVCaptureDeviceDiscoverySession - discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInMicrophone] - mediaType:AVMediaTypeAudio - position:AVCaptureDevicePositionUnspecified]; - - NSArray *captureDevices = [captureDeviceDiscoverySession devices]; - for (AVCaptureDevice *device in captureDevices) - devices << (new QCoreAudioDeviceInfo(QString::fromNSString(device.uniqueID).toUtf8(), QAudioDevice::Input))->create(); - return devices; -#else - return availableAudioDevices(QAudioDevice::Input); -#endif -} - -QList<QAudioDevice> QDarwinMediaDevices::audioOutputs() const -{ -#ifdef Q_OS_IOS - QList<QAudioDevice> devices; - devices.append((new QCoreAudioDeviceInfo("default", QAudioDevice::Output))->create()); - return devices; -#else - return availableAudioDevices(QAudioDevice::Output); -#endif -} - -QList<QCameraDevice> QDarwinMediaDevices::videoInputs() const -{ - return m_cameraDevices; -} - -void QDarwinMediaDevices::updateCameraDevices() -{ -#ifdef Q_OS_IOS - // Cameras can't change dynamically on iOS. Update only once. - if (!m_cameraDevices.isEmpty()) - return; -#endif - - QList<QCameraDevice> cameras; - - AVCaptureDevice *defaultDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; - NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; - - for (AVCaptureDevice *device in videoDevices) { - - QCameraDevicePrivate *info = new QCameraDevicePrivate; - if (defaultDevice && [defaultDevice.uniqueID isEqualToString:device.uniqueID]) - info->isDefault = true; - info->id = QByteArray([[device uniqueID] UTF8String]); - info->description = QString::fromNSString([device localizedName]); - - QSet<QSize> photoResolutions; - QList<QCameraFormat> videoFormats; - - for (AVCaptureDeviceFormat *format in device.formats) { - if (![format.mediaType isEqualToString:AVMediaTypeVideo]) - continue; - - auto dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription); - QSize resolution(dimensions.width, dimensions.height); - photoResolutions.insert(resolution); - - float maxFrameRate = 0; - float minFrameRate = 1.e6; - - auto encoding = CMVideoFormatDescriptionGetCodecType(format.formatDescription); - auto pixelFormat = AVFVideoBuffer::fromCVPixelFormat(encoding); - // Ignore pixel formats we can't handle - if (pixelFormat == QVideoFrameFormat::Format_Invalid) - continue; - - for (AVFrameRateRange *frameRateRange in format.videoSupportedFrameRateRanges) { - if (frameRateRange.minFrameRate < minFrameRate) - minFrameRate = frameRateRange.minFrameRate; - if (frameRateRange.maxFrameRate > maxFrameRate) - maxFrameRate = frameRateRange.maxFrameRate; - } - -#ifdef Q_OS_IOS - // From Apple's docs (iOS): - // By default, AVCaptureStillImageOutput emits images with the same dimensions as - // its source AVCaptureDevice instance’s activeFormat.formatDescription. However, - // if you set this property to YES, the receiver emits still images at the capture - // device’s highResolutionStillImageDimensions value. - const QSize hrRes(qt_device_format_high_resolution(format)); - if (!hrRes.isNull() && hrRes.isValid()) - photoResolutions.insert(hrRes); -#endif - - auto *f = new QCameraFormatPrivate{ - QSharedData(), - pixelFormat, - resolution, - minFrameRate, - maxFrameRate - }; - videoFormats << f->create(); - } - info->videoFormats = videoFormats; - info->photoResolutions = photoResolutions.values(); - - cameras.append(info->create()); - } - - if (cameras != m_cameraDevices) { - m_cameraDevices = cameras; - videoInputsChanged(); - } -} - - -void QDarwinMediaDevices::updateAudioDevices() -{ -#ifdef Q_OS_MACOS - QList<QAudioDevice> inputs = availableAudioDevices(QAudioDevice::Input); - if (m_audioInputs != inputs) { - m_audioInputs = inputs; - audioInputsChanged(); - } - - QList<QAudioDevice> outputs = availableAudioDevices(QAudioDevice::Output); - if (m_audioOutputs!= outputs) { - m_audioOutputs = outputs; - audioOutputsChanged(); - } -#endif -} - -QPlatformAudioSource *QDarwinMediaDevices::createAudioSource(const QAudioDevice &info) -{ - return new QDarwinAudioSource(info); -} - -QPlatformAudioSink *QDarwinMediaDevices::createAudioSink(const QAudioDevice &info) -{ - return new QDarwinAudioSink(info); -} - - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/darwin/qdarwinmediadevices_p.h b/src/multimedia/platform/darwin/qdarwinmediadevices_p.h deleted file mode 100644 index 0c3515f2b..000000000 --- a/src/multimedia/platform/darwin/qdarwinmediadevices_p.h +++ /dev/null @@ -1,94 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QDARWINMEDIADEVICES_H -#define QDARWINMEDIADEVICES_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediadevices_p.h> -#include <qelapsedtimer.h> -#include <qcameradevice.h> - -Q_FORWARD_DECLARE_OBJC_CLASS(NSObject); -Q_FORWARD_DECLARE_OBJC_CLASS(AVCaptureDeviceDiscoverySession); - -QT_BEGIN_NAMESPACE - -class QCameraDevice; - -class QDarwinMediaDevices : public QPlatformMediaDevices -{ -public: - QDarwinMediaDevices(); - ~QDarwinMediaDevices(); - - QList<QAudioDevice> audioInputs() const override; - QList<QAudioDevice> audioOutputs() const override; - QList<QCameraDevice> videoInputs() const override; - QPlatformAudioSource *createAudioSource(const QAudioDevice &info) override; - QPlatformAudioSink *createAudioSink(const QAudioDevice &info) override; - - void updateCameraDevices(); - void updateAudioDevices(); - -private: - QList<QCameraDevice> m_cameraDevices; - QList<QAudioDevice> m_audioInputs; - QList<QAudioDevice> m_audioOutputs; - - NSObject *m_deviceConnectedObserver; - NSObject *m_deviceDisconnectedObserver; -#ifdef Q_OS_MACOS - void *m_audioDevicesProperty; -#endif -}; - -QT_END_NAMESPACE - -#endif 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 diff --git a/src/multimedia/platform/gstreamer/audio/qgstreameraudiodecoder_p.h b/src/multimedia/platform/gstreamer/audio/qgstreameraudiodecoder_p.h deleted file mode 100644 index 693b00c3d..000000000 --- a/src/multimedia/platform/gstreamer/audio/qgstreameraudiodecoder_p.h +++ /dev/null @@ -1,144 +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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERAUDIODECODERCONTROL_H -#define QGSTREAMERAUDIODECODERCONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtMultimedia/private/qtmultimediaglobal_p.h> -#include <QObject> -#include <QtCore/qmutex.h> -#include <QtCore/qurl.h> - -#include "private/qplatformaudiodecoder_p.h" -#include <private/qgstpipeline_p.h> -#include "qaudiodecoder.h" - -#if QT_CONFIG(gstreamer_app) -#include <private/qgstappsrc_p.h> -#endif - -#include <private/qgst_p.h> -#include <gst/app/gstappsink.h> - -QT_BEGIN_NAMESPACE - -class QGstreamerMessage; - -class QGstreamerAudioDecoder - : public QPlatformAudioDecoder, - public QGstreamerBusMessageFilter -{ - Q_OBJECT - -public: - QGstreamerAudioDecoder(QAudioDecoder *parent); - virtual ~QGstreamerAudioDecoder(); - - QUrl source() const override; - void setSource(const QUrl &fileName) override; - - QIODevice *sourceDevice() const override; - void setSourceDevice(QIODevice *device) override; - - void start() override; - void stop() override; - - QAudioFormat audioFormat() const override; - void setAudioFormat(const QAudioFormat &format) override; - - QAudioBuffer read() override; - bool bufferAvailable() const override; - - qint64 position() const override; - qint64 duration() const override; - - // GStreamerBusMessageFilter interface - bool processBusMessage(const QGstreamerMessage &message) override; - -#if QT_CONFIG(gstreamer_app) - QGstAppSrc *appsrc() const { return m_appSrc; } - static void configureAppSrcElement(GObject*, GObject*, GParamSpec*, QGstreamerAudioDecoder *_this); -#endif - - static GstFlowReturn new_sample(GstAppSink *sink, gpointer user_data); - -private slots: - void updateDuration(); - -private: - void setAudioFlags(bool wantNativeAudio); - void addAppSink(); - void removeAppSink(); - - void processInvalidMedia(QAudioDecoder::Error errorCode, const QString& errorString); - static qint64 getPositionFromBuffer(GstBuffer* buffer); - - QGstPipeline m_playbin; - QGstBin m_outputBin; - QGstElement m_audioConvert; - GstAppSink *m_appSink = nullptr; - QGstAppSrc *m_appSrc = nullptr; - - QUrl mSource; - QIODevice *mDevice = nullptr; - QAudioFormat mFormat; - - mutable QMutex m_buffersMutex; - int m_buffersAvailable = 0; - - qint64 m_position = -1; - qint64 m_duration = -1; - - int m_durationQueries = 0; -}; - -QT_END_NAMESPACE - -#endif // QGSTREAMERPLAYERSESSION_H diff --git a/src/multimedia/platform/gstreamer/audio/qgstreameraudiodevice.cpp b/src/multimedia/platform/gstreamer/audio/qgstreameraudiodevice.cpp deleted file mode 100644 index 301283013..000000000 --- a/src/multimedia/platform/gstreamer/audio/qgstreameraudiodevice.cpp +++ /dev/null @@ -1,92 +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$ -** -****************************************************************************/ - -#include "qgstreameraudiodevice_p.h" - -#include <private/qgstutils_p.h> -#include <private/qplatformmediaintegration_p.h> -#include <private/qgstreamermediadevices_p.h> - -QT_BEGIN_NAMESPACE - -QGStreamerAudioDeviceInfo::QGStreamerAudioDeviceInfo(GstDevice *d, const QByteArray &device, QAudioDevice::Mode mode) - : QAudioDevicePrivate(device, mode), - gstDevice(d) -{ - Q_ASSERT(gstDevice); - gst_object_ref(gstDevice); - - auto *n = gst_device_get_display_name(gstDevice); - description = QString::fromUtf8(n); - g_free(n); - - QGstCaps caps = gst_device_get_caps(gstDevice); - int size = caps.size(); - for (int i = 0; i < size; ++i) { - auto c = caps.at(i); - if (c.name() == "audio/x-raw") { - auto rate = c["rate"].toIntRange(); - if (rate) { - minimumSampleRate = rate->min; - maximumSampleRate = rate->max; - } - auto channels = c["channels"].toIntRange(); - if (channels) { - minimumChannelCount = channels->min; - maximumChannelCount = channels->max; - } - supportedSampleFormats = c["format"].getSampleFormats(); - } - } - - preferredFormat.setChannelCount(qBound(minimumChannelCount, 2, maximumChannelCount)); - preferredFormat.setSampleRate(qBound(minimumSampleRate, 48000, maximumSampleRate)); - QAudioFormat::SampleFormat f = QAudioFormat::Int16; - if (!supportedSampleFormats.contains(f)) - f = supportedSampleFormats.value(0, QAudioFormat::Unknown); - preferredFormat.setSampleFormat(f); -} - -QGStreamerAudioDeviceInfo::~QGStreamerAudioDeviceInfo() -{ - if (gstDevice) - gst_object_unref(gstDevice); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/gstreamer/audio/qgstreameraudiodevice_p.h b/src/multimedia/platform/gstreamer/audio/qgstreameraudiodevice_p.h deleted file mode 100644 index 9da109e38..000000000 --- a/src/multimedia/platform/gstreamer/audio/qgstreameraudiodevice_p.h +++ /dev/null @@ -1,78 +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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERAUDIODEVICEINFO_H -#define QGSTREAMERAUDIODEVICEINFO_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qbytearray.h> -#include <QtCore/qstringlist.h> -#include <QtCore/qlist.h> - -#include "qaudio.h" -#include "qaudiodevice.h" -#include <private/qaudiodevice_p.h> - -#include <gst/gst.h> - -QT_BEGIN_NAMESPACE - -class QGStreamerAudioDeviceInfo : public QAudioDevicePrivate -{ -public: - QGStreamerAudioDeviceInfo(GstDevice *gstDevice, const QByteArray &device, QAudioDevice::Mode mode); - ~QGStreamerAudioDeviceInfo(); - - GstDevice *gstDevice = nullptr; -}; - -QT_END_NAMESPACE - -#endif - diff --git a/src/multimedia/platform/gstreamer/audio/qgstreameraudiosink.cpp b/src/multimedia/platform/gstreamer/audio/qgstreameraudiosink.cpp deleted file mode 100644 index 3bf5b597b..000000000 --- a/src/multimedia/platform/gstreamer/audio/qgstreameraudiosink.cpp +++ /dev/null @@ -1,395 +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$ -** -****************************************************************************/ - -#include <QtCore/qcoreapplication.h> -#include <QtCore/qdebug.h> -#include <QtCore/qmath.h> -#include <private/qaudiohelpers_p.h> - -#include "qgstreameraudiosink_p.h" -#include "qgstreameraudiodevice_p.h" -#include <sys/types.h> -#include <unistd.h> - -#include <private/qgstpipeline_p.h> -#include <private/qgstappsrc_p.h> - -#include <private/qgstutils_p.h> -#include <private/qgstreamermessage_p.h> - -QT_BEGIN_NAMESPACE - -QGStreamerAudioSink::QGStreamerAudioSink(const QAudioDevice &device) - : m_device(device.id()), - gstPipeline("pipeline") -{ - gstPipeline.installMessageFilter(this); - - m_appSrc = new QGstAppSrc; - connect(m_appSrc, &QGstAppSrc::bytesProcessed, this, &QGStreamerAudioSink::bytesProcessedByAppSrc); - connect(m_appSrc, &QGstAppSrc::noMoreData, this, &QGStreamerAudioSink::needData); - gstAppSrc = m_appSrc->element(); - - // gstDecodeBin = gst_element_factory_make ("decodebin", "dec"); - QGstElement queue("queue", "queue"); - QGstElement conv("audioconvert", "conv"); - gstVolume = QGstElement("volume", "volume"); - if (m_volume != 1.) - gstVolume.set("volume", m_volume); - - // link decodeBin to audioconvert in a callback once we get a pad from the decoder - // g_signal_connect (gstDecodeBin, "pad-added", (GCallback) padAdded, conv); - - const auto *audioInfo = static_cast<const QGStreamerAudioDeviceInfo *>(device.handle()); - gstOutput = gst_device_create_element(audioInfo->gstDevice, nullptr); - - gstPipeline.add(gstAppSrc, queue, /*gstDecodeBin, */ conv, gstVolume, gstOutput); - gstAppSrc.link(queue, conv, gstVolume, gstOutput); -} - -QGStreamerAudioSink::~QGStreamerAudioSink() -{ - close(); - gstPipeline = {}; - gstVolume = {}; - gstAppSrc = {}; - delete m_appSrc; - m_appSrc = nullptr; -} - -void QGStreamerAudioSink::setError(QAudio::Error error) -{ - if (m_errorState == error) - return; - - m_errorState = error; - emit errorChanged(error); -} - -QAudio::Error QGStreamerAudioSink::error() const -{ - return m_errorState; -} - -void QGStreamerAudioSink::setState(QAudio::State state) -{ - if (m_deviceState == state) - return; - - m_deviceState = state; - emit stateChanged(state); -} - -QAudio::State QGStreamerAudioSink::state() const -{ - return m_deviceState; -} - -void QGStreamerAudioSink::start(QIODevice *device) -{ - setState(QAudio::StoppedState); - setError(QAudio::NoError); - - close(); - - if (!m_format.isValid()) { - setError(QAudio::OpenError); - return; - } - - m_pullMode = true; - m_audioSource = device; - - if (!open()) { - m_audioSource = nullptr; - setError(QAudio::OpenError); - return; - } - - setState(QAudio::ActiveState); -} - -QIODevice *QGStreamerAudioSink::start() -{ - setState(QAudio::StoppedState); - setError(QAudio::NoError); - - close(); - - if (!m_format.isValid()) { - setError(QAudio::OpenError); - return nullptr; - } - - m_pullMode = false; - - if (!open()) - return nullptr; - - m_audioSource = new GStreamerOutputPrivate(this); - m_audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered); - - setState(QAudio::IdleState); - - return m_audioSource; -} - -#if 0 -static void padAdded(GstElement *element, GstPad *pad, gpointer data) -{ - GstElement *other = static_cast<GstElement *>(data); - - gchar *name = gst_pad_get_name(pad); - qDebug("A new pad %s was created for %s\n", name, gst_element_get_name(element)); - g_free(name); - - qDebug("element %s will be linked to %s\n", - gst_element_get_name(element), - gst_element_get_name(other)); - gst_element_link(element, other); -} -#endif - -bool QGStreamerAudioSink::processBusMessage(const QGstreamerMessage &message) -{ - auto *msg = message.rawMessage(); - switch (GST_MESSAGE_TYPE (msg)) { - case GST_MESSAGE_EOS: - setState(QAudio::IdleState); - break; - case GST_MESSAGE_ERROR: { - setError(QAudio::IOError); - gchar *debug; - GError *error; - - gst_message_parse_error (msg, &error, &debug); - g_free (debug); - - qDebug("Error: %s\n", error->message); - g_error_free (error); - - break; - } - default: - break; - } - - return true; -} - -bool QGStreamerAudioSink::open() -{ - if (m_opened) - return true; - - if (gstOutput.isNull()) { - setError(QAudio::OpenError); - setState(QAudio::StoppedState); - return false; - } - -// qDebug() << "GST caps:" << gst_caps_to_string(caps); - m_appSrc->setup(m_audioSource, m_audioSource ? m_audioSource->pos() : 0); - m_appSrc->setAudioFormat(m_format); - - /* run */ - gstPipeline.setState(GST_STATE_PLAYING); - - m_opened = true; - - m_timeStamp.restart(); - m_bytesProcessed = 0; - - return true; -} - -void QGStreamerAudioSink::close() -{ - if (!m_opened) - return; - - if (!gstPipeline.setStateSync(GST_STATE_NULL)) - qWarning() << "failed to close the audio output stream"; - - if (!m_pullMode && m_audioSource) - delete m_audioSource; - m_audioSource = nullptr; - m_opened = false; -} - -qint64 QGStreamerAudioSink::write(const char *data, qint64 len) -{ - if (!len) - return 0; - if (m_errorState == QAudio::UnderrunError) - m_errorState = QAudio::NoError; - - m_appSrc->write(data, len); - return len; -} - -void QGStreamerAudioSink::stop() -{ - if (m_deviceState == QAudio::StoppedState) - return; - - close(); - - setError(QAudio::NoError); - setState(QAudio::StoppedState); -} - -qsizetype QGStreamerAudioSink::bytesFree() const -{ - if (m_deviceState != QAudio::ActiveState && m_deviceState != QAudio::IdleState) - return 0; - - return m_appSrc->canAcceptMoreData() ? 4096*4 : 0; -} - -void QGStreamerAudioSink::setBufferSize(qsizetype value) -{ - m_bufferSize = value; - if (!gstAppSrc.isNull()) - gst_app_src_set_max_bytes(GST_APP_SRC(gstAppSrc.element()), value); -} - -qsizetype QGStreamerAudioSink::bufferSize() const -{ - return m_bufferSize; -} - -qint64 QGStreamerAudioSink::processedUSecs() const -{ - qint64 result = qint64(1000000) * m_bytesProcessed / - m_format.bytesPerFrame() / - m_format.sampleRate(); - - return result; -} - -void QGStreamerAudioSink::resume() -{ - if (m_deviceState == QAudio::SuspendedState) { - m_appSrc->resume(); - gstPipeline.setState(GST_STATE_PLAYING); - - setState(m_pullMode ? QAudio::ActiveState : QAudio::IdleState); - setError(QAudio::NoError); - } -} - -void QGStreamerAudioSink::setFormat(const QAudioFormat &format) -{ - m_format = format; -} - -QAudioFormat QGStreamerAudioSink::format() const -{ - return m_format; -} - -void QGStreamerAudioSink::suspend() -{ - if (m_deviceState == QAudio::ActiveState || m_deviceState == QAudio::IdleState) { - setError(QAudio::NoError); - setState(QAudio::SuspendedState); - - gstPipeline.setState(GST_STATE_PAUSED); - m_appSrc->suspend(); - // ### elapsed time - } -} - -void QGStreamerAudioSink::reset() -{ - stop(); -} - -GStreamerOutputPrivate::GStreamerOutputPrivate(QGStreamerAudioSink *audio) -{ - m_audioDevice = audio; -} - -qint64 GStreamerOutputPrivate::readData(char *data, qint64 len) -{ - Q_UNUSED(data); - Q_UNUSED(len); - - return 0; -} - -qint64 GStreamerOutputPrivate::writeData(const char *data, qint64 len) -{ - if (m_audioDevice->state() == QAudio::IdleState) - m_audioDevice->setState(QAudio::ActiveState); - return m_audioDevice->write(data, len); -} - -void QGStreamerAudioSink::setVolume(qreal vol) -{ - if (m_volume == vol) - return; - - m_volume = vol; - if (!gstVolume.isNull()) - gstVolume.set("volume", vol); -} - -qreal QGStreamerAudioSink::volume() const -{ - return m_volume; -} - -void QGStreamerAudioSink::bytesProcessedByAppSrc(int bytes) -{ - m_bytesProcessed += bytes; -} - -void QGStreamerAudioSink::needData() -{ - if (state() != QAudio::StoppedState && state() != QAudio::IdleState) { - setState(QAudio::IdleState); - setError(QAudio::UnderrunError); - } -} - -QT_END_NAMESPACE - -#include "moc_qgstreameraudiosink_p.cpp" diff --git a/src/multimedia/platform/gstreamer/audio/qgstreameraudiosink_p.h b/src/multimedia/platform/gstreamer/audio/qgstreameraudiosink_p.h deleted file mode 100644 index 872f0f241..000000000 --- a/src/multimedia/platform/gstreamer/audio/qgstreameraudiosink_p.h +++ /dev/null @@ -1,157 +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$ -** -****************************************************************************/ - -#ifndef QAUDIOOUTPUTPULSE_H -#define QAUDIOOUTPUTPULSE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qfile.h> -#include <QtCore/qtimer.h> -#include <QtCore/qstring.h> -#include <QtCore/qstringlist.h> -#include <QtCore/qelapsedtimer.h> -#include <QtCore/qiodevice.h> -#include <QtCore/private/qringbuffer_p.h> - -#include "qaudio.h" -#include "qaudiodevice.h" -#include <private/qaudiosystem_p.h> - -#include <private/qgst_p.h> -#include <private/qgstpipeline_p.h> - -QT_BEGIN_NAMESPACE - -class QGstAppSrc; - -class QGStreamerAudioSink - : public QPlatformAudioSink, - public QGstreamerBusMessageFilter -{ - friend class GStreamerOutputPrivate; - Q_OBJECT - -public: - QGStreamerAudioSink(const QAudioDevice &device); - ~QGStreamerAudioSink(); - - void start(QIODevice *device) override; - QIODevice *start() override; - void stop() override; - void reset() override; - void suspend() override; - void resume() override; - qsizetype bytesFree() const override; - void setBufferSize(qsizetype value) override; - qsizetype bufferSize() const override; - qint64 processedUSecs() const override; - QAudio::Error error() const override; - QAudio::State state() const override; - void setFormat(const QAudioFormat &format) override; - QAudioFormat format() const override; - - void setVolume(qreal volume) override; - qreal volume() const override; - -private Q_SLOTS: - void bytesProcessedByAppSrc(int bytes); - void needData(); - -private: - void setState(QAudio::State state); - void setError(QAudio::Error error); - - bool processBusMessage(const QGstreamerMessage &message) override; - - bool open(); - void close(); - qint64 write(const char *data, qint64 len); - -private: - QByteArray m_device; - QAudioFormat m_format; - QAudio::Error m_errorState = QAudio::NoError; - QAudio::State m_deviceState = QAudio::StoppedState; - bool m_pullMode = true; - bool m_opened = false; - QIODevice *m_audioSource = nullptr; - QTimer m_periodTimer; - int m_bufferSize = 0; - qint64 m_bytesProcessed = 0; - QElapsedTimer m_timeStamp; - qreal m_volume = 1.; - QByteArray pushData; - - QGstPipeline gstPipeline; - QGstElement gstOutput; - QGstElement gstVolume; - QGstElement gstAppSrc; - QGstAppSrc *m_appSrc = nullptr; -}; - -class GStreamerOutputPrivate : public QIODevice -{ - friend class QGStreamerAudioSink; - Q_OBJECT - -public: - GStreamerOutputPrivate(QGStreamerAudioSink *audio); - virtual ~GStreamerOutputPrivate() {} - -protected: - qint64 readData(char *data, qint64 len) override; - qint64 writeData(const char *data, qint64 len) override; - -private: - QGStreamerAudioSink *m_audioDevice; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/gstreamer/audio/qgstreameraudiosource.cpp b/src/multimedia/platform/gstreamer/audio/qgstreameraudiosource.cpp deleted file mode 100644 index ffa402627..000000000 --- a/src/multimedia/platform/gstreamer/audio/qgstreameraudiosource.cpp +++ /dev/null @@ -1,407 +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$ -** -****************************************************************************/ - -#include <QtCore/qcoreapplication.h> -#include <QtCore/qdebug.h> -#include <QtCore/qmath.h> -#include <private/qaudiohelpers_p.h> - -#include "qgstreameraudiosource_p.h" -#include "qgstreameraudiodevice_p.h" -#include <sys/types.h> -#include <unistd.h> - -#include <gst/gst.h> -Q_DECLARE_OPAQUE_POINTER(GstSample *); -Q_DECLARE_METATYPE(GstSample *); - -QT_BEGIN_NAMESPACE - - -QGStreamerAudioSource::QGStreamerAudioSource(const QAudioDevice &device) - : m_info(device), - m_device(device.id()) -{ - qRegisterMetaType<GstSample *>(); -} - -QGStreamerAudioSource::~QGStreamerAudioSource() -{ - close(); -} - -void QGStreamerAudioSource::setError(QAudio::Error error) -{ - if (m_errorState == error) - return; - - m_errorState = error; - emit errorChanged(error); -} - -QAudio::Error QGStreamerAudioSource::error() const -{ - return m_errorState; -} - -void QGStreamerAudioSource::setState(QAudio::State state) -{ - if (m_deviceState == state) - return; - - m_deviceState = state; - emit stateChanged(state); -} - -QAudio::State QGStreamerAudioSource::state() const -{ - return m_deviceState; -} - -void QGStreamerAudioSource::setFormat(const QAudioFormat &format) -{ - if (m_deviceState == QAudio::StoppedState) - m_format = format; -} - -QAudioFormat QGStreamerAudioSource::format() const -{ - return m_format; -} - -void QGStreamerAudioSource::start(QIODevice *device) -{ - setState(QAudio::StoppedState); - setError(QAudio::NoError); - - close(); - - if (!open()) - return; - - m_pullMode = true; - m_audioSink = device; - - setState(QAudio::ActiveState); -} - -QIODevice *QGStreamerAudioSource::start() -{ - setState(QAudio::StoppedState); - setError(QAudio::NoError); - - close(); - - if (!open()) - return nullptr; - - m_pullMode = false; - m_audioSink = new GStreamerInputPrivate(this); - m_audioSink->open(QIODevice::ReadOnly | QIODevice::Unbuffered); - - setState(QAudio::IdleState); - - return m_audioSink; -} - -void QGStreamerAudioSource::stop() -{ - if (m_deviceState == QAudio::StoppedState) - return; - - close(); - - setError(QAudio::NoError); - setState(QAudio::StoppedState); -} - -bool QGStreamerAudioSource::open() -{ - if (m_opened) - return true; - - const auto *deviceInfo = static_cast<const QGStreamerAudioDeviceInfo *>(m_info.handle()); - if (!deviceInfo->gstDevice) { - setError(QAudio::OpenError); - setState(QAudio::StoppedState); - return false; - } - - gstInput = QGstElement(gst_device_create_element(deviceInfo->gstDevice, nullptr)); - if (gstInput.isNull()) { - setError(QAudio::OpenError); - setState(QAudio::StoppedState); - return false; - } - - auto gstCaps = QGstUtils::capsForAudioFormat(m_format); - - if (gstCaps.isNull()) { - setError(QAudio::OpenError); - setState(QAudio::StoppedState); - return false; - } - - -#ifdef DEBUG_AUDIO - qDebug() << "Opening input" << QTime::currentTime(); - qDebug() << "Caps: " << gst_caps_to_string(gstCaps); -#endif - - gstPipeline = QGstPipeline("pipeline"); - - auto *gstBus = gst_pipeline_get_bus(gstPipeline.pipeline()); - gst_bus_add_watch(gstBus, &QGStreamerAudioSource::busMessage, this); - gst_object_unref (gstBus); - - gstAppSink = createAppSink(); - gstAppSink.set("caps", gstCaps); - - QGstElement conv("audioconvert", "conv"); - gstVolume = QGstElement("volume", "volume"); - if (m_volume != 1.) - gstVolume.set("volume", m_volume); - - gstPipeline.add(gstInput, gstVolume, conv, gstAppSink); - gstInput.link(gstVolume, conv, gstAppSink); - - gstPipeline.setState(GST_STATE_PLAYING); - - m_opened = true; - - m_timeStamp.restart(); - m_elapsedTimeOffset = 0; - m_bytesWritten = 0; - - return true; -} - -void QGStreamerAudioSource::close() -{ - if (!m_opened) - return; - - gstPipeline.setState(GST_STATE_NULL); - gstPipeline = {}; - gstVolume = {}; - gstAppSink = {}; - gstInput = {}; - - if (!m_pullMode && m_audioSink) { - delete m_audioSink; - } - m_audioSink = nullptr; - m_opened = false; -} - -gboolean QGStreamerAudioSource::busMessage(GstBus *, GstMessage *msg, gpointer user_data) -{ - QGStreamerAudioSource *input = static_cast<QGStreamerAudioSource *>(user_data); - switch (GST_MESSAGE_TYPE (msg)) { - case GST_MESSAGE_EOS: - input->stop(); - break; - case GST_MESSAGE_ERROR: { - input->setError(QAudio::IOError); - gchar *debug; - GError *error; - - gst_message_parse_error (msg, &error, &debug); - g_free (debug); - - qDebug("Error: %s\n", error->message); - g_error_free (error); - - break; - } - default: - break; - } - return false; -} - -qsizetype QGStreamerAudioSource::bytesReady() const -{ - return m_buffer.size(); -} - -void QGStreamerAudioSource::resume() -{ - if (m_deviceState == QAudio::SuspendedState || m_deviceState == QAudio::IdleState) { - gstPipeline.setState(GST_STATE_PLAYING); - setState(QAudio::ActiveState); - setError(QAudio::NoError); - } -} - -void QGStreamerAudioSource::setVolume(qreal vol) -{ - if (m_volume == vol) - return; - - m_volume = vol; - if (!gstVolume.isNull()) - gstVolume.set("volume", vol); -} - -qreal QGStreamerAudioSource::volume() const -{ - return m_volume; -} - -void QGStreamerAudioSource::setBufferSize(qsizetype value) -{ - m_bufferSize = value; -} - -qsizetype QGStreamerAudioSource::bufferSize() const -{ - return m_bufferSize; -} - -qint64 QGStreamerAudioSource::processedUSecs() const -{ - return m_format.durationForBytes(m_bytesWritten); -} - -void QGStreamerAudioSource::suspend() -{ - if (m_deviceState == QAudio::ActiveState) { - setError(QAudio::NoError); - setState(QAudio::SuspendedState); - - gstPipeline.setState(GST_STATE_PAUSED); - } -} - -void QGStreamerAudioSource::reset() -{ - stop(); - m_buffer.clear(); -} - -//#define MAX_BUFFERS_IN_QUEUE 4 - -QGstElement QGStreamerAudioSource::createAppSink() -{ - QGstElement sink("appsink", "appsink"); - GstAppSink *appSink = reinterpret_cast<GstAppSink *>(sink.element()); - - GstAppSinkCallbacks callbacks; - memset(&callbacks, 0, sizeof(callbacks)); - callbacks.eos = &eos; - callbacks.new_sample = &new_sample; - gst_app_sink_set_callbacks(appSink, &callbacks, this, nullptr); -// gst_app_sink_set_max_buffers(appSink, MAX_BUFFERS_IN_QUEUE); - gst_base_sink_set_sync(GST_BASE_SINK(appSink), FALSE); - - return sink; -} - -void QGStreamerAudioSource::newDataAvailable(GstSample *sample) -{ - if (m_audioSink) { - GstBuffer *buffer = gst_sample_get_buffer(sample); - GstMapInfo mapInfo; - gst_buffer_map(buffer, &mapInfo, GST_MAP_READ); - const char *bufferData = (const char*)mapInfo.data; - gsize bufferSize = mapInfo.size; - - if (!m_pullMode) { - // need to store that data in the QBuffer - m_buffer.append(bufferData, bufferSize); - m_audioSink->readyRead(); - } else { - m_bytesWritten += bufferSize; - m_audioSink->write(bufferData, bufferSize); - } - - gst_buffer_unmap(buffer, &mapInfo); - } - - gst_sample_unref(sample); -} - -GstFlowReturn QGStreamerAudioSource::new_sample(GstAppSink *sink, gpointer user_data) -{ - // "Note that the preroll buffer will also be returned as the first buffer when calling gst_app_sink_pull_buffer()." - QGStreamerAudioSource *control = static_cast<QGStreamerAudioSource*>(user_data); - - GstSample *sample = gst_app_sink_pull_sample(sink); - QMetaObject::invokeMethod(control, "newDataAvailable", Qt::AutoConnection, Q_ARG(GstSample *, sample)); - - return GST_FLOW_OK; -} - -void QGStreamerAudioSource::eos(GstAppSink *, gpointer user_data) -{ - QGStreamerAudioSource *control = static_cast<QGStreamerAudioSource*>(user_data); - control->setState(QAudio::StoppedState); -} - -GStreamerInputPrivate::GStreamerInputPrivate(QGStreamerAudioSource *audio) -{ - m_audioDevice = qobject_cast<QGStreamerAudioSource*>(audio); -} - -qint64 GStreamerInputPrivate::readData(char *data, qint64 len) -{ - if (m_audioDevice->state() == QAudio::IdleState) - m_audioDevice->setState(QAudio::ActiveState); - qint64 bytes = m_audioDevice->m_buffer.read(data, len); - m_audioDevice->m_bytesWritten += bytes; - return bytes; -} - -qint64 GStreamerInputPrivate::writeData(const char *data, qint64 len) -{ - Q_UNUSED(data); - Q_UNUSED(len); - return 0; -} - -qint64 GStreamerInputPrivate::bytesAvailable() const -{ - return m_audioDevice->m_buffer.size(); -} - - -QT_END_NAMESPACE - -#include "moc_qgstreameraudiosource_p.cpp" diff --git a/src/multimedia/platform/gstreamer/audio/qgstreameraudiosource_p.h b/src/multimedia/platform/gstreamer/audio/qgstreameraudiosource_p.h deleted file mode 100644 index 67e11c1c5..000000000 --- a/src/multimedia/platform/gstreamer/audio/qgstreameraudiosource_p.h +++ /dev/null @@ -1,159 +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$ -** -****************************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of other Qt classes. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#ifndef QAUDIOINPUTPULSE_H -#define QAUDIOINPUTPULSE_H - -#include <QtCore/qfile.h> -#include <QtCore/qtimer.h> -#include <QtCore/qstring.h> -#include <QtCore/qstringlist.h> -#include <QtCore/qelapsedtimer.h> -#include <QtCore/qiodevice.h> -#include <QtCore/qmutex.h> -#include <QtCore/qatomic.h> -#include <QtCore/private/qringbuffer_p.h> - -#include "qaudio.h" -#include "qaudiodevice.h" -#include <private/qaudiosystem_p.h> - -#include <private/qgstutils_p.h> -#include <private/qgstpipeline_p.h> -#include <gst/app/gstappsink.h> - -QT_BEGIN_NAMESPACE - -class GStreamerInputPrivate; - -class QGStreamerAudioSource - : public QPlatformAudioSource -{ - Q_OBJECT - friend class GStreamerInputPrivate; -public: - QGStreamerAudioSource(const QAudioDevice &device); - ~QGStreamerAudioSource(); - - void start(QIODevice *device) override; - QIODevice *start() override; - void stop() override; - void reset() override; - void suspend() override; - void resume() override; - qsizetype bytesReady() const override; - void setBufferSize(qsizetype value) override; - qsizetype bufferSize() const override; - qint64 processedUSecs() const override; - QAudio::Error error() const override; - QAudio::State state() const override; - void setFormat(const QAudioFormat &format) override; - QAudioFormat format() const override; - - void setVolume(qreal volume) override; - qreal volume() const override; - -private Q_SLOTS: - void newDataAvailable(GstSample *sample); - -private: - void setState(QAudio::State state); - void setError(QAudio::Error error); - - QGstElement createAppSink(); - static GstFlowReturn new_sample(GstAppSink *, gpointer user_data); - static void eos(GstAppSink *, gpointer user_data); - - bool open(); - void close(); - - static gboolean busMessage(GstBus *bus, GstMessage *msg, gpointer user_data); - - QAudioDevice m_info; - qint64 m_bytesWritten = 0; - QIODevice *m_audioSink = nullptr; - QAudioFormat m_format; - QAudio::Error m_errorState = QAudio::NoError; - QAudio::State m_deviceState = QAudio::StoppedState; - qreal m_volume = 1.; - - QRingBuffer m_buffer; - QAtomicInteger<bool> m_pullMode = true; - bool m_opened = false; - int m_bufferSize = 0; - qint64 m_elapsedTimeOffset = 0; - QElapsedTimer m_timeStamp; - QByteArray m_device; - QByteArray m_tempBuffer; - - QGstElement gstInput; - QGstPipeline gstPipeline; - QGstElement gstVolume; - QGstElement gstAppSink; -}; - -class GStreamerInputPrivate : public QIODevice -{ - Q_OBJECT -public: - GStreamerInputPrivate(QGStreamerAudioSource *audio); - ~GStreamerInputPrivate() {}; - - qint64 readData(char *data, qint64 len) override; - qint64 writeData(const char *data, qint64 len) override; - qint64 bytesAvailable() const override; - bool isSequential() const override { return true; } -private: - QGStreamerAudioSource *m_audioDevice; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/gstreamer/common/qgst_p.h b/src/multimedia/platform/gstreamer/common/qgst_p.h deleted file mode 100644 index 0130ca00f..000000000 --- a/src/multimedia/platform/gstreamer/common/qgst_p.h +++ /dev/null @@ -1,620 +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$ -** -****************************************************************************/ - -#ifndef QGST_P_H -#define QGST_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qtmultimediaglobal_p.h> - -#include <QSemaphore> -#include <QtCore/qlist.h> - -#include <QtMultimedia/qaudioformat.h> -#include <QtMultimedia/qvideoframe.h> - -#include <gst/gst.h> -#include <gst/video/video-info.h> - -#include <functional> - -#if QT_CONFIG(gstreamer_photography) -#define GST_USE_UNSTABLE_API -#include <gst/interfaces/photography.h> -#undef GST_USE_UNSTABLE_API -#endif -#ifndef QT_NO_DEBUG -#include <qdebug.h> -#endif - -QT_BEGIN_NAMESPACE - -class QSize; -class QGstStructure; -class QGstCaps; -class QGstPipelinePrivate; -class QCameraFormat; - -template <typename T> struct QGRange -{ - T min; - T max; -}; - -class QGString -{ - char *str; -public: - QGString(char *string) : str(string) {} - ~QGString() { g_free(str); } - operator QByteArray() { return QByteArray(str); } - operator const char *() { return str; } -}; - -class QGValue -{ -public: - QGValue(const GValue *v) : value(v) {} - const GValue *value; - - bool isNull() const { return !value; } - - std::optional<bool> toBool() const - { - if (!G_VALUE_HOLDS_BOOLEAN(value)) - return std::nullopt; - return g_value_get_boolean(value); - } - std::optional<int> toInt() const - { - if (!G_VALUE_HOLDS_INT(value)) - return std::nullopt; - return g_value_get_int(value); - } - std::optional<int> toInt64() const - { - if (!G_VALUE_HOLDS_INT64(value)) - return std::nullopt; - return g_value_get_int64(value); - } - template<typename T> - T *getPointer() const - { - return value ? static_cast<T *>(g_value_get_pointer(value)) : nullptr; - } - - const char *toString() const - { - return value ? g_value_get_string(value) : nullptr; - } - std::optional<float> getFraction() const - { - if (!GST_VALUE_HOLDS_FRACTION(value)) - return std::nullopt; - return (float)gst_value_get_fraction_numerator(value)/(float)gst_value_get_fraction_denominator(value); - } - - std::optional<QGRange<float>> getFractionRange() const - { - if (!GST_VALUE_HOLDS_FRACTION_RANGE(value)) - return std::nullopt; - QGValue min = gst_value_get_fraction_range_min(value); - QGValue max = gst_value_get_fraction_range_max(value); - return QGRange<float>{ *min.getFraction(), *max.getFraction() }; - } - - std::optional<QGRange<int>> toIntRange() const - { - if (!GST_VALUE_HOLDS_INT_RANGE(value)) - return std::nullopt; - return QGRange<int>{ gst_value_get_int_range_min(value), gst_value_get_int_range_max(value) }; - } - - inline QGstStructure toStructure() const; - inline QGstCaps toCaps() const; - - inline bool isList() const { return value && GST_VALUE_HOLDS_LIST(value); } - inline int listSize() const { return gst_value_list_get_size(value); } - inline QGValue at(int index) const { return gst_value_list_get_value(value, index); } - - Q_MULTIMEDIA_EXPORT QList<QAudioFormat::SampleFormat> getSampleFormats() const; -}; - -class QGstStructure { -public: - const GstStructure *structure = nullptr; - QGstStructure() = default; - QGstStructure(const GstStructure *s) : structure(s) {} - void free() { if (structure) gst_structure_free(const_cast<GstStructure *>(structure)); structure = nullptr; } - - bool isNull() const { return !structure; } - - QByteArrayView name() const { return gst_structure_get_name(structure); } - - QGValue operator[](const char *name) const { return gst_structure_get_value(structure, name); } - - GstMessage *getMessage() const - { - GstMessage *msg; - gst_structure_get(structure, "message", GST_TYPE_MESSAGE, &msg, nullptr); - return msg; - } - - Q_MULTIMEDIA_EXPORT QSize resolution() const; - Q_MULTIMEDIA_EXPORT QVideoFrameFormat::PixelFormat pixelFormat() const; - Q_MULTIMEDIA_EXPORT QGRange<float> frameRateRange() const; - - QByteArray toString() const - { - char *s = gst_structure_to_string(structure); - QByteArray str(s); - g_free(s); - return str; - } - QGstStructure copy() const { return gst_structure_copy(structure); } -}; - -class QGstCaps { - const GstCaps *caps = nullptr; -public: - enum MemoryFormat { - CpuMemory, - GLTexture, - DMABuf - }; - - QGstCaps() = default; - QGstCaps(const GstCaps *c) : caps(c) {} - - bool isNull() const { return !caps; } - - int size() const { return gst_caps_get_size(caps); } - QGstStructure at(int index) { return gst_caps_get_structure(caps, index); } - const GstCaps *get() const { return caps; } - QByteArray toString() const - { - gchar *c = gst_caps_to_string(caps); - QByteArray b(c); - g_free(c); - return b; - } - MemoryFormat memoryFormat() const { - auto *features = gst_caps_get_features(caps, 0); - if (gst_caps_features_contains(features, "memory:GLMemory")) - return GLTexture; - else if (gst_caps_features_contains(features, "memory:DMABuf")) - return DMABuf; - return CpuMemory; - } - QVideoFrameFormat formatForCaps(GstVideoInfo *info) const; -}; - -class QGstMutableCaps : public QGstCaps { - GstCaps *caps = nullptr; -public: - enum RefMode { HasRef, NeedsRef }; - QGstMutableCaps() = default; - QGstMutableCaps(GstCaps *c, RefMode mode = HasRef) - : QGstCaps(c), caps(c) - { - Q_ASSERT(QGstCaps::get() == caps); - if (mode == NeedsRef) - gst_caps_ref(caps); - } - QGstMutableCaps(const QGstMutableCaps &other) - : QGstCaps(other), caps(other.caps) - { - Q_ASSERT(QGstCaps::get() == caps); - if (caps) - gst_caps_ref(caps); - } - QGstMutableCaps &operator=(const QGstMutableCaps &other) - { - QGstCaps::operator=(other); - if (other.caps) - gst_caps_ref(other.caps); - if (caps) - gst_caps_unref(caps); - caps = other.caps; - Q_ASSERT(QGstCaps::get() == caps); - return *this; - } - ~QGstMutableCaps() { - Q_ASSERT(QGstCaps::get() == caps); - if (caps) - gst_caps_unref(caps); - } - - void create() { - caps = gst_caps_new_empty(); - QGstCaps::operator=(QGstCaps(caps)); - } - - void addPixelFormats(const QList<QVideoFrameFormat::PixelFormat> &formats, const char *modifier = nullptr); - static QGstMutableCaps fromCameraFormat(const QCameraFormat &format); - - GstCaps *get() const { return caps; } -}; - -class QGstObject -{ -protected: - GstObject *m_object = nullptr; -public: - enum RefMode { HasRef, NeedsRef }; - - QGstObject() = default; - QGstObject(GstObject *o, RefMode mode = HasRef) - : m_object(o) - { - if (o && mode == NeedsRef) - // Use ref_sink to remove any floating references - gst_object_ref_sink(m_object); - } - QGstObject(const QGstObject &other) - : m_object(other.m_object) - { - if (m_object) - gst_object_ref(m_object); - } - QGstObject &operator=(const QGstObject &other) - { - if (other.m_object) - gst_object_ref(other.m_object); - if (m_object) - gst_object_unref(m_object); - m_object = other.m_object; - return *this; - } - ~QGstObject() { - if (m_object) - gst_object_unref(m_object); - } - - friend bool operator==(const QGstObject &a, const QGstObject &b) - { return a.m_object == b.m_object; } - friend bool operator!=(const QGstObject &a, const QGstObject &b) - { return a.m_object != b.m_object; } - - bool isNull() const { return !m_object; } - - void set(const char *property, const char *str) { g_object_set(m_object, property, str, nullptr); } - void set(const char *property, bool b) { g_object_set(m_object, property, gboolean(b), nullptr); } - void set(const char *property, uint i) { g_object_set(m_object, property, guint(i), nullptr); } - void set(const char *property, int i) { g_object_set(m_object, property, gint(i), nullptr); } - void set(const char *property, qint64 i) { g_object_set(m_object, property, gint64(i), nullptr); } - void set(const char *property, quint64 i) { g_object_set(m_object, property, guint64(i), nullptr); } - void set(const char *property, double d) { g_object_set(m_object, property, gdouble(d), nullptr); } - void set(const char *property, const QGstObject &o) { g_object_set(m_object, property, o.object(), nullptr); } - void set(const char *property, const QGstMutableCaps &c) { g_object_set(m_object, property, c.get(), nullptr); } - - QGString getString(const char *property) const - { char *s = nullptr; g_object_get(m_object, property, &s, nullptr); return s; } - QGstStructure getStructure(const char *property) const - { GstStructure *s = nullptr; g_object_get(m_object, property, &s, nullptr); return QGstStructure(s); } - bool getBool(const char *property) const { gboolean b = false; g_object_get(m_object, property, &b, nullptr); return b; } - uint getUInt(const char *property) const { guint i = 0; g_object_get(m_object, property, &i, nullptr); return i; } - int getInt(const char *property) const { gint i = 0; g_object_get(m_object, property, &i, nullptr); return i; } - quint64 getUInt64(const char *property) const { guint64 i = 0; g_object_get(m_object, property, &i, nullptr); return i; } - qint64 getInt64(const char *property) const { gint64 i = 0; g_object_get(m_object, property, &i, nullptr); return i; } - float getFloat(const char *property) const { gfloat d = 0; g_object_get(m_object, property, &d, nullptr); return d; } - double getDouble(const char *property) const { gdouble d = 0; g_object_get(m_object, property, &d, nullptr); return d; } - QGstObject getObject(const char *property) const { GstObject *o = nullptr; g_object_get(m_object, property, &o, nullptr); return o; } - - void connect(const char *name, GCallback callback, gpointer userData) { g_signal_connect(m_object, name, callback, userData); } - - GstObject *object() const { return m_object; } - const char *name() const { return m_object ? GST_OBJECT_NAME(m_object) : "(null)"; } -}; - -class QGstElement; - -class QGstPad : public QGstObject -{ -public: - QGstPad() = default; - QGstPad(const QGstObject &o) - : QGstPad(GST_PAD(o.object()), NeedsRef) - {} - QGstPad(GstPad *pad, RefMode mode = NeedsRef) - : QGstObject(&pad->object, mode) - {} - - QGstMutableCaps currentCaps() const - { return QGstMutableCaps(gst_pad_get_current_caps(pad())); } - QGstCaps queryCaps() const - { return QGstCaps(gst_pad_query_caps(pad(), nullptr)); } - - bool isLinked() const { return gst_pad_is_linked(pad()); } - bool link(const QGstPad &sink) const { return gst_pad_link(pad(), sink.pad()) == GST_PAD_LINK_OK; } - bool unlink(const QGstPad &sink) const { return gst_pad_unlink(pad(), sink.pad()); } - bool unlinkPeer() const { return unlink(peer()); } - QGstPad peer() const { return QGstPad(gst_pad_get_peer(pad()), HasRef); } - inline QGstElement parent() const; - - GstPad *pad() const { return GST_PAD_CAST(object()); } - - GstEvent *stickyEvent(GstEventType type) { return gst_pad_get_sticky_event(pad(), type, 0); } - bool sendEvent(GstEvent *event) { return gst_pad_send_event (pad(), event); } - - template<auto Member, typename T> - void addProbe(T *instance, GstPadProbeType type) { - struct Impl { - static GstPadProbeReturn callback(GstPad *pad, GstPadProbeInfo *info, gpointer userData) { - return (static_cast<T *>(userData)->*Member)(QGstPad(pad, NeedsRef), info); - }; - }; - - gst_pad_add_probe (pad(), type, Impl::callback, instance, nullptr); - } - - void doInIdleProbe(std::function<void()> work) { - struct CallbackData { - QSemaphore waitDone; - std::function<void()> work; - } cd; - cd.work = work; - - auto callback= [](GstPad *, GstPadProbeInfo *, gpointer p) { - auto cd = reinterpret_cast<CallbackData*>(p); - cd->work(); - cd->waitDone.release(); - return GST_PAD_PROBE_REMOVE; - }; - - gst_pad_add_probe(pad(), GST_PAD_PROBE_TYPE_IDLE, callback, &cd, nullptr); - cd.waitDone.acquire(); - } - - template<auto Member, typename T> - void addEosProbe(T *instance) { - struct Impl { - static GstPadProbeReturn callback(GstPad */*pad*/, GstPadProbeInfo *info, gpointer userData) { - if (GST_EVENT_TYPE(GST_PAD_PROBE_INFO_DATA(info)) != GST_EVENT_EOS) - return GST_PAD_PROBE_PASS; - (static_cast<T *>(userData)->*Member)(); - return GST_PAD_PROBE_REMOVE; - }; - }; - - gst_pad_add_probe (pad(), GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, Impl::callback, instance, nullptr); - } -}; - -class QGstClock : public QGstObject -{ -public: - QGstClock() = default; - QGstClock(const QGstObject &o) - : QGstClock(GST_CLOCK(o.object())) - {} - QGstClock(GstClock *clock, RefMode mode = NeedsRef) - : QGstObject(&clock->object, mode) - {} - - GstClock *clock() const { return GST_CLOCK_CAST(object()); } - - GstClockTime time() const { return gst_clock_get_time(clock()); } -}; - -class QGstElement : public QGstObject -{ -public: - QGstElement() = default; - QGstElement(const QGstObject &o) - : QGstElement(GST_ELEMENT(o.object()), NeedsRef) - {} - QGstElement(GstElement *element, RefMode mode = NeedsRef) - : QGstObject(&element->object, mode) - {} - - QGstElement(const char *factory, const char *name = nullptr) - : QGstElement(gst_element_factory_make(factory, name), NeedsRef) - { - } - - bool linkFiltered(const QGstElement &next, const QGstMutableCaps &caps) - { return gst_element_link_filtered(element(), next.element(), caps.get()); } - bool link(const QGstElement &next) - { return gst_element_link(element(), next.element()); } - bool link(const QGstElement &n1, const QGstElement &n2) - { return gst_element_link_many(element(), n1.element(), n2.element(), nullptr); } - bool link(const QGstElement &n1, const QGstElement &n2, const QGstElement &n3) - { return gst_element_link_many(element(), n1.element(), n2.element(), n3.element(), nullptr); } - bool link(const QGstElement &n1, const QGstElement &n2, const QGstElement &n3, const QGstElement &n4) - { return gst_element_link_many(element(), n1.element(), n2.element(), n3.element(), n4.element(), nullptr); } - bool link(const QGstElement &n1, const QGstElement &n2, const QGstElement &n3, const QGstElement &n4, const QGstElement &n5) - { return gst_element_link_many(element(), n1.element(), n2.element(), n3.element(), n4.element(), n5.element(), nullptr); } - - void unlink(const QGstElement &next) - { gst_element_unlink(element(), next.element()); } - - QGstPad staticPad(const char *name) const { return QGstPad(gst_element_get_static_pad(element(), name), HasRef); } - QGstPad src() const { return staticPad("src"); } - QGstPad sink() const { return staticPad("sink"); } - QGstPad getRequestPad(const char *name) const { return QGstPad(gst_element_get_request_pad(element(), name), HasRef); } - void releaseRequestPad(const QGstPad &pad) const { return gst_element_release_request_pad(element(), pad.pad()); } - - GstState state() const - { - GstState state; - gst_element_get_state(element(), &state, nullptr, 0); - return state; - } - GstStateChangeReturn setState(GstState state) { return gst_element_set_state(element(), state); } - bool setStateSync(GstState state) - { - auto change = gst_element_set_state(element(), state); - if (change == GST_STATE_CHANGE_ASYNC) { - change = gst_element_get_state(element(), nullptr, &state, 1000*1e6 /*nano seconds*/); - } -#ifndef QT_NO_DEBUG - if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL) - qWarning() << "Could not change state of" << name() << "to" << state << change; -#endif - return change == GST_STATE_CHANGE_SUCCESS; - } - bool syncStateWithParent() { return gst_element_sync_state_with_parent(element()) == TRUE; } - bool finishStateChange() - { - auto change = gst_element_get_state(element(), nullptr, nullptr, 1000*1e6 /*nano seconds*/); -#ifndef QT_NO_DEBUG - if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL) - qWarning() << "Could finish change state of" << name(); -#endif - return change == GST_STATE_CHANGE_SUCCESS; - } - - void lockState(bool locked) { gst_element_set_locked_state(element(), locked); } - bool isStateLocked() const { return gst_element_is_locked_state(element()); } - - void sendEvent(GstEvent *event) const { gst_element_send_event(element(), event); } - void sendEos() const { sendEvent(gst_event_new_eos()); } - - template<auto Member, typename T> - void onPadAdded(T *instance) { - struct Impl { - static void callback(GstElement *e, GstPad *pad, gpointer userData) { - (static_cast<T *>(userData)->*Member)(QGstElement(e), QGstPad(pad, NeedsRef)); - }; - }; - - connect("pad-added", G_CALLBACK(Impl::callback), instance); - } - template<auto Member, typename T> - void onPadRemoved(T *instance) { - struct Impl { - static void callback(GstElement *e, GstPad *pad, gpointer userData) { - (static_cast<T *>(userData)->*Member)(QGstElement(e), QGstPad(pad, NeedsRef)); - }; - }; - - connect("pad-removed", G_CALLBACK(Impl::callback), instance); - } - template<auto Member, typename T> - void onNoMorePads(T *instance) { - struct Impl { - static void callback(GstElement *e, gpointer userData) { - (static_cast<T *>(userData)->*Member)(QGstElement(e)); - }; - }; - - connect("no-more-pads", G_CALLBACK(Impl::callback), instance); - } - - GstClockTime baseTime() const { return gst_element_get_base_time(element()); } - void setBaseTime(GstClockTime time) const { gst_element_set_base_time(element(), time); } - - GstElement *element() const { return GST_ELEMENT_CAST(m_object); } -}; - -inline QGstElement QGstPad::parent() const -{ - return QGstElement(gst_pad_get_parent_element(pad()), HasRef); -} - -class QGstBin : public QGstElement -{ -public: - QGstBin() = default; - QGstBin(const QGstObject &o) - : QGstBin(GST_BIN(o.object()), NeedsRef) - {} - QGstBin(const char *name) - : QGstElement(gst_bin_new(name), NeedsRef) - { - } - QGstBin(GstBin *bin, RefMode mode = NeedsRef) - : QGstElement(&bin->element, mode) - {} - - void add(const QGstElement &element) - { gst_bin_add(bin(), element.element()); } - void add(const QGstElement &e1, const QGstElement &e2) - { gst_bin_add_many(bin(), e1.element(), e2.element(), nullptr); } - void add(const QGstElement &e1, const QGstElement &e2, const QGstElement &e3) - { gst_bin_add_many(bin(), e1.element(), e2.element(), e3.element(), nullptr); } - void add(const QGstElement &e1, const QGstElement &e2, const QGstElement &e3, const QGstElement &e4) - { gst_bin_add_many(bin(), e1.element(), e2.element(), e3.element(), e4.element(), nullptr); } - void add(const QGstElement &e1, const QGstElement &e2, const QGstElement &e3, const QGstElement &e4, const QGstElement &e5) - { gst_bin_add_many(bin(), e1.element(), e2.element(), e3.element(), e4.element(), e5.element(), nullptr); } - void add(const QGstElement &e1, const QGstElement &e2, const QGstElement &e3, const QGstElement &e4, const QGstElement &e5, const QGstElement &e6) - { gst_bin_add_many(bin(), e1.element(), e2.element(), e3.element(), e4.element(), e5.element(), e6.element(), nullptr); } - void remove(const QGstElement &element) - { gst_bin_remove(bin(), element.element()); } - - GstBin *bin() const { return GST_BIN_CAST(m_object); } - - void addGhostPad(const QGstElement &child, const char *name) - { - addGhostPad(name, child.staticPad(name)); - } - void addGhostPad(const char *name, const QGstPad &pad) - { - gst_element_add_pad(element(), gst_ghost_pad_new(name, pad.pad())); - } -}; - -inline QGstStructure QGValue::toStructure() const -{ - if (!value || !GST_VALUE_HOLDS_STRUCTURE(value)) - return QGstStructure(); - return QGstStructure(gst_value_get_structure(value)); -} - -inline QGstCaps QGValue::toCaps() const -{ - if (!value || !GST_VALUE_HOLDS_CAPS(value)) - return QGstCaps(); - return QGstCaps(gst_value_get_caps(value)); -} - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/gstreamer/common/qgstappsrc.cpp b/src/multimedia/platform/gstreamer/common/qgstappsrc.cpp deleted file mode 100644 index 992e3d5a9..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstappsrc.cpp +++ /dev/null @@ -1,308 +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$ -** -****************************************************************************/ - -#include <QDebug> - -#include "qgstappsrc_p.h" -#include "qgstutils_p.h" -#include "qnetworkreply.h" -#include "qloggingcategory.h" - -Q_LOGGING_CATEGORY(qLcAppSrc, "qt.multimedia.appsrc") - -QGstAppSrc::QGstAppSrc(QObject *parent) - : QObject(parent) -{ - m_appSrc = QGstElement("appsrc", "appsrc"); - if (m_appSrc.isNull()) - qWarning() << "Could not create GstAppSrc."; -} - -QGstAppSrc::~QGstAppSrc() -{ - m_appSrc.setStateSync(GST_STATE_NULL); - streamDestroyed(); - qCDebug(qLcAppSrc) << "~QGstAppSrc"; -} - -bool QGstAppSrc::setup(QIODevice *stream, qint64 offset) -{ - if (m_appSrc.isNull()) - return false; - - if (!setStream(stream, offset)) - return false; - - auto *appSrc = GST_APP_SRC(m_appSrc.element()); - GstAppSrcCallbacks m_callbacks; - memset(&m_callbacks, 0, sizeof(GstAppSrcCallbacks)); - m_callbacks.need_data = &QGstAppSrc::on_need_data; - m_callbacks.enough_data = &QGstAppSrc::on_enough_data; - m_callbacks.seek_data = &QGstAppSrc::on_seek_data; - gst_app_src_set_callbacks(appSrc, (GstAppSrcCallbacks*)&m_callbacks, this, nullptr); - - m_maxBytes = gst_app_src_get_max_bytes(appSrc); - m_suspended = false; - - if (m_sequential) - m_streamType = GST_APP_STREAM_TYPE_STREAM; - else - m_streamType = GST_APP_STREAM_TYPE_RANDOM_ACCESS; - gst_app_src_set_stream_type(appSrc, m_streamType); - gst_app_src_set_size(appSrc, m_sequential ? -1 : m_stream->size() - m_offset); - - m_networkReply = qobject_cast<QNetworkReply *>(m_stream); - m_noMoreData = true; - - return true; -} - -void QGstAppSrc::setAudioFormat(const QAudioFormat &f) -{ - m_format = f; - if (!m_format.isValid()) - return; - - auto caps = QGstUtils::capsForAudioFormat(m_format); - Q_ASSERT(!caps.isNull()); - m_appSrc.set("caps", caps); - m_appSrc.set("format", GST_FORMAT_TIME); -} - -void QGstAppSrc::setExternalAppSrc(const QGstElement &appsrc) -{ - m_appSrc = appsrc; -} - -bool QGstAppSrc::setStream(QIODevice *stream, qint64 offset) -{ - if (m_stream) { - disconnect(m_stream, SIGNAL(readyRead()), this, SLOT(onDataReady())); - disconnect(m_stream, SIGNAL(destroyed()), this, SLOT(streamDestroyed())); - m_stream = nullptr; - } - - m_dataRequestSize = 0; - m_sequential = true; - m_maxBytes = 0; - streamedSamples = 0; - - if (stream) { - if (!stream->isOpen() && !stream->open(QIODevice::ReadOnly)) - return false; - m_stream = stream; - connect(m_stream, SIGNAL(destroyed()), SLOT(streamDestroyed())); - connect(m_stream, SIGNAL(readyRead()), this, SLOT(onDataReady())); - m_sequential = m_stream->isSequential(); - m_offset = offset; - } - return true; -} - -QGstElement QGstAppSrc::element() -{ - return m_appSrc; -} - -void QGstAppSrc::write(const char *data, qsizetype size) -{ - qCDebug(qLcAppSrc) << "write" << size << m_noMoreData << m_dataRequestSize; - if (!size) - return; - Q_ASSERT(!m_stream); - m_buffer.append(data, size); - m_noMoreData = false; - pushData(); -} - -void QGstAppSrc::onDataReady() -{ - qCDebug(qLcAppSrc) << "onDataReady" << m_stream->bytesAvailable() << m_stream->size(); - pushData(); -} - -void QGstAppSrc::streamDestroyed() -{ - qCDebug(qLcAppSrc) << "stream destroyed"; - m_stream = nullptr; - m_dataRequestSize = 0; - streamedSamples = 0; - sendEOS(); -} - -void QGstAppSrc::pushData() -{ - if (m_appSrc.isNull() || !m_dataRequestSize || m_suspended) { - qCDebug(qLcAppSrc) << "push data: return immediately" << m_appSrc.isNull() << m_dataRequestSize << m_suspended; - return; - } - - qCDebug(qLcAppSrc) << "pushData" << (m_stream ? m_stream : nullptr) << m_buffer.size(); - if ((m_stream && m_stream->atEnd())) { - eosOrIdle(); - qCDebug(qLcAppSrc) << "end pushData" << (m_stream ? m_stream : nullptr) << m_buffer.size(); - return; - } - - qint64 size; - if (m_stream) - size = m_stream->bytesAvailable(); - else - size = m_buffer.size(); - - if (!m_dataRequestSize) - m_dataRequestSize = m_maxBytes; - size = qMin(size, (qint64)m_dataRequestSize); - qCDebug(qLcAppSrc) << " reading" << size << "bytes" << size << m_dataRequestSize; - - GstBuffer* buffer = gst_buffer_new_and_alloc(size); - - if (m_sequential || !m_stream) - buffer->offset = bytesReadSoFar; - else - buffer->offset = m_stream->pos(); - - if (m_format.isValid()) { - // timestamp raw audio data - uint nSamples = size/m_format.bytesPerFrame(); - - GST_BUFFER_TIMESTAMP(buffer) = gst_util_uint64_scale(streamedSamples, GST_SECOND, m_format.sampleRate()); - GST_BUFFER_DURATION(buffer) = gst_util_uint64_scale(nSamples, GST_SECOND, m_format.sampleRate()); - streamedSamples += nSamples; - } - - GstMapInfo mapInfo; - gst_buffer_map(buffer, &mapInfo, GST_MAP_WRITE); - void* bufferData = mapInfo.data; - - qint64 bytesRead; - if (m_stream) - bytesRead = m_stream->read((char*)bufferData, size); - else - bytesRead = m_buffer.read((char*)bufferData, size); - buffer->offset_end = buffer->offset + bytesRead - 1; - bytesReadSoFar += bytesRead; - - gst_buffer_unmap(buffer, &mapInfo); - qCDebug(qLcAppSrc) << "pushing bytes into gstreamer" << buffer->offset << bytesRead; - if (bytesRead == 0) { - gst_buffer_unref(buffer); - eosOrIdle(); - qCDebug(qLcAppSrc) << "end pushData" << (m_stream ? m_stream : nullptr) << m_buffer.size(); - return; - } - m_noMoreData = false; - emit bytesProcessed(bytesRead); - - GstFlowReturn ret = gst_app_src_push_buffer(GST_APP_SRC(m_appSrc.element()), buffer); - if (ret == GST_FLOW_ERROR) { - qWarning() << "QGstAppSrc: push buffer error"; - } else if (ret == GST_FLOW_FLUSHING) { - qWarning() << "QGstAppSrc: push buffer wrong state"; - } - qCDebug(qLcAppSrc) << "end pushData" << (m_stream ? m_stream : nullptr) << m_buffer.size(); - -} - -bool QGstAppSrc::doSeek(qint64 value) -{ - if (isStreamValid()) - return m_stream->seek(value + m_offset); - return false; -} - - -gboolean QGstAppSrc::on_seek_data(GstAppSrc *, guint64 arg0, gpointer userdata) -{ - // we do get some spurious seeks to INT_MAX, ignore those - if (arg0 == std::numeric_limits<quint64>::max()) - return true; - - QGstAppSrc *self = reinterpret_cast<QGstAppSrc*>(userdata); - Q_ASSERT(self); - if (self->m_sequential) - return false; - - QMetaObject::invokeMethod(self, "doSeek", Qt::AutoConnection, Q_ARG(qint64, arg0)); - return true; -} - -void QGstAppSrc::on_enough_data(GstAppSrc *, gpointer userdata) -{ - qCDebug(qLcAppSrc) << "on_enough_data"; - QGstAppSrc *self = static_cast<QGstAppSrc*>(userdata); - Q_ASSERT(self); - self->m_dataRequestSize = 0; -} - -void QGstAppSrc::on_need_data(GstAppSrc *, guint arg0, gpointer userdata) -{ - qCDebug(qLcAppSrc) << "on_need_data requesting bytes" << arg0; - QGstAppSrc *self = static_cast<QGstAppSrc*>(userdata); - Q_ASSERT(self); - self->m_dataRequestSize = arg0; - QMetaObject::invokeMethod(self, "pushData", Qt::AutoConnection); - qCDebug(qLcAppSrc) << "done on_need_data"; -} - -void QGstAppSrc::sendEOS() -{ - qCDebug(qLcAppSrc) << "sending EOS"; - if (m_appSrc.isNull()) - return; - - gst_app_src_end_of_stream(GST_APP_SRC(m_appSrc.element())); -} - -void QGstAppSrc::eosOrIdle() -{ - qCDebug(qLcAppSrc) << "eosOrIdle"; - if (m_appSrc.isNull()) - return; - - if (!m_sequential) { - sendEOS(); - return; - } - if (m_noMoreData) - return; - qCDebug(qLcAppSrc) << " idle!"; - m_noMoreData = true; - emit noMoreData(); -} diff --git a/src/multimedia/platform/gstreamer/common/qgstappsrc_p.h b/src/multimedia/platform/gstreamer/common/qgstappsrc_p.h deleted file mode 100644 index 3a978454c..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstappsrc_p.h +++ /dev/null @@ -1,132 +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$ -** -****************************************************************************/ - -#ifndef QGSTAPPSRC_H -#define QGSTAPPSRC_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qtmultimediaglobal_p.h> -#include <qaudioformat.h> - -#include <QtCore/qobject.h> -#include <QtCore/qiodevice.h> -#include <QtCore/private/qringbuffer_p.h> -#include <QtCore/qatomic.h> - -#include <private/qgst_p.h> -#include <gst/app/gstappsrc.h> - -QT_BEGIN_NAMESPACE - -class QNetworkReply; - -class Q_MULTIMEDIA_EXPORT QGstAppSrc : public QObject -{ - Q_OBJECT -public: - QGstAppSrc(QObject *parent = 0); - ~QGstAppSrc(); - - bool setup(QIODevice *stream = nullptr, qint64 offset = 0); - void setAudioFormat(const QAudioFormat &f); - - void setExternalAppSrc(const QGstElement &appsrc); - QGstElement element(); - - void write(const char *data, qsizetype size); - - bool canAcceptMoreData() { return m_noMoreData || m_dataRequestSize != 0; } - - void suspend() { m_suspended = true; } - void resume() { m_suspended = false; m_noMoreData = true; } - -Q_SIGNALS: - void bytesProcessed(int bytes); - void noMoreData(); - -private Q_SLOTS: - void pushData(); - bool doSeek(qint64); - void onDataReady(); - - void streamDestroyed(); -private: - bool setStream(QIODevice *, qint64 offset); - bool isStreamValid() const - { - return m_stream != nullptr && m_stream->isOpen(); - } - - static gboolean on_seek_data(GstAppSrc *element, guint64 arg0, gpointer userdata); - static void on_enough_data(GstAppSrc *element, gpointer userdata); - static void on_need_data(GstAppSrc *element, uint arg0, gpointer userdata); - - void sendEOS(); - void eosOrIdle(); - - QIODevice *m_stream = nullptr; - QNetworkReply *m_networkReply = nullptr; - QRingBuffer m_buffer; - QAudioFormat m_format; - - QGstElement m_appSrc; - bool m_sequential = true; - bool m_suspended = false; - bool m_noMoreData = false; - GstAppStreamType m_streamType = GST_APP_STREAM_TYPE_RANDOM_ACCESS; - qint64 m_offset = 0; - qint64 m_maxBytes = 0; - qint64 bytesReadSoFar = 0; - QAtomicInteger<unsigned int> m_dataRequestSize = 0; - int streamedSamples = 0; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/gstreamer/common/qgstpipeline.cpp b/src/multimedia/platform/gstreamer/common/qgstpipeline.cpp deleted file mode 100644 index 0f98e7d21..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstpipeline.cpp +++ /dev/null @@ -1,375 +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$ -** -****************************************************************************/ - -#include <QtCore/qmap.h> -#include <QtCore/qtimer.h> -#include <QtCore/qmutex.h> -#include <QtCore/qlist.h> -#include <QtCore/qabstracteventdispatcher.h> -#include <QtCore/qcoreapplication.h> -#include <QtCore/qproperty.h> - -#include "qgstpipeline_p.h" -#include "qgstreamermessage_p.h" - -QT_BEGIN_NAMESPACE - -class QGstPipelinePrivate : public QObject -{ - Q_OBJECT -public: - - int m_ref = 0; - guint m_tag = 0; - GstBus *m_bus = nullptr; - QTimer *m_intervalTimer = nullptr; - QMutex filterMutex; - QList<QGstreamerSyncMessageFilter*> syncFilters; - QList<QGstreamerBusMessageFilter*> busFilters; - QProperty<bool> inStoppedState; - mutable qint64 m_position = 0; - double m_rate = 1.; - bool m_flushOnConfigChanges = false; - bool m_pendingFlush = false; - - int m_configCounter = 0; - GstState m_savedState = GST_STATE_NULL; - - QGstPipelinePrivate(GstBus* bus, QObject* parent = 0); - ~QGstPipelinePrivate(); - - void ref() { ++ m_ref; } - void deref() { if (!--m_ref) delete this; } - - void installMessageFilter(QGstreamerSyncMessageFilter *filter); - void removeMessageFilter(QGstreamerSyncMessageFilter *filter); - void installMessageFilter(QGstreamerBusMessageFilter *filter); - void removeMessageFilter(QGstreamerBusMessageFilter *filter); - - static GstBusSyncReply syncGstBusFilter(GstBus* bus, GstMessage* message, QGstPipelinePrivate *d) - { - Q_UNUSED(bus); - QMutexLocker lock(&d->filterMutex); - - for (QGstreamerSyncMessageFilter *filter : qAsConst(d->syncFilters)) { - if (filter->processSyncMessage(QGstreamerMessage(message))) { - gst_message_unref(message); - return GST_BUS_DROP; - } - } - - return GST_BUS_PASS; - } - -private Q_SLOTS: - void interval() - { - GstMessage* message; - while ((message = gst_bus_poll(m_bus, GST_MESSAGE_ANY, 0)) != nullptr) { - processMessage(message); - gst_message_unref(message); - } - } - void doProcessMessage(const QGstreamerMessage& msg) - { - for (QGstreamerBusMessageFilter *filter : qAsConst(busFilters)) { - if (filter->processBusMessage(msg)) - break; - } - } - -private: - void processMessage(GstMessage* message) - { - QGstreamerMessage msg(message); - doProcessMessage(msg); - } - - void queueMessage(GstMessage* message) - { - QGstreamerMessage msg(message); - QMetaObject::invokeMethod(this, "doProcessMessage", Qt::QueuedConnection, - Q_ARG(QGstreamerMessage, msg)); - } - - static gboolean busCallback(GstBus *bus, GstMessage *message, gpointer data) - { - Q_UNUSED(bus); - static_cast<QGstPipelinePrivate *>(data)->queueMessage(message); - return TRUE; - } -}; - -QGstPipelinePrivate::QGstPipelinePrivate(GstBus* bus, QObject* parent) - : QObject(parent), - m_bus(bus), - inStoppedState(true) -{ - gst_object_ref(GST_OBJECT(bus)); - - // glib event loop can be disabled either by env variable or QT_NO_GLIB define, so check the dispacher - QAbstractEventDispatcher *dispatcher = QCoreApplication::eventDispatcher(); - const bool hasGlib = dispatcher && dispatcher->inherits("QEventDispatcherGlib"); - if (!hasGlib) { - m_intervalTimer = new QTimer(this); - m_intervalTimer->setInterval(250); - connect(m_intervalTimer, SIGNAL(timeout()), SLOT(interval())); - m_intervalTimer->start(); - } else { - m_tag = gst_bus_add_watch_full(bus, G_PRIORITY_DEFAULT, busCallback, this, nullptr); - } - - gst_bus_set_sync_handler(bus, (GstBusSyncHandler)syncGstBusFilter, this, nullptr); -} - -QGstPipelinePrivate::~QGstPipelinePrivate() -{ - delete m_intervalTimer; - - if (m_tag) - gst_bus_remove_watch(m_bus); - - gst_bus_set_sync_handler(m_bus, nullptr, nullptr, nullptr); - gst_object_unref(GST_OBJECT(m_bus)); -} - -void QGstPipelinePrivate::installMessageFilter(QGstreamerSyncMessageFilter *filter) -{ - if (filter) { - QMutexLocker lock(&filterMutex); - if (!syncFilters.contains(filter)) - syncFilters.append(filter); - } -} - -void QGstPipelinePrivate::removeMessageFilter(QGstreamerSyncMessageFilter *filter) -{ - if (filter) { - QMutexLocker lock(&filterMutex); - syncFilters.removeAll(filter); - } -} - -void QGstPipelinePrivate::installMessageFilter(QGstreamerBusMessageFilter *filter) -{ - if (filter && !busFilters.contains(filter)) - busFilters.append(filter); -} - -void QGstPipelinePrivate::removeMessageFilter(QGstreamerBusMessageFilter *filter) -{ - if (filter) - busFilters.removeAll(filter); -} - -QGstPipeline::QGstPipeline(const QGstPipeline &o) - : QGstBin(o.bin(), NeedsRef), - d(o.d) -{ - if (d) - d->ref(); -} - -QGstPipeline &QGstPipeline::operator=(const QGstPipeline &o) -{ - if (o.d) - o.d->ref(); - if (d) - d->deref(); - QGstBin::operator=(o); - d = o.d; - return *this; -} - -QGstPipeline::QGstPipeline(const char *name) - : QGstBin(GST_BIN(gst_pipeline_new(name)), NeedsRef) -{ - d = new QGstPipelinePrivate(gst_pipeline_get_bus(pipeline())); - d->ref(); -} - -QGstPipeline::QGstPipeline(GstPipeline *p) - : QGstBin(&p->bin, NeedsRef) -{ - d = new QGstPipelinePrivate(gst_pipeline_get_bus(pipeline())); - d->ref(); -} - -QGstPipeline::~QGstPipeline() -{ - if (d) - d->deref(); -} - -QProperty<bool> *QGstPipeline::inStoppedState() -{ - Q_ASSERT(d); - return &d->inStoppedState; -} - -void QGstPipeline::setFlushOnConfigChanges(bool flush) -{ - d->m_flushOnConfigChanges = flush; -} - -void QGstPipeline::installMessageFilter(QGstreamerSyncMessageFilter *filter) -{ - Q_ASSERT(d); - d->installMessageFilter(filter); -} - -void QGstPipeline::removeMessageFilter(QGstreamerSyncMessageFilter *filter) -{ - Q_ASSERT(d); - d->removeMessageFilter(filter); -} - -void QGstPipeline::installMessageFilter(QGstreamerBusMessageFilter *filter) -{ - Q_ASSERT(d); - d->installMessageFilter(filter); -} - -void QGstPipeline::removeMessageFilter(QGstreamerBusMessageFilter *filter) -{ - Q_ASSERT(d); - d->removeMessageFilter(filter); -} - -GstStateChangeReturn QGstPipeline::setState(GstState state) -{ - auto retval = gst_element_set_state(element(), state); - if (d->m_pendingFlush) { - d->m_pendingFlush = false; - flush(); - } - return retval; -} - -void QGstPipeline::beginConfig() -{ - if (!d) - return; - Q_ASSERT(!isNull()); - - ++d->m_configCounter; - if (d->m_configCounter > 1) - return; - - d->m_savedState = state(); - if (d->m_savedState == GST_STATE_PLAYING) - setStateSync(GST_STATE_PAUSED); -} - -void QGstPipeline::endConfig() -{ - if (!d) - return; - Q_ASSERT(!isNull()); - - --d->m_configCounter; - if (d->m_configCounter) - return; - - if (d->m_flushOnConfigChanges) - d->m_pendingFlush = true; - if (d->m_savedState == GST_STATE_PLAYING) - setState(GST_STATE_PLAYING); - d->m_savedState = GST_STATE_NULL; -} - -void QGstPipeline::flush() -{ - seek(position(), d->m_rate); -} - -bool QGstPipeline::seek(qint64 pos, double rate) -{ - // always adjust the rate, so it can be set before playback starts - // setting position needs a loaded media file that's seekable - d->m_rate = rate; - bool success = gst_element_seek(element(), rate, GST_FORMAT_TIME, - GstSeekFlags(GST_SEEK_FLAG_FLUSH), - GST_SEEK_TYPE_SET, pos, - GST_SEEK_TYPE_SET, -1); - if (!success) - return false; - - d->m_position = pos; - return true; -} - -bool QGstPipeline::setPlaybackRate(double rate) -{ - if (rate == d->m_rate) - return false; - seek(position(), rate); - return true; -} - -double QGstPipeline::playbackRate() const -{ - return d->m_rate; -} - -bool QGstPipeline::setPosition(qint64 pos) -{ - return seek(pos, d->m_rate); -} - -qint64 QGstPipeline::position() const -{ - gint64 pos; - if (gst_element_query_position(element(), GST_FORMAT_TIME, &pos)) - d->m_position = pos; - return d->m_position; -} - -qint64 QGstPipeline::duration() const -{ - gint64 d; - if (!gst_element_query_duration(element(), GST_FORMAT_TIME, &d)) - return 0.; - return d; -} - -QT_END_NAMESPACE - -#include "qgstpipeline.moc" - diff --git a/src/multimedia/platform/gstreamer/common/qgstpipeline_p.h b/src/multimedia/platform/gstreamer/common/qgstpipeline_p.h deleted file mode 100644 index 6ffaf33d6..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstpipeline_p.h +++ /dev/null @@ -1,139 +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$ -** -****************************************************************************/ - -#ifndef qgstpipeline_p_H -#define qgstpipeline_p_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qtmultimediaglobal_p.h> -#include <QObject> - -#include <private/qgst_p.h> - -QT_BEGIN_NAMESPACE - -class QGstreamerMessage; - -class QGstreamerSyncMessageFilter { -public: - //returns true if message was processed and should be dropped, false otherwise - virtual bool processSyncMessage(const QGstreamerMessage &message) = 0; -}; - - -class QGstreamerBusMessageFilter { -public: - //returns true if message was processed and should be dropped, false otherwise - virtual bool processBusMessage(const QGstreamerMessage &message) = 0; -}; - -class QGstPipelinePrivate; - -class QGstPipeline : public QGstBin -{ - QGstPipelinePrivate *d = nullptr; -public: - constexpr QGstPipeline() = default; - QGstPipeline(const QGstPipeline &o); - QGstPipeline &operator=(const QGstPipeline &o); - QGstPipeline(const char *name); - QGstPipeline(GstPipeline *p); - ~QGstPipeline(); - - // This is needed to help us avoid sending QVideoFrames or audio buffers to the - // application while we're prerolling the pipeline. - // QMediaPlayer is still in a stopped state, while we put the gstreamer pipeline into a - // Paused state so that we can get the required metadata of the stream and also have a fast - // transition to play. - QProperty<bool> *inStoppedState(); - - void setFlushOnConfigChanges(bool flush); - - void installMessageFilter(QGstreamerSyncMessageFilter *filter); - void removeMessageFilter(QGstreamerSyncMessageFilter *filter); - void installMessageFilter(QGstreamerBusMessageFilter *filter); - void removeMessageFilter(QGstreamerBusMessageFilter *filter); - - GstStateChangeReturn setState(GstState state); - - GstPipeline *pipeline() const { return GST_PIPELINE_CAST(m_object); } - - void dumpGraph(const char *fileName) - { - if (isNull()) - return; - -#if 1 //def QT_GST_CAPTURE_DEBUG - GST_DEBUG_BIN_TO_DOT_FILE(bin(), - GstDebugGraphDetails(GST_DEBUG_GRAPH_SHOW_ALL | - GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE | GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS | GST_DEBUG_GRAPH_SHOW_STATES), - fileName); -#else - Q_UNUSED(fileName); -#endif - } - - void beginConfig(); - void endConfig(); - - void flush(); - - bool seek(qint64 pos, double rate); - bool setPlaybackRate(double rate); - double playbackRate() const; - - bool setPosition(qint64 pos); - qint64 position() const; - - qint64 duration() const; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/gstreamer/common/qgstreameraudioinput.cpp b/src/multimedia/platform/gstreamer/common/qgstreameraudioinput.cpp deleted file mode 100644 index cd1233c1d..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstreameraudioinput.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include <private/qgstreameraudioinput_p.h> -#include <private/qgstreameraudiodevice_p.h> -#include <qaudiodevice.h> -#include <qaudioinput.h> - -#include <QtCore/qloggingcategory.h> -#include <QtNetwork/qnetworkaccessmanager.h> -#include <QtNetwork/qnetworkreply.h> - -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> - -Q_LOGGING_CATEGORY(qLcMediaAudioInput, "qt.multimedia.audioInput") - -QT_BEGIN_NAMESPACE - -QGstreamerAudioInput::QGstreamerAudioInput(QAudioInput *parent) - : QObject(parent), - QPlatformAudioInput(parent), - gstAudioInput("audioInput") -{ - audioSrc = QGstElement("autoaudiosrc", "autoaudiosrc"); - audioVolume = QGstElement("volume", "volume"); - gstAudioInput.add(audioSrc, audioVolume); - audioSrc.link(audioVolume); - - gstAudioInput.addGhostPad(audioVolume, "src"); -} - -QGstreamerAudioInput::~QGstreamerAudioInput() -{ - gstAudioInput.setStateSync(GST_STATE_NULL); -} - -int QGstreamerAudioInput::volume() const -{ - return m_volume; -} - -bool QGstreamerAudioInput::isMuted() const -{ - return m_muted; -} - -void QGstreamerAudioInput::setVolume(float vol) -{ - if (vol == m_volume) - return; - m_volume = vol; - audioVolume.set("volume", vol); - emit volumeChanged(m_volume); -} - -void QGstreamerAudioInput::setMuted(bool muted) -{ - if (muted == m_muted) - return; - m_muted = muted; - audioVolume.set("mute", muted); - emit mutedChanged(muted); -} - -void QGstreamerAudioInput::setAudioDevice(const QAudioDevice &device) -{ - if (device == m_audioDevice) - return; - qCDebug(qLcMediaAudioInput) << "setAudioInput" << device.description() << device.isNull(); - m_audioDevice = device; - - QGstElement newSrc; - auto *deviceInfo = static_cast<const QGStreamerAudioDeviceInfo *>(m_audioDevice.handle()); - if (deviceInfo && deviceInfo->gstDevice) - newSrc = gst_device_create_element(deviceInfo->gstDevice, "audiosrc"); - - if (newSrc.isNull()) - newSrc = QGstElement("autoaudiosrc", "audiosrc"); - - // FIXME: most probably source can be disconnected outside of idle probe - audioSrc.staticPad("src").doInIdleProbe([&](){ - audioSrc.unlink(audioVolume); - }); - audioSrc.setStateSync(GST_STATE_NULL); - gstAudioInput.remove(audioSrc); - audioSrc = newSrc; - gstAudioInput.add(audioSrc); - audioSrc.link(audioVolume); - audioSrc.syncStateWithParent(); -} - -QAudioDevice QGstreamerAudioInput::audioInput() const -{ - return m_audioDevice; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/gstreamer/common/qgstreameraudioinput_p.h b/src/multimedia/platform/gstreamer/common/qgstreameraudioinput_p.h deleted file mode 100644 index a0e7d96ab..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstreameraudioinput_p.h +++ /dev/null @@ -1,107 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERAUDIOINPUT_P_H -#define QGSTREAMERAUDIOINPUT_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qtmultimediaglobal_p.h> -#include <qaudiodevice.h> - -#include <QtCore/qobject.h> - -#include <private/qgst_p.h> -#include <private/qgstpipeline_p.h> -#include <private/qplatformaudioinput_p.h> - -QT_BEGIN_NAMESPACE - -class QGstreamerMessage; -class QAudioDevice; - -class Q_MULTIMEDIA_EXPORT QGstreamerAudioInput : public QObject, public QPlatformAudioInput -{ - Q_OBJECT - -public: - QGstreamerAudioInput(QAudioInput *parent); - ~QGstreamerAudioInput(); - - int volume() const; - bool isMuted() const; - - bool setAudioInput(const QAudioDevice &); - QAudioDevice audioInput() const; - - void setAudioDevice(const QAudioDevice &) override; - void setVolume(float volume) override; - void setMuted(bool muted) override; - - QGstElement gstElement() const { return gstAudioInput; } - -Q_SIGNALS: - void mutedChanged(bool); - void volumeChanged(int); - -private: - float m_volume = 1.; - bool m_muted = false; - - QAudioDevice m_audioDevice; - - // Gst elements - QGstBin gstAudioInput; - - QGstElement audioSrc; - QGstElement audioVolume; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/gstreamer/common/qgstreameraudiooutput.cpp b/src/multimedia/platform/gstreamer/common/qgstreameraudiooutput.cpp deleted file mode 100644 index 6338c1ee0..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstreameraudiooutput.cpp +++ /dev/null @@ -1,120 +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$ -** -****************************************************************************/ - -#include <private/qgstreameraudiooutput_p.h> -#include <private/qgstreameraudiodevice_p.h> -#include <qaudiodevice.h> -#include <qaudiooutput.h> - -#include <QtCore/qloggingcategory.h> -#include <QtNetwork/qnetworkaccessmanager.h> -#include <QtNetwork/qnetworkreply.h> - -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> - -Q_LOGGING_CATEGORY(qLcMediaAudioOutput, "qt.multimedia.audiooutput") - -QT_BEGIN_NAMESPACE - -QGstreamerAudioOutput::QGstreamerAudioOutput(QAudioOutput *parent) - : QObject(parent), - QPlatformAudioOutput(parent), - gstAudioOutput("audioOutput") -{ - audioQueue = QGstElement("queue", "audioQueue"); - audioConvert = QGstElement("audioconvert", "audioConvert"); - audioResample = QGstElement("audioresample", "audioResample"); - audioVolume = QGstElement("volume", "volume"); - audioSink = QGstElement("autoaudiosink", "autoAudioSink"); - gstAudioOutput.add(audioQueue, audioConvert, audioResample, audioVolume, audioSink); - audioQueue.link(audioConvert, audioResample, audioVolume, audioSink); - - gstAudioOutput.addGhostPad(audioQueue, "sink"); -} - -QGstreamerAudioOutput::~QGstreamerAudioOutput() -{ - gstAudioOutput.setStateSync(GST_STATE_NULL); -} - -void QGstreamerAudioOutput::setVolume(float vol) -{ - audioVolume.set("volume", vol); -} - -void QGstreamerAudioOutput::setMuted(bool muted) -{ - audioVolume.set("mute", muted); -} - -void QGstreamerAudioOutput::setPipeline(const QGstPipeline &pipeline) -{ - gstPipeline = pipeline; -} - -void QGstreamerAudioOutput::setAudioDevice(const QAudioDevice &info) -{ - if (info == m_audioOutput) - return; - qCDebug(qLcMediaAudioOutput) << "setAudioOutput" << info.description() << info.isNull(); - m_audioOutput = info; - - QGstElement newSink; - auto *deviceInfo = static_cast<const QGStreamerAudioDeviceInfo *>(m_audioOutput.handle()); - if (deviceInfo && deviceInfo->gstDevice) - newSink = gst_device_create_element(deviceInfo->gstDevice , "audiosink"); - - if (newSink.isNull()) - newSink = QGstElement("autoaudiosink", "audiosink"); - - audioVolume.staticPad("src").doInIdleProbe([&](){ - audioVolume.unlink(audioSink); - gstAudioOutput.remove(audioSink); - gstAudioOutput.add(newSink); - newSink.syncStateWithParent(); - audioVolume.link(newSink); - }); - - audioSink.setStateSync(GST_STATE_NULL); - audioSink = newSink; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/gstreamer/common/qgstreameraudiooutput_p.h b/src/multimedia/platform/gstreamer/common/qgstreameraudiooutput_p.h deleted file mode 100644 index c3b33c4f4..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstreameraudiooutput_p.h +++ /dev/null @@ -1,104 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERAUDIOOUTPUT_P_H -#define QGSTREAMERAUDIOOUTPUT_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qtmultimediaglobal_p.h> -#include <qaudiodevice.h> - -#include <QtCore/qobject.h> - -#include <private/qgst_p.h> -#include <private/qgstpipeline_p.h> -#include <private/qplatformaudiooutput_p.h> - -QT_BEGIN_NAMESPACE - -class QGstreamerMessage; -class QAudioDevice; - -class Q_MULTIMEDIA_EXPORT QGstreamerAudioOutput : public QObject, public QPlatformAudioOutput -{ - Q_OBJECT - -public: - QGstreamerAudioOutput(QAudioOutput *parent); - ~QGstreamerAudioOutput(); - - void setAudioDevice(const QAudioDevice &) override; - void setVolume(float volume) override; - void setMuted(bool muted) override; - - void setPipeline(const QGstPipeline &pipeline); - - QGstElement gstElement() const { return gstAudioOutput; } - -Q_SIGNALS: - void mutedChanged(bool); - void volumeChanged(int); - -private: - QAudioDevice m_audioOutput; - - // Gst elements - QGstPipeline gstPipeline; - QGstBin gstAudioOutput; - - QGstElement audioQueue; - QGstElement audioConvert; - QGstElement audioResample; - QGstElement audioVolume; - QGstElement audioSink; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/gstreamer/common/qgstreamerbufferprobe.cpp b/src/multimedia/platform/gstreamer/common/qgstreamerbufferprobe.cpp deleted file mode 100644 index 3af8000bb..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstreamerbufferprobe.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Jolla 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$ -** -****************************************************************************/ - -#include "qgstreamerbufferprobe_p.h" -#include "qgstutils_p.h" - -QT_BEGIN_NAMESPACE - -QGstreamerBufferProbe::QGstreamerBufferProbe(Flags flags) - : m_flags(flags) -{ -} - -QGstreamerBufferProbe::~QGstreamerBufferProbe() = default; - -void QGstreamerBufferProbe::addProbeToPad(GstPad *pad, bool downstream) -{ - if (GstCaps *caps = gst_pad_get_current_caps(pad)) { - probeCaps(caps); - gst_caps_unref(caps); - } - if (m_flags & ProbeCaps) { - m_capsProbeId = gst_pad_add_probe( - pad, - downstream - ? GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM - : GST_PAD_PROBE_TYPE_EVENT_UPSTREAM, - capsProbe, - this, - nullptr); - } - if (m_flags & ProbeBuffers) { - m_bufferProbeId = gst_pad_add_probe( - pad, GST_PAD_PROBE_TYPE_BUFFER, bufferProbe, this, nullptr); - } -} - -void QGstreamerBufferProbe::removeProbeFromPad(GstPad *pad) -{ - if (m_capsProbeId != -1) { - gst_pad_remove_probe(pad, m_capsProbeId); - m_capsProbeId = -1; - } - if (m_bufferProbeId != -1) { - gst_pad_remove_probe(pad, m_bufferProbeId); - m_bufferProbeId = -1; - } -} - -void QGstreamerBufferProbe::probeCaps(GstCaps *) -{ -} - -bool QGstreamerBufferProbe::probeBuffer(GstBuffer *) -{ - return true; -} - -GstPadProbeReturn QGstreamerBufferProbe::capsProbe(GstPad *, GstPadProbeInfo *info, gpointer user_data) -{ - QGstreamerBufferProbe * const control = static_cast<QGstreamerBufferProbe *>(user_data); - - if (GstEvent * const event = gst_pad_probe_info_get_event(info)) { - if (GST_EVENT_TYPE(event) == GST_EVENT_CAPS) { - GstCaps *caps; - gst_event_parse_caps(event, &caps); - - control->probeCaps(caps); - } - } - return GST_PAD_PROBE_OK; -} - -GstPadProbeReturn QGstreamerBufferProbe::bufferProbe( - GstPad *, GstPadProbeInfo *info, gpointer user_data) -{ - QGstreamerBufferProbe * const control = static_cast<QGstreamerBufferProbe *>(user_data); - if (GstBuffer * const buffer = gst_pad_probe_info_get_buffer(info)) - return control->probeBuffer(buffer) ? GST_PAD_PROBE_OK : GST_PAD_PROBE_DROP; - return GST_PAD_PROBE_OK; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/gstreamer/common/qgstreamerbufferprobe_p.h b/src/multimedia/platform/gstreamer/common/qgstreamerbufferprobe_p.h deleted file mode 100644 index d3ca9db2e..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstreamerbufferprobe_p.h +++ /dev/null @@ -1,92 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Jolla 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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERBUFFERPROBE_H -#define QGSTREAMERBUFFERPROBE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qtmultimediaglobal_p.h> -#include <gst/gst.h> - -#include <QtCore/qglobal.h> - - -QT_BEGIN_NAMESPACE - -class Q_MULTIMEDIA_EXPORT QGstreamerBufferProbe -{ -public: - enum Flags - { - ProbeCaps = 0x01, - ProbeBuffers = 0x02, - ProbeAll = ProbeCaps | ProbeBuffers - }; - - explicit QGstreamerBufferProbe(Flags flags = ProbeAll); - virtual ~QGstreamerBufferProbe(); - - void addProbeToPad(GstPad *pad, bool downstream = true); - void removeProbeFromPad(GstPad *pad); - -protected: - virtual void probeCaps(GstCaps *caps); - virtual bool probeBuffer(GstBuffer *buffer); - -private: - static GstPadProbeReturn capsProbe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data); - static GstPadProbeReturn bufferProbe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data); - int m_capsProbeId = -1; - int m_bufferProbeId = -1; - const Flags m_flags; -}; - -QT_END_NAMESPACE - -#endif // QGSTREAMERBUFFERPROBE_H diff --git a/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer.cpp b/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer.cpp deleted file mode 100644 index 2c05c0cfe..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer.cpp +++ /dev/null @@ -1,864 +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$ -** -****************************************************************************/ - -#include <private/qgstreamermediaplayer_p.h> -#include <private/qgstpipeline_p.h> -#include <private/qgstreamermetadata_p.h> -#include <private/qgstreamerformatinfo_p.h> -#include <private/qgstreameraudiooutput_p.h> -#include <private/qgstreamervideooutput_p.h> -#include <private/qgstreamervideosink_p.h> -#include "private/qgstreamermessage_p.h" -#include <private/qgstreameraudiodevice_p.h> -#include <private/qgstappsrc_p.h> -#include <qaudiodevice.h> - -#include <QtCore/qdir.h> -#include <QtCore/qsocketnotifier.h> -#include <QtCore/qurl.h> -#include <QtCore/qdebug.h> -#include <QtCore/qloggingcategory.h> -#include <QtNetwork/qnetworkaccessmanager.h> -#include <QtNetwork/qnetworkreply.h> - -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> - -Q_LOGGING_CATEGORY(qLcMediaPlayer, "qt.multimedia.player") - -QT_BEGIN_NAMESPACE - -QGstreamerMediaPlayer::TrackSelector::TrackSelector(TrackType type, const char *name) - : selector(QGstElement("input-selector", name)), - type(type) -{ - selector.set("sync-streams", true); - - if (type == SubtitleStream) { - selector.set("sync-mode", 1 /*clock*/); - selector.set("cache-buffers", true); - } - - nullTrack = selector.getRequestPad("sink_%u"); -} - -QGstPad QGstreamerMediaPlayer::TrackSelector::createInputPad() -{ - auto pad = selector.getRequestPad("sink_%u"); - tracks.append(pad); - return pad; -} - -void QGstreamerMediaPlayer::TrackSelector::removeAllInputPads() -{ - for (auto &pad : tracks) - selector.releaseRequestPad(pad); - tracks.clear(); -} - -void QGstreamerMediaPlayer::TrackSelector::removeInputPad(QGstPad pad) -{ - selector.releaseRequestPad(pad); - tracks.removeOne(pad); -} - -QGstPad QGstreamerMediaPlayer::TrackSelector::inputPad(int index) -{ - if (index == -1) - return nullTrack; - if (index >= 0 && index < tracks.count()) - return tracks[index]; - return {}; -} - -QGstreamerMediaPlayer::TrackSelector &QGstreamerMediaPlayer::trackSelector(TrackType type) -{ - auto &ts = trackSelectors[type]; - Q_ASSERT(ts.type == type); - return ts; -} - -QGstreamerMediaPlayer::QGstreamerMediaPlayer(QMediaPlayer *parent) - : QObject(parent), - QPlatformMediaPlayer(parent), - trackSelectors{{{ VideoStream, "videoInputSelector" }, - { AudioStream, "audioInputSelector" }, - { SubtitleStream, "subTitleInputSelector" }}}, - playerPipeline("playerPipeline") -{ - playerPipeline.setFlushOnConfigChanges(true); - - gstVideoOutput = new QGstreamerVideoOutput(this); - gstVideoOutput->setPipeline(playerPipeline); - - for (auto &ts : trackSelectors) - playerPipeline.add(ts.selector); - - playerPipeline.setState(GST_STATE_NULL); - playerPipeline.installMessageFilter(static_cast<QGstreamerBusMessageFilter *>(this)); - playerPipeline.installMessageFilter(static_cast<QGstreamerSyncMessageFilter *>(this)); - - /* Taken from gstdicoverer.c: - * This is ugly. We get the GType of decodebin so we can quickly detect - * when a decodebin is added to uridecodebin so we can set the - * post-stream-topology setting to TRUE */ - auto decodebin = QGstElement("decodebin", nullptr); - decodebinType = G_OBJECT_TYPE(decodebin.element()); - connect(&positionUpdateTimer, &QTimer::timeout, this, &QGstreamerMediaPlayer::updatePosition); -} - -QGstreamerMediaPlayer::~QGstreamerMediaPlayer() -{ - playerPipeline.removeMessageFilter(static_cast<QGstreamerBusMessageFilter *>(this)); - playerPipeline.removeMessageFilter(static_cast<QGstreamerSyncMessageFilter *>(this)); - playerPipeline.setStateSync(GST_STATE_NULL); - topology.free(); -} - -qint64 QGstreamerMediaPlayer::position() const -{ - if (playerPipeline.isNull() || m_url.isEmpty()) - return 0; - - return playerPipeline.position()/1e6; -} - -qint64 QGstreamerMediaPlayer::duration() const -{ - return m_duration; -} - -float QGstreamerMediaPlayer::bufferProgress() const -{ - return m_bufferProgress/100.; -} - -QMediaTimeRange QGstreamerMediaPlayer::availablePlaybackRanges() const -{ - return QMediaTimeRange(); -} - -qreal QGstreamerMediaPlayer::playbackRate() const -{ - return playerPipeline.playbackRate(); -} - -void QGstreamerMediaPlayer::setPlaybackRate(qreal rate) -{ - if (playerPipeline.setPlaybackRate(rate)) - playbackRateChanged(rate); -} - -void QGstreamerMediaPlayer::setPosition(qint64 pos) -{ - qint64 currentPos = playerPipeline.position()/1e6; - if (pos == currentPos) - return; - playerPipeline.finishStateChange(); - playerPipeline.setPosition(pos*1e6); - qCDebug(qLcMediaPlayer) << Q_FUNC_INFO << pos << playerPipeline.position()/1e6; - if (mediaStatus() == QMediaPlayer::EndOfMedia) - mediaStatusChanged(QMediaPlayer::LoadedMedia); - positionChanged(pos); -} - -void QGstreamerMediaPlayer::play() -{ - if (state() == QMediaPlayer::PlayingState || m_url.isEmpty()) - return; - - *playerPipeline.inStoppedState() = false; - if (mediaStatus() == QMediaPlayer::EndOfMedia) { - playerPipeline.setPosition(0); - updatePosition(); - } - - qCDebug(qLcMediaPlayer) << "play()."; - int ret = playerPipeline.setState(GST_STATE_PLAYING); - if (m_requiresSeekOnPlay) { - // Flushing the pipeline is required to get track changes - // immediately, when they happen while paused. - playerPipeline.flush(); - m_requiresSeekOnPlay = false; - } - if (ret == GST_STATE_CHANGE_FAILURE) - qCDebug(qLcMediaPlayer) << "Unable to set the pipeline to the playing state."; - if (mediaStatus() == QMediaPlayer::LoadedMedia) - mediaStatusChanged(QMediaPlayer::BufferedMedia); - emit stateChanged(QMediaPlayer::PlayingState); - positionUpdateTimer.start(100); -} - -void QGstreamerMediaPlayer::pause() -{ - if (state() == QMediaPlayer::PausedState || m_url.isEmpty()) - return; - - positionUpdateTimer.stop(); - if (*playerPipeline.inStoppedState()) { - *playerPipeline.inStoppedState() = false; - playerPipeline.flush(); - } - int ret = playerPipeline.setState(GST_STATE_PAUSED); - if (ret == GST_STATE_CHANGE_FAILURE) - qCDebug(qLcMediaPlayer) << "Unable to set the pipeline to the paused state."; - if (mediaStatus() == QMediaPlayer::EndOfMedia) { - playerPipeline.setPosition(0); - mediaStatusChanged(QMediaPlayer::BufferedMedia); - } - updatePosition(); - emit stateChanged(QMediaPlayer::PausedState); -} - -void QGstreamerMediaPlayer::stop() -{ - if (state() == QMediaPlayer::StoppedState) - return; - stopOrEOS(false); -} - -void QGstreamerMediaPlayer::stopOrEOS(bool eos) -{ - positionUpdateTimer.stop(); - *playerPipeline.inStoppedState() = true; - bool ret = playerPipeline.setStateSync(GST_STATE_PAUSED); - if (!ret) - qCDebug(qLcMediaPlayer) << "Unable to set the pipeline to the stopped state."; - if (!eos) - playerPipeline.setPosition(0); - updatePosition(); - emit stateChanged(QMediaPlayer::StoppedState); - mediaStatusChanged(eos ? QMediaPlayer::EndOfMedia : QMediaPlayer::LoadedMedia); -} - -bool QGstreamerMediaPlayer::processBusMessage(const QGstreamerMessage &message) -{ - if (message.isNull()) - return false; - -// qCDebug(qLcMediaPlayer) << "received bus message from" << message.source().name() << message.type() << (message.type() == GST_MESSAGE_TAG); - - GstMessage* gm = message.rawMessage(); - switch (message.type()) { - case GST_MESSAGE_TAG: { - // #### This isn't ideal. We shouldn't catch stream specific tags here, rather the global ones - GstTagList *tag_list; - gst_message_parse_tag(gm, &tag_list); - //qCDebug(qLcMediaPlayer) << "Got tags: " << message.source().name() << gst_tag_list_to_string(tag_list); - auto metaData = QGstreamerMetaData::fromGstTagList(tag_list); - for (auto k : metaData.keys()) - m_metaData.insert(k, metaData.value(k)); - break; - } - case GST_MESSAGE_DURATION_CHANGED: { - qint64 d = playerPipeline.duration()/1e6; - qCDebug(qLcMediaPlayer) << " duration changed message" << d; - if (d != m_duration) { - m_duration = d; - emit durationChanged(duration()); - } - return false; - } - case GST_MESSAGE_EOS: - stopOrEOS(true); - break; - case GST_MESSAGE_BUFFERING: { - qCDebug(qLcMediaPlayer) << " buffering message"; - int progress = 0; - gst_message_parse_buffering(gm, &progress); - m_bufferProgress = progress; - mediaStatusChanged(m_bufferProgress == 100 ? QMediaPlayer::BufferedMedia : QMediaPlayer::BufferingMedia); - emit bufferProgressChanged(m_bufferProgress/100.); - break; - } - case GST_MESSAGE_STATE_CHANGED: { - if (message.source() != playerPipeline) - return false; - - GstState oldState; - GstState newState; - GstState pending; - - gst_message_parse_state_changed(gm, &oldState, &newState, &pending); - qCDebug(qLcMediaPlayer) << " state changed message" << oldState << newState << pending; - -#ifdef DEBUG_PLAYBIN - static QStringList states = { - QStringLiteral("GST_STATE_VOID_PENDING"), QStringLiteral("GST_STATE_NULL"), - QStringLiteral("GST_STATE_READY"), QStringLiteral("GST_STATE_PAUSED"), - QStringLiteral("GST_STATE_PLAYING") }; - - qCDebug(qLcMediaPlayer) << QStringLiteral("state changed: old: %1 new: %2 pending: %3") \ - .arg(states[oldState]) \ - .arg(states[newState]) \ - .arg(states[pending]); -#endif - - switch (newState) { - case GST_STATE_VOID_PENDING: - case GST_STATE_NULL: - case GST_STATE_READY: - seekableChanged(false); - break; - case GST_STATE_PAUSED: - { - if (prerolling) { - qCDebug(qLcMediaPlayer) << "Preroll done, setting status to Loaded"; - prerolling = false; - GST_DEBUG_BIN_TO_DOT_FILE(playerPipeline.bin(), GST_DEBUG_GRAPH_SHOW_ALL, "playerPipeline"); - - parseStreamsAndMetadata(); - - qint64 d = playerPipeline.duration()/1e6; - if (d != m_duration) { - m_duration = d; - qCDebug(qLcMediaPlayer) << " duration changed" << d; - emit durationChanged(duration()); - } - - emit tracksChanged(); - mediaStatusChanged(QMediaPlayer::LoadedMedia); - } - - break; - } - case GST_STATE_PLAYING: - mediaStatusChanged(QMediaPlayer::BufferedMedia); - break; - } - 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) - emit error(QMediaPlayer::FormatError, tr("Cannot play stream of type: <unknown>")); - else - emit error(QMediaPlayer::ResourceError, QString::fromUtf8(err->message)); - playerPipeline.dumpGraph("error"); - mediaStatusChanged(QMediaPlayer::InvalidMedia); - g_error_free(err); - g_free(debug); - break; - } - case GST_MESSAGE_WARNING: { - GError *err; - gchar *debug; - gst_message_parse_warning (gm, &err, &debug); - qCWarning(qLcMediaPlayer) << "Warning:" << QString::fromUtf8(err->message); - playerPipeline.dumpGraph("warning"); - g_error_free (err); - g_free (debug); - break; - } - case GST_MESSAGE_INFO: { - if (qLcMediaPlayer().isDebugEnabled()) { - GError *err; - gchar *debug; - gst_message_parse_info (gm, &err, &debug); - qCDebug(qLcMediaPlayer) << "Info:" << QString::fromUtf8(err->message); - g_error_free (err); - g_free (debug); - } - break; - } - case GST_MESSAGE_SEGMENT_START: { - qCDebug(qLcMediaPlayer) << " segment start message, updating position"; - QGstStructure structure(gst_message_get_structure(gm)); - auto p = structure["position"].toInt64(); - if (p) { - qint64 position = (*p)/1000000; - emit positionChanged(position); - } - break; - } - case GST_MESSAGE_ELEMENT: { - QGstStructure structure(gst_message_get_structure(gm)); - auto type = structure.name(); - if (type == "stream-topology") { - topology.free(); - topology = structure.copy(); - } - break; - } - - default: -// qCDebug(qLcMediaPlayer) << " default message handler, doing nothing"; - - break; - } - - return false; -} - -bool QGstreamerMediaPlayer::processSyncMessage(const QGstreamerMessage &message) -{ -#if QT_CONFIG(gstreamer_gl) - if (message.type() != GST_MESSAGE_NEED_CONTEXT) - return false; - const gchar *type = nullptr; - gst_message_parse_context_type (message.rawMessage(), &type); - if (strcmp(type, GST_GL_DISPLAY_CONTEXT_TYPE)) - return false; - if (!gstVideoOutput || !gstVideoOutput->gstreamerVideoSink()) - return false; - auto *context = gstVideoOutput->gstreamerVideoSink()->gstGlDisplayContext(); - if (!context) - return false; - gst_element_set_context(GST_ELEMENT(GST_MESSAGE_SRC(message.rawMessage())), context); - playerPipeline.dumpGraph("need_context"); - return true; -#else - Q_UNUSED(message); - return false; -#endif -} - -QUrl QGstreamerMediaPlayer::media() const -{ - return m_url; -} - -const QIODevice *QGstreamerMediaPlayer::mediaStream() const -{ - return m_stream; -} - -void QGstreamerMediaPlayer::decoderPadAdded(const QGstElement &src, const QGstPad &pad) -{ - if (src != decoder) - return; - - auto caps = pad.currentCaps(); - auto type = caps.at(0).name(); - qCDebug(qLcMediaPlayer) << "Received new pad" << pad.name() << "from" << src.name() << "type" << type; - qCDebug(qLcMediaPlayer) << " " << caps.toString(); - - TrackType streamType = NTrackTypes; - if (type.startsWith("video/x-raw")) { - streamType = VideoStream; - } else if (type.startsWith("audio/x-raw")) { - streamType = AudioStream; - } else if (type.startsWith("text/")) { - streamType = SubtitleStream; - } else { - qCWarning(qLcMediaPlayer) << "Ignoring unknown media stream:" << pad.name() << type; - return; - } - - auto &ts = trackSelector(streamType); - connectOutput(ts); - - QGstPad sinkPad = ts.createInputPad(); - if (!pad.link(sinkPad)) { - qCWarning(qLcMediaPlayer) << "Failed to add track, cannot link pads"; - return; - } - qCDebug(qLcMediaPlayer) << "Adding track"; - - if (ts.trackCount() == 1) { - if (streamType == VideoStream) { - ts.setActiveInputPad(sinkPad); - emit videoAvailableChanged(true); - } - else if (streamType == AudioStream) { - ts.setActiveInputPad(sinkPad); - emit audioAvailableChanged(true); - } - } - - if (!prerolling) - emit tracksChanged(); - - decoderOutputMap.insert(pad.name(), sinkPad); -} - -void QGstreamerMediaPlayer::decoderPadRemoved(const QGstElement &src, const QGstPad &pad) -{ - if (src != decoder) - return; - - qCDebug(qLcMediaPlayer) << "Removed pad" << pad.name() << "from" << src.name(); - auto track = decoderOutputMap.value(pad.name()); - if (track.isNull()) - return; - - auto ts = std::find_if(std::begin(trackSelectors), std::end(trackSelectors), - [&](TrackSelector &ts){ return ts.selector == track.parent(); }); - if (ts == std::end(trackSelectors)) - return; - - qCDebug(qLcMediaPlayer) << " was linked to pad" << track.name() << "from" << ts->selector.name(); - ts->removeInputPad(track); - - if (ts->trackCount() == 0) { - removeOutput(*ts); - if (ts->type == AudioStream) - audioAvailableChanged(false); - else if (ts->type == VideoStream) - videoAvailableChanged(false); - } - - if (!prerolling) - tracksChanged(); -} - -void QGstreamerMediaPlayer::removeAllOutputs() -{ - for (auto &ts : trackSelectors) { - removeOutput(ts); - ts.removeAllInputPads(); - } - audioAvailableChanged(false); - videoAvailableChanged(false); -} - -void QGstreamerMediaPlayer::connectOutput(TrackSelector &ts) -{ - if (ts.isConnected) - return; - - QGstElement e; - switch (ts.type) { - case AudioStream: - e = gstAudioOutput ? gstAudioOutput->gstElement() : QGstElement{}; - break; - case VideoStream: - e = gstVideoOutput ? gstVideoOutput->gstElement() : QGstElement{}; - break; - case SubtitleStream: - gstVideoOutput->linkSubtitleStream(ts.selector); - break; - default: - return; - } - - if (!e.isNull()) { - qCDebug(qLcMediaPlayer) << "connecting output for track type" << ts.type; - playerPipeline.add(e); - ts.selector.link(e); - e.setState(GST_STATE_PAUSED); - } - - ts.isConnected = true; -} - -void QGstreamerMediaPlayer::removeOutput(TrackSelector &ts) -{ - if (!ts.isConnected) - return; - - QGstElement e; - switch (ts.type) { - case AudioStream: - e = gstAudioOutput ? gstAudioOutput->gstElement() : QGstElement{}; - break; - case VideoStream: - e = gstVideoOutput ? gstVideoOutput->gstElement() : QGstElement{}; - break; - case SubtitleStream: - // TODO: unlink subtitle stream - break; - default: - break; - } - - if (!e.isNull()) { - qCDebug(qLcMediaPlayer) << "removing output for track type" << ts.type; - e.setState(GST_STATE_NULL); - playerPipeline.remove(e); - } - - ts.isConnected = false; -} - -void QGstreamerMediaPlayer::uridecodebinElementAddedCallback(GstElement */*uridecodebin*/, GstElement *child, QGstreamerMediaPlayer *that) -{ - QGstElement c(child); - qCDebug(qLcMediaPlayer) << "New element added to uridecodebin:" << c.name(); - - if (G_OBJECT_TYPE(child) == that->decodebinType) { - qCDebug(qLcMediaPlayer) << " -> setting post-stream-topology property"; - c.set("post-stream-topology", true); - } else if (!qstrcmp(gst_element_get_name(child), "source")) { - GstBaseSrc *src = GST_BASE_SRC(child); - bool seekable = src && GST_BASE_SRC_GET_CLASS(src)->is_seekable(src); - that->seekableChanged(seekable); - } -} - -void QGstreamerMediaPlayer::setMedia(const QUrl &content, QIODevice *stream) -{ - qCDebug(qLcMediaPlayer) << Q_FUNC_INFO << "setting location to" << content; - - prerolling = true; - - bool ret = playerPipeline.setStateSync(GST_STATE_NULL); - if (!ret) - qCDebug(qLcMediaPlayer) << "Unable to set the pipeline to the stopped state."; - - m_url = content; - m_stream = stream; - - if (!src.isNull()) - playerPipeline.remove(src); - if (!decoder.isNull()) - playerPipeline.remove(decoder); - src = QGstElement(); - decoder = QGstElement(); - removeAllOutputs(); - seekableChanged(false); - - if (m_duration != 0) { - m_duration = 0; - durationChanged(0); - } - stateChanged(QMediaPlayer::StoppedState); - if (position() != 0) - positionChanged(0); - mediaStatusChanged(QMediaPlayer::NoMedia); - if (!m_metaData.isEmpty()) { - m_metaData.clear(); - metaDataChanged(); - } - - if (content.isEmpty()) - return; - - if (m_stream) { - if (!m_appSrc) - m_appSrc = new QGstAppSrc(this); - src = m_appSrc->element(); - decoder = QGstElement("decodebin", "decoder"); - decoder.set("post-stream-topology", true); - playerPipeline.add(src, decoder); - src.link(decoder); - - m_appSrc->setup(m_stream); - seekableChanged(!stream->isSequential()); - } else { - // use uridecodebin - decoder = QGstElement("uridecodebin", "uridecoder"); - playerPipeline.add(decoder); - // can't set post-stream-topology to true, as uridecodebin doesn't have the property. Use a hack - decoder.connect("element-added", GCallback(QGstreamerMediaPlayer::uridecodebinElementAddedCallback), this); - - decoder.set("uri", content.toEncoded().constData()); - if (m_bufferProgress != 0) { - m_bufferProgress = 0; - emit bufferProgressChanged(0.); - } - } - decoder.onPadAdded<&QGstreamerMediaPlayer::decoderPadAdded>(this); - decoder.onPadRemoved<&QGstreamerMediaPlayer::decoderPadRemoved>(this); - - mediaStatusChanged(QMediaPlayer::LoadingMedia); - - if (state() == QMediaPlayer::PlayingState) { - int ret = playerPipeline.setState(GST_STATE_PLAYING); - if (ret == GST_STATE_CHANGE_FAILURE) - qCWarning(qLcMediaPlayer) << "Unable to set the pipeline to the playing state."; - } else { - int ret = playerPipeline.setState(GST_STATE_PAUSED); - if (!ret) - qCWarning(qLcMediaPlayer) << "Unable to set the pipeline to the paused state."; - } - - playerPipeline.setPosition(0); - positionChanged(0); -} - -void QGstreamerMediaPlayer::setAudioOutput(QPlatformAudioOutput *output) -{ - if (gstAudioOutput == output) - return; - - auto &ts = trackSelector(AudioStream); - - playerPipeline.beginConfig(); - if (gstAudioOutput) { - removeOutput(ts); - gstAudioOutput->setPipeline({}); - } - gstAudioOutput = static_cast<QGstreamerAudioOutput *>(output); - if (gstAudioOutput) { - gstAudioOutput->setPipeline(playerPipeline); - connectOutput(ts); - } - playerPipeline.endConfig(); -} - -QMediaMetaData QGstreamerMediaPlayer::metaData() const -{ - return m_metaData; -} - -void QGstreamerMediaPlayer::setVideoSink(QVideoSink *sink) -{ - gstVideoOutput->setVideoSink(sink); -} - -static QGstStructure endOfChain(const QGstStructure &s) -{ - QGstStructure e = s; - while (1) { - auto next = e["next"].toStructure(); - if (!next.isNull()) - e = next; - else - break; - } - return e; -} - -void QGstreamerMediaPlayer::parseStreamsAndMetadata() -{ - qCDebug(qLcMediaPlayer) << "============== parse topology ============"; - if (topology.isNull()) { - qCDebug(qLcMediaPlayer) << " null topology"; - return; - } - auto caps = topology["caps"].toCaps(); - auto structure = caps.at(0); - auto fileFormat = QGstreamerFormatInfo::fileFormatForCaps(structure); - qCDebug(qLcMediaPlayer) << caps.toString() << fileFormat; - m_metaData.insert(QMediaMetaData::FileFormat, QVariant::fromValue(fileFormat)); - m_metaData.insert(QMediaMetaData::Duration, duration()); - m_metaData.insert(QMediaMetaData::Url, m_url); - QGValue tags = topology["tags"]; - if (!tags.isNull()) { - GstTagList *tagList = nullptr; - gst_structure_get(topology.structure, "tags", GST_TYPE_TAG_LIST, &tagList, nullptr); - const auto metaData = QGstreamerMetaData::fromGstTagList(tagList); - for (auto k : metaData.keys()) - m_metaData.insert(k, metaData.value(k)); - } - - auto demux = endOfChain(topology); - auto next = demux["next"]; - if (!next.isList()) { - qCDebug(qLcMediaPlayer) << " no additional streams"; - emit metaDataChanged(); - return; - } - - // collect stream info - int size = next.listSize(); - for (int i = 0; i < size; ++i) { - auto val = next.at(i); - caps = val.toStructure()["caps"].toCaps(); - structure = caps.at(0); - if (structure.name().startsWith("audio/")) { - auto codec = QGstreamerFormatInfo::audioCodecForCaps(structure); - m_metaData.insert(QMediaMetaData::AudioCodec, QVariant::fromValue(codec)); - qCDebug(qLcMediaPlayer) << " audio" << caps.toString() << (int)codec; - } else if (structure.name().startsWith("video/")) { - auto codec = QGstreamerFormatInfo::videoCodecForCaps(structure); - m_metaData.insert(QMediaMetaData::VideoCodec, QVariant::fromValue(codec)); - qCDebug(qLcMediaPlayer) << " video" << caps.toString() << (int)codec; - auto framerate = structure["framerate"].getFraction(); - if (framerate) - m_metaData.insert(QMediaMetaData::VideoFrameRate, *framerate); - auto width = structure["width"].toInt(); - auto height = structure["height"].toInt(); - if (width && height) - m_metaData.insert(QMediaMetaData::Resolution, QSize(*width, *height)); - } - } - - auto sinkPad = trackSelector(VideoStream).activeInputPad(); - if (!sinkPad.isNull()) { - bool hasTags = g_object_class_find_property (G_OBJECT_GET_CLASS (sinkPad.object()), "tags") != NULL; - - GstTagList *tl = nullptr; - g_object_get(sinkPad.object(), "tags", &tl, nullptr); - qCDebug(qLcMediaPlayer) << " tags=" << hasTags << (tl ? gst_tag_list_to_string(tl) : "(null)"); - } - - - qCDebug(qLcMediaPlayer) << "============== end parse topology ============"; - emit metaDataChanged(); - playerPipeline.dumpGraph("playback"); -} - -int QGstreamerMediaPlayer::trackCount(QPlatformMediaPlayer::TrackType type) -{ - return trackSelector(type).trackCount(); -} - -QMediaMetaData QGstreamerMediaPlayer::trackMetaData(QPlatformMediaPlayer::TrackType type, int index) -{ - auto track = trackSelector(type).inputPad(index); - if (track.isNull()) - return {}; - - GstTagList *tagList = nullptr; - g_object_get(track.object(), "tags", &tagList, nullptr); - - return tagList ? QGstreamerMetaData::fromGstTagList(tagList) : QMediaMetaData{}; -} - -int QGstreamerMediaPlayer::activeTrack(TrackType type) -{ - return trackSelector(type).activeInputIndex(); -} - -void QGstreamerMediaPlayer::setActiveTrack(TrackType type, int index) -{ - auto &ts = trackSelector(type); - auto track = ts.inputPad(index); - if (track.isNull()) - return; - - if (type == QPlatformMediaPlayer::SubtitleStream) - gstVideoOutput->flushSubtitles(); - - qCDebug(qLcMediaPlayer) << "Setting active track type" << type << "to" << index; - ts.setActiveInputPad(track); - - // seek to force an immediate change of the stream - if (playerPipeline.state() == GST_STATE_PLAYING) - playerPipeline.flush(); - else - m_requiresSeekOnPlay = true; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer_p.h b/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer_p.h deleted file mode 100644 index a811e2832..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstreamermediaplayer_p.h +++ /dev/null @@ -1,187 +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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERMEDIAPLAYER_P_H -#define QGSTREAMERMEDIAPLAYER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qstack.h> -#include <private/qplatformmediaplayer_p.h> -#include <private/qtmultimediaglobal_p.h> -#include <qurl.h> -#include <private/qgst_p.h> -#include <private/qgstpipeline_p.h> - -#include <QtCore/qtimer.h> - -#include <array> - -QT_BEGIN_NAMESPACE - -class QNetworkAccessManager; -class QGstreamerMessage; -class QGstAppSrc; -class QGstreamerAudioOutput; -class QGstreamerVideoOutput; - -class Q_MULTIMEDIA_EXPORT QGstreamerMediaPlayer - : public QObject, - public QPlatformMediaPlayer, - public QGstreamerBusMessageFilter, - public QGstreamerSyncMessageFilter -{ - Q_OBJECT - -public: - QGstreamerMediaPlayer(QMediaPlayer *parent = 0); - ~QGstreamerMediaPlayer(); - - qint64 position() const override; - qint64 duration() const override; - - float bufferProgress() const override; - - QMediaTimeRange availablePlaybackRanges() const override; - - qreal playbackRate() const override; - void setPlaybackRate(qreal rate) override; - - QUrl media() const override; - const QIODevice *mediaStream() const override; - void setMedia(const QUrl&, QIODevice *) override; - - bool streamPlaybackSupported() const override { return true; } - - void setAudioOutput(QPlatformAudioOutput *output) override; - - QMediaMetaData metaData() const override; - - void setVideoSink(QVideoSink *sink) override; - - int trackCount(TrackType) override; - QMediaMetaData trackMetaData(TrackType /*type*/, int /*streamNumber*/) override; - int activeTrack(TrackType) override; - void setActiveTrack(TrackType, int /*streamNumber*/) override; - - void setPosition(qint64 pos) override; - - void play() override; - void pause() override; - void stop() override; - - bool processBusMessage(const QGstreamerMessage& message) override; - bool processSyncMessage(const QGstreamerMessage& message) override; - -public Q_SLOTS: - void updatePosition() { positionChanged(position()); } - -private: - struct TrackSelector { - TrackSelector(TrackType, const char *name); - QGstPad createInputPad(); - void removeInputPad(QGstPad pad); - void removeAllInputPads(); - QGstPad inputPad(int index); - int activeInputIndex() const { return tracks.indexOf(activeInputPad()); } - QGstPad activeInputPad() const { return selector.getObject("active-pad"); } - void setActiveInputPad(QGstPad input) { selector.set("active-pad", input); } - int trackCount() const { return tracks.count(); } - - QGstElement selector; - TrackType type; - QList<QGstPad> tracks; - QGstPad nullTrack; - bool isConnected = false; - }; - - friend class QGstreamerStreamsControl; - void decoderPadAdded(const QGstElement &src, const QGstPad &pad); - void decoderPadRemoved(const QGstElement &src, const QGstPad &pad); - static void uridecodebinElementAddedCallback(GstElement *uridecodebin, GstElement *child, QGstreamerMediaPlayer *that); - void parseStreamsAndMetadata(); - void connectOutput(TrackSelector &ts); - void removeOutput(TrackSelector &ts); - void removeAllOutputs(); - void stopOrEOS(bool eos); - - std::array<TrackSelector, NTrackTypes> trackSelectors; - TrackSelector &trackSelector(TrackType type); - - QMediaMetaData m_metaData; - - int m_bufferProgress = -1; - QUrl m_url; - QIODevice *m_stream = nullptr; - - bool prerolling = false; - bool m_requiresSeekOnPlay = false; - qint64 m_duration = 0; - QTimer positionUpdateTimer; - - QGstAppSrc *m_appSrc = nullptr; - - GType decodebinType; - QGstStructure topology; - - // Gst elements - QGstPipeline playerPipeline; - QGstElement src; - QGstElement decoder; - - QGstreamerAudioOutput *gstAudioOutput = nullptr; - QGstreamerVideoOutput *gstVideoOutput = nullptr; - - // QGstElement streamSynchronizer; - - QHash<QByteArray, QGstPad> decoderOutputMap; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/gstreamer/common/qgstreamermessage.cpp b/src/multimedia/platform/gstreamer/common/qgstreamermessage.cpp deleted file mode 100644 index 950cc46cb..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstreamermessage.cpp +++ /dev/null @@ -1,90 +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$ -** -****************************************************************************/ - -#include <gst/gst.h> - -#include "qgstreamermessage_p.h" - -QT_BEGIN_NAMESPACE - -/*! - \class QGstreamerMessage - \internal -*/ - -QGstreamerMessage::QGstreamerMessage(GstMessage* message): - m_message(message) -{ - gst_message_ref(m_message); -} - -QGstreamerMessage::QGstreamerMessage(QGstreamerMessage const& m): - m_message(m.m_message) -{ - gst_message_ref(m_message); -} - - -QGstreamerMessage::~QGstreamerMessage() -{ - if (m_message != nullptr) - gst_message_unref(m_message); -} - -GstMessage* QGstreamerMessage::rawMessage() const -{ - return m_message; -} - -QGstreamerMessage& QGstreamerMessage::operator=(QGstreamerMessage const& rhs) -{ - if (rhs.m_message != m_message) { - if (rhs.m_message != nullptr) - gst_message_ref(rhs.m_message); - - if (m_message != nullptr) - gst_message_unref(m_message); - - m_message = rhs.m_message; - } - - return *this; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/gstreamer/common/qgstreamermessage_p.h b/src/multimedia/platform/gstreamer/common/qgstreamermessage_p.h deleted file mode 100644 index 8d5ff440e..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstreamermessage_p.h +++ /dev/null @@ -1,86 +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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERMESSAGE_P_H -#define QGSTREAMERMESSAGE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qtmultimediaglobal_p.h> -#include <private/qgst_p.h> - -QT_BEGIN_NAMESPACE - -// Required for QDoc workaround -class QString; - -class Q_MULTIMEDIA_EXPORT QGstreamerMessage -{ -public: - QGstreamerMessage() = default; - QGstreamerMessage(GstMessage* message); - QGstreamerMessage(QGstreamerMessage const& m); - ~QGstreamerMessage(); - - bool isNull() const { return !m_message; } - GstMessageType type() const { return GST_MESSAGE_TYPE(m_message); } - QGstObject source() const { return QGstObject(GST_MESSAGE_SRC(m_message), QGstObject::NeedsRef); } - - GstMessage* rawMessage() const; - - QGstreamerMessage& operator=(QGstreamerMessage const& rhs); - -private: - GstMessage* m_message = nullptr; -}; - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(QGstreamerMessage); - -#endif diff --git a/src/multimedia/platform/gstreamer/common/qgstreamermetadata.cpp b/src/multimedia/platform/gstreamer/common/qgstreamermetadata.cpp deleted file mode 100644 index 3cf3146a3..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstreamermetadata.cpp +++ /dev/null @@ -1,305 +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$ -** -****************************************************************************/ - -#include "qgstreamermetadata_p.h" -#include <QDebug> -#include <QtMultimedia/qmediametadata.h> -#include <QtCore/qdatetime.h> - -#include <gst/gstversion.h> -#include <private/qgstutils_p.h> - -QT_BEGIN_NAMESPACE - -struct { - const char *tag; - QMediaMetaData::Key key; -} gstTagToMetaDataKey[] = { - { GST_TAG_TITLE, QMediaMetaData::Title }, - { GST_TAG_COMMENT, QMediaMetaData::Comment }, - { GST_TAG_DESCRIPTION, QMediaMetaData::Description }, - { GST_TAG_GENRE, QMediaMetaData::Genre }, - { GST_TAG_DATE_TIME, QMediaMetaData::Date }, - { GST_TAG_DATE, QMediaMetaData::Date }, - - { GST_TAG_LANGUAGE_CODE, QMediaMetaData::Language }, - - { GST_TAG_ORGANIZATION, QMediaMetaData::Publisher }, - { GST_TAG_COPYRIGHT, QMediaMetaData::Copyright }, - - // Media - { GST_TAG_DURATION, QMediaMetaData::Duration }, - - // Audio - { GST_TAG_BITRATE, QMediaMetaData::AudioBitRate }, - { GST_TAG_AUDIO_CODEC, QMediaMetaData::AudioCodec }, - - // Music - { GST_TAG_ALBUM, QMediaMetaData::AlbumTitle }, - { GST_TAG_ALBUM_ARTIST, QMediaMetaData::AlbumArtist }, - { GST_TAG_ARTIST, QMediaMetaData::ContributingArtist }, - { GST_TAG_TRACK_NUMBER, QMediaMetaData::TrackNumber }, - - { GST_TAG_PREVIEW_IMAGE, QMediaMetaData::ThumbnailImage }, - { GST_TAG_IMAGE, QMediaMetaData::CoverArtImage }, - - // Image/Video - { "resolution", QMediaMetaData::Resolution }, - { GST_TAG_IMAGE_ORIENTATION, QMediaMetaData::Orientation }, - - // Video - { GST_TAG_VIDEO_CODEC, QMediaMetaData::VideoCodec }, - - // Movie - { GST_TAG_PERFORMER, QMediaMetaData::LeadPerformer }, - - { nullptr, QMediaMetaData::Title } -}; - -static QMediaMetaData::Key tagToKey(const char *tag) -{ - auto *map = gstTagToMetaDataKey; - while (map->tag) { - if (!strcmp(map->tag, tag)) - return map->key; - ++map; - } - return QMediaMetaData::Key(-1); -} - -static const char *keyToTag(QMediaMetaData::Key key) -{ - auto *map = gstTagToMetaDataKey; - while (map->tag) { - if (map->key == key) - return map->tag; - ++map; - } - return nullptr; -} - -//internal -static void addTagToMap(const GstTagList *list, - const gchar *tag, - gpointer user_data) -{ - QMediaMetaData::Key key = tagToKey(tag); - if (key == QMediaMetaData::Key(-1)) - return; - - auto *map = reinterpret_cast<QHash<QMediaMetaData::Key, QVariant>* >(user_data); - - GValue val; - val.g_type = 0; - gst_tag_list_copy_value(&val, list, tag); - - - switch( G_VALUE_TYPE(&val) ) { - case G_TYPE_STRING: - { - const gchar *str_value = g_value_get_string(&val); - if (key == QMediaMetaData::Language) { - map->insert(key, QLocale::codeToLanguage(QString::fromUtf8(str_value))); - break; - } - map->insert(key, QString::fromUtf8(str_value)); - break; - } - case G_TYPE_INT: - map->insert(key, g_value_get_int(&val)); - break; - case G_TYPE_UINT: - map->insert(key, g_value_get_uint(&val)); - break; - case G_TYPE_LONG: - map->insert(key, qint64(g_value_get_long(&val))); - break; - case G_TYPE_BOOLEAN: - map->insert(key, g_value_get_boolean(&val)); - break; - case G_TYPE_CHAR: - map->insert(key, g_value_get_schar(&val)); - break; - case G_TYPE_DOUBLE: - map->insert(key, g_value_get_double(&val)); - break; - default: - // GST_TYPE_DATE is a function, not a constant, so pull it out of the switch - if (G_VALUE_TYPE(&val) == G_TYPE_DATE) { - const GDate *date = (const GDate *)g_value_get_boxed(&val); - if (g_date_valid(date)) { - int year = g_date_get_year(date); - int month = g_date_get_month(date); - int day = g_date_get_day(date); - // don't insert if we already have a datetime. - if (!map->contains(key)) - map->insert(key, QDateTime(QDate(year, month, day), QTime())); - } - } else if (G_VALUE_TYPE(&val) == GST_TYPE_DATE_TIME) { - const GstDateTime *dateTime = (const GstDateTime *)g_value_get_boxed(&val); - int year = gst_date_time_has_year(dateTime) ? gst_date_time_get_year(dateTime) : 0; - int month = gst_date_time_has_month(dateTime) ? gst_date_time_get_month(dateTime) : 0; - int day = gst_date_time_has_day(dateTime) ? gst_date_time_get_day(dateTime) : 0; - int hour = 0; - int minute = 0; - int second = 0; - float tz = 0; - if (gst_date_time_has_time(dateTime)) { - hour = gst_date_time_get_hour(dateTime); - minute = gst_date_time_get_minute(dateTime); - second = gst_date_time_get_second(dateTime); - tz = gst_date_time_get_time_zone_offset(dateTime); - } - QDateTime qDateTime(QDate(year, month, day), QTime(hour, minute, second), - Qt::OffsetFromUTC, tz * 60 * 60); - map->insert(key, qDateTime); - } else if (G_VALUE_TYPE(&val) == GST_TYPE_SAMPLE) { - GstSample *sample = (GstSample *)g_value_get_boxed(&val); - GstCaps* caps = gst_sample_get_caps(sample); - if (caps && !gst_caps_is_empty(caps)) { - GstStructure *structure = gst_caps_get_structure(caps, 0); - const gchar *name = gst_structure_get_name(structure); - if (QByteArray(name).startsWith("image/")) { - GstBuffer *buffer = gst_sample_get_buffer(sample); - if (buffer) { - GstMapInfo info; - gst_buffer_map(buffer, &info, GST_MAP_READ); - map->insert(key, QImage::fromData(info.data, info.size, name)); - gst_buffer_unmap(buffer, &info); - } - } - } - } else if (G_VALUE_TYPE(&val) == GST_TYPE_FRACTION) { - int nom = gst_value_get_fraction_numerator(&val); - int denom = gst_value_get_fraction_denominator(&val); - - if (denom > 0) { - map->insert(key, double(nom)/denom); - } - } - break; - } - - g_value_unset(&val); -} - - -QGstreamerMetaData QGstreamerMetaData::fromGstTagList(const GstTagList *tags) -{ - QGstreamerMetaData m; - gst_tag_list_foreach(tags, addTagToMap, &m.data); - return m; -} - - -void QGstreamerMetaData::setMetaData(GstElement *element) const -{ - if (!GST_IS_TAG_SETTER(element)) - return; - - gst_tag_setter_reset_tags(GST_TAG_SETTER(element)); - - for (auto it = data.cbegin(), end = data.cend(); it != end; ++it) { - const char *tagName = keyToTag(it.key()); - if (!tagName) - continue; - const QVariant &tagValue = it.value(); - - switch (tagValue.typeId()) { - case QMetaType::QString: - gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE, - tagName, - tagValue.toString().toUtf8().constData(), - nullptr); - break; - case QMetaType::Int: - case QMetaType::LongLong: - gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE, - tagName, - tagValue.toInt(), - nullptr); - break; - case QMetaType::Double: - gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE, - tagName, - tagValue.toDouble(), - nullptr); - break; - case QMetaType::QDateTime: { - QDateTime date = tagValue.toDateTime(); - gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE, - tagName, - gst_date_time_new(date.offsetFromUtc() / 60. / 60., - date.date().year(), date.date().month(), date.date().day(), - date.time().hour(), date.time().minute(), date.time().second()), - nullptr); - break; - } - case QMetaType::QLocale: { - QString language = QLocale::languageToCode(tagValue.value<QLocale::Language>()); - gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE, - tagName, - language.toUtf8().constData(), - nullptr); - } - - default: - break; - } - } -} - -void QGstreamerMetaData::setMetaData(GstBin *bin) const -{ - GstIterator *elements = gst_bin_iterate_all_by_interface(bin, GST_TYPE_TAG_SETTER); - GValue item = G_VALUE_INIT; - while (gst_iterator_next(elements, &item) == GST_ITERATOR_OK) { - GstElement * const element = GST_ELEMENT(g_value_get_object(&item)); - setMetaData(element); - } - gst_iterator_free(elements); -} - - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/gstreamer/common/qgstreamermetadata_p.h b/src/multimedia/platform/gstreamer/common/qgstreamermetadata_p.h deleted file mode 100644 index 53b29f548..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstreamermetadata_p.h +++ /dev/null @@ -1,73 +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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERMETADATA_H -#define QGSTREAMERMETADATA_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qmediametadata.h> -#include <qvariant.h> - -#include <gst/gst.h> - -QT_BEGIN_NAMESPACE - -class QGstreamerMetaData : public QMediaMetaData -{ -public: - static QGstreamerMetaData fromGstTagList(const GstTagList *tags); - GstTagList *toGstTagList() const; - - void setMetaData(GstBin *bin) const; - void setMetaData(GstElement *element) const; -}; - -QT_END_NAMESPACE - -#endif // QGSTREAMERMETADATA_H diff --git a/src/multimedia/platform/gstreamer/common/qgstreamervideooutput.cpp b/src/multimedia/platform/gstreamer/common/qgstreamervideooutput.cpp deleted file mode 100644 index 36e0823d8..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstreamervideooutput.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include <private/qgstreamervideooutput_p.h> -#include <private/qgstreamervideosink_p.h> -#include <private/qgstsubtitlesink_p.h> -#include <qvideosink.h> - -#include <QtCore/qloggingcategory.h> -#include <qthread.h> - -Q_LOGGING_CATEGORY(qLcMediaVideoOutput, "qt.multimedia.videooutput") - -QT_BEGIN_NAMESPACE - -QGstreamerVideoOutput::QGstreamerVideoOutput(QObject *parent) - : QObject(parent), - gstVideoOutput("videoOutput") -{ - videoQueue = QGstElement("queue", "videoQueue"); - videoConvert = QGstElement("videoconvert", "videoConvert"); - videoSink = QGstElement("fakesink", "fakeVideoSink"); - gstVideoOutput.add(videoQueue, videoConvert, videoSink); - if (!videoQueue.link(videoConvert, videoSink)) - qCDebug(qLcMediaVideoOutput) << ">>>>>> linking failed"; - - gstVideoOutput.addGhostPad(videoQueue, "sink"); -} - -QGstreamerVideoOutput::~QGstreamerVideoOutput() -{ - gstVideoOutput.setStateSync(GST_STATE_NULL); -} - -void QGstreamerVideoOutput::setVideoSink(QVideoSink *sink) -{ - auto *gstVideoSink = sink ? static_cast<QGstreamerVideoSink *>(sink->platformVideoSink()) : nullptr; - if (gstVideoSink == m_videoSink) - return; - - if (m_videoSink) - m_videoSink->setPipeline({}); - - m_videoSink = gstVideoSink; - if (m_videoSink) - m_videoSink->setPipeline(gstPipeline); - - QGstElement gstSink; - if (m_videoSink) { - gstSink = m_videoSink->gstSink(); - isFakeSink = false; - } else { - gstSink = QGstElement("fakesink", "fakevideosink"); - isFakeSink = true; - } - - if (videoSink == gstSink) - return; - - gstPipeline.beginConfig(); - if (!videoSink.isNull()) { - videoSink.setStateSync(GST_STATE_NULL); - gstVideoOutput.remove(videoSink); - } - videoSink = gstSink; - gstVideoOutput.add(videoSink); - - videoConvert.link(videoSink); - GstEvent *event = gst_event_new_reconfigure(); - gst_element_send_event(videoSink.element(), event); - videoSink.setState(GST_STATE_PAUSED); - - doLinkSubtitleStream(); - - gstPipeline.endConfig(); - - qCDebug(qLcMediaVideoOutput) << "sinkChanged" << gstSink.name(); - - GST_DEBUG_BIN_TO_DOT_FILE(gstPipeline.bin(), - GstDebugGraphDetails(/*GST_DEBUG_GRAPH_SHOW_ALL |*/ GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE | - GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS | GST_DEBUG_GRAPH_SHOW_STATES), - videoSink.name()); - -} - -void QGstreamerVideoOutput::setPipeline(const QGstPipeline &pipeline) -{ - gstPipeline = pipeline; - if (m_videoSink) - m_videoSink->setPipeline(gstPipeline); -} - -void QGstreamerVideoOutput::linkSubtitleStream(QGstElement src) -{ - qCDebug(qLcMediaVideoOutput) << "link subtitle stream" << src.isNull(); - if (src == subtitleSrc) - return; - - gstPipeline.beginConfig(); - subtitleSrc = src; - doLinkSubtitleStream(); - gstPipeline.endConfig(); -} - -void QGstreamerVideoOutput::doLinkSubtitleStream() -{ - if (!subtitleSink.isNull()) { - subtitleSink.setStateSync(GST_STATE_NULL); - gstPipeline.remove(subtitleSink); - subtitleSink = {}; - } - if (!m_videoSink || subtitleSrc.isNull()) - return; - if (subtitleSink.isNull()) { - subtitleSink = m_videoSink->subtitleSink(); - gstPipeline.add(subtitleSink); - } - if (!subtitleSrc.link(subtitleSink)) - qCDebug(qLcMediaVideoOutput) << "link subtitle stream failed"; -} - -void QGstreamerVideoOutput::setIsPreview() -{ - // configures the queue to be fast and lightweight for camera preview - // also avoids blocking the queue in case we have an encodebin attached to the tee as well - videoQueue.set("leaky", 2 /*downstream*/); - videoQueue.set("silent", true); - videoQueue.set("max-size-buffers", 1); - videoQueue.set("max-size-bytes", 0); - videoQueue.set("max-size-time", 0); -} - -void QGstreamerVideoOutput::flushSubtitles() -{ - if (!subtitleSink.isNull()) { - auto pad = subtitleSink.staticPad("sink"); - auto *event = gst_event_new_flush_start(); - pad.sendEvent(event); - event = gst_event_new_flush_stop(false); - pad.sendEvent(event); - } -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/gstreamer/common/qgstreamervideooutput_p.h b/src/multimedia/platform/gstreamer/common/qgstreamervideooutput_p.h deleted file mode 100644 index 41e8da413..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstreamervideooutput_p.h +++ /dev/null @@ -1,106 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERVIDEOOUTPUT_P_H -#define QGSTREAMERVIDEOOUTPUT_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qobject.h> -#include <private/qtmultimediaglobal_p.h> -#include <private/qgst_p.h> -#include <private/qgstpipeline_p.h> -#include <qwaitcondition.h> -#include <qmutex.h> -#include <qpointer.h> -#include <private/qgstreamervideosink_p.h> - -QT_BEGIN_NAMESPACE - -class QVideoSink; - -class Q_MULTIMEDIA_EXPORT QGstreamerVideoOutput : public QObject -{ - Q_OBJECT - -public: - QGstreamerVideoOutput(QObject *parent = 0); - ~QGstreamerVideoOutput(); - - void setVideoSink(QVideoSink *sink); - QGstreamerVideoSink *gstreamerVideoSink() const { return m_videoSink; } - - void setPipeline(const QGstPipeline &pipeline); - - QGstElement gstElement() const { return gstVideoOutput; } - void linkSubtitleStream(QGstElement subtitleSrc); - - void setIsPreview(); - void flushSubtitles(); - -private: - void doLinkSubtitleStream(); - - QPointer<QGstreamerVideoSink> m_videoSink; - bool isFakeSink = true; - - // Gst elements - QGstPipeline gstPipeline; - - QGstBin gstVideoOutput; - QGstElement videoQueue; - QGstElement videoConvert; - QGstElement videoSink; - - QGstElement subtitleSrc; - QGstElement subtitleSink; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/gstreamer/common/qgstreamervideooverlay.cpp b/src/multimedia/platform/gstreamer/common/qgstreamervideooverlay.cpp deleted file mode 100644 index f682e8665..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstreamervideooverlay.cpp +++ /dev/null @@ -1,253 +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$ -** -****************************************************************************/ - -#include "qgstreamervideooverlay_p.h" - -#include <QtGui/qguiapplication.h> -#include "qgstutils_p.h" -#include "private/qgst_p.h" -#include "private/qgstreamermessage_p.h" -#include "private/qgstreamervideosink_p.h" - -#include <gst/video/videooverlay.h> - -#include <QtMultimedia/private/qtmultimediaglobal_p.h> - -QT_BEGIN_NAMESPACE - -struct ElementMap -{ - const char *qtPlatform; - const char *gstreamerElement; -}; - -// Ordered by descending priority -static constexpr ElementMap elementMap[] = -{ - { "xcb", "xvimagesink" }, - { "xcb", "ximagesink" }, - - // wayland - { "wayland", "waylandsink" } -}; - -static bool qt_gst_element_is_functioning(QGstElement element) -{ - GstStateChangeReturn ret = element.setState(GST_STATE_READY); - if (ret == GST_STATE_CHANGE_SUCCESS) { - element.setState(GST_STATE_NULL); - return true; - } - - return false; -} - -static QGstElement findBestVideoSink() -{ - QString platform = QGuiApplication::platformName(); - - // First, try some known video sinks, depending on the Qt platform plugin in use. - for (auto i : elementMap) { - if (platform != QLatin1String(i.qtPlatform)) - continue; - QGstElement choice(i.gstreamerElement, i.gstreamerElement); - if (choice.isNull()) - continue; - - if (qt_gst_element_is_functioning(choice)) - return choice; - } - - // We need a native window ID to use the GstVideoOverlay interface. - // Bail out if the Qt platform plugin in use cannot provide a sensible WId. - if (platform != QLatin1String("xcb") && platform != QLatin1String("wayland")) - return {}; - - QGstElement choice; - // If none of the known video sinks are available, try to find one that implements the - // GstVideoOverlay interface and has autoplugging rank. - GList *list = qt_gst_video_sinks(); - for (GList *item = list; item != nullptr; item = item->next) { - GstElementFactory *f = GST_ELEMENT_FACTORY(item->data); - - if (!gst_element_factory_has_interface(f, "GstVideoOverlay")) - continue; - - choice = QGstElement(gst_element_factory_create(f, nullptr)); - if (choice.isNull()) - continue; - - if (qt_gst_element_is_functioning(choice)) - break; - choice = {}; - } - - gst_plugin_feature_list_free(list); - if (choice.isNull()) - qWarning() << "Could not find a valid windowed video sink"; - - return choice; -} - -QGstreamerVideoOverlay::QGstreamerVideoOverlay(QGstreamerVideoSink *parent, const QByteArray &elementName) - : QObject(parent) - , QGstreamerBufferProbe(QGstreamerBufferProbe::ProbeCaps) - , m_gstreamerVideoSink(parent) -{ - QGstElement sink; - if (!elementName.isEmpty()) - sink = QGstElement(elementName.constData(), nullptr); - else - sink = findBestVideoSink(); - - setVideoSink(sink); -} - -QGstreamerVideoOverlay::~QGstreamerVideoOverlay() -{ - if (!m_videoSink.isNull()) { - QGstPad pad = m_videoSink.staticPad("sink"); - removeProbeFromPad(pad.pad()); - } -} - -QGstElement QGstreamerVideoOverlay::videoSink() const -{ - return m_videoSink; -} - -void QGstreamerVideoOverlay::setVideoSink(QGstElement sink) -{ - if (sink.isNull()) - return; - - m_videoSink = sink; - - QGstPad pad = m_videoSink.staticPad("sink"); - addProbeToPad(pad.pad()); - - auto *klass = G_OBJECT_GET_CLASS(m_videoSink.object()); - m_hasForceAspectRatio = g_object_class_find_property(klass, "force-aspect-ratio"); - m_hasFullscreen = g_object_class_find_property(klass, "fullscreen"); -} - -QSize QGstreamerVideoOverlay::nativeVideoSize() const -{ - return m_nativeVideoSize; -} - -void QGstreamerVideoOverlay::setWindowHandle(WId id) -{ - m_windowId = id; - - if (!m_videoSink.isNull() && GST_IS_VIDEO_OVERLAY(m_videoSink.object())) { - applyRenderRect(); - - // Properties need to be reset when changing the winId. - setAspectRatioMode(m_aspectRatioMode); - setFullScreen(m_fullScreen); - applyRenderRect(); - } -} - -void QGstreamerVideoOverlay::setRenderRectangle(const QRect &rect) -{ - renderRect = rect; - applyRenderRect(); -} - -void QGstreamerVideoOverlay::applyRenderRect() -{ - if (!m_windowId) - return; - - int x = -1; - int y = -1; - int w = -1; - int h = -1; - - if (!renderRect.isEmpty()) { - x = renderRect.x(); - y = renderRect.y(); - w = renderRect.width(); - h = renderRect.height(); - QSize scaledVideo = m_nativeVideoSize.scaled(w, h, m_aspectRatioMode); - x += (w - scaledVideo.width())/2; - y += (h - scaledVideo.height())/2; - w = scaledVideo.width(); - h = scaledVideo.height(); - } - - if (!m_videoSink.isNull() && GST_IS_VIDEO_OVERLAY(m_videoSink.object())) - gst_video_overlay_set_render_rectangle(GST_VIDEO_OVERLAY(m_videoSink.object()), x, y, w, h); -} - -void QGstreamerVideoOverlay::probeCaps(GstCaps *caps) -{ - QSize size = QGstCaps(caps).at(0).resolution(); - if (size != m_nativeVideoSize) { - m_nativeVideoSize = size; - m_gstreamerVideoSink->setNativeSize(m_nativeVideoSize); - applyRenderRect(); - } -} - -void QGstreamerVideoOverlay::setAspectRatioMode(Qt::AspectRatioMode mode) -{ - m_aspectRatioMode = mode; - if (m_hasForceAspectRatio) - m_videoSink.set("force-aspect-ratio", (mode == Qt::KeepAspectRatio)); -} - -void QGstreamerVideoOverlay::setFullScreen(bool fullscreen) -{ - m_fullScreen = fullscreen; - if (m_hasFullscreen) - m_videoSink.set("fullscreen", fullscreen); -} - -bool QGstreamerVideoOverlay::processSyncMessage(const QGstreamerMessage &message) -{ - if (!gst_is_video_overlay_prepare_window_handle_message(message.rawMessage())) - return false; - gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(m_videoSink.object()), m_windowId); - return true; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/gstreamer/common/qgstreamervideooverlay_p.h b/src/multimedia/platform/gstreamer/common/qgstreamervideooverlay_p.h deleted file mode 100644 index 234fc56ff..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstreamervideooverlay_p.h +++ /dev/null @@ -1,110 +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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERVIDEOOVERLAY_P_H -#define QGSTREAMERVIDEOOVERLAY_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qgstpipeline_p.h> -#include <private/qgstreamerbufferprobe_p.h> -#include <private/qgst_p.h> -#include <QtGui/qwindowdefs.h> - -QT_BEGIN_NAMESPACE -class QGstreamerVideoSink; - -class Q_MULTIMEDIA_EXPORT QGstreamerVideoOverlay - : public QObject - , public QGstreamerSyncMessageFilter - , private QGstreamerBufferProbe -{ - Q_OBJECT -public: - explicit QGstreamerVideoOverlay(QGstreamerVideoSink *parent = 0, const QByteArray &elementName = QByteArray()); - virtual ~QGstreamerVideoOverlay(); - - QGstElement videoSink() const; - void setVideoSink(QGstElement); - QSize nativeVideoSize() const; - - void setWindowHandle(WId id); - void setRenderRectangle(const QRect &rect); - - void setAspectRatioMode(Qt::AspectRatioMode mode); - void setFullScreen(bool fullscreen); - - bool processSyncMessage(const QGstreamerMessage &message) override; - - bool isNull() const { return m_videoSink.isNull(); } - -Q_SIGNALS: - void nativeVideoSizeChanged(); - void activeChanged(); - -private: - void probeCaps(GstCaps *caps) override; - void applyRenderRect(); - - QGstreamerVideoSink *m_gstreamerVideoSink = nullptr; - QGstElement m_videoSink; - QSize m_nativeVideoSize; - - bool m_hasForceAspectRatio = false; - bool m_hasFullscreen = false; - Qt::AspectRatioMode m_aspectRatioMode = Qt::KeepAspectRatio; - bool m_fullScreen = false; - - WId m_windowId = 0; - QRect renderRect; -}; - -QT_END_NAMESPACE - -#endif // QGSTREAMERVIDEOOVERLAY_P_H - diff --git a/src/multimedia/platform/gstreamer/common/qgstreamervideosink.cpp b/src/multimedia/platform/gstreamer/common/qgstreamervideosink.cpp deleted file mode 100644 index ca7daf6eb..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstreamervideosink.cpp +++ /dev/null @@ -1,261 +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$ -** -****************************************************************************/ - -#include "qgstreamervideosink_p.h" -#include "private/qgstvideorenderersink_p.h" -#include "private/qgstsubtitlesink_p.h" -#include <private/qgstutils_p.h> -#include <QtGui/private/qrhi_p.h> - -#if QT_CONFIG(gstreamer_gl) -#include <QtGui/private/qrhigles2_p.h> -#include <QGuiApplication> -#include <QtGui/qopenglcontext.h> -#include <QWindow> -#include <qpa/qplatformnativeinterface.h> -#include <gst/gl/gstglconfig.h> - -#if GST_GL_HAVE_WINDOW_X11 -# include <gst/gl/x11/gstgldisplay_x11.h> -#endif -#if GST_GL_HAVE_PLATFORM_EGL -# include <gst/gl/egl/gstgldisplay_egl.h> -# include <EGL/egl.h> -# include <EGL/eglext.h> -#endif -#if GST_GL_HAVE_WINDOW_WAYLAND -# include <gst/gl/wayland/gstgldisplay_wayland.h> -#endif -#endif // #if QT_CONFIG(gstreamer_gl) - -#include <QtCore/qdebug.h> - -#include <QtCore/qloggingcategory.h> - -QT_BEGIN_NAMESPACE - -Q_LOGGING_CATEGORY(qLcMediaVideoSink, "qt.multimedia.videosink") - -QGstreamerVideoSink::QGstreamerVideoSink(QVideoSink *parent) - : QPlatformVideoSink(parent) -{ - sinkBin = QGstBin("videoSinkBin"); - // This is a hack for some iMX platforms. Thos require the use of a special video - // conversion element in the pipeline before the video sink, as they unfortunately - // output some proprietary format from the decoder even though it's marked as - // a regular supported video/x-raw format. - // - // To fix this, simply insert the element into the pipeline if it's available. Otherwise - // we simply use an identity element. - gstQueue = QGstElement("queue"); - auto imxVideoConvert = QGstElement("imxvideoconvert_g2d"); - if (!imxVideoConvert.isNull()) - gstPreprocess = imxVideoConvert; - else - gstPreprocess = QGstElement("identity"); - sinkBin.add(gstQueue, gstPreprocess); - gstQueue.link(gstPreprocess); - sinkBin.addGhostPad(gstQueue, "sink"); - - gstSubtitleSink = GST_ELEMENT(QGstSubtitleSink::createSink(this)); -} - -QGstreamerVideoSink::~QGstreamerVideoSink() -{ - unrefGstContexts(); - - setPipeline(QGstPipeline()); -} - -QGstElement QGstreamerVideoSink::gstSink() -{ - updateSinkElement(); - return sinkBin; -} - -void QGstreamerVideoSink::setPipeline(QGstPipeline pipeline) -{ - gstPipeline = pipeline; -} - -void QGstreamerVideoSink::setRhi(QRhi *rhi) -{ - if (rhi && rhi->backend() != QRhi::OpenGLES2) - rhi = nullptr; - if (m_rhi == rhi) - return; - - m_rhi = rhi; - updateGstContexts(); - if (!gstQtSink.isNull()) { - // force creation of a new sink with proper caps - createQtSink(); - updateSinkElement(); - } -} - -void QGstreamerVideoSink::createQtSink() -{ - gstQtSink = QGstElement(reinterpret_cast<GstElement *>(QGstVideoRendererSink::createSink(this))); -} - -void QGstreamerVideoSink::updateSinkElement() -{ - QGstElement newSink; - if (gstQtSink.isNull()) - createQtSink(); - newSink = gstQtSink; - - if (newSink == gstVideoSink) - return; - - gstPipeline.beginConfig(); - - if (!gstVideoSink.isNull()) { - gstVideoSink.setStateSync(GST_STATE_NULL); - sinkBin.remove(gstVideoSink); - } - - gstVideoSink = newSink; - sinkBin.add(gstVideoSink); - if (!gstPreprocess.link(gstVideoSink)) - qCDebug(qLcMediaVideoSink) << "couldn't link preprocess and sink"; - gstVideoSink.setState(GST_STATE_PAUSED); - - gstPipeline.endConfig(); - gstPipeline.dumpGraph("updateVideoSink"); -} - -void QGstreamerVideoSink::unrefGstContexts() -{ - if (m_gstGlDisplayContext) - gst_context_unref(m_gstGlDisplayContext); - m_gstGlDisplayContext = nullptr; - if (m_gstGlLocalContext) - gst_context_unref(m_gstGlLocalContext); - m_gstGlLocalContext = nullptr; - m_eglDisplay = nullptr; - m_eglImageTargetTexture2D = nullptr; -} - -void QGstreamerVideoSink::updateGstContexts() -{ - unrefGstContexts(); - -#if QT_CONFIG(gstreamer_gl) - if (!m_rhi || m_rhi->backend() != QRhi::OpenGLES2) - return; - - auto *nativeHandles = static_cast<const QRhiGles2NativeHandles *>(m_rhi->nativeHandles()); - auto glContext = nativeHandles->context; - Q_ASSERT(glContext); - - const QString platform = QGuiApplication::platformName(); - QPlatformNativeInterface *pni = QGuiApplication::platformNativeInterface(); - m_eglDisplay = pni->nativeResourceForIntegration("egldisplay"); - qDebug() << "platform is" << platform << m_eglDisplay; - - GstGLDisplay *gstGlDisplay = nullptr; - const char *contextName = "eglcontext"; - GstGLPlatform glPlatform = GST_GL_PLATFORM_EGL; - // use the egl display if we have one - if (m_eglDisplay) { -#if GST_GL_HAVE_PLATFORM_EGL - gstGlDisplay = (GstGLDisplay *)gst_gl_display_egl_new_with_egl_display(m_eglDisplay); - m_eglImageTargetTexture2D = eglGetProcAddress("glEGLImageTargetTexture2DOES"); -#endif - } else { - auto display = pni->nativeResourceForIntegration("display"); - - if (display) { -#if GST_GL_HAVE_WINDOW_X11 - if (platform == QLatin1String("xcb")) { - contextName = "glxcontext"; - glPlatform = GST_GL_PLATFORM_GLX; - - gstGlDisplay = (GstGLDisplay *)gst_gl_display_x11_new_with_display((Display *)display); - } -#endif -#if GST_GL_HAVE_WINDOW_WAYLAND - if (platform.startsWith(QLatin1String("wayland"))) { - Q_ASSERT(!gstGlDisplay); - gstGlDisplay = (GstGLDisplay *)gst_gl_display_wayland_new_with_display((struct wl_display *)display); - } -#endif - } - } - - if (!gstGlDisplay) { - qWarning() << "Could not create GstGLDisplay"; - return; - } - - void *nativeContext = pni->nativeResourceForContext(contextName, glContext); - if (!nativeContext) - qWarning() << "Could not find resource for" << contextName; - - GstGLAPI glApi = QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL ? GST_GL_API_OPENGL : GST_GL_API_GLES2; - GstGLContext *appContext = gst_gl_context_new_wrapped(gstGlDisplay, (guintptr)nativeContext, glPlatform, glApi); - if (!appContext) - qWarning() << "Could not create wrappped context for platform:" << glPlatform; - - GstGLContext *displayContext = nullptr; - GError *error = nullptr; - gst_gl_display_create_context(gstGlDisplay, appContext, &displayContext, &error); - if (error) { - qWarning() << "Could not create display context:" << error->message; - g_clear_error(&error); - } - - if (appContext) - gst_object_unref(appContext); - - m_gstGlDisplayContext = gst_context_new(GST_GL_DISPLAY_CONTEXT_TYPE, false); - gst_context_set_gl_display(m_gstGlDisplayContext, gstGlDisplay); - - m_gstGlLocalContext = gst_context_new("gst.gl.local_context", false); - GstStructure *structure = gst_context_writable_structure(m_gstGlLocalContext); - gst_structure_set(structure, "context", GST_TYPE_GL_CONTEXT, displayContext, nullptr); - - if (!gstPipeline.isNull()) - gst_element_set_context(gstPipeline.element(), m_gstGlLocalContext); -#endif // #if QT_CONFIG(gstreamer_gl) -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/gstreamer/common/qgstreamervideosink_p.h b/src/multimedia/platform/gstreamer/common/qgstreamervideosink_p.h deleted file mode 100644 index a049bc465..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstreamervideosink_p.h +++ /dev/null @@ -1,116 +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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERVIDEOWINDOW_H -#define QGSTREAMERVIDEOWINDOW_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qtmultimediaglobal_p.h> -#include <private/qplatformvideosink_p.h> - -#include <private/qgstpipeline_p.h> -#include <private/qgstreamervideooverlay_p.h> -#include <QtGui/qcolor.h> -#include <qvideosink.h> - -#if QT_CONFIG(gstreamer_gl) -#include <gst/gl/gl.h> -#endif - -QT_BEGIN_NAMESPACE -class QGstreamerVideoRenderer; -class QVideoWindow; - -class Q_MULTIMEDIA_EXPORT QGstreamerVideoSink - : public QPlatformVideoSink -{ - Q_OBJECT -public: - explicit QGstreamerVideoSink(QVideoSink *parent = 0); - ~QGstreamerVideoSink(); - - void setRhi(QRhi *rhi) override; - QRhi *rhi() const { return m_rhi; } - - QGstElement gstSink(); - QGstElement subtitleSink() const { return gstSubtitleSink; } - - void setPipeline(QGstPipeline pipeline); - - GstContext *gstGlDisplayContext() const { return m_gstGlDisplayContext; } - GstContext *gstGlLocalContext() const { return m_gstGlLocalContext; } - Qt::HANDLE eglDisplay() const { return m_eglDisplay; } - QFunctionPointer eglImageTargetTexture2D() const { return m_eglImageTargetTexture2D; } - -private: - void createQtSink(); - void updateSinkElement(); - - void unrefGstContexts(); - void updateGstContexts(); - - QGstPipeline gstPipeline; - QGstBin sinkBin; - QGstElement gstQueue; - QGstElement gstPreprocess; - QGstElement gstVideoSink; - QGstElement gstQtSink; - QGstElement gstSubtitleSink; - - QRhi *m_rhi = nullptr; - - Qt::HANDLE m_eglDisplay = nullptr; - QFunctionPointer m_eglImageTargetTexture2D = nullptr; - GstContext *m_gstGlLocalContext = nullptr; - GstContext *m_gstGlDisplayContext = nullptr; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/gstreamer/common/qgstsubtitlesink.cpp b/src/multimedia/platform/gstreamer/common/qgstsubtitlesink.cpp deleted file mode 100644 index d9b76d57f..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstsubtitlesink.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company -** 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$ -** -****************************************************************************/ - -#include <QDebug> -#include <QThread> -#include <QEvent> - -#include "qgstreamervideosink_p.h" -#include "qgstsubtitlesink_p.h" -#include "qgstutils_p.h" - -QT_BEGIN_NAMESPACE - -static GstBaseSinkClass *sink_parent_class; -static thread_local QGstreamerVideoSink *current_sink; - -#define ST_SINK(s) QGstSubtitleSink *sink(reinterpret_cast<QGstSubtitleSink *>(s)) - -QGstSubtitleSink *QGstSubtitleSink::createSink(QGstreamerVideoSink *sink) -{ - current_sink = sink; - - QGstSubtitleSink *gstSink = reinterpret_cast<QGstSubtitleSink *>( - g_object_new(QGstSubtitleSink::get_type(), nullptr)); - g_object_set(gstSink, "async", false, nullptr); - - return gstSink; -} - -GType QGstSubtitleSink::get_type() -{ - static GType type = 0; - - if (type == 0) { - static const GTypeInfo info = - { - sizeof(QGstSubtitleSinkClass), // class_size - base_init, // base_init - nullptr, // base_finalize - class_init, // class_init - nullptr, // class_finalize - nullptr, // class_data - sizeof(QGstSubtitleSink), // instance_size - 0, // n_preallocs - instance_init, // instance_init - nullptr // value_table - }; - - type = g_type_register_static( - GST_TYPE_BASE_SINK, "QGstSubtitleSink", &info, GTypeFlags(0)); - - // Register the sink type to be used in custom piplines. - // When surface is ready the sink can be used. - gst_element_register(nullptr, "qtsubtitlesink", GST_RANK_PRIMARY, type); - } - - return type; -} - -void QGstSubtitleSink::class_init(gpointer g_class, gpointer class_data) -{ - Q_UNUSED(class_data); - - sink_parent_class = reinterpret_cast<GstBaseSinkClass *>(g_type_class_peek_parent(g_class)); - - GstBaseSinkClass *base_sink_class = reinterpret_cast<GstBaseSinkClass *>(g_class); - base_sink_class->render = QGstSubtitleSink::render; - base_sink_class->get_caps = QGstSubtitleSink::get_caps; - base_sink_class->set_caps = QGstSubtitleSink::set_caps; - base_sink_class->propose_allocation = QGstSubtitleSink::propose_allocation; - base_sink_class->wait_event = QGstSubtitleSink::wait_event; - - GstElementClass *element_class = reinterpret_cast<GstElementClass *>(g_class); - element_class->change_state = QGstSubtitleSink::change_state; - gst_element_class_set_metadata(element_class, - "Qt built-in subtitle sink", - "Sink/Subtitle", - "Qt default built-in subtitle sink", - "The Qt Company"); - - GObjectClass *object_class = reinterpret_cast<GObjectClass *>(g_class); - object_class->finalize = QGstSubtitleSink::finalize; -} - -void QGstSubtitleSink::base_init(gpointer g_class) -{ - static GstStaticPadTemplate sink_pad_template = GST_STATIC_PAD_TEMPLATE( - "sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS("ANY")); - - gst_element_class_add_pad_template( - GST_ELEMENT_CLASS(g_class), gst_static_pad_template_get(&sink_pad_template)); -} - -void QGstSubtitleSink::instance_init(GTypeInstance *instance, gpointer g_class) -{ - Q_UNUSED(g_class); - ST_SINK(instance); - - Q_ASSERT(current_sink); - sink->sink = current_sink; - current_sink = nullptr; -} - -void QGstSubtitleSink::finalize(GObject *object) -{ - // Chain up - G_OBJECT_CLASS(sink_parent_class)->finalize(object); -} - -GstStateChangeReturn QGstSubtitleSink::change_state(GstElement *element, GstStateChange transition) -{ - return GST_ELEMENT_CLASS(sink_parent_class)->change_state(element, transition); -} - -GstCaps *QGstSubtitleSink::get_caps(GstBaseSink *base, GstCaps *filter) -{ - return sink_parent_class->get_caps(base, filter); -} - -gboolean QGstSubtitleSink::set_caps(GstBaseSink *base, GstCaps *caps) -{ - qDebug() << "set_caps:" << QGstCaps(caps).toString(); - return sink_parent_class->set_caps(base, caps); -} - -gboolean QGstSubtitleSink::propose_allocation(GstBaseSink *base, GstQuery *query) -{ - return sink_parent_class->propose_allocation(base, query); -} - -GstFlowReturn QGstSubtitleSink::wait_event(GstBaseSink *base, GstEvent *event) -{ - GstFlowReturn retval = sink_parent_class->wait_event(base, event); - ST_SINK(base); - if (event->type == GST_EVENT_GAP) { -// qDebug() << "gap, clearing subtitle"; - sink->sink->setSubtitleText(QString()); - } - return retval; -} - -GstFlowReturn QGstSubtitleSink::render(GstBaseSink *base, GstBuffer *buffer) -{ - ST_SINK(base); - GstMemory *mem = gst_buffer_get_memory(buffer, 0); - GstMapInfo info; - QString subtitle; - if (gst_memory_map(mem, &info, GST_MAP_READ)) - subtitle = QString::fromUtf8(info.data); - gst_memory_unmap(mem, &info); -// qDebug() << "render" << buffer << subtitle; - sink->sink->setSubtitleText(subtitle); - return GST_FLOW_OK; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/gstreamer/common/qgstsubtitlesink_p.h b/src/multimedia/platform/gstreamer/common/qgstsubtitlesink_p.h deleted file mode 100644 index aa9127c7d..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstsubtitlesink_p.h +++ /dev/null @@ -1,106 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company -** 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$ -** -****************************************************************************/ - -#ifndef QGSTSUBTITLESINK_P_H -#define QGSTSUBTITLESINK_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtMultimedia/private/qtmultimediaglobal_p.h> - -#include <QtCore/qlist.h> -#include <QtCore/qmutex.h> -#include <QtCore/qqueue.h> -#include <QtCore/qpointer.h> -#include <QtCore/qwaitcondition.h> -#include <private/qgst_p.h> -#include <gst/base/gstbasesink.h> - -QT_BEGIN_NAMESPACE - -class QGstreamerVideoSink; - -class Q_MULTIMEDIA_EXPORT QGstSubtitleSink -{ -public: - GstBaseSink parent; - - static QGstSubtitleSink *createSink(QGstreamerVideoSink *sink); - -private: - static GType get_type(); - static void class_init(gpointer g_class, gpointer class_data); - static void base_init(gpointer g_class); - static void instance_init(GTypeInstance *instance, gpointer g_class); - - static void finalize(GObject *object); - - static GstStateChangeReturn change_state(GstElement *element, GstStateChange transition); - - static GstCaps *get_caps(GstBaseSink *sink, GstCaps *filter); - static gboolean set_caps(GstBaseSink *sink, GstCaps *caps); - - static gboolean propose_allocation(GstBaseSink *sink, GstQuery *query); - - static GstFlowReturn wait_event(GstBaseSink * sink, GstEvent * event); - static GstFlowReturn render(GstBaseSink *sink, GstBuffer *buffer); - -private: - QGstreamerVideoSink *sink = nullptr; -}; - - -class QGstSubtitleSinkClass -{ -public: - GstBaseSinkClass parent_class; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/gstreamer/common/qgstutils.cpp b/src/multimedia/platform/gstreamer/common/qgstutils.cpp deleted file mode 100644 index 3a0215e35..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstutils.cpp +++ /dev/null @@ -1,423 +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$ -** -****************************************************************************/ - -#include <QtMultimedia/private/qtmultimediaglobal_p.h> -#include "qgstutils_p.h" - -#include <QtCore/qdatetime.h> -#include <QtCore/qdir.h> -#include <QtCore/qbytearray.h> -#include <QtCore/qvariant.h> -#include <QtCore/qregularexpression.h> -#include <QtCore/qsize.h> -#include <QtCore/qset.h> -#include <QtCore/qstringlist.h> -#include <QtGui/qimage.h> -#include <qaudioformat.h> -#include <QtCore/qelapsedtimer.h> -#include <QtMultimedia/qvideoframeformat.h> -#include <private/qmultimediautils_p.h> - -#include <gst/audio/audio.h> -#include <gst/video/video.h> - -template<typename T, int N> constexpr int lengthOf(const T (&)[N]) { return N; } - -QT_BEGIN_NAMESPACE - - -namespace { - -static const char *audioSampleFormatNames[QAudioFormat::NSampleFormats] = { - nullptr, -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - "U8", - "S16LE", - "S32LE", - "F32LE" -#else - "U8", - "S16BE", - "S32BE", - "F32BE" -#endif -}; - -static QAudioFormat::SampleFormat gstSampleFormatToSampleFormat(const char *fmt) -{ - if (fmt) { - for (int i = 1; i < QAudioFormat::NSampleFormats; ++i) { - if (strcmp(fmt, audioSampleFormatNames[i])) - continue; - return QAudioFormat::SampleFormat(i); - } - } - return QAudioFormat::Unknown; -} - -} - -/* - Returns audio format for a sample \a sample. - If the buffer doesn't have a valid audio format, an empty QAudioFormat is returned. -*/ -QAudioFormat QGstUtils::audioFormatForSample(GstSample *sample) -{ - QGstCaps caps = gst_sample_get_caps(sample); - if (caps.isNull()) - return QAudioFormat(); - return audioFormatForCaps(caps); -} - -QAudioFormat QGstUtils::audioFormatForCaps(QGstCaps caps) -{ - QAudioFormat format; - QGstStructure s = caps.at(0); - if (s.name() != "audio/x-raw") - return format; - - auto rate = s["rate"].toInt(); - auto channels = s["channels"].toInt(); - QAudioFormat::SampleFormat fmt = gstSampleFormatToSampleFormat(s["format"].toString()); - if (!rate || !channels || fmt == QAudioFormat::Unknown) - return format; - - format.setSampleRate(*rate); - format.setChannelCount(*channels); - format.setSampleFormat(fmt); - - return format; -} - -/* - Builds GstCaps for an audio format \a format. - Returns 0 if the audio format is not valid. - - \note Caller must unreference GstCaps. -*/ - -QGstMutableCaps QGstUtils::capsForAudioFormat(const QAudioFormat &format) -{ - if (!format.isValid()) - return {}; - - auto sampleFormat = format.sampleFormat(); - return gst_caps_new_simple( - "audio/x-raw", - "format" , G_TYPE_STRING, audioSampleFormatNames[sampleFormat], - "rate" , G_TYPE_INT , format.sampleRate(), - "channels", G_TYPE_INT , format.channelCount(), - "layout" , G_TYPE_STRING, "interleaved", - nullptr); -} - -QList<QAudioFormat::SampleFormat> QGValue::getSampleFormats() const -{ - if (!GST_VALUE_HOLDS_LIST(value)) - return {}; - - QList<QAudioFormat::SampleFormat> formats; - guint nFormats = gst_value_list_get_size(value); - for (guint f = 0; f < nFormats; ++f) { - QGValue v = gst_value_list_get_value(value, f); - auto *name = v.toString(); - QAudioFormat::SampleFormat fmt = gstSampleFormatToSampleFormat(name); - if (fmt == QAudioFormat::Unknown) - continue;; - formats.append(fmt); - } - return formats; -} - -namespace { - -struct VideoFormat -{ - QVideoFrameFormat::PixelFormat pixelFormat; - GstVideoFormat gstFormat; -}; - -static const VideoFormat qt_videoFormatLookup[] = -{ - { QVideoFrameFormat::Format_YUV420P, GST_VIDEO_FORMAT_I420 }, - { QVideoFrameFormat::Format_YUV422P, GST_VIDEO_FORMAT_Y42B }, - { QVideoFrameFormat::Format_YV12 , GST_VIDEO_FORMAT_YV12 }, - { QVideoFrameFormat::Format_UYVY , GST_VIDEO_FORMAT_UYVY }, - { QVideoFrameFormat::Format_YUYV , GST_VIDEO_FORMAT_YUY2 }, - { QVideoFrameFormat::Format_NV12 , GST_VIDEO_FORMAT_NV12 }, - { QVideoFrameFormat::Format_NV21 , GST_VIDEO_FORMAT_NV21 }, - { QVideoFrameFormat::Format_AYUV , GST_VIDEO_FORMAT_AYUV }, - { QVideoFrameFormat::Format_Y8 , GST_VIDEO_FORMAT_GRAY8 }, - { QVideoFrameFormat::Format_XRGB8888 , GST_VIDEO_FORMAT_xRGB }, - { QVideoFrameFormat::Format_XBGR8888 , GST_VIDEO_FORMAT_xBGR }, - { QVideoFrameFormat::Format_RGBX8888 , GST_VIDEO_FORMAT_RGBx }, - { QVideoFrameFormat::Format_BGRX8888 , GST_VIDEO_FORMAT_BGRx }, - { QVideoFrameFormat::Format_ARGB8888, GST_VIDEO_FORMAT_ARGB }, - { QVideoFrameFormat::Format_ABGR8888, GST_VIDEO_FORMAT_ABGR }, - { QVideoFrameFormat::Format_RGBA8888, GST_VIDEO_FORMAT_RGBA }, - { QVideoFrameFormat::Format_BGRA8888, GST_VIDEO_FORMAT_BGRA }, -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - { QVideoFrameFormat::Format_Y16 , GST_VIDEO_FORMAT_GRAY16_LE }, - { QVideoFrameFormat::Format_P010 , GST_VIDEO_FORMAT_P010_10LE }, -#else - { QVideoFrameFormat::Format_Y16 , GST_VIDEO_FORMAT_GRAY16_BE }, - { QVideoFrameFormat::Format_P010 , GST_VIDEO_FORMAT_P010_10BE }, -#endif -}; - -static int indexOfVideoFormat(QVideoFrameFormat::PixelFormat format) -{ - for (int i = 0; i < lengthOf(qt_videoFormatLookup); ++i) - if (qt_videoFormatLookup[i].pixelFormat == format) - return i; - - return -1; -} - -static int indexOfVideoFormat(GstVideoFormat format) -{ - for (int i = 0; i < lengthOf(qt_videoFormatLookup); ++i) - if (qt_videoFormatLookup[i].gstFormat == format) - return i; - - return -1; -} - -} - -QVideoFrameFormat QGstCaps::formatForCaps(GstVideoInfo *info) const -{ - GstVideoInfo vidInfo; - GstVideoInfo *infoPtr = info ? info : &vidInfo; - - if (gst_video_info_from_caps(infoPtr, caps)) { - int index = indexOfVideoFormat(infoPtr->finfo->format); - - if (index != -1) { - QVideoFrameFormat format( - QSize(infoPtr->width, infoPtr->height), - qt_videoFormatLookup[index].pixelFormat); - - if (infoPtr->fps_d > 0) - format.setFrameRate(qreal(infoPtr->fps_n) / infoPtr->fps_d); - - return format; - } - } - return QVideoFrameFormat(); -} - -void QGstMutableCaps::addPixelFormats(const QList<QVideoFrameFormat::PixelFormat> &formats, const char *modifier) -{ - GValue list = {}; - g_value_init(&list, GST_TYPE_LIST); - - for (QVideoFrameFormat::PixelFormat format : formats) { - int index = indexOfVideoFormat(format); - if (index == -1) - continue; - GValue item = {}; - - g_value_init(&item, G_TYPE_STRING); - g_value_set_string(&item, gst_video_format_to_string(qt_videoFormatLookup[index].gstFormat)); - gst_value_list_append_value(&list, &item); - g_value_unset(&item); - } - QGValue v(&list); - auto *structure = gst_structure_new("video/x-raw", - "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, INT_MAX, 1, - "width" , GST_TYPE_INT_RANGE, 1, INT_MAX, - "height" , GST_TYPE_INT_RANGE, 1, INT_MAX, - nullptr); - gst_structure_set_value(structure, "format", &list); - gst_caps_append_structure(caps, structure); - g_value_unset(&list); - - if (modifier) - gst_caps_set_features(caps, size() - 1, gst_caps_features_from_string(modifier)); -} - -QGstMutableCaps QGstMutableCaps::fromCameraFormat(const QCameraFormat &format) -{ - QGstMutableCaps caps; - caps.create(); - - QSize size = format.resolution(); - GstStructure *structure = nullptr; -// int num = 0; -// int den = 1; -// if (format.maxFrameRate() > 0) -// qt_real_to_fraction(1. / format.maxFrameRate(), &num, &den); -// qDebug() << "fromCameraFormat" << format.maxFrameRate() << num << den; - - if (format.pixelFormat() == QVideoFrameFormat::Format_Jpeg) { - structure = gst_structure_new("image/jpeg", - "width" , G_TYPE_INT, size.width(), - "height" , G_TYPE_INT, size.height(), -// "framerate", GST_TYPE_FRACTION, den, num, - nullptr); - } else { - int index = indexOfVideoFormat(format.pixelFormat()); - if (index < 0) - return QGstMutableCaps(); - auto gstFormat = qt_videoFormatLookup[index].gstFormat; - structure = gst_structure_new("video/x-raw", - "format" , G_TYPE_STRING, gst_video_format_to_string(gstFormat), - "width" , G_TYPE_INT, size.width(), - "height" , G_TYPE_INT, size.height(), -// "framerate", GST_TYPE_FRACTION, den, num, - nullptr); - } - gst_caps_append_structure(caps.caps, structure); - return caps; -} - -void QGstUtils::setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buffer) -{ - // GStreamer uses nanoseconds, Qt uses microseconds - qint64 startTime = GST_BUFFER_TIMESTAMP(buffer); - if (startTime >= 0) { - frame->setStartTime(startTime/G_GINT64_CONSTANT (1000)); - - qint64 duration = GST_BUFFER_DURATION(buffer); - if (duration >= 0) - frame->setEndTime((startTime + duration)/G_GINT64_CONSTANT (1000)); - } -} - -QSize QGstStructure::resolution() const -{ - QSize size; - - int w, h; - if (structure && - gst_structure_get_int(structure, "width", &w) && - gst_structure_get_int(structure, "height", &h)) { - size.rwidth() = w; - size.rheight() = h; - } - - return size; -} - -QVideoFrameFormat::PixelFormat QGstStructure::pixelFormat() const -{ - QVideoFrameFormat::PixelFormat pixelFormat = QVideoFrameFormat::Format_Invalid; - - if (!structure) - return pixelFormat; - - if (gst_structure_has_name(structure, "video/x-raw")) { - const gchar *s = gst_structure_get_string(structure, "format"); - if (s) { - GstVideoFormat format = gst_video_format_from_string(s); - int index = indexOfVideoFormat(format); - - if (index != -1) - pixelFormat = qt_videoFormatLookup[index].pixelFormat; - } - } else if (gst_structure_has_name(structure, "image/jpeg")) { - pixelFormat = QVideoFrameFormat::Format_Jpeg; - } - - return pixelFormat; -} - -QGRange<float> QGstStructure::frameRateRange() const -{ - float minRate = 0.; - float maxRate = 0.; - - if (!structure) - return {0.f, 0.f}; - - auto extractFraction = [] (const GValue *v) -> float { - return (float)gst_value_get_fraction_numerator(v)/(float)gst_value_get_fraction_denominator(v); - }; - auto extractFrameRate = [&] (const GValue *v) { - auto insert = [&] (float min, float max) { - if (max > maxRate) - maxRate = max; - if (min < minRate) - minRate = min; - }; - - if (GST_VALUE_HOLDS_FRACTION(v)) { - float rate = extractFraction(v); - insert(rate, rate); - } else if (GST_VALUE_HOLDS_FRACTION_RANGE(v)) { - auto *min = gst_value_get_fraction_range_max(v); - auto *max = gst_value_get_fraction_range_max(v); - insert(extractFraction(min), extractFraction(max)); - } - }; - - const GValue *gstFrameRates = gst_structure_get_value(structure, "framerate"); - if (gstFrameRates) { - if (GST_VALUE_HOLDS_LIST(gstFrameRates)) { - guint nFrameRates = gst_value_list_get_size(gstFrameRates); - for (guint f = 0; f < nFrameRates; ++f) { - extractFrameRate(gst_value_list_get_value(gstFrameRates, f)); - } - } else { - extractFrameRate(gstFrameRates); - } - } else { - const GValue *min = gst_structure_get_value(structure, "min-framerate"); - const GValue *max = gst_structure_get_value(structure, "max-framerate"); - if (min && max) { - minRate = extractFraction(min); - maxRate = extractFraction(max); - } - } - - return {minRate, maxRate}; -} - -GList *qt_gst_video_sinks() -{ - GList *list = nullptr; - - list = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_SINK | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO, - GST_RANK_MARGINAL); - - return list; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/gstreamer/common/qgstutils_p.h b/src/multimedia/platform/gstreamer/common/qgstutils_p.h deleted file mode 100644 index a0dc51a56..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstutils_p.h +++ /dev/null @@ -1,85 +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$ -** -****************************************************************************/ - -#ifndef QGSTUTILS_P_H -#define QGSTUTILS_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qtmultimediaglobal_p.h> -#include <QtCore/qlist.h> -#include <QtCore/qmap.h> -#include <QtCore/qset.h> -#include <private/qgst_p.h> -#include <gst/video/video.h> -#include <qaudioformat.h> -#include <qcamera.h> -#include <qvideoframe.h> -#include <QDebug> - -QT_BEGIN_NAMESPACE - -class QSize; -class QVariant; -class QByteArray; -class QImage; -class QVideoFrameFormat; - -namespace QGstUtils { - Q_MULTIMEDIA_EXPORT QAudioFormat audioFormatForSample(GstSample *sample); - QAudioFormat audioFormatForCaps(QGstCaps caps); - Q_MULTIMEDIA_EXPORT QGstMutableCaps capsForAudioFormat(const QAudioFormat &format); - - void setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buffer); -} - -Q_MULTIMEDIA_EXPORT GList *qt_gst_video_sinks(); - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/gstreamer/common/qgstvideobuffer.cpp b/src/multimedia/platform/gstreamer/common/qgstvideobuffer.cpp deleted file mode 100644 index 878b4d410..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstvideobuffer.cpp +++ /dev/null @@ -1,373 +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$ -** -****************************************************************************/ - -#include "qgstvideobuffer_p.h" -#include "qgstreamervideosink_p.h" -#include "qvideotexturehelper_p.h" -#include <qpa/qplatformnativeinterface.h> -#include <qguiapplication.h> - -#include <gst/video/video.h> -#include <gst/video/video-frame.h> -#include <gst/video/gstvideometa.h> -#include <gst/pbutils/gstpluginsbaseversion.h> - -#include "qgstutils_p.h" - -#if QT_CONFIG(gstreamer_gl) -#include <QtGui/private/qrhi_p.h> -#include <QtGui/private/qrhigles2_p.h> -#include <QtGui/qopenglcontext.h> -#include <QtGui/qopenglfunctions.h> -#include <QtGui/qopengl.h> - -#include <gst/gl/gstglconfig.h> -#include <gst/gl/gstglmemory.h> -#include <gst/gl/gstglsyncmeta.h> -#include <gst/allocators/gstdmabuf.h> - -#include <EGL/egl.h> -#include <EGL/eglext.h> -#endif - -QT_BEGIN_NAMESPACE - -// keep things building without drm_fourcc.h -#define fourcc_code(a, b, c, d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \ - ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24)) - -#define DRM_FORMAT_RGBA8888 fourcc_code('R', 'A', '2', '4') /* [31:0] R:G:B:A 8:8:8:8 little endian */ -#define DRM_FORMAT_RGB888 fourcc_code('R', 'G', '2', '4') /* [23:0] R:G:B little endian */ -#define DRM_FORMAT_RG88 fourcc_code('R', 'G', '8', '8') /* [15:0] R:G 8:8 little endian */ -#define DRM_FORMAT_ABGR8888 fourcc_code('A', 'B', '2', '4') /* [31:0] A:B:G:R 8:8:8:8 little endian */ -#define DRM_FORMAT_BGR888 fourcc_code('B', 'G', '2', '4') /* [23:0] B:G:R little endian */ -#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */ -#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */ -#define DRM_FORMAT_R16 fourcc_code('R', '1', '6', ' ') /* [15:0] R little endian */ -#define DRM_FORMAT_RGB565 fourcc_code('R', 'G', '1', '6') /* [15:0] R:G:B 5:6:5 little endian */ -#define DRM_FORMAT_RG1616 fourcc_code('R', 'G', '3', '2') /* [31:0] R:G 16:16 little endian */ -#define DRM_FORMAT_GR1616 fourcc_code('G', 'R', '3', '2') /* [31:0] G:R 16:16 little endian */ -#define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0') /* [31:0] B:G:R:A 10:10:10:2 little endian */ - -QGstVideoBuffer::QGstVideoBuffer(GstBuffer *buffer, const GstVideoInfo &info, QGstreamerVideoSink *sink, - const QVideoFrameFormat &frameFormat, - QGstCaps::MemoryFormat format) - : QAbstractVideoBuffer((sink && sink->rhi() && format != QGstCaps::CpuMemory) ? - QVideoFrame::RhiTextureHandle : QVideoFrame::NoHandle, sink ? sink->rhi() : nullptr) - , memoryFormat(format) - , m_frameFormat(frameFormat) - , m_videoInfo(info) - , m_buffer(buffer) -{ - gst_buffer_ref(m_buffer); - if (sink) { - eglDisplay = sink->eglDisplay(); - eglImageTargetTexture2D = sink->eglImageTargetTexture2D(); - } -} - -QGstVideoBuffer::~QGstVideoBuffer() -{ - unmap(); - - gst_buffer_unref(m_buffer); - if (m_syncBuffer) - gst_buffer_unref(m_syncBuffer); - - if (m_ownTextures && glContext) { - int planes = 0; - for (planes = 0; planes < 3; ++planes) { - if (m_textures[planes] == 0) - break; - } -#if QT_CONFIG(gstreamer_gl) - if (rhi) { - rhi->makeThreadLocalNativeContextCurrent(); - QOpenGLFunctions functions(glContext); - functions.glDeleteTextures(planes, m_textures); - } -#endif - } -} - - -QVideoFrame::MapMode QGstVideoBuffer::mapMode() const -{ - return m_mode; -} - -QAbstractVideoBuffer::MapData QGstVideoBuffer::map(QVideoFrame::MapMode mode) -{ - const GstMapFlags flags = GstMapFlags(((mode & QVideoFrame::ReadOnly) ? GST_MAP_READ : 0) - | ((mode & QVideoFrame::WriteOnly) ? GST_MAP_WRITE : 0)); - - MapData mapData; - if (mode == QVideoFrame::NotMapped || m_mode != QVideoFrame::NotMapped) - return mapData; - - if (m_videoInfo.finfo->n_planes == 0) { // Encoded - if (gst_buffer_map(m_buffer, &m_frame.map[0], flags)) { - mapData.nPlanes = 1; - mapData.bytesPerLine[0] = -1; - mapData.size[0] = m_frame.map[0].size; - mapData.data[0] = static_cast<uchar *>(m_frame.map[0].data); - - m_mode = mode; - } - } else if (gst_video_frame_map(&m_frame, &m_videoInfo, m_buffer, flags)) { - mapData.nPlanes = GST_VIDEO_FRAME_N_PLANES(&m_frame); - - for (guint i = 0; i < GST_VIDEO_FRAME_N_PLANES(&m_frame); ++i) { - mapData.bytesPerLine[i] = GST_VIDEO_FRAME_PLANE_STRIDE(&m_frame, i); - mapData.data[i] = static_cast<uchar *>(GST_VIDEO_FRAME_PLANE_DATA(&m_frame, i)); - mapData.size[i] = mapData.bytesPerLine[i]*GST_VIDEO_FRAME_COMP_HEIGHT(&m_frame, i); - } - - m_mode = mode; - } - return mapData; -} - -void QGstVideoBuffer::unmap() -{ - if (m_mode != QVideoFrame::NotMapped) { - if (m_videoInfo.finfo->n_planes == 0) - gst_buffer_unmap(m_buffer, &m_frame.map[0]); - else - gst_video_frame_unmap(&m_frame); - } - m_mode = QVideoFrame::NotMapped; -} - -#if QT_CONFIG(gstreamer_gl) -static int -fourccFromVideoInfo(const GstVideoInfo * info, int plane) -{ - GstVideoFormat format = GST_VIDEO_INFO_FORMAT (info); -#if G_BYTE_ORDER == G_LITTLE_ENDIAN - const gint rgba_fourcc = DRM_FORMAT_ABGR8888; - const gint rgb_fourcc = DRM_FORMAT_BGR888; - const gint rg_fourcc = DRM_FORMAT_GR88; -#else - const gint rgba_fourcc = DRM_FORMAT_RGBA8888; - const gint rgb_fourcc = DRM_FORMAT_RGB888; - const gint rg_fourcc = DRM_FORMAT_RG88; -#endif - - GST_DEBUG ("Getting DRM fourcc for %s plane %i", - gst_video_format_to_string (format), plane); - - switch (format) { - case GST_VIDEO_FORMAT_RGB16: - case GST_VIDEO_FORMAT_BGR16: - return DRM_FORMAT_RGB565; - - case GST_VIDEO_FORMAT_RGB: - case GST_VIDEO_FORMAT_BGR: - return rgb_fourcc; - - case GST_VIDEO_FORMAT_RGBA: - case GST_VIDEO_FORMAT_RGBx: - case GST_VIDEO_FORMAT_BGRA: - case GST_VIDEO_FORMAT_BGRx: - case GST_VIDEO_FORMAT_ARGB: - case GST_VIDEO_FORMAT_xRGB: - case GST_VIDEO_FORMAT_ABGR: - case GST_VIDEO_FORMAT_xBGR: - case GST_VIDEO_FORMAT_AYUV: -#if GST_CHECK_PLUGINS_BASE_VERSION(1,16,0) - case GST_VIDEO_FORMAT_VUYA: -#endif - return rgba_fourcc; - - case GST_VIDEO_FORMAT_GRAY8: - return DRM_FORMAT_R8; - - case GST_VIDEO_FORMAT_YUY2: - case GST_VIDEO_FORMAT_UYVY: - case GST_VIDEO_FORMAT_GRAY16_LE: - case GST_VIDEO_FORMAT_GRAY16_BE: - return rg_fourcc; - - case GST_VIDEO_FORMAT_NV12: - case GST_VIDEO_FORMAT_NV21: - return plane == 0 ? DRM_FORMAT_R8 : rg_fourcc; - - case GST_VIDEO_FORMAT_I420: - case GST_VIDEO_FORMAT_YV12: - case GST_VIDEO_FORMAT_Y41B: - case GST_VIDEO_FORMAT_Y42B: - case GST_VIDEO_FORMAT_Y444: - return DRM_FORMAT_R8; - -#if GST_CHECK_PLUGINS_BASE_VERSION(1,16,0) - case GST_VIDEO_FORMAT_BGR10A2_LE: - return DRM_FORMAT_BGRA1010102; -#endif - -// case GST_VIDEO_FORMAT_RGB10A2_LE: -// return DRM_FORMAT_RGBA1010102; - - case GST_VIDEO_FORMAT_P010_10LE: -// case GST_VIDEO_FORMAT_P012_LE: -// case GST_VIDEO_FORMAT_P016_LE: - return plane == 0 ? DRM_FORMAT_R16 : DRM_FORMAT_GR1616; - - case GST_VIDEO_FORMAT_P010_10BE: -// case GST_VIDEO_FORMAT_P012_BE: -// case GST_VIDEO_FORMAT_P016_BE: - return plane == 0 ? DRM_FORMAT_R16 : DRM_FORMAT_RG1616; - - default: - GST_ERROR ("Unsupported format for DMABuf."); - return -1; - } -} -#endif - -void QGstVideoBuffer::mapTextures() -{ - if (!rhi) - return; - -#if QT_CONFIG(gstreamer_gl) - if (memoryFormat == QGstCaps::GLTexture) { - auto *mem = GST_GL_BASE_MEMORY_CAST(gst_buffer_peek_memory(m_buffer, 0)); - Q_ASSERT(mem); - if (!gst_video_frame_map(&m_frame, &m_videoInfo, m_buffer, GstMapFlags(GST_MAP_READ|GST_MAP_GL))) { - qWarning() << "Could not map GL textures"; - } else { - auto *sync_meta = gst_buffer_get_gl_sync_meta(m_buffer); - - if (!sync_meta) { - m_syncBuffer = gst_buffer_new(); - sync_meta = gst_buffer_add_gl_sync_meta(mem->context, m_syncBuffer); - } - gst_gl_sync_meta_set_sync_point (sync_meta, mem->context); - gst_gl_sync_meta_wait (sync_meta, mem->context); - - int nPlanes = m_frame.info.finfo->n_planes; - for (int i = 0; i < nPlanes; ++i) { - m_textures[i] = *(guint32 *)m_frame.data[i]; - } - gst_video_frame_unmap(&m_frame); - } - } -#if GST_GL_HAVE_PLATFORM_EGL - else if (memoryFormat == QGstCaps::DMABuf) { - if (m_textures[0]) - return; - Q_ASSERT(gst_is_dmabuf_memory(gst_buffer_peek_memory(m_buffer, 0))); - Q_ASSERT(eglDisplay); - Q_ASSERT(eglImageTargetTexture2D); - - auto *nativeHandles = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles()); - glContext = nativeHandles->context; - if (!glContext) { - qWarning() << "no GL context"; - return; - } - - if (!gst_video_frame_map(&m_frame, &m_videoInfo, m_buffer, GstMapFlags(GST_MAP_READ))) { - qDebug() << "Couldn't map DMA video frame"; - return; - } - - int nPlanes = GST_VIDEO_FRAME_N_PLANES(&m_frame); -// int width = GST_VIDEO_FRAME_WIDTH(&m_frame); -// int height = GST_VIDEO_FRAME_HEIGHT(&m_frame); - Q_ASSERT(GST_VIDEO_FRAME_N_PLANES(&m_frame) == gst_buffer_n_memory(m_buffer)); - - QOpenGLFunctions functions(glContext); - functions.glGenTextures(nPlanes, m_textures); - m_ownTextures = true; - -// qDebug() << Qt::hex << "glGenTextures: glerror" << glGetError() << "egl error" << eglGetError(); -// qDebug() << "converting DMA buffer nPlanes=" << nPlanes << m_textures[0] << m_textures[1] << m_textures[2]; - - for (int i = 0; i < nPlanes; ++i) { - auto offset = GST_VIDEO_FRAME_PLANE_OFFSET(&m_frame, i); - auto stride = GST_VIDEO_FRAME_PLANE_STRIDE(&m_frame, i); - int planeWidth = GST_VIDEO_FRAME_COMP_WIDTH(&m_frame, i); - int planeHeight = GST_VIDEO_FRAME_COMP_HEIGHT(&m_frame, i); - auto mem = gst_buffer_peek_memory(m_buffer, i); - int fd = gst_dmabuf_memory_get_fd(mem); - -// qDebug() << " plane" << i << "size" << width << height << "stride" << stride << "offset" << offset << "fd=" << fd; - // ### do we need to open/close the fd? - // ### can we convert several planes at once? - // Get the correct DRM_FORMATs from the texture format in the description - EGLAttrib const attribute_list[] = { - EGL_WIDTH, planeWidth, - EGL_HEIGHT, planeHeight, - EGL_LINUX_DRM_FOURCC_EXT, fourccFromVideoInfo(&m_videoInfo, i), - EGL_DMA_BUF_PLANE0_FD_EXT, fd, - EGL_DMA_BUF_PLANE0_OFFSET_EXT, (EGLAttrib)offset, - EGL_DMA_BUF_PLANE0_PITCH_EXT, stride, - EGL_NONE - }; - EGLImage image = eglCreateImage(eglDisplay, - EGL_NO_CONTEXT, - EGL_LINUX_DMA_BUF_EXT, - nullptr, - attribute_list); - if (image == EGL_NO_IMAGE_KHR) { - qWarning() << "could not create EGL image for plane" << i << Qt::hex << eglGetError(); - } -// qDebug() << Qt::hex << "eglCreateImage: glerror" << glGetError() << "egl error" << eglGetError(); - functions.glBindTexture(GL_TEXTURE_2D, m_textures[i]); -// qDebug() << Qt::hex << "bind texture: glerror" << glGetError() << "egl error" << eglGetError(); - auto EGLImageTargetTexture2D = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglImageTargetTexture2D; - EGLImageTargetTexture2D(GL_TEXTURE_2D, image); -// qDebug() << Qt::hex << "glerror" << glGetError() << "egl error" << eglGetError(); - eglDestroyImage(eglDisplay, image); - } - gst_video_frame_unmap(&m_frame); - } -#endif -#endif - m_texturesUploaded = true; -} - -quint64 QGstVideoBuffer::textureHandle(int plane) const -{ - return m_textures[plane]; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/gstreamer/common/qgstvideobuffer_p.h b/src/multimedia/platform/gstreamer/common/qgstvideobuffer_p.h deleted file mode 100644 index bcd60539b..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstvideobuffer_p.h +++ /dev/null @@ -1,103 +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$ -** -****************************************************************************/ - -#ifndef QGSTVIDEOBUFFER_P_H -#define QGSTVIDEOBUFFER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qtmultimediaglobal_p.h> -#include <private/qabstractvideobuffer_p.h> -#include <QtCore/qvariant.h> - -#include <private/qgst_p.h> -#include <gst/video/video.h> - -QT_BEGIN_NAMESPACE -class QVideoFrameFormat; -class QGstreamerVideoSink; -class QOpenGLContext; - -class Q_MULTIMEDIA_EXPORT QGstVideoBuffer : public QAbstractVideoBuffer -{ -public: - - QGstVideoBuffer(GstBuffer *buffer, const GstVideoInfo &info, QGstreamerVideoSink *sink, - const QVideoFrameFormat &frameFormat, QGstCaps::MemoryFormat format); - QGstVideoBuffer(GstBuffer *buffer, const QVideoFrameFormat &format, const GstVideoInfo &info) - : QGstVideoBuffer(buffer, info, nullptr, format, QGstCaps::CpuMemory) - {} - ~QGstVideoBuffer(); - - GstBuffer *buffer() const { return m_buffer; } - QVideoFrame::MapMode mapMode() const override; - - MapData map(QVideoFrame::MapMode mode) override; - void unmap() override; - - void mapTextures() override; - quint64 textureHandle(int plane) const override; -private: - QGstCaps::MemoryFormat memoryFormat = QGstCaps::CpuMemory; - QVideoFrameFormat m_frameFormat; - mutable GstVideoInfo m_videoInfo; - mutable GstVideoFrame m_frame; - GstBuffer *m_buffer = nullptr; - GstBuffer *m_syncBuffer = nullptr; - QVideoFrame::MapMode m_mode = QVideoFrame::NotMapped; - QOpenGLContext *glContext = nullptr; - Qt::HANDLE eglDisplay = nullptr; - QFunctionPointer eglImageTargetTexture2D = nullptr; - uint m_textures[3] = {}; - bool m_texturesUploaded = false; - bool m_ownTextures = false; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/gstreamer/common/qgstvideorenderersink.cpp b/src/multimedia/platform/gstreamer/common/qgstvideorenderersink.cpp deleted file mode 100644 index a74fa1b4b..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstvideorenderersink.cpp +++ /dev/null @@ -1,601 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Jolla 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$ -** -****************************************************************************/ - -#include <qvideoframe.h> -#include <qvideosink.h> -#include <QDebug> -#include <QMap> -#include <QThread> -#include <QEvent> -#include <QCoreApplication> - -#include <private/qfactoryloader_p.h> -#include "qgstvideobuffer_p.h" -#include "qgstreamervideosink_p.h" - -#include "qgstvideorenderersink_p.h" - -#include <gst/video/video.h> -#include <gst/video/gstvideometa.h> - -#include "qgstutils_p.h" - -#include <QtGui/private/qrhi_p.h> -#if QT_CONFIG(gstreamer_gl) -#include <gst/gl/gl.h> -#endif // #if QT_CONFIG(gstreamer_gl) - -// DMA support -#include <gst/allocators/gstdmabuf.h> - -//#define DEBUG_VIDEO_SURFACE_SINK - -QT_BEGIN_NAMESPACE - -QGstVideoRenderer::QGstVideoRenderer(QGstreamerVideoSink *sink) - : m_sink(sink) -{ - createSurfaceCaps(); -} - -QGstVideoRenderer::~QGstVideoRenderer() -{ -} - -void QGstVideoRenderer::createSurfaceCaps() -{ - QRhi *rhi = m_sink->rhi(); - Q_UNUSED(rhi); - - QGstMutableCaps caps; - caps.create(); - - // All the formats that both we and gstreamer support - auto formats = QList<QVideoFrameFormat::PixelFormat>() - << QVideoFrameFormat::Format_YUV420P - << QVideoFrameFormat::Format_YUV422P - << QVideoFrameFormat::Format_YV12 - << QVideoFrameFormat::Format_UYVY - << QVideoFrameFormat::Format_YUYV - << QVideoFrameFormat::Format_NV12 - << QVideoFrameFormat::Format_NV21 - << QVideoFrameFormat::Format_AYUV - << QVideoFrameFormat::Format_P010 - << QVideoFrameFormat::Format_XRGB8888 - << QVideoFrameFormat::Format_XBGR8888 - << QVideoFrameFormat::Format_RGBX8888 - << QVideoFrameFormat::Format_BGRX8888 - << QVideoFrameFormat::Format_ARGB8888 - << QVideoFrameFormat::Format_ABGR8888 - << QVideoFrameFormat::Format_RGBA8888 - << QVideoFrameFormat::Format_BGRA8888 - << QVideoFrameFormat::Format_Y8 - << QVideoFrameFormat::Format_Y16 - ; -#if QT_CONFIG(gstreamer_gl) - if (rhi && rhi->backend() == QRhi::OpenGLES2) { - caps.addPixelFormats(formats, GST_CAPS_FEATURE_MEMORY_GL_MEMORY); - if (m_sink->eglDisplay() && m_sink->eglImageTargetTexture2D()) { - // We currently do not handle planar DMA buffers, as it's somewhat unclear how to - // convert the planar EGLImage into something we can use from OpenGL - auto singlePlaneFormats = QList<QVideoFrameFormat::PixelFormat>() - << QVideoFrameFormat::Format_UYVY - << QVideoFrameFormat::Format_YUYV - << QVideoFrameFormat::Format_AYUV - << QVideoFrameFormat::Format_XRGB8888 - << QVideoFrameFormat::Format_XBGR8888 - << QVideoFrameFormat::Format_RGBX8888 - << QVideoFrameFormat::Format_BGRX8888 - << QVideoFrameFormat::Format_ARGB8888 - << QVideoFrameFormat::Format_ABGR8888 - << QVideoFrameFormat::Format_RGBA8888 - << QVideoFrameFormat::Format_BGRA8888 - << QVideoFrameFormat::Format_Y8 - << QVideoFrameFormat::Format_Y16 - ; - caps.addPixelFormats(singlePlaneFormats, GST_CAPS_FEATURE_MEMORY_DMABUF); - } - } -#endif - caps.addPixelFormats(formats); - - m_surfaceCaps = caps; -} - -QGstMutableCaps QGstVideoRenderer::caps() -{ - QMutexLocker locker(&m_mutex); - - return m_surfaceCaps; -} - -bool QGstVideoRenderer::start(GstCaps *caps) -{ -// qDebug() << "QGstVideoRenderer::start" << QGstCaps(caps).toString(); - QMutexLocker locker(&m_mutex); - - if (m_active) { - m_flush = true; - m_stop = true; - } - - m_startCaps = QGstMutableCaps(caps, QGstMutableCaps::NeedsRef); - - /* - Waiting for start() to be invoked in the main thread may block - if gstreamer blocks the main thread until this call is finished. - This situation is rare and usually caused by setState(Null) - while pipeline is being prerolled. - - The proper solution to this involves controlling gstreamer pipeline from - other thread than video surface. - - Currently start() fails if wait() timed out. - */ - if (!waitForAsyncEvent(&locker, &m_setupCondition, 1000) && !m_startCaps.isNull()) { - qWarning() << "Failed to start video surface due to main thread blocked."; - m_startCaps = {}; - } - - return m_active; -} - -void QGstVideoRenderer::stop() -{ - QMutexLocker locker(&m_mutex); - - if (!m_active) - return; - - m_flush = true; - m_stop = true; - - m_startCaps = {}; - - waitForAsyncEvent(&locker, &m_setupCondition, 500); -} - -void QGstVideoRenderer::unlock() -{ - QMutexLocker locker(&m_mutex); - - m_setupCondition.wakeAll(); - m_renderCondition.wakeAll(); -} - -bool QGstVideoRenderer::proposeAllocation(GstQuery *query) -{ - Q_UNUSED(query); - QMutexLocker locker(&m_mutex); - return m_active; -} - -void QGstVideoRenderer::flush() -{ - QMutexLocker locker(&m_mutex); - - m_flush = true; - m_renderBuffer = nullptr; - m_renderCondition.wakeAll(); - - notify(); -} - -GstFlowReturn QGstVideoRenderer::render(GstBuffer *buffer) -{ - QMutexLocker locker(&m_mutex); - - m_renderReturn = GST_FLOW_OK; - m_renderBuffer = buffer; - - waitForAsyncEvent(&locker, &m_renderCondition, 300); - - m_renderBuffer = nullptr; - - return m_renderReturn; -} - -bool QGstVideoRenderer::query(GstQuery *query) -{ -#if QT_CONFIG(gstreamer_gl) - if (GST_QUERY_TYPE(query) == GST_QUERY_CONTEXT) { - const gchar *type; - gst_query_parse_context_type(query, &type); - - if (strcmp(type, "gst.gl.local_context") != 0) - return false; - - auto *gstGlContext = m_sink->gstGlLocalContext(); - if (!gstGlContext) - return false; - - gst_query_set_context(query, gstGlContext); - - return true; - } -#else - Q_UNUSED(query); -#endif - return false; -} - -bool QGstVideoRenderer::event(QEvent *event) -{ - if (event->type() == QEvent::UpdateRequest) { - QMutexLocker locker(&m_mutex); - - if (m_notified) { - while (handleEvent(&locker)) {} - m_notified = false; - } - return true; - } - - return QObject::event(event); -} - -bool QGstVideoRenderer::handleEvent(QMutexLocker<QMutex> *locker) -{ - if (m_flush) { - m_flush = false; - if (m_active) { - locker->unlock(); - - if (m_sink && !m_flushed) - m_sink->setVideoFrame(QVideoFrame()); - m_flushed = true; - } - } else if (m_stop) { - m_stop = false; - - if (m_active) { - m_active = false; - m_flushed = true; - } - } else if (!m_startCaps.isNull()) { - Q_ASSERT(!m_active); - - auto startCaps = m_startCaps; - m_startCaps = nullptr; - - if (m_sink) { - locker->unlock(); - - m_flushed = true; - m_format = startCaps.formatForCaps(&m_videoInfo); - memoryFormat = startCaps.memoryFormat(); - - locker->relock(); - m_active = m_format.isValid(); - } else if (m_active) { - m_active = false; - m_flushed = true; - } - - } else if (m_renderBuffer) { - GstBuffer *buffer = m_renderBuffer; - m_renderBuffer = nullptr; - m_renderReturn = GST_FLOW_ERROR; - - if (m_active && m_sink) { - gst_buffer_ref(buffer); - - locker->unlock(); - - m_flushed = false; - - auto meta = gst_buffer_get_video_crop_meta (buffer); - if (meta) { - QRect vp(meta->x, meta->y, meta->width, meta->height); - if (m_format.viewport() != vp) { -#ifdef DEBUG_VIDEO_SURFACE_SINK - qDebug() << Q_FUNC_INFO << " Update viewport on Metadata: [" << meta->height << "x" << meta->width << " | " << meta->x << "x" << meta->y << "]"; -#endif \ - //Update viewport if data is not the same - m_format.setViewport(vp); - } - } - - QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer, m_videoInfo, m_sink, m_format, memoryFormat); - QVideoFrame frame(videoBuffer, m_format); - QGstUtils::setFrameTimeStamps(&frame, buffer); - - m_sink->setVideoFrame(frame); - - gst_buffer_unref(buffer); - - locker->relock(); - - m_renderReturn = GST_FLOW_OK; - } - - m_renderCondition.wakeAll(); - } else { - m_setupCondition.wakeAll(); - - return false; - } - return true; -} - -void QGstVideoRenderer::notify() -{ - if (!m_notified) { - m_notified = true; - QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); - } -} - -bool QGstVideoRenderer::waitForAsyncEvent( - QMutexLocker<QMutex> *locker, QWaitCondition *condition, unsigned long time) -{ - if (QThread::currentThread() == thread()) { - while (handleEvent(locker)) {} - m_notified = false; - - return true; - } - - notify(); - - return condition->wait(&m_mutex, time); -} - -static GstVideoSinkClass *sink_parent_class; -static thread_local QGstreamerVideoSink *current_sink; - -#define VO_SINK(s) QGstVideoRendererSink *sink(reinterpret_cast<QGstVideoRendererSink *>(s)) - -QGstVideoRendererSink *QGstVideoRendererSink::createSink(QGstreamerVideoSink *sink) -{ - setSink(sink); - QGstVideoRendererSink *gstSink = reinterpret_cast<QGstVideoRendererSink *>( - g_object_new(QGstVideoRendererSink::get_type(), nullptr)); - - g_signal_connect(G_OBJECT(gstSink), "notify::show-preroll-frame", G_CALLBACK(handleShowPrerollChange), gstSink); - - return gstSink; -} - -void QGstVideoRendererSink::setSink(QGstreamerVideoSink *sink) -{ - current_sink = sink; - get_type(); -} - -GType QGstVideoRendererSink::get_type() -{ - static GType type = 0; - - if (type == 0) { - static const GTypeInfo info = - { - sizeof(QGstVideoRendererSinkClass), // class_size - base_init, // base_init - nullptr, // base_finalize - class_init, // class_init - nullptr, // class_finalize - nullptr, // class_data - sizeof(QGstVideoRendererSink), // instance_size - 0, // n_preallocs - instance_init, // instance_init - nullptr // value_table - }; - - type = g_type_register_static( - GST_TYPE_VIDEO_SINK, "QGstVideoRendererSink", &info, GTypeFlags(0)); - - // Register the sink type to be used in custom piplines. - // When surface is ready the sink can be used. - gst_element_register(nullptr, "qtvideosink", GST_RANK_PRIMARY, type); - } - - return type; -} - -void QGstVideoRendererSink::class_init(gpointer g_class, gpointer class_data) -{ - Q_UNUSED(class_data); - - sink_parent_class = reinterpret_cast<GstVideoSinkClass *>(g_type_class_peek_parent(g_class)); - - GstVideoSinkClass *video_sink_class = reinterpret_cast<GstVideoSinkClass *>(g_class); - video_sink_class->show_frame = QGstVideoRendererSink::show_frame; - - GstBaseSinkClass *base_sink_class = reinterpret_cast<GstBaseSinkClass *>(g_class); - base_sink_class->get_caps = QGstVideoRendererSink::get_caps; - base_sink_class->set_caps = QGstVideoRendererSink::set_caps; - base_sink_class->propose_allocation = QGstVideoRendererSink::propose_allocation; - base_sink_class->stop = QGstVideoRendererSink::stop; - base_sink_class->unlock = QGstVideoRendererSink::unlock; - base_sink_class->query = QGstVideoRendererSink::query; - - GstElementClass *element_class = reinterpret_cast<GstElementClass *>(g_class); - element_class->change_state = QGstVideoRendererSink::change_state; - gst_element_class_set_metadata(element_class, - "Qt built-in video renderer sink", - "Sink/Video", - "Qt default built-in video renderer sink", - "The Qt Company"); - - GObjectClass *object_class = reinterpret_cast<GObjectClass *>(g_class); - object_class->finalize = QGstVideoRendererSink::finalize; -} - -void QGstVideoRendererSink::base_init(gpointer g_class) -{ - static GstStaticPadTemplate sink_pad_template = GST_STATIC_PAD_TEMPLATE( - "sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS( - "video/x-raw, " - "framerate = (fraction) [ 0, MAX ], " - "width = (int) [ 1, MAX ], " - "height = (int) [ 1, MAX ]")); - - gst_element_class_add_pad_template( - GST_ELEMENT_CLASS(g_class), gst_static_pad_template_get(&sink_pad_template)); -} - -void QGstVideoRendererSink::instance_init(GTypeInstance *instance, gpointer g_class) -{ - Q_UNUSED(g_class); - VO_SINK(instance); - - Q_ASSERT(current_sink); - - sink->renderer = new QGstVideoRenderer(current_sink); - sink->renderer->moveToThread(current_sink->thread()); - current_sink = nullptr; -} - -void QGstVideoRendererSink::finalize(GObject *object) -{ - VO_SINK(object); - - delete sink->renderer; - - // Chain up - G_OBJECT_CLASS(sink_parent_class)->finalize(object); -} - -void QGstVideoRendererSink::handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d) -{ - Q_UNUSED(o); - Q_UNUSED(p); - QGstVideoRendererSink *sink = reinterpret_cast<QGstVideoRendererSink *>(d); - - gboolean showPrerollFrame = true; // "show-preroll-frame" property is true by default - g_object_get(G_OBJECT(sink), "show-preroll-frame", &showPrerollFrame, nullptr); - - if (!showPrerollFrame) { - GstState state = GST_STATE_VOID_PENDING; - GstClockTime timeout = 10000000; // 10 ms - gst_element_get_state(GST_ELEMENT(sink), &state, nullptr, timeout); - // show-preroll-frame being set to 'false' while in GST_STATE_PAUSED means - // the QMediaPlayer was stopped from the paused state. - // We need to flush the current frame. - if (state == GST_STATE_PAUSED) - sink->renderer->flush(); - } -} - -GstStateChangeReturn QGstVideoRendererSink::change_state( - GstElement *element, GstStateChange transition) -{ - QGstVideoRendererSink *sink = reinterpret_cast<QGstVideoRendererSink *>(element); - - gboolean showPrerollFrame = true; // "show-preroll-frame" property is true by default - g_object_get(G_OBJECT(element), "show-preroll-frame", &showPrerollFrame, nullptr); - - // If show-preroll-frame is 'false' when transitioning from GST_STATE_PLAYING to - // GST_STATE_PAUSED, it means the QMediaPlayer was stopped. - // We need to flush the current frame. - if (transition == GST_STATE_CHANGE_PLAYING_TO_PAUSED && !showPrerollFrame) - sink->renderer->flush(); - - return GST_ELEMENT_CLASS(sink_parent_class)->change_state(element, transition); -} - -GstCaps *QGstVideoRendererSink::get_caps(GstBaseSink *base, GstCaps *filter) -{ - VO_SINK(base); - - QGstMutableCaps caps = sink->renderer->caps(); - if (filter) - caps = gst_caps_intersect(caps.get(), filter); - - gst_caps_ref(caps.get()); - return caps.get(); -} - -gboolean QGstVideoRendererSink::set_caps(GstBaseSink *base, GstCaps *caps) -{ - VO_SINK(base); - -#ifdef DEBUG_VIDEO_SURFACE_SINK - qDebug() << "set_caps:"; - qDebug() << caps; -#endif - - if (!caps) { - sink->renderer->stop(); - - return TRUE; - } else if (sink->renderer->start(caps)) { - return TRUE; - } else { - return FALSE; - } -} - -gboolean QGstVideoRendererSink::propose_allocation(GstBaseSink *base, GstQuery *query) -{ - VO_SINK(base); - return sink->renderer->proposeAllocation(query); -} - -gboolean QGstVideoRendererSink::stop(GstBaseSink *base) -{ - VO_SINK(base); - sink->renderer->stop(); - return TRUE; -} - -gboolean QGstVideoRendererSink::unlock(GstBaseSink *base) -{ - VO_SINK(base); - sink->renderer->unlock(); - return TRUE; -} - -GstFlowReturn QGstVideoRendererSink::show_frame(GstVideoSink *base, GstBuffer *buffer) -{ - VO_SINK(base); - return sink->renderer->render(buffer); -} - -gboolean QGstVideoRendererSink::query(GstBaseSink *base, GstQuery *query) -{ - VO_SINK(base); - if (sink->renderer->query(query)) - return TRUE; - - return GST_BASE_SINK_CLASS(sink_parent_class)->query(base, query); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/gstreamer/common/qgstvideorenderersink_p.h b/src/multimedia/platform/gstreamer/common/qgstvideorenderersink_p.h deleted file mode 100644 index 9cbd6b5b2..000000000 --- a/src/multimedia/platform/gstreamer/common/qgstvideorenderersink_p.h +++ /dev/null @@ -1,171 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Jolla 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$ -** -****************************************************************************/ - -#ifndef QGSTVIDEORENDERERSINK_P_H -#define QGSTVIDEORENDERERSINK_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtMultimedia/private/qtmultimediaglobal_p.h> -#include <gst/video/gstvideosink.h> -#include <gst/video/video.h> - -#include <QtCore/qlist.h> -#include <QtCore/qmutex.h> -#include <QtCore/qqueue.h> -#include <QtCore/qpointer.h> -#include <QtCore/qwaitcondition.h> -#include <qvideoframeformat.h> -#include <qvideoframe.h> -#include <private/qgstvideobuffer_p.h> -#include <private/qgst_p.h> - -QT_BEGIN_NAMESPACE -class QVideoSink; - -class QGstVideoRenderer : public QObject -{ - Q_OBJECT -public: - QGstVideoRenderer(QGstreamerVideoSink *sink); - ~QGstVideoRenderer(); - - QGstMutableCaps caps(); - - bool start(GstCaps *caps); - void stop(); - void unlock(); - bool proposeAllocation(GstQuery *query); - - void flush(); - - GstFlowReturn render(GstBuffer *buffer); - - bool event(QEvent *event) override; - bool query(GstQuery *query); - -private slots: - bool handleEvent(QMutexLocker<QMutex> *locker); - -private: - void notify(); - bool waitForAsyncEvent(QMutexLocker<QMutex> *locker, QWaitCondition *condition, unsigned long time); - void createSurfaceCaps(); - - QPointer<QGstreamerVideoSink> m_sink; - - QMutex m_mutex; - QWaitCondition m_setupCondition; - QWaitCondition m_renderCondition; - - // --- accessed from multiple threads, need to hold mutex to access - GstFlowReturn m_renderReturn = GST_FLOW_OK; - bool m_active = false; - - QGstMutableCaps m_surfaceCaps; - - QGstMutableCaps m_startCaps; - GstBuffer *m_renderBuffer = nullptr; - - bool m_notified = false; - bool m_stop = false; - bool m_flush = false; - - // --- only accessed from one thread - QVideoFrameFormat m_format; - GstVideoInfo m_videoInfo; - bool m_flushed = true; - QGstCaps::MemoryFormat memoryFormat = QGstCaps::CpuMemory; -}; - -class Q_MULTIMEDIA_EXPORT QGstVideoRendererSink -{ -public: - GstVideoSink parent; - - static QGstVideoRendererSink *createSink(QGstreamerVideoSink *surface); - static void setSink(QGstreamerVideoSink *surface); - -private: - static GType get_type(); - static void class_init(gpointer g_class, gpointer class_data); - static void base_init(gpointer g_class); - static void instance_init(GTypeInstance *instance, gpointer g_class); - - static void finalize(GObject *object); - - static void handleShowPrerollChange(GObject *o, GParamSpec *p, gpointer d); - - static GstStateChangeReturn change_state(GstElement *element, GstStateChange transition); - - static GstCaps *get_caps(GstBaseSink *sink, GstCaps *filter); - static gboolean set_caps(GstBaseSink *sink, GstCaps *caps); - - static gboolean propose_allocation(GstBaseSink *sink, GstQuery *query); - - static gboolean stop(GstBaseSink *sink); - - static gboolean unlock(GstBaseSink *sink); - - static GstFlowReturn show_frame(GstVideoSink *sink, GstBuffer *buffer); - static gboolean query(GstBaseSink *element, GstQuery *query); - -private: - QGstVideoRenderer *renderer = nullptr; -}; - - -class QGstVideoRendererSinkClass -{ -public: - GstVideoSinkClass parent_class; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/gstreamer/mediacapture/qgstreamercamera.cpp b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercamera.cpp deleted file mode 100644 index 1ee0244ef..000000000 --- a/src/multimedia/platform/gstreamer/mediacapture/qgstreamercamera.cpp +++ /dev/null @@ -1,738 +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$ -** -****************************************************************************/ - -#include <qcameradevice.h> - -#include "qgstreamercamera_p.h" -#include "qgstreamerimagecapture_p.h" -#include <private/qgstreamermediadevices_p.h> -#include <private/qgstreamerintegration_p.h> -#include <qmediacapturesession.h> - -#if QT_CONFIG(linux_v4l) -#include <linux/videodev2.h> -#include <private/qcore_unix_p.h> -#endif - -#include <QtCore/qdebug.h> - -QGstreamerCamera::QGstreamerCamera(QCamera *camera) - : QPlatformCamera(camera) -{ - gstCamera = QGstElement("videotestsrc"); - gstCapsFilter = QGstElement("capsfilter", "videoCapsFilter"); - gstDecode = QGstElement("identity"); - gstVideoConvert = QGstElement("videoconvert", "videoConvert"); - gstVideoScale = QGstElement("videoscale", "videoScale"); - gstCameraBin = QGstBin("camerabin"); - gstCameraBin.add(gstCamera, gstCapsFilter, gstDecode, gstVideoConvert, gstVideoScale); - gstCamera.link(gstCapsFilter, gstDecode, gstVideoConvert, gstVideoScale); - gstCameraBin.addGhostPad(gstVideoScale, "src"); -} - -QGstreamerCamera::~QGstreamerCamera() -{ -#if QT_CONFIG(linux_v4l) - if (v4l2FileDescriptor >= 0) - qt_safe_close(v4l2FileDescriptor); - v4l2FileDescriptor = -1; -#endif - gstCameraBin.setStateSync(GST_STATE_NULL); -} - -bool QGstreamerCamera::isActive() const -{ - return m_active; -} - -void QGstreamerCamera::setActive(bool active) -{ - if (m_active == active) - return; - if (m_cameraDevice.isNull() && active) - return; - - m_active = active; - - emit activeChanged(active); -} - -void QGstreamerCamera::setCamera(const QCameraDevice &camera) -{ - if (m_cameraDevice == camera) - return; - qDebug() << "setCamera" << camera; - - m_cameraDevice = camera; - - QGstElement gstNewCamera; - if (camera.isNull()) { - gstNewCamera = QGstElement("videotestsrc"); - } else { - auto *devices = static_cast<QGstreamerMediaDevices *>(QGstreamerIntegration::instance()->devices()); - auto *device = devices->videoDevice(camera.id()); - gstNewCamera = gst_device_create_element(device, "camerasrc"); - QGstStructure properties = gst_device_get_properties(device); - if (properties.name() == "v4l2deviceprovider") - m_v4l2Device = QString::fromUtf8(properties["device.path"].toString()); - } - - QCameraFormat f = findBestCameraFormat(camera); - auto caps = QGstMutableCaps::fromCameraFormat(f); - auto gstNewDecode = QGstElement(f.pixelFormat() == QVideoFrameFormat::Format_Jpeg ? "jpegdec" : "identity"); - - gstCamera.setStateSync(GST_STATE_NULL); - gstDecode.setStateSync(GST_STATE_NULL); - - gstCamera.unlink(gstCapsFilter); - gstCapsFilter.unlink(gstDecode); - gstDecode.unlink(gstVideoConvert); - - gstCameraBin.remove(gstCamera); - gstCameraBin.remove(gstDecode); - - gstCapsFilter.set("caps", caps); - - gstCameraBin.add(gstNewCamera, gstNewDecode); - - gstNewDecode.link(gstVideoConvert); - gstCapsFilter.link(gstNewDecode); - - if (!gstNewCamera.link(gstCapsFilter)) - qWarning() << "linking camera failed" << gstCamera.name() << caps.toString(); - - // Start sending frames once pipeline is linked - // FIXME: put camera to READY state before linking to decoder as in the NULL state it does not know its true caps - gstCapsFilter.syncStateWithParent(); - gstNewDecode.syncStateWithParent(); - gstNewCamera.syncStateWithParent(); - - gstCamera = gstNewCamera; - gstDecode = gstNewDecode; - - updateCameraProperties(); -} - -bool QGstreamerCamera::setCameraFormat(const QCameraFormat &format) -{ - if (!format.isNull() && !m_cameraDevice.videoFormats().contains(format)) - return false; - - qDebug() << "Set camera format"; - - QCameraFormat f = format; - if (f.isNull()) - f = findBestCameraFormat(m_cameraDevice); - - auto caps = QGstMutableCaps::fromCameraFormat(f); - - auto newGstDecode = QGstElement(f.pixelFormat() == QVideoFrameFormat::Format_Jpeg ? "jpegdec" : "identity"); - gstCameraBin.add(newGstDecode); - newGstDecode.syncStateWithParent(); - - gstCamera.staticPad("src").doInIdleProbe([&](){ - gstCamera.unlink(gstCapsFilter); - gstCapsFilter.unlink(gstDecode); - gstDecode.unlink(gstVideoConvert); - - gstCapsFilter.set("caps", caps); - - newGstDecode.link(gstVideoConvert); - gstCapsFilter.link(newGstDecode); - if (!gstCamera.link(gstCapsFilter)) - qWarning() << "linking filtered camera to decoder failed" << gstCamera.name() << caps.toString(); - }); - - gstDecode.setStateSync(GST_STATE_NULL); - gstCameraBin.remove(gstDecode); - - gstDecode = newGstDecode; - - return true; -} - -void QGstreamerCamera::updateCameraProperties() -{ -#if QT_CONFIG(linux_v4l) - if (isV4L2Camera()) { - initV4L2Controls(); - return; - } -#endif -#if QT_CONFIG(gstreamer_photography) - if (auto *p = photography()) - gst_photography_set_white_balance_mode(p, GST_PHOTOGRAPHY_WB_MODE_AUTO); - QCamera::Features f = QCamera::Feature::ColorTemperature | QCamera::Feature::ExposureCompensation | - QCamera::Feature::IsoSensitivity | QCamera::Feature::ManualExposureTime; - supportedFeaturesChanged(f); -#endif - -} - -#if QT_CONFIG(gstreamer_photography) -GstPhotography *QGstreamerCamera::photography() const -{ - if (!gstCamera.isNull() && GST_IS_PHOTOGRAPHY(gstCamera.element())) - return GST_PHOTOGRAPHY(gstCamera.element()); - return nullptr; -} -#endif - -void QGstreamerCamera::setFocusMode(QCamera::FocusMode mode) -{ - if (mode == focusMode()) - return; - -#if QT_CONFIG(gstreamer_photography) - auto p = photography(); - if (p) { - GstPhotographyFocusMode photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_CONTINUOUS_NORMAL; - - switch (mode) { - case QCamera::FocusModeAutoNear: - photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_MACRO; - break; - case QCamera::FocusModeAutoFar: - // not quite, but hey :) - Q_FALLTHROUGH(); - case QCamera::FocusModeHyperfocal: - photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_HYPERFOCAL; - break; - case QCamera::FocusModeInfinity: - photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_INFINITY; - break; - case QCamera::FocusModeManual: - photographyMode = GST_PHOTOGRAPHY_FOCUS_MODE_MANUAL; - break; - default: // QCamera::FocusModeAuto: - break; - } - - if (gst_photography_set_focus_mode(p, photographyMode)) - focusModeChanged(mode); - } -#endif -} - -bool QGstreamerCamera::isFocusModeSupported(QCamera::FocusMode mode) const -{ -#if QT_CONFIG(gstreamer_photography) - if (photography()) - return true; -#endif - return mode == QCamera::FocusModeAuto; -} - -void QGstreamerCamera::setFlashMode(QCamera::FlashMode mode) -{ - Q_UNUSED(mode); - -#if QT_CONFIG(gstreamer_photography) - if (auto *p = photography()) { - GstPhotographyFlashMode flashMode; - gst_photography_get_flash_mode(p, &flashMode); - - switch (mode) { - case QCamera::FlashAuto: - flashMode = GST_PHOTOGRAPHY_FLASH_MODE_AUTO; - break; - case QCamera::FlashOff: - flashMode = GST_PHOTOGRAPHY_FLASH_MODE_OFF; - break; - case QCamera::FlashOn: - flashMode = GST_PHOTOGRAPHY_FLASH_MODE_ON; - break; - } - - if (gst_photography_set_flash_mode(p, flashMode)) - flashModeChanged(mode); - } -#endif -} - -bool QGstreamerCamera::isFlashModeSupported(QCamera::FlashMode mode) const -{ -#if QT_CONFIG(gstreamer_photography) - if (photography()) - return true; -#endif - - return mode == QCamera::FlashAuto; -} - -bool QGstreamerCamera::isFlashReady() const -{ -#if QT_CONFIG(gstreamer_photography) - if (photography()) - return true; -#endif - - return false; -} - -void QGstreamerCamera::setExposureMode(QCamera::ExposureMode mode) -{ - Q_UNUSED(mode); -#if QT_CONFIG(linux_v4l) - if (isV4L2Camera() && v4l2AutoExposureSupported && v4l2ManualExposureSupported) { - if (mode != QCamera::ExposureAuto && mode != QCamera::ExposureManual) - return; - int value = QCamera::ExposureAuto ? V4L2_EXPOSURE_AUTO : V4L2_EXPOSURE_MANUAL; - setV4L2Parameter(V4L2_CID_EXPOSURE_AUTO, value); - exposureModeChanged(mode); - return; - } -#endif - -#if QT_CONFIG(gstreamer_photography) - auto *p = photography(); - if (!p) - return; - - GstPhotographySceneMode sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_AUTO; - - switch (mode) { - case QCamera::ExposureManual: - sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_MANUAL; - break; - case QCamera::ExposurePortrait: - sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_PORTRAIT; - break; - case QCamera::ExposureSports: - sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_SPORT; - break; - case QCamera::ExposureNight: - sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_NIGHT; - break; - case QCamera::ExposureAuto: - sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_AUTO; - break; - case QCamera::ExposureLandscape: - sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_LANDSCAPE; - break; - case QCamera::ExposureSnow: - sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_SNOW; - break; - case QCamera::ExposureBeach: - sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_BEACH; - break; - case QCamera::ExposureAction: - sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_ACTION; - break; - case QCamera::ExposureNightPortrait: - sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_NIGHT_PORTRAIT; - break; - case QCamera::ExposureTheatre: - sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_THEATRE; - break; - case QCamera::ExposureSunset: - sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_SUNSET; - break; - case QCamera::ExposureSteadyPhoto: - sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_STEADY_PHOTO; - break; - case QCamera::ExposureFireworks: - sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_FIREWORKS; - break; - case QCamera::ExposureParty: - sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_PARTY; - break; - case QCamera::ExposureCandlelight: - sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_CANDLELIGHT; - break; - case QCamera::ExposureBarcode: - sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_BARCODE; - break; - default: - return; - } - - if (gst_photography_set_scene_mode(p, sceneMode)) - exposureModeChanged(mode); -#endif -} - -bool QGstreamerCamera::isExposureModeSupported(QCamera::ExposureMode mode) const -{ - if (mode == QCamera::ExposureAuto) - return true; -#if QT_CONFIG(linux_v4l) - if (isV4L2Camera() && v4l2ManualExposureSupported && v4l2AutoExposureSupported) - return mode == QCamera::ExposureManual; -#endif -#if QT_CONFIG(gstreamer_photography) - if (photography()) - return true; -#endif - - return false; -} - -void QGstreamerCamera::setExposureCompensation(float compensation) -{ - Q_UNUSED(compensation); -#if QT_CONFIG(linux_v4l) - if (isV4L2Camera() && (v4l2MinExposureAdjustment != 0 || v4l2MaxExposureAdjustment != 0)) { - int value = qBound(v4l2MinExposureAdjustment, (int)(compensation*1000), v4l2MaxExposureAdjustment); - setV4L2Parameter(V4L2_CID_AUTO_EXPOSURE_BIAS, value); - exposureCompensationChanged(value/1000.); - return; - } -#endif - -#if QT_CONFIG(gstreamer_photography) - if (auto *p = photography()) { - if (gst_photography_set_ev_compensation(p, compensation)) - exposureCompensationChanged(compensation); - } -#endif -} - -void QGstreamerCamera::setManualIsoSensitivity(int iso) -{ - Q_UNUSED(iso); -#if QT_CONFIG(linux_v4l) - if (isV4L2Camera()) { - if (!(supportedFeatures() & QCamera::Feature::IsoSensitivity)) - return; - setV4L2Parameter(V4L2_CID_ISO_SENSITIVITY_AUTO, iso <= 0 ? V4L2_ISO_SENSITIVITY_AUTO : V4L2_ISO_SENSITIVITY_MANUAL); - if (iso > 0) { - iso = qBound(minIso(), iso, maxIso()); - setV4L2Parameter(V4L2_CID_ISO_SENSITIVITY, iso); - } - return; - } -#endif -#if QT_CONFIG(gstreamer_photography) - if (auto *p = photography()) { - if (gst_photography_set_iso_speed(p, iso)) - isoSensitivityChanged(iso); - } -#endif -} - -int QGstreamerCamera::isoSensitivity() const -{ -#if QT_CONFIG(linux_v4l) - if (isV4L2Camera()) { - if (!(supportedFeatures() & QCamera::Feature::IsoSensitivity)) - return -1; - return getV4L2Parameter(V4L2_CID_ISO_SENSITIVITY); - } -#endif -#if QT_CONFIG(gstreamer_photography) - if (auto *p = photography()) { - guint speed = 0; - if (gst_photography_get_iso_speed(p, &speed)) - return speed; - } -#endif - return 100; -} - -void QGstreamerCamera::setManualExposureTime(float secs) -{ - Q_UNUSED(secs); -#if QT_CONFIG(linux_v4l) - if (isV4L2Camera() && v4l2ManualExposureSupported && v4l2AutoExposureSupported) { - int exposure = qBound(v4l2MinExposure, qRound(secs*10000.), v4l2MaxExposure); - setV4L2Parameter(V4L2_CID_EXPOSURE_ABSOLUTE, exposure); - exposureTimeChanged(exposure/10000.); - return; - } -#endif - -#if QT_CONFIG(gstreamer_photography) - if (auto *p = photography()) { - if (gst_photography_set_exposure(p, guint(secs*1000000))) - exposureTimeChanged(secs); - } -#endif -} - -float QGstreamerCamera::exposureTime() const -{ -#if QT_CONFIG(linux_v4l) - if (isV4L2Camera()) { - return getV4L2Parameter(V4L2_CID_EXPOSURE_ABSOLUTE)/10000.; - } -#endif -#if QT_CONFIG(gstreamer_photography) - if (auto *p = photography()) { - guint32 exposure = 0; - if (gst_photography_get_exposure(p, &exposure)) - return exposure/1000000.; - } -#endif - return -1; -} - -bool QGstreamerCamera::isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const -{ - if (mode == QCamera::WhiteBalanceAuto) - return true; - -#if QT_CONFIG(linux_v4l) - if (isV4L2Camera()) { - if (v4l2AutoWhiteBalanceSupported && v4l2ColorTemperatureSupported) - return true; - } -#endif -#if QT_CONFIG(gstreamer_photography) - if (auto *p = photography()) { - Q_UNUSED(p); - switch (mode) { - case QCamera::WhiteBalanceAuto: - case QCamera::WhiteBalanceSunlight: - case QCamera::WhiteBalanceCloudy: - case QCamera::WhiteBalanceShade: - case QCamera::WhiteBalanceSunset: - case QCamera::WhiteBalanceTungsten: - case QCamera::WhiteBalanceFluorescent: - return true; - case QCamera::WhiteBalanceManual: { -#if GST_CHECK_VERSION(1, 18, 0) - GstPhotographyInterface *iface = GST_PHOTOGRAPHY_GET_INTERFACE(p); - if (iface->set_color_temperature && iface->get_color_temperature) - return true; -#endif - break; - } - default: - break; - } - } -#endif - - return mode == QCamera::WhiteBalanceAuto; -} - -void QGstreamerCamera::setWhiteBalanceMode(QCamera::WhiteBalanceMode mode) -{ - Q_ASSERT(isWhiteBalanceModeSupported(mode)); - -#if QT_CONFIG(linux_v4l) - if (isV4L2Camera()) { - int temperature = colorTemperatureForWhiteBalance(mode); - int t = setV4L2ColorTemperature(temperature); - if (t == 0) - mode = QCamera::WhiteBalanceAuto; - whiteBalanceModeChanged(mode); - return; - } -#endif - -#if QT_CONFIG(gstreamer_photography) - if (auto *p = photography()) { - GstPhotographyWhiteBalanceMode gstMode = GST_PHOTOGRAPHY_WB_MODE_AUTO; - switch (mode) { - case QCamera::WhiteBalanceSunlight: - gstMode = GST_PHOTOGRAPHY_WB_MODE_DAYLIGHT; - break; - case QCamera::WhiteBalanceCloudy: - gstMode = GST_PHOTOGRAPHY_WB_MODE_CLOUDY; - break; - case QCamera::WhiteBalanceShade: - gstMode = GST_PHOTOGRAPHY_WB_MODE_SHADE; - break; - case QCamera::WhiteBalanceSunset: - gstMode = GST_PHOTOGRAPHY_WB_MODE_SUNSET; - break; - case QCamera::WhiteBalanceTungsten: - gstMode = GST_PHOTOGRAPHY_WB_MODE_TUNGSTEN; - break; - case QCamera::WhiteBalanceFluorescent: - gstMode = GST_PHOTOGRAPHY_WB_MODE_FLUORESCENT; - break; - case QCamera::WhiteBalanceAuto: - default: - break; - } - if (gst_photography_set_white_balance_mode(p, gstMode)) { - whiteBalanceModeChanged(mode); - return; - } - } -#endif -} - -void QGstreamerCamera::setColorTemperature(int temperature) -{ - if (temperature == 0) { - setWhiteBalanceMode(QCamera::WhiteBalanceAuto); - return; - } - - Q_ASSERT(isWhiteBalanceModeSupported(QCamera::WhiteBalanceManual)); - -#if QT_CONFIG(linux_v4l) - if (isV4L2Camera()) { - int t = setV4L2ColorTemperature(temperature); - if (t) - colorTemperatureChanged(t); - return; - } -#endif - -#if QT_CONFIG(gstreamer_photography) && GST_CHECK_VERSION(1, 18, 0) - if (auto *p = photography()) { - GstPhotographyInterface *iface = GST_PHOTOGRAPHY_GET_INTERFACE(p); - Q_ASSERT(iface->set_color_temperature); - iface->set_color_temperature(p, temperature); - return; - } -#endif -} - -#if QT_CONFIG(linux_v4l) -void QGstreamerCamera::initV4L2Controls() -{ - v4l2AutoWhiteBalanceSupported = false; - v4l2ColorTemperatureSupported = false; - QCamera::Features features; - - - const QString deviceName = v4l2Device(); - Q_ASSERT(!deviceName.isEmpty()); - - v4l2FileDescriptor = qt_safe_open(deviceName.toLocal8Bit().constData(), O_RDONLY); - if (v4l2FileDescriptor == -1) { - qWarning() << "Unable to open the camera" << deviceName - << "for read to query the parameter info:" << qt_error_string(errno); - return; - } - - struct v4l2_queryctrl queryControl; - ::memset(&queryControl, 0, sizeof(queryControl)); - queryControl.id = V4L2_CID_AUTO_WHITE_BALANCE; - - if (::ioctl(v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) { - v4l2AutoWhiteBalanceSupported = true; - setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE, true); - } - - ::memset(&queryControl, 0, sizeof(queryControl)); - queryControl.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE; - if (::ioctl(v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) { - v4l2MinColorTemp = queryControl.minimum; - v4l2MaxColorTemp = queryControl.maximum; - v4l2ColorTemperatureSupported = true; - features |= QCamera::Feature::ColorTemperature; - } - - ::memset(&queryControl, 0, sizeof(queryControl)); - queryControl.id = V4L2_CID_EXPOSURE_AUTO; - if (::ioctl(v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) { - v4l2AutoExposureSupported = true; - } - - ::memset(&queryControl, 0, sizeof(queryControl)); - queryControl.id = V4L2_CID_EXPOSURE_ABSOLUTE; - if (::ioctl(v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) { - v4l2ManualExposureSupported = true; - v4l2MinExposure = queryControl.minimum; - v4l2MaxExposure = queryControl.maximum; - features |= QCamera::Feature::ManualExposureTime; - } - - ::memset(&queryControl, 0, sizeof(queryControl)); - queryControl.id = V4L2_CID_AUTO_EXPOSURE_BIAS; - if (::ioctl(v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) { - v4l2MinExposureAdjustment = queryControl.minimum; - v4l2MaxExposureAdjustment = queryControl.maximum; - features |= QCamera::Feature::ExposureCompensation; - } - - ::memset(&queryControl, 0, sizeof(queryControl)); - queryControl.id = V4L2_CID_ISO_SENSITIVITY_AUTO; - if (::ioctl(v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) { - queryControl.id = V4L2_CID_ISO_SENSITIVITY; - if (::ioctl(v4l2FileDescriptor, VIDIOC_QUERYCTRL, &queryControl) == 0) { - features |= QCamera::Feature::IsoSensitivity; - minIsoChanged(queryControl.minimum); - maxIsoChanged(queryControl.minimum); - } - } - - supportedFeaturesChanged(features); -} - -int QGstreamerCamera::setV4L2ColorTemperature(int temperature) -{ - struct v4l2_control control; - ::memset(&control, 0, sizeof(control)); - - if (v4l2AutoWhiteBalanceSupported) { - setV4L2Parameter(V4L2_CID_AUTO_WHITE_BALANCE, temperature == 0 ? true : false); - } else if (temperature == 0) { - temperature = 5600; - } - - if (temperature != 0 && v4l2ColorTemperatureSupported) { - temperature = qBound(v4l2MinColorTemp, temperature, v4l2MaxColorTemp); - if (!setV4L2Parameter(V4L2_CID_WHITE_BALANCE_TEMPERATURE, qBound(v4l2MinColorTemp, temperature, v4l2MaxColorTemp))) - temperature = 0; - } else { - temperature = 0; - } - - return temperature; -} - -bool QGstreamerCamera::setV4L2Parameter(quint32 id, qint32 value) -{ - struct v4l2_control control{id, value}; - if (::ioctl(v4l2FileDescriptor, VIDIOC_S_CTRL, &control) != 0) { - qWarning() << "Unable to set the V4L2 Parameter" << Qt::hex << id << "to" << value << qt_error_string(errno); - return false; - } - return true; -} - -int QGstreamerCamera::getV4L2Parameter(quint32 id) const -{ - struct v4l2_control control{id, 0}; - if (::ioctl(v4l2FileDescriptor, VIDIOC_G_CTRL, &control) != 0) { - qWarning() << "Unable to get the V4L2 Parameter" << Qt::hex << id << qt_error_string(errno); - return 0; - } - return control.value; -} - -#endif diff --git a/src/multimedia/platform/gstreamer/mediacapture/qgstreamercamera_p.h b/src/multimedia/platform/gstreamer/mediacapture/qgstreamercamera_p.h deleted file mode 100644 index 94f2f8678..000000000 --- a/src/multimedia/platform/gstreamer/mediacapture/qgstreamercamera_p.h +++ /dev/null @@ -1,138 +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$ -** -****************************************************************************/ - - -#ifndef QGSTREAMERCAMERACONTROL_H -#define QGSTREAMERCAMERACONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QHash> -#include <private/qplatformcamera_p.h> -#include "qgstreamermediacapture_p.h" -#include <private/qgst_p.h> - -QT_BEGIN_NAMESPACE - -class QGstreamerCamera : public QPlatformCamera -{ - Q_OBJECT -public: - QGstreamerCamera(QCamera *camera); - virtual ~QGstreamerCamera(); - - bool isActive() const override; - void setActive(bool active) override; - - void setCamera(const QCameraDevice &camera) override; - bool setCameraFormat(const QCameraFormat &format) override; - - QGstElement gstElement() const { return gstCameraBin.element(); } -#if QT_CONFIG(gstreamer_photography) - GstPhotography *photography() const; -#endif - - void setFocusMode(QCamera::FocusMode mode) override; - bool isFocusModeSupported(QCamera::FocusMode mode) const override; - - void setFlashMode(QCamera::FlashMode mode) override; - bool isFlashModeSupported(QCamera::FlashMode mode) const override; - bool isFlashReady() const override; - - void setExposureMode(QCamera::ExposureMode) override; - bool isExposureModeSupported(QCamera::ExposureMode mode) const override; - void setExposureCompensation(float) override; - void setManualIsoSensitivity(int) override; - int isoSensitivity() const override; - void setManualExposureTime(float) override; - float exposureTime() const override; - - bool isWhiteBalanceModeSupported(QCamera::WhiteBalanceMode mode) const override; - void setWhiteBalanceMode(QCamera::WhiteBalanceMode mode) override; - void setColorTemperature(int temperature) override; - - QString v4l2Device() const { return m_v4l2Device; } - bool isV4L2Camera() const { return !m_v4l2Device.isEmpty(); } - -private: - void updateCameraProperties(); -#if QT_CONFIG(linux_v4l) - void initV4L2Controls(); - int setV4L2ColorTemperature(int temperature); - bool setV4L2Parameter(quint32 id, qint32 value); - int getV4L2Parameter(quint32 id) const; - - bool v4l2AutoWhiteBalanceSupported = false; - bool v4l2ColorTemperatureSupported = false; - bool v4l2AutoExposureSupported = false; - bool v4l2ManualExposureSupported = false; - qint32 v4l2MinColorTemp = 5600; // Daylight... - qint32 v4l2MaxColorTemp = 5600; - qint32 v4l2MinExposure = 0; - qint32 v4l2MaxExposure = 0; - qint32 v4l2MinExposureAdjustment = 0; - qint32 v4l2MaxExposureAdjustment = 0; - int v4l2FileDescriptor = -1; -#endif - - QCameraDevice m_cameraDevice; - - QGstBin gstCameraBin; - QGstElement gstCamera; - QGstElement gstCapsFilter; - QGstElement gstDecode; - QGstElement gstVideoConvert; - QGstElement gstVideoScale; - - bool m_active = false; - QString m_v4l2Device; -}; - -QT_END_NAMESPACE - -#endif // QGSTREAMERCAMERACONTROL_H diff --git a/src/multimedia/platform/gstreamer/mediacapture/qgstreamerimagecapture.cpp b/src/multimedia/platform/gstreamer/mediacapture/qgstreamerimagecapture.cpp deleted file mode 100644 index 2d943def2..000000000 --- a/src/multimedia/platform/gstreamer/mediacapture/qgstreamerimagecapture.cpp +++ /dev/null @@ -1,301 +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$ -** -****************************************************************************/ - -#include "qgstreamerimagecapture_p.h" -#include "qplatformcamera_p.h" -#include <private/qplatformimagecapture_p.h> -#include <private/qgstvideobuffer_p.h> -#include <private/qgstutils_p.h> -#include <private/qgstreamermetadata_p.h> -#include <qvideoframeformat.h> -#include "qmediastoragelocation_p.h" - -#include <QtCore/QDebug> -#include <QtCore/QDir> -#include <qstandardpaths.h> - -#include <qloggingcategory.h> - -QT_BEGIN_NAMESPACE - -Q_LOGGING_CATEGORY(qLcImageCapture, "qt.multimedia.imageCapture") - -QGstreamerImageCapture::QGstreamerImageCapture(QImageCapture *parent) - : QPlatformImageCapture(parent), - QGstreamerBufferProbe(ProbeBuffers) -{ - bin = QGstBin("imageCaptureBin"); - - queue = QGstElement("queue", "imageCaptureQueue"); - // configures the queue to be fast, lightweight and non blocking - queue.set("leaky", 2 /*downstream*/); - queue.set("silent", true); - queue.set("max-size-buffers", 1); - queue.set("max-size-bytes", 0); - queue.set("max-size-time", 0); - - videoConvert = QGstElement("videoconvert", "imageCaptureConvert"); - encoder = QGstElement("jpegenc", "jpegEncoder"); - muxer = QGstElement("jifmux", "jpegMuxer"); - sink = QGstElement("fakesink","imageCaptureSink"); - // imageCaptureSink do not wait for a preroll buffer when going READY -> PAUSED - // as no buffer will arrive until capture() is called - sink.set("async", false); - - bin.add(queue, videoConvert, encoder, muxer, sink); - queue.link(videoConvert, encoder, muxer, sink); - bin.addGhostPad(queue, "sink"); - - addProbeToPad(queue.staticPad("src").pad(), false); - - sink.set("signal-handoffs", true); - g_signal_connect(sink.object(), "handoff", G_CALLBACK(&QGstreamerImageCapture::saveImageFilter), this); -} - -QGstreamerImageCapture::~QGstreamerImageCapture() -{ - bin.setStateSync(GST_STATE_NULL); -} - -bool QGstreamerImageCapture::isReadyForCapture() const -{ - return m_session && !passImage && cameraActive; -} - -int QGstreamerImageCapture::capture(const QString &fileName) -{ - QString path = QMediaStorageLocation::generateFileName(fileName, QStandardPaths::PicturesLocation, QLatin1String("jpg")); - return doCapture(path); -} - -int QGstreamerImageCapture::captureToBuffer() -{ - return doCapture(QString()); -} - -int QGstreamerImageCapture::doCapture(const QString &fileName) -{ - qCDebug(qLcImageCapture) << "do capture"; - if (!m_session) { - //emit error in the next event loop, - //so application can associate it with returned request id. - QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection, - Q_ARG(int, -1), - Q_ARG(int, QImageCapture::ResourceError), - Q_ARG(QString, QPlatformImageCapture::msgImageCaptureNotSet())); - - qCDebug(qLcImageCapture) << "error 1"; - return -1; - } - if (!m_session->camera()) { - //emit error in the next event loop, - //so application can associate it with returned request id. - QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection, - Q_ARG(int, -1), - Q_ARG(int, QImageCapture::ResourceError), - Q_ARG(QString,tr("No camera available."))); - - qCDebug(qLcImageCapture) << "error 2"; - return -1; - } - if (passImage) { - //emit error in the next event loop, - //so application can associate it with returned request id. - QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection, - Q_ARG(int, -1), - Q_ARG(int, QImageCapture::NotReadyError), - Q_ARG(QString, QPlatformImageCapture::msgCameraNotReady())); - - qCDebug(qLcImageCapture) << "error 3"; - return -1; - } - m_lastId++; - - pendingImages.enqueue({m_lastId, fileName, QMediaMetaData{}}); - // let one image pass the pipeline - passImage = true; - - emit readyForCaptureChanged(false); - return m_lastId; -} - -bool QGstreamerImageCapture::probeBuffer(GstBuffer *buffer) -{ - if (!passImage) - return false; - qCDebug(qLcImageCapture) << "probe buffer"; - - passImage = false; - - emit readyForCaptureChanged(isReadyForCapture()); - - QGstCaps caps = gst_pad_get_current_caps(bin.staticPad("sink").pad()); - GstVideoInfo previewInfo; - gst_video_info_from_caps(&previewInfo, caps.get()); - - auto memoryFormat = caps.memoryFormat(); - auto fmt = QGstCaps(caps).formatForCaps(&previewInfo); - auto *sink = m_session->gstreamerVideoSink(); - auto *gstBuffer = new QGstVideoBuffer(buffer, previewInfo, sink, fmt, memoryFormat); - QVideoFrame frame(gstBuffer, fmt); - QImage img = frame.toImage(); - if (img.isNull()) { - qDebug() << "received a null image"; - return true; - } - - auto &imageData = pendingImages.head(); - - emit imageExposed(imageData.id); - - qCDebug(qLcImageCapture) << "Image available!"; - emit imageAvailable(imageData.id, frame); - - emit imageCaptured(imageData.id, img); - - QMediaMetaData metaData = this->metaData(); - metaData.insert(QMediaMetaData::Date, QDateTime::currentDateTime()); - metaData.insert(QMediaMetaData::Resolution, frame.size()); - imageData.metaData = metaData; - - // ensure taginject injects this metaData - const auto &md = static_cast<const QGstreamerMetaData &>(metaData); - md.setMetaData(muxer.element()); - - emit imageMetadataAvailable(imageData.id, metaData); - - return true; -} - -void QGstreamerImageCapture::setCaptureSession(QPlatformMediaCaptureSession *session) -{ - QGstreamerMediaCapture *captureSession = static_cast<QGstreamerMediaCapture *>(session); - if (m_session == captureSession) - return; - - if (m_session) { - disconnect(m_session, nullptr, this, nullptr); - m_lastId = 0; - pendingImages.clear(); - passImage = false; - cameraActive = false; - } - - m_session = captureSession; - if (!m_session) - return; - - connect(m_session, &QPlatformMediaCaptureSession::cameraChanged, this, &QGstreamerImageCapture::onCameraChanged); - onCameraChanged(); -} - -void QGstreamerImageCapture::cameraActiveChanged(bool active) -{ - qCDebug(qLcImageCapture) << "cameraActiveChanged" << cameraActive << active; - if (cameraActive == active) - return; - cameraActive = active; - qCDebug(qLcImageCapture) << "isReady" << isReadyForCapture(); - emit readyForCaptureChanged(isReadyForCapture()); -} - -void QGstreamerImageCapture::onCameraChanged() -{ - if (m_session->camera()) { - cameraActiveChanged(m_session->camera()->isActive()); - connect(m_session->camera(), &QPlatformCamera::activeChanged, this, &QGstreamerImageCapture::cameraActiveChanged); - } - -} - -gboolean QGstreamerImageCapture::saveImageFilter(GstElement *element, - GstBuffer *buffer, - GstPad *pad, - void *appdata) -{ - Q_UNUSED(element); - Q_UNUSED(pad); - QGstreamerImageCapture *capture = static_cast<QGstreamerImageCapture *>(appdata); - - capture->passImage = false; - - if (capture->pendingImages.isEmpty()) { - return true; - } - - auto imageData = capture->pendingImages.dequeue(); - if (imageData.filename.isEmpty()) { - return true; - } - - qCDebug(qLcImageCapture) << "saving image as" << imageData.filename; - - QFile f(imageData.filename); - if (f.open(QFile::WriteOnly)) { - GstMapInfo info; - if (gst_buffer_map(buffer, &info, GST_MAP_READ)) { - f.write(reinterpret_cast<const char *>(info.data), info.size); - gst_buffer_unmap(buffer, &info); - } - f.close(); - - static QMetaMethod savedSignal = QMetaMethod::fromSignal(&QGstreamerImageCapture::imageSaved); - savedSignal.invoke(capture, - Qt::QueuedConnection, - Q_ARG(int, imageData.id), - Q_ARG(QString, imageData.filename)); - } - - return TRUE; -} - -QImageEncoderSettings QGstreamerImageCapture::imageSettings() const -{ - return m_settings; -} - -void QGstreamerImageCapture::setImageSettings(const QImageEncoderSettings &settings) -{ - if (m_settings != settings) { - m_settings = settings; - // ### - } -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/gstreamer/mediacapture/qgstreamerimagecapture_p.h b/src/multimedia/platform/gstreamer/mediacapture/qgstreamerimagecapture_p.h deleted file mode 100644 index a0f5d1a0b..000000000 --- a/src/multimedia/platform/gstreamer/mediacapture/qgstreamerimagecapture_p.h +++ /dev/null @@ -1,121 +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$ -** -****************************************************************************/ - - -#ifndef QGSTREAMERIMAGECAPTURECONTROL_H -#define QGSTREAMERIMAGECAPTURECONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformimagecapture_p.h> -#include "qgstreamermediacapture_p.h" -#include "private/qgstreamerbufferprobe_p.h" - -#include <qqueue.h> - -#include <private/qgst_p.h> -#include <gst/video/video.h> - -QT_BEGIN_NAMESPACE - -class QGstreamerImageCapture : public QPlatformImageCapture, private QGstreamerBufferProbe - -{ - Q_OBJECT -public: - QGstreamerImageCapture(QImageCapture *parent); - virtual ~QGstreamerImageCapture(); - - bool isReadyForCapture() const override; - int capture(const QString &fileName) override; - int captureToBuffer() override; - - QImageEncoderSettings imageSettings() const override; - void setImageSettings(const QImageEncoderSettings &settings) override; - - bool probeBuffer(GstBuffer *buffer) override; - - void setCaptureSession(QPlatformMediaCaptureSession *session); - - QGstElement gstElement() const { return bin.element(); } - -public Q_SLOTS: - void cameraActiveChanged(bool active); - void onCameraChanged(); - -private: - int doCapture(const QString &fileName); - static gboolean saveImageFilter(GstElement *element, GstBuffer *buffer, GstPad *pad, void *appdata); - - QGstreamerMediaCapture *m_session = nullptr; - int m_lastId = 0; - QImageEncoderSettings m_settings; - - struct PendingImage { - int id; - QString filename; - QMediaMetaData metaData; - }; - - QQueue<PendingImage> pendingImages; - - QGstBin bin; - QGstElement queue; - QGstElement videoConvert; - QGstElement encoder; - QGstElement muxer; - QGstElement sink; - QGstPad videoSrcPad; - - bool passImage = false; - bool cameraActive = false; -}; - -QT_END_NAMESPACE - -#endif // QGSTREAMERCAPTURECORNTROL_H diff --git a/src/multimedia/platform/gstreamer/mediacapture/qgstreamermediacapture.cpp b/src/multimedia/platform/gstreamer/mediacapture/qgstreamermediacapture.cpp deleted file mode 100644 index 478eb1c0a..000000000 --- a/src/multimedia/platform/gstreamer/mediacapture/qgstreamermediacapture.cpp +++ /dev/null @@ -1,370 +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$ -** -****************************************************************************/ - -#include "qgstreamermediacapture_p.h" -#include "qgstreamermediaencoder_p.h" -#include "qgstreamerimagecapture_p.h" -#include "qgstreamercamera_p.h" -#include <private/qgstpipeline_p.h> - -#include "private/qgstreameraudioinput_p.h" -#include "private/qgstreameraudiooutput_p.h" -#include "private/qgstreamervideooutput_p.h" - -#include <qloggingcategory.h> - -QT_BEGIN_NAMESPACE - -Q_LOGGING_CATEGORY(qLcMediaCapture, "qt.multimedia.capture") - - -static void linkTeeToPad(QGstElement tee, QGstPad sink) -{ - if (tee.isNull() || sink.isNull()) - return; - - auto source = tee.getRequestPad("src_%u"); - source.link(sink); -} - -static void unlinkTeeFromPad(QGstElement tee, QGstPad sink) -{ - if (tee.isNull() || sink.isNull()) - return; - - auto source = sink.peer(); - source.unlink(sink); - - tee.releaseRequestPad(source); -} - - -QGstreamerMediaCapture::QGstreamerMediaCapture() - : gstPipeline("pipeline") -{ - gstVideoOutput = new QGstreamerVideoOutput(this); - gstVideoOutput->setIsPreview(); - gstVideoOutput->setPipeline(gstPipeline); - - // Use system clock to drive all elements in the pipeline. Otherwise, - // the clock is sourced from the elements (e.g. from an audio source). - // Since the elements are added and removed dynamically the clock would - // also change causing lost of synchronization in the pipeline. - gst_pipeline_use_clock(gstPipeline.pipeline(), gst_system_clock_obtain()); - - // This is the recording pipeline with only live sources, thus the pipeline - // will be always in the playing state. - gstPipeline.setState(GST_STATE_PLAYING); - - // TODO: remove this debug before final commit - // Four basic elements of the capture session showing the current position and state - // All presented position should show similar progress. - heartbeat.setInterval(1000); - heartbeat.start(); - QObject::connect(&heartbeat, &QTimer::timeout, [this]() { - if (!gstPipeline.isNull()) { - gint64 current = -1; - gst_element_query_position(gstPipeline.element(),GST_FORMAT_TIME, ¤t); - qDebug() << "Pipeline " << current / 1000000 << gstPipeline.state(); -// auto name = QString::number(current).toLocal8Bit().data(); -// gstPipeline.dumpGraph(name); - } - if (gstAudioInput && !gstAudioInput->gstElement().isNull()) { - gint64 current = -1; - auto element = gstAudioInput->gstElement().element(); - gst_element_query_position(element,GST_FORMAT_TIME, ¤t); - qDebug() << "Audio " << current / 1000000 << gstAudioInput->gstElement().state(); - } - if (gstCamera && !gstCamera->gstElement().isNull()) { - gint64 current = -1; - gst_element_query_position(gstCamera->gstElement().element(),GST_FORMAT_TIME, ¤t); - qDebug() << "Camera " << current / 1000000 << gstCamera->gstElement().state(); - } - auto encoder = !m_mediaEncoder ? QGstElement{} : m_mediaEncoder->getEncoder(); - if (!encoder.isNull()) { - gint64 current = -1; - gst_element_query_position(encoder.element(),GST_FORMAT_TIME, ¤t); - qDebug() << "Encoder " << current / 1000000 << encoder.state(); - } - }); - - gstPipeline.dumpGraph("initial"); -} - -QGstreamerMediaCapture::~QGstreamerMediaCapture() -{ - setMediaEncoder(nullptr); - setImageCapture(nullptr); - setCamera(nullptr); - gstPipeline.setStateSync(GST_STATE_NULL); -} - -QPlatformCamera *QGstreamerMediaCapture::camera() -{ - return gstCamera; -} - -void QGstreamerMediaCapture::setCamera(QPlatformCamera *camera) -{ - QGstreamerCamera *control = static_cast<QGstreamerCamera *>(camera); - if (gstCamera == control) - return; - - if (gstCamera) { - unlinkTeeFromPad(gstVideoTee, encoderVideoSink); - unlinkTeeFromPad(gstVideoTee, imageCaptureSink); - - auto camera = gstCamera->gstElement(); - camera.setStateSync(GST_STATE_NULL); - gstVideoTee.setStateSync(GST_STATE_NULL); - gstVideoOutput->gstElement().setStateSync(GST_STATE_NULL); - - gstPipeline.remove(camera); - gstPipeline.remove(gstVideoTee); - gstPipeline.remove(gstVideoOutput->gstElement()); - - gstVideoTee = {}; - gstCamera->setCaptureSession(nullptr); - } - - gstCamera = control; - if (gstCamera) { - QGstElement camera = gstCamera->gstElement(); - gstVideoTee = QGstElement("tee", "videotee"); - gstVideoTee.set("allow-not-linked", true); - - gstPipeline.add(gstVideoOutput->gstElement(), camera, gstVideoTee); - - linkTeeToPad(gstVideoTee, encoderVideoSink); - linkTeeToPad(gstVideoTee, gstVideoOutput->gstElement().staticPad("sink")); - linkTeeToPad(gstVideoTee, imageCaptureSink); - - camera.link(gstVideoTee); - - gstVideoOutput->gstElement().setState(GST_STATE_PLAYING); - gstVideoTee.setState(GST_STATE_PLAYING); - camera.setState(GST_STATE_PLAYING); - } - - gstPipeline.dumpGraph("camera"); - - emit cameraChanged(); -} - -QPlatformImageCapture *QGstreamerMediaCapture::imageCapture() -{ - return m_imageCapture; -} - -void QGstreamerMediaCapture::setImageCapture(QPlatformImageCapture *imageCapture) -{ - QGstreamerImageCapture *control = static_cast<QGstreamerImageCapture *>(imageCapture); - if (m_imageCapture == control) - return; - - if (m_imageCapture) { - unlinkTeeFromPad(gstVideoTee, imageCaptureSink); - m_imageCapture->gstElement().setStateSync(GST_STATE_NULL); - gstPipeline.remove(m_imageCapture->gstElement()); - imageCaptureSink = {}; - m_imageCapture->setCaptureSession(nullptr); - } - - m_imageCapture = control; - if (m_imageCapture) { - imageCaptureSink = m_imageCapture->gstElement().staticPad("sink"); - m_imageCapture->gstElement().setState(GST_STATE_PLAYING); - gstPipeline.add(m_imageCapture->gstElement()); - linkTeeToPad(gstVideoTee, imageCaptureSink); - m_imageCapture->setCaptureSession(this); - } - - gstPipeline.dumpGraph("imageCapture"); - - emit imageCaptureChanged(); -} - -void QGstreamerMediaCapture::setMediaEncoder(QPlatformMediaEncoder *encoder) -{ - QGstreamerMediaEncoder *control = static_cast<QGstreamerMediaEncoder *>(encoder); - if (m_mediaEncoder == control) - return; - - if (m_mediaEncoder) - m_mediaEncoder->setCaptureSession(nullptr); - m_mediaEncoder = control; - if (m_mediaEncoder) - m_mediaEncoder->setCaptureSession(this); - - emit encoderChanged(); - gstPipeline.dumpGraph("encoder"); -} - -QPlatformMediaEncoder *QGstreamerMediaCapture::mediaEncoder() -{ - return m_mediaEncoder; -} - -void QGstreamerMediaCapture::linkEncoder(QGstPad audioSink, QGstPad videoSink) -{ - if (!gstVideoTee.isNull() && !videoSink.isNull()) { - auto caps = gst_pad_get_current_caps(gstVideoTee.sink().pad()); - - encoderVideoCapsFilter = QGstElement("capsfilter", "encoderVideoCapsFilter"); - encoderVideoCapsFilter.set("caps", QGstMutableCaps(caps)); - - gstPipeline.add(encoderVideoCapsFilter); - - encoderVideoCapsFilter.src().link(videoSink); - linkTeeToPad(gstVideoTee, encoderVideoCapsFilter.sink()); - encoderVideoCapsFilter.setState(GST_STATE_PLAYING); - encoderVideoSink = encoderVideoCapsFilter.sink(); - } - - if (!gstAudioTee.isNull() && !audioSink.isNull()) { - auto caps = gst_pad_get_current_caps(gstAudioTee.sink().pad()); - - encoderAudioCapsFilter = QGstElement("capsfilter", "encoderAudioCapsFilter"); - encoderAudioCapsFilter.set("caps", QGstMutableCaps(caps)); - - gstPipeline.add(encoderAudioCapsFilter); - - encoderAudioCapsFilter.src().link(audioSink); - linkTeeToPad(gstAudioTee, encoderAudioCapsFilter.sink()); - encoderAudioCapsFilter.setState(GST_STATE_PLAYING); - encoderAudioSink = encoderAudioCapsFilter.sink(); - } -} - -void QGstreamerMediaCapture::unlinkEncoder() -{ - if (!encoderVideoCapsFilter.isNull()) { - encoderVideoCapsFilter.src().unlinkPeer(); - unlinkTeeFromPad(gstVideoTee, encoderVideoCapsFilter.sink()); - encoderVideoCapsFilter.setStateSync(GST_STATE_NULL); - gstPipeline.remove(encoderVideoCapsFilter); - encoderVideoCapsFilter = {}; - } - - if (!encoderAudioCapsFilter.isNull()) { - encoderAudioCapsFilter.src().unlinkPeer(); - unlinkTeeFromPad(gstAudioTee, encoderAudioCapsFilter.sink()); - encoderAudioCapsFilter.setStateSync(GST_STATE_NULL); - gstPipeline.remove(encoderAudioCapsFilter); - encoderAudioCapsFilter = {}; - } - - encoderAudioSink = {}; - encoderVideoSink = {}; -} - -void QGstreamerMediaCapture::setAudioInput(QPlatformAudioInput *input) -{ - if (gstAudioInput == input) - return; - - if (gstAudioInput) { - unlinkTeeFromPad(gstAudioTee, encoderAudioSink); - - if (gstAudioOutput) { - unlinkTeeFromPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink")); - gstAudioOutput->gstElement().setStateSync(GST_STATE_NULL); - gstPipeline.remove(gstAudioOutput->gstElement()); - } - - gstAudioInput->gstElement().setStateSync(GST_STATE_NULL); - gstPipeline.remove(gstAudioInput->gstElement()); - gstAudioTee.setStateSync(GST_STATE_NULL); - gstPipeline.remove(gstAudioTee); - gstAudioTee = {}; - } - - gstAudioInput = static_cast<QGstreamerAudioInput *>(input); - if (gstAudioInput) { - Q_ASSERT(gstAudioTee.isNull()); - gstAudioTee = QGstElement("tee", "audiotee"); - gstAudioTee.set("allow-not-linked", true); - gstPipeline.add(gstAudioInput->gstElement(), gstAudioTee); - gstAudioInput->gstElement().link(gstAudioTee); - - if (gstAudioOutput) { - gstPipeline.add(gstAudioOutput->gstElement()); - gstAudioOutput->gstElement().setState(GST_STATE_PLAYING); - linkTeeToPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink")); - } - - gstAudioTee.setState(GST_STATE_PLAYING); - gstAudioInput->gstElement().setStateSync(GST_STATE_PLAYING); - - linkTeeToPad(gstAudioTee, encoderAudioSink); - } -} - -void QGstreamerMediaCapture::setVideoPreview(QVideoSink *sink) -{ - gstVideoOutput->setVideoSink(sink); -} - -void QGstreamerMediaCapture::setAudioOutput(QPlatformAudioOutput *output) -{ - if (gstAudioOutput == output) - return; - - if (gstAudioOutput && gstAudioInput) { - // If audio input is set, the output is in the pipeline - unlinkTeeFromPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink")); - gstAudioOutput->gstElement().setStateSync(GST_STATE_NULL); - gstPipeline.remove(gstAudioOutput->gstElement()); - } - - gstAudioOutput = static_cast<QGstreamerAudioOutput *>(output); - if (gstAudioOutput && gstAudioInput) { - gstPipeline.add(gstAudioOutput->gstElement()); - gstAudioOutput->gstElement().setState(GST_STATE_PLAYING); - linkTeeToPad(gstAudioTee, gstAudioOutput->gstElement().staticPad("sink")); - } -} - -QGstreamerVideoSink *QGstreamerMediaCapture::gstreamerVideoSink() const -{ - return gstVideoOutput ? gstVideoOutput->gstreamerVideoSink() : nullptr; -} - - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/gstreamer/mediacapture/qgstreamermediacapture_p.h b/src/multimedia/platform/gstreamer/mediacapture/qgstreamermediacapture_p.h deleted file mode 100644 index ed75a27d1..000000000 --- a/src/multimedia/platform/gstreamer/mediacapture/qgstreamermediacapture_p.h +++ /dev/null @@ -1,129 +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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERCAPTURESERVICE_H -#define QGSTREAMERCAPTURESERVICE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediacapture_p.h> -#include <private/qplatformmediaintegration_p.h> - -#include <private/qgst_p.h> -#include <private/qgstpipeline_p.h> - -#include <qtimer.h> - -QT_BEGIN_NAMESPACE - -class QGstreamerCamera; -class QGstreamerImageCapture; -class QGstreamerMediaEncoder; -class QGstreamerAudioInput; -class QGstreamerAudioOutput; -class QGstreamerVideoOutput; -class QGstreamerVideoSink; - -class QGstreamerMediaCapture : public QPlatformMediaCaptureSession -{ - Q_OBJECT - -public: - QGstreamerMediaCapture(); - virtual ~QGstreamerMediaCapture(); - - QPlatformCamera *camera() override; - void setCamera(QPlatformCamera *camera) override; - - QPlatformImageCapture *imageCapture() override; - void setImageCapture(QPlatformImageCapture *imageCapture) override; - - QPlatformMediaEncoder *mediaEncoder() override; - void setMediaEncoder(QPlatformMediaEncoder *encoder) override; - - void setAudioInput(QPlatformAudioInput *input) override; - QGstreamerAudioInput *audioInput() { return gstAudioInput; } - - void setVideoPreview(QVideoSink *sink) override; - void setAudioOutput(QPlatformAudioOutput *output) override; - - void linkEncoder(QGstPad audioSink, QGstPad videoSink); - void unlinkEncoder(); - - QGstPipeline pipeline() const { return gstPipeline; } - - QGstreamerVideoSink *gstreamerVideoSink() const; - -private: - friend QGstreamerMediaEncoder; - // Gst elements - QGstPipeline gstPipeline; - QTimer heartbeat; - - QGstreamerAudioInput *gstAudioInput = nullptr; - QGstreamerCamera *gstCamera = nullptr; - - QGstElement gstAudioTee; - QGstElement gstVideoTee; - QGstElement encoderVideoCapsFilter; - QGstElement encoderAudioCapsFilter; - - QGstPad encoderAudioSink; - QGstPad encoderVideoSink; - QGstPad imageCaptureSink; - - QGstreamerAudioOutput *gstAudioOutput = nullptr; - QGstreamerVideoOutput *gstVideoOutput = nullptr; - - QGstreamerMediaEncoder *m_mediaEncoder = nullptr; - QGstreamerImageCapture *m_imageCapture = nullptr; -}; - -QT_END_NAMESPACE - -#endif // QGSTREAMERCAPTURESERVICE_H diff --git a/src/multimedia/platform/gstreamer/mediacapture/qgstreamermediaencoder.cpp b/src/multimedia/platform/gstreamer/mediacapture/qgstreamermediaencoder.cpp deleted file mode 100644 index 434a6ca67..000000000 --- a/src/multimedia/platform/gstreamer/mediacapture/qgstreamermediaencoder.cpp +++ /dev/null @@ -1,439 +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$ -** -****************************************************************************/ - -#include "qgstreamermediaencoder_p.h" -#include "private/qgstreamerintegration_p.h" -#include "private/qgstreamerformatinfo_p.h" -#include "private/qgstpipeline_p.h" -#include "private/qgstreamermessage_p.h" -#include "private/qplatformcamera_p.h" -#include "qaudiodevice.h" -#include "qmediastoragelocation_p.h" - -#include <qdebug.h> -#include <qstandardpaths.h> -#include <qmimetype.h> -#include <qloggingcategory.h> - -#include <gst/gsttagsetter.h> -#include <gst/gstversion.h> -#include <gst/video/video.h> -#include <gst/pbutils/encoding-profile.h> - -Q_LOGGING_CATEGORY(qLcMediaEncoder, "qt.multimedia.encoder") - -QGstreamerMediaEncoder::QGstreamerMediaEncoder(QMediaRecorder *parent) - : QPlatformMediaEncoder(parent), - audioPauseControl(*this), - videoPauseControl(*this) -{ - signalDurationChangedTimer.setInterval(100); - signalDurationChangedTimer.callOnTimeout([this](){ durationChanged(duration()); }); -} - -QGstreamerMediaEncoder::~QGstreamerMediaEncoder() -{ - if (!gstPipeline.isNull()) { - finalize(); - gstPipeline.removeMessageFilter(this); - gstPipeline.setStateSync(GST_STATE_NULL); - } -} - -bool QGstreamerMediaEncoder::isLocationWritable(const QUrl &) const -{ - return true; -} - -void QGstreamerMediaEncoder::handleSessionError(QMediaRecorder::Error code, const QString &description) -{ - error(code, description); - stop(); -} - -bool QGstreamerMediaEncoder::processBusMessage(const QGstreamerMessage &message) -{ - GstMessage *gm = message.rawMessage(); - if (!gm) - return false; - -// qCDebug(qLcMediaEncoder) << "received event from" << message.source().name() << Qt::hex << message.type(); -// if (message.type() == GST_MESSAGE_STATE_CHANGED) { -// GstState oldState; -// GstState newState; -// GstState pending; -// gst_message_parse_state_changed(gm, &oldState, &newState, &pending); -// qCDebug(qLcMediaEncoder) << "received state change from" << message.source().name() << oldState << newState << pending; -// } - if (message.type() == GST_MESSAGE_ELEMENT) { - QGstStructure s = gst_message_get_structure(gm); - qCDebug(qLcMediaEncoder) << "received element message from" << message.source().name() << s.name(); - if (s.name() == "GstBinForwarded") - gm = s.getMessage(); - if (!gm) - return false; - } - - if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_EOS) { - qCDebug(qLcMediaEncoder) << "received EOS from" << QGstObject(GST_MESSAGE_SRC(gm)).name(); - finalize(); - return false; - } - - if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ERROR) { - GError *err; - gchar *debug; - gst_message_parse_error(gm, &err, &debug); - error(QMediaRecorder::ResourceError, QString::fromUtf8(err->message)); - g_error_free(err); - g_free(debug); - finalize(); - } - - if (GST_MESSAGE_SRC(gm) == gstEncoder.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); - - if (newState == GST_STATE_PAUSED && !m_metaData.isEmpty()) - setMetaData(m_metaData); - break; - } - default: - break; - } - } - return false; -} - -qint64 QGstreamerMediaEncoder::duration() const -{ - return std::max(audioPauseControl.duration, videoPauseControl.duration); -} - - -static GstEncodingContainerProfile *createContainerProfile(const QMediaEncoderSettings &settings) -{ - auto *formatInfo = QGstreamerIntegration::instance()->m_formatsInfo; - - QGstMutableCaps caps = formatInfo->formatCaps(settings.fileFormat()); - - GstEncodingContainerProfile *profile = (GstEncodingContainerProfile *)gst_encoding_container_profile_new( - "container_profile", - (gchar *)"custom container profile", - const_cast<GstCaps *>(caps.get()), - nullptr); //preset - return profile; -} - -static GstEncodingProfile *createVideoProfile(const QMediaEncoderSettings &settings) -{ - auto *formatInfo = QGstreamerIntegration::instance()->m_formatsInfo; - - QGstMutableCaps caps = formatInfo->videoCaps(settings.mediaFormat()); - if (caps.isNull()) - return nullptr; - - GstEncodingVideoProfile *profile = gst_encoding_video_profile_new( - const_cast<GstCaps *>(caps.get()), - nullptr, - nullptr, //restriction - 0); //presence - - gst_encoding_video_profile_set_pass(profile, 0); - gst_encoding_video_profile_set_variableframerate(profile, TRUE); - - return (GstEncodingProfile *)profile; -} - -static GstEncodingProfile *createAudioProfile(const QMediaEncoderSettings &settings) -{ - auto *formatInfo = QGstreamerIntegration::instance()->m_formatsInfo; - - auto caps = formatInfo->audioCaps(settings.mediaFormat()); - if (caps.isNull()) - return nullptr; - - GstEncodingProfile *profile = (GstEncodingProfile *)gst_encoding_audio_profile_new( - const_cast<GstCaps *>(caps.get()), - nullptr, //preset - nullptr, //restriction - 0); //presence - - return profile; -} - - -static GstEncodingContainerProfile *createEncodingProfile(const QMediaEncoderSettings &settings) -{ - auto *containerProfile = createContainerProfile(settings); - if (!containerProfile) { - qWarning() << "QGstreamerMediaEncoder: failed to create container profile!"; - return nullptr; - } - - GstEncodingProfile *audioProfile = createAudioProfile(settings); - GstEncodingProfile *videoProfile = nullptr; - if (settings.videoCodec() != QMediaFormat::VideoCodec::Unspecified) - videoProfile = createVideoProfile(settings); -// qDebug() << "audio profile" << (audioProfile ? gst_caps_to_string(gst_encoding_profile_get_format(audioProfile)) : "(null)"); -// qDebug() << "video profile" << (videoProfile ? gst_caps_to_string(gst_encoding_profile_get_format(videoProfile)) : "(null)"); -// qDebug() << "conta profile" << gst_caps_to_string(gst_encoding_profile_get_format((GstEncodingProfile *)containerProfile)); - - if (videoProfile) { - if (!gst_encoding_container_profile_add_profile(containerProfile, videoProfile)) { - qWarning() << "QGstreamerMediaEncoder: failed to add video profile!"; - gst_encoding_profile_unref(videoProfile); - } - } - if (audioProfile) { - if (!gst_encoding_container_profile_add_profile(containerProfile, audioProfile)) { - qWarning() << "QGstreamerMediaEncoder: failed to add audio profile!"; - gst_encoding_profile_unref(audioProfile); - } - } - - return containerProfile; -} - -void QGstreamerMediaEncoder::PauseControl::reset() -{ - pauseOffsetPts = 0; - pauseStartPts.reset(); - duration = 0; - firstBufferPts.reset(); -} - -void QGstreamerMediaEncoder::PauseControl::installOn(QGstPad pad) -{ - pad.addProbe<&QGstreamerMediaEncoder::PauseControl::processBuffer>(this, GST_PAD_PROBE_TYPE_BUFFER); -} - -GstPadProbeReturn QGstreamerMediaEncoder::PauseControl::processBuffer(QGstPad, GstPadProbeInfo *info) -{ - auto buffer = GST_PAD_PROBE_INFO_BUFFER(info); - if (!buffer) - return GST_PAD_PROBE_OK; - - buffer = gst_buffer_make_writable(buffer); - - if (!buffer) - return GST_PAD_PROBE_OK; - - GST_PAD_PROBE_INFO_DATA(info) = buffer; - - if (!GST_BUFFER_PTS_IS_VALID(buffer)) - return GST_PAD_PROBE_OK; - - if (!firstBufferPts) - firstBufferPts = GST_BUFFER_PTS(buffer); - - if (encoder.state() == QMediaRecorder::PausedState) { - if (!pauseStartPts) - pauseStartPts = GST_BUFFER_PTS(buffer); - - return GST_PAD_PROBE_DROP; - } - - if (pauseStartPts) { - pauseOffsetPts += GST_BUFFER_PTS(buffer) - *pauseStartPts; - pauseStartPts.reset(); - } - GST_BUFFER_PTS(buffer) -= pauseOffsetPts; - - duration = (GST_BUFFER_PTS(buffer) - *firstBufferPts) / GST_MSECOND; - - return GST_PAD_PROBE_OK; -} - -void QGstreamerMediaEncoder::record(QMediaEncoderSettings &settings) -{ - if (!m_session || state() != QMediaRecorder::StoppedState) - return; - - const auto hasVideo = m_session->camera() && m_session->camera()->isActive(); - const auto hasAudio = m_session->audioInput() != nullptr; - - if (!hasVideo && !hasAudio) { - error(QMediaRecorder::ResourceError, QMediaRecorder::tr("No camera or audio input")); - return; - } - - const auto audioOnly = settings.videoCodec() == QMediaFormat::VideoCodec::Unspecified; - - auto primaryLocation = audioOnly ? QStandardPaths::MusicLocation : QStandardPaths::MoviesLocation; - auto container = settings.mimeType().preferredSuffix(); - auto location = QMediaStorageLocation::generateFileName(outputLocation().toLocalFile(), primaryLocation, container); - - QUrl actualSink = QUrl::fromLocalFile(QDir::currentPath()).resolved(location); - qCDebug(qLcMediaEncoder) << "recording new video to" << actualSink; - - Q_ASSERT(!actualSink.isEmpty()); - - gstEncoder = QGstElement("encodebin", "encodebin"); - auto *encodingProfile = createEncodingProfile(settings); - g_object_set (gstEncoder.object(), "profile", encodingProfile, nullptr); - gst_encoding_profile_unref(encodingProfile); - - gstFileSink = QGstElement("filesink", "filesink"); - gstFileSink.set("location", QFile::encodeName(actualSink.toLocalFile()).constData()); - gstFileSink.set("async", false); - - QGstPad audioSink = {}; - QGstPad videoSink = {}; - - audioPauseControl.reset(); - videoPauseControl.reset(); - - if (hasAudio) { - audioSink = gstEncoder.getRequestPad("audio_%u"); - if (audioSink.isNull()) - qWarning() << "Unsupported audio codec"; - else - audioPauseControl.installOn(audioSink); - } - - if (hasVideo) { - videoSink = gstEncoder.getRequestPad("video_%u"); - if (videoSink.isNull()) - qWarning() << "Unsupported video codec"; - else - videoPauseControl.installOn(videoSink); - } - - gstPipeline.add(gstEncoder, gstFileSink); - gstEncoder.link(gstFileSink); - m_session->linkEncoder(audioSink, videoSink); - - gstEncoder.syncStateWithParent(); - gstFileSink.syncStateWithParent(); - - signalDurationChangedTimer.start(); - gstPipeline.dumpGraph("recording"); - - stateChanged(QMediaRecorder::RecordingState); - actualLocationChanged(QUrl::fromLocalFile(location)); -} - -void QGstreamerMediaEncoder::pause() -{ - if (!m_session || state() != QMediaRecorder::RecordingState) - return; - signalDurationChangedTimer.stop(); - gstPipeline.dumpGraph("before-pause"); - stateChanged(QMediaRecorder::PausedState); -} - -void QGstreamerMediaEncoder::resume() -{ - gstPipeline.dumpGraph("before-resume"); - if (!m_session || state() != QMediaRecorder::PausedState) - return; - signalDurationChangedTimer.start(); - stateChanged(QMediaRecorder::RecordingState); -} - -void QGstreamerMediaEncoder::stop() -{ - if (!m_session || state() == QMediaRecorder::StoppedState) - return; - qCDebug(qLcMediaEncoder) << "stop"; - - m_session->unlinkEncoder(); - signalDurationChangedTimer.stop(); - - //with live sources it's necessary to send EOS even to pipeline - //before going to STOPPED state - qCDebug(qLcMediaEncoder) << ">>>>>>>>>>>>> sending EOS"; - gstEncoder.sendEos(); -} - -void QGstreamerMediaEncoder::finalize() -{ - if (!m_session || gstEncoder.isNull()) - return; - - qCDebug(qLcMediaEncoder) << "finalize"; - - gstEncoder.setState(GST_STATE_NULL); - gstFileSink.setState(GST_STATE_NULL); - gstPipeline.remove(gstEncoder); - gstPipeline.remove(gstFileSink); - gstFileSink = {}; - gstEncoder = {}; - stateChanged(QMediaRecorder::StoppedState); -} - -void QGstreamerMediaEncoder::setMetaData(const QMediaMetaData &metaData) -{ - if (!m_session) - return; - m_metaData = static_cast<const QGstreamerMetaData &>(metaData); - m_metaData.setMetaData(gstEncoder.bin()); - -} - -QMediaMetaData QGstreamerMediaEncoder::metaData() const -{ - return m_metaData; -} - -void QGstreamerMediaEncoder::setCaptureSession(QPlatformMediaCaptureSession *session) -{ - QGstreamerMediaCapture *captureSession = static_cast<QGstreamerMediaCapture *>(session); - if (m_session == captureSession) - return; - - if (m_session) { - finalize(); - gstPipeline.removeMessageFilter(this); - gstPipeline = {}; - } - - m_session = captureSession; - if (!m_session) - return; - - gstPipeline = captureSession->gstPipeline; - gstPipeline.set("message-forward", true); - gstPipeline.installMessageFilter(this); -} diff --git a/src/multimedia/platform/gstreamer/mediacapture/qgstreamermediaencoder_p.h b/src/multimedia/platform/gstreamer/mediacapture/qgstreamermediaencoder_p.h deleted file mode 100644 index 649edbfd9..000000000 --- a/src/multimedia/platform/gstreamer/mediacapture/qgstreamermediaencoder_p.h +++ /dev/null @@ -1,125 +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$ -** -****************************************************************************/ - - -#ifndef QGSTREAMERENCODERCONTROL_H -#define QGSTREAMERENCODERCONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediaencoder_p.h> -#include "qgstreamermediacapture_p.h" -#include "private/qgstreamermetadata_p.h" - -#include <QtCore/qurl.h> -#include <QtCore/qdir.h> -#include <qelapsedtimer.h> -#include <qtimer.h> - -QT_BEGIN_NAMESPACE - -class QMediaMetaData; -class QGstreamerMessage; - -class QGstreamerMediaEncoder : public QPlatformMediaEncoder, QGstreamerBusMessageFilter -{ -public: - QGstreamerMediaEncoder(QMediaRecorder *parent); - virtual ~QGstreamerMediaEncoder(); - - bool isLocationWritable(const QUrl &sink) const override; - - qint64 duration() const override; - - void record(QMediaEncoderSettings &settings) override; - void pause() override; - void resume() override; - void stop() override; - - void setMetaData(const QMediaMetaData &) override; - QMediaMetaData metaData() const override; - - void setCaptureSession(QPlatformMediaCaptureSession *session); - - QGstElement getEncoder() { return gstEncoder; } -private: - bool processBusMessage(const QGstreamerMessage& message) override; - -private: - struct PauseControl { - PauseControl(QPlatformMediaEncoder &encoder) : encoder(encoder) {} - - GstPadProbeReturn processBuffer(QGstPad pad, GstPadProbeInfo *info); - void installOn(QGstPad pad); - void reset(); - - QPlatformMediaEncoder &encoder; - GstClockTime pauseOffsetPts = 0; - std::optional<GstClockTime> pauseStartPts; - std::optional<GstClockTime> firstBufferPts; - qint64 duration = 0; - }; - - PauseControl audioPauseControl; - PauseControl videoPauseControl; - - void handleSessionError(QMediaRecorder::Error code, const QString &description); - void finalize(); - - QGstreamerMediaCapture *m_session = nullptr; - QGstreamerMetaData m_metaData; - QTimer signalDurationChangedTimer; - - QGstPipeline gstPipeline; - QGstBin gstEncoder; - QGstElement gstFileSink; -}; - -QT_END_NAMESPACE - -#endif // QGSTREAMERENCODERCONTROL_H diff --git a/src/multimedia/platform/gstreamer/qgstreamerformatinfo.cpp b/src/multimedia/platform/gstreamer/qgstreamerformatinfo.cpp deleted file mode 100644 index 1bf17ac89..000000000 --- a/src/multimedia/platform/gstreamer/qgstreamerformatinfo.cpp +++ /dev/null @@ -1,459 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qgstreamerformatinfo_p.h" - -#include "private/qgstutils_p.h" - -QT_BEGIN_NAMESPACE - -QMediaFormat::AudioCodec QGstreamerFormatInfo::audioCodecForCaps(QGstStructure structure) -{ - const char *name = structure.name().data(); - - if (!name || strncmp(name, "audio/", 6)) - return QMediaFormat::AudioCodec::Unspecified; - name += 6; - if (!strcmp(name, "mpeg")) { - auto version = structure["mpegversion"].toInt(); - if (version == 1) { - auto layer = structure["layer"]; - if (!layer.isNull()) - return QMediaFormat::AudioCodec::MP3; - } - if (version == 4) - return QMediaFormat::AudioCodec::AAC; - } else if (!strcmp(name, "x-ac3")) { - return QMediaFormat::AudioCodec::AC3; - } else if (!strcmp(name, "x-eac3")) { - return QMediaFormat::AudioCodec::EAC3; - } else if (!strcmp(name, "x-flac")) { - return QMediaFormat::AudioCodec::FLAC; - } else if (!strcmp(name, "x-alac")) { - return QMediaFormat::AudioCodec::ALAC; - } else if (!strcmp(name, "x-true-hd")) { - return QMediaFormat::AudioCodec::DolbyTrueHD; - } else if (!strcmp(name, "x-vorbis")) { - return QMediaFormat::AudioCodec::Vorbis; - } else if (!strcmp(name, "x-opus")) { - return QMediaFormat::AudioCodec::Opus; - } else if (!strcmp(name, "x-wav")) { - return QMediaFormat::AudioCodec::Wave; - } else if (!strcmp(name, "x-wma")) { - return QMediaFormat::AudioCodec::WMA; - } - return QMediaFormat::AudioCodec::Unspecified; -} - -QMediaFormat::VideoCodec QGstreamerFormatInfo::videoCodecForCaps(QGstStructure structure) -{ - const char *name = structure.name().data(); - - if (!name || strncmp(name, "video/", 6)) - return QMediaFormat::VideoCodec::Unspecified; - name += 6; - - if (!strcmp(name, "mpeg")) { - auto version = structure["mpegversion"].toInt(); - if (version == 1) - return QMediaFormat::VideoCodec::MPEG1; - else if (version == 2) - return QMediaFormat::VideoCodec::MPEG2; - else if (version == 4) - return QMediaFormat::VideoCodec::MPEG4; - } else if (!strcmp(name, "x-h264")) { - return QMediaFormat::VideoCodec::H264; -#if GST_CHECK_VERSION(1, 17, 0) // x265enc seems to be broken on 1.16 at least - } else if (!strcmp(name, "x-h265")) { - return QMediaFormat::VideoCodec::H265; -#endif - } else if (!strcmp(name, "x-vp8")) { - return QMediaFormat::VideoCodec::VP8; - } else if (!strcmp(name, "x-vp9")) { - return QMediaFormat::VideoCodec::VP9; - } else if (!strcmp(name, "x-av1")) { - return QMediaFormat::VideoCodec::AV1; - } else if (!strcmp(name, "x-theora")) { - return QMediaFormat::VideoCodec::Theora; - } else if (!strcmp(name, "x-jpeg")) { - return QMediaFormat::VideoCodec::MotionJPEG; - } else if (!strcmp(name, "x-wmv")) { - return QMediaFormat::VideoCodec::WMV; - } - return QMediaFormat::VideoCodec::Unspecified; -} - -QMediaFormat::FileFormat QGstreamerFormatInfo::fileFormatForCaps(QGstStructure structure) -{ - const char *name = structure.name().data(); - - if (!strcmp(name, "video/x-ms-asf")) { - return QMediaFormat::FileFormat::WMV; - } else if (!strcmp(name, "video/x-msvideo")) { - return QMediaFormat::FileFormat::AVI; - } else if (!strcmp(name, "video/x-matroska")) { - return QMediaFormat::FileFormat::Matroska; - } else if (!strcmp(name, "video/quicktime")) { - auto variant = structure["variant"].toString(); - if (!variant) - return QMediaFormat::FileFormat::QuickTime; - else if (!strcmp(variant, "iso")) - return QMediaFormat::FileFormat::MPEG4; - } else if (!strcmp(name, "video/ogg")) { - return QMediaFormat::FileFormat::Ogg; - } else if (!strcmp(name, "video/webm")) { - return QMediaFormat::FileFormat::WebM; - } else if (!strcmp(name, "audio/x-m4a")) { - return QMediaFormat::FileFormat::Mpeg4Audio; - } else if (!strcmp(name, "audio/x-wav")) { - return QMediaFormat::FileFormat::Wave; - } else if (!strcmp(name, "audio/mpeg")) { - auto mpegversion = structure["mpegversion"].toInt(); - if (mpegversion == 1) { - auto layer = structure["layer"]; - if (!layer.isNull()) - return QMediaFormat::FileFormat::MP3; - } - } - return QMediaFormat::UnspecifiedFormat; -} - - -QImageCapture::FileFormat QGstreamerFormatInfo::imageFormatForCaps(QGstStructure structure) -{ - const char *name = structure.name().data(); - - if (!strcmp(name, "image/jpeg")) { - return QImageCapture::JPEG; - } else if (!strcmp(name, "image/png")) { - return QImageCapture::PNG; - } else if (!strcmp(name, "image/webp")) { - return QImageCapture::WebP; - } else if (!strcmp(name, "image/webp")) { - return QImageCapture::WebP; - } else if (!strcmp(name, "image/tiff")) { - return QImageCapture::Tiff; - } - return QImageCapture::UnspecifiedFormat; -} - -static QPair<QList<QMediaFormat::AudioCodec>, QList<QMediaFormat::VideoCodec>> getCodecsList(bool decode) -{ - QList<QMediaFormat::AudioCodec> audio; - QList<QMediaFormat::VideoCodec> video; - - GstPadDirection padDirection = decode ? GST_PAD_SINK : GST_PAD_SRC; - - GList *elementList = gst_element_factory_list_get_elements(decode ? GST_ELEMENT_FACTORY_TYPE_DECODER : GST_ELEMENT_FACTORY_TYPE_ENCODER, - GST_RANK_MARGINAL); - - GList *element = elementList; - while (element) { - GstElementFactory *factory = (GstElementFactory *)element->data; - element = element->next; - - const GList *padTemplates = gst_element_factory_get_static_pad_templates(factory); - while (padTemplates) { - GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *)padTemplates->data; - padTemplates = padTemplates->next; - - if (padTemplate->direction == padDirection) { - QGstMutableCaps caps = gst_static_caps_get(&padTemplate->static_caps); - - for (int i = 0; i < caps.size(); i++) { - QGstStructure structure = caps.at(i); - auto a = QGstreamerFormatInfo::audioCodecForCaps(structure); - if (a != QMediaFormat::AudioCodec::Unspecified && !audio.contains(a)) - audio.append(a); - auto v = QGstreamerFormatInfo::videoCodecForCaps(structure); - if (v != QMediaFormat::VideoCodec::Unspecified && !video.contains(v)) - video.append(v); - } - } - } - } - gst_plugin_feature_list_free(elementList); - return {audio, video}; -} - - -QList<QGstreamerFormatInfo::CodecMap> QGstreamerFormatInfo::getMuxerList(bool demuxer, - QList<QMediaFormat::AudioCodec> supportedAudioCodecs, - QList<QMediaFormat::VideoCodec> supportedVideoCodecs) -{ - QList<QGstreamerFormatInfo::CodecMap> muxers; - - GstPadDirection padDirection = demuxer ? GST_PAD_SINK : GST_PAD_SRC; - - GList *elementList = gst_element_factory_list_get_elements(demuxer ? GST_ELEMENT_FACTORY_TYPE_DEMUXER : GST_ELEMENT_FACTORY_TYPE_MUXER, - GST_RANK_MARGINAL); - GList *element = elementList; - while (element) { - GstElementFactory *factory = (GstElementFactory *)element->data; - element = element->next; - - QList<QMediaFormat::FileFormat> fileFormats; - - const GList *padTemplates = gst_element_factory_get_static_pad_templates(factory); - while (padTemplates) { - GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *)padTemplates->data; - padTemplates = padTemplates->next; - - if (padTemplate->direction == padDirection) { - QGstMutableCaps caps = gst_static_caps_get(&padTemplate->static_caps); - - for (int i = 0; i < caps.size(); i++) { - QGstStructure structure = caps.at(i); - auto fmt = fileFormatForCaps(structure); - if (fmt != QMediaFormat::UnspecifiedFormat) - fileFormats.append(fmt); - } - } - } - if (fileFormats.isEmpty()) - continue; - - QList<QMediaFormat::AudioCodec> audioCodecs; - QList<QMediaFormat::VideoCodec> videoCodecs; - - padTemplates = gst_element_factory_get_static_pad_templates(factory); - while (padTemplates) { - GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *)padTemplates->data; - padTemplates = padTemplates->next; - - // check the other side for supported inputs/outputs - if (padTemplate->direction != padDirection) { - QGstMutableCaps caps = gst_static_caps_get(&padTemplate->static_caps); - - bool acceptsRawAudio = false; - for (int i = 0; i < caps.size(); i++) { - QGstStructure structure = caps.at(i); - if (structure.name() == "audio/x-raw") - acceptsRawAudio = true; - auto audio = audioCodecForCaps(structure); - if (audio != QMediaFormat::AudioCodec::Unspecified && supportedAudioCodecs.contains(audio)) - audioCodecs.append(audio); - auto video = videoCodecForCaps(structure); - if (video != QMediaFormat::VideoCodec::Unspecified && supportedVideoCodecs.contains(video)) - videoCodecs.append(video); - } - if (acceptsRawAudio && fileFormats.size() == 1) { - switch (fileFormats.at(0)) { - case QMediaFormat::Mpeg4Audio: - default: - break; - case QMediaFormat::MP3: - audioCodecs.append(QMediaFormat::AudioCodec::MP3); - break; - case QMediaFormat::FLAC: - audioCodecs.append(QMediaFormat::AudioCodec::FLAC); - break; - case QMediaFormat::Wave: - audioCodecs.append(QMediaFormat::AudioCodec::Wave); - break; - } - } - } - } - if (!audioCodecs.isEmpty() || !videoCodecs.isEmpty()) { - for (auto f : qAsConst(fileFormats)) { - muxers.append({f, audioCodecs, videoCodecs}); - if (f == QMediaFormat::MPEG4 && !fileFormats.contains(QMediaFormat::Mpeg4Audio)) { - muxers.append({QMediaFormat::Mpeg4Audio, audioCodecs, {}}); - if (audioCodecs.contains(QMediaFormat::AudioCodec::AAC)) - muxers.append({QMediaFormat::AAC, { QMediaFormat::AudioCodec::AAC }, {}}); - } else if (f == QMediaFormat::WMV && !fileFormats.contains(QMediaFormat::WMA)) { - muxers.append({QMediaFormat::WMA, audioCodecs, {}}); - } - } - } - } - gst_plugin_feature_list_free(elementList); - return muxers; -} - -static QList<QImageCapture::FileFormat> getImageFormatList() -{ - QSet<QImageCapture::FileFormat> formats; - - GList *elementList = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_ENCODER, - GST_RANK_MARGINAL); - - GList *element = elementList; - while (element) { - GstElementFactory *factory = (GstElementFactory *)element->data; - element = element->next; - - const GList *padTemplates = gst_element_factory_get_static_pad_templates(factory); - while (padTemplates) { - GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *)padTemplates->data; - padTemplates = padTemplates->next; - - if (padTemplate->direction == GST_PAD_SRC) { - QGstMutableCaps caps = gst_static_caps_get(&padTemplate->static_caps); - - for (int i = 0; i < caps.size(); i++) { - QGstStructure structure = caps.at(i); - auto f = QGstreamerFormatInfo::imageFormatForCaps(structure); - if (f != QImageCapture::UnspecifiedFormat) { -// qDebug() << structure.toString() << f; - formats.insert(f); - } - } - } - } - } - gst_plugin_feature_list_free(elementList); - return formats.values(); -} - -#if 0 -static void dumpAudioCodecs(const QList<QMediaFormat::AudioCodec> &codecList) -{ - qDebug() << "Audio codecs:"; - for (const auto &c : codecList) - qDebug() << " " << QMediaFormat::audioCodecName(c); -} - -static void dumpVideoCodecs(const QList<QMediaFormat::VideoCodec> &codecList) -{ - qDebug() << "Video codecs:"; - for (const auto &c : codecList) - qDebug() << " " << QMediaFormat::videoCodecName(c); -} - -static void dumpMuxers(const QList<QPlatformMediaFormatInfo::CodecMap> &muxerList) -{ - for (const auto &m : muxerList) { - qDebug() << " " << QMediaFormat::fileFormatName(m.format); - qDebug() << " Audio"; - for (const auto &a : m.audio) - qDebug() << " " << QMediaFormat::audioCodecName(a); - qDebug() << " Video"; - for (const auto &v : m.video) - qDebug() << " " << QMediaFormat::videoCodecName(v); - } - -} -#endif - -QGstreamerFormatInfo::QGstreamerFormatInfo() -{ - auto codecs = getCodecsList(/*decode = */ true); - decoders = getMuxerList(true, codecs.first, codecs.second); - - codecs = getCodecsList(/*decode = */ false); - encoders = getMuxerList(/* demuxer = */false, codecs.first, codecs.second); -// dumpAudioCodecs(codecs.first); -// dumpVideoCodecs(codecs.second); -// dumpMuxers(encoders); - - imageFormats = getImageFormatList(); -} - -QGstreamerFormatInfo::~QGstreamerFormatInfo() = default; - -QGstMutableCaps QGstreamerFormatInfo::formatCaps(const QMediaFormat &f) const -{ - auto format = f.fileFormat(); - Q_ASSERT(format != QMediaFormat::UnspecifiedFormat); - - const char *capsForFormat[QMediaFormat::LastFileFormat + 1] = { - "video/x-ms-asf", // WMV - "video/x-msvideo", // AVI - "video/x-matroska", // Matroska - "video/quicktime, variant=(string)iso", // MPEG4 - "video/ogg", // Ogg - "video/quicktime", // QuickTime - "video/webm", // WebM - "video/quicktime, variant=(string)iso", // Mpeg4Audio is the same is mp4... - "video/quicktime, variant=(string)iso", // AAC is also an MP4 container - "video/x-ms-asf", // WMA, same as WMV - "audio/mpeg, mpegversion=(int)1, layer=(int)3", // MP3 - "audio/x-flac", // FLAC - "audio/x-wav" // Wave - }; - return gst_caps_from_string(capsForFormat[format]); -} - -QGstMutableCaps QGstreamerFormatInfo::audioCaps(const QMediaFormat &f) const -{ - auto codec = f.audioCodec(); - if (codec == QMediaFormat::AudioCodec::Unspecified) - return nullptr; - - const char *capsForCodec[(int)QMediaFormat::AudioCodec::LastAudioCodec + 1] = { - "audio/mpeg, mpegversion=(int)1, layer=(int)3", // MP3 - "audio/mpeg, mpegversion=(int)4", // AAC - "audio/x-ac3", // AC3 - "audio/x-eac3", // EAC3 - "audio/x-flac", // FLAC - "audio/x-true-hd", // DolbyTrueHD - "audio/x-opus", // Opus - "audio/x-vorbis", // Vorbis - "audio/x-raw", // WAVE - "audio/x-wma", // WMA - "audio/x-alac", // ALAC - }; - return gst_caps_from_string(capsForCodec[(int)codec]); -} - -QGstMutableCaps QGstreamerFormatInfo::videoCaps(const QMediaFormat &f) const -{ - auto codec = f.videoCodec(); - if (codec == QMediaFormat::VideoCodec::Unspecified) - return nullptr; - - const char *capsForCodec[(int)QMediaFormat::VideoCodec::LastVideoCodec + 1] = { - "video/mpeg, mpegversion=(int)1", // MPEG1, - "video/mpeg, mpegversion=(int)2", // MPEG2, - "video/mpeg, mpegversion=(int)4", // MPEG4, - "video/x-h264", // H264, - "video/x-h265", // H265, - "video/x-vp8", // VP8, - "video/x-vp9", // VP9, - "video/x-av1", // AV1, - "video/x-theora", // Theora, - "audio/x-wmv", // WMV - "video/x-jpeg", // MotionJPEG, - }; - return gst_caps_from_string(capsForCodec[(int)codec]); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/gstreamer/qgstreamerformatinfo_p.h b/src/multimedia/platform/gstreamer/qgstreamerformatinfo_p.h deleted file mode 100644 index f8de964e4..000000000 --- a/src/multimedia/platform/gstreamer/qgstreamerformatinfo_p.h +++ /dev/null @@ -1,81 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERFORMATINFO_H -#define QGSTREAMERFORMATINFO_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediaformatinfo_p.h> -#include <qhash.h> -#include <qlist.h> -#include <private/qgstutils_p.h> - -QT_BEGIN_NAMESPACE - -class QGstreamerFormatInfo : public QPlatformMediaFormatInfo -{ -public: - QGstreamerFormatInfo(); - ~QGstreamerFormatInfo(); - - QGstMutableCaps formatCaps(const QMediaFormat &f) const; - QGstMutableCaps audioCaps(const QMediaFormat &f) const; - QGstMutableCaps videoCaps(const QMediaFormat &f) const; - - static QMediaFormat::AudioCodec audioCodecForCaps(QGstStructure structure); - static QMediaFormat::VideoCodec videoCodecForCaps(QGstStructure structure); - static QMediaFormat::FileFormat fileFormatForCaps(QGstStructure structure); - static QImageCapture::FileFormat imageFormatForCaps(QGstStructure structure); - - QList<CodecMap> getMuxerList(bool demuxer, QList<QMediaFormat::AudioCodec> audioCodecs, QList<QMediaFormat::VideoCodec> videoCodecs); -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/gstreamer/qgstreamerintegration.cpp b/src/multimedia/platform/gstreamer/qgstreamerintegration.cpp deleted file mode 100644 index 6f28fe42d..000000000 --- a/src/multimedia/platform/gstreamer/qgstreamerintegration.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qgstreamerintegration_p.h" -#include "qgstreamermediadevices_p.h" -#include "private/qgstreamermediaplayer_p.h" -#include "private/qgstreamermediacapture_p.h" -#include "private/qgstreameraudiodecoder_p.h" -#include "private/qgstreamercamera_p.h" -#include "private/qgstreamermediaencoder_p.h" -#include "private/qgstreamerimagecapture_p.h" -#include "private/qgstreamerformatinfo_p.h" -#include "private/qgstreamervideosink_p.h" -#include "private/qgstreameraudioinput_p.h" -#include "private/qgstreameraudiooutput_p.h" - -QT_BEGIN_NAMESPACE - -QGstreamerIntegration::QGstreamerIntegration() -{ - gst_init(nullptr, nullptr); - m_devices = new QGstreamerMediaDevices(); - m_formatsInfo = new QGstreamerFormatInfo(); -} - -QGstreamerIntegration::~QGstreamerIntegration() -{ - delete m_devices; - delete m_formatsInfo; -} - -QPlatformMediaDevices *QGstreamerIntegration::devices() -{ - return m_devices; -} - -QPlatformMediaFormatInfo *QGstreamerIntegration::formatInfo() -{ - return m_formatsInfo; -} - -QPlatformAudioDecoder *QGstreamerIntegration::createAudioDecoder(QAudioDecoder *decoder) -{ - return new QGstreamerAudioDecoder(decoder); -} - -QPlatformMediaCaptureSession *QGstreamerIntegration::createCaptureSession() -{ - return new QGstreamerMediaCapture(); -} - -QPlatformMediaPlayer *QGstreamerIntegration::createPlayer(QMediaPlayer *player) -{ - return new QGstreamerMediaPlayer(player); -} - -QPlatformCamera *QGstreamerIntegration::createCamera(QCamera *camera) -{ - return new QGstreamerCamera(camera); -} - -QPlatformMediaEncoder *QGstreamerIntegration::createEncoder(QMediaRecorder *encoder) -{ - return new QGstreamerMediaEncoder(encoder); -} - -QPlatformImageCapture *QGstreamerIntegration::createImageCapture(QImageCapture *imageCapture) -{ - return new QGstreamerImageCapture(imageCapture); -} - -QPlatformVideoSink *QGstreamerIntegration::createVideoSink(QVideoSink *sink) -{ - return new QGstreamerVideoSink(sink); -} - -QPlatformAudioInput *QGstreamerIntegration::createAudioInput(QAudioInput *q) -{ - return new QGstreamerAudioInput(q); -} - -QPlatformAudioOutput *QGstreamerIntegration::createAudioOutput(QAudioOutput *q) -{ - return new QGstreamerAudioOutput(q); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/gstreamer/qgstreamerintegration_p.h b/src/multimedia/platform/gstreamer/qgstreamerintegration_p.h deleted file mode 100644 index 14167e750..000000000 --- a/src/multimedia/platform/gstreamer/qgstreamerintegration_p.h +++ /dev/null @@ -1,90 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERINTEGRATION_H -#define QGSTREAMERINTEGRATION_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediaintegration_p.h> - -QT_BEGIN_NAMESPACE - -class QGstreamerMediaDevices; -class QGstreamerPlayerInterface; -class QGstreamerFormatInfo; - -class QGstreamerIntegration : public QPlatformMediaIntegration -{ -public: - QGstreamerIntegration(); - ~QGstreamerIntegration(); - - static QGstreamerIntegration *instance() { return static_cast<QGstreamerIntegration *>(QPlatformMediaIntegration::instance()); } - QPlatformMediaDevices *devices() override; - QPlatformMediaFormatInfo *formatInfo() override; - - QPlatformAudioDecoder *createAudioDecoder(QAudioDecoder *decoder) override; - QPlatformMediaCaptureSession *createCaptureSession() override; - QPlatformMediaPlayer *createPlayer(QMediaPlayer *player) override; - QPlatformCamera *createCamera(QCamera *) override; - QPlatformMediaEncoder *createEncoder(QMediaRecorder *) override; - QPlatformImageCapture *createImageCapture(QImageCapture *) override; - - QPlatformVideoSink *createVideoSink(QVideoSink *sink) override; - - QPlatformAudioInput *createAudioInput(QAudioInput *) override; - QPlatformAudioOutput *createAudioOutput(QAudioOutput *) override; - - QGstreamerMediaDevices *m_devices = nullptr; - QGstreamerFormatInfo *m_formatsInfo = nullptr; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/gstreamer/qgstreamermediadevices.cpp b/src/multimedia/platform/gstreamer/qgstreamermediadevices.cpp deleted file mode 100644 index f66329f5e..000000000 --- a/src/multimedia/platform/gstreamer/qgstreamermediadevices.cpp +++ /dev/null @@ -1,265 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qgstreamermediadevices_p.h" -#include "qmediadevices.h" -#include "qcameradevice_p.h" - -#include "private/qgstreameraudiosource_p.h" -#include "private/qgstreameraudiosink_p.h" -#include "private/qgstreameraudiodevice_p.h" -#include "private/qgstutils_p.h" - -QT_BEGIN_NAMESPACE - -static gboolean deviceMonitor(GstBus *, GstMessage *message, gpointer m) -{ - QGstreamerMediaDevices *manager = static_cast<QGstreamerMediaDevices *>(m); - GstDevice *device = nullptr; - - switch (GST_MESSAGE_TYPE (message)) { - case GST_MESSAGE_DEVICE_ADDED: - gst_message_parse_device_added(message, &device); - manager->addDevice(device); - break; - case GST_MESSAGE_DEVICE_REMOVED: - gst_message_parse_device_removed(message, &device); - manager->removeDevice(device); - break; - default: - break; - } - if (device) - gst_object_unref (device); - - return G_SOURCE_CONTINUE; -} - -QGstreamerMediaDevices::QGstreamerMediaDevices() - : QPlatformMediaDevices() -{ - GstDeviceMonitor *monitor; - GstBus *bus; - - monitor = gst_device_monitor_new(); - - gst_device_monitor_add_filter (monitor, "Video/Source", nullptr); - gst_device_monitor_add_filter (monitor, "Audio/Source", nullptr); - gst_device_monitor_add_filter (monitor, "Audio/Sink", nullptr); - - bus = gst_device_monitor_get_bus(monitor); - gst_bus_add_watch(bus, deviceMonitor, this); - gst_object_unref(bus); - - gst_device_monitor_start(monitor); - - auto devices = gst_device_monitor_get_devices(monitor); - - while (devices) { - GstDevice *device = static_cast<GstDevice *>(devices->data); - addDevice(device); - gst_object_unref(device); - devices = g_list_delete_link(devices, devices); - } -} - -static QList<QAudioDevice> devicesFromSet(const QSet<GstDevice *> &deviceSet, QAudioDevice::Mode mode) -{ - QList<QAudioDevice> devices; - for (auto *d : deviceSet) { - auto *properties = gst_device_get_properties(d); - if (properties) { - auto *klass = gst_structure_get_string(properties, "device.class"); - if (qstrcmp(klass, "monitor")) { - auto *name = gst_structure_get_string(properties, "sysfs.path"); - gboolean def; - auto *info = new QGStreamerAudioDeviceInfo(d, name, mode); - if (gst_structure_get_boolean(properties, "is-default", &def) && def) - devices.prepend(info->create()); - else - devices.append(info->create()); - } - - gst_structure_free(properties); - } - } - return devices; -}; - -QList<QAudioDevice> QGstreamerMediaDevices::audioInputs() const -{ - return devicesFromSet(m_audioSources, QAudioDevice::Input); -} - -QList<QAudioDevice> QGstreamerMediaDevices::audioOutputs() const -{ - return devicesFromSet(m_audioSinks, QAudioDevice::Output); -} - -QList<QCameraDevice> QGstreamerMediaDevices::videoInputs() const -{ - QList<QCameraDevice> devices; - - for (auto *d : qAsConst(m_videoSources)) { - QGstStructure properties = gst_device_get_properties(d); - if (!properties.isNull()) { - QCameraDevicePrivate *info = new QCameraDevicePrivate; - auto *desc = gst_device_get_display_name(d); - info->description = QString::fromUtf8(desc); - g_free(desc); - - info->id = properties["device.path"].toString(); - auto def = properties["is-default"].toBool(); - info->isDefault = def && *def; - if (def) - devices.prepend(info->create()); - else - devices.append(info->create()); - properties.free(); - QGstCaps caps = gst_device_get_caps(d); - if (!caps.isNull()) { - QList<QCameraFormat> formats; - QSet<QSize> photoResolutions; - - int size = caps.size(); - for (int i = 0; i < size; ++i) { - auto cap = caps.at(i); - - QSize resolution = cap.resolution(); - if (!resolution.isValid()) - continue; - - auto pixelFormat = cap.pixelFormat(); - auto frameRate = cap.frameRateRange(); - - auto *f = new QCameraFormatPrivate{ - QSharedData(), - pixelFormat, - resolution, - frameRate.min, - frameRate.max - }; - formats << f->create(); - photoResolutions.insert(resolution); - } - info->videoFormats = formats; - // ### sort resolutions? - info->photoResolutions = photoResolutions.values(); - } - } - } - return devices; -} - -QPlatformAudioSource *QGstreamerMediaDevices::createAudioSource(const QAudioDevice &deviceInfo) -{ - return new QGStreamerAudioSource(deviceInfo); -} - -QPlatformAudioSink *QGstreamerMediaDevices::createAudioSink(const QAudioDevice &deviceInfo) -{ - return new QGStreamerAudioSink(deviceInfo); -} - -void QGstreamerMediaDevices::addDevice(GstDevice *device) -{ - gchar *type = gst_device_get_device_class(device); -// qDebug() << "adding device:" << device << type << gst_device_get_display_name(device) << gst_structure_to_string(gst_device_get_properties(device)); - gst_object_ref(device); - if (!strcmp(type, "Video/Source")) { - m_videoSources.insert(device); - videoInputsChanged(); - } else if (!strcmp(type, "Audio/Source")) { - m_audioSources.insert(device); - audioInputsChanged(); - } else if (!strcmp(type, "Audio/Sink")) { - m_audioSinks.insert(device); - audioOutputsChanged(); - } else { - gst_object_unref(device); - } - g_free(type); -} - -void QGstreamerMediaDevices::removeDevice(GstDevice *device) -{ -// qDebug() << "removing device:" << device << gst_device_get_display_name(device); - if (m_videoSources.remove(device)) { - videoInputsChanged(); - } else if (m_audioSources.remove(device)) { - audioInputsChanged(); - } else if (m_audioSinks.remove(device)) { - audioOutputsChanged(); - } - - gst_object_unref(device); -} - -static GstDevice *getDevice(const QSet<GstDevice *> &devices, const char *key, const QByteArray &id) -{ - GstDevice *gstDevice = nullptr; - for (auto *d : devices) { - QGstStructure properties = gst_device_get_properties(d); - if (!properties.isNull()) { - auto *name = properties[key].toString(); - if (id == name) { - gstDevice = d; - } - } - properties.free(); - if (gstDevice) - break; - } - return gstDevice; - -} - -GstDevice *QGstreamerMediaDevices::audioDevice(const QByteArray &id, QAudioDevice::Mode mode) const -{ - const auto devices = (mode == QAudioDevice::Output) ? m_audioSinks : m_audioSources; - - return getDevice(devices, "sysfs.path", id); -} - -GstDevice *QGstreamerMediaDevices::videoDevice(const QByteArray &id) const -{ - return getDevice(m_videoSources, "device.path", id); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/gstreamer/qgstreamermediadevices_p.h b/src/multimedia/platform/gstreamer/qgstreamermediadevices_p.h deleted file mode 100644 index e3f34433f..000000000 --- a/src/multimedia/platform/gstreamer/qgstreamermediadevices_p.h +++ /dev/null @@ -1,86 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QGSTREAMERMEDIADEVICES_H -#define QGSTREAMERMEDIADEVICES_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediadevices_p.h> -#include <gst/gst.h> -#include <qset.h> -#include <qaudiodevice.h> - -QT_BEGIN_NAMESPACE - -class QGstreamerMediaDevices : public QPlatformMediaDevices -{ -public: - QGstreamerMediaDevices(); - - QList<QAudioDevice> audioInputs() const override; - QList<QAudioDevice> audioOutputs() const override; - QList<QCameraDevice> videoInputs() const override; - QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo) override; - QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo) override; - - void addDevice(GstDevice *); - void removeDevice(GstDevice *); - - GstDevice *audioDevice(const QByteArray &id, QAudioDevice::Mode mode) const; - GstDevice *videoDevice(const QByteArray &id) const; - -private: - QSet<GstDevice *> m_videoSources; - QSet<GstDevice *> m_audioSources; - QSet<GstDevice *> m_audioSinks; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/pulseaudio/qaudioengine_pulse.cpp b/src/multimedia/platform/pulseaudio/qaudioengine_pulse.cpp deleted file mode 100644 index 53ffffe53..000000000 --- a/src/multimedia/platform/pulseaudio/qaudioengine_pulse.cpp +++ /dev/null @@ -1,479 +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$ -** -****************************************************************************/ - -#include <QtCore/qdebug.h> - -#include <qaudiodevice.h> -#include <QTimer> -#include "qaudioengine_pulse_p.h" -#include "qpulseaudiodevice_p.h" -#include "qpulsehelpers_p.h" -#include <sys/types.h> -#include <unistd.h> - -QT_BEGIN_NAMESPACE - -static void serverInfoCallback(pa_context *context, const pa_server_info *info, void *userdata) -{ - if (!info) { - qWarning() << QString::fromLatin1("Failed to get server information: %s").arg(QString::fromUtf8(pa_strerror(pa_context_errno(context)))); - return; - } - -#ifdef DEBUG_PULSE - char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; - - pa_sample_spec_snprint(ss, sizeof(ss), &info->sample_spec); - pa_channel_map_snprint(cm, sizeof(cm), &info->channel_map); - - qDebug() << QString("User name: %1\n" - "Host Name: %2\n" - "Server Name: %3\n" - "Server Version: %4\n" - "Default Sample Specification: %5\n" - "Default Channel Map: %6\n" - "Default Sink: %7\n" - "Default Source: %8\n").arg( - info->user_name, - info->host_name, - info->server_name, - info->server_version, - ss, - cm, - info->default_sink_name, - info->default_source_name); -#endif - - QPulseAudioEngine *pulseEngine = static_cast<QPulseAudioEngine*>(userdata); - pulseEngine->m_serverLock.lockForWrite(); - pulseEngine->m_defaultSink = info->default_sink_name; - pulseEngine->m_defaultSource = info->default_source_name; - // ### ensure the QAudioDevices are updated if default changes and emit changed signal in the device manager - pulseEngine->m_serverLock.unlock(); - - pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); -} - -static void sinkInfoCallback(pa_context *context, const pa_sink_info *info, int isLast, void *userdata) -{ - QPulseAudioEngine *pulseEngine = static_cast<QPulseAudioEngine*>(userdata); - - if (isLast < 0) { - qWarning() << QString::fromLatin1("Failed to get sink information: %s").arg(QString::fromUtf8(pa_strerror(pa_context_errno(context)))); - return; - } - - if (isLast) { - pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); - return; - } - - Q_ASSERT(info); - -#ifdef DEBUG_PULSE - QMap<pa_sink_state, QString> stateMap; - stateMap[PA_SINK_INVALID_STATE] = "n/a"; - stateMap[PA_SINK_RUNNING] = "RUNNING"; - stateMap[PA_SINK_IDLE] = "IDLE"; - stateMap[PA_SINK_SUSPENDED] = "SUSPENDED"; - - qDebug() << QString("Sink #%1\n" - "\tState: %2\n" - "\tName: %3\n" - "\tDescription: %4\n" - ).arg(QString::number(info->index), - stateMap.value(info->state), - info->name, - info->description); -#endif - - QWriteLocker locker(&pulseEngine->m_sinkLock); - bool isDefault = pulseEngine->m_defaultSink == info->name; - auto *dinfo = new QPulseAudioDeviceInfo(info->name, info->description, isDefault, QAudioDevice::Output); - pulseEngine->m_sinks.insert(info->index, dinfo->create()); -} - -static void sourceInfoCallback(pa_context *context, const pa_source_info *info, int isLast, void *userdata) -{ - Q_UNUSED(context); - QPulseAudioEngine *pulseEngine = static_cast<QPulseAudioEngine*>(userdata); - - if (isLast) { - pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); - return; - } - - Q_ASSERT(info); - -#ifdef DEBUG_PULSE - QMap<pa_source_state, QString> stateMap; - stateMap[PA_SOURCE_INVALID_STATE] = "n/a"; - stateMap[PA_SOURCE_RUNNING] = "RUNNING"; - stateMap[PA_SOURCE_IDLE] = "IDLE"; - stateMap[PA_SOURCE_SUSPENDED] = "SUSPENDED"; - - qDebug() << QString("Source #%1\n" - "\tState: %2\n" - "\tName: %3\n" - "\tDescription: %4\n" - ).arg(QString::number(info->index), - stateMap.value(info->state), - info->name, - info->description); -#endif - - QWriteLocker locker(&pulseEngine->m_sourceLock); - // skip monitor channels - if (info->monitor_of_sink != PA_INVALID_INDEX) - return; - bool isDefault = pulseEngine->m_defaultSink == info->name; - auto *dinfo = new QPulseAudioDeviceInfo(info->name, info->description, isDefault, QAudioDevice::Input); - pulseEngine->m_sources.insert(info->index, dinfo->create()); -} - -static void event_cb(pa_context* context, pa_subscription_event_type_t t, uint32_t index, void* userdata) -{ - QPulseAudioEngine *pulseEngine = static_cast<QPulseAudioEngine*>(userdata); - - int type = t & PA_SUBSCRIPTION_EVENT_TYPE_MASK; - int facility = t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK; - - switch (type) { - case PA_SUBSCRIPTION_EVENT_NEW: - case PA_SUBSCRIPTION_EVENT_CHANGE: - switch (facility) { - case PA_SUBSCRIPTION_EVENT_SERVER: { - pa_operation *op = pa_context_get_server_info(context, serverInfoCallback, userdata); - if (op) - pa_operation_unref(op); - else - qWarning("PulseAudioService: failed to get server info"); - break; - } - case PA_SUBSCRIPTION_EVENT_SINK: { - pa_operation *op = pa_context_get_sink_info_by_index(context, index, sinkInfoCallback, userdata); - if (op) - pa_operation_unref(op); - else - qWarning("PulseAudioService: failed to get sink info"); - break; - } - case PA_SUBSCRIPTION_EVENT_SOURCE: { - pa_operation *op = pa_context_get_source_info_by_index(context, index, sourceInfoCallback, userdata); - if (op) - pa_operation_unref(op); - else - qWarning("PulseAudioService: failed to get source info"); - break; - } - default: - break; - } - break; - case PA_SUBSCRIPTION_EVENT_REMOVE: - switch (facility) { - case PA_SUBSCRIPTION_EVENT_SINK: - pulseEngine->m_sinkLock.lockForWrite(); - pulseEngine->m_sinks.remove(index); - pulseEngine->m_sinkLock.unlock(); - break; - case PA_SUBSCRIPTION_EVENT_SOURCE: - pulseEngine->m_sourceLock.lockForWrite(); - pulseEngine->m_sources.remove(index); - pulseEngine->m_sourceLock.unlock(); - break; - default: - break; - } - break; - default: - break; - } -} - -static void contextStateCallbackInit(pa_context *context, void *userdata) -{ - Q_UNUSED(context); -#ifdef DEBUG_PULSE - qDebug() << QPulseAudioInternal::stateToQString(pa_context_get_state(context)); -#endif - QPulseAudioEngine *pulseEngine = reinterpret_cast<QPulseAudioEngine*>(userdata); - pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); -} - -static void contextStateCallback(pa_context *c, void *userdata) -{ - QPulseAudioEngine *self = reinterpret_cast<QPulseAudioEngine*>(userdata); - pa_context_state_t state = pa_context_get_state(c); - -#ifdef DEBUG_PULSE - qDebug() << QPulseAudioInternal::stateToQString(state); -#endif - - if (state == PA_CONTEXT_FAILED) - QMetaObject::invokeMethod(self, "onContextFailed", Qt::QueuedConnection); -} - -Q_GLOBAL_STATIC(QPulseAudioEngine, pulseEngine); - -QPulseAudioEngine::QPulseAudioEngine(QObject *parent) - : QObject(parent) - , m_mainLoopApi(nullptr) - , m_context(nullptr) - , m_prepared(false) -{ - prepare(); -} - -QPulseAudioEngine::~QPulseAudioEngine() -{ - if (m_prepared) - release(); -} - -void QPulseAudioEngine::prepare() -{ - bool keepGoing = true; - bool ok = true; - - m_mainLoop = pa_threaded_mainloop_new(); - if (m_mainLoop == nullptr) { - qWarning("PulseAudioService: unable to create pulseaudio mainloop"); - return; - } - - if (pa_threaded_mainloop_start(m_mainLoop) != 0) { - qWarning("PulseAudioService: unable to start pulseaudio mainloop"); - pa_threaded_mainloop_free(m_mainLoop); - m_mainLoop = nullptr; - return; - } - - m_mainLoopApi = pa_threaded_mainloop_get_api(m_mainLoop); - - lock(); - - m_context = pa_context_new(m_mainLoopApi, QString(QLatin1String("QtPulseAudio:%1")).arg(::getpid()).toLatin1().constData()); - - if (m_context == nullptr) { - qWarning("PulseAudioService: Unable to create new pulseaudio context"); - pa_threaded_mainloop_unlock(m_mainLoop); - pa_threaded_mainloop_free(m_mainLoop); - m_mainLoop = nullptr; - onContextFailed(); - return; - } - - pa_context_set_state_callback(m_context, contextStateCallbackInit, this); - - if (pa_context_connect(m_context, nullptr, (pa_context_flags_t)0, nullptr) < 0) { - qWarning("PulseAudioService: pa_context_connect() failed"); - pa_context_unref(m_context); - pa_threaded_mainloop_unlock(m_mainLoop); - pa_threaded_mainloop_free(m_mainLoop); - m_mainLoop = nullptr; - m_context = nullptr; - return; - } - - pa_threaded_mainloop_wait(m_mainLoop); - - while (keepGoing) { - switch (pa_context_get_state(m_context)) { - case PA_CONTEXT_CONNECTING: - case PA_CONTEXT_AUTHORIZING: - case PA_CONTEXT_SETTING_NAME: - break; - - case PA_CONTEXT_READY: -#ifdef DEBUG_PULSE - qDebug("Connection established."); -#endif - keepGoing = false; - break; - - case PA_CONTEXT_TERMINATED: - qCritical("PulseAudioService: Context terminated."); - keepGoing = false; - ok = false; - break; - - case PA_CONTEXT_FAILED: - default: - qCritical() << QString::fromLatin1("PulseAudioService: Connection failure: %1") - .arg(QString::fromUtf8(pa_strerror(pa_context_errno(m_context)))); - keepGoing = false; - ok = false; - } - - if (keepGoing) - pa_threaded_mainloop_wait(m_mainLoop); - } - - if (ok) { - pa_context_set_state_callback(m_context, contextStateCallback, this); - - pa_context_set_subscribe_callback(m_context, event_cb, this); - pa_operation *op = pa_context_subscribe(m_context, - pa_subscription_mask_t(PA_SUBSCRIPTION_MASK_SINK | - PA_SUBSCRIPTION_MASK_SOURCE | - PA_SUBSCRIPTION_MASK_SERVER), - nullptr, nullptr); - if (op) - pa_operation_unref(op); - else - qWarning("PulseAudioService: failed to subscribe to context notifications"); - } else { - pa_context_unref(m_context); - m_context = nullptr; - } - - unlock(); - - if (ok) { - updateDevices(); - m_prepared = true; - } else { - pa_threaded_mainloop_free(m_mainLoop); - m_mainLoop = nullptr; - onContextFailed(); - } -} - -void QPulseAudioEngine::release() -{ - if (!m_prepared) - return; - - if (m_context) { - pa_context_disconnect(m_context); - pa_context_unref(m_context); - m_context = nullptr; - } - - if (m_mainLoop) { - pa_threaded_mainloop_stop(m_mainLoop); - pa_threaded_mainloop_free(m_mainLoop); - m_mainLoop = nullptr; - } - - m_prepared = false; -} - -void QPulseAudioEngine::updateDevices() -{ - lock(); - - // Get default input and output devices - pa_operation *operation = pa_context_get_server_info(m_context, serverInfoCallback, this); - if (operation) { - while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) - pa_threaded_mainloop_wait(m_mainLoop); - pa_operation_unref(operation); - } else { - qWarning("PulseAudioService: failed to get server info"); - } - - // Get output devices - operation = pa_context_get_sink_info_list(m_context, sinkInfoCallback, this); - if (operation) { - while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) - pa_threaded_mainloop_wait(m_mainLoop); - pa_operation_unref(operation); - } else { - qWarning("PulseAudioService: failed to get sink info"); - } - - // Get input devices - operation = pa_context_get_source_info_list(m_context, sourceInfoCallback, this); - if (operation) { - while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) - pa_threaded_mainloop_wait(m_mainLoop); - pa_operation_unref(operation); - } else { - qWarning("PulseAudioService: failed to get source info"); - } - - unlock(); -} - -void QPulseAudioEngine::onContextFailed() -{ - // Give a chance to the connected slots to still use the Pulse main loop before releasing it. - emit contextFailed(); - - release(); - - // Try to reconnect later - QTimer::singleShot(3000, this, SLOT(prepare())); -} - -QPulseAudioEngine *QPulseAudioEngine::instance() -{ - return pulseEngine(); -} - -QList<QAudioDevice> QPulseAudioEngine::availableDevices(QAudioDevice::Mode mode) const -{ - QList<QAudioDevice> devices; - QByteArray defaultDevice; - - m_serverLock.lockForRead(); - - if (mode == QAudioDevice::Output) { - QReadLocker locker(&m_sinkLock); - devices = m_sinks.values(); - defaultDevice = m_defaultSink; - } else { - QReadLocker locker(&m_sourceLock); - devices = m_sources.values(); - defaultDevice = m_defaultSource; - } - - m_serverLock.unlock(); - - return devices; -} - -QByteArray QPulseAudioEngine::defaultDevice(QAudioDevice::Mode mode) const -{ - return (mode == QAudioDevice::Output) ? m_defaultSink : m_defaultSource; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/pulseaudio/qaudioengine_pulse_p.h b/src/multimedia/platform/pulseaudio/qaudioengine_pulse_p.h deleted file mode 100644 index c384d274b..000000000 --- a/src/multimedia/platform/pulseaudio/qaudioengine_pulse_p.h +++ /dev/null @@ -1,127 +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$ -** -****************************************************************************/ - -#ifndef QPULSEAUDIOENGINE_H -#define QPULSEAUDIOENGINE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qmap.h> -#include <QtCore/qbytearray.h> -#include <QtCore/qreadwritelock.h> -#include <pulse/pulseaudio.h> -#include "qpulsehelpers_p.h" -#include <qaudioformat.h> - -QT_BEGIN_NAMESPACE - -class QPulseAudioEngine : public QObject -{ - Q_OBJECT - -public: - QPulseAudioEngine(QObject *parent = 0); - ~QPulseAudioEngine(); - - static QPulseAudioEngine *instance(); - pa_threaded_mainloop *mainloop() { return m_mainLoop; } - pa_context *context() { return m_context; } - - inline void lock() - { - if (m_mainLoop) - pa_threaded_mainloop_lock(m_mainLoop); - } - - inline void unlock() - { - if (m_mainLoop) - pa_threaded_mainloop_unlock(m_mainLoop); - } - - inline void wait(pa_operation *op) - { - while (m_mainLoop && pa_operation_get_state(op) == PA_OPERATION_RUNNING) - pa_threaded_mainloop_wait(m_mainLoop); - } - - QList<QAudioDevice> availableDevices(QAudioDevice::Mode mode) const; - QByteArray defaultDevice(QAudioDevice::Mode mode) const; - -Q_SIGNALS: - void contextFailed(); - -private Q_SLOTS: - void prepare(); - void onContextFailed(); - -private: - void updateDevices(); - void release(); - -public: - QMap<int, QAudioDevice> m_sinks; - QMap<int, QAudioDevice> m_sources; - - QByteArray m_defaultSink; - QByteArray m_defaultSource; - - mutable QReadWriteLock m_sinkLock; - mutable QReadWriteLock m_sourceLock; - mutable QReadWriteLock m_serverLock; - -private: - pa_mainloop_api *m_mainLoopApi; - pa_threaded_mainloop *m_mainLoop; - pa_context *m_context; - bool m_prepared; - }; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/pulseaudio/qpulseaudiodevice.cpp b/src/multimedia/platform/pulseaudio/qpulseaudiodevice.cpp deleted file mode 100644 index df161ab18..000000000 --- a/src/multimedia/platform/pulseaudio/qpulseaudiodevice.cpp +++ /dev/null @@ -1,87 +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$ -** -****************************************************************************/ - -#include "qpulseaudiodevice_p.h" -#include "qaudioengine_pulse_p.h" -#include "qpulsehelpers_p.h" - -QT_BEGIN_NAMESPACE - -QPulseAudioDeviceInfo::QPulseAudioDeviceInfo(const char *device, const char *desc, bool isDef, QAudioDevice::Mode mode) - : QAudioDevicePrivate(device, mode) -{ - description = QString::fromUtf8(desc); - isDefault = isDef; - - minimumChannelCount = 1; - maximumChannelCount = PA_CHANNELS_MAX; - minimumSampleRate = 1; - maximumSampleRate = PA_RATE_MAX; - - constexpr bool isBigEndian = QSysInfo::ByteOrder == QSysInfo::BigEndian; - - const struct { - pa_sample_format pa_fmt; - QAudioFormat::SampleFormat qt_fmt; - } formatMap[] = { - { PA_SAMPLE_U8, QAudioFormat::UInt8 }, - { isBigEndian ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE, QAudioFormat::Int16 }, - { isBigEndian ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE, QAudioFormat::Int32 }, - { isBigEndian ? PA_SAMPLE_FLOAT32BE : PA_SAMPLE_FLOAT32LE, QAudioFormat::Float }, - }; - - pa_sample_spec spec; - spec.channels = 1; - spec.rate = 48000; - - for (const auto &f : formatMap) { - spec.format = f.pa_fmt; - if (pa_sample_spec_valid(&spec) != 0) - supportedSampleFormats.append(f.qt_fmt); - } - - preferredFormat.setChannelCount(2); - preferredFormat.setSampleRate(48000); - QAudioFormat::SampleFormat f = QAudioFormat::Int16; - if (!supportedSampleFormats.contains(f)) - f = supportedSampleFormats.value(0, QAudioFormat::Unknown); - preferredFormat.setSampleFormat(f); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/pulseaudio/qpulseaudiodevice_p.h b/src/multimedia/platform/pulseaudio/qpulseaudiodevice_p.h deleted file mode 100644 index 111134614..000000000 --- a/src/multimedia/platform/pulseaudio/qpulseaudiodevice_p.h +++ /dev/null @@ -1,75 +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$ -** -****************************************************************************/ - -#ifndef QAUDIODEVICEINFOPULSE_H -#define QAUDIODEVICEINFOPULSE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qbytearray.h> -#include <QtCore/qstringlist.h> -#include <QtCore/qlist.h> - -#include "qaudio.h" -#include "qaudiodevice.h" -#include <private/qaudiosystem_p.h> -#include <private/qaudiodevice_p.h> - -QT_BEGIN_NAMESPACE - -class QPulseAudioDeviceInfo : public QAudioDevicePrivate -{ -public: - QPulseAudioDeviceInfo(const char *device, const char *description, bool isDefault, QAudioDevice::Mode mode); - ~QPulseAudioDeviceInfo() {} -}; - -QT_END_NAMESPACE - -#endif - diff --git a/src/multimedia/platform/pulseaudio/qpulseaudiointegration.cpp b/src/multimedia/platform/pulseaudio/qpulseaudiointegration.cpp deleted file mode 100644 index f68f2934a..000000000 --- a/src/multimedia/platform/pulseaudio/qpulseaudiointegration.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qpulseaudiointegration_p.h" -#include "qpulseaudiomediadevices_p.h" - -QT_BEGIN_NAMESPACE - -QPulseAudioIntegration::QPulseAudioIntegration() -{ - pulseEngine = new QPulseAudioEngine; -} - -QPulseAudioIntegration::~QPulseAudioIntegration() -{ - delete m_devices; -} - -QPlatformMediaDevices *QPulseAudioIntegration::devices() -{ - if (!m_devices) - m_devices = new QPulseAudioMediaDevices(pulseEngine); - return m_devices; -} - -QPlatformMediaFormatInfo *QPulseAudioIntegration::formatInfo() -{ - Q_ASSERT(!"In need of implementation"); // TODO - return nullptr; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/pulseaudio/qpulseaudiointegration_p.h b/src/multimedia/platform/pulseaudio/qpulseaudiointegration_p.h deleted file mode 100644 index eb83e6856..000000000 --- a/src/multimedia/platform/pulseaudio/qpulseaudiointegration_p.h +++ /dev/null @@ -1,76 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QPULSEAUDIOINTEGRATION_H -#define QPULSEAUDIOINTEGRATION_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediaintegration_p.h> -#include <private/qaudioengine_pulse_p.h> - -QT_BEGIN_NAMESPACE - -class QPulseAudioMediaDevices; - -class QPulseAudioIntegration : public QPlatformMediaIntegration -{ -public: - QPulseAudioIntegration(); - ~QPulseAudioIntegration(); - - QPlatformMediaDevices *devices() override; - QPlatformMediaFormatInfo *formatInfo() override; - - QPulseAudioMediaDevices *m_devices = nullptr; - QPulseAudioEngine *pulseEngine = nullptr; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/pulseaudio/qpulseaudiomediadevices.cpp b/src/multimedia/platform/pulseaudio/qpulseaudiomediadevices.cpp deleted file mode 100644 index 4c3c561b4..000000000 --- a/src/multimedia/platform/pulseaudio/qpulseaudiomediadevices.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qpulseaudiomediadevices_p.h" -#include "qmediadevices.h" -#include "qcameradevice_p.h" - -#include "private/qpulseaudiosource_p.h" -#include "private/qpulseaudiosink_p.h" -#include "private/qpulseaudiodevice_p.h" -#include "private/qaudioengine_pulse_p.h" - -QT_BEGIN_NAMESPACE - -QPulseAudioMediaDevices::QPulseAudioMediaDevices(QPulseAudioEngine *engine) - : QPlatformMediaDevices(), - pulseEngine(engine) -{ -} - -QList<QAudioDevice> QPulseAudioMediaDevices::audioInputs() const -{ - return pulseEngine->availableDevices(QAudioDevice::Input); -} - -QList<QAudioDevice> QPulseAudioMediaDevices::audioOutputs() const -{ - return pulseEngine->availableDevices(QAudioDevice::Output); -} - -QList<QCameraDevice> QPulseAudioMediaDevices::videoInputs() const -{ - return {}; -} - -QPlatformAudioSource *QPulseAudioMediaDevices::createAudioSource(const QAudioDevice &deviceInfo) -{ - return new QPulseAudioSource(deviceInfo.id()); -} - -QPlatformAudioSink *QPulseAudioMediaDevices::createAudioSink(const QAudioDevice &deviceInfo) -{ - return new QPulseAudioSink(deviceInfo.id()); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/pulseaudio/qpulseaudiomediadevices_p.h b/src/multimedia/platform/pulseaudio/qpulseaudiomediadevices_p.h deleted file mode 100644 index 4cb28056f..000000000 --- a/src/multimedia/platform/pulseaudio/qpulseaudiomediadevices_p.h +++ /dev/null @@ -1,79 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QPULSEAUDIOMEDIADEVICES_H -#define QPULSEAUDIOMEDIADEVICES_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediadevices_p.h> -#include <qset.h> -#include <qaudio.h> - -QT_BEGIN_NAMESPACE - -class QPulseAudioEngine; - -class QPulseAudioMediaDevices : public QPlatformMediaDevices -{ -public: - QPulseAudioMediaDevices(QPulseAudioEngine *engine); - - QList<QAudioDevice> audioInputs() const override; - QList<QAudioDevice> audioOutputs() const override; - QList<QCameraDevice> videoInputs() const override; - QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo) override; - QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo) override; - -private: - QPulseAudioEngine *pulseEngine; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/pulseaudio/qpulseaudiosink.cpp b/src/multimedia/platform/pulseaudio/qpulseaudiosink.cpp deleted file mode 100644 index 87779e5a7..000000000 --- a/src/multimedia/platform/pulseaudio/qpulseaudiosink.cpp +++ /dev/null @@ -1,709 +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$ -** -****************************************************************************/ - -#include <QtCore/qcoreapplication.h> -#include <QtCore/qdebug.h> -#include <QtCore/qmath.h> -#include <private/qaudiohelpers_p.h> - -#include "qpulseaudiosink_p.h" -#include "qpulseaudiodevice_p.h" -#include "qaudioengine_pulse_p.h" -#include "qpulsehelpers_p.h" -#include <sys/types.h> -#include <unistd.h> - -QT_BEGIN_NAMESPACE - -const int PeriodTimeMs = 20; -const int LowLatencyPeriodTimeMs = 10; -const int LowLatencyBufferSizeMs = 40; - -#define LOW_LATENCY_CATEGORY_NAME "game" - -static void outputStreamWriteCallback(pa_stream *stream, size_t length, void *userdata) -{ - Q_UNUSED(stream); - Q_UNUSED(length); - Q_UNUSED(userdata); - QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); -} - -static void outputStreamStateCallback(pa_stream *stream, void *userdata) -{ - Q_UNUSED(userdata); - pa_stream_state_t state = pa_stream_get_state(stream); -#ifdef DEBUG_PULSE - qDebug() << "Stream state: " << QPulseAudioInternal::stateToQString(state); -#endif - switch (state) { - case PA_STREAM_CREATING: - case PA_STREAM_READY: - case PA_STREAM_TERMINATED: - break; - - case PA_STREAM_FAILED: - default: - qWarning() << QString::fromLatin1("Stream error: %1").arg(QString::fromUtf8(pa_strerror(pa_context_errno(pa_stream_get_context(stream))))); - QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); - break; - } -} - -static void outputStreamUnderflowCallback(pa_stream *stream, void *userdata) -{ - Q_UNUSED(stream); - ((QPulseAudioSink*)userdata)->streamUnderflowCallback(); -} - -static void outputStreamOverflowCallback(pa_stream *stream, void *userdata) -{ - Q_UNUSED(stream); - Q_UNUSED(userdata); - qWarning() << "Got a buffer overflow!"; -} - -static void outputStreamLatencyCallback(pa_stream *stream, void *userdata) -{ - Q_UNUSED(stream); - Q_UNUSED(userdata); - -#ifdef DEBUG_PULSE - const pa_timing_info *info = pa_stream_get_timing_info(stream); - - qDebug() << "Write index corrupt: " << info->write_index_corrupt; - qDebug() << "Write index: " << info->write_index; - qDebug() << "Read index corrupt: " << info->read_index_corrupt; - qDebug() << "Read index: " << info->read_index; - qDebug() << "Sink usec: " << info->sink_usec; - qDebug() << "Configured sink usec: " << info->configured_sink_usec; -#endif -} - -static void outputStreamSuccessCallback(pa_stream *stream, int success, void *userdata) -{ - Q_UNUSED(stream); - Q_UNUSED(success); - Q_UNUSED(userdata); - - QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); -} - -static void outputStreamDrainComplete(pa_stream *stream, int success, void *userdata) -{ - Q_UNUSED(stream); - Q_UNUSED(success); - Q_UNUSED(userdata); - -#ifdef DEBUG_PULSE - qDebug() << "Draining completed successfully: " << (bool)success; -#endif -} - -static void streamAdjustPrebufferCallback(pa_stream *stream, int success, void *userdata) -{ - Q_UNUSED(stream); - Q_UNUSED(success); - Q_UNUSED(userdata); - -#ifdef DEBUG_PULSE - qDebug() << "Adjust prebuffer completed successfully: " << (bool)success; -#endif -} - - -QPulseAudioSink::QPulseAudioSink(const QByteArray &device) - : m_device(device) - , m_errorState(QAudio::NoError) - , m_deviceState(QAudio::StoppedState) - , m_pullMode(true) - , m_opened(false) - , m_audioSource(nullptr) - , m_periodTime(0) - , m_stream(nullptr) - , m_periodSize(0) - , m_bufferSize(0) - , m_maxBufferSize(0) - , m_totalTimeValue(0) - , m_tickTimer(new QTimer(this)) - , m_audioBuffer(nullptr) - , m_resuming(false) - , m_volume(1.0) -{ - connect(m_tickTimer, SIGNAL(timeout()), SLOT(userFeed())); -} - -QPulseAudioSink::~QPulseAudioSink() -{ - close(); - disconnect(m_tickTimer, SIGNAL(timeout())); - QCoreApplication::processEvents(); -} - -void QPulseAudioSink::setError(QAudio::Error error) -{ - if (m_errorState == error) - return; - - m_errorState = error; - emit errorChanged(error); -} - -QAudio::Error QPulseAudioSink::error() const -{ - return m_errorState; -} - -void QPulseAudioSink::setState(QAudio::State state) -{ - if (m_deviceState == state) - return; - - m_deviceState = state; - emit stateChanged(state); -} - -QAudio::State QPulseAudioSink::state() const -{ - return m_deviceState; -} - -void QPulseAudioSink::streamUnderflowCallback() -{ - if (m_deviceState != QAudio::IdleState && !m_resuming) { - setError(QAudio::UnderrunError); - setState(QAudio::IdleState); - } -} - -void QPulseAudioSink::start(QIODevice *device) -{ - setState(QAudio::StoppedState); - setError(QAudio::NoError); - - // Handle change of mode - if (m_audioSource && !m_pullMode) { - delete m_audioSource; - } - m_audioSource = nullptr; - - close(); - - m_pullMode = true; - m_audioSource = device; - - if (!open()) { - m_audioSource = nullptr; - return; - } - - setState(QAudio::ActiveState); -} - -QIODevice *QPulseAudioSink::start() -{ - setState(QAudio::StoppedState); - setError(QAudio::NoError); - - // Handle change of mode - if (m_audioSource && !m_pullMode) { - delete m_audioSource; - } - m_audioSource = nullptr; - - close(); - - m_pullMode = false; - - if (!open()) - return nullptr; - - m_audioSource = new PulseOutputPrivate(this); - m_audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered); - - setState(QAudio::IdleState); - - return m_audioSource; -} - -bool QPulseAudioSink::open() -{ - if (m_opened) - return true; - - QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - - if (!pulseEngine->context() || pa_context_get_state(pulseEngine->context()) != PA_CONTEXT_READY) { - setError(QAudio::FatalError); - setState(QAudio::StoppedState); - emit stateChanged(m_deviceState); - return false; - } - - pa_sample_spec spec = QPulseAudioInternal::audioFormatToSampleSpec(m_format); - - if (!pa_sample_spec_valid(&spec)) { - setError(QAudio::OpenError); - setState(QAudio::StoppedState); - emit stateChanged(m_deviceState); - return false; - } - - m_spec = spec; - m_totalTimeValue = 0; - - if (m_streamName.isNull()) - m_streamName = QString(QLatin1String("QtmPulseStream-%1-%2")).arg(::getpid()).arg(quintptr(this)).toUtf8(); - -#ifdef DEBUG_PULSE - qDebug() << "Format: " << QPulseAudioInternal::sampleFormatToQString(spec.format); - qDebug() << "Rate: " << spec.rate; - qDebug() << "Channels: " << spec.channels; - qDebug() << "Frame size: " << pa_frame_size(&spec); -#endif - - pulseEngine->lock(); - - - pa_proplist *propList = pa_proplist_new(); -#if 0 - qint64 bytesPerSecond = m_format.sampleRate() * m_format.bytesPerFrame(); - static const char *mediaRoleFromAudioRole[] = { - nullptr, // UnknownRole - "music", // MusicRole - "video", // VideoRole - "phone", // VoiceCommunicationRole - "event", // AlarmRole - "event", // NotificationRole - "phone", // RingtoneRole - "a11y", // AccessibilityRole - nullptr, // SonificationRole - "game" // GameRole - }; - - const char *r = mediaRoleFromAudioRole[m_role]; - if (r) - pa_proplist_sets(propList, PA_PROP_MEDIA_ROLE, r); -#endif - - static const auto mapName = qEnvironmentVariable("QT_PA_CHANNEL_MAP"); - pa_channel_map_def_t mapDef = PA_CHANNEL_MAP_DEFAULT; - if (mapName == QLatin1String("ALSA")) - mapDef = PA_CHANNEL_MAP_ALSA; - else if (mapName == QLatin1String("AUX")) - mapDef = PA_CHANNEL_MAP_AUX; - else if (mapName == QLatin1String("WAVEEX")) - mapDef = PA_CHANNEL_MAP_WAVEEX; - else if (mapName == QLatin1String("OSS")) - mapDef = PA_CHANNEL_MAP_OSS; - else if (!mapName.isEmpty()) - qWarning() << "Unknown pulse audio channel mapping definition:" << mapName; - - pa_channel_map m; - auto channelMap = pa_channel_map_init_extend(&m, m_spec.channels, mapDef); - if (!channelMap) - qWarning() << "QAudioSink: pa_channel_map_init_extend() Could not initialize channel map"; - - m_stream = pa_stream_new_with_proplist(pulseEngine->context(), m_streamName.constData(), &m_spec, channelMap, propList); - if (!m_stream) { - qWarning() << "QAudioSink: pa_stream_new_with_proplist() failed!"; - pulseEngine->unlock(); - setError(QAudio::OpenError); - setState(QAudio::StoppedState); - emit stateChanged(m_deviceState); - return false; - } - - pa_proplist_free(propList); - - pa_stream_set_state_callback(m_stream, outputStreamStateCallback, this); - pa_stream_set_write_callback(m_stream, outputStreamWriteCallback, this); - - pa_stream_set_underflow_callback(m_stream, outputStreamUnderflowCallback, this); - pa_stream_set_overflow_callback(m_stream, outputStreamOverflowCallback, this); - pa_stream_set_latency_update_callback(m_stream, outputStreamLatencyCallback, this); - -// if (m_bufferSize <= 0 && m_role == QAudio::GameRoleRole) -// m_bufferSize = bytesPerSecond * LowLatencyBufferSizeMs / qint64(1000); - - pa_buffer_attr requestedBuffer; - requestedBuffer.fragsize = (uint32_t)-1; - requestedBuffer.maxlength = (uint32_t)-1; - requestedBuffer.minreq = (uint32_t)-1; - requestedBuffer.prebuf = (uint32_t)-1; - requestedBuffer.tlength = m_bufferSize; - - if (pa_stream_connect_playback(m_stream, m_device.data(), (m_bufferSize > 0) ? &requestedBuffer : nullptr, (pa_stream_flags_t)0, nullptr, nullptr) < 0) { - qWarning() << "pa_stream_connect_playback() failed!"; - pa_stream_unref(m_stream); - m_stream = nullptr; - pulseEngine->unlock(); - setError(QAudio::OpenError); - setState(QAudio::StoppedState); - emit stateChanged(m_deviceState); - return false; - } - - while (pa_stream_get_state(m_stream) != PA_STREAM_READY) - pa_threaded_mainloop_wait(pulseEngine->mainloop()); - - const pa_buffer_attr *buffer = pa_stream_get_buffer_attr(m_stream); - m_periodTime = /*(m_role == QAudio::GameRole) ? LowLatencyPeriodTimeMs :*/ PeriodTimeMs; - m_periodSize = pa_usec_to_bytes(m_periodTime*1000, &m_spec); - m_bufferSize = buffer->tlength; - m_maxBufferSize = buffer->maxlength; - m_audioBuffer = new char[m_maxBufferSize]; - - const qint64 streamSize = m_audioSource ? m_audioSource->size() : 0; - if (m_pullMode && streamSize > 0 && static_cast<qint64>(buffer->prebuf) > streamSize) { - pa_buffer_attr newBufferAttr; - newBufferAttr = *buffer; - newBufferAttr.prebuf = streamSize; - pa_operation *o = pa_stream_set_buffer_attr(m_stream, &newBufferAttr, streamAdjustPrebufferCallback, nullptr); - if (o) - pa_operation_unref(o); - } - -#ifdef DEBUG_PULSE - qDebug() << "Buffering info:"; - qDebug() << "\tMax length: " << buffer->maxlength; - qDebug() << "\tTarget length: " << buffer->tlength; - qDebug() << "\tPre-buffering: " << buffer->prebuf; - qDebug() << "\tMinimum request: " << buffer->minreq; - qDebug() << "\tFragment size: " << buffer->fragsize; -#endif - - pulseEngine->unlock(); - - connect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioSink::onPulseContextFailed); - - m_opened = true; - - m_tickTimer->start(m_periodTime); - - m_elapsedTimeOffset = 0; - - return true; -} - -void QPulseAudioSink::close() -{ - if (!m_opened) - return; - - m_tickTimer->stop(); - - QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - - if (m_stream) { - pulseEngine->lock(); - - pa_stream_set_state_callback(m_stream, nullptr, nullptr); - pa_stream_set_write_callback(m_stream, nullptr, nullptr); - pa_stream_set_underflow_callback(m_stream, nullptr, nullptr); - pa_stream_set_overflow_callback(m_stream, nullptr, nullptr); - pa_stream_set_latency_update_callback(m_stream, nullptr, nullptr); - - pa_operation *o = pa_stream_drain(m_stream, outputStreamDrainComplete, nullptr); - if (o) - pa_operation_unref(o); - - pa_stream_disconnect(m_stream); - pa_stream_unref(m_stream); - m_stream = nullptr; - - pulseEngine->unlock(); - } - - disconnect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioSink::onPulseContextFailed); - - if (!m_pullMode && m_audioSource) { - delete m_audioSource; - m_audioSource = nullptr; - } - m_opened = false; - if (m_audioBuffer) { - delete[] m_audioBuffer; - m_audioBuffer = nullptr; - } -} - -void QPulseAudioSink::userFeed() -{ - if (m_deviceState == QAudio::StoppedState || m_deviceState == QAudio::SuspendedState) - return; - - m_resuming = false; - - if (m_pullMode) { - int writableSize = bytesFree(); - int chunks = writableSize / m_periodSize; - if (chunks == 0) - return; - - int input = m_periodSize; // always request 1 chunk of data from user - if (input > m_maxBufferSize) - input = m_maxBufferSize; - - int audioBytesPulled = m_audioSource->read(m_audioBuffer, input); - Q_ASSERT(audioBytesPulled <= input); - if (m_audioBuffer && audioBytesPulled > 0) { - if (audioBytesPulled > input) { - qWarning() << "QPulseAudioSink::userFeed() - Invalid audio data size provided from user:" - << audioBytesPulled << "should be less than" << input; - audioBytesPulled = input; - } - qint64 bytesWritten = write(m_audioBuffer, audioBytesPulled); - Q_ASSERT(bytesWritten == audioBytesPulled); //unfinished write should not happen since the data provided is less than writableSize - Q_UNUSED(bytesWritten); - - if (chunks > 1) { - // PulseAudio needs more data. Ask for it immediately. - QMetaObject::invokeMethod(this, "userFeed", Qt::QueuedConnection); - } - } - } - - if (m_deviceState != QAudio::ActiveState) - return; -} - -qint64 QPulseAudioSink::write(const char *data, qint64 len) -{ - QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - - pulseEngine->lock(); - - len = qMin(len, static_cast<qint64>(pa_stream_writable_size(m_stream))); - - if (m_volume < 1.0f) { - // Don't use PulseAudio volume, as it might affect all other streams of the same category - // or even affect the system volume if flat volumes are enabled - void *dest = nullptr; - size_t nbytes = len; - if (pa_stream_begin_write(m_stream, &dest, &nbytes) < 0) { - qWarning("QAudioSink(pulseaudio): pa_stream_begin_write, error = %s", - pa_strerror(pa_context_errno(pulseEngine->context()))); - setError(QAudio::IOError); - return 0; - } - - len = int(nbytes); - QAudioHelperInternal::qMultiplySamples(m_volume, m_format, data, dest, len); - data = reinterpret_cast<char *>(dest); - } - - if (pa_stream_write(m_stream, data, len, nullptr, 0, PA_SEEK_RELATIVE) < 0) { - qWarning("QAudioSink(pulseaudio): pa_stream_write, error = %s", - pa_strerror(pa_context_errno(pulseEngine->context()))); - setError(QAudio::IOError); - return 0; - } - - pulseEngine->unlock(); - m_totalTimeValue += len; - - setError(QAudio::NoError); - setState(QAudio::ActiveState); - - return len; -} - -void QPulseAudioSink::stop() -{ - if (m_deviceState == QAudio::StoppedState) - return; - - close(); - - setError(QAudio::NoError); - setState(QAudio::StoppedState); -} - -qsizetype QPulseAudioSink::bytesFree() const -{ - if (m_deviceState != QAudio::ActiveState && m_deviceState != QAudio::IdleState) - return 0; - - QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - pulseEngine->lock(); - int writableSize = pa_stream_writable_size(m_stream); - pulseEngine->unlock(); - return writableSize; -} - -void QPulseAudioSink::setBufferSize(qsizetype value) -{ - m_bufferSize = value; -} - -qsizetype QPulseAudioSink::bufferSize() const -{ - return m_bufferSize; -} - -qint64 QPulseAudioSink::processedUSecs() const -{ - qint64 result = qint64(1000000) * m_totalTimeValue / m_format.bytesPerFrame() / m_format.sampleRate(); - - return result; -} - -void QPulseAudioSink::resume() -{ - if (m_deviceState == QAudio::SuspendedState) { - m_resuming = true; - - QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - - pulseEngine->lock(); - - pa_operation *operation = pa_stream_cork(m_stream, 0, outputStreamSuccessCallback, nullptr); - pulseEngine->wait(operation); - pa_operation_unref(operation); - - operation = pa_stream_trigger(m_stream, outputStreamSuccessCallback, nullptr); - pulseEngine->wait(operation); - pa_operation_unref(operation); - - pulseEngine->unlock(); - - m_tickTimer->start(m_periodTime); - - setState(m_pullMode ? QAudio::ActiveState : QAudio::IdleState); - setError(QAudio::NoError); - } -} - -void QPulseAudioSink::setFormat(const QAudioFormat &format) -{ - m_format = format; -} - -QAudioFormat QPulseAudioSink::format() const -{ - return m_format; -} - -void QPulseAudioSink::suspend() -{ - if (m_deviceState == QAudio::ActiveState || m_deviceState == QAudio::IdleState) { - setError(QAudio::NoError); - setState(QAudio::SuspendedState); - - m_tickTimer->stop(); - - QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - pa_operation *operation; - - pulseEngine->lock(); - - operation = pa_stream_cork(m_stream, 1, outputStreamSuccessCallback, nullptr); - pulseEngine->wait(operation); - pa_operation_unref(operation); - - pulseEngine->unlock(); - } -} - -void QPulseAudioSink::reset() -{ - stop(); -} - -PulseOutputPrivate::PulseOutputPrivate(QPulseAudioSink *audio) -{ - m_audioDevice = qobject_cast<QPulseAudioSink*>(audio); -} - -qint64 PulseOutputPrivate::readData(char *data, qint64 len) -{ - Q_UNUSED(data); - Q_UNUSED(len); - - return 0; -} - -qint64 PulseOutputPrivate::writeData(const char *data, qint64 len) -{ - int retry = 0; - qint64 written = 0; - - if ((m_audioDevice->m_deviceState == QAudio::ActiveState - || m_audioDevice->m_deviceState == QAudio::IdleState)) { - while(written < len) { - int chunk = m_audioDevice->write(data+written, (len-written)); - if (chunk <= 0) - retry++; - written+=chunk; - if (retry > 10) - return written; - } - } - - return written; -} - -void QPulseAudioSink::setVolume(qreal vol) -{ - if (qFuzzyCompare(m_volume, vol)) - return; - - m_volume = qBound(qreal(0), vol, qreal(1)); -} - -qreal QPulseAudioSink::volume() const -{ - return m_volume; -} - -void QPulseAudioSink::onPulseContextFailed() -{ - close(); - - setError(QAudio::FatalError); - setState(QAudio::StoppedState); -} - -QT_END_NAMESPACE - -#include "moc_qpulseaudiosink_p.cpp" diff --git a/src/multimedia/platform/pulseaudio/qpulseaudiosink_p.h b/src/multimedia/platform/pulseaudio/qpulseaudiosink_p.h deleted file mode 100644 index 9397c507a..000000000 --- a/src/multimedia/platform/pulseaudio/qpulseaudiosink_p.h +++ /dev/null @@ -1,155 +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$ -** -****************************************************************************/ - -#ifndef QAUDIOOUTPUTPULSE_H -#define QAUDIOOUTPUTPULSE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qfile.h> -#include <QtCore/qtimer.h> -#include <QtCore/qstring.h> -#include <QtCore/qstringlist.h> -#include <QtCore/qelapsedtimer.h> -#include <QtCore/qiodevice.h> - -#include "qaudio.h" -#include "qaudiodevice.h" -#include <private/qaudiosystem_p.h> - -#include <pulse/pulseaudio.h> - -QT_BEGIN_NAMESPACE - -class QPulseAudioSink : public QPlatformAudioSink -{ - friend class PulseOutputPrivate; - Q_OBJECT - -public: - QPulseAudioSink(const QByteArray &device); - ~QPulseAudioSink(); - - void start(QIODevice *device) override; - QIODevice *start() override; - void stop() override; - void reset() override; - void suspend() override; - void resume() override; - qsizetype bytesFree() const override; - void setBufferSize(qsizetype value) override; - qsizetype bufferSize() const override; - qint64 processedUSecs() const override; - QAudio::Error error() const override; - QAudio::State state() const override; - void setFormat(const QAudioFormat &format) override; - QAudioFormat format() const override; - - void setVolume(qreal volume) override; - qreal volume() const override; - -public: - void streamUnderflowCallback(); - -private: - void setState(QAudio::State state); - void setError(QAudio::Error error); - - bool open(); - void close(); - qint64 write(const char *data, qint64 len); - -private Q_SLOTS: - void userFeed(); - void onPulseContextFailed(); - -private: - QByteArray m_device; - QByteArray m_streamName; - QAudioFormat m_format; - QAudio::Error m_errorState; - QAudio::State m_deviceState; - bool m_pullMode; - bool m_opened; - QIODevice *m_audioSource; - QTimer m_periodTimer; - int m_periodTime; - pa_stream *m_stream; - int m_periodSize; - int m_bufferSize; - int m_maxBufferSize; - qint64 m_totalTimeValue; - QTimer *m_tickTimer; - char *m_audioBuffer; - qint64 m_elapsedTimeOffset; - bool m_resuming; - - qreal m_volume; - pa_sample_spec m_spec; -}; - -class PulseOutputPrivate : public QIODevice -{ - friend class QPulseAudioSink; - Q_OBJECT - -public: - PulseOutputPrivate(QPulseAudioSink *audio); - virtual ~PulseOutputPrivate() {} - -protected: - qint64 readData(char *data, qint64 len) override; - qint64 writeData(const char *data, qint64 len) override; - -private: - QPulseAudioSink *m_audioDevice; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/pulseaudio/qpulseaudiosource.cpp b/src/multimedia/platform/pulseaudio/qpulseaudiosource.cpp deleted file mode 100644 index a7a0d4702..000000000 --- a/src/multimedia/platform/pulseaudio/qpulseaudiosource.cpp +++ /dev/null @@ -1,645 +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$ -** -****************************************************************************/ - -#include <QtCore/qcoreapplication.h> -#include <QtCore/qdebug.h> -#include <QtCore/qmath.h> -#include <private/qaudiohelpers_p.h> - -#include "qpulseaudiosource_p.h" -#include "qaudioengine_pulse_p.h" -#include "qpulseaudiodevice_p.h" -#include "qpulsehelpers_p.h" -#include <sys/types.h> -#include <unistd.h> - -QT_BEGIN_NAMESPACE - -const int PeriodTimeMs = 50; - -static void inputStreamReadCallback(pa_stream *stream, size_t length, void *userdata) -{ - Q_UNUSED(userdata); - Q_UNUSED(length); - Q_UNUSED(stream); - QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); -} - -static void inputStreamStateCallback(pa_stream *stream, void *userdata) -{ - Q_UNUSED(userdata); - pa_stream_state_t state = pa_stream_get_state(stream); -#ifdef DEBUG_PULSE - qDebug() << "Stream state: " << QPulseAudioInternal::stateToQString(state); -#endif - switch (state) { - case PA_STREAM_CREATING: - break; - case PA_STREAM_READY: { -#ifdef DEBUG_PULSE - QPulseAudioSource *audioInput = static_cast<QPulseAudioSource*>(userdata); - const pa_buffer_attr *buffer_attr = pa_stream_get_buffer_attr(stream); - qDebug() << "*** maxlength: " << buffer_attr->maxlength; - qDebug() << "*** prebuf: " << buffer_attr->prebuf; - qDebug() << "*** fragsize: " << buffer_attr->fragsize; - qDebug() << "*** minreq: " << buffer_attr->minreq; - qDebug() << "*** tlength: " << buffer_attr->tlength; - - pa_sample_spec spec = QPulseAudioInternal::audioFormatToSampleSpec(audioInput->format()); - qDebug() << "*** bytes_to_usec: " << pa_bytes_to_usec(buffer_attr->fragsize, &spec); -#endif - } - break; - case PA_STREAM_TERMINATED: - break; - case PA_STREAM_FAILED: - default: - qWarning() << QString::fromLatin1("Stream error: %1").arg(QString::fromUtf8(pa_strerror(pa_context_errno(pa_stream_get_context(stream))))); - QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); - break; - } -} - -static void inputStreamUnderflowCallback(pa_stream *stream, void *userdata) -{ - Q_UNUSED(userdata); - Q_UNUSED(stream); - qWarning() << "Got a buffer underflow!"; -} - -static void inputStreamOverflowCallback(pa_stream *stream, void *userdata) -{ - Q_UNUSED(stream); - Q_UNUSED(userdata); - qWarning() << "Got a buffer overflow!"; -} - -static void inputStreamSuccessCallback(pa_stream *stream, int success, void *userdata) -{ - Q_UNUSED(stream); - Q_UNUSED(userdata); - Q_UNUSED(success); - - //if (!success) - //TODO: Is cork success? i->operation_success = success; - - QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); -} - -QPulseAudioSource::QPulseAudioSource(const QByteArray &device) - : m_totalTimeValue(0) - , m_audioSource(nullptr) - , m_errorState(QAudio::NoError) - , m_deviceState(QAudio::StoppedState) - , m_volume(qreal(1.0f)) - , m_pullMode(true) - , m_opened(false) - , m_bytesAvailable(0) - , m_bufferSize(0) - , m_periodSize(0) - , m_periodTime(PeriodTimeMs) - , m_stream(nullptr) - , m_device(device) -{ - m_timer = new QTimer(this); - connect(m_timer, SIGNAL(timeout()), SLOT(userFeed())); -} - -QPulseAudioSource::~QPulseAudioSource() -{ - close(); - disconnect(m_timer, SIGNAL(timeout())); - QCoreApplication::processEvents(); - delete m_timer; -} - -void QPulseAudioSource::setError(QAudio::Error error) -{ - if (m_errorState == error) - return; - - m_errorState = error; - emit errorChanged(error); -} - -QAudio::Error QPulseAudioSource::error() const -{ - return m_errorState; -} - -void QPulseAudioSource::setState(QAudio::State state) -{ - if (m_deviceState == state) - return; - - m_deviceState = state; - emit stateChanged(state); -} - -QAudio::State QPulseAudioSource::state() const -{ - return m_deviceState; -} - -void QPulseAudioSource::setFormat(const QAudioFormat &format) -{ - if (m_deviceState == QAudio::StoppedState) - m_format = format; -} - -QAudioFormat QPulseAudioSource::format() const -{ - return m_format; -} - -void QPulseAudioSource::start(QIODevice *device) -{ - setState(QAudio::StoppedState); - setError(QAudio::NoError); - - if (!m_pullMode && m_audioSource) { - delete m_audioSource; - m_audioSource = nullptr; - } - - close(); - - if (!open()) - return; - - m_pullMode = true; - m_audioSource = device; - - setState(QAudio::ActiveState); -} - -QIODevice *QPulseAudioSource::start() -{ - setState(QAudio::StoppedState); - setError(QAudio::NoError); - - if (!m_pullMode && m_audioSource) { - delete m_audioSource; - m_audioSource = nullptr; - } - - close(); - - if (!open()) - return nullptr; - - m_pullMode = false; - m_audioSource = new PulseInputPrivate(this); - m_audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered); - - setState(QAudio::IdleState); - - return m_audioSource; -} - -void QPulseAudioSource::stop() -{ - if (m_deviceState == QAudio::StoppedState) - return; - - close(); - - setError(QAudio::NoError); - setState(QAudio::StoppedState); -} - -bool QPulseAudioSource::open() -{ - if (m_opened) - return true; - - QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - - if (!pulseEngine->context() || pa_context_get_state(pulseEngine->context()) != PA_CONTEXT_READY) { - setError(QAudio::FatalError); - setState(QAudio::StoppedState); - return false; - } - - pa_sample_spec spec = QPulseAudioInternal::audioFormatToSampleSpec(m_format); - - if (!pa_sample_spec_valid(&spec)) { - setError(QAudio::OpenError); - setState(QAudio::StoppedState); - return false; - } - - m_spec = spec; - -#ifdef DEBUG_PULSE -// QTime now(QTime::currentTime()); -// qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; -#endif - - if (m_streamName.isNull()) - m_streamName = QString(QLatin1String("QtmPulseStream-%1-%2")).arg(::getpid()).arg(quintptr(this)).toUtf8(); - -#ifdef DEBUG_PULSE - qDebug() << "Format: " << QPulseAudioInternal::sampleFormatToQString(spec.format); - qDebug() << "Rate: " << spec.rate; - qDebug() << "Channels: " << spec.channels; - qDebug() << "Frame size: " << pa_frame_size(&spec); -#endif - - pulseEngine->lock(); - pa_channel_map channel_map; - - pa_channel_map_init_extend(&channel_map, spec.channels, PA_CHANNEL_MAP_DEFAULT); - - if (!pa_channel_map_compatible(&channel_map, &spec)) - qWarning() << "Channel map doesn't match sample specification!"; - - m_stream = pa_stream_new(pulseEngine->context(), m_streamName.constData(), &spec, &channel_map); - - pa_stream_set_state_callback(m_stream, inputStreamStateCallback, this); - pa_stream_set_read_callback(m_stream, inputStreamReadCallback, this); - - pa_stream_set_underflow_callback(m_stream, inputStreamUnderflowCallback, this); - pa_stream_set_overflow_callback(m_stream, inputStreamOverflowCallback, this); - - m_periodSize = pa_usec_to_bytes(PeriodTimeMs*1000, &spec); - - int flags = 0; - pa_buffer_attr buffer_attr; - buffer_attr.maxlength = (uint32_t) -1; - buffer_attr.prebuf = (uint32_t) -1; - buffer_attr.tlength = (uint32_t) -1; - buffer_attr.minreq = (uint32_t) -1; - flags |= PA_STREAM_ADJUST_LATENCY; - - if (m_bufferSize > 0) - buffer_attr.fragsize = (uint32_t) m_bufferSize; - else - buffer_attr.fragsize = (uint32_t) m_periodSize; - - if (pa_stream_connect_record(m_stream, m_device.data(), &buffer_attr, (pa_stream_flags_t)flags) < 0) { - qWarning() << "pa_stream_connect_record() failed!"; - pa_stream_unref(m_stream); - m_stream = nullptr; - pulseEngine->unlock(); - setError(QAudio::OpenError); - setState(QAudio::StoppedState); - return false; - } - - while (pa_stream_get_state(m_stream) != PA_STREAM_READY) - pa_threaded_mainloop_wait(pulseEngine->mainloop()); - - const pa_buffer_attr *actualBufferAttr = pa_stream_get_buffer_attr(m_stream); - m_periodSize = actualBufferAttr->fragsize; - m_periodTime = pa_bytes_to_usec(m_periodSize, &spec) / 1000; - if (actualBufferAttr->tlength != (uint32_t)-1) - m_bufferSize = actualBufferAttr->tlength; - - pulseEngine->unlock(); - - connect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioSource::onPulseContextFailed); - - m_opened = true; - m_timer->start(m_periodTime); - - m_elapsedTimeOffset = 0; - m_totalTimeValue = 0; - - return true; -} - -void QPulseAudioSource::close() -{ - if (!m_opened) - return; - - m_timer->stop(); - - QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - - if (m_stream) { - pulseEngine->lock(); - - pa_stream_set_state_callback(m_stream, nullptr, nullptr); - pa_stream_set_read_callback(m_stream, nullptr, nullptr); - pa_stream_set_underflow_callback(m_stream, nullptr, nullptr); - pa_stream_set_overflow_callback(m_stream, nullptr, nullptr); - - pa_stream_disconnect(m_stream); - pa_stream_unref(m_stream); - m_stream = nullptr; - - pulseEngine->unlock(); - } - - disconnect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioSource::onPulseContextFailed); - - if (!m_pullMode && m_audioSource) { - delete m_audioSource; - m_audioSource = nullptr; - } - m_opened = false; -} - -int QPulseAudioSource::checkBytesReady() -{ - if (m_deviceState != QAudio::ActiveState && m_deviceState != QAudio::IdleState) { - m_bytesAvailable = 0; - } else { - m_bytesAvailable = pa_stream_readable_size(m_stream); - } - - return m_bytesAvailable; -} - -qsizetype QPulseAudioSource::bytesReady() const -{ - return qMax(m_bytesAvailable, 0); -} - -qint64 QPulseAudioSource::read(char *data, qint64 len) -{ - m_bytesAvailable = checkBytesReady(); - - setError(QAudio::NoError); - setState(QAudio::ActiveState); - - int readBytes = 0; - - if (!m_pullMode && !m_tempBuffer.isEmpty()) { - readBytes = qMin(static_cast<int>(len), m_tempBuffer.size()); - memcpy(data, m_tempBuffer.constData(), readBytes); - m_totalTimeValue += readBytes; - - if (readBytes < m_tempBuffer.size()) { - m_tempBuffer.remove(0, readBytes); - return readBytes; - } - - m_tempBuffer.clear(); - } - - while (pa_stream_readable_size(m_stream) > 0) { - size_t readLength = 0; - -#ifdef DEBUG_PULSE - qDebug() << "QPulseAudioSource::read -- " << pa_stream_readable_size(m_stream) << " bytes available from pulse audio"; -#endif - - QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - pulseEngine->lock(); - - const void *audioBuffer; - - // Second and third parameters (audioBuffer and length) to pa_stream_peek are output parameters, - // the audioBuffer pointer is set to point to the actual pulse audio data, - // and the length is set to the length of this data. - if (pa_stream_peek(m_stream, &audioBuffer, &readLength) < 0) { - qWarning() << QString::fromLatin1("pa_stream_peek() failed: %1") - .arg(QString::fromUtf8(pa_strerror(pa_context_errno(pa_stream_get_context(m_stream))))); - pulseEngine->unlock(); - return 0; - } - - qint64 actualLength = 0; - if (m_pullMode) { - QByteArray adjusted(readLength, Qt::Uninitialized); - applyVolume(audioBuffer, adjusted.data(), readLength); - actualLength = m_audioSource->write(adjusted); - - if (actualLength < qint64(readLength)) { - pulseEngine->unlock(); - - setError(QAudio::UnderrunError); - setState(QAudio::IdleState); - - return actualLength; - } - } else { - actualLength = qMin(static_cast<int>(len - readBytes), static_cast<int>(readLength)); - applyVolume(audioBuffer, data + readBytes, actualLength); - } - -#ifdef DEBUG_PULSE - qDebug() << "QPulseAudioSource::read -- wrote " << actualLength << " to client"; -#endif - - if (actualLength < qint64(readLength)) { -#ifdef DEBUG_PULSE - qDebug() << "QPulseAudioSource::read -- appending " << readLength - actualLength << " bytes of data to temp buffer"; -#endif - int diff = readLength - actualLength; - int oldSize = m_tempBuffer.size(); - m_tempBuffer.resize(m_tempBuffer.size() + diff); - applyVolume(static_cast<const char *>(audioBuffer) + actualLength, m_tempBuffer.data() + oldSize, diff); - QMetaObject::invokeMethod(this, "userFeed", Qt::QueuedConnection); - } - - m_totalTimeValue += actualLength; - readBytes += actualLength; - - pa_stream_drop(m_stream); - pulseEngine->unlock(); - - if (!m_pullMode && readBytes >= len) - break; - } - -#ifdef DEBUG_PULSE - qDebug() << "QPulseAudioSource::read -- returning after reading " << readBytes << " bytes"; -#endif - - return readBytes; -} - -void QPulseAudioSource::applyVolume(const void *src, void *dest, int len) -{ - if (m_volume < 1.f) - QAudioHelperInternal::qMultiplySamples(m_volume, m_format, src, dest, len); - else - memcpy(dest, src, len); -} - -void QPulseAudioSource::resume() -{ - if (m_deviceState == QAudio::SuspendedState || m_deviceState == QAudio::IdleState) { - QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - pa_operation *operation; - - pulseEngine->lock(); - - operation = pa_stream_cork(m_stream, 0, inputStreamSuccessCallback, nullptr); - pulseEngine->wait(operation); - pa_operation_unref(operation); - - pulseEngine->unlock(); - - m_timer->start(m_periodTime); - - setState(QAudio::ActiveState); - setError(QAudio::NoError); - } -} - -void QPulseAudioSource::setVolume(qreal vol) -{ - if (qFuzzyCompare(m_volume, vol)) - return; - - m_volume = qBound(qreal(0), vol, qreal(1)); -} - -qreal QPulseAudioSource::volume() const -{ - return m_volume; -} - -void QPulseAudioSource::setBufferSize(qsizetype value) -{ - m_bufferSize = value; -} - -qsizetype QPulseAudioSource::bufferSize() const -{ - return m_bufferSize; -} - -qint64 QPulseAudioSource::processedUSecs() const -{ - pa_sample_spec spec = QPulseAudioInternal::audioFormatToSampleSpec(m_format); - qint64 result = pa_bytes_to_usec(m_totalTimeValue, &spec); - - return result; -} - -void QPulseAudioSource::suspend() -{ - if (m_deviceState == QAudio::ActiveState) { - setError(QAudio::NoError); - setState(QAudio::SuspendedState); - - m_timer->stop(); - - QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - pa_operation *operation; - - pulseEngine->lock(); - - operation = pa_stream_cork(m_stream, 1, inputStreamSuccessCallback, nullptr); - pulseEngine->wait(operation); - pa_operation_unref(operation); - - pulseEngine->unlock(); - } -} - -void QPulseAudioSource::userFeed() -{ - if (m_deviceState == QAudio::StoppedState || m_deviceState == QAudio::SuspendedState) - return; -#ifdef DEBUG_PULSE -// QTime now(QTime::currentTime()); -// qDebug()<< now.second() << "s " << now.msec() << "ms :userFeed() IN"; -#endif - deviceReady(); -} - -bool QPulseAudioSource::deviceReady() -{ - if (m_pullMode) { - // reads some audio data and writes it to QIODevice - read(nullptr,0); - } else { - // emits readyRead() so user will call read() on QIODevice to get some audio data - if (m_audioSource != nullptr) { - PulseInputPrivate *a = qobject_cast<PulseInputPrivate*>(m_audioSource); - a->trigger(); - } - } - m_bytesAvailable = checkBytesReady(); - - if (m_deviceState != QAudio::ActiveState) - return true; - - return true; -} - -void QPulseAudioSource::reset() -{ - stop(); - m_bytesAvailable = 0; -} - -void QPulseAudioSource::onPulseContextFailed() -{ - close(); - - setError(QAudio::FatalError); - setState(QAudio::StoppedState); -} - -PulseInputPrivate::PulseInputPrivate(QPulseAudioSource *audio) -{ - m_audioDevice = qobject_cast<QPulseAudioSource*>(audio); -} - -qint64 PulseInputPrivate::readData(char *data, qint64 len) -{ - return m_audioDevice->read(data, len); -} - -qint64 PulseInputPrivate::writeData(const char *data, qint64 len) -{ - Q_UNUSED(data); - Q_UNUSED(len); - return 0; -} - -void PulseInputPrivate::trigger() -{ - emit readyRead(); -} - -QT_END_NAMESPACE - -#include "moc_qpulseaudiosource_p.cpp" diff --git a/src/multimedia/platform/pulseaudio/qpulseaudiosource_p.h b/src/multimedia/platform/pulseaudio/qpulseaudiosource_p.h deleted file mode 100644 index 7a9f047dd..000000000 --- a/src/multimedia/platform/pulseaudio/qpulseaudiosource_p.h +++ /dev/null @@ -1,154 +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$ -** -****************************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of other Qt classes. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#ifndef QAUDIOINPUTPULSE_H -#define QAUDIOINPUTPULSE_H - -#include <QtCore/qfile.h> -#include <QtCore/qtimer.h> -#include <QtCore/qstring.h> -#include <QtCore/qstringlist.h> -#include <QtCore/qelapsedtimer.h> -#include <QtCore/qiodevice.h> - -#include "qaudio.h" -#include "qaudiodevice.h" -#include <private/qaudiosystem_p.h> - -#include <pulse/pulseaudio.h> - -QT_BEGIN_NAMESPACE - -class PulseInputPrivate; - -class QPulseAudioSource : public QPlatformAudioSource -{ - Q_OBJECT - -public: - QPulseAudioSource(const QByteArray &device); - ~QPulseAudioSource(); - - qint64 read(char *data, qint64 len); - - void start(QIODevice *device) override; - QIODevice *start() override; - void stop() override; - void reset() override; - void suspend() override; - void resume() override; - qsizetype bytesReady() const override; - void setBufferSize(qsizetype value) override; - qsizetype bufferSize() const override; - qint64 processedUSecs() const override; - QAudio::Error error() const override; - QAudio::State state() const override; - void setFormat(const QAudioFormat &format) override; - QAudioFormat format() const override; - - void setVolume(qreal volume) override; - qreal volume() const override; - - qint64 m_totalTimeValue; - QIODevice *m_audioSource; - QAudioFormat m_format; - QAudio::Error m_errorState; - QAudio::State m_deviceState; - qreal m_volume; - -private slots: - void userFeed(); - bool deviceReady(); - void onPulseContextFailed(); - -private: - void setState(QAudio::State state); - void setError(QAudio::Error error); - - void applyVolume(const void *src, void *dest, int len); - - int checkBytesReady(); - bool open(); - void close(); - - bool m_pullMode; - bool m_opened; - int m_bytesAvailable; - int m_bufferSize; - int m_periodSize; - unsigned int m_periodTime; - QTimer *m_timer; - qint64 m_elapsedTimeOffset; - pa_stream *m_stream; - QByteArray m_streamName; - QByteArray m_device; - QByteArray m_tempBuffer; - pa_sample_spec m_spec; -}; - -class PulseInputPrivate : public QIODevice -{ - Q_OBJECT -public: - PulseInputPrivate(QPulseAudioSource *audio); - ~PulseInputPrivate() {}; - - qint64 readData(char *data, qint64 len) override; - qint64 writeData(const char *data, qint64 len) override; - - void trigger(); - -private: - QPulseAudioSource *m_audioDevice; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/pulseaudio/qpulsehelpers.cpp b/src/multimedia/platform/pulseaudio/qpulsehelpers.cpp deleted file mode 100644 index c3ca6f8c9..000000000 --- a/src/multimedia/platform/pulseaudio/qpulsehelpers.cpp +++ /dev/null @@ -1,126 +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$ -** -****************************************************************************/ - -#include "qpulsehelpers_p.h" - -QT_BEGIN_NAMESPACE - -namespace QPulseAudioInternal -{ -pa_sample_spec audioFormatToSampleSpec(const QAudioFormat &format) -{ - pa_sample_spec spec; - - spec.rate = format.sampleRate(); - spec.channels = format.channelCount(); - spec.format = PA_SAMPLE_INVALID; - const bool isBigEndian = QSysInfo::ByteOrder == QSysInfo::BigEndian; - - if (format.sampleFormat() == QAudioFormat::UInt8) { - spec.format = PA_SAMPLE_U8; - } else if (format.sampleFormat() == QAudioFormat::Int16) { - spec.format = isBigEndian ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; - } else if (format.sampleFormat() == QAudioFormat::Int32) { - spec.format = isBigEndian ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; - } else if (format.sampleFormat() == QAudioFormat::Float) { - spec.format = isBigEndian ? PA_SAMPLE_FLOAT32BE : PA_SAMPLE_FLOAT32LE; - } - - return spec; -} - -#ifdef DEBUG_PULSE -QString stateToQString(pa_stream_state_t state) -{ - switch (state) - { - case PA_STREAM_UNCONNECTED: return "Unconnected"; - case PA_STREAM_CREATING: return "Creating"; - case PA_STREAM_READY: return "Ready"; - case PA_STREAM_FAILED: return "Failed"; - case PA_STREAM_TERMINATED: return "Terminated"; - } - - return QString("Unknown state: %0").arg(state); -} - -QString sampleFormatToQString(pa_sample_format format) -{ - switch (format) - { - case PA_SAMPLE_U8: return "Unsigned 8 Bit PCM."; - case PA_SAMPLE_ALAW: return "8 Bit a-Law "; - case PA_SAMPLE_ULAW: return "8 Bit mu-Law"; - case PA_SAMPLE_S16LE: return "Signed 16 Bit PCM, little endian (PC)."; - case PA_SAMPLE_S16BE: return "Signed 16 Bit PCM, big endian."; - case PA_SAMPLE_FLOAT32LE: return "32 Bit IEEE floating point, little endian (PC), range -1.0 to 1.0"; - case PA_SAMPLE_FLOAT32BE: return "32 Bit IEEE floating point, big endian, range -1.0 to 1.0"; - case PA_SAMPLE_S32LE: return "Signed 32 Bit PCM, little endian (PC)."; - case PA_SAMPLE_S32BE: return "Signed 32 Bit PCM, big endian."; - case PA_SAMPLE_S24LE: return "Signed 24 Bit PCM packed, little endian (PC)."; - case PA_SAMPLE_S24BE: return "Signed 24 Bit PCM packed, big endian."; - case PA_SAMPLE_S24_32LE: return "Signed 24 Bit PCM in LSB of 32 Bit words, little endian (PC)."; - case PA_SAMPLE_S24_32BE: return "Signed 24 Bit PCM in LSB of 32 Bit words, big endian."; - case PA_SAMPLE_MAX: return "Upper limit of valid sample types."; - case PA_SAMPLE_INVALID: return "Invalid sample format"; - } - - return QString("Invalid value: %0").arg(format); -} - -QString stateToQString(pa_context_state_t state) -{ - switch (state) - { - case PA_CONTEXT_UNCONNECTED: return "Unconnected"; - case PA_CONTEXT_CONNECTING: return "Connecting"; - case PA_CONTEXT_AUTHORIZING: return "Authorizing"; - case PA_CONTEXT_SETTING_NAME: return "Setting Name"; - case PA_CONTEXT_READY: return "Ready"; - case PA_CONTEXT_FAILED: return "Failed"; - case PA_CONTEXT_TERMINATED: return "Terminated"; - } - - return QString("Unknown state: %0").arg(state); -} -#endif - -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/pulseaudio/qpulsehelpers_p.h b/src/multimedia/platform/pulseaudio/qpulsehelpers_p.h deleted file mode 100644 index 5663e73ed..000000000 --- a/src/multimedia/platform/pulseaudio/qpulsehelpers_p.h +++ /dev/null @@ -1,70 +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$ -** -****************************************************************************/ - -#ifndef QPULSEHELPER_H -#define QPULSEHELPER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qaudiodevice.h" -#include <qaudioformat.h> -#include <pulse/pulseaudio.h> - -QT_BEGIN_NAMESPACE - -namespace QPulseAudioInternal -{ -pa_sample_spec audioFormatToSampleSpec(const QAudioFormat &format); -QString stateToQString(pa_stream_state_t state); -QString stateToQString(pa_context_state_t state); -QString sampleFormatToQString(pa_sample_format format); -} - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qgstreamer_platformspecificinterface.cpp b/src/multimedia/platform/qgstreamer_platformspecificinterface.cpp new file mode 100644 index 000000000..b0fee3327 --- /dev/null +++ b/src/multimedia/platform/qgstreamer_platformspecificinterface.cpp @@ -0,0 +1,21 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtMultimedia/private/qgstreamer_platformspecificinterface_p.h> + +QT_BEGIN_NAMESPACE + +QGStreamerPlatformSpecificInterface::~QGStreamerPlatformSpecificInterface() = default; + +QT_END_NAMESPACE diff --git a/src/multimedia/platform/qgstreamer_platformspecificinterface_p.h b/src/multimedia/platform/qgstreamer_platformspecificinterface_p.h new file mode 100644 index 000000000..87dd4f5aa --- /dev/null +++ b/src/multimedia/platform/qgstreamer_platformspecificinterface_p.h @@ -0,0 +1,34 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#ifndef GSTREAMER_PLATFORMSPECIFICINTERFACE_P_H +#define GSTREAMER_PLATFORMSPECIFICINTERFACE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtMultimedia/private/qplatformmediaintegration_p.h> + +QT_BEGIN_NAMESPACE + +class Q_MULTIMEDIA_EXPORT QGStreamerPlatformSpecificInterface + : public QAbstractPlatformSpecificInterface +{ +public: + ~QGStreamerPlatformSpecificInterface() override; + +public: + virtual QAudioDevice makeCustomGStreamerAudioInput(const QByteArray &gstreamerPipeline) = 0; + virtual QAudioDevice makeCustomGStreamerAudioOutput(const QByteArray &gstreamerPipeline) = 0; +}; + +QT_END_NAMESPACE + +#endif // GSTREAMER_PLATFORMSPECIFICINTERFACE_P_H diff --git a/src/multimedia/platform/qnx/audio/qnxaudioutils.cpp b/src/multimedia/platform/qnx/audio/qnxaudioutils.cpp deleted file mode 100644 index 7aa31e408..000000000 --- a/src/multimedia/platform/qnx/audio/qnxaudioutils.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ - -#include "qnxaudioutils_p.h" - -QT_BEGIN_NAMESPACE - -snd_pcm_channel_params_t QnxAudioUtils::formatToChannelParams(const QAudioFormat &format, QAudioDevice::Mode mode, int fragmentSize) -{ - snd_pcm_channel_params_t params; - memset(¶ms, 0, sizeof(params)); - params.channel = (mode == QAudioDevice::Output) ? SND_PCM_CHANNEL_PLAYBACK : SND_PCM_CHANNEL_CAPTURE; - params.mode = SND_PCM_MODE_BLOCK; - params.start_mode = SND_PCM_START_DATA; - params.stop_mode = SND_PCM_STOP_ROLLOVER; - params.buf.block.frag_size = fragmentSize; - params.buf.block.frags_min = 1; - params.buf.block.frags_max = 1; - strcpy(params.sw_mixer_subchn_name, "QAudio Channel"); - - params.format.interleave = 1; - params.format.rate = format.sampleRate(); - params.format.voices = format.channelCount(); - - switch (format.sampleSize()) { - case 8: - switch (format.sampleType()) { - case QAudioFormat::SignedInt: - params.format.format = SND_PCM_SFMT_S8; - break; - case QAudioFormat::UnSignedInt: - params.format.format = SND_PCM_SFMT_U8; - break; - default: - break; - } - break; - - case 16: - switch (format.sampleType()) { - case QAudioFormat::SignedInt: - if (format.byteOrder() == QAudioFormat::LittleEndian) { - params.format.format = SND_PCM_SFMT_S16_LE; - } else { - params.format.format = SND_PCM_SFMT_S16_BE; - } - break; - case QAudioFormat::UnSignedInt: - if (format.byteOrder() == QAudioFormat::LittleEndian) { - params.format.format = SND_PCM_SFMT_U16_LE; - } else { - params.format.format = SND_PCM_SFMT_U16_BE; - } - break; - default: - break; - } - break; - - case 32: - switch (format.sampleType()) { - case QAudioFormat::SignedInt: - if (format.byteOrder() == QAudioFormat::LittleEndian) { - params.format.format = SND_PCM_SFMT_S32_LE; - } else { - params.format.format = SND_PCM_SFMT_S32_BE; - } - break; - case QAudioFormat::UnSignedInt: - if (format.byteOrder() == QAudioFormat::LittleEndian) { - params.format.format = SND_PCM_SFMT_U32_LE; - } else { - params.format.format = SND_PCM_SFMT_U32_BE; - } - break; - case QAudioFormat::Float: - if (format.byteOrder() == QAudioFormat::LittleEndian) { - params.format.format = SND_PCM_SFMT_FLOAT_LE; - } else { - params.format.format = SND_PCM_SFMT_FLOAT_BE; - } - break; - default: - break; - } - break; - } - - return params; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/audio/qnxaudioutils_p.h b/src/multimedia/platform/qnx/audio/qnxaudioutils_p.h deleted file mode 100644 index d8e454de2..000000000 --- a/src/multimedia/platform/qnx/audio/qnxaudioutils_p.h +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ - -#ifndef QNXAUDIOUTILS_H -#define QNXAUDIOUTILS_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qaudiosystem_p.h" -#include <sys/asoundlib.h> - -QT_BEGIN_NAMESPACE - -namespace QnxAudioUtils -{ - snd_pcm_channel_params_t formatToChannelParams(const QAudioFormat &format, QAudioDevice::Mode mode, int fragmentSize); -} - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qnx/audio/qqnxaudiodevice.cpp b/src/multimedia/platform/qnx/audio/qqnxaudiodevice.cpp deleted file mode 100644 index ff7d23192..000000000 --- a/src/multimedia/platform/qnx/audio/qqnxaudiodevice.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ - -#include "qqnxaudiodevice_p.h" - -#include "qnxaudioutils_p.h" - -#include <sys/asoundlib.h> - -QT_BEGIN_NAMESPACE - -QnxAudioDeviceInfo::QnxAudioDeviceInfo(const QByteArray &deviceName, QAudioDevice::Mode mode) - : QAudioDevicePrivate(deviceName, mode) -{ -} - -QnxAudioDeviceInfo::~QnxAudioDeviceInfo() -{ -} - -QAudioFormat QnxAudioDeviceInfo::preferredFormat() const -{ - QAudioFormat format; - format.setSampleRate(44100); - format.setByteOrder(QAudioFormat::LittleEndian); - format.setSampleType(QAudioFormat::SignedInt); - format.setSampleSize(16); - format.setChannelCount(2); - if(mode == QAudioDevice::Input && !isFormatSupported(format)) - format.setChannelCount(1); - return format; -} - -bool QnxAudioDeviceInfo::isFormatSupported(const QAudioFormat &format) const -{ - const int pcmMode = (mode == QAudioDevice::Output) ? SND_PCM_OPEN_PLAYBACK : SND_PCM_OPEN_CAPTURE; - snd_pcm_t *handle; - - int card = 0; - int device = 0; - if (snd_pcm_open_preferred(&handle, &card, &device, pcmMode) < 0) - return false; - - snd_pcm_channel_info_t info; - memset (&info, 0, sizeof(info)); - info.channel = (mode == QAudioDevice::Output) ? SND_PCM_CHANNEL_PLAYBACK : SND_PCM_CHANNEL_CAPTURE; - - if (snd_pcm_plugin_info(handle, &info) < 0) { - qWarning("QAudioDevice: couldn't get channel info"); - snd_pcm_close(handle); - return false; - } - - snd_pcm_channel_params_t params = QnxAudioUtils::formatToChannelParams(format, mode, info.max_fragment_size); - const int errorCode = snd_pcm_plugin_params(handle, ¶ms); - snd_pcm_close(handle); - - return errorCode == 0; -} - -QList<int> QnxAudioDeviceInfo::supportedSampleRates() const -{ - return QList<int>() << 8000 << 11025 << 22050 << 44100 << 48000; -} - -QList<int> QnxAudioDeviceInfo::supportedChannelCounts() const -{ - return QList<int>() << 1 << 2; -} - -QList<int> QnxAudioDeviceInfo::supportedSampleSizes() const -{ - return QList<int>() << 8 << 16 << 32; -} - -QList<QAudioFormat::Endian> QnxAudioDeviceInfo::supportedByteOrders() const -{ - return QList<QAudioFormat::Endian>() << QAudioFormat::LittleEndian << QAudioFormat::BigEndian; -} - -QList<QAudioFormat::SampleType> QnxAudioDeviceInfo::supportedSampleTypes() const -{ - return QList<QAudioFormat::SampleType>() << QAudioFormat::SignedInt << QAudioFormat::UnSignedInt << QAudioFormat::Float; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/audio/qqnxaudiodevice_p.h b/src/multimedia/platform/qnx/audio/qqnxaudiodevice_p.h deleted file mode 100644 index 96e65759a..000000000 --- a/src/multimedia/platform/qnx/audio/qqnxaudiodevice_p.h +++ /dev/null @@ -1,77 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ - -#ifndef QNXAUDIODEVICEINFO_H -#define QNXAUDIODEVICEINFO_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qaudiosystem_p.h" -#include <private/qaudiodevice_p.h> - -QT_BEGIN_NAMESPACE - -class QnxAudioDeviceInfo : public QAudioDevicePrivate -{ -public: - QnxAudioDeviceInfo(const QByteArray &deviceName, QAudioDevice::Mode mode); - ~QnxAudioDeviceInfo(); - - QAudioFormat preferredFormat() const override; - bool isFormatSupported(const QAudioFormat &format) const override; - QString description() const override { return QString::fromUtf8(id()); } - QList<int> supportedSampleRates() const override; - QList<int> supportedChannelCounts() const override; - QList<int> supportedSampleSizes() const override; - QList<QAudioFormat::Endian> supportedByteOrders() const override; - QList<QAudioFormat::SampleType> supportedSampleTypes() const override; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qnx/audio/qqnxaudiosink.cpp b/src/multimedia/platform/qnx/audio/qqnxaudiosink.cpp deleted file mode 100644 index d2c1f98c7..000000000 --- a/src/multimedia/platform/qnx/audio/qqnxaudiosink.cpp +++ /dev/null @@ -1,533 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ - -#include "qqnxaudiosink_p.h" - -#include "qnxaudioutils_p.h" - -#include <private/qaudiohelpers_p.h> - -#pragma GCC diagnostic ignored "-Wvla" - -QT_BEGIN_NAMESPACE - -QQnxAudioSink::QQnxAudioSink() - : m_source(0) - , m_pushSource(false) - , m_error(QAudio::NoError) - , m_state(QAudio::StoppedState) - , m_volume(1.0) - , m_periodSize(0) - , m_pcmHandle(0) - , m_bytesWritten(0) -#if _NTO_VERSION >= 700 - , m_pcmNotifier(0) -#endif -{ - m_timer.setSingleShot(false); - m_timer.setInterval(20); - connect(&m_timer, SIGNAL(timeout()), this, SLOT(pullData())); -} - -QQnxAudioSink::~QQnxAudioSink() -{ - stop(); -} - -void QQnxAudioSink::start(QIODevice *source) -{ - if (m_state != QAudio::StoppedState) - stop(); - - m_error = QAudio::NoError; - m_source = source; - m_pushSource = false; - - if (open()) { - setState(QAudio::ActiveState); - m_timer.start(); - } else { - setError(QAudio::OpenError); - setState(QAudio::StoppedState); - } -} - -QIODevice *QQnxAudioSink::start() -{ - if (m_state != QAudio::StoppedState) - stop(); - - m_error = QAudio::NoError; - m_source = new QnxPushIODevice(this); - m_source->open(QIODevice::WriteOnly|QIODevice::Unbuffered); - m_pushSource = true; - - if (open()) - setState(QAudio::IdleState); - else { - setError(QAudio::OpenError); - setState(QAudio::StoppedState); - } - - return m_source; -} - -void QQnxAudioSink::stop() -{ - if (m_state == QAudio::StoppedState) - return; - - setError(QAudio::NoError); - setState(QAudio::StoppedState); - close(); -} - -void QQnxAudioSink::reset() -{ - if (m_pcmHandle) -#if SND_PCM_VERSION < SND_PROTOCOL_VERSION('P',3,0,2) - snd_pcm_playback_drain(m_pcmHandle); -#else - snd_pcm_channel_drain(m_pcmHandle, SND_PCM_CHANNEL_PLAYBACK); -#endif - stop(); -} - -void QQnxAudioSink::suspend() -{ - snd_pcm_playback_pause(m_pcmHandle); - suspendInternal(QAudio::SuspendedState); -} - -void QQnxAudioSink::resume() -{ - snd_pcm_playback_resume(m_pcmHandle); - resumeInternal(); -} - -qsizetype QQnxAudioSink::bytesFree() const -{ - if (m_state != QAudio::ActiveState && m_state != QAudio::IdleState) - return 0; - - snd_pcm_channel_status_t status; - memset(&status, 0, sizeof(status)); - status.channel = SND_PCM_CHANNEL_PLAYBACK; - const int errorCode = snd_pcm_plugin_status(m_pcmHandle, &status); - - if (errorCode) - return 0; - else - return status.free; -} - -qint64 QQnxAudioSink::processedUSecs() const -{ - return qint64(1000000) * m_format.framesForBytes(m_bytesWritten) / m_format.sampleRate(); -} - -QAudio::Error QQnxAudioSink::error() const -{ - return m_error; -} - -QAudio::State QQnxAudioSink::state() const -{ - return m_state; -} - -void QQnxAudioSink::setFormat(const QAudioFormat &format) -{ - if (m_state == QAudio::StoppedState) - m_format = format; -} - -QAudioFormat QQnxAudioSink::format() const -{ - return m_format; -} - -void QQnxAudioSink::setVolume(qreal volume) -{ - m_volume = qBound(qreal(0.0), volume, qreal(1.0)); -} - -qreal QQnxAudioSink::volume() const -{ - return m_volume; -} - -void QQnxAudioSink::pullData() -{ - if (m_state == QAudio::StoppedState - || m_state == QAudio::SuspendedState) - return; - - const int bytesAvailable = bytesFree(); - const int frames = m_format.framesForBytes(bytesAvailable); - - if (frames == 0 || bytesAvailable < periodSize()) - return; - - // The buffer is placed on the stack so no more than 64K or 1 frame - // whichever is larger. - const int maxFrames = qMax(m_format.framesForBytes(64 * 1024), 1); - const int bytesRequested = m_format.bytesForFrames(qMin(frames, maxFrames)); - - char buffer[bytesRequested]; - const int bytesRead = m_source->read(buffer, bytesRequested); - - // reading can take a while and stream may have been stopped - if (!m_pcmHandle) - return; - - if (bytesRead > 0) { - // Got some data to output - if (m_state != QAudio::ActiveState) - return; - - const qint64 bytesWritten = write(buffer, bytesRead); - if (bytesWritten != bytesRead) - m_source->seek(m_source->pos()-(bytesRead-bytesWritten)); - - } else { - // We're done - close(); - if (bytesRead != 0) - setError(QAudio::IOError); - setState(QAudio::StoppedState); - } - - if (m_state != QAudio::ActiveState) - return; -} - -bool QQnxAudioSink::open() -{ - if (!m_format.isValid() || m_format.sampleRate() <= 0) { - if (!m_format.isValid()) - qWarning("QQnxAudioSink: open error, invalid format."); - else - qWarning("QQnxAudioSink: open error, invalid sample rate (%d).", m_format.sampleRate()); - - return false; - } - - int errorCode = 0; - - int card = 0; - int device = 0; - if ((errorCode = snd_pcm_open_preferred(&m_pcmHandle, &card, &device, SND_PCM_OPEN_PLAYBACK)) < 0) { - qWarning("QQnxAudioSink: open error, couldn't open card (0x%x)", -errorCode); - return false; - } - - if ((errorCode = snd_pcm_nonblock_mode(m_pcmHandle, 0)) < 0) { - qWarning("QQnxAudioSink: open error, couldn't set non block mode (0x%x)", -errorCode); - close(); - return false; - } - - addPcmEventFilter(); - - // Necessary so that bytesFree() which uses the "free" member of the status struct works - snd_pcm_plugin_set_disable(m_pcmHandle, PLUGIN_MMAP); - - snd_pcm_channel_info_t info; - memset(&info, 0, sizeof(info)); - info.channel = SND_PCM_CHANNEL_PLAYBACK; - if ((errorCode = snd_pcm_plugin_info(m_pcmHandle, &info)) < 0) { - qWarning("QQnxAudioSink: open error, couldn't get channel info (0x%x)", -errorCode); - close(); - return false; - } - - snd_pcm_channel_params_t params = QnxAudioUtils::formatToChannelParams(m_format, QAudioDevice::Output, info.max_fragment_size); - setTypeName(¶ms); - - if ((errorCode = snd_pcm_plugin_params(m_pcmHandle, ¶ms)) < 0) { - qWarning("QQnxAudioSink: open error, couldn't set channel params (0x%x)", -errorCode); - close(); - return false; - } - - if ((errorCode = snd_pcm_plugin_prepare(m_pcmHandle, SND_PCM_CHANNEL_PLAYBACK)) < 0) { - qWarning("QQnxAudioSink: open error, couldn't prepare channel (0x%x)", -errorCode); - close(); - return false; - } - - snd_pcm_channel_setup_t setup; - memset(&setup, 0, sizeof(setup)); - setup.channel = SND_PCM_CHANNEL_PLAYBACK; - if ((errorCode = snd_pcm_plugin_setup(m_pcmHandle, &setup)) < 0) { - qWarning("QQnxAudioSink: open error, couldn't get channel setup (0x%x)", -errorCode); - close(); - return false; - } - - m_periodSize = qMin(2048, setup.buf.block.frag_size); - m_bytesWritten = 0; - - createPcmNotifiers(); - - return true; -} - -void QQnxAudioSink::close() -{ - m_timer.stop(); - - destroyPcmNotifiers(); - - if (m_pcmHandle) { -#if SND_PCM_VERSION < SND_PROTOCOL_VERSION('P',3,0,2) - snd_pcm_plugin_flush(m_pcmHandle, SND_PCM_CHANNEL_PLAYBACK); -#else - snd_pcm_plugin_drop(m_pcmHandle, SND_PCM_CHANNEL_PLAYBACK); -#endif - snd_pcm_close(m_pcmHandle); - m_pcmHandle = 0; - } - - if (m_pushSource) { - delete m_source; - m_source = 0; - } -} - -void QQnxAudioSink::setError(QAudio::Error error) -{ - if (m_error != error) { - m_error = error; - emit errorChanged(error); - } -} - -void QQnxAudioSink::setState(QAudio::State state) -{ - if (m_state != state) { - m_state = state; - emit stateChanged(state); - } -} - -qint64 QQnxAudioSink::write(const char *data, qint64 len) -{ - if (!m_pcmHandle) - return 0; - - // Make sure we're writing (N * frame) worth of bytes - const int size = m_format.bytesForFrames(qBound(qint64(0), qint64(bytesFree()), len) / m_format.bytesPerFrame()); - - if (size == 0) - return 0; - - int written = 0; - - if (m_volume < 1.0f) { - char out[size]; - QAudioHelperInternal::qMultiplySamples(m_volume, m_format, data, out, size); - written = snd_pcm_plugin_write(m_pcmHandle, out, size); - } else { - written = snd_pcm_plugin_write(m_pcmHandle, data, size); - } - - if (written > 0) { - m_bytesWritten += written; - setError(QAudio::NoError); - setState(QAudio::ActiveState); - return written; - } else { - close(); - setError(QAudio::FatalError); - setState(QAudio::StoppedState); - return 0; - } -} - -void QQnxAudioSink::suspendInternal(QAudio::State suspendState) -{ - m_timer.stop(); - setState(suspendState); -} - -void QQnxAudioSink::resumeInternal() -{ - if (m_pushSource) { - setState(QAudio::IdleState); - } else { - setState(QAudio::ActiveState); - m_timer.start(); - } -} - -#if _NTO_VERSION >= 700 - -QAudio::State suspendState(const snd_pcm_event_t &event) -{ - Q_ASSERT(event.type == SND_PCM_EVENT_AUDIOMGMT_STATUS); - Q_ASSERT(event.data.audiomgmt_status.new_status == SND_PCM_STATUS_SUSPENDED); - return QAudio::SuspendedState; -} - -void QQnxAudioSink::addPcmEventFilter() -{ - /* Enable PCM events */ - snd_pcm_filter_t filter; - memset(&filter, 0, sizeof(filter)); - filter.enable = (1<<SND_PCM_EVENT_AUDIOMGMT_STATUS) | - (1<<SND_PCM_EVENT_AUDIOMGMT_MUTE) | - (1<<SND_PCM_EVENT_OUTPUTCLASS); - snd_pcm_set_filter(m_pcmHandle, SND_PCM_CHANNEL_PLAYBACK, &filter); -} - -void QQnxAudioSink::createPcmNotifiers() -{ - // QSocketNotifier::Read for poll based event dispatcher. Exception for - // select based event dispatcher. - m_pcmNotifier = new QSocketNotifier(snd_pcm_file_descriptor(m_pcmHandle, - SND_PCM_CHANNEL_PLAYBACK), - QSocketNotifier::Read, this); - connect(m_pcmNotifier, &QSocketNotifier::activated, - this, &QQnxAudioSink::pcmNotifierActivated); -} - -void QQnxAudioSink::destroyPcmNotifiers() -{ - if (m_pcmNotifier) { - delete m_pcmNotifier; - m_pcmNotifier = 0; - } -} - -void QQnxAudioSink::setTypeName(snd_pcm_channel_params_t *params) -{ -#if 0 -// Use some mapping from QAudio::Role - if (m_category.isEmpty()) - return; - - QByteArray latin1Category = m_category.toLatin1(); - - if (QString::fromLatin1(latin1Category) != m_category) { - qWarning("QQnxAudioSink: audio category name isn't a Latin1 string."); - return; - } - - if (latin1Category.size() >= static_cast<int>(sizeof(params->audio_type_name))) { - qWarning("QQnxAudioSink: audio category name too long."); - return; - } - - strcpy(params->audio_type_name, latin1Category.constData()); -#endif -} - -void QQnxAudioSink::pcmNotifierActivated(int socket) -{ - Q_UNUSED(socket); - - snd_pcm_event_t pcm_event; - memset(&pcm_event, 0, sizeof(pcm_event)); - while (snd_pcm_channel_read_event(m_pcmHandle, SND_PCM_CHANNEL_PLAYBACK, &pcm_event) == 0) { - if (pcm_event.type == SND_PCM_EVENT_AUDIOMGMT_STATUS) { - if (pcm_event.data.audiomgmt_status.new_status == SND_PCM_STATUS_SUSPENDED) - suspendInternal(suspendState(pcm_event)); - else if (pcm_event.data.audiomgmt_status.new_status == SND_PCM_STATUS_RUNNING) - resumeInternal(); - else if (pcm_event.data.audiomgmt_status.new_status == SND_PCM_STATUS_PAUSED) - suspendInternal(QAudio::SuspendedState); - } - } -} - -#else - -void QQnxAudioSink::addPcmEventFilter() {} -void QQnxAudioSink::createPcmNotifiers() {} -void QQnxAudioSink::destroyPcmNotifiers() {} -void QQnxAudioSink::setTypeName(snd_pcm_channel_params_t *) {} - -#endif - -QnxPushIODevice::QnxPushIODevice(QQnxAudioSink *output) - : QIODevice(output), - m_output(output) -{ -} - -QnxPushIODevice::~QnxPushIODevice() -{ -} - -qint64 QnxPushIODevice::readData(char *data, qint64 len) -{ - Q_UNUSED(data); - Q_UNUSED(len); - return 0; -} - -qint64 QnxPushIODevice::writeData(const char *data, qint64 len) -{ - int retry = 0; - qint64 written = 0; - - if (m_output->state() == QAudio::ActiveState - || m_output->state() == QAudio::IdleState) { - while (written < len) { - const int writeSize = m_output->write(data + written, len - written); - - if (writeSize <= 0) { - retry++; - if (retry > 10) - return written; - else - continue; - } - - retry = 0; - written += writeSize; - } - } - - return written; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/audio/qqnxaudiosink_p.h b/src/multimedia/platform/qnx/audio/qqnxaudiosink_p.h deleted file mode 100644 index 2ce38315a..000000000 --- a/src/multimedia/platform/qnx/audio/qqnxaudiosink_p.h +++ /dev/null @@ -1,150 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ - -#ifndef QNXAUDIOOUTPUT_H -#define QNXAUDIOOUTPUT_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qaudiosystem_p.h" - -#include <QElapsedTimer> -#include <QTimer> -#include <QIODevice> -#include <QSocketNotifier> - -#include <sys/asoundlib.h> -#include <sys/neutrino.h> - -QT_BEGIN_NAMESPACE - -class QnxPushIODevice; - -class QQnxAudioSink : public QPlatformAudioSink -{ - Q_OBJECT - -public: - QQnxAudioSink(); - ~QQnxAudioSink(); - - void start(QIODevice *source) override; - QIODevice *start() override; - void stop() override; - void reset() override; - void suspend() override; - void resume() override; - qsizetype bytesFree() const override; - void setBufferSize(qsizetype) override {} - qsizetype bufferSize() const override { return 0; } - qint64 processedUSecs() const override; - QAudio::Error error() const override; - QAudio::State state() const override; - void setFormat(const QAudioFormat &format) override; - QAudioFormat format() const override; - void setVolume(qreal volume) override; - qreal volume() const override; - -private slots: - void pullData(); - -private: - bool open(); - void close(); - void setError(QAudio::Error error); - void setState(QAudio::State state); - - void addPcmEventFilter(); - void createPcmNotifiers(); - void destroyPcmNotifiers(); - void setTypeName(snd_pcm_channel_params_t *params); - - void suspendInternal(QAudio::State suspendState); - void resumeInternal(); - - friend class QnxPushIODevice; - qint64 write(const char *data, qint64 len); - - QIODevice *m_source; - bool m_pushSource; - QTimer m_timer; - - QAudio::Error m_error; - QAudio::State m_state; - QAudioFormat m_format; - qreal m_volume; - int m_periodSize; - - snd_pcm_t *m_pcmHandle; - qint64 m_bytesWritten; - -#if _NTO_VERSION >= 700 - QSocketNotifier *m_pcmNotifier; - -private slots: - void pcmNotifierActivated(int socket); -#endif -}; - -class QnxPushIODevice : public QIODevice -{ - Q_OBJECT -public: - explicit QnxPushIODevice(QQnxAudioSink *output); - ~QnxPushIODevice(); - - qint64 readData(char *data, qint64 len); - qint64 writeData(const char *data, qint64 len); - -private: - QQnxAudioSink *m_output; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qnx/audio/qqnxaudiosource.cpp b/src/multimedia/platform/qnx/audio/qqnxaudiosource.cpp deleted file mode 100644 index b90681a83..000000000 --- a/src/multimedia/platform/qnx/audio/qqnxaudiosource.cpp +++ /dev/null @@ -1,421 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ - -#include "qqnxaudiosource_p.h" - -#include "qnxaudioutils_p.h" - -#include <private/qaudiohelpers_p.h> - -#include <QDebug> - -QT_BEGIN_NAMESPACE - -QQnxAudioSource::QQnxAudioSource() - : m_audioSource(0) - , m_pcmHandle(0) - , m_pcmNotifier(0) - , m_error(QAudio::NoError) - , m_state(QAudio::StoppedState) - , m_bytesRead(0) - , m_elapsedTimeOffset(0) - , m_totalTimeValue(0) - , m_volume(qreal(1.0f)) - , m_bytesAvailable(0) - , m_bufferSize(0) - , m_periodSize(0) - , m_pullMode(true) -{ -} - -QQnxAudioSource::~QQnxAudioSource() -{ - close(); -} - -void QQnxAudioSource::start(QIODevice *device) -{ - if (m_state != QAudio::StoppedState) - close(); - - if (!m_pullMode && m_audioSource) - delete m_audioSource; - - m_pullMode = true; - m_audioSource = device; - - if (open()) { - setError(QAudio::NoError); - setState(QAudio::ActiveState); - } else { - setError(QAudio::OpenError); - setState(QAudio::StoppedState); - } -} - -QIODevice *QQnxAudioSource::start() -{ - if (m_state != QAudio::StoppedState) - close(); - - if (!m_pullMode && m_audioSource) - delete m_audioSource; - - m_pullMode = false; - m_audioSource = new InputPrivate(this); - m_audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered); - - if (open()) { - setError(QAudio::NoError); - setState(QAudio::IdleState); - } else { - delete m_audioSource; - m_audioSource = 0; - - setError(QAudio::OpenError); - setState(QAudio::StoppedState); - } - - return m_audioSource; -} - -void QQnxAudioSource::stop() -{ - if (m_state == QAudio::StoppedState) - return; - - setError(QAudio::NoError); - setState(QAudio::StoppedState); - close(); -} - -void QQnxAudioSource::reset() -{ - stop(); - m_bytesAvailable = 0; -} - -void QQnxAudioSource::suspend() -{ - snd_pcm_capture_pause(m_pcmHandle); - - if (m_pcmNotifier) - m_pcmNotifier->setEnabled(false); - - setState(QAudio::SuspendedState); -} - -void QQnxAudioSource::resume() -{ - snd_pcm_capture_resume(m_pcmHandle); - - if (m_pcmNotifier) - m_pcmNotifier->setEnabled(true); - - if (m_pullMode) { - setState(QAudio::ActiveState); - } else { - setState(QAudio::IdleState); - } -} - -qsizetype QQnxAudioSource::bytesReady() const -{ - return qMax(m_bytesAvailable, 0); -} - -void QQnxAudioSource::setBufferSize(qsizetype bufferSize) -{ - m_bufferSize = bufferSize; -} - -qsizetype QQnxAudioSource::bufferSize() const -{ - return m_bufferSize; -} - -qint64 QQnxAudioSource::processedUSecs() const -{ - return qint64(1000000) * m_format.framesForBytes(m_bytesRead) / m_format.sampleRate(); -} - -QAudio::Error QQnxAudioSource::error() const -{ - return m_error; -} - -QAudio::State QQnxAudioSource::state() const -{ - return m_state; -} - -void QQnxAudioSource::setFormat(const QAudioFormat &format) -{ - if (m_state == QAudio::StoppedState) - m_format = format; -} - -QAudioFormat QQnxAudioSource::format() const -{ - return m_format; -} - -void QQnxAudioSource::setVolume(qreal volume) -{ - m_volume = qBound(qreal(0.0), volume, qreal(1.0)); -} - -qreal QQnxAudioSource::volume() const -{ - return m_volume; -} - -void QQnxAudioSource::userFeed() -{ - if (m_state == QAudio::StoppedState || m_state == QAudio::SuspendedState) - return; - - deviceReady(); -} - -bool QQnxAudioSource::deviceReady() -{ - if (m_pullMode) { - // reads some audio data and writes it to QIODevice - read(0, 0); - } else { - m_bytesAvailable = m_periodSize; - - // emits readyRead() so user will call read() on QIODevice to get some audio data - if (m_audioSource != 0) { - InputPrivate *input = qobject_cast<InputPrivate*>(m_audioSource); - input->trigger(); - } - } - - if (m_state != QAudio::ActiveState) - return true; - - return true; -} - -bool QQnxAudioSource::open() -{ - if (!m_format.isValid() || m_format.sampleRate() <= 0) { - if (!m_format.isValid()) - qWarning("QQnxAudioSource: open error, invalid format."); - else - qWarning("QQnxAudioSource: open error, invalid sample rate (%d).", m_format.sampleRate()); - - return false; - } - - int errorCode = 0; - - int card = 0; - int device = 0; - if ((errorCode = snd_pcm_open_preferred(&m_pcmHandle, &card, &device, SND_PCM_OPEN_CAPTURE)) < 0) { - qWarning("QQnxAudioSource: open error, couldn't open card (0x%x)", -errorCode); - return false; - } - - // Necessary so that bytesFree() which uses the "free" member of the status struct works - snd_pcm_plugin_set_disable(m_pcmHandle, PLUGIN_MMAP); - - snd_pcm_channel_info_t info; - memset(&info, 0, sizeof(info)); - info.channel = SND_PCM_CHANNEL_CAPTURE; - if ((errorCode = snd_pcm_plugin_info(m_pcmHandle, &info)) < 0) { - qWarning("QQnxAudioSource: open error, couldn't get channel info (0x%x)", -errorCode); - close(); - return false; - } - - snd_pcm_channel_params_t params = QnxAudioUtils::formatToChannelParams(m_format, QAudioDevice::Input, info.max_fragment_size); - - if ((errorCode = snd_pcm_plugin_params(m_pcmHandle, ¶ms)) < 0) { - qWarning("QQnxAudioSource: open error, couldn't set channel params (0x%x)", -errorCode); - close(); - return false; - } - - if ((errorCode = snd_pcm_plugin_prepare(m_pcmHandle, SND_PCM_CHANNEL_CAPTURE)) < 0) { - qWarning("QQnxAudioSource: open error, couldn't prepare channel (0x%x)", -errorCode); - close(); - return false; - } - - snd_pcm_channel_setup_t setup; - - memset(&setup, 0, sizeof(setup)); - setup.channel = SND_PCM_CHANNEL_CAPTURE; - if ((errorCode = snd_pcm_plugin_setup(m_pcmHandle, &setup)) < 0) { - qWarning("QQnxAudioSource: open error, couldn't get channel setup (0x%x)", -errorCode); - close(); - return false; - } - - m_periodSize = qMin(2048, setup.buf.block.frag_size); - - m_elapsedTimeOffset = 0; - m_totalTimeValue = 0; - m_bytesRead = 0; - - m_pcmNotifier = new QSocketNotifier(snd_pcm_file_descriptor(m_pcmHandle, SND_PCM_CHANNEL_CAPTURE), - QSocketNotifier::Read, this); - connect(m_pcmNotifier, SIGNAL(activated(QSocketDescriptor)), SLOT(userFeed())); - - return true; -} - -void QQnxAudioSource::close() -{ - if (m_pcmHandle) -#if SND_PCM_VERSION < SND_PROTOCOL_VERSION('P',3,0,2) - snd_pcm_plugin_flush(m_pcmHandle, SND_PCM_CHANNEL_CAPTURE); -#else - snd_pcm_plugin_drop(m_pcmHandle, SND_PCM_CHANNEL_CAPTURE); -#endif - - if (m_pcmNotifier) { - delete m_pcmNotifier; - m_pcmNotifier = 0; - } - - if (m_pcmHandle) { - snd_pcm_close(m_pcmHandle); - m_pcmHandle = 0; - } - - if (!m_pullMode && m_audioSource) { - delete m_audioSource; - m_audioSource = 0; - } -} - -qint64 QQnxAudioSource::read(char *data, qint64 len) -{ - int errorCode = 0; - QByteArray tempBuffer(m_periodSize, 0); - - const int actualRead = snd_pcm_plugin_read(m_pcmHandle, tempBuffer.data(), m_periodSize); - if (actualRead < 1) { - snd_pcm_channel_status_t status; - memset(&status, 0, sizeof(status)); - status.channel = SND_PCM_CHANNEL_CAPTURE; - if ((errorCode = snd_pcm_plugin_status(m_pcmHandle, &status)) < 0) { - qWarning("QQnxAudioSource: read error, couldn't get plugin status (0x%x)", -errorCode); - close(); - setError(QAudio::FatalError); - setState(QAudio::StoppedState); - return -1; - } - - if (status.status == SND_PCM_STATUS_READY - || status.status == SND_PCM_STATUS_OVERRUN) { - if ((errorCode = snd_pcm_plugin_prepare(m_pcmHandle, SND_PCM_CHANNEL_CAPTURE)) < 0) { - qWarning("QQnxAudioSource: read error, couldn't prepare plugin (0x%x)", -errorCode); - close(); - setError(QAudio::FatalError); - setState(QAudio::StoppedState); - return -1; - } - } - } else { - setError(QAudio::NoError); - setState(QAudio::ActiveState); - } - - if (m_volume < 1.0f) - QAudioHelperInternal::qMultiplySamples(m_volume, m_format, tempBuffer.data(), tempBuffer.data(), actualRead); - - m_bytesRead += actualRead; - - if (m_pullMode) { - m_audioSource->write(tempBuffer.data(), actualRead); - } else { - memcpy(data, tempBuffer.data(), qMin(static_cast<qint64>(actualRead), len)); - } - - m_bytesAvailable = 0; - - return actualRead; -} - -void QQnxAudioSource::setError(QAudio::Error error) -{ - if (m_error == error) - return; - - m_error = error; - emit errorChanged(m_error); -} - -void QQnxAudioSource::setState(QAudio::State state) -{ - if (m_state == state) - return; - - m_state = state; - emit stateChanged(m_state); -} - -InputPrivate::InputPrivate(QQnxAudioSource *audio) - : m_audioDevice(audio) -{ -} - -qint64 InputPrivate::readData(char *data, qint64 len) -{ - return m_audioDevice->read(data, len); -} - -qint64 InputPrivate::writeData(const char *data, qint64 len) -{ - Q_UNUSED(data); - Q_UNUSED(len); - return 0; -} - -void InputPrivate::trigger() -{ - emit readyRead(); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/audio/qqnxaudiosource_p.h b/src/multimedia/platform/qnx/audio/qqnxaudiosource_p.h deleted file mode 100644 index 3432f7544..000000000 --- a/src/multimedia/platform/qnx/audio/qqnxaudiosource_p.h +++ /dev/null @@ -1,142 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ - -#ifndef QNXAUDIOINPUT_H -#define QNXAUDIOINPUT_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qaudiosystem_p.h" - -#include <QSocketNotifier> -#include <QIODevice> -#include <QElapsedTimer> -#include <QTimer> - -#include <sys/asoundlib.h> - -QT_BEGIN_NAMESPACE - -class QQnxAudioSource : public QPlatformAudioSource -{ - Q_OBJECT - -public: - QQnxAudioSource(); - ~QQnxAudioSource(); - - void start(QIODevice*) override; - QIODevice* start() override; - void stop() override; - void reset() override; - void suspend() override; - void resume() override; - qsizetype bytesReady() const override; - void setBufferSize(qsizetype ) override; - qsizetype bufferSize() const override; - qint64 processedUSecs() const override; - QAudio::Error error() const override; - QAudio::State state() const override; - void setFormat(const QAudioFormat&) override; - QAudioFormat format() const override; - void setVolume(qreal) override; - qreal volume() const override; - -private slots: - void userFeed(); - bool deviceReady(); - -private: - friend class InputPrivate; - - bool open(); - void close(); - qint64 read(char *data, qint64 len); - void setError(QAudio::Error error); - void setState(QAudio::State state); - - QAudioFormat m_format; - - QIODevice *m_audioSource; - snd_pcm_t *m_pcmHandle; - QSocketNotifier *m_pcmNotifier; - - QAudio::Error m_error; - QAudio::State m_state; - - qint64 m_bytesRead; - qint64 m_elapsedTimeOffset; - qint64 m_totalTimeValue; - - qreal m_volume; - - int m_bytesAvailable; - int m_bufferSize; - int m_periodSize; - - bool m_pullMode; -}; - -class InputPrivate : public QIODevice -{ - Q_OBJECT -public: - InputPrivate(QQnxAudioSource *audio); - - qint64 readData(char *data, qint64 len) override; - qint64 writeData(const char *data, qint64 len) override; - - void trigger(); - -private: - QQnxAudioSource *m_audioDevice; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qnx/camera/bbcameraaudioencodersettingscontrol.cpp b/src/multimedia/platform/qnx/camera/bbcameraaudioencodersettingscontrol.cpp deleted file mode 100644 index 2c0529bc4..000000000 --- a/src/multimedia/platform/qnx/camera/bbcameraaudioencodersettingscontrol.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#include "bbcameraaudioencodersettingscontrol_p.h" - -#include "bbcamerasession_p.h" - -QT_BEGIN_NAMESPACE - -BbCameraAudioEncoderSettingsControl::BbCameraAudioEncoderSettingsControl(BbCameraSession *session, QObject *parent) - : QAudioEncoderSettingsControl(parent) - , m_session(session) -{ -} - -QStringList BbCameraAudioEncoderSettingsControl::supportedAudioCodecs() const -{ - return QStringList() << QLatin1String("none") << QLatin1String("aac") << QLatin1String("raw"); -} - -QString BbCameraAudioEncoderSettingsControl::codecDescription(const QString &codecName) const -{ - if (codecName == QLatin1String("none")) - return tr("No compression"); - else if (codecName == QLatin1String("aac")) - return tr("AAC compression"); - else if (codecName == QLatin1String("raw")) - return tr("PCM uncompressed"); - - return QString(); -} - -QList<int> BbCameraAudioEncoderSettingsControl::supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous) const -{ - Q_UNUSED(settings); - Q_UNUSED(continuous); - - // no API provided by BB10 yet - return QList<int>(); -} - -QAudioEncoderSettings BbCameraAudioEncoderSettingsControl::audioSettings() const -{ - return m_session->audioSettings(); -} - -void BbCameraAudioEncoderSettingsControl::setAudioSettings(const QAudioEncoderSettings &settings) -{ - m_session->setAudioSettings(settings); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/camera/bbcameraaudioencodersettingscontrol_p.h b/src/multimedia/platform/qnx/camera/bbcameraaudioencodersettingscontrol_p.h deleted file mode 100644 index cdc384537..000000000 --- a/src/multimedia/platform/qnx/camera/bbcameraaudioencodersettingscontrol_p.h +++ /dev/null @@ -1,77 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#ifndef BBCAMERAAUDIOENCODERSETTINGSCONTROL_H -#define BBCAMERAAUDIOENCODERSETTINGSCONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qaudioencodersettingscontrol.h> - -QT_BEGIN_NAMESPACE - -class BbCameraSession; - -class BbCameraAudioEncoderSettingsControl : public QAudioEncoderSettingsControl -{ - Q_OBJECT -public: - explicit BbCameraAudioEncoderSettingsControl(BbCameraSession *session, QObject *parent = 0); - - QStringList supportedAudioCodecs() const override; - QString codecDescription(const QString &codecName) const override; - QList<int> supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous = 0) const override; - QAudioEncoderSettings audioSettings() const override; - void setAudioSettings(const QAudioEncoderSettings &settings) override; - -private: - BbCameraSession *m_session; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qnx/camera/bbcameracontrol.cpp b/src/multimedia/platform/qnx/camera/bbcameracontrol.cpp deleted file mode 100644 index 335b7c584..000000000 --- a/src/multimedia/platform/qnx/camera/bbcameracontrol.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#include "bbcameracontrol_p.h" - -#include "bbcamerasession_p.h" -#include <qcameradevice.h> - -QT_BEGIN_NAMESPACE - -BbCameraControl::BbCameraControl(BbCameraSession *session, QObject *parent) - : QPlatformCamera(parent) - , m_session(session) -{ - connect(m_session, SIGNAL(statusChanged(QCamera::Status)), this, SIGNAL(statusChanged(QCamera::Status))); - connect(m_session, SIGNAL(stateChanged(QCamera::State)), this, SIGNAL(stateChanged(QCamera::State))); - connect(m_session, SIGNAL(error(int,QString)), this, SIGNAL(error(int,QString))); - connect(m_session, SIGNAL(captureModeChanged(QCamera::CaptureModes)), this, SIGNAL(captureModeChanged(QCamera::CaptureModes))); - - connect(m_session, SIGNAL(cameraOpened()), SLOT(cameraOpened())); -} - -QCamera::State BbCameraControl::state() const -{ - return m_session->state(); -} - -void BbCameraControl::setState(QCamera::State state) -{ - m_session->setState(state); -} - -QCamera::CaptureModes BbCameraControl::captureMode() const -{ - return m_session->captureMode(); -} - -void BbCameraControl::setCaptureMode(QCamera::CaptureModes mode) -{ - m_session->setCaptureMode(mode); -} - -QCamera::Status BbCameraControl::status() const -{ - return m_session->status(); -} - -void BbCameraControl::setCamera(const QCameraDevice &camera) -{ - m_session->setDevice(camera.id()); -} - -bool BbCameraControl::isCaptureModeSupported(QCamera::CaptureModes mode) const -{ - return m_session->isCaptureModeSupported(mode); -} - -void BbCameraControl::cameraOpened() -{ -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/camera/bbcameracontrol_p.h b/src/multimedia/platform/qnx/camera/bbcameracontrol_p.h deleted file mode 100644 index 4990f6517..000000000 --- a/src/multimedia/platform/qnx/camera/bbcameracontrol_p.h +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#ifndef BBCAMERACONTROL_H -#define BBCAMERACONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformcamera_p.h> - -QT_BEGIN_NAMESPACE - -class BbCameraSession; - -class BbCameraControl : public QPlatformCamera -{ - Q_OBJECT -public: - explicit BbCameraControl(BbCameraSession *session, QObject *parent = 0); - - QCamera::State state() const override; - void setState(QCamera::State state) override; - - QCamera::Status status() const override; - - void setCamera(const QCameraDevice &camera) override; - - QCamera::CaptureModes captureMode() const override; - void setCaptureMode(QCamera::CaptureModes) override; - bool isCaptureModeSupported(QCamera::CaptureModes mode) const override; - - enum LocksApplyMode - { - IndependentMode, - FocusExposureBoundMode, - AllBoundMode, - FocusOnlyMode - }; - -private Q_SLOTS: - void cameraOpened(); - -private: - BbCameraSession *m_session; - -private: - BbCameraSession *m_session; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qnx/camera/bbcameraexposurecontrol.cpp b/src/multimedia/platform/qnx/camera/bbcameraexposurecontrol.cpp deleted file mode 100644 index 81ab2ba7f..000000000 --- a/src/multimedia/platform/qnx/camera/bbcameraexposurecontrol.cpp +++ /dev/null @@ -1,288 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#include "bbcameraexposurecontrol_p.h" - -#include "bbcamerasession_p.h" - -#include <QDebug> - -QT_BEGIN_NAMESPACE - -BbCameraExposureControl::BbCameraExposureControl(BbCameraSession *session, QObject *parent) - : QPlatformCameraExposure(parent) - , m_session(session) - , m_requestedExposureMode(QCamera::ExposureAuto) -{ - connect(m_session, SIGNAL(statusChanged(QCamera::Status)), this, SLOT(statusChanged(QCamera::Status))); -} - -bool BbCameraExposureControl::isParameterSupported(ExposureParameter parameter) const -{ - switch (parameter) { - case QPlatformCameraExposure::ISO: - return false; - case QPlatformCameraExposure::ShutterSpeed: - return false; - case QPlatformCameraExposure::ExposureCompensation: - return false; - case QPlatformCameraExposure::FlashPower: - return false; - case QPlatformCameraExposure::FlashCompensation: - return false; - case QPlatformCameraExposure::TorchPower: - return false; - case QPlatformCameraExposure::ExposureMode: - return true; - case QPlatformCameraExposure::MeteringMode: - return false; - default: - return false; - } -} - -QVariantList BbCameraExposureControl::supportedParameterRange(ExposureParameter parameter, bool *continuous) const -{ - if (parameter != QPlatformCameraExposure::ExposureMode) // no other parameter supported by BB10 API at the moment - return QVariantList(); - - if (m_session->status() != QCamera::ActiveStatus) // we can query supported exposure modes only with active viewfinder - return QVariantList(); - - if (continuous) - *continuous = false; - - int supported = 0; - camera_scenemode_t modes[20]; - const camera_error_t result = camera_get_scene_modes(m_session->handle(), 20, &supported, modes); - if (result != CAMERA_EOK) { - qWarning() << "Unable to retrieve supported scene modes:" << result; - return QVariantList(); - } - - QVariantList exposureModes; - for (int i = 0; i < supported; ++i) { - switch (modes[i]) { - case CAMERA_SCENE_AUTO: - exposureModes << QVariant::fromValue(QCamera::ExposureAuto); - break; - case CAMERA_SCENE_SPORTS: - exposureModes << QVariant::fromValue(QCamera::ExposureSports); - break; - case CAMERA_SCENE_CLOSEUP: - exposureModes << QVariant::fromValue(QCamera::ExposurePortrait); - break; - case CAMERA_SCENE_ACTION: - exposureModes << QVariant::fromValue(QCamera::ExposureSports); - break; - case CAMERA_SCENE_BEACHANDSNOW: - exposureModes << QVariant::fromValue(QCamera::ExposureBeach) << QVariant::fromValue(QCamera::ExposureSnow); - break; - case CAMERA_SCENE_NIGHT: - exposureModes << QVariant::fromValue(QCamera::ExposureNight); - break; - default: break; - } - } - - return exposureModes; -} - -QVariant BbCameraExposureControl::requestedValue(ExposureParameter parameter) const -{ - if (parameter != QPlatformCameraExposure::ExposureMode) // no other parameter supported by BB10 API at the moment - return QVariant(); - - return QVariant::fromValue(m_requestedExposureMode); -} - -QVariant BbCameraExposureControl::actualValue(ExposureParameter parameter) const -{ - if (parameter != QPlatformCameraExposure::ExposureMode) // no other parameter supported by BB10 API at the moment - return QVariantList(); - - if (m_session->status() != QCamera::ActiveStatus) // we can query actual scene modes only with active viewfinder - return QVariantList(); - - camera_scenemode_t sceneMode = CAMERA_SCENE_DEFAULT; - const camera_error_t result = camera_get_scene_mode(m_session->handle(), &sceneMode); - - if (result != CAMERA_EOK) { - qWarning() << "Unable to retrieve scene mode:" << result; - return QVariant(); - } - - switch (sceneMode) { - case CAMERA_SCENE_AUTO: - return QVariant::fromValue(QCamera::ExposureAuto); - case CAMERA_SCENE_SPORTS: - return QVariant::fromValue(QCamera::ExposureSports); - case CAMERA_SCENE_CLOSEUP: - return QVariant::fromValue(QCamera::ExposurePortrait); - case CAMERA_SCENE_ACTION: - return QVariant::fromValue(QCamera::ExposureSports); - case CAMERA_SCENE_BEACHANDSNOW: - return (m_requestedExposureMode == QCamera::ExposureBeach ? QVariant::fromValue(QCamera::ExposureBeach) - : QVariant::fromValue(QCamera::ExposureSnow)); - case CAMERA_SCENE_NIGHT: - return QVariant::fromValue(QCamera::ExposureNight); - default: - break; - } - - return QVariant(); -} - -bool BbCameraExposureControl::setValue(ExposureParameter parameter, const QVariant& value) -{ - if (parameter != QPlatformCameraExposure::ExposureMode) // no other parameter supported by BB10 API at the moment - return false; - - if (m_session->status() != QCamera::ActiveStatus) // we can set actual scene modes only with active viewfinder - return false; - - camera_scenemode_t sceneMode = CAMERA_SCENE_DEFAULT; - - if (value.isValid()) { - m_requestedExposureMode = value.value<QCamera::ExposureMode>(); - emit requestedValueChanged(QPlatformCameraExposure::ExposureMode); - - switch (m_requestedExposureMode) { - case QCamera::ExposureAuto: - sceneMode = CAMERA_SCENE_AUTO; - break; - case QCamera::ExposureSports: - sceneMode = CAMERA_SCENE_SPORTS; - break; - case QCamera::ExposurePortrait: - sceneMode = CAMERA_SCENE_CLOSEUP; - break; - case QCamera::ExposureBeach: - sceneMode = CAMERA_SCENE_BEACHANDSNOW; - break; - case QCamera::ExposureSnow: - sceneMode = CAMERA_SCENE_BEACHANDSNOW; - break; - case QCamera::ExposureNight: - sceneMode = CAMERA_SCENE_NIGHT; - break; - default: - sceneMode = CAMERA_SCENE_DEFAULT; - break; - } - } - - const camera_error_t result = camera_set_scene_mode(m_session->handle(), sceneMode); - - if (result != CAMERA_EOK) { - qWarning() << "Unable to set scene mode:" << result; - return false; - } - - emit actualValueChanged(QPlatformCameraExposure::ExposureMode); - - return true; -} - -void BbCameraExposureControl::statusChanged(QCamera::Status status) -{ - if (status == QCamera::ActiveStatus || status == QCamera::LoadedStatus) - emit parameterRangeChanged(QPlatformCameraExposure::ExposureMode); -} - -QCamera::FlashModes BbCameraExposureControl::flashMode() const -{ - return m_flashMode; -} - -void BbCameraExposureControl::setFlashMode(QCamera::FlashModes mode) -{ - if (m_flashMode == mode) - return; - - if (m_session->status() != QCamera::ActiveStatus) // can only be changed when viewfinder is active - return; - -// if (m_flashMode == QCamera::FlashVideoLight) { -// const camera_error_t result = camera_config_videolight(m_session->handle(), CAMERA_VIDEOLIGHT_OFF); -// if (result != CAMERA_EOK) -// qWarning() << "Unable to switch off video light:" << result; -// } - - m_flashMode = mode; - -// if (m_flashMode == QCamera::FlashVideoLight) { -// const camera_error_t result = camera_config_videolight(m_session->handle(), CAMERA_VIDEOLIGHT_ON); -// if (result != CAMERA_EOK) -// qWarning() << "Unable to switch on video light:" << result; -// } else - { - camera_flashmode_t flashMode = CAMERA_FLASH_AUTO; - - if (m_flashMode == QCamera::FlashAuto) - flashMode = CAMERA_FLASH_AUTO; - else if (mode == QCamera::FlashOff) - flashMode = CAMERA_FLASH_OFF; - else if (mode == QCamera::FlashOn) - flashMode = CAMERA_FLASH_ON; - - const camera_error_t result = camera_config_flash(m_session->handle(), flashMode); - if (result != CAMERA_EOK) - qWarning() << "Unable to configure flash:" << result; - } -} - -bool BbCameraExposureControl::isFlashModeSupported(QCamera::FlashModes mode) const -{ - // ### Videolight maps to QCamera::TorchOn. -// bool supportsVideoLight = false; -// if (m_session->handle() != CAMERA_HANDLE_INVALID) { -// supportsVideoLight = camera_has_feature(m_session->handle(), CAMERA_FEATURE_VIDEOLIGHT); -// } - - // ### is this correct? - return true; -} - -bool BbCameraExposureControl::isFlashReady() const -{ - //TODO: check for flash charge-level here?!? - return true; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/camera/bbcameraexposurecontrol_p.h b/src/multimedia/platform/qnx/camera/bbcameraexposurecontrol_p.h deleted file mode 100644 index 60ffcd0f0..000000000 --- a/src/multimedia/platform/qnx/camera/bbcameraexposurecontrol_p.h +++ /dev/null @@ -1,89 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#ifndef BBCAMERAEXPOSURECONTROL_H -#define BBCAMERAEXPOSURECONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformcameraexposure_p.h> - -QT_BEGIN_NAMESPACE - -class BbCameraSession; - -class BbCameraExposureControl : public QPlatformCameraExposure -{ - Q_OBJECT -public: - explicit BbCameraExposureControl(BbCameraSession *session, QObject *parent = 0); - - bool isParameterSupported(ExposureParameter parameter) const override; - QVariantList supportedParameterRange(ExposureParameter parameter, bool *continuous) const override; - - QVariant requestedValue(ExposureParameter parameter) const override; - QVariant actualValue(ExposureParameter parameter) const override; - bool setValue(ExposureParameter parameter, const QVariant& value) override; - - QCamera::FlashMode flashMode() const override; - void setFlashMode(QCamera::FlashMode mode) override; - bool isFlashModeSupported(QCamera::FlashMode mode) const override; - bool isFlashReady() const override; - -private Q_SLOTS: - void statusChanged(QCamera::Status status); - -private: - BbCameraSession *m_session; - QCamera::ExposureMode m_requestedExposureMode; - - QCamera::FlashMode m_flashMode = QCamera::FlashAuto; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qnx/camera/bbcamerafocuscontrol.cpp b/src/multimedia/platform/qnx/camera/bbcamerafocuscontrol.cpp deleted file mode 100644 index 20d2d540a..000000000 --- a/src/multimedia/platform/qnx/camera/bbcamerafocuscontrol.cpp +++ /dev/null @@ -1,300 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#include "bbcamerafocuscontrol_p.h" - -#include "bbcamerasession_p.h" - -#include <QDebug> - -QT_BEGIN_NAMESPACE - -BbCameraFocusControl::BbCameraFocusControl(BbCameraSession *session, QObject *parent) - : QPlatformCameraFocus(parent) - , m_session(session) - , m_focusMode(QCamera::FocusModeAuto) - , m_customFocusPoint(QPointF(0, 0)) -{ - connect(m_session, SIGNAL(statusChanged(QCamera::Status)), this, SLOT(statusChanged(QCamera::Status))); -} - -QCamera::FocusMode BbCameraFocusControl::focusMode() const -{ - camera_focusmode_t focusMode = CAMERA_FOCUSMODE_OFF; - - const camera_error_t result = camera_get_focus_mode(m_session->handle(), &focusMode); - if (result != CAMERA_EOK) { - qWarning() << "Unable to retrieve focus mode from camera:" << result; - return QCamera::FocusModeAuto; - } - - switch (focusMode) { - case CAMERA_FOCUSMODE_EDOF: - return QCamera::FocusModeHyperfocal; - case CAMERA_FOCUSMODE_MANUAL: - return QCamera::FocusModeManual; - case CAMERA_FOCUSMODE_CONTINUOUS_MACRO: // fall through - case CAMERA_FOCUSMODE_MACRO: - return QCamera::FocusModeAutoNear; - case CAMERA_FOCUSMODE_AUTO: // fall through - case CAMERA_FOCUSMODE_CONTINUOUS_AUTO: - return QCamera::FocusModeAuto; - case CAMERA_FOCUSMODE_OFF: - default: - return QCamera::FocusModeAuto; - } -} - -void BbCameraFocusControl::setFocusMode(QCamera::FocusMode mode) -{ - if (m_focusMode == mode) - return; - - camera_focusmode_t focusMode = CAMERA_FOCUSMODE_OFF; - - switch (mode) { - case QCamera::FocusModeHyperfocal: - case QCamera::FocusModeInfinity: // not 100%, but close - focusMode = CAMERA_FOCUSMODE_EDOF; - break; - case QCamera::FocusModeManual: - focusMode = CAMERA_FOCUSMODE_MANUAL; - break; - case QCamera::FocusModeAutoNear: - focusMode = CAMERA_FOCUSMODE_MACRO; - break; - case QCamera::FocusModeAuto: - case QCamera::FocusModeAutoFar: - focusMode = CAMERA_FOCUSMODE_CONTINUOUS_AUTO; - break; - } - - const camera_error_t result = camera_set_focus_mode(m_session->handle(), focusMode); - - if (result != CAMERA_EOK) { - qWarning() << "Unable to set focus mode:" << result; - return; - } - - m_focusMode = mode; - emit focusModeChanged(m_focusMode); -} - -bool BbCameraFocusControl::isFocusModeSupported(QCamera::FocusMode mode) const -{ - if (m_session->state() == QCamera::UnloadedState) - return false; - - if (mode == QCamera::FocusModeHyperfocal) - return false; //TODO how to check? - else if (mode == QCamera::FocusModeManual) - return camera_has_feature(m_session->handle(), CAMERA_FEATURE_MANUALFOCUS); - else if (mode == QCamera::FocusModeAuto) - return camera_has_feature(m_session->handle(), CAMERA_FEATURE_AUTOFOCUS); - else if (mode == QCamera::FocusModeAutoNear) - return camera_has_feature(m_session->handle(), CAMERA_FEATURE_MACROFOCUS); - - return false; -} - -QPointF BbCameraFocusControl::focusPoint() const -{ - return m_customFocusPoint; -} - -void BbCameraFocusControl::setCustomFocusPoint(const QPointF &point) -{ - if (m_customFocusPoint == point) - return; - - m_customFocusPoint = point; - emit customFocusPointChanged(m_customFocusPoint); - - updateCustomFocusRegion(); -} - -void BbCameraFocusControl::updateCustomFocusRegion() -{ - // get the size of the viewfinder - int viewfinderWidth = 0; - int viewfinderHeight = 0; - - if (!retrieveViewfinderSize(&viewfinderWidth, &viewfinderHeight)) - return; - - // define a 40x40 pixel focus region around the custom focus point - camera_region_t focusRegion; - focusRegion.left = qMax(0, static_cast<int>(m_customFocusPoint.x() * viewfinderWidth) - 20); - focusRegion.top = qMax(0, static_cast<int>(m_customFocusPoint.y() * viewfinderHeight) - 20); - focusRegion.width = 40; - focusRegion.height = 40; - - camera_error_t result = camera_set_focus_regions(m_session->handle(), 1, &focusRegion); - if (result != CAMERA_EOK) { - qWarning() << "Unable to set focus region:" << result; - return; - } - - // re-set focus mode to apply focus region changes - camera_focusmode_t focusMode = CAMERA_FOCUSMODE_OFF; - result = camera_get_focus_mode(m_session->handle(), &focusMode); - camera_set_focus_mode(m_session->handle(), focusMode); -} - -bool BbCameraFocusControl::retrieveViewfinderSize(int *width, int *height) -{ - if (!width || !height) - return false; - - camera_error_t result = CAMERA_EOK; - if (m_session->captureMode() & QCamera::CaptureStillImage) - result = camera_get_photovf_property(m_session->handle(), - CAMERA_IMGPROP_WIDTH, width, - CAMERA_IMGPROP_HEIGHT, height); - else if (m_session->captureMode() & QCamera::CaptureVideo) - result = camera_get_videovf_property(m_session->handle(), - CAMERA_IMGPROP_WIDTH, width, - CAMERA_IMGPROP_HEIGHT, height); - - if (result != CAMERA_EOK) { - qWarning() << "Unable to retrieve viewfinder size:" << result; - return false; - } - - return true; -} - - -qreal BbCameraFocusControl::maximumOpticalZoom() const -{ - //TODO: optical zoom support not available in BB10 API yet - return 1.0; -} - -qreal BbCameraFocusControl::maximumDigitalZoom() const -{ - return m_maximumZoomFactor; -} - -qreal BbCameraFocusControl::requestedOpticalZoom() const -{ - //TODO: optical zoom support not available in BB10 API yet - return 1.0; -} - -qreal BbCameraFocusControl::requestedDigitalZoom() const -{ - return currentDigitalZoom(); -} - -qreal BbCameraFocusControl::currentOpticalZoom() const -{ - //TODO: optical zoom support not available in BB10 API yet - return 1.0; -} - -qreal BbCameraFocusControl::currentDigitalZoom() const -{ - if (m_session->status() != QCamera::ActiveStatus) - return 1.0; - - unsigned int zoomFactor = 0; - camera_error_t result = CAMERA_EOK; - - if (m_session->captureMode() & QCamera::CaptureStillImage) - result = camera_get_photovf_property(m_session->handle(), CAMERA_IMGPROP_ZOOMFACTOR, &zoomFactor); - else if (m_session->captureMode() & QCamera::CaptureVideo) - result = camera_get_videovf_property(m_session->handle(), CAMERA_IMGPROP_ZOOMFACTOR, &zoomFactor); - - if (result != CAMERA_EOK) - return 1.0; - - return zoomFactor; -} - -void BbCameraFocusControl::zoomTo(qreal optical, qreal digital) -{ - Q_UNUSED(optical); - - if (m_session->status() != QCamera::ActiveStatus) - return; - - const qreal actualZoom = qBound(m_minimumZoomFactor, digital, m_maximumZoomFactor); - - const camera_error_t result = camera_set_zoom(m_session->handle(), actualZoom, false); - - if (result != CAMERA_EOK) { - qWarning() << "Unable to change zoom factor:" << result; - return; - } - - if (m_requestedZoomFactor != digital) { - m_requestedZoomFactor = digital; - emit requestedDigitalZoomChanged(m_requestedZoomFactor); - } - - emit currentDigitalZoomChanged(actualZoom); -} - -void BbCameraFocusControl::statusChanged(QCamera::Status status) -{ - if (status == QCamera::ActiveStatus) { - // retrieve information about zoom limits - unsigned int maximumZoomLimit = 0; - unsigned int minimumZoomLimit = 0; - bool smoothZoom = false; - - const camera_error_t result = camera_get_zoom_limits(m_session->handle(), &maximumZoomLimit, &minimumZoomLimit, &smoothZoom); - if (result == CAMERA_EOK) { - const qreal oldMaximumZoomFactor = m_maximumZoomFactor; - m_maximumZoomFactor = maximumZoomLimit; - - if (oldMaximumZoomFactor != m_maximumZoomFactor) - emit maximumDigitalZoomChanged(m_maximumZoomFactor); - - m_minimumZoomFactor = minimumZoomLimit; - m_supportsSmoothZoom = smoothZoom; - } else { - m_maximumZoomFactor = 1.0; - m_minimumZoomFactor = 1.0; - m_supportsSmoothZoom = false; - } - } -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/camera/bbcamerafocuscontrol_p.h b/src/multimedia/platform/qnx/camera/bbcamerafocuscontrol_p.h deleted file mode 100644 index 895701283..000000000 --- a/src/multimedia/platform/qnx/camera/bbcamerafocuscontrol_p.h +++ /dev/null @@ -1,99 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#ifndef BBCAMERAFOCUSCONTROL_H -#define BBCAMERAFOCUSCONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformcamerafocus_p.h> - -QT_BEGIN_NAMESPACE - -class BbCameraSession; - -class BbCameraFocusControl : public QPlatformCameraFocus -{ - Q_OBJECT -public: - explicit BbCameraFocusControl(BbCameraSession *session, QObject *parent = 0); - - QCamera::FocusMode focusMode() const override; - void setFocusMode(QCamera::FocusMode mode) override; - bool isFocusModeSupported(QCamera::FocusMode mode) const override; - QPointF focusPoint() const override; - void setCustomFocusPoint(const QPointF &point) override; - - qreal maximumOpticalZoom() const override; - qreal maximumDigitalZoom() const override; - qreal requestedOpticalZoom() const override; - qreal requestedDigitalZoom() const override; - qreal currentOpticalZoom() const override; - qreal currentDigitalZoom() const override; - void zoomTo(qreal optical, qreal digital) override; - -private Q_SLOTS: - void statusChanged(QCamera::Status status); - -private: - void updateCustomFocusRegion(); - bool retrieveViewfinderSize(int *width, int *height); - - BbCameraSession *m_session; - - QCamera::FocusMode m_focusMode; - QPointF m_customFocusPoint; - - qreal m_minimumZoomFactor; - qreal m_maximumZoomFactor; - bool m_supportsSmoothZoom; - qreal m_requestedZoomFactor; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qnx/camera/bbcameraimagecapturecontrol.cpp b/src/multimedia/platform/qnx/camera/bbcameraimagecapturecontrol.cpp deleted file mode 100644 index 5a78e1ee0..000000000 --- a/src/multimedia/platform/qnx/camera/bbcameraimagecapturecontrol.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#include "bbcameraimagecapturecontrol_p.h" - -#include "bbcamerasession_p.h" - -QT_BEGIN_NAMESPACE - -BbCameraImageCaptureControl::BbCameraImageCaptureControl(BbCameraSession *session, QObject *parent) - : QPlatformImageCapture(parent) - , m_session(session) -{ - connect(m_session, SIGNAL(readyForCaptureChanged(bool)), this, SIGNAL(readyForCaptureChanged(bool))); - connect(m_session, SIGNAL(imageExposed(int)), this, SIGNAL(imageExposed(int))); - connect(m_session, SIGNAL(imageCaptured(int,QImage)), this, SIGNAL(imageCaptured(int,QImage))); - connect(m_session, SIGNAL(imageMetadataAvailable(int,QString,QVariant)), this, SIGNAL(imageMetadataAvailable(int,QString,QVariant))); - connect(m_session, SIGNAL(imageAvailable(int,QVideoFrame)), this, SIGNAL(imageAvailable(int,QVideoFrame))); - connect(m_session, SIGNAL(imageSaved(int,QString)), this, SIGNAL(imageSaved(int,QString))); - connect(m_session, SIGNAL(imageCaptureError(int,int,QString)), this, SIGNAL(error(int,int,QString))); -} - -bool BbCameraImageCaptureControl::isReadyForCapture() const -{ - return m_session->isReadyForCapture(); -} - -int BbCameraImageCaptureControl::capture(const QString &fileName) -{ - return m_session->capture(fileName); -} - -int BbCameraImageCaptureControl::captureToBuffer() -{ - // ### implement me - return -1; -} - -void BbCameraImageCaptureControl::cancelCapture() -{ - m_session->cancelCapture(); -} - -QImageEncoderSettings BbCameraImageCaptureControl::imageSettings() const -{ - return m_session->imageSettings(); -} - -void BbCameraImageCaptureControl::setImageSettings(const QImageEncoderSettings &settings) -{ - m_session->setImageSettings(settings); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/camera/bbcameraimagecapturecontrol_p.h b/src/multimedia/platform/qnx/camera/bbcameraimagecapturecontrol_p.h deleted file mode 100644 index 62ba19c97..000000000 --- a/src/multimedia/platform/qnx/camera/bbcameraimagecapturecontrol_p.h +++ /dev/null @@ -1,80 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#ifndef BBCAMERAIMAGECAPTURECONTROL_H -#define BBCAMERAIMAGECAPTURECONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformimagecapture_p.h> - -QT_BEGIN_NAMESPACE - -class BbCameraSession; - -class BbCameraImageCaptureControl : public QPlatformImageCapture -{ - Q_OBJECT -public: - explicit BbCameraImageCaptureControl(BbCameraSession *session, QObject *parent = 0); - - bool isReadyForCapture() const override; - - int capture(const QString &fileName) override; - int captureToBuffer() override; - void cancelCapture() override; - - QImageEncoderSettings imageSettings() const override; - void setImageSettings(const QImageEncoderSettings &settings) override; - -private: - BbCameraSession *m_session; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qnx/camera/bbcameraimageprocessingcontrol.cpp b/src/multimedia/platform/qnx/camera/bbcameraimageprocessingcontrol.cpp deleted file mode 100644 index 08e468eea..000000000 --- a/src/multimedia/platform/qnx/camera/bbcameraimageprocessingcontrol.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#include "bbcameraimageprocessingcontrol_p.h" - -#include "bbcamerasession_p.h" - -#include <QDebug> - -QT_BEGIN_NAMESPACE - -BbCameraImageProcessingControl::BbCameraImageProcessingControl(BbCameraSession *session, QObject *parent) - : QPlatformCameraImageProcessing(parent) - , m_session(session) -{ -} - -bool BbCameraImageProcessingControl::isParameterSupported(ProcessingParameter parameter) const -{ - return (parameter == QPlatformCameraImageProcessing::WhiteBalancePreset); -} - -bool BbCameraImageProcessingControl::isParameterValueSupported(ProcessingParameter parameter, const QVariant &value) const -{ - if (parameter != QPlatformCameraImageProcessing::WhiteBalancePreset) - return false; - - if (m_session->handle() == CAMERA_HANDLE_INVALID) - return false; - - int supported = 0; - camera_whitebalancemode_t modes[20]; - const camera_error_t result = camera_get_whitebalance_modes(m_session->handle(), 20, &supported, modes); - - if (result != CAMERA_EOK) { - qWarning() << "Unable to retrieve supported whitebalance modes:" << result; - return false; - } - - QSet<QCamera::WhiteBalanceMode> supportedModes; - for (int i = 0; i < supported; ++i) { - switch (modes[i]) { - case CAMERA_WHITEBALANCEMODE_AUTO: - supportedModes.insert(QCamera::WhiteBalanceAuto); - break; - case CAMERA_WHITEBALANCEMODE_MANUAL: - supportedModes.insert(QCamera::WhiteBalanceManual); - break; - default: - break; - } - } - - return supportedModes.contains(value.value<QCamera::WhiteBalanceMode>()); -} - -void BbCameraImageProcessingControl::setParameter(ProcessingParameter parameter, const QVariant &value) -{ - if (parameter != QPlatformCameraImageProcessing::WhiteBalancePreset) - return; - - if (m_session->handle() == CAMERA_HANDLE_INVALID) - return; - - camera_whitebalancemode_t mode = CAMERA_WHITEBALANCEMODE_DEFAULT; - switch (value.value<QCamera::WhiteBalanceMode>()) { - case QCamera::WhiteBalanceAuto: - mode = CAMERA_WHITEBALANCEMODE_AUTO; - break; - case QCamera::WhiteBalanceManual: - mode = CAMERA_WHITEBALANCEMODE_MANUAL; - break; - default: - break; - } - - const camera_error_t result = camera_set_whitebalance_mode(m_session->handle(), mode); - - if (result != CAMERA_EOK) - qWarning() << "Unable to set whitebalance mode:" << result; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/camera/bbcameraimageprocessingcontrol_p.h b/src/multimedia/platform/qnx/camera/bbcameraimageprocessingcontrol_p.h deleted file mode 100644 index 6a0af4a85..000000000 --- a/src/multimedia/platform/qnx/camera/bbcameraimageprocessingcontrol_p.h +++ /dev/null @@ -1,75 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#ifndef BBCAMERAIMAGEPROCESSINGCONTROL_H -#define BBCAMERAIMAGEPROCESSINGCONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformcameraimageprocessing_p.h> - -QT_BEGIN_NAMESPACE - -class BbCameraSession; - -class BbCameraImageProcessingControl : public QPlatformCameraImageProcessing -{ - Q_OBJECT -public: - explicit BbCameraImageProcessingControl(BbCameraSession *session, QObject *parent = 0); - - bool isParameterSupported(ProcessingParameter) const override; - bool isParameterValueSupported(ProcessingParameter parameter, const QVariant &value) const override; - void setParameter(ProcessingParameter parameter, const QVariant &value) override; - -private: - BbCameraSession *m_session; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qnx/camera/bbcameramediarecordercontrol.cpp b/src/multimedia/platform/qnx/camera/bbcameramediarecordercontrol.cpp deleted file mode 100644 index a90ae33be..000000000 --- a/src/multimedia/platform/qnx/camera/bbcameramediarecordercontrol.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#include "bbcameramediarecordercontrol_p.h" - -#include "bbcamerasession_p.h" - -#include <QDebug> -#include <QUrl> - -#include <audio/audio_manager_device.h> -#include <audio/audio_manager_volume.h> - -QT_BEGIN_NAMESPACE - -static audio_manager_device_t currentAudioInputDevice() -{ - audio_manager_device_t device = AUDIO_DEVICE_HEADSET; - - const int result = audio_manager_get_default_input_device(&device); - if (result != EOK) { - qWarning() << "Unable to retrieve default audio input device:" << result; - return AUDIO_DEVICE_HEADSET; - } - - return device; -} - -BbCameraMediaRecorderControl::BbCameraMediaRecorderControl(BbCameraSession *session, QObject *parent) - : QPlatformMediaEncoder(parent) - , m_session(session) -{ - connect(m_session, SIGNAL(videoStateChanged(QMediaRecorder::RecorderState)), this, SIGNAL(stateChanged(QMediaRecorder::RecorderState))); - connect(m_session, SIGNAL(durationChanged(qint64)), this, SIGNAL(durationChanged(qint64))); - connect(m_session, SIGNAL(actualLocationChanged(QUrl)), this, SIGNAL(actualLocationChanged(QUrl))); - connect(m_session, SIGNAL(videoError(int,QString)), this, SIGNAL(error(int,QString))); -} - -bool BbCameraMediaRecorderControl::isLocationWritable(const QUrl &location) const -{ - return true; -} - -QMediaRecorder::RecorderState BbCameraMediaRecorderControl::state() const -{ - return m_session->videoState(); -} - -qint64 BbCameraMediaRecorderControl::duration() const -{ - return m_session->duration(); -} - -bool BbCameraMediaRecorderControl::isMuted() const -{ - bool muted = false; - - const int result = audio_manager_get_input_mute(currentAudioInputDevice(), &muted); - if (result != EOK) { - emit const_cast<BbCameraMediaRecorderControl*>(this)->error(QMediaRecorder::ResourceError, tr("Unable to retrieve mute status")); - return false; - } - - return muted; -} - -qreal BbCameraMediaRecorderControl::volume() const -{ - double level = 0.0; - - const int result = audio_manager_get_input_level(currentAudioInputDevice(), &level); - if (result != EOK) { - emit const_cast<BbCameraMediaRecorderControl*>(this)->error(QMediaRecorder::ResourceError, tr("Unable to retrieve audio input volume")); - return 0.0; - } - - return (level / 100); -} - -void BbCameraMediaRecorderControl::record(QMediaEncoderSettings &) -{ - if (m_session) { - m_session->applyVideoSettings(); - m_session->startVideoRecording(outputLocation()); - } -} - -void BbCameraMediaRecorderControl::stop() -{ - if (m_session) - m_session->stopVideoRecording(); -} - -void BbCameraMediaRecorderControl::setMuted(bool muted) -{ - const int result = audio_manager_set_input_mute(currentAudioInputDevice(), muted); - if (result != EOK) { - emit error(QMediaRecorder::ResourceError, tr("Unable to set mute status")); - } else { - emit mutedChanged(muted); - } -} - -void BbCameraMediaRecorderControl::setVolume(qreal volume) -{ - const int result = audio_manager_set_input_level(currentAudioInputDevice(), (volume * 100)); - if (result != EOK) { - emit error(QMediaRecorder::ResourceError, tr("Unable to set audio input volume")); - } else { - emit volumeChanged(volume); - } -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/camera/bbcameramediarecordercontrol_p.h b/src/multimedia/platform/qnx/camera/bbcameramediarecordercontrol_p.h deleted file mode 100644 index 5ea453a0b..000000000 --- a/src/multimedia/platform/qnx/camera/bbcameramediarecordercontrol_p.h +++ /dev/null @@ -1,84 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#ifndef BBCAMERAMEDIARECORDERCONTROL_H -#define BBCAMERAMEDIARECORDERCONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediaencoder_p.h> - -QT_BEGIN_NAMESPACE - -class BbCameraSession; - -class BbCameraMediaRecorderControl : public QPlatformMediaEncoder -{ - Q_OBJECT -public: - explicit BbCameraMediaRecorderControl(BbCameraSession *session, QObject *parent = 0); - - QMediaRecorder::RecorderState state() const override; - bool isLocationWritable(const QUrl &location) const override; - qint64 duration() const override; - bool isMuted() const override; - qreal volume() const override; - void record(QMediaEncoderSettings &settings) override; - void stop() override; - - -public Q_SLOTS: - void setMuted(bool muted) override; - void setVolume(qreal volume) override; - -private: - BbCameraSession *m_session; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qnx/camera/bbcameraorientationhandler.cpp b/src/multimedia/platform/qnx/camera/bbcameraorientationhandler.cpp deleted file mode 100644 index d600f3db0..000000000 --- a/src/multimedia/platform/qnx/camera/bbcameraorientationhandler.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#include "bbcameraorientationhandler.h" - -#include <QAbstractEventDispatcher> -#include <QGuiApplication> -#include <QScreen> -#include <QDebug> - -#include <bps/orientation.h> - -QT_BEGIN_NAMESPACE - -BbCameraOrientationHandler::BbCameraOrientationHandler(QObject *parent) - : QObject(parent) - , m_orientation(0) -{ - QCoreApplication::eventDispatcher()->installNativeEventFilter(this); - int result = orientation_request_events(0); - if (result == BPS_FAILURE) - qWarning() << "Unable to register for orientation change events"; - - orientation_direction_t direction = ORIENTATION_FACE_UP; - int angle = 0; - - result = orientation_get(&direction, &angle); - if (result == BPS_FAILURE) { - qWarning() << "Unable to retrieve initial orientation"; - } else { - m_orientation = angle; - } -} - -BbCameraOrientationHandler::~BbCameraOrientationHandler() -{ - const int result = orientation_stop_events(0); - if (result == BPS_FAILURE) - qWarning() << "Unable to unregister for orientation change events"; - - QCoreApplication::eventDispatcher()->removeNativeEventFilter(this); -} - -bool BbCameraOrientationHandler::nativeEventFilter(const QByteArray&, void *message, qintptr *) -{ - bps_event_t* const event = static_cast<bps_event_t*>(message); - if (!event || bps_event_get_domain(event) != orientation_get_domain()) - return false; - - const int angle = orientation_event_get_angle(event); - if (angle != m_orientation) { - if (angle == 180) // The screen does not rotate at 180 degrees - return false; - - m_orientation = angle; - emit orientationChanged(m_orientation); - } - - return false; // do not drop the event -} - -int BbCameraOrientationHandler::viewfinderOrientation() const -{ - // On a keyboard device we do not rotate the screen at all - if (qGuiApp->primaryScreen()->nativeOrientation() - != qGuiApp->primaryScreen()->primaryOrientation()) { - return m_orientation; - } - - return 0; -} - -int BbCameraOrientationHandler::orientation() const -{ - return m_orientation; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/camera/bbcameraorientationhandler_p.h b/src/multimedia/platform/qnx/camera/bbcameraorientationhandler_p.h deleted file mode 100644 index ace5118ef..000000000 --- a/src/multimedia/platform/qnx/camera/bbcameraorientationhandler_p.h +++ /dev/null @@ -1,80 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#ifndef BBCAMERAORIENTATIONHANDLER_H -#define BBCAMERAORIENTATIONHANDLER_H - -#include <QAbstractNativeEventFilter> -#include <QObject> - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -QT_BEGIN_NAMESPACE - -class BbCameraOrientationHandler : public QObject, public QAbstractNativeEventFilter -{ - Q_OBJECT -public: - explicit BbCameraOrientationHandler(QObject *parent = 0); - ~BbCameraOrientationHandler(); - - bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override; - - int orientation() const; - - int viewfinderOrientation() const; - -Q_SIGNALS: - void orientationChanged(int degree); - -private: - int m_orientation; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qnx/camera/bbcameraservice.cpp b/src/multimedia/platform/qnx/camera/bbcameraservice.cpp deleted file mode 100644 index 6f68e68ed..000000000 --- a/src/multimedia/platform/qnx/camera/bbcameraservice.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#include "bbcameraservice_p.h" - -#include "bbcameraaudioencodersettingscontrol_p.h" -#include "bbcameracontrol_p.h" -#include "bbcameraexposurecontrol_p.h" -#include "bbcamerafocuscontrol_p.h" -#include "bbcameraimagecapturecontrol_p.h" -#include "bbcameraimageprocessingcontrol_p.h" -#include "bbcameramediarecordercontrol_p.h" -#include "bbcamerasession_p.h" -#include "bbcameravideoencodersettingscontrol_p.h" -#include "bbvideorenderercontrol_p.h" - -#include <QDebug> -#include <QVariant> - -QT_BEGIN_NAMESPACE - -BbCameraService::BbCameraService(QObject *parent) - : QObject(parent) - , m_cameraSession(new BbCameraSession(this)) - , m_cameraAudioEncoderSettingsControl(new BbCameraAudioEncoderSettingsControl(m_cameraSession, this)) - , m_cameraControl(new BbCameraControl(m_cameraSession, this)) - , m_cameraExposureControl(new BbCameraExposureControl(m_cameraSession, this)) - , m_cameraFocusControl(new BbCameraFocusControl(m_cameraSession, this)) - , m_cameraImageCaptureControl(new BbCameraImageCaptureControl(m_cameraSession, this)) - , m_cameraImageProcessingControl(new BbCameraImageProcessingControl(m_cameraSession, this)) - , m_cameraMediaRecorderControl(new BbCameraMediaRecorderControl(m_cameraSession, this)) - , m_cameraVideoEncoderSettingsControl(new BbCameraVideoEncoderSettingsControl(m_cameraSession, this)) - , m_videoRendererControl(new BbVideoRendererControl(m_cameraSession, this)) -{ -} - -BbCameraService::~BbCameraService() -{ -} - -QPlatformCamera *BbCameraService::camera() -{ - return m_cameraControl; -} - -QPlatformImageCapture *BbCameraService::imageCapture() -{ - return m_cameraImageCaptureControl; -} - -QPlatformMediaEncoder *BbCameraService::mediaEncoder() -{ - return m_cameraMediaRecorderControl; -} - -void BbCameraService::setVideoPreview(QVideoSink *surface) -{ - // #### -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/camera/bbcameraservice_p.h b/src/multimedia/platform/qnx/camera/bbcameraservice_p.h deleted file mode 100644 index 128fc1154..000000000 --- a/src/multimedia/platform/qnx/camera/bbcameraservice_p.h +++ /dev/null @@ -1,100 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#ifndef BBCAMERASERVICE_H -#define BBCAMERASERVICE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QObject> - -#include <private/qplatformmediacapture_p.h> - -QT_BEGIN_NAMESPACE - -class BbCameraAudioEncoderSettingsControl; -class BbCameraControl; -class BbCameraExposureControl; -class BbCameraFocusControl; -class BbCameraImageCaptureControl; -class BbCameraImageProcessingControl; -class BbCameraMediaRecorderControl; -class BbCameraSession; -class BbCameraVideoEncoderSettingsControl; -class BbVideoRendererControl; - -class BbCameraService : public QPlatformMediaCaptureSession -{ - Q_OBJECT - -public: - explicit BbCameraService(QObject *parent = 0); - ~BbCameraService(); - - QPlatformCamera *camera() override; - QPlatformImageCapture *imageCapture() override; - QPlatformMediaEncoder *mediaEncoder() override; - - void setVideoPreview(QVideoSink *surface) override; - -private: - BbCameraSession* m_cameraSession; - - BbCameraAudioEncoderSettingsControl* m_cameraAudioEncoderSettingsControl; - BbCameraControl* m_cameraControl; - BbCameraExposureControl* m_cameraExposureControl; - BbCameraFocusControl* m_cameraFocusControl; - BbCameraImageCaptureControl* m_cameraImageCaptureControl; - BbCameraImageProcessingControl* m_cameraImageProcessingControl; - BbCameraMediaRecorderControl* m_cameraMediaRecorderControl; - BbCameraVideoEncoderSettingsControl* m_cameraVideoEncoderSettingsControl; - BbVideoRendererControl* m_videoRendererControl; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qnx/camera/bbcamerasession.cpp b/src/multimedia/platform/qnx/camera/bbcamerasession.cpp deleted file mode 100644 index d82cbe482..000000000 --- a/src/multimedia/platform/qnx/camera/bbcamerasession.cpp +++ /dev/null @@ -1,1049 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#include "bbcamerasession_p.h" - -#include "bbcameraorientationhandler_p.h" -#include "windowgrabber_p.h" - -#include <QBuffer> -#include <QDebug> -#include <QImage> -#include <QUrl> -#include <QVideoFrameFormat> -#include <qmath.h> -#include <private/qmediarecorder_p.h> -#include <private/qplatformimagecapture_p.h> -#include <private/qmediastoragelocation_p.h> - -#include <algorithm> - -QT_BEGIN_NAMESPACE - -static QString errorToString(camera_error_t error) -{ - switch (error) { - case CAMERA_EOK: - return QLatin1String("No error"); - case CAMERA_EAGAIN: - return QLatin1String("Camera unavailable"); - case CAMERA_EINVAL: - return QLatin1String("Invalid argument"); - case CAMERA_ENODEV: - return QLatin1String("Camera not found"); - case CAMERA_EMFILE: - return QLatin1String("File table overflow"); - case CAMERA_EBADF: - return QLatin1String("Invalid handle passed"); - case CAMERA_EACCESS: - return QLatin1String("No permission"); - case CAMERA_EBADR: - return QLatin1String("Invalid file descriptor"); - case CAMERA_ENOENT: - return QLatin1String("File or directory does not exists"); - case CAMERA_ENOMEM: - return QLatin1String("Memory allocation failed"); - case CAMERA_EOPNOTSUPP: - return QLatin1String("Operation not supported"); - case CAMERA_ETIMEDOUT: - return QLatin1String("Communication timeout"); - case CAMERA_EALREADY: - return QLatin1String("Operation already in progress"); - case CAMERA_EUNINIT: - return QLatin1String("Camera library not initialized"); - case CAMERA_EREGFAULT: - return QLatin1String("Callback registration failed"); - case CAMERA_EMICINUSE: - return QLatin1String("Microphone in use already"); - case CAMERA_ENODATA: - return QLatin1String("Data does not exist"); - case CAMERA_EBUSY: - return QLatin1String("Camera busy"); - case CAMERA_EDESKTOPCAMERAINUSE: - return QLatin1String("Desktop camera in use already"); - case CAMERA_ENOSPC: - return QLatin1String("Disk is full"); - case CAMERA_EPOWERDOWN: - return QLatin1String("Camera in power down state"); - case CAMERA_3ALOCKED: - return QLatin1String("3A have been locked"); -// case CAMERA_EVIEWFINDERFROZEN: // not yet available in 10.2 NDK -// return QLatin1String("Freeze flag set"); - default: - return QLatin1String("Unknown error"); - } -} - -QDebug operator<<(QDebug debug, camera_error_t error) -{ - debug.nospace() << errorToString(error); - return debug.space(); -} - -BbCameraSession::BbCameraSession(QObject *parent) - : QObject(parent) - , m_nativeCameraOrientation(0) - , m_orientationHandler(new BbCameraOrientationHandler(this)) - , m_status(QCamera::UnloadedStatus) - , m_state(QCamera::UnloadedState) - , m_captureMode(QCamera::CaptureStillImage) - , m_device("bb:RearCamera") - , m_previewIsVideo(true) - , m_surface(0) - , m_lastImageCaptureId(0) - , m_videoState(QMediaRecorder::StoppedState) - , m_handle(CAMERA_HANDLE_INVALID) - , m_windowGrabber(new WindowGrabber(this)) -{ - connect(this, SIGNAL(statusChanged(QCamera::Status)), SLOT(updateReadyForCapture())); - connect(this, SIGNAL(captureModeChanged(QCamera::CaptureModes)), SLOT(updateReadyForCapture())); - connect(m_orientationHandler, SIGNAL(orientationChanged(int)), SLOT(deviceOrientationChanged(int))); - - connect(m_windowGrabber, SIGNAL(frameGrabbed(QImage, int)), SLOT(viewfinderFrameGrabbed(QImage))); -} - -BbCameraSession::~BbCameraSession() -{ - stopViewFinder(); - closeCamera(); -} - -camera_handle_t BbCameraSession::handle() const -{ - return m_handle; -} - -QCamera::State BbCameraSession::state() const -{ - return m_state; -} - -void BbCameraSession::setState(QCamera::State state) -{ - if (m_state == state) - return; - - const QCamera::State previousState = m_state; - - if (previousState == QCamera::UnloadedState) { - if (state == QCamera::LoadedState) { - if (openCamera()) { - m_state = state; - } - } else if (state == QCamera::ActiveState) { - if (openCamera()) { - QMetaObject::invokeMethod(this, "applyConfiguration", Qt::QueuedConnection); - m_state = state; - } - } - } else if (previousState == QCamera::LoadedState) { - if (state == QCamera::UnloadedState) { - closeCamera(); - m_state = state; - } else if (state == QCamera::ActiveState) { - QMetaObject::invokeMethod(this, "applyConfiguration", Qt::QueuedConnection); - m_state = state; - } - } else if (previousState == QCamera::ActiveState) { - if (state == QCamera::LoadedState) { - stopViewFinder(); - m_state = state; - } else if (state == QCamera::UnloadedState) { - stopViewFinder(); - closeCamera(); - m_state = state; - } - } - - if (m_state != previousState) - emit stateChanged(m_state); -} - -QCamera::Status BbCameraSession::status() const -{ - return m_status; -} - -QCamera::CaptureModes BbCameraSession::captureMode() const -{ - return m_captureMode; -} - -void BbCameraSession::setCaptureMode(QCamera::CaptureModes captureMode) -{ - if (m_captureMode == captureMode) - return; - - m_captureMode = captureMode; - emit captureModeChanged(m_captureMode); -} - -bool BbCameraSession::isCaptureModeSupported(QCamera::CaptureModes mode) const -{ - if (m_handle == CAMERA_HANDLE_INVALID) { - // the camera has not been loaded yet via QCamera::load(), so - // we open it temporarily to peek for the supported capture modes - - camera_unit_t unit = CAMERA_UNIT_REAR; - if (m_device == cameraIdentifierFront()) - unit = CAMERA_UNIT_FRONT; - else if (m_device == cameraIdentifierRear()) - unit = CAMERA_UNIT_REAR; - else if (m_device == cameraIdentifierDesktop()) - unit = CAMERA_UNIT_DESKTOP; - - camera_handle_t handle; - const camera_error_t result = camera_open(unit, CAMERA_MODE_RW, &handle); - if (result != CAMERA_EOK) - return true; - - const bool supported = isCaptureModeSupported(handle, mode); - - camera_close(handle); - - return supported; - } else { - return isCaptureModeSupported(m_handle, mode); - } -} - -QByteArray BbCameraSession::cameraIdentifierFront() -{ - return "bb:FrontCamera"; -} - -QByteArray BbCameraSession::cameraIdentifierRear() -{ - return "bb:RearCamera"; -} - -QByteArray BbCameraSession::cameraIdentifierDesktop() -{ - return "bb:DesktopCamera"; -} - -void BbCameraSession::setDevice(const QByteArray &device) -{ - m_device = device; -} - -QByteArray BbCameraSession::device() const -{ - return m_device; -} - -void BbCameraSession::setSurface(QVideoSink *surface) -{ - QMutexLocker locker(&m_surfaceMutex); - - if (m_surface == surface) - return; - - m_surface = surface; -} - -bool BbCameraSession::isReadyForCapture() const -{ - if (m_captureMode & QCamera::CaptureStillImage) - return (m_status == QCamera::ActiveStatus); - - if (m_captureMode & QCamera::CaptureVideo) - return (m_status == QCamera::ActiveStatus); - - return false; -} - -/** - * A helper structure that keeps context data for image capture callbacks. - */ -struct ImageCaptureData -{ - int requestId; - QString fileName; - BbCameraSession *session; -}; - -static void imageCaptureShutterCallback(camera_handle_t handle, void *context) -{ - Q_UNUSED(handle); - - const ImageCaptureData *data = static_cast<ImageCaptureData*>(context); - - // We are inside a worker thread here, so emit imageExposed inside the main thread - QMetaObject::invokeMethod(data->session, "imageExposed", Qt::QueuedConnection, - Q_ARG(int, data->requestId)); -} - -static void imageCaptureImageCallback(camera_handle_t handle, camera_buffer_t *buffer, void *context) -{ - Q_UNUSED(handle); - - QScopedPointer<ImageCaptureData> data(static_cast<ImageCaptureData*>(context)); - - if (buffer->frametype != CAMERA_FRAMETYPE_JPEG) { - // We are inside a worker thread here, so emit error signal inside the main thread - QMetaObject::invokeMethod(data->session, "imageCaptureError", Qt::QueuedConnection, - Q_ARG(int, data->requestId), - Q_ARG(QImageCapture::Error, QImageCapture::FormatError), - Q_ARG(QString, BbCameraSession::tr("Camera provides image in unsupported format"))); - return; - } - - const QByteArray rawData((const char*)buffer->framebuf, buffer->framedesc.jpeg.bufsize); - - QImage image; - const bool ok = image.loadFromData(rawData, "JPG"); - if (!ok) { - const QString errorMessage = BbCameraSession::tr("Could not load JPEG data from frame"); - // We are inside a worker thread here, so emit error signal inside the main thread - QMetaObject::invokeMethod(data->session, "imageCaptureError", Qt::QueuedConnection, - Q_ARG(int, data->requestId), - Q_ARG(QImageCapture::Error, QImageCapture::FormatError), - Q_ARG(QString, errorMessage)); - return; - } - - - // We are inside a worker thread here, so invoke imageCaptured inside the main thread - QMetaObject::invokeMethod(data->session, "imageCaptured", Qt::QueuedConnection, - Q_ARG(int, data->requestId), - Q_ARG(QImage, image), - Q_ARG(QString, data->fileName)); -} - -int BbCameraSession::capture(const QString &fileName) -{ - m_lastImageCaptureId++; - - if (!isReadyForCapture()) { - emit imageCaptureError(m_lastImageCaptureId, QImageCapture::NotReadyError, - QPlatformImageCapture::msgCameraNotReady()); - return m_lastImageCaptureId; - } - - // prepare context object for callback - ImageCaptureData *context = new ImageCaptureData; - context->requestId = m_lastImageCaptureId; - context->fileName = fileName; - context->session = this; - - const camera_error_t result = camera_take_photo(m_handle, - imageCaptureShutterCallback, - 0, - 0, - imageCaptureImageCallback, - context, false); - - if (result != CAMERA_EOK) - qWarning() << "Unable to take photo:" << result; - - return m_lastImageCaptureId; -} - -void BbCameraSession::cancelCapture() -{ - // BB10 API doesn't provide functionality for that -} - -QList<QSize> BbCameraSession::supportedResolutions(const QImageEncoderSettings&, bool *continuous) const -{ - if (continuous) - *continuous = false; - - if (m_status == QCamera::UnloadedStatus) - return QList<QSize>(); - - if (m_captureMode & QCamera::CaptureStillImage) { - return supportedResolutions(QCamera::CaptureStillImage); - } else if (m_captureMode & QCamera::CaptureVideo) { - return supportedResolutions(QCamera::CaptureVideo); - } - - return QList<QSize>(); -} - -QImageEncoderSettings BbCameraSession::imageSettings() const -{ - return m_imageEncoderSettings; -} - -void BbCameraSession::setImageSettings(const QImageEncoderSettings &settings) -{ - m_imageEncoderSettings = settings; - if (m_imageEncoderSettings.codec().isEmpty()) - m_imageEncoderSettings.setCodec(QLatin1String("jpeg")); -} - -QMediaRecorder::RecorderState BbCameraSession::videoState() const -{ - return m_videoState; -} - -qint64 BbCameraSession::duration() const -{ - return (m_videoRecordingDuration.isValid() ? m_videoRecordingDuration.elapsed() : 0); -} - -void BbCameraSession::applyVideoSettings() -{ - if (m_handle == CAMERA_HANDLE_INVALID) - return; - - // apply viewfinder configuration - const QList<QSize> videoOutputResolutions = supportedResolutions(QCamera::CaptureVideo); - - if (!m_videoEncoderSettings.resolution().isValid() || !videoOutputResolutions.contains(m_videoEncoderSettings.resolution())) - m_videoEncoderSettings.setResolution(videoOutputResolutions.first()); - - QSize viewfinderResolution; - - if (m_previewIsVideo) { - // The viewfinder is responsible for encoding the video frames, so the resolutions must match. - viewfinderResolution = m_videoEncoderSettings.resolution(); - } else { - // The frames are encoded separately from the viewfinder, so only the aspect ratio must match. - const QSize videoResolution = m_videoEncoderSettings.resolution(); - const qreal aspectRatio = static_cast<qreal>(videoResolution.width())/static_cast<qreal>(videoResolution.height()); - - QList<QSize> sizes = supportedViewfinderResolutions(QCamera::CaptureVideo); - std::reverse(sizes.begin(), sizes.end()); // use smallest possible resolution - for (const QSize &size : qAsConst(sizes)) { - // search for viewfinder resolution with the same aspect ratio - if (qFuzzyCompare(aspectRatio, (static_cast<qreal>(size.width())/static_cast<qreal>(size.height())))) { - viewfinderResolution = size; - break; - } - } - } - - Q_ASSERT(viewfinderResolution.isValid()); - - const QByteArray windowId = QString().sprintf("qcamera_vf_%p", this).toLatin1(); - m_windowGrabber->setWindowId(windowId); - - const QByteArray windowGroupId = m_windowGrabber->windowGroupId(); - - const int rotationAngle = (360 - m_nativeCameraOrientation); - - camera_error_t result = CAMERA_EOK; - result = camera_set_videovf_property(m_handle, - CAMERA_IMGPROP_WIN_GROUPID, windowGroupId.data(), - CAMERA_IMGPROP_WIN_ID, windowId.data(), - CAMERA_IMGPROP_WIDTH, viewfinderResolution.width(), - CAMERA_IMGPROP_HEIGHT, viewfinderResolution.height(), - CAMERA_IMGPROP_ROTATION, rotationAngle); - - if (result != CAMERA_EOK) { - qWarning() << "Unable to apply video viewfinder settings:" << result; - return; - } - - const QSize resolution = m_videoEncoderSettings.resolution(); - - QString videoCodec = m_videoEncoderSettings.codec(); - if (videoCodec.isEmpty()) - videoCodec = QLatin1String("h264"); - - camera_videocodec_t cameraVideoCodec = CAMERA_VIDEOCODEC_H264; - if (videoCodec == QLatin1String("none")) - cameraVideoCodec = CAMERA_VIDEOCODEC_NONE; - else if (videoCodec == QLatin1String("avc1")) - cameraVideoCodec = CAMERA_VIDEOCODEC_AVC1; - else if (videoCodec == QLatin1String("h264")) - cameraVideoCodec = CAMERA_VIDEOCODEC_H264; - - qreal frameRate = m_videoEncoderSettings.frameRate(); - if (frameRate == 0) { - const QList<qreal> frameRates = supportedFrameRates(QVideoEncoderSettings(), 0); - if (!frameRates.isEmpty()) - frameRate = frameRates.last(); - } - - QString audioCodec = m_audioEncoderSettings.codec(); - if (audioCodec.isEmpty()) - audioCodec = QLatin1String("aac"); - - camera_audiocodec_t cameraAudioCodec = CAMERA_AUDIOCODEC_AAC; - if (audioCodec == QLatin1String("none")) - cameraAudioCodec = CAMERA_AUDIOCODEC_NONE; - else if (audioCodec == QLatin1String("aac")) - cameraAudioCodec = CAMERA_AUDIOCODEC_AAC; - else if (audioCodec == QLatin1String("raw")) - cameraAudioCodec = CAMERA_AUDIOCODEC_RAW; - - result = camera_set_video_property(m_handle, - CAMERA_IMGPROP_WIDTH, resolution.width(), - CAMERA_IMGPROP_HEIGHT, resolution.height(), - CAMERA_IMGPROP_ROTATION, rotationAngle, - CAMERA_IMGPROP_VIDEOCODEC, cameraVideoCodec, - CAMERA_IMGPROP_AUDIOCODEC, cameraAudioCodec); - - if (result != CAMERA_EOK) { - qWarning() << "Unable to apply video settings:" << result; - emit videoError(QMediaRecorder::ResourceError, tr("Unable to apply video settings")); - } -} - -QList<QSize> BbCameraSession::supportedResolutions(const QVideoEncoderSettings &settings, bool *continuous) const -{ - Q_UNUSED(settings); - - if (continuous) - *continuous = false; - - return supportedResolutions(QCamera::CaptureVideo); -} - -QList<qreal> BbCameraSession::supportedFrameRates(const QVideoEncoderSettings &settings, bool *continuous) const -{ - Q_UNUSED(settings); - - if (m_handle == CAMERA_HANDLE_INVALID) - return QList<qreal>(); - - int supported = 0; - double rates[20]; - bool maxmin = false; - - /** - * Since in current version of the BB10 platform the video viewfinder encodes the video frames, we use - * the values as returned by camera_get_video_vf_framerates(). - */ - const camera_error_t result = camera_get_video_vf_framerates(m_handle, 20, &supported, rates, &maxmin); - if (result != CAMERA_EOK) { - qWarning() << "Unable to retrieve supported viewfinder framerates:" << result; - return QList<qreal>(); - } - - QList<qreal> frameRates; - for (int i = 0; i < supported; ++i) - frameRates << rates[i]; - - if (continuous) - *continuous = maxmin; - - return frameRates; -} - -QVideoEncoderSettings BbCameraSession::videoSettings() const -{ - return m_videoEncoderSettings; -} - -void BbCameraSession::setVideoSettings(const QVideoEncoderSettings &settings) -{ - m_videoEncoderSettings = settings; -} - -QAudioEncoderSettings BbCameraSession::audioSettings() const -{ - return m_audioEncoderSettings; -} - -void BbCameraSession::setAudioSettings(const QAudioEncoderSettings &settings) -{ - m_audioEncoderSettings = settings; -} - -void BbCameraSession::updateReadyForCapture() -{ - emit readyForCaptureChanged(isReadyForCapture()); -} - -void BbCameraSession::imageCaptured(int requestId, const QImage &rawImage, const QString &fileName) -{ - QTransform transform; - - // subtract out the native rotation - transform.rotate(m_nativeCameraOrientation); - - // subtract out the current device orientation - if (m_device == cameraIdentifierRear()) - transform.rotate(360 - m_orientationHandler->orientation()); - else - transform.rotate(m_orientationHandler->orientation()); - - const QImage image = rawImage.transformed(transform); - - // Generate snap preview as downscaled image - { - QSize previewSize = image.size(); - int downScaleSteps = 0; - while (previewSize.width() > 800 && downScaleSteps < 8) { - previewSize.rwidth() /= 2; - previewSize.rheight() /= 2; - downScaleSteps++; - } - - const QImage snapPreview = image.scaled(previewSize); - - emit imageCaptured(requestId, snapPreview); - } - - if (/* capture to buffer */ fileName.isEmpty()) { - QVideoFrame frame(image); - emit imageAvailable(requestId, frame); - } else { - const QString actualFileName = QMediaStorageLocation::generateFileName(fileName, QStandardPaths::PicturesLocation, QLatin1String("jpg")); - QFile file(actualFileName); - if (file.open(QFile::WriteOnly)) { - if (image.save(&file, "jpg")) { - emit imageSaved(requestId, actualFileName); - } else { - emit imageCaptureError(requestId, QImageCapture::OutOfSpaceError, file.errorString()); - } - } else { - const QString errorMessage = tr("Could not open destination file:\n%1").arg(actualFileName); - emit imageCaptureError(requestId, QImageCapture::ResourceError, errorMessage); - } - } -} - -void BbCameraSession::handleVideoRecordingPaused() -{ - //TODO: implement once BB10 API supports pausing a video -} - -void BbCameraSession::handleVideoRecordingResumed() -{ - if (m_videoRecordingDuration.isValid()) - m_videoRecordingDuration.restart(); -} - -void BbCameraSession::deviceOrientationChanged(int angle) -{ - if (m_handle != CAMERA_HANDLE_INVALID) - camera_set_device_orientation(m_handle, angle); -} - -void BbCameraSession::handleCameraPowerUp() -{ - stopViewFinder(); - startViewFinder(); -} - -void BbCameraSession::viewfinderFrameGrabbed(const QImage &image) -{ - QTransform transform; - - // subtract out the native rotation - transform.rotate(m_nativeCameraOrientation); - - // subtract out the current device orientation - if (m_device == cameraIdentifierRear()) - transform.rotate(360 - m_orientationHandler->viewfinderOrientation()); - else - transform.rotate(m_orientationHandler->viewfinderOrientation()); - - QImage frame = image.copy().transformed(transform); - - QMutexLocker locker(&m_surfaceMutex); - if (m_surface) { - if (frame.size() != m_surface->surfaceFormat().frameSize()) { - m_surface->stop(); - m_surface->start(QVideoFrameFormat(frame.size(), QVideoFrameFormat::Format_ARGB32)); - } - - QVideoFrame videoFrame(frame); - - m_surface->present(videoFrame); - } -} - -bool BbCameraSession::openCamera() -{ - if (m_handle != CAMERA_HANDLE_INVALID) // camera is already open - return true; - - m_status = QCamera::LoadingStatus; - emit statusChanged(m_status); - - camera_unit_t unit = CAMERA_UNIT_REAR; - if (m_device == cameraIdentifierFront()) - unit = CAMERA_UNIT_FRONT; - else if (m_device == cameraIdentifierRear()) - unit = CAMERA_UNIT_REAR; - else if (m_device == cameraIdentifierDesktop()) - unit = CAMERA_UNIT_DESKTOP; - - camera_error_t result = camera_open(unit, CAMERA_MODE_RW, &m_handle); - if (result != CAMERA_EOK) { - m_handle = CAMERA_HANDLE_INVALID; - m_status = QCamera::UnloadedStatus; - emit statusChanged(m_status); - - qWarning() << "Unable to open camera:" << result; - emit error(QCamera::CameraError, tr("Unable to open camera")); - return false; - } - - result = camera_get_native_orientation(m_handle, &m_nativeCameraOrientation); - if (result != CAMERA_EOK) { - qWarning() << "Unable to retrieve native camera orientation:" << result; - emit error(QCamera::CameraError, tr("Unable to retrieve native camera orientation")); - return false; - } - - m_previewIsVideo = camera_has_feature(m_handle, CAMERA_FEATURE_PREVIEWISVIDEO); - - m_status = QCamera::LoadedStatus; - emit statusChanged(m_status); - - emit cameraOpened(); - - return true; -} - -void BbCameraSession::closeCamera() -{ - if (m_handle == CAMERA_HANDLE_INVALID) // camera is closed already - return; - - m_status = QCamera::UnloadingStatus; - emit statusChanged(m_status); - - const camera_error_t result = camera_close(m_handle); - if (result != CAMERA_EOK) { - m_status = QCamera::LoadedStatus; - emit statusChanged(m_status); - - qWarning() << "Unable to close camera:" << result; - emit error(QCamera::CameraError, tr("Unable to close camera")); - return; - } - - m_handle = CAMERA_HANDLE_INVALID; - - m_status = QCamera::UnloadedStatus; - emit statusChanged(m_status); -} - -static void viewFinderStatusCallback(camera_handle_t handle, camera_devstatus_t status, uint16_t value, void *context) -{ - Q_UNUSED(handle); - - if (status == CAMERA_STATUS_FOCUS_CHANGE) { - BbCameraSession *session = static_cast<BbCameraSession*>(context); - QMetaObject::invokeMethod(session, "focusStatusChanged", Qt::QueuedConnection, Q_ARG(int, value)); - return; - } else if (status == CAMERA_STATUS_POWERUP) { - BbCameraSession *session = static_cast<BbCameraSession*>(context); - QMetaObject::invokeMethod(session, "handleCameraPowerUp", Qt::QueuedConnection); - } -} - -bool BbCameraSession::startViewFinder() -{ - m_status = QCamera::StartingStatus; - emit statusChanged(m_status); - - QSize viewfinderResolution; - camera_error_t result = CAMERA_EOK; - if (m_captureMode & QCamera::CaptureStillImage) { - result = camera_start_photo_viewfinder(m_handle, 0, viewFinderStatusCallback, this); - viewfinderResolution = currentViewfinderResolution(QCamera::CaptureStillImage); - } else if (m_captureMode & QCamera::CaptureVideo) { - result = camera_start_video_viewfinder(m_handle, 0, viewFinderStatusCallback, this); - viewfinderResolution = currentViewfinderResolution(QCamera::CaptureVideo); - } - - if (result != CAMERA_EOK) { - qWarning() << "Unable to start viewfinder:" << result; - return false; - } - - const int angle = m_orientationHandler->viewfinderOrientation(); - - const QSize rotatedSize = ((angle == 0 || angle == 180) ? viewfinderResolution - : viewfinderResolution.transposed()); - - m_surfaceMutex.lock(); - if (m_surface) { - const bool ok = m_surface->start(QVideoFrameFormat(rotatedSize, QVideoFrameFormat::Format_ARGB32)); - if (!ok) - qWarning() << "Unable to start camera viewfinder surface"; - } - m_surfaceMutex.unlock(); - - m_status = QCamera::ActiveStatus; - emit statusChanged(m_status); - - return true; -} - -void BbCameraSession::stopViewFinder() -{ - m_windowGrabber->stop(); - - m_status = QCamera::StoppingStatus; - emit statusChanged(m_status); - - m_surfaceMutex.lock(); - if (m_surface) { - m_surface->stop(); - } - m_surfaceMutex.unlock(); - - camera_error_t result = CAMERA_EOK; - if (m_captureMode & QCamera::CaptureStillImage) - result = camera_stop_photo_viewfinder(m_handle); - else if (m_captureMode & QCamera::CaptureVideo) - result = camera_stop_video_viewfinder(m_handle); - - if (result != CAMERA_EOK) { - qWarning() << "Unable to stop viewfinder:" << result; - return; - } - - m_status = QCamera::LoadedStatus; - emit statusChanged(m_status); -} - -void BbCameraSession::applyConfiguration() -{ - if (m_captureMode & QCamera::CaptureStillImage) { - const QList<QSize> photoOutputResolutions = supportedResolutions(QCamera::CaptureStillImage); - - if (!m_imageEncoderSettings.resolution().isValid() || !photoOutputResolutions.contains(m_imageEncoderSettings.resolution())) - m_imageEncoderSettings.setResolution(photoOutputResolutions.first()); - - const QSize photoResolution = m_imageEncoderSettings.resolution(); - const qreal aspectRatio = static_cast<qreal>(photoResolution.width())/static_cast<qreal>(photoResolution.height()); - - // apply viewfinder configuration - QSize viewfinderResolution; - QList<QSize> sizes = supportedViewfinderResolutions(QCamera::CaptureStillImage); - std::reverse(sizes.begin(), sizes.end()); // use smallest possible resolution - for (const QSize &size : qAsConst(sizes)) { - // search for viewfinder resolution with the same aspect ratio - if (qFuzzyCompare(aspectRatio, (static_cast<qreal>(size.width())/static_cast<qreal>(size.height())))) { - viewfinderResolution = size; - break; - } - } - - Q_ASSERT(viewfinderResolution.isValid()); - - const QByteArray windowId = QString().sprintf("qcamera_vf_%p", this).toLatin1(); - m_windowGrabber->setWindowId(windowId); - - const QByteArray windowGroupId = m_windowGrabber->windowGroupId(); - - camera_error_t result = camera_set_photovf_property(m_handle, - CAMERA_IMGPROP_WIN_GROUPID, windowGroupId.data(), - CAMERA_IMGPROP_WIN_ID, windowId.data(), - CAMERA_IMGPROP_WIDTH, viewfinderResolution.width(), - CAMERA_IMGPROP_HEIGHT, viewfinderResolution.height(), - CAMERA_IMGPROP_FORMAT, CAMERA_FRAMETYPE_NV12, - CAMERA_IMGPROP_ROTATION, 360 - m_nativeCameraOrientation); - - if (result != CAMERA_EOK) { - qWarning() << "Unable to apply photo viewfinder settings:" << result; - return; - } - - - int jpegQuality = 100; - switch (m_imageEncoderSettings.quality()) { - case QMediaRecorder::VeryLowQuality: - jpegQuality = 20; - break; - case QMediaRecorder::LowQuality: - jpegQuality = 40; - break; - case QMediaRecorder::NormalQuality: - jpegQuality = 60; - break; - case QMediaRecorder::HighQuality: - jpegQuality = 80; - break; - case QMediaRecorder::VeryHighQuality: - jpegQuality = 100; - break; - } - - // apply photo configuration - result = camera_set_photo_property(m_handle, - CAMERA_IMGPROP_WIDTH, photoResolution.width(), - CAMERA_IMGPROP_HEIGHT, photoResolution.height(), - CAMERA_IMGPROP_JPEGQFACTOR, jpegQuality, - CAMERA_IMGPROP_ROTATION, 360 - m_nativeCameraOrientation); - - if (result != CAMERA_EOK) { - qWarning() << "Unable to apply photo settings:" << result; - return; - } - - } else if (m_captureMode & QCamera::CaptureVideo) { - applyVideoSettings(); - } - - startViewFinder(); -} - -static void videoRecordingStatusCallback(camera_handle_t handle, camera_devstatus_t status, uint16_t value, void *context) -{ - Q_UNUSED(handle); - Q_UNUSED(value); - - if (status == CAMERA_STATUS_VIDEO_PAUSE) { - BbCameraSession *session = static_cast<BbCameraSession*>(context); - QMetaObject::invokeMethod(session, "handleVideoRecordingPaused", Qt::QueuedConnection); - } else if (status == CAMERA_STATUS_VIDEO_RESUME) { - BbCameraSession *session = static_cast<BbCameraSession*>(context); - QMetaObject::invokeMethod(session, "handleVideoRecordingResumed", Qt::QueuedConnection); - } -} - -void BbCameraSession::startVideoRecording(const QUrl &outputLocation) -{ - if (m_videoState == QMediaRecorder::RecordingState) - return; - - m_videoRecordingDuration.invalidate(); - - auto videoOutputLocation = QMediaStorageLocation::generateFileName(outputLocation.toLocalFile(), QStandardPaths::MoviesLocation, QLatin1String("mp4")); - - emit actualLocationChanged(videoOutputLocation); - - const camera_error_t result = camera_start_video(m_handle, QFile::encodeName(videoOutputLocation), 0, videoRecordingStatusCallback, this); - if (result != CAMERA_EOK) { - emit videoError(QMediaRecorder::ResourceError, - QMediaRecorderPrivate::msgFailedStartRecording()); - } else { - m_videoState = QMediaRecorder::RecordingState; - } - emit videoStateChanged(m_videoState); -} - -void BbCameraSession::stopVideoRecording() -{ - if (m_videoState == QMediaRecorder::StoppedState) - return; - - const camera_error_t result = camera_stop_video(m_handle); - if (result != CAMERA_EOK) { - emit videoError(QMediaRecorder::ResourceError, tr("Unable to stop video recording")); - } - - m_videoRecordingDuration.invalidate(); - m_videoState = QMediaRecorder::StoppedState; - emit videoStateChanged(m_videoState); -} - -bool BbCameraSession::isCaptureModeSupported(camera_handle_t handle, QCamera::CaptureModes mode) const -{ - if (mode & QCamera::CaptureStillImage) - return camera_has_feature(handle, CAMERA_FEATURE_PHOTO); - - if (mode & QCamera::CaptureVideo) - return camera_has_feature(handle, CAMERA_FEATURE_VIDEO); - - return false; -} - -QList<QSize> BbCameraSession::supportedResolutions(QCamera::CaptureMode mode) const -{ - Q_ASSERT(m_handle != CAMERA_HANDLE_INVALID); - - QList<QSize> list; - - camera_error_t result = CAMERA_EOK; - camera_res_t resolutions[20]; - unsigned int supported = 0; - - if (mode == QCamera::CaptureStillImage) - result = camera_get_photo_output_resolutions(m_handle, CAMERA_FRAMETYPE_JPEG, 20, &supported, resolutions); - else if (mode == QCamera::CaptureVideo) - result = camera_get_video_output_resolutions(m_handle, 20, &supported, resolutions); - - if (result != CAMERA_EOK) - return list; - - for (unsigned int i = 0; i < supported; ++i) - list << QSize(resolutions[i].width, resolutions[i].height); - - return list; -} - -QList<QSize> BbCameraSession::supportedViewfinderResolutions(QCamera::CaptureMode mode) const -{ - Q_ASSERT(m_handle != CAMERA_HANDLE_INVALID); - - QList<QSize> list; - - camera_error_t result = CAMERA_EOK; - camera_res_t resolutions[20]; - unsigned int supported = 0; - - if (mode == QCamera::CaptureStillImage) - result = camera_get_photo_vf_resolutions(m_handle, 20, &supported, resolutions); - else if (mode == QCamera::CaptureVideo) - result = camera_get_video_vf_resolutions(m_handle, 20, &supported, resolutions); - - if (result != CAMERA_EOK) - return list; - - for (unsigned int i = 0; i < supported; ++i) - list << QSize(resolutions[i].width, resolutions[i].height); - - return list; -} - -QSize BbCameraSession::currentViewfinderResolution(QCamera::CaptureMode mode) const -{ - Q_ASSERT(m_handle != CAMERA_HANDLE_INVALID); - - camera_error_t result = CAMERA_EOK; - int width = 0; - int height = 0; - - if (mode == QCamera::CaptureStillImage) - result = camera_get_photovf_property(m_handle, CAMERA_IMGPROP_WIDTH, &width, - CAMERA_IMGPROP_HEIGHT, &height); - else if (mode == QCamera::CaptureVideo) - result = camera_get_videovf_property(m_handle, CAMERA_IMGPROP_WIDTH, &width, - CAMERA_IMGPROP_HEIGHT, &height); - - if (result != CAMERA_EOK) - return QSize(); - - return QSize(width, height); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/camera/bbcamerasession_p.h b/src/multimedia/platform/qnx/camera/bbcamerasession_p.h deleted file mode 100644 index 4ea7110e7..000000000 --- a/src/multimedia/platform/qnx/camera/bbcamerasession_p.h +++ /dev/null @@ -1,202 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#ifndef BBCAMERASESSION_H -#define BBCAMERASESSION_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QCamera> -#include <QImageCapture> -#include <QElapsedTimer> -#include <QMediaRecorder> -#include <QMutex> -#include <QObject> -#include <QPointer> -#include <qvideosink.h> - -#include <camera/camera_api.h> - -QT_BEGIN_NAMESPACE - -class BbCameraOrientationHandler; -class WindowGrabber; - -class BbCameraSession : public QObject -{ - Q_OBJECT -public: - explicit BbCameraSession(QObject *parent = 0); - ~BbCameraSession(); - - camera_handle_t handle() const; - - // camera control - QCamera::State state() const; - void setState(QCamera::State state); - QCamera::Status status() const; - QCamera::CaptureModes captureMode() const; - void setCaptureMode(QCamera::CaptureModes); - bool isCaptureModeSupported(QCamera::CaptureModes mode) const; - - // video device selector control - static QByteArray cameraIdentifierFront(); - static QByteArray cameraIdentifierRear(); - static QByteArray cameraIdentifierDesktop(); - - void setDevice(const QByteArray &device); - QByteArray device() const; - - // video renderer control - void setSurface(QVideoSink *surface); - - // image capture control - bool isReadyForCapture() const; - int capture(const QString &fileName); - void cancelCapture(); - - // image encoder control - QList<QSize> supportedResolutions(const QImageEncoderSettings &settings, bool *continuous) const; - QImageEncoderSettings imageSettings() const; - void setImageSettings(const QImageEncoderSettings &settings); - - // media recorder control - QMediaRecorder::RecorderState videoState() const; - qint64 duration() const; - void applyVideoSettings(); - - bool startVideoRecording(const QUrl &outputLocation); - void stopVideoRecording(); - - // video encoder settings control - QList<QSize> supportedResolutions(const QVideoEncoderSettings &settings, bool *continuous) const; - QList<qreal> supportedFrameRates(const QVideoEncoderSettings &settings, bool *continuous) const; - QVideoEncoderSettings videoSettings() const; - void setVideoSettings(const QVideoEncoderSettings &settings); - - // audio encoder settings control - QAudioEncoderSettings audioSettings() const; - void setAudioSettings(const QAudioEncoderSettings &settings); - -Q_SIGNALS: - // camera control - void stateChanged(QCamera::State); - void error(int error, const QString &errorString); - void captureModeChanged(QCamera::CaptureModes); - - // image capture control - void readyForCaptureChanged(bool); - void imageExposed(int id); - void imageCaptured(int id, const QImage &preview); - void imageMetadataAvailable(int id, const QString &key, const QVariant &value); - void imageAvailable(int id, const QVideoFrame &buffer); - void imageSaved(int id, const QString &fileName); - void imageCaptureError(int id, int error, const QString &errorString); - - // media recorder control - void videoStateChanged(QMediaRecorder::RecorderState state); - void durationChanged(qint64 duration); - void actualLocationChanged(const QUrl &location); - void videoError(int error, const QString &errorString); - - void cameraOpened(); - void focusStatusChanged(int status); - -private slots: - void updateReadyForCapture(); - void imageCaptured(int, const QImage&, const QString&); - void handleVideoRecordingPaused(); - void handleVideoRecordingResumed(); - void deviceOrientationChanged(int); - void handleCameraPowerUp(); - void viewfinderFrameGrabbed(const QImage &image); - void applyConfiguration(); - -private: - bool openCamera(); - void closeCamera(); - bool startViewFinder(); - void stopViewFinder(); - - bool isCaptureModeSupported(camera_handle_t handle, QCamera::CaptureModes mode) const; - QList<QSize> supportedResolutions(QCamera::CaptureMode mode) const; - QList<QSize> supportedViewfinderResolutions(QCamera::CaptureMode mode) const; - QSize currentViewfinderResolution(QCamera::CaptureMode mode) const; - - quint32 m_nativeCameraOrientation; - BbCameraOrientationHandler* m_orientationHandler; - - QCamera::Status m_status; - QCamera::State m_state; - QCamera::CaptureModes m_captureMode; - - QByteArray m_device; - bool m_previewIsVideo; - - QPointer<QVideoSink> m_surface; - QMutex m_surfaceMutex; - - int m_lastImageCaptureId; - - QImageEncoderSettings m_imageEncoderSettings; - - QMediaRecorder::RecorderState m_videoState; - QElapsedTimer m_videoRecordingDuration; - - QVideoEncoderSettings m_videoEncoderSettings; - QAudioEncoderSettings m_audioEncoderSettings; - - camera_handle_t m_handle; - - WindowGrabber* m_windowGrabber; -}; - -QDebug operator<<(QDebug debug, camera_error_t error); - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qnx/camera/bbcameravideoencodersettingscontrol.cpp b/src/multimedia/platform/qnx/camera/bbcameravideoencodersettingscontrol.cpp deleted file mode 100644 index d16d7a307..000000000 --- a/src/multimedia/platform/qnx/camera/bbcameravideoencodersettingscontrol.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#include "bbcameravideoencodersettingscontrol_p.h" - -#include "bbcamerasession_p.h" - -QT_BEGIN_NAMESPACE - -BbCameraVideoEncoderSettingsControl::BbCameraVideoEncoderSettingsControl(BbCameraSession *session, QObject *parent) - : QVideoEncoderSettingsControl(parent) - , m_session(session) -{ -} - -QList<QSize> BbCameraVideoEncoderSettingsControl::supportedResolutions(const QVideoEncoderSettings &settings, bool *continuous) const -{ - return m_session->supportedResolutions(settings, continuous); -} - -QList<qreal> BbCameraVideoEncoderSettingsControl::supportedFrameRates(const QVideoEncoderSettings &settings, bool *continuous) const -{ - return m_session->supportedFrameRates(settings, continuous); -} - -QStringList BbCameraVideoEncoderSettingsControl::supportedVideoCodecs() const -{ - return QStringList() << QLatin1String("none") << QLatin1String("avc1") << QLatin1String("h264"); -} - -QString BbCameraVideoEncoderSettingsControl::videoCodecDescription(const QString &codecName) const -{ - if (codecName == QLatin1String("none")) - return tr("No compression"); - else if (codecName == QLatin1String("avc1")) - return tr("AVC1 compression"); - else if (codecName == QLatin1String("h264")) - return tr("H264 compression"); - - return QString(); -} - -QVideoEncoderSettings BbCameraVideoEncoderSettingsControl::videoSettings() const -{ - return m_session->videoSettings(); -} - -void BbCameraVideoEncoderSettingsControl::setVideoSettings(const QVideoEncoderSettings &settings) -{ - m_session->setVideoSettings(settings); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/camera/bbcameravideoencodersettingscontrol_p.h b/src/multimedia/platform/qnx/camera/bbcameravideoencodersettingscontrol_p.h deleted file mode 100644 index 893b26d5d..000000000 --- a/src/multimedia/platform/qnx/camera/bbcameravideoencodersettingscontrol_p.h +++ /dev/null @@ -1,78 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#ifndef BBCAMERAVIDEOENCODERSETTINGSCONTROL_H -#define BBCAMERAVIDEOENCODERSETTINGSCONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qvideoencodersettingscontrol.h> - -QT_BEGIN_NAMESPACE - -class BbCameraSession; - -class BbCameraVideoEncoderSettingsControl : public QVideoEncoderSettingsControl -{ - Q_OBJECT -public: - explicit BbCameraVideoEncoderSettingsControl(BbCameraSession *session, QObject *parent = 0); - - QList<QSize> supportedResolutions(const QVideoEncoderSettings &settings, bool *continuous = 0) const override; - QList<qreal> supportedFrameRates(const QVideoEncoderSettings &settings, bool *continuous = 0) const override; - QStringList supportedVideoCodecs() const override; - QString videoCodecDescription(const QString &codecName) const override; - QVideoEncoderSettings videoSettings() const override; - void setVideoSettings(const QVideoEncoderSettings &settings) override; - -private: - BbCameraSession *m_session; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qnx/common/windowgrabber.cpp b/src/multimedia/platform/qnx/common/windowgrabber.cpp deleted file mode 100644 index e4c8c926d..000000000 --- a/src/multimedia/platform/qnx/common/windowgrabber.cpp +++ /dev/null @@ -1,419 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ - -#include "windowgrabber_p.h" - -#include <QAbstractEventDispatcher> -#include <QDebug> -#include <QGuiApplication> -#include <QImage> -#include <QThread> -#include <qpa/qplatformnativeinterface.h> - -#include <QOpenGLContext> -#include <QOpenGLFunctions> - -#include <errno.h> - -QT_BEGIN_NAMESPACE - -static PFNEGLCREATEIMAGEKHRPROC s_eglCreateImageKHR; -static PFNEGLDESTROYIMAGEKHRPROC s_eglDestroyImageKHR; - -WindowGrabber::WindowGrabber(QObject *parent) - : QObject(parent), - m_windowParent(nullptr), - m_screenContext(0), - m_active(false), - m_currentFrame(0), - m_eglImageSupported(false), - m_eglImageCheck(false) -{ - // grab the window frame with 60 frames per second - m_timer.setInterval(1000/60); - - connect(&m_timer, SIGNAL(timeout()), SLOT(triggerUpdate())); - - QCoreApplication::eventDispatcher()->installNativeEventFilter(this); - - for ( int i = 0; i < 2; ++i ) - m_images[i] = 0; - - // Use of EGL images can be disabled by setting QQNX_MM_DISABLE_EGLIMAGE_SUPPORT to something - // non-zero. This is probably useful only to test that this path still works since it results - // in a high CPU load. - if (!s_eglCreateImageKHR && qgetenv("QQNX_MM_DISABLE_EGLIMAGE_SUPPORT").toInt() == 0) { - s_eglCreateImageKHR = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR")); - s_eglDestroyImageKHR = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR")); - } - - QPlatformNativeInterface *const nativeInterface = QGuiApplication::platformNativeInterface(); - if (nativeInterface) { - m_screenContext = static_cast<screen_context_t>( - nativeInterface->nativeResourceForIntegration("screenContext")); - } - - // Create a parent window for the window whose content will be grabbed. Since the - // window is only a buffer conduit, the characteristics of the parent window are - // irrelevant. The contents of the window can be grabbed so long as the window - // joins the parent window's group and the parent window is in this process. - // Using the window that displays this content isn't possible because there's no - // way to reliably retrieve it from this code or any calling code. - screen_create_window(&m_windowParent, m_screenContext); - screen_create_window_group(m_windowParent, nullptr); -} - -WindowGrabber::~WindowGrabber() -{ - screen_destroy_window(m_windowParent); - QCoreApplication::eventDispatcher()->removeNativeEventFilter(this); - cleanup(); -} - -void WindowGrabber::setFrameRate(int frameRate) -{ - m_timer.setInterval(1000/frameRate); -} - - -void WindowGrabber::setWindowId(const QByteArray &windowId) -{ - m_windowId = windowId; -} - -void WindowGrabber::start() -{ - if (m_active) - return; - - if (!m_screenContext) - screen_get_window_property_pv(m_window, SCREEN_PROPERTY_CONTEXT, reinterpret_cast<void**>(&m_screenContext)); - - m_timer.start(); - - m_active = true; -} - -void WindowGrabber::stop() -{ - if (!m_active) - return; - - cleanup(); - - m_timer.stop(); - - m_active = false; -} - -void WindowGrabber::pause() -{ - m_timer.stop(); -} - -void WindowGrabber::resume() -{ - if (!m_active) - return; - - m_timer.start(); -} - -bool WindowGrabber::handleScreenEvent(screen_event_t screen_event) -{ - - int eventType; - if (screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_TYPE, &eventType) != 0) { - qWarning() << "WindowGrabber: Failed to query screen event type"; - return false; - } - - if (eventType != SCREEN_EVENT_CREATE) - return false; - - screen_window_t window = 0; - if (screen_get_event_property_pv(screen_event, SCREEN_PROPERTY_WINDOW, (void**)&window) != 0) { - qWarning() << "WindowGrabber: Failed to query window property"; - return false; - } - - const int maxIdStrLength = 128; - char idString[maxIdStrLength]; - if (screen_get_window_property_cv(window, SCREEN_PROPERTY_ID_STRING, maxIdStrLength, idString) != 0) { - qWarning() << "WindowGrabber: Failed to query window ID string"; - return false; - } - - // Grab windows that have a non-empty ID string and a matching window id to grab - if (idString[0] != '\0' && m_windowId == idString) { - m_window = window; - start(); - } - - return false; -} - -bool WindowGrabber::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *) -{ - if (eventType == "screen_event_t") { - const screen_event_t event = static_cast<screen_event_t>(message); - return handleScreenEvent(event); - } - - return false; -} - -QByteArray WindowGrabber::windowGroupId() const -{ - char groupName[256]; - memset(groupName, 0, sizeof(groupName)); - screen_get_window_property_cv(m_windowParent, - SCREEN_PROPERTY_GROUP, - sizeof(groupName) - 1, - groupName); - return QByteArray(groupName); -} - -bool WindowGrabber::eglImageSupported() -{ - return m_eglImageSupported; -} - -void WindowGrabber::checkForEglImageExtension() -{ - QOpenGLContext *m_context = QOpenGLContext::currentContext(); - if (!m_context) //Should not happen, because we are called from the render thread - return; - - QByteArray eglExtensions = QByteArray(eglQueryString(eglGetDisplay(EGL_DEFAULT_DISPLAY), - EGL_EXTENSIONS)); - m_eglImageSupported = m_context->hasExtension(QByteArrayLiteral("GL_OES_EGL_image")) - && eglExtensions.contains(QByteArrayLiteral("EGL_KHR_image")) - && s_eglCreateImageKHR && s_eglDestroyImageKHR; - - if (strstr(reinterpret_cast<const char*>(glGetString(GL_VENDOR)), "VMware")) - m_eglImageSupported = false; - - m_eglImageCheck = true; -} - -void WindowGrabber::triggerUpdate() -{ - if (!m_eglImageCheck) // We did not check for egl images yet - return; - - int size[2] = { 0, 0 }; - - int result = screen_get_window_property_iv(m_window, SCREEN_PROPERTY_SOURCE_SIZE, size); - if (result != 0) { - cleanup(); - qWarning() << "WindowGrabber: cannot get window size:" << strerror(errno); - return; - } - - if (m_size.width() != size[0] || m_size.height() != size[1]) - m_size = QSize(size[0], size[1]); - - emit updateScene(m_size); -} - -bool WindowGrabber::selectBuffer() -{ - // If we're using egl images we need to double buffer since the gpu may still be using the last - // video frame. If we're not, it doesn't matter since the data is immediately copied. - if (eglImageSupported()) - m_currentFrame = (m_currentFrame + 1) % 2; - - if (!m_images[m_currentFrame]) { - m_images[m_currentFrame] = new WindowGrabberImage(); - if (!m_images[m_currentFrame]->initialize(m_screenContext)) { - delete m_images[m_currentFrame]; - m_images[m_currentFrame] = 0; - return false; - } - } - return true; -} - -int WindowGrabber::getNextTextureId() -{ - if (!selectBuffer()) - return 0; - return m_images[m_currentFrame]->getTexture(m_window, m_size); -} - -QImage WindowGrabber::getNextImage() -{ - if (!selectBuffer()) - return QImage(); - - return m_images[m_currentFrame]->getImage(m_window, m_size); -} - -void WindowGrabber::cleanup() -{ - for ( int i = 0; i < 2; ++i ) { - if (m_images[i]) { - m_images[i]->destroy(); - m_images[i] = 0; - } - } -} - - -WindowGrabberImage::WindowGrabberImage() - : m_pixmap(0), m_pixmapBuffer(0), m_eglImage(0), m_glTexture(0), m_bufferAddress(0), m_bufferStride(0) -{ -} - -WindowGrabberImage::~WindowGrabberImage() -{ - if (m_glTexture) - glDeleteTextures(1, &m_glTexture); - if (m_eglImage) - s_eglDestroyImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), m_eglImage); - if (m_pixmap) - screen_destroy_pixmap(m_pixmap); -} - -bool -WindowGrabberImage::initialize(screen_context_t screenContext) -{ - if (screen_create_pixmap(&m_pixmap, screenContext) != 0) { - qWarning() << "WindowGrabber: cannot create pixmap:" << strerror(errno); - return false; - } - const int usage = SCREEN_USAGE_WRITE | SCREEN_USAGE_READ | SCREEN_USAGE_NATIVE; - screen_set_pixmap_property_iv(m_pixmap, SCREEN_PROPERTY_USAGE, &usage); - - const int format = SCREEN_FORMAT_RGBX8888; - screen_set_pixmap_property_iv(m_pixmap, SCREEN_PROPERTY_FORMAT, &format); - - return true; -} - -void -WindowGrabberImage::destroy() -{ - // We want to delete in the thread we were created in since we need the thread that - // has called eglMakeCurrent on the right EGL context. This doesn't actually guarantee - // this but that would be hard to achieve and in practice it works. - if (QThread::currentThread() == thread()) - delete this; - else - deleteLater(); -} - -bool -WindowGrabberImage::resize(const QSize &newSize) -{ - if (m_pixmapBuffer) { - screen_destroy_pixmap_buffer(m_pixmap); - m_pixmapBuffer = 0; - m_bufferAddress = 0; - m_bufferStride = 0; - } - - int size[2] = { newSize.width(), newSize.height() }; - - screen_set_pixmap_property_iv(m_pixmap, SCREEN_PROPERTY_BUFFER_SIZE, size); - - if (screen_create_pixmap_buffer(m_pixmap) == 0) { - screen_get_pixmap_property_pv(m_pixmap, SCREEN_PROPERTY_RENDER_BUFFERS, - reinterpret_cast<void**>(&m_pixmapBuffer)); - screen_get_buffer_property_pv(m_pixmapBuffer, SCREEN_PROPERTY_POINTER, - reinterpret_cast<void**>(&m_bufferAddress)); - screen_get_buffer_property_iv(m_pixmapBuffer, SCREEN_PROPERTY_STRIDE, &m_bufferStride); - m_size = newSize; - - return true; - } else { - m_size = QSize(); - return false; - } -} - -bool -WindowGrabberImage::grab(screen_window_t window) -{ - const int rect[] = { 0, 0, m_size.width(), m_size.height() }; - return screen_read_window(window, m_pixmapBuffer, 1, rect, 0) == 0; -} - -QImage -WindowGrabberImage::getImage(screen_window_t window, const QSize &size) -{ - if (size != m_size) { - if (!resize(size)) - return QImage(); - } - if (!m_bufferAddress || !grab(window)) - return QImage(); - - return QImage(m_bufferAddress, m_size.width(), m_size.height(), m_bufferStride, QImage::Format_ARGB32); -} - -GLuint -WindowGrabberImage::getTexture(screen_window_t window, const QSize &size) -{ - if (size != m_size) { - if (!m_glTexture) - glGenTextures(1, &m_glTexture); - glBindTexture(GL_TEXTURE_2D, m_glTexture); - if (m_eglImage) { - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, 0); - s_eglDestroyImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), m_eglImage); - m_eglImage = 0; - } - if (!resize(size)) - return 0; - m_eglImage = s_eglCreateImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), EGL_NO_CONTEXT, - EGL_NATIVE_PIXMAP_KHR, m_pixmap, 0); - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage); - } - - if (!m_pixmap || !grab(window)) - return 0; - - return m_glTexture; -} - - - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/common/windowgrabber_p.h b/src/multimedia/platform/qnx/common/windowgrabber_p.h deleted file mode 100644 index 79a234b2d..000000000 --- a/src/multimedia/platform/qnx/common/windowgrabber_p.h +++ /dev/null @@ -1,155 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#ifndef WINDOWGRABBER_H -#define WINDOWGRABBER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#define EGL_EGLEXT_PROTOTYPES -#define GL_GLEXT_PROTOTYPES -#include <EGL/egl.h> -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> -#include <EGL/eglext.h> -#include <QAbstractNativeEventFilter> -#include <QObject> -#include <QSize> -#include <QTimer> - -#include <screen/screen.h> - -QT_BEGIN_NAMESPACE - -class WindowGrabberImage : public QObject -{ - Q_OBJECT - -public: - WindowGrabberImage(); - ~WindowGrabberImage(); - - bool initialize(screen_context_t screenContext); - - void destroy(); - - QImage getImage(screen_window_t window, const QSize &size); - GLuint getTexture(screen_window_t window, const QSize &size); - -private: - bool grab(screen_window_t window); - bool resize(const QSize &size); - - QSize m_size; - screen_pixmap_t m_pixmap; - screen_buffer_t m_pixmapBuffer; - EGLImageKHR m_eglImage; - GLuint m_glTexture; - unsigned char *m_bufferAddress; - int m_bufferStride; -}; - -class WindowGrabber : public QObject, public QAbstractNativeEventFilter -{ - Q_OBJECT - -public: - explicit WindowGrabber(QObject *parent = 0); - ~WindowGrabber(); - - void setFrameRate(int frameRate); - - void setWindowId(const QByteArray &windowId); - - void start(); - void stop(); - - void pause(); - void resume(); - - bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override; - - bool handleScreenEvent(screen_event_t event); - - QByteArray windowGroupId() const; - - bool eglImageSupported(); - void checkForEglImageExtension(); - - int getNextTextureId(); - QImage getNextImage(); - -signals: - void updateScene(const QSize &size); - -private slots: - void triggerUpdate(); - -private: - bool selectBuffer(); - void cleanup(); - - QTimer m_timer; - - QByteArray m_windowId; - - screen_window_t m_windowParent; - screen_window_t m_window; - screen_context_t m_screenContext; - - WindowGrabberImage *m_images[2]; - QSize m_size; - - bool m_active; - int m_currentFrame; - bool m_eglImageSupported; - bool m_eglImageCheck; // We must not send a grabed frame before this is true -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp b/src/multimedia/platform/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp deleted file mode 100644 index 4eff994e4..000000000 --- a/src/multimedia/platform/qnx/mediaplayer/mmrenderermediaplayercontrol.cpp +++ /dev/null @@ -1,630 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#include "mmrendereraudiorolecontrol_p.h" -#include "mmrenderermediaplayercontrol_p.h" -#include "mmrendererplayervideorenderercontrol_p.h" -#include "mmrendererutil_p.h" -#include "mmrenderervideowindowcontrol_p.h" -#include <QtCore/qabstracteventdispatcher.h> -#include <QtCore/qcoreapplication.h> -#include <QtCore/qdir.h> -#include <QtCore/qfileinfo.h> -#include <QtCore/quuid.h> -#include <mm/renderer.h> - -#include <errno.h> -#include <sys/strm.h> -#include <sys/stat.h> - -QT_BEGIN_NAMESPACE - -static int idCounter = 0; - -MmRendererMediaPlayerControl::MmRendererMediaPlayerControl(QMediaPlayer *parent) - : QPlatformMediaPlayer(parent), - m_context(0), - m_id(-1), - m_connection(0), - m_audioId(-1), - m_state(QMediaPlayer::StoppedState), - m_volume(100), - m_muted(false), - m_rate(1), - m_position(0), - m_mediaStatus(QMediaPlayer::NoMedia), - m_playAfterMediaLoaded(false), - m_inputAttached(false), - m_bufferLevel(0) -{ - m_loadingTimer.setSingleShot(true); - m_loadingTimer.setInterval(0); - connect(&m_loadingTimer, SIGNAL(timeout()), this, SLOT(continueLoadMedia())); - QCoreApplication::eventDispatcher()->installNativeEventFilter(this); -} - -void MmRendererMediaPlayerControl::destroy() -{ - stop(); - detach(); - closeConnection(); - QCoreApplication::eventDispatcher()->removeNativeEventFilter(this); -} - -void MmRendererMediaPlayerControl::openConnection() -{ - m_connection = mmr_connect(NULL); - if (!m_connection) { - emitPError("Unable to connect to the multimedia renderer"); - return; - } - - m_id = idCounter++; - m_contextName = QString("MmRendererMediaPlayerControl_%1_%2").arg(m_id) - .arg(QCoreApplication::applicationPid()); - m_context = mmr_context_create(m_connection, m_contextName.toLatin1(), - 0, S_IRWXU|S_IRWXG|S_IRWXO); - if (!m_context) { - emitPError("Unable to create context"); - closeConnection(); - return; - } - - startMonitoring(); -} - -void MmRendererMediaPlayerControl::handleMmStopped() -{ - // Only react to stop events that happen when the end of the stream is reached and - // playback is stopped because of this. - // Ignore other stop event sources, such as calling mmr_stop() ourselves. - if (m_state != QMediaPlayer::StoppedState) { - setMediaStatus(QMediaPlayer::EndOfMedia); - stopInternal(IgnoreMmRenderer); - } -} - -void MmRendererMediaPlayerControl::handleMmSuspend(const QString &reason) -{ - if (m_state == QMediaPlayer::StoppedState) - return; - - Q_UNUSED(reason); - setMediaStatus(QMediaPlayer::StalledMedia); -} - -void MmRendererMediaPlayerControl::handleMmSuspendRemoval(const QString &bufferProgress) -{ - if (m_state == QMediaPlayer::StoppedState) - return; - - if (bufferProgress == QLatin1String("buffering")) - setMediaStatus(QMediaPlayer::BufferingMedia); - else - setMediaStatus(QMediaPlayer::BufferedMedia); -} - -void MmRendererMediaPlayerControl::handleMmPause() -{ - if (m_state == QMediaPlayer::PlayingState) { - setState(QMediaPlayer::PausedState); - } -} - -void MmRendererMediaPlayerControl::handleMmPlay() -{ - if (m_state == QMediaPlayer::PausedState) { - setState(QMediaPlayer::PlayingState); - } -} - -void MmRendererMediaPlayerControl::closeConnection() -{ - stopMonitoring(); - - if (m_context) { - mmr_context_destroy(m_context); - m_context = 0; - m_contextName.clear(); - } - - if (m_connection) { - mmr_disconnect(m_connection); - m_connection = 0; - } -} - -QByteArray MmRendererMediaPlayerControl::resourcePathForUrl(const QUrl &url) -{ - // If this is a local file, mmrenderer expects the file:// prefix and an absolute path. - // We treat URLs without scheme as local files, most likely someone just forgot to set the - // file:// prefix when constructing the URL. - if (url.isLocalFile() || url.scheme().isEmpty()) { - QString relativeFilePath; - if (!url.scheme().isEmpty()) - relativeFilePath = url.toLocalFile(); - else - relativeFilePath = url.path(); - const QFileInfo fileInfo(relativeFilePath); - return QFile::encodeName(QStringLiteral("file://") + fileInfo.absoluteFilePath()); - - // HTTP or similar URL - } else { - return url.toEncoded(); - } -} - -void MmRendererMediaPlayerControl::attach() -{ - // Should only be called in detached state - Q_ASSERT(m_audioId == -1 && !m_inputAttached); - - if (m_media.isNull() || !m_context) { - setMediaStatus(QMediaPlayer::NoMedia); - return; - } - - resetMonitoring(); - - if (m_videoRendererControl) - m_videoRendererControl->attachDisplay(m_context); - - if (m_videoWindowControl) - m_videoWindowControl->attachDisplay(m_context); - - const QByteArray defaultAudioDevice = qgetenv("QQNX_RENDERER_DEFAULT_AUDIO_SINK"); - m_audioId = mmr_output_attach(m_context, defaultAudioDevice.isEmpty() ? "audio:default" : defaultAudioDevice.constData(), "audio"); - if (m_audioId == -1) { - emitMmError("mmr_output_attach() for audio failed"); - return; - } - - if (m_audioId != -1) { - QString audioType = qnxAudioType(m_role); - QByteArray latin1AudioType = audioType.toLatin1(); - if (!audioType.isEmpty() && latin1AudioType == audioType) { - strm_dict_t *dict = strm_dict_new(); - dict = strm_dict_set(dict, "audio_type", latin1AudioType.constData()); - if (mmr_output_parameters(m_context, m_audioId, dict) != 0) - emitMmError("mmr_output_parameters: Setting audio_type failed"); - } - } - - const QByteArray resourcePath = resourcePathForUrl(m_media.request().url()); - if (resourcePath.isEmpty()) { - detach(); - return; - } - - if (mmr_input_attach(m_context, resourcePath.constData(), "track") != 0) { - emitMmError(QStringLiteral("mmr_input_attach() failed for ") + QString(resourcePath)); - setMediaStatus(QMediaPlayer::InvalidMedia); - detach(); - return; - } - - m_inputAttached = true; - setMediaStatus(QMediaPlayer::LoadedMedia); - - // mm-renderer has buffer properties "status" and "level" - // QMediaPlayer's buffer status maps to mm-renderer's buffer level - m_bufferLevel = 0; - emit bufferProgressChanged(m_bufferLevel/100.); -} - -void MmRendererMediaPlayerControl::detach() -{ - if (m_context) { - if (m_inputAttached) { - mmr_input_detach(m_context); - m_inputAttached = false; - } - if (m_videoRendererControl) - m_videoRendererControl->detachDisplay(); - if (m_videoWindowControl) - m_videoWindowControl->detachDisplay(); - if (m_audioId != -1 && m_context) { - mmr_output_detach(m_context, m_audioId); - m_audioId = -1; - } - } - - m_loadingTimer.stop(); -} - -QMediaPlayer::State MmRendererMediaPlayerControl::state() const -{ - return m_state; -} - -QMediaPlayer::MediaStatus MmRendererMediaPlayerControl::mediaStatus() const -{ - return m_mediaStatus; -} - -qint64 MmRendererMediaPlayerControl::duration() const -{ - return m_metaData.duration(); -} - -qint64 MmRendererMediaPlayerControl::position() const -{ - return m_position; -} - -void MmRendererMediaPlayerControl::setPosition(qint64 position) -{ - if (m_position != position) { - m_position = position; - - // Don't update in stopped state, it would not have any effect. Instead, the position is - // updated in play(). - if (m_state != QMediaPlayer::StoppedState) - setPositionInternal(m_position); - - emit positionChanged(m_position); - } -} - -int MmRendererMediaPlayerControl::volume() const -{ - return m_volume; -} - -void MmRendererMediaPlayerControl::setVolumeInternal(int newVolume) -{ - if (!m_context) - return; - - newVolume = qBound(0, newVolume, 100); - if (m_audioId != -1) { - strm_dict_t * dict = strm_dict_new(); - dict = strm_dict_set(dict, "volume", QString::number(newVolume).toLatin1()); - if (mmr_output_parameters(m_context, m_audioId, dict) != 0) - emitMmError("mmr_output_parameters: Setting volume failed"); - } -} - -void MmRendererMediaPlayerControl::setPlaybackRateInternal(qreal rate) -{ - if (!m_context) - return; - - const int mmRate = rate * 1000; - if (mmr_speed_set(m_context, mmRate) != 0) - emitMmError("mmr_speed_set failed"); -} - -void MmRendererMediaPlayerControl::setPositionInternal(qint64 position) -{ - if (!m_context) - return; - - if (m_metaData.isSeekable()) { - if (mmr_seek(m_context, QString::number(position).toLatin1()) != 0) - emitMmError("Seeking failed"); - } -} - -void MmRendererMediaPlayerControl::setMediaStatus(QMediaPlayer::MediaStatus status) -{ - if (m_mediaStatus != status) { - m_mediaStatus = status; - emit mediaStatusChanged(m_mediaStatus); - } -} - -void MmRendererMediaPlayerControl::setState(QMediaPlayer::State state) -{ - if (m_state != state) { - if (m_videoRendererControl) { - if (state == QMediaPlayer::PausedState || state == QMediaPlayer::StoppedState) { - m_videoRendererControl->pause(); - } else if ((state == QMediaPlayer::PlayingState) - && (m_state == QMediaPlayer::PausedState - || m_state == QMediaPlayer::StoppedState)) { - m_videoRendererControl->resume(); - } - } - - m_state = state; - emit stateChanged(m_state); - } -} - -void MmRendererMediaPlayerControl::stopInternal(StopCommand stopCommand) -{ - resetMonitoring(); - setPosition(0); - - if (m_state != QMediaPlayer::StoppedState) { - - if (stopCommand == StopMmRenderer) { - mmr_stop(m_context); - } - - setState(QMediaPlayer::StoppedState); - } -} - -void MmRendererMediaPlayerControl::setVolume(int volume) -{ - const int newVolume = qBound(0, volume, 100); - if (m_volume != newVolume) { - m_volume = newVolume; - if (!m_muted) - setVolumeInternal(m_volume); - emit volumeChanged(m_volume); - } -} - -bool MmRendererMediaPlayerControl::isMuted() const -{ - return m_muted; -} - -void MmRendererMediaPlayerControl::setMuted(bool muted) -{ - if (m_muted != muted) { - m_muted = muted; - setVolumeInternal(muted ? 0 : m_volume); - emit mutedChanged(muted); - } -} - -float MmRendererMediaPlayerControl::bufferProgress() const -{ - // mm-renderer has buffer properties "status" and "level" - // QMediaPlayer's buffer status maps to mm-renderer's buffer level - return m_bufferLevel/100.; -} - -bool MmRendererMediaPlayerControl::isAudioAvailable() const -{ - return m_metaData.hasAudio(); -} - -bool MmRendererMediaPlayerControl::isVideoAvailable() const -{ - return m_metaData.hasVideo(); -} - -bool MmRendererMediaPlayerControl::isSeekable() const -{ - return m_metaData.isSeekable(); -} - -QMediaTimeRange MmRendererMediaPlayerControl::availablePlaybackRanges() const -{ - // We can't get this information from the mmrenderer API yet, so pretend we can seek everywhere - return QMediaTimeRange(0, m_metaData.duration()); -} - -qreal MmRendererMediaPlayerControl::playbackRate() const -{ - return m_rate; -} - -void MmRendererMediaPlayerControl::setPlaybackRate(qreal rate) -{ - if (m_rate != rate) { - m_rate = rate; - setPlaybackRateInternal(m_rate); - emit playbackRateChanged(m_rate); - } -} - -QUrl MmRendererMediaPlayerControl::media() const -{ - return m_media; -} - -const QIODevice *MmRendererMediaPlayerControl::mediaStream() const -{ - // Always 0, we don't support QIODevice streams - return 0; -} - -void MmRendererMediaPlayerControl::setMedia(const QUrl &media, QIODevice *stream) -{ - Q_UNUSED(stream); // not supported - - stop(); - detach(); - - m_media = media; - - // Slight hack: With MediaPlayer QtQuick elements that have autoPlay set to true, playback - // would start before the QtQuick canvas is propagated to all elements, and therefore our - // video output would not work. Therefore, delay actually playing the media a bit so that the - // canvas is ready. - // The mmrenderer doesn't allow to attach video outputs after playing has started, otherwise - // this would be unnecessary. - if (!m_media.isNull()) { - setMediaStatus(QMediaPlayer::LoadingMedia); - m_loadingTimer.start(); // singleshot timer to continueLoadMedia() - } else { - continueLoadMedia(); // still needed, as it will update the media status and clear metadata - } -} - -void MmRendererMediaPlayerControl::continueLoadMedia() -{ - updateMetaData(nullptr); - attach(); - if (m_playAfterMediaLoaded) - play(); -} - -MmRendererVideoWindowControl *MmRendererMediaPlayerControl::videoWindowControl() const -{ - return m_videoWindowControl; -} - -void MmRendererMediaPlayerControl::play() -{ - if (m_playAfterMediaLoaded) - m_playAfterMediaLoaded = false; - - // No-op if we are already playing, except if we were called from continueLoadMedia(), in which - // case m_playAfterMediaLoaded is true (hence the 'else'). - else if (m_state == QMediaPlayer::PlayingState) - return; - - if (m_mediaStatus == QMediaPlayer::LoadingMedia) { - - // State changes are supposed to be synchronous - setState(QMediaPlayer::PlayingState); - - // Defer playing to later, when the timer triggers continueLoadMedia() - m_playAfterMediaLoaded = true; - return; - } - - // Un-pause the state when it is paused - if (m_state == QMediaPlayer::PausedState) { - setPlaybackRateInternal(m_rate); - setState(QMediaPlayer::PlayingState); - return; - } - - if (m_media.isNull() || !m_connection || !m_context || m_audioId == -1) { - setState(QMediaPlayer::StoppedState); - return; - } - - if (m_mediaStatus == QMediaPlayer::EndOfMedia) - m_position = 0; - - resetMonitoring(); - setPositionInternal(m_position); - setVolumeInternal(m_muted ? 0 : m_volume); - setPlaybackRateInternal(m_rate); - - if (mmr_play(m_context) != 0) { - setState(QMediaPlayer::StoppedState); - emitMmError("mmr_play() failed"); - return; - } - - setState( QMediaPlayer::PlayingState); -} - -void MmRendererMediaPlayerControl::pause() -{ - if (m_state == QMediaPlayer::PlayingState) { - setPlaybackRateInternal(0); - setState(QMediaPlayer::PausedState); - } -} - -void MmRendererMediaPlayerControl::stop() -{ - stopInternal(StopMmRenderer); -} - -MmRendererPlayerVideoRendererControl *MmRendererMediaPlayerControl::videoRendererControl() const -{ - return m_videoRendererControl; -} - -void MmRendererMediaPlayerControl::setVideoRendererControl(MmRendererPlayerVideoRendererControl *videoControl) -{ - m_videoRendererControl = videoControl; -} - -void MmRendererMediaPlayerControl::setVideoWindowControl(MmRendererVideoWindowControl *videoControl) -{ - m_videoWindowControl = videoControl; -} - -void MmRendererMediaPlayerControl::setMmPosition(qint64 newPosition) -{ - if (newPosition != 0 && newPosition != m_position) { - m_position = newPosition; - emit positionChanged(m_position); - } -} - -void MmRendererMediaPlayerControl::setMmBufferStatus(const QString &bufferProgress) -{ - if (bufferProgress == QLatin1String("buffering")) - setMediaStatus(QMediaPlayer::BufferingMedia); - else if (bufferProgress == QLatin1String("playing")) - setMediaStatus(QMediaPlayer::BufferedMedia); - // ignore "idle" buffer status -} - -void MmRendererMediaPlayerControl::setMmBufferLevel(int level, int capacity) -{ - m_bufferLevel = capacity == 0 ? 0 : level / static_cast<float>(capacity) * 100.0f; - m_bufferLevel = qBound(0, m_bufferLevel, 100); - emit bufferProgressChanged(m_bufferLevel/100.); -} - -void MmRendererMediaPlayerControl::updateMetaData(const strm_dict *dict) -{ - m_metaData.update(dict); - - if (m_videoWindowControl) - m_videoWindowControl->setMetaData(m_metaData); - - // ### convert to QMediaMetaData and notify the player about metadata changes - emit durationChanged(m_metaData.duration()); - emit audioAvailableChanged(m_metaData.hasAudio()); - emit videoAvailableChanged(m_metaData.hasVideo()); - emit availablePlaybackRangesChanged(availablePlaybackRanges()); - emit seekableChanged(m_metaData.isSeekable()); -} - -void MmRendererMediaPlayerControl::emitMmError(const QString &msg) -{ - int errorCode = MMR_ERROR_NONE; - const QString errorMessage = mmErrorMessage(msg, m_context, &errorCode); - qDebug() << errorMessage; - emit error(errorCode, errorMessage); -} - -void MmRendererMediaPlayerControl::emitPError(const QString &msg) -{ - const QString errorMessage = QString("%1: %2").arg(msg).arg(strerror(errno)); - qDebug() << errorMessage; - emit error(errno, errorMessage); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/mediaplayer/mmrenderermediaplayercontrol_p.h b/src/multimedia/platform/qnx/mediaplayer/mmrenderermediaplayercontrol_p.h deleted file mode 100644 index 63ae3c407..000000000 --- a/src/multimedia/platform/qnx/mediaplayer/mmrenderermediaplayercontrol_p.h +++ /dev/null @@ -1,183 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#ifndef MMRENDERERMEDIAPLAYERCONTROL_H -#define MMRENDERERMEDIAPLAYERCONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "mmrenderermetadata_p.h" -#include <private/qplatformmediaplayer_p.h> -#include <QtCore/qabstractnativeeventfilter.h> -#include <QtCore/qpointer.h> -#include <QtCore/qtimer.h> - -typedef struct mmr_connection mmr_connection_t; -typedef struct mmr_context mmr_context_t; -typedef struct mmrenderer_monitor mmrenderer_monitor_t; -typedef struct strm_dict strm_dict_t; - -QT_BEGIN_NAMESPACE - -class MmRendererAudioRoleControl; -class MmRendererPlayerVideoRendererControl; -class MmRendererVideoWindowControl; - -class MmRendererMediaPlayerControl : public QPlatformMediaPlayer, public QAbstractNativeEventFilter -{ - Q_OBJECT -public: - explicit MmRendererMediaPlayerControl(QMediaPlayer *parent = 0); - - QMediaPlayer::State state() const override; - - QMediaPlayer::MediaStatus mediaStatus() const override; - - qint64 duration() const override; - - qint64 position() const override; - void setPosition(qint64 position) override; - - int volume() const override; - void setVolume(int volume) override; - - bool isMuted() const override; - void setMuted(bool muted) override; - - float bufferProgress() const override; - - bool isAudioAvailable() const override; - bool isVideoAvailable() const override; - - bool isSeekable() const override; - - QMediaTimeRange availablePlaybackRanges() const override; - - qreal playbackRate() const override; - void setPlaybackRate(qreal rate) override; - - QUrl media() const override; - const QIODevice *mediaStream() const override; - void setMedia(const QUrl &media, QIODevice *stream) override; - - void play() override; - void pause() override; - void stop() override; - - MmRendererPlayerVideoRendererControl *videoRendererControl() const; - void setVideoRendererControl(MmRendererPlayerVideoRendererControl *videoControl); - - MmRendererVideoWindowControl *videoWindowControl() const; - void setVideoWindowControl(MmRendererVideoWindowControl *videoControl); - -protected: - virtual void startMonitoring() = 0; - virtual void stopMonitoring() = 0; - virtual void resetMonitoring() = 0; - - void openConnection(); - void emitMmError(const QString &msg); - void emitPError(const QString &msg); - void setMmPosition(qint64 newPosition); - void setMmBufferStatus(const QString &bufferProgress); - void setMmBufferLevel(int level, int capacity); - void handleMmStopped(); - void handleMmSuspend(const QString &reason); - void handleMmSuspendRemoval(const QString &bufferProgress); - void handleMmPause(); - void handleMmPlay(); - void updateMetaData(const strm_dict_t *dict); - - // must be called from subclass dtors (calls virtual function stopMonitoring()) - void destroy(); - - mmr_context_t *m_context; - int m_id; - QString m_contextName; - -private Q_SLOTS: - void continueLoadMedia(); - -private: - QByteArray resourcePathForUrl(const QUrl &url); - void closeConnection(); - void attach(); - void detach(); - - // All these set the specified value to the backend, but neither emit changed signals - // nor change the member value. - void setVolumeInternal(int newVolume); - void setPlaybackRateInternal(qreal rate); - void setPositionInternal(qint64 position); - - void setMediaStatus(QMediaPlayer::MediaStatus status); - void setState(QMediaPlayer::State state); - - enum StopCommand { StopMmRenderer, IgnoreMmRenderer }; - void stopInternal(StopCommand stopCommand); - - QUrl m_media; - mmr_connection_t *m_connection; - int m_audioId; - QMediaPlayer::State m_state; - int m_volume; - bool m_muted; - qreal m_rate; - QPointer<MmRendererPlayerVideoRendererControl> m_videoRendererControl; - QPointer<MmRendererVideoWindowControl> m_videoWindowControl; - MmRendererMetaData m_metaData; - qint64 m_position; - QMediaPlayer::MediaStatus m_mediaStatus; - bool m_playAfterMediaLoaded; - bool m_inputAttached; - int m_bufferLevel; - QTimer m_loadingTimer; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qnx/mediaplayer/mmrenderermetadata.cpp b/src/multimedia/platform/qnx/mediaplayer/mmrenderermetadata.cpp deleted file mode 100644 index d369ea560..000000000 --- a/src/multimedia/platform/qnx/mediaplayer/mmrenderermetadata.cpp +++ /dev/null @@ -1,298 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#include "mmrenderermetadata_p.h" - -#include <QtCore/qdebug.h> -#include <QtCore/qfile.h> -#include <QtCore/qstringlist.h> - -#include <mm/renderer/events.h> -#include <sys/neutrino.h> -#include <sys/strm.h> - -static const char *strm_string_getx(const strm_string_t *sstr, const char *defaultValue) -{ - return sstr ? strm_string_get(sstr) : defaultValue; -} - -#if _NTO_VERSION < 700 -static strm_dict_t *mmr_metadata_split(strm_dict_t const *, const char *, unsigned) -{ - return nullptr; -} -#endif - -QT_BEGIN_NAMESPACE - -MmRendererMetaData::MmRendererMetaData() -{ - clear(); -} - -static const char * titleKey = "md_title_name"; -static const char * artistKey = "md_title_artist"; -static const char * commentKey = "md_title_comment"; -static const char * genreKey = "md_title_genre"; -static const char * yearKey = "md_title_year"; -static const char * durationKey = "md_title_duration"; -static const char * bitRateKey = "md_title_bitrate"; -static const char * sampleKey = "md_title_samplerate"; -static const char * albumKey = "md_title_album"; -static const char * trackKey = "md_title_track"; -static const char * widthKey = "md_video_width"; -static const char * heightKey = "md_video_height"; -static const char * mediaTypeKey = "md_title_mediatype"; -static const char * pixelWidthKey = "md_video_pixel_width"; -static const char * pixelHeightKey = "md_video_pixel_height"; -static const char * seekableKey = "md_title_seekable"; -static const char * trackSampleKey = "sample_rate"; -static const char * trackBitRateKey = "bitrate"; -static const char * trackWidthKey = "width"; -static const char * trackHeightKey = "height"; -static const char * trackPixelWidthKey = "pixel_width"; -static const char * trackPixelHeightKey = "pixel_height"; - -static const int mediaTypeAudioFlag = 4; -static const int mediaTypeVideoFlag = 2; - -bool MmRendererMetaData::update(const strm_dict_t *dict) -{ - if (!dict) { - clear(); - return true; - } - - const strm_string_t *value; - - value = strm_dict_find_rstr(dict, durationKey); - m_duration = QByteArray(strm_string_getx(value, "0")).toLongLong(); - - value = strm_dict_find_rstr(dict, mediaTypeKey); - m_mediaType = QByteArray(strm_string_getx(value, "-1")).toInt(); - - value = strm_dict_find_rstr(dict, titleKey); - m_title = QString::fromLatin1(QByteArray(strm_string_getx(value, nullptr))); - - value = strm_dict_find_rstr(dict, seekableKey); - m_seekable = (strcmp(strm_string_getx(value, "1"), "0") != 0); - - value = strm_dict_find_rstr(dict, artistKey); - m_artist = QString::fromLatin1(QByteArray(strm_string_getx(value, nullptr))); - - value = strm_dict_find_rstr(dict, commentKey); - m_comment = QString::fromLatin1(QByteArray(strm_string_getx(value, nullptr))); - - value = strm_dict_find_rstr(dict, genreKey); - m_genre = QString::fromLatin1(QByteArray(strm_string_getx(value, nullptr))); - - value = strm_dict_find_rstr(dict, yearKey); - m_year = QByteArray(strm_string_getx(value, "0")).toInt(); - - value = strm_dict_find_rstr(dict, albumKey); - m_album = QString::fromLatin1(QByteArray(strm_string_getx(value, nullptr))); - - value = strm_dict_find_rstr(dict, trackKey); - m_track = QByteArray(strm_string_getx(value, "0")).toInt(); - - strm_dict_t *at = mmr_metadata_split(dict, "audio", 0); - if (at) { - value = strm_dict_find_rstr(at, trackSampleKey); - m_sampleRate = QByteArray(strm_string_getx(value, "0")).toInt(); - - value = strm_dict_find_rstr(at, trackBitRateKey); - m_audioBitRate = QByteArray(strm_string_getx(value, "0")).toInt(); - - strm_dict_destroy(at); - } else { - value = strm_dict_find_rstr(dict, sampleKey); - m_sampleRate = QByteArray(strm_string_getx(value, "0")).toInt(); - - value = strm_dict_find_rstr(dict, bitRateKey); - m_audioBitRate = QByteArray(strm_string_getx(value, "0")).toInt(); - } - - strm_dict_t *vt = mmr_metadata_split(dict, "video", 0); - if (vt) { - value = strm_dict_find_rstr(vt, trackWidthKey); - m_width = QByteArray(strm_string_getx(value, "0")).toInt(); - - value = strm_dict_find_rstr(vt, trackHeightKey); - m_height = QByteArray(strm_string_getx(value, "0")).toInt(); - - value = strm_dict_find_rstr(vt, trackPixelWidthKey); - m_pixelWidth = QByteArray(strm_string_getx(value, "1")).toFloat(); - - value = strm_dict_find_rstr(vt, trackPixelHeightKey); - m_pixelHeight = QByteArray(strm_string_getx(value, "1")).toFloat(); - - strm_dict_destroy(vt); - } else { - value = strm_dict_find_rstr(dict, widthKey); - m_width = QByteArray(strm_string_getx(value, "0")).toInt(); - - value = strm_dict_find_rstr(dict, heightKey); - m_height = QByteArray(strm_string_getx(value, "0")).toInt(); - - value = strm_dict_find_rstr(dict, pixelWidthKey); - m_pixelWidth = QByteArray(strm_string_getx(value, "1")).toFloat(); - - value = strm_dict_find_rstr(dict, pixelHeightKey); - m_pixelHeight = QByteArray(strm_string_getx(value, "1")).toFloat(); - } - - return true; -} - -void MmRendererMetaData::clear() -{ - strm_dict_t *dict; - dict = strm_dict_new(); - update(dict); - strm_dict_destroy(dict); -} - -qlonglong MmRendererMetaData::duration() const -{ - return m_duration; -} - -// Handling of pixel aspect ratio -// -// If the pixel aspect ratio is different from 1:1, it means the video needs to be stretched in -// order to look natural. -// For example, if the pixel width is 2, and the pixel height is 1, it means a video of 300x200 -// pixels needs to be displayed as 600x200 to look correct. -// In order to support this the easiest way, we simply pretend that the actual size of the video -// is 600x200, which will cause the video to be displayed in an aspect ratio of 3:1 instead of 3:2, -// and therefore look correct. - -int MmRendererMetaData::height() const -{ - return m_height * m_pixelHeight; -} - -int MmRendererMetaData::width() const -{ - return m_width * m_pixelWidth; -} - -bool MmRendererMetaData::hasVideo() const -{ - // By default, assume no video if we can't extract the information - if (m_mediaType == -1) - return false; - - return (m_mediaType & mediaTypeVideoFlag); -} - -bool MmRendererMetaData::hasAudio() const -{ - // By default, assume audio only if we can't extract the information - if (m_mediaType == -1) - return true; - - return (m_mediaType & mediaTypeAudioFlag); -} - -QString MmRendererMetaData::title() const -{ - return m_title; -} - -bool MmRendererMetaData::isSeekable() const -{ - return m_seekable; -} - -QString MmRendererMetaData::artist() const -{ - return m_artist; -} - -QString MmRendererMetaData::comment() const -{ - return m_comment; -} - -QString MmRendererMetaData::genre() const -{ - return m_genre; -} - -int MmRendererMetaData::year() const -{ - return m_year; -} - -QString MmRendererMetaData::mediaType() const -{ - if (hasVideo()) - return QLatin1String("video"); - else if (hasAudio()) - return QLatin1String("audio"); - else - return QString(); -} - -int MmRendererMetaData::audioBitRate() const -{ - return m_audioBitRate; -} - -int MmRendererMetaData::sampleRate() const -{ - return m_sampleRate; -} - -QString MmRendererMetaData::album() const -{ - return m_album; -} - -int MmRendererMetaData::track() const -{ - return m_track; -} - -QSize MmRendererMetaData::resolution() const -{ - return QSize(width(), height()); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/mediaplayer/mmrenderermetadata_p.h b/src/multimedia/platform/qnx/mediaplayer/mmrenderermetadata_p.h deleted file mode 100644 index 72b10a110..000000000 --- a/src/multimedia/platform/qnx/mediaplayer/mmrenderermetadata_p.h +++ /dev/null @@ -1,110 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#ifndef MMRENDERERMETADATA_H -#define MMRENDERERMETADATA_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qglobal.h> -#include <QtCore/QSize> -#include <QtCore/QString> - -typedef struct strm_dict strm_dict_t; - -QT_BEGIN_NAMESPACE - -class MmRendererMetaData -{ -public: - MmRendererMetaData(); - bool update(const strm_dict_t *dict); - void clear(); - - // Duration in milliseconds - qlonglong duration() const; - - int height() const; - int width() const; - bool hasVideo() const; - bool hasAudio() const; - bool isSeekable() const; - - QString title() const; - QString artist() const; - QString comment() const; - QString genre() const; - int year() const; - QString mediaType() const; - int audioBitRate() const; - int sampleRate() const; - QString album() const; - int track() const; - QSize resolution() const; - -private: - qlonglong m_duration; - int m_height; - int m_width; - int m_mediaType; - float m_pixelWidth; - float m_pixelHeight; - bool m_seekable; - QString m_title; - QString m_artist; - QString m_comment; - QString m_genre; - int m_year; - int m_audioBitRate; - int m_sampleRate; - QString m_album; - int m_track; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qnx/mediaplayer/mmrendererplayervideorenderercontrol.cpp b/src/multimedia/platform/qnx/mediaplayer/mmrendererplayervideorenderercontrol.cpp deleted file mode 100644 index dbcfad245..000000000 --- a/src/multimedia/platform/qnx/mediaplayer/mmrendererplayervideorenderercontrol.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ - -#include "mmrendererplayervideorenderercontrol_p.h" - -#include "windowgrabber.h" - -#include <QCoreApplication> -#include <QDebug> -#include <QVideoFrameFormat> -#include <QOpenGLContext> - -#include <mm/renderer.h> - -QT_BEGIN_NAMESPACE - -static int winIdCounter = 0; - -MmRendererPlayerVideoRendererControl::MmRendererPlayerVideoRendererControl(QObject *parent) - : QVideoRendererControl(parent) - , m_windowGrabber(new WindowGrabber(this)) - , m_context(0) - , m_videoId(-1) -{ - connect(m_windowGrabber, SIGNAL(updateScene(const QSize &)), SLOT(updateScene(const QSize &))); -} - -MmRendererPlayerVideoRendererControl::~MmRendererPlayerVideoRendererControl() -{ - detachDisplay(); -} - -QVideoSink *MmRendererPlayerVideoRendererControl::sink() const -{ - return m_sink; -} - -void MmRendererPlayerVideoRendererControl::setSink(QVideoSink *surface) -{ - m_sink = QPointer<QAbstractVideoSurface>(surface); - if (QOpenGLContext::currentContext()) - m_windowGrabber->checkForEglImageExtension(); - else if (m_sink) - m_sink->setProperty("_q_GLThreadCallback", QVariant::fromValue<QObject*>(this)); -} - -void MmRendererPlayerVideoRendererControl::attachDisplay(mmr_context_t *context) -{ - if (m_videoId != -1) { - qWarning() << "MmRendererPlayerVideoRendererControl: Video output already attached!"; - return; - } - - if (!context) { - qWarning() << "MmRendererPlayerVideoRendererControl: No media player context!"; - return; - } - - const QByteArray windowGroupId = m_windowGrabber->windowGroupId(); - if (windowGroupId.isEmpty()) { - qWarning() << "MmRendererPlayerVideoRendererControl: Unable to find window group"; - return; - } - - const QString windowName = QStringLiteral("MmRendererPlayerVideoRendererControl_%1_%2") - .arg(winIdCounter++) - .arg(QCoreApplication::applicationPid()); - - m_windowGrabber->setWindowId(windowName.toLatin1()); - - // Start with an invisible window, because we just want to grab the frames from it. - const QString videoDeviceUrl = QStringLiteral("screen:?winid=%1&wingrp=%2&initflags=invisible&nodstviewport=1") - .arg(windowName) - .arg(QString::fromLatin1(windowGroupId)); - - m_videoId = mmr_output_attach(context, videoDeviceUrl.toLatin1(), "video"); - if (m_videoId == -1) { - qWarning() << "mmr_output_attach() for video failed"; - return; - } - - m_context = context; -} - -void MmRendererPlayerVideoRendererControl::detachDisplay() -{ - m_windowGrabber->stop(); - - if (m_sink) - m_sink->stop(); - - if (m_context && m_videoId != -1) - mmr_output_detach(m_context, m_videoId); - - m_context = 0; - m_videoId = -1; -} - -void MmRendererPlayerVideoRendererControl::pause() -{ - m_windowGrabber->pause(); -} - -void MmRendererPlayerVideoRendererControl::resume() -{ - m_windowGrabber->resume(); -} - -class QnxTextureBuffer : public QAbstractVideoBuffer -{ -public: - QnxTextureBuffer(WindowGrabber *windowGrabber) : - QAbstractVideoBuffer(QVideoFrame::GLTextureHandle) - { - m_windowGrabber = windowGrabber; - m_handle = 0; - } - MapMode mapMode() const { - return QVideoFrame::ReadWrite; - } - void unmap() {} - MapData map(MapMode mode) override { return {}; } - QVariant handle() const { - if (!m_handle) { - const_cast<QnxTextureBuffer*>(this)->m_handle = m_windowGrabber->getNextTextureId(); - } - return m_handle; - } -private: - WindowGrabber *m_windowGrabber; - int m_handle; -}; - -void MmRendererPlayerVideoRendererControl::updateScene(const QSize &size) -{ - if (m_sink) { - // Depending on the support of EGL images on the current platform we either pass a texture - // handle or a copy of the image data - if (m_windowGrabber->eglImageSupported()) { - QnxTextureBuffer *textBuffer = new QnxTextureBuffer(m_windowGrabber); - QVideoFrame actualFrame(textBuffer, QVideoFrameFormat(size, QVideoFrameFormat::Format_BGR32)); - m_sink->setVideoFrame(actualFrame); - } else { - m_sink->setVideoFrame(m_windowGrabber->getNextImage().copy()); - } - } -} - -void MmRendererPlayerVideoRendererControl::customEvent(QEvent *e) -{ - // This is running in the render thread (OpenGL enabled) - if (e->type() == QEvent::User) - m_windowGrabber->checkForEglImageExtension(); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/mediaplayer/mmrendererplayervideorenderercontrol_p.h b/src/multimedia/platform/qnx/mediaplayer/mmrendererplayervideorenderercontrol_p.h deleted file mode 100644 index 3c529e0b4..000000000 --- a/src/multimedia/platform/qnx/mediaplayer/mmrendererplayervideorenderercontrol_p.h +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#ifndef MMRENDERERPLAYERVIDEORENDERERCONTROL_H -#define MMRENDERERPLAYERVIDEORENDERERCONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QPointer> -#include <qobject.h> - -typedef struct mmr_context mmr_context_t; - -QT_BEGIN_NAMESPACE - -class WindowGrabber; -class QVideoSink; - -class MmRendererPlayerVideoRendererControl : public QObject -{ - Q_OBJECT -public: - explicit MmRendererPlayerVideoRendererControl(QObject *parent = 0); - ~MmRendererPlayerVideoRendererControl(); - - QVideoSink *sink() const; - void setSink(QVideoSink *sink); - - // Called by media control - void attachDisplay(mmr_context_t *context); - void detachDisplay(); - void pause(); - void resume(); - - void customEvent(QEvent *) override; - -private Q_SLOTS: - void updateScene(const QSize &size); - -private: - QPointer<QVideoSink> m_sink; - - WindowGrabber* m_windowGrabber; - mmr_context_t *m_context; - - int m_videoId; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qnx/mediaplayer/mmrendererutil.cpp b/src/multimedia/platform/qnx/mediaplayer/mmrendererutil.cpp deleted file mode 100644 index 89cf8489b..000000000 --- a/src/multimedia/platform/qnx/mediaplayer/mmrendererutil.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#include "mmrendererutil_p.h" - -#include <QDebug> -#include <QDir> -#include <QFile> -#include <QJsonDocument> -#include <QJsonObject> -#include <QJsonValue> -#include <QMutex> -#include <QMutex> -#include <QString> -#include <QXmlStreamReader> - -#include <mm/renderer.h> - -QT_BEGIN_NAMESPACE - -struct MmError { - int errorCode; - const char *name; -}; - -#define MM_ERROR_ENTRY(error) { error, #error } -static const MmError mmErrors[] = { - MM_ERROR_ENTRY(MMR_ERROR_NONE), - MM_ERROR_ENTRY(MMR_ERROR_UNKNOWN ), - MM_ERROR_ENTRY(MMR_ERROR_INVALID_PARAMETER ), - MM_ERROR_ENTRY(MMR_ERROR_INVALID_STATE), - MM_ERROR_ENTRY(MMR_ERROR_UNSUPPORTED_VALUE), - MM_ERROR_ENTRY(MMR_ERROR_UNSUPPORTED_MEDIA_TYPE), - MM_ERROR_ENTRY(MMR_ERROR_MEDIA_PROTECTED), - MM_ERROR_ENTRY(MMR_ERROR_UNSUPPORTED_OPERATION), - MM_ERROR_ENTRY(MMR_ERROR_READ), - MM_ERROR_ENTRY(MMR_ERROR_WRITE), - MM_ERROR_ENTRY(MMR_ERROR_MEDIA_UNAVAILABLE), - MM_ERROR_ENTRY(MMR_ERROR_MEDIA_CORRUPTED), - MM_ERROR_ENTRY(MMR_ERROR_OUTPUT_UNAVAILABLE), - MM_ERROR_ENTRY(MMR_ERROR_NO_MEMORY), - MM_ERROR_ENTRY(MMR_ERROR_RESOURCE_UNAVAILABLE), - MM_ERROR_ENTRY(MMR_ERROR_MEDIA_DRM_NO_RIGHTS), - MM_ERROR_ENTRY(MMR_ERROR_DRM_CORRUPTED_DATA_STORE), - MM_ERROR_ENTRY(MMR_ERROR_DRM_OUTPUT_PROTECTION), - MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_HDMI), - MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_DISPLAYPORT), - MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_DVI), - MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_ANALOG_VIDEO), - MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_ANALOG_AUDIO), - MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_TOSLINK), - MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_SPDIF), - MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_BLUETOOTH), - MM_ERROR_ENTRY(MMR_ERROR_DRM_OPL_WIRELESSHD), -}; -static const unsigned int numMmErrors = sizeof(mmErrors) / sizeof(MmError); - -static QBasicMutex roleMapMutex; -static bool roleMapInitialized = false; -static QString roleMap[QAudio::GameRole + 1]; - -template <typename T, size_t N> -constexpr size_t countof(T (&)[N]) -{ - return N; -} - -QString keyValueMapsLocation() -{ - QByteArray qtKeyValueMaps = qgetenv("QT_KEY_VALUE_MAPS"); - if (qtKeyValueMaps.isNull()) - return QStringLiteral("/etc/qt/keyvaluemaps"); - else - return qtKeyValueMaps; -} - -QJsonObject loadMapObject(const QString &keyValueMapPath) -{ - QFile mapFile(keyValueMapsLocation() + keyValueMapPath); - if (mapFile.open(QIODevice::ReadOnly)) { - QByteArray mapFileContents = mapFile.readAll(); - QJsonDocument mapDocument = QJsonDocument::fromJson(mapFileContents); - if (mapDocument.isObject()) { - QJsonObject mapObject = mapDocument.object(); - return mapObject; - } - } - return QJsonObject(); -} - -QString mmErrorMessage(const QString &msg, mmr_context_t *context, int *errorCode) -{ - const mmr_error_info_t * const mmError = mmr_error_info(context); - - if (errorCode) - *errorCode = mmError->error_code; - - if (mmError->error_code < numMmErrors) { - return QString("%1: %2 (code %3)").arg(msg).arg(mmErrors[mmError->error_code].name) - .arg(mmError->error_code); - } else { - return QString("%1: Unknown error code %2").arg(msg).arg(mmError->error_code); - } -} - -bool checkForDrmPermission() -{ - QDir sandboxDir = QDir::home(); // always returns 'data' directory - sandboxDir.cdUp(); // change to app sandbox directory - - QFile file(sandboxDir.filePath("app/native/bar-descriptor.xml")); - if (!file.open(QIODevice::ReadOnly)) { - qWarning() << "checkForDrmPermission: Unable to open bar-descriptor.xml"; - return false; - } - - QXmlStreamReader reader(&file); - while (!reader.atEnd()) { - reader.readNextStartElement(); - if (reader.name() == QLatin1String("action") - || reader.name() == QLatin1String("permission")) { - if (reader.readElementText().trimmed() == QLatin1String("access_protected_media")) - return true; - } - } - - return false; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/mediaplayer/mmrendererutil_p.h b/src/multimedia/platform/qnx/mediaplayer/mmrendererutil_p.h deleted file mode 100644 index e1f972840..000000000 --- a/src/multimedia/platform/qnx/mediaplayer/mmrendererutil_p.h +++ /dev/null @@ -1,68 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#ifndef MMRENDERERUTIL_H -#define MMRENDERERUTIL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qglobal.h> -#include <QtMultimedia/qaudio.h> - -typedef struct mmr_context mmr_context_t; - -QT_BEGIN_NAMESPACE - -class QString; - -QString mmErrorMessage(const QString &msg, mmr_context_t *context, int * errorCode = 0); - -bool checkForDrmPermission(); - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qnx/mediaplayer/mmrenderervideowindowcontrol.cpp b/src/multimedia/platform/qnx/mediaplayer/mmrenderervideowindowcontrol.cpp deleted file mode 100644 index aca860828..000000000 --- a/src/multimedia/platform/qnx/mediaplayer/mmrenderervideowindowcontrol.cpp +++ /dev/null @@ -1,378 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#include "mmrenderervideowindowcontrol_p.h" -#include "mmrendererutil_p.h" -#include <QtCore/qdebug.h> -#include <QtGui/qguiapplication.h> -#include <QtGui/qpa/qplatformnativeinterface.h> -#include <QtGui/qscreen.h> -#include <QtGui/qwindow.h> -#include <mm/renderer.h> - -QT_BEGIN_NAMESPACE - -static int winIdCounter = 0; - -MmRendererVideoWindowControl::MmRendererVideoWindowControl(QObject *parent) - : QPlatformVideoSink(parent), - m_videoId(-1), - m_winId(0), - m_context(0), - m_fullscreen(false), - m_aspectRatioMode(Qt::IgnoreAspectRatio), - m_window(0), - m_hue(0), - m_brightness(0), - m_contrast(0), - m_saturation(0) -{ -} - -MmRendererVideoWindowControl::~MmRendererVideoWindowControl() -{ -} - -WId MmRendererVideoWindowControl::winId() const -{ - return m_winId; -} - -void MmRendererVideoWindowControl::setWinId(WId id) -{ - m_winId = id; -} - -void MmRendererVideoWindowControl::setDisplayRect(const QRect &rect) -{ - if (m_displayRect != rect) { - m_displayRect = rect; - updateVideoPosition(); - } -} - -void MmRendererVideoWindowControl::setFullScreen(bool fullScreen) -{ - if (m_fullscreen != fullScreen) { - m_fullscreen = fullScreen; - updateVideoPosition(); - emit fullScreenChanged(m_fullscreen); - } -} - -void MmRendererVideoWindowControl::repaint() -{ - // Nothing we can or should do here -} - -Qt::AspectRatioMode MmRendererVideoWindowControl::aspectRatioMode() const -{ - return m_aspectRatioMode; -} - -void MmRendererVideoWindowControl::setAspectRatioMode(Qt::AspectRatioMode mode) -{ - m_aspectRatioMode = mode; -} - -void MmRendererVideoWindowControl::setBrightness(float brightness) -{ - if (m_brightness != brightness) { - m_brightness = brightness; - updateBrightness(); - emit brightnessChanged(m_brightness); - } -} - -void MmRendererVideoWindowControl::setContrast(float contrast) -{ - if (m_contrast != contrast) { - m_contrast = contrast; - updateContrast(); - emit contrastChanged(m_contrast); - } -} - -void MmRendererVideoWindowControl::setHue(float hue) -{ - if (m_hue != hue) { - m_hue = hue; - updateHue(); - emit hueChanged(m_hue); - } -} - -void MmRendererVideoWindowControl::setSaturation(float saturation) -{ - if (m_saturation != saturation) { - m_saturation = saturation; - updateSaturation(); - emit saturationChanged(m_saturation); - } -} - -void MmRendererVideoWindowControl::attachDisplay(mmr_context_t *context) -{ - if (m_videoId != -1) { - qDebug() << "MmRendererVideoWindowControl: Video output already attached!"; - return; - } - - if (!context) { - qDebug() << "MmRendererVideoWindowControl: No media player context!"; - return; - } - - QWindow *window = findWindow(m_winId); - if (!window) { - qDebug() << "MmRendererVideoWindowControl: No video window!"; - return; - } - - QPlatformNativeInterface * const nativeInterface = QGuiApplication::platformNativeInterface(); - if (!nativeInterface) { - qDebug() << "MmRendererVideoWindowControl: Unable to get platform native interface"; - return; - } - - const char * const groupNameData = static_cast<const char *>( - nativeInterface->nativeResourceForWindow("windowGroup", window)); - if (!groupNameData) { - qDebug() << "MmRendererVideoWindowControl: Unable to find window group for window" - << window; - return; - } - - const QString groupName = QString::fromLatin1(groupNameData); - m_windowName = QString("MmRendererVideoWindowControl_%1_%2").arg(winIdCounter++) - .arg(QCoreApplication::applicationPid()); - - nativeInterface->setWindowProperty(window->handle(), - QStringLiteral("mmRendererWindowName"), m_windowName); - - // Start with an invisible window. If it would be visible right away, it would be at the wrong - // position, and we can only change the position once we get the window handle. - const QString videoDeviceUrl = - QString("screen:?winid=%1&wingrp=%2&initflags=invisible&nodstviewport=1").arg(m_windowName).arg(groupName); - - m_videoId = mmr_output_attach(context, videoDeviceUrl.toLatin1(), "video"); - if (m_videoId == -1) { - qDebug() << mmErrorMessage("mmr_output_attach() for video failed", context); - return; - } - - m_context = context; - updateVideoPosition(); - updateHue(); - updateContrast(); - updateBrightness(); - updateSaturation(); -} - -void MmRendererVideoWindowControl::updateVideoPosition() -{ - QWindow * const window = findWindow(m_winId); - if (m_context && m_videoId != -1 && window) { - QPoint topLeft = m_displayRect.topLeft(); - - QScreen * const screen = window->screen(); - int width = m_fullscreen ? - screen->size().width() : - m_displayRect.width(); - int height = m_fullscreen ? - screen->size().height() : - m_displayRect.height(); - - if (m_metaData.hasVideo() && m_metaData.width() > 0 && m_metaData.height() > 0) { - // We need the source size to do aspect ratio scaling - const qreal sourceRatio = m_metaData.width() / static_cast<float>(m_metaData.height()); - const qreal targetRatio = width / static_cast<float>(height); - - if (m_aspectRatioMode == Qt::KeepAspectRatio) { - if (targetRatio < sourceRatio) { - // Need to make height smaller - const int newHeight = width / sourceRatio; - const int heightDiff = height - newHeight; - topLeft.ry() += heightDiff / 2; - height = newHeight; - } else { - // Need to make width smaller - const int newWidth = sourceRatio * height; - const int widthDiff = width - newWidth; - topLeft.rx() += widthDiff / 2; - width = newWidth; - } - - } else if (m_aspectRatioMode == Qt::KeepAspectRatioByExpanding) { - if (targetRatio < sourceRatio) { - // Need to make width larger - const int newWidth = sourceRatio * height; - const int widthDiff = newWidth - width; - topLeft.rx() -= widthDiff / 2; - width = newWidth; - } else { - // Need to make height larger - const int newHeight = width / sourceRatio; - const int heightDiff = newHeight - height; - topLeft.ry() -= heightDiff / 2; - height = newHeight; - } - } - } - - if (m_window != 0) { - const int position[2] = { topLeft.x(), topLeft.y() }; - const int size[2] = { width, height }; - const int visible = m_displayRect.isValid(); - if (screen_set_window_property_iv(m_window, SCREEN_PROPERTY_POSITION, position) != 0) - perror("Setting video position failed"); - if (screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SIZE, size) != 0) - perror("Setting video size failed"); - if (screen_set_window_property_iv(m_window, SCREEN_PROPERTY_VISIBLE, &visible) != 0) - perror("Setting video visibility failed"); - } - } -} - -void MmRendererVideoWindowControl::updateBrightness() -{ - if (m_window != 0) { - const int backendValue = m_brightness * 255f; - if (screen_set_window_property_iv(m_window, SCREEN_PROPERTY_BRIGHTNESS, &backendValue) != 0) - perror("Setting brightness failed"); - } -} - -void MmRendererVideoWindowControl::updateContrast() -{ - if (m_window != 0) { - const int backendValue = m_contrast * 127f; - if (screen_set_window_property_iv(m_window, SCREEN_PROPERTY_CONTRAST, &backendValue) != 0) - perror("Setting contrast failed"); - } -} - -void MmRendererVideoWindowControl::updateHue() -{ - if (m_window != 0) { - const int backendValue = m_hue * 127f; - if (screen_set_window_property_iv(m_window, SCREEN_PROPERTY_HUE, &backendValue) != 0) - perror("Setting hue failed"); - } -} - -void MmRendererVideoWindowControl::updateSaturation() -{ - if (m_window != 0) { - const int backendValue = m_saturation * 127f; - if (screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SATURATION, &backendValue) != 0) - perror("Setting saturation failed"); - } -} - -void MmRendererVideoWindowControl::detachDisplay() -{ - if (m_context && m_videoId != -1) - mmr_output_detach(m_context, m_videoId); - - m_context = 0; - m_videoId = -1; - m_metaData.clear(); - m_windowName.clear(); - m_window = 0; - m_hue = 0; - m_brightness = 0; - m_contrast = 0; - m_saturation = 0; -} - -void MmRendererVideoWindowControl::setMetaData(const MmRendererMetaData &metaData) -{ - m_metaData = metaData; - setNativeSize(QSize(m_metaData.width(), m_metaData.height());) - - // To handle the updated source size data - updateVideoPosition(); -} - -void MmRendererVideoWindowControl::screenEventHandler(const screen_event_t &screen_event) -{ - int eventType; - if (screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_TYPE, &eventType) != 0) { - perror("MmRendererVideoWindowControl: Failed to query screen event type"); - return; - } - - if (eventType != SCREEN_EVENT_CREATE) - return; - - screen_window_t window = 0; - if (screen_get_event_property_pv(screen_event, SCREEN_PROPERTY_WINDOW, (void**)&window) != 0) { - perror("MmRendererVideoWindowControl: Failed to query window property"); - return; - } - - const int maxIdStrLength = 128; - char idString[maxIdStrLength]; - if (screen_get_window_property_cv(window, SCREEN_PROPERTY_ID_STRING, maxIdStrLength, idString) != 0) { - perror("MmRendererVideoWindowControl: Failed to query window ID string"); - return; - } - - if (m_windowName == idString) { - m_window = window; - updateVideoPosition(); - - const int visibleFlag = 1; - if (screen_set_window_property_iv(m_window, SCREEN_PROPERTY_VISIBLE, &visibleFlag) != 0) { - perror("MmRendererVideoWindowControl: Failed to make window visible"); - return; - } - } -} - -QWindow *MmRendererVideoWindowControl::findWindow(WId id) const -{ - const auto allWindows = QGuiApplication::allWindows(); - for (QWindow *window : allWindows) - if (window->winId() == id) - return window; - return 0; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/mediaplayer/mmrenderervideowindowcontrol_p.h b/src/multimedia/platform/qnx/mediaplayer/mmrenderervideowindowcontrol_p.h deleted file mode 100644 index 6e9cc92f3..000000000 --- a/src/multimedia/platform/qnx/mediaplayer/mmrenderervideowindowcontrol_p.h +++ /dev/null @@ -1,118 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion -** 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$ -** -****************************************************************************/ -#ifndef MMRENDERERVIDEOWINDOWCONTROL_H -#define MMRENDERERVIDEOWINDOWCONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "mmrenderermetadata_p.h" -#include "private/qplatformvideosink_p.h" -#include <screen/screen.h> - -typedef struct mmr_context mmr_context_t; - -QT_BEGIN_NAMESPACE - -class MmRendererVideoWindowControl : public QPlatformVideoSink -{ - Q_OBJECT -public: - explicit MmRendererVideoWindowControl(QObject *parent = 0); - ~MmRendererVideoWindowControl(); - - WId winId() const override; - void setWinId(WId id) override; - - void setDisplayRect(const QRect &rect) override; - - void setFullScreen(bool fullScreen) override; - - void repaint() override; - - Qt::AspectRatioMode aspectRatioMode() const override; - void setAspectRatioMode(Qt::AspectRatioMode mode) override; - - void setBrightness(float brightness) override; - void setContrast(float contrast) override; - void setHue(float hue) override; - void setSaturation(float saturation) override; - - // - // Called by media control - // - void detachDisplay(); - void attachDisplay(mmr_context_t *context); - void setMetaData(const MmRendererMetaData &metaData); - void screenEventHandler(const screen_event_t &event); - -private: - QWindow *findWindow(WId id) const; - void updateVideoPosition(); - void updateBrightness(); - void updateContrast(); - void updateHue(); - void updateSaturation(); - - int m_videoId; - WId m_winId; - QRect m_displayRect; - mmr_context_t *m_context; - bool m_fullscreen; - MmRendererMetaData m_metaData; - Qt::AspectRatioMode m_aspectRatioMode; - QString m_windowName; - screen_window_t m_window; - float m_hue; - float m_brightness; - float m_contrast; - float m_saturation; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qnx/mediaplayer/mmreventmediaplayercontrol.cpp b/src/multimedia/platform/qnx/mediaplayer/mmreventmediaplayercontrol.cpp deleted file mode 100644 index 218fab7e9..000000000 --- a/src/multimedia/platform/qnx/mediaplayer/mmreventmediaplayercontrol.cpp +++ /dev/null @@ -1,229 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 QNX Software Systems. All rights reserved. -** 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$ -** -****************************************************************************/ - -#include "mmreventmediaplayercontrol_p.h" -#include "mmreventthread_p.h" -#include "mmrenderervideowindowcontrol_p.h" - -#include <mm/renderer.h> -#include <tuple> - -QT_BEGIN_NAMESPACE - -static std::tuple<int, int, bool> parseBufferLevel(const QByteArray &value) -{ - const int slashPos = value.indexOf('/'); - if (slashPos <= 0) - return std::make_tuple(0, 0, false); - - bool ok = false; - const int level = value.left(slashPos).toInt(&ok); - if (!ok || level < 0) - return std::make_tuple(0, 0, false); - - const int capacity = value.mid(slashPos + 1).toInt(&ok); - if (!ok || capacity < 0) - return std::make_tuple(0, 0, false); - - return std::make_tuple(level, capacity, true); -} - -MmrEventMediaPlayerControl::MmrEventMediaPlayerControl(QObject *parent) - : MmRendererMediaPlayerControl(parent) - , m_eventThread(nullptr) - , m_bufferProgress("") - , m_bufferLevel(0) - , m_bufferCapacity(0) - , m_position(0) - , m_suspended(false) - , m_suspendedReason("unknown") - , m_state(MMR_STATE_IDLE) - , m_speed(0) -{ - openConnection(); -} - -MmrEventMediaPlayerControl::~MmrEventMediaPlayerControl() -{ - destroy(); -} - -void MmrEventMediaPlayerControl::startMonitoring() -{ - m_eventThread = new MmrEventThread(m_context); - - connect(m_eventThread, &MmrEventThread::eventPending, - this, &MmrEventMediaPlayerControl::readEvents); - - m_eventThread->setObjectName(QStringLiteral("MmrEventThread-") + QString::number(m_id)); - m_eventThread->start(); -} - -void MmrEventMediaPlayerControl::stopMonitoring() -{ - delete m_eventThread; - m_eventThread = nullptr; -} - -void MmrEventMediaPlayerControl::resetMonitoring() -{ - m_bufferProgress = ""; - m_bufferLevel = 0; - m_bufferCapacity = 0; - m_position = 0; - m_suspended = false; - m_suspendedReason = "unknown"; - m_state = MMR_STATE_IDLE; - m_speed = 0; -} - -bool MmrEventMediaPlayerControl::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) -{ - Q_UNUSED(result); - if (eventType == "screen_event_t") { - screen_event_t event = static_cast<screen_event_t>(message); - if (MmRendererVideoWindowControl *control = videoWindowControl()) - control->screenEventHandler(event); - } - - return false; -} - -void MmrEventMediaPlayerControl::readEvents() -{ - const mmr_event_t *event; - - while ((event = mmr_event_get(m_context))) { - if (event->type == MMR_EVENT_NONE) - break; - - switch (event->type) { - case MMR_EVENT_STATUS: { - if (event->data) { - const strm_string_t *value; - value = strm_dict_find_rstr(event->data, "bufferstatus"); - if (value) { - m_bufferProgress = QByteArray(strm_string_get(value)); - if (!m_suspended) - setMmBufferStatus(m_bufferProgress); - } - - value = strm_dict_find_rstr(event->data, "bufferlevel"); - if (value) { - const char *cstrValue = strm_string_get(value); - int level; - int capacity; - bool ok; - std::tie(level, capacity, ok) = parseBufferLevel(QByteArray(cstrValue)); - if (!ok) { - qCritical("Could not parse buffer capacity from '%s'", cstrValue); - } else { - m_bufferLevel = level; - m_bufferCapacity = capacity; - setMmBufferLevel(level, capacity); - } - } - - value = strm_dict_find_rstr(event->data, "suspended"); - if (value) { - if (!m_suspended) { - m_suspended = true; - m_suspendedReason = strm_string_get(value); - handleMmSuspend(m_suspendedReason); - } - } else if (m_suspended) { - m_suspended = false; - handleMmSuspendRemoval(m_bufferProgress); - } - } - - if (event->pos_str) { - const QByteArray valueBa = QByteArray(event->pos_str); - bool ok; - m_position = valueBa.toLongLong(&ok); - if (!ok) { - qCritical("Could not parse position from '%s'", valueBa.constData()); - } else { - setMmPosition(m_position); - } - } - break; - } - case MMR_EVENT_STATE: { - if (event->state == MMR_STATE_PLAYING && m_speed != event->speed) { - m_speed = event->speed; - if (m_speed == 0) - handleMmPause(); - else - handleMmPlay(); - } - break; - } - case MMR_EVENT_METADATA: { - updateMetaData(event->data); - break; - } - case MMR_EVENT_ERROR: - case MMR_EVENT_NONE: - case MMR_EVENT_OVERFLOW: - case MMR_EVENT_WARNING: - case MMR_EVENT_PLAYLIST: - case MMR_EVENT_INPUT: - case MMR_EVENT_OUTPUT: - case MMR_EVENT_CTXTPAR: - case MMR_EVENT_TRKPAR: - case MMR_EVENT_OTHER: { - break; - } - } - - // Currently, any exit from the playing state is considered a stop (end-of-media). - // If you ever need to separate end-of-media from things like "stopped unexpectedly" - // or "stopped because of an error", you'll find that end-of-media is signaled by an - // MMR_EVENT_ERROR of MMR_ERROR_NONE with state changed to MMR_STATE_STOPPED. - if (event->state != m_state && m_state == MMR_STATE_PLAYING) - handleMmStopped(); - m_state = event->state; - } - - if (m_eventThread) - m_eventThread->signalRead(); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/mediaplayer/mmreventmediaplayercontrol_p.h b/src/multimedia/platform/qnx/mediaplayer/mmreventmediaplayercontrol_p.h deleted file mode 100644 index d8e437b1d..000000000 --- a/src/multimedia/platform/qnx/mediaplayer/mmreventmediaplayercontrol_p.h +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 QNX Software Systems. All rights reserved. -** 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$ -** -****************************************************************************/ -#ifndef MMREVENTMEDIAPLAYERCONTROL_H -#define MMREVENTMEDIAPLAYERCONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "mmrenderermediaplayercontrol_p.h" - -#include <mm/renderer/events.h> - -QT_BEGIN_NAMESPACE - -class MmrEventThread; - -class MmrEventMediaPlayerControl final : public MmRendererMediaPlayerControl -{ - Q_OBJECT -public: - explicit MmrEventMediaPlayerControl(QObject *parent = 0); - ~MmrEventMediaPlayerControl() override; - - void startMonitoring() override; - void stopMonitoring() override; - void resetMonitoring() override; - - bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override; - -private Q_SLOTS: - void readEvents(); - -private: - MmrEventThread *m_eventThread; - - // status properties. - QByteArray m_bufferProgress; - int m_bufferLevel; - int m_bufferCapacity; - qint64 m_position; - bool m_suspended; - QByteArray m_suspendedReason; - - // state properties. - mmr_state_t m_state; - int m_speed; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qnx/mediaplayer/mmreventthread.cpp b/src/multimedia/platform/qnx/mediaplayer/mmreventthread.cpp deleted file mode 100644 index 25f26e216..000000000 --- a/src/multimedia/platform/qnx/mediaplayer/mmreventthread.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 QNX Software Systems. All rights reserved. -** 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$ -** -****************************************************************************/ - -#include "mmreventthread.h" - -#include <QtCore/QDebug> - -#include <errno.h> -#include <mm/renderer/events.h> -#include <sys/neutrino.h> - -static const int c_mmrCode = _PULSE_CODE_MINAVAIL + 0; -static const int c_readCode = _PULSE_CODE_MINAVAIL + 1; -static const int c_quitCode = _PULSE_CODE_MINAVAIL + 2; - -MmrEventThread::MmrEventThread(mmr_context_t *context) - : QThread(), - m_mmrContext(context) -{ - if (Q_UNLIKELY((m_channelId = ChannelCreate(_NTO_CHF_DISCONNECT - | _NTO_CHF_UNBLOCK - | _NTO_CHF_PRIVATE)) == -1)) { - qFatal("MmrEventThread: Can't continue without a channel"); - } - - if (Q_UNLIKELY((m_connectionId = ConnectAttach(0, 0, m_channelId, - _NTO_SIDE_CHANNEL, 0)) == -1)) { - ChannelDestroy(m_channelId); - qFatal("MmrEventThread: Can't continue without a channel connection"); - } - - SIGEV_PULSE_INIT(&m_mmrEvent, m_connectionId, SIGEV_PULSE_PRIO_INHERIT, c_mmrCode, 0); -} - -MmrEventThread::~MmrEventThread() -{ - // block until thread terminates - shutdown(); - - ConnectDetach(m_connectionId); - ChannelDestroy(m_channelId); -} - -void MmrEventThread::run() -{ - int armResult = mmr_event_arm(m_mmrContext, &m_mmrEvent); - if (armResult > 0) - emit eventPending(); - - while (1) { - struct _pulse msg; - memset(&msg, 0, sizeof(msg)); - int receiveId = MsgReceive(m_channelId, &msg, sizeof(msg), nullptr); - if (receiveId == 0) { - if (msg.code == c_mmrCode) { - emit eventPending(); - } else if (msg.code == c_readCode) { - armResult = mmr_event_arm(m_mmrContext, &m_mmrEvent); - if (armResult > 0) - emit eventPending(); - } else if (msg.code == c_quitCode) { - break; - } else { - qWarning() << Q_FUNC_INFO << "Unexpected pulse" << msg.code; - } - } else if (receiveId > 0) { - qWarning() << Q_FUNC_INFO << "Unexpected message" << msg.code; - } else { - qWarning() << Q_FUNC_INFO << "MsgReceive error" << strerror(errno); - } - } -} - -void MmrEventThread::signalRead() -{ - MsgSendPulse(m_connectionId, SIGEV_PULSE_PRIO_INHERIT, c_readCode, 0); -} - -void MmrEventThread::shutdown() -{ - MsgSendPulse(m_connectionId, SIGEV_PULSE_PRIO_INHERIT, c_quitCode, 0); - - // block until thread terminates - wait(); -} diff --git a/src/multimedia/platform/qnx/mediaplayer/mmreventthread_p.h b/src/multimedia/platform/qnx/mediaplayer/mmreventthread_p.h deleted file mode 100644 index 946548686..000000000 --- a/src/multimedia/platform/qnx/mediaplayer/mmreventthread_p.h +++ /dev/null @@ -1,90 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 QNX Software Systems. All rights reserved. -** 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$ -** -****************************************************************************/ - -#ifndef MMREVENTTHREAD_H -#define MMREVENTTHREAD_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/QThread> - -#include <sys/neutrino.h> -#include <sys/siginfo.h> - -QT_BEGIN_NAMESPACE - -typedef struct mmr_context mmr_context_t; - -class MmrEventThread : public QThread -{ - Q_OBJECT - -public: - MmrEventThread(mmr_context_t *context); - ~MmrEventThread() override; - - void signalRead(); - -protected: - void run() override; - -Q_SIGNALS: - void eventPending(); - -private: - void shutdown(); - - int m_channelId; - int m_connectionId; - struct sigevent m_mmrEvent; - mmr_context_t *m_mmrContext; -}; - -QT_END_NAMESPACE - -#endif // MMREVENTTHREAD_H diff --git a/src/multimedia/platform/qnx/qqnxdevicemanager.cpp b/src/multimedia/platform/qnx/qqnxdevicemanager.cpp deleted file mode 100644 index 23bc0bcf0..000000000 --- a/src/multimedia/platform/qnx/qqnxdevicemanager.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qqnxmediadevices_p.h" -#include "qmediadevices.h" -#include "qcameradevice_p.h" - -#include "private/qqnxaudiosource_p.h" -#include "private/qqnxaudiosink_p.h" -#include "private/qqnxaudiodevice_p.h" -#include "bbcamerasession_p.h" - -QT_BEGIN_NAMESPACE - -static QList<QCameraDevice> enumerateCameras() -{ - - camera_unit_t cameras[10]; - - unsigned int knownCameras = 0; - const camera_error_t result = camera_get_supported_cameras(10, &knownCameras, cameras); - if (result != CAMERA_EOK) { - qWarning() << "Unable to retrieve supported camera types:" << result; - return {}; - } - - QList<QCameraDevice> cameras; - for (unsigned int i = 0; i < knownCameras; ++i) { - QCameraDevicePrivate *p = new QCameraDevicePrivate; - switch (cameras[i]) { - case CAMERA_UNIT_FRONT: - p->id = BbCameraSession::cameraIdentifierFront(); - p->description = tr("Front Camera"); - p->position = QCameraDevice::FrontFace; - break; - case CAMERA_UNIT_REAR: - p->id = BbCameraSession::cameraIdentifierRear(); - p->description = tr("Rear Camera"); - p->position = QCameraDevice::BackFace; - break; - case CAMERA_UNIT_DESKTOP: - p->id = devices->append(BbCameraSession::cameraIdentifierDesktop(); - p->description = tr("Desktop Camera"); - p->position = QCameraDevice::UnspecifiedPosition; - break; - default: - break; - } - if (i == 0) - p->isDefault = true; - cameras.append(p->create()); - } - return cameras; -} - -QQnxMediaDevices::QQnxMediaDevices() - : QMediaPlatformMediaDevices() -{ -} - -QList<QAudioDevice> QQnxMediaDevices::audioInputs() const -{ - return { QAudioDevice(new QnxAudioDeviceInfo("default", QAudioDevice::Input)) }; -} - -QList<QAudioDevice> QQnxMediaDevices::audioOutputs() const -{ - return { QAudioDevice(new QnxAudioDeviceInfo("default", QAudioDevice::Output)) }; -} - -QList<QCameraDevice> QQnxMediaDevices::videoInputs() const -{ - return enumerateCameras(); -} - -QPlatformAudioSource *QQnxMediaDevices::createAudioSource(const QAudioDevice &deviceInfo) -{ - return new QQnxAudioSource(); -} - -QPlatformAudioSink *QQnxMediaDevices::createAudioSink(const QAudioDevice &deviceInfo) -{ - return new QNxAudioOutput(); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/qqnxdevicemanager_p.h b/src/multimedia/platform/qnx/qqnxdevicemanager_p.h deleted file mode 100644 index a23d451c8..000000000 --- a/src/multimedia/platform/qnx/qqnxdevicemanager_p.h +++ /dev/null @@ -1,73 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QQNXMEDIADEVICES_H -#define QQNXMEDIADEVICES_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediadevices_p.h> -#include <qaudio.h> - -QT_BEGIN_NAMESPACE - -class QQnxMediaDevices : public QPlatformMediaDevices -{ -public: - QQnxMediaDevices(); - - QList<QAudioDevice> audioInputs() const override; - QList<QAudioDevice> audioOutputs() const override; - QList<QCameraDevice> videoInputs() const override; - QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo) override; - QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo) override; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qnx/qqnxintegration.cpp b/src/multimedia/platform/qnx/qqnxintegration.cpp deleted file mode 100644 index 2e5496434..000000000 --- a/src/multimedia/platform/qnx/qqnxintegration.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qqnxintegration_p.h" -#include "qqnxmediadevices_p.h" -#include "private/mmrenderermediaplayercontrol_p.h" -#include "private/mmrendererutil_p.h" - -QT_BEGIN_NAMESPACE - -QQnxIntegration::QQnxIntegration() -{ - -} - -QQnxIntegration::~QQnxIntegration() -{ - delete m_devices; -} - -QMediaPlatformMediaDevices *QQnxIntegration::devices() -{ - if (!m_devices) - m_devices = new QQnxMediaDevices(); - return m_devices; -} - -QPlatformMediaPlayer *QQnxIntegration::createPlayer(QMediaPlayer *parent) -{ - return new MmRendererMediaPlayerControl(parent); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qnx/qqnxintegration_p.h b/src/multimedia/platform/qnx/qqnxintegration_p.h deleted file mode 100644 index 8cc4ce9fb..000000000 --- a/src/multimedia/platform/qnx/qqnxintegration_p.h +++ /dev/null @@ -1,76 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QQNXINTEGRATION_H -#define QQNXINTEGRATION_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediaintegration_p.h> - -QT_BEGIN_NAMESPACE - -class QQnxMediaDevices; -class QQnxPlayerInterface; - -class QQnxIntegration : public QPlatformMediaIntegration -{ -public: - QQnxIntegration(); - ~QQnxIntegration(); - - QPlatformMediaDevices *devices() override; - - QPlatformMediaPlayer *createPlayer(QMediaPlayer *parent) override; - - QQnxMediaDevices *m_devices = nullptr; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/qplatformaudiodecoder.cpp b/src/multimedia/platform/qplatformaudiodecoder.cpp index 61492defb..0bf472410 100644 --- a/src/multimedia/platform/qplatformaudiodecoder.cpp +++ b/src/multimedia/platform/qplatformaudiodecoder.cpp @@ -1,122 +1,14 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qplatformaudiodecoder_p.h" #include "qthread.h" QT_BEGIN_NAMESPACE +QPlatformAudioDecoder::QPlatformAudioDecoder(QAudioDecoder *parent) : q(parent) { } -/*! - \class QPlatformAudioDecoder - \obsolete - \inmodule QtMultimedia - - - \ingroup multimedia_control - - \brief The QPlatformAudioDecoder class provides access to audio decoding - functionality. - - \preliminary -*/ - -/*! - Constructs a new audio decoder control with the given \a parent. -*/ -QPlatformAudioDecoder::QPlatformAudioDecoder(QAudioDecoder *parent) - : QObject(parent), - q(parent) -{ -} - -/*! - \fn QPlatformAudioDecoder::source() const - - Returns the current media source filename, or a null QString if none (or a device) -*/ - -/*! - \fn QPlatformAudioDecoder::setSource(const QUrl &fileName) - - Sets the current source to \a fileName. Changing the source will - stop any current decoding and discard any buffers. - - Sources are exclusive, so only one can be set. -*/ - -/*! - \fn QPlatformAudioDecoder::sourceDevice() const - - Returns the current media source QIODevice, or 0 if none (or a file). -*/ - -/*! - \fn QPlatformAudioDecoder::setSourceDevice(QIODevice *device) - - Sets the current source to \a device. Changing the source will - stop any current decoding and discard any buffers. - - Sources are exclusive, so only one can be set. -*/ - -/*! - \fn QPlatformAudioDecoder::start() - - Starts decoding the current media. - - \sa read() -*/ - -/*! - \fn QPlatformAudioDecoder::stop() - - Stops playback of the current media and discards any buffers. - - If successful, the player control will immediately stop decoding. -*/ - -/*! - \fn QPlatformAudioDecoder::error(int error, const QString &errorString) - - Signals that an \a error has occurred. The \a errorString provides a more detailed explanation. -*/ +QPlatformAudioDecoder::~QPlatformAudioDecoder() = default; void QPlatformAudioDecoder::error(int error, const QString &errorString) { @@ -124,29 +16,25 @@ void QPlatformAudioDecoder::error(int error, const QString &errorString) return; m_error = QAudioDecoder::Error(error); m_errorString = errorString; - setIsDecoding(false); - emit q->error(m_error); + if (m_error != QAudioDecoder::NoError) { + setIsDecoding(false); + emit q->error(m_error); + } } -/*! - \fn QPlatformAudioDecoder::bufferAvailableChanged(bool available) - - Signals that the bufferAvailable property has changed to \a available. -*/ void QPlatformAudioDecoder::bufferAvailableChanged(bool available) { + if (m_bufferAvailable == available) + return; + m_bufferAvailable = available; + if (QThread::currentThread() != q->thread()) QMetaObject::invokeMethod(q, "bufferAvailableChanged", Qt::QueuedConnection, Q_ARG(bool, available)); else emit q->bufferAvailableChanged(available); } -/*! - \fn QPlatformAudioDecoder::bufferReady() - - Signals that a new buffer is ready for reading. -*/ void QPlatformAudioDecoder::bufferReady() { if (QThread::currentThread() != q->thread()) @@ -155,82 +43,39 @@ void QPlatformAudioDecoder::bufferReady() emit q->bufferReady(); } -/*! - \fn QPlatformAudioDecoder::sourceChanged() - - Signals that the current source of the decoder has changed. - - \sa source(), sourceDevice() -*/ void QPlatformAudioDecoder::sourceChanged() { emit q->sourceChanged(); } -/*! - \fn QPlatformAudioDecoder::formatChanged(const QAudioFormat &format) - - Signals that the current audio format of the decoder has changed to \a format. -*/ void QPlatformAudioDecoder::formatChanged(const QAudioFormat &format) { emit q->formatChanged(format); } -/*! - \fn void QPlatformAudioDecoder::finished() - - Signals that the decoding has finished successfully. - If decoding fails, error signal is emitted instead. - - \sa start(), stop(), error() -*/ void QPlatformAudioDecoder::finished() { + durationChanged(-1); setIsDecoding(false); emit q->finished(); } -/*! - \fn void QPlatformAudioDecoder::positionChanged(qint64 position) - - Signals that the current \a position of the decoder has changed. - - \sa durationChanged() -*/ void QPlatformAudioDecoder::positionChanged(qint64 position) { - q->positionChanged(position); + if (m_position == position) + return; + m_position = position; + emit q->positionChanged(position); } -/*! - \fn void QPlatformAudioDecoder::durationChanged(qint64 duration) - - Signals that the estimated \a duration of the decoded data has changed. - - \sa positionChanged() -*/ void QPlatformAudioDecoder::durationChanged(qint64 duration) { - q->durationChanged(duration); + if (m_duration == duration) + return; + m_duration = duration; + emit q->durationChanged(duration); } -/*! - \fn QPlatformAudioDecoder::read() - Attempts to read a buffer from the decoder, without blocking. Returns invalid buffer if there are - no decoded buffers available, or on error. -*/ - -/*! - \fn QPlatformAudioDecoder::position() const - Returns position (in milliseconds) of the last buffer read from - the decoder or -1 if no buffers have been read. -*/ - -/*! - \fn QPlatformAudioDecoder::duration() const - Returns total duration (in milliseconds) of the audio stream - or -1 if not available. -*/ - QT_END_NAMESPACE + +#include "moc_qplatformaudiodecoder_p.cpp" diff --git a/src/multimedia/platform/qplatformaudiodecoder_p.h b/src/multimedia/platform/qplatformaudiodecoder_p.h index b9334102a..1159a37ca 100644 --- a/src/multimedia/platform/qplatformaudiodecoder_p.h +++ b/src/multimedia/platform/qplatformaudiodecoder_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QAUDIODECODERCONTROL_H #define QAUDIODECODERCONTROL_H @@ -51,12 +15,11 @@ // We mean it. // -#include <QtMultimedia/qaudiodecoder.h> - -#include <QtCore/qpair.h> - #include <QtMultimedia/qaudiobuffer.h> #include <QtMultimedia/qaudiodecoder.h> +#include <QtCore/qpair.h> +#include <QtCore/qurl.h> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -79,10 +42,10 @@ public: virtual void setAudioFormat(const QAudioFormat &format) = 0; virtual QAudioBuffer read() = 0; - virtual bool bufferAvailable() const = 0; + virtual bool bufferAvailable() const { return m_bufferAvailable; } - virtual qint64 position() const = 0; - virtual qint64 duration() const = 0; + virtual qint64 position() const { return m_position; } + virtual qint64 duration() const { return m_duration; } void formatChanged(const QAudioFormat &format); @@ -108,14 +71,20 @@ public: QAudioDecoder::Error error() const { return m_error; } QString errorString() const { return m_errorString; } + virtual ~QPlatformAudioDecoder(); + protected: explicit QPlatformAudioDecoder(QAudioDecoder *parent); + private: QAudioDecoder *q = nullptr; + qint64 m_duration = -1; + qint64 m_position = -1; QAudioDecoder::Error m_error = QAudioDecoder::NoError; QString m_errorString; bool m_isDecoding = false; + bool m_bufferAvailable = false; }; QT_END_NAMESPACE diff --git a/src/multimedia/platform/qplatformaudioinput_p.h b/src/multimedia/platform/qplatformaudioinput_p.h index c4e4334b3..d6497c06e 100644 --- a/src/multimedia/platform/qplatformaudioinput_p.h +++ b/src/multimedia/platform/qplatformaudioinput_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPLATFORMAUDIOINPUT_H #define QPLATFORMAUDIOINPUT_H @@ -53,6 +17,8 @@ #include <private/qtmultimediaglobal_p.h> #include <qaudiodevice.h> +#include <functional> + QT_BEGIN_NAMESPACE class QAudioInput; @@ -60,10 +26,10 @@ class QAudioInput; class Q_MULTIMEDIA_EXPORT QPlatformAudioInput { public: - QPlatformAudioInput(QAudioInput *qq) : q(qq) {} - virtual ~QPlatformAudioInput() {} + explicit QPlatformAudioInput(QAudioInput *qq) : q(qq) { } + virtual ~QPlatformAudioInput() = default; - virtual void setAudioDevice(const QAudioDevice &/*device*/) {} + virtual void setAudioDevice(const QAudioDevice & /*device*/) { } virtual void setMuted(bool /*muted*/) {} virtual void setVolume(float /*volume*/) {} @@ -71,6 +37,7 @@ public: QAudioDevice device; float volume = 1.; bool muted = false; + std::function<void()> disconnectFunction; }; QT_END_NAMESPACE diff --git a/src/multimedia/platform/qplatformaudiooutput_p.h b/src/multimedia/platform/qplatformaudiooutput_p.h index 594d59ed0..b8f5af6f3 100644 --- a/src/multimedia/platform/qplatformaudiooutput_p.h +++ b/src/multimedia/platform/qplatformaudiooutput_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPLATFORMAUDIOOUTPUT_H #define QPLATFORMAUDIOOUTPUT_H @@ -60,8 +24,8 @@ class QAudioOutput; class Q_MULTIMEDIA_EXPORT QPlatformAudioOutput { public: - QPlatformAudioOutput(QAudioOutput *qq) : q(qq) {} - virtual ~QPlatformAudioOutput() {} + explicit QPlatformAudioOutput(QAudioOutput *qq) : q(qq) { } + virtual ~QPlatformAudioOutput() = default; virtual void setAudioDevice(const QAudioDevice &/*device*/) {} virtual void setMuted(bool /*muted*/) {} @@ -71,6 +35,7 @@ public: QAudioDevice device; float volume = 1.; bool muted = false; + std::function<void()> disconnectFunction; }; QT_END_NAMESPACE diff --git a/src/multimedia/platform/qplatformaudioresampler_p.h b/src/multimedia/platform/qplatformaudioresampler_p.h new file mode 100644 index 000000000..fd8edc2be --- /dev/null +++ b/src/multimedia/platform/qplatformaudioresampler_p.h @@ -0,0 +1,33 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QPLATFORMAUDIORESAMPLER_P_H +#define QPLATFORMAUDIORESAMPLER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qtmultimediaglobal_p.h> +#include <qaudiobuffer.h> + +QT_BEGIN_NAMESPACE + +class QPlatformAudioResampler +{ +public: + virtual ~QPlatformAudioResampler() = default; + + virtual QAudioBuffer resample(const char *data, size_t size) = 0; +}; + +QT_END_NAMESPACE + +#endif // QPLATFORMAUDIORESAMPLER_P_H diff --git a/src/multimedia/platform/qplatformcamera.cpp b/src/multimedia/platform/qplatformcamera.cpp index 21a13ea97..d03c19d67 100644 --- a/src/multimedia/platform/qplatformcamera.cpp +++ b/src/multimedia/platform/qplatformcamera.cpp @@ -1,93 +1,59 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qplatformcamera_p.h" +#include "private/qcameradevice_p.h" QT_BEGIN_NAMESPACE -/*! - \class QPlatformCamera - \obsolete - +QPlatformCamera::QPlatformCamera(QCamera *parent) : QPlatformVideoSource(parent), m_camera(parent) +{ + qRegisterMetaType<QVideoFrame>(); +} +QCameraFormat QPlatformCamera::findBestCameraFormat(const QCameraDevice &camera) const +{ + // check if fmt is better. We try to find the highest resolution that offers + // at least 30 FPS + // we use 29 FPS to compare against as some cameras report 29.97 FPS... - \brief The QPlatformCamera class is an abstract base class for - classes that control still cameras or video cameras. + auto makeCriteria = [this](const QCameraFormat &fmt) { + constexpr float MinSufficientFrameRate = 29.f; - \inmodule QtMultimedia + const auto isValid = fmt.pixelFormat() != QVideoFrameFormat::Format_Invalid; + const auto resolution = fmt.resolution(); + const auto sufficientFrameRate = std::min(fmt.maxFrameRate(), MinSufficientFrameRate); + const auto pixelFormatScore = + cameraPixelFormatScore(fmt.pixelFormat(), QCameraFormatPrivate::getColorRange(fmt)); - \ingroup multimedia_control -*/ + return std::make_tuple( + isValid, // 1st: ensure valid formats + sufficientFrameRate, // 2nd: ensure the highest frame rate in the range [0; 29]*/ + resolution.width() * resolution.height(), // 3rd: ensure the highest resolution + pixelFormatScore, // 4th: eshure the best pixel format + fmt.maxFrameRate()); // 5th: ensure the highest framerate in the whole range + }; -/*! - Constructs a camera control object with \a parent. -*/ + const auto formats = camera.videoFormats(); + const auto found = + std::max_element(formats.begin(), formats.end(), + [makeCriteria](const QCameraFormat &fmtA, const QCameraFormat &fmtB) { + return makeCriteria(fmtA) < makeCriteria(fmtB); + }); -QPlatformCamera::QPlatformCamera(QCamera *parent) - : QObject(parent), - m_camera(parent) -{ + return found == formats.end() ? QCameraFormat{} : *found; } -QCameraFormat QPlatformCamera::findBestCameraFormat(const QCameraDevice &camera) +QVideoFrameFormat QPlatformCamera::frameFormat() const { - QCameraFormat f; - const auto formats = camera.videoFormats(); - for (const auto &fmt : formats) { - // check if fmt is better. We try to find the highest resolution that offers - // at least 30 FPS - if (f.maxFrameRate() < 30 && fmt.maxFrameRate() > f.maxFrameRate()) - f = fmt; - else if (f.maxFrameRate() == fmt.maxFrameRate() && - f.resolution().width()*f.resolution().height() < fmt.resolution().width()*fmt.resolution().height()) - f = fmt; - } - return f; + QVideoFrameFormat result(m_cameraFormat.resolution(), + m_framePixelFormat == QVideoFrameFormat::Format_Invalid + ? m_cameraFormat.pixelFormat() + : m_framePixelFormat); + result.setStreamFrameRate(m_cameraFormat.maxFrameRate()); + return result; } -/*! - \fn void QPlatformCamera::error(int error, const QString &errorString) - - Signal emitted when an error occurs with error code \a error and - a description of the error \a errorString. -*/ - void QPlatformCamera::supportedFeaturesChanged(QCamera::Features f) { if (m_supportedFeatures == f) @@ -255,7 +221,12 @@ int QPlatformCamera::colorTemperatureForWhiteBalance(QCamera::WhiteBalanceMode m return 0; } - +void QPlatformCamera::updateError(QCamera::Error error, const QString &errorString) +{ + QMetaObject::invokeMethod(this, [this, error, errorString]() { + m_error.setAndNotify(error, errorString, *this); + }); +} QT_END_NAMESPACE diff --git a/src/multimedia/platform/qplatformcamera_p.h b/src/multimedia/platform/qplatformcamera_p.h index 8ddaf51ae..341bf9121 100644 --- a/src/multimedia/platform/qplatformcamera_p.h +++ b/src/multimedia/platform/qplatformcamera_p.h @@ -1,44 +1,8 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef QCAMERACONTROL_H -#define QCAMERACONTROL_H +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QPLATFORMCAMERA_H +#define QPLATFORMCAMERA_H // // W A R N I N G @@ -51,25 +15,20 @@ // We mean it. // -#include <QtCore/qobject.h> -#include <QtMultimedia/qtmultimediaglobal.h> - +#include "qplatformvideosource_p.h" +#include "private/qerrorinfo_p.h" #include <QtMultimedia/qcamera.h> QT_BEGIN_NAMESPACE -class Q_MULTIMEDIA_EXPORT QPlatformCamera : public QObject +class Q_MULTIMEDIA_EXPORT QPlatformCamera : public QPlatformVideoSource { Q_OBJECT public: - virtual bool isActive() const = 0; - virtual void setActive(bool active) = 0; - virtual void setCamera(const QCameraDevice &camera) = 0; virtual bool setCameraFormat(const QCameraFormat &/*format*/) { return false; } - - virtual void setCaptureSession(QPlatformMediaCaptureSession *) {} + QCameraFormat cameraFormat() const { return m_cameraFormat; } virtual bool isFocusModeSupported(QCamera::FocusMode mode) const { return mode == QCamera::FocusModeAuto; } virtual void setFocusMode(QCamera::FocusMode /*mode*/) {} @@ -100,6 +59,8 @@ public: virtual void setWhiteBalanceMode(QCamera::WhiteBalanceMode /*mode*/) {} virtual void setColorTemperature(int /*temperature*/) {} + QVideoFrameFormat frameFormat() const override; + QCamera::Features supportedFeatures() const { return m_supportedFeatures; } QCamera::FocusMode focusMode() const { return m_focusMode; } @@ -149,14 +110,27 @@ public: static int colorTemperatureForWhiteBalance(QCamera::WhiteBalanceMode mode); + QCamera::Error error() const { return m_error.code(); } + QString errorString() const final { return m_error.description(); } + + void updateError(QCamera::Error error, const QString &errorString); + Q_SIGNALS: - void activeChanged(bool); - void error(int error, const QString &errorString); + void errorOccurred(QCamera::Error error, const QString &errorString); protected: explicit QPlatformCamera(QCamera *parent); - static QCameraFormat findBestCameraFormat(const QCameraDevice &camera); + virtual int cameraPixelFormatScore(QVideoFrameFormat::PixelFormat /*format*/, + QVideoFrameFormat::ColorRange /*colorRange*/) const + { + return 0; + } + + QCameraFormat findBestCameraFormat(const QCameraDevice &camera) const; + QCameraFormat m_cameraFormat; + QVideoFrameFormat::PixelFormat m_framePixelFormat = QVideoFrameFormat::Format_Invalid; + private: QCamera *m_camera = nullptr; QCamera::Features m_supportedFeatures = {}; @@ -181,10 +155,11 @@ private: float m_maxExposureTime = -1.; QCamera::WhiteBalanceMode m_whiteBalance = QCamera::WhiteBalanceAuto; int m_colorTemperature = 0; + QErrorInfo<QCamera::Error> m_error; }; QT_END_NAMESPACE -#endif // QCAMERACONTROL_H +#endif // QPLATFORMCAMERA_H diff --git a/src/multimedia/platform/qplatformcapturablewindows_p.h b/src/multimedia/platform/qplatformcapturablewindows_p.h new file mode 100644 index 000000000..41949aaac --- /dev/null +++ b/src/multimedia/platform/qplatformcapturablewindows_p.h @@ -0,0 +1,44 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QPLATFORMCAPTURABLEWINDOWS_P_H +#define QPLATFORMCAPTURABLEWINDOWS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qtmultimediaglobal_p.h" +#include "qcapturablewindow.h" + +#include <qlist.h> + +QT_BEGIN_NAMESPACE + +class QCapturableWindow; +class QCapturableWindowPrivate; + +class QPlatformCapturableWindows +{ +public: + QPlatformCapturableWindows() = default; + + virtual ~QPlatformCapturableWindows() = default; + + virtual QList<QCapturableWindow> windows() const { return {}; } + + virtual bool isWindowValid(const QCapturableWindowPrivate &) const { return false; } + + Q_DISABLE_COPY(QPlatformCapturableWindows); +}; + +QT_END_NAMESPACE + +#endif // QPLATFORMCAPTURABLEWINDOWS_P_H diff --git a/src/multimedia/platform/qplatformimagecapture.cpp b/src/multimedia/platform/qplatformimagecapture.cpp index 5cfc7e31e..5d8349256 100644 --- a/src/multimedia/platform/qplatformimagecapture.cpp +++ b/src/multimedia/platform/qplatformimagecapture.cpp @@ -1,58 +1,16 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qplatformimagecapture_p.h" #include <QtCore/qstringlist.h> QT_BEGIN_NAMESPACE -/*! - \class QPlatformImageCapture - \obsolete - - \brief The QPlatformImageCapture class provides a control interface - for image capture services. - - \inmodule QtMultimedia - \ingroup multimedia_control - -*/ +QPlatformImageCapture::QPlatformImageCapture(QImageCapture *parent) + : QObject(parent), + m_imageCapture(parent) +{ +} QString QPlatformImageCapture::msgCameraNotReady() { @@ -64,114 +22,6 @@ QString QPlatformImageCapture::msgImageCaptureNotSet() return QImageCapture::tr("No instance of QImageCapture set on QMediaCaptureSession."); } -/*! - Constructs a new image capture control object with the given \a parent -*/ -QPlatformImageCapture::QPlatformImageCapture(QImageCapture *parent) - : QObject(parent), - m_imageCapture(parent) -{ -} - -/*! - \fn QPlatformImageCapture::isReadyForCapture() const - - Identifies if a capture control is ready to perform a capture - immediately (all the resources necessary for image capture are allocated, - hardware initialized, flash is charged, etc). - - Returns true if the camera is ready for capture; and false if it is not. - - It's permissible to call capture() while the camera status is QCamera::ActiveStatus - regardless of isReadyForCapture property value. - If camera is not ready to capture image immediately, - the capture request is queued with all the related camera settings - to be executed as soon as possible. -*/ - -/*! - \fn QPlatformImageCapture::readyForCaptureChanged(bool ready) - - Signals that a capture control's \a ready state has changed. -*/ - -/*! - \fn QPlatformImageCapture::capture(const QString &fileName) - - Initiates the capture of an image to \a fileName. - The \a fileName can be relative or empty, - in this case the service should use the system specific place - and file naming scheme. - - The Camera service should save all the capture parameters - like exposure settings or image processing parameters, - so changes to camera parameters after capture() is called - do not affect previous capture requests. - - Returns the capture request id number, which is used later - with imageExposed(), imageCaptured() and imageSaved() signals. -*/ - -/*! - \fn QPlatformImageCapture::imageExposed(int requestId) - - Signals that an image with it \a requestId - has just been exposed. - This signal can be used for the shutter sound or other indicaton. -*/ - -/*! - \fn QPlatformImageCapture::imageCaptured(int requestId, const QImage &preview) - - Signals that an image with it \a requestId - has been captured and a \a preview is available. -*/ - -/*! - \fn QPlatformImageCapture::imageMetadataAvailable(int id, const QMediaMetaData &metaData) - - Signals that a metadata for an image with request \a id is available. - - This signal should be emitted between imageExposed and imageSaved signals. -*/ - -/*! - \fn QPlatformImageCapture::imageAvailable(int requestId, const QVideoFrame &buffer) - - Signals that a captured \a buffer with a \a requestId is available. -*/ - -/*! - \fn QPlatformImageCapture::imageSaved(int requestId, const QString &fileName) - - Signals that a captured image with a \a requestId has been saved - to \a fileName. -*/ - -/*! - \fn QPlatformImageCapture::imageSettings() const - - Returns the currently used image encoder settings. - - The returned value may be different than passed to setImageSettings() - if the settings contains defaulted or undefined parameters. -*/ - -/*! - \fn QPlatformImageCapture::setImageSettings(const QImageEncoderSettings &settings) - - Sets the selected image encoder \a settings. -*/ - -/*! - \fn QPlatformImageCapture::error(int id, int error, const QString &errorString) - - Signals the capture request \a id failed with \a error code and message \a errorString. - - \sa QImageCapture::Error -*/ - - QT_END_NAMESPACE #include "moc_qplatformimagecapture_p.cpp" diff --git a/src/multimedia/platform/qplatformimagecapture_p.h b/src/multimedia/platform/qplatformimagecapture_p.h index 01842987d..5bfb15ced 100644 --- a/src/multimedia/platform/qplatformimagecapture_p.h +++ b/src/multimedia/platform/qplatformimagecapture_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QCAMERAIMAGECAPTURECONTROL_H #define QCAMERAIMAGECAPTURECONTROL_H @@ -54,6 +18,7 @@ #include <QtMultimedia/qimagecapture.h> #include <QtMultimedia/qmediametadata.h> #include <QtMultimedia/qimagecapture.h> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE diff --git a/src/multimedia/platform/qplatformmediacapture.cpp b/src/multimedia/platform/qplatformmediacapture.cpp index b041f69db..c8aded824 100644 --- a/src/multimedia/platform/qplatformmediacapture.cpp +++ b/src/multimedia/platform/qplatformmediacapture.cpp @@ -1,52 +1,44 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include <qtmultimediaglobal_p.h> -#include "qplatformmediacapture_p.h" -#include "qaudiodevice.h" -#include "qaudioinput.h" +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <QtMultimedia/qaudiodevice.h> +#include <QtMultimedia/qaudioinput.h> +#include <QtMultimedia/qmediacapturesession.h> +#include <QtMultimedia/private/qplatformcamera_p.h> +#include <QtMultimedia/private/qplatformmediacapture_p.h> +#include <QtMultimedia/private/qmediacapturesession_p.h> +#include <QtMultimedia/private/qplatformsurfacecapture_p.h> +#include <QtMultimedia/private/qtmultimediaglobal_p.h> QT_BEGIN_NAMESPACE -QPlatformMediaCaptureSession::~QPlatformMediaCaptureSession() +QPlatformMediaCaptureSession::~QPlatformMediaCaptureSession() = default; + +std::vector<QPlatformVideoSource *> QPlatformMediaCaptureSession::activeVideoSources() { + std::vector<QPlatformVideoSource *> result; + + auto checkSource = [&result](QPlatformVideoSource *source) { + if (source && source->isActive()) + result.push_back(source); + }; + + checkSource(camera()); + checkSource(screenCapture()); + checkSource(windowCapture()); + + return result; +} + +void *QPlatformMediaCaptureSession::nativePipeline(QMediaCaptureSession *session) +{ + auto sessionPrivate = session->d_func(); + if (!sessionPrivate || !sessionPrivate->captureSession) + return nullptr; + + return sessionPrivate->captureSession->nativePipeline(); } QT_END_NAMESPACE +#include "moc_qplatformmediacapture_p.cpp" diff --git a/src/multimedia/platform/qplatformmediacapture_p.h b/src/multimedia/platform/qplatformmediacapture_p.h index a988f3de4..981cf199b 100644 --- a/src/multimedia/platform/qplatformmediacapture_p.h +++ b/src/multimedia/platform/qplatformmediacapture_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPLATFORMMEDIACAPTURE_H #define QPLATFORMMEDIACAPTURE_H @@ -56,28 +20,40 @@ QT_BEGIN_NAMESPACE class QPlatformCamera; class QPlatformImageCapture; -class QPlatformMediaEncoder; +class QPlatformMediaRecorder; class QAudioDevice; class QCameraDevice; class QVideoSink; class QPlatformAudioInput; class QPlatformAudioOutput; +class QMediaCaptureSession; +class QPlatformSurfaceCapture; +class QPlatformVideoSource; class Q_MULTIMEDIA_EXPORT QPlatformMediaCaptureSession : public QObject { Q_OBJECT public: QPlatformMediaCaptureSession() = default; - virtual ~QPlatformMediaCaptureSession(); + ~QPlatformMediaCaptureSession() override; + + void setCaptureSession(QMediaCaptureSession *session) { m_session = session; } + QMediaCaptureSession *captureSession() const { return m_session; } virtual QPlatformCamera *camera() = 0; virtual void setCamera(QPlatformCamera *) {} + virtual QPlatformSurfaceCapture *screenCapture() { return nullptr; } + virtual void setScreenCapture(QPlatformSurfaceCapture *) {} + + virtual QPlatformSurfaceCapture *windowCapture() { return nullptr; } + virtual void setWindowCapture(QPlatformSurfaceCapture *) { } + virtual QPlatformImageCapture *imageCapture() = 0; virtual void setImageCapture(QPlatformImageCapture *) {} - virtual QPlatformMediaEncoder *mediaEncoder() = 0; - virtual void setMediaEncoder(QPlatformMediaEncoder *) {} + virtual QPlatformMediaRecorder *mediaRecorder() = 0; + virtual void setMediaRecorder(QPlatformMediaRecorder *) {} virtual void setAudioInput(QPlatformAudioInput *input) = 0; @@ -85,10 +61,23 @@ public: virtual void setAudioOutput(QPlatformAudioOutput *) {} + // TBD: implement ordering of the sources basing on the order of adding + std::vector<QPlatformVideoSource *> activeVideoSources(); + + virtual void *nativePipeline() { return nullptr; } + + // private API, the purpose is getting GstPipeline + static void *nativePipeline(QMediaCaptureSession *); + Q_SIGNALS: void cameraChanged(); + void screenCaptureChanged(); + void windowCaptureChanged(); void imageCaptureChanged(); void encoderChanged(); + +private: + QMediaCaptureSession *m_session = nullptr; }; QT_END_NAMESPACE diff --git a/src/multimedia/platform/qplatformmediadevices.cpp b/src/multimedia/platform/qplatformmediadevices.cpp index 4ac1ec14c..a6029228d 100644 --- a/src/multimedia/platform/qplatformmediadevices.cpp +++ b/src/multimedia/platform/qplatformmediadevices.cpp @@ -1,125 +1,114 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qplatformmediadevices_p.h" -#include "qmediadevices.h" -#include "qaudiodevice.h" +#include "qplatformmediaintegration_p.h" #include "qcameradevice.h" #include "qaudiosystem_p.h" +#include "qaudiodevice.h" +#include "qplatformvideodevices_p.h" + +#if defined(Q_OS_ANDROID) +#include <qandroidmediadevices_p.h> +#elif defined(Q_OS_DARWIN) +#include <qdarwinmediadevices_p.h> +#elif defined(Q_OS_WINDOWS) && QT_CONFIG(wmf) +#include <qwindowsmediadevices_p.h> +#elif QT_CONFIG(alsa) +#include <qalsamediadevices_p.h> +#elif QT_CONFIG(pulseaudio) +#include <qpulseaudiomediadevices_p.h> +#elif defined(Q_OS_QNX) +#include <qqnxmediadevices_p.h> +#elif defined(Q_OS_WASM) +#include <private/qwasmmediadevices_p.h> +#endif + QT_BEGIN_NAMESPACE +std::unique_ptr<QPlatformMediaDevices> QPlatformMediaDevices::create() +{ +#ifdef Q_OS_DARWIN + return std::make_unique<QDarwinMediaDevices>(); +#elif defined(Q_OS_WINDOWS) && QT_CONFIG(wmf) + return std::make_unique<QWindowsMediaDevices>(); +#elif defined(Q_OS_ANDROID) + return std::make_unique<QAndroidMediaDevices>(); +#elif QT_CONFIG(alsa) + return std::make_unique<QAlsaMediaDevices>(); +#elif QT_CONFIG(pulseaudio) + return std::make_unique<QPulseAudioMediaDevices>(); +#elif defined(Q_OS_QNX) + return std::make_unique<QQnxMediaDevices>(); +#elif defined(Q_OS_WASM) + return std::make_unique<QWasmMediaDevices>(); +#else + return std::make_unique<QPlatformMediaDevices>(); +#endif +} + QPlatformMediaDevices::QPlatformMediaDevices() = default; +void QPlatformMediaDevices::initVideoDevicesConnection() +{ + // Make sure we are notified if video inputs changed + if (const auto videoDevices = QPlatformMediaIntegration::instance()->videoDevices()) + connect(videoDevices, &QPlatformVideoDevices::videoInputsChanged, this, + &QPlatformMediaDevices::videoInputsChanged, Qt::UniqueConnection); +} + QPlatformMediaDevices::~QPlatformMediaDevices() = default; -QAudioDevice QPlatformMediaDevices::audioInput(const QByteArray &id) const +QList<QAudioDevice> QPlatformMediaDevices::audioInputs() const { - const auto inputs = audioInputs(); - for (auto i : inputs) { - if (i.id() == id) - return i; - } return {}; } -QAudioDevice QPlatformMediaDevices::audioOutput(const QByteArray &id) const +QList<QAudioDevice> QPlatformMediaDevices::audioOutputs() const { - const auto outputs = audioOutputs(); - for (auto o : outputs) { - if (o.id() == id) - return o; - } return {}; } -QCameraDevice QPlatformMediaDevices::videoInput(const QByteArray &id) const +QPlatformAudioSource *QPlatformMediaDevices::createAudioSource(const QAudioDevice &, QObject *) +{ + return nullptr; +} +QPlatformAudioSink *QPlatformMediaDevices::createAudioSink(const QAudioDevice &, QObject *) { - const auto inputs = videoInputs(); - for (auto i : inputs) { - if (i.id() == id) - return i; - } - return QCameraDevice(); + return nullptr; } -QPlatformAudioSource* QPlatformMediaDevices::audioInputDevice(const QAudioFormat &format, const QAudioDevice &deviceInfo) +QPlatformAudioSource *QPlatformMediaDevices::audioInputDevice(const QAudioFormat &format, + const QAudioDevice &deviceInfo, + QObject *parent) { QAudioDevice info = deviceInfo; if (info.isNull()) info = audioInputs().value(0); - QPlatformAudioSource* p = createAudioSource(info); + QPlatformAudioSource* p = !info.isNull() ? createAudioSource(info, parent) : nullptr; if (p) p->setFormat(format); return p; } -QPlatformAudioSink* QPlatformMediaDevices::audioOutputDevice(const QAudioFormat &format, const QAudioDevice &deviceInfo) +QPlatformAudioSink *QPlatformMediaDevices::audioOutputDevice(const QAudioFormat &format, + const QAudioDevice &deviceInfo, + QObject *parent) { QAudioDevice info = deviceInfo; if (info.isNull()) info = audioOutputs().value(0); - QPlatformAudioSink* p = createAudioSink(info); + QPlatformAudioSink* p = !info.isNull() ? createAudioSink(info, parent) : nullptr; if (p) p->setFormat(format); return p; } -void QPlatformMediaDevices::audioInputsChanged() const -{ - for (auto m : m_devices) - emit m->audioInputsChanged(); -} - -void QPlatformMediaDevices::audioOutputsChanged() const -{ - for (auto m : m_devices) - emit m->audioOutputsChanged(); -} - -void QPlatformMediaDevices::videoInputsChanged() const -{ - for (auto m : m_devices) - emit m->videoInputsChanged(); -} - +void QPlatformMediaDevices::prepareAudio() { } QT_END_NAMESPACE + +#include "moc_qplatformmediadevices_p.cpp" diff --git a/src/multimedia/platform/qplatformmediadevices_p.h b/src/multimedia/platform/qplatformmediadevices_p.h index 62b7229ab..0de41a973 100644 --- a/src/multimedia/platform/qplatformmediadevices_p.h +++ b/src/multimedia/platform/qplatformmediadevices_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPLATFORMMEDIADEVICES_H #define QPLATFORMMEDIADEVICES_H @@ -53,51 +17,44 @@ #include <private/qtmultimediaglobal_p.h> #include <qlist.h> +#include <qobject.h> +#include <memory> QT_BEGIN_NAMESPACE -class QMediaDevices; class QAudioDevice; -class QCameraDevice; class QPlatformAudioSource; class QPlatformAudioSink; class QAudioFormat; -class Q_MULTIMEDIA_EXPORT QPlatformMediaDevices +class Q_MULTIMEDIA_EXPORT QPlatformMediaDevices : public QObject { + Q_OBJECT public: QPlatformMediaDevices(); - virtual ~QPlatformMediaDevices(); - - virtual QList<QAudioDevice> audioInputs() const = 0; - virtual QList<QAudioDevice> audioOutputs() const = 0; - virtual QList<QCameraDevice> videoInputs() const = 0; - virtual QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo) = 0; - virtual QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo) = 0; - - QAudioDevice audioInput(const QByteArray &id) const; - QAudioDevice audioOutput(const QByteArray &id) const; - QCameraDevice videoInput(const QByteArray &id) const; - - QPlatformAudioSource *audioInputDevice(const QAudioFormat &format, const QAudioDevice &deviceInfo); - QPlatformAudioSink *audioOutputDevice(const QAudioFormat &format, const QAudioDevice &deviceInfo); - - void addDevices(QMediaDevices *m) - { - m_devices.append(m); - } - void removeDevices(QMediaDevices *m) - { - m_devices.removeAll(m); - } - -protected: - void audioInputsChanged() const; - void audioOutputsChanged() const; - void videoInputsChanged() const; - -private: - QList<QMediaDevices *> m_devices; + ~QPlatformMediaDevices() override; + + static std::unique_ptr<QPlatformMediaDevices> create(); + + virtual QList<QAudioDevice> audioInputs() const; + virtual QList<QAudioDevice> audioOutputs() const; + + virtual QPlatformAudioSource *createAudioSource(const QAudioDevice &, QObject *parent); + virtual QPlatformAudioSink *createAudioSink(const QAudioDevice &, QObject *parent); + + QPlatformAudioSource *audioInputDevice(const QAudioFormat &format, + const QAudioDevice &deviceInfo, QObject *parent); + QPlatformAudioSink *audioOutputDevice(const QAudioFormat &format, + const QAudioDevice &deviceInfo, QObject *parent); + + virtual void prepareAudio(); + + void initVideoDevicesConnection(); + +Q_SIGNALS: + void audioInputsChanged(); + void audioOutputsChanged(); + void videoInputsChanged(); }; QT_END_NAMESPACE diff --git a/src/multimedia/platform/qplatformmediaencoder.cpp b/src/multimedia/platform/qplatformmediaencoder.cpp deleted file mode 100644 index b803740c3..000000000 --- a/src/multimedia/platform/qplatformmediaencoder.cpp +++ /dev/null @@ -1,195 +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$ -** -****************************************************************************/ - -#include "qplatformmediaencoder_p.h" -#include <QObject> - -QT_BEGIN_NAMESPACE - - -/*! - \class QPlatformMediaEncoder - \obsolete - \inmodule QtMultimedia - - - \ingroup multimedia_control - - \brief The QPlatformMediaEncoder class provides access to the recording - functionality. - - This control provides a means to set the \l {outputLocation()}{output location}, - and record(), pause(), resume(), and stop() recording. It also - provides feedback on the \l {duration()}{duration} of the recording. - - \sa QMediaRecorder - -*/ - -/*! - Constructs a media recorder control with the given \a parent. -*/ - -QPlatformMediaEncoder::QPlatformMediaEncoder(QMediaRecorder *parent) - : q(parent) -{ -} - -/*! - \fn QUrl QPlatformMediaEncoder::outputLocation() const - - Returns the current output location being used. -*/ - -/*! - \fn bool QPlatformMediaEncoder::setOutputLocation(const QUrl &location) - - Sets the output \a location and returns if this operation is successful. - If file at the output location already exists, it should be overwritten. - - The \a location can be relative or empty; - in this case the service should use the system specific place and file naming scheme. - - After recording has started, the backend should report the actual file location - with actualLocationChanged() signal. -*/ - -/*! - \fn QMediaRecorder::RecorderState QPlatformMediaEncoder::state() const - - Return the current recording state. -*/ - -/*! - \fn qint64 QPlatformMediaEncoder::duration() const - - Return the current duration in milliseconds. -*/ - -/*! - \fn void QPlatformMediaEncoder::record(QMediaEncoderSettings &settings) - - Start media recording in accordance with \a{settings}. -*/ - -/*! - \fn void QPlatformMediaEncoder::pause() - - Pause media recording. Not all platforms supports this operation -*/ -void QPlatformMediaEncoder::pause() { - error(QMediaRecorder::FormatError, QMediaRecorder::tr("Pause not supported")); -} - -/*! - \fn void QPlatformMediaEncoder::resume() - - Resume media recording. Not all platforms supports this operation -*/ -void QPlatformMediaEncoder::resume() { - error(QMediaRecorder::FormatError, QMediaRecorder::tr("Resume not supported")); -} - -/*! - \fn void QPlatformMediaEncoder::stop() - - Stop media recording -*/ - -/*! - \fn void QPlatformMediaEncoder::stateChanged(QMediaRecorder::RecorderState state) - - Signals that the \a state of a media recorder has changed. -*/ -void QPlatformMediaEncoder::stateChanged(QMediaRecorder::RecorderState state) -{ - if (m_state == state) - return; - m_state = state; - emit q->recorderStateChanged(state); -} - -/*! - \fn void QPlatformMediaEncoder::durationChanged(qint64 duration) - - Signals that the \a duration of the recorded media has changed. - - This only emitted when there is a discontinuous change in the duration such as being reset to 0. -*/ -void QPlatformMediaEncoder::durationChanged(qint64 duration) -{ - emit q->durationChanged(duration); -} - -/*! - \fn void QPlatformMediaEncoder::actualLocationChanged(const QUrl &location) - - Signals that the actual media \a location has changed. - This signal should be emitted at start of recording. -*/ -void QPlatformMediaEncoder::actualLocationChanged(const QUrl &location) -{ - if (m_actualLocation == location) - return; - m_actualLocation = location; - emit q->actualLocationChanged(location); -} - -/*! - \fn void QPlatformMediaEncoder::error(QMediaRecorder::Error error, const QString &errorString) - - Signals that an \a error has occurred. The \a errorString describes the error. -*/ -void QPlatformMediaEncoder::error(QMediaRecorder::Error error, const QString &errorString) -{ - if (error == m_error && errorString == m_errorString) - return; - m_error = error; - m_errorString = errorString; - if (error != QMediaRecorder::NoError) - emit q->errorOccurred(error, errorString); - emit q->errorChanged(); -} - -void QPlatformMediaEncoder::metaDataChanged() -{ - emit q->metaDataChanged(); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/qplatformmediaformatinfo.cpp b/src/multimedia/platform/qplatformmediaformatinfo.cpp index d4bfecafc..e69b32ed3 100644 --- a/src/multimedia/platform/qplatformmediaformatinfo.cpp +++ b/src/multimedia/platform/qplatformmediaformatinfo.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qplatformmediaformatinfo_p.h" #include <qset.h> diff --git a/src/multimedia/platform/qplatformmediaformatinfo_p.h b/src/multimedia/platform/qplatformmediaformatinfo_p.h index 8842304ca..4229a3c4c 100644 --- a/src/multimedia/platform/qplatformmediaformatinfo_p.h +++ b/src/multimedia/platform/qplatformmediaformatinfo_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPLATFORMMEDIAFORMATINFO_H #define QPLATFORMMEDIAFORMATINFO_H diff --git a/src/multimedia/platform/qplatformmediaintegration.cpp b/src/multimedia/platform/qplatformmediaintegration.cpp index c3b4f1444..4bacc488f 100644 --- a/src/multimedia/platform/qplatformmediaintegration.cpp +++ b/src/multimedia/platform/qplatformmediaintegration.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <qtmultimediaglobal_p.h> #include "qplatformmediaintegration_p.h" @@ -43,88 +7,216 @@ #include <qmutex.h> #include <qplatformaudioinput_p.h> #include <qplatformaudiooutput_p.h> +#include <qplatformaudioresampler_p.h> +#include <qplatformvideodevices_p.h> +#include <qmediadevices.h> +#include <qcameradevice.h> +#include <qloggingcategory.h> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qapplicationstatic.h> -#if QT_CONFIG(gstreamer) -#include <private/qgstreamerintegration_p.h> -using PlatformIntegration = QGstreamerIntegration; -#elif QT_CONFIG(pulseaudio) -#include <private/qpulseaudiointegration_p.h> -using PlatformIntegration = QPulseAudioIntegration; -#elif QT_CONFIG(alsa) -#include <private/qalsaintegration_p.h> -using PlatformIntegration = QAlsaIntegration; -#elif QT_CONFIG(avfoundation) -#include <private/qdarwinintegration_p.h> -using PlatformIntegration = QDarwinIntegration; -#elif QT_CONFIG(wmf) -#include <private/qwindowsintegration_p.h> -using PlatformIntegration = QWindowsMediaIntegration; -#elif defined(Q_OS_ANDROID) -#include <private/qandroidintegration_p.h> -using PlatformIntegration = QAndroidIntegration; -#elif defined(Q_OS_WASM) -#include <private/qwasmmediaintegration_p.h> -using PlatformIntegration = QWasmMediaIntegration; -#else -class QDummyIntegration : public QPlatformMediaIntegration +#include "qplatformcapturablewindows_p.h" +#include "qplatformmediadevices_p.h" +#include <QtCore/private/qfactoryloader_p.h> +#include <QtCore/private/qcoreapplication_p.h> +#include <private/qplatformmediaformatinfo_p.h> +#include "qplatformmediaplugin_p.h" + +namespace { + +class QFallbackIntegration : public QPlatformMediaIntegration { public: - QDummyIntegration() { qFatal("QtMultimedia is not currently supported on this platform or compiler."); } - QPlatformMediaDevices *devices() override { return nullptr; } - QPlatformMediaFormatInfo *formatInfo() override { return nullptr; } + QFallbackIntegration() : QPlatformMediaIntegration(QLatin1String("fallback")) + { + qWarning("No QtMultimedia backends found. Only QMediaDevices, QAudioDevice, QSoundEffect, QAudioSink, and QAudioSource are available."); + } }; -using PlatformIntegration = QDummyIntegration; + +static Q_LOGGING_CATEGORY(qLcMediaPlugin, "qt.multimedia.plugin") + +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QPlatformMediaPlugin_iid, + QLatin1String("/multimedia"))) + +static const auto FFmpegBackend = QStringLiteral("ffmpeg"); + +static QString defaultBackend(const QStringList &backends) +{ +#ifdef QT_DEFAULT_MEDIA_BACKEND + auto backend = QString::fromUtf8(QT_DEFAULT_MEDIA_BACKEND); + if (backends.contains(backend)) + return backend; #endif -QT_BEGIN_NAMESPACE +#if defined(Q_OS_DARWIN) || defined(Q_OS_LINUX) || defined(Q_OS_WINDOWS) || defined(Q_OS_ANDROID) + // Return ffmpeg backend by default. + // Platform backends for the OS list are optionally available but have limited support. + if (backends.contains(FFmpegBackend)) + return FFmpegBackend; +#else + // Return platform backend (non-ffmpeg) by default. + if (backends.size() > 1 && backends[0] == FFmpegBackend) + return backends[1]; +#endif -namespace { -struct Holder { - ~Holder() + return backends[0]; +} + +struct InstanceHolder +{ + InstanceHolder() { - QMutexLocker locker(&mutex); - delete nativeInstance; - nativeInstance = nullptr; - instance = nullptr; + if (!QCoreApplication::instance()) + qCCritical(qLcMediaPlugin()) << "Qt Multimedia requires a QCoreApplication instance"; + + const QStringList backends = QPlatformMediaIntegration::availableBackends(); + QString backend = QString::fromUtf8(qgetenv("QT_MEDIA_BACKEND")); + if (backend.isEmpty() && !backends.isEmpty()) + backend = defaultBackend(backends); + + qCDebug(qLcMediaPlugin) << "Loading media backend" << backend; + instance.reset( + qLoadPlugin<QPlatformMediaIntegration, QPlatformMediaPlugin>(loader(), backend)); + + if (!instance) { + // No backends found. Use fallback to support basic functionality + instance = std::make_unique<QFallbackIntegration>(); + } } - QBasicMutex mutex; - QPlatformMediaIntegration *instance = nullptr; - QAtomicPointer<QPlatformMediaIntegration> nativeInstance = nullptr; -} holder; -} + ~InstanceHolder() + { + instance.reset(); + qCDebug(qLcMediaPlugin) << "Released media backend"; + } + + std::unique_ptr<QPlatformMediaIntegration> instance; +}; + +Q_APPLICATION_STATIC(InstanceHolder, s_instanceHolder); + +} // namespace + +QT_BEGIN_NAMESPACE QPlatformMediaIntegration *QPlatformMediaIntegration::instance() { - if (!holder.nativeInstance.loadRelaxed()) { - QMutexLocker locker(&holder.mutex); - if (!holder.nativeInstance.loadAcquire()) - holder.nativeInstance.storeRelease(new PlatformIntegration); - } - if (!holder.instance) - holder.instance = holder.nativeInstance.loadRelaxed(); - return holder.instance; + return s_instanceHolder->instance.get(); } -/* - This API is there to be able to test with a mock backend. -*/ -void QPlatformMediaIntegration::setIntegration(QPlatformMediaIntegration *integration) +QList<QCameraDevice> QPlatformMediaIntegration::videoInputs() { - holder.instance = integration; + auto devices = videoDevices(); + return devices ? devices->videoDevices() : QList<QCameraDevice>{}; } -QPlatformAudioInput *QPlatformMediaIntegration::createAudioInput(QAudioInput *q) +QMaybe<std::unique_ptr<QPlatformAudioResampler>> +QPlatformMediaIntegration::createAudioResampler(const QAudioFormat &, const QAudioFormat &) +{ + return notAvailable; +} + +QMaybe<QPlatformAudioInput *> QPlatformMediaIntegration::createAudioInput(QAudioInput *q) { return new QPlatformAudioInput(q); } -QPlatformAudioOutput *QPlatformMediaIntegration::createAudioOutput(QAudioOutput *q) +QMaybe<QPlatformAudioOutput *> QPlatformMediaIntegration::createAudioOutput(QAudioOutput *q) { return new QPlatformAudioOutput(q); } -QPlatformMediaIntegration::~QPlatformMediaIntegration() -= default; +QList<QCapturableWindow> QPlatformMediaIntegration::capturableWindowsList() +{ + const auto capturableWindows = this->capturableWindows(); + return capturableWindows ? capturableWindows->windows() : QList<QCapturableWindow>{}; +} + +bool QPlatformMediaIntegration::isCapturableWindowValid(const QCapturableWindowPrivate &window) +{ + const auto capturableWindows = this->capturableWindows(); + return capturableWindows && capturableWindows->isWindowValid(window); +} + +const QPlatformMediaFormatInfo *QPlatformMediaIntegration::formatInfo() +{ + std::call_once(m_formatInfoOnceFlg, [this]() { + m_formatInfo.reset(createFormatInfo()); + Q_ASSERT(m_formatInfo); + }); + return m_formatInfo.get(); +} + +QPlatformMediaFormatInfo *QPlatformMediaIntegration::createFormatInfo() +{ + return new QPlatformMediaFormatInfo; +} + +std::unique_ptr<QPlatformMediaDevices> QPlatformMediaIntegration::createMediaDevices() +{ + return QPlatformMediaDevices::create(); +} + +// clang-format off +QPlatformVideoDevices *QPlatformMediaIntegration::videoDevices() +{ + std::call_once(m_videoDevicesOnceFlag, + [this]() { + m_videoDevices.reset(createVideoDevices()); + }); + return m_videoDevices.get(); +} + +QPlatformCapturableWindows *QPlatformMediaIntegration::capturableWindows() +{ + std::call_once(m_capturableWindowsOnceFlag, + [this]() { + m_capturableWindows.reset(createCapturableWindows()); + }); + return m_capturableWindows.get(); +} + +QPlatformMediaDevices *QPlatformMediaIntegration::mediaDevices() +{ + std::call_once(m_mediaDevicesOnceFlag, [this] { + m_mediaDevices = createMediaDevices(); + }); + return m_mediaDevices.get(); +} + +// clang-format on + +QStringList QPlatformMediaIntegration::availableBackends() +{ + QStringList list; + + if (QFactoryLoader *fl = loader()) { + const auto keyMap = fl->keyMap(); + for (auto it = keyMap.constBegin(); it != keyMap.constEnd(); ++it) + if (!list.contains(it.value())) + list << it.value(); + } + + qCDebug(qLcMediaPlugin) << "Available backends" << list; + return list; +} + +QLatin1String QPlatformMediaIntegration::name() +{ + return m_backendName; +} + +QVideoFrame QPlatformMediaIntegration::convertVideoFrame(QVideoFrame &, + const QVideoFrameFormat &) +{ + return {}; +} + +QPlatformMediaIntegration::QPlatformMediaIntegration(QLatin1String name) : m_backendName(name) { } + +QPlatformMediaIntegration::~QPlatformMediaIntegration() = default; QT_END_NAMESPACE + +#include "moc_qplatformmediaintegration_p.cpp" diff --git a/src/multimedia/platform/qplatformmediaintegration_p.h b/src/multimedia/platform/qplatformmediaintegration_p.h index 67938d910..d03d0c794 100644 --- a/src/multimedia/platform/qplatformmediaintegration_p.h +++ b/src/multimedia/platform/qplatformmediaintegration_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPLATFORMMEDIAINTEGRATION_H #define QPLATFORMMEDIAINTEGRATION_H @@ -51,13 +15,21 @@ // #include <private/qtmultimediaglobal_p.h> +#include <private/qmultimediautils_p.h> +#include <qcapturablewindow.h> #include <qmediarecorder.h> +#include <qstring.h> + +#include <memory> +#include <mutex> QT_BEGIN_NAMESPACE class QMediaPlayer; class QAudioDecoder; class QCamera; +class QScreenCapture; +class QWindowCapture; class QMediaRecorder; class QImageCapture; class QMediaDevices; @@ -65,8 +37,10 @@ class QPlatformMediaDevices; class QPlatformMediaCaptureSession; class QPlatformMediaPlayer; class QPlatformAudioDecoder; +class QPlatformAudioResampler; class QPlatformCamera; -class QPlatformMediaEncoder; +class QPlatformSurfaceCapture; +class QPlatformMediaRecorder; class QPlatformImageCapture; class QPlatformMediaFormatInfo; class QObject; @@ -76,30 +50,87 @@ class QAudioInput; class QAudioOutput; class QPlatformAudioInput; class QPlatformAudioOutput; +class QPlatformVideoDevices; +class QCapturableWindow; +class QPlatformCapturableWindows; +class QVideoFrame; -class Q_MULTIMEDIA_EXPORT QPlatformMediaIntegration +class Q_MULTIMEDIA_EXPORT QAbstractPlatformSpecificInterface { public: - static QPlatformMediaIntegration *instance(); + virtual ~QAbstractPlatformSpecificInterface() = default; +}; - // API to be able to test with a mock backend - static void setIntegration(QPlatformMediaIntegration *); +class Q_MULTIMEDIA_EXPORT QPlatformMediaIntegration : public QObject +{ + Q_OBJECT + inline static const QString notAvailable = QStringLiteral("Not available"); +public: + static QPlatformMediaIntegration *instance(); + explicit QPlatformMediaIntegration(QLatin1String); virtual ~QPlatformMediaIntegration(); - virtual QPlatformMediaDevices *devices() = 0; - virtual QPlatformMediaFormatInfo *formatInfo() = 0; + const QPlatformMediaFormatInfo *formatInfo(); + + virtual QList<QCameraDevice> videoInputs(); + virtual QMaybe<QPlatformCamera *> createCamera(QCamera *) { return notAvailable; } + virtual QPlatformSurfaceCapture *createScreenCapture(QScreenCapture *) { return nullptr; } + virtual QPlatformSurfaceCapture *createWindowCapture(QWindowCapture *) { return nullptr; } + + virtual QMaybe<QPlatformAudioDecoder *> createAudioDecoder(QAudioDecoder *) { return notAvailable; } + virtual QMaybe<std::unique_ptr<QPlatformAudioResampler>> + createAudioResampler(const QAudioFormat & /*inputFormat*/, + const QAudioFormat & /*outputFormat*/); + virtual QMaybe<QPlatformMediaCaptureSession *> createCaptureSession() { return notAvailable; } + virtual QMaybe<QPlatformMediaPlayer *> createPlayer(QMediaPlayer *) { return notAvailable; } + virtual QMaybe<QPlatformMediaRecorder *> createRecorder(QMediaRecorder *) { return notAvailable; } + virtual QMaybe<QPlatformImageCapture *> createImageCapture(QImageCapture *) { return notAvailable; } + + virtual QMaybe<QPlatformAudioInput *> createAudioInput(QAudioInput *); + virtual QMaybe<QPlatformAudioOutput *> createAudioOutput(QAudioOutput *); + + virtual QMaybe<QPlatformVideoSink *> createVideoSink(QVideoSink *) { return notAvailable; } + + QList<QCapturableWindow> capturableWindowsList(); + bool isCapturableWindowValid(const QCapturableWindowPrivate &); + + QPlatformVideoDevices *videoDevices(); + + QPlatformCapturableWindows *capturableWindows(); + + QPlatformMediaDevices *mediaDevices(); + + static QStringList availableBackends(); + QLatin1String name(); // for unit tests + + // Convert a QVideoFrame to the destination format + virtual QVideoFrame convertVideoFrame(QVideoFrame &, const QVideoFrameFormat &); + + virtual QAbstractPlatformSpecificInterface *platformSpecificInterface() { return nullptr; } + +protected: + virtual QPlatformMediaFormatInfo *createFormatInfo(); + + virtual QPlatformVideoDevices *createVideoDevices() { return nullptr; } + + virtual QPlatformCapturableWindows *createCapturableWindows() { return nullptr; } + + virtual std::unique_ptr<QPlatformMediaDevices> createMediaDevices(); + +private: + std::unique_ptr<QPlatformVideoDevices> m_videoDevices; + std::once_flag m_videoDevicesOnceFlag; + + std::unique_ptr<QPlatformCapturableWindows> m_capturableWindows; + std::once_flag m_capturableWindowsOnceFlag; - virtual QPlatformAudioDecoder *createAudioDecoder(QAudioDecoder *) { return nullptr; } - virtual QPlatformMediaCaptureSession *createCaptureSession() { return nullptr; } - virtual QPlatformMediaPlayer *createPlayer(QMediaPlayer *) { return nullptr; } - virtual QPlatformCamera *createCamera(QCamera *) { return nullptr; } - virtual QPlatformMediaEncoder *createEncoder(QMediaRecorder *) { return nullptr; } - virtual QPlatformImageCapture *createImageCapture(QImageCapture *) { return nullptr; } + mutable std::unique_ptr<QPlatformMediaFormatInfo> m_formatInfo; + mutable std::once_flag m_formatInfoOnceFlg; - virtual QPlatformAudioInput *createAudioInput(QAudioInput *); - virtual QPlatformAudioOutput *createAudioOutput(QAudioOutput *); + std::unique_ptr<QPlatformMediaDevices> m_mediaDevices; + std::once_flag m_mediaDevicesOnceFlag; - virtual QPlatformVideoSink *createVideoSink(QVideoSink *) { return nullptr; } + const QLatin1String m_backendName; }; QT_END_NAMESPACE diff --git a/src/multimedia/platform/qplatformmediaplayer.cpp b/src/multimedia/platform/qplatformmediaplayer.cpp index 21e25ec9b..ea22f94df 100644 --- a/src/multimedia/platform/qplatformmediaplayer.cpp +++ b/src/multimedia/platform/qplatformmediaplayer.cpp @@ -1,92 +1,23 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qplatformmediaplayer_p.h" #include <private/qmediaplayer_p.h> #include "qmediaplayer.h" +#include "qplatformmediadevices_p.h" +#include "qplatformmediaintegration_p.h" QT_BEGIN_NAMESPACE - -/*! - \class QPlatformMediaPlayer - \internal - - \brief The QPlatformMediaPlayer class provides access to the media playing - functionality. - - This control provides a means to set the \l {setMedia()}{media} to play, - \l {play()}{start}, \l {pause()} {pause} and \l {stop()}{stop} playback, - \l {setPosition()}{seek}, and control the \l {setVolume()}{volume}. - It also provides feedback on the \l {duration()}{duration} of the media, - the current \l {position()}{position}, and \l {bufferProgress()}{buffering} - progress. - - The functionality provided by this control is exposed to application - code through the QMediaPlayer class. - - \sa QMediaPlayer -*/ +QPlatformMediaPlayer::QPlatformMediaPlayer(QMediaPlayer *parent) : player(parent) +{ + QPlatformMediaIntegration::instance()->mediaDevices()->prepareAudio(); +} QPlatformMediaPlayer::~QPlatformMediaPlayer() { } -/*! \fn QPlatformMediaPlayer::QPlatformMediaPlayer(QMediaPlayer *parent) - - Constructs a new media player control with the given \a parent. -*/ - -/*! - \fn QPlatformMediaPlayer::state() const - - Returns the state of a player control. -*/ - -/*! - \fn QPlatformMediaPlayer::stateChanged(QMediaPlayer::State newState) - - Signals that the state of a player control has changed to \a newState. - - \sa state() -*/ - void QPlatformMediaPlayer::stateChanged(QMediaPlayer::PlaybackState newState) { if (newState == m_state) @@ -95,20 +26,6 @@ void QPlatformMediaPlayer::stateChanged(QMediaPlayer::PlaybackState newState) player->d_func()->setState(newState); } -/*! - \fn QPlatformMediaPlayer::mediaStatus() const - - Returns the status of the current media. -*/ - - -/*! - \fn QPlatformMediaPlayer::mediaStatusChanged(QMediaPlayer::MediaStatus status) - - Signals that the \a status of the current media has changed. - - \sa mediaStatus() -*/ void QPlatformMediaPlayer::mediaStatusChanged(QMediaPlayer::MediaStatus status) { if (m_status == status) @@ -119,254 +36,19 @@ void QPlatformMediaPlayer::mediaStatusChanged(QMediaPlayer::MediaStatus status) void QPlatformMediaPlayer::error(int error, const QString &errorString) { - player->d_func()->setError(error, errorString); + player->d_func()->setError(QMediaPlayer::Error(error), errorString); } -/*! - \fn QPlatformMediaPlayer::duration() const - - Returns the duration of the current media in milliseconds. -*/ - -/*! - \fn QPlatformMediaPlayer::durationChanged(qint64 duration) - - Signals that the \a duration of the current media has changed. - - \sa duration() -*/ - -/*! - \fn QPlatformMediaPlayer::position() const - - Returns the current playback position in milliseconds. -*/ - -/*! - \fn QPlatformMediaPlayer::setPosition(qint64 position) - - Sets the playback \a position of the current media. This will initiate a seek and it may take - some time for playback to reach the position set. -*/ - -/*! - \fn QPlatformMediaPlayer::positionChanged(qint64 position) - - Signals the playback \a position has changed. - - This is only emitted in when there has been a discontinous change in the playback postion, such - as a seek or the position being reset. - - \sa position() -*/ - -/*! - \fn QPlatformMediaPlayer::volume() const - - Returns the audio volume of a player control. -*/ - -/*! - \fn QPlatformMediaPlayer::setVolume(int volume) - - Sets the audio \a volume of a player control. - - The volume is scaled linearly, ranging from \c 0 (silence) to \c 100 (full volume). -*/ - -/*! - \fn QPlatformMediaPlayer::volumeChanged(int volume) - - Signals the audio \a volume of a player control has changed. - - \sa volume() -*/ - -/*! - \fn QPlatformMediaPlayer::isMuted() const - - Returns the mute state of a player control. -*/ - -/*! - \fn QPlatformMediaPlayer::setMuted(bool mute) - - Sets the \a mute state of a player control. -*/ - -/*! - \fn QPlatformMediaPlayer::mutedChanged(bool mute) - - Signals a change in the \a mute status of a player control. - - \sa isMuted() -*/ - -/*! - \fn QPlatformMediaPlayer::bufferProgress() const - - Returns the buffering progress of the current media. Progress is measured as a number between - 0 and 1. -*/ - -/*! - \fn QPlatformMediaPlayer::bufferProgressChanged(float filled) - - Signal the amount of the local buffer filled as a relative number between 0 and 1. - - \sa bufferProgress() -*/ - -/*! - \fn QPlatformMediaPlayer::isAudioAvailable() const - - Identifies if there is audio output available for the current media. - - Returns true if audio output is available and false otherwise. -*/ - -/*! - \fn QPlatformMediaPlayer::audioAvailableChanged(bool audioAvailable) - - Signals that there has been a change in the availability of audio output \a audioAvailable. - - \sa isAudioAvailable() -*/ - -/*! - \fn QPlatformMediaPlayer::isVideoAvailable() const - - Identifies if there is video output available for the current media. - - Returns true if video output is available and false otherwise. -*/ - -/*! - \fn QPlatformMediaPlayer::videoAvailableChanged(bool videoAvailable) - - Signal that the availability of visual content has changed to \a videoAvailable. - - \sa isVideoAvailable() -*/ - -/*! - \fn QPlatformMediaPlayer::isSeekable() const - - Identifies if the current media is seekable. - - Returns true if it possible to seek within the current media, and false otherwise. -*/ - -/*! - \fn QPlatformMediaPlayer::seekableChanged(bool seekable) - - Signals that the \a seekable state of a player control has changed. - - \sa isSeekable() -*/ - -/*! - \fn QPlatformMediaPlayer::availablePlaybackRanges() const - - Returns a range of times in milliseconds that can be played back. - - Usually for local files this is a continuous interval equal to [0..duration()] - or an empty time range if seeking is not supported, but for network sources - it refers to the buffered parts of the media. -*/ - -/*! - \fn qreal QPlatformMediaPlayer::playbackRate() const - - Returns the rate of playback. -*/ - -/*! - \fn QPlatformMediaPlayer::setPlaybackRate(qreal rate) - - Sets the \a rate of playback. -*/ - -/*! - \fn QPlatformMediaPlayer::media() const - - Returns the current media source. -*/ - -/*! - \fn QPlatformMediaPlayer::mediaStream() const - - Returns the current media stream. This is only a valid if a stream was passed to setMedia(). - - \sa setMedia() -*/ - -/*! - \fn QPlatformMediaPlayer::setMedia(const QUrl &media, QIODevice *stream) - - Sets the current \a media source. If a \a stream is supplied; data will be read from that - instead of attempting to resolve the media source. The media source may still be used to - supply media information such as mime type. - - Setting the media to a null QUrl will cause the control to discard all - information relating to the current media source and to cease all I/O operations related - to that media. - - Qt resource files are never passed as is. If the control supports - stream playback, a \a stream is supplied, pointing to an opened - QFile. Otherwise, the resource is copied into a temporary file and \a media contains the - url to that file. - - \sa streamPlaybackSupported() -*/ - -/*! - \fn QPlatformMediaPlayer::mediaChanged(const QUrl& content) - - Signals that the current media \a content has changed. -*/ - -/*! - \fn QPlatformMediaPlayer::play() - - Starts playback of the current media. - - If successful the player control will immediately enter the \l {QMediaPlayer::PlayingState} - {playing} state. - - \sa state() -*/ - -/*! - \fn QPlatformMediaPlayer::pause() - - Pauses playback of the current media. - - If successful the player control will immediately enter the \l {QMediaPlayer::PausedState} - {paused} state. - - \sa state(), play(), stop() -*/ - -/*! - \fn QPlatformMediaPlayer::stop() - - Stops playback of the current media. - - If successful the player control will immediately enter the \l {QMediaPlayer::StoppedState} - {stopped} state. -*/ - -/*! - \fn QPlatformMediaPlayer::error(int error, const QString &errorString) - - Signals that an \a error has occurred. The \a errorString provides a more detailed explanation. -*/ +void *QPlatformMediaPlayer::nativePipeline(QMediaPlayer *player) +{ + if (!player) + return nullptr; -/*! - \fn QPlatformMediaPlayer::playbackRateChanged(qreal rate) + auto playerPrivate = player->d_func(); + if (!playerPrivate || !playerPrivate->control) + return nullptr; - Signal emitted when playback rate changes to \a rate. -*/ + return playerPrivate->control->nativePipeline(); +} QT_END_NAMESPACE diff --git a/src/multimedia/platform/qplatformmediaplayer_p.h b/src/multimedia/platform/qplatformmediaplayer_p.h index cc9297229..6e3590763 100644 --- a/src/multimedia/platform/qplatformmediaplayer_p.h +++ b/src/multimedia/platform/qplatformmediaplayer_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // // W A R N I N G @@ -57,6 +21,7 @@ #include <QtMultimedia/qmediametadata.h> #include <QtCore/qpair.h> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -72,7 +37,7 @@ public: virtual qint64 duration() const = 0; - virtual qint64 position() const = 0; + virtual qint64 position() const { return m_position; } virtual void setPosition(qint64 position) = 0; virtual float bufferProgress() const = 0; @@ -111,40 +76,62 @@ public: virtual int activeTrack(TrackType) { return -1; } virtual void setActiveTrack(TrackType, int /*streamNumber*/) {} - void durationChanged(qint64 duration) { player->durationChanged(duration); } - void positionChanged(qint64 position) { player->positionChanged(position); } + void durationChanged(qint64 duration) { emit player->durationChanged(duration); } + void positionChanged(qint64 position) { + if (m_position == position) + return; + m_position = position; + emit player->positionChanged(position); + } void audioAvailableChanged(bool audioAvailable) { if (m_audioAvailable == audioAvailable) return; m_audioAvailable = audioAvailable; - player->hasAudioChanged(audioAvailable); + emit player->hasAudioChanged(audioAvailable); } void videoAvailableChanged(bool videoAvailable) { if (m_videoAvailable == videoAvailable) return; m_videoAvailable = videoAvailable; - player->hasVideoChanged(videoAvailable); + emit player->hasVideoChanged(videoAvailable); } void seekableChanged(bool seekable) { if (m_seekable == seekable) return; m_seekable = seekable; - player->seekableChanged(seekable); + emit player->seekableChanged(seekable); } - void playbackRateChanged(qreal rate) { player->playbackRateChanged(rate); } - void bufferProgressChanged(float progress) { player->bufferProgressChanged(progress); } - void metaDataChanged() { player->metaDataChanged(); } - void tracksChanged() { player->tracksChanged(); } - void activeTracksChanged() { player->activeTracksChanged(); } + void playbackRateChanged(qreal rate) { emit player->playbackRateChanged(rate); } + void bufferProgressChanged(float progress) { emit player->bufferProgressChanged(progress); } + void metaDataChanged() { emit player->metaDataChanged(); } + void tracksChanged() { emit player->tracksChanged(); } + void activeTracksChanged() { emit player->activeTracksChanged(); } void stateChanged(QMediaPlayer::PlaybackState newState); void mediaStatusChanged(QMediaPlayer::MediaStatus status); void error(int error, const QString &errorString); + void resetCurrentLoop() { m_currentLoop = 0; } + bool doLoop() { + return isSeekable() && (m_loops < 0 || ++m_currentLoop < m_loops); + } + int loops() { return m_loops; } + virtual void setLoops(int loops) + { + if (m_loops == loops) + return; + m_loops = loops; + Q_EMIT player->loopsChanged(); + } + + virtual void *nativePipeline() { return nullptr; } + + // private API, the purpose is getting GstPipeline + static void *nativePipeline(QMediaPlayer *player); + protected: - explicit QPlatformMediaPlayer(QMediaPlayer *parent = nullptr) - : player(parent) - {} + explicit QPlatformMediaPlayer(QMediaPlayer *parent = nullptr); + private: QMediaPlayer *player = nullptr; QMediaPlayer::MediaStatus m_status = QMediaPlayer::NoMedia; @@ -152,6 +139,9 @@ private: bool m_seekable = false; bool m_videoAvailable = false; bool m_audioAvailable = false; + int m_loops = 1; + int m_currentLoop = 0; + qint64 m_position = 0; }; QT_END_NAMESPACE diff --git a/src/multimedia/platform/qplatformmediaplugin.cpp b/src/multimedia/platform/qplatformmediaplugin.cpp new file mode 100644 index 000000000..7828fa08e --- /dev/null +++ b/src/multimedia/platform/qplatformmediaplugin.cpp @@ -0,0 +1,14 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qplatformmediaplugin_p.h" + +QT_BEGIN_NAMESPACE + +QPlatformMediaPlugin::QPlatformMediaPlugin(QObject *parent) : QObject(parent) { } + +QPlatformMediaPlugin::~QPlatformMediaPlugin() = default; + +QT_END_NAMESPACE + +#include "moc_qplatformmediaplugin_p.cpp" diff --git a/src/multimedia/platform/qplatformmediaplugin_p.h b/src/multimedia/platform/qplatformmediaplugin_p.h new file mode 100644 index 000000000..4c8b9e458 --- /dev/null +++ b/src/multimedia/platform/qplatformmediaplugin_p.h @@ -0,0 +1,42 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QPLATFORMMEDIAPLUGIN_P_H +#define QPLATFORMMEDIAPLUGIN_P_H + +#include <QtMultimedia/qtmultimediaglobal.h> +#include <QtCore/qplugin.h> +#include <QtCore/qfactoryinterface.h> +#include <QtCore/private/qglobal_p.h> + +QT_BEGIN_NAMESPACE + +class QPlatformMediaIntegration; + +#define QPlatformMediaPlugin_iid "org.qt-project.Qt.QPlatformMediaPlugin" + +class Q_MULTIMEDIA_EXPORT QPlatformMediaPlugin : public QObject +{ + Q_OBJECT +public: + explicit QPlatformMediaPlugin(QObject *parent = nullptr); + ~QPlatformMediaPlugin() override; + + virtual QPlatformMediaIntegration *create(const QString &key) = 0; + +}; + +QT_END_NAMESPACE + +#endif // QPLATFORMMEDIAPLUGIN_P_H diff --git a/src/multimedia/platform/qplatformmediarecorder.cpp b/src/multimedia/platform/qplatformmediarecorder.cpp new file mode 100644 index 000000000..30dba0a45 --- /dev/null +++ b/src/multimedia/platform/qplatformmediarecorder.cpp @@ -0,0 +1,75 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qplatformmediarecorder_p.h" +#include "qstandardpaths.h" +#include "qmediastoragelocation_p.h" +#include <QObject> + +QT_BEGIN_NAMESPACE + +QPlatformMediaRecorder::QPlatformMediaRecorder(QMediaRecorder *parent) + : q(parent) +{ +} + +void QPlatformMediaRecorder::pause() +{ + updateError(QMediaRecorder::FormatError, QMediaRecorder::tr("Pause not supported")); +} + +void QPlatformMediaRecorder::resume() +{ + updateError(QMediaRecorder::FormatError, QMediaRecorder::tr("Resume not supported")); +} + +void QPlatformMediaRecorder::stateChanged(QMediaRecorder::RecorderState state) +{ + if (m_state == state) + return; + m_state = state; + emit q->recorderStateChanged(state); +} + +void QPlatformMediaRecorder::durationChanged(qint64 duration) +{ + if (m_duration == duration) + return; + m_duration = duration; + emit q->durationChanged(duration); +} + +void QPlatformMediaRecorder::actualLocationChanged(const QUrl &location) +{ + if (m_actualLocation == location) + return; + m_actualLocation = location; + emit q->actualLocationChanged(location); +} + +void QPlatformMediaRecorder::updateError(QMediaRecorder::Error error, const QString &errorString) +{ + m_error.setAndNotify(error, errorString, *q); +} + +void QPlatformMediaRecorder::metaDataChanged() +{ + emit q->metaDataChanged(); +} + +QString QPlatformMediaRecorder::findActualLocation(const QMediaEncoderSettings &settings) const +{ + const auto audioOnly = settings.videoCodec() == QMediaFormat::VideoCodec::Unspecified; + + const auto primaryLocation = + audioOnly ? QStandardPaths::MusicLocation : QStandardPaths::MoviesLocation; + const QString suffix = settings.mimeType().preferredSuffix(); + const QString location = QMediaStorageLocation::generateFileName( + outputLocation().toString(QUrl::PreferLocalFile), primaryLocation, suffix); + + Q_ASSERT(!location.isEmpty()); + + return location; +} + +QT_END_NAMESPACE diff --git a/src/multimedia/platform/qplatformmediaencoder_p.h b/src/multimedia/platform/qplatformmediarecorder_p.h index 354998b5c..dea45ac70 100644 --- a/src/multimedia/platform/qplatformmediaencoder_p.h +++ b/src/multimedia/platform/qplatformmediarecorder_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // // W A R N I N G @@ -48,16 +12,20 @@ // We mean it. // -#ifndef QPLATFORMMEDIAENCODER_H -#define QPLATFORMMEDIAENCODER_H +#ifndef QPLATFORMMEDIARECORDER_H +#define QPLATFORMMEDIARECORDER_H #include <QtCore/qurl.h> #include <QtCore/qsize.h> #include <QtCore/qmimetype.h> +#include <QtCore/qpointer.h> +#include <QtCore/qiodevice.h> #include <QtMultimedia/qmediarecorder.h> #include <QtMultimedia/qmediametadata.h> #include <QtMultimedia/qmediaformat.h> +#include <QtMultimedia/private/qerrorinfo_p.h> +#include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -132,10 +100,10 @@ public: { return !operator==(other); } }; -class Q_MULTIMEDIA_EXPORT QPlatformMediaEncoder +class Q_MULTIMEDIA_EXPORT QPlatformMediaRecorder { public: - virtual ~QPlatformMediaEncoder() {} + virtual ~QPlatformMediaRecorder() {} virtual bool isLocationWritable(const QUrl &location) const = 0; @@ -145,35 +113,43 @@ public: virtual void resume(); virtual void stop() = 0; - virtual qint64 duration() const = 0; + virtual qint64 duration() const { return m_duration; } virtual void setMetaData(const QMediaMetaData &) {} virtual QMediaMetaData metaData() const { return {}; } - QMediaRecorder::Error error() const { return m_error;} - QString errorString() const { return m_errorString; } + QMediaRecorder::Error error() const { return m_error.code(); } + QString errorString() const { return m_error.description(); } QUrl outputLocation() const { return m_outputLocation; } virtual void setOutputLocation(const QUrl &location) { m_outputLocation = location; } QUrl actualLocation() const { return m_actualLocation; } void clearActualLocation() { m_actualLocation.clear(); } - void clearError() { error(QMediaRecorder::NoError, QString()); } + void clearError() { updateError(QMediaRecorder::NoError, QString()); } + + QIODevice *outputDevice() const { return m_outputDevice; } + void setOutputDevice(QIODevice *device) { m_outputDevice = device; } protected: - explicit QPlatformMediaEncoder(QMediaRecorder *parent); + explicit QPlatformMediaRecorder(QMediaRecorder *parent); void stateChanged(QMediaRecorder::RecorderState state); void durationChanged(qint64 position); void actualLocationChanged(const QUrl &location); - void error(QMediaRecorder::Error error, const QString &errorString); + void updateError(QMediaRecorder::Error error, const QString &errorString); void metaDataChanged(); + QMediaRecorder *mediaRecorder() { return q; } + + QString findActualLocation(const QMediaEncoderSettings &settings) const; + private: QMediaRecorder *q = nullptr; - QMediaRecorder::Error m_error = QMediaRecorder::NoError; - QString m_errorString; + QErrorInfo<QMediaRecorder::Error> m_error; QUrl m_actualLocation; QUrl m_outputLocation; + QPointer<QIODevice> m_outputDevice; + qint64 m_duration = 0; QMediaRecorder::RecorderState m_state = QMediaRecorder::StoppedState; }; diff --git a/src/multimedia/platform/qplatformsurfacecapture.cpp b/src/multimedia/platform/qplatformsurfacecapture.cpp new file mode 100644 index 000000000..a56f48b62 --- /dev/null +++ b/src/multimedia/platform/qplatformsurfacecapture.cpp @@ -0,0 +1,83 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "platform/qplatformsurfacecapture_p.h" +#include "qvideoframe.h" +#include "qguiapplication.h" +#include "qdebug.h" + +QT_BEGIN_NAMESPACE + +QPlatformSurfaceCapture::QPlatformSurfaceCapture(Source initialSource) : m_source(initialSource) +{ + Q_ASSERT(std::visit([](auto source) { return source == decltype(source){}; }, initialSource)); + qRegisterMetaType<QVideoFrame>(); +} + +void QPlatformSurfaceCapture::setActive(bool active) +{ + if (m_active == active) + return; + + if (!setActiveInternal(active)) + return; + + m_active = active; + emit activeChanged(active); +} + +bool QPlatformSurfaceCapture::isActive() const +{ + return m_active; +} + +void QPlatformSurfaceCapture::setSource(Source source) +{ + Q_ASSERT(source.index() == m_source.index()); + + if (m_source == source) + return; + + if (m_active) + setActiveInternal(false); + + m_source = source; + + if (m_active && !setActiveInternal(true)) { + m_active = false; + emit activeChanged(false); + } + + std::visit([this](auto source) { emit sourceChanged(source); }, m_source); +} + +QPlatformSurfaceCapture::Error QPlatformSurfaceCapture::error() const +{ + return m_error.code(); +} + +QString QPlatformSurfaceCapture::errorString() const +{ + return m_error.description(); +} + +void QPlatformSurfaceCapture::updateError(Error error, const QString &errorString) +{ + m_error.setAndNotify(error, errorString, *this); +} + +bool QPlatformSurfaceCapture::checkScreenWithError(ScreenSource &screen) +{ + if (!screen) + screen = QGuiApplication::primaryScreen(); + + if (screen) + return true; + + updateError(NotFound, QLatin1String("No screens found")); + return false; +} + +QT_END_NAMESPACE + +#include "moc_qplatformsurfacecapture_p.cpp" diff --git a/src/multimedia/platform/qplatformsurfacecapture_p.h b/src/multimedia/platform/qplatformsurfacecapture_p.h new file mode 100644 index 000000000..e4c59c6f4 --- /dev/null +++ b/src/multimedia/platform/qplatformsurfacecapture_p.h @@ -0,0 +1,87 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QPLATFORMSURFACECAPTURE_H +#define QPLATFORMSURFACECAPTURE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qplatformvideosource_p.h" +#include "qscreen.h" +#include "qcapturablewindow.h" +#include "qpointer.h" +#include "private/qerrorinfo_p.h" + +#include <optional> +#include <variant> + +QT_BEGIN_NAMESPACE + +class QVideoFrame; + +class Q_MULTIMEDIA_EXPORT QPlatformSurfaceCapture : public QPlatformVideoSource +{ + Q_OBJECT + +public: + enum Error { + NoError = 0, + InternalError = 1, + CapturingNotSupported = 2, + CaptureFailed = 4, + NotFound = 5, + }; + + using ScreenSource = QPointer<QScreen>; + using WindowSource = QCapturableWindow; + + using Source = std::variant<ScreenSource, WindowSource>; + + explicit QPlatformSurfaceCapture(Source initialSource); + + void setActive(bool active) override; + bool isActive() const override; + + void setSource(Source source); + + template<typename Type> + Type source() const { + return *q_check_ptr(std::get_if<Type>(&m_source)); + } + + Source source() const { return m_source; } + + Error error() const; + QString errorString() const final; + +protected: + virtual bool setActiveInternal(bool) = 0; + + bool checkScreenWithError(ScreenSource &screen); + +public Q_SLOTS: + void updateError(Error error, const QString &errorString); + +Q_SIGNALS: + void sourceChanged(WindowSource); + void sourceChanged(ScreenSource); + void errorOccurred(Error error, QString errorString); + +private: + QErrorInfo<Error> m_error; + Source m_source; + bool m_active = false; +}; + +QT_END_NAMESPACE + +#endif // QPLATFORMSURFACECAPTURE_H diff --git a/src/multimedia/platform/qplatformvideodevices.cpp b/src/multimedia/platform/qplatformvideodevices.cpp new file mode 100644 index 000000000..bcf664cd2 --- /dev/null +++ b/src/multimedia/platform/qplatformvideodevices.cpp @@ -0,0 +1,12 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qplatformvideodevices_p.h" + +QT_BEGIN_NAMESPACE + +QPlatformVideoDevices::~QPlatformVideoDevices() = default; + +QT_END_NAMESPACE + +#include "moc_qplatformvideodevices_p.cpp" diff --git a/src/multimedia/platform/qplatformvideodevices_p.h b/src/multimedia/platform/qplatformvideodevices_p.h new file mode 100644 index 000000000..008322be2 --- /dev/null +++ b/src/multimedia/platform/qplatformvideodevices_p.h @@ -0,0 +1,46 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#ifndef QPLATFORMVIDEODEVICES_H +#define QPLATFORMVIDEODEVICES_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qtmultimediaglobal_p.h> +#include <qmediarecorder.h> +#include <qobject.h> + +QT_BEGIN_NAMESPACE + +class QPlatformMediaIntegration; + +class Q_MULTIMEDIA_EXPORT QPlatformVideoDevices : public QObject +{ + Q_OBJECT +public: + QPlatformVideoDevices(QPlatformMediaIntegration *integration) + : m_integration(integration) + {} + + ~QPlatformVideoDevices() override; + + virtual QList<QCameraDevice> videoDevices() const = 0; + +Q_SIGNALS: + void videoInputsChanged(); + +protected: + QPlatformMediaIntegration *m_integration = nullptr; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/platform/qplatformvideosink.cpp b/src/multimedia/platform/qplatformvideosink.cpp index d3e5e24b2..abf82af0f 100644 --- a/src/multimedia/platform/qplatformvideosink.cpp +++ b/src/multimedia/platform/qplatformvideosink.cpp @@ -1,99 +1,77 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qplatformvideosink_p.h" +#include "qmultimediautils_p.h" QT_BEGIN_NAMESPACE -/*! - \class QPlatformVideoSink - \obsolete +QPlatformVideoSink::QPlatformVideoSink(QVideoSink *parent) : QObject(parent), m_sink(parent) { } - \inmodule QtMultimedia +QPlatformVideoSink::~QPlatformVideoSink() = default; - \ingroup multimedia_control - \brief The QPlatformVideoSink class provides a media control for rendering video to a window. - - QPlatformVideoSink is one of a number of possible video output controls. - - \sa QVideoWidget -*/ - -/*! - Constructs a new video window control with the given \a parent. -*/ -QPlatformVideoSink::QPlatformVideoSink(QVideoSink *parent) - : QObject(parent), - sink(parent) +QSize QPlatformVideoSink::nativeSize() const { + QMutexLocker locker(&m_mutex); + return m_nativeSize; } -/*! - \fn QPlatformVideoSink::setWinId(WId id) - - Sets the \a id of the window a video overlay end point renders to. -*/ - -/*! - \fn QPlatformVideoSink::setDisplayRect(const QRect &rect) - Sets the sub-\a rect of a window where video is displayed. -*/ - -/*! - \fn QPlatformVideoSink::setFullScreen(bool fullScreen) - - Sets whether a video overlay is a \a fullScreen overlay. -*/ +void QPlatformVideoSink::setNativeSize(QSize s) +{ + { + QMutexLocker locker(&m_mutex); + if (m_nativeSize == s) + return; + m_nativeSize = s; + } + emit m_sink->videoSizeChanged(); +} -/*! - \fn QPlatformVideoSink::nativeSize() const +void QPlatformVideoSink::setVideoFrame(const QVideoFrame &frame) +{ + bool sizeChanged = false; + + { + QMutexLocker locker(&m_mutex); + if (frame == m_currentVideoFrame) + return; + m_currentVideoFrame = frame; + m_currentVideoFrame.setSubtitleText(m_subtitleText); + const auto size = qRotatedFrameSize(frame); + if (size != m_nativeSize) { + m_nativeSize = size; + sizeChanged = true; + } + } + + // emit signals outside the mutex to avoid deadlocks on the user side + if (sizeChanged) + emit m_sink->videoSizeChanged(); + emit m_sink->videoFrameChanged(frame); +} - Returns a suggested size for the video display based on the resolution and aspect ratio of the - video. -*/ +QVideoFrame QPlatformVideoSink::currentVideoFrame() const +{ + QMutexLocker locker(&m_mutex); + return m_currentVideoFrame; +} -/*! - \fn QPlatformVideoSink::setAspectRatioMode(Qt::AspectRatioMode mode) +void QPlatformVideoSink::setSubtitleText(const QString &subtitleText) +{ + { + QMutexLocker locker(&m_mutex); + if (m_subtitleText == subtitleText) + return; + m_subtitleText = subtitleText; + } + emit m_sink->subtitleTextChanged(subtitleText); +} - Sets the aspect ratio \a mode which determines how video is scaled to the fit the display region - with respect to its aspect ratio. -*/ +QString QPlatformVideoSink::subtitleText() const +{ + QMutexLocker locker(&m_mutex); + return m_subtitleText; +} QT_END_NAMESPACE diff --git a/src/multimedia/platform/qplatformvideosink_p.h b/src/multimedia/platform/qplatformvideosink_p.h index 95d1aa085..53eca374f 100644 --- a/src/multimedia/platform/qplatformvideosink_p.h +++ b/src/multimedia/platform/qplatformvideosink_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPLATFORMVIDEOSINK_H #define QPLATFORMVIDEOSINK_H @@ -60,6 +24,7 @@ #include <qvideosink.h> #include <qvideoframe.h> #include <qdebug.h> +#include <private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -71,6 +36,8 @@ class Q_MULTIMEDIA_EXPORT QPlatformVideoSink : public QObject Q_OBJECT public: + ~QPlatformVideoSink() override; + virtual void setRhi(QRhi * /*rhi*/) {} virtual void setWinId(WId) {} @@ -78,54 +45,34 @@ public: virtual void setFullScreen(bool) {} virtual void setAspectRatioMode(Qt::AspectRatioMode) {} - // ### make non virtual, once Windows is ported - virtual QSize nativeSize() const - { - QMutexLocker locker(&mutex); - return m_nativeSize; - } + QSize nativeSize() const; virtual void setBrightness(float /*brightness*/) {} virtual void setContrast(float /*contrast*/) {} virtual void setHue(float /*hue*/) {} virtual void setSaturation(float /*saturation*/) {} - QVideoSink *videoSink() { return sink; } - - void setNativeSize(QSize s) { - QMutexLocker locker(&mutex); - if (m_nativeSize == s) - return; - m_nativeSize = s; - sink->videoSizeChanged(); - } - void setVideoFrame(const QVideoFrame &frame) { - setNativeSize(frame.size()); - m_currentVideoFrame = frame; - m_currentVideoFrame.setSubtitleText(subtitleText()); - sink->videoFrameChanged(m_currentVideoFrame); - } - QVideoFrame currentVideoFrame() const { return m_currentVideoFrame; } - - void setSubtitleText(const QString &subtitleText) - { - QMutexLocker locker(&mutex); - if (m_subtitleText == subtitleText) - return; - m_subtitleText = subtitleText; - sink->subtitleTextChanged(subtitleText); - } - QString subtitleText() const - { - QMutexLocker locker(&mutex); - return m_subtitleText; - } + QVideoSink *videoSink() { return m_sink; } + + void setNativeSize(QSize s); + + virtual void setVideoFrame(const QVideoFrame &frame); + + QVideoFrame currentVideoFrame() const; + + void setSubtitleText(const QString &subtitleText); + + QString subtitleText() const; protected: explicit QPlatformVideoSink(QVideoSink *parent); - QVideoSink *sink = nullptr; - mutable QMutex mutex; + +Q_SIGNALS: + void rhiChanged(QRhi *rhi); + private: + QVideoSink *m_sink = nullptr; + mutable QMutex m_mutex; QSize m_nativeSize; QString m_subtitleText; QVideoFrame m_currentVideoFrame; diff --git a/src/multimedia/platform/qplatformvideosource.cpp b/src/multimedia/platform/qplatformvideosource.cpp new file mode 100644 index 000000000..a23ed91ae --- /dev/null +++ b/src/multimedia/platform/qplatformvideosource.cpp @@ -0,0 +1,15 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qplatformvideosource_p.h" + +QT_BEGIN_NAMESPACE + +std::optional<int> QPlatformVideoSource::ffmpegHWPixelFormat() const +{ + return {}; +}; + +QT_END_NAMESPACE + +//#include "moc_qplatformvideosource_p.cpp diff --git a/src/multimedia/platform/qplatformvideosource_p.h b/src/multimedia/platform/qplatformvideosource_p.h new file mode 100644 index 000000000..b11524226 --- /dev/null +++ b/src/multimedia/platform/qplatformvideosource_p.h @@ -0,0 +1,58 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QPLATFORMVIDEOSOURCE_P_H +#define QPLATFORMVIDEOSOURCE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qvideoframeformat.h" + +#include <QtCore/qobject.h> +#include <QtCore/qnativeinterface.h> +#include <QtCore/private/qglobal_p.h> + +#include <optional> + +QT_BEGIN_NAMESPACE + +class QVideoFrame; +class QPlatformMediaCaptureSession; + +class Q_MULTIMEDIA_EXPORT QPlatformVideoSource : public QObject +{ + Q_OBJECT +public: + using QObject::QObject; + + virtual void setActive(bool active) = 0; + virtual bool isActive() const = 0; + + virtual QVideoFrameFormat frameFormat() const = 0; + + virtual std::optional<int> ffmpegHWPixelFormat() const; + + virtual void setCaptureSession(QPlatformMediaCaptureSession *) { } + + virtual QString errorString() const = 0; + + bool hasError() const { return !errorString().isEmpty(); } + +Q_SIGNALS: + void newVideoFrame(const QVideoFrame &); + void activeChanged(bool); + void errorChanged(); +}; + +QT_END_NAMESPACE + +#endif // QPLATFORMVIDEOSOURCE_P_H diff --git a/src/multimedia/platform/wasm/audio/qwasmaudiodevice.cpp b/src/multimedia/platform/wasm/audio/qwasmaudiodevice.cpp deleted file mode 100644 index 5eb6a3816..000000000 --- a/src/multimedia/platform/wasm/audio/qwasmaudiodevice.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qwasmaudiodevice_p.h" -#include <emscripten.h> -#include <AL/al.h> -#include <AL/alc.h> - -QT_BEGIN_NAMESPACE - -QWasmAudioDevice::QWasmAudioDevice(const char *device, const char *desc, bool isDef, QAudioDevice::Mode mode) - : QAudioDevicePrivate(device, mode) -{ - description = QString::fromUtf8(desc); - isDefault = isDef; - - minimumChannelCount = 1; - maximumChannelCount = 2; - minimumSampleRate = 1; - maximumSampleRate = 192'000; - - // native openAL formats - supportedSampleFormats.append(QAudioFormat::UInt8); - supportedSampleFormats.append(QAudioFormat::Int16); - - // Browsers use 32bit floats as native, but emscripten reccomends checking for the exension. - if (alIsExtensionPresent("AL_EXT_float32")) - supportedSampleFormats.append(QAudioFormat::Float); - - preferredFormat.setChannelCount(2); - - preferredFormat.setSampleRate(EM_ASM_INT({ - var AudioContext = window.AudioContext || window.webkitAudioContext; - var ctx = new AudioContext(); - var sr = ctx.sampleRate; - ctx.close(); - return sr; - })); - - auto f = QAudioFormat::Float; - - if (!supportedSampleFormats.contains(f)) - f = QAudioFormat::Int16; - preferredFormat.setSampleFormat(f); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/wasm/audio/qwasmaudiodevice_p.h b/src/multimedia/platform/wasm/audio/qwasmaudiodevice_p.h deleted file mode 100644 index fbe14e6e2..000000000 --- a/src/multimedia/platform/wasm/audio/qwasmaudiodevice_p.h +++ /dev/null @@ -1,67 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QWASMAUDIODEVICEINFO_H -#define QWASMAUDIODEVICEINFO_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qaudiodevice_p.h" - -QT_BEGIN_NAMESPACE - -class QWasmAudioDevice : public QAudioDevicePrivate -{ -public: - QWasmAudioDevice(const char *device, const char *description, bool isDefault, QAudioDevice::Mode mode); - -}; - -QT_END_NAMESPACE - -#endif // QWASMAUDIODEVICEINFO_H diff --git a/src/multimedia/platform/wasm/audio/qwasmaudiosink.cpp b/src/multimedia/platform/wasm/audio/qwasmaudiosink.cpp deleted file mode 100644 index 82456e0b6..000000000 --- a/src/multimedia/platform/wasm/audio/qwasmaudiosink.cpp +++ /dev/null @@ -1,487 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qwasmaudiosink_p.h" - - -#include <emscripten.h> -#include <AL/al.h> -#include <AL/alc.h> -#include <QDebug> -#include <QtMath> -#include <QIODevice> - -// non native al formats (AL_EXT_float32) -#define AL_FORMAT_MONO_FLOAT32 0x10010 -#define AL_FORMAT_STEREO_FLOAT32 0x10011 - -constexpr unsigned int DEFAULT_BUFFER_DURATION = 50'000; - -class ALData { -public: - ALCcontext *context = nullptr; - ALCdevice *device = nullptr; - ALuint source; - ALuint *buffers = nullptr; - ALuint *buffer = nullptr; - ALenum format; -}; - -QT_BEGIN_NAMESPACE - -class QWasmAudioSinkDevice : public QIODevice { - - QWasmAudioSink *m_out; - -public: - QWasmAudioSinkDevice(QWasmAudioSink *parent); - -protected: - qint64 readData(char *data, qint64 maxlen) override; - qint64 writeData(const char *data, qint64 len) override; -}; - -QWasmAudioSink::QWasmAudioSink(const QByteArray &device) : m_name(device) -{ - m_timer.setSingleShot(false); - aldata = new ALData(); - connect(&m_timer, &QTimer::timeout, this, [this](){ - if (m_pullMode) - nextALBuffers(); - else { - unloadALBuffers(); - m_device->write(nullptr, 0); - updateState(); - } - }); -} - -QWasmAudioSink::~QWasmAudioSink() -{ - delete aldata; - if (m_tmpData) - delete[] m_tmpData; -} - -void QWasmAudioSink::start(QIODevice *device) -{ - Q_ASSERT(device); - Q_ASSERT(device->openMode().testFlag(QIODevice::ReadOnly)); - m_device = device; - start(true); -} - -QIODevice *QWasmAudioSink::start() -{ - m_device = new QWasmAudioSinkDevice(this); - m_device->open(QIODevice::WriteOnly); - start(false); - return m_device; -} - -void QWasmAudioSink::start(bool mode) -{ - auto formatError = [this](){ - qWarning() << "Unsupported audio format " << m_format; - setError(QAudio::OpenError); - }; - switch (m_format.sampleFormat()) { - case QAudioFormat::UInt8: - switch (m_format.channelCount()) { - case 1: - aldata->format = AL_FORMAT_MONO8; - break; - case 2: - aldata->format = AL_FORMAT_STEREO8; - break; - default: - return formatError(); - } - break; - case QAudioFormat::Int16: - switch (m_format.channelCount()) { - case 1: - aldata->format = AL_FORMAT_MONO16; - break; - case 2: - aldata->format = AL_FORMAT_STEREO16; - break; - default: - return formatError(); - } - break; - case QAudioFormat::Float: - switch (m_format.channelCount()) { - case 1: - aldata->format = AL_FORMAT_MONO_FLOAT32; - break; - case 2: - aldata->format = AL_FORMAT_STEREO_FLOAT32; - break; - default: - return formatError(); - } - break; - default: - return formatError(); - } - - alGetError(); - aldata->device = alcOpenDevice(m_name.data()); - if (!aldata->device) { - qWarning() << "Failed to open audio device" << alGetString(alGetError()); - return setError(QAudio::OpenError); - } - ALint attrlist[] = {ALC_FREQUENCY, m_format.sampleRate(), 0}; - aldata->context = alcCreateContext(aldata->device, attrlist); - - if (!aldata->context) { - qWarning() << "Failed to create audio context" << alGetString(alGetError()); - return setError(QAudio::OpenError); - } - alcMakeContextCurrent(aldata->context); - - - alGenSources(1, &aldata->source); - - if (m_bufferSize > 0) - m_bufferFragmentsCount = qMax(2,qCeil((qreal)m_bufferSize/(m_bufferFragmentSize))); - m_bufferSize = m_bufferFragmentsCount * m_bufferFragmentSize; - aldata->buffers = new ALuint[m_bufferFragmentsCount]; - aldata->buffer = aldata->buffers; - alGenBuffers(m_bufferFragmentsCount, aldata->buffers); - m_processed = 0; - m_tmpDataOffset = 0; - m_pullMode = mode; - alSourcef(aldata->source, AL_GAIN, m_volume); - if (m_pullMode) - loadALBuffers(); - m_timer.setInterval(DEFAULT_BUFFER_DURATION / 3000); - m_timer.start(); - if (m_pullMode) - alSourcePlay(aldata->source); - m_running = true; - m_elapsedTimer.start(); - updateState(); -} - -void QWasmAudioSink::stop() -{ - if (!m_running) - return; - m_elapsedTimer.invalidate(); - alSourceStop(aldata->source); - alSourceRewind(aldata->source); - m_timer.stop(); - m_bufferFragmentsBusyCount = 0; - alDeleteSources(1, &aldata->source); - alDeleteBuffers(m_bufferFragmentsCount, aldata->buffers); - delete[] aldata->buffers; - alcMakeContextCurrent(nullptr); - alcDestroyContext(aldata->context); - alcCloseDevice(aldata->device); - m_running = false; - m_processed = 0; - if (!m_pullMode) - m_device->deleteLater(); - updateState(); -} - -void QWasmAudioSink::reset() -{ - stop(); - m_error = QAudio::NoError; -} - -void QWasmAudioSink::suspend() -{ - if (!m_running) - return; - - alSourcePause(aldata->source); -} - -void QWasmAudioSink::resume() -{ - if (!m_running) - return; - - alSourcePlay(aldata->source); -} - -int QWasmAudioSink::bytesFree() const -{ - int processed; - alGetSourcei(aldata->source, AL_BUFFERS_PROCESSED, &processed); - return m_running ? m_bufferFragmentSize * (m_bufferFragmentsCount - m_bufferFragmentsBusyCount - + processed) : 0; -} - -void QWasmAudioSink::setBufferSize(int value) -{ - if (m_running) - return; - - m_bufferSize = value; -} - -int QWasmAudioSink::bufferSize() const -{ - return m_bufferSize; -} - -qint64 QWasmAudioSink::processedUSecs() const -{ - int processed; - alGetSourcei(aldata->source, AL_BUFFERS_PROCESSED, &processed); - return m_format.durationForBytes(m_processed + m_format.bytesForDuration( - DEFAULT_BUFFER_DURATION * processed)); -} - -QAudio::Error QWasmAudioSink::error() const -{ - return m_error; -} - -QAudio::State QWasmAudioSink::state() const -{ - if (!m_running) - return QAudio::StoppedState; - ALint state; - alGetSourcei(aldata->source, AL_SOURCE_STATE, &state); - switch (state) { - case AL_INITIAL: - return QAudio::IdleState; - case AL_PLAYING: - return QAudio::ActiveState; - case AL_PAUSED: - return QAudio::SuspendedState; - case AL_STOPPED: - return m_running ? QAudio::IdleState : QAudio::StoppedState; - } - return QAudio::StoppedState; -} - -void QWasmAudioSink::setFormat(const QAudioFormat &fmt) -{ - if (m_running) - return; - m_format = fmt; - if (m_tmpData) - delete[] m_tmpData; - m_bufferFragmentSize = m_format.bytesForDuration(DEFAULT_BUFFER_DURATION); - m_bufferSize = m_bufferFragmentSize * m_bufferFragmentsCount; - m_tmpData = new char[m_bufferFragmentSize]; -} - -QAudioFormat QWasmAudioSink::format() const -{ - return m_format; -} - -void QWasmAudioSink::setVolume(qreal volume) -{ - if (m_volume == volume) - return; - m_volume = volume; - if (m_running) - alSourcef(aldata->source, AL_GAIN, volume); -} - -qreal QWasmAudioSink::volume() const -{ - return m_volume; -} - -void QWasmAudioSink::loadALBuffers() -{ - if (m_bufferFragmentsBusyCount == m_bufferFragmentsCount) - return; - - auto size = m_device->read(m_tmpData + m_tmpDataOffset, m_bufferFragmentSize - - m_tmpDataOffset); - m_tmpDataOffset += size; - if (!m_tmpDataOffset || (m_tmpDataOffset != m_bufferFragmentSize && - m_bufferFragmentsBusyCount >= m_bufferFragmentsCount * 2 / 3)) - return; - - alBufferData(*aldata->buffer, aldata->format, m_tmpData, m_tmpDataOffset, - m_format.sampleRate()); - m_tmpDataOffset = 0; - alGetError(); - alSourceQueueBuffers(aldata->source, 1, aldata->buffer); - if (alGetError()) - return; - - m_bufferFragmentsBusyCount++; - m_processed += size; - if (++aldata->buffer == aldata->buffers + m_bufferFragmentsCount) - aldata->buffer = aldata->buffers; -} - -void QWasmAudioSink::unloadALBuffers() -{ - int processed; - alGetSourcei(aldata->source, AL_BUFFERS_PROCESSED, &processed); - - if (processed) { - auto head = aldata->buffer - m_bufferFragmentsBusyCount; - if (head < aldata->buffers) { - if (head + processed > aldata->buffers) { - auto batch = m_bufferFragmentsBusyCount - (aldata->buffer - aldata->buffers); - alGetError(); - alSourceUnqueueBuffers(aldata->source, batch, head + m_bufferFragmentsCount); - if (!alGetError()) { - m_bufferFragmentsBusyCount -= batch; - m_processed += m_bufferFragmentSize*batch; - } - processed -= batch; - if (!processed) - return; - head = aldata->buffers; - } else { - head += m_bufferFragmentsCount; - } - } - alGetError(); - alSourceUnqueueBuffers(aldata->source, processed, head); - if (!alGetError()) - m_bufferFragmentsBusyCount -= processed; - } -} - -void QWasmAudioSink::nextALBuffers() -{ - updateState(); - unloadALBuffers(); - loadALBuffers(); - ALint state; - alGetSourcei(aldata->source, AL_SOURCE_STATE, &state); - if (state != AL_PLAYING) - alSourcePlay(aldata->source); - updateState(); -} - -void QWasmAudioSink::updateState() -{ - auto current = state(); - if (m_state == current) - return; - m_state = current; - - if (m_state == QAudio::IdleState && m_running) - setError(QAudio::UnderrunError); - - emit stateChanged(m_state); - -} - -void QWasmAudioSink::setError(QAudio::Error error) -{ - if (error == m_error) - return; - m_error = error; - emit errorChanged(error); -} - -QWasmAudioSinkDevice::QWasmAudioSinkDevice(QWasmAudioSink *parent) : QIODevice(parent), - m_out(parent) -{ -} - -qint64 QWasmAudioSinkDevice::readData(char *data, qint64 maxlen) -{ - Q_UNUSED(data) - Q_UNUSED(maxlen) - return 0; -} - - -qint64 QWasmAudioSinkDevice::writeData(const char *data, qint64 len) -{ - ALint state; - alGetSourcei(m_out->aldata->source, AL_SOURCE_STATE, &state); - if (state != AL_INITIAL) - m_out->unloadALBuffers(); - if (m_out->m_bufferFragmentsBusyCount < m_out->m_bufferFragmentsCount) { - bool exceeds = m_out->m_tmpDataOffset + len > m_out->m_bufferFragmentSize; - bool flush = m_out->m_bufferFragmentsBusyCount < m_out->m_bufferFragmentsCount * 2 / 3 || - m_out->m_tmpDataOffset + len >= m_out->m_bufferFragmentSize; - const char *read; - char *tmp = m_out->m_tmpData; - int size = 0; - if (m_out->m_tmpDataOffset && exceeds) { - size = m_out->m_tmpDataOffset + len; - tmp = new char[m_out->m_tmpDataOffset + len]; - std::memcpy(tmp, m_out->m_tmpData, m_out->m_tmpDataOffset); - } - if (flush && !m_out->m_tmpDataOffset) { - read = data; - size = len; - } else { - std::memcpy(tmp + m_out->m_tmpDataOffset, data, len); - read = tmp; - if (!exceeds) { - m_out->m_tmpDataOffset += len; - size = m_out->m_tmpDataOffset; - } - } - m_out->m_processed += size; - if (size && flush) { - alBufferData(*m_out->aldata->buffer, m_out->aldata->format, read, size, - m_out->m_format.sampleRate()); - if (tmp && tmp != m_out->m_tmpData) - delete[] tmp; - m_out->m_tmpDataOffset = 0; - alGetError(); - alSourceQueueBuffers(m_out->aldata->source, 1, m_out->aldata->buffer); - if (alGetError()) - return 0; - m_out->m_bufferFragmentsBusyCount++; - if (++m_out->aldata->buffer == m_out->aldata->buffers + m_out->m_bufferFragmentsCount) - m_out->aldata->buffer = m_out->aldata->buffers; - if (state != AL_PLAYING) - alSourcePlay(m_out->aldata->source); - } - return len; - } - return 0; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/wasm/audio/qwasmaudiosink_p.h b/src/multimedia/platform/wasm/audio/qwasmaudiosink_p.h deleted file mode 100644 index 1c75ec258..000000000 --- a/src/multimedia/platform/wasm/audio/qwasmaudiosink_p.h +++ /dev/null @@ -1,124 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QWASMAUDIOSINK_H -#define QWASMAUDIOSINK_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qaudiosystem_p.h> -#include <QTimer> -#include <QElapsedTimer> - -class ALData; -class QIODevice; - -QT_BEGIN_NAMESPACE - -class QWasmAudioSink : public QPlatformAudioSink -{ - Q_OBJECT - - QByteArray m_name; - ALData *aldata = nullptr; - QTimer m_timer; - QIODevice *m_device = nullptr; - QAudioFormat m_format; - QAudio::Error m_error = QAudio::NoError; - bool m_running = false; - QAudio::State m_state = QAudio::StoppedState; - int m_bufferSize = 0; - quint64 m_processed = 0; - QElapsedTimer m_elapsedTimer; - int m_bufferFragmentsCount = 10; - int m_notifyInterval = 0; - char *m_tmpData = nullptr; - int m_bufferFragmentSize = 0; - int m_lastNotified = 0; - int m_tmpDataOffset = 0; - int m_bufferFragmentsBusyCount = 0; - bool m_pullMode; - qreal m_volume = 1; - - void loadALBuffers(); - void unloadALBuffers(); - void nextALBuffers(); - -private slots: - void updateState(); - void setError(QAudio::Error); - -public: - QWasmAudioSink(const QByteArray &device); - ~QWasmAudioSink(); - -public: - void start(QIODevice *device) override; - QIODevice *start() override; - void start(bool mode); - void stop() override; - void reset() override; - void suspend() override; - void resume() override; - int bytesFree() const override; - void setBufferSize(int value) override; - int bufferSize() const override; - qint64 processedUSecs() const override; - QAudio::Error error() const override; - QAudio::State state() const override; - void setFormat(const QAudioFormat &fmt) override; - QAudioFormat format() const override; - void setVolume(qreal volume) override; - qreal volume() const override; - - friend class QWasmAudioSinkDevice; -}; - -QT_END_NAMESPACE - -#endif // QWASMAUDIOSINK_H diff --git a/src/multimedia/platform/wasm/audio/qwasmaudiosource.cpp b/src/multimedia/platform/wasm/audio/qwasmaudiosource.cpp deleted file mode 100644 index 981eeefc0..000000000 --- a/src/multimedia/platform/wasm/audio/qwasmaudiosource.cpp +++ /dev/null @@ -1,347 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qwasmaudiosource_p.h" - -#include <emscripten.h> -#include <AL/al.h> -#include <AL/alc.h> -#include <QDataStream> -#include <QDebug> -#include <QtMath> -#include <private/qaudiohelpers_p.h> -#include <QIODevice> - -QT_BEGIN_NAMESPACE - -#define AL_FORMAT_MONO_FLOAT32 0x10010 -#define AL_FORMAT_STEREO_FLOAT32 0x10011 - -constexpr unsigned int DEFAULT_BUFFER_DURATION = 50'000; - -class QWasmAudioSourceDevice : public QIODevice -{ - QWasmAudioSource *m_in; - -public: - explicit QWasmAudioSourceDevice(QWasmAudioSource *in); - -protected: - qint64 readData(char *data, qint64 maxlen) override; - qint64 writeData(const char *data, qint64 len) override; -}; - -class ALData { -public: - ALCdevice *device = nullptr; - ALCcontext *context = nullptr; -}; - -void QWasmAudioSource::setError(const QAudio::Error &error) -{ - if (m_error == error) - return; - m_error = error; - emit errorChanged(error); -} - -void QWasmAudioSource::writeBuffer() -{ - int samples = 0; - alcGetIntegerv(aldata->device, ALC_CAPTURE_SAMPLES, 1, &samples); - samples = qMin(samples, m_format.framesForBytes(m_bufferSize)); - auto bytes = m_format.bytesForFrames(samples); - auto err = alcGetError(aldata->device); - alcCaptureSamples(aldata->device, m_tmpData, samples); - err = alcGetError(aldata->device); - if (err) { - qWarning() << alcGetString(aldata->device, err); - return setError(QAudio::FatalError); - } - if (m_volume < 1) - QAudioHelperInternal::qMultiplySamples(m_volume, m_format, m_tmpData, m_tmpData, bytes); - m_processed += bytes; - m_device->write(m_tmpData,bytes); -} - -QWasmAudioSource::QWasmAudioSource(const QByteArray &device) - : QPlatformAudioSource(), m_name(device) -{ - aldata = new ALData(); - connect(&m_timer, &QTimer::timeout, this, [this](){ - Q_ASSERT(m_running); - if (m_pullMode) - writeBuffer(); - else - if (bytesReady() > 0) - emit m_device->readyRead(); - }); -} - -void QWasmAudioSource::start(QIODevice *device) -{ - m_device = device; - start(true); -} - -QIODevice *QWasmAudioSource::start() -{ - m_device = new QWasmAudioSourceDevice(this); - m_device->open(QIODevice::ReadOnly); - start(false); - return m_device; -} - -void QWasmAudioSource::start(bool mode) -{ - m_pullMode = mode; - auto formatError = [this](){ - qWarning() << "Unsupported audio format " << m_format; - setError(QAudio::OpenError); - }; - ALCenum format; - switch (m_format.sampleFormat()) { - case QAudioFormat::UInt8: - switch (m_format.channelCount()) { - case 1: - format = AL_FORMAT_MONO8; - break; - case 2: - format = AL_FORMAT_STEREO8; - break; - default: - return formatError(); - } - break; - case QAudioFormat::Int16: - switch (m_format.channelCount()) { - case 1: - format = AL_FORMAT_MONO16; - break; - case 2: - format = AL_FORMAT_STEREO16; - break; - default: - return formatError(); - } - break; - case QAudioFormat::Float: - switch (m_format.channelCount()) { - case 1: - format = AL_FORMAT_MONO_FLOAT32; - break; - case 2: - format = AL_FORMAT_STEREO_FLOAT32; - break; - default: - return formatError(); - } - break; - default: - return formatError(); - } - if (m_tmpData) - delete[] m_tmpData; - if (m_pullMode) - m_tmpData = new char[m_bufferSize]; - else - m_tmpData = nullptr; - m_timer.setInterval(m_format.durationForBytes(m_bufferSize) / 3'000); - - aldata->device = alcCaptureOpenDevice(m_name.data(), m_format.sampleRate(), format, - m_format.framesForBytes(m_bufferSize)); - - auto err = alcGetError(aldata->device); - if (err) { - qWarning() << "alcCaptureOpenDevice" << alcGetString(aldata->device, err); - return setError(QAudio::OpenError); - } - alcCaptureStart(aldata->device); - m_elapsedTimer.start(); - auto cerr = alcGetError(aldata->device); - if (cerr) { - qWarning() << "alcCaptureStart" << alcGetString(aldata->device, cerr); - return setError(QAudio::OpenError); - } - m_processed = 0; - m_running = true; - m_timer.start(); -} - -void QWasmAudioSource::stop() -{ - if (m_pullMode) - writeBuffer(); - alcCaptureStop(aldata->device); - alcCaptureCloseDevice(aldata->device); - m_elapsedTimer.invalidate(); - if (m_tmpData) { - delete[] m_tmpData; - m_tmpData = nullptr; - } - if (!m_pullMode) - m_device->deleteLater(); - m_timer.stop(); - m_running = false; -} - -void QWasmAudioSource::reset() -{ - stop(); - if (m_tmpData) { - delete[] m_tmpData; - m_tmpData = nullptr; - } - m_running = false; - m_processed = 0; - m_error = QAudio::NoError; -} - -void QWasmAudioSource::suspend() -{ - if (!m_running) - return; - - m_suspended = true; - alcCaptureStop(aldata->device); -} - -void QWasmAudioSource::resume() -{ - if (!m_running) - return; - - m_suspended = false; - alcCaptureStart(aldata->device); -} - -int QWasmAudioSource::bytesReady() const -{ - if (!m_running) - return 0; - int samples; - alcGetIntegerv(aldata->device, ALC_CAPTURE_SAMPLES, 1, &samples); - return m_format.bytesForFrames(samples); -} - -void QWasmAudioSource::setBufferSize(int value) -{ - if (!m_running) - return; - m_bufferSize = value; -} - -int QWasmAudioSource::bufferSize() const -{ - return m_bufferSize; -} - -qint64 QWasmAudioSource::processedUSecs() const -{ - return m_format.durationForBytes(m_processed); -} - -QAudio::Error QWasmAudioSource::error() const -{ - return m_error; -} - -QAudio::State QWasmAudioSource::state() const -{ - if (m_running) - return QAudio::ActiveState; - else - return QAudio::StoppedState; -} - -void QWasmAudioSource::setFormat(const QAudioFormat &fmt) -{ - m_format = fmt; - m_bufferSize = m_format.bytesForDuration(DEFAULT_BUFFER_DURATION); -} - -QAudioFormat QWasmAudioSource::format() const -{ - return m_format; -} - -void QWasmAudioSource::setVolume(qreal volume) -{ - m_volume = volume; -} - -qreal QWasmAudioSource::volume() const -{ - return m_volume; -} - -QWasmAudioSourceDevice::QWasmAudioSourceDevice(QWasmAudioSource *in) : QIODevice(in), m_in(in) -{ - -} - -qint64 QWasmAudioSourceDevice::readData(char *data, qint64 maxlen) -{ - int samples; - alcGetIntegerv(m_in->aldata->device, ALC_CAPTURE_SAMPLES, 1, &samples); - samples = qMin(samples, m_in->m_format.framesForBytes(maxlen)); - auto bytes = m_in->m_format.bytesForFrames(samples); - alcGetError(m_in->aldata->device); - alcCaptureSamples(m_in->aldata->device, data, samples); - if (m_in->m_volume < 1) - QAudioHelperInternal::qMultiplySamples(m_in->m_volume, m_in->m_format, data, data, bytes); - auto err = alcGetError(m_in->aldata->device); - if (err) { - qWarning() << alcGetString(m_in->aldata->device, err); - m_in->setError(QAudio::FatalError); - return 0; - } - m_in->m_processed += bytes; - return bytes; -} - -qint64 QWasmAudioSourceDevice::writeData(const char *data, qint64 len) -{ - Q_UNREACHABLE(); - Q_UNUSED(data); - Q_UNUSED(len); - return 0; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/wasm/audio/qwasmaudiosource_p.h b/src/multimedia/platform/wasm/audio/qwasmaudiosource_p.h deleted file mode 100644 index aef81d75b..000000000 --- a/src/multimedia/platform/wasm/audio/qwasmaudiosource_p.h +++ /dev/null @@ -1,111 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QWASMAUDIOSOURCE_H -#define QWASMAUDIOSOURCE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qaudiosystem_p.h> -#include <QTimer> -#include <QElapsedTimer> - -QT_BEGIN_NAMESPACE - -class ALData; - -class QWasmAudioSource : public QPlatformAudioSource -{ - Q_OBJECT - - QByteArray m_name; - ALData *aldata = nullptr; - QTimer m_timer; - QIODevice *m_device = nullptr; - QAudioFormat m_format; - qreal m_volume = 1; - int m_bufferSize; - bool m_running = false; - bool m_suspended = false; - QAudio::Error m_error; - bool m_pullMode; - char *m_tmpData = nullptr; - QElapsedTimer m_elapsedTimer; - int m_notifyInterval = 0; - quint64 m_processed = 0; - - void writeBuffer(); -public: - QWasmAudioSource(const QByteArray &device); - -public: - void start(QIODevice *device) override; - QIODevice *start() override; - void start(bool mode); - void stop() override; - void reset() override; - void suspend() override; - void resume() override; - int bytesReady() const override; - void setBufferSize(int value) override; - int bufferSize() const override; - qint64 processedUSecs() const override; - QAudio::Error error() const override; - QAudio::State state() const override; - void setFormat(const QAudioFormat &fmt) override; - QAudioFormat format() const override; - void setVolume(qreal volume) override; - qreal volume() const override; - - friend class QWasmAudioSourceDevice; - void setError(const QAudio::Error &error); -}; - -QT_END_NAMESPACE - -#endif // QWASMAUDIOSOURCE_H diff --git a/src/multimedia/platform/wasm/qwasmmediadevices.cpp b/src/multimedia/platform/wasm/qwasmmediadevices.cpp deleted file mode 100644 index c21533943..000000000 --- a/src/multimedia/platform/wasm/qwasmmediadevices.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qwasmmediadevices_p.h" -#include "qcameradevice_p.h" - -#include "audio/qwasmaudiosource_p.h" -#include "audio/qwasmaudiosink_p.h" -#include "audio/qwasmaudiodevice_p.h" -#include <AL/al.h> -#include <AL/alc.h> - -QT_BEGIN_NAMESPACE - -QWasmMediaDevices::QWasmMediaDevices() - : QPlatformMediaDevices() -{ - auto capture = alcGetString(nullptr, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER); - // present even if there is no capture device - if (capture) - m_ins.append((new QWasmAudioDevice(capture, "WebAssembly audio capture device", true, - QAudioDevice::Input))->create()); - - auto playback = alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER); - // present even if there is no playback device - if (playback) - m_outs.append((new QWasmAudioDevice(playback, "WebAssembly audio playback device", true, - QAudioDevice::Output))->create()); -} - -QList<QAudioDevice> QWasmMediaDevices::audioInputs() const -{ - return m_ins; -} - -QList<QAudioDevice> QWasmMediaDevices::audioOutputs() const -{ - return m_outs; -} - -QList<QCameraDevice> QWasmMediaDevices::videoInputs() const -{ - return {}; -} - -QPlatformAudioSource *QWasmMediaDevices::createAudioSource(const QAudioDevice &deviceInfo) -{ - return new QWasmAudioSource(deviceInfo.id()); -} - -QPlatformAudioSink *QWasmMediaDevices::createAudioSink(const QAudioDevice &deviceInfo) -{ - return new QWasmAudioSink(deviceInfo.id()); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/wasm/qwasmmediadevices_p.h b/src/multimedia/platform/wasm/qwasmmediadevices_p.h deleted file mode 100644 index 63fa05476..000000000 --- a/src/multimedia/platform/wasm/qwasmmediadevices_p.h +++ /dev/null @@ -1,81 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QWASMMEDIADEVICES_H -#define QWASMMEDIADEVICES_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediadevices_p.h> -#include <qset.h> -#include <qaudio.h> -#include <qaudiodevice.h> - -QT_BEGIN_NAMESPACE - -class QWasmAudioEngine; - -class QWasmMediaDevices : public QPlatformMediaDevices -{ -public: - QWasmMediaDevices(); - - QList<QAudioDevice> audioInputs() const override; - QList<QAudioDevice> audioOutputs() const override; - QList<QCameraDevice> videoInputs() const override; - QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo) override; - QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo) override; - -private: - QList<QAudioDevice> m_outs; - QList<QAudioDevice> m_ins; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/wasm/qwasmmediaintegration.cpp b/src/multimedia/platform/wasm/qwasmmediaintegration.cpp deleted file mode 100644 index 30ee3cca9..000000000 --- a/src/multimedia/platform/wasm/qwasmmediaintegration.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qwasmmediaintegration_p.h" -#include "qwasmmediadevices_p.h" -#include <QLoggingCategory> - -#include <private/qplatformmediaformatinfo_p.h> - -QT_BEGIN_NAMESPACE - -Q_LOGGING_CATEGORY(qtWasmMediaPlugin, "qt.multimedia.wasm") - -QWasmMediaIntegration::QWasmMediaIntegration() -{ - -} - -QWasmMediaIntegration::~QWasmMediaIntegration() -{ - delete m_devices; - delete m_formatInfo; -} - -QPlatformMediaFormatInfo *QWasmMediaIntegration::formatInfo() -{ - if (!m_formatInfo) - m_formatInfo = new QPlatformMediaFormatInfo(); - return m_formatInfo; -} - -QPlatformMediaDevices *QWasmMediaIntegration::devices() -{ - if (!m_devices) - m_devices = new QWasmMediaDevices(); - return m_devices; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/wasm/qwasmmediaintegration_p.h b/src/multimedia/platform/wasm/qwasmmediaintegration_p.h deleted file mode 100644 index 1251dc9f5..000000000 --- a/src/multimedia/platform/wasm/qwasmmediaintegration_p.h +++ /dev/null @@ -1,76 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QWASMMEDIAINTEGRATION_H -#define QWASMMEDIAINTEGRATION_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediaintegration_p.h> - -QT_BEGIN_NAMESPACE - -class QWasmMediaDevices; - -class QWasmMediaIntegration : public QPlatformMediaIntegration -{ -public: - QWasmMediaIntegration(); - ~QWasmMediaIntegration(); - - QPlatformMediaFormatInfo *formatInfo() override; - - QWasmMediaDevices *m_devices = nullptr; - QPlatformMediaFormatInfo *m_formatInfo = nullptr; - - QPlatformMediaDevices *devices() override; -}; - -QT_END_NAMESPACE - -#endif // QWASMMEDIAINTEGRATION_H diff --git a/src/multimedia/platform/windows/audio/qwindowsaudiodevice.cpp b/src/multimedia/platform/windows/audio/qwindowsaudiodevice.cpp deleted file mode 100644 index 793d1472f..000000000 --- a/src/multimedia/platform/windows/audio/qwindowsaudiodevice.cpp +++ /dev/null @@ -1,230 +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$ -** -****************************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of other Qt classes. This header file may change from version to -// version without notice, or even be removed. -// -// INTERNAL USE ONLY: Do NOT use for any other purpose. -// - - -#include <QtCore/qt_windows.h> -#include <QtCore/QDataStream> -#include <QtCore/QIODevice> -#include <mmsystem.h> -#include "qwindowsaudiodevice_p.h" -#include "qwindowsaudioutils_p.h" - -QT_BEGIN_NAMESPACE - -QWindowsAudioDeviceInfo::QWindowsAudioDeviceInfo(QByteArray dev, int waveID, const QString &description, QAudioDevice::Mode mode) - : QAudioDevicePrivate(dev, mode), - devId(waveID) -{ - this->description = description; - preferredFormat.setSampleRate(44100); - preferredFormat.setChannelCount(2); - preferredFormat.setSampleFormat(QAudioFormat::Int16); - - DWORD fmt = 0; - - if(mode == QAudioDevice::Output) { - WAVEOUTCAPS woc; - if (waveOutGetDevCaps(devId, &woc, sizeof(WAVEOUTCAPS)) == MMSYSERR_NOERROR) - fmt = woc.dwFormats; - } else { - WAVEINCAPS woc; - if (waveInGetDevCaps(devId, &woc, sizeof(WAVEINCAPS)) == MMSYSERR_NOERROR) - fmt = woc.dwFormats; - } - - if (!fmt) - return; - - // Check sample size - if ((fmt & WAVE_FORMAT_1M08) - || (fmt & WAVE_FORMAT_1S08) - || (fmt & WAVE_FORMAT_2M08) - || (fmt & WAVE_FORMAT_2S08) - || (fmt & WAVE_FORMAT_4M08) - || (fmt & WAVE_FORMAT_4S08) - || (fmt & WAVE_FORMAT_48M08) - || (fmt & WAVE_FORMAT_48S08) - || (fmt & WAVE_FORMAT_96M08) - || (fmt & WAVE_FORMAT_96S08)) { - supportedSampleFormats.append(QAudioFormat::UInt8); - } - if ((fmt & WAVE_FORMAT_1M16) - || (fmt & WAVE_FORMAT_1S16) - || (fmt & WAVE_FORMAT_2M16) - || (fmt & WAVE_FORMAT_2S16) - || (fmt & WAVE_FORMAT_4M16) - || (fmt & WAVE_FORMAT_4S16) - || (fmt & WAVE_FORMAT_48M16) - || (fmt & WAVE_FORMAT_48S16) - || (fmt & WAVE_FORMAT_96M16) - || (fmt & WAVE_FORMAT_96S16)) { - supportedSampleFormats.append(QAudioFormat::Int16); - } - - minimumSampleRate = std::numeric_limits<int>::max(); - maximumSampleRate = 0; - // Check sample rate - if ((fmt & WAVE_FORMAT_1M08) - || (fmt & WAVE_FORMAT_1S08) - || (fmt & WAVE_FORMAT_1M16) - || (fmt & WAVE_FORMAT_1S16)) { - minimumSampleRate = qMin(minimumSampleRate, 11025); - maximumSampleRate = qMax(maximumSampleRate, 11025); - } - if ((fmt & WAVE_FORMAT_2M08) - || (fmt & WAVE_FORMAT_2S08) - || (fmt & WAVE_FORMAT_2M16) - || (fmt & WAVE_FORMAT_2S16)) { - minimumSampleRate = qMin(minimumSampleRate, 22050); - maximumSampleRate = qMax(maximumSampleRate, 22050); - } - if ((fmt & WAVE_FORMAT_4M08) - || (fmt & WAVE_FORMAT_4S08) - || (fmt & WAVE_FORMAT_4M16) - || (fmt & WAVE_FORMAT_4S16)) { - minimumSampleRate = qMin(minimumSampleRate, 44100); - maximumSampleRate = qMax(maximumSampleRate, 44100); - } - if ((fmt & WAVE_FORMAT_48M08) - || (fmt & WAVE_FORMAT_48S08) - || (fmt & WAVE_FORMAT_48M16) - || (fmt & WAVE_FORMAT_48S16)) { - minimumSampleRate = qMin(minimumSampleRate, 48000); - maximumSampleRate = qMax(maximumSampleRate, 48000); - } - if ((fmt & WAVE_FORMAT_96M08) - || (fmt & WAVE_FORMAT_96S08) - || (fmt & WAVE_FORMAT_96M16) - || (fmt & WAVE_FORMAT_96S16)) { - minimumSampleRate = qMin(minimumSampleRate, 96000); - maximumSampleRate = qMax(maximumSampleRate, 96000); - } - if (minimumSampleRate == std::numeric_limits<int>::max()) - minimumSampleRate = 0; - - minimumChannelCount = std::numeric_limits<int>::max(); - maximumChannelCount = 0; - // Check channel count - if (fmt & WAVE_FORMAT_1M08 - || fmt & WAVE_FORMAT_1M16 - || fmt & WAVE_FORMAT_2M08 - || fmt & WAVE_FORMAT_2M16 - || fmt & WAVE_FORMAT_4M08 - || fmt & WAVE_FORMAT_4M16 - || fmt & WAVE_FORMAT_48M08 - || fmt & WAVE_FORMAT_48M16 - || fmt & WAVE_FORMAT_96M08 - || fmt & WAVE_FORMAT_96M16) { - minimumChannelCount = 1; - maximumChannelCount = 1; - } - if (fmt & WAVE_FORMAT_1S08 - || fmt & WAVE_FORMAT_1S16 - || fmt & WAVE_FORMAT_2S08 - || fmt & WAVE_FORMAT_2S16 - || fmt & WAVE_FORMAT_4S08 - || fmt & WAVE_FORMAT_4S16 - || fmt & WAVE_FORMAT_48S08 - || fmt & WAVE_FORMAT_48S16 - || fmt & WAVE_FORMAT_96S08 - || fmt & WAVE_FORMAT_96S16) { - minimumChannelCount = qMin(minimumChannelCount, 2); - maximumChannelCount = qMax(maximumChannelCount, 2); - } - - if (minimumChannelCount == std::numeric_limits<int>::max()) - minimumChannelCount = 0; - - // WAVEOUTCAPS and WAVEINCAPS contains information only for the previously tested parameters. - // WaveOut and WaveInt might actually support more formats, the only way to know is to try - // opening the device with it. - QAudioFormat testFormat; - testFormat.setChannelCount(maximumChannelCount); - testFormat.setSampleRate(maximumSampleRate); - const QAudioFormat defaultTestFormat(testFormat); - - // Check if float samples are supported - testFormat.setSampleFormat(QAudioFormat::Float); - if (testSettings(testFormat)) - supportedSampleFormats.append(QAudioFormat::Float); - - // Check channel counts > 2 - testFormat = defaultTestFormat; - for (int i = 18; i > 2; --i) { // <mmreg.h> defines 18 different channels - testFormat.setChannelCount(i); - if (testSettings(testFormat)) { - maximumChannelCount = i; - break; - } - } -} - -QWindowsAudioDeviceInfo::~QWindowsAudioDeviceInfo() -{ -} - -bool QWindowsAudioDeviceInfo::testSettings(const QAudioFormat& format) const -{ - WAVEFORMATEXTENSIBLE wfx; - if (qt_convertFormat(format, &wfx)) { - // query only, do not open device - if (mode == QAudioDevice::Output) { - return (waveOutOpen(NULL, UINT_PTR(devId), &wfx.Format, 0, 0, - WAVE_FORMAT_QUERY) == MMSYSERR_NOERROR); - } else { // AudioInput - return (waveInOpen(NULL, UINT_PTR(devId), &wfx.Format, 0, 0, - WAVE_FORMAT_QUERY) == MMSYSERR_NOERROR); - } - } - - return false; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/windows/audio/qwindowsaudiodevice_p.h b/src/multimedia/platform/windows/audio/qwindowsaudiodevice_p.h deleted file mode 100644 index 7d55a5820..000000000 --- a/src/multimedia/platform/windows/audio/qwindowsaudiodevice_p.h +++ /dev/null @@ -1,91 +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$ -** -****************************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of other Qt classes. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - - -#ifndef QWINDOWSAUDIODEVICEINFO_H -#define QWINDOWSAUDIODEVICEINFO_H - -#include <QtCore/qbytearray.h> -#include <QtCore/qstringlist.h> -#include <QtCore/qlist.h> -#include <QtCore/qdebug.h> - -#include <QtMultimedia/qaudiodevice.h> -#include <private/qaudiosystem_p.h> -#include <private/qaudiodevice_p.h> - - -QT_BEGIN_NAMESPACE - -const unsigned int MAX_SAMPLE_RATES = 5; -const unsigned int SAMPLE_RATES[] = { 8000, 11025, 22050, 44100, 48000 }; - -class QWindowsAudioDeviceInfo : public QAudioDevicePrivate -{ -public: - QWindowsAudioDeviceInfo(QByteArray dev, int waveID, const QString &description, QAudioDevice::Mode mode); - ~QWindowsAudioDeviceInfo(); - - bool open(); - void close(); - - bool testSettings(const QAudioFormat& format) const; - - int waveId() const { return devId; } -private: - quint32 devId; -}; - - - -QT_END_NAMESPACE - - -#endif // QWINDOWSAUDIODEVICEINFO_H diff --git a/src/multimedia/platform/windows/audio/qwindowsaudiosink.cpp b/src/multimedia/platform/windows/audio/qwindowsaudiosink.cpp deleted file mode 100644 index 8cc4d33cf..000000000 --- a/src/multimedia/platform/windows/audio/qwindowsaudiosink.cpp +++ /dev/null @@ -1,608 +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$ -** -****************************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of other Qt classes. This header file may change from version to -// version without notice, or even be removed. -// -// INTERNAL USE ONLY: Do NOT use for any other purpose. -// - -#include "qwindowsaudiosink_p.h" -#include "qwindowsaudiodevice_p.h" -#include "qwindowsaudioutils_p.h" -#include <QtEndian> -#include <QtCore/QDataStream> -#include <QtCore/qtimer.h> -#include <private/qaudiohelpers_p.h> - -#include <qloggingcategory.h> - -QT_BEGIN_NAMESPACE - -Q_LOGGING_CATEGORY(qLcAudioOutput, "qt.multimedia.audiooutput") - -QWindowsAudioSink::QWindowsAudioSink(int deviceId) -{ - bytesAvailable = 0; - buffer_size = 0; - period_size = 0; - m_deviceId = deviceId; - totalTimeValue = 0; - errorState = QAudio::NoError; - deviceState = QAudio::StoppedState; - audioSource = 0; - pullMode = true; - volumeCache = qreal(1.0); - blocks_count = 5; -} - -QWindowsAudioSink::~QWindowsAudioSink() -{ - close(); -} - -void CALLBACK QWindowsAudioSink::waveOutProc( HWAVEOUT hWaveOut, UINT uMsg, - DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 ) -{ - Q_UNUSED(dwParam1); - Q_UNUSED(dwParam2); - Q_UNUSED(hWaveOut); - - QWindowsAudioSink* qAudio; - qAudio = (QWindowsAudioSink*)(dwInstance); - if(!qAudio) - return; - - QMutexLocker locker(&qAudio->mutex); - - switch(uMsg) { - case WOM_OPEN: - qAudio->feedback(); - break; - case WOM_CLOSE: - return; - case WOM_DONE: - if(qAudio->buffer_size == 0 || qAudio->period_size == 0) { - return; - } - qAudio->waveFreeBlockCount++; - if (qAudio->waveFreeBlockCount >= qAudio->blocks_count) - qAudio->waveFreeBlockCount = qAudio->blocks_count; - - qAudio->feedback(); - break; - default: - return; - } -} - -WAVEHDR* QWindowsAudioSink::allocateBlocks(int size, int count) -{ - int i; - unsigned char* buffer; - WAVEHDR* blocks; - DWORD totalBufferSize = (size + sizeof(WAVEHDR))*count; - - if((buffer=(unsigned char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, - totalBufferSize)) == 0) { - qWarning("QAudioSink: Memory allocation error"); - return 0; - } - blocks = (WAVEHDR*)buffer; - buffer += sizeof(WAVEHDR)*count; - for(i = 0; i < count; i++) { - blocks[i].dwBufferLength = size; - blocks[i].lpData = (LPSTR)buffer; - buffer += size; - } - return blocks; -} - -void QWindowsAudioSink::freeBlocks(WAVEHDR* blockArray) -{ - WAVEHDR* blocks = blockArray; - for (int i = 0; i < blocks_count; ++i) { - waveOutUnprepareHeader(hWaveOut,blocks, sizeof(WAVEHDR)); - blocks++; - } - HeapFree(GetProcessHeap(), 0, blockArray); -} - -QAudioFormat QWindowsAudioSink::format() const -{ - return settings; -} - -void QWindowsAudioSink::setFormat(const QAudioFormat& fmt) -{ - if (deviceState == QAudio::StoppedState) - settings = fmt; -} - -void QWindowsAudioSink::start(QIODevice* device) -{ - qCDebug(qLcAudioOutput) << "start(ioDevice)"; - if(deviceState != QAudio::StoppedState) - close(); - - if(!pullMode && audioSource) - delete audioSource; - - pullMode = true; - audioSource = device; - - deviceState = QAudio::ActiveState; - - if(!open()) - return; - - emit stateChanged(deviceState); -} - -QIODevice* QWindowsAudioSink::start() -{ - qCDebug(qLcAudioOutput) << "start()"; - if(deviceState != QAudio::StoppedState) - close(); - - if(!pullMode && audioSource) - delete audioSource; - - pullMode = false; - audioSource = new OutputPrivate(this); - audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered); - - deviceState = QAudio::IdleState; - - if(!open()) - return 0; - - emit stateChanged(deviceState); - - return audioSource; -} - -void QWindowsAudioSink::stop() -{ - qCDebug(qLcAudioOutput) << "stop()"; - if(deviceState == QAudio::StoppedState) - return; - close(); - if(!pullMode && audioSource) { - delete audioSource; - audioSource = 0; - } - emit stateChanged(deviceState); -} - -bool QWindowsAudioSink::open() -{ - qCDebug(qLcAudioOutput) << "open()"; - - period_size = 0; - - if (!qt_convertFormat(settings, &wfx)) { - qWarning("QAudioSink: open error, invalid format."); - } else if (buffer_size == 0) { - // Default buffer size, 200ms, default period size is 40ms - buffer_size - = (settings.sampleRate() - * settings.bytesPerFrame() - + 39) / 5; - period_size = buffer_size / 5; - } else { - period_size = buffer_size / 5; - } - - // Make even size of wave block to prevent crackling - // due to waveOutWrite() does not like odd buffer length - period_size &= ~1; - - if (period_size == 0) { - errorState = QAudio::OpenError; - deviceState = QAudio::StoppedState; - emit stateChanged(deviceState); - return false; - } - - const int periods = buffer_size / period_size; - bool ok = false; - static int wave_buffers = qEnvironmentVariableIntValue("QT_WAVE_BUFFERS", &ok); - if (wave_buffers < periods) { - if (ok) - qWarning("Number of WAVE buffers (QT_WAVE_BUFFERS=%d) cannot be less than %d.", wave_buffers, periods); - wave_buffers = periods; - } - - blocks_count = wave_buffers; - waveBlocks = allocateBlocks(period_size, blocks_count); - - mutex.lock(); - waveFreeBlockCount = blocks_count; - mutex.unlock(); - - waveCurrentBlock = 0; - - if (audioBuffer == nullptr) - audioBuffer = new char[blocks_count * period_size]; - - elapsedTimeOffset = 0; - - if (waveOutOpen(&hWaveOut, UINT_PTR(m_deviceId), &wfx.Format, - (DWORD_PTR)&waveOutProc, - (DWORD_PTR) this, - CALLBACK_FUNCTION) != MMSYSERR_NOERROR) { - errorState = QAudio::OpenError; - deviceState = QAudio::StoppedState; - emit stateChanged(deviceState); - qWarning("QAudioSink: open error"); - return false; - } - - totalTimeValue = 0; - elapsedTimeOffset = 0; - - errorState = QAudio::NoError; - if(pullMode) { - deviceState = QAudio::ActiveState; - QTimer::singleShot(10, this, SLOT(feedback())); - } else - deviceState = QAudio::IdleState; - - return true; -} - -void QWindowsAudioSink::pauseAndSleep() -{ - waveOutPause(hWaveOut); - int bitrate = settings.sampleRate() * settings.bytesPerFrame(); - // Time of written data. - int delay = (buffer_size - bytesFree()) * 1000 / bitrate; - Sleep(delay + 10); -} - -void QWindowsAudioSink::close() -{ - qCDebug(qLcAudioOutput) << "close()"; - if(deviceState == QAudio::StoppedState) - return; - - // Pause playback before reset to avoid uneeded crackling at the end. - pauseAndSleep(); - QMutexLocker locker(&mutex); - deviceState = QAudio::StoppedState; - errorState = QAudio::NoError; - locker.unlock(); - waveOutReset(hWaveOut); - - freeBlocks(waveBlocks); - waveOutClose(hWaveOut); - locker.relock(); - delete [] audioBuffer; - audioBuffer = nullptr; - buffer_size = 0; - qCDebug(qLcAudioOutput) << "end close()"; -} - -qsizetype QWindowsAudioSink::bytesFree() const -{ - int buf; - QMutexLocker locker(&mutex); - buf = waveFreeBlockCount*period_size; - - return buf; -} - -void QWindowsAudioSink::setBufferSize(qsizetype value) -{ - if(deviceState == QAudio::StoppedState) - buffer_size = value; -} - -qsizetype QWindowsAudioSink::bufferSize() const -{ - return buffer_size; -} - -qint64 QWindowsAudioSink::processedUSecs() const -{ - if (deviceState == QAudio::StoppedState) - return 0; - qint64 result = qint64(1000000) * totalTimeValue / - settings.bytesPerFrame() / settings.sampleRate(); - - return result; -} - -qint64 QWindowsAudioSink::write(const char *data, qint64 len) -{ - qCDebug(qLcAudioOutput) << "write()" << len << deviceState << period_size; - - // Write out some audio data - if (deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState) - return 0; - - WAVEHDR* current; - qint64 written = 0; - current = &waveBlocks[waveCurrentBlock]; - while(len > 0) { - mutex.lock(); - if(waveFreeBlockCount==0) { - mutex.unlock(); - break; - } - mutex.unlock(); - - if(current->dwFlags & WHDR_PREPARED) - waveOutUnprepareHeader(hWaveOut, current, sizeof(WAVEHDR)); - - qint64 remain; - if(len < period_size) - remain = len; - else - remain = period_size; - - if (volumeCache < qreal(1.0)) - QAudioHelperInternal::qMultiplySamples(volumeCache, settings, data, current->lpData, remain); - else - memcpy(current->lpData, data, remain); - - len -= remain; - data += remain; - current->dwBufferLength = remain; - written += remain; - waveOutPrepareHeader(hWaveOut, current, sizeof(WAVEHDR)); - waveOutWrite(hWaveOut, current, sizeof(WAVEHDR)); - - mutex.lock(); - waveFreeBlockCount--; - qCDebug(qLcAudioOutput) << "write out" << current->dwBufferLength << "waveFreeBlockCount" << waveFreeBlockCount; - mutex.unlock(); - - totalTimeValue += current->dwBufferLength; - waveCurrentBlock++; - waveCurrentBlock %= blocks_count; - current = &waveBlocks[waveCurrentBlock]; - current->dwUser = 0; - errorState = QAudio::NoError; - if (deviceState != QAudio::ActiveState) { - deviceState = QAudio::ActiveState; - emit stateChanged(deviceState); - } - } - return written; -} - -void QWindowsAudioSink::resume() -{ - qCDebug(qLcAudioOutput) << "resume()"; - if(deviceState == QAudio::SuspendedState) { - deviceState = pullMode ? QAudio::ActiveState : QAudio::IdleState; - errorState = QAudio::NoError; - waveOutRestart(hWaveOut); - QTimer::singleShot(10, this, SLOT(feedback())); - emit stateChanged(deviceState); - } -} - -void QWindowsAudioSink::suspend() -{ - qCDebug(qLcAudioOutput) << "suspend()"; - if(deviceState == QAudio::ActiveState || deviceState == QAudio::IdleState) { - pauseAndSleep(); - deviceState = QAudio::SuspendedState; - errorState = QAudio::NoError; - emit stateChanged(deviceState); - } -} - -void QWindowsAudioSink::feedback() -{ - qCDebug(qLcAudioOutput) << "feedback()"; - bytesAvailable = waveFreeBlockCount * period_size; - - if (deviceState != QAudio::StoppedState && deviceState != QAudio::SuspendedState) { - if (bytesAvailable >= period_size) { - qCDebug(qLcAudioOutput) << " ->invoke()"; - QMetaObject::invokeMethod(this, "deviceReady", Qt::QueuedConnection); - } - } -} - -bool QWindowsAudioSink::deviceReady() -{ - qCDebug(qLcAudioOutput) << ">>>> deviceReady() state=" << deviceState << pullMode; - - if(deviceState == QAudio::StoppedState || deviceState == QAudio::SuspendedState) - return false; - - if(pullMode) { - qCDebug(qLcAudioOutput) << "deviceReady() avail="<<bytesAvailable<<" bytes, period size="<<period_size<<" bytes"; - bool startup = false; - if(totalTimeValue == 0) - startup = true; - - bool full=false; - - mutex.lock(); - if (waveFreeBlockCount == 0) - full = true; - mutex.unlock(); - - if (full) { - qCDebug(qLcAudioOutput) << "Skipping data as unable to write"; - return true; - } - - if(startup) - waveOutPause(hWaveOut); - int input = bytesAvailable; - int l = audioSource->read(audioBuffer,input); - if(l > 0) { - int out = write(audioBuffer, l); - if(out > 0) { - if (deviceState != QAudio::ActiveState) { - deviceState = QAudio::ActiveState; - emit stateChanged(deviceState); - } - } - if ( out < l) { - // Didn't write all data - audioSource->seek(audioSource->pos()-(l-out)); - } - qCDebug(qLcAudioOutput) << "wrote" << out << "bytes out of" << l << "read"; - - if (startup) - waveOutRestart(hWaveOut); - } else if(l == 0) { - bytesAvailable = bytesFree(); - - int check = 0; - - mutex.lock(); - check = waveFreeBlockCount; - mutex.unlock(); - - if (check == blocks_count) { - if (deviceState != QAudio::IdleState) { - errorState = QAudio::UnderrunError; - deviceState = QAudio::IdleState; - emit stateChanged(deviceState); - } - } - - } else if(l < 0) { - bytesAvailable = bytesFree(); - if (errorState != QAudio::IOError) - errorState = QAudio::IOError; - } - } else { - int buffered; - - mutex.lock(); - buffered = waveFreeBlockCount; - mutex.unlock(); - - if (buffered >= blocks_count && deviceState == QAudio::ActiveState) { - if (deviceState != QAudio::IdleState) { - errorState = QAudio::UnderrunError; - deviceState = QAudio::IdleState; - emit stateChanged(deviceState); - } - } - } - qCDebug(qLcAudioOutput) << "<<<< end deviceReady() state=" << deviceState; - - return true; -} - -QAudio::Error QWindowsAudioSink::error() const -{ - return errorState; -} - -QAudio::State QWindowsAudioSink::state() const -{ - return deviceState; -} - -void QWindowsAudioSink::setVolume(qreal v) -{ - if (qFuzzyCompare(volumeCache, v)) - return; - - volumeCache = qBound(qreal(0), v, qreal(1)); -} - -qreal QWindowsAudioSink::volume() const -{ - return volumeCache; -} - -void QWindowsAudioSink::reset() -{ - stop(); -} - -OutputPrivate::OutputPrivate(QWindowsAudioSink* audio) -{ - audioDevice = qobject_cast<QWindowsAudioSink*>(audio); -} - -OutputPrivate::~OutputPrivate() {} - -qint64 OutputPrivate::readData( char* data, qint64 len) -{ - Q_UNUSED(data); - Q_UNUSED(len); - - return 0; -} - -qint64 OutputPrivate::writeData(const char* data, qint64 len) -{ - int retry = 0; - qint64 written = 0; - - if((audioDevice->deviceState == QAudio::ActiveState) - ||(audioDevice->deviceState == QAudio::IdleState)) { - qint64 l = len; - while(written < l) { - int chunk = audioDevice->write(data+written,(l-written)); - if(chunk <= 0) - retry++; - else - written+=chunk; - - if(retry > 10) - return written; - } - audioDevice->deviceState = QAudio::ActiveState; - } - return written; -} - -QT_END_NAMESPACE - -#include "moc_qwindowsaudiosink_p.cpp" diff --git a/src/multimedia/platform/windows/audio/qwindowsaudiosink_p.h b/src/multimedia/platform/windows/audio/qwindowsaudiosink_p.h deleted file mode 100644 index d865011b5..000000000 --- a/src/multimedia/platform/windows/audio/qwindowsaudiosink_p.h +++ /dev/null @@ -1,160 +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$ -** -****************************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of other Qt classes. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#ifndef QWINDOWSAUDIOOUTPUT_H -#define QWINDOWSAUDIOOUTPUT_H - -#include "qwindowsaudioutils_p.h" - -#include <QtCore/qdebug.h> -#include <QtCore/qelapsedtimer.h> -#include <QtCore/qiodevice.h> -#include <QtCore/qstring.h> -#include <QtCore/qstringlist.h> -#include <QtCore/qdatetime.h> -#include <QtCore/qmutex.h> - -#include <QtMultimedia/qaudio.h> -#include <QtMultimedia/qaudiodevice.h> -#include <private/qaudiosystem_p.h> - -// For compat with 4.6 -#if !defined(QT_WIN_CALLBACK) -# if defined(Q_CC_MINGW) -# define QT_WIN_CALLBACK CALLBACK __attribute__ ((force_align_arg_pointer)) -# else -# define QT_WIN_CALLBACK CALLBACK -# endif -#endif - -QT_BEGIN_NAMESPACE - -class QWindowsAudioSink : public QPlatformAudioSink -{ - Q_OBJECT -public: - QWindowsAudioSink(int deviceId); - ~QWindowsAudioSink(); - - qint64 write( const char *data, qint64 len ); - - void setFormat(const QAudioFormat& fmt); - QAudioFormat format() const; - QIODevice* start(); - void start(QIODevice* device); - void stop(); - void reset(); - void suspend(); - void resume(); - qsizetype bytesFree() const; - void setBufferSize(qsizetype value); - qsizetype bufferSize() const; - qint64 processedUSecs() const; - QAudio::Error error() const; - QAudio::State state() const; - void setVolume(qreal); - qreal volume() const; - - QIODevice* audioSource; - QAudioFormat settings; - QAudio::Error errorState; - QAudio::State deviceState; - -private slots: - void feedback(); - bool deviceReady(); - -private: - void pauseAndSleep(); - int m_deviceId; - int bytesAvailable; - qint64 elapsedTimeOffset; - qint32 buffer_size; - qint32 period_size; - qint32 blocks_count; - qint64 totalTimeValue; - bool pullMode; - qreal volumeCache; - static void QT_WIN_CALLBACK waveOutProc( HWAVEOUT hWaveOut, UINT uMsg, - DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 ); - - mutable QMutex mutex; - - WAVEHDR* allocateBlocks(int size, int count); - void freeBlocks(WAVEHDR* blockArray); - bool open(); - void close(); - - WAVEFORMATEXTENSIBLE wfx; - HWAVEOUT hWaveOut; - WAVEHDR* waveBlocks; - int waveFreeBlockCount = 0; - int waveCurrentBlock = 0; - char *audioBuffer = nullptr; -}; - -class OutputPrivate : public QIODevice -{ - Q_OBJECT -public: - OutputPrivate(QWindowsAudioSink* audio); - ~OutputPrivate(); - - qint64 readData( char* data, qint64 len); - qint64 writeData(const char* data, qint64 len); - -private: - QWindowsAudioSink *audioDevice; -}; - -QT_END_NAMESPACE - - -#endif // QWINDOWSAUDIOOUTPUT_H diff --git a/src/multimedia/platform/windows/audio/qwindowsaudiosource.cpp b/src/multimedia/platform/windows/audio/qwindowsaudiosource.cpp deleted file mode 100644 index 9eb6e98fa..000000000 --- a/src/multimedia/platform/windows/audio/qwindowsaudiosource.cpp +++ /dev/null @@ -1,698 +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$ -** -****************************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of other Qt classes. This header file may change from version to -// version without notice, or even be removed. -// -// INTERNAL USE ONLY: Do NOT use for any other purpose. -// - - -#include "qwindowsaudiosource_p.h" - -#include <QtCore/QDataStream> -#include <QtCore/qtimer.h> - -QT_BEGIN_NAMESPACE - -//#define DEBUG_AUDIO 1 - -QWindowsAudioSource::QWindowsAudioSource(int deviceId) -{ - bytesAvailable = 0; - buffer_size = 0; - period_size = 0; - m_deviceId = deviceId; - totalTimeValue = 0; - errorState = QAudio::NoError; - deviceState = QAudio::StoppedState; - audioSource = 0; - pullMode = true; - resuming = false; - finished = false; - waveBlockOffset = 0; - - mixerID = 0; - cachedVolume = -1.0f; - memset(&mixerLineControls, 0, sizeof(mixerLineControls)); -} - -QWindowsAudioSource::~QWindowsAudioSource() -{ - stop(); -} - -void QT_WIN_CALLBACK QWindowsAudioSource::waveInProc( HWAVEIN hWaveIn, UINT uMsg, - DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 ) -{ - Q_UNUSED(dwParam1); - Q_UNUSED(dwParam2); - Q_UNUSED(hWaveIn); - - QWindowsAudioSource* qAudio; - qAudio = (QWindowsAudioSource*)(dwInstance); - if(!qAudio) - return; - - QMutexLocker locker(&qAudio->mutex); - - switch(uMsg) { - case WIM_OPEN: - break; - case WIM_DATA: - if(qAudio->waveFreeBlockCount > 0) - qAudio->waveFreeBlockCount--; - qAudio->feedback(); - break; - case WIM_CLOSE: - qAudio->finished = true; - break; - default: - return; - } -} - -WAVEHDR* QWindowsAudioSource::allocateBlocks(int size, int count) -{ - int i; - unsigned char* buffer; - WAVEHDR* blocks; - DWORD totalBufferSize = (size + sizeof(WAVEHDR))*count; - - if((buffer=(unsigned char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, - totalBufferSize)) == 0) { - qWarning("QAudioSource: Memory allocation error"); - return 0; - } - blocks = (WAVEHDR*)buffer; - buffer += sizeof(WAVEHDR)*count; - for(i = 0; i < count; i++) { - blocks[i].dwBufferLength = size; - blocks[i].lpData = (LPSTR)buffer; - blocks[i].dwBytesRecorded=0; - blocks[i].dwUser = 0L; - blocks[i].dwFlags = 0L; - blocks[i].dwLoops = 0L; - result = waveInPrepareHeader(hWaveIn,&blocks[i], sizeof(WAVEHDR)); - if(result != MMSYSERR_NOERROR) { - qWarning("QAudioSource: Can't prepare block %d",i); - return 0; - } - buffer += size; - } - return blocks; -} - -void QWindowsAudioSource::freeBlocks(WAVEHDR* blockArray) -{ - WAVEHDR* blocks = blockArray; - - int count = buffer_size/period_size; - - for(int i = 0; i < count; i++) { - waveInUnprepareHeader(hWaveIn,blocks, sizeof(WAVEHDR)); - blocks++; - } - HeapFree(GetProcessHeap(), 0, blockArray); -} - -QAudio::Error QWindowsAudioSource::error() const -{ - return errorState; -} - -QAudio::State QWindowsAudioSource::state() const -{ - return deviceState; -} - -#ifndef DRVM_MAPPER_CONSOLEVOICECOM_GET - #ifndef DRVM_MAPPER - #define DRVM_MAPPER 0x2000 - #endif - #ifndef DRVM_MAPPER_STATUS - #define DRVM_MAPPER_STATUS (DRVM_MAPPER+0) - #endif - #define DRVM_USER 0x4000 - #define DRVM_MAPPER_RECONFIGURE (DRVM_MAPPER+1) - #define DRVM_MAPPER_PREFERRED_GET (DRVM_MAPPER+21) - #define DRVM_MAPPER_CONSOLEVOICECOM_GET (DRVM_MAPPER+23) -#endif - -void QWindowsAudioSource::setVolume(qreal volume) -{ - cachedVolume = volume; - for (DWORD i = 0; i < mixerLineControls.cControls; i++) { - - MIXERCONTROLDETAILS controlDetails; - controlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS); - controlDetails.dwControlID = mixerLineControls.pamxctrl[i].dwControlID; - controlDetails.cChannels = 1; - - if ((mixerLineControls.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_FADER) || - (mixerLineControls.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)) { - MIXERCONTROLDETAILS_UNSIGNED controlDetailsUnsigned; - controlDetailsUnsigned.dwValue = qBound(DWORD(0), DWORD(65535.0 * volume + 0.5), DWORD(65535)); - controlDetails.cMultipleItems = 0; - controlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); - controlDetails.paDetails = &controlDetailsUnsigned; - mixerSetControlDetails(mixerID, &controlDetails, MIXER_SETCONTROLDETAILSF_VALUE); - } - } -} - -qreal QWindowsAudioSource::volume() const -{ - for (DWORD i = 0; i < mixerLineControls.cControls; i++) { - if ((mixerLineControls.pamxctrl[i].dwControlType != MIXERCONTROL_CONTROLTYPE_FADER) && - (mixerLineControls.pamxctrl[i].dwControlType != MIXERCONTROL_CONTROLTYPE_VOLUME)) { - continue; - } - - MIXERCONTROLDETAILS controlDetails; - controlDetails.cbStruct = sizeof(controlDetails); - controlDetails.dwControlID = mixerLineControls.pamxctrl[i].dwControlID; - controlDetails.cChannels = 1; - controlDetails.cMultipleItems = 0; - controlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); - MIXERCONTROLDETAILS_UNSIGNED detailsUnsigned; - controlDetails.paDetails = &detailsUnsigned; - memset(controlDetails.paDetails, 0, controlDetails.cbDetails); - - MMRESULT result = mixerGetControlDetails(mixerID, &controlDetails, MIXER_GETCONTROLDETAILSF_VALUE); - if (result != MMSYSERR_NOERROR) - continue; - if (controlDetails.cbDetails < sizeof(MIXERCONTROLDETAILS_UNSIGNED)) - continue; - return detailsUnsigned.dwValue / 65535.0; - } - - return qFuzzyCompare(cachedVolume, qreal(-1.0f)) ? 1.0f : cachedVolume; -} - -void QWindowsAudioSource::setFormat(const QAudioFormat& fmt) -{ - if (deviceState == QAudio::StoppedState) - settings = fmt; -} - -QAudioFormat QWindowsAudioSource::format() const -{ - return settings; -} - -void QWindowsAudioSource::start(QIODevice* device) -{ - if(deviceState != QAudio::StoppedState) - close(); - - if(!pullMode && audioSource) - delete audioSource; - - pullMode = true; - audioSource = device; - - deviceState = QAudio::ActiveState; - - if(!open()) - return; - - emit stateChanged(deviceState); -} - -QIODevice* QWindowsAudioSource::start() -{ - if(deviceState != QAudio::StoppedState) - close(); - - if(!pullMode && audioSource) - delete audioSource; - - pullMode = false; - audioSource = new InputPrivate(this); - audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered); - - deviceState = QAudio::IdleState; - - if(!open()) - return 0; - - emit stateChanged(deviceState); - - return audioSource; -} - -void QWindowsAudioSource::stop() -{ - if(deviceState == QAudio::StoppedState) - return; - - close(); - emit stateChanged(deviceState); -} - -bool QWindowsAudioSource::open() -{ -#ifdef DEBUG_AUDIO - QTime now(QTime::currentTime()); - qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; -#endif - header = 0; - - period_size = 0; - - if (!qt_convertFormat(settings, &wfx)) { - qWarning("QAudioSource: open error, invalid format."); - } else if (buffer_size == 0) { - buffer_size - = (settings.sampleRate() - * settings.channelCount() - * settings.bytesPerSample() - + 39) / 5; - period_size = buffer_size / 5; - } else { - period_size = buffer_size / 5; - } - - if (period_size == 0) { - errorState = QAudio::OpenError; - deviceState = QAudio::StoppedState; - emit stateChanged(deviceState); - return false; - } - - elapsedTimeOffset = 0; - - if (waveInOpen(&hWaveIn, UINT_PTR(m_deviceId), &wfx.Format, - (DWORD_PTR)&waveInProc, - (DWORD_PTR) this, - CALLBACK_FUNCTION) != MMSYSERR_NOERROR) { - errorState = QAudio::OpenError; - deviceState = QAudio::StoppedState; - emit stateChanged(deviceState); - qWarning("QAudioSource: failed to open audio device"); - return false; - } - waveBlocks = allocateBlocks(period_size, buffer_size/period_size); - waveBlockOffset = 0; - - if(waveBlocks == 0) { - errorState = QAudio::OpenError; - deviceState = QAudio::StoppedState; - emit stateChanged(deviceState); - qWarning("QAudioSource: failed to allocate blocks. open failed"); - return false; - } - - mutex.lock(); - waveFreeBlockCount = buffer_size/period_size; - mutex.unlock(); - - for(int i=0; i<buffer_size/period_size; i++) { - result = waveInAddBuffer(hWaveIn, &waveBlocks[i], sizeof(WAVEHDR)); - if(result != MMSYSERR_NOERROR) { - qWarning("QAudioSource: failed to setup block %d,err=%d",i,result); - errorState = QAudio::OpenError; - deviceState = QAudio::StoppedState; - emit stateChanged(deviceState); - return false; - } - } - result = waveInStart(hWaveIn); - if(result) { - qWarning("QAudioSource: failed to start audio input"); - errorState = QAudio::OpenError; - deviceState = QAudio::StoppedState; - emit stateChanged(deviceState); - return false; - } - elapsedTimeOffset = 0; - totalTimeValue = 0; - errorState = QAudio::NoError; - initMixer(); - return true; -} - -void QWindowsAudioSource::close() -{ - if(deviceState == QAudio::StoppedState) - return; - - deviceState = QAudio::StoppedState; - waveInReset(hWaveIn); - - mutex.lock(); - for (int i=0; i<waveFreeBlockCount; i++) - waveInUnprepareHeader(hWaveIn,&waveBlocks[i],sizeof(WAVEHDR)); - freeBlocks(waveBlocks); - mutex.unlock(); - - waveInClose(hWaveIn); - closeMixer(); - - int count = 0; - while(!finished && count < 500) { - count++; - Sleep(10); - } -} - -void QWindowsAudioSource::initMixer() -{ - // Get the Mixer ID from the Sound Device ID - UINT mixerIntID = 0; - if (mixerGetID(reinterpret_cast<HMIXEROBJ>(hWaveIn), - &mixerIntID, MIXER_OBJECTF_HWAVEIN) != MMSYSERR_NOERROR) - return; - mixerID = reinterpret_cast<HMIXEROBJ>(quintptr(mixerIntID)); - - // Get the Destination (Recording) Line Information - MIXERLINE mixerLine; - mixerLine.cbStruct = sizeof(MIXERLINE); - mixerLine.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN; - if (mixerGetLineInfo(mixerID, &mixerLine, MIXER_GETLINEINFOF_COMPONENTTYPE) != MMSYSERR_NOERROR) - return; - - // Set all the Destination (Recording) Line Controls - if (mixerLine.cControls > 0) { - mixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS); - mixerLineControls.dwLineID = mixerLine.dwLineID; - mixerLineControls.cControls = mixerLine.cControls; - mixerLineControls.cbmxctrl = sizeof(MIXERCONTROL); - mixerLineControls.pamxctrl = new MIXERCONTROL[mixerLineControls.cControls]; - if (mixerGetLineControls(mixerID, &mixerLineControls, MIXER_GETLINECONTROLSF_ALL) != MMSYSERR_NOERROR) - closeMixer(); - else if (!qFuzzyCompare(cachedVolume, qreal(-1.0f))) - setVolume(cachedVolume); - } -} - -void QWindowsAudioSource::closeMixer() -{ - delete[] mixerLineControls.pamxctrl; - memset(&mixerLineControls, 0, sizeof(mixerLineControls)); -} - -qsizetype QWindowsAudioSource::bytesReady() const -{ - if (period_size == 0 || buffer_size == 0) - return 0; - if (deviceState == QAudio::StoppedState || deviceState == QAudio::SuspendedState) - return 0; - - int buf = ((buffer_size/period_size)-waveFreeBlockCount)*period_size; - if(buf < 0) - buf = 0; - return buf; -} - -qint64 QWindowsAudioSource::read(char* data, qint64 len) -{ - bool done = false; - - char* p = data; - qint64 l = 0; - qint64 written = 0; - while(!done) { - // Read in some audio data - if(waveBlocks[header].dwBytesRecorded > 0 && waveBlocks[header].dwFlags & WHDR_DONE) { - if(pullMode) { - l = audioSource->write(waveBlocks[header].lpData + waveBlockOffset, - waveBlocks[header].dwBytesRecorded - waveBlockOffset); -#ifdef DEBUG_AUDIO - qDebug()<<"IN: "<<waveBlocks[header].dwBytesRecorded<<", OUT: "<<l; -#endif - if(l < 0) { - // error - qWarning("QAudioSource: IOError"); - errorState = QAudio::IOError; - - } else if(l == 0) { - // cant write to IODevice - qWarning("QAudioSource: IOError, can't write to QIODevice"); - errorState = QAudio::IOError; - - } else { - totalTimeValue += l; - errorState = QAudio::NoError; - if (deviceState != QAudio::ActiveState) { - deviceState = QAudio::ActiveState; - emit stateChanged(deviceState); - } - resuming = false; - } - } else { - l = qMin<qint64>(len, waveBlocks[header].dwBytesRecorded - waveBlockOffset); - // push mode - memcpy(p, waveBlocks[header].lpData + waveBlockOffset, l); - - len -= l; - -#ifdef DEBUG_AUDIO - qDebug()<<"IN: "<<waveBlocks[header].dwBytesRecorded<<", OUT: "<<l; -#endif - totalTimeValue += l; - errorState = QAudio::NoError; - if (deviceState != QAudio::ActiveState) { - deviceState = QAudio::ActiveState; - emit stateChanged(deviceState); - } - resuming = false; - } - } else { - //no data, not ready yet, next time - break; - } - - if (l < waveBlocks[header].dwBytesRecorded - waveBlockOffset) { - waveBlockOffset += l; - done = true; - } else { - waveBlockOffset = 0; - - waveInUnprepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR)); - - mutex.lock(); - waveFreeBlockCount++; - mutex.unlock(); - - waveBlocks[header].dwBytesRecorded=0; - waveBlocks[header].dwFlags = 0L; - result = waveInPrepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR)); - if(result != MMSYSERR_NOERROR) { - result = waveInPrepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR)); - qWarning("QAudioSource: failed to prepare block %d,err=%d",header,result); - errorState = QAudio::IOError; - - mutex.lock(); - waveFreeBlockCount--; - mutex.unlock(); - - return 0; - } - result = waveInAddBuffer(hWaveIn, &waveBlocks[header], sizeof(WAVEHDR)); - if(result != MMSYSERR_NOERROR) { - qWarning("QAudioSource: failed to setup block %d,err=%d",header,result); - errorState = QAudio::IOError; - - mutex.lock(); - waveFreeBlockCount--; - mutex.unlock(); - - return 0; - } - header++; - if(header >= buffer_size/period_size) - header = 0; - p+=l; - - mutex.lock(); - if(!pullMode) { - if(len < period_size || waveFreeBlockCount == buffer_size/period_size) - done = true; - } else { - if(waveFreeBlockCount == buffer_size/period_size) - done = true; - } - mutex.unlock(); - } - - written+=l; - } -#ifdef DEBUG_AUDIO - qDebug()<<"read in len="<<written; -#endif - return written; -} - -void QWindowsAudioSource::resume() -{ - if (deviceState == QAudio::SuspendedState) { - deviceState = QAudio::ActiveState; - for(int i=0; i<buffer_size/period_size; i++) { - result = waveInAddBuffer(hWaveIn, &waveBlocks[i], sizeof(WAVEHDR)); - if(result != MMSYSERR_NOERROR) { - qWarning("QAudioSource: failed to setup block %d,err=%d",i,result); - errorState = QAudio::OpenError; - deviceState = QAudio::StoppedState; - emit stateChanged(deviceState); - return; - } - } - - mutex.lock(); - waveFreeBlockCount = buffer_size/period_size; - mutex.unlock(); - - header = 0; - resuming = true; - waveBlockOffset = 0; - waveInStart(hWaveIn); - QTimer::singleShot(20,this,SLOT(feedback())); - emit stateChanged(deviceState); - } -} - -void QWindowsAudioSource::setBufferSize(qsizetype value) -{ - buffer_size = value; -} - -qsizetype QWindowsAudioSource::bufferSize() const -{ - return buffer_size; -} - -qint64 QWindowsAudioSource::processedUSecs() const -{ - if (deviceState == QAudio::StoppedState) - return 0; - qint64 result = qint64(1000000) * totalTimeValue / - settings.bytesPerFrame() / settings.sampleRate(); - - return result; -} - -void QWindowsAudioSource::suspend() -{ - if(deviceState == QAudio::ActiveState) { - deviceState = QAudio::SuspendedState; - waveInReset(hWaveIn); - emit stateChanged(deviceState); - } -} - -void QWindowsAudioSource::feedback() -{ -#ifdef DEBUG_AUDIO - QTime now(QTime::currentTime()); - qDebug()<<now.second()<<"s "<<now.msec()<<"ms :feedback() INPUT "<<this; -#endif - if(deviceState != QAudio::StoppedState && deviceState != QAudio::SuspendedState) - QMetaObject::invokeMethod(this, "deviceReady", Qt::QueuedConnection); -} - -bool QWindowsAudioSource::deviceReady() -{ - bytesAvailable = bytesReady(); -#ifdef DEBUG_AUDIO - QTime now(QTime::currentTime()); - qDebug()<<now.second()<<"s "<<now.msec()<<"ms :deviceReady() INPUT" << bytesReady(); -#endif - if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState) - return true; - - if(pullMode) { - // reads some audio data and writes it to QIODevice - read(0, buffer_size); - } else { - // emits readyRead() so user will call read() on QIODevice to get some audio data - InputPrivate* a = qobject_cast<InputPrivate*>(audioSource); - a->trigger(); - } - - return true; -} - -void QWindowsAudioSource::reset() -{ - stop(); - if (period_size > 0) - waveFreeBlockCount = buffer_size / period_size; -} - -InputPrivate::InputPrivate(QWindowsAudioSource* audio) -{ - audioDevice = qobject_cast<QWindowsAudioSource*>(audio); -} - -InputPrivate::~InputPrivate() {} - -qint64 InputPrivate::readData( char* data, qint64 len) -{ - // push mode, user read() called - if(audioDevice->deviceState != QAudio::ActiveState && - audioDevice->deviceState != QAudio::IdleState) - return 0; - // Read in some audio data - return audioDevice->read(data,len); -} - -qint64 InputPrivate::writeData(const char* data, qint64 len) -{ - Q_UNUSED(data); - Q_UNUSED(len); - - emit readyRead(); - return 0; -} - -void InputPrivate::trigger() -{ - emit readyRead(); -} - -QT_END_NAMESPACE - -#include "moc_qwindowsaudiosource_p.cpp" diff --git a/src/multimedia/platform/windows/audio/qwindowsaudiosource_p.h b/src/multimedia/platform/windows/audio/qwindowsaudiosource_p.h deleted file mode 100644 index ff68daab6..000000000 --- a/src/multimedia/platform/windows/audio/qwindowsaudiosource_p.h +++ /dev/null @@ -1,170 +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$ -** -****************************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of other Qt classes. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#ifndef QWINDOWSAUDIOINPUT_H -#define QWINDOWSAUDIOINPUT_H - -#include "qwindowsaudioutils_p.h" - -#include <QtCore/qfile.h> -#include <QtCore/qdebug.h> -#include <QtCore/qelapsedtimer.h> -#include <QtCore/qstring.h> -#include <QtCore/qstringlist.h> -#include <QtCore/qdatetime.h> -#include <QtCore/qmutex.h> - -#include <QtMultimedia/qaudio.h> -#include <QtMultimedia/qaudiodevice.h> -#include <private/qaudiosystem_p.h> - - -QT_BEGIN_NAMESPACE - - -// For compat with 4.6 -#if !defined(QT_WIN_CALLBACK) -# if defined(Q_CC_MINGW) -# define QT_WIN_CALLBACK CALLBACK __attribute__ ((force_align_arg_pointer)) -# else -# define QT_WIN_CALLBACK CALLBACK -# endif -#endif - -class QWindowsAudioSource : public QPlatformAudioSource -{ - Q_OBJECT -public: - QWindowsAudioSource(int deviceId); - ~QWindowsAudioSource(); - - qint64 read(char* data, qint64 len); - - void setFormat(const QAudioFormat& fmt); - QAudioFormat format() const; - QIODevice* start(); - void start(QIODevice* device); - void stop(); - void reset(); - void suspend(); - void resume(); - qsizetype bytesReady() const; - void setBufferSize(qsizetype value); - qsizetype bufferSize() const; - qint64 processedUSecs() const; - QAudio::Error error() const; - QAudio::State state() const; - void setVolume(qreal volume); - qreal volume() const; - - QIODevice* audioSource; - QAudioFormat settings; - QAudio::Error errorState; - QAudio::State deviceState; - -private: - qint32 buffer_size; - qint32 period_size; - qint32 header; - int m_deviceId; - int bytesAvailable; - qint64 elapsedTimeOffset; - qint64 totalTimeValue; - bool pullMode; - bool resuming; - WAVEFORMATEXTENSIBLE wfx; - HWAVEIN hWaveIn; - MMRESULT result; - WAVEHDR* waveBlocks; - volatile bool finished; - volatile int waveFreeBlockCount; - int waveBlockOffset; - - QMutex mutex; - static void QT_WIN_CALLBACK waveInProc( HWAVEIN hWaveIn, UINT uMsg, - DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 ); - - WAVEHDR* allocateBlocks(int size, int count); - void freeBlocks(WAVEHDR* blockArray); - bool open(); - void close(); - - void initMixer(); - void closeMixer(); - HMIXEROBJ mixerID; - MIXERLINECONTROLS mixerLineControls; - qreal cachedVolume; - -private slots: - void feedback(); - bool deviceReady(); - -signals: - void processMore(); -}; - -class InputPrivate : public QIODevice -{ - Q_OBJECT -public: - InputPrivate(QWindowsAudioSource* audio); - ~InputPrivate(); - - qint64 readData( char* data, qint64 len); - qint64 writeData(const char* data, qint64 len); - - void trigger(); -private: - QWindowsAudioSource *audioDevice; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/windows/audio/qwindowsaudioutils.cpp b/src/multimedia/platform/windows/audio/qwindowsaudioutils.cpp deleted file mode 100644 index 69e50a9a6..000000000 --- a/src/multimedia/platform/windows/audio/qwindowsaudioutils.cpp +++ /dev/null @@ -1,114 +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$ -** -****************************************************************************/ - -#include "qwindowsaudioutils_p.h" - -#ifndef SPEAKER_FRONT_LEFT - #define SPEAKER_FRONT_LEFT 0x00000001 - #define SPEAKER_FRONT_RIGHT 0x00000002 - #define SPEAKER_FRONT_CENTER 0x00000004 - #define SPEAKER_LOW_FREQUENCY 0x00000008 - #define SPEAKER_BACK_LEFT 0x00000010 - #define SPEAKER_BACK_RIGHT 0x00000020 - #define SPEAKER_FRONT_LEFT_OF_CENTER 0x00000040 - #define SPEAKER_FRONT_RIGHT_OF_CENTER 0x00000080 - #define SPEAKER_BACK_CENTER 0x00000100 - #define SPEAKER_SIDE_LEFT 0x00000200 - #define SPEAKER_SIDE_RIGHT 0x00000400 - #define SPEAKER_TOP_CENTER 0x00000800 - #define SPEAKER_TOP_FRONT_LEFT 0x00001000 - #define SPEAKER_TOP_FRONT_CENTER 0x00002000 - #define SPEAKER_TOP_FRONT_RIGHT 0x00004000 - #define SPEAKER_TOP_BACK_LEFT 0x00008000 - #define SPEAKER_TOP_BACK_CENTER 0x00010000 - #define SPEAKER_TOP_BACK_RIGHT 0x00020000 - #define SPEAKER_RESERVED 0x7FFC0000 - #define SPEAKER_ALL 0x80000000 -#endif - -#ifndef WAVE_FORMAT_EXTENSIBLE - #define WAVE_FORMAT_EXTENSIBLE 0xFFFE -#endif - -#ifndef WAVE_FORMAT_IEEE_FLOAT - #define WAVE_FORMAT_IEEE_FLOAT 0x0003 -#endif - -static const GUID _KSDATAFORMAT_SUBTYPE_PCM = { - 0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - -static const GUID _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { - 0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - -QT_BEGIN_NAMESPACE - -bool qt_convertFormat(const QAudioFormat &format, WAVEFORMATEXTENSIBLE *wfx) -{ - if (!wfx - || !format.isValid() - || format.sampleRate() <= 0 - || format.channelCount() <= 0) { - return false; - } - - wfx->Format.nSamplesPerSec = format.sampleRate(); - wfx->Format.wBitsPerSample = wfx->Samples.wValidBitsPerSample = format.bytesPerSample()*8; - wfx->Format.nChannels = format.channelCount(); - wfx->Format.nBlockAlign = (wfx->Format.wBitsPerSample / 8) * wfx->Format.nChannels; - wfx->Format.nAvgBytesPerSec = wfx->Format.nBlockAlign * wfx->Format.nSamplesPerSec; - wfx->Format.cbSize = 0; - - if (format.sampleFormat() == QAudioFormat::Float) { - wfx->Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; - wfx->SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - } else { - wfx->Format.wFormatTag = WAVE_FORMAT_PCM; - wfx->SubFormat = _KSDATAFORMAT_SUBTYPE_PCM; - } - - if (format.channelCount() > 2) { - wfx->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - wfx->Format.cbSize = 22; - wfx->dwChannelMask = 0xFFFFFFFF >> (32 - format.channelCount()); - } - - return true; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/windows/audio/qwindowsaudioutils_p.h b/src/multimedia/platform/windows/audio/qwindowsaudioutils_p.h deleted file mode 100644 index 15bd50b2b..000000000 --- a/src/multimedia/platform/windows/audio/qwindowsaudioutils_p.h +++ /dev/null @@ -1,153 +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$ -** -****************************************************************************/ - -#ifndef QWINDOWSAUDIOUTILS_H -#define QWINDOWSAUDIOUTILS_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qaudioformat.h> -#include <QtCore/qt_windows.h> -#include <mmsystem.h> - -#ifndef _WAVEFORMATEXTENSIBLE_ - - #define _WAVEFORMATEXTENSIBLE_ - typedef struct - { - WAVEFORMATEX Format; // Base WAVEFORMATEX data - union - { - WORD wValidBitsPerSample; // Valid bits in each sample container - WORD wSamplesPerBlock; // Samples per block of audio data; valid - // if wBitsPerSample=0 (but rarely used). - WORD wReserved; // Zero if neither case above applies. - } Samples; - DWORD dwChannelMask; // Positions of the audio channels - GUID SubFormat; // Format identifier GUID - } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE, *LPPWAVEFORMATEXTENSIBLE; - typedef const WAVEFORMATEXTENSIBLE* LPCWAVEFORMATEXTENSIBLE; - -#endif - -#if defined(Q_CC_MINGW) && !defined(__MINGW64_VERSION_MAJOR) -struct IBaseFilter; // Needed for strmif.h from stock MinGW. -struct _DDPIXELFORMAT; -typedef struct _DDPIXELFORMAT* LPDDPIXELFORMAT; -#endif - -#include <strmif.h> -#if !defined(Q_CC_MINGW) || defined(__MINGW64_VERSION_MAJOR) -# include <uuids.h> -#else - -extern GUID CLSID_AudioInputDeviceCategory; -extern GUID CLSID_AudioRendererCategory; -extern GUID IID_ICreateDevEnum; -extern GUID CLSID_SystemDeviceEnum; - -#ifndef __ICreateDevEnum_INTERFACE_DEFINED__ -#define __ICreateDevEnum_INTERFACE_DEFINED__ - -DECLARE_INTERFACE_(ICreateDevEnum, IUnknown) -{ - STDMETHOD(CreateClassEnumerator)(REFCLSID clsidDeviceClass, - IEnumMoniker **ppEnumMoniker, - DWORD dwFlags) PURE; -}; - -#endif // __ICreateDevEnum_INTERFACE_DEFINED__ - -#ifndef __IErrorLog_INTERFACE_DEFINED__ -#define __IErrorLog_INTERFACE_DEFINED__ - -DECLARE_INTERFACE_(IErrorLog, IUnknown) -{ - STDMETHOD(AddError)(THIS_ LPCOLESTR, EXCEPINFO *) PURE; -}; - -#endif /* __IErrorLog_INTERFACE_DEFINED__ */ - -#ifndef __IPropertyBag_INTERFACE_DEFINED__ -#define __IPropertyBag_INTERFACE_DEFINED__ - -const GUID IID_IPropertyBag = {0x55272A00, 0x42CB, 0x11CE, {0x81, 0x35, 0x00, 0xAA, 0x00, 0x4B, 0xB8, 0x51}}; - -DECLARE_INTERFACE_(IPropertyBag, IUnknown) -{ - STDMETHOD(Read)(THIS_ LPCOLESTR, VARIANT *, IErrorLog *) PURE; - STDMETHOD(Write)(THIS_ LPCOLESTR, VARIANT *) PURE; -}; - -#endif /* __IPropertyBag_INTERFACE_DEFINED__ */ - -#endif // defined(Q_CC_MINGW) && !defined(__MINGW64_VERSION_MAJOR) - -// For mingw toolchain mmsystem.h only defines half the defines, so add if needed. -#ifndef WAVE_FORMAT_44M08 -#define WAVE_FORMAT_44M08 0x00000100 -#define WAVE_FORMAT_44S08 0x00000200 -#define WAVE_FORMAT_44M16 0x00000400 -#define WAVE_FORMAT_44S16 0x00000800 -#define WAVE_FORMAT_48M08 0x00001000 -#define WAVE_FORMAT_48S08 0x00002000 -#define WAVE_FORMAT_48M16 0x00004000 -#define WAVE_FORMAT_48S16 0x00008000 -#define WAVE_FORMAT_96M08 0x00010000 -#define WAVE_FORMAT_96S08 0x00020000 -#define WAVE_FORMAT_96M16 0x00040000 -#define WAVE_FORMAT_96S16 0x00080000 -#endif - -QT_BEGIN_NAMESPACE - -bool qt_convertFormat(const QAudioFormat &format, WAVEFORMATEXTENSIBLE *wfx); - -QT_END_NAMESPACE - -#endif // QWINDOWSAUDIOUTILS_H diff --git a/src/multimedia/platform/windows/common/mfmetadata.cpp b/src/multimedia/platform/windows/common/mfmetadata.cpp deleted file mode 100644 index ebc884759..000000000 --- a/src/multimedia/platform/windows/common/mfmetadata.cpp +++ /dev/null @@ -1,521 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Mobility Components. -** -** $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$ -** -****************************************************************************/ - -#include <qmediametadata.h> -#include <qdatetime.h> -#include <qimage.h> -#include <quuid.h> - -#include <mfapi.h> -#include <mfidl.h> -#include <propvarutil.h> -#include <propkey.h> - -#include "qwindowsmultimediautils_p.h" -#include "mfmetadata_p.h" - -//#define DEBUG_MEDIAFOUNDATION - -static QString nameForGUID(GUID guid) -{ - // Audio formats - if (guid == MFAudioFormat_AAC) - return QStringLiteral("MPEG AAC Audio"); - else if (guid == MFAudioFormat_ADTS) - return QStringLiteral("MPEG ADTS AAC Audio"); - else if (guid == MFAudioFormat_Dolby_AC3_SPDIF) - return QStringLiteral("Dolby AC-3 SPDIF"); - else if (guid == MFAudioFormat_DRM) - return QStringLiteral("DRM"); - else if (guid == MFAudioFormat_DTS) - return QStringLiteral("Digital Theater Systems Audio (DTS)"); - else if (guid == MFAudioFormat_Float) - return QStringLiteral("IEEE Float Audio"); - else if (guid == MFAudioFormat_MP3) - return QStringLiteral("MPEG Audio Layer-3 (MP3)"); - else if (guid == MFAudioFormat_MPEG) - return QStringLiteral("MPEG-1 Audio"); - else if (guid == MFAudioFormat_MSP1) - return QStringLiteral("Windows Media Audio Voice"); - else if (guid == MFAudioFormat_PCM) - return QStringLiteral("Uncompressed PCM Audio"); - else if (guid == MFAudioFormat_WMASPDIF) - return QStringLiteral("Windows Media Audio 9 SPDIF"); - else if (guid == MFAudioFormat_WMAudioV8) - return QStringLiteral("Windows Media Audio 8 (WMA2)"); - else if (guid == MFAudioFormat_WMAudioV9) - return QStringLiteral("Windows Media Audio 9 (WMA3"); - else if (guid == MFAudioFormat_WMAudio_Lossless) - return QStringLiteral("Windows Media Audio 9 Lossless"); - - // Video formats - if (guid == MFVideoFormat_DV25) - return QStringLiteral("DVCPRO 25 (DV25)"); - else if (guid == MFVideoFormat_DV50) - return QStringLiteral("DVCPRO 50 (DV50)"); - else if (guid == MFVideoFormat_DVC) - return QStringLiteral("DVC/DV Video"); - else if (guid == MFVideoFormat_DVH1) - return QStringLiteral("DVCPRO 100 (DVH1)"); - else if (guid == MFVideoFormat_DVHD) - return QStringLiteral("HD-DVCR (DVHD)"); - else if (guid == MFVideoFormat_DVSD) - return QStringLiteral("SDL-DVCR (DVSD)"); - else if (guid == MFVideoFormat_DVSL) - return QStringLiteral("SD-DVCR (DVSL)"); - else if (guid == MFVideoFormat_H264) - return QStringLiteral("H.264 Video"); - else if (guid == MFVideoFormat_M4S2) - return QStringLiteral("MPEG-4 part 2 Video (M4S2)"); - else if (guid == MFVideoFormat_MJPG) - return QStringLiteral("Motion JPEG (MJPG)"); - else if (guid == MFVideoFormat_MP43) - return QStringLiteral("Microsoft MPEG 4 version 3 (MP43)"); - else if (guid == MFVideoFormat_MP4S) - return QStringLiteral("ISO MPEG 4 version 1 (MP4S)"); - else if (guid == MFVideoFormat_MP4V) - return QStringLiteral("MPEG-4 part 2 Video (MP4V)"); - else if (guid == MFVideoFormat_MPEG2) - return QStringLiteral("MPEG-2 Video"); - else if (guid == MFVideoFormat_MPG1) - return QStringLiteral("MPEG-1 Video"); - else if (guid == MFVideoFormat_MSS1) - return QStringLiteral("Windows Media Screen 1 (MSS1)"); - else if (guid == MFVideoFormat_MSS2) - return QStringLiteral("Windows Media Video 9 Screen (MSS2)"); - else if (guid == MFVideoFormat_WMV1) - return QStringLiteral("Windows Media Video 7 (WMV1)"); - else if (guid == MFVideoFormat_WMV2) - return QStringLiteral("Windows Media Video 8 (WMV2)"); - else if (guid == MFVideoFormat_WMV3) - return QStringLiteral("Windows Media Video 9 (WMV3)"); - else if (guid == MFVideoFormat_WVC1) - return QStringLiteral("Windows Media Video VC1 (WVC1)"); - - else - return QStringLiteral("Unknown codec"); -} - -static QVariant convertValue(const PROPVARIANT& var) -{ - QVariant value; - switch (var.vt) { - case VT_LPWSTR: - value = QString::fromUtf16(reinterpret_cast<const char16_t *>(var.pwszVal)); - break; - case VT_UI4: - value = uint(var.ulVal); - break; - case VT_UI8: - value = qulonglong(var.uhVal.QuadPart); - break; - case VT_BOOL: - value = bool(var.boolVal); - break; - case VT_FILETIME: - SYSTEMTIME t; - if (!FileTimeToSystemTime(&var.filetime, &t)) - break; - - value = QDateTime(QDate(t.wYear, t.wMonth, t.wDay), - QTime(t.wHour, t.wMinute, t.wSecond, t.wMilliseconds), - Qt::UTC); - break; - case VT_STREAM: - { - STATSTG stat; - if (FAILED(var.pStream->Stat(&stat, STATFLAG_NONAME))) - break; - void *data = malloc(stat.cbSize.QuadPart); - ULONG read = 0; - if (FAILED(var.pStream->Read(data, stat.cbSize.QuadPart, &read))) { - free(data); - break; - } - value = QImage::fromData((const uchar*)data, read); - free(data); - } - break; - case VT_VECTOR | VT_LPWSTR: - QStringList vList; - for (ULONG i = 0; i < var.calpwstr.cElems; ++i) - vList.append(QString::fromUtf16(reinterpret_cast<const char16_t *>(var.calpwstr.pElems[i]))); - value = vList; - break; - } - return value; -} - -static QVariant metaDataValue(IPropertyStore *content, const PROPERTYKEY &key) -{ - QVariant value; - - PROPVARIANT var; - PropVariantInit(&var); - HRESULT hr = S_FALSE; - if (content) - hr = content->GetValue(key, &var); - - if (SUCCEEDED(hr)) { - value = convertValue(var); - - // some metadata needs to be reformatted - if (value.isValid() && content) { - if (key == PKEY_Media_ClassPrimaryID /*QMediaMetaData::MediaType*/) { - QString v = value.toString(); - if (v == QLatin1String("{D1607DBC-E323-4BE2-86A1-48A42A28441E}")) - value = QStringLiteral("Music"); - else if (v == QLatin1String("{DB9830BD-3AB3-4FAB-8A37-1A995F7FF74B}")) - value = QStringLiteral("Video"); - else if (v == QLatin1String("{01CD0F29-DA4E-4157-897B-6275D50C4F11}")) - value = QStringLiteral("Audio"); - else if (v == QLatin1String("{FCF24A76-9A57-4036-990D-E35DD8B244E1}")) - value = QStringLiteral("Other"); - } else if (key == PKEY_Media_Duration) { - // duration is provided in 100-nanosecond units, convert to milliseconds - value = (value.toLongLong() + 10000) / 10000; - } else if (key == PKEY_Video_Compression) { - value = int(QWindowsMultimediaUtils::codecForVideoFormat(value.toUuid())); - } else if (key == PKEY_Audio_Format) { - value = int(QWindowsMultimediaUtils::codecForAudioFormat(value.toUuid())); - } else if (key == PKEY_Video_FrameHeight /*Resolution*/) { - QSize res; - res.setHeight(value.toUInt()); - if (content && SUCCEEDED(content->GetValue(PKEY_Video_FrameWidth, &var))) - res.setWidth(convertValue(var).toUInt()); - value = res; - } else if (key == PKEY_Video_Orientation) { - uint orientation = 0; - if (content && SUCCEEDED(content->GetValue(PKEY_Video_Orientation, &var))) - orientation = convertValue(var).toUInt(); - value = orientation; - } else if (key == PKEY_Video_FrameRate) { - value = value.toReal() / 1000.f; - } - } - } - - PropVariantClear(&var); - return value; -} - -QMediaMetaData MFMetaData::fromNative(IMFMediaSource* mediaSource) -{ - QMediaMetaData metaData; - - IPropertyStore *content = nullptr; - if (!SUCCEEDED(MFGetService(mediaSource, MF_PROPERTY_HANDLER_SERVICE, IID_PPV_ARGS(&content)))) - return metaData; - - Q_ASSERT(content); - DWORD cProps; - if (SUCCEEDED(content->GetCount(&cProps))) { - for (DWORD i = 0; i < cProps; i++) - { - PROPERTYKEY key; - if (FAILED(content->GetAt(i, &key))) - continue; - QMediaMetaData::Key mediaKey; - if (key == PKEY_Author) { - mediaKey = QMediaMetaData::Author; - } else if (key == PKEY_Title) { - mediaKey = QMediaMetaData::Title; -// } else if (key == PKEY_Media_SubTitle) { -// mediaKey = QMediaMetaData::SubTitle; -// } else if (key == PKEY_ParentalRating) { -// mediaKey = QMediaMetaData::ParentalRating; - } else if (key == PKEY_Media_EncodingSettings) { - mediaKey = QMediaMetaData::Description; - } else if (key == PKEY_Copyright) { - mediaKey = QMediaMetaData::Copyright; - } else if (key == PKEY_Comment) { - mediaKey = QMediaMetaData::Comment; - } else if (key == PKEY_Media_ProviderStyle) { - mediaKey = QMediaMetaData::Genre; - } else if (key == PKEY_Media_DateEncoded) { - mediaKey = QMediaMetaData::Date; -// } else if (key == PKEY_Rating) { -// mediaKey = QMediaMetaData::UserRating; -// } else if (key == PKEY_Keywords) { -// mediaKey = QMediaMetaData::Keywords; - } else if (key == PKEY_Language) { - mediaKey = QMediaMetaData::Language; - } else if (key == PKEY_Media_Publisher) { - mediaKey = QMediaMetaData::Publisher; - } else if (key == PKEY_Media_ClassPrimaryID) { - mediaKey = QMediaMetaData::MediaType; - } else if (key == PKEY_Media_Duration) { - mediaKey = QMediaMetaData::Duration; - } else if (key == PKEY_Audio_EncodingBitrate) { - mediaKey = QMediaMetaData::AudioBitRate; - } else if (key == PKEY_Audio_Format) { - mediaKey = QMediaMetaData::AudioCodec; -// } else if (key == PKEY_Media_AverageLevel) { -// mediaKey = QMediaMetaData::AverageLevel; -// } else if (key == PKEY_Audio_ChannelCount) { -// mediaKey = QMediaMetaData::ChannelCount; -// } else if (key == PKEY_Audio_PeakValue) { -// mediaKey = QMediaMetaData::PeakValue; -// } else if (key == PKEY_Audio_SampleRate) { -// mediaKey = QMediaMetaData::SampleRate; - } else if (key == PKEY_Music_AlbumTitle) { - mediaKey = QMediaMetaData::AlbumTitle; - } else if (key == PKEY_Music_AlbumArtist) { - mediaKey = QMediaMetaData::AlbumArtist; - } else if (key == PKEY_Music_Artist) { - mediaKey = QMediaMetaData::ContributingArtist; - } else if (key == PKEY_Music_Composer) { - mediaKey = QMediaMetaData::Composer; -// } else if (key == PKEY_Music_Conductor) { -// mediaKey = QMediaMetaData::Conductor; -// } else if (key == PKEY_Music_Lyrics) { -// mediaKey = QMediaMetaData::Lyrics; -// } else if (key == PKEY_Music_Mood) { -// mediaKey = QMediaMetaData::Mood; - } else if (key == PKEY_Music_TrackNumber) { - mediaKey = QMediaMetaData::TrackNumber; - } else if (key == PKEY_Music_Genre) { - mediaKey = QMediaMetaData::Genre; - } else if (key == PKEY_ThumbnailStream) { - mediaKey = QMediaMetaData::ThumbnailImage; - } else if (key == PKEY_Video_FrameHeight) { - mediaKey = QMediaMetaData::Resolution; - } else if (key == PKEY_Video_Orientation) { - mediaKey = QMediaMetaData::Orientation; -// } else if (key == PKEY_Video_FrameRate) { -// mediaKey = QMediaMetaData::VideoFrameRate; - } else if (key == PKEY_Video_EncodingBitrate) { - mediaKey = QMediaMetaData::VideoBitRate; - } else if (key == PKEY_Video_Compression) { - mediaKey = QMediaMetaData::VideoCodec; -// } else if (key == PKEY_Video_Director) { -// mediaKey = QMediaMetaData::Director; -// } else if (key == PKEY_Media_Writer) { -// mediaKey = QMediaMetaData::Writer; - } else { - continue; - } - metaData.insert(mediaKey, metaDataValue(content, key)); - } - } - - content->Release(); - - return metaData; -} - -static REFPROPERTYKEY propertyKeyForMetaDataKey(QMediaMetaData::Key key) -{ - switch (key) { - case QMediaMetaData::Key::Title: - return PKEY_Title; - case QMediaMetaData::Key::Author: - return PKEY_Author; - case QMediaMetaData::Key::Comment: - return PKEY_Comment; - case QMediaMetaData::Key::Genre: - return PKEY_Music_Genre; - case QMediaMetaData::Key::Copyright: - return PKEY_Copyright; - case QMediaMetaData::Key::Publisher: - return PKEY_Media_Publisher; - case QMediaMetaData::Key::Url: - return PKEY_Media_AuthorUrl; - case QMediaMetaData::Key::AlbumTitle: - return PKEY_Music_AlbumTitle; - case QMediaMetaData::Key::AlbumArtist: - return PKEY_Music_AlbumArtist; - case QMediaMetaData::Key::TrackNumber: - return PKEY_Music_TrackNumber; - case QMediaMetaData::Key::Date: - return PKEY_Media_DateEncoded; - case QMediaMetaData::Key::Composer: - return PKEY_Music_Composer; - case QMediaMetaData::Key::Duration: - return PKEY_Media_Duration; - case QMediaMetaData::Key::Language: - return PKEY_Language; - case QMediaMetaData::Key::Description: - return PKEY_Media_EncodingSettings; - case QMediaMetaData::Key::AudioBitRate: - return PKEY_Audio_EncodingBitrate; - case QMediaMetaData::Key::ContributingArtist: - return PKEY_Music_Artist; - case QMediaMetaData::Key::ThumbnailImage: - return PKEY_ThumbnailStream; - case QMediaMetaData::Key::Orientation: - return PKEY_Video_Orientation; - case QMediaMetaData::Key::VideoFrameRate: - return PKEY_Video_FrameRate; - case QMediaMetaData::Key::VideoBitRate: - return PKEY_Video_EncodingBitrate; - case QMediaMetaData::MediaType: - return PKEY_Media_ClassPrimaryID; - default: - return PKEY_Null; - } -} - -static void setStringProperty(IPropertyStore *content, REFPROPERTYKEY key, const QString &value) -{ - PROPVARIANT propValue = {}; - if (SUCCEEDED(InitPropVariantFromString(reinterpret_cast<LPCWSTR>(value.utf16()), &propValue))) { - if (SUCCEEDED(PSCoerceToCanonicalValue(key, &propValue))) - content->SetValue(key, propValue); - PropVariantClear(&propValue); - } -} - -static void setUInt32Property(IPropertyStore *content, REFPROPERTYKEY key, quint32 value) -{ - PROPVARIANT propValue = {}; - if (SUCCEEDED(InitPropVariantFromUInt32(ULONG(value), &propValue))) { - if (SUCCEEDED(PSCoerceToCanonicalValue(key, &propValue))) - content->SetValue(key, propValue); - PropVariantClear(&propValue); - } -} - -static void setUInt64Property(IPropertyStore *content, REFPROPERTYKEY key, quint64 value) -{ - PROPVARIANT propValue = {}; - if (SUCCEEDED(InitPropVariantFromUInt64(ULONGLONG(value), &propValue))) { - if (SUCCEEDED(PSCoerceToCanonicalValue(key, &propValue))) - content->SetValue(key, propValue); - PropVariantClear(&propValue); - } -} - -static void setFileTimeProperty(IPropertyStore *content, REFPROPERTYKEY key, const FILETIME *ft) -{ - PROPVARIANT propValue = {}; - if (SUCCEEDED(InitPropVariantFromFileTime(ft, &propValue))) { - if (SUCCEEDED(PSCoerceToCanonicalValue(key, &propValue))) - content->SetValue(key, propValue); - PropVariantClear(&propValue); - } -} - -void MFMetaData::toNative(const QMediaMetaData &metaData, IPropertyStore *content) -{ - if (content) { - - for (const auto &key : metaData.keys()) { - - QVariant value = metaData.value(key); - - if (key == QMediaMetaData::Key::MediaType) { - - QString strValue = metaData.stringValue(key); - QString v; - - // Sets property to one of the MediaClassPrimaryID values defined by Microsoft: - // https://docs.microsoft.com/en-us/windows/win32/wmformat/wm-mediaprimaryid - if (strValue == QLatin1String("Music")) - v = QLatin1String("{D1607DBC-E323-4BE2-86A1-48A42A28441E}"); - else if (strValue == QLatin1String("Video")) - v = QLatin1String("{DB9830BD-3AB3-4FAB-8A37-1A995F7FF74B}"); - else if (strValue == QLatin1String("Audio")) - v = QLatin1String("{01CD0F29-DA4E-4157-897B-6275D50C4F11}"); - else - v = QLatin1String("{FCF24A76-9A57-4036-990D-E35DD8B244E1}"); - - setStringProperty(content, PKEY_Media_ClassPrimaryID, v); - - } else if (key == QMediaMetaData::Key::Duration) { - - setUInt64Property(content, PKEY_Media_Duration, value.toULongLong() * 10000); - - } else if (key == QMediaMetaData::Key::Resolution) { - - QSize res = value.toSize(); - setUInt32Property(content, PKEY_Video_FrameWidth, quint32(res.width())); - setUInt32Property(content, PKEY_Video_FrameHeight, quint32(res.height())); - - } else if (key == QMediaMetaData::Key::Orientation) { - - setUInt32Property(content, PKEY_Video_Orientation, value.toUInt()); - - } else if (key == QMediaMetaData::Key::VideoFrameRate) { - - qreal fps = value.toReal(); - setUInt32Property(content, PKEY_Video_FrameRate, quint32(fps * 1000)); - - } else if (key == QMediaMetaData::Key::TrackNumber) { - - setUInt32Property(content, PKEY_Music_TrackNumber, value.toUInt()); - - } else if (key == QMediaMetaData::Key::AudioBitRate) { - - setUInt32Property(content, PKEY_Audio_EncodingBitrate, value.toUInt()); - - } else if (key == QMediaMetaData::Key::VideoBitRate) { - - setUInt32Property(content, PKEY_Video_EncodingBitrate, value.toUInt()); - - } else if (key == QMediaMetaData::Key::Date) { - - // Convert QDateTime to FILETIME by converting to 100-nsecs since - // 01/01/1970 UTC and adding the difference from 1601 to 1970. - ULARGE_INTEGER t = {}; - t.QuadPart = ULONGLONG(value.toDateTime().toUTC().toMSecsSinceEpoch() * 10000 - + 116444736000000000LL); - - FILETIME ft = {}; - ft.dwHighDateTime = t.HighPart; - ft.dwLowDateTime = t.LowPart; - - setFileTimeProperty(content, PKEY_Media_DateEncoded, &ft); - - } else { - - // By default use as string and let PSCoerceToCanonicalValue() - // do validation and type conversion. - REFPROPERTYKEY propKey = propertyKeyForMetaDataKey(key); - - if (propKey != PKEY_Null) { - QString strValue = metaData.stringValue(key); - if (!strValue.isEmpty()) - setStringProperty(content, propKey, strValue); - } - } - } - } -} - diff --git a/src/multimedia/platform/windows/common/mfmetadata_p.h b/src/multimedia/platform/windows/common/mfmetadata_p.h deleted file mode 100644 index d1846e9c5..000000000 --- a/src/multimedia/platform/windows/common/mfmetadata_p.h +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Mobility Components. -** -** $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$ -** -****************************************************************************/ - -#ifndef MFMETADATACONTROL_H -#define MFMETADATACONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qmediametadata.h> -#include "Mfidl.h" - -QT_USE_NAMESPACE - -class MFMetaData -{ -public: - static QMediaMetaData fromNative(IMFMediaSource* mediaSource); - static void toNative(const QMediaMetaData &metaData, IPropertyStore *content); -}; - -#endif diff --git a/src/multimedia/platform/windows/common/qwindowsiupointer_p.h b/src/multimedia/platform/windows/common/qwindowsiupointer_p.h deleted file mode 100644 index a5c08ccc9..000000000 --- a/src/multimedia/platform/windows/common/qwindowsiupointer_p.h +++ /dev/null @@ -1,90 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QWINDOWSIUPOINTER_H -#define QWINDOWSIUPOINTER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -template <class T> -class QWindowsIUPointer -{ -public: - explicit QWindowsIUPointer(T *ptr = nullptr) : m_ptr(ptr) {} - QWindowsIUPointer(const QWindowsIUPointer<T> &uiPtr) : m_ptr(uiPtr.m_ptr) { if (m_ptr) m_ptr->AddRef(); } - QWindowsIUPointer(QWindowsIUPointer<T> &&uiPtr) : m_ptr(uiPtr.m_ptr) { uiPtr.m_ptr = nullptr; } - ~QWindowsIUPointer() { if (m_ptr) m_ptr->Release(); } - - QWindowsIUPointer& operator=(const QWindowsIUPointer<T> &rhs) { - if (m_ptr) - m_ptr->Release(); - m_ptr = rhs.m_ptr; - m_ptr->AddRef(); - return *this; - } - - QWindowsIUPointer& operator=(QWindowsIUPointer<T> &&rhs) noexcept { - if (m_ptr) - m_ptr->Release(); - m_ptr = rhs.m_ptr; - rhs.m_ptr = nullptr; - return *this; - } - - explicit operator bool() const { return m_ptr != nullptr; } - operator T*() const { return m_ptr; } - T* operator->() const { return m_ptr; } - - T** address() { Q_ASSERT(m_ptr == nullptr); return &m_ptr; } - void reset(T* ptr = nullptr) { if (m_ptr) m_ptr->Release(); m_ptr = ptr; } - -private: - T* m_ptr; -}; - -#endif diff --git a/src/multimedia/platform/windows/common/qwindowsmultimediautils.cpp b/src/multimedia/platform/windows/common/qwindowsmultimediautils.cpp deleted file mode 100644 index ad6a234eb..000000000 --- a/src/multimedia/platform/windows/common/qwindowsmultimediautils.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qwindowsmultimediautils_p.h" - -#include <mfapi.h> -#include <mfidl.h> - -QT_BEGIN_NAMESPACE - -QVideoFrameFormat::PixelFormat QWindowsMultimediaUtils::pixelFormatFromMediaSubtype(const GUID &subtype) -{ - if (subtype == MFVideoFormat_ARGB32) -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - return QVideoFrameFormat::Format_BGRA8888; -#else - return QVideoFrameFormat::Format_ARGB8888; -#endif - if (subtype == MFVideoFormat_RGB32) -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - return QVideoFrameFormat::Format_BGRX8888; -#else - return QVideoFrameFormat::Format_XRGB8888; -#endif - if (subtype == MFVideoFormat_AYUV) - return QVideoFrameFormat::Format_AYUV; - if (subtype == MFVideoFormat_I420) - return QVideoFrameFormat::Format_YUV420P; - if (subtype == MFVideoFormat_UYVY) - return QVideoFrameFormat::Format_UYVY; - if (subtype == MFVideoFormat_YV12) - return QVideoFrameFormat::Format_YV12; - if (subtype == MFVideoFormat_NV12) - return QVideoFrameFormat::Format_NV12; - if (subtype == MFVideoFormat_YUY2) - return QVideoFrameFormat::Format_YUYV; - if (subtype == MFVideoFormat_P010) - return QVideoFrameFormat::Format_P010; - if (subtype == MFVideoFormat_P016) - return QVideoFrameFormat::Format_P016; - if (subtype == MFVideoFormat_L8) - return QVideoFrameFormat::Format_Y8; - if (subtype == MFVideoFormat_L16) - return QVideoFrameFormat::Format_Y16; - - return QVideoFrameFormat::Format_Invalid; -} - -GUID QWindowsMultimediaUtils::videoFormatForCodec(QMediaFormat::VideoCodec codec) -{ - switch (codec) { - case QMediaFormat::VideoCodec::MPEG1: - return MFVideoFormat_MPG1; - case QMediaFormat::VideoCodec::MPEG2: - return MFVideoFormat_MPEG2; - case QMediaFormat::VideoCodec::MPEG4: - return MFVideoFormat_MP4V; - case QMediaFormat::VideoCodec::H264: - return MFVideoFormat_H264; - case QMediaFormat::VideoCodec::H265: - return MFVideoFormat_H265; - case QMediaFormat::VideoCodec::VP8: - return MFVideoFormat_VP80; - case QMediaFormat::VideoCodec::VP9: - return MFVideoFormat_VP90; - case QMediaFormat::VideoCodec::AV1: - return MFVideoFormat_AV1; - case QMediaFormat::VideoCodec::WMV: - return MFVideoFormat_WMV3; - case QMediaFormat::VideoCodec::MotionJPEG: - return MFVideoFormat_MJPG; - default: - return MFVideoFormat_H264; - } -} - -QMediaFormat::VideoCodec QWindowsMultimediaUtils::codecForVideoFormat(GUID format) -{ - if (format == MFVideoFormat_MPG1) - return QMediaFormat::VideoCodec::MPEG1; - if (format == MFVideoFormat_MPEG2) - return QMediaFormat::VideoCodec::MPEG2; - if (format == MFVideoFormat_MP4V - || format == MFVideoFormat_M4S2 - || format == MFVideoFormat_MP4S - || format == MFVideoFormat_MP43) - return QMediaFormat::VideoCodec::MPEG4; - if (format == MFVideoFormat_H264) - return QMediaFormat::VideoCodec::H264; - if (format == MFVideoFormat_H265) - return QMediaFormat::VideoCodec::H265; - if (format == MFVideoFormat_VP80) - return QMediaFormat::VideoCodec::VP8; - if (format == MFVideoFormat_VP90) - return QMediaFormat::VideoCodec::VP9; - if (format == MFVideoFormat_AV1) - return QMediaFormat::VideoCodec::AV1; - if (format == MFVideoFormat_WMV1 - || format == MFVideoFormat_WMV2 - || format == MFVideoFormat_WMV3) - return QMediaFormat::VideoCodec::WMV; - if (format == MFVideoFormat_MJPG) - return QMediaFormat::VideoCodec::MotionJPEG; - return QMediaFormat::VideoCodec::Unspecified; -} - -GUID QWindowsMultimediaUtils::audioFormatForCodec(QMediaFormat::AudioCodec codec) -{ - switch (codec) { - case QMediaFormat::AudioCodec::MP3: - return MFAudioFormat_MP3; - case QMediaFormat::AudioCodec::AAC: - return MFAudioFormat_AAC; - case QMediaFormat::AudioCodec::ALAC: - return MFAudioFormat_ALAC; - case QMediaFormat::AudioCodec::FLAC: - return MFAudioFormat_FLAC; - case QMediaFormat::AudioCodec::Vorbis: - return MFAudioFormat_Vorbis; - case QMediaFormat::AudioCodec::Wave: - return MFAudioFormat_PCM; - case QMediaFormat::AudioCodec::Opus: - return MFAudioFormat_Opus; - case QMediaFormat::AudioCodec::AC3: - return MFAudioFormat_Dolby_AC3; - case QMediaFormat::AudioCodec::EAC3: - return MFAudioFormat_Dolby_DDPlus; - case QMediaFormat::AudioCodec::WMA: - return MFAudioFormat_WMAudioV9; - default: - return MFAudioFormat_AAC; - } -} - -QMediaFormat::AudioCodec QWindowsMultimediaUtils::codecForAudioFormat(GUID format) -{ - if (format == MFAudioFormat_MP3) - return QMediaFormat::AudioCodec::MP3; - if (format == MFAudioFormat_AAC) - return QMediaFormat::AudioCodec::AAC; - if (format == MFAudioFormat_ALAC) - return QMediaFormat::AudioCodec::ALAC; - if (format == MFAudioFormat_FLAC) - return QMediaFormat::AudioCodec::FLAC; - if (format == MFAudioFormat_Vorbis) - return QMediaFormat::AudioCodec::Vorbis; - if (format == MFAudioFormat_PCM) - return QMediaFormat::AudioCodec::Wave; - if (format == MFAudioFormat_Opus) - return QMediaFormat::AudioCodec::Opus; - if (format == MFAudioFormat_Dolby_AC3) - return QMediaFormat::AudioCodec::AC3; - if (format == MFAudioFormat_Dolby_DDPlus) - return QMediaFormat::AudioCodec::EAC3; - if (format == MFAudioFormat_WMAudioV8 - || format == MFAudioFormat_WMAudioV9 - || format == MFAudioFormat_WMAudio_Lossless) - return QMediaFormat::AudioCodec::WMA; - return QMediaFormat::AudioCodec::Unspecified; -} - -GUID QWindowsMultimediaUtils::containerForVideoFileFormat(QMediaFormat::FileFormat format) -{ - switch (format) { - case QMediaFormat::FileFormat::MPEG4: - return MFTranscodeContainerType_MPEG4; - case QMediaFormat::FileFormat::WMV: - return MFTranscodeContainerType_ASF; - case QMediaFormat::FileFormat::AVI: - return MFTranscodeContainerType_AVI; - default: - return MFTranscodeContainerType_MPEG4; - } -} - -GUID QWindowsMultimediaUtils::containerForAudioFileFormat(QMediaFormat::FileFormat format) -{ - switch (format) { - case QMediaFormat::FileFormat::MP3: - return MFTranscodeContainerType_MP3; - case QMediaFormat::FileFormat::AAC: - return MFTranscodeContainerType_ADTS; - case QMediaFormat::FileFormat::Mpeg4Audio: - return MFTranscodeContainerType_MPEG4; - case QMediaFormat::FileFormat::WMA: - return MFTranscodeContainerType_ASF; - case QMediaFormat::FileFormat::FLAC: - return MFTranscodeContainerType_FLAC; - case QMediaFormat::FileFormat::Wave: - return MFTranscodeContainerType_WAVE; - default: - return MFTranscodeContainerType_MPEG4; - } -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/windows/common/qwindowsmultimediautils_p.h b/src/multimedia/platform/windows/common/qwindowsmultimediautils_p.h deleted file mode 100644 index 100059cf0..000000000 --- a/src/multimedia/platform/windows/common/qwindowsmultimediautils_p.h +++ /dev/null @@ -1,80 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QWINDOWSMULTIMEDIATUTILS_P_H -#define QWINDOWSMULTIMEDIATUTILS_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qtmultimediaglobal_p.h> -#include <private/qplatformmediaformatinfo_p.h> -#include <qvideoframeformat.h> -#include <guiddef.h> - -QT_BEGIN_NAMESPACE - -namespace QWindowsMultimediaUtils { - - QVideoFrameFormat::PixelFormat pixelFormatFromMediaSubtype(const GUID &subtype); - - GUID videoFormatForCodec(QMediaFormat::VideoCodec codec); - - QMediaFormat::VideoCodec codecForVideoFormat(GUID format); - - GUID audioFormatForCodec(QMediaFormat::AudioCodec codec); - - QMediaFormat::AudioCodec codecForAudioFormat(GUID format); - - GUID containerForVideoFileFormat(QMediaFormat::FileFormat format); - - GUID containerForAudioFileFormat(QMediaFormat::FileFormat format); -} - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/windows/decoder/mfaudiodecodercontrol.cpp b/src/multimedia/platform/windows/decoder/mfaudiodecodercontrol.cpp deleted file mode 100644 index de1311835..000000000 --- a/src/multimedia/platform/windows/decoder/mfaudiodecodercontrol.cpp +++ /dev/null @@ -1,454 +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$ -** -****************************************************************************/ - -#include "Wmcodecdsp.h" -#include "mfaudiodecodercontrol_p.h" - -MFAudioDecoderControl::MFAudioDecoderControl(QAudioDecoder *parent) - : QPlatformAudioDecoder(parent) - , m_decoderSourceReader(new MFDecoderSourceReader) - , m_sourceResolver(new SourceResolver) - , m_resampler(0) - , m_device(0) - , m_mfInputStreamID(0) - , m_mfOutputStreamID(0) - , m_bufferReady(false) - , m_duration(-1) - , m_position(-1) - , m_loadingSource(false) - , m_mfOutputType(0) - , m_convertSample(0) - , m_sourceReady(false) - , m_resamplerDirty(false) -{ - CoCreateInstance(CLSID_CResamplerMediaObject, NULL, CLSCTX_INPROC_SERVER, IID_IMFTransform, (LPVOID*)(&m_resampler)); - if (!m_resampler) { - qCritical("MFAudioDecoderControl: Failed to create resampler(CLSID_CResamplerMediaObject)!"); - return; - } - m_resampler->AddInputStreams(1, &m_mfInputStreamID); - - connect(m_sourceResolver, SIGNAL(mediaSourceReady()), this, SLOT(handleMediaSourceReady())); - connect(m_sourceResolver, SIGNAL(error(long)), this, SLOT(handleMediaSourceError(long))); - connect(m_decoderSourceReader, SIGNAL(finished()), this, SLOT(handleSourceFinished())); - - QAudioFormat defaultFormat; - setAudioFormat(defaultFormat); -} - -MFAudioDecoderControl::~MFAudioDecoderControl() -{ - if (m_mfOutputType) - m_mfOutputType->Release(); - m_decoderSourceReader->shutdown(); - m_decoderSourceReader->Release(); - m_sourceResolver->Release(); - if (m_resampler) - m_resampler->Release(); -} - -QUrl MFAudioDecoderControl::source() const -{ - return m_source; -} - -void MFAudioDecoderControl::onSourceCleared() -{ - bool positionDirty = false; - bool durationDirty = false; - if (m_position != -1) { - m_position = -1; - positionDirty = true; - } - if (m_duration != -1) { - m_duration = -1; - durationDirty = true; - } - if (positionDirty) - positionChanged(m_position); - if (durationDirty) - durationChanged(m_duration); -} - -void MFAudioDecoderControl::setSource(const QUrl &fileName) -{ - if (!m_device && m_source == fileName) - return; - m_sourceReady = false; - m_sourceResolver->cancel(); - m_decoderSourceReader->setSource(nullptr, m_audioFormat); - m_device = 0; - m_source = fileName; - if (!m_source.isEmpty()) { - m_sourceResolver->shutdown(); - m_sourceResolver->load(m_source, 0); - m_loadingSource = true; - } else { - onSourceCleared(); - } - sourceChanged(); -} - -QIODevice* MFAudioDecoderControl::sourceDevice() const -{ - return m_device; -} - -void MFAudioDecoderControl::setSourceDevice(QIODevice *device) -{ - if (m_device == device && m_source.isEmpty()) - return; - m_sourceReady = false; - m_sourceResolver->cancel(); - m_decoderSourceReader->setSource(nullptr, m_audioFormat); - m_source.clear(); - m_device = device; - if (m_device) { - m_sourceResolver->shutdown(); - m_sourceResolver->load(QUrl(), m_device); - m_loadingSource = true; - } else { - onSourceCleared(); - } - sourceChanged(); -} - -void MFAudioDecoderControl::updateResamplerOutputType() -{ - m_resamplerDirty = false; - if (m_audioFormat == m_sourceOutputFormat) - return; - HRESULT hr = m_resampler->SetOutputType(m_mfOutputStreamID, m_mfOutputType, 0); - if (SUCCEEDED(hr)) { - MFT_OUTPUT_STREAM_INFO streamInfo; - m_resampler->GetOutputStreamInfo(m_mfOutputStreamID, &streamInfo); - if ((streamInfo.dwFlags & (MFT_OUTPUT_STREAM_PROVIDES_SAMPLES | MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES)) == 0) { - //if resampler does not allocate output sample memory, we do it here - if (m_convertSample) { - m_convertSample->Release(); - m_convertSample = 0; - } - if (SUCCEEDED(MFCreateSample(&m_convertSample))) { - IMFMediaBuffer *mbuf = 0;; - if (SUCCEEDED(MFCreateMemoryBuffer(streamInfo.cbSize, &mbuf))) { - m_convertSample->AddBuffer(mbuf); - mbuf->Release(); - } - } - } - } else { - qWarning() << "MFAudioDecoderControl: failed to SetOutputType of resampler" << hr; - } -} - -void MFAudioDecoderControl::handleMediaSourceReady() -{ - m_loadingSource = false; - m_sourceReady = true; - IMFMediaType *mediaType = m_decoderSourceReader->setSource(m_sourceResolver->mediaSource(), m_audioFormat); - m_sourceOutputFormat = QAudioFormat(); - - if (mediaType) { - m_sourceOutputFormat = m_audioFormat; - - UINT32 val = 0; - if (SUCCEEDED(mediaType->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &val))) { - m_sourceOutputFormat.setChannelCount(int(val)); - } - if (SUCCEEDED(mediaType->GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, &val))) { - m_sourceOutputFormat.setSampleRate(int(val)); - } - UINT32 bitsPerSample = 0; - mediaType->GetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, &bitsPerSample); - - GUID subType; - if (SUCCEEDED(mediaType->GetGUID(MF_MT_SUBTYPE, &subType))) { - if (subType == MFAudioFormat_Float) { - m_sourceOutputFormat.setSampleFormat(QAudioFormat::Float); - } else if (bitsPerSample == 8) { - m_sourceOutputFormat.setSampleFormat(QAudioFormat::UInt8); - } else if (bitsPerSample == 16) { - m_sourceOutputFormat.setSampleFormat(QAudioFormat::Int16); - } else if (bitsPerSample == 32){ - m_sourceOutputFormat.setSampleFormat(QAudioFormat::Int32); - } - } - - if (!m_audioFormat.isValid()) - setResamplerOutputFormat(m_sourceOutputFormat); - else { - setResamplerOutputFormat(m_audioFormat); - } - } - - if (m_sourceResolver->mediaSource()) { - if (mediaType && m_resampler) { - HRESULT hr = S_OK; - hr = m_resampler->SetInputType(m_mfInputStreamID, mediaType, 0); - if (SUCCEEDED(hr)) { - updateResamplerOutputType(); - } else { - qWarning() << "MFAudioDecoderControl: failed to SetInputType of resampler" << hr; - } - } - IMFPresentationDescriptor *pd; - if (SUCCEEDED(m_sourceResolver->mediaSource()->CreatePresentationDescriptor(&pd))) { - UINT64 duration = 0; - pd->GetUINT64(MF_PD_DURATION, &duration); - pd->Release(); - duration /= 10000; - if (m_duration != qint64(duration)) { - m_duration = qint64(duration); - durationChanged(m_duration); - } - } - if (isDecoding()) { - activatePipeline(); - } - } else if (isDecoding()) { - setIsDecoding(false); - } -} - -void MFAudioDecoderControl::setResamplerOutputFormat(const QAudioFormat &format) -{ - if (format.isValid()) { - IMFMediaType *mediaType = 0; - MFCreateMediaType(&mediaType); - mediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio); - if (format.sampleFormat() == QAudioFormat::Float) { - mediaType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_Float); - } else { - mediaType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM); - } - - mediaType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, UINT32(format.channelCount())); - mediaType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, UINT32(format.sampleRate())); - UINT32 alignmentBlock = UINT32(format.bytesPerFrame()); - mediaType->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, alignmentBlock); - UINT32 avgBytesPerSec = UINT32(format.sampleRate() * format.bytesPerFrame()); - mediaType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, avgBytesPerSec); - mediaType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, UINT32(format.bytesPerSample()*8)); - mediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); - - if (m_mfOutputType) - m_mfOutputType->Release(); - m_mfOutputType = mediaType; - } else { - if (m_mfOutputType) - m_mfOutputType->Release(); - m_mfOutputType = NULL; - } - - if (m_sourceReady && !isDecoding()) { - updateResamplerOutputType(); - } else { - m_resamplerDirty = true; - } -} - -void MFAudioDecoderControl::handleMediaSourceError(long hr) -{ - Q_UNUSED(hr); - m_loadingSource = false; - m_decoderSourceReader->setSource(nullptr, m_audioFormat); - setIsDecoding(false); -} - -void MFAudioDecoderControl::activatePipeline() -{ - Q_ASSERT(!m_bufferReady); - setIsDecoding(true); - connect(m_decoderSourceReader, SIGNAL(sampleAdded()), this, SLOT(handleSampleAdded())); - if (m_resamplerDirty) { - updateResamplerOutputType(); - } - m_decoderSourceReader->reset(); - m_decoderSourceReader->readNextSample(); - if (m_position != -1) { - m_position = -1; - positionChanged(-1); - } -} - -void MFAudioDecoderControl::start() -{ - if (isDecoding()) - return; - - if (m_loadingSource) { - //deferred starting - setIsDecoding(true); - return; - } - - if (!m_decoderSourceReader->mediaSource()) - return; - activatePipeline(); -} - -void MFAudioDecoderControl::stop() -{ - if (!isDecoding()) - return; - disconnect(m_decoderSourceReader, SIGNAL(sampleAdded()), this, SLOT(handleSampleAdded())); - if (m_bufferReady) { - m_bufferReady = false; - emit bufferAvailableChanged(m_bufferReady); - } - setIsDecoding(false); -} - -void MFAudioDecoderControl::handleSampleAdded() -{ - QList<IMFSample*> samples = m_decoderSourceReader->takeSamples(); - Q_ASSERT(samples.count() > 0); - Q_ASSERT(!m_bufferReady); - Q_ASSERT(m_resampler); - LONGLONG sampleStartTime = 0; - IMFSample *firstSample = samples.first(); - firstSample->GetSampleTime(&sampleStartTime); - QByteArray abuf; - QAudioFormat bufferFormat; - if (!m_audioFormat.isValid()) { - bufferFormat = m_sourceOutputFormat; - //no need for resampling - for (IMFSample *s : qAsConst(samples)) { - IMFMediaBuffer *buffer; - s->ConvertToContiguousBuffer(&buffer); - DWORD bufLen = 0; - BYTE *buf = 0; - if (SUCCEEDED(buffer->Lock(&buf, NULL, &bufLen))) { - abuf.push_back(QByteArray(reinterpret_cast<char*>(buf), bufLen)); - buffer->Unlock(); - } - buffer->Release(); - LONGLONG sampleTime = 0, sampleDuration = 0; - s->GetSampleTime(&sampleTime); - s->GetSampleDuration(&sampleDuration); - m_position = qint64(sampleTime + sampleDuration) / 10000; - s->Release(); - } - } else { - bufferFormat = m_audioFormat; - for (IMFSample *s : qAsConst(samples)) { - HRESULT hr = m_resampler->ProcessInput(m_mfInputStreamID, s, 0); - if (SUCCEEDED(hr)) { - MFT_OUTPUT_DATA_BUFFER outputDataBuffer; - outputDataBuffer.dwStreamID = m_mfOutputStreamID; - while (true) { - outputDataBuffer.pEvents = 0; - outputDataBuffer.dwStatus = 0; - outputDataBuffer.pSample = m_convertSample; - DWORD status = 0; - if (SUCCEEDED(m_resampler->ProcessOutput(0, 1, &outputDataBuffer, &status))) { - IMFMediaBuffer *buffer; - outputDataBuffer.pSample->ConvertToContiguousBuffer(&buffer); - DWORD bufLen = 0; - BYTE *buf = 0; - if (SUCCEEDED(buffer->Lock(&buf, NULL, &bufLen))) { - abuf.push_back(QByteArray(reinterpret_cast<char*>(buf), bufLen)); - buffer->Unlock(); - } - buffer->Release(); - } else { - break; - } - } - } - LONGLONG sampleTime = 0, sampleDuration = 0; - s->GetSampleTime(&sampleTime); - s->GetSampleDuration(&sampleDuration); - m_position = qint64(sampleTime + sampleDuration) / 10000; - s->Release(); - } - } - // WMF uses 100-nanosecond units, QAudioDecoder uses milliseconds, QAudioBuffer uses microseconds... - m_cachedAudioBuffer = QAudioBuffer(abuf, bufferFormat, qint64(sampleStartTime / 10)); - m_bufferReady = true; - emit positionChanged(m_position); - emit bufferAvailableChanged(m_bufferReady); - emit bufferReady(); -} - -void MFAudioDecoderControl::handleSourceFinished() -{ - stop(); - emit finished(); -} - -QAudioFormat MFAudioDecoderControl::audioFormat() const -{ - return m_audioFormat; -} - -void MFAudioDecoderControl::setAudioFormat(const QAudioFormat &format) -{ - if (m_audioFormat == format || !m_resampler) - return; - m_audioFormat = format; - setResamplerOutputFormat(format); - emit formatChanged(m_audioFormat); -} - -QAudioBuffer MFAudioDecoderControl::read() -{ - if (!m_bufferReady) - return QAudioBuffer(); - QAudioBuffer buffer = m_cachedAudioBuffer; - m_bufferReady = false; - emit bufferAvailableChanged(m_bufferReady); - m_decoderSourceReader->readNextSample(); - return buffer; -} - -bool MFAudioDecoderControl::bufferAvailable() const -{ - return m_bufferReady; -} - -qint64 MFAudioDecoderControl::position() const -{ - return m_position; -} - -qint64 MFAudioDecoderControl::duration() const -{ - return m_duration; -} diff --git a/src/multimedia/platform/windows/decoder/mfaudiodecodercontrol_p.h b/src/multimedia/platform/windows/decoder/mfaudiodecodercontrol_p.h deleted file mode 100644 index 6efdbb346..000000000 --- a/src/multimedia/platform/windows/decoder/mfaudiodecodercontrol_p.h +++ /dev/null @@ -1,117 +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$ -** -****************************************************************************/ - -#ifndef MFAUDIODECODERCONTROL_H -#define MFAUDIODECODERCONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "private/qplatformaudiodecoder_p.h" -#include "mfdecodersourcereader_p.h" -#include "private/sourceresolver_p.h" - -QT_USE_NAMESPACE - -class MFAudioDecoderControl : public QPlatformAudioDecoder -{ - Q_OBJECT -public: - MFAudioDecoderControl(QAudioDecoder *parent); - ~MFAudioDecoderControl(); - - QUrl source() const; - void setSource(const QUrl &fileName); - - QIODevice* sourceDevice() const; - void setSourceDevice(QIODevice *device); - - void start(); - void stop(); - - QAudioFormat audioFormat() const override; - void setAudioFormat(const QAudioFormat &format) override; - - QAudioBuffer read(); - bool bufferAvailable() const; - - qint64 position() const; - qint64 duration() const; - -private Q_SLOTS: - void handleMediaSourceReady(); - void handleMediaSourceError(long hr); - void handleSampleAdded(); - void handleSourceFinished(); - -private: - void updateResamplerOutputType(); - void activatePipeline(); - void onSourceCleared(); - void setResamplerOutputFormat(const QAudioFormat &format); - - MFDecoderSourceReader *m_decoderSourceReader; - SourceResolver *m_sourceResolver; - IMFTransform *m_resampler; - QUrl m_source; - QIODevice *m_device; - QAudioFormat m_audioFormat; - DWORD m_mfInputStreamID; - DWORD m_mfOutputStreamID; - bool m_bufferReady; - QAudioBuffer m_cachedAudioBuffer; - qint64 m_duration; - qint64 m_position; - bool m_loadingSource; - IMFMediaType *m_mfOutputType; - IMFSample *m_convertSample; - QAudioFormat m_sourceOutputFormat; - bool m_sourceReady; - bool m_resamplerDirty; -}; - -#endif//MFAUDIODECODERCONTROL_H diff --git a/src/multimedia/platform/windows/decoder/mfdecodersourcereader.cpp b/src/multimedia/platform/windows/decoder/mfdecodersourcereader.cpp deleted file mode 100644 index 381c60dc5..000000000 --- a/src/multimedia/platform/windows/decoder/mfdecodersourcereader.cpp +++ /dev/null @@ -1,197 +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$ -** -****************************************************************************/ - -#include "mfdecodersourcereader_p.h" - -MFDecoderSourceReader::MFDecoderSourceReader(QObject *parent) - : m_cRef(1) - , m_sourceReader(0) - , m_source(0) -{ - Q_UNUSED(parent); -} - -void MFDecoderSourceReader::shutdown() -{ - if (m_source) { - m_source->Release(); - m_source = NULL; - } - if (m_sourceReader) { - m_sourceReader->Release(); - m_sourceReader = NULL; - } -} - -IMFMediaSource* MFDecoderSourceReader::mediaSource() -{ - return m_source; -} - -IMFMediaType* MFDecoderSourceReader::setSource(IMFMediaSource *source, const QAudioFormat &audioFormat) -{ - IMFMediaType *mediaType = NULL; - if (m_source == source) - return mediaType; - if (m_source) { - m_source->Release(); - m_source = NULL; - } - if (m_sourceReader) { - m_sourceReader->Release(); - m_sourceReader = NULL; - } - if (!source) - return mediaType; - IMFAttributes *attr = NULL; - MFCreateAttributes(&attr, 1); - if (SUCCEEDED(attr->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, this))) { - if (SUCCEEDED(MFCreateSourceReaderFromMediaSource(source, attr, &m_sourceReader))) { - m_source = source; - m_source->AddRef(); - m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_ALL_STREAMS), FALSE); - m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), TRUE); - IMFMediaType *pPartialType = NULL; - MFCreateMediaType(&pPartialType); - pPartialType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio); - - if (audioFormat.sampleFormat() == QAudioFormat::Float) { - pPartialType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_Float); - } else { - pPartialType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM); - } - - m_sourceReader->SetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), NULL, pPartialType); - pPartialType->Release(); - m_sourceReader->GetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), &mediaType); - // Ensure the stream is selected. - m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), TRUE); - } - attr->Release(); - } - return mediaType; -} - -void MFDecoderSourceReader::reset() -{ - if (!m_sourceReader) - return; - PROPVARIANT vPos; - PropVariantInit(&vPos); - vPos.vt = VT_I8; - vPos.uhVal.QuadPart = 0; - m_sourceReader->SetCurrentPosition(GUID_NULL, vPos); -} - -void MFDecoderSourceReader::readNextSample() -{ - if (!m_sourceReader) - return; - m_sourceReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, NULL, NULL, NULL, NULL); -} - -QList<IMFSample*> MFDecoderSourceReader::takeSamples() //internal samples will be cleared after this -{ - QList<IMFSample*> samples; - m_samplesMutex.lock(); - samples = m_cachedSamples; - m_cachedSamples.clear(); - m_samplesMutex.unlock(); - return samples; -} - -//from IUnknown -STDMETHODIMP MFDecoderSourceReader::QueryInterface(REFIID riid, LPVOID *ppvObject) -{ - if (!ppvObject) - return E_POINTER; - if (riid == IID_IMFSourceReaderCallback) { - *ppvObject = static_cast<IMFSourceReaderCallback*>(this); - } else if (riid == IID_IUnknown) { - *ppvObject = static_cast<IUnknown*>(this); - } else { - *ppvObject = NULL; - return E_NOINTERFACE; - } - AddRef(); - return S_OK; -} - -STDMETHODIMP_(ULONG) MFDecoderSourceReader::AddRef(void) -{ - return InterlockedIncrement(&m_cRef); -} - -STDMETHODIMP_(ULONG) MFDecoderSourceReader::Release(void) -{ - LONG cRef = InterlockedDecrement(&m_cRef); - if (cRef == 0) { - this->deleteLater(); - } - return cRef; -} - -//from IMFSourceReaderCallback -STDMETHODIMP MFDecoderSourceReader::OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex, - DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample) -{ - Q_UNUSED(hrStatus); - Q_UNUSED(dwStreamIndex); - Q_UNUSED(llTimestamp); - if (pSample) { - pSample->AddRef(); - m_samplesMutex.lock(); - m_cachedSamples.push_back(pSample); - m_samplesMutex.unlock(); - emit sampleAdded(); - } else if ((dwStreamFlags & MF_SOURCE_READERF_ENDOFSTREAM) == MF_SOURCE_READERF_ENDOFSTREAM) { - emit finished(); - } - return S_OK; -} - -STDMETHODIMP MFDecoderSourceReader::OnFlush(DWORD) -{ - return S_OK; -} - -STDMETHODIMP MFDecoderSourceReader::OnEvent(DWORD, IMFMediaEvent*) -{ - return S_OK; -} diff --git a/src/multimedia/platform/windows/decoder/mfdecodersourcereader_p.h b/src/multimedia/platform/windows/decoder/mfdecodersourcereader_p.h deleted file mode 100644 index 7d63f5368..000000000 --- a/src/multimedia/platform/windows/decoder/mfdecodersourcereader_p.h +++ /dev/null @@ -1,101 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Mobility Components. -** -** $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$ -** -****************************************************************************/ - -#ifndef MFDECODERSOURCEREADER_H -#define MFDECODERSOURCEREADER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <mfapi.h> -#include <mfidl.h> -#include <Mfreadwrite.h> - -#include <QtCore/qobject.h> -#include <QtCore/qmutex.h> -#include "qaudioformat.h" - -QT_USE_NAMESPACE - -class MFDecoderSourceReader : public QObject, public IMFSourceReaderCallback -{ - Q_OBJECT -public: - MFDecoderSourceReader(QObject *parent = 0); - void shutdown(); - - IMFMediaSource* mediaSource(); - IMFMediaType* setSource(IMFMediaSource *source, const QAudioFormat &audioFormat); - - void reset(); - void readNextSample(); - QList<IMFSample*> takeSamples(); //internal samples will be cleared after this - - //from IUnknown - STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject); - STDMETHODIMP_(ULONG) AddRef(void); - STDMETHODIMP_(ULONG) Release(void); - - //from IMFSourceReaderCallback - STDMETHODIMP OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex, - DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample); - STDMETHODIMP OnFlush(DWORD dwStreamIndex); - STDMETHODIMP OnEvent(DWORD dwStreamIndex, IMFMediaEvent *pEvent); - -Q_SIGNALS: - void sampleAdded(); - void finished(); - -private: - long m_cRef; - QList<IMFSample*> m_cachedSamples; - QMutex m_samplesMutex; - - IMFSourceReader *m_sourceReader; - IMFMediaSource *m_source; -}; -#endif//MFDECODERSOURCEREADER_H diff --git a/src/multimedia/platform/windows/evr/evrcustompresenter.cpp b/src/multimedia/platform/windows/evr/evrcustompresenter.cpp deleted file mode 100644 index 086fb57d4..000000000 --- a/src/multimedia/platform/windows/evr/evrcustompresenter.cpp +++ /dev/null @@ -1,2005 +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$ -** -****************************************************************************/ - -#include "evrcustompresenter_p.h" - -#include "evrd3dpresentengine_p.h" -#include "evrhelpers_p.h" -#include <private/qwindowsmultimediautils_p.h> -#include <private/qplatformvideosink_p.h> - -#include <QtGui/private/qrhi_p.h> - -#include <QtCore/qmutex.h> -#include <QtCore/qvarlengtharray.h> -#include <QtCore/qrect.h> -#include <qthread.h> -#include <qcoreapplication.h> -#include <qmath.h> -#include <QtCore/qdebug.h> - -#include <mutex> - -#include <float.h> -#include <evcode.h> - -QT_BEGIN_NAMESPACE - -const static MFRatio g_DefaultFrameRate = { 30, 1 }; -static const DWORD SCHEDULER_TIMEOUT = 5000; -static const MFTIME ONE_SECOND = 10000000; -static const LONG ONE_MSEC = 1000; - -// Function declarations. -static HRESULT setDesiredSampleTime(IMFSample *sample, const LONGLONG& hnsSampleTime, const LONGLONG& hnsDuration); -static HRESULT clearDesiredSampleTime(IMFSample *sample); -static HRESULT setMixerSourceRect(IMFTransform *mixer, const MFVideoNormalizedRect& nrcSource); -static QVideoFrameFormat::PixelFormat pixelFormatFromMediaType(IMFMediaType *type); - -static inline LONG MFTimeToMsec(const LONGLONG& time) -{ - return (LONG)(time / (ONE_SECOND / ONE_MSEC)); -} - -bool qt_evr_setCustomPresenter(IUnknown *evr, EVRCustomPresenter *presenter) -{ - if (!evr || !presenter) - return false; - - HRESULT result = E_FAIL; - - IMFVideoRenderer *renderer = NULL; - if (SUCCEEDED(evr->QueryInterface(IID_PPV_ARGS(&renderer)))) { - result = renderer->InitializeRenderer(NULL, presenter); - renderer->Release(); - } - - return result == S_OK; -} - -class PresentSampleEvent : public QEvent -{ -public: - PresentSampleEvent(IMFSample *sample) - : QEvent(QEvent::Type(EVRCustomPresenter::PresentSample)) - , m_sample(sample) - { - if (m_sample) - m_sample->AddRef(); - } - - ~PresentSampleEvent() override - { - if (m_sample) - m_sample->Release(); - } - - IMFSample *sample() const { return m_sample; } - -private: - IMFSample *m_sample; -}; - -Scheduler::Scheduler(EVRCustomPresenter *presenter) - : m_presenter(presenter) - , m_clock(NULL) - , m_threadID(0) - , m_schedulerThread(0) - , m_threadReadyEvent(0) - , m_flushEvent(0) - , m_playbackRate(1.0f) - , m_perFrameInterval(0) - , m_perFrame_1_4th(0) - , m_lastSampleTime(0) -{ -} - -Scheduler::~Scheduler() -{ - qt_evr_safe_release(&m_clock); - for (int i = 0; i < m_scheduledSamples.size(); ++i) - m_scheduledSamples[i]->Release(); - m_scheduledSamples.clear(); -} - -void Scheduler::setFrameRate(const MFRatio& fps) -{ - UINT64 AvgTimePerFrame = 0; - - // Convert to a duration. - MFFrameRateToAverageTimePerFrame(fps.Numerator, fps.Denominator, &AvgTimePerFrame); - - m_perFrameInterval = (MFTIME)AvgTimePerFrame; - - // Calculate 1/4th of this value, because we use it frequently. - m_perFrame_1_4th = m_perFrameInterval / 4; -} - -HRESULT Scheduler::startScheduler(IMFClock *clock) -{ - if (m_schedulerThread) - return E_UNEXPECTED; - - HRESULT hr = S_OK; - DWORD dwID = 0; - HANDLE hObjects[2]; - DWORD dwWait = 0; - - if (m_clock) - m_clock->Release(); - m_clock = clock; - if (m_clock) - m_clock->AddRef(); - - // Set a high the timer resolution (ie, short timer period). - timeBeginPeriod(1); - - // Create an event to wait for the thread to start. - m_threadReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if (!m_threadReadyEvent) { - hr = HRESULT_FROM_WIN32(GetLastError()); - goto done; - } - - // Create an event to wait for flush commands to complete. - m_flushEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if (!m_flushEvent) { - hr = HRESULT_FROM_WIN32(GetLastError()); - goto done; - } - - // Create the scheduler thread. - m_schedulerThread = CreateThread(NULL, 0, schedulerThreadProc, (LPVOID)this, 0, &dwID); - if (!m_schedulerThread) { - hr = HRESULT_FROM_WIN32(GetLastError()); - goto done; - } - - // Wait for the thread to signal the "thread ready" event. - hObjects[0] = m_threadReadyEvent; - hObjects[1] = m_schedulerThread; - dwWait = WaitForMultipleObjects(2, hObjects, FALSE, INFINITE); // Wait for EITHER of these handles. - if (WAIT_OBJECT_0 != dwWait) { - // The thread terminated early for some reason. This is an error condition. - CloseHandle(m_schedulerThread); - m_schedulerThread = NULL; - - hr = E_UNEXPECTED; - goto done; - } - - m_threadID = dwID; - -done: - // Regardless success/failure, we are done using the "thread ready" event. - if (m_threadReadyEvent) { - CloseHandle(m_threadReadyEvent); - m_threadReadyEvent = NULL; - } - return hr; -} - -HRESULT Scheduler::stopScheduler() -{ - if (!m_schedulerThread) - return S_OK; - - // Ask the scheduler thread to exit. - PostThreadMessage(m_threadID, Terminate, 0, 0); - - // Wait for the thread to exit. - WaitForSingleObject(m_schedulerThread, INFINITE); - - // Close handles. - CloseHandle(m_schedulerThread); - m_schedulerThread = NULL; - - CloseHandle(m_flushEvent); - m_flushEvent = NULL; - - // Discard samples. - m_mutex.lock(); - for (int i = 0; i < m_scheduledSamples.size(); ++i) - m_scheduledSamples[i]->Release(); - m_scheduledSamples.clear(); - m_mutex.unlock(); - - // Restore the timer resolution. - timeEndPeriod(1); - - return S_OK; -} - -HRESULT Scheduler::flush() -{ - if (m_schedulerThread) { - // Ask the scheduler thread to flush. - PostThreadMessage(m_threadID, Flush, 0 , 0); - - // Wait for the scheduler thread to signal the flush event, - // OR for the thread to terminate. - HANDLE objects[] = { m_flushEvent, m_schedulerThread }; - - WaitForMultipleObjects(ARRAYSIZE(objects), objects, FALSE, SCHEDULER_TIMEOUT); - } - - return S_OK; -} - -bool Scheduler::areSamplesScheduled() -{ - QMutexLocker locker(&m_mutex); - return m_scheduledSamples.count() > 0; -} - -HRESULT Scheduler::scheduleSample(IMFSample *sample, bool presentNow) -{ - if (!m_schedulerThread) - return MF_E_NOT_INITIALIZED; - - HRESULT hr = S_OK; - DWORD dwExitCode = 0; - - GetExitCodeThread(m_schedulerThread, &dwExitCode); - if (dwExitCode != STILL_ACTIVE) - return E_FAIL; - - if (presentNow || !m_clock) { - m_presenter->presentSample(sample); - } else { - // Queue the sample and ask the scheduler thread to wake up. - m_mutex.lock(); - sample->AddRef(); - m_scheduledSamples.enqueue(sample); - m_mutex.unlock(); - - if (SUCCEEDED(hr)) - PostThreadMessage(m_threadID, Schedule, 0, 0); - } - - return hr; -} - -HRESULT Scheduler::processSamplesInQueue(LONG *nextSleep) -{ - HRESULT hr = S_OK; - LONG wait = 0; - IMFSample *sample = NULL; - - // Process samples until the queue is empty or until the wait time > 0. - while (!m_scheduledSamples.isEmpty()) { - m_mutex.lock(); - sample = m_scheduledSamples.dequeue(); - m_mutex.unlock(); - - // Process the next sample in the queue. If the sample is not ready - // for presentation. the value returned in wait is > 0, which - // means the scheduler should sleep for that amount of time. - - hr = processSample(sample, &wait); - qt_evr_safe_release(&sample); - - if (FAILED(hr) || wait > 0) - break; - } - - // If the wait time is zero, it means we stopped because the queue is - // empty (or an error occurred). Set the wait time to infinite; this will - // make the scheduler thread sleep until it gets another thread message. - if (wait == 0) - wait = INFINITE; - - *nextSleep = wait; - return hr; -} - -HRESULT Scheduler::processSample(IMFSample *sample, LONG *pNextSleep) -{ - HRESULT hr = S_OK; - - LONGLONG hnsPresentationTime = 0; - LONGLONG hnsTimeNow = 0; - MFTIME hnsSystemTime = 0; - - bool presentNow = true; - LONG nextSleep = 0; - - if (m_clock) { - // Get the sample's time stamp. It is valid for a sample to - // have no time stamp. - hr = sample->GetSampleTime(&hnsPresentationTime); - - // Get the clock time. (But if the sample does not have a time stamp, - // we don't need the clock time.) - if (SUCCEEDED(hr)) - hr = m_clock->GetCorrelatedTime(0, &hnsTimeNow, &hnsSystemTime); - - // Calculate the time until the sample's presentation time. - // A negative value means the sample is late. - LONGLONG hnsDelta = hnsPresentationTime - hnsTimeNow; - if (m_playbackRate < 0) { - // For reverse playback, the clock runs backward. Therefore, the - // delta is reversed. - hnsDelta = - hnsDelta; - } - - if (hnsDelta < - m_perFrame_1_4th) { - // This sample is late. - presentNow = true; - } else if (hnsDelta > (3 * m_perFrame_1_4th)) { - // This sample is still too early. Go to sleep. - nextSleep = MFTimeToMsec(hnsDelta - (3 * m_perFrame_1_4th)); - - // Adjust the sleep time for the clock rate. (The presentation clock runs - // at m_fRate, but sleeping uses the system clock.) - if (m_playbackRate != 0) - nextSleep = (LONG)(nextSleep / qFabs(m_playbackRate)); - - // Don't present yet. - presentNow = false; - } - } - - if (presentNow) { - m_presenter->presentSample(sample); - } else { - // The sample is not ready yet. Return it to the queue. - m_mutex.lock(); - sample->AddRef(); - m_scheduledSamples.prepend(sample); - m_mutex.unlock(); - } - - *pNextSleep = nextSleep; - - return hr; -} - -DWORD WINAPI Scheduler::schedulerThreadProc(LPVOID parameter) -{ - Scheduler* scheduler = reinterpret_cast<Scheduler*>(parameter); - if (!scheduler) - return -1; - return scheduler->schedulerThreadProcPrivate(); -} - -DWORD Scheduler::schedulerThreadProcPrivate() -{ - HRESULT hr = S_OK; - MSG msg; - LONG wait = INFINITE; - bool exitThread = false; - - // Force the system to create a message queue for this thread. - // (See MSDN documentation for PostThreadMessage.) - PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); - - // Signal to the scheduler that the thread is ready. - SetEvent(m_threadReadyEvent); - - while (!exitThread) { - // Wait for a thread message OR until the wait time expires. - DWORD result = MsgWaitForMultipleObjects(0, NULL, FALSE, wait, QS_POSTMESSAGE); - - if (result == WAIT_TIMEOUT) { - // If we timed out, then process the samples in the queue - hr = processSamplesInQueue(&wait); - if (FAILED(hr)) - exitThread = true; - } - - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { - bool processSamples = true; - - switch (msg.message) { - case Terminate: - exitThread = true; - break; - case Flush: - // Flushing: Clear the sample queue and set the event. - m_mutex.lock(); - for (int i = 0; i < m_scheduledSamples.size(); ++i) - m_scheduledSamples[i]->Release(); - m_scheduledSamples.clear(); - m_mutex.unlock(); - wait = INFINITE; - SetEvent(m_flushEvent); - break; - case Schedule: - // Process as many samples as we can. - if (processSamples) { - hr = processSamplesInQueue(&wait); - if (FAILED(hr)) - exitThread = true; - processSamples = (wait != (LONG)INFINITE); - } - break; - } - } - - } - - return (SUCCEEDED(hr) ? 0 : 1); -} - - -SamplePool::SamplePool() - : m_initialized(false) -{ -} - -SamplePool::~SamplePool() -{ - clear(); -} - -HRESULT SamplePool::getSample(IMFSample **sample) -{ - QMutexLocker locker(&m_mutex); - - if (!m_initialized) - return MF_E_NOT_INITIALIZED; - - if (m_videoSampleQueue.isEmpty()) - return MF_E_SAMPLEALLOCATOR_EMPTY; - - // Get a sample from the allocated queue. - - // It doesn't matter if we pull them from the head or tail of the list, - // but when we get it back, we want to re-insert it onto the opposite end. - // (see ReturnSample) - - IMFSample *taken = m_videoSampleQueue.takeFirst(); - - // Give the sample to the caller. - *sample = taken; - (*sample)->AddRef(); - - taken->Release(); - - return S_OK; -} - -HRESULT SamplePool::returnSample(IMFSample *sample) -{ - QMutexLocker locker(&m_mutex); - - if (!m_initialized) - return MF_E_NOT_INITIALIZED; - - m_videoSampleQueue.append(sample); - sample->AddRef(); - - return S_OK; -} - -HRESULT SamplePool::initialize(QList<IMFSample*> &samples) -{ - QMutexLocker locker(&m_mutex); - - if (m_initialized) - return MF_E_INVALIDREQUEST; - - // Move these samples into our allocated queue. - for (auto sample : qAsConst(samples)) { - sample->AddRef(); - m_videoSampleQueue.append(sample); - } - - m_initialized = true; - - for (auto sample : qAsConst(samples)) - sample->Release(); - samples.clear(); - return S_OK; -} - -HRESULT SamplePool::clear() -{ - QMutexLocker locker(&m_mutex); - - for (auto sample : qAsConst(m_videoSampleQueue)) - sample->Release(); - m_videoSampleQueue.clear(); - m_initialized = false; - - return S_OK; -} - - -EVRCustomPresenter::EVRCustomPresenter(QVideoSink *sink) - : QObject() - , m_sampleFreeCB(this, &EVRCustomPresenter::onSampleFree) - , m_refCount(1) - , m_renderState(RenderShutdown) - , m_scheduler(this) - , m_tokenCounter(0) - , m_sampleNotify(false) - , m_repaint(false) - , m_prerolled(false) - , m_endStreaming(false) - , m_playbackRate(1.0f) - , m_presentEngine(new D3DPresentEngine) - , m_clock(0) - , m_mixer(0) - , m_mediaEventSink(0) - , m_mediaType(0) - , m_videoSink(0) - , m_canRenderToSurface(false) - , m_positionOffset(0) -{ - // Initial source rectangle = (0,0,1,1) - m_sourceRect.top = 0; - m_sourceRect.left = 0; - m_sourceRect.bottom = 1; - m_sourceRect.right = 1; - - setSink(sink); -} - -EVRCustomPresenter::~EVRCustomPresenter() -{ - m_scheduler.flush(); - m_scheduler.stopScheduler(); - m_samplePool.clear(); - - qt_evr_safe_release(&m_clock); - qt_evr_safe_release(&m_mixer); - qt_evr_safe_release(&m_mediaEventSink); - qt_evr_safe_release(&m_mediaType); - - delete m_presentEngine; -} - -HRESULT EVRCustomPresenter::QueryInterface(REFIID riid, void ** ppvObject) -{ - if (!ppvObject) - return E_POINTER; - if (riid == IID_IMFGetService) { - *ppvObject = static_cast<IMFGetService*>(this); - } else if (riid == IID_IMFTopologyServiceLookupClient) { - *ppvObject = static_cast<IMFTopologyServiceLookupClient*>(this); - } else if (riid == IID_IMFVideoDeviceID) { - *ppvObject = static_cast<IMFVideoDeviceID*>(this); - } else if (riid == IID_IMFVideoPresenter) { - *ppvObject = static_cast<IMFVideoPresenter*>(this); - } else if (riid == IID_IMFRateSupport) { - *ppvObject = static_cast<IMFRateSupport*>(this); - } else if (riid == IID_IUnknown) { - *ppvObject = static_cast<IUnknown*>(static_cast<IMFGetService*>(this)); - } else if (riid == IID_IMFClockStateSink) { - *ppvObject = static_cast<IMFClockStateSink*>(this); - } else { - *ppvObject = NULL; - return E_NOINTERFACE; - } - AddRef(); - return S_OK; -} - -ULONG EVRCustomPresenter::AddRef() -{ - return InterlockedIncrement(&m_refCount); -} - -ULONG EVRCustomPresenter::Release() -{ - ULONG uCount = InterlockedDecrement(&m_refCount); - if (uCount == 0) - delete this; - return uCount; -} - -HRESULT EVRCustomPresenter::GetService(REFGUID guidService, REFIID riid, LPVOID *ppvObject) -{ - HRESULT hr = S_OK; - - if (!ppvObject) - return E_POINTER; - - // The only service GUID that we support is MR_VIDEO_RENDER_SERVICE. - if (guidService != mr_VIDEO_RENDER_SERVICE) - return MF_E_UNSUPPORTED_SERVICE; - - // First try to get the service interface from the D3DPresentEngine object. - hr = m_presentEngine->getService(guidService, riid, ppvObject); - if (FAILED(hr)) - // Next, check if this object supports the interface. - hr = QueryInterface(riid, ppvObject); - - return hr; -} - -HRESULT EVRCustomPresenter::GetDeviceID(IID* deviceID) -{ - if (!deviceID) - return E_POINTER; - - *deviceID = iid_IDirect3DDevice9; - - return S_OK; -} - -HRESULT EVRCustomPresenter::InitServicePointers(IMFTopologyServiceLookup *lookup) -{ - if (!lookup) - return E_POINTER; - - HRESULT hr = S_OK; - DWORD objectCount = 0; - - const std::lock_guard<QRecursiveMutex> locker(m_mutex); - - // Do not allow initializing when playing or paused. - if (isActive()) - return MF_E_INVALIDREQUEST; - - qt_evr_safe_release(&m_clock); - qt_evr_safe_release(&m_mixer); - qt_evr_safe_release(&m_mediaEventSink); - - // Ask for the clock. Optional, because the EVR might not have a clock. - objectCount = 1; - - lookup->LookupService(MF_SERVICE_LOOKUP_GLOBAL, 0, - mr_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_clock), - &objectCount - ); - - // Ask for the mixer. (Required.) - objectCount = 1; - - hr = lookup->LookupService(MF_SERVICE_LOOKUP_GLOBAL, 0, - mr_VIDEO_MIXER_SERVICE, IID_PPV_ARGS(&m_mixer), - &objectCount - ); - - if (FAILED(hr)) - return hr; - - // Make sure that we can work with this mixer. - hr = configureMixer(m_mixer); - if (FAILED(hr)) - return hr; - - // Ask for the EVR's event-sink interface. (Required.) - objectCount = 1; - - hr = lookup->LookupService(MF_SERVICE_LOOKUP_GLOBAL, 0, - mr_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_mediaEventSink), - &objectCount - ); - - if (SUCCEEDED(hr)) - m_renderState = RenderStopped; - - return hr; -} - -HRESULT EVRCustomPresenter::ReleaseServicePointers() -{ - // Enter the shut-down state. - m_mutex.lock(); - - m_renderState = RenderShutdown; - - m_mutex.unlock(); - - // Flush any samples that were scheduled. - flush(); - - // Clear the media type and release related resources. - setMediaType(NULL); - - // Release all services that were acquired from InitServicePointers. - qt_evr_safe_release(&m_clock); - qt_evr_safe_release(&m_mixer); - qt_evr_safe_release(&m_mediaEventSink); - - return S_OK; -} - -bool EVRCustomPresenter::isValid() const -{ - return m_presentEngine->isValid() && m_canRenderToSurface; -} - -HRESULT EVRCustomPresenter::ProcessMessage(MFVP_MESSAGE_TYPE message, ULONG_PTR param) -{ - HRESULT hr = S_OK; - - const std::lock_guard<QRecursiveMutex> locker(m_mutex); - - hr = checkShutdown(); - if (FAILED(hr)) - return hr; - - switch (message) { - // Flush all pending samples. - case MFVP_MESSAGE_FLUSH: - hr = flush(); - break; - - // Renegotiate the media type with the mixer. - case MFVP_MESSAGE_INVALIDATEMEDIATYPE: - hr = renegotiateMediaType(); - break; - - // The mixer received a new input sample. - case MFVP_MESSAGE_PROCESSINPUTNOTIFY: - hr = processInputNotify(); - break; - - // Streaming is about to start. - case MFVP_MESSAGE_BEGINSTREAMING: - hr = beginStreaming(); - break; - - // Streaming has ended. (The EVR has stopped.) - case MFVP_MESSAGE_ENDSTREAMING: - hr = endStreaming(); - break; - - // All input streams have ended. - case MFVP_MESSAGE_ENDOFSTREAM: - // Set the EOS flag. - m_endStreaming = true; - // Check if it's time to send the EC_COMPLETE event to the EVR. - hr = checkEndOfStream(); - break; - - // Frame-stepping is starting. - case MFVP_MESSAGE_STEP: - hr = prepareFrameStep(DWORD(param)); - break; - - // Cancels frame-stepping. - case MFVP_MESSAGE_CANCELSTEP: - hr = cancelFrameStep(); - break; - - default: - hr = E_INVALIDARG; // Unknown message. This case should never occur. - break; - } - - return hr; -} - -HRESULT EVRCustomPresenter::GetCurrentMediaType(IMFVideoMediaType **mediaType) -{ - HRESULT hr = S_OK; - - if (!mediaType) - return E_POINTER; - - *mediaType = NULL; - - const std::lock_guard<QRecursiveMutex> locker(m_mutex); - - hr = checkShutdown(); - if (FAILED(hr)) - return hr; - - if (!m_mediaType) - return MF_E_NOT_INITIALIZED; - - return m_mediaType->QueryInterface(IID_PPV_ARGS(mediaType)); -} - -HRESULT EVRCustomPresenter::OnClockStart(MFTIME, LONGLONG clockStartOffset) -{ - const std::lock_guard<QRecursiveMutex> locker(m_mutex); - - // We cannot start after shutdown. - HRESULT hr = checkShutdown(); - if (FAILED(hr)) - return hr; - - // Check if the clock is already active (not stopped). - if (isActive()) { - m_renderState = RenderStarted; - - // If the clock position changes while the clock is active, it - // is a seek request. We need to flush all pending samples. - if (clockStartOffset != PRESENTATION_CURRENT_POSITION) - flush(); - } else { - m_renderState = RenderStarted; - - // The clock has started from the stopped state. - - // Possibly we are in the middle of frame-stepping OR have samples waiting - // in the frame-step queue. Deal with these two cases first: - hr = startFrameStep(); - if (FAILED(hr)) - return hr; - } - - // Now try to get new output samples from the mixer. - processOutputLoop(); - - return hr; -} - -HRESULT EVRCustomPresenter::OnClockRestart(MFTIME) -{ - const std::lock_guard<QRecursiveMutex> locker(m_mutex); - - HRESULT hr = checkShutdown(); - if (FAILED(hr)) - return hr; - - // The EVR calls OnClockRestart only while paused. - - m_renderState = RenderStarted; - - // Possibly we are in the middle of frame-stepping OR we have samples waiting - // in the frame-step queue. Deal with these two cases first: - hr = startFrameStep(); - if (FAILED(hr)) - return hr; - - // Now resume the presentation loop. - processOutputLoop(); - - return hr; -} - -HRESULT EVRCustomPresenter::OnClockStop(MFTIME) -{ - const std::lock_guard<QRecursiveMutex> locker(m_mutex); - - HRESULT hr = checkShutdown(); - if (FAILED(hr)) - return hr; - - if (m_renderState != RenderStopped) { - m_renderState = RenderStopped; - flush(); - - // If we are in the middle of frame-stepping, cancel it now. - if (m_frameStep.state != FrameStepNone) - cancelFrameStep(); - } - - return S_OK; -} - -HRESULT EVRCustomPresenter::OnClockPause(MFTIME) -{ - const std::lock_guard<QRecursiveMutex> locker(m_mutex); - - // We cannot pause the clock after shutdown. - HRESULT hr = checkShutdown(); - - if (SUCCEEDED(hr)) - m_renderState = RenderPaused; - - return hr; -} - -HRESULT EVRCustomPresenter::OnClockSetRate(MFTIME, float rate) -{ - // Note: - // The presenter reports its maximum rate through the IMFRateSupport interface. - // Here, we assume that the EVR honors the maximum rate. - - const std::lock_guard<QRecursiveMutex> locker(m_mutex); - - HRESULT hr = checkShutdown(); - if (FAILED(hr)) - return hr; - - // If the rate is changing from zero (scrubbing) to non-zero, cancel the - // frame-step operation. - if ((m_playbackRate == 0.0f) && (rate != 0.0f)) { - cancelFrameStep(); - for (auto sample : qAsConst(m_frameStep.samples)) - sample->Release(); - m_frameStep.samples.clear(); - } - - m_playbackRate = rate; - - // Tell the scheduler about the new rate. - m_scheduler.setClockRate(rate); - - return S_OK; -} - -HRESULT EVRCustomPresenter::GetSlowestRate(MFRATE_DIRECTION, BOOL, float *rate) -{ - if (!rate) - return E_POINTER; - - const std::lock_guard<QRecursiveMutex> locker(m_mutex); - - HRESULT hr = checkShutdown(); - - if (SUCCEEDED(hr)) { - // There is no minimum playback rate, so the minimum is zero. - *rate = 0; - } - - return S_OK; -} - -HRESULT EVRCustomPresenter::GetFastestRate(MFRATE_DIRECTION direction, BOOL thin, float *rate) -{ - if (!rate) - return E_POINTER; - - const std::lock_guard<QRecursiveMutex> locker(m_mutex); - - float maxRate = 0.0f; - - HRESULT hr = checkShutdown(); - if (FAILED(hr)) - return hr; - - // Get the maximum *forward* rate. - maxRate = getMaxRate(thin); - - // For reverse playback, it's the negative of maxRate. - if (direction == MFRATE_REVERSE) - maxRate = -maxRate; - - *rate = maxRate; - - return S_OK; -} - -HRESULT EVRCustomPresenter::IsRateSupported(BOOL thin, float rate, float *nearestSupportedRate) -{ - const std::lock_guard<QRecursiveMutex> locker(m_mutex); - - float maxRate = 0.0f; - float nearestRate = rate; // If we support rate, that is the nearest. - - HRESULT hr = checkShutdown(); - if (FAILED(hr)) - return hr; - - // Find the maximum forward rate. - // Note: We have no minimum rate (that is, we support anything down to 0). - maxRate = getMaxRate(thin); - - if (qFabs(rate) > maxRate) { - // The (absolute) requested rate exceeds the maximum rate. - hr = MF_E_UNSUPPORTED_RATE; - - // The nearest supported rate is maxRate. - nearestRate = maxRate; - if (rate < 0) { - // Negative for reverse playback. - nearestRate = -nearestRate; - } - } - - // Return the nearest supported rate. - if (nearestSupportedRate) - *nearestSupportedRate = nearestRate; - - return hr; -} - -void EVRCustomPresenter::supportedFormatsChanged() -{ - const std::lock_guard<QRecursiveMutex> locker(m_mutex); - - m_canRenderToSurface = false; - m_presentEngine->setHint(D3DPresentEngine::RenderToTexture, false); - - // check if we can render to the surface (compatible formats) - if (m_videoSink) { - if (m_presentEngine->supportsTextureRendering() && m_videoSink->rhi() && m_videoSink->rhi()->backend() == QRhi::OpenGLES2) { - m_presentEngine->setHint(D3DPresentEngine::RenderToTexture, true); - m_canRenderToSurface = true; - } else { - for (int f = 0; f < QVideoFrameFormat::NPixelFormats; ++f) { - // ### set a better preference order - QVideoFrameFormat::PixelFormat format = QVideoFrameFormat::PixelFormat(f); - if (SUCCEEDED(m_presentEngine->checkFormat(qt_evr_D3DFormatFromPixelFormat(format)))) { - m_canRenderToSurface = true; - break; - } - } - } - } - - // TODO: if media type already set, renegotiate? -} - -void EVRCustomPresenter::setSink(QVideoSink *sink) -{ - m_mutex.lock(); - m_videoSink = sink; - m_mutex.unlock(); - - supportedFormatsChanged(); -} - -HRESULT EVRCustomPresenter::configureMixer(IMFTransform *mixer) -{ - // Set the zoom rectangle (ie, the source clipping rectangle). - return setMixerSourceRect(mixer, m_sourceRect); -} - -HRESULT EVRCustomPresenter::renegotiateMediaType() -{ - HRESULT hr = S_OK; - bool foundMediaType = false; - - IMFMediaType *mixerType = NULL; - IMFMediaType *optimalType = NULL; - - if (!m_mixer) - return MF_E_INVALIDREQUEST; - - // Loop through all of the mixer's proposed output types. - DWORD typeIndex = 0; - while (!foundMediaType && (hr != MF_E_NO_MORE_TYPES)) { - qt_evr_safe_release(&mixerType); - qt_evr_safe_release(&optimalType); - - // Step 1. Get the next media type supported by mixer. - hr = m_mixer->GetOutputAvailableType(0, typeIndex++, &mixerType); - if (FAILED(hr)) - break; - - // From now on, if anything in this loop fails, try the next type, - // until we succeed or the mixer runs out of types. - - // Step 2. Check if we support this media type. - if (SUCCEEDED(hr)) - hr = isMediaTypeSupported(mixerType); - - // Step 3. Adjust the mixer's type to match our requirements. - if (SUCCEEDED(hr)) - hr = createOptimalVideoType(mixerType, &optimalType); - - // Step 4. Check if the mixer will accept this media type. - if (SUCCEEDED(hr)) - hr = m_mixer->SetOutputType(0, optimalType, MFT_SET_TYPE_TEST_ONLY); - - // Step 5. Try to set the media type on ourselves. - if (SUCCEEDED(hr)) - hr = setMediaType(optimalType); - - // Step 6. Set output media type on mixer. - if (SUCCEEDED(hr)) { - hr = m_mixer->SetOutputType(0, optimalType, 0); - - // If something went wrong, clear the media type. - if (FAILED(hr)) - setMediaType(NULL); - } - - if (SUCCEEDED(hr)) - foundMediaType = true; - } - - qt_evr_safe_release(&mixerType); - qt_evr_safe_release(&optimalType); - - return hr; -} - -HRESULT EVRCustomPresenter::flush() -{ - m_prerolled = false; - - // The scheduler might have samples that are waiting for - // their presentation time. Tell the scheduler to flush. - - // This call blocks until the scheduler threads discards all scheduled samples. - m_scheduler.flush(); - - // Flush the frame-step queue. - for (auto sample : qAsConst(m_frameStep.samples)) - sample->Release(); - m_frameStep.samples.clear(); - - if (m_renderState == RenderStopped && m_videoSink) { - // Repaint with black. - presentSample(NULL); - } - - return S_OK; -} - -HRESULT EVRCustomPresenter::processInputNotify() -{ - HRESULT hr = S_OK; - - // Set the flag that says the mixer has a new sample. - m_sampleNotify = true; - - if (!m_mediaType) { - // We don't have a valid media type yet. - hr = MF_E_TRANSFORM_TYPE_NOT_SET; - } else { - // Try to process an output sample. - processOutputLoop(); - } - return hr; -} - -HRESULT EVRCustomPresenter::beginStreaming() -{ - HRESULT hr = S_OK; - - // Start the scheduler thread. - hr = m_scheduler.startScheduler(m_clock); - - return hr; -} - -HRESULT EVRCustomPresenter::endStreaming() -{ - HRESULT hr = S_OK; - - // Stop the scheduler thread. - hr = m_scheduler.stopScheduler(); - - return hr; -} - -HRESULT EVRCustomPresenter::checkEndOfStream() -{ - if (!m_endStreaming) { - // The EVR did not send the MFVP_MESSAGE_ENDOFSTREAM message. - return S_OK; - } - - if (m_sampleNotify) { - // The mixer still has input. - return S_OK; - } - - if (m_scheduler.areSamplesScheduled()) { - // Samples are still scheduled for rendering. - return S_OK; - } - - // Everything is complete. Now we can tell the EVR that we are done. - notifyEvent(EC_COMPLETE, (LONG_PTR)S_OK, 0); - m_endStreaming = false; - - stopSurface(); - return S_OK; -} - -HRESULT EVRCustomPresenter::prepareFrameStep(DWORD steps) -{ - HRESULT hr = S_OK; - - // Cache the step count. - m_frameStep.steps += steps; - - // Set the frame-step state. - m_frameStep.state = FrameStepWaitingStart; - - // If the clock is are already running, we can start frame-stepping now. - // Otherwise, we will start when the clock starts. - if (m_renderState == RenderStarted) - hr = startFrameStep(); - - return hr; -} - -HRESULT EVRCustomPresenter::startFrameStep() -{ - HRESULT hr = S_OK; - IMFSample *sample = NULL; - - if (m_frameStep.state == FrameStepWaitingStart) { - // We have a frame-step request, and are waiting for the clock to start. - // Set the state to "pending," which means we are waiting for samples. - m_frameStep.state = FrameStepPending; - - // If the frame-step queue already has samples, process them now. - while (!m_frameStep.samples.isEmpty() && (m_frameStep.state == FrameStepPending)) { - sample = m_frameStep.samples.takeFirst(); - - hr = deliverFrameStepSample(sample); - if (FAILED(hr)) - goto done; - - qt_evr_safe_release(&sample); - - // We break from this loop when: - // (a) the frame-step queue is empty, or - // (b) the frame-step operation is complete. - } - } else if (m_frameStep.state == FrameStepNone) { - // We are not frame stepping. Therefore, if the frame-step queue has samples, - // we need to process them normally. - while (!m_frameStep.samples.isEmpty()) { - sample = m_frameStep.samples.takeFirst(); - - hr = deliverSample(sample, false); - if (FAILED(hr)) - goto done; - - qt_evr_safe_release(&sample); - } - } - -done: - qt_evr_safe_release(&sample); - return hr; -} - -HRESULT EVRCustomPresenter::completeFrameStep(IMFSample *sample) -{ - HRESULT hr = S_OK; - MFTIME sampleTime = 0; - MFTIME systemTime = 0; - - // Update our state. - m_frameStep.state = FrameStepComplete; - m_frameStep.sampleNoRef = 0; - - // Notify the EVR that the frame-step is complete. - notifyEvent(EC_STEP_COMPLETE, FALSE, 0); // FALSE = completed (not cancelled) - - // If we are scrubbing (rate == 0), also send the "scrub time" event. - if (isScrubbing()) { - // Get the time stamp from the sample. - hr = sample->GetSampleTime(&sampleTime); - if (FAILED(hr)) { - // No time stamp. Use the current presentation time. - if (m_clock) - m_clock->GetCorrelatedTime(0, &sampleTime, &systemTime); - - hr = S_OK; // (Not an error condition.) - } - - notifyEvent(EC_SCRUB_TIME, DWORD(sampleTime), DWORD(((sampleTime) >> 32) & 0xffffffff)); - } - return hr; -} - -HRESULT EVRCustomPresenter::cancelFrameStep() -{ - FrameStepState oldState = m_frameStep.state; - - m_frameStep.state = FrameStepNone; - m_frameStep.steps = 0; - m_frameStep.sampleNoRef = 0; - // Don't clear the frame-step queue yet, because we might frame step again. - - if (oldState > FrameStepNone && oldState < FrameStepComplete) { - // We were in the middle of frame-stepping when it was cancelled. - // Notify the EVR. - notifyEvent(EC_STEP_COMPLETE, TRUE, 0); // TRUE = cancelled - } - return S_OK; -} - -HRESULT EVRCustomPresenter::createOptimalVideoType(IMFMediaType *proposedType, IMFMediaType **optimalType) -{ - HRESULT hr = S_OK; - - RECT rcOutput; - ZeroMemory(&rcOutput, sizeof(rcOutput)); - - MFVideoArea displayArea; - ZeroMemory(&displayArea, sizeof(displayArea)); - - IMFMediaType *mtOptimal = NULL; - - UINT64 size; - int width; - int height; - - // Clone the proposed type. - - hr = MFCreateMediaType(&mtOptimal); - if (FAILED(hr)) - goto done; - - hr = proposedType->CopyAllItems(mtOptimal); - if (FAILED(hr)) - goto done; - - // Modify the new type. - - hr = proposedType->GetUINT64(MF_MT_FRAME_SIZE, &size); - width = int(HI32(size)); - height = int(LO32(size)); - rcOutput.left = 0; - rcOutput.top = 0; - rcOutput.right = width; - rcOutput.bottom = height; - - // Set the geometric aperture, and disable pan/scan. - displayArea = qt_evr_makeMFArea(0, 0, rcOutput.right, rcOutput.bottom); - - hr = mtOptimal->SetUINT32(MF_MT_PAN_SCAN_ENABLED, FALSE); - if (FAILED(hr)) - goto done; - - hr = mtOptimal->SetBlob(MF_MT_GEOMETRIC_APERTURE, reinterpret_cast<UINT8*>(&displayArea), - sizeof(displayArea)); - if (FAILED(hr)) - goto done; - - // Set the pan/scan aperture and the minimum display aperture. We don't care - // about them per se, but the mixer will reject the type if these exceed the - // frame dimentions. - hr = mtOptimal->SetBlob(MF_MT_PAN_SCAN_APERTURE, reinterpret_cast<UINT8*>(&displayArea), - sizeof(displayArea)); - if (FAILED(hr)) - goto done; - - hr = mtOptimal->SetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE, reinterpret_cast<UINT8*>(&displayArea), - sizeof(displayArea)); - if (FAILED(hr)) - goto done; - - // Return the pointer to the caller. - *optimalType = mtOptimal; - (*optimalType)->AddRef(); - -done: - qt_evr_safe_release(&mtOptimal); - return hr; - -} - -HRESULT EVRCustomPresenter::setMediaType(IMFMediaType *mediaType) -{ - // Note: mediaType can be NULL (to clear the type) - - // Clearing the media type is allowed in any state (including shutdown). - if (!mediaType) { - stopSurface(); - qt_evr_safe_release(&m_mediaType); - releaseResources(); - return S_OK; - } - - MFRatio fps = { 0, 0 }; - QList<IMFSample*> sampleQueue; - - // Cannot set the media type after shutdown. - HRESULT hr = checkShutdown(); - if (FAILED(hr)) - goto done; - - // Check if the new type is actually different. - // Note: This function safely handles NULL input parameters. - if (qt_evr_areMediaTypesEqual(m_mediaType, mediaType)) - goto done; // Nothing more to do. - - // We're really changing the type. First get rid of the old type. - qt_evr_safe_release(&m_mediaType); - releaseResources(); - - // Initialize the presenter engine with the new media type. - // The presenter engine allocates the samples. - - hr = m_presentEngine->createVideoSamples(mediaType, sampleQueue); - if (FAILED(hr)) - goto done; - - // Mark each sample with our token counter. If this batch of samples becomes - // invalid, we increment the counter, so that we know they should be discarded. - for (auto sample : qAsConst(sampleQueue)) { - hr = sample->SetUINT32(MFSamplePresenter_SampleCounter, m_tokenCounter); - if (FAILED(hr)) - goto done; - } - - // Add the samples to the sample pool. - hr = m_samplePool.initialize(sampleQueue); - if (FAILED(hr)) - goto done; - - // Set the frame rate on the scheduler. - if (SUCCEEDED(qt_evr_getFrameRate(mediaType, &fps)) && (fps.Numerator != 0) && (fps.Denominator != 0)) { - m_scheduler.setFrameRate(fps); - } else { - // NOTE: The mixer's proposed type might not have a frame rate, in which case - // we'll use an arbitrary default. (Although it's unlikely the video source - // does not have a frame rate.) - m_scheduler.setFrameRate(g_DefaultFrameRate); - } - - // Store the media type. - m_mediaType = mediaType; - m_mediaType->AddRef(); - - startSurface(); - -done: - if (FAILED(hr)) - releaseResources(); - return hr; -} - -HRESULT EVRCustomPresenter::isMediaTypeSupported(IMFMediaType *proposed) -{ - D3DFORMAT d3dFormat = D3DFMT_UNKNOWN; - BOOL compressed = FALSE; - MFVideoInterlaceMode interlaceMode = MFVideoInterlace_Unknown; - MFVideoArea videoCropArea; - UINT32 width = 0, height = 0; - - // Validate the format. - HRESULT hr = qt_evr_getFourCC(proposed, reinterpret_cast<DWORD*>(&d3dFormat)); - if (FAILED(hr)) - return hr; - - QVideoFrameFormat::PixelFormat pixelFormat = pixelFormatFromMediaType(proposed); - if (pixelFormat == QVideoFrameFormat::Format_Invalid) - return MF_E_INVALIDMEDIATYPE; - - // Reject compressed media types. - hr = proposed->IsCompressedFormat(&compressed); - if (FAILED(hr)) - return hr; - - if (compressed) - return MF_E_INVALIDMEDIATYPE; - - // The D3DPresentEngine checks whether surfaces can be created using this format - hr = m_presentEngine->checkFormat(d3dFormat); - if (FAILED(hr)) - return hr; - - // Reject interlaced formats. - hr = proposed->GetUINT32(MF_MT_INTERLACE_MODE, reinterpret_cast<UINT32*>(&interlaceMode)); - if (FAILED(hr)) - return hr; - - if (interlaceMode != MFVideoInterlace_Progressive) - return MF_E_INVALIDMEDIATYPE; - - hr = MFGetAttributeSize(proposed, MF_MT_FRAME_SIZE, &width, &height); - if (FAILED(hr)) - return hr; - - // Validate the various apertures (cropping regions) against the frame size. - // Any of these apertures may be unspecified in the media type, in which case - // we ignore it. We just want to reject invalid apertures. - - if (SUCCEEDED(proposed->GetBlob(MF_MT_PAN_SCAN_APERTURE, - reinterpret_cast<UINT8*>(&videoCropArea), - sizeof(videoCropArea), nullptr))) { - hr = qt_evr_validateVideoArea(videoCropArea, width, height); - } - if (SUCCEEDED(proposed->GetBlob(MF_MT_GEOMETRIC_APERTURE, - reinterpret_cast<UINT8*>(&videoCropArea), - sizeof(videoCropArea), nullptr))) { - hr = qt_evr_validateVideoArea(videoCropArea, width, height); - } - if (SUCCEEDED(proposed->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE, - reinterpret_cast<UINT8*>(&videoCropArea), - sizeof(videoCropArea), nullptr))) { - hr = qt_evr_validateVideoArea(videoCropArea, width, height); - } - return hr; -} - -void EVRCustomPresenter::processOutputLoop() -{ - HRESULT hr = S_OK; - - // Process as many samples as possible. - while (hr == S_OK) { - // If the mixer doesn't have a new input sample, break from the loop. - if (!m_sampleNotify) { - hr = MF_E_TRANSFORM_NEED_MORE_INPUT; - break; - } - - // Try to process a sample. - hr = processOutput(); - - // NOTE: ProcessOutput can return S_FALSE to indicate it did not - // process a sample. If so, break out of the loop. - } - - if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { - // The mixer has run out of input data. Check for end-of-stream. - checkEndOfStream(); - } -} - -HRESULT EVRCustomPresenter::processOutput() -{ - HRESULT hr = S_OK; - DWORD status = 0; - LONGLONG mixerStartTime = 0, mixerEndTime = 0; - MFTIME systemTime = 0; - BOOL repaint = m_repaint; // Temporarily store this state flag. - - MFT_OUTPUT_DATA_BUFFER dataBuffer; - ZeroMemory(&dataBuffer, sizeof(dataBuffer)); - - IMFSample *sample = NULL; - - // If the clock is not running, we present the first sample, - // and then don't present any more until the clock starts. - - if ((m_renderState != RenderStarted) && !m_repaint && m_prerolled) - return S_FALSE; - - // Make sure we have a pointer to the mixer. - if (!m_mixer) - return MF_E_INVALIDREQUEST; - - // Try to get a free sample from the video sample pool. - hr = m_samplePool.getSample(&sample); - if (hr == MF_E_SAMPLEALLOCATOR_EMPTY) // No free samples. Try again when a sample is released. - return S_FALSE; - if (FAILED(hr)) - return hr; - - // From now on, we have a valid video sample pointer, where the mixer will - // write the video data. - - if (m_repaint) { - // Repaint request. Ask the mixer for the most recent sample. - setDesiredSampleTime(sample, m_scheduler.lastSampleTime(), m_scheduler.frameDuration()); - - m_repaint = false; // OK to clear this flag now. - } else { - // Not a repaint request. Clear the desired sample time; the mixer will - // give us the next frame in the stream. - clearDesiredSampleTime(sample); - - if (m_clock) { - // Latency: Record the starting time for ProcessOutput. - m_clock->GetCorrelatedTime(0, &mixerStartTime, &systemTime); - } - } - - // Now we are ready to get an output sample from the mixer. - dataBuffer.dwStreamID = 0; - dataBuffer.pSample = sample; - dataBuffer.dwStatus = 0; - - hr = m_mixer->ProcessOutput(0, 1, &dataBuffer, &status); - - if (FAILED(hr)) { - // Return the sample to the pool. - HRESULT hr2 = m_samplePool.returnSample(sample); - if (FAILED(hr2)) { - hr = hr2; - goto done; - } - // Handle some known error codes from ProcessOutput. - if (hr == MF_E_TRANSFORM_TYPE_NOT_SET) { - // The mixer's format is not set. Negotiate a new format. - hr = renegotiateMediaType(); - } else if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { - // There was a dynamic media type change. Clear our media type. - setMediaType(NULL); - } else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { - // The mixer needs more input. - // We have to wait for the mixer to get more input. - m_sampleNotify = false; - } - } else { - // We got an output sample from the mixer. - - if (m_clock && !repaint) { - // Latency: Record the ending time for the ProcessOutput operation, - // and notify the EVR of the latency. - - m_clock->GetCorrelatedTime(0, &mixerEndTime, &systemTime); - - LONGLONG latencyTime = mixerEndTime - mixerStartTime; - notifyEvent(EC_PROCESSING_LATENCY, reinterpret_cast<LONG_PTR>(&latencyTime), 0); - } - - // Set up notification for when the sample is released. - hr = trackSample(sample); - if (FAILED(hr)) - goto done; - - // Schedule the sample. - if ((m_frameStep.state == FrameStepNone) || repaint) { - hr = deliverSample(sample, repaint); - if (FAILED(hr)) - goto done; - } else { - // We are frame-stepping (and this is not a repaint request). - hr = deliverFrameStepSample(sample); - if (FAILED(hr)) - goto done; - } - - m_prerolled = true; // We have presented at least one sample now. - } - -done: - qt_evr_safe_release(&sample); - - // Important: Release any events returned from the ProcessOutput method. - qt_evr_safe_release(&dataBuffer.pEvents); - return hr; -} - -HRESULT EVRCustomPresenter::deliverSample(IMFSample *sample, bool repaint) -{ - // If we are not actively playing, OR we are scrubbing (rate = 0) OR this is a - // repaint request, then we need to present the sample immediately. Otherwise, - // schedule it normally. - - bool presentNow = ((m_renderState != RenderStarted) || isScrubbing() || repaint); - - HRESULT hr = m_scheduler.scheduleSample(sample, presentNow); - - if (FAILED(hr)) { - // Notify the EVR that we have failed during streaming. The EVR will notify the - // pipeline. - - notifyEvent(EC_ERRORABORT, hr, 0); - } - - return hr; -} - -HRESULT EVRCustomPresenter::deliverFrameStepSample(IMFSample *sample) -{ - HRESULT hr = S_OK; - IUnknown *unk = NULL; - - // For rate 0, discard any sample that ends earlier than the clock time. - if (isScrubbing() && m_clock && qt_evr_isSampleTimePassed(m_clock, sample)) { - // Discard this sample. - } else if (m_frameStep.state >= FrameStepScheduled) { - // A frame was already submitted. Put this sample on the frame-step queue, - // in case we are asked to step to the next frame. If frame-stepping is - // cancelled, this sample will be processed normally. - sample->AddRef(); - m_frameStep.samples.append(sample); - } else { - // We're ready to frame-step. - - // Decrement the number of steps. - if (m_frameStep.steps > 0) - m_frameStep.steps--; - - if (m_frameStep.steps > 0) { - // This is not the last step. Discard this sample. - } else if (m_frameStep.state == FrameStepWaitingStart) { - // This is the right frame, but the clock hasn't started yet. Put the - // sample on the frame-step queue. When the clock starts, the sample - // will be processed. - sample->AddRef(); - m_frameStep.samples.append(sample); - } else { - // This is the right frame *and* the clock has started. Deliver this sample. - hr = deliverSample(sample, false); - if (FAILED(hr)) - goto done; - - // Query for IUnknown so that we can identify the sample later. - // Per COM rules, an object always returns the same pointer when QI'ed for IUnknown. - hr = sample->QueryInterface(IID_PPV_ARGS(&unk)); - if (FAILED(hr)) - goto done; - - m_frameStep.sampleNoRef = reinterpret_cast<DWORD_PTR>(unk); // No add-ref. - - // NOTE: We do not AddRef the IUnknown pointer, because that would prevent the - // sample from invoking the OnSampleFree callback after the sample is presented. - // We use this IUnknown pointer purely to identify the sample later; we never - // attempt to dereference the pointer. - - m_frameStep.state = FrameStepScheduled; - } - } -done: - qt_evr_safe_release(&unk); - return hr; -} - -HRESULT EVRCustomPresenter::trackSample(IMFSample *sample) -{ - IMFTrackedSample *tracked = NULL; - - HRESULT hr = sample->QueryInterface(IID_PPV_ARGS(&tracked)); - - if (SUCCEEDED(hr)) - hr = tracked->SetAllocator(&m_sampleFreeCB, NULL); - - qt_evr_safe_release(&tracked); - return hr; -} - -void EVRCustomPresenter::releaseResources() -{ - // Increment the token counter to indicate that all existing video samples - // are "stale." As these samples get released, we'll dispose of them. - // - // Note: The token counter is required because the samples are shared - // between more than one thread, and they are returned to the presenter - // through an asynchronous callback (onSampleFree). Without the token, we - // might accidentally re-use a stale sample after the ReleaseResources - // method returns. - - m_tokenCounter++; - - flush(); - - m_samplePool.clear(); - - m_presentEngine->releaseResources(); -} - -HRESULT EVRCustomPresenter::onSampleFree(IMFAsyncResult *result) -{ - IUnknown *object = NULL; - IMFSample *sample = NULL; - IUnknown *unk = NULL; - UINT32 token; - - // Get the sample from the async result object. - HRESULT hr = result->GetObject(&object); - if (FAILED(hr)) - goto done; - - hr = object->QueryInterface(IID_PPV_ARGS(&sample)); - if (FAILED(hr)) - goto done; - - // If this sample was submitted for a frame-step, the frame step operation - // is complete. - - if (m_frameStep.state == FrameStepScheduled) { - // Query the sample for IUnknown and compare it to our cached value. - hr = sample->QueryInterface(IID_PPV_ARGS(&unk)); - if (FAILED(hr)) - goto done; - - if (m_frameStep.sampleNoRef == reinterpret_cast<DWORD_PTR>(unk)) { - // Notify the EVR. - hr = completeFrameStep(sample); - if (FAILED(hr)) - goto done; - } - - // Note: Although object is also an IUnknown pointer, it is not - // guaranteed to be the exact pointer value returned through - // QueryInterface. Therefore, the second QueryInterface call is - // required. - } - - m_mutex.lock(); - - token = MFGetAttributeUINT32(sample, MFSamplePresenter_SampleCounter, (UINT32)-1); - - if (token == m_tokenCounter) { - // Return the sample to the sample pool. - hr = m_samplePool.returnSample(sample); - if (SUCCEEDED(hr)) { - // A free sample is available. Process more data if possible. - processOutputLoop(); - } - } - - m_mutex.unlock(); - -done: - if (FAILED(hr)) - notifyEvent(EC_ERRORABORT, hr, 0); - qt_evr_safe_release(&object); - qt_evr_safe_release(&sample); - qt_evr_safe_release(&unk); - return hr; -} - -float EVRCustomPresenter::getMaxRate(bool thin) -{ - // Non-thinned: - // If we have a valid frame rate and a monitor refresh rate, the maximum - // playback rate is equal to the refresh rate. Otherwise, the maximum rate - // is unbounded (FLT_MAX). - - // Thinned: The maximum rate is unbounded. - - float maxRate = FLT_MAX; - MFRatio fps = { 0, 0 }; - UINT monitorRateHz = 0; - - if (!thin && m_mediaType) { - qt_evr_getFrameRate(m_mediaType, &fps); - monitorRateHz = m_presentEngine->refreshRate(); - - if (fps.Denominator && fps.Numerator && monitorRateHz) { - // Max Rate = Refresh Rate / Frame Rate - maxRate = (float)MulDiv(monitorRateHz, fps.Denominator, fps.Numerator); - } - } - - return maxRate; -} - -bool EVRCustomPresenter::event(QEvent *e) -{ - switch (int(e->type())) { - case StartSurface: - startSurface(); - return true; - case StopSurface: - stopSurface(); - return true; - case PresentSample: - presentSample(static_cast<PresentSampleEvent *>(e)->sample()); - return true; - default: - break; - } - return QObject::event(e); -} - -void EVRCustomPresenter::startSurface() -{ - if (thread() != QThread::currentThread()) { - QCoreApplication::postEvent(this, new QEvent(QEvent::Type(StartSurface))); - return; - } -} - -void EVRCustomPresenter::stopSurface() -{ - if (thread() != QThread::currentThread()) { - QCoreApplication::postEvent(this, new QEvent(QEvent::Type(StopSurface))); - return; - } -} - -void EVRCustomPresenter::presentSample(IMFSample *sample) -{ - if (thread() != QThread::currentThread()) { - QCoreApplication::postEvent(this, new PresentSampleEvent(sample)); - return; - } - - if (!m_videoSink || !m_presentEngine->videoSurfaceFormat().isValid()) - return; - - QVideoFrame frame = m_presentEngine->makeVideoFrame(sample); - - // Since start/end times are related to a position when the clock is started, - // to have times from the beginning, need to adjust it by adding seeked position. - if (m_positionOffset) { - if (frame.startTime()) - frame.setStartTime(frame.startTime() + m_positionOffset); - if (frame.endTime()) - frame.setEndTime(frame.endTime() + m_positionOffset); - } - - m_videoSink->platformVideoSink()->setVideoFrame(frame); -} - -void EVRCustomPresenter::positionChanged(qint64 position) -{ - m_positionOffset = position * 1000; -} - -HRESULT setDesiredSampleTime(IMFSample *sample, const LONGLONG &sampleTime, const LONGLONG &duration) -{ - if (!sample) - return E_POINTER; - - HRESULT hr = S_OK; - IMFDesiredSample *desired = NULL; - - hr = sample->QueryInterface(IID_PPV_ARGS(&desired)); - if (SUCCEEDED(hr)) - desired->SetDesiredSampleTimeAndDuration(sampleTime, duration); - - qt_evr_safe_release(&desired); - return hr; -} - -HRESULT clearDesiredSampleTime(IMFSample *sample) -{ - if (!sample) - return E_POINTER; - - HRESULT hr = S_OK; - - IMFDesiredSample *desired = NULL; - IUnknown *unkSwapChain = NULL; - - // We store some custom attributes on the sample, so we need to cache them - // and reset them. - // - // This works around the fact that IMFDesiredSample::Clear() removes all of the - // attributes from the sample. - - UINT32 counter = MFGetAttributeUINT32(sample, MFSamplePresenter_SampleCounter, (UINT32)-1); - - hr = sample->QueryInterface(IID_PPV_ARGS(&desired)); - if (SUCCEEDED(hr)) { - desired->Clear(); - - hr = sample->SetUINT32(MFSamplePresenter_SampleCounter, counter); - if (FAILED(hr)) - goto done; - } - -done: - qt_evr_safe_release(&unkSwapChain); - qt_evr_safe_release(&desired); - return hr; -} - -HRESULT setMixerSourceRect(IMFTransform *mixer, const MFVideoNormalizedRect &sourceRect) -{ - if (!mixer) - return E_POINTER; - - IMFAttributes *attributes = NULL; - - HRESULT hr = mixer->GetAttributes(&attributes); - if (SUCCEEDED(hr)) { - hr = attributes->SetBlob(video_ZOOM_RECT, reinterpret_cast<const UINT8*>(&sourceRect), - sizeof(sourceRect)); - attributes->Release(); - } - return hr; -} - -static QVideoFrameFormat::PixelFormat pixelFormatFromMediaType(IMFMediaType *type) -{ - GUID majorType; - if (FAILED(type->GetMajorType(&majorType))) - return QVideoFrameFormat::Format_Invalid; - if (majorType != MFMediaType_Video) - return QVideoFrameFormat::Format_Invalid; - - GUID subtype; - if (FAILED(type->GetGUID(MF_MT_SUBTYPE, &subtype))) - return QVideoFrameFormat::Format_Invalid; - - return QWindowsMultimediaUtils::pixelFormatFromMediaSubtype(subtype); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/windows/evr/evrcustompresenter_p.h b/src/multimedia/platform/windows/evr/evrcustompresenter_p.h deleted file mode 100644 index 9b34e449f..000000000 --- a/src/multimedia/platform/windows/evr/evrcustompresenter_p.h +++ /dev/null @@ -1,387 +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$ -** -****************************************************************************/ - -#ifndef EVRCUSTOMPRESENTER_H -#define EVRCUSTOMPRESENTER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QObject> -#include <qmutex.h> -#include <qqueue.h> -#include <qevent.h> -#include <qvideoframeformat.h> -#include <qvideosink.h> - -#include "evrdefs_p.h" - -QT_BEGIN_NAMESPACE - -class EVRCustomPresenter; -class D3DPresentEngine; - -template<class T> -class AsyncCallback : public IMFAsyncCallback -{ - Q_DISABLE_COPY(AsyncCallback) -public: - typedef HRESULT (T::*InvokeFn)(IMFAsyncResult *asyncResult); - - AsyncCallback(T *parent, InvokeFn fn) : m_parent(parent), m_invokeFn(fn) - { - } - - // IUnknown - STDMETHODIMP QueryInterface(REFIID iid, void** ppv) override - { - if (!ppv) - return E_POINTER; - - if (iid == __uuidof(IUnknown)) { - *ppv = static_cast<IUnknown*>(static_cast<IMFAsyncCallback*>(this)); - } else if (iid == __uuidof(IMFAsyncCallback)) { - *ppv = static_cast<IMFAsyncCallback*>(this); - } else { - *ppv = NULL; - return E_NOINTERFACE; - } - AddRef(); - return S_OK; - } - - STDMETHODIMP_(ULONG) AddRef() override { - // Delegate to parent class. - return m_parent->AddRef(); - } - STDMETHODIMP_(ULONG) Release() override { - // Delegate to parent class. - return m_parent->Release(); - } - - // IMFAsyncCallback methods - STDMETHODIMP GetParameters(DWORD*, DWORD*) override - { - // Implementation of this method is optional. - return E_NOTIMPL; - } - - STDMETHODIMP Invoke(IMFAsyncResult* asyncResult) override - { - return (m_parent->*m_invokeFn)(asyncResult); - } - - T *m_parent; - InvokeFn m_invokeFn; -}; - -class Scheduler -{ - Q_DISABLE_COPY(Scheduler) -public: - enum ScheduleEvent - { - Terminate = WM_USER, - Schedule = WM_USER + 1, - Flush = WM_USER + 2 - }; - - Scheduler(EVRCustomPresenter *presenter); - ~Scheduler(); - - void setFrameRate(const MFRatio &fps); - void setClockRate(float rate) { m_playbackRate = rate; } - - const LONGLONG &lastSampleTime() const { return m_lastSampleTime; } - const LONGLONG &frameDuration() const { return m_perFrameInterval; } - - HRESULT startScheduler(IMFClock *clock); - HRESULT stopScheduler(); - - HRESULT scheduleSample(IMFSample *sample, bool presentNow); - HRESULT processSamplesInQueue(LONG *nextSleep); - HRESULT processSample(IMFSample *sample, LONG *nextSleep); - HRESULT flush(); - - bool areSamplesScheduled(); - - // ThreadProc for the scheduler thread. - static DWORD WINAPI schedulerThreadProc(LPVOID parameter); - -private: - DWORD schedulerThreadProcPrivate(); - - EVRCustomPresenter *m_presenter; - - QQueue<IMFSample*> m_scheduledSamples; // Samples waiting to be presented. - - IMFClock *m_clock; // Presentation clock. Can be NULL. - - DWORD m_threadID; - HANDLE m_schedulerThread; - HANDLE m_threadReadyEvent; - HANDLE m_flushEvent; - - float m_playbackRate; - MFTIME m_perFrameInterval; // Duration of each frame. - LONGLONG m_perFrame_1_4th; // 1/4th of the frame duration. - MFTIME m_lastSampleTime; // Most recent sample time. - - QMutex m_mutex; -}; - -class SamplePool -{ - Q_DISABLE_COPY(SamplePool) -public: - SamplePool(); - ~SamplePool(); - - HRESULT initialize(QList<IMFSample*> &samples); - HRESULT clear(); - - HRESULT getSample(IMFSample **sample); - HRESULT returnSample(IMFSample *sample); - -private: - QMutex m_mutex; - QList<IMFSample*> m_videoSampleQueue; - bool m_initialized; -}; - -class EVRCustomPresenter - : public QObject - , public IMFVideoDeviceID - , public IMFVideoPresenter // Inherits IMFClockStateSink - , public IMFRateSupport - , public IMFGetService - , public IMFTopologyServiceLookupClient -{ - Q_DISABLE_COPY(EVRCustomPresenter) -public: - // Defines the state of the presenter. - enum RenderState - { - RenderStarted = 1, - RenderStopped, - RenderPaused, - RenderShutdown // Initial state. - }; - - // Defines the presenter's state with respect to frame-stepping. - enum FrameStepState - { - FrameStepNone, // Not frame stepping. - FrameStepWaitingStart, // Frame stepping, but the clock is not started. - FrameStepPending, // Clock is started. Waiting for samples. - FrameStepScheduled, // Submitted a sample for rendering. - FrameStepComplete // Sample was rendered. - }; - - enum PresenterEvents - { - StartSurface = QEvent::User, - StopSurface = QEvent::User + 1, - PresentSample = QEvent::User + 2 - }; - - EVRCustomPresenter(QVideoSink *sink = 0); - ~EVRCustomPresenter() override; - - bool isValid() const; - - // IUnknown methods - STDMETHODIMP QueryInterface(REFIID riid, void ** ppv) override; - STDMETHODIMP_(ULONG) AddRef() override; - STDMETHODIMP_(ULONG) Release() override; - - // IMFGetService methods - STDMETHODIMP GetService(REFGUID guidService, REFIID riid, LPVOID *ppvObject) override; - - // IMFVideoPresenter methods - STDMETHODIMP ProcessMessage(MFVP_MESSAGE_TYPE message, ULONG_PTR param) override; - STDMETHODIMP GetCurrentMediaType(IMFVideoMediaType** mediaType) override; - - // IMFClockStateSink methods - STDMETHODIMP OnClockStart(MFTIME systemTime, LONGLONG clockStartOffset) override; - STDMETHODIMP OnClockStop(MFTIME systemTime) override; - STDMETHODIMP OnClockPause(MFTIME systemTime) override; - STDMETHODIMP OnClockRestart(MFTIME systemTime) override; - STDMETHODIMP OnClockSetRate(MFTIME systemTime, float rate) override; - - // IMFRateSupport methods - STDMETHODIMP GetSlowestRate(MFRATE_DIRECTION direction, BOOL thin, float *rate) override; - STDMETHODIMP GetFastestRate(MFRATE_DIRECTION direction, BOOL thin, float *rate) override; - STDMETHODIMP IsRateSupported(BOOL thin, float rate, float *nearestSupportedRate) override; - - // IMFVideoDeviceID methods - STDMETHODIMP GetDeviceID(IID* deviceID) override; - - // IMFTopologyServiceLookupClient methods - STDMETHODIMP InitServicePointers(IMFTopologyServiceLookup *lookup) override; - STDMETHODIMP ReleaseServicePointers() override; - - void supportedFormatsChanged(); - void setSink(QVideoSink *sink); - - void startSurface(); - void stopSurface(); - void presentSample(IMFSample *sample); - - bool event(QEvent *) override; - -public Q_SLOTS: - void positionChanged(qint64 position); - -private: - HRESULT checkShutdown() const - { - if (m_renderState == RenderShutdown) - return MF_E_SHUTDOWN; - else - return S_OK; - } - - // The "active" state is started or paused. - inline bool isActive() const - { - return ((m_renderState == RenderStarted) || (m_renderState == RenderPaused)); - } - - // Scrubbing occurs when the frame rate is 0. - inline bool isScrubbing() const { return m_playbackRate == 0.0f; } - - // Send an event to the EVR through its IMediaEventSink interface. - void notifyEvent(long eventCode, LONG_PTR param1, LONG_PTR param2) - { - if (m_mediaEventSink) - m_mediaEventSink->Notify(eventCode, param1, param2); - } - - float getMaxRate(bool thin); - - // Mixer operations - HRESULT configureMixer(IMFTransform *mixer); - - // Formats - HRESULT createOptimalVideoType(IMFMediaType* proposed, IMFMediaType **optimal); - HRESULT setMediaType(IMFMediaType *mediaType); - HRESULT isMediaTypeSupported(IMFMediaType *mediaType); - - // Message handlers - HRESULT flush(); - HRESULT renegotiateMediaType(); - HRESULT processInputNotify(); - HRESULT beginStreaming(); - HRESULT endStreaming(); - HRESULT checkEndOfStream(); - - // Managing samples - void processOutputLoop(); - HRESULT processOutput(); - HRESULT deliverSample(IMFSample *sample, bool repaint); - HRESULT trackSample(IMFSample *sample); - void releaseResources(); - - // Frame-stepping - HRESULT prepareFrameStep(DWORD steps); - HRESULT startFrameStep(); - HRESULT deliverFrameStepSample(IMFSample *sample); - HRESULT completeFrameStep(IMFSample *sample); - HRESULT cancelFrameStep(); - - // Callback when a video sample is released. - HRESULT onSampleFree(IMFAsyncResult *result); - AsyncCallback<EVRCustomPresenter> m_sampleFreeCB; - - // Holds information related to frame-stepping. - struct FrameStep - { - FrameStepState state = FrameStepNone; - QList<IMFSample*> samples; - DWORD steps = 0; - DWORD_PTR sampleNoRef = 0; - }; - - long m_refCount; - - RenderState m_renderState; - FrameStep m_frameStep; - - QRecursiveMutex m_mutex; - - // Samples and scheduling - Scheduler m_scheduler; // Manages scheduling of samples. - SamplePool m_samplePool; // Pool of allocated samples. - DWORD m_tokenCounter; // Counter. Incremented whenever we create new samples. - - // Rendering state - bool m_sampleNotify; // Did the mixer signal it has an input sample? - bool m_repaint; // Do we need to repaint the last sample? - bool m_prerolled; // Have we presented at least one sample? - bool m_endStreaming; // Did we reach the end of the stream (EOS)? - - MFVideoNormalizedRect m_sourceRect; - float m_playbackRate; - - D3DPresentEngine *m_presentEngine; // Rendering engine. (Never null if the constructor succeeds.) - - IMFClock *m_clock; // The EVR's clock. - IMFTransform *m_mixer; // The EVR's mixer. - IMediaEventSink *m_mediaEventSink; // The EVR's event-sink interface. - IMFMediaType *m_mediaType; // Output media type - - QVideoSink *m_videoSink; - bool m_canRenderToSurface; - qint64 m_positionOffset; // Seek position in microseconds. -}; - -bool qt_evr_setCustomPresenter(IUnknown *evr, EVRCustomPresenter *presenter); - -QT_END_NAMESPACE - -#endif // EVRCUSTOMPRESENTER_H diff --git a/src/multimedia/platform/windows/evr/evrd3dpresentengine.cpp b/src/multimedia/platform/windows/evr/evrd3dpresentengine.cpp deleted file mode 100644 index 89c22d3ae..000000000 --- a/src/multimedia/platform/windows/evr/evrd3dpresentengine.cpp +++ /dev/null @@ -1,393 +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$ -** -****************************************************************************/ - -#include "evrd3dpresentengine_p.h" - -#include "evrhelpers_p.h" - -#include <private/qabstractvideobuffer_p.h> -#include <qvideoframe.h> -#include <QDebug> -#include <qthread.h> -#include <QOffscreenSurface> - -static const int PRESENTER_BUFFER_COUNT = 3; - -QT_BEGIN_NAMESPACE - -class IMFSampleVideoBuffer: public QAbstractVideoBuffer -{ -public: - IMFSampleVideoBuffer(D3DPresentEngine *engine, IMFSample *sample, QVideoFrame::HandleType handleType) - : QAbstractVideoBuffer(handleType) - , m_sample(sample) - , m_surface(0) - , m_mapMode(QVideoFrame::NotMapped) - { - if (m_sample) { - m_sample->AddRef(); - - IMFMediaBuffer *buffer; - if (SUCCEEDED(m_sample->GetBufferByIndex(0, &buffer))) { - MFGetService(buffer, - mr_BUFFER_SERVICE, - iid_IDirect3DSurface9, - reinterpret_cast<void **>(&m_surface)); - buffer->Release(); - } - } - } - - ~IMFSampleVideoBuffer() override - { - if (m_surface) { - if (m_mapMode != QVideoFrame::NotMapped) - m_surface->UnlockRect(); - m_surface->Release(); - } - if (m_sample) - m_sample->Release(); - } - - QVideoFrame::MapMode mapMode() const override { return m_mapMode; } - MapData map(QVideoFrame::MapMode mode) override; - void unmap() override; - -private: - IMFSample *m_sample; - IDirect3DSurface9 *m_surface; - QVideoFrame::MapMode m_mapMode; -}; - -IMFSampleVideoBuffer::MapData IMFSampleVideoBuffer::map(QVideoFrame::MapMode mode) -{ - if (!m_surface || m_mapMode != QVideoFrame::NotMapped) - return {}; - - D3DSURFACE_DESC desc; - if (FAILED(m_surface->GetDesc(&desc))) - return {}; - - D3DLOCKED_RECT rect; - if (FAILED(m_surface->LockRect(&rect, NULL, mode == QVideoFrame::ReadOnly ? D3DLOCK_READONLY : 0))) - return {}; - - m_mapMode = mode; - - MapData mapData; - mapData.nPlanes = 1; - mapData.bytesPerLine[0] = (int)rect.Pitch; - mapData.data[0] = reinterpret_cast<uchar *>(rect.pBits); - mapData.size[0] = (int)(rect.Pitch * desc.Height); - return mapData; -} - -void IMFSampleVideoBuffer::unmap() -{ - if (m_mapMode == QVideoFrame::NotMapped) - return; - - m_mapMode = QVideoFrame::NotMapped; - m_surface->UnlockRect(); -} - -D3DPresentEngine::D3DPresentEngine() - : m_deviceResetToken(0) - , m_D3D9(0) - , m_device(0) - , m_devices(0) - , m_useTextureRendering(false) -{ - ZeroMemory(&m_displayMode, sizeof(m_displayMode)); - - HRESULT hr = initializeD3D(); - - if (SUCCEEDED(hr)) { - hr = createD3DDevice(); - if (FAILED(hr)) - qWarning("Failed to create D3D device"); - } else { - qWarning("Failed to initialize D3D"); - } -} - -D3DPresentEngine::~D3DPresentEngine() -{ - releaseResources(); - - qt_evr_safe_release(&m_device); - qt_evr_safe_release(&m_devices); - qt_evr_safe_release(&m_D3D9); -} - -HRESULT D3DPresentEngine::initializeD3D() -{ - HRESULT hr = Direct3DCreate9Ex(D3D_SDK_VERSION, &m_D3D9); - - if (SUCCEEDED(hr)) - hr = DXVA2CreateDirect3DDeviceManager9(&m_deviceResetToken, &m_devices); - - return hr; -} - -HRESULT D3DPresentEngine::createD3DDevice() -{ - HRESULT hr = S_OK; - HWND hwnd = NULL; - UINT uAdapterID = D3DADAPTER_DEFAULT; - DWORD vp = 0; - - D3DCAPS9 ddCaps; - ZeroMemory(&ddCaps, sizeof(ddCaps)); - - IDirect3DDevice9Ex* device = NULL; - - if (!m_D3D9 || !m_devices) - return MF_E_NOT_INITIALIZED; - - hwnd = ::GetShellWindow(); - - D3DPRESENT_PARAMETERS pp; - ZeroMemory(&pp, sizeof(pp)); - - pp.BackBufferWidth = 1; - pp.BackBufferHeight = 1; - pp.BackBufferFormat = D3DFMT_UNKNOWN; - pp.BackBufferCount = 1; - pp.Windowed = TRUE; - pp.SwapEffect = D3DSWAPEFFECT_DISCARD; - pp.BackBufferFormat = D3DFMT_UNKNOWN; - pp.hDeviceWindow = hwnd; - pp.Flags = D3DPRESENTFLAG_VIDEO; - pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; - - hr = m_D3D9->GetDeviceCaps(uAdapterID, D3DDEVTYPE_HAL, &ddCaps); - if (FAILED(hr)) - goto done; - - if (ddCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) - vp = D3DCREATE_HARDWARE_VERTEXPROCESSING; - else - vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; - - hr = m_D3D9->CreateDeviceEx( - uAdapterID, - D3DDEVTYPE_HAL, - pp.hDeviceWindow, - vp | D3DCREATE_NOWINDOWCHANGES | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE, - &pp, - NULL, - &device - ); - if (FAILED(hr)) - goto done; - - hr = m_D3D9->GetAdapterDisplayMode(uAdapterID, &m_displayMode); - if (FAILED(hr)) - goto done; - - hr = m_devices->ResetDevice(device, m_deviceResetToken); - if (FAILED(hr)) - goto done; - - qt_evr_safe_release(&m_device); - - m_device = device; - m_device->AddRef(); - -done: - qt_evr_safe_release(&device); - return hr; -} - -bool D3DPresentEngine::isValid() const -{ - return m_device != NULL; -} - -void D3DPresentEngine::releaseResources() -{ - m_surfaceFormat = QVideoFrameFormat(); -} - -HRESULT D3DPresentEngine::getService(REFGUID, REFIID riid, void** ppv) -{ - HRESULT hr = S_OK; - - if (riid == __uuidof(IDirect3DDeviceManager9)) { - if (m_devices == NULL) { - hr = MF_E_UNSUPPORTED_SERVICE; - } else { - *ppv = m_devices; - m_devices->AddRef(); - } - } else { - hr = MF_E_UNSUPPORTED_SERVICE; - } - - return hr; -} - -HRESULT D3DPresentEngine::checkFormat(D3DFORMAT format) -{ - if (!m_D3D9 || !m_device) - return E_FAIL; - - HRESULT hr = S_OK; - - D3DDISPLAYMODE mode; - D3DDEVICE_CREATION_PARAMETERS params; - - hr = m_device->GetCreationParameters(¶ms); - if (FAILED(hr)) - return hr; - - UINT uAdapter = params.AdapterOrdinal; - D3DDEVTYPE type = params.DeviceType; - - hr = m_D3D9->GetAdapterDisplayMode(uAdapter, &mode); - if (FAILED(hr)) - return hr; - - hr = m_D3D9->CheckDeviceFormat(uAdapter, type, mode.Format, - D3DUSAGE_RENDERTARGET, - D3DRTYPE_SURFACE, - format); - - if (m_useTextureRendering && format != D3DFMT_X8R8G8B8 && format != D3DFMT_A8R8G8B8) { - // The texture is always in RGB32 so the d3d driver must support conversion from the - // requested format to RGB32. - hr = m_D3D9->CheckDeviceFormatConversion(uAdapter, type, format, D3DFMT_X8R8G8B8); - } - - return hr; -} - -bool D3DPresentEngine::supportsTextureRendering() const -{ - return false; -} - -void D3DPresentEngine::setHint(Hint hint, bool enable) -{ - if (hint == RenderToTexture) - m_useTextureRendering = enable && supportsTextureRendering(); -} - -HRESULT D3DPresentEngine::createVideoSamples(IMFMediaType *format, QList<IMFSample*> &videoSampleQueue) -{ - if (!format) - return MF_E_UNEXPECTED; - - HRESULT hr = S_OK; - - IDirect3DSurface9 *surface = NULL; - IMFSample *videoSample = NULL; - - releaseResources(); - - UINT32 width = 0, height = 0; - hr = MFGetAttributeSize(format, MF_MT_FRAME_SIZE, &width, &height); - if (FAILED(hr)) - return hr; - - DWORD d3dFormat = 0; - hr = qt_evr_getFourCC(format, &d3dFormat); - if (FAILED(hr)) - return hr; - - // Create the video samples. - for (int i = 0; i < PRESENTER_BUFFER_COUNT; i++) { - hr = m_device->CreateRenderTarget(width, height, - (D3DFORMAT)d3dFormat, - D3DMULTISAMPLE_NONE, - 0, - TRUE, - &surface, NULL); - if (FAILED(hr)) - goto done; - - hr = MFCreateVideoSampleFromSurface(surface, &videoSample); - if (FAILED(hr)) - goto done; - - videoSample->AddRef(); - videoSampleQueue.append(videoSample); - - qt_evr_safe_release(&videoSample); - qt_evr_safe_release(&surface); - } - -done: - if (SUCCEEDED(hr)) { - m_surfaceFormat = QVideoFrameFormat(QSize(width, height), - m_useTextureRendering ? QVideoFrameFormat::Format_BGRX8888 - : qt_evr_pixelFormatFromD3DFormat(d3dFormat)); - } else { - releaseResources(); - } - - qt_evr_safe_release(&videoSample); - qt_evr_safe_release(&surface); - return hr; -} - -QVideoFrame D3DPresentEngine::makeVideoFrame(IMFSample *sample) -{ - if (!sample) - return QVideoFrame(); - - QVideoFrame frame(new IMFSampleVideoBuffer(this, sample, (m_useTextureRendering ? QVideoFrame::RhiTextureHandle : QVideoFrame::NoHandle)), - m_surfaceFormat); - - // WMF uses 100-nanosecond units, Qt uses microseconds - LONGLONG startTime = 0; - auto hr = sample->GetSampleTime(&startTime); - if (SUCCEEDED(hr)) { - frame.setStartTime(startTime * 0.1); - - LONGLONG duration = -1; - if (SUCCEEDED(sample->GetSampleDuration(&duration))) - frame.setEndTime((startTime + duration) * 0.1); - } - - return frame; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/windows/evr/evrd3dpresentengine_p.h b/src/multimedia/platform/windows/evr/evrd3dpresentengine_p.h deleted file mode 100644 index 6b8353c53..000000000 --- a/src/multimedia/platform/windows/evr/evrd3dpresentengine_p.h +++ /dev/null @@ -1,165 +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$ -** -****************************************************************************/ - -#ifndef EVRD3DPRESENTENGINE_H -#define EVRD3DPRESENTENGINE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QMutex> -#include <QVideoFrameFormat> - -#include <d3d9.h> - -struct IDirect3D9Ex; -struct IDirect3DDevice9Ex; -struct IDirect3DDeviceManager9; -struct IDirect3DSurface9; -struct IDirect3DTexture9; -struct IMFSample; -struct IMFMediaType; - -QT_BEGIN_NAMESPACE -class QVideoFrame; -QT_END_NAMESPACE - -// Randomly generated GUIDs -static const GUID MFSamplePresenter_SampleCounter = -{ 0xb0bb83cc, 0xf10f, 0x4e2e, { 0xaa, 0x2b, 0x29, 0xea, 0x5e, 0x92, 0xef, 0x85 } }; - -QT_BEGIN_NAMESPACE - -#ifdef MAYBE_ANGLE - -class OpenGLResources; - -class EGLWrapper -{ - Q_DISABLE_COPY(EGLWrapper) -public: - EGLWrapper(); - - __eglMustCastToProperFunctionPointerType getProcAddress(const char *procname); - EGLSurface createPbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list); - EGLBoolean destroySurface(EGLDisplay dpy, EGLSurface surface); - EGLBoolean bindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer); - EGLBoolean releaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer); - -private: - typedef __eglMustCastToProperFunctionPointerType (EGLAPIENTRYP EglGetProcAddress)(const char *procname); - typedef EGLSurface (EGLAPIENTRYP EglCreatePbufferSurface)(EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list); - typedef EGLBoolean (EGLAPIENTRYP EglDestroySurface)(EGLDisplay dpy, EGLSurface surface); - typedef EGLBoolean (EGLAPIENTRYP EglBindTexImage)(EGLDisplay dpy, EGLSurface surface, EGLint buffer); - typedef EGLBoolean (EGLAPIENTRYP EglReleaseTexImage)(EGLDisplay dpy, EGLSurface surface, EGLint buffer); - - EglGetProcAddress m_eglGetProcAddress; - EglCreatePbufferSurface m_eglCreatePbufferSurface; - EglDestroySurface m_eglDestroySurface; - EglBindTexImage m_eglBindTexImage; - EglReleaseTexImage m_eglReleaseTexImage; -}; - -#endif // MAYBE_ANGLE - -class D3DPresentEngine -{ - Q_DISABLE_COPY(D3DPresentEngine) -public: - enum Hint - { - RenderToTexture - }; - - D3DPresentEngine(); - virtual ~D3DPresentEngine(); - - bool isValid() const; - void setHint(Hint hint, bool enable = true); - - HRESULT getService(REFGUID guidService, REFIID riid, void** ppv); - HRESULT checkFormat(D3DFORMAT format); - UINT refreshRate() const { return m_displayMode.RefreshRate; } - - bool supportsTextureRendering() const; - bool isTextureRenderingEnabled() const { return m_useTextureRendering; } - - HRESULT createVideoSamples(IMFMediaType *format, QList<IMFSample*>& videoSampleQueue); - QVideoFrameFormat videoSurfaceFormat() const { return m_surfaceFormat; } - QVideoFrame makeVideoFrame(IMFSample* sample); - - void releaseResources(); - -private: - HRESULT initializeD3D(); - HRESULT createD3DDevice(); - - - UINT m_deviceResetToken; - D3DDISPLAYMODE m_displayMode; - - IDirect3D9Ex *m_D3D9; - IDirect3DDevice9Ex *m_device; - IDirect3DDeviceManager9 *m_devices; - - QVideoFrameFormat m_surfaceFormat; - - bool m_useTextureRendering; - -#ifdef MAYBE_ANGLE - unsigned int updateTexture(IDirect3DSurface9 *src); - - OpenGLResources *m_glResources; - IDirect3DTexture9 *m_texture; -#endif - - friend class IMFSampleVideoBuffer; -}; - -QT_END_NAMESPACE - -#endif // EVRD3DPRESENTENGINE_H diff --git a/src/multimedia/platform/windows/evr/evrdefs.cpp b/src/multimedia/platform/windows/evr/evrdefs.cpp deleted file mode 100644 index 94370a14a..000000000 --- a/src/multimedia/platform/windows/evr/evrdefs.cpp +++ /dev/null @@ -1,48 +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$ -** -****************************************************************************/ - -#include "evrdefs_p.h" - -const CLSID clsid_EnhancedVideoRenderer = { 0xfa10746c, 0x9b63, 0x4b6c, {0xbc, 0x49, 0xfc, 0x30, 0xe, 0xa5, 0xf2, 0x56} }; -const GUID mr_VIDEO_RENDER_SERVICE = { 0x1092a86c, 0xab1a, 0x459a, {0xa3, 0x36, 0x83, 0x1f, 0xbc, 0x4d, 0x11, 0xff} }; -const GUID mr_VIDEO_MIXER_SERVICE = { 0x73cd2fc, 0x6cf4, 0x40b7, {0x88, 0x59, 0xe8, 0x95, 0x52, 0xc8, 0x41, 0xf8} }; -const GUID mr_BUFFER_SERVICE = { 0xa562248c, 0x9ac6, 0x4ffc, {0x9f, 0xba, 0x3a, 0xf8, 0xf8, 0xad, 0x1a, 0x4d} }; -const GUID video_ZOOM_RECT = { 0x7aaa1638, 0x1b7f, 0x4c93, {0xbd, 0x89, 0x5b, 0x9c, 0x9f, 0xb6, 0xfc, 0xf0} }; -const GUID iid_IDirect3DDevice9 = { 0xd0223b96, 0xbf7a, 0x43fd, {0x92, 0xbd, 0xa4, 0x3b, 0xd, 0x82, 0xb9, 0xeb} }; -const GUID iid_IDirect3DSurface9 = { 0xcfbaf3a, 0x9ff6, 0x429a, {0x99, 0xb3, 0xa2, 0x79, 0x6a, 0xf8, 0xb8, 0x9b} }; diff --git a/src/multimedia/platform/windows/evr/evrdefs_p.h b/src/multimedia/platform/windows/evr/evrdefs_p.h deleted file mode 100644 index f9df48387..000000000 --- a/src/multimedia/platform/windows/evr/evrdefs_p.h +++ /dev/null @@ -1,364 +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$ -** -****************************************************************************/ - -#ifndef EVRDEFS_H -#define EVRDEFS_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <d3d9.h> -#include <evr9.h> -#include <evr.h> -#include <dxva2api.h> -#include <mfapi.h> -#include <mfidl.h> -#include <mferror.h> - -extern const CLSID clsid_EnhancedVideoRenderer; -extern const GUID mr_VIDEO_RENDER_SERVICE; -extern const GUID mr_VIDEO_MIXER_SERVICE; -extern const GUID mr_BUFFER_SERVICE; -extern const GUID video_ZOOM_RECT; -extern const GUID iid_IDirect3DDevice9; -extern const GUID iid_IDirect3DSurface9; - -// The following is required to compile with MinGW - -extern "C" { -HRESULT WINAPI MFCreateVideoSampleFromSurface(IUnknown *pUnkSurface, IMFSample **ppSample); -HRESULT WINAPI Direct3DCreate9Ex(UINT SDKVersion, IDirect3D9Ex**); -} - -#ifndef PRESENTATION_CURRENT_POSITION -#define PRESENTATION_CURRENT_POSITION 0x7fffffffffffffff -#endif - -#ifndef MF_E_SHUTDOWN -#define MF_E_SHUTDOWN ((HRESULT)0xC00D3E85L) -#endif - -#ifndef MF_E_SAMPLEALLOCATOR_EMPTY -#define MF_E_SAMPLEALLOCATOR_EMPTY ((HRESULT)0xC00D4A3EL) -#endif - -#ifndef MF_E_TRANSFORM_STREAM_CHANGE -#define MF_E_TRANSFORM_STREAM_CHANGE ((HRESULT)0xC00D6D61L) -#endif - -#ifndef MF_E_TRANSFORM_NEED_MORE_INPUT -#define MF_E_TRANSFORM_NEED_MORE_INPUT ((HRESULT)0xC00D6D72L) -#endif - -#if defined(__GNUC__) && !defined(_MFVideoNormalizedRect_) -#define _MFVideoNormalizedRect_ -typedef struct MFVideoNormalizedRect { - float left; - float top; - float right; - float bottom; -} MFVideoNormalizedRect; -#endif - -#include <initguid.h> - -#ifndef __IMFGetService_INTERFACE_DEFINED__ -#define __IMFGetService_INTERFACE_DEFINED__ -DEFINE_GUID(IID_IMFGetService, 0xfa993888, 0x4383, 0x415a, 0xa9,0x30, 0xdd,0x47,0x2a,0x8c,0xf6,0xf7); -MIDL_INTERFACE("fa993888-4383-415a-a930-dd472a8cf6f7") -IMFGetService : public IUnknown -{ - virtual HRESULT STDMETHODCALLTYPE GetService(REFGUID, REFIID, LPVOID *) = 0; -}; -#ifdef __CRT_UUID_DECL -__CRT_UUID_DECL(IMFGetService, 0xfa993888, 0x4383, 0x415a, 0xa9,0x30, 0xdd,0x47,0x2a,0x8c,0xf6,0xf7) -#endif -#endif // __IMFGetService_INTERFACE_DEFINED__ - -#ifndef __IMFVideoDisplayControl_INTERFACE_DEFINED__ -#define __IMFVideoDisplayControl_INTERFACE_DEFINED__ -typedef enum MFVideoAspectRatioMode -{ - MFVideoARMode_None = 0, - MFVideoARMode_PreservePicture = 0x1, - MFVideoARMode_PreservePixel = 0x2, - MFVideoARMode_NonLinearStretch = 0x4, - MFVideoARMode_Mask = 0x7 -} MFVideoAspectRatioMode; - -DEFINE_GUID(IID_IMFVideoDisplayControl, 0xa490b1e4, 0xab84, 0x4d31, 0xa1,0xb2, 0x18,0x1e,0x03,0xb1,0x07,0x7a); -MIDL_INTERFACE("a490b1e4-ab84-4d31-a1b2-181e03b1077a") -IMFVideoDisplayControl : public IUnknown -{ - virtual HRESULT STDMETHODCALLTYPE GetNativeVideoSize(SIZE *, SIZE *) = 0; - virtual HRESULT STDMETHODCALLTYPE GetIdealVideoSize(SIZE *, SIZE *) = 0; - virtual HRESULT STDMETHODCALLTYPE SetVideoPosition(const MFVideoNormalizedRect *, const LPRECT) = 0; - virtual HRESULT STDMETHODCALLTYPE GetVideoPosition(MFVideoNormalizedRect *, LPRECT) = 0; - virtual HRESULT STDMETHODCALLTYPE SetAspectRatioMode(DWORD) = 0; - virtual HRESULT STDMETHODCALLTYPE GetAspectRatioMode(DWORD *) = 0; - virtual HRESULT STDMETHODCALLTYPE SetVideoWindow(HWND) = 0; - virtual HRESULT STDMETHODCALLTYPE GetVideoWindow(HWND *) = 0; - virtual HRESULT STDMETHODCALLTYPE RepaintVideo(void) = 0; - virtual HRESULT STDMETHODCALLTYPE GetCurrentImage(BITMAPINFOHEADER *, BYTE **, DWORD *, LONGLONG *) = 0; - virtual HRESULT STDMETHODCALLTYPE SetBorderColor(COLORREF) = 0; - virtual HRESULT STDMETHODCALLTYPE GetBorderColor(COLORREF *) = 0; - virtual HRESULT STDMETHODCALLTYPE SetRenderingPrefs(DWORD) = 0; - virtual HRESULT STDMETHODCALLTYPE GetRenderingPrefs(DWORD *) = 0; - virtual HRESULT STDMETHODCALLTYPE SetFullscreen(BOOL) = 0; - virtual HRESULT STDMETHODCALLTYPE GetFullscreen(BOOL *) = 0; -}; -#ifdef __CRT_UUID_DECL -__CRT_UUID_DECL(IMFVideoDisplayControl, 0xa490b1e4, 0xab84, 0x4d31, 0xa1,0xb2, 0x18,0x1e,0x03,0xb1,0x07,0x7a) -#endif -#endif // __IMFVideoDisplayControl_INTERFACE_DEFINED__ - -#ifndef __IMFVideoProcessor_INTERFACE_DEFINED__ -#define __IMFVideoProcessor_INTERFACE_DEFINED__ -DEFINE_GUID(IID_IMFVideoProcessor, 0x6AB0000C, 0xFECE, 0x4d1f, 0xA2,0xAC, 0xA9,0x57,0x35,0x30,0x65,0x6E); -MIDL_INTERFACE("6AB0000C-FECE-4d1f-A2AC-A9573530656E") -IMFVideoProcessor : public IUnknown -{ - virtual HRESULT STDMETHODCALLTYPE GetAvailableVideoProcessorModes(UINT *, GUID **) = 0; - virtual HRESULT STDMETHODCALLTYPE GetVideoProcessorCaps(LPGUID, DXVA2_VideoProcessorCaps *) = 0; - virtual HRESULT STDMETHODCALLTYPE GetVideoProcessorMode(LPGUID) = 0; - virtual HRESULT STDMETHODCALLTYPE SetVideoProcessorMode(LPGUID) = 0; - virtual HRESULT STDMETHODCALLTYPE GetProcAmpRange(DWORD, DXVA2_ValueRange *) = 0; - virtual HRESULT STDMETHODCALLTYPE GetProcAmpValues(DWORD, DXVA2_ProcAmpValues *) = 0; - virtual HRESULT STDMETHODCALLTYPE SetProcAmpValues(DWORD, DXVA2_ProcAmpValues *) = 0; - virtual HRESULT STDMETHODCALLTYPE GetFilteringRange(DWORD, DXVA2_ValueRange *) = 0; - virtual HRESULT STDMETHODCALLTYPE GetFilteringValue(DWORD, DXVA2_Fixed32 *) = 0; - virtual HRESULT STDMETHODCALLTYPE SetFilteringValue(DWORD, DXVA2_Fixed32 *) = 0; - virtual HRESULT STDMETHODCALLTYPE GetBackgroundColor(COLORREF *) = 0; - virtual HRESULT STDMETHODCALLTYPE SetBackgroundColor(COLORREF) = 0; -}; -#ifdef __CRT_UUID_DECL -__CRT_UUID_DECL(IMFVideoProcessor, 0x6AB0000C, 0xFECE, 0x4d1f, 0xA2,0xAC, 0xA9,0x57,0x35,0x30,0x65,0x6E) -#endif -#endif // __IMFVideoProcessor_INTERFACE_DEFINED__ - -#ifndef __IMFVideoDeviceID_INTERFACE_DEFINED__ -#define __IMFVideoDeviceID_INTERFACE_DEFINED__ -DEFINE_GUID(IID_IMFVideoDeviceID, 0xA38D9567, 0x5A9C, 0x4f3c, 0xB2,0x93, 0x8E,0xB4,0x15,0xB2,0x79,0xBA); -MIDL_INTERFACE("A38D9567-5A9C-4f3c-B293-8EB415B279BA") -IMFVideoDeviceID : public IUnknown -{ -public: - virtual HRESULT STDMETHODCALLTYPE GetDeviceID(IID *pDeviceID) = 0; -}; -#ifdef __CRT_UUID_DECL -__CRT_UUID_DECL(IMFVideoDeviceID, 0xA38D9567, 0x5A9C, 0x4f3c, 0xB2,0x93, 0x8E,0xB4,0x15,0xB2,0x79,0xBA) -#endif -#endif // __IMFVideoDeviceID_INTERFACE_DEFINED__ - -#ifndef __IMFClockStateSink_INTERFACE_DEFINED__ -#define __IMFClockStateSink_INTERFACE_DEFINED__ -DEFINE_GUID(IID_IMFClockStateSink, 0xF6696E82, 0x74F7, 0x4f3d, 0xA1,0x78, 0x8A,0x5E,0x09,0xC3,0x65,0x9F); -MIDL_INTERFACE("F6696E82-74F7-4f3d-A178-8A5E09C3659F") -IMFClockStateSink : public IUnknown -{ -public: - virtual HRESULT STDMETHODCALLTYPE OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset) = 0; - virtual HRESULT STDMETHODCALLTYPE OnClockStop(MFTIME hnsSystemTime) = 0; - virtual HRESULT STDMETHODCALLTYPE OnClockPause(MFTIME hnsSystemTime) = 0; - virtual HRESULT STDMETHODCALLTYPE OnClockRestart(MFTIME hnsSystemTime) = 0; - virtual HRESULT STDMETHODCALLTYPE OnClockSetRate(MFTIME hnsSystemTime, float flRate) = 0; -}; -#ifdef __CRT_UUID_DECL -__CRT_UUID_DECL(IMFClockStateSink, 0xF6696E82, 0x74F7, 0x4f3d, 0xA1,0x78, 0x8A,0x5E,0x09,0xC3,0x65,0x9F) -#endif -#endif // __IMFClockStateSink_INTERFACE_DEFINED__ - -#ifndef __IMFVideoPresenter_INTERFACE_DEFINED__ -#define __IMFVideoPresenter_INTERFACE_DEFINED__ -typedef enum MFVP_MESSAGE_TYPE -{ - MFVP_MESSAGE_FLUSH = 0, - MFVP_MESSAGE_INVALIDATEMEDIATYPE = 0x1, - MFVP_MESSAGE_PROCESSINPUTNOTIFY = 0x2, - MFVP_MESSAGE_BEGINSTREAMING = 0x3, - MFVP_MESSAGE_ENDSTREAMING = 0x4, - MFVP_MESSAGE_ENDOFSTREAM = 0x5, - MFVP_MESSAGE_STEP = 0x6, - MFVP_MESSAGE_CANCELSTEP = 0x7 -} MFVP_MESSAGE_TYPE; - -DEFINE_GUID(IID_IMFVideoPresenter, 0x29AFF080, 0x182A, 0x4a5d, 0xAF,0x3B, 0x44,0x8F,0x3A,0x63,0x46,0xCB); -MIDL_INTERFACE("29AFF080-182A-4a5d-AF3B-448F3A6346CB") -IMFVideoPresenter : public IMFClockStateSink -{ -public: - virtual HRESULT STDMETHODCALLTYPE ProcessMessage(MFVP_MESSAGE_TYPE eMessage, ULONG_PTR ulParam) = 0; - virtual HRESULT STDMETHODCALLTYPE GetCurrentMediaType(IMFVideoMediaType **ppMediaType) = 0; -}; -#ifdef __CRT_UUID_DECL -__CRT_UUID_DECL(IMFVideoPresenter, 0x29AFF080, 0x182A, 0x4a5d, 0xAF,0x3B, 0x44,0x8F,0x3A,0x63,0x46,0xCB) -#endif -#endif // __IMFVideoPresenter_INTERFACE_DEFINED__ - -#ifndef __IMFRateSupport_INTERFACE_DEFINED__ -#define __IMFRateSupport_INTERFACE_DEFINED__ -DEFINE_GUID(IID_IMFRateSupport, 0x0a9ccdbc, 0xd797, 0x4563, 0x96,0x67, 0x94,0xec,0x5d,0x79,0x29,0x2d); -MIDL_INTERFACE("0a9ccdbc-d797-4563-9667-94ec5d79292d") -IMFRateSupport : public IUnknown -{ -public: - virtual HRESULT STDMETHODCALLTYPE GetSlowestRate(MFRATE_DIRECTION eDirection, BOOL fThin, float *pflRate) = 0; - virtual HRESULT STDMETHODCALLTYPE GetFastestRate(MFRATE_DIRECTION eDirection, BOOL fThin, float *pflRate) = 0; - virtual HRESULT STDMETHODCALLTYPE IsRateSupported(BOOL fThin, float flRate, float *pflNearestSupportedRate) = 0; -}; -#ifdef __CRT_UUID_DECL -__CRT_UUID_DECL(IMFRateSupport, 0x0a9ccdbc, 0xd797, 0x4563, 0x96,0x67, 0x94,0xec,0x5d,0x79,0x29,0x2d) -#endif -#endif // __IMFRateSupport_INTERFACE_DEFINED__ - -#ifndef __IMFTopologyServiceLookup_INTERFACE_DEFINED__ -#define __IMFTopologyServiceLookup_INTERFACE_DEFINED__ -typedef enum _MF_SERVICE_LOOKUP_TYPE -{ - MF_SERVICE_LOOKUP_UPSTREAM = 0, - MF_SERVICE_LOOKUP_UPSTREAM_DIRECT = (MF_SERVICE_LOOKUP_UPSTREAM + 1), - MF_SERVICE_LOOKUP_DOWNSTREAM = (MF_SERVICE_LOOKUP_UPSTREAM_DIRECT + 1), - MF_SERVICE_LOOKUP_DOWNSTREAM_DIRECT = (MF_SERVICE_LOOKUP_DOWNSTREAM + 1), - MF_SERVICE_LOOKUP_ALL = (MF_SERVICE_LOOKUP_DOWNSTREAM_DIRECT + 1), - MF_SERVICE_LOOKUP_GLOBAL = (MF_SERVICE_LOOKUP_ALL + 1) -} MF_SERVICE_LOOKUP_TYPE; - -DEFINE_GUID(IID_IMFTopologyServiceLookup, 0xfa993889, 0x4383, 0x415a, 0xa9,0x30, 0xdd,0x47,0x2a,0x8c,0xf6,0xf7); -MIDL_INTERFACE("fa993889-4383-415a-a930-dd472a8cf6f7") -IMFTopologyServiceLookup : public IUnknown -{ -public: - virtual HRESULT STDMETHODCALLTYPE LookupService(MF_SERVICE_LOOKUP_TYPE Type, - DWORD dwIndex, - REFGUID guidService, - REFIID riid, - LPVOID *ppvObjects, - DWORD *pnObjects) = 0; -}; -#ifdef __CRT_UUID_DECL -__CRT_UUID_DECL(IMFTopologyServiceLookup, 0xfa993889, 0x4383, 0x415a, 0xa9,0x30, 0xdd,0x47,0x2a,0x8c,0xf6,0xf7) -#endif -#endif // __IMFTopologyServiceLookup_INTERFACE_DEFINED__ - -#ifndef __IMFTopologyServiceLookupClient_INTERFACE_DEFINED__ -#define __IMFTopologyServiceLookupClient_INTERFACE_DEFINED__ -DEFINE_GUID(IID_IMFTopologyServiceLookupClient, 0xfa99388a, 0x4383, 0x415a, 0xa9,0x30, 0xdd,0x47,0x2a,0x8c,0xf6,0xf7); -MIDL_INTERFACE("fa99388a-4383-415a-a930-dd472a8cf6f7") -IMFTopologyServiceLookupClient : public IUnknown -{ -public: - virtual HRESULT STDMETHODCALLTYPE InitServicePointers(IMFTopologyServiceLookup *pLookup) = 0; - virtual HRESULT STDMETHODCALLTYPE ReleaseServicePointers(void) = 0; -}; -#ifdef __CRT_UUID_DECL -__CRT_UUID_DECL(IMFTopologyServiceLookupClient, 0xfa99388a, 0x4383, 0x415a, 0xa9,0x30, 0xdd,0x47,0x2a,0x8c,0xf6,0xf7) -#endif -#endif // __IMFTopologyServiceLookupClient_INTERFACE_DEFINED__ - -#ifndef __IMediaEventSink_INTERFACE_DEFINED__ -#define __IMediaEventSink_INTERFACE_DEFINED__ -DEFINE_GUID(IID_IMediaEventSink, 0x56a868a2, 0x0ad4, 0x11ce, 0xb0,0x3a, 0x00,0x20,0xaf,0x0b,0xa7,0x70); -MIDL_INTERFACE("56a868a2-0ad4-11ce-b03a-0020af0ba770") -IMediaEventSink : public IUnknown -{ -public: - virtual HRESULT STDMETHODCALLTYPE Notify(long EventCode, LONG_PTR EventParam1, LONG_PTR EventParam2) = 0; -}; -#ifdef __CRT_UUID_DECL -__CRT_UUID_DECL(IMediaEventSink, 0x56a868a2, 0x0ad4, 0x11ce, 0xb0,0x3a, 0x00,0x20,0xaf,0x0b,0xa7,0x70) -#endif -#endif // __IMediaEventSink_INTERFACE_DEFINED__ - -#ifndef __IMFVideoRenderer_INTERFACE_DEFINED__ -#define __IMFVideoRenderer_INTERFACE_DEFINED__ -DEFINE_GUID(IID_IMFVideoRenderer, 0xDFDFD197, 0xA9CA, 0x43d8, 0xB3,0x41, 0x6A,0xF3,0x50,0x37,0x92,0xCD); -MIDL_INTERFACE("DFDFD197-A9CA-43d8-B341-6AF3503792CD") -IMFVideoRenderer : public IUnknown -{ -public: - virtual HRESULT STDMETHODCALLTYPE InitializeRenderer(IMFTransform *pVideoMixer, - IMFVideoPresenter *pVideoPresenter) = 0; -}; -#ifdef __CRT_UUID_DECL -__CRT_UUID_DECL(IMFVideoRenderer, 0xDFDFD197, 0xA9CA, 0x43d8, 0xB3,0x41, 0x6A,0xF3,0x50,0x37,0x92,0xCD) -#endif -#endif // __IMFVideoRenderer_INTERFACE_DEFINED__ - -#ifndef __IMFTrackedSample_INTERFACE_DEFINED__ -#define __IMFTrackedSample_INTERFACE_DEFINED__ -DEFINE_GUID(IID_IMFTrackedSample, 0x245BF8E9, 0x0755, 0x40f7, 0x88,0xA5, 0xAE,0x0F,0x18,0xD5,0x5E,0x17); -MIDL_INTERFACE("245BF8E9-0755-40f7-88A5-AE0F18D55E17") -IMFTrackedSample : public IUnknown -{ -public: - virtual HRESULT STDMETHODCALLTYPE SetAllocator(IMFAsyncCallback *pSampleAllocator, IUnknown *pUnkState) = 0; -}; -#ifdef __CRT_UUID_DECL -__CRT_UUID_DECL(IMFTrackedSample, 0x245BF8E9, 0x0755, 0x40f7, 0x88,0xA5, 0xAE,0x0F,0x18,0xD5,0x5E,0x17) -#endif -#endif // __IMFTrackedSample_INTERFACE_DEFINED__ - -#ifndef __IMFDesiredSample_INTERFACE_DEFINED__ -#define __IMFDesiredSample_INTERFACE_DEFINED__ -DEFINE_GUID(IID_IMFDesiredSample, 0x56C294D0, 0x753E, 0x4260, 0x8D,0x61, 0xA3,0xD8,0x82,0x0B,0x1D,0x54); -MIDL_INTERFACE("56C294D0-753E-4260-8D61-A3D8820B1D54") -IMFDesiredSample : public IUnknown -{ -public: - virtual HRESULT STDMETHODCALLTYPE GetDesiredSampleTimeAndDuration(LONGLONG *phnsSampleTime, - LONGLONG *phnsSampleDuration) = 0; - virtual void STDMETHODCALLTYPE SetDesiredSampleTimeAndDuration(LONGLONG hnsSampleTime, - LONGLONG hnsSampleDuration) = 0; - virtual void STDMETHODCALLTYPE Clear( void) = 0; -}; -#ifdef __CRT_UUID_DECL -__CRT_UUID_DECL(IMFDesiredSample, 0x56C294D0, 0x753E, 0x4260, 0x8D,0x61, 0xA3,0xD8,0x82,0x0B,0x1D,0x54) -#endif -#endif - -#endif // EVRDEFS_H - diff --git a/src/multimedia/platform/windows/evr/evrhelpers.cpp b/src/multimedia/platform/windows/evr/evrhelpers.cpp deleted file mode 100644 index 3f2059585..000000000 --- a/src/multimedia/platform/windows/evr/evrhelpers.cpp +++ /dev/null @@ -1,174 +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$ -** -****************************************************************************/ - -#include "evrhelpers_p.h" - -#ifndef D3DFMT_YV12 -#define D3DFMT_YV12 (D3DFORMAT)MAKEFOURCC ('Y', 'V', '1', '2') -#endif -#ifndef D3DFMT_NV12 -#define D3DFMT_NV12 (D3DFORMAT)MAKEFOURCC ('N', 'V', '1', '2') -#endif - -QT_BEGIN_NAMESPACE - -HRESULT qt_evr_getFourCC(IMFMediaType *type, DWORD *fourCC) -{ - if (!fourCC) - return E_POINTER; - - HRESULT hr = S_OK; - GUID guidSubType = GUID_NULL; - - if (SUCCEEDED(hr)) - hr = type->GetGUID(MF_MT_SUBTYPE, &guidSubType); - - if (SUCCEEDED(hr)) - *fourCC = guidSubType.Data1; - - return hr; -} - -bool qt_evr_areMediaTypesEqual(IMFMediaType *type1, IMFMediaType *type2) -{ - if (!type1 && !type2) - return true; - if (!type1 || !type2) - return false; - - DWORD dwFlags = 0; - HRESULT hr = type1->IsEqual(type2, &dwFlags); - - return (hr == S_OK); -} - -HRESULT qt_evr_validateVideoArea(const MFVideoArea& area, UINT32 width, UINT32 height) -{ - float fOffsetX = qt_evr_MFOffsetToFloat(area.OffsetX); - float fOffsetY = qt_evr_MFOffsetToFloat(area.OffsetY); - - if ( ((LONG)fOffsetX + area.Area.cx > (LONG)width) || - ((LONG)fOffsetY + area.Area.cy > (LONG)height) ) { - return MF_E_INVALIDMEDIATYPE; - } - return S_OK; -} - -bool qt_evr_isSampleTimePassed(IMFClock *clock, IMFSample *sample) -{ - if (!sample || !clock) - return false; - - HRESULT hr = S_OK; - MFTIME hnsTimeNow = 0; - MFTIME hnsSystemTime = 0; - MFTIME hnsSampleStart = 0; - MFTIME hnsSampleDuration = 0; - - hr = clock->GetCorrelatedTime(0, &hnsTimeNow, &hnsSystemTime); - - if (SUCCEEDED(hr)) - hr = sample->GetSampleTime(&hnsSampleStart); - - if (SUCCEEDED(hr)) - hr = sample->GetSampleDuration(&hnsSampleDuration); - - if (SUCCEEDED(hr)) { - if (hnsSampleStart + hnsSampleDuration < hnsTimeNow) - return true; - } - - return false; -} - -QVideoFrameFormat::PixelFormat qt_evr_pixelFormatFromD3DFormat(DWORD format) -{ - switch (format) { - case D3DFMT_A8R8G8B8: - return QVideoFrameFormat::Format_BGRA8888; - case D3DFMT_X8R8G8B8: - return QVideoFrameFormat::Format_BGRX8888; - case D3DFMT_A8: - return QVideoFrameFormat::Format_Y8; - case D3DFMT_A8B8G8R8: - return QVideoFrameFormat::Format_RGBA8888; - case D3DFMT_X8B8G8R8: - return QVideoFrameFormat::Format_RGBX8888; - case D3DFMT_UYVY: - return QVideoFrameFormat::Format_UYVY; - case D3DFMT_YUY2: - return QVideoFrameFormat::Format_YUYV; - case D3DFMT_NV12: - return QVideoFrameFormat::Format_NV12; - case D3DFMT_YV12: - return QVideoFrameFormat::Format_YV12; - case D3DFMT_UNKNOWN: - default: - return QVideoFrameFormat::Format_Invalid; - } -} - -D3DFORMAT qt_evr_D3DFormatFromPixelFormat(QVideoFrameFormat::PixelFormat format) -{ - switch (format) { - case QVideoFrameFormat::Format_BGRA8888: - return D3DFMT_A8R8G8B8; - case QVideoFrameFormat::Format_BGRX8888: - return D3DFMT_X8R8G8B8; - case QVideoFrameFormat::Format_Y8: - return D3DFMT_A8; - case QVideoFrameFormat::Format_RGBA8888: - return D3DFMT_A8B8G8R8; - case QVideoFrameFormat::Format_RGBX8888: - return D3DFMT_X8B8G8R8; - case QVideoFrameFormat::Format_UYVY: - return D3DFMT_UYVY; - case QVideoFrameFormat::Format_YUYV: - return D3DFMT_YUY2; - case QVideoFrameFormat::Format_NV12: - return D3DFMT_NV12; - case QVideoFrameFormat::Format_YV12: - return D3DFMT_YV12; - case QVideoFrameFormat::Format_Invalid: - default: - return D3DFMT_UNKNOWN; - } -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/windows/evr/evrhelpers_p.h b/src/multimedia/platform/windows/evr/evrhelpers_p.h deleted file mode 100644 index 340658571..000000000 --- a/src/multimedia/platform/windows/evr/evrhelpers_p.h +++ /dev/null @@ -1,112 +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$ -** -****************************************************************************/ - -#ifndef EVRHELPERS_H -#define EVRHELPERS_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "evrdefs_p.h" -#include <qvideoframe.h> - -QT_BEGIN_NAMESPACE - -template<class T> -static inline void qt_evr_safe_release(T **unk) -{ - if (*unk) { - (*unk)->Release(); - *unk = NULL; - } -} - -HRESULT qt_evr_getFourCC(IMFMediaType *type, DWORD *fourCC); - -bool qt_evr_areMediaTypesEqual(IMFMediaType *type1, IMFMediaType *type2); - -HRESULT qt_evr_validateVideoArea(const MFVideoArea& area, UINT32 width, UINT32 height); - -bool qt_evr_isSampleTimePassed(IMFClock *clock, IMFSample *sample); - -inline float qt_evr_MFOffsetToFloat(const MFOffset& offset) -{ - return offset.value + (float(offset.fract) / 65536); -} - -inline MFOffset qt_evr_makeMFOffset(float v) -{ - MFOffset offset; - offset.value = short(v); - offset.fract = WORD(65536 * (v-offset.value)); - return offset; -} - -inline MFVideoArea qt_evr_makeMFArea(float x, float y, DWORD width, DWORD height) -{ - MFVideoArea area; - area.OffsetX = qt_evr_makeMFOffset(x); - area.OffsetY = qt_evr_makeMFOffset(y); - area.Area.cx = width; - area.Area.cy = height; - return area; -} - -inline HRESULT qt_evr_getFrameRate(IMFMediaType *pType, MFRatio *pRatio) -{ - return MFGetAttributeRatio(pType, MF_MT_FRAME_RATE, - reinterpret_cast<UINT32*>(&pRatio->Numerator), - reinterpret_cast<UINT32*>(&pRatio->Denominator)); -} - -QVideoFrameFormat::PixelFormat qt_evr_pixelFormatFromD3DFormat(DWORD format); -D3DFORMAT qt_evr_D3DFormatFromPixelFormat(QVideoFrameFormat::PixelFormat format); - -QT_END_NAMESPACE - -#endif // EVRHELPERS_H - diff --git a/src/multimedia/platform/windows/evr/evrvideowindowcontrol.cpp b/src/multimedia/platform/windows/evr/evrvideowindowcontrol.cpp deleted file mode 100644 index 18f883289..000000000 --- a/src/multimedia/platform/windows/evr/evrvideowindowcontrol.cpp +++ /dev/null @@ -1,269 +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$ -** -****************************************************************************/ - -#include "evrvideowindowcontrol_p.h" - -EvrVideoWindowControl::EvrVideoWindowControl(QVideoSink *parent) - : QPlatformVideoSink(parent) - , m_windowId(0) - , m_windowColor(RGB(0, 0, 0)) - , m_dirtyValues(0) - , m_aspectRatioMode(Qt::KeepAspectRatio) - , m_brightness(0) - , m_contrast(0) - , m_hue(0) - , m_saturation(0) - , m_fullScreen(false) - , m_displayControl(0) - , m_processor(0) -{ -} - -EvrVideoWindowControl::~EvrVideoWindowControl() -{ - clear(); -} - -bool EvrVideoWindowControl::setEvr(IUnknown *evr) -{ - clear(); - - if (!evr) - return true; - - IMFGetService *service = NULL; - - if (SUCCEEDED(evr->QueryInterface(IID_PPV_ARGS(&service))) - && SUCCEEDED(service->GetService(mr_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_displayControl)))) { - - service->GetService(mr_VIDEO_MIXER_SERVICE, IID_PPV_ARGS(&m_processor)); - - setWinId(m_windowId); - setDisplayRect(m_displayRect); - setAspectRatioMode(m_aspectRatioMode); - m_dirtyValues = DXVA2_ProcAmp_Brightness | DXVA2_ProcAmp_Contrast | DXVA2_ProcAmp_Hue | DXVA2_ProcAmp_Saturation; - applyImageControls(); - } - - if (service) - service->Release(); - - return m_displayControl != NULL; -} - -void EvrVideoWindowControl::clear() -{ - if (m_displayControl) - m_displayControl->Release(); - m_displayControl = NULL; - - if (m_processor) - m_processor->Release(); - m_processor = NULL; -} - -void EvrVideoWindowControl::setWinId(WId id) -{ - m_windowId = id; - - if (m_displayControl) - m_displayControl->SetVideoWindow(HWND(m_windowId)); -} - -void EvrVideoWindowControl::setDisplayRect(const QRect &rect) -{ - m_displayRect = rect; - - if (m_displayControl) { - RECT displayRect = { rect.left(), rect.top(), rect.right() + 1, rect.bottom() + 1 }; - QSize sourceSize = nativeSize(); - - RECT sourceRect = { 0, 0, sourceSize.width(), sourceSize.height() }; - - if (m_aspectRatioMode == Qt::KeepAspectRatioByExpanding) { - QSize clippedSize = rect.size(); - clippedSize.scale(sourceRect.right, sourceRect.bottom, Qt::KeepAspectRatio); - - sourceRect.left = (sourceRect.right - clippedSize.width()) / 2; - sourceRect.top = (sourceRect.bottom - clippedSize.height()) / 2; - sourceRect.right = sourceRect.left + clippedSize.width(); - sourceRect.bottom = sourceRect.top + clippedSize.height(); - } - - if (sourceSize.width() > 0 && sourceSize.height() > 0) { - MFVideoNormalizedRect sourceNormRect; - sourceNormRect.left = float(sourceRect.left) / float(sourceRect.right); - sourceNormRect.top = float(sourceRect.top) / float(sourceRect.bottom); - sourceNormRect.right = float(sourceRect.right) / float(sourceRect.right); - sourceNormRect.bottom = float(sourceRect.bottom) / float(sourceRect.bottom); - m_displayControl->SetVideoPosition(&sourceNormRect, &displayRect); - } else { - m_displayControl->SetVideoPosition(NULL, &displayRect); - } - } -} - -void EvrVideoWindowControl::setFullScreen(bool fullScreen) -{ - if (m_fullScreen == fullScreen) - return; -} - -QSize EvrVideoWindowControl::nativeSize() const -{ - QSize size; - if (m_displayControl) { - SIZE sourceSize; - if (SUCCEEDED(m_displayControl->GetNativeVideoSize(&sourceSize, 0))) - size = QSize(sourceSize.cx, sourceSize.cy); - } - return size; -} - -void EvrVideoWindowControl::setAspectRatioMode(Qt::AspectRatioMode mode) -{ - m_aspectRatioMode = mode; - - if (m_displayControl) { - switch (mode) { - case Qt::IgnoreAspectRatio: - //comment from MSDN: Do not maintain the aspect ratio of the video. Stretch the video to fit the output rectangle. - m_displayControl->SetAspectRatioMode(MFVideoARMode_None); - break; - case Qt::KeepAspectRatio: - //comment from MSDN: Preserve the aspect ratio of the video by letterboxing or within the output rectangle. - m_displayControl->SetAspectRatioMode(MFVideoARMode_PreservePicture); - break; - case Qt::KeepAspectRatioByExpanding: - //for this mode, more adjustment will be done in setDisplayRect - m_displayControl->SetAspectRatioMode(MFVideoARMode_PreservePicture); - break; - default: - break; - } - setDisplayRect(m_displayRect); - } -} - -void EvrVideoWindowControl::setBrightness(float brightness) -{ - if (m_brightness == brightness) - return; - - m_brightness = brightness; - - m_dirtyValues |= DXVA2_ProcAmp_Brightness; - - applyImageControls(); -} - -void EvrVideoWindowControl::setContrast(float contrast) -{ - if (m_contrast == contrast) - return; - - m_contrast = contrast; - - m_dirtyValues |= DXVA2_ProcAmp_Contrast; - - applyImageControls(); -} - -void EvrVideoWindowControl::setHue(float hue) -{ - if (m_hue == hue) - return; - - m_hue = hue; - - m_dirtyValues |= DXVA2_ProcAmp_Hue; - - applyImageControls(); -} - -void EvrVideoWindowControl::setSaturation(float saturation) -{ - if (m_saturation == saturation) - return; - - m_saturation = saturation; - - m_dirtyValues |= DXVA2_ProcAmp_Saturation; - - applyImageControls(); -} - -void EvrVideoWindowControl::applyImageControls() -{ - if (m_processor) { - DXVA2_ProcAmpValues values; - if (m_dirtyValues & DXVA2_ProcAmp_Brightness) { - values.Brightness = scaleProcAmpValue(DXVA2_ProcAmp_Brightness, m_brightness); - } - if (m_dirtyValues & DXVA2_ProcAmp_Contrast) { - values.Contrast = scaleProcAmpValue(DXVA2_ProcAmp_Contrast, m_contrast); - } - if (m_dirtyValues & DXVA2_ProcAmp_Hue) { - values.Hue = scaleProcAmpValue(DXVA2_ProcAmp_Hue, m_hue); - } - if (m_dirtyValues & DXVA2_ProcAmp_Saturation) { - values.Saturation = scaleProcAmpValue(DXVA2_ProcAmp_Saturation, m_saturation); - } - - if (SUCCEEDED(m_processor->SetProcAmpValues(m_dirtyValues, &values))) { - m_dirtyValues = 0; - } - } -} - -DXVA2_Fixed32 EvrVideoWindowControl::scaleProcAmpValue(DWORD prop, float value) const -{ - float scaledValue = 0.0; - - DXVA2_ValueRange range; - if (SUCCEEDED(m_processor->GetProcAmpRange(prop, &range))) { - scaledValue = DXVA2FixedToFloat(range.DefaultValue); - if (value > 0) - scaledValue += float(value) * (DXVA2FixedToFloat(range.MaxValue) - DXVA2FixedToFloat(range.DefaultValue)); - else if (value < 0) - scaledValue -= float(value) * (DXVA2FixedToFloat(range.MinValue) - DXVA2FixedToFloat(range.DefaultValue)); - } - - return DXVA2FloatToFixed(scaledValue); -} diff --git a/src/multimedia/platform/windows/evr/evrvideowindowcontrol_p.h b/src/multimedia/platform/windows/evr/evrvideowindowcontrol_p.h deleted file mode 100644 index b9fbc271c..000000000 --- a/src/multimedia/platform/windows/evr/evrvideowindowcontrol_p.h +++ /dev/null @@ -1,107 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Mobility Components. -** -** $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$ -** -****************************************************************************/ - -#ifndef EVRVIDEOWINDOWCONTROL_H -#define EVRVIDEOWINDOWCONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "private/qplatformvideosink_p.h" - -#include "evrdefs_p.h" - -QT_BEGIN_NAMESPACE - -class EvrVideoWindowControl : public QPlatformVideoSink -{ - Q_OBJECT -public: - EvrVideoWindowControl(QVideoSink *parent = 0); - ~EvrVideoWindowControl() override; - - bool setEvr(IUnknown *evr); - - void setWinId(WId id) override; - - void setDisplayRect(const QRect &rect) override; - - void setFullScreen(bool fullScreen) override; - - QSize nativeSize() const override; - - void setAspectRatioMode(Qt::AspectRatioMode mode) override; - - void setBrightness(float brightness) override; - void setContrast(float contrast) override; - void setHue(float hue) override; - void setSaturation(float saturation) override; - - void applyImageControls(); - -private: - void clear(); - DXVA2_Fixed32 scaleProcAmpValue(DWORD prop, float value) const; - - WId m_windowId; - COLORREF m_windowColor; - DWORD m_dirtyValues; - Qt::AspectRatioMode m_aspectRatioMode; - QRect m_displayRect; - float m_brightness; - float m_contrast; - float m_hue; - float m_saturation; - bool m_fullScreen; - - IMFVideoDisplayControl *m_displayControl; - IMFVideoProcessor *m_processor; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/windows/mediacapture/qwindowscamera.cpp b/src/multimedia/platform/windows/mediacapture/qwindowscamera.cpp deleted file mode 100644 index 53e8650f7..000000000 --- a/src/multimedia/platform/windows/mediacapture/qwindowscamera.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qwindowscamera_p.h" - -#include "qwindowsmediadevicesession_p.h" -#include "qwindowsmediacapture_p.h" -#include <qcameradevice.h> - -QT_BEGIN_NAMESPACE - -QWindowsCamera::QWindowsCamera(QCamera *camera) - : QPlatformCamera(camera) -{ -} - -QWindowsCamera::~QWindowsCamera() = default; - -bool QWindowsCamera::isActive() const -{ - return m_active; -} - -void QWindowsCamera::setActive(bool active) -{ - if (m_active == active) - return; - if (m_cameraDevice.isNull() && active) - return; - m_active = active; - if (m_mediaDeviceSession) - m_mediaDeviceSession->setActive(active); - - emit activeChanged(m_active); -} - -void QWindowsCamera::setCamera(const QCameraDevice &camera) -{ - if (m_cameraDevice == camera) - return; - m_cameraDevice = camera; - if (m_mediaDeviceSession) - m_mediaDeviceSession->setActiveCamera(camera); -} - -void QWindowsCamera::setCaptureSession(QPlatformMediaCaptureSession *session) -{ - QWindowsMediaCaptureService *captureService = static_cast<QWindowsMediaCaptureService *>(session); - if (m_captureService == captureService) - return; - - if (m_mediaDeviceSession) { - m_mediaDeviceSession->disconnect(this); - m_mediaDeviceSession->setActive(false); - m_mediaDeviceSession->setCameraFormat({}); - m_mediaDeviceSession->setActiveCamera({}); - } - - m_captureService = captureService; - if (!m_captureService) { - m_mediaDeviceSession = nullptr; - return; - } - - m_mediaDeviceSession = m_captureService->session(); - Q_ASSERT(m_mediaDeviceSession); - - m_mediaDeviceSession->setActive(false); - m_mediaDeviceSession->setActiveCamera(m_cameraDevice); - m_mediaDeviceSession->setCameraFormat(m_cameraFormat); - m_mediaDeviceSession->setActive(m_active); - - connect(m_mediaDeviceSession, &QWindowsMediaDeviceSession::activeChanged, - this, &QWindowsCamera::onActiveChanged); -} - -bool QWindowsCamera::setCameraFormat(const QCameraFormat &format) -{ - if (!format.isNull() && !m_cameraDevice.videoFormats().contains(format)) - return false; - - m_cameraFormat = format.isNull() ? findBestCameraFormat(m_cameraDevice) : format; - - if (m_mediaDeviceSession) - m_mediaDeviceSession->setCameraFormat(m_cameraFormat); - return true; -} - -void QWindowsCamera::onActiveChanged(bool active) -{ - if (m_active == active) - return; - if (m_cameraDevice.isNull() && active) - return; - m_active = active; - emit activeChanged(m_active); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/windows/mediacapture/qwindowscamera_p.h b/src/multimedia/platform/windows/mediacapture/qwindowscamera_p.h deleted file mode 100644 index 379a2cba3..000000000 --- a/src/multimedia/platform/windows/mediacapture/qwindowscamera_p.h +++ /dev/null @@ -1,91 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QWINDOWSCAMERA_H -#define QWINDOWSCAMERA_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformcamera_p.h> - -QT_BEGIN_NAMESPACE - -class QWindowsMediaCaptureService; -class QWindowsMediaDeviceSession; - -class QWindowsCamera : public QPlatformCamera -{ - Q_OBJECT -public: - explicit QWindowsCamera(QCamera *camera); - virtual ~QWindowsCamera(); - - bool isActive() const override; - - void setCamera(const QCameraDevice &camera) override; - - void setCaptureSession(QPlatformMediaCaptureSession *) override; - - bool setCameraFormat(const QCameraFormat &format) override; - - void setActive(bool active) override; - -private Q_SLOTS: - void onActiveChanged(bool active); - -private: - QWindowsMediaCaptureService *m_captureService = nullptr; - QWindowsMediaDeviceSession *m_mediaDeviceSession = nullptr; - QCameraDevice m_cameraDevice; - QCameraFormat m_cameraFormat; - bool m_active = false; -}; - -QT_END_NAMESPACE - -#endif // QWINDOWSCAMERA_H diff --git a/src/multimedia/platform/windows/mediacapture/qwindowsimagecapture.cpp b/src/multimedia/platform/windows/mediacapture/qwindowsimagecapture.cpp deleted file mode 100644 index d61cd6cde..000000000 --- a/src/multimedia/platform/windows/mediacapture/qwindowsimagecapture.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qwindowsimagecapture_p.h" - -#include "qwindowsmediadevicesession_p.h" -#include "qwindowsmediacapture_p.h" -#include "qmediastoragelocation_p.h" - -#include <QtConcurrent/qtconcurrentrun.h> -#include <QtGui/qimagewriter.h> - -QT_BEGIN_NAMESPACE - -QWindowsImageCapture::QWindowsImageCapture(QImageCapture *parent) - : QPlatformImageCapture(parent) -{ -} - -QWindowsImageCapture::~QWindowsImageCapture() = default; - -bool QWindowsImageCapture::isReadyForCapture() const -{ - if (!m_mediaDeviceSession) - return false; - return !m_capturing && m_mediaDeviceSession->isActive() && !m_mediaDeviceSession->activeCamera().isNull(); -} - -int QWindowsImageCapture::capture(const QString &fileName) -{ - auto ext = writerFormat(m_settings.format()); - auto path = QMediaStorageLocation::generateFileName(fileName, QStandardPaths::PicturesLocation, ext); - return doCapture(path); -} - -int QWindowsImageCapture::captureToBuffer() -{ - return doCapture(QString()); -} - -int QWindowsImageCapture::doCapture(const QString &fileName) -{ - if (!isReadyForCapture()) - return -1; - m_fileName = fileName; - m_capturing = true; - return m_captureId; -} - -QImageEncoderSettings QWindowsImageCapture::imageSettings() const -{ - return m_settings; -} - -void QWindowsImageCapture::setImageSettings(const QImageEncoderSettings &settings) -{ - m_settings = settings; -} - -void QWindowsImageCapture::setCaptureSession(QPlatformMediaCaptureSession *session) -{ - QWindowsMediaCaptureService *captureService = static_cast<QWindowsMediaCaptureService *>(session); - if (m_captureService == captureService) - return; - - if (m_mediaDeviceSession) - disconnect(m_mediaDeviceSession, nullptr, this, nullptr); - - m_captureService = captureService; - if (!m_captureService) { - m_mediaDeviceSession = nullptr; - return; - } - - m_mediaDeviceSession = m_captureService->session(); - Q_ASSERT(m_mediaDeviceSession); - - connect(m_mediaDeviceSession, SIGNAL(readyForCaptureChanged(bool)), - this, SIGNAL(readyForCaptureChanged(bool))); - - connect(m_mediaDeviceSession, SIGNAL(videoFrameChanged(QVideoFrame)), - this, SLOT(handleVideoFrameChanged(QVideoFrame))); -} - -void QWindowsImageCapture::handleVideoFrameChanged(const QVideoFrame &frame) -{ - if (m_capturing) { - - QImage image = frame.toImage(); - - emit imageExposed(m_captureId); - emit imageAvailable(m_captureId, frame); - emit imageCaptured(m_captureId, image); - - QMediaMetaData metaData = this->metaData(); - metaData.insert(QMediaMetaData::Date, QDateTime::currentDateTime()); - metaData.insert(QMediaMetaData::Resolution, frame.size()); - - emit imageMetadataAvailable(m_captureId, metaData); - - if (!m_fileName.isEmpty()) { - - (void)QtConcurrent::run(&QWindowsImageCapture::saveImage, this, - m_captureId, m_fileName, image, metaData, m_settings); - } - - ++m_captureId; - m_capturing = false; - } -} - -void QWindowsImageCapture::saveImage(int captureId, const QString &fileName, - const QImage &image, const QMediaMetaData &metaData, - const QImageEncoderSettings &settings) -{ - QImageWriter imageWriter; - imageWriter.setFileName(fileName); - - QString format = writerFormat(settings.format()); - imageWriter.setFormat(format.toUtf8()); - - int quality = writerQuality(format, settings.quality()); - if (quality > -1) - imageWriter.setQuality(quality); - - for (auto key : metaData.keys()) - imageWriter.setText(QMediaMetaData::metaDataKeyToString(key), - metaData.stringValue(key)); - - imageWriter.write(image); - - QMetaObject::invokeMethod(this, "imageSaved", Qt::QueuedConnection, - Q_ARG(int, captureId), Q_ARG(QString, fileName)); -} - -QString QWindowsImageCapture::writerFormat(QImageCapture::FileFormat reqFormat) -{ - QString format; - - switch (reqFormat) { - case QImageCapture::FileFormat::JPEG: - format = QLatin1String("jpg"); - break; - case QImageCapture::FileFormat::PNG: - format = QLatin1String("png"); - break; - case QImageCapture::FileFormat::WebP: - format = QLatin1String("webp"); - break; - case QImageCapture::FileFormat::Tiff: - format = QLatin1String("tiff"); - break; - default: - format = QLatin1String("jpg"); - } - - auto supported = QImageWriter::supportedImageFormats(); - for (const auto &f : supported) - if (format.compare(QString::fromUtf8(f), Qt::CaseInsensitive) == 0) - return format; - - return QLatin1String("jpg"); -} - -int QWindowsImageCapture::writerQuality(const QString &writerFormat, - QImageCapture::Quality quality) -{ - if (writerFormat.compare(QLatin1String("jpg"), Qt::CaseInsensitive) == 0 || - writerFormat.compare(QLatin1String("jpeg"), Qt::CaseInsensitive) == 0) { - - switch (quality) { - case QImageCapture::Quality::VeryLowQuality: - return 10; - case QImageCapture::Quality::LowQuality: - return 30; - case QImageCapture::Quality::NormalQuality: - return 75; - case QImageCapture::Quality::HighQuality: - return 90; - case QImageCapture::Quality::VeryHighQuality: - return 98; - default: - return 75; - } - } - return -1; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/windows/mediacapture/qwindowsimagecapture_p.h b/src/multimedia/platform/windows/mediacapture/qwindowsimagecapture_p.h deleted file mode 100644 index 6c3bc8107..000000000 --- a/src/multimedia/platform/windows/mediacapture/qwindowsimagecapture_p.h +++ /dev/null @@ -1,100 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QWindowsImageCapture_H -#define QWindowsImageCapture_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformimagecapture_p.h> - -QT_BEGIN_NAMESPACE - -class QWindowsMediaDeviceSession; -class QWindowsMediaCaptureService; - -class QWindowsImageCapture : public QPlatformImageCapture -{ - Q_OBJECT -public: - explicit QWindowsImageCapture(QImageCapture *parent); - virtual ~QWindowsImageCapture(); - - bool isReadyForCapture() const override; - - int capture(const QString &fileName) override; - int captureToBuffer() override; - - QImageEncoderSettings imageSettings() const override; - void setImageSettings(const QImageEncoderSettings &settings) override; - - void setCaptureSession(QPlatformMediaCaptureSession *session); - -private Q_SLOTS: - void handleVideoFrameChanged(const QVideoFrame &frame); - -private: - int doCapture(const QString &fileName); - void saveImage(int captureId, const QString &fileName, - const QImage &image, const QMediaMetaData &metaData, - const QImageEncoderSettings &settings); - QString writerFormat(QImageCapture::FileFormat reqFormat); - int writerQuality(const QString &writerFormat, - QImageCapture::Quality quality); - - QWindowsMediaCaptureService *m_captureService = nullptr; - QWindowsMediaDeviceSession *m_mediaDeviceSession = nullptr; - QImageEncoderSettings m_settings; - int m_captureId = 0; - bool m_capturing = false; - QString m_fileName; -}; - -QT_END_NAMESPACE - -#endif // QWindowsImageCapture_H diff --git a/src/multimedia/platform/windows/mediacapture/qwindowsmediacapture.cpp b/src/multimedia/platform/windows/mediacapture/qwindowsmediacapture.cpp deleted file mode 100644 index a298039ee..000000000 --- a/src/multimedia/platform/windows/mediacapture/qwindowsmediacapture.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qwindowsmediacapture_p.h" - -#include "qwindowsmediaencoder_p.h" -#include "qwindowscamera_p.h" -#include "qwindowsmediadevicesession_p.h" -#include "qwindowsimagecapture_p.h" -#include "qmediadevices.h" -#include "qaudiodevice.h" -#include "qplatformaudioinput_p.h" -#include "qplatformaudiooutput_p.h" - -QT_BEGIN_NAMESPACE - -QWindowsMediaCaptureService::QWindowsMediaCaptureService() -{ - m_mediaDeviceSession = new QWindowsMediaDeviceSession(this); -} - -QWindowsMediaCaptureService::~QWindowsMediaCaptureService() -{ - delete m_mediaDeviceSession; -} - -QPlatformCamera *QWindowsMediaCaptureService::camera() -{ - return m_camera; -} - -void QWindowsMediaCaptureService::setCamera(QPlatformCamera *camera) -{ - QWindowsCamera *control = static_cast<QWindowsCamera*>(camera); - if (m_camera == control) - return; - - if (m_camera) - m_camera->setCaptureSession(nullptr); - - m_camera = control; - if (m_camera) - m_camera->setCaptureSession(this); - emit cameraChanged(); -} - -QPlatformImageCapture *QWindowsMediaCaptureService::imageCapture() -{ - return m_imageCapture; -} - -void QWindowsMediaCaptureService::setImageCapture(QPlatformImageCapture *imageCapture) -{ - QWindowsImageCapture *control = static_cast<QWindowsImageCapture *>(imageCapture); - if (m_imageCapture == control) - return; - - if (m_imageCapture) - m_imageCapture->setCaptureSession(nullptr); - - m_imageCapture = control; - if (m_imageCapture) - m_imageCapture->setCaptureSession(this); - emit imageCaptureChanged(); -} - -QPlatformMediaEncoder *QWindowsMediaCaptureService::mediaEncoder() -{ - return m_encoder; -} - -void QWindowsMediaCaptureService::setMediaEncoder(QPlatformMediaEncoder *encoder) -{ - QWindowsMediaEncoder *control = static_cast<QWindowsMediaEncoder *>(encoder); - if (m_encoder == control) - return; - - if (m_encoder) - m_encoder->setCaptureSession(nullptr); - - m_encoder = control; - if (m_encoder) - m_encoder->setCaptureSession(this); - emit encoderChanged(); -} - -void QWindowsMediaCaptureService::setAudioInput(QPlatformAudioInput *input) -{ - m_mediaDeviceSession->setAudioInput(input ? input->q : nullptr); -} - -void QWindowsMediaCaptureService::setAudioOutput(QPlatformAudioOutput *output) -{ - m_mediaDeviceSession->setAudioOutput(output ? output->q : nullptr); -} - -void QWindowsMediaCaptureService::setVideoPreview(QVideoSink *sink) -{ - m_mediaDeviceSession->setVideoSink(sink); -} - -QWindowsMediaDeviceSession *QWindowsMediaCaptureService::session() const -{ - return m_mediaDeviceSession; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/windows/mediacapture/qwindowsmediacapture_p.h b/src/multimedia/platform/windows/mediacapture/qwindowsmediacapture_p.h deleted file mode 100644 index 147fb72ed..000000000 --- a/src/multimedia/platform/windows/mediacapture/qwindowsmediacapture_p.h +++ /dev/null @@ -1,98 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ -#ifndef QWINDOWSMEDIACAPTURE_H -#define QWINDOWSMEDIACAPTURE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediacapture_p.h> -#include <private/qplatformmediaintegration_p.h> - -QT_BEGIN_NAMESPACE - -class QWindowsMediaEncoder; -class QWindowsCamera; -class QWindowsMediaDeviceSession; -class QWindowsImageCapture; -class QPlatformAudioInput; - -class QWindowsMediaCaptureService : public QPlatformMediaCaptureSession -{ - Q_OBJECT - -public: - QWindowsMediaCaptureService(); - virtual ~QWindowsMediaCaptureService(); - - QPlatformCamera *camera() override; - void setCamera(QPlatformCamera *camera) override; - - QPlatformImageCapture *imageCapture() override; - void setImageCapture(QPlatformImageCapture *imageCapture) override; - - QPlatformMediaEncoder *mediaEncoder() override; - void setMediaEncoder(QPlatformMediaEncoder *encoder) override; - - void setAudioInput(QPlatformAudioInput *) override; - - void setAudioOutput(QPlatformAudioOutput *output) override; - - void setVideoPreview(QVideoSink *sink) override; - - QWindowsMediaDeviceSession *session() const; - -private: - QWindowsCamera *m_camera = nullptr; - QWindowsMediaDeviceSession *m_mediaDeviceSession = nullptr; - QWindowsImageCapture *m_imageCapture = nullptr; - QWindowsMediaEncoder *m_encoder = nullptr; -}; - -QT_END_NAMESPACE - -#endif // QWINDOWSMEDIAINTERFACE_H diff --git a/src/multimedia/platform/windows/mediacapture/qwindowsmediadevicereader.cpp b/src/multimedia/platform/windows/mediacapture/qwindowsmediadevicereader.cpp deleted file mode 100644 index 2e338476e..000000000 --- a/src/multimedia/platform/windows/mediacapture/qwindowsmediadevicereader.cpp +++ /dev/null @@ -1,1054 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qwindowsmediadevicereader_p.h" - -#include "qwindowsmultimediautils_p.h" -#include <qvideosink.h> -#include <qmediadevices.h> -#include <qaudiodevice.h> -#include <private/qmemoryvideobuffer_p.h> -#include <QtCore/qdebug.h> - -#include <mmdeviceapi.h> - -QT_BEGIN_NAMESPACE - -enum { MEDIA_TYPE_INDEX_DEFAULT = 0xffffffff }; - -QWindowsMediaDeviceReader::QWindowsMediaDeviceReader(QObject *parent) - : QObject(parent) -{ - m_durationTimer.setInterval(100); - connect(&m_durationTimer, SIGNAL(timeout()), this, SLOT(updateDuration())); -} - -QWindowsMediaDeviceReader::~QWindowsMediaDeviceReader() -{ - stopRecording(); - deactivate(); -} - -// Creates a video or audio media source specified by deviceId (symbolic link) -HRESULT QWindowsMediaDeviceReader::createSource(const QString &deviceId, bool video, IMFMediaSource **source) -{ - if (!source) - return E_INVALIDARG; - - *source = nullptr; - IMFAttributes *sourceAttributes = nullptr; - - HRESULT hr = MFCreateAttributes(&sourceAttributes, 2); - if (SUCCEEDED(hr)) { - - hr = sourceAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, - video ? MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID - : MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID); - if (SUCCEEDED(hr)) { - - hr = sourceAttributes->SetString(video ? MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK - : MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_ENDPOINT_ID, - reinterpret_cast<LPCWSTR>(deviceId.utf16())); - if (SUCCEEDED(hr)) { - - hr = MFCreateDeviceSource(sourceAttributes, source); - } - } - sourceAttributes->Release(); - } - - return hr; -} - -// Creates a source/reader aggregating two other sources (video/audio). -// If one of the sources is null the result will be video-only or audio-only. -HRESULT QWindowsMediaDeviceReader::createAggregateReader(IMFMediaSource *firstSource, - IMFMediaSource *secondSource, - IMFMediaSource **aggregateSource, - IMFSourceReader **sourceReader) -{ - if ((!firstSource && !secondSource) || !aggregateSource || !sourceReader) - return E_INVALIDARG; - - *aggregateSource = nullptr; - *sourceReader = nullptr; - - IMFCollection *sourceCollection = nullptr; - - HRESULT hr = MFCreateCollection(&sourceCollection); - if (SUCCEEDED(hr)) { - - if (firstSource) - sourceCollection->AddElement(firstSource); - - if (secondSource) - sourceCollection->AddElement(secondSource); - - hr = MFCreateAggregateSource(sourceCollection, aggregateSource); - if (SUCCEEDED(hr)) { - - IMFAttributes *readerAttributes = nullptr; - - hr = MFCreateAttributes(&readerAttributes, 1); - if (SUCCEEDED(hr)) { - - // Set callback so OnReadSample() is called for each new video frame or audio sample. - hr = readerAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, - static_cast<IMFSourceReaderCallback*>(this)); - if (SUCCEEDED(hr)) { - - hr = MFCreateSourceReaderFromMediaSource(*aggregateSource, readerAttributes, sourceReader); - } - readerAttributes->Release(); - } - } - sourceCollection->Release(); - } - return hr; -} - -// Selects the requested resolution/frame rate (if specified), -// or chooses a high quality configuration otherwise. -DWORD QWindowsMediaDeviceReader::findMediaTypeIndex(const QCameraFormat &reqFormat) -{ - DWORD mediaIndex = MEDIA_TYPE_INDEX_DEFAULT; - - if (m_sourceReader && m_videoSource) { - - DWORD index = 0; - IMFMediaType *mediaType = nullptr; - - UINT32 currArea = 0; - float currFrameRate = 0.0f; - - while (SUCCEEDED(m_sourceReader->GetNativeMediaType(DWORD(MF_SOURCE_READER_FIRST_VIDEO_STREAM), - index, &mediaType))) { - - GUID subtype = GUID_NULL; - if (SUCCEEDED(mediaType->GetGUID(MF_MT_SUBTYPE, &subtype))) { - - auto pixelFormat = QWindowsMultimediaUtils::pixelFormatFromMediaSubtype(subtype); - if (pixelFormat != QVideoFrameFormat::Format_Invalid) { - - UINT32 width, height; - if (SUCCEEDED(MFGetAttributeSize(mediaType, MF_MT_FRAME_SIZE, &width, &height))) { - - UINT32 num, den; - if (SUCCEEDED(MFGetAttributeRatio(mediaType, MF_MT_FRAME_RATE, &num, &den))) { - - UINT32 area = width * height; - float frameRate = float(num) / den; - - if (!reqFormat.isNull() - && reqFormat.resolution().width() == width - && reqFormat.resolution().height() == height - && qFuzzyCompare(reqFormat.maxFrameRate(), frameRate) - && reqFormat.pixelFormat() == pixelFormat) { - mediaType->Release(); - return index; - } - - if ((currFrameRate < 29.9 && currFrameRate < frameRate) || - (currFrameRate == frameRate && currArea < area)) { - currArea = area; - currFrameRate = frameRate; - mediaIndex = index; - } - } - } - } - } - mediaType->Release(); - ++index; - } - } - - return mediaIndex; -} - - -// Prepares the source video stream and gets some metadata. -HRESULT QWindowsMediaDeviceReader::prepareVideoStream(DWORD mediaTypeIndex) -{ - if (!m_sourceReader) - return E_FAIL; - - if (!m_videoSource) - return S_OK; // It may be audio-only - - HRESULT hr; - - if (mediaTypeIndex == MEDIA_TYPE_INDEX_DEFAULT) { - hr = m_sourceReader->GetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_VIDEO_STREAM), - &m_videoMediaType); - } else { - hr = m_sourceReader->GetNativeMediaType(DWORD(MF_SOURCE_READER_FIRST_VIDEO_STREAM), - mediaTypeIndex, &m_videoMediaType); - if (SUCCEEDED(hr)) - hr = m_sourceReader->SetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_VIDEO_STREAM), - nullptr, m_videoMediaType); - } - - if (SUCCEEDED(hr)) { - - GUID subtype = GUID_NULL; - hr = m_videoMediaType->GetGUID(MF_MT_SUBTYPE, &subtype); - if (SUCCEEDED(hr)) { - - m_pixelFormat = QWindowsMultimediaUtils::pixelFormatFromMediaSubtype(subtype); - - if (m_pixelFormat == QVideoFrameFormat::Format_Invalid) { - hr = E_FAIL; - } else { - - // get the frame dimensions - hr = MFGetAttributeSize(m_videoMediaType, MF_MT_FRAME_SIZE, &m_frameWidth, &m_frameHeight); - if (SUCCEEDED(hr)) { - - // and the stride, which we need to convert the frame later - hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, m_frameWidth, &m_stride); - if (SUCCEEDED(hr)) { - - UINT32 frameRateNum, frameRateDen; - hr = MFGetAttributeRatio(m_videoMediaType, MF_MT_FRAME_RATE, &frameRateNum, &frameRateDen); - if (SUCCEEDED(hr)) { - - m_frameRate = qreal(frameRateNum) / frameRateDen; - - hr = m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_FIRST_VIDEO_STREAM), TRUE); - } - } - } - } - } - } - - return hr; -} - -HRESULT QWindowsMediaDeviceReader::initAudioType(IMFMediaType *mediaType, UINT32 channels, UINT32 samplesPerSec, bool flt) -{ - if (!mediaType) - return E_INVALIDARG; - - HRESULT hr = mediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio); - if (SUCCEEDED(hr)) { - hr = mediaType->SetGUID(MF_MT_SUBTYPE, flt ? MFAudioFormat_Float : MFAudioFormat_PCM); - if (SUCCEEDED(hr)) { - hr = mediaType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, channels); - if (SUCCEEDED(hr)) { - hr = mediaType->SetUINT32(MF_MT_AUDIO_CHANNEL_MASK, - (channels == 1) ? SPEAKER_FRONT_CENTER : (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT )); - if (SUCCEEDED(hr)) { - hr = mediaType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, samplesPerSec); - if (SUCCEEDED(hr)) { - UINT32 bitsPerSample = flt ? 32 : 16; - UINT32 bytesPerFrame = channels * bitsPerSample/8; - hr = mediaType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, bitsPerSample); - if (SUCCEEDED(hr)) { - hr = mediaType->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, bytesPerFrame); - if (SUCCEEDED(hr)) { - hr = mediaType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, bytesPerFrame * samplesPerSec); - } - } - } - } - } - } - } - - return hr; -} - -// Prepares the source audio stream. -HRESULT QWindowsMediaDeviceReader::prepareAudioStream() -{ - if (!m_sourceReader) - return E_FAIL; - - if (!m_audioSource) - return S_OK; // It may be video-only - - HRESULT hr = m_sourceReader->GetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), - &m_audioMediaType); - if (SUCCEEDED(hr)) { - hr = initAudioType(m_audioMediaType, 2, 48000, true); - if (SUCCEEDED(hr)) { - hr = m_sourceReader->SetCurrentMediaType(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), - nullptr, m_audioMediaType); - if (SUCCEEDED(hr)) { - hr = m_sourceReader->SetStreamSelection(DWORD(MF_SOURCE_READER_FIRST_AUDIO_STREAM), TRUE); - } - } - } - return hr; -} - -// Retrieves the indexes for selected video/audio streams. -HRESULT QWindowsMediaDeviceReader::initSourceIndexes() -{ - if (!m_sourceReader) - return E_FAIL; - - m_sourceVideoStreamIndex = MF_SOURCE_READER_INVALID_STREAM_INDEX; - m_sourceAudioStreamIndex = MF_SOURCE_READER_INVALID_STREAM_INDEX; - - DWORD index = 0; - BOOL selected = FALSE; - - while (m_sourceReader->GetStreamSelection(index, &selected) == S_OK) { - if (selected) { - IMFMediaType *mediaType = nullptr; - if (SUCCEEDED(m_sourceReader->GetCurrentMediaType(index, &mediaType))) { - GUID majorType = GUID_NULL; - if (SUCCEEDED(mediaType->GetGUID(MF_MT_MAJOR_TYPE, &majorType))) { - if (majorType == MFMediaType_Video) - m_sourceVideoStreamIndex = index; - else if (majorType == MFMediaType_Audio) - m_sourceAudioStreamIndex = index; - } - mediaType->Release(); - } - } - ++index; - } - if ((m_videoSource && m_sourceVideoStreamIndex == MF_SOURCE_READER_INVALID_STREAM_INDEX) || - (m_audioSource && m_sourceAudioStreamIndex == MF_SOURCE_READER_INVALID_STREAM_INDEX)) - return E_FAIL; - return S_OK; -} - -bool QWindowsMediaDeviceReader::setAudioOutput(const QString &audioOutputId) -{ - QMutexLocker locker(&m_mutex); - - stopMonitoring(); - - m_audioOutputId = audioOutputId; - - if (!m_active || m_audioOutputId.isEmpty()) - return true; - - HRESULT hr = startMonitoring(); - - return SUCCEEDED(hr); -} - -HRESULT QWindowsMediaDeviceReader::startMonitoring() -{ - if (m_audioOutputId.isEmpty()) - return E_FAIL; - - IMFAttributes *sinkAttributes = nullptr; - - HRESULT hr = MFCreateAttributes(&sinkAttributes, 1); - if (SUCCEEDED(hr)) { - - hr = sinkAttributes->SetString(MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ID, - reinterpret_cast<LPCWSTR>(m_audioOutputId.utf16())); - if (SUCCEEDED(hr)) { - - IMFMediaSink *mediaSink = nullptr; - hr = MFCreateAudioRenderer(sinkAttributes, &mediaSink); - if (SUCCEEDED(hr)) { - - IMFStreamSink *streamSink = nullptr; - hr = mediaSink->GetStreamSinkByIndex(0, &streamSink); - if (SUCCEEDED(hr)) { - - IMFMediaTypeHandler *typeHandler = nullptr; - hr = streamSink->GetMediaTypeHandler(&typeHandler); - if (SUCCEEDED(hr)) { - - hr = typeHandler->IsMediaTypeSupported(m_audioMediaType, nullptr); - if (SUCCEEDED(hr)) { - - hr = typeHandler->SetCurrentMediaType(m_audioMediaType); - if (SUCCEEDED(hr)) { - - IMFAttributes *writerAttributes = nullptr; - - HRESULT hr = MFCreateAttributes(&writerAttributes, 1); - if (SUCCEEDED(hr)) { - - hr = writerAttributes->SetUINT32(MF_SINK_WRITER_DISABLE_THROTTLING, TRUE); - if (SUCCEEDED(hr)) { - - IMFSinkWriter *sinkWriter = nullptr; - hr = MFCreateSinkWriterFromMediaSink(mediaSink, writerAttributes, &sinkWriter); - if (SUCCEEDED(hr)) { - - hr = sinkWriter->SetInputMediaType(0, m_audioMediaType, nullptr); - if (SUCCEEDED(hr)) { - - IMFSimpleAudioVolume *audioVolume = nullptr; - - if (SUCCEEDED(MFGetService(mediaSink, MR_POLICY_VOLUME_SERVICE, IID_PPV_ARGS(&audioVolume)))) { - audioVolume->SetMasterVolume(float(m_outputVolume)); - audioVolume->SetMute(m_outputMuted); - audioVolume->Release(); - } - - hr = sinkWriter->BeginWriting(); - if (SUCCEEDED(hr)) { - m_monitorSink = mediaSink; - m_monitorSink->AddRef(); - m_monitorWriter = sinkWriter; - m_monitorWriter->AddRef(); - } - } - sinkWriter->Release(); - } - } - writerAttributes->Release(); - } - } - } - typeHandler->Release(); - } - streamSink->Release(); - } - mediaSink->Release(); - } - } - sinkAttributes->Release(); - } - - return hr; -} - -void QWindowsMediaDeviceReader::stopMonitoring() -{ - if (m_monitorWriter) { - m_monitorWriter->Release(); - m_monitorWriter = nullptr; - } - if (m_monitorSink) { - m_monitorSink->Shutdown(); - m_monitorSink->Release(); - m_monitorSink = nullptr; - } -} - -// Activates the requested camera/microphone for streaming. -// One of the IDs may be empty for video-only/audio-only. -bool QWindowsMediaDeviceReader::activate(const QString &cameraId, - const QCameraFormat &cameraFormat, - const QString µphoneId) -{ - QMutexLocker locker(&m_mutex); - - if (cameraId.isEmpty() && microphoneId.isEmpty()) - return false; - - stopMonitoring(); - releaseResources(); - - m_active = false; - m_streaming = false; - - if (!cameraId.isEmpty()) { - if (!SUCCEEDED(createSource(cameraId, true, &m_videoSource))) { - releaseResources(); - return false; - } - } - - if (!microphoneId.isEmpty()) { - if (!SUCCEEDED(createSource(microphoneId, false, &m_audioSource))) { - releaseResources(); - return false; - } - } - - if (!SUCCEEDED(createAggregateReader(m_videoSource, m_audioSource, &m_aggregateSource, &m_sourceReader))) { - releaseResources(); - return false; - } - - DWORD mediaTypeIndex = findMediaTypeIndex(cameraFormat); - - if (!SUCCEEDED(prepareVideoStream(mediaTypeIndex))) { - releaseResources(); - return false; - } - - if (!SUCCEEDED(prepareAudioStream())) { - releaseResources(); - return false; - } - - if (!SUCCEEDED(initSourceIndexes())) { - releaseResources(); - return false; - } - - updateSinkInputMediaTypes(); - startMonitoring(); - - // Request the first frame or audio sample. - if (!SUCCEEDED(m_sourceReader->ReadSample(MF_SOURCE_READER_ANY_STREAM, 0, nullptr, nullptr, nullptr, nullptr))) { - releaseResources(); - return false; - } - - m_active = true; - return true; -} - -void QWindowsMediaDeviceReader::deactivate() -{ - stopMonitoring(); - stopStreaming(); - m_active = false; - m_streaming = false; -} - -void QWindowsMediaDeviceReader::stopStreaming() -{ - QMutexLocker locker(&m_mutex); - releaseResources(); -} - -// Releases allocated streaming stuff. -void QWindowsMediaDeviceReader::releaseResources() -{ - if (m_videoMediaType) { - m_videoMediaType->Release(); - m_videoMediaType = nullptr; - } - if (m_audioMediaType) { - m_audioMediaType->Release(); - m_audioMediaType = nullptr; - } - if (m_sourceReader) { - m_sourceReader->Release(); - m_sourceReader = nullptr; - } - if (m_aggregateSource) { - m_aggregateSource->Release(); - m_aggregateSource = nullptr; - } - if (m_videoSource) { - m_videoSource->Release(); - m_videoSource = nullptr; - } - if (m_audioSource) { - m_audioSource->Release(); - m_audioSource = nullptr; - } -} - -HRESULT QWindowsMediaDeviceReader::createVideoMediaType(const GUID &format, UINT32 bitRate, UINT32 width, - UINT32 height, qreal frameRate, IMFMediaType **mediaType) -{ - if (!mediaType) - return E_INVALIDARG; - - *mediaType = nullptr; - IMFMediaType *targetMediaType = nullptr; - - if (SUCCEEDED(MFCreateMediaType(&targetMediaType))) { - - if (SUCCEEDED(targetMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video))) { - - if (SUCCEEDED(targetMediaType->SetGUID(MF_MT_SUBTYPE, format))) { - - if (SUCCEEDED(targetMediaType->SetUINT32(MF_MT_AVG_BITRATE, bitRate))) { - - if (SUCCEEDED(MFSetAttributeSize(targetMediaType, MF_MT_FRAME_SIZE, width, height))) { - - if (SUCCEEDED(MFSetAttributeRatio(targetMediaType, MF_MT_FRAME_RATE, - UINT32(frameRate * 1000), 1000))) { - UINT32 t1, t2; - if (SUCCEEDED(MFGetAttributeRatio(m_videoMediaType, MF_MT_PIXEL_ASPECT_RATIO, &t1, &t2))) { - - if (SUCCEEDED(MFSetAttributeRatio(targetMediaType, MF_MT_PIXEL_ASPECT_RATIO, t1, t2))) { - - if (SUCCEEDED(m_videoMediaType->GetUINT32(MF_MT_INTERLACE_MODE, &t1))) { - - if (SUCCEEDED(targetMediaType->SetUINT32(MF_MT_INTERLACE_MODE, t1))) { - - *mediaType = targetMediaType; - return S_OK; - } - } - } - } - } - } - } - } - } - targetMediaType->Release(); - } - return E_FAIL; -} - -HRESULT QWindowsMediaDeviceReader::createAudioMediaType(const GUID &format, UINT32 bitRate, IMFMediaType **mediaType) -{ - if (!mediaType) - return E_INVALIDARG; - - *mediaType = nullptr; - IMFMediaType *targetMediaType = nullptr; - - if (SUCCEEDED(MFCreateMediaType(&targetMediaType))) { - - if (SUCCEEDED(targetMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio))) { - - if (SUCCEEDED(targetMediaType->SetGUID(MF_MT_SUBTYPE, format))) { - - if (bitRate == 0 || SUCCEEDED(targetMediaType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, bitRate / 8))) { - - *mediaType = targetMediaType; - return S_OK; - } - } - } - targetMediaType->Release(); - } - return E_FAIL; -} - -HRESULT QWindowsMediaDeviceReader::updateSinkInputMediaTypes() -{ - HRESULT hr = S_OK; - if (m_sinkWriter) { - if (m_videoSource && m_videoMediaType && m_sinkVideoStreamIndex != MF_SINK_WRITER_INVALID_STREAM_INDEX) { - hr = m_sinkWriter->SetInputMediaType(m_sinkVideoStreamIndex, m_videoMediaType, nullptr); - } - if (SUCCEEDED(hr)) { - if (m_audioSource && m_audioMediaType && m_sinkAudioStreamIndex != MF_SINK_WRITER_INVALID_STREAM_INDEX) { - hr = m_sinkWriter->SetInputMediaType(m_sinkAudioStreamIndex, m_audioMediaType, nullptr); - } - } - } - return hr; -} - -bool QWindowsMediaDeviceReader::startRecording(const QString &fileName, const GUID &container, - const GUID &videoFormat, UINT32 videoBitRate, UINT32 width, - UINT32 height, qreal frameRate, const GUID &audioFormat, - UINT32 audioBitRate) -{ - QMutexLocker locker(&m_mutex); - - if (!m_active || m_recording || (videoFormat == GUID_NULL && audioFormat == GUID_NULL)) - return false; - - IMFAttributes *writerAttributes = nullptr; - - HRESULT hr = MFCreateAttributes(&writerAttributes, 2); - if (SUCCEEDED(hr)) { - - // Set callback so OnFinalize() is called after video is saved. - hr = writerAttributes->SetUnknown(MF_SINK_WRITER_ASYNC_CALLBACK, - static_cast<IMFSinkWriterCallback*>(this)); - if (SUCCEEDED(hr)) { - - hr = writerAttributes->SetGUID(MF_TRANSCODE_CONTAINERTYPE, container); - if (SUCCEEDED(hr)) { - - hr = MFCreateSinkWriterFromURL(reinterpret_cast<LPCWSTR>(fileName.utf16()), - nullptr, writerAttributes, &m_sinkWriter); - if (SUCCEEDED(hr)) { - - m_sinkVideoStreamIndex = MF_SINK_WRITER_INVALID_STREAM_INDEX; - m_sinkAudioStreamIndex = MF_SINK_WRITER_INVALID_STREAM_INDEX; - - if (m_videoSource && videoFormat != GUID_NULL) { - IMFMediaType *targetMediaType = nullptr; - - hr = createVideoMediaType(videoFormat, videoBitRate, width, height, - frameRate, &targetMediaType); - if (SUCCEEDED(hr)) { - - hr = m_sinkWriter->AddStream(targetMediaType, &m_sinkVideoStreamIndex); - if (SUCCEEDED(hr)) { - - hr = m_sinkWriter->SetInputMediaType(m_sinkVideoStreamIndex, - m_videoMediaType, nullptr); - } - targetMediaType->Release(); - } - } - - if (SUCCEEDED(hr)) { - - if (m_audioSource && audioFormat != GUID_NULL) { - IMFMediaType *targetMediaType = nullptr; - - hr = createAudioMediaType(audioFormat, audioBitRate, &targetMediaType); - if (SUCCEEDED(hr)) { - - hr = m_sinkWriter->AddStream(targetMediaType, &m_sinkAudioStreamIndex); - if (SUCCEEDED(hr)) { - - hr = m_sinkWriter->SetInputMediaType(m_sinkAudioStreamIndex, - m_audioMediaType, nullptr); - } - targetMediaType->Release(); - } - } - - if (SUCCEEDED(hr)) { - - hr = m_sinkWriter->BeginWriting(); - if (SUCCEEDED(hr)) { - m_lastDuration = -1; - m_currentDuration = 0; - updateDuration(); - m_durationTimer.start(); - m_recording = true; - m_firstFrame = true; - m_paused = false; - m_pauseChanging = false; - } - } - } - } - } - } - writerAttributes->Release(); - } - if (m_sinkWriter && !SUCCEEDED(hr)) { - m_sinkWriter->Release(); - m_sinkWriter = nullptr; - } - return SUCCEEDED(hr); -} - -void QWindowsMediaDeviceReader::stopRecording() -{ - QMutexLocker locker(&m_mutex); - - if (m_sinkWriter && m_recording) { - - HRESULT hr = m_sinkWriter->Finalize(); - - if (SUCCEEDED(hr)) { - m_hasFinalized.wait(&m_mutex); - } else { - m_sinkWriter->Release(); - m_sinkWriter = nullptr; - - QMetaObject::invokeMethod(this, "recordingError", - Qt::QueuedConnection, Q_ARG(int, hr)); - } - } - - m_recording = false; - m_paused = false; - m_pauseChanging = false; - - m_durationTimer.stop(); - m_lastDuration = -1; - m_currentDuration = -1; -} - -bool QWindowsMediaDeviceReader::pauseRecording() -{ - if (!m_recording || m_paused) - return false; - m_pauseTime = m_lastTimestamp; - m_paused = true; - m_pauseChanging = true; - return true; -} - -bool QWindowsMediaDeviceReader::resumeRecording() -{ - if (!m_recording || !m_paused) - return false; - m_paused = false; - m_pauseChanging = true; - return true; -} - -//from IUnknown -STDMETHODIMP QWindowsMediaDeviceReader::QueryInterface(REFIID riid, LPVOID *ppvObject) -{ - if (!ppvObject) - return E_POINTER; - if (riid == IID_IMFSourceReaderCallback) { - *ppvObject = static_cast<IMFSourceReaderCallback*>(this); - } else if (riid == IID_IMFSinkWriterCallback) { - *ppvObject = static_cast<IMFSinkWriterCallback*>(this); - } else if (riid == IID_IUnknown) { - *ppvObject = static_cast<IUnknown*>(static_cast<IMFSourceReaderCallback*>(this)); - } else { - *ppvObject = nullptr; - return E_NOINTERFACE; - } - AddRef(); - return S_OK; -} - -STDMETHODIMP_(ULONG) QWindowsMediaDeviceReader::AddRef(void) -{ - return InterlockedIncrement(&m_cRef); -} - -STDMETHODIMP_(ULONG) QWindowsMediaDeviceReader::Release(void) -{ - LONG cRef = InterlockedDecrement(&m_cRef); - if (cRef == 0) { - this->deleteLater(); - } - return cRef; -} - -UINT32 QWindowsMediaDeviceReader::frameWidth() const -{ - return m_frameWidth; -} - -UINT32 QWindowsMediaDeviceReader::frameHeight() const -{ - return m_frameHeight; -} - -qreal QWindowsMediaDeviceReader::frameRate() const -{ - return m_frameRate; -} - -void QWindowsMediaDeviceReader::setInputMuted(bool muted) -{ - m_inputMuted = muted; -} - -void QWindowsMediaDeviceReader::setInputVolume(qreal volume) -{ - m_inputVolume = qBound(0.0, volume, 1.0); -} - -void QWindowsMediaDeviceReader::setOutputMuted(bool muted) -{ - QMutexLocker locker(&m_mutex); - - m_outputMuted = muted; - - if (m_active && m_monitorSink) { - IMFSimpleAudioVolume *audioVolume = nullptr; - if (SUCCEEDED(MFGetService(m_monitorSink, MR_POLICY_VOLUME_SERVICE, - IID_PPV_ARGS(&audioVolume)))) { - audioVolume->SetMute(m_outputMuted); - audioVolume->Release(); - } - } -} - -void QWindowsMediaDeviceReader::setOutputVolume(qreal volume) -{ - QMutexLocker locker(&m_mutex); - - m_outputVolume = qBound(0.0, volume, 1.0); - - if (m_active && m_monitorSink) { - IMFSimpleAudioVolume *audioVolume = nullptr; - if (SUCCEEDED(MFGetService(m_monitorSink, MR_POLICY_VOLUME_SERVICE, - IID_PPV_ARGS(&audioVolume)))) { - audioVolume->SetMasterVolume(float(m_outputVolume)); - audioVolume->Release(); - } - } -} - -void QWindowsMediaDeviceReader::updateDuration() -{ - if (m_currentDuration >= 0 && m_lastDuration != m_currentDuration) { - m_lastDuration = m_currentDuration; - emit durationChanged(m_currentDuration); - } -} - -//from IMFSourceReaderCallback -STDMETHODIMP QWindowsMediaDeviceReader::OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex, - DWORD dwStreamFlags, LONGLONG llTimestamp, - IMFSample *pSample) -{ - QMutexLocker locker(&m_mutex); - - if (FAILED(hrStatus)) { - emit streamingError(int(hrStatus)); - return hrStatus; - } - - m_lastTimestamp = llTimestamp; - - if ((dwStreamFlags & MF_SOURCE_READERF_ENDOFSTREAM) == MF_SOURCE_READERF_ENDOFSTREAM) { - m_streaming = false; - emit streamingStopped(); - } else { - - if (!m_streaming) { - m_streaming = true; - emit streamingStarted(); - } - if (pSample) { - - if (m_monitorWriter && dwStreamIndex == m_sourceAudioStreamIndex) - m_monitorWriter->WriteSample(0, pSample); - - if (m_recording) { - - if (m_firstFrame) { - m_timeOffset = llTimestamp; - m_firstFrame = false; - emit recordingStarted(); - } - - if (m_pauseChanging) { - // Recording time should not pass while paused. - if (m_paused) - m_pauseTime = llTimestamp; - else - m_timeOffset += llTimestamp - m_pauseTime; - m_pauseChanging = false; - } - - // Send the video frame or audio sample to be encoded. - if (m_sinkWriter && !m_paused) { - - pSample->SetSampleTime(llTimestamp - m_timeOffset); - - if (dwStreamIndex == m_sourceVideoStreamIndex) { - - m_sinkWriter->WriteSample(m_sinkVideoStreamIndex, pSample); - - } else if (dwStreamIndex == m_sourceAudioStreamIndex) { - - float volume = m_inputMuted ? 0.0f : float(m_inputVolume); - - // Change the volume of the audio sample, if needed. - if (volume != 1.0f) { - IMFMediaBuffer *mediaBuffer = nullptr; - if (SUCCEEDED(pSample->ConvertToContiguousBuffer(&mediaBuffer))) { - - DWORD bufLen = 0; - BYTE *buffer = nullptr; - - if (SUCCEEDED(mediaBuffer->Lock(&buffer, nullptr, &bufLen))) { - - float *floatBuffer = reinterpret_cast<float*>(buffer); - - for (DWORD i = 0; i < bufLen/4; ++i) - floatBuffer[i] *= volume; - - mediaBuffer->Unlock(); - } - mediaBuffer->Release(); - } - } - - m_sinkWriter->WriteSample(m_sinkAudioStreamIndex, pSample); - } - m_currentDuration = (llTimestamp - m_timeOffset) / 10000; - } - } - - // Generate a new QVideoFrame from IMFSample. - if (dwStreamIndex == m_sourceVideoStreamIndex) { - IMFMediaBuffer *mediaBuffer = nullptr; - if (SUCCEEDED(pSample->ConvertToContiguousBuffer(&mediaBuffer))) { - - DWORD bufLen = 0; - BYTE *buffer = nullptr; - - if (SUCCEEDED(mediaBuffer->Lock(&buffer, nullptr, &bufLen))) { - auto bytes = QByteArray(reinterpret_cast<char*>(buffer), bufLen); - - QVideoFrame frame(new QMemoryVideoBuffer(bytes, m_stride), - QVideoFrameFormat(QSize(m_frameWidth, m_frameHeight), m_pixelFormat)); - - // WMF uses 100-nanosecond units, Qt uses microseconds - frame.setStartTime(llTimestamp * 0.1); - - LONGLONG duration = -1; - if (SUCCEEDED(pSample->GetSampleDuration(&duration))) - frame.setEndTime((llTimestamp + duration) * 0.1); - - emit videoFrameChanged(frame); - - mediaBuffer->Unlock(); - } - mediaBuffer->Release(); - } - } - } - // request the next video frame or sound sample - if (m_sourceReader) - m_sourceReader->ReadSample(MF_SOURCE_READER_ANY_STREAM, - 0, nullptr, nullptr, nullptr, nullptr); - } - - return S_OK; -} - -STDMETHODIMP QWindowsMediaDeviceReader::OnFlush(DWORD) -{ - return S_OK; -} - -STDMETHODIMP QWindowsMediaDeviceReader::OnEvent(DWORD, IMFMediaEvent*) -{ - return S_OK; -} - -//from IMFSinkWriterCallback -STDMETHODIMP QWindowsMediaDeviceReader::OnFinalize(HRESULT) -{ - QMutexLocker locker(&m_mutex); - if (m_sinkWriter) { - m_sinkWriter->Release(); - m_sinkWriter = nullptr; - } - emit recordingStopped(); - m_hasFinalized.notify_one(); - return S_OK; -} - -STDMETHODIMP QWindowsMediaDeviceReader::OnMarker(DWORD, LPVOID) -{ - return S_OK; -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/windows/mediacapture/qwindowsmediadevicereader_p.h b/src/multimedia/platform/windows/mediacapture/qwindowsmediadevicereader_p.h deleted file mode 100644 index e7af22e5d..000000000 --- a/src/multimedia/platform/windows/mediacapture/qwindowsmediadevicereader_p.h +++ /dev/null @@ -1,189 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Mobility Components. -** -** $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$ -** -****************************************************************************/ - -#ifndef QWINDOWSMEDIADEVICEREADER_H -#define QWINDOWSMEDIADEVICEREADER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <mfapi.h> -#include <mfidl.h> -#include <Mferror.h> -#include <Mfreadwrite.h> - -#include <QtCore/qobject.h> -#include <QtCore/qmutex.h> -#include <QtCore/qwaitcondition.h> -#include <QtCore/qtimer.h> -#include <qvideoframe.h> -#include <qcameradevice.h> - -QT_BEGIN_NAMESPACE - -class QVideoSink; - -class QWindowsMediaDeviceReader : public QObject, - public IMFSourceReaderCallback, - public IMFSinkWriterCallback -{ - Q_OBJECT -public: - explicit QWindowsMediaDeviceReader(QObject *parent = nullptr); - ~QWindowsMediaDeviceReader(); - - //from IUnknown - STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject); - STDMETHODIMP_(ULONG) AddRef(void); - STDMETHODIMP_(ULONG) Release(void); - - //from IMFSourceReaderCallback - STDMETHODIMP OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex, - DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample *pSample); - STDMETHODIMP OnFlush(DWORD dwStreamIndex); - STDMETHODIMP OnEvent(DWORD dwStreamIndex, IMFMediaEvent *pEvent); - - //from IMFSinkWriterCallback - STDMETHODIMP OnFinalize(HRESULT hrStatus); - STDMETHODIMP OnMarker(DWORD dwStreamIndex, LPVOID pvContext); - - bool activate(const QString &cameraId, - const QCameraFormat &cameraFormat, - const QString µphoneId); - void deactivate(); - - bool startRecording(const QString &fileName, const GUID &container, - const GUID &videoFormat, UINT32 videoBitRate, UINT32 width, - UINT32 height, qreal frameRate, const GUID &audioFormat, - UINT32 audioBitRate); - void stopRecording(); - bool pauseRecording(); - bool resumeRecording(); - - UINT32 frameWidth() const; - UINT32 frameHeight() const; - qreal frameRate() const; - void setInputMuted(bool muted); - void setInputVolume(qreal volume); - void setOutputMuted(bool muted); - void setOutputVolume(qreal volume); - bool setAudioOutput(const QString &audioOutputId); - -Q_SIGNALS: - void streamingStarted(); - void streamingStopped(); - void streamingError(int errorCode); - void recordingStarted(); - void recordingStopped(); - void recordingError(int errorCode); - void durationChanged(qint64 duration); - void videoFrameChanged(const QVideoFrame &frame); - -private slots: - void updateDuration(); - -private: - HRESULT createSource(const QString &deviceId, bool video, IMFMediaSource **source); - HRESULT createAggregateReader(IMFMediaSource *firstSource, IMFMediaSource *secondSource, - IMFMediaSource **aggregateSource, IMFSourceReader **sourceReader); - HRESULT createVideoMediaType(const GUID &format, UINT32 bitRate, UINT32 width, UINT32 height, - qreal frameRate, IMFMediaType **mediaType); - HRESULT createAudioMediaType(const GUID &format, UINT32 bitRate, IMFMediaType **mediaType); - HRESULT initAudioType(IMFMediaType *mediaType, UINT32 channels, UINT32 samplesPerSec, bool flt); - HRESULT prepareVideoStream(DWORD mediaTypeIndex); - HRESULT prepareAudioStream(); - HRESULT initSourceIndexes(); - HRESULT updateSinkInputMediaTypes(); - HRESULT startMonitoring(); - void stopMonitoring(); - void releaseResources(); - void stopStreaming(); - DWORD findMediaTypeIndex(const QCameraFormat &reqFormat); - - long m_cRef = 1; - QMutex m_mutex; - QWaitCondition m_hasFinalized; - IMFMediaSource *m_videoSource = nullptr; - IMFMediaType *m_videoMediaType = nullptr; - IMFMediaSource *m_audioSource = nullptr; - IMFMediaType *m_audioMediaType = nullptr; - IMFMediaSource *m_aggregateSource = nullptr; - IMFSourceReader *m_sourceReader = nullptr; - IMFSinkWriter *m_sinkWriter = nullptr; - IMFMediaSink *m_monitorSink = nullptr; - IMFSinkWriter *m_monitorWriter = nullptr; - QString m_audioOutputId; - DWORD m_sourceVideoStreamIndex = MF_SOURCE_READER_INVALID_STREAM_INDEX; - DWORD m_sourceAudioStreamIndex = MF_SOURCE_READER_INVALID_STREAM_INDEX; - DWORD m_sinkVideoStreamIndex = MF_SINK_WRITER_INVALID_STREAM_INDEX; - DWORD m_sinkAudioStreamIndex = MF_SINK_WRITER_INVALID_STREAM_INDEX; - UINT32 m_frameWidth = 0; - UINT32 m_frameHeight = 0; - qreal m_frameRate = 0.0; - LONG m_stride = 0; - bool m_active = false; - bool m_streaming = false; - bool m_recording = false; - bool m_firstFrame = false; - bool m_paused = false; - bool m_pauseChanging = false; - bool m_inputMuted = false; - bool m_outputMuted = false; - qreal m_inputVolume = 1.0; - qreal m_outputVolume = 1.0; - QVideoFrameFormat::PixelFormat m_pixelFormat = QVideoFrameFormat::Format_Invalid; - LONGLONG m_timeOffset = 0; - LONGLONG m_pauseTime = 0; - LONGLONG m_lastTimestamp = 0; - QTimer m_durationTimer; - qint64 m_currentDuration = -1; - qint64 m_lastDuration = -1; -}; - -QT_END_NAMESPACE - -#endif // QWINDOWSMEDIADEVICEREADER_H diff --git a/src/multimedia/platform/windows/mediacapture/qwindowsmediadevicesession.cpp b/src/multimedia/platform/windows/mediacapture/qwindowsmediadevicesession.cpp deleted file mode 100644 index c43771d23..000000000 --- a/src/multimedia/platform/windows/mediacapture/qwindowsmediadevicesession.cpp +++ /dev/null @@ -1,399 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qwindowsmediadevicesession_p.h" - -#include "qwindowsmediadevicereader_p.h" -#include "qwindowsmultimediautils_p.h" -#include "private/qplatformvideosink_p.h" -#include <qvideosink.h> -#include <QtCore/qdebug.h> -#include <qaudioinput.h> -#include <qaudiooutput.h> - -QT_BEGIN_NAMESPACE - -QWindowsMediaDeviceSession::QWindowsMediaDeviceSession(QObject *parent) - : QObject(parent) -{ - m_mediaDeviceReader = new QWindowsMediaDeviceReader(this); - connect(m_mediaDeviceReader, SIGNAL(streamingStarted()), this, SLOT(handleStreamingStarted())); - connect(m_mediaDeviceReader, SIGNAL(streamingStopped()), this, SLOT(handleStreamingStopped())); - connect(m_mediaDeviceReader, SIGNAL(streamingError(int)), this, SLOT(handleStreamingError(int))); - connect(m_mediaDeviceReader, SIGNAL(videoFrameChanged(QVideoFrame)), this, SLOT(handleVideoFrameChanged(QVideoFrame))); - connect(m_mediaDeviceReader, SIGNAL(recordingStarted()), this, SIGNAL(recordingStarted())); - connect(m_mediaDeviceReader, SIGNAL(recordingStopped()), this, SIGNAL(recordingStopped())); - connect(m_mediaDeviceReader, SIGNAL(recordingError(int)), this, SIGNAL(recordingError(int))); - connect(m_mediaDeviceReader, SIGNAL(durationChanged(qint64)), this, SIGNAL(durationChanged(qint64))); -} - -QWindowsMediaDeviceSession::~QWindowsMediaDeviceSession() -{ - delete m_mediaDeviceReader; -} - -bool QWindowsMediaDeviceSession::isActive() const -{ - return m_active; -} - -bool QWindowsMediaDeviceSession::isActivating() const -{ - return m_activating; -} - -void QWindowsMediaDeviceSession::setActive(bool active) -{ - if ((active && (m_active || m_activating)) || (!active && !m_active && !m_activating)) - return; - - if (active) { - auto camId = QString::fromUtf8(m_activeCameraDevice.id()); - auto micId = m_audioInput ? QString::fromUtf8(m_audioInput->device().id()) : QString(); - if (!camId.isEmpty() || !micId.isEmpty()) { - if (m_mediaDeviceReader->activate(camId, m_cameraFormat, micId)) { - m_activating = true; - } else { - emit streamingError(MF_E_NOT_AVAILABLE); - } - } else { - qWarning() << Q_FUNC_INFO << "Camera ID and Microphone ID both undefined."; - } - } else { - m_mediaDeviceReader->deactivate(); - m_active = false; - m_activating = false; - emit activeChanged(m_active); - emit readyForCaptureChanged(m_active); - } -} - -void QWindowsMediaDeviceSession::reactivate() -{ - if (m_active || m_activating) { - pauseRecording(); - setActive(false); - setActive(true); - resumeRecording(); - } -} - -void QWindowsMediaDeviceSession::setActiveCamera(const QCameraDevice &camera) -{ - m_activeCameraDevice = camera; - reactivate(); -} - -QCameraDevice QWindowsMediaDeviceSession::activeCamera() const -{ - return m_activeCameraDevice; -} - -void QWindowsMediaDeviceSession::setCameraFormat(const QCameraFormat &cameraFormat) -{ - m_cameraFormat = cameraFormat; -} - -void QWindowsMediaDeviceSession::setVideoSink(QVideoSink *surface) -{ - m_surface = surface; -} - -void QWindowsMediaDeviceSession::handleStreamingStarted() -{ - m_active = true; - m_activating = false; - emit activeChanged(m_active); - emit readyForCaptureChanged(m_active); -} - -void QWindowsMediaDeviceSession::handleStreamingStopped() -{ - m_active = false; - emit activeChanged(m_active); - emit readyForCaptureChanged(m_active); -} - -void QWindowsMediaDeviceSession::handleStreamingError(int errorCode) -{ - if (m_surface) - emit m_surface->platformVideoSink()->setVideoFrame(QVideoFrame()); - emit streamingError(errorCode); -} - -void QWindowsMediaDeviceSession::handleVideoFrameChanged(const QVideoFrame &frame) -{ - if (m_surface) - emit m_surface->platformVideoSink()->setVideoFrame(frame); - emit videoFrameChanged(frame); -} - -void QWindowsMediaDeviceSession::setAudioInputMuted(bool muted) -{ - m_mediaDeviceReader->setInputMuted(muted); -} - -void QWindowsMediaDeviceSession::setAudioInputVolume(float volume) -{ - m_mediaDeviceReader->setInputVolume(volume); -} - -void QWindowsMediaDeviceSession::audioInputDeviceChanged() -{ - reactivate(); -} - -void QWindowsMediaDeviceSession::setAudioOutputMuted(bool muted) -{ - m_mediaDeviceReader->setOutputMuted(muted); -} - -void QWindowsMediaDeviceSession::setAudioOutputVolume(float volume) -{ - m_mediaDeviceReader->setOutputVolume(volume); -} - -void QWindowsMediaDeviceSession::audioOutputDeviceChanged() -{ - if (m_active || m_activating) - m_mediaDeviceReader->setAudioOutput(QString::fromUtf8(m_audioOutput->device().id())); -} - -void QWindowsMediaDeviceSession::setAudioInput(QAudioInput *input) -{ - if (m_audioInput == input) - return; - if (m_audioInput) - m_audioInput->disconnect(this); - m_audioInput = input; - - audioInputDeviceChanged(); - - if (!m_audioInput) - return; - connect(m_audioInput, &QAudioInput::mutedChanged, this, &QWindowsMediaDeviceSession::setAudioInputMuted); - connect(m_audioInput, &QAudioInput::volumeChanged, this, &QWindowsMediaDeviceSession::setAudioInputVolume); - connect(m_audioInput, &QAudioInput::deviceChanged, this, &QWindowsMediaDeviceSession::audioInputDeviceChanged); -} - -void QWindowsMediaDeviceSession::setAudioOutput(QAudioOutput *output) -{ - if (m_audioOutput == output) - return; - if (m_audioOutput) - m_audioOutput->disconnect(this); - m_audioOutput = output; - if (!m_audioOutput) { - m_mediaDeviceReader->setAudioOutput({}); - return; - } - - m_mediaDeviceReader->setAudioOutput(QString::fromUtf8(m_audioOutput->device().id())); - - connect(m_audioOutput, &QAudioOutput::mutedChanged, this, &QWindowsMediaDeviceSession::setAudioOutputMuted); - connect(m_audioOutput, &QAudioOutput::volumeChanged, this, &QWindowsMediaDeviceSession::setAudioOutputVolume); - connect(m_audioOutput, &QAudioOutput::deviceChanged, this, &QWindowsMediaDeviceSession::audioOutputDeviceChanged); -} - -bool QWindowsMediaDeviceSession::startRecording(QMediaEncoderSettings &settings, const QString &fileName, bool audioOnly) -{ - GUID container = QWindowsMultimediaUtils::containerForVideoFileFormat(settings.mediaFormat().fileFormat()); - GUID videoFormat = QWindowsMultimediaUtils::videoFormatForCodec(settings.videoCodec()); - GUID audioFormat = QWindowsMultimediaUtils::audioFormatForCodec(settings.audioCodec()); - - QSize res = settings.videoResolution(); - UINT32 width, height; - if (res.width() > 0 && res.height() > 0) { - width = UINT32(res.width()); - height = UINT32(res.height()); - } else { - width = m_mediaDeviceReader->frameWidth(); - height = m_mediaDeviceReader->frameHeight(); - settings.setVideoResolution(QSize(int(width), int(height))); - } - - qreal frameRate = settings.videoFrameRate(); - if (frameRate <= 0) { - frameRate = m_mediaDeviceReader->frameRate(); - settings.setVideoFrameRate(frameRate); - } - - auto quality = settings.quality(); - - UINT32 videoBitRate = 0; - if (settings.videoBitRate() > 0) { - videoBitRate = UINT32(settings.videoBitRate()); - } else { - videoBitRate = estimateVideoBitRate(videoFormat, width, height, frameRate, quality); - settings.setVideoBitRate(int(videoBitRate)); - } - - UINT32 audioBitRate = 0; - if (settings.audioBitRate() > 0) { - audioBitRate = UINT32(settings.audioBitRate()); - } else { - audioBitRate = estimateAudioBitRate(audioFormat, quality); - settings.setAudioBitRate(int(audioBitRate)); - } - - return m_mediaDeviceReader->startRecording(fileName, container, audioOnly ? GUID_NULL : videoFormat, - videoBitRate, width, height, frameRate, - audioFormat, audioBitRate); -} - -void QWindowsMediaDeviceSession::stopRecording() -{ - m_mediaDeviceReader->stopRecording(); -} - -bool QWindowsMediaDeviceSession::pauseRecording() -{ - return m_mediaDeviceReader->pauseRecording(); -} - -bool QWindowsMediaDeviceSession::resumeRecording() -{ - return m_mediaDeviceReader->resumeRecording(); -} - -// empirical estimate of the required video bitrate (for H.264) -quint32 QWindowsMediaDeviceSession::estimateVideoBitRate(const GUID &videoFormat, quint32 width, quint32 height, - qreal frameRate, QMediaRecorder::Quality quality) -{ - Q_UNUSED(videoFormat); - - qreal bitsPerPixel; - switch (quality) { - case QMediaRecorder::Quality::VeryLowQuality: - bitsPerPixel = 0.08; - break; - case QMediaRecorder::Quality::LowQuality: - bitsPerPixel = 0.2; - break; - case QMediaRecorder::Quality::NormalQuality: - bitsPerPixel = 0.3; - break; - case QMediaRecorder::Quality::HighQuality: - bitsPerPixel = 0.5; - break; - case QMediaRecorder::Quality::VeryHighQuality: - bitsPerPixel = 0.8; - break; - default: - bitsPerPixel = 0.3; - } - - // Required bitrate is not linear on the number of pixels; small resolutions - // require more BPP, thus the minimum values, to try to compensate it. - quint32 pixelsPerSec = quint32(qMax(width, 320u) * qMax(height, 240u) * qMax(frameRate, 6.0)); - return pixelsPerSec * bitsPerPixel; -} - -quint32 QWindowsMediaDeviceSession::estimateAudioBitRate(const GUID &audioFormat, QMediaRecorder::Quality quality) -{ - if (audioFormat == MFAudioFormat_AAC) { - // Bitrates supported by the AAC encoder are 96K, 128K, 160K, 192K. - switch (quality) { - case QMediaRecorder::Quality::VeryLowQuality: - return 96000; - case QMediaRecorder::Quality::LowQuality: - return 96000; - case QMediaRecorder::Quality::NormalQuality: - return 128000; - case QMediaRecorder::Quality::HighQuality: - return 160000; - case QMediaRecorder::Quality::VeryHighQuality: - return 192000; - default: - return 128000; - } - } else if (audioFormat == MFAudioFormat_MP3) { - // Bitrates supported by the MP3 encoder are - // 32K, 40K, 48K, 56K, 64K, 80K, 96K, 112K, 128K, 160K, 192K, 224K, 256K, 320K. - switch (quality) { - case QMediaRecorder::Quality::VeryLowQuality: - return 48000; - case QMediaRecorder::Quality::LowQuality: - return 96000; - case QMediaRecorder::Quality::NormalQuality: - return 128000; - case QMediaRecorder::Quality::HighQuality: - return 224000; - case QMediaRecorder::Quality::VeryHighQuality: - return 320000; - default: - return 128000; - } - } else if (audioFormat == MFAudioFormat_WMAudioV8) { - // Bitrates supported by the Windows Media Audio 8 encoder - switch (quality) { - case QMediaRecorder::Quality::VeryLowQuality: - return 32000; - case QMediaRecorder::Quality::LowQuality: - return 96000; - case QMediaRecorder::Quality::NormalQuality: - return 192000; - case QMediaRecorder::Quality::HighQuality: - return 256016; - case QMediaRecorder::Quality::VeryHighQuality: - return 320032; - default: - return 192000; - } - } else if (audioFormat == MFAudioFormat_WMAudioV9) { - // Bitrates supported by the Windows Media Audio 9 encoder - switch (quality) { - case QMediaRecorder::Quality::VeryLowQuality: - return 32000; - case QMediaRecorder::Quality::LowQuality: - return 96000; - case QMediaRecorder::Quality::NormalQuality: - return 192000; - case QMediaRecorder::Quality::HighQuality: - return 256016; - case QMediaRecorder::Quality::VeryHighQuality: - return 384000; - default: - return 192000; - } - } - return 0; // Use default for format -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/windows/mediacapture/qwindowsmediadevicesession_p.h b/src/multimedia/platform/windows/mediacapture/qwindowsmediadevicesession_p.h deleted file mode 100644 index 4620bd6fb..000000000 --- a/src/multimedia/platform/windows/mediacapture/qwindowsmediadevicesession_p.h +++ /dev/null @@ -1,136 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QWINDOWSMEDIADEVICESESSION_H -#define QWINDOWSMEDIADEVICESESSION_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qtmultimediaglobal_p.h> -#include <qcamera.h> -#include <qaudiodevice.h> -#include <qwindowsmultimediautils_p.h> -#include <qplatformmediaencoder_p.h> - -QT_BEGIN_NAMESPACE - -class QAudioInput; -class QAudioOutput; -class QVideoSink; -class QWindowsMediaDeviceReader; - -class QWindowsMediaDeviceSession : public QObject -{ - Q_OBJECT -public: - explicit QWindowsMediaDeviceSession(QObject *parent = nullptr); - ~QWindowsMediaDeviceSession(); - - bool isActive() const; - void setActive(bool active); - - bool isActivating() const; - - void setActiveCamera(const QCameraDevice &camera); - QCameraDevice activeCamera() const; - - void setCameraFormat(const QCameraFormat &cameraFormat); - - void setVideoSink(QVideoSink *surface); - -public Q_SLOTS: - void setAudioInputMuted(bool muted); - void setAudioInputVolume(float volume); - void audioInputDeviceChanged(); - void setAudioOutputMuted(bool muted); - void setAudioOutputVolume(float volume); - void audioOutputDeviceChanged(); - -public: - void setAudioInput(QAudioInput *input); - void setAudioOutput(QAudioOutput *output); - - bool startRecording(QMediaEncoderSettings &settings, const QString &fileName, bool audioOnly); - void stopRecording(); - bool pauseRecording(); - bool resumeRecording(); - -Q_SIGNALS: - void activeChanged(bool); - void readyForCaptureChanged(bool); - void durationChanged(qint64 duration); - void recordingStarted(); - void recordingStopped(); - void streamingError(int errorCode); - void recordingError(int errorCode); - void videoFrameChanged(const QVideoFrame &frame); - -private Q_SLOTS: - void handleStreamingStarted(); - void handleStreamingStopped(); - void handleStreamingError(int errorCode); - void handleVideoFrameChanged(const QVideoFrame &frame); - -private: - void reactivate(); - quint32 estimateVideoBitRate(const GUID &videoFormat, quint32 width, quint32 height, - qreal frameRate, QMediaRecorder::Quality quality); - quint32 estimateAudioBitRate(const GUID &audioFormat, QMediaRecorder::Quality quality); - bool m_active = false; - bool m_activating = false; - QCameraDevice m_activeCameraDevice; - QCameraFormat m_cameraFormat; - QWindowsMediaDeviceReader *m_mediaDeviceReader = nullptr; - QAudioInput *m_audioInput = nullptr; - QAudioOutput *m_audioOutput = nullptr; - QVideoSink *m_surface = nullptr; -}; - -QT_END_NAMESPACE - -#endif // QWINDOWSMEDIADEVICESESSION_H diff --git a/src/multimedia/platform/windows/mediacapture/qwindowsmediaencoder.cpp b/src/multimedia/platform/windows/mediacapture/qwindowsmediaencoder.cpp deleted file mode 100644 index 555d5bbda..000000000 --- a/src/multimedia/platform/windows/mediacapture/qwindowsmediaencoder.cpp +++ /dev/null @@ -1,253 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qwindowsmediaencoder_p.h" - -#include "qwindowsmediadevicesession_p.h" -#include "qwindowsmediacapture_p.h" -#include "qmediastoragelocation_p.h" -#include "mfmetadata_p.h" -#include <QtCore/QUrl> -#include <QtCore/QMimeType> -#include <Mferror.h> -#include <shobjidl.h> -#include <private/qmediarecorder_p.h> - -QT_BEGIN_NAMESPACE - -QWindowsMediaEncoder::QWindowsMediaEncoder(QMediaRecorder *parent) - : QObject(parent), - QPlatformMediaEncoder(parent) -{ -} - -bool QWindowsMediaEncoder::isLocationWritable(const QUrl &location) const -{ - return location.scheme() == QLatin1String("file") || location.scheme().isEmpty(); -} - -QMediaRecorder::RecorderState QWindowsMediaEncoder::state() const -{ - return m_state; -} - -qint64 QWindowsMediaEncoder::duration() const -{ - return m_duration; -} - -void QWindowsMediaEncoder::record(QMediaEncoderSettings &settings) -{ - if (!m_captureService || !m_mediaDeviceSession) { - qWarning() << Q_FUNC_INFO << "Encoder is not set to a capture session"; - return; - } - if (m_state != QMediaRecorder::StoppedState) - return; - - m_mediaDeviceSession->setActive(true); - - if (!m_mediaDeviceSession->isActive() && !m_mediaDeviceSession->isActivating()) { - error(QMediaRecorder::ResourceError, - QMediaRecorderPrivate::msgFailedStartRecording()); - return; - } - - const auto audioOnly = settings.videoCodec() == QMediaFormat::VideoCodec::Unspecified; - - const QString path = (outputLocation().scheme() == QLatin1String("file") ? - outputLocation().path() : outputLocation().toString()); - - m_fileName = QMediaStorageLocation::generateFileName(path, audioOnly - ? QStandardPaths::MusicLocation - : QStandardPaths::MoviesLocation, - settings.mimeType().preferredSuffix()); - - if (m_mediaDeviceSession->startRecording(settings, m_fileName, audioOnly)) { - - m_state = QMediaRecorder::RecordingState; - - actualLocationChanged(QUrl::fromLocalFile(m_fileName)); - stateChanged(m_state); - - } else { - error(QMediaRecorder::FormatError, - QMediaRecorderPrivate::msgFailedStartRecording()); - } -} - -void QWindowsMediaEncoder::pause() -{ - if (!m_mediaDeviceSession || m_state != QMediaRecorder::RecordingState) - return; - - if (m_mediaDeviceSession->pauseRecording()) { - m_state = QMediaRecorder::PausedState; - stateChanged(m_state); - } else { - error(QMediaRecorder::FormatError, tr("Failed to pause recording")); - } -} - -void QWindowsMediaEncoder::resume() -{ - if (!m_mediaDeviceSession || m_state != QMediaRecorder::PausedState) - return; - - if (m_mediaDeviceSession->resumeRecording()) { - m_state = QMediaRecorder::RecordingState; - stateChanged(m_state); - } else { - error(QMediaRecorder::FormatError, tr("Failed to resume recording")); - } -} - -void QWindowsMediaEncoder::stop() -{ - if (m_mediaDeviceSession && m_state != QMediaRecorder::StoppedState) - m_mediaDeviceSession->stopRecording(); -} - - - -void QWindowsMediaEncoder::setCaptureSession(QPlatformMediaCaptureSession *session) -{ - QWindowsMediaCaptureService *captureSession = static_cast<QWindowsMediaCaptureService *>(session); - if (m_captureService == captureSession) - return; - - if (m_captureService) - stop(); - - m_captureService = captureSession; - if (!m_captureService) { - m_mediaDeviceSession = nullptr; - return; - } - - m_mediaDeviceSession = m_captureService->session(); - Q_ASSERT(m_mediaDeviceSession); - - connect(m_mediaDeviceSession, &QWindowsMediaDeviceSession::recordingStarted, this, &QWindowsMediaEncoder::onRecordingStarted); - connect(m_mediaDeviceSession, &QWindowsMediaDeviceSession::recordingStopped, this, &QWindowsMediaEncoder::onRecordingStopped); - connect(m_mediaDeviceSession, &QWindowsMediaDeviceSession::streamingError, this, &QWindowsMediaEncoder::onStreamingError); - connect(m_mediaDeviceSession, &QWindowsMediaDeviceSession::recordingError, this, &QWindowsMediaEncoder::onRecordingError); - connect(m_mediaDeviceSession, &QWindowsMediaDeviceSession::durationChanged, this, &QWindowsMediaEncoder::onDurationChanged); - connect(m_captureService, &QWindowsMediaCaptureService::cameraChanged, this, &QWindowsMediaEncoder::onCameraChanged); - onCameraChanged(); -} - -void QWindowsMediaEncoder::setMetaData(const QMediaMetaData &metaData) -{ - m_metaData = metaData; -} - -QMediaMetaData QWindowsMediaEncoder::metaData() const -{ - return m_metaData; -} - -void QWindowsMediaEncoder::saveMetadata() -{ - if (!m_metaData.isEmpty()) { - - const QString nativeFileName = QDir::toNativeSeparators(m_fileName); - - IPropertyStore *store = nullptr; - - if (SUCCEEDED(SHGetPropertyStoreFromParsingName(reinterpret_cast<LPCWSTR>(nativeFileName.utf16()), - nullptr, GPS_READWRITE, IID_PPV_ARGS(&store)))) { - - MFMetaData::toNative(m_metaData, store); - - store->Commit(); - store->Release(); - } - } -} - -void QWindowsMediaEncoder::onDurationChanged(qint64 duration) -{ - m_duration = duration; - durationChanged(m_duration); -} - -void QWindowsMediaEncoder::onStreamingError(int errorCode) -{ - if (errorCode == MF_E_VIDEO_RECORDING_DEVICE_INVALIDATED) - error(QMediaRecorder::ResourceError, tr("Camera is no longer present")); - else if (errorCode == MF_E_AUDIO_RECORDING_DEVICE_INVALIDATED) - error(QMediaRecorder::ResourceError, tr("Audio input is no longer present")); - else - error(QMediaRecorder::ResourceError, tr("Streaming error")); - - if (m_state != QMediaRecorder::StoppedState) { - m_mediaDeviceSession->stopRecording(); - } -} - -void QWindowsMediaEncoder::onRecordingError(int errorCode) -{ - error(QMediaRecorder::ResourceError, tr("Recording error")); - - auto lastState = m_state; - m_state = QMediaRecorder::StoppedState; - if (m_state != lastState) - stateChanged(m_state); -} - -void QWindowsMediaEncoder::onCameraChanged() -{ -} - -void QWindowsMediaEncoder::onRecordingStarted() -{ -} - -void QWindowsMediaEncoder::onRecordingStopped() -{ - saveMetadata(); - - auto lastState = m_state; - m_state = QMediaRecorder::StoppedState; - if (m_state != lastState) - stateChanged(m_state); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/windows/mediacapture/qwindowsmediaencoder_p.h b/src/multimedia/platform/windows/mediacapture/qwindowsmediaencoder_p.h deleted file mode 100644 index c7b9d8931..000000000 --- a/src/multimedia/platform/windows/mediacapture/qwindowsmediaencoder_p.h +++ /dev/null @@ -1,106 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#ifndef QWINDOWSMEDIAENCODER_H -#define QWINDOWSMEDIAENCODER_H - -#include <private/qplatformmediaencoder_p.h> - -#include <QtCore/qglobal.h> -#include <QtCore/qurl.h> - -QT_BEGIN_NAMESPACE - -class QWindowsMediaDeviceSession; -class QPlatformMediaCaptureSession; -class QWindowsMediaCaptureService; - -class QWindowsMediaEncoder : public QObject, public QPlatformMediaEncoder -{ - Q_OBJECT -public: - explicit QWindowsMediaEncoder(QMediaRecorder *parent); - - bool isLocationWritable(const QUrl &location) const override; - QMediaRecorder::RecorderState state() const override; - qint64 duration() const override; - - void setMetaData(const QMediaMetaData &metaData) override; - QMediaMetaData metaData() const override; - - void setCaptureSession(QPlatformMediaCaptureSession *session); - - void record(QMediaEncoderSettings &settings) override; - void pause() override; - void resume() override; - void stop() override; - -private Q_SLOTS: - void onCameraChanged(); - void onRecordingStarted(); - void onRecordingStopped(); - void onDurationChanged(qint64 duration); - void onStreamingError(int errorCode); - void onRecordingError(int errorCode); - -private: - void saveMetadata(); - - QWindowsMediaCaptureService *m_captureService = nullptr; - QWindowsMediaDeviceSession *m_mediaDeviceSession = nullptr; - QMediaRecorder::RecorderState m_state = QMediaRecorder::StoppedState; - QString m_fileName; - QMediaMetaData m_metaData; - qint64 m_duration = 0; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/windows/mfstream.cpp b/src/multimedia/platform/windows/mfstream.cpp deleted file mode 100644 index b01dbb7b1..000000000 --- a/src/multimedia/platform/windows/mfstream.cpp +++ /dev/null @@ -1,361 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Mobility Components. -** -** $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$ -** -****************************************************************************/ - -#include "mfstream_p.h" -#include <QtCore/qcoreapplication.h> - -//MFStream is added for supporting QIODevice type of media source. -//It is used to delegate invocations from media foundation(through IMFByteStream) to QIODevice. - -MFStream::MFStream(QIODevice *stream, bool ownStream) - : m_cRef(1) - , m_stream(stream) - , m_ownStream(ownStream) - , m_currentReadResult(0) -{ - //Move to the thread of the stream object - //to make sure invocations on stream - //are happened in the same thread of stream object - this->moveToThread(stream->thread()); - connect(stream, SIGNAL(readyRead()), this, SLOT(handleReadyRead())); -} - -MFStream::~MFStream() -{ - if (m_currentReadResult) - m_currentReadResult->Release(); - if (m_ownStream) - m_stream->deleteLater(); -} - -//from IUnknown -STDMETHODIMP MFStream::QueryInterface(REFIID riid, LPVOID *ppvObject) -{ - if (!ppvObject) - return E_POINTER; - if (riid == IID_IMFByteStream) { - *ppvObject = static_cast<IMFByteStream*>(this); - } else if (riid == IID_IUnknown) { - *ppvObject = static_cast<IUnknown*>(this); - } else { - *ppvObject = NULL; - return E_NOINTERFACE; - } - AddRef(); - return S_OK; -} - -STDMETHODIMP_(ULONG) MFStream::AddRef(void) -{ - return InterlockedIncrement(&m_cRef); -} - -STDMETHODIMP_(ULONG) MFStream::Release(void) -{ - LONG cRef = InterlockedDecrement(&m_cRef); - if (cRef == 0) { - this->deleteLater(); - } - return cRef; -} - - -//from IMFByteStream -STDMETHODIMP MFStream::GetCapabilities(DWORD *pdwCapabilities) -{ - if (!pdwCapabilities) - return E_INVALIDARG; - *pdwCapabilities = MFBYTESTREAM_IS_READABLE; - if (!m_stream->isSequential()) - *pdwCapabilities |= MFBYTESTREAM_IS_SEEKABLE; - return S_OK; -} - -STDMETHODIMP MFStream::GetLength(QWORD *pqwLength) -{ - if (!pqwLength) - return E_INVALIDARG; - QMutexLocker locker(&m_mutex); - *pqwLength = QWORD(m_stream->size()); - return S_OK; -} - -STDMETHODIMP MFStream::SetLength(QWORD) -{ - return E_NOTIMPL; -} - -STDMETHODIMP MFStream::GetCurrentPosition(QWORD *pqwPosition) -{ - if (!pqwPosition) - return E_INVALIDARG; - QMutexLocker locker(&m_mutex); - *pqwPosition = m_stream->pos(); - return S_OK; -} - -STDMETHODIMP MFStream::SetCurrentPosition(QWORD qwPosition) -{ - QMutexLocker locker(&m_mutex); - //SetCurrentPosition may happend during the BeginRead/EndRead pair, - //refusing to execute SetCurrentPosition during that time seems to be - //the simplest workable solution - if (m_currentReadResult) - return S_FALSE; - - bool seekOK = m_stream->seek(qint64(qwPosition)); - if (seekOK) - return S_OK; - else - return S_FALSE; -} - -STDMETHODIMP MFStream::IsEndOfStream(BOOL *pfEndOfStream) -{ - if (!pfEndOfStream) - return E_INVALIDARG; - QMutexLocker locker(&m_mutex); - *pfEndOfStream = m_stream->atEnd() ? TRUE : FALSE; - return S_OK; -} - -STDMETHODIMP MFStream::Read(BYTE *pb, ULONG cb, ULONG *pcbRead) -{ - QMutexLocker locker(&m_mutex); - qint64 read = m_stream->read((char*)(pb), qint64(cb)); - if (pcbRead) - *pcbRead = ULONG(read); - return S_OK; -} - -STDMETHODIMP MFStream::BeginRead(BYTE *pb, ULONG cb, IMFAsyncCallback *pCallback, - IUnknown *punkState) -{ - if (!pCallback || !pb) - return E_INVALIDARG; - - Q_ASSERT(m_currentReadResult == NULL); - - AsyncReadState *state = new (std::nothrow) AsyncReadState(pb, cb); - if (state == NULL) - return E_OUTOFMEMORY; - - HRESULT hr = MFCreateAsyncResult(state, pCallback, punkState, &m_currentReadResult); - state->Release(); - if (FAILED(hr)) - return hr; - - QCoreApplication::postEvent(this, new QEvent(QEvent::User)); - return hr; -} - -STDMETHODIMP MFStream::EndRead(IMFAsyncResult* pResult, ULONG *pcbRead) -{ - if (!pcbRead) - return E_INVALIDARG; - IUnknown *pUnk; - pResult->GetObject(&pUnk); - AsyncReadState *state = static_cast<AsyncReadState*>(pUnk); - *pcbRead = state->bytesRead(); - pUnk->Release(); - - m_currentReadResult->Release(); - m_currentReadResult = NULL; - - return S_OK; -} - -STDMETHODIMP MFStream::Write(const BYTE *, ULONG, ULONG *) -{ - return E_NOTIMPL; -} - -STDMETHODIMP MFStream::BeginWrite(const BYTE *, ULONG , - IMFAsyncCallback *, - IUnknown *) -{ - return E_NOTIMPL; -} - -STDMETHODIMP MFStream::EndWrite(IMFAsyncResult *, - ULONG *) -{ - return E_NOTIMPL; -} - -STDMETHODIMP MFStream::Seek( - MFBYTESTREAM_SEEK_ORIGIN SeekOrigin, - LONGLONG llSeekOffset, - DWORD, - QWORD *pqwCurrentPosition) -{ - QMutexLocker locker(&m_mutex); - if (m_currentReadResult) - return S_FALSE; - - qint64 pos = qint64(llSeekOffset); - switch (SeekOrigin) { - case msoBegin: - break; - case msoCurrent: - pos += m_stream->pos(); - break; - } - bool seekOK = m_stream->seek(pos); - if (pqwCurrentPosition) - *pqwCurrentPosition = pos; - if (seekOK) - return S_OK; - else - return S_FALSE; -} - -STDMETHODIMP MFStream::Flush() -{ - return E_NOTIMPL; -} - -STDMETHODIMP MFStream::Close() -{ - QMutexLocker locker(&m_mutex); - if (m_ownStream) - m_stream->close(); - return S_OK; -} - -void MFStream::doRead() -{ - bool readDone = true; - IUnknown *pUnk = NULL; - HRESULT hr = m_currentReadResult->GetObject(&pUnk); - if (SUCCEEDED(hr)) { - //do actual read - AsyncReadState *state = static_cast<AsyncReadState*>(pUnk); - ULONG cbRead; - Read(state->pb(), state->cb() - state->bytesRead(), &cbRead); - pUnk->Release(); - - state->setBytesRead(cbRead + state->bytesRead()); - if (state->cb() > state->bytesRead() && !m_stream->atEnd()) { - readDone = false; - } - } - - if (readDone) { - //now inform the original caller - m_currentReadResult->SetStatus(hr); - MFInvokeCallback(m_currentReadResult); - } -} - - -void MFStream::handleReadyRead() -{ - doRead(); -} - -void MFStream::customEvent(QEvent *event) -{ - if (event->type() != QEvent::User) { - QObject::customEvent(event); - return; - } - doRead(); -} - -//AsyncReadState is a helper class used in BeginRead for asynchronous operation -//to record some BeginRead parameters, so these parameters could be -//used later when actually executing the read operation in another thread. -MFStream::AsyncReadState::AsyncReadState(BYTE *pb, ULONG cb) - : m_cRef(1) - , m_pb(pb) - , m_cb(cb) - , m_cbRead(0) -{ -} - -//from IUnknown -STDMETHODIMP MFStream::AsyncReadState::QueryInterface(REFIID riid, LPVOID *ppvObject) -{ - if (!ppvObject) - return E_POINTER; - - if (riid == IID_IUnknown) { - *ppvObject = static_cast<IUnknown*>(this); - } else { - *ppvObject = NULL; - return E_NOINTERFACE; - } - AddRef(); - return S_OK; -} - -STDMETHODIMP_(ULONG) MFStream::AsyncReadState::AddRef(void) -{ - return InterlockedIncrement(&m_cRef); -} - -STDMETHODIMP_(ULONG) MFStream::AsyncReadState::Release(void) -{ - LONG cRef = InterlockedDecrement(&m_cRef); - if (cRef == 0) - delete this; - // For thread safety, return a temporary variable. - return cRef; -} - -BYTE* MFStream::AsyncReadState::pb() const -{ - return m_pb; -} - -ULONG MFStream::AsyncReadState::cb() const -{ - return m_cb; -} - -ULONG MFStream::AsyncReadState::bytesRead() const -{ - return m_cbRead; -} - -void MFStream::AsyncReadState::setBytesRead(ULONG cbRead) -{ - m_cbRead = cbRead; -} diff --git a/src/multimedia/platform/windows/mfstream_p.h b/src/multimedia/platform/windows/mfstream_p.h deleted file mode 100644 index 975d02c9d..000000000 --- a/src/multimedia/platform/windows/mfstream_p.h +++ /dev/null @@ -1,159 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Mobility Components. -** -** $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$ -** -****************************************************************************/ - -#ifndef MFSTREAM_H -#define MFSTREAM_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <mfapi.h> -#include <mfidl.h> -#include <QtCore/qmutex.h> -#include <QtCore/qiodevice.h> -#include <QtCore/qcoreevent.h> - -QT_USE_NAMESPACE - -class MFStream : public QObject, public IMFByteStream -{ - Q_OBJECT -public: - MFStream(QIODevice *stream, bool ownStream); - - ~MFStream(); - - //from IUnknown - STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject); - - STDMETHODIMP_(ULONG) AddRef(void); - - STDMETHODIMP_(ULONG) Release(void); - - - //from IMFByteStream - STDMETHODIMP GetCapabilities(DWORD *pdwCapabilities); - - STDMETHODIMP GetLength(QWORD *pqwLength); - - STDMETHODIMP SetLength(QWORD); - - STDMETHODIMP GetCurrentPosition(QWORD *pqwPosition); - - STDMETHODIMP SetCurrentPosition(QWORD qwPosition); - - STDMETHODIMP IsEndOfStream(BOOL *pfEndOfStream); - - STDMETHODIMP Read(BYTE *pb, ULONG cb, ULONG *pcbRead); - - STDMETHODIMP BeginRead(BYTE *pb, ULONG cb, IMFAsyncCallback *pCallback, - IUnknown *punkState); - - STDMETHODIMP EndRead(IMFAsyncResult* pResult, ULONG *pcbRead); - - STDMETHODIMP Write(const BYTE *, ULONG, ULONG *); - - STDMETHODIMP BeginWrite(const BYTE *, ULONG , - IMFAsyncCallback *, - IUnknown *); - - STDMETHODIMP EndWrite(IMFAsyncResult *, - ULONG *); - - STDMETHODIMP Seek( - MFBYTESTREAM_SEEK_ORIGIN SeekOrigin, - LONGLONG llSeekOffset, - DWORD, - QWORD *pqwCurrentPosition); - - STDMETHODIMP Flush(); - - STDMETHODIMP Close(); - -private: - class AsyncReadState : public IUnknown - { - public: - AsyncReadState(BYTE *pb, ULONG cb); - - //from IUnknown - STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject); - - STDMETHODIMP_(ULONG) AddRef(void); - - STDMETHODIMP_(ULONG) Release(void); - - BYTE* pb() const; - ULONG cb() const; - ULONG bytesRead() const; - - void setBytesRead(ULONG cbRead); - - private: - long m_cRef; - BYTE *m_pb; - ULONG m_cb; - ULONG m_cbRead; - }; - - long m_cRef; - QIODevice *m_stream; - bool m_ownStream; - DWORD m_workQueueId; - QMutex m_mutex; - - void doRead(); - -private Q_SLOTS: - void handleReadyRead(); - -protected: - void customEvent(QEvent *event); - IMFAsyncResult *m_currentReadResult; -}; - -#endif diff --git a/src/multimedia/platform/windows/player/mfactivate.cpp b/src/multimedia/platform/windows/player/mfactivate.cpp deleted file mode 100644 index 05d9321be..000000000 --- a/src/multimedia/platform/windows/player/mfactivate.cpp +++ /dev/null @@ -1,87 +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$ -** -****************************************************************************/ - -#include "mfactivate_p.h" - -#include <mfapi.h> - -MFAbstractActivate::MFAbstractActivate() - : m_attributes(0) - , m_cRef(1) -{ - MFCreateAttributes(&m_attributes, 0); -} - -MFAbstractActivate::~MFAbstractActivate() -{ - if (m_attributes) - m_attributes->Release(); -} - - -HRESULT MFAbstractActivate::QueryInterface(REFIID riid, LPVOID *ppvObject) -{ - if (!ppvObject) - return E_POINTER; - if (riid == IID_IMFActivate) { - *ppvObject = static_cast<IMFActivate*>(this); - } else if (riid == IID_IMFAttributes) { - *ppvObject = static_cast<IMFAttributes*>(this); - } else if (riid == IID_IUnknown) { - *ppvObject = static_cast<IUnknown*>(static_cast<IMFActivate*>(this)); - } else { - *ppvObject = NULL; - return E_NOINTERFACE; - } - AddRef(); - return S_OK; -} - -ULONG MFAbstractActivate::AddRef(void) -{ - return InterlockedIncrement(&m_cRef); -} - -ULONG MFAbstractActivate::Release(void) -{ - ULONG cRef = InterlockedDecrement(&m_cRef); - if (cRef == 0) - delete this; - return cRef; -} diff --git a/src/multimedia/platform/windows/player/mfactivate_p.h b/src/multimedia/platform/windows/player/mfactivate_p.h deleted file mode 100644 index 86ef1c438..000000000 --- a/src/multimedia/platform/windows/player/mfactivate_p.h +++ /dev/null @@ -1,223 +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$ -** -****************************************************************************/ - -#ifndef MFACTIVATE_H -#define MFACTIVATE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <mfidl.h> - -class MFAbstractActivate : public IMFActivate -{ -public: - explicit MFAbstractActivate(); - virtual ~MFAbstractActivate(); - - //from IUnknown - STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject); - STDMETHODIMP_(ULONG) AddRef(void); - STDMETHODIMP_(ULONG) Release(void); - - //from IMFAttributes - STDMETHODIMP GetItem(REFGUID guidKey, PROPVARIANT *pValue) - { - return m_attributes->GetItem(guidKey, pValue); - } - - STDMETHODIMP GetItemType(REFGUID guidKey, MF_ATTRIBUTE_TYPE *pType) - { - return m_attributes->GetItemType(guidKey, pType); - } - - STDMETHODIMP CompareItem(REFGUID guidKey, REFPROPVARIANT Value, BOOL *pbResult) - { - return m_attributes->CompareItem(guidKey, Value, pbResult); - } - - STDMETHODIMP Compare(IMFAttributes *pTheirs, MF_ATTRIBUTES_MATCH_TYPE MatchType, BOOL *pbResult) - { - return m_attributes->Compare(pTheirs, MatchType, pbResult); - } - - STDMETHODIMP GetUINT32(REFGUID guidKey, UINT32 *punValue) - { - return m_attributes->GetUINT32(guidKey, punValue); - } - - STDMETHODIMP GetUINT64(REFGUID guidKey, UINT64 *punValue) - { - return m_attributes->GetUINT64(guidKey, punValue); - } - - STDMETHODIMP GetDouble(REFGUID guidKey, double *pfValue) - { - return m_attributes->GetDouble(guidKey, pfValue); - } - - STDMETHODIMP GetGUID(REFGUID guidKey, GUID *pguidValue) - { - return m_attributes->GetGUID(guidKey, pguidValue); - } - - STDMETHODIMP GetStringLength(REFGUID guidKey, UINT32 *pcchLength) - { - return m_attributes->GetStringLength(guidKey, pcchLength); - } - - STDMETHODIMP GetString(REFGUID guidKey, LPWSTR pwszValue, UINT32 cchBufSize, UINT32 *pcchLength) - { - return m_attributes->GetString(guidKey, pwszValue, cchBufSize, pcchLength); - } - - STDMETHODIMP GetAllocatedString(REFGUID guidKey, LPWSTR *ppwszValue, UINT32 *pcchLength) - { - return m_attributes->GetAllocatedString(guidKey, ppwszValue, pcchLength); - } - - STDMETHODIMP GetBlobSize(REFGUID guidKey, UINT32 *pcbBlobSize) - { - return m_attributes->GetBlobSize(guidKey, pcbBlobSize); - } - - STDMETHODIMP GetBlob(REFGUID guidKey, UINT8 *pBuf, UINT32 cbBufSize, UINT32 *pcbBlobSize) - { - return m_attributes->GetBlob(guidKey, pBuf, cbBufSize, pcbBlobSize); - } - - STDMETHODIMP GetAllocatedBlob(REFGUID guidKey, UINT8 **ppBuf, UINT32 *pcbSize) - { - return m_attributes->GetAllocatedBlob(guidKey, ppBuf, pcbSize); - } - - STDMETHODIMP GetUnknown(REFGUID guidKey, REFIID riid, LPVOID *ppv) - { - return m_attributes->GetUnknown(guidKey, riid, ppv); - } - - STDMETHODIMP SetItem(REFGUID guidKey, REFPROPVARIANT Value) - { - return m_attributes->SetItem(guidKey, Value); - } - - STDMETHODIMP DeleteItem(REFGUID guidKey) - { - return m_attributes->DeleteItem(guidKey); - } - - STDMETHODIMP DeleteAllItems() - { - return m_attributes->DeleteAllItems(); - } - - STDMETHODIMP SetUINT32(REFGUID guidKey, UINT32 unValue) - { - return m_attributes->SetUINT32(guidKey, unValue); - } - - STDMETHODIMP SetUINT64(REFGUID guidKey, UINT64 unValue) - { - return m_attributes->SetUINT64(guidKey, unValue); - } - - STDMETHODIMP SetDouble(REFGUID guidKey, double fValue) - { - return m_attributes->SetDouble(guidKey, fValue); - } - - STDMETHODIMP SetGUID(REFGUID guidKey, REFGUID guidValue) - { - return m_attributes->SetGUID(guidKey, guidValue); - } - - STDMETHODIMP SetString(REFGUID guidKey, LPCWSTR wszValue) - { - return m_attributes->SetString(guidKey, wszValue); - } - - STDMETHODIMP SetBlob(REFGUID guidKey, const UINT8 *pBuf, UINT32 cbBufSize) - { - return m_attributes->SetBlob(guidKey, pBuf, cbBufSize); - } - - STDMETHODIMP SetUnknown(REFGUID guidKey, IUnknown *pUnknown) - { - return m_attributes->SetUnknown(guidKey, pUnknown); - } - - STDMETHODIMP LockStore() - { - return m_attributes->LockStore(); - } - - STDMETHODIMP UnlockStore() - { - return m_attributes->UnlockStore(); - } - - STDMETHODIMP GetCount(UINT32 *pcItems) - { - return m_attributes->GetCount(pcItems); - } - - STDMETHODIMP GetItemByIndex(UINT32 unIndex, GUID *pguidKey, PROPVARIANT *pValue) - { - return m_attributes->GetItemByIndex(unIndex, pguidKey, pValue); - } - - STDMETHODIMP CopyAllItems(IMFAttributes *pDest) - { - return m_attributes->CopyAllItems(pDest); - } - -private: - IMFAttributes *m_attributes; - ULONG m_cRef; -}; - -#endif // MFACTIVATE_H diff --git a/src/multimedia/platform/windows/player/mfevrvideowindowcontrol.cpp b/src/multimedia/platform/windows/player/mfevrvideowindowcontrol.cpp deleted file mode 100644 index 105424253..000000000 --- a/src/multimedia/platform/windows/player/mfevrvideowindowcontrol.cpp +++ /dev/null @@ -1,91 +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$ -** -****************************************************************************/ - -#include "mfevrvideowindowcontrol_p.h" - -#include <qdebug.h> - -MFEvrVideoWindowControl::MFEvrVideoWindowControl(QVideoSink *parent) - : EvrVideoWindowControl(parent) - , m_currentActivate(NULL) - , m_evrSink(NULL) -{ -} - -MFEvrVideoWindowControl::~MFEvrVideoWindowControl() -{ - clear(); -} - -void MFEvrVideoWindowControl::clear() -{ - setEvr(NULL); - - if (m_evrSink) - m_evrSink->Release(); - if (m_currentActivate) { - m_currentActivate->ShutdownObject(); - m_currentActivate->Release(); - } - m_evrSink = NULL; - m_currentActivate = NULL; -} - -IMFActivate* MFEvrVideoWindowControl::createActivate() -{ - clear(); - - if (FAILED(MFCreateVideoRendererActivate(0, &m_currentActivate))) { - qWarning() << "Failed to create evr video renderer activate!"; - return NULL; - } - if (FAILED(m_currentActivate->ActivateObject(IID_IMFMediaSink, (LPVOID*)(&m_evrSink)))) { - qWarning() << "Failed to activate evr media sink!"; - return NULL; - } - if (!setEvr(m_evrSink)) - return NULL; - - return m_currentActivate; -} - -void MFEvrVideoWindowControl::releaseActivate() -{ - clear(); -} diff --git a/src/multimedia/platform/windows/player/mfevrvideowindowcontrol_p.h b/src/multimedia/platform/windows/player/mfevrvideowindowcontrol_p.h deleted file mode 100644 index 2f9d867d6..000000000 --- a/src/multimedia/platform/windows/player/mfevrvideowindowcontrol_p.h +++ /dev/null @@ -1,74 +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$ -** -****************************************************************************/ - -#ifndef MFEVRVIDEOWINDOWCONTROL_H -#define MFEVRVIDEOWINDOWCONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "private/evrvideowindowcontrol_p.h" - -QT_USE_NAMESPACE - -class MFEvrVideoWindowControl : public EvrVideoWindowControl -{ -public: - MFEvrVideoWindowControl(QVideoSink *parent = 0); - ~MFEvrVideoWindowControl(); - - IMFActivate* createActivate(); - void releaseActivate(); - -private: - void clear(); - - IMFActivate *m_currentActivate; - IMFMediaSink *m_evrSink; -}; - -#endif // MFEVRVIDEOWINDOWCONTROL_H diff --git a/src/multimedia/platform/windows/player/mfplayercontrol.cpp b/src/multimedia/platform/windows/player/mfplayercontrol.cpp deleted file mode 100644 index 47e74ab38..000000000 --- a/src/multimedia/platform/windows/player/mfplayercontrol.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Mobility Components. -** -** $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$ -** -****************************************************************************/ - -#include "mfplayercontrol_p.h" -#include "mfplayersession_p.h" -#include "mfvideorenderercontrol_p.h" -#include <qdebug.h> - -//#define DEBUG_MEDIAFOUNDATION - -MFPlayerControl::MFPlayerControl(QMediaPlayer *player) - : QPlatformMediaPlayer(player) - , m_state(QMediaPlayer::StoppedState) - , m_stateDirty(false) - , m_videoAvailable(false) - , m_audioAvailable(false) - , m_duration(0) - , m_seekable(false) -{ - m_session = new MFPlayerSession(this); -} - -MFPlayerControl::~MFPlayerControl() -{ - m_session->close(); - m_session->Release(); -} - -void MFPlayerControl::setMedia(const QUrl &media, QIODevice *stream) -{ - if (m_state != QMediaPlayer::StoppedState) { - changeState(QMediaPlayer::StoppedState); - m_session->stop(true); - refreshState(); - } - - m_media = media; - m_stream = stream; - resetAudioVideoAvailable(); - handleDurationUpdate(0); - handleSeekableUpdate(false); - m_session->load(media, stream); -} - -void MFPlayerControl::play() -{ - if (m_state == QMediaPlayer::PlayingState) - return; - if (QMediaPlayer::InvalidMedia == m_session->status()) - m_session->load(m_media, m_stream); - - switch (m_session->status()) { - case QMediaPlayer::NoMedia: - case QMediaPlayer::InvalidMedia: - return; - case QMediaPlayer::LoadedMedia: - case QMediaPlayer::BufferingMedia: - case QMediaPlayer::BufferedMedia: - case QMediaPlayer::EndOfMedia: - changeState(QMediaPlayer::PlayingState); - m_session->start(); - break; - default: //Loading/Stalled - changeState(QMediaPlayer::PlayingState); - break; - } - refreshState(); -} - -void MFPlayerControl::pause() -{ - if (m_state != QMediaPlayer::PlayingState) - return; - changeState(QMediaPlayer::PausedState); - m_session->pause(); - refreshState(); -} - -void MFPlayerControl::stop() -{ - if (m_state == QMediaPlayer::StoppedState) - return; - changeState(QMediaPlayer::StoppedState); - m_session->stop(); - refreshState(); -} - -QMediaMetaData MFPlayerControl::metaData() const -{ - return m_session->metaData(); -} - -void MFPlayerControl::setAudioOutput(QPlatformAudioOutput *output) -{ - m_session->setAudioOutput(output); -} - -void MFPlayerControl::setVideoSink(QVideoSink *sink) -{ - m_session->setVideoSink(sink); -} - -void MFPlayerControl::changeState(QMediaPlayer::PlaybackState state) -{ - if (m_state == state) - return; - m_state = state; - m_stateDirty = true; -} - -void MFPlayerControl::refreshState() -{ - if (!m_stateDirty) - return; - m_stateDirty = false; -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "MFPlayerControl::emit stateChanged" << m_state; -#endif - emit stateChanged(m_state); -} - -void MFPlayerControl::handleStatusChanged() -{ - QMediaPlayer::MediaStatus status = m_session->status(); - switch (status) { - case QMediaPlayer::EndOfMedia: - changeState(QMediaPlayer::StoppedState); - break; - case QMediaPlayer::InvalidMedia: - break; - case QMediaPlayer::LoadedMedia: - case QMediaPlayer::BufferingMedia: - case QMediaPlayer::BufferedMedia: - if (m_state == QMediaPlayer::PlayingState) - m_session->start(); - break; - } - emit mediaStatusChanged(m_session->status()); - refreshState(); -} - -void MFPlayerControl::handleTracksChanged() -{ - tracksChanged(); -} - -void MFPlayerControl::handleVideoAvailable() -{ - if (m_videoAvailable) - return; - m_videoAvailable = true; - emit videoAvailableChanged(m_videoAvailable); -} - -void MFPlayerControl::handleAudioAvailable() -{ - if (m_audioAvailable) - return; - m_audioAvailable = true; - emit audioAvailableChanged(m_audioAvailable); -} - -void MFPlayerControl::resetAudioVideoAvailable() -{ - bool videoDirty = false; - if (m_videoAvailable) { - m_videoAvailable = false; - videoDirty = true; - } - if (m_audioAvailable) { - m_audioAvailable = false; - emit audioAvailableChanged(m_audioAvailable); - } - if (videoDirty) - emit videoAvailableChanged(m_videoAvailable); -} - -void MFPlayerControl::handleDurationUpdate(qint64 duration) -{ - if (m_duration == duration) - return; - m_duration = duration; - emit durationChanged(m_duration); -} - -void MFPlayerControl::handleSeekableUpdate(bool seekable) -{ - if (m_seekable == seekable) - return; - m_seekable = seekable; - emit seekableChanged(m_seekable); -} - -QMediaPlayer::PlaybackState MFPlayerControl::state() const -{ - return m_state; -} - -QMediaPlayer::MediaStatus MFPlayerControl::mediaStatus() const -{ - return m_session->status(); -} - -qint64 MFPlayerControl::duration() const -{ - return m_duration; -} - -qint64 MFPlayerControl::position() const -{ - return m_session->position(); -} - -void MFPlayerControl::setPosition(qint64 position) -{ - if (!m_seekable || position == m_session->position()) - return; - m_session->setPosition(position); -} - -float MFPlayerControl::bufferProgress() const -{ - return m_session->bufferProgress() / 100.; -} - -bool MFPlayerControl::isAudioAvailable() const -{ - return m_audioAvailable; -} - -bool MFPlayerControl::isVideoAvailable() const -{ - return m_videoAvailable; -} - -bool MFPlayerControl::isSeekable() const -{ - return m_seekable; -} - -QMediaTimeRange MFPlayerControl::availablePlaybackRanges() const -{ - return m_session->availablePlaybackRanges(); -} - -qreal MFPlayerControl::playbackRate() const -{ - return m_session->playbackRate(); -} - -void MFPlayerControl::setPlaybackRate(qreal rate) -{ - m_session->setPlaybackRate(rate); -} - -QUrl MFPlayerControl::media() const -{ - return m_media; -} - -const QIODevice* MFPlayerControl::mediaStream() const -{ - return m_stream; -} - -void MFPlayerControl::handleError(QMediaPlayer::Error errorCode, const QString& errorString, bool isFatal) -{ - if (isFatal) - stop(); - emit error(int(errorCode), errorString); -} - -void MFPlayerControl::setActiveTrack(TrackType type, int index) -{ - m_session->setActiveTrack(type, index); -} - -int MFPlayerControl::activeTrack(TrackType type) -{ - return m_session->activeTrack(type); -} - -int MFPlayerControl::trackCount(TrackType type) -{ - return m_session->trackCount(type); -} - -QMediaMetaData MFPlayerControl::trackMetaData(TrackType type, int trackNumber) -{ - return m_session->trackMetaData(type, trackNumber); -} - diff --git a/src/multimedia/platform/windows/player/mfplayercontrol_p.h b/src/multimedia/platform/windows/player/mfplayercontrol_p.h deleted file mode 100644 index e8467ab8d..000000000 --- a/src/multimedia/platform/windows/player/mfplayercontrol_p.h +++ /dev/null @@ -1,139 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Mobility Components. -** -** $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$ -** -****************************************************************************/ - -#ifndef MFPLAYERCONTROL_H -#define MFPLAYERCONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "QUrl.h" -#include "qplatformmediaplayer_p.h" - -#include <QtCore/qcoreevent.h> - -QT_USE_NAMESPACE - -class MFPlayerSession; - -class MFPlayerControl : public QPlatformMediaPlayer -{ -public: - MFPlayerControl(QMediaPlayer *player); - ~MFPlayerControl(); - - QMediaPlayer::PlaybackState state() const override; - - QMediaPlayer::MediaStatus mediaStatus() const override; - - qint64 duration() const override; - - qint64 position() const override; - void setPosition(qint64 position) override; - - float bufferProgress() const override; - - bool isAudioAvailable() const override; - bool isVideoAvailable() const override; - - bool isSeekable() const override; - - QMediaTimeRange availablePlaybackRanges() const override; - - qreal playbackRate() const override; - void setPlaybackRate(qreal rate) override; - - QUrl media() const override; - const QIODevice *mediaStream() const override; - void setMedia(const QUrl &media, QIODevice *stream) override; - - void play() override; - void pause() override; - void stop() override; - - bool streamPlaybackSupported() const override { return true; } - - QMediaMetaData metaData() const override; - - void setAudioOutput(QPlatformAudioOutput *output) override; - - void setVideoSink(QVideoSink *sink) override; - - void setActiveTrack(TrackType type, int index) override; - int activeTrack(TrackType type) override; - int trackCount(TrackType type) override; - QMediaMetaData trackMetaData(TrackType type, int trackNumber) override; - - void handleStatusChanged(); - void handleTracksChanged(); - void handleVideoAvailable(); - void handleAudioAvailable(); - void handleDurationUpdate(qint64 duration); - void handleSeekableUpdate(bool seekable); - void handleError(QMediaPlayer::Error errorCode, const QString& errorString, bool isFatal); - -private: - void changeState(QMediaPlayer::PlaybackState state); - void resetAudioVideoAvailable(); - void refreshState(); - - QMediaPlayer::PlaybackState m_state; - bool m_stateDirty; - QMediaPlayer::MediaStatus m_status; - QMediaPlayer::Error m_error; - - bool m_videoAvailable; - bool m_audioAvailable; - qint64 m_duration; - bool m_seekable; - - QIODevice *m_stream; - QUrl m_media; - MFPlayerSession *m_session; -}; - -#endif diff --git a/src/multimedia/platform/windows/player/mfplayersession.cpp b/src/multimedia/platform/windows/player/mfplayersession.cpp deleted file mode 100644 index aeeb7c792..000000000 --- a/src/multimedia/platform/windows/player/mfplayersession.cpp +++ /dev/null @@ -1,2009 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Mobility Components. -** -** $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$ -** -****************************************************************************/ - -#include "qplatformmediaplayer_p.h" - -#include <QtCore/qcoreapplication.h> -#include <QtCore/qdatetime.h> -#include <QtCore/qthread.h> -#include <QtCore/qvarlengtharray.h> -#include <QtCore/qdebug.h> -#include <QtCore/qfile.h> -#include <QtCore/qbuffer.h> - -#include "qplatformaudiooutput_p.h" -#include "qaudiooutput.h" - -#include "mfplayercontrol_p.h" -#include "mfevrvideowindowcontrol_p.h" -#include "mfvideorenderercontrol_p.h" -#include <private/mfmetadata_p.h> - -#include "mfplayersession_p.h" -#include <mferror.h> -#include <nserror.h> -#include "private/sourceresolver_p.h" -#include "samplegrabber_p.h" -#include "mftvideo_p.h" -#include <wmcodecdsp.h> - -#include <mmdeviceapi.h> -#include <propvarutil.h> -#include <Functiondiscoverykeys_devpkey.h> - -//#define DEBUG_MEDIAFOUNDATION - -MFPlayerSession::MFPlayerSession(MFPlayerControl *playerControl) - : m_cRef(1) - , m_playerControl(playerControl) - , m_session(0) - , m_presentationClock(0) - , m_rateControl(0) - , m_rateSupport(0) - , m_volumeControl(0) - , m_netsourceStatistics(0) - , m_scrubbing(false) - , m_restoreRate(1) - , m_sourceResolver(0) - , m_hCloseEvent(0) - , m_closing(false) - , m_mediaTypes(0) - , m_pendingRate(1) - , m_status(QMediaPlayer::NoMedia) - , m_audioSampleGrabber(0) - , m_audioSampleGrabberNode(0) - , m_videoProbeMFT(0) - -{ - QObject::connect(this, SIGNAL(sessionEvent(IMFMediaEvent*)), this, SLOT(handleSessionEvent(IMFMediaEvent*))); - - m_signalPositionChangeTimer.setInterval(100); - m_signalPositionChangeTimer.callOnTimeout([this](){ - positionChanged(position()); - }); - - m_pendingState = NoPending; - ZeroMemory(&m_state, sizeof(m_state)); - m_state.command = CmdStop; - m_state.prevCmd = CmdNone; - m_state.rate = 1.0f; - ZeroMemory(&m_request, sizeof(m_request)); - m_request.command = CmdNone; - m_request.prevCmd = CmdNone; - m_request.rate = 1.0f; - - m_audioSampleGrabber = new AudioSampleGrabberCallback; - m_videoRendererControl = new MFVideoRendererControl; -} - -void MFPlayerSession::close() -{ -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "close"; -#endif - - clear(); - if (!m_session) - return; - - HRESULT hr = S_OK; - if (m_session) { - m_closing = true; - hr = m_session->Close(); - if (SUCCEEDED(hr)) { - DWORD dwWaitResult = WaitForSingleObject(m_hCloseEvent, 100); - if (dwWaitResult == WAIT_TIMEOUT) { - qWarning() << "session close time out!"; - } - } - m_closing = false; - } - - if (SUCCEEDED(hr)) { - if (m_session) - m_session->Shutdown(); - if (m_sourceResolver) - m_sourceResolver->shutdown(); - } - if (m_sourceResolver) { - m_sourceResolver->Release(); - m_sourceResolver = 0; - } - if (m_videoProbeMFT) { - m_videoProbeMFT->Release(); - m_videoProbeMFT = 0; - } - - m_videoRendererControl->releaseActivate(); -// } else if (m_playerService->videoWindowControl()) { -// m_playerService->videoWindowControl()->releaseActivate(); -// } - - if (m_session) - m_session->Release(); - m_session = 0; - if (m_hCloseEvent) - CloseHandle(m_hCloseEvent); - m_hCloseEvent = 0; -} - -MFPlayerSession::~MFPlayerSession() -{ - m_audioSampleGrabber->Release(); -} - - -void MFPlayerSession::load(const QUrl &url, QIODevice *stream) -{ -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "load"; -#endif - clear(); - - if (m_status == QMediaPlayer::LoadingMedia && m_sourceResolver) - m_sourceResolver->cancel(); - - if (url.isEmpty() && !stream) { - close(); - changeStatus(QMediaPlayer::NoMedia); - } else if (stream && (!stream->isReadable())) { - close(); - changeStatus(QMediaPlayer::InvalidMedia); - emit error(QMediaPlayer::ResourceError, tr("Invalid stream source."), true); - } else { - createSession(); - changeStatus(QMediaPlayer::LoadingMedia); - m_sourceResolver->load(url, stream); - } - emit positionChanged(position()); -} - -void MFPlayerSession::handleSourceError(long hr) -{ - QString errorString; - QMediaPlayer::Error errorCode = QMediaPlayer::ResourceError; - switch (hr) { - case QMediaPlayer::FormatError: - errorCode = QMediaPlayer::FormatError; - errorString = tr("Attempting to play invalid Qt resource."); - break; - case NS_E_FILE_NOT_FOUND: - errorString = tr("The system cannot find the file specified."); - break; - case NS_E_SERVER_NOT_FOUND: - errorString = tr("The specified server could not be found."); - break; - case MF_E_UNSUPPORTED_BYTESTREAM_TYPE: - errorCode = QMediaPlayer::FormatError; - errorString = tr("Unsupported media type."); - break; - default: - errorString = tr("Failed to load source."); - break; - } - changeStatus(QMediaPlayer::InvalidMedia); - emit error(errorCode, errorString, true); -} - -void MFPlayerSession::handleMediaSourceReady() -{ - if (QMediaPlayer::LoadingMedia != m_status || !m_sourceResolver || m_sourceResolver != sender()) - return; -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "handleMediaSourceReady"; -#endif - HRESULT hr = S_OK; - IMFMediaSource* mediaSource = m_sourceResolver->mediaSource(); - - DWORD dwCharacteristics = 0; - mediaSource->GetCharacteristics(&dwCharacteristics); - emit seekableUpdate(MFMEDIASOURCE_CAN_SEEK & dwCharacteristics); - - IMFPresentationDescriptor* sourcePD; - hr = mediaSource->CreatePresentationDescriptor(&sourcePD); - if (SUCCEEDED(hr)) { - m_duration = 0; - m_metaData = MFMetaData::fromNative(mediaSource); - emit metaDataChanged(); - sourcePD->GetUINT64(MF_PD_DURATION, &m_duration); - //convert from 100 nanosecond to milisecond - emit durationUpdate(qint64(m_duration / 10000)); - setupPlaybackTopology(mediaSource, sourcePD); - tracksChanged(); - sourcePD->Release(); - } else { - changeStatus(QMediaPlayer::InvalidMedia); - emit error(QMediaPlayer::ResourceError, tr("Cannot create presentation descriptor."), true); - } -} - -bool MFPlayerSession::getStreamInfo(IMFStreamDescriptor *stream, - MFPlayerSession::MediaType *type, - QString *name, - QString *language) const -{ - if (!stream || !type || !name || !language) - return false; - - *type = Unknown; - *name = QString(); - *language = QString(); - - IMFMediaTypeHandler *typeHandler = nullptr; - - if (SUCCEEDED(stream->GetMediaTypeHandler(&typeHandler))) { - - UINT32 len = 0; - if (SUCCEEDED(stream->GetStringLength(MF_SD_STREAM_NAME, &len)) && len > 0) { - WCHAR *wstr = new WCHAR[len+1]; - if (SUCCEEDED(stream->GetString(MF_SD_STREAM_NAME, wstr, len+1, &len))) { - *name = QString::fromUtf16(reinterpret_cast<const char16_t *>(wstr)); - } - delete []wstr; - } - if (SUCCEEDED(stream->GetStringLength(MF_SD_LANGUAGE, &len)) && len > 0) { - WCHAR *wstr = new WCHAR[len+1]; - if (SUCCEEDED(stream->GetString(MF_SD_LANGUAGE, wstr, len+1, &len))) { - *language = QString::fromUtf16(reinterpret_cast<const char16_t *>(wstr)); - } - delete []wstr; - } - - GUID guidMajorType; - if (SUCCEEDED(typeHandler->GetMajorType(&guidMajorType))) { - if (guidMajorType == MFMediaType_Audio) - *type = Audio; - else if (guidMajorType == MFMediaType_Video) - *type = Video; - } - typeHandler->Release(); - } - - return *type != Unknown; -} - -void MFPlayerSession::setupPlaybackTopology(IMFMediaSource *source, IMFPresentationDescriptor *sourcePD) -{ - HRESULT hr = S_OK; - // Get the number of streams in the media source. - DWORD cSourceStreams = 0; - hr = sourcePD->GetStreamDescriptorCount(&cSourceStreams); - if (FAILED(hr)) { - changeStatus(QMediaPlayer::InvalidMedia); - emit error(QMediaPlayer::ResourceError, tr("Failed to get stream count."), true); - return; - } - - IMFTopology *topology; - hr = MFCreateTopology(&topology); - if (FAILED(hr)) { - changeStatus(QMediaPlayer::InvalidMedia); - emit error(QMediaPlayer::ResourceError, tr("Failed to create topology."), true); - return; - } - - // For each stream, create the topology nodes and add them to the topology. - DWORD succeededCount = 0; - for (DWORD i = 0; i < cSourceStreams; i++) { - BOOL selected = FALSE; - bool streamAdded = false; - IMFStreamDescriptor *streamDesc = NULL; - - HRESULT hr = sourcePD->GetStreamDescriptorByIndex(i, &selected, &streamDesc); - if (SUCCEEDED(hr)) { - // The media might have multiple audio and video streams, - // only use one of each kind, and only if it is selected by default. - MediaType mediaType = Unknown; - QString streamName; - QString streamLanguage; - - if (getStreamInfo(streamDesc, &mediaType, &streamName, &streamLanguage)) { - - QPlatformMediaPlayer::TrackType trackType = (mediaType == Audio) ? - QPlatformMediaPlayer::AudioStream : QPlatformMediaPlayer::VideoStream; - - QLocale::Language lang = streamLanguage.isEmpty() ? - QLocale::Language::AnyLanguage : QLocale(streamLanguage).language(); - - QMediaMetaData metaData; - metaData.insert(QMediaMetaData::Title, streamName); - metaData.insert(QMediaMetaData::Language, lang); - - m_trackInfo[trackType].metaData.append(metaData); - m_trackInfo[trackType].nativeIndexes.append(i); - - if (((m_mediaTypes & mediaType) == 0) && selected) { // Check if this type isn't already added - IMFTopologyNode *sourceNode = addSourceNode(topology, source, sourcePD, streamDesc); - if (sourceNode) { - IMFTopologyNode *outputNode = addOutputNode(mediaType, topology, 0); - if (outputNode) { - bool connected = false; - if (mediaType == Audio) { - if (!m_audioSampleGrabberNode) - connected = setupAudioSampleGrabber(topology, sourceNode, outputNode); - } - sourceNode->GetTopoNodeID(&m_trackInfo[trackType].sourceNodeId); - outputNode->GetTopoNodeID(&m_trackInfo[trackType].outputNodeId); - - if (!connected) - hr = sourceNode->ConnectOutput(0, outputNode, 0); - - if (FAILED(hr)) { - emit error(QMediaPlayer::FormatError, tr("Unable to play any stream."), false); - } else { - m_trackInfo[trackType].currentIndex = m_trackInfo[trackType].nativeIndexes.count() - 1; - streamAdded = true; - succeededCount++; - m_mediaTypes |= mediaType; - switch (mediaType) { - case Audio: - emit audioAvailable(); - break; - case Video: - emit videoAvailable(); - break; - } - } - outputNode->Release(); - } - sourceNode->Release(); - } - } - } - - if (selected && !streamAdded) - sourcePD->DeselectStream(i); - - streamDesc->Release(); - } - } - - if (succeededCount == 0) { - changeStatus(QMediaPlayer::InvalidMedia); - emit error(QMediaPlayer::ResourceError, tr("Unable to play."), true); - } else { - if (m_trackInfo[QPlatformMediaPlayer::VideoStream].outputNodeId != -1) - topology = insertMFT(topology, m_trackInfo[QPlatformMediaPlayer::VideoStream].outputNodeId); - - hr = m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, topology); - if (SUCCEEDED(hr)) { - m_updatingTopology = true; - } else { - changeStatus(QMediaPlayer::InvalidMedia); - emit error(QMediaPlayer::ResourceError, tr("Failed to set topology."), true); - } - } - topology->Release(); -} - -IMFTopologyNode* MFPlayerSession::addSourceNode(IMFTopology* topology, IMFMediaSource* source, - IMFPresentationDescriptor* presentationDesc, IMFStreamDescriptor *streamDesc) -{ - IMFTopologyNode *node = NULL; - HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &node); - if (SUCCEEDED(hr)) { - hr = node->SetUnknown(MF_TOPONODE_SOURCE, source); - if (SUCCEEDED(hr)) { - hr = node->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, presentationDesc); - if (SUCCEEDED(hr)) { - hr = node->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, streamDesc); - if (SUCCEEDED(hr)) { - hr = topology->AddNode(node); - if (SUCCEEDED(hr)) - return node; - } - } - } - node->Release(); - } - return NULL; -} - -IMFTopologyNode* MFPlayerSession::addOutputNode(MediaType mediaType, IMFTopology* topology, DWORD sinkID) -{ - IMFTopologyNode *node = NULL; - if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &node))) - return NULL; - - IMFActivate *activate = NULL; - if (mediaType == Audio) { - if (m_audioOutput) { - HRESULT hr = MFCreateAudioRendererActivate(&activate); - if (FAILED(hr)) { - qWarning() << "Failed to create audio renderer activate"; - node->Release(); - return NULL; - } - - auto id = m_audioOutput->device.id(); - if (!id.isEmpty()) { - QString s = QString::fromUtf8(id); - hr = activate->SetString(MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ID, (LPCWSTR)s.utf16()); - } else { - //This is the default one that has been inserted in updateEndpoints(), - //so give the activate a hint that we want to use the device for multimedia playback - //then the media foundation will choose an appropriate one. - - //from MSDN: - //The ERole enumeration defines constants that indicate the role that the system has assigned to an audio endpoint device. - //eMultimedia: Music, movies, narration, and live music recording. - hr = activate->SetUINT32(MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ROLE, eMultimedia); - } - - if (FAILED(hr)) { - qWarning() << "Failed to set attribute for audio device" << m_audioOutput->device.description(); - activate->Release(); - node->Release(); - return NULL; - } - } - } else if (mediaType == Video) { - activate = m_videoRendererControl->createActivate(); - } else { - // Unknown stream type. - emit error(QMediaPlayer::FormatError, tr("Unknown stream type."), false); - } - - if (!activate - || FAILED(node->SetObject(activate)) - || FAILED(node->SetUINT32(MF_TOPONODE_STREAMID, sinkID)) - || FAILED(node->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE)) - || FAILED(topology->AddNode(node))) { - node->Release(); - node = NULL; - } - - if (activate && mediaType == Audio) - activate->Release(); - - return node; -} - -bool MFPlayerSession::addAudioSampleGrabberNode(IMFTopology *topology) -{ - HRESULT hr = S_OK; - IMFMediaType *pType = 0; - IMFActivate *sinkActivate = 0; - do { - hr = MFCreateMediaType(&pType); - if (FAILED(hr)) - break; - - hr = pType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio); - if (FAILED(hr)) - break; - - hr = pType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM); - if (FAILED(hr)) - break; - - hr = MFCreateSampleGrabberSinkActivate(pType, m_audioSampleGrabber, &sinkActivate); - if (FAILED(hr)) - break; - - // Note: Data is distorted if this attribute is enabled - hr = sinkActivate->SetUINT32(MF_SAMPLEGRABBERSINK_IGNORE_CLOCK, FALSE); - if (FAILED(hr)) - break; - - hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &m_audioSampleGrabberNode); - if (FAILED(hr)) - break; - - hr = m_audioSampleGrabberNode->SetObject(sinkActivate); - if (FAILED(hr)) - break; - - hr = m_audioSampleGrabberNode->SetUINT32(MF_TOPONODE_STREAMID, 0); // Identifier of the stream sink. - if (FAILED(hr)) - break; - - hr = m_audioSampleGrabberNode->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE); - if (FAILED(hr)) - break; - - hr = topology->AddNode(m_audioSampleGrabberNode); - if (FAILED(hr)) - break; - - pType->Release(); - sinkActivate->Release(); - return true; - } while (false); - - if (pType) - pType->Release(); - if (sinkActivate) - sinkActivate->Release(); - if (m_audioSampleGrabberNode) { - m_audioSampleGrabberNode->Release(); - m_audioSampleGrabberNode = NULL; - } - return false; -} - -bool MFPlayerSession::setupAudioSampleGrabber(IMFTopology *topology, IMFTopologyNode *sourceNode, IMFTopologyNode *outputNode) -{ - if (!addAudioSampleGrabberNode(topology)) - return false; - - HRESULT hr = S_OK; - IMFTopologyNode *pTeeNode = NULL; - - IMFMediaTypeHandler *typeHandler = NULL; - IMFMediaType *mediaType = NULL; - do { - hr = MFCreateTopologyNode(MF_TOPOLOGY_TEE_NODE, &pTeeNode); - if (FAILED(hr)) - break; - hr = sourceNode->ConnectOutput(0, pTeeNode, 0); - if (FAILED(hr)) - break; - hr = pTeeNode->ConnectOutput(0, outputNode, 0); - if (FAILED(hr)) - break; - hr = pTeeNode->ConnectOutput(1, m_audioSampleGrabberNode, 0); - if (FAILED(hr)) - break; - } while (false); - - if (pTeeNode) - pTeeNode->Release(); - if (mediaType) - mediaType->Release(); - if (typeHandler) - typeHandler->Release(); - return hr == S_OK; -} - -QAudioFormat MFPlayerSession::audioFormatForMFMediaType(IMFMediaType *mediaType) const -{ - WAVEFORMATEX *wfx = 0; - UINT32 size; - HRESULT hr = MFCreateWaveFormatExFromMFMediaType(mediaType, &wfx, &size, MFWaveFormatExConvertFlag_Normal); - if (FAILED(hr)) - return QAudioFormat(); - - if (size < sizeof(WAVEFORMATEX)) { - CoTaskMemFree(wfx); - return QAudioFormat(); - } - - if (wfx->wFormatTag != WAVE_FORMAT_PCM) { - CoTaskMemFree(wfx); - return QAudioFormat(); - } - - QAudioFormat format; - format.setSampleRate(wfx->nSamplesPerSec); - format.setChannelCount(wfx->nChannels); - if (wfx->wBitsPerSample == 8) - format.setSampleFormat(QAudioFormat::UInt8); - else if (wfx->wBitsPerSample == 16) - format.setSampleFormat(QAudioFormat::Int16); - else if (wfx->wBitsPerSample == 32) - format.setSampleFormat(QAudioFormat::Int32); - - CoTaskMemFree(wfx); - return format; -} - -// BindOutputNode -// Sets the IMFStreamSink pointer on an output node. -// IMFActivate pointer in the output node must be converted to an -// IMFStreamSink pointer before the topology loader resolves the topology. -HRESULT BindOutputNode(IMFTopologyNode *pNode) -{ - IUnknown *nodeObject = NULL; - IMFActivate *activate = NULL; - IMFStreamSink *stream = NULL; - IMFMediaSink *sink = NULL; - - HRESULT hr = pNode->GetObject(&nodeObject); - if (FAILED(hr)) - return hr; - - hr = nodeObject->QueryInterface(IID_PPV_ARGS(&activate)); - if (SUCCEEDED(hr)) { - DWORD dwStreamID = 0; - - // Try to create the media sink. - hr = activate->ActivateObject(IID_PPV_ARGS(&sink)); - if (SUCCEEDED(hr)) - dwStreamID = MFGetAttributeUINT32(pNode, MF_TOPONODE_STREAMID, 0); - - if (SUCCEEDED(hr)) { - // First check if the media sink already has a stream sink with the requested ID. - hr = sink->GetStreamSinkById(dwStreamID, &stream); - if (FAILED(hr)) { - // Create the stream sink. - hr = sink->AddStreamSink(dwStreamID, NULL, &stream); - } - } - - // Replace the node's object pointer with the stream sink. - if (SUCCEEDED(hr)) { - hr = pNode->SetObject(stream); - } - } else { - hr = nodeObject->QueryInterface(IID_PPV_ARGS(&stream)); - } - - if (nodeObject) - nodeObject->Release(); - if (activate) - activate->Release(); - if (stream) - stream->Release(); - if (sink) - sink->Release(); - return hr; -} - -// BindOutputNodes -// Sets the IMFStreamSink pointers on all of the output nodes in a topology. -HRESULT BindOutputNodes(IMFTopology *pTopology) -{ - IMFCollection *collection; - - // Get the collection of output nodes. - HRESULT hr = pTopology->GetOutputNodeCollection(&collection); - - // Enumerate all of the nodes in the collection. - if (SUCCEEDED(hr)) { - DWORD cNodes; - hr = collection->GetElementCount(&cNodes); - - if (SUCCEEDED(hr)) { - for (DWORD i = 0; i < cNodes; i++) { - IUnknown *element; - hr = collection->GetElement(i, &element); - if (FAILED(hr)) - break; - - IMFTopologyNode *node; - hr = element->QueryInterface(IID_IMFTopologyNode, (void**)&node); - element->Release(); - if (FAILED(hr)) - break; - - // Bind this node. - hr = BindOutputNode(node); - node->Release(); - if (FAILED(hr)) - break; - } - } - collection->Release(); - } - - return hr; -} - -// This method binds output nodes to complete the topology, -// then loads the topology and inserts MFT between the output node -// and a filter connected to the output node. -IMFTopology *MFPlayerSession::insertMFT(IMFTopology *topology, TOPOID outputNodeId) -{ - bool isNewTopology = false; - - IMFTopoLoader *topoLoader = 0; - IMFTopology *resolvedTopology = 0; - IMFCollection *outputNodes = 0; - - do { - if (FAILED(BindOutputNodes(topology))) - break; - - if (FAILED(MFCreateTopoLoader(&topoLoader))) - break; - - if (FAILED(topoLoader->Load(topology, &resolvedTopology, NULL))) { - // Topology could not be resolved, adding ourselves a color converter - // to the topology might solve the problem - insertColorConverter(topology, outputNodeId); - if (FAILED(topoLoader->Load(topology, &resolvedTopology, NULL))) - break; - } - - if (insertResizer(resolvedTopology)) - isNewTopology = true; - - // Get all output nodes and search for video output node. - if (FAILED(resolvedTopology->GetOutputNodeCollection(&outputNodes))) - break; - - DWORD elementCount = 0; - if (FAILED(outputNodes->GetElementCount(&elementCount))) - break; - - for (DWORD n = 0; n < elementCount; n++) { - IUnknown *element = 0; - IMFTopologyNode *node = 0; - IUnknown *outputObject = 0; - IMFTopologyNode *inputNode = 0; - IMFTopologyNode *mftNode = 0; - bool mftAdded = false; - - do { - if (FAILED(outputNodes->GetElement(n, &element))) - break; - - if (FAILED(element->QueryInterface(IID_IMFTopologyNode, (void**)&node))) - break; - - TOPOID id; - if (FAILED(node->GetTopoNodeID(&id))) - break; - - if (id != outputNodeId) - break; - - if (FAILED(node->GetObject(&outputObject))) - break; - - m_videoProbeMFT->setVideoSink(outputObject); - - // Insert MFT between the output node and the node connected to it. - DWORD outputIndex = 0; - if (FAILED(node->GetInput(0, &inputNode, &outputIndex))) - break; - - if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &mftNode))) - break; - - if (FAILED(mftNode->SetObject(m_videoProbeMFT))) - break; - - if (FAILED(resolvedTopology->AddNode(mftNode))) - break; - - if (FAILED(inputNode->ConnectOutput(0, mftNode, 0))) - break; - - if (FAILED(mftNode->ConnectOutput(0, node, 0))) - break; - - mftAdded = true; - isNewTopology = true; - } while (false); - - if (mftNode) - mftNode->Release(); - if (inputNode) - inputNode->Release(); - if (node) - node->Release(); - if (element) - element->Release(); - if (outputObject) - outputObject->Release(); - - if (mftAdded) - break; - else - m_videoProbeMFT->setVideoSink(NULL); - } - } while (false); - - if (outputNodes) - outputNodes->Release(); - - if (topoLoader) - topoLoader->Release(); - - if (isNewTopology) { - topology->Release(); - return resolvedTopology; - } - - if (resolvedTopology) - resolvedTopology->Release(); - - return topology; -} - -// This method checks if the topology contains a color converter transform (CColorConvertDMO), -// if it does it inserts a resizer transform (CResizerDMO) to handle dynamic frame size change -// of the video stream. -// Returns true if it inserted a resizer -bool MFPlayerSession::insertResizer(IMFTopology *topology) -{ - bool inserted = false; - WORD elementCount = 0; - IMFTopologyNode *node = 0; - IUnknown *object = 0; - IWMColorConvProps *colorConv = 0; - IMFTransform *resizer = 0; - IMFTopologyNode *resizerNode = 0; - IMFTopologyNode *inputNode = 0; - - HRESULT hr = topology->GetNodeCount(&elementCount); - if (FAILED(hr)) - return false; - - for (WORD i = 0; i < elementCount; ++i) { - if (node) { - node->Release(); - node = 0; - } - if (object) { - object->Release(); - object = 0; - } - - if (FAILED(topology->GetNode(i, &node))) - break; - - MF_TOPOLOGY_TYPE nodeType; - if (FAILED(node->GetNodeType(&nodeType))) - break; - - if (nodeType != MF_TOPOLOGY_TRANSFORM_NODE) - continue; - - if (FAILED(node->GetObject(&object))) - break; - - if (FAILED(object->QueryInterface(&colorConv))) - continue; - - if (FAILED(CoCreateInstance(CLSID_CResizerDMO, NULL, CLSCTX_INPROC_SERVER, IID_IMFTransform, (void**)&resizer))) - break; - - if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &resizerNode))) - break; - - if (FAILED(resizerNode->SetObject(resizer))) - break; - - if (FAILED(topology->AddNode(resizerNode))) - break; - - DWORD outputIndex = 0; - if (FAILED(node->GetInput(0, &inputNode, &outputIndex))) { - topology->RemoveNode(resizerNode); - break; - } - - if (FAILED(inputNode->ConnectOutput(0, resizerNode, 0))) { - topology->RemoveNode(resizerNode); - break; - } - - if (FAILED(resizerNode->ConnectOutput(0, node, 0))) { - inputNode->ConnectOutput(0, node, 0); - topology->RemoveNode(resizerNode); - break; - } - - inserted = true; - break; - } - - if (node) - node->Release(); - if (object) - object->Release(); - if (colorConv) - colorConv->Release(); - if (resizer) - resizer->Release(); - if (resizerNode) - resizerNode->Release(); - if (inputNode) - inputNode->Release(); - - return inserted; -} - -// This method inserts a color converter (CColorConvertDMO) in the topology, -// typically to convert to RGB format. -// Usually this converter is automatically inserted when the topology is resolved but -// for some reason it fails to do so in some cases, we then do it ourselves. -void MFPlayerSession::insertColorConverter(IMFTopology *topology, TOPOID outputNodeId) -{ - IMFCollection *outputNodes = 0; - - if (FAILED(topology->GetOutputNodeCollection(&outputNodes))) - return; - - DWORD elementCount = 0; - if (FAILED(outputNodes->GetElementCount(&elementCount))) - goto done; - - for (DWORD n = 0; n < elementCount; n++) { - IUnknown *element = 0; - IMFTopologyNode *node = 0; - IMFTopologyNode *inputNode = 0; - IMFTopologyNode *mftNode = 0; - IMFTransform *converter = 0; - - do { - if (FAILED(outputNodes->GetElement(n, &element))) - break; - - if (FAILED(element->QueryInterface(IID_IMFTopologyNode, (void**)&node))) - break; - - TOPOID id; - if (FAILED(node->GetTopoNodeID(&id))) - break; - - if (id != outputNodeId) - break; - - DWORD outputIndex = 0; - if (FAILED(node->GetInput(0, &inputNode, &outputIndex))) - break; - - if (FAILED(MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &mftNode))) - break; - - if (FAILED(CoCreateInstance(CLSID_CColorConvertDMO, NULL, CLSCTX_INPROC_SERVER, IID_IMFTransform, (void**)&converter))) - break; - - if (FAILED(mftNode->SetObject(converter))) - break; - - if (FAILED(topology->AddNode(mftNode))) - break; - - if (FAILED(inputNode->ConnectOutput(0, mftNode, 0))) - break; - - if (FAILED(mftNode->ConnectOutput(0, node, 0))) - break; - - } while (false); - - if (mftNode) - mftNode->Release(); - if (inputNode) - inputNode->Release(); - if (node) - node->Release(); - if (element) - element->Release(); - if (converter) - converter->Release(); - } - -done: - if (outputNodes) - outputNodes->Release(); -} - -void MFPlayerSession::stop(bool immediate) -{ -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "stop"; -#endif - if (!immediate && m_pendingState != NoPending) { - m_request.setCommand(CmdStop); - } else { - if (m_state.command == CmdStop) - return; - - if (m_scrubbing) - scrub(false); - - if (SUCCEEDED(m_session->Stop())) { - - m_state.setCommand(CmdStop); - m_pendingState = CmdPending; - if (m_status != QMediaPlayer::EndOfMedia) { - m_position = 0; - } - } else { - emit error(QMediaPlayer::ResourceError, tr("Failed to stop."), true); - } - } -} - -void MFPlayerSession::start() -{ - if (m_status == QMediaPlayer::EndOfMedia) - m_position = 0; // restart from the beginning - -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "start"; -#endif - - if (m_pendingState != NoPending) { - m_request.setCommand(CmdStart); - } else { - if (m_state.command == CmdStart) - return; - - if (m_scrubbing) - scrub(false); - - if (m_restorePosition >= 0) { - m_position = m_restorePosition; - if (!m_updatingTopology) - m_restorePosition = -1; - } - - PROPVARIANT varStart; - InitPropVariantFromInt64(m_position, &varStart); - - if (SUCCEEDED(m_session->Start(&GUID_NULL, &varStart))) { - m_state.setCommand(CmdStart); - m_pendingState = CmdPending; - } else { - emit error(QMediaPlayer::ResourceError, tr("failed to start playback"), true); - } - PropVariantClear(&varStart); - } -} - -void MFPlayerSession::pause() -{ -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "pause"; -#endif - if (m_pendingState != NoPending) { - m_request.setCommand(CmdPause); - } else { - if (m_state.command == CmdPause) - return; - - if (SUCCEEDED(m_session->Pause())) { - m_state.setCommand(CmdPause); - m_pendingState = CmdPending; - } else { - emit error(QMediaPlayer::ResourceError, tr("Failed to pause."), false); - } - } -} - -void MFPlayerSession::changeStatus(QMediaPlayer::MediaStatus newStatus) -{ - if (m_status == newStatus) - return; -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "MFPlayerSession::changeStatus" << newStatus; -#endif - m_status = newStatus; - emit statusChanged(); -} - -QMediaPlayer::MediaStatus MFPlayerSession::status() const -{ - return m_status; -} - -void MFPlayerSession::createSession() -{ - close(); - - m_hCloseEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - - m_sourceResolver = new SourceResolver(); - QObject::connect(m_sourceResolver, SIGNAL(mediaSourceReady()), this, SLOT(handleMediaSourceReady())); - QObject::connect(m_sourceResolver, SIGNAL(error(long)), this, SLOT(handleSourceError(long))); - - m_videoProbeMFT = new MFTransform; -// for (int i = 0; i < m_videoProbes.size(); ++i) -// m_videoProbeMFT->addProbe(m_videoProbes.at(i)); - - Q_ASSERT(m_session == NULL); - HRESULT hr = MFCreateMediaSession(NULL, &m_session); - if (FAILED(hr)) { - changeStatus(QMediaPlayer::InvalidMedia); - emit error(QMediaPlayer::ResourceError, tr("Unable to create mediasession."), true); - } - - hr = m_session->BeginGetEvent(this, m_session); - - if (FAILED(hr)) { - changeStatus(QMediaPlayer::InvalidMedia); - emit error(QMediaPlayer::ResourceError, tr("Unable to pull session events."), false); - } - - m_position = 0; -} - -qint64 MFPlayerSession::position() -{ - if (m_request.command == CmdSeek || m_request.command == CmdSeekResume) - return m_request.start; - - if (m_pendingState == SeekPending) - return m_state.start; - - if (m_state.command == CmdStop) { - return m_position / 10000; - } - - if (m_presentationClock) { - MFTIME time, sysTime; - if (FAILED(m_presentationClock->GetCorrelatedTime(0, &time, &sysTime))) - return 0; - return qint64(time / 10000); - } - return 0; -} - -void MFPlayerSession::setPosition(qint64 position) -{ -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "setPosition"; -#endif - if (m_pendingState != NoPending) { - m_request.setCommand(CmdSeek); - m_request.start = position; - } else { - setPositionInternal(position, CmdNone); - } -} - -void MFPlayerSession::setPositionInternal(qint64 position, Command requestCmd) -{ - if (m_status == QMediaPlayer::EndOfMedia) - changeStatus(QMediaPlayer::LoadedMedia); - if (m_state.command == CmdStop && requestCmd != CmdSeekResume) { - m_position = position * 10000; - // Even though the position is not actually set on the session yet, - // report it to have changed anyway for UI controls to be updated - emit positionChanged(this->position()); - return; - } - - if (m_state.command == CmdPause) - scrub(true); - -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "setPositionInternal"; -#endif - - PROPVARIANT varStart; - varStart.vt = VT_I8; - varStart.hVal.QuadPart = LONGLONG(position * 10000); - if (SUCCEEDED(m_session->Start(NULL, &varStart))) - { - PropVariantClear(&varStart); - // Store the pending state. - m_state.setCommand(CmdStart); - m_state.start = position; - m_pendingState = SeekPending; - } else { - emit error(QMediaPlayer::ResourceError, tr("Failed to seek."), true); - } -} - -qreal MFPlayerSession::playbackRate() const -{ - if (m_scrubbing) - return m_restoreRate; - return m_state.rate; -} - -void MFPlayerSession::setPlaybackRate(qreal rate) -{ - if (m_scrubbing) { - m_restoreRate = rate; - emit playbackRateChanged(rate); - return; - } - setPlaybackRateInternal(rate); -} - -void MFPlayerSession::setPlaybackRateInternal(qreal rate) -{ - if (rate == m_request.rate) - return; - - m_pendingRate = rate; - if (!m_rateSupport) - return; - -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "setPlaybackRate"; -#endif - BOOL isThin = FALSE; - - //from MSDN http://msdn.microsoft.com/en-us/library/aa965220%28v=vs.85%29.aspx - //Thinning applies primarily to video streams. - //In thinned mode, the source drops delta frames and deliver only key frames. - //At very high playback rates, the source might skip some key frames (for example, deliver every other key frame). - - if (FAILED(m_rateSupport->IsRateSupported(FALSE, rate, NULL))) { - isThin = TRUE; - if (FAILED(m_rateSupport->IsRateSupported(isThin, rate, NULL))) { - qWarning() << "unable to set playbackrate = " << rate; - m_pendingRate = m_request.rate = m_state.rate; - return; - } - } - if (m_pendingState != NoPending) { - m_request.rate = rate; - m_request.isThin = isThin; - // Remember the current transport state (play, paused, etc), so that we - // can restore it after the rate change, if necessary. However, if - // anothercommand is already pending, that one takes precedent. - if (m_request.command == CmdNone) - m_request.setCommand(m_state.command); - } else { - //No pending operation. Commit the new rate. - commitRateChange(rate, isThin); - } -} - -void MFPlayerSession::commitRateChange(qreal rate, BOOL isThin) -{ -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "commitRateChange"; -#endif - Q_ASSERT(m_pendingState == NoPending); - MFTIME hnsSystemTime = 0; - MFTIME hnsClockTime = 0; - Command cmdNow = m_state.command; - bool resetPosition = false; - // Allowed rate transitions: - // Positive <-> negative: Stopped - // Negative <-> zero: Stopped - // Postive <-> zero: Paused or stopped - if ((rate > 0 && m_state.rate <= 0) || (rate < 0 && m_state.rate >= 0)) { - if (cmdNow == CmdStart) { - // Get the current clock position. This will be the restart time. - m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime); - Q_ASSERT(hnsSystemTime != 0); - - if (rate < 0 || m_state.rate < 0) - m_request.setCommand(CmdSeekResume); - else if (isThin || m_state.isThin) - m_request.setCommand(CmdStartAndSeek); - else - m_request.setCommand(CmdStart); - - // We need to stop only when dealing with negative rates - if (rate >= 0 && m_state.rate >= 0) - pause(); - else - stop(); - - // If we deal with negative rates, we stopped the session and consequently - // reset the position to zero. We then need to resume to the current position. - m_request.start = hnsClockTime / 10000; - } else if (cmdNow == CmdPause) { - if (rate < 0 || m_state.rate < 0) { - // The current state is paused. - // For this rate change, the session must be stopped. However, the - // session cannot transition back from stopped to paused. - // Therefore, this rate transition is not supported while paused. - qWarning() << "Unable to change rate from positive to negative or vice versa in paused state"; - rate = m_state.rate; - isThin = m_state.isThin; - goto done; - } - - // This happens when resuming playback after scrubbing in pause mode. - // This transition requires the session to be paused. Even though our - // internal state is set to paused, the session might not be so we need - // to enforce it - if (rate > 0 && m_state.rate == 0) { - m_state.setCommand(CmdNone); - pause(); - } - } - } else if (rate == 0 && m_state.rate > 0) { - if (cmdNow != CmdPause) { - // Transition to paused. - // This transisition requires the paused state. - // Pause and set the rate. - pause(); - - // Request: Switch back to current state. - m_request.setCommand(cmdNow); - } - } else if (rate == 0 && m_state.rate < 0) { - // Changing rate from negative to zero requires to stop the session - m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime); - - m_request.setCommand(CmdSeekResume); - - stop(); - - // Resume to the current position (stop() will reset the position to 0) - m_request.start = hnsClockTime / 10000; - } else if (!isThin && m_state.isThin) { - if (cmdNow == CmdStart) { - // When thinning, only key frames are read and presented. Going back - // to normal playback requires to reset the current position to force - // the pipeline to decode the actual frame at the current position - // (which might be earlier than the last decoded key frame) - resetPosition = true; - } else if (cmdNow == CmdPause) { - // If paused, don't reset the position until we resume, otherwise - // a new frame will be rendered - m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime); - m_request.setCommand(CmdSeekResume); - m_request.start = hnsClockTime / 10000; - } - - } - - // Set the rate. - if (FAILED(m_rateControl->SetRate(isThin, rate))) { - qWarning() << "failed to set playbackrate = " << rate; - rate = m_state.rate; - isThin = m_state.isThin; - goto done; - } - - if (resetPosition) { - m_presentationClock->GetCorrelatedTime(0, &hnsClockTime, &hnsSystemTime); - setPosition(hnsClockTime / 10000); - } - -done: - // Adjust our current rate and requested rate. - m_pendingRate = m_request.rate = m_state.rate = rate; - if (rate != 0) - m_state.isThin = isThin; - emit playbackRateChanged(rate); -} - -void MFPlayerSession::scrub(bool enableScrub) -{ - if (m_scrubbing == enableScrub) - return; - - m_scrubbing = enableScrub; - - if (!canScrub()) { - if (!enableScrub) - m_pendingRate = m_restoreRate; - return; - } - - if (enableScrub) { - // Enter scrubbing mode. Cache the rate. - m_restoreRate = m_request.rate; - setPlaybackRateInternal(0.0f); - } else { - // Leaving scrubbing mode. Restore the old rate. - setPlaybackRateInternal(m_restoreRate); - } -} - -void MFPlayerSession::setVolume(float volume) -{ - if (m_volume == volume) - return; - m_volume = volume; - - if (!m_muted) - setVolumeInternal(volume); -} - -void MFPlayerSession::setMuted(bool muted) -{ - if (m_muted == muted) - return; - m_muted = muted; - - setVolumeInternal(muted ? 0 : m_volume); -} - -void MFPlayerSession::setVolumeInternal(float volume) -{ - if (m_volumeControl) { - quint32 channelCount = 0; - if (!SUCCEEDED(m_volumeControl->GetChannelCount(&channelCount)) - || channelCount == 0) - return; - - for (quint32 i = 0; i < channelCount; ++i) - m_volumeControl->SetChannelVolume(i, volume); - } -} - -float MFPlayerSession::bufferProgress() -{ - if (!m_netsourceStatistics) - return 0; - PROPVARIANT var; - PropVariantInit(&var); - PROPERTYKEY key; - key.fmtid = MFNETSOURCE_STATISTICS; - key.pid = MFNETSOURCE_BUFFERPROGRESS_ID; - int progress = -1; - // GetValue returns S_FALSE if the property is not available, which has - // a value > 0. We therefore can't use the SUCCEEDED macro here. - if (m_netsourceStatistics->GetValue(key, &var) == S_OK) { - progress = var.lVal; - PropVariantClear(&var); - } - -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "bufferProgress: progress = " << progress; -#endif - - return progress/100.; -} - -QMediaTimeRange MFPlayerSession::availablePlaybackRanges() -{ - // defaults to the whole media - qint64 start = 0; - qint64 end = qint64(m_duration / 10000); - - if (m_netsourceStatistics) { - PROPVARIANT var; - PropVariantInit(&var); - PROPERTYKEY key; - key.fmtid = MFNETSOURCE_STATISTICS; - key.pid = MFNETSOURCE_SEEKRANGESTART_ID; - // GetValue returns S_FALSE if the property is not available, which has - // a value > 0. We therefore can't use the SUCCEEDED macro here. - if (m_netsourceStatistics->GetValue(key, &var) == S_OK) { - start = qint64(var.uhVal.QuadPart / 10000); - PropVariantClear(&var); - PropVariantInit(&var); - key.pid = MFNETSOURCE_SEEKRANGEEND_ID; - if (m_netsourceStatistics->GetValue(key, &var) == S_OK) { - end = qint64(var.uhVal.QuadPart / 10000); - PropVariantClear(&var); - } - } - } - - return QMediaTimeRange(start, end); -} - -HRESULT MFPlayerSession::QueryInterface(REFIID riid, void** ppvObject) -{ - if (!ppvObject) - return E_POINTER; - if (riid == IID_IMFAsyncCallback) { - *ppvObject = static_cast<IMFAsyncCallback*>(this); - } else if (riid == IID_IUnknown) { - *ppvObject = static_cast<IUnknown*>(this); - } else { - *ppvObject = NULL; - return E_NOINTERFACE; - } - return S_OK; -} - -ULONG MFPlayerSession::AddRef(void) -{ - return InterlockedIncrement(&m_cRef); -} - -ULONG MFPlayerSession::Release(void) -{ - LONG cRef = InterlockedDecrement(&m_cRef); - if (cRef == 0) - this->deleteLater(); - return cRef; -} - -HRESULT MFPlayerSession::Invoke(IMFAsyncResult *pResult) -{ - if (pResult->GetStateNoAddRef() != m_session) - return S_OK; - - IMFMediaEvent *pEvent = NULL; - // Get the event from the event queue. - HRESULT hr = m_session->EndGetEvent(pResult, &pEvent); - if (FAILED(hr)) { - return S_OK; - } - - MediaEventType meType = MEUnknown; - hr = pEvent->GetType(&meType); - if (FAILED(hr)) { - pEvent->Release(); - return S_OK; - } - - if (meType == MESessionClosed) { - SetEvent(m_hCloseEvent); - pEvent->Release(); - return S_OK; - } else { - hr = m_session->BeginGetEvent(this, m_session); - if (FAILED(hr)) { - pEvent->Release(); - return S_OK; - } - } - - if (!m_closing) { - emit sessionEvent(pEvent); - } else { - pEvent->Release(); - } - return S_OK; -} - -void MFPlayerSession::handleSessionEvent(IMFMediaEvent *sessionEvent) -{ - HRESULT hrStatus = S_OK; - HRESULT hr = sessionEvent->GetStatus(&hrStatus); - if (FAILED(hr) || !m_session) { - sessionEvent->Release(); - return; - } - - MediaEventType meType = MEUnknown; - hr = sessionEvent->GetType(&meType); - -#ifdef DEBUG_MEDIAFOUNDATION - if (FAILED(hrStatus)) - qDebug() << "handleSessionEvent: MediaEventType = " << meType << "Failed"; - else - qDebug() << "handleSessionEvent: MediaEventType = " << meType; -#endif - - switch (meType) { - case MENonFatalError: { - PROPVARIANT var; - PropVariantInit(&var); - sessionEvent->GetValue(&var); - qWarning() << "handleSessionEvent: non fatal error = " << var.ulVal; - PropVariantClear(&var); - emit error(QMediaPlayer::ResourceError, tr("Media session non-fatal error."), false); - } - break; - case MESourceUnknown: - changeStatus(QMediaPlayer::InvalidMedia); - break; - case MEError: - changeStatus(QMediaPlayer::InvalidMedia); - qWarning() << "handleSessionEvent: serious error = " << hrStatus; - emit error(QMediaPlayer::ResourceError, tr("Media session serious error."), true); - break; - case MESessionRateChanged: - // If the rate change succeeded, we've already got the rate - // cached. If it failed, try to get the actual rate. - if (FAILED(hrStatus)) { - PROPVARIANT var; - PropVariantInit(&var); - if (SUCCEEDED(sessionEvent->GetValue(&var)) && (var.vt == VT_R4)) { - m_state.rate = var.fltVal; - } - emit playbackRateChanged(playbackRate()); - } - break; - case MESessionScrubSampleComplete : - if (m_scrubbing) - updatePendingCommands(CmdStart); - break; - case MESessionStarted: - if (m_status == QMediaPlayer::EndOfMedia - || m_status == QMediaPlayer::LoadedMedia) { - // If the session started, then enough data is buffered to play - changeStatus(QMediaPlayer::BufferedMedia); - } - - updatePendingCommands(CmdStart); - // playback started, we can now set again the procAmpValues if they have been - // changed previously (these are lost when loading a new media) -// if (m_playerService->videoWindowControl()) { -// m_playerService->videoWindowControl()->applyImageControls(); -// } - m_signalPositionChangeTimer.start(); - break; - case MESessionStopped: - if (m_status != QMediaPlayer::EndOfMedia) { - m_position = 0; - - // Reset to Loaded status unless we are loading a new media - // or changing the playback rate to negative values (stop required) - if (m_status != QMediaPlayer::LoadingMedia && m_request.command != CmdSeekResume) - changeStatus(QMediaPlayer::LoadedMedia); - } - updatePendingCommands(CmdStop); - m_signalPositionChangeTimer.stop(); - break; - case MESessionPaused: - m_position = position() * 10000; - updatePendingCommands(CmdPause); - m_signalPositionChangeTimer.stop(); - break; - case MEReconnectStart: -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "MEReconnectStart" << ((hrStatus == S_OK) ? "OK" : "Failed"); -#endif - break; - case MEReconnectEnd: -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "MEReconnectEnd" << ((hrStatus == S_OK) ? "OK" : "Failed"); -#endif - break; - case MESessionTopologySet: - if (FAILED(hrStatus)) { - changeStatus(QMediaPlayer::InvalidMedia); - emit error(QMediaPlayer::FormatError, tr("Unsupported media, a codec is missing."), true); - } else { - if (m_audioSampleGrabberNode) { - IUnknown *obj = 0; - if (SUCCEEDED(m_audioSampleGrabberNode->GetObject(&obj))) { - IMFStreamSink *streamSink = 0; - if (SUCCEEDED(obj->QueryInterface(IID_PPV_ARGS(&streamSink)))) { - IMFMediaTypeHandler *typeHandler = 0; - if (SUCCEEDED(streamSink->GetMediaTypeHandler((&typeHandler)))) { - IMFMediaType *mediaType = 0; - if (SUCCEEDED(typeHandler->GetCurrentMediaType(&mediaType))) { - m_audioSampleGrabber->setFormat(audioFormatForMFMediaType(mediaType)); - mediaType->Release(); - } - typeHandler->Release(); - } - streamSink->Release(); - } - obj->Release(); - } - } - - // Topology is resolved and successfuly set, this happens only after loading a new media. - // Make sure we always start the media from the beginning - m_position = 0; - - changeStatus(QMediaPlayer::LoadedMedia); - } - break; - } - - if (FAILED(hrStatus)) { - sessionEvent->Release(); - return; - } - - switch (meType) { - case MEBufferingStarted: - changeStatus(QMediaPlayer::StalledMedia); - emit bufferProgressChanged(bufferProgress()); - break; - case MEBufferingStopped: - changeStatus(QMediaPlayer::BufferedMedia); - emit bufferProgressChanged(bufferProgress()); - break; - case MESessionEnded: - m_pendingState = NoPending; - m_state.command = CmdStop; - m_state.prevCmd = CmdNone; - m_request.command = CmdNone; - m_request.prevCmd = CmdNone; - - //keep reporting the final position after end of media - m_position = qint64(m_duration); - emit positionChanged(position()); - - changeStatus(QMediaPlayer::EndOfMedia); - break; - case MEEndOfPresentationSegment: - break; - case MESessionTopologyStatus: { - UINT32 status; - if (SUCCEEDED(sessionEvent->GetUINT32(MF_EVENT_TOPOLOGY_STATUS, &status))) { - if (status == MF_TOPOSTATUS_READY) { - IMFClock* clock; - if (SUCCEEDED(m_session->GetClock(&clock))) { - clock->QueryInterface(IID_IMFPresentationClock, (void**)(&m_presentationClock)); - clock->Release(); - } - - if (SUCCEEDED(MFGetService(m_session, MF_RATE_CONTROL_SERVICE, IID_PPV_ARGS(&m_rateControl)))) { - if (SUCCEEDED(MFGetService(m_session, MF_RATE_CONTROL_SERVICE, IID_PPV_ARGS(&m_rateSupport)))) { - if ((m_mediaTypes & Video) == Video - && SUCCEEDED(m_rateSupport->IsRateSupported(TRUE, 0, NULL))) - m_canScrub = true; - } - BOOL isThin = FALSE; - float rate = 1; - if (SUCCEEDED(m_rateControl->GetRate(&isThin, &rate))) { - if (m_pendingRate != rate) { - m_state.rate = m_request.rate = rate; - setPlaybackRate(m_pendingRate); - } - } - } - MFGetService(m_session, MFNETSOURCE_STATISTICS_SERVICE, IID_PPV_ARGS(&m_netsourceStatistics)); - - if (SUCCEEDED(MFGetService(m_session, MR_STREAM_VOLUME_SERVICE, IID_PPV_ARGS(&m_volumeControl)))) - setVolumeInternal(m_muted ? 0 : m_volume); - - m_updatingTopology = false; - stop(); - } - } - } - break; - default: - break; - } - - sessionEvent->Release(); -} - -void MFPlayerSession::updatePendingCommands(Command command) -{ - emit positionChanged(position()); - if (m_state.command != command || m_pendingState == NoPending) - return; - - // Seek while paused completed - if (m_pendingState == SeekPending && m_state.prevCmd == CmdPause) { - m_pendingState = NoPending; - // A seek operation actually restarts playback. If scrubbing is possible, playback rate - // is set to 0.0 at this point and we just need to reset the current state to Pause. - // If scrubbing is not possible, the playback rate was not changed and we explicitly need - // to re-pause playback. - if (!canScrub()) - pause(); - else - m_state.setCommand(CmdPause); - } - - m_pendingState = NoPending; - - //First look for rate changes. - if (m_request.rate != m_state.rate) { - commitRateChange(m_request.rate, m_request.isThin); - } - - // Now look for new requests. - if (m_pendingState == NoPending) { - switch (m_request.command) { - case CmdStart: - start(); - break; - case CmdPause: - pause(); - break; - case CmdStop: - stop(); - break; - case CmdSeek: - case CmdSeekResume: - setPositionInternal(m_request.start, m_request.command); - break; - case CmdStartAndSeek: - start(); - setPositionInternal(m_request.start, m_request.command); - break; - } - m_request.setCommand(CmdNone); - } - -} - -bool MFPlayerSession::canScrub() const -{ - return m_canScrub && m_rateSupport && m_rateControl; -} - -void MFPlayerSession::clear() -{ -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "MFPlayerSession::clear"; -#endif - m_mediaTypes = 0; - m_canScrub = false; - - m_pendingState = NoPending; - m_state.command = CmdStop; - m_state.prevCmd = CmdNone; - m_request.command = CmdNone; - m_request.prevCmd = CmdNone; - - for (int i = 0; i < QPlatformMediaPlayer::NTrackTypes; ++i) { - m_trackInfo[i].metaData.clear(); - m_trackInfo[i].nativeIndexes.clear(); - m_trackInfo[i].currentIndex = -1; - m_trackInfo[i].sourceNodeId = -1; - m_trackInfo[i].outputNodeId = -1; - } - - if (!m_metaData.isEmpty()) { - m_metaData.clear(); - emit metaDataChanged(); - } - - if (m_presentationClock) { - m_presentationClock->Release(); - m_presentationClock = NULL; - } - if (m_rateControl) { - m_rateControl->Release(); - m_rateControl = NULL; - } - if (m_rateSupport) { - m_rateSupport->Release(); - m_rateSupport = NULL; - } - if (m_volumeControl) { - m_volumeControl->Release(); - m_volumeControl = NULL; - } - if (m_netsourceStatistics) { - m_netsourceStatistics->Release(); - m_netsourceStatistics = NULL; - } - if (m_audioSampleGrabberNode) { - m_audioSampleGrabberNode->Release(); - m_audioSampleGrabberNode = NULL; - } -} - -void MFPlayerSession::setAudioOutput(QPlatformAudioOutput *device) -{ - if (m_audioOutput == device) - return; - - if (m_audioOutput) - m_audioOutput->q->disconnect(this); - - m_audioOutput = device; - if (m_audioOutput) { - setMuted(m_audioOutput->q->isMuted()); - setVolume(m_audioOutput->q->volume()); - updateOutputRouting(); - connect(m_audioOutput->q, &QAudioOutput::deviceChanged, this, &MFPlayerSession::updateOutputRouting); - connect(m_audioOutput->q, &QAudioOutput::volumeChanged, this, &MFPlayerSession::setVolume); - connect(m_audioOutput->q, &QAudioOutput::mutedChanged, this, &MFPlayerSession::setMuted); - } -} - -void MFPlayerSession::updateOutputRouting() -{ - int currentAudioTrack = m_trackInfo[QPlatformMediaPlayer::AudioStream].currentIndex; - if (currentAudioTrack > -1) - setActiveTrack(QPlatformMediaPlayer::AudioStream, currentAudioTrack); -} - -void MFPlayerSession::setVideoSink(QVideoSink *sink) -{ - m_videoRendererControl->setSink(sink); -} - -void MFPlayerSession::setActiveTrack(QPlatformMediaPlayer::TrackType type, int index) -{ - if (!m_session) - return; - - // Only audio track selection is currently supported. - if (type != QPlatformMediaPlayer::AudioStream) - return; - - const auto &nativeIndexes = m_trackInfo[type].nativeIndexes; - - if (index < -1 || index >= nativeIndexes.count()) - return; - - IMFTopology *topology = nullptr; - - if (SUCCEEDED(m_session->GetFullTopology(MFSESSION_GETFULLTOPOLOGY_CURRENT, 0, &topology))) { - - m_restorePosition = position() * 10000; - - if (m_state.command == CmdStart) - stop(); - - if (m_trackInfo[type].outputNodeId != -1) { - IMFTopologyNode *node = nullptr; - if (SUCCEEDED(topology->GetNodeByID(m_trackInfo[type].outputNodeId, &node))) { - topology->RemoveNode(node); - node->Release(); - m_trackInfo[type].outputNodeId = -1; - } - } - if (m_trackInfo[type].sourceNodeId != -1) { - IMFTopologyNode *node = nullptr; - if (SUCCEEDED(topology->GetNodeByID(m_trackInfo[type].sourceNodeId, &node))) { - topology->RemoveNode(node); - node->Release(); - m_trackInfo[type].sourceNodeId = -1; - } - } - - IMFMediaSource *mediaSource = m_sourceResolver->mediaSource(); - - IMFPresentationDescriptor *sourcePD = nullptr; - if (SUCCEEDED(mediaSource->CreatePresentationDescriptor(&sourcePD))) { - - if (m_trackInfo[type].currentIndex >= 0 && m_trackInfo[type].currentIndex < nativeIndexes.count()) - sourcePD->DeselectStream(nativeIndexes.at(m_trackInfo[type].currentIndex)); - - m_trackInfo[type].currentIndex = index; - - if (index == -1) { - m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, topology); - } else { - int nativeIndex = nativeIndexes.at(index); - sourcePD->SelectStream(nativeIndex); - - IMFStreamDescriptor *streamDesc = nullptr; - BOOL selected = FALSE; - - if (SUCCEEDED(sourcePD->GetStreamDescriptorByIndex(nativeIndex, &selected, &streamDesc))) { - IMFTopologyNode *sourceNode = addSourceNode(topology, mediaSource, sourcePD, streamDesc); - if (sourceNode) { - IMFTopologyNode *outputNode = addOutputNode(MFPlayerSession::Audio, topology, 0); - if (outputNode) { - if (SUCCEEDED(sourceNode->ConnectOutput(0, outputNode, 0))) { - sourceNode->GetTopoNodeID(&m_trackInfo[type].sourceNodeId); - outputNode->GetTopoNodeID(&m_trackInfo[type].outputNodeId); - m_session->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, topology); - } - outputNode->Release(); - } - sourceNode->Release(); - } - streamDesc->Release(); - } - } - m_updatingTopology = true; - sourcePD->Release(); - } - topology->Release(); - } -} - -int MFPlayerSession::activeTrack(QPlatformMediaPlayer::TrackType type) -{ - if (type < 0 || type >= QPlatformMediaPlayer::NTrackTypes) - return -1; - return m_trackInfo[type].currentIndex; -} - -int MFPlayerSession::trackCount(QPlatformMediaPlayer::TrackType type) -{ - if (type < 0 || type >= QPlatformMediaPlayer::NTrackTypes) - return -1; - return m_trackInfo[type].metaData.count(); -} - -QMediaMetaData MFPlayerSession::trackMetaData(QPlatformMediaPlayer::TrackType type, int trackNumber) -{ - if (type < 0 || type >= QPlatformMediaPlayer::NTrackTypes) - return {}; - - if (trackNumber < 0 || trackNumber >= m_trackInfo[type].metaData.count()) - return {}; - - return m_trackInfo[type].metaData.at(trackNumber); -} - diff --git a/src/multimedia/platform/windows/player/mfplayersession_p.h b/src/multimedia/platform/windows/player/mfplayersession_p.h deleted file mode 100644 index 3aafedb86..000000000 --- a/src/multimedia/platform/windows/player/mfplayersession_p.h +++ /dev/null @@ -1,275 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Mobility Components. -** -** $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$ -** -****************************************************************************/ - -#ifndef MFPLAYERSESSION_H -#define MFPLAYERSESSION_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <mfapi.h> -#include <mfidl.h> - -#include "qmediaplayer.h" -#include "qmediatimerange.h" - -#include <QtCore/qcoreevent.h> -#include <QtCore/qmutex.h> -#include <QtCore/qurl.h> -#include <QtCore/qwaitcondition.h> -#include <QtMultimedia/qaudioformat.h> -#include <QtMultimedia/qvideoframeformat.h> -#include <qaudiodevice.h> -#include <qtimer.h> -#include "mfplayercontrol_p.h" - -QT_BEGIN_NAMESPACE -class QUrl; -QT_END_NAMESPACE - -QT_USE_NAMESPACE - -class SourceResolver; -class MFVideoRendererControl; -class MFPlayerControl; -class MFPlayerService; -class AudioSampleGrabberCallback; -class MFTransform; - -class MFPlayerSession : public QObject, public IMFAsyncCallback -{ - Q_OBJECT - friend class SourceResolver; -public: - MFPlayerSession(MFPlayerControl *playerControl = 0); - ~MFPlayerSession(); - - STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject); - - STDMETHODIMP_(ULONG) AddRef(void); - - STDMETHODIMP_(ULONG) Release(void); - - STDMETHODIMP Invoke(IMFAsyncResult *pResult); - - STDMETHODIMP GetParameters(DWORD *pdwFlags, DWORD *pdwQueue) - { - Q_UNUSED(pdwFlags); - Q_UNUSED(pdwQueue); - return E_NOTIMPL; - } - - void load(const QUrl &media, QIODevice *stream); - void stop(bool immediate = false); - void start(); - void pause(); - - QMediaPlayer::MediaStatus status() const; - qint64 position(); - void setPosition(qint64 position); - qreal playbackRate() const; - void setPlaybackRate(qreal rate); - float bufferProgress(); - QMediaTimeRange availablePlaybackRanges(); - - void changeStatus(QMediaPlayer::MediaStatus newStatus); - - void close(); - - void setAudioOutput(QPlatformAudioOutput *device); - - QMediaMetaData metaData() const { return m_metaData; } - - void setVideoSink(QVideoSink *sink); - - void setActiveTrack(QPlatformMediaPlayer::TrackType type, int index); - int activeTrack(QPlatformMediaPlayer::TrackType type); - int trackCount(QPlatformMediaPlayer::TrackType); - QMediaMetaData trackMetaData(QPlatformMediaPlayer::TrackType type, int trackNumber); - - void statusChanged() { if (m_playerControl) m_playerControl->handleStatusChanged(); } - void tracksChanged() { if (m_playerControl) m_playerControl->handleTracksChanged(); } - void audioAvailable() { if (m_playerControl) m_playerControl->handleAudioAvailable(); } - void videoAvailable() { if (m_playerControl) m_playerControl->handleVideoAvailable(); } - void durationUpdate(qint64 duration) { if (m_playerControl) m_playerControl->handleDurationUpdate(duration); } - void seekableUpdate(bool seekable) { if (m_playerControl) m_playerControl->handleSeekableUpdate(seekable); } - void error(QMediaPlayer::Error error, QString errorString, bool isFatal) { if (m_playerControl) m_playerControl->handleError(error, errorString, isFatal); } - void playbackRateChanged(qreal rate) { if (m_playerControl) m_playerControl->playbackRateChanged(rate); } - void bufferProgressChanged(float percentFilled) { if (m_playerControl) m_playerControl->bufferProgressChanged(percentFilled); } - void metaDataChanged() { if (m_playerControl) m_playerControl->metaDataChanged(); } - void positionChanged(qint64 position) { if (m_playerControl) m_playerControl->positionChanged(position); } - -public Q_SLOTS: - void setVolume(float volume); - void setMuted(bool muted); - -Q_SIGNALS: - void sessionEvent(IMFMediaEvent *sessionEvent); - -private Q_SLOTS: - void handleMediaSourceReady(); - void handleSessionEvent(IMFMediaEvent *sessionEvent); - void handleSourceError(long hr); - void updateOutputRouting(); - -private: - long m_cRef; - MFPlayerControl *m_playerControl = nullptr; - MFVideoRendererControl *m_videoRendererControl = nullptr; - IMFMediaSession *m_session; - IMFPresentationClock *m_presentationClock; - IMFRateControl *m_rateControl; - IMFRateSupport *m_rateSupport; - IMFAudioStreamVolume *m_volumeControl; - IPropertyStore *m_netsourceStatistics; - qint64 m_position = 0; - qint64 m_restorePosition = -1; - UINT64 m_duration = 0; - bool m_updatingTopology = false; - - enum Command - { - CmdNone = 0, - CmdStop, - CmdStart, - CmdPause, - CmdSeek, - CmdSeekResume, - CmdStartAndSeek - }; - - void clear(); - void setPositionInternal(qint64 position, Command requestCmd); - void setPlaybackRateInternal(qreal rate); - void commitRateChange(qreal rate, BOOL isThin); - bool canScrub() const; - void scrub(bool enableScrub); - bool m_scrubbing; - float m_restoreRate; - - SourceResolver *m_sourceResolver; - HANDLE m_hCloseEvent; - bool m_closing; - - enum MediaType - { - Unknown = 0, - Audio = 1, - Video = 2, - }; - DWORD m_mediaTypes; - - enum PendingState - { - NoPending = 0, - CmdPending, - SeekPending, - }; - - struct SeekState - { - void setCommand(Command cmd) { - prevCmd = command; - command = cmd; - } - Command command; - Command prevCmd; - float rate; // Playback rate - BOOL isThin; // Thinned playback? - qint64 start; // Start position - }; - SeekState m_state; // Current nominal state. - SeekState m_request; // Pending request. - PendingState m_pendingState; - float m_pendingRate; - void updatePendingCommands(Command command); - - struct TrackInfo - { - QList<QMediaMetaData> metaData; - QList<int> nativeIndexes; - int currentIndex = -1; - TOPOID sourceNodeId = -1; - TOPOID outputNodeId = -1; - }; - TrackInfo m_trackInfo[QPlatformMediaPlayer::NTrackTypes]; - - QMediaPlayer::MediaStatus m_status; - bool m_canScrub; - float m_volume = 1.; - bool m_muted = false; - - QPlatformAudioOutput *m_audioOutput = nullptr; - QMediaMetaData m_metaData; - - void setVolumeInternal(float volume); - - void createSession(); - void setupPlaybackTopology(IMFMediaSource *source, IMFPresentationDescriptor *sourcePD); - bool getStreamInfo(IMFStreamDescriptor *stream, MFPlayerSession::MediaType *type, QString *name, QString *language) const; - IMFTopologyNode* addSourceNode(IMFTopology* topology, IMFMediaSource* source, - IMFPresentationDescriptor* presentationDesc, IMFStreamDescriptor *streamDesc); - IMFTopologyNode* addOutputNode(MediaType mediaType, IMFTopology* topology, DWORD sinkID); - - bool addAudioSampleGrabberNode(IMFTopology* topology); - bool setupAudioSampleGrabber(IMFTopology *topology, IMFTopologyNode *sourceNode, IMFTopologyNode *outputNode); - QAudioFormat audioFormatForMFMediaType(IMFMediaType *mediaType) const; - // ### Below can be used to monitor the audio channel. Currently unused. - AudioSampleGrabberCallback *m_audioSampleGrabber; - IMFTopologyNode *m_audioSampleGrabberNode; - - IMFTopology *insertMFT(IMFTopology *topology, TOPOID outputNodeId); - bool insertResizer(IMFTopology *topology); - void insertColorConverter(IMFTopology *topology, TOPOID outputNodeId); - // ### Below can be used to monitor the video channel. Functionality currently unused. - MFTransform *m_videoProbeMFT; - - QTimer m_signalPositionChangeTimer; -}; - - -#endif diff --git a/src/multimedia/platform/windows/player/mftvideo.cpp b/src/multimedia/platform/windows/player/mftvideo.cpp deleted file mode 100644 index 5028bd053..000000000 --- a/src/multimedia/platform/windows/player/mftvideo.cpp +++ /dev/null @@ -1,725 +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$ -** -****************************************************************************/ - -#include "mftvideo_p.h" -#include <private/qmemoryvideobuffer_p.h> -#include <private/qwindowsmultimediautils_p.h> -#include <mferror.h> -#include <strmif.h> -#include <uuids.h> -#include <InitGuid.h> -#include <d3d9.h> -#include <qdebug.h> - -// This MFT sends all samples it processes to connected video probes. -// Sample is sent to probes in ProcessInput. -// In ProcessOutput this MFT simply returns the original sample. - -// The implementation is based on a boilerplate from the MF SDK example. - -MFTransform::MFTransform(): - m_cRef(1), - m_inputType(0), - m_outputType(0), - m_sample(0), - m_videoSinkTypeHandler(0), - m_bytesPerLine(0) -{ -} - -MFTransform::~MFTransform() -{ - if (m_inputType) - m_inputType->Release(); - - if (m_outputType) - m_outputType->Release(); - - if (m_videoSinkTypeHandler) - m_videoSinkTypeHandler->Release(); -} - -//void MFTransform::addProbe(MFVideoProbeControl *probe) -//{ -// QMutexLocker locker(&m_videoProbeMutex); - -// if (m_videoProbes.contains(probe)) -// return; - -// m_videoProbes.append(probe); -//} - -//void MFTransform::removeProbe(MFVideoProbeControl *probe) -//{ -// QMutexLocker locker(&m_videoProbeMutex); -// m_videoProbes.removeOne(probe); -//} - -void MFTransform::setVideoSink(IUnknown *videoSink) -{ - // This transform supports the same input types as the video sink. - // Store its type handler interface in order to report the correct supported types. - - if (m_videoSinkTypeHandler) { - m_videoSinkTypeHandler->Release(); - m_videoSinkTypeHandler = NULL; - } - - if (videoSink) - videoSink->QueryInterface(IID_PPV_ARGS(&m_videoSinkTypeHandler)); -} - -STDMETHODIMP MFTransform::QueryInterface(REFIID riid, void** ppv) -{ - if (!ppv) - return E_POINTER; - if (riid == IID_IMFTransform) { - *ppv = static_cast<IMFTransform*>(this); - } else if (riid == IID_IUnknown) { - *ppv = static_cast<IUnknown*>(this); - } else { - *ppv = NULL; - return E_NOINTERFACE; - } - AddRef(); - return S_OK; -} - -STDMETHODIMP_(ULONG) MFTransform::AddRef() -{ - return InterlockedIncrement(&m_cRef); -} - -STDMETHODIMP_(ULONG) MFTransform::Release() -{ - ULONG cRef = InterlockedDecrement(&m_cRef); - if (cRef == 0) { - delete this; - } - return cRef; -} - -STDMETHODIMP MFTransform::GetStreamLimits(DWORD *pdwInputMinimum, DWORD *pdwInputMaximum, DWORD *pdwOutputMinimum, DWORD *pdwOutputMaximum) -{ - if (!pdwInputMinimum || !pdwInputMaximum || !pdwOutputMinimum || !pdwOutputMaximum) - return E_POINTER; - *pdwInputMinimum = 1; - *pdwInputMaximum = 1; - *pdwOutputMinimum = 1; - *pdwOutputMaximum = 1; - return S_OK; -} - -STDMETHODIMP MFTransform::GetStreamCount(DWORD *pcInputStreams, DWORD *pcOutputStreams) -{ - if (!pcInputStreams || !pcOutputStreams) - return E_POINTER; - - *pcInputStreams = 1; - *pcOutputStreams = 1; - return S_OK; -} - -STDMETHODIMP MFTransform::GetStreamIDs(DWORD dwInputIDArraySize, DWORD *pdwInputIDs, DWORD dwOutputIDArraySize, DWORD *pdwOutputIDs) -{ - // streams are numbered consecutively - Q_UNUSED(dwInputIDArraySize); - Q_UNUSED(pdwInputIDs); - Q_UNUSED(dwOutputIDArraySize); - Q_UNUSED(pdwOutputIDs); - return E_NOTIMPL; -} - -STDMETHODIMP MFTransform::GetInputStreamInfo(DWORD dwInputStreamID, MFT_INPUT_STREAM_INFO *pStreamInfo) -{ - QMutexLocker locker(&m_mutex); - - if (dwInputStreamID > 0) - return MF_E_INVALIDSTREAMNUMBER; - - if (!pStreamInfo) - return E_POINTER; - - pStreamInfo->cbSize = 0; - pStreamInfo->hnsMaxLatency = 0; - pStreamInfo->cbMaxLookahead = 0; - pStreamInfo->cbAlignment = 0; - pStreamInfo->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES - | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER - | MFT_INPUT_STREAM_PROCESSES_IN_PLACE; - - return S_OK; -} - -STDMETHODIMP MFTransform::GetOutputStreamInfo(DWORD dwOutputStreamID, MFT_OUTPUT_STREAM_INFO *pStreamInfo) -{ - QMutexLocker locker(&m_mutex); - - if (dwOutputStreamID > 0) - return MF_E_INVALIDSTREAMNUMBER; - - if (!pStreamInfo) - return E_POINTER; - - pStreamInfo->cbSize = 0; - pStreamInfo->cbAlignment = 0; - pStreamInfo->dwFlags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES - | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER - | MFT_OUTPUT_STREAM_PROVIDES_SAMPLES - | MFT_OUTPUT_STREAM_DISCARDABLE; - - return S_OK; -} - -STDMETHODIMP MFTransform::GetAttributes(IMFAttributes **pAttributes) -{ - // This MFT does not support attributes. - Q_UNUSED(pAttributes); - return E_NOTIMPL; -} - -STDMETHODIMP MFTransform::GetInputStreamAttributes(DWORD dwInputStreamID, IMFAttributes **pAttributes) -{ - // This MFT does not support input stream attributes. - Q_UNUSED(dwInputStreamID); - Q_UNUSED(pAttributes); - return E_NOTIMPL; -} - -STDMETHODIMP MFTransform::GetOutputStreamAttributes(DWORD dwOutputStreamID, IMFAttributes **pAttributes) -{ - // This MFT does not support output stream attributes. - Q_UNUSED(dwOutputStreamID); - Q_UNUSED(pAttributes); - return E_NOTIMPL; -} - -STDMETHODIMP MFTransform::DeleteInputStream(DWORD dwStreamID) -{ - // This MFT has a fixed number of input streams. - Q_UNUSED(dwStreamID); - return E_NOTIMPL; -} - -STDMETHODIMP MFTransform::AddInputStreams(DWORD cStreams, DWORD *adwStreamIDs) -{ - // This MFT has a fixed number of input streams. - Q_UNUSED(cStreams); - Q_UNUSED(adwStreamIDs); - return E_NOTIMPL; -} - -STDMETHODIMP MFTransform::GetInputAvailableType(DWORD dwInputStreamID, DWORD dwTypeIndex, IMFMediaType **ppType) -{ - // We support the same input types as the video sink - if (!m_videoSinkTypeHandler) - return E_NOTIMPL; - - if (dwInputStreamID > 0) - return MF_E_INVALIDSTREAMNUMBER; - - if (!ppType) - return E_POINTER; - - return m_videoSinkTypeHandler->GetMediaTypeByIndex(dwTypeIndex, ppType); -} - -STDMETHODIMP MFTransform::GetOutputAvailableType(DWORD dwOutputStreamID, DWORD dwTypeIndex, IMFMediaType **ppType) -{ - // Since we don't modify the samples, the output type must be the same as the input type. - // Report our input type as the only available output type. - - if (dwOutputStreamID > 0) - return MF_E_INVALIDSTREAMNUMBER; - - if (!ppType) - return E_POINTER; - - // Input type must be set first - if (!m_inputType) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - if (dwTypeIndex > 0) - return MF_E_NO_MORE_TYPES; - - // Return a copy to make sure our type is not modified - if (FAILED(MFCreateMediaType(ppType))) - return E_OUTOFMEMORY; - - return m_inputType->CopyAllItems(*ppType); -} - -STDMETHODIMP MFTransform::SetInputType(DWORD dwInputStreamID, IMFMediaType *pType, DWORD dwFlags) -{ - if (dwInputStreamID > 0) - return MF_E_INVALIDSTREAMNUMBER; - - QMutexLocker locker(&m_mutex); - - if (m_sample) - return MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING; - - if (!isMediaTypeSupported(pType)) - return MF_E_INVALIDMEDIATYPE; - - if (dwFlags == MFT_SET_TYPE_TEST_ONLY) - return pType ? S_OK : E_POINTER; - - if (m_inputType) { - m_inputType->Release(); - // Input type has changed, discard output type (if it's set) so it's reset later on - DWORD flags = 0; - if (m_outputType && m_outputType->IsEqual(pType, &flags) != S_OK) { - m_outputType->Release(); - m_outputType = 0; - } - } - - m_inputType = pType; - - if (m_inputType) - m_inputType->AddRef(); - - return S_OK; -} - -STDMETHODIMP MFTransform::SetOutputType(DWORD dwOutputStreamID, IMFMediaType *pType, DWORD dwFlags) -{ - if (dwOutputStreamID > 0) - return MF_E_INVALIDSTREAMNUMBER; - - if (dwFlags == MFT_SET_TYPE_TEST_ONLY && !pType) - return E_POINTER; - - QMutexLocker locker(&m_mutex); - - // Input type must be set first - if (!m_inputType) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - if (m_sample) - return MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING; - - DWORD flags = 0; - if (pType && m_inputType->IsEqual(pType, &flags) != S_OK) - return MF_E_INVALIDMEDIATYPE; - - if (dwFlags == MFT_SET_TYPE_TEST_ONLY) - return pType ? S_OK : E_POINTER; - - if (m_outputType) - m_outputType->Release(); - - m_outputType = pType; - - if (m_outputType) { - m_outputType->AddRef(); - m_format = videoFormatForMFMediaType(m_outputType, &m_bytesPerLine); - } - - return S_OK; -} - -STDMETHODIMP MFTransform::GetInputCurrentType(DWORD dwInputStreamID, IMFMediaType **ppType) -{ - if (dwInputStreamID > 0) - return MF_E_INVALIDSTREAMNUMBER; - - if (ppType == NULL) - return E_POINTER; - - QMutexLocker locker(&m_mutex); - - if (!m_inputType) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - // Return a copy to make sure our type is not modified - if (FAILED(MFCreateMediaType(ppType))) - return E_OUTOFMEMORY; - - return m_inputType->CopyAllItems(*ppType); -} - -STDMETHODIMP MFTransform::GetOutputCurrentType(DWORD dwOutputStreamID, IMFMediaType **ppType) -{ - if (dwOutputStreamID > 0) - return MF_E_INVALIDSTREAMNUMBER; - - if (ppType == NULL) - return E_POINTER; - - QMutexLocker locker(&m_mutex); - - if (!m_outputType) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - // Return a copy to make sure our type is not modified - if (FAILED(MFCreateMediaType(ppType))) - return E_OUTOFMEMORY; - - return m_outputType->CopyAllItems(*ppType); -} - -STDMETHODIMP MFTransform::GetInputStatus(DWORD dwInputStreamID, DWORD *pdwFlags) -{ - if (dwInputStreamID > 0) - return MF_E_INVALIDSTREAMNUMBER; - - if (!pdwFlags) - return E_POINTER; - - QMutexLocker locker(&m_mutex); - - if (!m_inputType || !m_outputType) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - if (m_sample) - *pdwFlags = 0; - else - *pdwFlags = MFT_INPUT_STATUS_ACCEPT_DATA; - - return S_OK; -} - -STDMETHODIMP MFTransform::GetOutputStatus(DWORD *pdwFlags) -{ - if (!pdwFlags) - return E_POINTER; - - QMutexLocker locker(&m_mutex); - - if (!m_inputType || !m_outputType) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - if (m_sample) - *pdwFlags = MFT_OUTPUT_STATUS_SAMPLE_READY; - else - *pdwFlags = 0; - - return S_OK; -} - -STDMETHODIMP MFTransform::SetOutputBounds(LONGLONG hnsLowerBound, LONGLONG hnsUpperBound) -{ - Q_UNUSED(hnsLowerBound); - Q_UNUSED(hnsUpperBound); - return E_NOTIMPL; -} - -STDMETHODIMP MFTransform::ProcessEvent(DWORD dwInputStreamID, IMFMediaEvent *pEvent) -{ - // This MFT ignores all events, and the pipeline should send all events downstream. - Q_UNUSED(dwInputStreamID); - Q_UNUSED(pEvent); - return E_NOTIMPL; -} - -STDMETHODIMP MFTransform::ProcessMessage(MFT_MESSAGE_TYPE eMessage, ULONG_PTR ulParam) -{ - Q_UNUSED(ulParam); - - HRESULT hr = S_OK; - - switch (eMessage) - { - case MFT_MESSAGE_COMMAND_FLUSH: - hr = OnFlush(); - break; - - case MFT_MESSAGE_COMMAND_DRAIN: - // Drain: Tells the MFT not to accept any more input until - // all of the pending output has been processed. That is our - // default behevior already, so there is nothing to do. - break; - - case MFT_MESSAGE_SET_D3D_MANAGER: - // The pipeline should never send this message unless the MFT - // has the MF_SA_D3D_AWARE attribute set to TRUE. However, if we - // do get this message, it's invalid and we don't implement it. - hr = E_NOTIMPL; - break; - - // The remaining messages do not require any action from this MFT. - case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING: - case MFT_MESSAGE_NOTIFY_END_STREAMING: - case MFT_MESSAGE_NOTIFY_END_OF_STREAM: - case MFT_MESSAGE_NOTIFY_START_OF_STREAM: - break; - } - - return hr; -} - -STDMETHODIMP MFTransform::ProcessInput(DWORD dwInputStreamID, IMFSample *pSample, DWORD dwFlags) -{ - if (dwInputStreamID > 0) - return MF_E_INVALIDSTREAMNUMBER; - - if (dwFlags != 0) - return E_INVALIDARG; // dwFlags is reserved and must be zero. - - QMutexLocker locker(&m_mutex); - - if (!m_inputType) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - if (m_sample) - return MF_E_NOTACCEPTING; - - // Validate the number of buffers. There should only be a single buffer to hold the video frame. - DWORD dwBufferCount = 0; - HRESULT hr = pSample->GetBufferCount(&dwBufferCount); - if (FAILED(hr)) - return hr; - - if (dwBufferCount == 0) - return E_FAIL; - - if (dwBufferCount > 1) - return MF_E_SAMPLE_HAS_TOO_MANY_BUFFERS; - - m_sample = pSample; - m_sample->AddRef(); - - QMutexLocker lockerProbe(&m_videoProbeMutex); - -// if (!m_videoProbes.isEmpty()) { -// QVideoFrame frame = makeVideoFrame(); - -// for (MFVideoProbeControl* probe : qAsConst(m_videoProbes)) -// probe->bufferProbed(frame); -// } - - return S_OK; -} - -STDMETHODIMP MFTransform::ProcessOutput(DWORD dwFlags, DWORD cOutputBufferCount, MFT_OUTPUT_DATA_BUFFER *pOutputSamples, DWORD *pdwStatus) -{ - if (pOutputSamples == NULL || pdwStatus == NULL) - return E_POINTER; - - if (cOutputBufferCount != 1) - return E_INVALIDARG; - - QMutexLocker locker(&m_mutex); - - if (!m_inputType) - return MF_E_TRANSFORM_TYPE_NOT_SET; - - if (!m_outputType) { - pOutputSamples[0].dwStatus = MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE; - return MF_E_TRANSFORM_STREAM_CHANGE; - } - - IMFMediaBuffer *input = NULL; - IMFMediaBuffer *output = NULL; - - if (dwFlags == MFT_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER) - goto done; - else if (dwFlags != 0) - return E_INVALIDARG; - - if (!m_sample) - return MF_E_TRANSFORM_NEED_MORE_INPUT; - - // Since the MFT_OUTPUT_STREAM_PROVIDES_SAMPLES flag is set, the client - // should not be providing samples here - if (pOutputSamples[0].pSample != NULL) - return E_INVALIDARG; - - pOutputSamples[0].pSample = m_sample; - pOutputSamples[0].pSample->AddRef(); - - // Send video frame to probes - // We do it here (instead of inside ProcessInput) to make sure samples discarded by the renderer - // are not sent. - m_videoProbeMutex.lock(); -// if (!m_videoProbes.isEmpty()) { -// QVideoFrame frame = makeVideoFrame(); - -// for (MFVideoProbeControl* probe : qAsConst(m_videoProbes)) -// probe->bufferProbed(frame); -// } - m_videoProbeMutex.unlock(); - -done: - pOutputSamples[0].dwStatus = 0; - *pdwStatus = 0; - - m_sample->Release(); - m_sample = 0; - - if (input) - input->Release(); - if (output) - output->Release(); - - return S_OK; -} - -HRESULT MFTransform::OnFlush() -{ - QMutexLocker locker(&m_mutex); - - if (m_sample) { - m_sample->Release(); - m_sample = 0; - } - return S_OK; -} - -QVideoFrameFormat MFTransform::videoFormatForMFMediaType(IMFMediaType *mediaType, int *bytesPerLine) -{ - UINT32 stride; - if (FAILED(mediaType->GetUINT32(MF_MT_DEFAULT_STRIDE, &stride))) { - *bytesPerLine = 0; - return QVideoFrameFormat(); - } - - *bytesPerLine = (int)stride; - - QSize size; - UINT32 width, height; - if (FAILED(MFGetAttributeSize(mediaType, MF_MT_FRAME_SIZE, &width, &height))) - return QVideoFrameFormat(); - - size.setWidth(width); - size.setHeight(height); - - GUID subtype = GUID_NULL; - if (FAILED(mediaType->GetGUID(MF_MT_SUBTYPE, &subtype))) - return QVideoFrameFormat(); - - QVideoFrameFormat::PixelFormat pixelFormat = - QWindowsMultimediaUtils::pixelFormatFromMediaSubtype(subtype); - QVideoFrameFormat format(size, pixelFormat); - - quint32 num, den; - if (SUCCEEDED(MFGetAttributeRatio(mediaType, MF_MT_FRAME_RATE, &num, &den))) { - format.setFrameRate(qreal(num)/den); - } - - return format; -} - -QVideoFrame MFTransform::makeVideoFrame() -{ - QVideoFrame frame; - - if (!m_format.isValid()) - return frame; - - IMFMediaBuffer *buffer = 0; - - do { - if (FAILED(m_sample->ConvertToContiguousBuffer(&buffer))) - break; - - QByteArray array = dataFromBuffer(buffer, m_format.frameHeight(), &m_bytesPerLine); - if (array.isEmpty()) - break; - - // Wrapping IMFSample or IMFMediaBuffer in a QVideoFrame is not possible because we cannot hold - // IMFSample for a "long" time without affecting the rest of the topology. - // If IMFSample is held for more than 5 frames decoder starts to reuse it even though it hasn't been released it yet. - // That is why we copy data from IMFMediaBuffer here. - frame = QVideoFrame(new QMemoryVideoBuffer(array, m_bytesPerLine), m_format); - - // WMF uses 100-nanosecond units, Qt uses microseconds - LONGLONG startTime = -1; - if (SUCCEEDED(m_sample->GetSampleTime(&startTime))) { - frame.setStartTime(startTime * 0.1); - - LONGLONG duration = -1; - if (SUCCEEDED(m_sample->GetSampleDuration(&duration))) - frame.setEndTime((startTime + duration) * 0.1); - } - } while (false); - - if (buffer) - buffer->Release(); - - return frame; -} - -QByteArray MFTransform::dataFromBuffer(IMFMediaBuffer *buffer, int height, int *bytesPerLine) -{ - QByteArray array; - BYTE *bytes; - DWORD length; - HRESULT hr = buffer->Lock(&bytes, NULL, &length); - if (SUCCEEDED(hr)) { - array = QByteArray((const char *)bytes, (int)length); - buffer->Unlock(); - } else { - // try to lock as Direct3DSurface - IDirect3DSurface9 *surface = 0; - do { - if (FAILED(MFGetService(buffer, MR_BUFFER_SERVICE, IID_IDirect3DSurface9, (void**)&surface))) - break; - - D3DLOCKED_RECT rect; - if (FAILED(surface->LockRect(&rect, NULL, D3DLOCK_READONLY))) - break; - - if (bytesPerLine) - *bytesPerLine = (int)rect.Pitch; - - array = QByteArray((const char *)rect.pBits, rect.Pitch * height); - surface->UnlockRect(); - } while (false); - - if (surface) { - surface->Release(); - surface = 0; - } - } - - return array; -} - -bool MFTransform::isMediaTypeSupported(IMFMediaType *type) -{ - // If we don't have the video sink's type handler, - // assume it supports anything... - if (!m_videoSinkTypeHandler || !type) - return true; - - return m_videoSinkTypeHandler->IsMediaTypeSupported(type, NULL) == S_OK; -} diff --git a/src/multimedia/platform/windows/player/mftvideo_p.h b/src/multimedia/platform/windows/player/mftvideo_p.h deleted file mode 100644 index a8a0337cc..000000000 --- a/src/multimedia/platform/windows/player/mftvideo_p.h +++ /dev/null @@ -1,131 +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$ -** -****************************************************************************/ - -#ifndef MFTRANSFORM_H -#define MFTRANSFORM_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <mfapi.h> -#include <mfidl.h> -#include <QtCore/qlist.h> -#include <QtCore/qmutex.h> -#include <QtMultimedia/qvideoframeformat.h> - -QT_USE_NAMESPACE - -class MFVideoProbeControl; - -QT_BEGIN_NAMESPACE -class QVideoFrame; -QT_END_NAMESPACE - -class MFTransform: public IMFTransform -{ -public: - MFTransform(); - ~MFTransform(); - - void addProbe(MFVideoProbeControl* probe); - void removeProbe(MFVideoProbeControl* probe); - - void setVideoSink(IUnknown *videoSink); - - // IUnknown methods - STDMETHODIMP QueryInterface(REFIID iid, void** ppv); - STDMETHODIMP_(ULONG) AddRef(); - STDMETHODIMP_(ULONG) Release(); - - // IMFTransform methods - STDMETHODIMP GetStreamLimits(DWORD *pdwInputMinimum, DWORD *pdwInputMaximum, DWORD *pdwOutputMinimum, DWORD *pdwOutputMaximum); - STDMETHODIMP GetStreamCount(DWORD *pcInputStreams, DWORD *pcOutputStreams); - STDMETHODIMP GetStreamIDs(DWORD dwInputIDArraySize, DWORD *pdwInputIDs, DWORD dwOutputIDArraySize, DWORD *pdwOutputIDs); - STDMETHODIMP GetInputStreamInfo(DWORD dwInputStreamID, MFT_INPUT_STREAM_INFO *pStreamInfo); - STDMETHODIMP GetOutputStreamInfo(DWORD dwOutputStreamID, MFT_OUTPUT_STREAM_INFO *pStreamInfo); - STDMETHODIMP GetAttributes(IMFAttributes **pAttributes); - STDMETHODIMP GetInputStreamAttributes(DWORD dwInputStreamID, IMFAttributes **pAttributes); - STDMETHODIMP GetOutputStreamAttributes(DWORD dwOutputStreamID, IMFAttributes **pAttributes); - STDMETHODIMP DeleteInputStream(DWORD dwStreamID); - STDMETHODIMP AddInputStreams(DWORD cStreams, DWORD *adwStreamIDs); - STDMETHODIMP GetInputAvailableType(DWORD dwInputStreamID, DWORD dwTypeIndex, IMFMediaType **ppType); - STDMETHODIMP GetOutputAvailableType(DWORD dwOutputStreamID,DWORD dwTypeIndex, IMFMediaType **ppType); - STDMETHODIMP SetInputType(DWORD dwInputStreamID, IMFMediaType *pType, DWORD dwFlags); - STDMETHODIMP SetOutputType(DWORD dwOutputStreamID, IMFMediaType *pType, DWORD dwFlags); - STDMETHODIMP GetInputCurrentType(DWORD dwInputStreamID, IMFMediaType **ppType); - STDMETHODIMP GetOutputCurrentType(DWORD dwOutputStreamID, IMFMediaType **ppType); - STDMETHODIMP GetInputStatus(DWORD dwInputStreamID, DWORD *pdwFlags); - STDMETHODIMP GetOutputStatus(DWORD *pdwFlags); - STDMETHODIMP SetOutputBounds(LONGLONG hnsLowerBound, LONGLONG hnsUpperBound); - STDMETHODIMP ProcessEvent(DWORD dwInputStreamID, IMFMediaEvent *pEvent); - STDMETHODIMP ProcessMessage(MFT_MESSAGE_TYPE eMessage, ULONG_PTR ulParam); - STDMETHODIMP ProcessInput(DWORD dwInputStreamID, IMFSample *pSample, DWORD dwFlags); - STDMETHODIMP ProcessOutput(DWORD dwFlags, DWORD cOutputBufferCount, MFT_OUTPUT_DATA_BUFFER *pOutputSamples, DWORD *pdwStatus); - -private: - HRESULT OnFlush(); - static QVideoFrameFormat videoFormatForMFMediaType(IMFMediaType *mediaType, int *bytesPerLine); - QVideoFrame makeVideoFrame(); - QByteArray dataFromBuffer(IMFMediaBuffer *buffer, int height, int *bytesPerLine); - bool isMediaTypeSupported(IMFMediaType *type); - - long m_cRef; - IMFMediaType *m_inputType; - IMFMediaType *m_outputType; - IMFSample *m_sample; - QMutex m_mutex; - - IMFMediaTypeHandler *m_videoSinkTypeHandler; - -// QList<MFVideoProbeControl*> m_videoProbes; - QMutex m_videoProbeMutex; - - QVideoFrameFormat m_format; - int m_bytesPerLine; -}; - -#endif diff --git a/src/multimedia/platform/windows/player/mfvideorenderercontrol.cpp b/src/multimedia/platform/windows/player/mfvideorenderercontrol.cpp deleted file mode 100644 index a2a83d1cb..000000000 --- a/src/multimedia/platform/windows/player/mfvideorenderercontrol.cpp +++ /dev/null @@ -1,2318 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Mobility Components. -** -** $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$ -** -****************************************************************************/ - -#include "mfvideorenderercontrol_p.h" -#include "mfactivate_p.h" - -#include "evrcustompresenter_p.h" - -#include <private/qplatformvideosink_p.h> -#include <private/qabstractvideobuffer_p.h> -#include <qvideosink.h> -#include <qvideoframeformat.h> -#include <qtcore/qtimer.h> -#include <qtcore/qmutex.h> -#include <qtcore/qcoreevent.h> -#include <qtcore/qcoreapplication.h> -#include <qtcore/qthread.h> -#include "guiddef.h" -#include <qtcore/qdebug.h> - -//#define DEBUG_MEDIAFOUNDATION -#define PAD_TO_DWORD(x) (((x) + 3) & ~3) - -namespace -{ - class MediaSampleVideoBuffer : public QAbstractVideoBuffer - { - public: - MediaSampleVideoBuffer(IMFMediaBuffer *buffer, int bytesPerLine) - : QAbstractVideoBuffer(QVideoFrame::NoHandle) - , m_buffer(buffer) - , m_bytesPerLine(bytesPerLine) - , m_mapMode(QVideoFrame::NotMapped) - { - buffer->AddRef(); - } - - ~MediaSampleVideoBuffer() - { - m_buffer->Release(); - } - - MapData map(QVideoFrame::MapMode mode) override - { - MapData mapData; - if (m_mapMode == QVideoFrame::NotMapped && mode != QVideoFrame::NotMapped) { - BYTE *bytes; - DWORD length; - HRESULT hr = m_buffer->Lock(&bytes, NULL, &length); - if (SUCCEEDED(hr)) { - mapData.nPlanes = 1; - mapData.bytesPerLine[0] = m_bytesPerLine; - mapData.data[0] = reinterpret_cast<uchar *>(bytes); - mapData.size[0] = qsizetype(length); - m_mapMode = mode; - } else { - qWarning("Faild to lock mf buffer!"); - } - } - return mapData; - } - - void unmap() override - { - if (m_mapMode == QVideoFrame::NotMapped) - return; - m_mapMode = QVideoFrame::NotMapped; - m_buffer->Unlock(); - } - - QVideoFrame::MapMode mapMode() const override - { - return m_mapMode; - } - - private: - IMFMediaBuffer *m_buffer; - int m_bytesPerLine; - QVideoFrame::MapMode m_mapMode; - }; - - // Custom interface for handling IMFStreamSink::PlaceMarker calls asynchronously. - MIDL_INTERFACE("a3ff32de-1031-438a-8b47-82f8acda59b7") - IMarker : public IUnknown - { - virtual STDMETHODIMP GetMarkerType(MFSTREAMSINK_MARKER_TYPE *pType) = 0; - virtual STDMETHODIMP GetMarkerValue(PROPVARIANT *pvar) = 0; - virtual STDMETHODIMP GetContext(PROPVARIANT *pvar) = 0; - }; - - class Marker : public IMarker - { - public: - static HRESULT Create( - MFSTREAMSINK_MARKER_TYPE eMarkerType, - const PROPVARIANT* pvarMarkerValue, // Can be NULL. - const PROPVARIANT* pvarContextValue, // Can be NULL. - IMarker **ppMarker) - { - if (ppMarker == NULL) - return E_POINTER; - - HRESULT hr = S_OK; - Marker *pMarker = new Marker(eMarkerType); - if (pMarker == NULL) - hr = E_OUTOFMEMORY; - - // Copy the marker data. - if (SUCCEEDED(hr) && pvarMarkerValue) - hr = PropVariantCopy(&pMarker->m_varMarkerValue, pvarMarkerValue); - - if (SUCCEEDED(hr) && pvarContextValue) - hr = PropVariantCopy(&pMarker->m_varContextValue, pvarContextValue); - - if (SUCCEEDED(hr)) { - *ppMarker = pMarker; - (*ppMarker)->AddRef(); - } - - if (pMarker) - pMarker->Release(); - - return hr; - } - - // IUnknown methods. - STDMETHODIMP QueryInterface(REFIID iid, void** ppv) - { - if (!ppv) - return E_POINTER; - if (iid == IID_IUnknown) { - *ppv = static_cast<IUnknown*>(this); - } else if (iid == __uuidof(IMarker)) { - *ppv = static_cast<IMarker*>(this); - } else { - *ppv = NULL; - return E_NOINTERFACE; - } - AddRef(); - return S_OK; - } - - STDMETHODIMP_(ULONG) AddRef() - { - return InterlockedIncrement(&m_cRef); - } - - STDMETHODIMP_(ULONG) Release() - { - LONG cRef = InterlockedDecrement(&m_cRef); - if (cRef == 0) - delete this; - // For thread safety, return a temporary variable. - return cRef; - } - - STDMETHODIMP GetMarkerType(MFSTREAMSINK_MARKER_TYPE *pType) - { - if (pType == NULL) - return E_POINTER; - *pType = m_eMarkerType; - return S_OK; - } - - STDMETHODIMP GetMarkerValue(PROPVARIANT *pvar) - { - if (pvar == NULL) - return E_POINTER; - return PropVariantCopy(pvar, &m_varMarkerValue); - } - - STDMETHODIMP GetContext(PROPVARIANT *pvar) - { - if (pvar == NULL) - return E_POINTER; - return PropVariantCopy(pvar, &m_varContextValue); - } - - protected: - MFSTREAMSINK_MARKER_TYPE m_eMarkerType; - PROPVARIANT m_varMarkerValue; - PROPVARIANT m_varContextValue; - - private: - long m_cRef; - - Marker(MFSTREAMSINK_MARKER_TYPE eMarkerType) : m_cRef(1), m_eMarkerType(eMarkerType) - { - PropVariantInit(&m_varMarkerValue); - PropVariantInit(&m_varContextValue); - } - - virtual ~Marker() - { - PropVariantClear(&m_varMarkerValue); - PropVariantClear(&m_varContextValue); - } - }; - - class MediaStream : public QObject, public IMFStreamSink, public IMFMediaTypeHandler - { - Q_OBJECT - friend class MFVideoRendererControl; - public: - static const DWORD DEFAULT_MEDIA_STREAM_ID = 0x0; - - MediaStream(IMFMediaSink *parent, MFVideoRendererControl *rendererControl) - : m_cRef(1) - , m_eventQueue(0) - , m_shutdown(false) - , m_videoSink(0) - , m_state(State_TypeNotSet) - , m_currentFormatIndex(-1) - , m_bytesPerLine(0) - , m_workQueueId(0) - , m_workQueueCB(this, &MediaStream::onDispatchWorkItem) - , m_finalizeResult(0) - , m_scheduledBuffer(0) - , m_bufferStartTime(-1) - , m_bufferDuration(-1) - , m_presentationClock(0) - , m_sampleRequested(false) - , m_currentMediaType(0) - , m_prerolling(false) - , m_prerollTargetTime(0) - , m_startTime(0) - , m_rendererControl(rendererControl) - , m_rate(1.f) - { - m_sink = parent; - - if (FAILED(MFCreateEventQueue(&m_eventQueue))) - qWarning("Failed to create mf event queue!"); - if (FAILED(MFAllocateWorkQueue(&m_workQueueId))) - qWarning("Failed to allocated mf work queue!"); - } - - ~MediaStream() - { - Q_ASSERT(m_shutdown); - } - - //from IUnknown - STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject) - { - if (!ppvObject) - return E_POINTER; - if (riid == IID_IMFStreamSink) { - *ppvObject = static_cast<IMFStreamSink*>(this); - } else if (riid == IID_IMFMediaEventGenerator) { - *ppvObject = static_cast<IMFMediaEventGenerator*>(this); - } else if (riid == IID_IMFMediaTypeHandler) { - *ppvObject = static_cast<IMFMediaTypeHandler*>(this); - } else if (riid == IID_IUnknown) { - *ppvObject = static_cast<IUnknown*>(static_cast<IMFStreamSink*>(this)); - } else { - *ppvObject = NULL; - return E_NOINTERFACE; - } - AddRef(); - return S_OK; - } - - STDMETHODIMP_(ULONG) AddRef(void) - { - return InterlockedIncrement(&m_cRef); - } - - STDMETHODIMP_(ULONG) Release(void) - { - LONG cRef = InterlockedDecrement(&m_cRef); - if (cRef == 0) - delete this; - // For thread safety, return a temporary variable. - return cRef; - } - - //from IMFMediaEventGenerator - STDMETHODIMP GetEvent( - DWORD dwFlags, - IMFMediaEvent **ppEvent) - { - // GetEvent can block indefinitely, so we don't hold the lock. - // This requires some juggling with the event queue pointer. - HRESULT hr = S_OK; - IMFMediaEventQueue *queue = NULL; - - m_mutex.lock(); - if (m_shutdown) - hr = MF_E_SHUTDOWN; - if (SUCCEEDED(hr)) { - queue = m_eventQueue; - queue->AddRef(); - } - m_mutex.unlock(); - - // Now get the event. - if (SUCCEEDED(hr)) { - hr = queue->GetEvent(dwFlags, ppEvent); - queue->Release(); - } - - return hr; - } - - STDMETHODIMP BeginGetEvent( - IMFAsyncCallback *pCallback, - IUnknown *punkState) - { - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - return m_eventQueue->BeginGetEvent(pCallback, punkState); - } - - STDMETHODIMP EndGetEvent( - IMFAsyncResult *pResult, - IMFMediaEvent **ppEvent) - { - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - return m_eventQueue->EndGetEvent(pResult, ppEvent); - } - - STDMETHODIMP QueueEvent( - MediaEventType met, - REFGUID guidExtendedType, - HRESULT hrStatus, - const PROPVARIANT *pvValue) - { -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "MediaStream::QueueEvent" << met; -#endif - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - return m_eventQueue->QueueEventParamVar(met, guidExtendedType, hrStatus, pvValue); - } - - //from IMFStreamSink - STDMETHODIMP GetMediaSink( - IMFMediaSink **ppMediaSink) - { - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - else if (!ppMediaSink) - return E_INVALIDARG; - - m_sink->AddRef(); - *ppMediaSink = m_sink; - return S_OK; - } - - STDMETHODIMP GetIdentifier( - DWORD *pdwIdentifier) - { - *pdwIdentifier = MediaStream::DEFAULT_MEDIA_STREAM_ID; - return S_OK; - } - - STDMETHODIMP GetMediaTypeHandler( - IMFMediaTypeHandler **ppHandler) - { - LPVOID handler = NULL; - HRESULT hr = QueryInterface(IID_IMFMediaTypeHandler, &handler); - *ppHandler = (IMFMediaTypeHandler*)(handler); - return hr; - } - - STDMETHODIMP ProcessSample( - IMFSample *pSample) - { - if (pSample == NULL) - return E_INVALIDARG; - HRESULT hr = S_OK; - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - - if (!m_prerolling) { - hr = validateOperation(OpProcessSample); - if (FAILED(hr)) - return hr; - } - - pSample->AddRef(); - m_sampleQueue.push_back(pSample); - - // Unless we are paused, start an async operation to dispatch the next sample. - if (m_state != State_Paused) - hr = queueAsyncOperation(OpProcessSample); - - return hr; - } - - STDMETHODIMP PlaceMarker( - MFSTREAMSINK_MARKER_TYPE eMarkerType, - const PROPVARIANT *pvarMarkerValue, - const PROPVARIANT *pvarContextValue) - { - HRESULT hr = S_OK; - QMutexLocker locker(&m_mutex); - IMarker *pMarker = NULL; - if (m_shutdown) - return MF_E_SHUTDOWN; - - hr = validateOperation(OpPlaceMarker); - if (FAILED(hr)) - return hr; - - // Create a marker object and put it on the sample queue. - hr = Marker::Create(eMarkerType, pvarMarkerValue, pvarContextValue, &pMarker); - if (FAILED(hr)) - return hr; - - m_sampleQueue.push_back(pMarker); - - // Unless we are paused, start an async operation to dispatch the next sample/marker. - if (m_state != State_Paused) - hr = queueAsyncOperation(OpPlaceMarker); // Increments ref count on pOp. - return hr; - } - - STDMETHODIMP Flush( void) - { -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "MediaStream::Flush"; -#endif - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - // Note: Even though we are flushing data, we still need to send - // any marker events that were queued. - clearBufferCache(); - return processSamplesFromQueue(DropSamples); - } - - //from IMFMediaTypeHandler - STDMETHODIMP IsMediaTypeSupported( - IMFMediaType *pMediaType, - IMFMediaType **ppMediaType) - { - if (ppMediaType) - *ppMediaType = NULL; - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - - int index = getMediaTypeIndex(pMediaType); - if (index < 0) { - if (ppMediaType && m_mediaTypes.size() > 0) { - *ppMediaType = m_mediaTypes[0]; - (*ppMediaType)->AddRef(); - } - return MF_E_INVALIDMEDIATYPE; - } - - BOOL compressed = TRUE; - pMediaType->IsCompressedFormat(&compressed); - if (compressed) { - if (ppMediaType && (SUCCEEDED(MFCreateMediaType(ppMediaType)))) { - (*ppMediaType)->CopyAllItems(pMediaType); - (*ppMediaType)->SetUINT32(MF_MT_FIXED_SIZE_SAMPLES, TRUE); - (*ppMediaType)->SetUINT32(MF_MT_COMPRESSED, FALSE); - (*ppMediaType)->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); - } - return MF_E_INVALIDMEDIATYPE; - } - - return S_OK; - } - - STDMETHODIMP GetMediaTypeCount( - DWORD *pdwTypeCount) - { - if (pdwTypeCount == NULL) - return E_INVALIDARG; - QMutexLocker locker(&m_mutex); - *pdwTypeCount = DWORD(m_mediaTypes.size()); - return S_OK; - } - - STDMETHODIMP GetMediaTypeByIndex( - DWORD dwIndex, - IMFMediaType **ppType) - { - if (ppType == NULL) - return E_INVALIDARG; - HRESULT hr = S_OK; - QMutexLocker locker(&m_mutex); - if (m_shutdown) - hr = MF_E_SHUTDOWN; - - if (SUCCEEDED(hr)) { - if (dwIndex >= DWORD(m_mediaTypes.size())) - hr = MF_E_NO_MORE_TYPES; - } - - if (SUCCEEDED(hr)) { - *ppType = m_mediaTypes[dwIndex]; - (*ppType)->AddRef(); - } - return hr; - } - - STDMETHODIMP SetCurrentMediaType( - IMFMediaType *pMediaType) - { - HRESULT hr = S_OK; - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - - DWORD flag = MF_MEDIATYPE_EQUAL_MAJOR_TYPES | - MF_MEDIATYPE_EQUAL_FORMAT_TYPES | - MF_MEDIATYPE_EQUAL_FORMAT_DATA; - - if (m_currentMediaType && (m_currentMediaType->IsEqual(pMediaType, &flag) == S_OK)) - return S_OK; - - hr = validateOperation(OpSetMediaType); - - if (SUCCEEDED(hr)) { - int index = getMediaTypeIndex(pMediaType); - if (index >= 0) { - UINT64 size; - hr = pMediaType->GetUINT64(MF_MT_FRAME_SIZE, &size); - if (SUCCEEDED(hr)) { - m_currentFormatIndex = index; - int width = int(HI32(size)); - int height = int(LO32(size)); - QVideoFrameFormat format(QSize(width, height), m_pixelFormats[index]); - m_surfaceFormat = format; - - MFVideoArea viewport; - if (SUCCEEDED(pMediaType->GetBlob(MF_MT_GEOMETRIC_APERTURE, - reinterpret_cast<UINT8*>(&viewport), - sizeof(MFVideoArea), - NULL))) { - - m_surfaceFormat.setViewport(QRect(viewport.OffsetX.value, - viewport.OffsetY.value, - viewport.Area.cx, - viewport.Area.cy)); - } - - if (FAILED(pMediaType->GetUINT32(MF_MT_DEFAULT_STRIDE, (UINT32*)&m_bytesPerLine))) { - m_bytesPerLine = getBytesPerLine(format); - } - - m_state = State_Ready; - if (m_currentMediaType) - m_currentMediaType->Release(); - m_currentMediaType = pMediaType; - pMediaType->AddRef(); - } - } else { - hr = MF_E_INVALIDREQUEST; - } - } - return hr; - } - - STDMETHODIMP GetCurrentMediaType( - IMFMediaType **ppMediaType) - { - if (ppMediaType == NULL) - return E_INVALIDARG; - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - if (m_currentFormatIndex < 0) - return MF_E_NOT_INITIALIZED; - *ppMediaType = m_currentMediaType; - (*ppMediaType)->AddRef(); - return S_OK; - } - - STDMETHODIMP GetMajorType( - GUID *pguidMajorType) - { - if (pguidMajorType == NULL) - return E_INVALIDARG; - *pguidMajorType = MFMediaType_Video; - return S_OK; - } - - // - void setSink(QVideoSink *sink) - { - m_mutex.lock(); - m_videoSink = sink; - m_mutex.unlock(); - supportedFormatsChanged(); - } - - void setClock(IMFPresentationClock *presentationClock) - { - QMutexLocker locker(&m_mutex); - if (!m_shutdown) { - if (m_presentationClock) - m_presentationClock->Release(); - m_presentationClock = presentationClock; - if (m_presentationClock) - m_presentationClock->AddRef(); - } - } - - void shutdown() - { - QMutexLocker locker(&m_mutex); - Q_ASSERT(!m_shutdown); - - if (m_currentMediaType) { - m_currentMediaType->Release(); - m_currentMediaType = NULL; - m_currentFormatIndex = -1; - } - - if (m_eventQueue) - m_eventQueue->Shutdown(); - - MFUnlockWorkQueue(m_workQueueId); - - if (m_presentationClock) { - m_presentationClock->Release(); - m_presentationClock = NULL; - } - - clearMediaTypes(); - clearSampleQueue(); - clearBufferCache(); - - if (m_eventQueue) { - m_eventQueue->Release(); - m_eventQueue = NULL; - } - - m_shutdown = true; - } - - HRESULT startPreroll(MFTIME hnsUpcomingStartTime) - { - QMutexLocker locker(&m_mutex); - HRESULT hr = validateOperation(OpPreroll); - if (SUCCEEDED(hr)) { - m_state = State_Prerolling; - m_prerollTargetTime = hnsUpcomingStartTime; - hr = queueAsyncOperation(OpPreroll); - } - return hr; - } - - HRESULT finalize(IMFAsyncCallback *pCallback, IUnknown *punkState) - { - QMutexLocker locker(&m_mutex); - HRESULT hr = S_OK; - hr = validateOperation(OpFinalize); - if (SUCCEEDED(hr) && m_finalizeResult != NULL) - hr = MF_E_INVALIDREQUEST; // The operation is already pending. - - // Create and store the async result object. - if (SUCCEEDED(hr)) - hr = MFCreateAsyncResult(NULL, pCallback, punkState, &m_finalizeResult); - - if (SUCCEEDED(hr)) { - m_state = State_Finalized; - hr = queueAsyncOperation(OpFinalize); - } - return hr; - } - - HRESULT start(MFTIME start) - { -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "MediaStream::start" << start; -#endif - HRESULT hr = S_OK; - QMutexLocker locker(&m_mutex); - if (m_rate != 0) - hr = validateOperation(OpStart); - - if (SUCCEEDED(hr)) { - MFTIME sysTime; - if (start != PRESENTATION_CURRENT_POSITION) - m_startTime = start; // Cache the start time. - else - m_presentationClock->GetCorrelatedTime(0, &m_startTime, &sysTime); - m_state = State_Started; - hr = queueAsyncOperation(OpStart); - } - return hr; - } - - HRESULT restart() - { -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "MediaStream::restart"; -#endif - QMutexLocker locker(&m_mutex); - HRESULT hr = validateOperation(OpRestart); - if (SUCCEEDED(hr)) { - m_state = State_Started; - hr = queueAsyncOperation(OpRestart); - } - return hr; - } - - HRESULT stop() - { -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "MediaStream::stop"; -#endif - QMutexLocker locker(&m_mutex); - HRESULT hr = validateOperation(OpStop); - if (SUCCEEDED(hr)) { - m_state = State_Stopped; - hr = queueAsyncOperation(OpStop); - } - return hr; - } - - HRESULT pause() - { -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "MediaStream::pause"; -#endif - QMutexLocker locker(&m_mutex); - HRESULT hr = validateOperation(OpPause); - if (SUCCEEDED(hr)) { - m_state = State_Paused; - hr = queueAsyncOperation(OpPause); - } - return hr; - } - - HRESULT setRate(float rate) - { -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "MediaStream::setRate" << rate; -#endif - QMutexLocker locker(&m_mutex); - HRESULT hr = validateOperation(OpSetRate); - if (SUCCEEDED(hr)) { - m_rate = rate; - hr = queueAsyncOperation(OpSetRate); - } - return hr; - } - - void supportedFormatsChanged() - { - QMutexLocker locker(&m_mutex); - m_pixelFormats.clear(); - clearMediaTypes(); - if (!m_videoSink) - return; - for (int f = 0; f < QVideoFrameFormat::NPixelFormats; ++f) { - QVideoFrameFormat::PixelFormat format = QVideoFrameFormat::PixelFormat(f); - IMFMediaType *mediaType; - if (FAILED(MFCreateMediaType(&mediaType))) { - qWarning("Failed to create mf media type!"); - continue; - } - mediaType->SetUINT32(MF_MT_FIXED_SIZE_SAMPLES, TRUE); - mediaType->SetUINT32(MF_MT_COMPRESSED, FALSE); - mediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); - mediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); - switch (format) { - case QVideoFrameFormat::Format_BGRA8888: - case QVideoFrameFormat::Format_BGRA8888_Premultiplied: - mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_ARGB32); - break; - case QVideoFrameFormat::Format_BGRX8888: - mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32); - break; - case QVideoFrameFormat::Format_AYUV: - case QVideoFrameFormat::Format_AYUV_Premultiplied: - mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_AYUV); - break; - case QVideoFrameFormat::Format_YUV420P: - mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_I420); - break; - case QVideoFrameFormat::Format_UYVY: - mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_UYVY); - break; - case QVideoFrameFormat::Format_YV12: - mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_YV12); - break; - case QVideoFrameFormat::Format_NV12: - mediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12); - break; - default: - mediaType->Release(); - continue; - } - // #### QAbstractVideoSurface::supportedPixelFormats() returns formats in descending - // order of preference, while IMFMediaTypeHandler is supposed to return supported - // formats in ascending order of preference. We need to reverse the list. - m_pixelFormats.prepend(format); - m_mediaTypes.prepend(mediaType); - } - } - - void present() - { - QMutexLocker locker(&m_mutex); - if (!m_scheduledBuffer) - return; - QVideoFrame frame = QVideoFrame( - new MediaSampleVideoBuffer(m_scheduledBuffer, m_bytesPerLine), m_surfaceFormat); - frame.setStartTime(m_bufferStartTime * 0.1); - frame.setEndTime((m_bufferStartTime + m_bufferDuration) * 0.1); - m_videoSink->platformVideoSink()->setVideoFrame(frame); - m_scheduledBuffer->Release(); - m_scheduledBuffer = NULL; - if (m_rate != 0) - schedulePresentation(true); - } - - void clearScheduledFrame() - { - QMutexLocker locker(&m_mutex); - if (m_scheduledBuffer) { - m_scheduledBuffer->Release(); - m_scheduledBuffer = NULL; - schedulePresentation(true); - } - } - - enum - { - PresentSurface - }; - - class PresentEvent : public QEvent - { - public: - PresentEvent(MFTIME targetTime) - : QEvent(QEvent::Type(PresentSurface)) - , m_time(targetTime) - { - } - - MFTIME targetTime() - { - return m_time; - } - - private: - MFTIME m_time; - }; - - protected: - HRESULT m_startResult; - - private: - enum FlushState - { - DropSamples = 0, - WriteSamples - }; - - // State enum: Defines the current state of the stream. - enum State - { - State_TypeNotSet = 0, // No media type is set - State_Ready, // Media type is set, Start has never been called. - State_Prerolling, - State_Started, - State_Paused, - State_Stopped, - State_WaitForSurfaceStart, - State_Finalized, - State_Count = State_Finalized + 1 // Number of states - }; - - // StreamOperation: Defines various operations that can be performed on the stream. - enum StreamOperation - { - OpSetMediaType = 0, - OpStart, - OpPreroll, - OpRestart, - OpPause, - OpStop, - OpSetRate, - OpProcessSample, - OpPlaceMarker, - OpFinalize, - - Op_Count = OpFinalize + 1 // Number of operations - }; - - // AsyncOperation: - // Used to queue asynchronous operations. When we call MFPutWorkItem, we use this - // object for the callback state (pState). Then, when the callback is invoked, - // we can use the object to determine which asynchronous operation to perform. - class AsyncOperation : public IUnknown - { - public: - AsyncOperation(StreamOperation op) - :m_cRef(1), m_op(op) - { - } - - StreamOperation m_op; // The operation to perform. - - //from IUnknown - STDMETHODIMP QueryInterface(REFIID iid, void** ppv) - { - if (!ppv) - return E_POINTER; - if (iid == IID_IUnknown) { - *ppv = static_cast<IUnknown*>(this); - } else { - *ppv = NULL; - return E_NOINTERFACE; - } - AddRef(); - return S_OK; - } - STDMETHODIMP_(ULONG) AddRef() - { - return InterlockedIncrement(&m_cRef); - } - STDMETHODIMP_(ULONG) Release() - { - ULONG uCount = InterlockedDecrement(&m_cRef); - if (uCount == 0) - delete this; - // For thread safety, return a temporary variable. - return uCount; - } - - private: - long m_cRef; - virtual ~AsyncOperation() - { - Q_ASSERT(m_cRef == 0); - } - }; - - // ValidStateMatrix: Defines a look-up table that says which operations - // are valid from which states. - static BOOL ValidStateMatrix[State_Count][Op_Count]; - - long m_cRef; - QMutex m_mutex; - - IMFMediaType *m_currentMediaType; - State m_state; - IMFMediaSink *m_sink; - IMFMediaEventQueue *m_eventQueue; - DWORD m_workQueueId; - AsyncCallback<MediaStream> m_workQueueCB; - QList<IUnknown*> m_sampleQueue; - IMFAsyncResult *m_finalizeResult; // Result object for Finalize operation. - MFTIME m_startTime; // Presentation time when the clock started. - - bool m_shutdown; - QList<IMFMediaType*> m_mediaTypes; - QList<QVideoFrameFormat::PixelFormat> m_pixelFormats; - int m_currentFormatIndex; - int m_bytesPerLine; - QVideoFrameFormat m_surfaceFormat; - QVideoSink *m_videoSink; - MFVideoRendererControl *m_rendererControl; - - void clearMediaTypes() - { - for (IMFMediaType* mediaType : qAsConst(m_mediaTypes)) - mediaType->Release(); - m_mediaTypes.clear(); - } - - int getMediaTypeIndex(IMFMediaType *mt) - { - GUID majorType; - if (FAILED(mt->GetMajorType(&majorType))) - return -1; - if (majorType != MFMediaType_Video) - return -1; - - GUID subType; - if (FAILED(mt->GetGUID(MF_MT_SUBTYPE, &subType))) - return -1; - - for (int index = 0; index < m_mediaTypes.size(); ++index) { - GUID st; - m_mediaTypes[index]->GetGUID(MF_MT_SUBTYPE, &st); - if (st == subType) - return index; - } - return -1; - } - - int getBytesPerLine(const QVideoFrameFormat &format) - { - switch (format.pixelFormat()) { - // 32 bpp packed formats. - case QVideoFrameFormat::Format_XBGR8888: - case QVideoFrameFormat::Format_BGRX8888: - case QVideoFrameFormat::Format_XRGB8888: - case QVideoFrameFormat::Format_RGBX8888: - case QVideoFrameFormat::Format_AYUV: - return format.frameWidth() * 4; - // 16 bpp packed formats. - case QVideoFrameFormat::Format_YUYV: - case QVideoFrameFormat::Format_UYVY: - return PAD_TO_DWORD(format.frameWidth() * 2); - // Planar formats. - case QVideoFrameFormat::Format_IMC1: - case QVideoFrameFormat::Format_IMC2: - case QVideoFrameFormat::Format_IMC3: - case QVideoFrameFormat::Format_IMC4: - case QVideoFrameFormat::Format_YV12: - case QVideoFrameFormat::Format_NV12: - case QVideoFrameFormat::Format_YUV420P: - return PAD_TO_DWORD(format.frameWidth()); - default: - return 0; - } - } - - // Callback for MFPutWorkItem. - HRESULT onDispatchWorkItem(IMFAsyncResult* pAsyncResult) - { - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - - HRESULT hr = S_OK; - IUnknown *pState = NULL; - hr = pAsyncResult->GetState(&pState); - if (SUCCEEDED(hr)) { - // The state object is an AsncOperation object. - AsyncOperation *pOp = (AsyncOperation*)pState; - StreamOperation op = pOp->m_op; - switch (op) { - case OpStart: - endPreroll(S_FALSE); - schedulePresentation(true); - case OpRestart: - endPreroll(S_FALSE); - if (SUCCEEDED(hr)) { - // Send MEStreamSinkStarted. - hr = queueEvent(MEStreamSinkStarted, GUID_NULL, hr, NULL); - // Kick things off by requesting samples... - schedulePresentation(true); - // There might be samples queue from earlier (ie, while paused). - if (SUCCEEDED(hr)) - hr = processSamplesFromQueue(WriteSamples); - } - break; - case OpPreroll: - beginPreroll(); - break; - case OpStop: - // Drop samples from queue. - hr = processSamplesFromQueue(DropSamples); - clearBufferCache(); - // Send the event even if the previous call failed. - hr = queueEvent(MEStreamSinkStopped, GUID_NULL, hr, NULL); - break; - case OpPause: - hr = queueEvent(MEStreamSinkPaused, GUID_NULL, hr, NULL); - break; - case OpSetRate: - hr = queueEvent(MEStreamSinkRateChanged, GUID_NULL, S_OK, NULL); - break; - case OpProcessSample: - case OpPlaceMarker: - hr = dispatchProcessSample(pOp); - break; - case OpFinalize: - endPreroll(S_FALSE); - hr = dispatchFinalize(pOp); - break; - } - } - - if (pState) - pState->Release(); - return hr; - } - - - HRESULT queueEvent(MediaEventType met, REFGUID guidExtendedType, HRESULT hrStatus, const PROPVARIANT* pvValue) - { - HRESULT hr = S_OK; - if (m_shutdown) - hr = MF_E_SHUTDOWN; - if (SUCCEEDED(hr)) - hr = m_eventQueue->QueueEventParamVar(met, guidExtendedType, hrStatus, pvValue); - return hr; - } - - HRESULT validateOperation(StreamOperation op) - { - Q_ASSERT(!m_shutdown); - if (ValidStateMatrix[m_state][op]) - return S_OK; - else - return MF_E_INVALIDREQUEST; - } - - HRESULT queueAsyncOperation(StreamOperation op) - { - HRESULT hr = S_OK; - AsyncOperation *asyncOp = new AsyncOperation(op); - if (asyncOp == NULL) - hr = E_OUTOFMEMORY; - - if (SUCCEEDED(hr)) - hr = MFPutWorkItem(m_workQueueId, &m_workQueueCB, asyncOp); - - if (asyncOp) - asyncOp->Release(); - - return hr; - } - - HRESULT processSamplesFromQueue(FlushState bFlushData) - { - HRESULT hr = S_OK; - QList<IUnknown*>::Iterator pos = m_sampleQueue.begin(); - // Enumerate all of the samples/markers in the queue. - while (pos != m_sampleQueue.end()) { - IUnknown *pUnk = NULL; - IMarker *pMarker = NULL; - IMFSample *pSample = NULL; - pUnk = *pos; - // Figure out if this is a marker or a sample. - if (SUCCEEDED(hr)) { - hr = pUnk->QueryInterface(__uuidof(IMarker), (void**)&pMarker); - if (hr == E_NOINTERFACE) - hr = pUnk->QueryInterface(IID_IMFSample, (void**)&pSample); - } - - // Now handle the sample/marker appropriately. - if (SUCCEEDED(hr)) { - if (pMarker) { - hr = sendMarkerEvent(pMarker, bFlushData); - } else { - Q_ASSERT(pSample != NULL); // Not a marker, must be a sample - if (bFlushData == WriteSamples) - hr = processSampleData(pSample); - } - } - if (pMarker) - pMarker->Release(); - if (pSample) - pSample->Release(); - - if (FAILED(hr)) - break; - - pos++; - } - - clearSampleQueue(); - return hr; - } - - void beginPreroll() - { - if (m_prerolling) - return; - m_prerolling = true; - clearSampleQueue(); - clearBufferCache(); - queueEvent(MEStreamSinkRequestSample, GUID_NULL, S_OK, NULL); - } - - void endPreroll(HRESULT hrStatus) - { - if (!m_prerolling) - return; - m_prerolling = false; - queueEvent(MEStreamSinkPrerolled, GUID_NULL, hrStatus, NULL); - } - MFTIME m_prerollTargetTime; - bool m_prerolling; - - void clearSampleQueue() { - for (IUnknown* sample : qAsConst(m_sampleQueue)) - sample->Release(); - m_sampleQueue.clear(); - } - - HRESULT sendMarkerEvent(IMarker *pMarker, FlushState FlushState) - { - HRESULT hr = S_OK; - HRESULT hrStatus = S_OK; // Status code for marker event. - if (FlushState == DropSamples) - hrStatus = E_ABORT; - - PROPVARIANT var; - PropVariantInit(&var); - - // Get the context data. - hr = pMarker->GetContext(&var); - - if (SUCCEEDED(hr)) - hr = queueEvent(MEStreamSinkMarker, GUID_NULL, hrStatus, &var); - - PropVariantClear(&var); - return hr; - } - - HRESULT dispatchProcessSample(AsyncOperation* pOp) - { - HRESULT hr = S_OK; - Q_ASSERT(pOp != NULL); - Q_UNUSED(pOp); - hr = processSamplesFromQueue(WriteSamples); - // We are in the middle of an asynchronous operation, so if something failed, send an error. - if (FAILED(hr)) - hr = queueEvent(MEError, GUID_NULL, hr, NULL); - - return hr; - } - - HRESULT dispatchFinalize(AsyncOperation*) - { - HRESULT hr = S_OK; - // Write any samples left in the queue... - hr = processSamplesFromQueue(WriteSamples); - - // Set the async status and invoke the callback. - m_finalizeResult->SetStatus(hr); - hr = MFInvokeCallback(m_finalizeResult); - return hr; - } - - HRESULT processSampleData(IMFSample *pSample) - { - m_sampleRequested = false; - - LONGLONG time, duration = -1; - HRESULT hr = pSample->GetSampleTime(&time); - if (SUCCEEDED(hr)) - pSample->GetSampleDuration(&duration); - - if (m_prerolling) { - if (SUCCEEDED(hr) && ((time - m_prerollTargetTime) * m_rate) >= 0) { - IMFMediaBuffer *pBuffer = NULL; - hr = pSample->ConvertToContiguousBuffer(&pBuffer); - if (SUCCEEDED(hr)) { - SampleBuffer sb; - sb.m_buffer = pBuffer; - sb.m_time = time; - sb.m_duration = duration; - m_bufferCache.push_back(sb); - endPreroll(S_OK); - } - } else { - queueEvent(MEStreamSinkRequestSample, GUID_NULL, S_OK, NULL); - } - } else { - bool requestSample = true; - // If the time stamp is too early, just discard this sample. - if (SUCCEEDED(hr) && ((time - m_startTime) * m_rate) >= 0) { - IMFMediaBuffer *pBuffer = NULL; - hr = pSample->ConvertToContiguousBuffer(&pBuffer); - if (SUCCEEDED(hr)) { - SampleBuffer sb; - sb.m_buffer = pBuffer; - sb.m_time = time; - sb.m_duration = duration; - m_bufferCache.push_back(sb); - } - if (m_rate == 0) - requestSample = false; - } - schedulePresentation(requestSample); - } - return hr; - } - - class SampleBuffer - { - public: - IMFMediaBuffer *m_buffer; - LONGLONG m_time; - LONGLONG m_duration; - }; - QList<SampleBuffer> m_bufferCache; - static const int BUFFER_CACHE_SIZE = 2; - - void clearBufferCache() - { - for (SampleBuffer sb : qAsConst(m_bufferCache)) - sb.m_buffer->Release(); - m_bufferCache.clear(); - - if (m_scheduledBuffer) { - m_scheduledBuffer->Release(); - m_scheduledBuffer = NULL; - } - } - - void schedulePresentation(bool requestSample) - { - if (m_state == State_Paused || m_state == State_Prerolling) - return; - if (!m_scheduledBuffer) { - //get time from presentation time - MFTIME currentTime = m_startTime, sysTime; - bool timeOK = true; - if (m_rate != 0) { - if (FAILED(m_presentationClock->GetCorrelatedTime(0, ¤tTime, &sysTime))) - timeOK = false; - } - while (!m_bufferCache.isEmpty()) { - SampleBuffer sb = m_bufferCache.takeFirst(); - if (timeOK && ((sb.m_time - currentTime) * m_rate) < 0) { - sb.m_buffer->Release(); -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "currentPresentTime =" << float(currentTime / 10000) * 0.001f << " and sampleTime is" << float(sb.m_time / 10000) * 0.001f; -#endif - continue; - } - m_scheduledBuffer = sb.m_buffer; - m_bufferStartTime = sb.m_time; - m_bufferDuration = sb.m_duration; - QCoreApplication::postEvent(m_rendererControl, new PresentEvent(sb.m_time)); - if (m_rate == 0) - queueEvent(MEStreamSinkScrubSampleComplete, GUID_NULL, S_OK, NULL); - break; - } - } - if (requestSample && !m_sampleRequested && m_bufferCache.size() < BUFFER_CACHE_SIZE) { - m_sampleRequested = true; - queueEvent(MEStreamSinkRequestSample, GUID_NULL, S_OK, NULL); - } - } - IMFMediaBuffer *m_scheduledBuffer; - MFTIME m_bufferStartTime; - MFTIME m_bufferDuration; - IMFPresentationClock *m_presentationClock; - bool m_sampleRequested; - float m_rate; - }; - - BOOL MediaStream::ValidStateMatrix[MediaStream::State_Count][MediaStream::Op_Count] = - { - // States: Operations: - // SetType Start Preroll, Restart Pause Stop SetRate Sample Marker Finalize - /* NotSet */ TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, - - /* Ready */ TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, - - /* Prerolling */ TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, - - /* Start */ FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, - - /* Pause */ FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, - - /* Stop */ FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, - - /*WaitForSurfaceStart*/ FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, - - /* Final */ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE - - // Note about states: - // 1. OnClockRestart should only be called from paused state. - // 2. While paused, the sink accepts samples but does not process them. - }; - - class MediaSink : public IMFFinalizableMediaSink, - public IMFClockStateSink, - public IMFMediaSinkPreroll, - public IMFGetService, - public IMFRateSupport - { - public: - MediaSink(MFVideoRendererControl *rendererControl) - : m_cRef(1) - , m_shutdown(false) - , m_presentationClock(0) - , m_playRate(1) - { - m_stream = new MediaStream(this, rendererControl); - } - - ~MediaSink() - { - Q_ASSERT(m_shutdown); - } - - void setSurface(QVideoSink *surface) - { - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return; - m_stream->setSink(surface); - } - - void present() - { - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return; - m_stream->present(); - } - - void clearScheduledFrame() - { - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return; - m_stream->clearScheduledFrame(); - } - - MFTIME getTime() - { - QMutexLocker locker(&m_mutex); - if (!m_presentationClock) - return 0; - MFTIME time, sysTime; - m_presentationClock->GetCorrelatedTime(0, &time, &sysTime); - return time; - } - - float getPlayRate() - { - QMutexLocker locker(&m_mutex); - return m_playRate; - } - - //from IUnknown - STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject) - { - if (!ppvObject) - return E_POINTER; - if (riid == IID_IMFMediaSink) { - *ppvObject = static_cast<IMFMediaSink*>(this); - } else if (riid == IID_IMFGetService) { - *ppvObject = static_cast<IMFGetService*>(this); - } else if (riid == IID_IMFMediaSinkPreroll) { - *ppvObject = static_cast<IMFMediaSinkPreroll*>(this); - } else if (riid == IID_IMFClockStateSink) { - *ppvObject = static_cast<IMFClockStateSink*>(this); - } else if (riid == IID_IMFRateSupport) { - *ppvObject = static_cast<IMFRateSupport*>(this); - } else if (riid == IID_IUnknown) { - *ppvObject = static_cast<IUnknown*>(static_cast<IMFFinalizableMediaSink*>(this)); - } else { - *ppvObject = NULL; - return E_NOINTERFACE; - } - AddRef(); - return S_OK; - } - - STDMETHODIMP_(ULONG) AddRef(void) - { - return InterlockedIncrement(&m_cRef); - } - - STDMETHODIMP_(ULONG) Release(void) - { - LONG cRef = InterlockedDecrement(&m_cRef); - if (cRef == 0) - delete this; - // For thread safety, return a temporary variable. - return cRef; - } - - // IMFGetService methods - STDMETHODIMP GetService(const GUID &guidService, - const IID &riid, - LPVOID *ppvObject) - { - if (!ppvObject) - return E_POINTER; - - if (guidService != MF_RATE_CONTROL_SERVICE) - return MF_E_UNSUPPORTED_SERVICE; - - return QueryInterface(riid, ppvObject); - } - - //IMFMediaSinkPreroll - STDMETHODIMP NotifyPreroll(MFTIME hnsUpcomingStartTime) - { - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - return m_stream->startPreroll(hnsUpcomingStartTime); - } - - //from IMFFinalizableMediaSink - STDMETHODIMP BeginFinalize(IMFAsyncCallback *pCallback, IUnknown *punkState) - { - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - return m_stream->finalize(pCallback, punkState); - } - - STDMETHODIMP EndFinalize(IMFAsyncResult *pResult) - { - HRESULT hr = S_OK; - // Return the status code from the async result. - if (pResult == NULL) - hr = E_INVALIDARG; - else - hr = pResult->GetStatus(); - return hr; - } - - //from IMFMediaSink - STDMETHODIMP GetCharacteristics( - DWORD *pdwCharacteristics) - { - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - *pdwCharacteristics = MEDIASINK_FIXED_STREAMS | MEDIASINK_CAN_PREROLL; - return S_OK; - } - - STDMETHODIMP AddStreamSink( - DWORD, - IMFMediaType *, - IMFStreamSink **) - { - QMutexLocker locker(&m_mutex); - return m_shutdown ? MF_E_SHUTDOWN : MF_E_STREAMSINKS_FIXED; - } - - STDMETHODIMP RemoveStreamSink( - DWORD) - { - QMutexLocker locker(&m_mutex); - return m_shutdown ? MF_E_SHUTDOWN : MF_E_STREAMSINKS_FIXED; - } - - STDMETHODIMP GetStreamSinkCount( - DWORD *pcStreamSinkCount) - { - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - *pcStreamSinkCount = 1; - return S_OK; - } - - STDMETHODIMP GetStreamSinkByIndex( - DWORD dwIndex, - IMFStreamSink **ppStreamSink) - { - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - - if (dwIndex != 0) - return MF_E_INVALIDINDEX; - - *ppStreamSink = m_stream; - m_stream->AddRef(); - return S_OK; - } - - STDMETHODIMP GetStreamSinkById( - DWORD dwStreamSinkIdentifier, - IMFStreamSink **ppStreamSink) - { - if (ppStreamSink == NULL) - return E_INVALIDARG; - if (dwStreamSinkIdentifier != MediaStream::DEFAULT_MEDIA_STREAM_ID) - return MF_E_INVALIDSTREAMNUMBER; - - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - - *ppStreamSink = m_stream; - m_stream->AddRef(); - return S_OK; - } - - STDMETHODIMP SetPresentationClock( - IMFPresentationClock *pPresentationClock) - { - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - - if (m_presentationClock) { - m_presentationClock->RemoveClockStateSink(this); - m_presentationClock->Release(); - } - m_presentationClock = pPresentationClock; - if (m_presentationClock) { - m_presentationClock->AddRef(); - m_presentationClock->AddClockStateSink(this); - } - m_stream->setClock(m_presentationClock); - return S_OK; - } - - STDMETHODIMP GetPresentationClock( - IMFPresentationClock **ppPresentationClock) - { - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - *ppPresentationClock = m_presentationClock; - if (m_presentationClock) { - m_presentationClock->AddRef(); - return S_OK; - } - return MF_E_NO_CLOCK; - } - - STDMETHODIMP Shutdown(void) - { - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - - m_stream->shutdown(); - if (m_presentationClock) { - m_presentationClock->Release(); - m_presentationClock = NULL; - } - m_stream->Release(); - m_stream = NULL; - m_shutdown = true; - return S_OK; - } - - // IMFClockStateSink methods - STDMETHODIMP OnClockStart(MFTIME, LONGLONG llClockStartOffset) - { - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - return m_stream->start(llClockStartOffset); - } - - STDMETHODIMP OnClockStop(MFTIME) - { - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - return m_stream->stop(); - } - - STDMETHODIMP OnClockPause(MFTIME) - { - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - return m_stream->pause(); - } - - STDMETHODIMP OnClockRestart(MFTIME) - { - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - return m_stream->restart(); - } - - STDMETHODIMP OnClockSetRate(MFTIME, float flRate) - { - QMutexLocker locker(&m_mutex); - if (m_shutdown) - return MF_E_SHUTDOWN; - m_playRate = flRate; - return m_stream->setRate(flRate); - } - - // IMFRateSupport methods - STDMETHODIMP GetFastestRate(MFRATE_DIRECTION eDirection, - BOOL fThin, - float *pflRate) - { - if (!pflRate) - return E_POINTER; - - *pflRate = (fThin ? 8.f : 2.0f) * (eDirection == MFRATE_FORWARD ? 1 : -1) ; - - return S_OK; - } - - STDMETHODIMP GetSlowestRate(MFRATE_DIRECTION eDirection, - BOOL fThin, - float *pflRate) - { - Q_UNUSED(eDirection); - Q_UNUSED(fThin); - - if (!pflRate) - return E_POINTER; - - // we support any rate - *pflRate = 0.f; - - return S_OK; - } - - STDMETHODIMP IsRateSupported(BOOL fThin, - float flRate, - float *pflNearestSupportedRate) - { - HRESULT hr = S_OK; - - if (!qFuzzyIsNull(flRate)) { - MFRATE_DIRECTION direction = flRate > 0.f ? MFRATE_FORWARD - : MFRATE_REVERSE; - - float fastestRate = 0.f; - float slowestRate = 0.f; - GetFastestRate(direction, fThin, &fastestRate); - GetSlowestRate(direction, fThin, &slowestRate); - - if (direction == MFRATE_REVERSE) - qSwap(fastestRate, slowestRate); - - if (flRate < slowestRate || flRate > fastestRate) { - hr = MF_E_UNSUPPORTED_RATE; - if (pflNearestSupportedRate) { - *pflNearestSupportedRate = qBound(slowestRate, - flRate, - fastestRate); - } - } - } else if (pflNearestSupportedRate) { - *pflNearestSupportedRate = flRate; - } - - return hr; - } - - private: - long m_cRef; - QMutex m_mutex; - bool m_shutdown; - IMFPresentationClock *m_presentationClock; - MediaStream *m_stream; - float m_playRate; - }; - - class VideoRendererActivate : public IMFActivate - { - public: - VideoRendererActivate(MFVideoRendererControl *rendererControl) - : m_cRef(1) - , m_sink(0) - , m_rendererControl(rendererControl) - , m_attributes(0) - , m_videoSink(0) - { - MFCreateAttributes(&m_attributes, 0); - m_sink = new MediaSink(rendererControl); - } - - ~VideoRendererActivate() - { - m_attributes->Release(); - } - - //from IUnknown - STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject) - { - if (!ppvObject) - return E_POINTER; - if (riid == IID_IMFActivate) { - *ppvObject = static_cast<IMFActivate*>(this); - } else if (riid == IID_IMFAttributes) { - *ppvObject = static_cast<IMFAttributes*>(this); - } else if (riid == IID_IUnknown) { - *ppvObject = static_cast<IUnknown*>(static_cast<IMFActivate*>(this)); - } else { - *ppvObject = NULL; - return E_NOINTERFACE; - } - AddRef(); - return S_OK; - } - - STDMETHODIMP_(ULONG) AddRef(void) - { - return InterlockedIncrement(&m_cRef); - } - - STDMETHODIMP_(ULONG) Release(void) - { - LONG cRef = InterlockedDecrement(&m_cRef); - if (cRef == 0) - delete this; - // For thread safety, return a temporary variable. - return cRef; - } - - //from IMFActivate - STDMETHODIMP ActivateObject(REFIID riid, void **ppv) - { - if (!ppv) - return E_INVALIDARG; - QMutexLocker locker(&m_mutex); - if (!m_sink) { - m_sink = new MediaSink(m_rendererControl); - if (m_videoSink) - m_sink->setSurface(m_videoSink); - } - return m_sink->QueryInterface(riid, ppv); - } - - STDMETHODIMP ShutdownObject(void) - { - QMutexLocker locker(&m_mutex); - HRESULT hr = S_OK; - if (m_sink) { - hr = m_sink->Shutdown(); - m_sink->Release(); - m_sink = NULL; - } - return hr; - } - - STDMETHODIMP DetachObject(void) - { - QMutexLocker locker(&m_mutex); - if (m_sink) { - m_sink->Release(); - m_sink = NULL; - } - return S_OK; - } - - //from IMFAttributes - STDMETHODIMP GetItem( - REFGUID guidKey, - PROPVARIANT *pValue) - { - return m_attributes->GetItem(guidKey, pValue); - } - - STDMETHODIMP GetItemType( - REFGUID guidKey, - MF_ATTRIBUTE_TYPE *pType) - { - return m_attributes->GetItemType(guidKey, pType); - } - - STDMETHODIMP CompareItem( - REFGUID guidKey, - REFPROPVARIANT Value, - BOOL *pbResult) - { - return m_attributes->CompareItem(guidKey, Value, pbResult); - } - - STDMETHODIMP Compare( - IMFAttributes *pTheirs, - MF_ATTRIBUTES_MATCH_TYPE MatchType, - BOOL *pbResult) - { - return m_attributes->Compare(pTheirs, MatchType, pbResult); - } - - STDMETHODIMP GetUINT32( - REFGUID guidKey, - UINT32 *punValue) - { - return m_attributes->GetUINT32(guidKey, punValue); - } - - STDMETHODIMP GetUINT64( - REFGUID guidKey, - UINT64 *punValue) - { - return m_attributes->GetUINT64(guidKey, punValue); - } - - STDMETHODIMP GetDouble( - REFGUID guidKey, - double *pfValue) - { - return m_attributes->GetDouble(guidKey, pfValue); - } - - STDMETHODIMP GetGUID( - REFGUID guidKey, - GUID *pguidValue) - { - return m_attributes->GetGUID(guidKey, pguidValue); - } - - STDMETHODIMP GetStringLength( - REFGUID guidKey, - UINT32 *pcchLength) - { - return m_attributes->GetStringLength(guidKey, pcchLength); - } - - STDMETHODIMP GetString( - REFGUID guidKey, - LPWSTR pwszValue, - UINT32 cchBufSize, - UINT32 *pcchLength) - { - return m_attributes->GetString(guidKey, pwszValue, cchBufSize, pcchLength); - } - - STDMETHODIMP GetAllocatedString( - REFGUID guidKey, - LPWSTR *ppwszValue, - UINT32 *pcchLength) - { - return m_attributes->GetAllocatedString(guidKey, ppwszValue, pcchLength); - } - - STDMETHODIMP GetBlobSize( - REFGUID guidKey, - UINT32 *pcbBlobSize) - { - return m_attributes->GetBlobSize(guidKey, pcbBlobSize); - } - - STDMETHODIMP GetBlob( - REFGUID guidKey, - UINT8 *pBuf, - UINT32 cbBufSize, - UINT32 *pcbBlobSize) - { - return m_attributes->GetBlob(guidKey, pBuf, cbBufSize, pcbBlobSize); - } - - STDMETHODIMP GetAllocatedBlob( - REFGUID guidKey, - UINT8 **ppBuf, - UINT32 *pcbSize) - { - return m_attributes->GetAllocatedBlob(guidKey, ppBuf, pcbSize); - } - - STDMETHODIMP GetUnknown( - REFGUID guidKey, - REFIID riid, - LPVOID *ppv) - { - return m_attributes->GetUnknown(guidKey, riid, ppv); - } - - STDMETHODIMP SetItem( - REFGUID guidKey, - REFPROPVARIANT Value) - { - return m_attributes->SetItem(guidKey, Value); - } - - STDMETHODIMP DeleteItem( - REFGUID guidKey) - { - return m_attributes->DeleteItem(guidKey); - } - - STDMETHODIMP DeleteAllItems(void) - { - return m_attributes->DeleteAllItems(); - } - - STDMETHODIMP SetUINT32( - REFGUID guidKey, - UINT32 unValue) - { - return m_attributes->SetUINT32(guidKey, unValue); - } - - STDMETHODIMP SetUINT64( - REFGUID guidKey, - UINT64 unValue) - { - return m_attributes->SetUINT64(guidKey, unValue); - } - - STDMETHODIMP SetDouble( - REFGUID guidKey, - double fValue) - { - return m_attributes->SetDouble(guidKey, fValue); - } - - STDMETHODIMP SetGUID( - REFGUID guidKey, - REFGUID guidValue) - { - return m_attributes->SetGUID(guidKey, guidValue); - } - - STDMETHODIMP SetString( - REFGUID guidKey, - LPCWSTR wszValue) - { - return m_attributes->SetString(guidKey, wszValue); - } - - STDMETHODIMP SetBlob( - REFGUID guidKey, - const UINT8 *pBuf, - UINT32 cbBufSize) - { - return m_attributes->SetBlob(guidKey, pBuf, cbBufSize); - } - - STDMETHODIMP SetUnknown( - REFGUID guidKey, - IUnknown *pUnknown) - { - return m_attributes->SetUnknown(guidKey, pUnknown); - } - - STDMETHODIMP LockStore(void) - { - return m_attributes->LockStore(); - } - - STDMETHODIMP UnlockStore(void) - { - return m_attributes->UnlockStore(); - } - - STDMETHODIMP GetCount( - UINT32 *pcItems) - { - return m_attributes->GetCount(pcItems); - } - - STDMETHODIMP GetItemByIndex( - UINT32 unIndex, - GUID *pguidKey, - PROPVARIANT *pValue) - { - return m_attributes->GetItemByIndex(unIndex, pguidKey, pValue); - } - - STDMETHODIMP CopyAllItems( - IMFAttributes *pDest) - { - return m_attributes->CopyAllItems(pDest); - } - - ///////////////////////////////// - void setSink(QVideoSink *sink) - { - QMutexLocker locker(&m_mutex); - if (m_videoSink == sink) - return; - - m_videoSink = sink; - - if (!m_sink) - return; - m_sink->setSurface(m_videoSink); - } - - void present() - { - QMutexLocker locker(&m_mutex); - if (!m_sink) - return; - m_sink->present(); - } - - void clearScheduledFrame() - { - QMutexLocker locker(&m_mutex); - if (!m_sink) - return; - m_sink->clearScheduledFrame(); - } - - MFTIME getTime() - { - if (m_sink) - return m_sink->getTime(); - return 0; - } - - float getPlayRate() - { - if (m_sink) - return m_sink->getPlayRate(); - return 1; - } - - private: - long m_cRef; - bool m_shutdown; - MediaSink *m_sink; - MFVideoRendererControl *m_rendererControl; - IMFAttributes *m_attributes; - QVideoSink *m_videoSink; - QMutex m_mutex; - }; -} - - -class EVRCustomPresenterActivate : public MFAbstractActivate -{ -public: - EVRCustomPresenterActivate(); - ~EVRCustomPresenterActivate() - { } - - STDMETHODIMP ActivateObject(REFIID riid, void **ppv); - STDMETHODIMP ShutdownObject(); - STDMETHODIMP DetachObject(); - - void setSink(QVideoSink *sink); - -private: - EVRCustomPresenter *m_presenter; - QVideoSink *m_videoSink; - QMutex m_mutex; -}; - - -MFVideoRendererControl::MFVideoRendererControl(QObject *parent) - : QObject(parent) -{ -} - -MFVideoRendererControl::~MFVideoRendererControl() -{ - clear(); -} - -void MFVideoRendererControl::clear() -{ - if (m_sink) - m_sink->platformVideoSink()->setVideoFrame(QVideoFrame()); - - if (m_presenterActivate) { - m_presenterActivate->ShutdownObject(); - m_presenterActivate->Release(); - m_presenterActivate = NULL; - } - - if (m_currentActivate) { - m_currentActivate->ShutdownObject(); - m_currentActivate->Release(); - } - m_currentActivate = NULL; -} - -void MFVideoRendererControl::releaseActivate() -{ - clear(); -} - -QVideoSink *MFVideoRendererControl::sink() const -{ - return m_sink; -} - -void MFVideoRendererControl::setSink(QVideoSink *sink) -{ - m_sink = sink; - - if (m_presenterActivate) - m_presenterActivate->setSink(m_sink); - else if (m_currentActivate) - static_cast<VideoRendererActivate*>(m_currentActivate)->setSink(m_sink); -} - -void MFVideoRendererControl::customEvent(QEvent *event) -{ - if (m_presenterActivate) - return; - - if (!m_currentActivate) - return; - - if (event->type() == MediaStream::PresentSurface) { - MFTIME targetTime = static_cast<MediaStream::PresentEvent*>(event)->targetTime(); - MFTIME currentTime = static_cast<VideoRendererActivate*>(m_currentActivate)->getTime(); - float playRate = static_cast<VideoRendererActivate*>(m_currentActivate)->getPlayRate(); - if (!qFuzzyIsNull(playRate) && targetTime != currentTime) { - // If the scheduled frame is too late, skip it - const int interval = ((targetTime - currentTime) / 10000) / playRate; - if (interval < 0) - static_cast<VideoRendererActivate*>(m_currentActivate)->clearScheduledFrame(); - else - QTimer::singleShot(interval, this, SLOT(present())); - } else { - present(); - } - return; - } - QObject::customEvent(event); -} - -void MFVideoRendererControl::present() -{ - if (m_presenterActivate) - return; - - if (m_currentActivate) - static_cast<VideoRendererActivate*>(m_currentActivate)->present(); -} - -IMFActivate* MFVideoRendererControl::createActivate() -{ - clear(); - - if (m_sink) { - // Create the EVR media sink, but replace the presenter with our own - if (SUCCEEDED(MFCreateVideoRendererActivate(::GetShellWindow(), &m_currentActivate))) { - m_presenterActivate = new EVRCustomPresenterActivate; - m_currentActivate->SetUnknown(MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_ACTIVATE, m_presenterActivate); - } else { - m_currentActivate = new VideoRendererActivate(this); - } - } - - setSink(m_sink); - - return m_currentActivate; -} - - -EVRCustomPresenterActivate::EVRCustomPresenterActivate() - : MFAbstractActivate() - , m_presenter(0) - , m_videoSink(0) -{ } - -HRESULT EVRCustomPresenterActivate::ActivateObject(REFIID riid, void **ppv) -{ - if (!ppv) - return E_INVALIDARG; - QMutexLocker locker(&m_mutex); - if (!m_presenter) { - m_presenter = new EVRCustomPresenter; - if (m_videoSink) - m_presenter->setSink(m_videoSink); - } - return m_presenter->QueryInterface(riid, ppv); -} - -HRESULT EVRCustomPresenterActivate::ShutdownObject() -{ - // The presenter does not implement IMFShutdown so - // this function is the same as DetachObject() - return DetachObject(); -} - -HRESULT EVRCustomPresenterActivate::DetachObject() -{ - QMutexLocker locker(&m_mutex); - if (m_presenter) { - m_presenter->Release(); - m_presenter = 0; - } - return S_OK; -} - -void EVRCustomPresenterActivate::setSink(QVideoSink *sink) -{ - QMutexLocker locker(&m_mutex); - if (m_videoSink == sink) - return; - - m_videoSink = sink; - - if (m_presenter) - m_presenter->setSink(sink); -} - -#include "moc_mfvideorenderercontrol_p.cpp" -#include "mfvideorenderercontrol.moc" diff --git a/src/multimedia/platform/windows/player/mfvideorenderercontrol_p.h b/src/multimedia/platform/windows/player/mfvideorenderercontrol_p.h deleted file mode 100644 index b4bb2d92d..000000000 --- a/src/multimedia/platform/windows/player/mfvideorenderercontrol_p.h +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Mobility Components. -** -** $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$ -** -****************************************************************************/ - -#ifndef MFVIDEORENDERERCONTROL_H -#define MFVIDEORENDERERCONTROL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qobject.h" -#include <mfapi.h> -#include <mfidl.h> - -QT_USE_NAMESPACE - -class EVRCustomPresenterActivate; - -QT_BEGIN_NAMESPACE -class QVideoSink; -QT_END_NAMESPACE - -class MFVideoRendererControl : public QObject -{ - Q_OBJECT -public: - MFVideoRendererControl(QObject *parent = 0); - ~MFVideoRendererControl(); - - QVideoSink *sink() const; - void setSink(QVideoSink *surface); - - IMFActivate* createActivate(); - void releaseActivate(); - -protected: - void customEvent(QEvent *event); - -private Q_SLOTS: - void present(); - -private: - void clear(); - - QVideoSink *m_sink = nullptr; - IMFActivate *m_currentActivate = nullptr; - IMFSampleGrabberSinkCallback *m_callback = nullptr; - - EVRCustomPresenterActivate *m_presenterActivate = nullptr; -}; - -#endif diff --git a/src/multimedia/platform/windows/player/samplegrabber.cpp b/src/multimedia/platform/windows/player/samplegrabber.cpp deleted file mode 100644 index ef039a902..000000000 --- a/src/multimedia/platform/windows/player/samplegrabber.cpp +++ /dev/null @@ -1,172 +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$ -** -****************************************************************************/ - -#include "samplegrabber_p.h" - -STDMETHODIMP SampleGrabberCallback::QueryInterface(REFIID riid, void** ppv) -{ - if (!ppv) - return E_POINTER; - if (riid == IID_IMFSampleGrabberSinkCallback) { - *ppv = static_cast<IMFSampleGrabberSinkCallback*>(this); - } else if (riid == IID_IMFClockStateSink) { - *ppv = static_cast<IMFClockStateSink*>(this); - } else if (riid == IID_IUnknown) { - *ppv = static_cast<IUnknown*>(this); - } else { - *ppv = NULL; - return E_NOINTERFACE; - } - AddRef(); - return S_OK; -} - -STDMETHODIMP_(ULONG) SampleGrabberCallback::AddRef() -{ - return InterlockedIncrement(&m_cRef); -} - -STDMETHODIMP_(ULONG) SampleGrabberCallback::Release() -{ - ULONG cRef = InterlockedDecrement(&m_cRef); - if (cRef == 0) { - delete this; - } - return cRef; - -} - -// IMFClockStateSink methods. - -STDMETHODIMP SampleGrabberCallback::OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset) -{ - Q_UNUSED(hnsSystemTime); - Q_UNUSED(llClockStartOffset); - return S_OK; -} - -STDMETHODIMP SampleGrabberCallback::OnClockStop(MFTIME hnsSystemTime) -{ - Q_UNUSED(hnsSystemTime); - return S_OK; -} - -STDMETHODIMP SampleGrabberCallback::OnClockPause(MFTIME hnsSystemTime) -{ - Q_UNUSED(hnsSystemTime); - return S_OK; -} - -STDMETHODIMP SampleGrabberCallback::OnClockRestart(MFTIME hnsSystemTime) -{ - Q_UNUSED(hnsSystemTime); - return S_OK; -} - -STDMETHODIMP SampleGrabberCallback::OnClockSetRate(MFTIME hnsSystemTime, float flRate) -{ - Q_UNUSED(hnsSystemTime); - Q_UNUSED(flRate); - return S_OK; -} - -// IMFSampleGrabberSink methods. - -STDMETHODIMP SampleGrabberCallback::OnSetPresentationClock(IMFPresentationClock* pClock) -{ - Q_UNUSED(pClock); - return S_OK; -} - -STDMETHODIMP SampleGrabberCallback::OnShutdown() -{ - return S_OK; -} - -//void AudioSampleGrabberCallback::addProbe(MFAudioProbeControl* probe) -//{ -// QMutexLocker locker(&m_audioProbeMutex); - -// if (m_audioProbes.contains(probe)) -// return; - -// m_audioProbes.append(probe); -//} - -//void AudioSampleGrabberCallback::removeProbe(MFAudioProbeControl* probe) -//{ -// QMutexLocker locker(&m_audioProbeMutex); -// m_audioProbes.removeOne(probe); -//} - -void AudioSampleGrabberCallback::setFormat(const QAudioFormat& format) -{ - m_format = format; -} - -STDMETHODIMP AudioSampleGrabberCallback::OnProcessSample(REFGUID guidMajorMediaType, DWORD dwSampleFlags, - LONGLONG llSampleTime, LONGLONG llSampleDuration, const BYTE * pSampleBuffer, - DWORD dwSampleSize) -{ - Q_UNUSED(dwSampleFlags); - Q_UNUSED(llSampleTime); - Q_UNUSED(llSampleDuration); - - if (guidMajorMediaType != GUID_NULL && guidMajorMediaType != MFMediaType_Audio) - return S_OK; - - QMutexLocker locker(&m_audioProbeMutex); - -// if (m_audioProbes.isEmpty()) - return S_OK; - - // Check if sample has a presentation time - if (llSampleTime == _I64_MAX) { - // Set default QAudioBuffer start time - llSampleTime = -1; - } else { - // WMF uses 100-nanosecond units, Qt uses microseconds - llSampleTime /= 10; - } - -// for (MFAudioProbeControl* probe : qAsConst(m_audioProbes)) -// probe->bufferProbed((const char*)pSampleBuffer, dwSampleSize, m_format, llSampleTime); - - return S_OK; -} diff --git a/src/multimedia/platform/windows/player/samplegrabber_p.h b/src/multimedia/platform/windows/player/samplegrabber_p.h deleted file mode 100644 index 31e00987c..000000000 --- a/src/multimedia/platform/windows/player/samplegrabber_p.h +++ /dev/null @@ -1,103 +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$ -** -****************************************************************************/ - -#ifndef SAMPLEGRABBER_H -#define SAMPLEGRABBER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qmutex.h> -#include <QtCore/qlist.h> -#include <QtMultimedia/qaudioformat.h> -#include <mfapi.h> -#include <mfidl.h> - -class SampleGrabberCallback : public IMFSampleGrabberSinkCallback -{ -public: - // IUnknown methods - STDMETHODIMP QueryInterface(REFIID iid, void** ppv); - STDMETHODIMP_(ULONG) AddRef(); - STDMETHODIMP_(ULONG) Release(); - - // IMFClockStateSink methods - STDMETHODIMP OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset); - STDMETHODIMP OnClockStop(MFTIME hnsSystemTime); - STDMETHODIMP OnClockPause(MFTIME hnsSystemTime); - STDMETHODIMP OnClockRestart(MFTIME hnsSystemTime); - STDMETHODIMP OnClockSetRate(MFTIME hnsSystemTime, float flRate); - - // IMFSampleGrabberSinkCallback methods - STDMETHODIMP OnSetPresentationClock(IMFPresentationClock* pClock); - STDMETHODIMP OnShutdown(); - -protected: - SampleGrabberCallback() : m_cRef(1) {} - -public: - virtual ~SampleGrabberCallback() {} - -private: - long m_cRef; -}; - -class AudioSampleGrabberCallback: public SampleGrabberCallback { -public: - void setFormat(const QAudioFormat& format); - - STDMETHODIMP OnProcessSample(REFGUID guidMajorMediaType, DWORD dwSampleFlags, - LONGLONG llSampleTime, LONGLONG llSampleDuration, const BYTE * pSampleBuffer, - DWORD dwSampleSize); - -private: -// QList<MFAudioProbeControl*> m_audioProbes; - QMutex m_audioProbeMutex; - QAudioFormat m_format; -}; - -#endif // SAMPLEGRABBER_H diff --git a/src/multimedia/platform/windows/qwindowsformatinfo.cpp b/src/multimedia/platform/windows/qwindowsformatinfo.cpp deleted file mode 100644 index 85c362b29..000000000 --- a/src/multimedia/platform/windows/qwindowsformatinfo.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qwindowsformatinfo_p.h" - - -QT_BEGIN_NAMESPACE - -QWindowsFormatInfo::QWindowsFormatInfo() -{ - decoders = { - { QMediaFormat::MPEG4, - { QMediaFormat::AudioCodec::AAC, QMediaFormat::AudioCodec::MP3, QMediaFormat::AudioCodec::ALAC, QMediaFormat::AudioCodec::AC3, QMediaFormat::AudioCodec::EAC3, }, - { QMediaFormat::VideoCodec::H264, QMediaFormat::VideoCodec::H265, QMediaFormat::VideoCodec::MotionJPEG } }, - { QMediaFormat::QuickTime, - { QMediaFormat::AudioCodec::AAC, QMediaFormat::AudioCodec::MP3, QMediaFormat::AudioCodec::ALAC, QMediaFormat::AudioCodec::AC3, QMediaFormat::AudioCodec::EAC3, }, - { QMediaFormat::VideoCodec::H264, QMediaFormat::VideoCodec::H265, QMediaFormat::VideoCodec::MotionJPEG } }, - { QMediaFormat::AAC, - { QMediaFormat::AudioCodec::AAC }, - {} }, - { QMediaFormat::MP3, - { QMediaFormat::AudioCodec::MP3 }, - {} }, - { QMediaFormat::FLAC, - { QMediaFormat::AudioCodec::FLAC }, - {} }, - { QMediaFormat::Mpeg4Audio, - { QMediaFormat::AudioCodec::AAC }, - {} }, - { QMediaFormat::WMA, - { QMediaFormat::AudioCodec::WMA }, - {} }, - { QMediaFormat::WMV, - { QMediaFormat::AudioCodec::WMA }, - { QMediaFormat::VideoCodec::WMV } } - }; - - encoders = { - { QMediaFormat::MPEG4, - { QMediaFormat::AudioCodec::AAC, QMediaFormat::AudioCodec::MP3 }, - { QMediaFormat::VideoCodec::H264 } }, - { QMediaFormat::AAC, - { QMediaFormat::AudioCodec::AAC }, - {} }, - { QMediaFormat::MP3, - { QMediaFormat::AudioCodec::MP3 }, - {} }, - { QMediaFormat::Mpeg4Audio, - { QMediaFormat::AudioCodec::AAC }, - {} }, - { QMediaFormat::WMA, - { QMediaFormat::AudioCodec::WMA }, - {} }, - { QMediaFormat::WMV, - { QMediaFormat::AudioCodec::WMA }, - { QMediaFormat::VideoCodec::WMV } } - }; - - // #### - imageFormats = { QImageCapture::JPEG, QImageCapture::PNG }; - -} - -QWindowsFormatInfo::~QWindowsFormatInfo() -{ -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/windows/qwindowsformatinfo_p.h b/src/multimedia/platform/windows/qwindowsformatinfo_p.h deleted file mode 100644 index 355aea7d7..000000000 --- a/src/multimedia/platform/windows/qwindowsformatinfo_p.h +++ /dev/null @@ -1,69 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QWINDOWSFORMATSINFO_H -#define QWINDOWSFORMATSINFO_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediaformatinfo_p.h> -#include <qhash.h> -#include <qlist.h> - -QT_BEGIN_NAMESPACE - -class QWindowsFormatInfo : public QPlatformMediaFormatInfo -{ -public: - QWindowsFormatInfo(); - ~QWindowsFormatInfo(); -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/windows/qwindowsintegration.cpp b/src/multimedia/platform/windows/qwindowsintegration.cpp deleted file mode 100644 index b224a8937..000000000 --- a/src/multimedia/platform/windows/qwindowsintegration.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qwindowsintegration_p.h" -#include <private/qwindowsmediadevices_p.h> -#include <private/qwindowsformatinfo_p.h> -#include <private/qwindowsmediacapture_p.h> -#include <private/qwindowsimagecapture_p.h> -#include <private/qwindowscamera_p.h> -#include <private/qwindowsmediaencoder_p.h> -#include <private/mfplayercontrol_p.h> -#include <private/mfaudiodecodercontrol_p.h> -#include <private/mfevrvideowindowcontrol_p.h> - -QT_BEGIN_NAMESPACE - -static int g_refCount = 0; - -QWindowsMediaIntegration::QWindowsMediaIntegration() -{ - g_refCount++; - if (g_refCount == 1) { - CoInitialize(NULL); - MFStartup(MF_VERSION); - } -} - -QWindowsMediaIntegration::~QWindowsMediaIntegration() -{ - delete m_devices; - delete m_formatInfo; - - g_refCount--; - if (g_refCount == 0) { - // ### This currently crashes on exit -// MFShutdown(); -// CoUninitialize(); - } -} - -QPlatformMediaDevices *QWindowsMediaIntegration::devices() -{ - if (!m_devices) - m_devices = new QWindowsMediaDevices(); - return m_devices; -} - -QPlatformMediaFormatInfo *QWindowsMediaIntegration::formatInfo() -{ - if (!m_formatInfo) - m_formatInfo = new QWindowsFormatInfo(); - return m_formatInfo; -} - -QPlatformMediaCaptureSession *QWindowsMediaIntegration::createCaptureSession() -{ - return new QWindowsMediaCaptureService(); -} - -QPlatformAudioDecoder *QWindowsMediaIntegration::createAudioDecoder(QAudioDecoder *decoder) -{ - return new MFAudioDecoderControl(decoder); -} - -QPlatformMediaPlayer *QWindowsMediaIntegration::createPlayer(QMediaPlayer *parent) -{ - return new MFPlayerControl(parent); -} - -QPlatformCamera *QWindowsMediaIntegration::createCamera(QCamera *camera) -{ - return new QWindowsCamera(camera); -} - -QPlatformMediaEncoder *QWindowsMediaIntegration::createEncoder(QMediaRecorder *encoder) -{ - return new QWindowsMediaEncoder(encoder); -} - -QPlatformImageCapture *QWindowsMediaIntegration::createImageCapture(QImageCapture *imageCapture) -{ - return new QWindowsImageCapture(imageCapture); -} - -QPlatformVideoSink *QWindowsMediaIntegration::createVideoSink(QVideoSink *sink) -{ - return new MFEvrVideoWindowControl(sink); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/windows/qwindowsintegration_p.h b/src/multimedia/platform/windows/qwindowsintegration_p.h deleted file mode 100644 index 835d502c1..000000000 --- a/src/multimedia/platform/windows/qwindowsintegration_p.h +++ /dev/null @@ -1,89 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QWINDOWSINTEGRATION_H -#define QWINDOWSINTEGRATION_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediaintegration_p.h> - -QT_BEGIN_NAMESPACE - -class QWindowsMediaDevices; -class QWindowsFormatInfo; - -class QWindowsMediaIntegration : public QPlatformMediaIntegration -{ -public: - QWindowsMediaIntegration(); - ~QWindowsMediaIntegration(); - - void addRefCount(); - void releaseRefCount(); - - QPlatformMediaDevices *devices() override; - QPlatformMediaFormatInfo *formatInfo() override; - - QPlatformMediaCaptureSession *createCaptureSession() override; - - QPlatformAudioDecoder *createAudioDecoder(QAudioDecoder *decoder) override; - QPlatformMediaPlayer *createPlayer(QMediaPlayer *parent) override; - QPlatformCamera *createCamera(QCamera *camera) override; - QPlatformMediaEncoder *createEncoder(QMediaRecorder *encoder) override; - QPlatformImageCapture *createImageCapture(QImageCapture *imageCapture) override; - - QPlatformVideoSink *createVideoSink(QVideoSink *sink) override; - - QWindowsMediaDevices *m_devices = nullptr; - QWindowsFormatInfo *m_formatInfo = nullptr; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/windows/qwindowsmediadevices.cpp b/src/multimedia/platform/windows/qwindowsmediadevices.cpp deleted file mode 100644 index 2b1a74a44..000000000 --- a/src/multimedia/platform/windows/qwindowsmediadevices.cpp +++ /dev/null @@ -1,532 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#include "qwindowsmediadevices_p.h" -#include "qmediadevices.h" -#include "qcameradevice_p.h" -#include "qvarlengtharray.h" - -#include "private/qwindowsaudiosource_p.h" -#include "private/qwindowsaudiosink_p.h" -#include "private/qwindowsaudiodevice_p.h" -#include "private/qwindowsmultimediautils_p.h" - -#include <private/mftvideo_p.h> - -#include <Dbt.h> -#include <ks.h> - -#include <mmsystem.h> -#include <mmddk.h> -#include <mfapi.h> -#include <mfobjects.h> -#include <mfidl.h> -#include <mfreadwrite.h> -#include <Mferror.h> -#include <mmdeviceapi.h> -#include <Functiondiscoverykeys_devpkey.h> -#include "private/qwindowsaudioutils_p.h" - -QT_BEGIN_NAMESPACE - -class CMMNotificationClient : public IMMNotificationClient -{ - LONG m_cRef; - QWindowsIUPointer<IMMDeviceEnumerator> m_enumerator; - QWindowsMediaDevices *m_windowsMediaDevices; - QMap<QString, DWORD> m_deviceState; - -public: - CMMNotificationClient(QWindowsMediaDevices *windowsMediaDevices, - QWindowsIUPointer<IMMDeviceEnumerator> enumerator, - QMap<QString, DWORD> &&deviceState) : - m_cRef(1), - m_enumerator(enumerator), - m_windowsMediaDevices(windowsMediaDevices), - m_deviceState(deviceState) - {} - - virtual ~CMMNotificationClient() {} - - // IUnknown methods -- AddRef, Release, and QueryInterface - ULONG STDMETHODCALLTYPE AddRef() - { - return InterlockedIncrement(&m_cRef); - } - - ULONG STDMETHODCALLTYPE Release() - { - ULONG ulRef = InterlockedDecrement(&m_cRef); - if (0 == ulRef) { - delete this; - } - return ulRef; - } - - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface) - { - if (IID_IUnknown == riid) { - AddRef(); - *ppvInterface = (IUnknown*)this; - } else if (__uuidof(IMMNotificationClient) == riid) { - AddRef(); - *ppvInterface = (IMMNotificationClient*)this; - } else { - *ppvInterface = NULL; - return E_NOINTERFACE; - } - return S_OK; - } - - HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR) - { - if (role == ERole::eMultimedia) - emitAudioDevicesChanged(flow); - - return S_OK; - } - - HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR deviceID) - { - auto it = m_deviceState.find(QString::fromWCharArray(deviceID)); - if (it == std::end(m_deviceState)) { - m_deviceState.insert(QString::fromWCharArray(deviceID), DEVICE_STATE_ACTIVE); - emitAudioDevicesChanged(deviceID); - } - - return S_OK; - }; - - HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR deviceID) - { - auto key = QString::fromWCharArray(deviceID); - auto it = m_deviceState.find(key); - if (it != std::end(m_deviceState)) { - if (it.value() == DEVICE_STATE_ACTIVE) - emitAudioDevicesChanged(deviceID); - m_deviceState.remove(key); - } - - return S_OK; - } - - HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR deviceID, DWORD newState) - { - if (auto it = m_deviceState.find(QString::fromWCharArray(deviceID)); it != std::end(m_deviceState)) { - // If either the old state or the new state is active emit device change - if ((it.value() == DEVICE_STATE_ACTIVE) != (newState == DEVICE_STATE_ACTIVE)) { - emitAudioDevicesChanged(deviceID); - } - it.value() = newState; - } - - return S_OK; - } - - HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR, const PROPERTYKEY) - { - return S_OK; - } - - void emitAudioDevicesChanged(EDataFlow flow) - { - // windowsMediaDevice may be deleted as we are executing the callback - if (flow == EDataFlow::eCapture) { - m_windowsMediaDevices->audioInputsChanged(); - } else if (flow == EDataFlow::eRender) { - m_windowsMediaDevices->audioOutputsChanged(); - } - } - - void emitAudioDevicesChanged(LPCWSTR deviceID) - { - QWindowsIUPointer<IMMDevice> device; - QWindowsIUPointer<IMMEndpoint> endpoint; - EDataFlow flow; - - if (SUCCEEDED(m_enumerator->GetDevice(deviceID, device.address())) - && SUCCEEDED(device->QueryInterface(__uuidof(IMMEndpoint), (void**)endpoint.address())) - && SUCCEEDED(endpoint->GetDataFlow(&flow))) - { - emitAudioDevicesChanged(flow); - } - } -}; - -LRESULT deviceNotificationWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - if (message == WM_DEVICECHANGE) { - auto b = (PDEV_BROADCAST_HDR)lParam; - if (b && b->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { - auto wmd = reinterpret_cast<QWindowsMediaDevices *>(GetWindowLongPtr(hWnd, GWLP_USERDATA)); - - if (wmd) { - if (wParam == DBT_DEVICEARRIVAL) { - wmd->videoInputsChanged(); - } else if (wParam == DBT_DEVICEREMOVECOMPLETE) { - wmd->videoInputsChanged(); - } - } - } - } - - return 1; -} - -static const auto windowClassName = TEXT("QWindowsMediaDevicesMessageWindow"); - -HWND createMessageOnlyWindow() -{ - WNDCLASSEX wx = {}; - wx.cbSize = sizeof(WNDCLASSEX); - wx.lpfnWndProc = deviceNotificationWndProc; - wx.hInstance = GetModuleHandle(nullptr); - wx.lpszClassName = windowClassName; - - if (!RegisterClassEx(&wx)) - return nullptr; - - auto hwnd = CreateWindowEx(0, windowClassName, TEXT("Message"), - 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, nullptr); - if (!hwnd) { - UnregisterClass(windowClassName, GetModuleHandle(nullptr)); - return nullptr; - } - - return hwnd; -} - -QWindowsMediaDevices::QWindowsMediaDevices() - : QPlatformMediaDevices(), - m_videoDeviceMsgWindow(nullptr), - m_videoDeviceNotification(nullptr) - -{ - CoInitialize(nullptr); - - auto hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, - CLSCTX_INPROC_SERVER,__uuidof(IMMDeviceEnumerator), - (void**)&m_deviceEnumerator); - - if (SUCCEEDED(hr)) { - QMap<QString, DWORD> devState; - QWindowsIUPointer<IMMDeviceCollection> devColl; - UINT count = 0; - - if (SUCCEEDED(m_deviceEnumerator->EnumAudioEndpoints(EDataFlow::eAll, DEVICE_STATEMASK_ALL, devColl.address())) - && SUCCEEDED(devColl->GetCount(&count))) - { - for (UINT i = 0; i < count; i++) { - QWindowsIUPointer<IMMDevice> device; - DWORD state = 0; - LPWSTR id = nullptr; - - if (SUCCEEDED(devColl->Item(i, device.address())) - && SUCCEEDED(device->GetState(&state)) - && SUCCEEDED(device->GetId(&id))) - { - devState.insert(QString::fromWCharArray(id), state); - CoTaskMemFree(id); - } - } - } - - - m_notificationClient.reset(new CMMNotificationClient(this, m_deviceEnumerator, std::move(devState))); - m_deviceEnumerator->RegisterEndpointNotificationCallback(m_notificationClient); - - } else { - qWarning() << "Audio device change notification disabled"; - } - - m_videoDeviceMsgWindow = createMessageOnlyWindow(); - if (m_videoDeviceMsgWindow) { - SetWindowLongPtr(m_videoDeviceMsgWindow, GWLP_USERDATA, (LONG_PTR)this); - - DEV_BROADCAST_DEVICEINTERFACE di = { 0 }; - di.dbcc_size = sizeof(di); - di.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; - di.dbcc_classguid = KSCATEGORY_VIDEO_CAMERA; - - m_videoDeviceNotification = - RegisterDeviceNotification(m_videoDeviceMsgWindow, &di, DEVICE_NOTIFY_WINDOW_HANDLE); - if (!m_videoDeviceNotification) { - DestroyWindow(m_videoDeviceMsgWindow); - m_videoDeviceMsgWindow = nullptr; - - UnregisterClass(windowClassName, GetModuleHandle(nullptr)); - } - } - - if (!m_videoDeviceNotification) { - qWarning() << "Video device change notification disabled"; - } -} - -QWindowsMediaDevices::~QWindowsMediaDevices() -{ - if (m_deviceEnumerator) { - m_deviceEnumerator->UnregisterEndpointNotificationCallback(m_notificationClient); - } - - m_deviceEnumerator.reset(); - m_notificationClient.reset(); - - if (m_videoDeviceNotification) { - UnregisterDeviceNotification(m_videoDeviceNotification); - } - - if (m_videoDeviceMsgWindow) { - DestroyWindow(m_videoDeviceMsgWindow); - UnregisterClass(windowClassName, GetModuleHandle(nullptr)); - } - - CoUninitialize(); -} - -QList<QAudioDevice> QWindowsMediaDevices::availableDevices(QAudioDevice::Mode mode) const -{ - const auto audioOut = mode == QAudioDevice::Output; - - const auto defaultAudioDeviceID = [this, audioOut]{ - const auto dataFlow = audioOut ? EDataFlow::eRender : EDataFlow::eCapture; - QWindowsIUPointer<IMMDevice> dev; - LPWSTR id = nullptr; - QString sid; - - if (SUCCEEDED(m_deviceEnumerator->GetDefaultAudioEndpoint(dataFlow, ERole::eMultimedia, dev.address())) - && SUCCEEDED(dev->GetId(&id))) { - sid = QString::fromWCharArray(id); - CoTaskMemFree(id); - } - return sid.toUtf8(); - }(); - - QList<QAudioDevice> devices; - - auto waveDevices = audioOut ? waveOutGetNumDevs() : waveInGetNumDevs(); - - for (auto waveID = 0u; waveID < waveDevices; waveID++) { - auto wave = IntToPtr(waveID); - auto waveMessage = [wave, audioOut](UINT msg, auto p0, auto p1) { - return audioOut ? waveOutMessage((HWAVEOUT)wave, msg, (DWORD_PTR)p0, (DWORD_PTR)p1) - : waveInMessage((HWAVEIN)wave, msg, (DWORD_PTR)p0, (DWORD_PTR)p1); - }; - - size_t len = 0; - if (waveMessage(DRV_QUERYFUNCTIONINSTANCEIDSIZE, &len, 0) != MMSYSERR_NOERROR) - continue; - - QVarLengthArray<WCHAR> id(len); - if (waveMessage(DRV_QUERYFUNCTIONINSTANCEID, id.data(), len) != MMSYSERR_NOERROR) - continue; - - QWindowsIUPointer<IMMDevice> device; - QWindowsIUPointer<IPropertyStore> props; - if (FAILED(m_deviceEnumerator->GetDevice(id.data(), device.address())) - || FAILED(device->OpenPropertyStore(STGM_READ, props.address()))) { - continue; - } - - PROPVARIANT varName; - PropVariantInit(&varName); - - if (SUCCEEDED(props->GetValue(PKEY_Device_FriendlyName, &varName))) { - auto description = QString::fromWCharArray(varName.pwszVal); - auto strID = QString::fromWCharArray(id.data()).toUtf8(); - - auto dev = new QWindowsAudioDeviceInfo(strID, waveID, description, mode); - dev->isDefault = strID == defaultAudioDeviceID; - - devices.append(dev->create()); - } - PropVariantClear(&varName); - } - - return devices; -} - -QList<QAudioDevice> QWindowsMediaDevices::audioInputs() const -{ - return availableDevices(QAudioDevice::Input); -} - -QList<QAudioDevice> QWindowsMediaDevices::audioOutputs() const -{ - return availableDevices(QAudioDevice::Output); -} - -QList<QCameraDevice> QWindowsMediaDevices::videoInputs() const -{ - QList<QCameraDevice> cameras; - - IMFAttributes *pAttributes = NULL; - IMFActivate **ppDevices = NULL; - - // Create an attribute store to specify the enumeration parameters. - HRESULT hr = MFCreateAttributes(&pAttributes, 1); - if (SUCCEEDED(hr)) { - // Source type: video capture devices - hr = pAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, - MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID); - - if (SUCCEEDED(hr)) { - // Enumerate devices. - UINT32 count; - hr = MFEnumDeviceSources(pAttributes, &ppDevices, &count); - if (SUCCEEDED(hr)) { - // Iterate through devices. - for (int index = 0; index < int(count); index++) { - QCameraDevicePrivate *info = new QCameraDevicePrivate; - - IMFMediaSource *pSource = NULL; - IMFSourceReader *reader = NULL; - - WCHAR *deviceName = NULL; - UINT32 deviceNameLength = 0; - UINT32 deviceIdLength = 0; - WCHAR *deviceId = NULL; - - hr = ppDevices[index]->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, - &deviceName, &deviceNameLength); - if (SUCCEEDED(hr)) - info->description = QString::fromWCharArray(deviceName); - CoTaskMemFree(deviceName); - - hr = ppDevices[index]->GetAllocatedString( - MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &deviceId, - &deviceIdLength); - if (SUCCEEDED(hr)) - info->id = QString::fromWCharArray(deviceId).toUtf8(); - CoTaskMemFree(deviceId); - - // Create the media source object. - hr = ppDevices[index]->ActivateObject( - IID_PPV_ARGS(&pSource)); - // Create the media source reader. - hr = MFCreateSourceReaderFromMediaSource(pSource, NULL, &reader); - if (SUCCEEDED(hr)) { - QList<QSize> photoResolutions; - QList<QCameraFormat> videoFormats; - - DWORD dwMediaTypeIndex = 0; - IMFMediaType *mediaFormat = NULL; - GUID subtype = GUID_NULL; - HRESULT mediaFormatResult = S_OK; - - UINT32 frameRateMin = 0u; - UINT32 frameRateMax = 0u; - UINT32 denominator = 0u; - DWORD index = 0u; - UINT32 width = 0u; - UINT32 height = 0u; - - while (SUCCEEDED(mediaFormatResult)) { - // Loop through the supported formats for the video device - mediaFormatResult = reader->GetNativeMediaType( - (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, dwMediaTypeIndex, - &mediaFormat); - if (mediaFormatResult == MF_E_NO_MORE_TYPES) - break; - else if (SUCCEEDED(mediaFormatResult)) { - QVideoFrameFormat::PixelFormat pixelFormat = QVideoFrameFormat::Format_Invalid; - QSize resolution; - float minFr = .0; - float maxFr = .0; - - if (SUCCEEDED(mediaFormat->GetGUID(MF_MT_SUBTYPE, &subtype))) - pixelFormat = QWindowsMultimediaUtils::pixelFormatFromMediaSubtype(subtype); - - if (SUCCEEDED(MFGetAttributeSize(mediaFormat, MF_MT_FRAME_SIZE, &width, - &height))) { - resolution.rheight() = (int)height; - resolution.rwidth() = (int)width; - photoResolutions << resolution; - } - - if (SUCCEEDED(MFGetAttributeRatio(mediaFormat, MF_MT_FRAME_RATE_RANGE_MIN, - &frameRateMin, &denominator))) - minFr = qreal(frameRateMin) / denominator; - if (SUCCEEDED(MFGetAttributeRatio(mediaFormat, MF_MT_FRAME_RATE_RANGE_MAX, - &frameRateMax, &denominator))) - maxFr = qreal(frameRateMax) / denominator; - - auto *f = new QCameraFormatPrivate { QSharedData(), pixelFormat, - resolution, minFr, maxFr }; - videoFormats << f->create(); - } - ++dwMediaTypeIndex; - } - if (mediaFormat) - mediaFormat->Release(); - - info->videoFormats = videoFormats; - info->photoResolutions = photoResolutions; - } - if (reader) - reader->Release(); - cameras.append(info->create()); - } - } - for (DWORD i = 0; i < count; i++) { - if (ppDevices[i]) - ppDevices[i]->Release(); - } - CoTaskMemFree(ppDevices); - } - } - if (pAttributes) - pAttributes->Release(); - - return cameras; -} - -QPlatformAudioSource *QWindowsMediaDevices::createAudioSource(const QAudioDevice &deviceInfo) -{ - const auto *devInfo = static_cast<const QWindowsAudioDeviceInfo *>(deviceInfo.handle()); - return new QWindowsAudioSource(devInfo->waveId()); -} - -QPlatformAudioSink *QWindowsMediaDevices::createAudioSink(const QAudioDevice &deviceInfo) -{ - const auto *devInfo = static_cast<const QWindowsAudioDeviceInfo *>(deviceInfo.handle()); - return new QWindowsAudioSink(devInfo->waveId()); -} - -QT_END_NAMESPACE diff --git a/src/multimedia/platform/windows/qwindowsmediadevices_p.h b/src/multimedia/platform/windows/qwindowsmediadevices_p.h deleted file mode 100644 index 29e214d79..000000000 --- a/src/multimedia/platform/windows/qwindowsmediadevices_p.h +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 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$ -** -****************************************************************************/ - -#ifndef QWINDOWSMEDIADEVICES_H -#define QWINDOWSMEDIADEVICES_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qplatformmediadevices_p.h> -#include <private/qwindowsiupointer_p.h> -#include <qset.h> -#include <qaudio.h> -#include <qaudiodevice.h> -#include <windows.h> - -struct IMMDeviceEnumerator; - -QT_BEGIN_NAMESPACE - -class QWindowsEngine; -class CMMNotificationClient; - -LRESULT deviceNotificationWndProc(HWND, UINT, WPARAM, LPARAM); - -class QWindowsMediaDevices : public QPlatformMediaDevices -{ -public: - QWindowsMediaDevices(); - virtual ~QWindowsMediaDevices(); - - QList<QAudioDevice> audioInputs() const override; - QList<QAudioDevice> audioOutputs() const override; - QList<QCameraDevice> videoInputs() const override; - QPlatformAudioSource *createAudioSource(const QAudioDevice &deviceInfo) override; - QPlatformAudioSink *createAudioSink(const QAudioDevice &deviceInfo) override; - -private: - QList<QAudioDevice> availableDevices(QAudioDevice::Mode mode) const; - - QWindowsIUPointer<IMMDeviceEnumerator> m_deviceEnumerator; - QWindowsIUPointer<CMMNotificationClient> m_notificationClient; - HWND m_videoDeviceMsgWindow; - HDEVNOTIFY m_videoDeviceNotification; - - friend CMMNotificationClient; - friend LRESULT deviceNotificationWndProc(HWND, UINT, WPARAM, LPARAM); -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/multimedia/platform/windows/sourceresolver.cpp b/src/multimedia/platform/windows/sourceresolver.cpp deleted file mode 100644 index 93af15a74..000000000 --- a/src/multimedia/platform/windows/sourceresolver.cpp +++ /dev/null @@ -1,325 +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$ -** -****************************************************************************/ - -#include "mfstream_p.h" -#include "sourceresolver_p.h" -#include <mferror.h> -#include <nserror.h> -#include <QtCore/qfile.h> -#include <QtCore/qdebug.h> -#include <QtMultimedia/qmediaplayer.h> - -/* - SourceResolver is separated from MFPlayerSession to handle the work of resolving a media source - asynchronously. You call SourceResolver::load to request resolving a media source asynchronously, - and it will emit mediaSourceReady() when resolving is done. You can call SourceResolver::cancel to - stop the previous load operation if there is any. -*/ - -SourceResolver::SourceResolver() - : m_cRef(1) - , m_cancelCookie(0) - , m_sourceResolver(0) - , m_mediaSource(0) - , m_stream(0) -{ -} - -SourceResolver::~SourceResolver() -{ - shutdown(); - if (m_mediaSource) { - m_mediaSource->Release(); - m_mediaSource = NULL; - } - - if (m_cancelCookie) - m_cancelCookie->Release(); - if (m_sourceResolver) - m_sourceResolver->Release(); -} - -STDMETHODIMP SourceResolver::QueryInterface(REFIID riid, LPVOID *ppvObject) -{ - if (!ppvObject) - return E_POINTER; - if (riid == IID_IUnknown) { - *ppvObject = static_cast<IUnknown*>(this); - } else if (riid == IID_IMFAsyncCallback) { - *ppvObject = static_cast<IMFAsyncCallback*>(this); - } else { - *ppvObject = NULL; - return E_NOINTERFACE; - } - AddRef(); - return S_OK; -} - -STDMETHODIMP_(ULONG) SourceResolver::AddRef(void) -{ - return InterlockedIncrement(&m_cRef); -} - -STDMETHODIMP_(ULONG) SourceResolver::Release(void) -{ - LONG cRef = InterlockedDecrement(&m_cRef); - if (cRef == 0) - this->deleteLater(); - return cRef; -} - -HRESULT STDMETHODCALLTYPE SourceResolver::Invoke(IMFAsyncResult *pAsyncResult) -{ - QMutexLocker locker(&m_mutex); - - if (!m_sourceResolver) - return S_OK; - - MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID; - IUnknown* pSource = NULL; - State *state = static_cast<State*>(pAsyncResult->GetStateNoAddRef()); - - HRESULT hr = S_OK; - if (state->fromStream()) - hr = m_sourceResolver->EndCreateObjectFromByteStream(pAsyncResult, &ObjectType, &pSource); - else - hr = m_sourceResolver->EndCreateObjectFromURL(pAsyncResult, &ObjectType, &pSource); - - if (state->sourceResolver() != m_sourceResolver) { - //This is a cancelled one - return S_OK; - } - - if (m_cancelCookie) { - m_cancelCookie->Release(); - m_cancelCookie = NULL; - } - - if (FAILED(hr)) { - emit error(hr); - return S_OK; - } - - if (m_mediaSource) { - m_mediaSource->Release(); - m_mediaSource = NULL; - } - - hr = pSource->QueryInterface(IID_PPV_ARGS(&m_mediaSource)); - pSource->Release(); - if (FAILED(hr)) { - emit error(hr); - return S_OK; - } - - emit mediaSourceReady(); - - return S_OK; -} - -HRESULT STDMETHODCALLTYPE SourceResolver::GetParameters(DWORD*, DWORD*) -{ - return E_NOTIMPL; -} - -void SourceResolver::load(const QUrl &url, QIODevice* stream) -{ - QMutexLocker locker(&m_mutex); - HRESULT hr = S_OK; - if (!m_sourceResolver) - hr = MFCreateSourceResolver(&m_sourceResolver); - - if (m_stream) { - m_stream->Release(); - m_stream = NULL; - } - - if (FAILED(hr)) { - qWarning() << "Failed to create Source Resolver!"; - emit error(hr); - } else if (stream) { - QString urlString = url.toString(); - m_stream = new MFStream(stream, false); - hr = m_sourceResolver->BeginCreateObjectFromByteStream( - m_stream, urlString.isEmpty() ? 0 : reinterpret_cast<LPCWSTR>(urlString.utf16()), - MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE - , NULL, &m_cancelCookie, this, new State(m_sourceResolver, true)); - if (FAILED(hr)) { - qWarning() << "Unsupported stream!"; - emit error(hr); - } - } else { -#ifdef DEBUG_MEDIAFOUNDATION - qDebug() << "loading :" << url; - qDebug() << "url path =" << url.path().mid(1); -#endif -#ifdef TEST_STREAMING - //Testing stream function - if (url.scheme() == QLatin1String("file")) { - stream = new QFile(url.path().mid(1)); - if (stream->open(QIODevice::ReadOnly)) { - m_stream = new MFStream(stream, true); - hr = m_sourceResolver->BeginCreateObjectFromByteStream( - m_stream, reinterpret_cast<const OLECHAR *>(url.toString().utf16()), - MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE, - NULL, &m_cancelCookie, this, new State(m_sourceResolver, true)); - if (FAILED(hr)) { - qWarning() << "Unsupported stream!"; - emit error(hr); - } - } else { - delete stream; - emit error(QMediaPlayer::FormatError); - } - } else -#endif - if (url.scheme() == QLatin1String("qrc")) { - // If the canonical URL refers to a Qt resource, open with QFile and use - // the stream playback capability to play. - stream = new QFile(QLatin1Char(':') + url.path()); - if (stream->open(QIODevice::ReadOnly)) { - m_stream = new MFStream(stream, true); - hr = m_sourceResolver->BeginCreateObjectFromByteStream( - m_stream, reinterpret_cast<const OLECHAR *>(url.toString().utf16()), - MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE, - NULL, &m_cancelCookie, this, new State(m_sourceResolver, true)); - if (FAILED(hr)) { - qWarning() << "Unsupported stream!"; - emit error(hr); - } - } else { - delete stream; - emit error(QMediaPlayer::FormatError); - } - } else { - hr = m_sourceResolver->BeginCreateObjectFromURL( - reinterpret_cast<const OLECHAR *>(url.toString().utf16()), - MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE, - NULL, &m_cancelCookie, this, new State(m_sourceResolver, false)); - if (FAILED(hr)) { - qWarning() << "Unsupported url scheme!"; - emit error(hr); - } - } - } -} - -void SourceResolver::cancel() -{ - QMutexLocker locker(&m_mutex); - if (m_cancelCookie) { - m_sourceResolver->CancelObjectCreation(m_cancelCookie); - m_cancelCookie->Release(); - m_cancelCookie = NULL; - m_sourceResolver->Release(); - m_sourceResolver = NULL; - } -} - -void SourceResolver::shutdown() -{ - if (m_mediaSource) { - m_mediaSource->Shutdown(); - m_mediaSource->Release(); - m_mediaSource = NULL; - } - - if (m_stream) { - m_stream->Release(); - m_stream = NULL; - } -} - -IMFMediaSource* SourceResolver::mediaSource() const -{ - return m_mediaSource; -} - -///////////////////////////////////////////////////////////////////////////////// -SourceResolver::State::State(IMFSourceResolver *sourceResolver, bool fromStream) - : m_cRef(0) - , m_sourceResolver(sourceResolver) - , m_fromStream(fromStream) -{ - sourceResolver->AddRef(); -} - -SourceResolver::State::~State() -{ - m_sourceResolver->Release(); -} - -STDMETHODIMP SourceResolver::State::QueryInterface(REFIID riid, LPVOID *ppvObject) -{ - if (!ppvObject) - return E_POINTER; - if (riid == IID_IUnknown) { - *ppvObject = static_cast<IUnknown*>(this); - } else { - *ppvObject = NULL; - return E_NOINTERFACE; - } - AddRef(); - return S_OK; -} - -STDMETHODIMP_(ULONG) SourceResolver::State::AddRef(void) -{ - return InterlockedIncrement(&m_cRef); -} - -STDMETHODIMP_(ULONG) SourceResolver::State::Release(void) -{ - LONG cRef = InterlockedDecrement(&m_cRef); - if (cRef == 0) - delete this; - // For thread safety, return a temporary variable. - return cRef; -} - -IMFSourceResolver* SourceResolver::State::sourceResolver() const -{ - return m_sourceResolver; -} - -bool SourceResolver::State::fromStream() const -{ - return m_fromStream; -} - diff --git a/src/multimedia/platform/windows/sourceresolver_p.h b/src/multimedia/platform/windows/sourceresolver_p.h deleted file mode 100644 index 07da1d8bd..000000000 --- a/src/multimedia/platform/windows/sourceresolver_p.h +++ /dev/null @@ -1,115 +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$ -** -****************************************************************************/ - -#ifndef SOURCERESOLVER_H -#define SOURCERESOLVER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "mfstream_p.h" -#include <QUrl> - -class SourceResolver: public QObject, public IMFAsyncCallback -{ - Q_OBJECT -public: - SourceResolver(); - - ~SourceResolver(); - - STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject); - STDMETHODIMP_(ULONG) AddRef(void); - STDMETHODIMP_(ULONG) Release(void); - - HRESULT STDMETHODCALLTYPE Invoke(IMFAsyncResult *pAsyncResult); - - HRESULT STDMETHODCALLTYPE GetParameters(DWORD*, DWORD*); - - void load(const QUrl &url, QIODevice* stream); - - void cancel(); - - void shutdown(); - - IMFMediaSource* mediaSource() const; - -Q_SIGNALS: - void error(long hr); - void mediaSourceReady(); - -private: - class State : public IUnknown - { - public: - State(IMFSourceResolver *sourceResolver, bool fromStream); - ~State(); - - STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObject); - - STDMETHODIMP_(ULONG) AddRef(void); - - STDMETHODIMP_(ULONG) Release(void); - - IMFSourceResolver* sourceResolver() const; - bool fromStream() const; - - private: - long m_cRef; - IMFSourceResolver *m_sourceResolver; - bool m_fromStream; - }; - - long m_cRef; - IUnknown *m_cancelCookie; - IMFSourceResolver *m_sourceResolver; - IMFMediaSource *m_mediaSource; - MFStream *m_stream; - QMutex m_mutex; -}; - -#endif |