diff options
105 files changed, 1396 insertions, 886 deletions
diff --git a/dependencies.yaml b/dependencies.yaml index 1ac2b5b7d..451bcd293 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -1,13 +1,13 @@ dependencies: ../qtbase: - ref: e7362764d4931f255d2377462df8ac7a0d4e7c84 + ref: 0cd5eb895cc96126a495eb3d2d258be47eed193a required: true ../qtdeclarative: - ref: 330fa93d6e9003c0ea188b9e703f2b3f0448f8c8 + ref: c63bb2bad5b4e741ed8a1e16d8f1f916c9baf61d required: false ../qtquick3d: - ref: 9653f92bc1cf129940c2159acac35481324ba538 + ref: b26d2cec2293e6b3efce6b695a58c11df1c78dae required: false ../qtshadertools: - ref: 626be35fe558e5d5d8337cc64fc8a8854ec6eff4 + ref: c11b69d81c83cb0d78e5de07b71b4eb1e8ae8a11 required: true diff --git a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtAudioDeviceManager.java b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtAudioDeviceManager.java index 83d704838..3bc589de6 100644 --- a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtAudioDeviceManager.java +++ b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtAudioDeviceManager.java @@ -239,7 +239,7 @@ public class QtAudioDeviceManager setAudioOutput(AudioManager.MODE_IN_COMMUNICATION, true, false); return true; case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER: - setAudioOutput(AudioManager.STREAM_MUSIC, false, true); + setAudioOutput(AudioManager.MODE_IN_COMMUNICATION, false, true); return true; case AudioDeviceInfo.TYPE_WIRED_HEADSET: case AudioDeviceInfo.TYPE_WIRED_HEADPHONES: diff --git a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtCamera2.java b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtCamera2.java index 39feff6c7..ac8140197 100644 --- a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtCamera2.java +++ b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtCamera2.java @@ -24,6 +24,7 @@ import android.graphics.ImageFormat; import android.os.Handler; import android.os.HandlerThread; import android.util.Log; +import android.util.Range; import android.view.Surface; import android.media.MediaCodec; import android.media.MediaCodecInfo; @@ -57,11 +58,12 @@ public class QtCamera2 { private int mState = STATE_PREVIEW; private Object mStartMutex = new Object(); private boolean mIsStarted = false; - private static int MaxNumberFrames = 10; + private static int MaxNumberFrames = 12; private int mFlashMode = CaptureRequest.CONTROL_AE_MODE_ON; private int mTorchMode = CameraMetadata.FLASH_MODE_OFF; private int mAFMode = CaptureRequest.CONTROL_AF_MODE_OFF; private float mZoomFactor = 1.0f; + private Range<Integer> mFpsRange = null; private QtExifDataHandler mExifDataHandler = null; native void onCameraOpened(String cameraId); @@ -261,7 +263,14 @@ public class QtCamera2 { } }; - public boolean addImageReader(int width, int height, int format) { + + public void prepareCamera(int width, int height, int format, int minFps, int maxFps) { + + addImageReader(width, height, format); + setFrameRate(minFps, maxFps); + } + + private void addImageReader(int width, int height, int format) { if (mImageReader != null) removeSurface(mImageReader.getSurface()); @@ -276,8 +285,14 @@ public class QtCamera2 { mCapturedPhotoReader = ImageReader.newInstance(width, height, format, MaxNumberFrames); mCapturedPhotoReader.setOnImageAvailableListener(mOnPhotoAvailableListener, mBackgroundHandler); addSurface(mCapturedPhotoReader.getSurface()); + } + + private void setFrameRate(int minFrameRate, int maxFrameRate) { - return true; + if (minFrameRate <= 0 || maxFrameRate <= 0) + mFpsRange = null; + else + mFpsRange = new Range<>(minFrameRate, maxFrameRate); } public boolean addSurface(Surface surface) { @@ -335,7 +350,8 @@ public class QtCamera2 { mPreviewRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, CameraMetadata.CONTROL_CAPTURE_INTENT_VIDEO_RECORD); if (mZoomFactor != 1.0f) mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, getScalerCropRegion()); - + if (mFpsRange != null) + mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, mFpsRange); mPreviewRequest = mPreviewRequestBuilder.build(); mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler); mIsStarted = true; diff --git a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtVideoDeviceManager.java b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtVideoDeviceManager.java index b3ba8f3dc..3339bddc9 100644 --- a/src/android/jar/src/org/qtproject/qt/android/multimedia/QtVideoDeviceManager.java +++ b/src/android/jar/src/org/qtproject/qt/android/multimedia/QtVideoDeviceManager.java @@ -137,6 +137,7 @@ public class QtVideoDeviceManager { return activeArraySize; } + static final int maxResolution = 3840*2160; // 4k resolution public String[] getStreamConfigurationsSizes(String cameraId, int imageFormat) { CameraCharacteristics characteristics = getCameraCharacteristics(cameraId); @@ -148,13 +149,14 @@ public class QtVideoDeviceManager { if (sizes == null) return new String[0]; - String[] stream = new String[sizes.length]; + ArrayList<String> stream = new ArrayList<>(); for (int index = 0; index < sizes.length; index++) { - stream[index] = sizes[index].toString(); + if (sizes[index].getWidth() * sizes[index].getHeight() <= maxResolution) + stream.add(sizes[index].toString()); } - return stream; + return stream.toArray(new String[0]); } public int stringToControlAEMode(String mode) { diff --git a/src/multimedia/alsa/qalsaaudiosink.cpp b/src/multimedia/alsa/qalsaaudiosink.cpp index 98a68861f..e515219a2 100644 --- a/src/multimedia/alsa/qalsaaudiosink.cpp +++ b/src/multimedia/alsa/qalsaaudiosink.cpp @@ -30,13 +30,13 @@ QAlsaAudioSink::QAlsaAudioSink(const QByteArray &device, QObject *parent) m_device = device; timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), this, SLOT(userFeed())); + connect(timer, &QTimer::timeout, this, &QAlsaAudioSink::userFeed); } QAlsaAudioSink::~QAlsaAudioSink() { close(); - disconnect(timer, SIGNAL(timeout())); + disconnect(timer, &QTimer::timeout, this, &QAlsaAudioSink::userFeed); QCoreApplication::processEvents(); delete timer; } @@ -130,6 +130,7 @@ int QAlsaAudioSink::setFormat() pcmformat = SND_PCM_FORMAT_FLOAT_BE; else pcmformat = SND_PCM_FORMAT_FLOAT_LE; + break; default: break; } diff --git a/src/multimedia/alsa/qalsaaudiosink_p.h b/src/multimedia/alsa/qalsaaudiosink_p.h index 7e8836f96..0f5a5aa5a 100644 --- a/src/multimedia/alsa/qalsaaudiosink_p.h +++ b/src/multimedia/alsa/qalsaaudiosink_p.h @@ -96,7 +96,6 @@ private: char* audioBuffer = nullptr; snd_pcm_t* handle = nullptr; snd_pcm_access_t access = SND_PCM_ACCESS_RW_INTERLEAVED; - snd_pcm_format_t pcmformat = SND_PCM_FORMAT_S16; snd_pcm_hw_params_t *hwparams = nullptr; qreal m_volume = 1.0f; }; diff --git a/src/multimedia/alsa/qalsaaudiosource.cpp b/src/multimedia/alsa/qalsaaudiosource.cpp index ce099463d..ebf6e24e2 100644 --- a/src/multimedia/alsa/qalsaaudiosource.cpp +++ b/src/multimedia/alsa/qalsaaudiosource.cpp @@ -16,7 +16,6 @@ #include <QtCore/qvarlengtharray.h> #include <QtMultimedia/private/qaudiohelpers_p.h> #include "qalsaaudiosource_p.h" -#include "qalsaaudiodevice_p.h" QT_BEGIN_NAMESPACE @@ -45,13 +44,13 @@ QAlsaAudioSource::QAlsaAudioSource(const QByteArray &device, QObject *parent) m_device = device; timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), this, SLOT(userFeed())); + connect(timer, &QTimer::timeout, this, &QAlsaAudioSource::userFeed); } QAlsaAudioSource::~QAlsaAudioSource() { close(); - disconnect(timer, SIGNAL(timeout())); + disconnect(timer, &QTimer::timeout, this, &QAlsaAudioSource::userFeed); QCoreApplication::processEvents(); delete timer; } @@ -143,21 +142,22 @@ int QAlsaAudioSource::setFormat() break; case QAudioFormat::Int16: if constexpr (QSysInfo::ByteOrder == QSysInfo::BigEndian) - pcmformat = SND_PCM_FORMAT_S16_LE; - else pcmformat = SND_PCM_FORMAT_S16_BE; + else + pcmformat = SND_PCM_FORMAT_S16_LE; break; case QAudioFormat::Int32: if constexpr (QSysInfo::ByteOrder == QSysInfo::BigEndian) - pcmformat = SND_PCM_FORMAT_S32_LE; - else pcmformat = SND_PCM_FORMAT_S32_BE; + else + pcmformat = SND_PCM_FORMAT_S32_LE; break; case QAudioFormat::Float: if constexpr (QSysInfo::ByteOrder == QSysInfo::BigEndian) - pcmformat = SND_PCM_FORMAT_FLOAT_LE; - else pcmformat = SND_PCM_FORMAT_FLOAT_BE; + else + pcmformat = SND_PCM_FORMAT_FLOAT_LE; + break; default: break; } @@ -370,7 +370,7 @@ bool QAlsaAudioSource::open() bytesAvailable = checkBytesReady(); if(pullMode) - connect(audioSource,SIGNAL(readyRead()),this,SLOT(userFeed())); + connect(audioSource, &QIODevice::readyRead, this, &QAlsaAudioSource::userFeed); // Step 6: Start audio processing chunks = buffer_size/period_size; diff --git a/src/multimedia/alsa/qalsamediadevices.cpp b/src/multimedia/alsa/qalsamediadevices.cpp index 5a133e9d1..9466fa0cd 100644 --- a/src/multimedia/alsa/qalsamediadevices.cpp +++ b/src/multimedia/alsa/qalsamediadevices.cpp @@ -13,6 +13,26 @@ QT_BEGIN_NAMESPACE +namespace { + +struct free_char +{ + void operator()(char *c) const { ::free(c); } +}; + +using unique_str = std::unique_ptr<char, free_char>; + +bool operator==(const unique_str &str, std::string_view sv) +{ + return std::string_view{ str.get() } == sv; +} +bool operator!=(const unique_str &str, std::string_view sv) +{ + return !(str == sv); +} + +} // namespace + QAlsaMediaDevices::QAlsaMediaDevices() : QPlatformMediaDevices() { @@ -22,52 +42,50 @@ 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; - bool hasDefault = false; - - if(snd_device_name_hint(-1, "pcm", &hints) < 0) { + void **hints; + 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"; - } + std::string_view filter = (mode == QAudioDevice::Input) ? "Input" : "Output"; - QAlsaAudioDeviceInfo* sysdefault = nullptr; + QAlsaAudioDeviceInfo *sysdefault = nullptr; - 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 (!hasDefault && strcmp(name, "default") == 0) { - infop->isDefault = true; - hasDefault = true; - } - else if (!sysdefault && !hasDefault && strcmp(name, "sysdefault") == 0) { - sysdefault = infop; - } + auto makeDeviceInfo = [&filter, mode](void *entry) -> QAlsaAudioDeviceInfo * { + unique_str name{ snd_device_name_get_hint(entry, "NAME") }; + if (name && name != "null") { + unique_str descr{ snd_device_name_get_hint(entry, "DESC") }; + unique_str io{ snd_device_name_get_hint(entry, "IOID") }; + + if (descr && (!io || (io == filter))) { + auto *infop = new QAlsaAudioDeviceInfo{ + name.get(), + QString::fromUtf8(descr.get()), + mode, + }; + return infop; } + } + return nullptr; + }; + + bool hasDefault = false; + void **n = hints; + while (*n != NULL) { + QAlsaAudioDeviceInfo *infop = makeDeviceInfo(*n++); - free(descr); - free(io); + if (infop) { + devices.append(infop->create()); + if (!hasDefault && infop->id.startsWith("default")) { + infop->isDefault = true; + hasDefault = true; + } + if (!sysdefault && infop->id.startsWith("sysdefault")) + sysdefault = infop; } - free(name); - ++n; } - snd_device_name_free_hint(hints); if (!hasDefault && sysdefault) { // Make "sysdefault" the default device if there is no "default" device exists @@ -75,11 +93,15 @@ static QList<QAudioDevice> availableDevices(QAudioDevice::Mode mode) hasDefault = true; } if (!hasDefault && devices.size() > 0) { - auto infop = new QAlsaAudioDeviceInfo("default", QString(), QAudioDevice::Output); - infop->isDefault = true; - devices.prepend(infop->create()); + // forcefully declare the first device as "default" + QAlsaAudioDeviceInfo *infop = makeDeviceInfo(hints[0]); + if (infop) { + infop->isDefault = true; + devices.prepend(infop->create()); + } } + snd_device_name_free_hint(hints); return devices; } diff --git a/src/multimedia/audio/qsamplecache_p.cpp b/src/multimedia/audio/qsamplecache_p.cpp index 825c79685..b4be09f72 100644 --- a/src/multimedia/audio/qsamplecache_p.cpp +++ b/src/multimedia/audio/qsamplecache_p.cpp @@ -357,12 +357,13 @@ void QSample::load() Q_ASSERT(QThread::currentThread()->objectName() == QLatin1String("QSampleCache::LoadingThread")); #endif qCDebug(qLcSampleCache) << "QSample: load [" << m_url << "]"; - m_stream = m_parent->networkAccessManager().get(QNetworkRequest(m_url)); - connect(m_stream, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), SLOT(loadingError(QNetworkReply::NetworkError))); + QNetworkReply *reply = m_parent->networkAccessManager().get(QNetworkRequest(m_url)); + m_stream = reply; + connect(reply, &QNetworkReply::errorOccurred, this, &QSample::loadingError); m_waveDecoder = new QWaveDecoder(m_stream); - connect(m_waveDecoder, SIGNAL(formatKnown()), SLOT(decoderReady())); - connect(m_waveDecoder, SIGNAL(parsingError()), SLOT(decoderError())); - connect(m_waveDecoder, SIGNAL(readyRead()), SLOT(readSample())); + connect(m_waveDecoder, &QWaveDecoder::formatKnown, this, &QSample::decoderReady); + connect(m_waveDecoder, &QWaveDecoder::parsingError, this, &QSample::decoderError); + connect(m_waveDecoder, &QIODevice::readyRead, this, &QSample::readSample); m_waveDecoder->open(QIODevice::ReadOnly); } diff --git a/src/multimedia/audio/qwavedecoder.cpp b/src/multimedia/audio/qwavedecoder.cpp index 36ac3c779..452363ddc 100644 --- a/src/multimedia/audio/qwavedecoder.cpp +++ b/src/multimedia/audio/qwavedecoder.cpp @@ -56,7 +56,7 @@ bool QWaveDecoder::open(QIODevice::OpenMode mode) if (canOpen && enoughDataAvailable()) handleData(); else - connect(device, SIGNAL(readyRead()), SLOT(handleData())); + connect(device, &QIODevice::readyRead, this, &QWaveDecoder::handleData); return canOpen; } @@ -274,7 +274,7 @@ bool QWaveDecoder::writeDataLength() void QWaveDecoder::parsingFailed() { Q_ASSERT(device); - device->disconnect(SIGNAL(readyRead()), this, SLOT(handleData())); + disconnect(device, &QIODevice::readyRead, this, &QWaveDecoder::handleData); emit parsingError(); } @@ -386,7 +386,7 @@ void QWaveDecoder::handleData() if (state == QWaveDecoder::WaitingForDataState) { if (findChunk("data")) { - device->disconnect(SIGNAL(readyRead()), this, SLOT(handleData())); + disconnect(device, &QIODevice::readyRead, this, &QWaveDecoder::handleData); chunk descriptor; device->read(reinterpret_cast<char *>(&descriptor), sizeof(chunk)); @@ -400,7 +400,7 @@ void QWaveDecoder::handleData() dataSize = device->size() - headerLength(); haveFormat = true; - connect(device, SIGNAL(readyRead()), SIGNAL(readyRead())); + connect(device, &QIODevice::readyRead, this, &QIODevice::readyRead); emit formatKnown(); return; diff --git a/src/multimedia/camera/qcamera.cpp b/src/multimedia/camera/qcamera.cpp index 527b14c25..a625fb96f 100644 --- a/src/multimedia/camera/qcamera.cpp +++ b/src/multimedia/camera/qcamera.cpp @@ -175,8 +175,8 @@ void QCameraPrivate::init(const QCameraDevice &device) if (cameraDevice.isNull()) _q_error(QCamera::CameraError, QStringLiteral("No camera detected")); control->setCamera(cameraDevice); - q->connect(control, SIGNAL(activeChanged(bool)), q, SIGNAL(activeChanged(bool))); - q->connect(control, SIGNAL(error(int,QString)), q, SLOT(_q_error(int,QString))); + q->connect(control, &QPlatformVideoSource::activeChanged, q, &QCamera::activeChanged); + q->connect(control, SIGNAL(error(int, QString)), q, SLOT(_q_error(int, QString))); } /*! diff --git a/src/multimedia/camera/qimagecapture.cpp b/src/multimedia/camera/qimagecapture.cpp index 9b92ce743..df3ddae3f 100644 --- a/src/multimedia/camera/qimagecapture.cpp +++ b/src/multimedia/camera/qimagecapture.cpp @@ -92,18 +92,15 @@ QImageCapture::QImageCapture(QObject *parent) } d->control = maybeControl.value(); - connect(d->control, SIGNAL(imageExposed(int)), - this, SIGNAL(imageExposed(int))); - connect(d->control, SIGNAL(imageCaptured(int,QImage)), - this, SIGNAL(imageCaptured(int,QImage))); - connect(d->control, SIGNAL(imageMetadataAvailable(int,QMediaMetaData)), - this, SIGNAL(imageMetadataAvailable(int,QMediaMetaData))); - connect(d->control, SIGNAL(imageAvailable(int,QVideoFrame)), - this, SIGNAL(imageAvailable(int,QVideoFrame))); - connect(d->control, SIGNAL(imageSaved(int,QString)), - this, SIGNAL(imageSaved(int,QString))); - connect(d->control, SIGNAL(readyForCaptureChanged(bool)), - this, SIGNAL(readyForCaptureChanged(bool))); + connect(d->control, &QPlatformImageCapture::imageExposed, this, &QImageCapture::imageExposed); + connect(d->control, &QPlatformImageCapture::imageCaptured, this, &QImageCapture::imageCaptured); + connect(d->control, &QPlatformImageCapture::imageMetadataAvailable, this, + &QImageCapture::imageMetadataAvailable); + connect(d->control, &QPlatformImageCapture::imageAvailable, this, + &QImageCapture::imageAvailable); + connect(d->control, &QPlatformImageCapture::imageSaved, this, &QImageCapture::imageSaved); + connect(d->control, &QPlatformImageCapture::readyForCaptureChanged, this, + &QImageCapture::readyForCaptureChanged); connect(d->control, SIGNAL(error(int,int,QString)), this, SLOT(_q_error(int,int,QString))); } diff --git a/src/multimedia/platform/qplatformmediarecorder.cpp b/src/multimedia/platform/qplatformmediarecorder.cpp index ba9ea0165..30dba0a45 100644 --- a/src/multimedia/platform/qplatformmediarecorder.cpp +++ b/src/multimedia/platform/qplatformmediarecorder.cpp @@ -15,12 +15,12 @@ QPlatformMediaRecorder::QPlatformMediaRecorder(QMediaRecorder *parent) void QPlatformMediaRecorder::pause() { - error(QMediaRecorder::FormatError, QMediaRecorder::tr("Pause not supported")); + updateError(QMediaRecorder::FormatError, QMediaRecorder::tr("Pause not supported")); } void QPlatformMediaRecorder::resume() { - error(QMediaRecorder::FormatError, QMediaRecorder::tr("Resume not supported")); + updateError(QMediaRecorder::FormatError, QMediaRecorder::tr("Resume not supported")); } void QPlatformMediaRecorder::stateChanged(QMediaRecorder::RecorderState state) @@ -47,7 +47,7 @@ void QPlatformMediaRecorder::actualLocationChanged(const QUrl &location) emit q->actualLocationChanged(location); } -void QPlatformMediaRecorder::error(QMediaRecorder::Error error, const QString &errorString) +void QPlatformMediaRecorder::updateError(QMediaRecorder::Error error, const QString &errorString) { m_error.setAndNotify(error, errorString, *q); } diff --git a/src/multimedia/platform/qplatformmediarecorder_p.h b/src/multimedia/platform/qplatformmediarecorder_p.h index 6e88dc187..dea45ac70 100644 --- a/src/multimedia/platform/qplatformmediarecorder_p.h +++ b/src/multimedia/platform/qplatformmediarecorder_p.h @@ -125,7 +125,7 @@ public: 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; } @@ -136,7 +136,7 @@ protected: 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; } diff --git a/src/multimedia/pulseaudio/qaudioengine_pulse.cpp b/src/multimedia/pulseaudio/qaudioengine_pulse.cpp index e54356404..5fac7234a 100644 --- a/src/multimedia/pulseaudio/qaudioengine_pulse.cpp +++ b/src/multimedia/pulseaudio/qaudioengine_pulse.cpp @@ -477,7 +477,7 @@ void QPulseAudioEngine::onContextFailed() release(); // Try to reconnect later - QTimer::singleShot(3000, this, SLOT(prepare())); + QTimer::singleShot(3000, this, &QPulseAudioEngine::prepare); } QPulseAudioEngine *QPulseAudioEngine::instance() diff --git a/src/multimedia/recording/qmediarecorder.cpp b/src/multimedia/recording/qmediarecorder.cpp index a7f5a31b8..d4237c87d 100644 --- a/src/multimedia/recording/qmediarecorder.cpp +++ b/src/multimedia/recording/qmediarecorder.cpp @@ -227,7 +227,7 @@ void QMediaRecorder::setOutputLocation(const QUrl &location) /*! Set the output IO device for media content. - The \a device must have been opened in the \l{QIODevice::Write}{Write} or + The \a device must have been opened in the \l{QIODevice::WriteOnly}{WriteOnly} or \l{QIODevice::ReadWrite}{ReadWrite} modes before the recording starts. The media recorder doesn't take ownership of the specified \a device. diff --git a/src/multimedia/video/qvideooutputorientationhandler.cpp b/src/multimedia/video/qvideooutputorientationhandler.cpp index c34e9e92a..ff91bd7fb 100644 --- a/src/multimedia/video/qvideooutputorientationhandler.cpp +++ b/src/multimedia/video/qvideooutputorientationhandler.cpp @@ -18,8 +18,8 @@ QVideoOutputOrientationHandler::QVideoOutputOrientationHandler(QObject *parent) if (!screen) return; - connect(screen, SIGNAL(orientationChanged(Qt::ScreenOrientation)), - this, SLOT(screenOrientationChanged(Qt::ScreenOrientation))); + connect(screen, &QScreen::orientationChanged, this, + &QVideoOutputOrientationHandler::screenOrientationChanged); screenOrientationChanged(screen->orientation()); } diff --git a/src/multimediaquick/qquickimagecapture.cpp b/src/multimediaquick/qquickimagecapture.cpp index 72dfb78a8..b7e56d18d 100644 --- a/src/multimediaquick/qquickimagecapture.cpp +++ b/src/multimediaquick/qquickimagecapture.cpp @@ -56,7 +56,7 @@ QT_BEGIN_NAMESPACE QQuickImageCapture::QQuickImageCapture(QObject *parent) : QImageCapture(parent) { - connect(this, SIGNAL(imageCaptured(int,QImage)), this, SLOT(_q_imageCaptured(int,QImage))); + connect(this, &QImageCapture::imageCaptured, this, &QQuickImageCapture::_q_imageCaptured); } QQuickImageCapture::~QQuickImageCapture() = default; diff --git a/src/plugins/multimedia/android/mediacapture/qandroidcapturesession.cpp b/src/plugins/multimedia/android/mediacapture/qandroidcapturesession.cpp index ee5af5dfd..3b005e4a5 100644 --- a/src/plugins/multimedia/android/mediacapture/qandroidcapturesession.cpp +++ b/src/plugins/multimedia/android/mediacapture/qandroidcapturesession.cpp @@ -133,7 +133,7 @@ void QAndroidCaptureSession::start(QMediaEncoderSettings &settings, const QUrl & return; if (!m_cameraSession && !m_audioInput) { - emit error(QMediaRecorder::ResourceError, QLatin1String("No devices are set")); + updateError(QMediaRecorder::ResourceError, QLatin1String("No devices are set")); return; } @@ -142,13 +142,13 @@ void QAndroidCaptureSession::start(QMediaEncoderSettings &settings, const QUrl & const bool validCameraSession = m_cameraSession && m_cameraSession->camera(); if (validCameraSession && !qt_androidCheckCameraPermission()) { - emit error(QMediaRecorder::ResourceError, QLatin1String("Camera permission denied.")); + updateError(QMediaRecorder::ResourceError, QLatin1String("Camera permission denied.")); setKeepAlive(false); return; } if (m_audioInput && !qt_androidCheckMicrophonePermission()) { - emit error(QMediaRecorder::ResourceError, QLatin1String("Microphone permission denied.")); + updateError(QMediaRecorder::ResourceError, QLatin1String("Microphone permission denied.")); setKeepAlive(false); return; } @@ -221,15 +221,15 @@ void QAndroidCaptureSession::start(QMediaEncoderSettings &settings, const QUrl & } if (!m_mediaRecorder->prepare()) { - emit error(QMediaRecorder::FormatError, QLatin1String("Unable to prepare the media recorder.")); + updateError(QMediaRecorder::FormatError, + QLatin1String("Unable to prepare the media recorder.")); restartViewfinder(); return; } if (!m_mediaRecorder->start()) { - emit error(QMediaRecorder::FormatError, - QMediaRecorderPrivate::msgFailedStartRecording()); + updateError(QMediaRecorder::FormatError, QMediaRecorderPrivate::msgFailedStartRecording()); restartViewfinder(); return; @@ -451,7 +451,7 @@ void QAndroidCaptureSession::onError(int what, int extra) Q_UNUSED(what); Q_UNUSED(extra); stop(true); - emit error(QMediaRecorder::ResourceError, QLatin1String("Unknown error.")); + updateError(QMediaRecorder::ResourceError, QLatin1String("Unknown error.")); } void QAndroidCaptureSession::onInfo(int what, int extra) @@ -460,11 +460,11 @@ void QAndroidCaptureSession::onInfo(int what, int extra) if (what == 800) { // MEDIA_RECORDER_INFO_MAX_DURATION_REACHED stop(); - emit error(QMediaRecorder::OutOfSpaceError, QLatin1String("Maximum duration reached.")); + updateError(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.")); + updateError(QMediaRecorder::OutOfSpaceError, QLatin1String("Maximum file size reached.")); } } diff --git a/src/plugins/multimedia/android/mediacapture/qandroidcapturesession_p.h b/src/plugins/multimedia/android/mediacapture/qandroidcapturesession_p.h index ab91fc3ef..161d47994 100644 --- a/src/plugins/multimedia/android/mediacapture/qandroidcapturesession_p.h +++ b/src/plugins/multimedia/android/mediacapture/qandroidcapturesession_p.h @@ -67,10 +67,10 @@ public: if (m_mediaEncoder) m_mediaEncoder->actualLocationChanged(location); } - void error(int error, const QString &errorString) + void updateError(int error, const QString &errorString) { if (m_mediaEncoder) - m_mediaEncoder->error(QMediaRecorder::Error(error), errorString); + m_mediaEncoder->updateError(QMediaRecorder::Error(error), errorString); } private Q_SLOTS: diff --git a/src/plugins/multimedia/darwin/camera/avfmediaencoder.mm b/src/plugins/multimedia/darwin/camera/avfmediaencoder.mm index 4a138d4e9..3fbc57995 100644 --- a/src/plugins/multimedia/darwin/camera/avfmediaencoder.mm +++ b/src/plugins/multimedia/darwin/camera/avfmediaencoder.mm @@ -479,7 +479,7 @@ void AVFMediaEncoder::record(QMediaEncoderSettings &settings) if (!cameraControl && !audioInput) { qWarning() << Q_FUNC_INFO << "Cannot record without any inputs"; - Q_EMIT error(QMediaRecorder::ResourceError, tr("No inputs specified")); + updateError(QMediaRecorder::ResourceError, tr("No inputs specified")); return; } @@ -491,8 +491,8 @@ void AVFMediaEncoder::record(QMediaEncoderSettings &settings) if (!audioOnly) { if (!cameraControl || !cameraControl->isActive()) { qCDebug(qLcCamera) << Q_FUNC_INFO << "can not start record while camera is not active"; - Q_EMIT error(QMediaRecorder::ResourceError, - QMediaRecorderPrivate::msgFailedStartRecording()); + updateError(QMediaRecorder::ResourceError, + QMediaRecorderPrivate::msgFailedStartRecording()); return; } } @@ -506,13 +506,13 @@ void AVFMediaEncoder::record(QMediaEncoderSettings &settings) NSURL *nsFileURL = fileURL.toNSURL(); if (!nsFileURL) { qWarning() << Q_FUNC_INFO << "invalid output URL:" << fileURL; - Q_EMIT error(QMediaRecorder::ResourceError, tr("Invalid output file URL")); + updateError(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")); + updateError(QMediaRecorder::ResourceError, tr("Non-writeable file location")); return; } if (qt_file_exists(nsFileURL)) { @@ -520,7 +520,7 @@ void AVFMediaEncoder::record(QMediaEncoderSettings &settings) // 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")); + updateError(QMediaRecorder::ResourceError, tr("File already exists")); return; } @@ -555,8 +555,7 @@ void AVFMediaEncoder::record(QMediaEncoderSettings &settings) [m_writer start]; } else { [session startRunning]; - Q_EMIT error(QMediaRecorder::FormatError, - QMediaRecorderPrivate::msgFailedStartRecording()); + updateError(QMediaRecorder::FormatError, QMediaRecorderPrivate::msgFailedStartRecording()); } } @@ -632,7 +631,7 @@ void AVFMediaEncoder::assetWriterFinished() void AVFMediaEncoder::assetWriterError(QString err) { - Q_EMIT error(QMediaRecorder::FormatError, err); + updateError(QMediaRecorder::FormatError, err); if (m_state != QMediaRecorder::StoppedState) stopWriter(); } diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegcodec.cpp b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegcodec.cpp index a99432c29..457b3603d 100644 --- a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegcodec.cpp +++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegcodec.cpp @@ -18,13 +18,6 @@ Codec::Data::Data(AVCodecContextUPtr context, AVStream *stream, AVFormatContext pixelAspectRatio = av_guess_sample_aspect_ratio(formatContext, stream, nullptr); } -Codec::Data::~Data() -{ - // TODO: investigate if we can remove avcodec_close - // FFmpeg doc says that avcodec_free_context is enough - avcodec_close(context.get()); -} - QMaybe<Codec> Codec::create(AVStream *stream, AVFormatContext *formatContext) { if (!stream) diff --git a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegcodec_p.h b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegcodec_p.h index 5510e0e84..449fb1f65 100644 --- a/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegcodec_p.h +++ b/src/plugins/multimedia/ffmpeg/playbackengine/qffmpegcodec_p.h @@ -31,7 +31,6 @@ class Codec { Data(AVCodecContextUPtr context, AVStream *stream, AVFormatContext *formatContext, std::unique_ptr<QFFmpeg::HWAccel> hwAccel); - ~Data(); QAtomicInt ref; AVCodecContextUPtr context; AVStream *stream = nullptr; diff --git a/src/plugins/multimedia/ffmpeg/qandroidcamera.cpp b/src/plugins/multimedia/ffmpeg/qandroidcamera.cpp index 97448c707..bf01a4e30 100644 --- a/src/plugins/multimedia/ffmpeg/qandroidcamera.cpp +++ b/src/plugins/multimedia/ffmpeg/qandroidcamera.cpp @@ -55,8 +55,8 @@ QCameraFormat getDefaultCameraFormat() QCameraFormatPrivate *defaultFormat = new QCameraFormatPrivate{ .pixelFormat = QVideoFrameFormat::Format_YUV420P, .resolution = { 1920, 1080 }, - .minFrameRate = 30, - .maxFrameRate = 60, + .minFrameRate = 12, + .maxFrameRate = 30, }; return defaultFormat->create(); } @@ -273,6 +273,15 @@ void QAndroidCamera::setActive(bool active) setState(State::WaitingOpen); g_qcameras->insert(m_cameraDevice.id(), this); + // this should use the camera format. + // but there is only 2 fully supported formats on android - JPG and YUV420P + // and JPEG is not supported for encoding in FFmpeg, so it's locked for YUV for now. + const static int imageFormat = + QJniObject::getStaticField<QtJniTypes::AndroidImageFormat, jint>("YUV_420_888"); + m_jniCamera.callMethod<void>("prepareCamera", jint(width), jint(height), + jint(imageFormat), jint(m_cameraFormat.minFrameRate()), + jint(m_cameraFormat.maxFrameRate())); + bool canOpen = m_jniCamera.callMethod<jboolean>( "open", QJniObject::fromString(m_cameraDevice.id()).object<jstring>()); @@ -282,15 +291,6 @@ void QAndroidCamera::setActive(bool active) emit error(QCamera::CameraError, QString("Failed to start camera: ").append(m_cameraDevice.description())); } - - // this should use the camera format. - // but there is only 2 fully supported formats on android - JPG and YUV420P - // and JPEG is not supported for encoding in FFmpeg, so it's locked for YUV for now. - const static int imageFormat = - QJniObject::getStaticField<QtJniTypes::AndroidImageFormat, jint>("YUV_420_888"); - m_jniCamera.callMethod<jboolean>("addImageReader", jint(width), jint(height), - jint(imageFormat)); - } else { m_jniCamera.callMethod<void>("stopAndClose"); m_jniCamera.callMethod<void>("clearSurfaces"); diff --git a/src/plugins/multimedia/ffmpeg/qffmpeg.cpp b/src/plugins/multimedia/ffmpeg/qffmpeg.cpp index f769ac4d4..fe86e480f 100644 --- a/src/plugins/multimedia/ffmpeg/qffmpeg.cpp +++ b/src/plugins/multimedia/ffmpeg/qffmpeg.cpp @@ -338,6 +338,9 @@ const char *preferredHwCodecNameSuffix(bool isEncoder, AVHWDeviceType deviceType return "_videotoolbox"; case AV_HWDEVICE_TYPE_D3D11VA: case AV_HWDEVICE_TYPE_DXVA2: +#if QT_FFMPEG_HAS_D3D12VA + case AV_HWDEVICE_TYPE_D3D12VA: +#endif return "_mf"; case AV_HWDEVICE_TYPE_CUDA: case AV_HWDEVICE_TYPE_VDPAU: @@ -489,6 +492,10 @@ AVPixelFormat pixelFormatForHwDevice(AVHWDeviceType deviceType) return AV_PIX_FMT_QSV; case AV_HWDEVICE_TYPE_D3D11VA: return AV_PIX_FMT_D3D11; +#if QT_FFMPEG_HAS_D3D12VA + case AV_HWDEVICE_TYPE_D3D12VA: + return AV_PIX_FMT_D3D12; +#endif case AV_HWDEVICE_TYPE_DXVA2: return AV_PIX_FMT_DXVA2_VLD; case AV_HWDEVICE_TYPE_DRM: diff --git a/src/plugins/multimedia/ffmpeg/qffmpegdefs_p.h b/src/plugins/multimedia/ffmpeg/qffmpegdefs_p.h index f3860377e..239d8ff0c 100644 --- a/src/plugins/multimedia/ffmpeg/qffmpegdefs_p.h +++ b/src/plugins/multimedia/ffmpeg/qffmpegdefs_p.h @@ -32,6 +32,8 @@ extern "C" { (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(60, 3, 100)) // since ffmpeg n6.0 #define QT_FFMPEG_STREAM_SIDE_DATA_DEPRECATED \ (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(60, 15, 100)) // since ffmpeg n6.1 +#define QT_FFMPEG_HAS_D3D12VA \ + (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(59, 8, 100)) // since ffmpeg n7.0 #define QT_FFMPEG_SWR_CONST_CH_LAYOUT (LIBSWRESAMPLE_VERSION_INT >= AV_VERSION_INT(4, 9, 100)) #define QT_FFMPEG_AVIO_WRITE_CONST \ (LIBAVFORMAT_VERSION_MAJOR >= 61) diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel.cpp b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel.cpp index 78e265b4b..06bd4f4d3 100644 --- a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel.cpp +++ b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel.cpp @@ -93,6 +93,11 @@ static bool precheckDriver(AVHWDeviceType type) if (type == AV_HWDEVICE_TYPE_D3D11VA) return QSystemLibrary(QLatin1String("d3d11.dll")).load(); +#if QT_FFMPEG_HAS_D3D12VA + if (type == AV_HWDEVICE_TYPE_D3D12VA) + return QSystemLibrary(QLatin1String("d3d12.dll")).load(); +#endif + if (type == AV_HWDEVICE_TYPE_DXVA2) return QSystemLibrary(QLatin1String("d3d9.dll")).load(); @@ -122,6 +127,9 @@ static bool checkHwType(AVHWDeviceType type) if (type == AV_HWDEVICE_TYPE_MEDIACODEC || type == AV_HWDEVICE_TYPE_VIDEOTOOLBOX || type == AV_HWDEVICE_TYPE_D3D11VA || +#if QT_FFMPEG_HAS_D3D12VA + type == AV_HWDEVICE_TYPE_D3D12VA || +#endif type == AV_HWDEVICE_TYPE_DXVA2) return true; // Don't waste time; it's expected to work fine of the precheck is OK diff --git a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi.cpp b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi.cpp index 81eef89ef..09ffaaf71 100644 --- a/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi.cpp +++ b/src/plugins/multimedia/ffmpeg/qffmpeghwaccel_vaapi.cpp @@ -231,7 +231,7 @@ TextureSet *VAAPITextureConverter::getTextures(AVFrame *frame) VASurfaceID vaSurface = (uintptr_t)frame->data[3]; - VADRMPRIMESurfaceDescriptor prime; + VADRMPRIMESurfaceDescriptor prime = {}; if (vaExportSurfaceHandle(vaDisplay, vaSurface, VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2, VA_EXPORT_SURFACE_READ_ONLY | @@ -245,6 +245,13 @@ TextureSet *VAAPITextureConverter::getTextures(AVFrame *frame) qWarning() << "vaExportSurfaceHandle failed"; return nullptr; } + + // Make sure all fd's in 'prime' are closed when we return from this function + QScopeGuard closeObjectsGuard([&prime]() { + for (uint32_t i = 0; i < prime.num_objects; ++i) + close(prime.objects[i].fd); + }); + // ### Check that prime.fourcc is what we expect vaSyncSurface(vaDisplay, vaSurface); @@ -325,9 +332,6 @@ TextureSet *VAAPITextureConverter::getTextures(AVFrame *frame) qWarning() << "eglImageTargetTexture2D failed with error code" << error; } - for (int i = 0; i < (int)prime.num_objects; ++i) - close(prime.objects[i].fd); - for (int i = 0; i < nPlanes; ++i) { functions.glActiveTexture(GL_TEXTURE0 + i); functions.glBindTexture(GL_TEXTURE_2D, 0); diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder.cpp b/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder.cpp index 9b1a70742..67eb46eb9 100644 --- a/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder.cpp +++ b/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder.cpp @@ -32,7 +32,7 @@ bool QFFmpegMediaRecorder::isLocationWritable(const QUrl &) const void QFFmpegMediaRecorder::handleSessionError(QMediaRecorder::Error code, const QString &description) { - error(code, description); + updateError(code, description); stop(); } @@ -46,7 +46,7 @@ void QFFmpegMediaRecorder::record(QMediaEncoderSettings &settings) const auto hasAudio = m_session->audioInput() != nullptr; if (!hasVideo && !hasAudio) { - error(QMediaRecorder::ResourceError, QMediaRecorder::tr("No video or audio input")); + updateError(QMediaRecorder::ResourceError, QMediaRecorder::tr("No video or audio input")); return; } @@ -72,18 +72,18 @@ void QFFmpegMediaRecorder::record(QMediaEncoderSettings &settings) << settings.audioCodec(); if (!formatContext->isAVIOOpen()) { - error(QMediaRecorder::LocationNotWritable, - QMediaRecorder::tr("Cannot open the output location for writing")); + updateError(QMediaRecorder::LocationNotWritable, + QMediaRecorder::tr("Cannot open the output location for writing")); return; } - m_encoder.reset(new RecordingEngine(settings, std::move(formatContext))); - m_encoder->setMetaData(m_metaData); - connect(m_encoder.get(), &QFFmpeg::RecordingEngine::durationChanged, this, + m_recordingEngine.reset(new RecordingEngine(settings, std::move(formatContext))); + m_recordingEngine->setMetaData(m_metaData); + connect(m_recordingEngine.get(), &QFFmpeg::RecordingEngine::durationChanged, this, &QFFmpegMediaRecorder::newDuration); - connect(m_encoder.get(), &QFFmpeg::RecordingEngine::finalizationDone, this, + connect(m_recordingEngine.get(), &QFFmpeg::RecordingEngine::finalizationDone, this, &QFFmpegMediaRecorder::finalizationDone); - connect(m_encoder.get(), &QFFmpeg::RecordingEngine::error, this, + connect(m_recordingEngine.get(), &QFFmpeg::RecordingEngine::error, this, &QFFmpegMediaRecorder::handleSessionError); auto *audioInput = m_session->audioInput(); @@ -91,17 +91,17 @@ void QFFmpegMediaRecorder::record(QMediaEncoderSettings &settings) if (audioInput->device.isNull()) qWarning() << "Audio input device is null; cannot encode audio"; else - m_encoder->addAudioInput(static_cast<QFFmpegAudioInput *>(audioInput)); + m_recordingEngine->addAudioInput(static_cast<QFFmpegAudioInput *>(audioInput)); } for (auto source : videoSources) - m_encoder->addVideoSource(source); + m_recordingEngine->addVideoSource(source); durationChanged(0); stateChanged(QMediaRecorder::RecordingState); actualLocationChanged(QUrl::fromLocalFile(actualLocation)); - m_encoder->start(); + m_recordingEngine->start(); } void QFFmpegMediaRecorder::pause() @@ -109,8 +109,8 @@ void QFFmpegMediaRecorder::pause() if (!m_session || state() != QMediaRecorder::RecordingState) return; - Q_ASSERT(m_encoder); - m_encoder->setPaused(true); + Q_ASSERT(m_recordingEngine); + m_recordingEngine->setPaused(true); stateChanged(QMediaRecorder::PausedState); } @@ -120,8 +120,8 @@ void QFFmpegMediaRecorder::resume() if (!m_session || state() != QMediaRecorder::PausedState) return; - Q_ASSERT(m_encoder); - m_encoder->setPaused(false); + Q_ASSERT(m_recordingEngine); + m_recordingEngine->setPaused(false); stateChanged(QMediaRecorder::RecordingState); } @@ -135,7 +135,7 @@ void QFFmpegMediaRecorder::stop() static_cast<QFFmpegAudioInput *>(input)->setRunning(false); qCDebug(qLcMediaEncoder) << "stop"; - m_encoder.reset(); + m_recordingEngine.reset(); } void QFFmpegMediaRecorder::finalizationDone() @@ -169,11 +169,12 @@ void QFFmpegMediaRecorder::setCaptureSession(QFFmpegMediaCaptureSession *session return; } -void QFFmpegMediaRecorder::EncoderDeleter::operator()(RecordingEngine *encoder) const +void QFFmpegMediaRecorder::RecordingEngineDeleter::operator()( + RecordingEngine *recordingEngine) const { // ### all of the below should be done asynchronous. finalize() should do it's work in a thread // to avoid blocking the UI in case of slow codecs - encoder->finalize(); + recordingEngine->finalize(); } QT_END_NAMESPACE diff --git a/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder_p.h b/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder_p.h index 3a3bbcf5c..8b73ad76d 100644 --- a/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder_p.h +++ b/src/plugins/multimedia/ffmpeg/qffmpegmediarecorder_p.h @@ -55,7 +55,7 @@ private Q_SLOTS: private: using RecordingEngine = QFFmpeg::RecordingEngine; - struct EncoderDeleter + struct RecordingEngineDeleter { void operator()(RecordingEngine *) const; }; @@ -63,7 +63,7 @@ private: QFFmpegMediaCaptureSession *m_session = nullptr; QMediaMetaData m_metaData; - std::unique_ptr<RecordingEngine, EncoderDeleter> m_encoder; + std::unique_ptr<RecordingEngine, RecordingEngineDeleter> m_recordingEngine; }; QT_END_NAMESPACE diff --git a/src/plugins/multimedia/ffmpeg/qffmpegthread.cpp b/src/plugins/multimedia/ffmpeg/qffmpegthread.cpp index c14434df3..fb14ced54 100644 --- a/src/plugins/multimedia/ffmpeg/qffmpegthread.cpp +++ b/src/plugins/multimedia/ffmpeg/qffmpegthread.cpp @@ -11,7 +11,7 @@ using namespace QFFmpeg; void ConsumerThread::stopAndDelete() { { - QMutexLocker locker(&m_exitMutex); + QMutexLocker locker(&m_loopDataMutex); m_exit = true; } dataReady(); @@ -31,9 +31,9 @@ void ConsumerThread::run() while (true) { { - QMutexLocker locker(&m_exitMutex); + QMutexLocker locker(&m_loopDataMutex); while (!hasData() && !m_exit) - m_condition.wait(&m_exitMutex); + m_condition.wait(&m_loopDataMutex); if (m_exit) break; @@ -45,4 +45,9 @@ void ConsumerThread::run() cleanup(); } +QMutexLocker<QMutex> ConsumerThread::lockLoopData() const +{ + return QMutexLocker(&m_loopDataMutex); +} + QT_END_NAMESPACE diff --git a/src/plugins/multimedia/ffmpeg/qffmpegthread_p.h b/src/plugins/multimedia/ffmpeg/qffmpegthread_p.h index 3f382c9c3..a7c5b0927 100644 --- a/src/plugins/multimedia/ffmpeg/qffmpegthread_p.h +++ b/src/plugins/multimedia/ffmpeg/qffmpegthread_p.h @@ -65,7 +65,8 @@ protected: /*! Wake thread from sleep and process data until - hasData() returns false. + hasData() returns false. The method is supposed to be invoked + right after the scope of QMutexLocker that lockLoopData returns. */ void dataReady(); @@ -74,10 +75,16 @@ protected: */ virtual bool hasData() const = 0; + /*! + Locks the loop data mutex. It must be used to protect loop data + like a queue of video frames. + */ + QMutexLocker<QMutex> lockLoopData() const; + private: void run() final; - QMutex m_exitMutex; // Protects exit flag. + mutable QMutex m_loopDataMutex; QWaitCondition m_condition; bool m_exit = false; }; diff --git a/src/plugins/multimedia/ffmpeg/qffmpegvaapisymbols.cpp b/src/plugins/multimedia/ffmpeg/qffmpegvaapisymbols.cpp index 58bf4dce7..9860b53a0 100644 --- a/src/plugins/multimedia/ffmpeg/qffmpegvaapisymbols.cpp +++ b/src/plugins/multimedia/ffmpeg/qffmpegvaapisymbols.cpp @@ -21,14 +21,15 @@ QT_BEGIN_NAMESPACE static Libs loadLibs() { + constexpr int version = VA_MAJOR_VERSION + 1; Libs libs; - libs.push_back(std::make_unique<QLibrary>("va")); + libs.push_back(std::make_unique<QLibrary>("va", version)); #ifdef DYNAMIC_RESOLVE_VA_DRM_SYMBOLS - libs.push_back(std::make_unique<QLibrary>("va-drm")); + libs.push_back(std::make_unique<QLibrary>("va-drm", version)); #endif #ifdef DYNAMIC_RESOLVE_VA_X11_SYMBOLS - libs.push_back(std::make_unique<QLibrary>("va-x11")); + libs.push_back(std::make_unique<QLibrary>("va-x11", version)); #endif if (LibSymbolsResolver::tryLoad(libs)) @@ -37,7 +38,7 @@ static Libs loadLibs() return {}; } -constexpr size_t symbolsCount = 38 +constexpr size_t symbolsCount = 40 #if VA_CHECK_VERSION(1, 9, 0) + 1 #endif @@ -114,6 +115,9 @@ DEFINE_FUNC(vaGetDisplayAttributes, 3, VA_STATUS_ERROR_OPERATION_FAILED); DEFINE_FUNC(vaSetDriverName, 2, VA_STATUS_ERROR_OPERATION_FAILED); +DEFINE_FUNC(vaAcquireBufferHandle, 3, VA_STATUS_ERROR_OPERATION_FAILED); +DEFINE_FUNC(vaReleaseBufferHandle, 2, VA_STATUS_ERROR_OPERATION_FAILED); + #ifdef DYNAMIC_RESOLVE_VA_DRM_SYMBOLS DEFINE_FUNC(vaGetDisplayDRM, 1); // va-drm #endif diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoder.cpp b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoder.cpp index 57b798fed..9948952e8 100644 --- a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoder.cpp +++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoder.cpp @@ -15,16 +15,16 @@ namespace QFFmpeg { static Q_LOGGING_CATEGORY(qLcFFmpegAudioEncoder, "qt.multimedia.ffmpeg.audioencoder"); -AudioEncoder::AudioEncoder(RecordingEngine *encoder, QFFmpegAudioInput *input, +AudioEncoder::AudioEncoder(RecordingEngine &recordingEngine, QFFmpegAudioInput *input, const QMediaEncoderSettings &settings) - : EncoderThread(encoder), m_input(input), m_settings(settings) + : EncoderThread(recordingEngine), m_input(input), m_settings(settings) { setObjectName(QLatin1String("AudioEncoder")); qCDebug(qLcFFmpegAudioEncoder) << "AudioEncoder" << settings.audioCodec(); m_format = input->device.preferredFormat(); auto codecID = QFFmpegMediaFormatInfo::codecIdForAudioCodec(settings.audioCodec()); - Q_ASSERT(avformat_query_codec(encoder->avFormatContext()->oformat, codecID, + Q_ASSERT(avformat_query_codec(recordingEngine.avFormatContext()->oformat, codecID, FF_COMPLIANCE_NORMAL)); const AVAudioFormat requestedAudioFormat(m_format); @@ -38,8 +38,8 @@ AudioEncoder::AudioEncoder(RecordingEngine *encoder, QFFmpegAudioInput *input, Q_ASSERT(m_avCodec); - m_stream = avformat_new_stream(encoder->avFormatContext(), nullptr); - m_stream->id = encoder->avFormatContext()->nb_streams - 1; + m_stream = avformat_new_stream(recordingEngine.avFormatContext(), nullptr); + m_stream->id = recordingEngine.avFormatContext()->nb_streams - 1; m_stream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; m_stream->codecpar->codec_id = codecID; #if QT_FFMPEG_OLD_CHANNEL_LAYOUT @@ -96,7 +96,7 @@ void AudioEncoder::open() void AudioEncoder::addBuffer(const QAudioBuffer &buffer) { - QMutexLocker locker(&m_queueMutex); + QMutexLocker locker = lockLoopData(); if (!m_paused.loadRelaxed()) { m_audioBufferQueue.push(buffer); locker.unlock(); @@ -106,7 +106,7 @@ void AudioEncoder::addBuffer(const QAudioBuffer &buffer) QAudioBuffer AudioEncoder::takeBuffer() { - QMutexLocker locker(&m_queueMutex); + QMutexLocker locker = lockLoopData(); return dequeueIfPossible(m_audioBufferQueue); } @@ -130,7 +130,6 @@ void AudioEncoder::cleanup() bool AudioEncoder::hasData() const { - QMutexLocker locker(&m_queueMutex); return !m_audioBufferQueue.empty(); } @@ -153,7 +152,7 @@ void AudioEncoder::retrievePackets() // qCDebug(qLcFFmpegEncoder) << "writing audio packet" << packet->size << packet->pts << // packet->dts; packet->stream_index = m_stream->id; - m_encoder->getMuxer()->addPacket(std::move(packet)); + m_recordingEngine.getMuxer()->addPacket(std::move(packet)); } } @@ -202,7 +201,7 @@ void AudioEncoder::processOne() m_samplesWritten += buffer.frameCount(); qint64 time = m_format.durationForFrames(m_samplesWritten); - m_encoder->newTimeStamp(time / 1000); + m_recordingEngine.newTimeStamp(time / 1000); // qCDebug(qLcFFmpegEncoder) << "sending audio frame" << buffer.byteCount() << frame->pts << // ((double)buffer.frameCount()/frame->sample_rate); diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoder_p.h b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoder_p.h index c4dc20eac..16d8d81a1 100644 --- a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoder_p.h +++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegaudioencoder_p.h @@ -19,7 +19,7 @@ namespace QFFmpeg { class AudioEncoder : public EncoderThread { public: - AudioEncoder(RecordingEngine *encoder, QFFmpegAudioInput *input, + AudioEncoder(RecordingEngine &recordingEngine, QFFmpegAudioInput *input, const QMediaEncoderSettings &settings); void open(); @@ -37,7 +37,6 @@ private: void processOne() override; private: - mutable QMutex m_queueMutex; std::queue<QAudioBuffer> m_audioBufferQueue; AVStream *m_stream = nullptr; diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderthread.cpp b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderthread.cpp index 97c8fb7a9..b673af450 100644 --- a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderthread.cpp +++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderthread.cpp @@ -6,7 +6,9 @@ QT_BEGIN_NAMESPACE namespace QFFmpeg { -EncoderThread::EncoderThread(RecordingEngine *encoder) : m_encoder(encoder) { } +EncoderThread::EncoderThread(RecordingEngine &recordingEngine) : m_recordingEngine(recordingEngine) +{ +} void EncoderThread::setPaused(bool b) { diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderthread_p.h b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderthread_p.h index 6ef5e97f6..1fe35303b 100644 --- a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderthread_p.h +++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegencoderthread_p.h @@ -14,12 +14,12 @@ class RecordingEngine; class EncoderThread : public ConsumerThread { public: - EncoderThread(RecordingEngine *encoder); + EncoderThread(RecordingEngine &recordingEngine); virtual void setPaused(bool b); protected: QAtomicInteger<bool> m_paused = false; - RecordingEngine *m_encoder = nullptr; + RecordingEngine &m_recordingEngine; }; } // namespace QFFmpeg diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegmuxer.cpp b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegmuxer.cpp index 6367dde3b..2df594017 100644 --- a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegmuxer.cpp +++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegmuxer.cpp @@ -18,7 +18,7 @@ Muxer::Muxer(RecordingEngine *encoder) : m_encoder(encoder) void Muxer::addPacket(AVPacketUPtr packet) { { - QMutexLocker locker(&m_queueMutex); + QMutexLocker locker = lockLoopData(); m_packetQueue.push(std::move(packet)); } @@ -28,7 +28,7 @@ void Muxer::addPacket(AVPacketUPtr packet) AVPacketUPtr Muxer::takePacket() { - QMutexLocker locker(&m_queueMutex); + QMutexLocker locker = lockLoopData(); return dequeueIfPossible(m_packetQueue); } @@ -37,11 +37,14 @@ void Muxer::init() qCDebug(qLcFFmpegMuxer) << "Muxer::init started thread."; } -void Muxer::cleanup() { } +void Muxer::cleanup() +{ + while (!m_packetQueue.empty()) + processOne(); +} bool QFFmpeg::Muxer::hasData() const { - QMutexLocker locker(&m_queueMutex); return !m_packetQueue.empty(); } diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegmuxer_p.h b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegmuxer_p.h index 8cdf73c6f..4f8f4d27a 100644 --- a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegmuxer_p.h +++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegmuxer_p.h @@ -29,7 +29,6 @@ private: void processOne() override; private: - mutable QMutex m_queueMutex; std::queue<AVPacketUPtr> m_packetQueue; RecordingEngine *m_encoder; diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine.cpp b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine.cpp index 20d38ace1..2b32af502 100644 --- a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine.cpp +++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine.cpp @@ -36,7 +36,7 @@ RecordingEngine::~RecordingEngine() void RecordingEngine::addAudioInput(QFFmpegAudioInput *input) { - m_audioEncoder = new AudioEncoder(this, input, m_settings); + m_audioEncoder = new AudioEncoder(*this, input, m_settings); addMediaFrameHandler(input, &QFFmpegAudioInput::newAudioBuffer, m_audioEncoder, &AudioEncoder::addBuffer); input->setRunning(true); @@ -63,7 +63,7 @@ void RecordingEngine::addVideoSource(QPlatformVideoSource * source) << "frameRate=" << frameFormat.frameRate() << "ffmpegHWPixelFormat=" << (hwPixelFormat ? *hwPixelFormat : AV_PIX_FMT_NONE); - auto veUPtr = std::make_unique<VideoEncoder>(this, m_settings, frameFormat, hwPixelFormat); + auto veUPtr = std::make_unique<VideoEncoder>(*this, m_settings, frameFormat, hwPixelFormat); if (!veUPtr->isValid()) { emit error(QMediaRecorder::FormatError, QLatin1StringView("Cannot initialize encoder")); return; @@ -101,36 +101,39 @@ void RecordingEngine::start() videoEncoder->start(); } -RecordingEngine::EncodingFinalizer::EncodingFinalizer(RecordingEngine *e) : m_encoder(e) +RecordingEngine::EncodingFinalizer::EncodingFinalizer(RecordingEngine &recordingEngine) + : m_recordingEngine(recordingEngine) { connect(this, &QThread::finished, this, &QObject::deleteLater); } void RecordingEngine::EncodingFinalizer::run() { - if (m_encoder->m_audioEncoder) - m_encoder->m_audioEncoder->stopAndDelete(); - for (auto &videoEncoder : m_encoder->m_videoEncoders) + if (m_recordingEngine.m_audioEncoder) + m_recordingEngine.m_audioEncoder->stopAndDelete(); + for (auto &videoEncoder : m_recordingEngine.m_videoEncoders) videoEncoder->stopAndDelete(); - m_encoder->m_muxer->stopAndDelete(); + m_recordingEngine.m_muxer->stopAndDelete(); - if (m_encoder->m_isHeaderWritten) { - const int res = av_write_trailer(m_encoder->avFormatContext()); + if (m_recordingEngine.m_isHeaderWritten) { + const int res = av_write_trailer(m_recordingEngine.avFormatContext()); if (res < 0) { const auto errorDescription = err2str(res); qCWarning(qLcFFmpegEncoder) << "could not write trailer" << res << errorDescription; - emit m_encoder->error(QMediaRecorder::FormatError, - QLatin1String("Cannot write trailer: ") + errorDescription); + emit m_recordingEngine.error(QMediaRecorder::FormatError, + QLatin1String("Cannot write trailer: ") + + errorDescription); } } // else ffmpeg might crash // close AVIO before emitting finalizationDone. - m_encoder->m_formatContext->closeAVIO(); + m_recordingEngine.m_formatContext->closeAVIO(); qCDebug(qLcFFmpegEncoder) << " done finalizing."; - emit m_encoder->finalizationDone(); - delete m_encoder; + emit m_recordingEngine.finalizationDone(); + auto recordingEnginePtr = &m_recordingEngine; + delete recordingEnginePtr; } void RecordingEngine::finalize() @@ -140,7 +143,7 @@ void RecordingEngine::finalize() for (auto &conn : m_connections) disconnect(conn); - auto *finalizer = new EncodingFinalizer(this); + auto *finalizer = new EncodingFinalizer(*this); finalizer->start(); } @@ -172,88 +175,6 @@ void RecordingEngine::addMediaFrameHandler(Args &&...args) auto connection = connect(std::forward<Args>(args)..., Qt::DirectConnection); m_connections.append(connection); } - -struct QVideoFrameHolder -{ - QVideoFrame f; - QImage i; -}; - -static void freeQVideoFrame(void *opaque, uint8_t *) -{ - delete reinterpret_cast<QVideoFrameHolder *>(opaque); -} - -void VideoEncoder::processOne() -{ - retrievePackets(); - - auto frame = takeFrame(); - if (!frame.isValid()) - return; - - if (!isValid()) - return; - -// qCDebug(qLcFFmpegEncoder) << "new video buffer" << frame.startTime(); - - AVFrameUPtr avFrame; - - auto *videoBuffer = dynamic_cast<QFFmpegVideoBuffer *>(frame.videoBuffer()); - if (videoBuffer) { - // ffmpeg video buffer, let's use the native AVFrame stored in there - auto *hwFrame = videoBuffer->getHWFrame(); - if (hwFrame && hwFrame->format == m_frameEncoder->sourceFormat()) - avFrame.reset(av_frame_clone(hwFrame)); - } - - if (!avFrame) { - frame.map(QVideoFrame::ReadOnly); - auto size = frame.size(); - avFrame = makeAVFrame(); - avFrame->format = m_frameEncoder->sourceFormat(); - avFrame->width = size.width(); - avFrame->height = size.height(); - - for (int i = 0; i < 4; ++i) { - avFrame->data[i] = const_cast<uint8_t *>(frame.bits(i)); - avFrame->linesize[i] = frame.bytesPerLine(i); - } - - QImage img; - if (frame.pixelFormat() == QVideoFrameFormat::Format_Jpeg) { - // the QImage is cached inside the video frame, so we can take the pointer to the image data here - img = frame.toImage(); - avFrame->data[0] = (uint8_t *)img.bits(); - avFrame->linesize[0] = img.bytesPerLine(); - } - - Q_ASSERT(avFrame->data[0]); - // ensure the video frame and it's data is alive as long as it's being used in the encoder - avFrame->opaque_ref = av_buffer_create(nullptr, 0, freeQVideoFrame, new QVideoFrameHolder{frame, img}, 0); - } - - if (m_baseTime.loadAcquire() == std::numeric_limits<qint64>::min()) { - m_baseTime.storeRelease(frame.startTime() - m_lastFrameTime); - qCDebug(qLcFFmpegEncoder) << ">>>> adjusting base time to" << m_baseTime.loadAcquire() - << frame.startTime() << m_lastFrameTime; - } - - qint64 time = frame.startTime() - m_baseTime.loadAcquire(); - m_lastFrameTime = frame.endTime() - m_baseTime.loadAcquire(); - - setAVFrameTime(*avFrame, m_frameEncoder->getPts(time), m_frameEncoder->getTimeBase()); - - m_encoder->newTimeStamp(time / 1000); - - qCDebug(qLcFFmpegEncoder) << ">>> sending frame" << avFrame->pts << time << m_lastFrameTime; - int ret = m_frameEncoder->sendFrame(std::move(avFrame)); - if (ret < 0) { - qCDebug(qLcFFmpegEncoder) << "error sending frame" << ret << err2str(ret); - emit m_encoder->error(QMediaRecorder::ResourceError, err2str(ret)); - } -} - } QT_END_NAMESPACE diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine_p.h b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine_p.h index 10174f9a4..b74fbba9f 100644 --- a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine_p.h +++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegrecordingengine_p.h @@ -82,12 +82,12 @@ private: class EncodingFinalizer : public QThread { public: - EncodingFinalizer(RecordingEngine *e); + EncodingFinalizer(RecordingEngine &recordingEngine); void run() override; private: - RecordingEngine *m_encoder = nullptr; + RecordingEngine &m_recordingEngine; }; private: diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder.cpp b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder.cpp index 04ed5a728..a47968096 100644 --- a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder.cpp +++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder.cpp @@ -13,10 +13,9 @@ namespace QFFmpeg { static Q_LOGGING_CATEGORY(qLcFFmpegVideoEncoder, "qt.multimedia.ffmpeg.videoencoder"); - -VideoEncoder::VideoEncoder(RecordingEngine *encoder, const QMediaEncoderSettings &settings, +VideoEncoder::VideoEncoder(RecordingEngine &recordingEngine, const QMediaEncoderSettings &settings, const QVideoFrameFormat &format, std::optional<AVPixelFormat> hwFormat) - : EncoderThread(encoder) + : EncoderThread(recordingEngine) { setObjectName(QLatin1String("VideoEncoder")); @@ -33,7 +32,7 @@ VideoEncoder::VideoEncoder(RecordingEngine *encoder, const QMediaEncoderSettings m_frameEncoder = VideoFrameEncoder::create(settings, format.frameSize(), frameRate, ffmpegPixelFormat, - swFormat, encoder->avFormatContext()); + swFormat, recordingEngine.avFormatContext()); } VideoEncoder::~VideoEncoder() = default; @@ -45,7 +44,7 @@ bool VideoEncoder::isValid() const void VideoEncoder::addFrame(const QVideoFrame &frame) { - QMutexLocker locker(&m_queueMutex); + QMutexLocker locker = lockLoopData(); // Drop frames if encoder can not keep up with the video source data rate const bool queueFull = m_videoFrameQueue.size() >= m_maxQueueSize; @@ -63,7 +62,7 @@ void VideoEncoder::addFrame(const QVideoFrame &frame) QVideoFrame VideoEncoder::takeFrame() { - QMutexLocker locker(&m_queueMutex); + QMutexLocker locker = lockLoopData(); return dequeueIfPossible(m_videoFrameQueue); } @@ -72,7 +71,7 @@ void VideoEncoder::retrievePackets() if (!m_frameEncoder) return; while (auto packet = m_frameEncoder->retrievePacket()) - m_encoder->getMuxer()->addPacket(std::move(packet)); + m_recordingEngine.getMuxer()->addPacket(std::move(packet)); } void VideoEncoder::init() @@ -80,7 +79,7 @@ void VideoEncoder::init() qCDebug(qLcFFmpegVideoEncoder) << "VideoEncoder::init started video device thread."; bool ok = m_frameEncoder->open(); if (!ok) - emit m_encoder->error(QMediaRecorder::ResourceError, "Could not initialize encoder"); + emit m_recordingEngine.error(QMediaRecorder::ResourceError, "Could not initialize encoder"); } void VideoEncoder::cleanup() @@ -96,10 +95,93 @@ void VideoEncoder::cleanup() bool VideoEncoder::hasData() const { - QMutexLocker locker(&m_queueMutex); return !m_videoFrameQueue.empty(); } +struct QVideoFrameHolder +{ + QVideoFrame f; + QImage i; +}; + +static void freeQVideoFrame(void *opaque, uint8_t *) +{ + delete reinterpret_cast<QVideoFrameHolder *>(opaque); +} + +void VideoEncoder::processOne() +{ + retrievePackets(); + + auto frame = takeFrame(); + if (!frame.isValid()) + return; + + if (!isValid()) + return; + + // qCDebug(qLcFFmpegEncoder) << "new video buffer" << frame.startTime(); + + AVFrameUPtr avFrame; + + auto *videoBuffer = dynamic_cast<QFFmpegVideoBuffer *>(frame.videoBuffer()); + if (videoBuffer) { + // ffmpeg video buffer, let's use the native AVFrame stored in there + auto *hwFrame = videoBuffer->getHWFrame(); + if (hwFrame && hwFrame->format == m_frameEncoder->sourceFormat()) + avFrame.reset(av_frame_clone(hwFrame)); + } + + if (!avFrame) { + frame.map(QVideoFrame::ReadOnly); + auto size = frame.size(); + avFrame = makeAVFrame(); + avFrame->format = m_frameEncoder->sourceFormat(); + avFrame->width = size.width(); + avFrame->height = size.height(); + + for (int i = 0; i < 4; ++i) { + avFrame->data[i] = const_cast<uint8_t *>(frame.bits(i)); + avFrame->linesize[i] = frame.bytesPerLine(i); + } + + QImage img; + if (frame.pixelFormat() == QVideoFrameFormat::Format_Jpeg) { + // the QImage is cached inside the video frame, so we can take the pointer to the image + // data here + img = frame.toImage(); + avFrame->data[0] = (uint8_t *)img.bits(); + avFrame->linesize[0] = img.bytesPerLine(); + } + + Q_ASSERT(avFrame->data[0]); + // ensure the video frame and it's data is alive as long as it's being used in the encoder + avFrame->opaque_ref = av_buffer_create(nullptr, 0, freeQVideoFrame, + new QVideoFrameHolder{ frame, img }, 0); + } + + if (m_baseTime.loadAcquire() == std::numeric_limits<qint64>::min()) { + m_baseTime.storeRelease(frame.startTime() - m_lastFrameTime); + qCDebug(qLcFFmpegVideoEncoder) << ">>>> adjusting base time to" << m_baseTime.loadAcquire() + << frame.startTime() << m_lastFrameTime; + } + + qint64 time = frame.startTime() - m_baseTime.loadAcquire(); + m_lastFrameTime = frame.endTime() - m_baseTime.loadAcquire(); + + setAVFrameTime(*avFrame, m_frameEncoder->getPts(time), m_frameEncoder->getTimeBase()); + + m_recordingEngine.newTimeStamp(time / 1000); + + qCDebug(qLcFFmpegVideoEncoder) + << ">>> sending frame" << avFrame->pts << time << m_lastFrameTime; + int ret = m_frameEncoder->sendFrame(std::move(avFrame)); + if (ret < 0) { + qCDebug(qLcFFmpegVideoEncoder) << "error sending frame" << ret << err2str(ret); + emit m_recordingEngine.error(QMediaRecorder::ResourceError, err2str(ret)); + } +} + } // namespace QFFmpeg QT_END_NAMESPACE diff --git a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder_p.h b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder_p.h index f07f146e2..8f9a943de 100644 --- a/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder_p.h +++ b/src/plugins/multimedia/ffmpeg/recordingengine/qffmpegvideoencoder_p.h @@ -16,11 +16,10 @@ class QMediaEncoderSettings; namespace QFFmpeg { class VideoFrameEncoder; - class VideoEncoder : public EncoderThread { public: - VideoEncoder(RecordingEngine *encoder, const QMediaEncoderSettings &settings, + VideoEncoder(RecordingEngine &recordingEngine, const QMediaEncoderSettings &settings, const QVideoFrameFormat &format, std::optional<AVPixelFormat> hwFormat); ~VideoEncoder() override; @@ -45,7 +44,6 @@ private: void processOne() override; private: - mutable QMutex m_queueMutex; std::queue<QVideoFrame> m_videoFrameQueue; const size_t m_maxQueueSize = 10; // Arbitrarily chosen to limit memory usage (332 MB @ 4K) diff --git a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder.cpp b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder.cpp index cf9655059..0cfa28169 100644 --- a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder.cpp +++ b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder.cpp @@ -327,6 +327,7 @@ void QGstreamerAudioDecoder::start() void QGstreamerAudioDecoder::stop() { m_playbin.setState(GST_STATE_NULL); + m_currentSessionId += 1; removeAppSink(); // GStreamer thread is stopped. Can safely access m_buffersAvailable @@ -365,55 +366,38 @@ QAudioBuffer QGstreamerAudioDecoder::read() { QAudioBuffer audioBuffer; - int buffersAvailable; - { - QMutexLocker locker(&m_buffersMutex); - buffersAvailable = m_buffersAvailable; + if (m_buffersAvailable == 0) + return audioBuffer; - // need to decrement before pulling a buffer - // to make sure assert in QGstreamerAudioDecoderControl::new_buffer works - m_buffersAvailable--; - } + m_buffersAvailable -= 1; + if (m_buffersAvailable == 0) + bufferAvailableChanged(false); - if (buffersAvailable) { - if (buffersAvailable == 1) - bufferAvailableChanged(false); - - const char* bufferData = nullptr; - int bufferSize = 0; - - QGstSampleHandle sample = m_appSink.pullSample(); - GstBuffer *buffer = gst_sample_get_buffer(sample.get()); - GstMapInfo mapInfo; - gst_buffer_map(buffer, &mapInfo, GST_MAP_READ); - bufferData = (const char*)mapInfo.data; - bufferSize = mapInfo.size; - QAudioFormat format = QGstUtils::audioFormatForSample(sample.get()); - - 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; - positionChanged(m_position); - } + QGstSampleHandle sample = m_appSink.pullSample(); + GstBuffer *buffer = gst_sample_get_buffer(sample.get()); + GstMapInfo mapInfo; + gst_buffer_map(buffer, &mapInfo, GST_MAP_READ); + const char *bufferData = (const char *)mapInfo.data; + int bufferSize = mapInfo.size; + QAudioFormat format = QGstUtils::audioFormatForSample(sample.get()); + + 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(bufferData, bufferSize), format, position); + position /= 1000; // convert to milliseconds + if (position != m_position) { + m_position = position; + positionChanged(m_position); } - gst_buffer_unmap(buffer, &mapInfo); } + gst_buffer_unmap(buffer, &mapInfo); return audioBuffer; } -bool QGstreamerAudioDecoder::bufferAvailable() const -{ - QMutexLocker locker(&m_buffersMutex); - return m_buffersAvailable > 0; -} - qint64 QGstreamerAudioDecoder::position() const { return m_position; @@ -430,30 +414,30 @@ void QGstreamerAudioDecoder::processInvalidMedia(QAudioDecoder::Error errorCode, error(int(errorCode), errorString); } -GstFlowReturn QGstreamerAudioDecoder::new_sample(GstAppSink *, gpointer user_data) +GstFlowReturn QGstreamerAudioDecoder::newSample(GstAppSink *) { - qCDebug(qLcGstreamerAudioDecoder) << "QGstreamerAudioDecoder::new_sample"; - // "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); - } - qCDebug(qLcGstreamerAudioDecoder) << "QGstreamerAudioDecoder::new_sample" << buffersAvailable; + QMetaObject::invokeMethod(this, [this, sessionId = m_currentSessionId] { + if (sessionId != m_currentSessionId) + return; // stop()ed before message is executed + + m_buffersAvailable += 1; + bufferAvailableChanged(true); + bufferReady(); + }); - if (!buffersAvailable) - decoder->bufferAvailableChanged(true); - decoder->bufferReady(); return GST_FLOW_OK; } +GstFlowReturn QGstreamerAudioDecoder::new_sample(GstAppSink *sink, gpointer user_data) +{ + QGstreamerAudioDecoder *decoder = reinterpret_cast<QGstreamerAudioDecoder *>(user_data); + qCDebug(qLcGstreamerAudioDecoder) << "QGstreamerAudioDecoder::new_sample"; + return decoder->newSample(sink); +} + void QGstreamerAudioDecoder::setAudioFlags(bool wantNativeAudio) { int flags = m_playbin.getInt("flags"); @@ -514,7 +498,7 @@ void QGstreamerAudioDecoder::updateDuration() if (m_durationQueries > 0) { //increase delay between duration requests int delay = 25 << (5 - m_durationQueries); - QTimer::singleShot(delay, this, SLOT(updateDuration())); + QTimer::singleShot(delay, this, &QGstreamerAudioDecoder::updateDuration); m_durationQueries--; } } diff --git a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder_p.h b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder_p.h index 2f6958947..eba1025fa 100644 --- a/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder_p.h +++ b/src/plugins/multimedia/gstreamer/audio/qgstreameraudiodecoder_p.h @@ -57,7 +57,6 @@ public: void setAudioFormat(const QAudioFormat &format) override; QAudioBuffer read() override; - bool bufferAvailable() const override; qint64 position() const override; qint64 duration() const override; @@ -73,6 +72,8 @@ private: #if QT_CONFIG(gstreamer_app) static GstFlowReturn new_sample(GstAppSink *sink, gpointer user_data); + GstFlowReturn newSample(GstAppSink *sink); + static void configureAppSrcElement(GObject *, GObject *, GParamSpec *, QGstreamerAudioDecoder *_this); #endif @@ -96,14 +97,14 @@ private: 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; + qint32 m_currentSessionId{}; + QGObjectHandlerScopedConnection m_deepNotifySourceConnection; }; diff --git a/src/plugins/multimedia/gstreamer/common/qgst.cpp b/src/plugins/multimedia/gstreamer/common/qgst.cpp index 8a77533a6..191fae49b 100644 --- a/src/plugins/multimedia/gstreamer/common/qgst.cpp +++ b/src/plugins/multimedia/gstreamer/common/qgst.cpp @@ -656,6 +656,11 @@ GType QGstObject::type() const return G_OBJECT_TYPE(get()); } +const char *QGstObject::typeName() const +{ + return g_type_name(type()); +} + GstObject *QGstObject::object() const { return get(); @@ -858,6 +863,25 @@ QGstElement QGstElement::createFromDevice(GstDevice *device, const char *name) }; } +QGstElement QGstElement::createFromPipelineDescription(const char *str) +{ + QUniqueGErrorHandle error; + QGstElement element{ + gst_parse_launch(str, &error), + QGstElement::NeedsRef, + }; + + if (error) // error does not mean that the element could not be constructed + qWarning() << "gst_parse_launch error:" << error; + + return element; +} + +QGstElement QGstElement::createFromPipelineDescription(const QByteArray &str) +{ + return createFromPipelineDescription(str.constData()); +} + QGstPad QGstElement::staticPad(const char *name) const { return QGstPad(gst_element_get_static_pad(element(), name), HasRef); @@ -933,8 +957,16 @@ bool QGstElement::finishStateChange(std::chrono::nanoseconds timeout) gst_element_get_state(element(), &state, &pending, timeout.count()); #ifndef QT_NO_DEBUG - if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL) + if (change != GST_STATE_CHANGE_SUCCESS && change != GST_STATE_CHANGE_NO_PREROLL) { qWarning() << "Could not finish change state of" << name() << change << state << pending; + + static const bool dumpEnabled = qEnvironmentVariableIsSet("GST_DEBUG_DUMP_DOT_DIR"); + if (dumpEnabled) { + QGstPipeline pipeline = getPipeline(); + if (pipeline) + pipeline.dumpGraph("finishStateChangeFailure"); + } + } #endif return change == GST_STATE_CHANGE_SUCCESS; } @@ -1016,6 +1048,36 @@ QGstBin QGstBin::createFromFactory(const char *factory, const char *name) }; } +QGstBin QGstBin::createFromPipelineDescription(const QByteArray &pipelineDescription, + const char *name, bool ghostUnlinkedPads) +{ + return createFromPipelineDescription(pipelineDescription.constData(), name, ghostUnlinkedPads); +} + +QGstBin QGstBin::createFromPipelineDescription(const char *pipelineDescription, const char *name, + bool ghostUnlinkedPads) +{ + QUniqueGErrorHandle error; + + GstElement *element = + gst_parse_bin_from_description_full(pipelineDescription, ghostUnlinkedPads, + /*context=*/nullptr, GST_PARSE_FLAG_NONE, &error); + + if (!element) { + qWarning() << "Failed to make element from pipeline description" << pipelineDescription + << error; + return QGstBin{}; + } + + if (name) + gst_element_set_name(element, name); + + return QGstBin{ + element, + NeedsRef, + }; +} + QGstBin::QGstBin(GstBin *bin, RefMode mode) : QGstElement{ qGstCheckedCast<GstElement>(bin), @@ -1049,11 +1111,7 @@ void QGstBin::dumpGraph(const char *fileNamePrefix) if (isNull()) return; - 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), + GST_DEBUG_BIN_TO_DOT_FILE(bin(), GstDebugGraphDetails(GST_DEBUG_GRAPH_SHOW_ALL), fileNamePrefix); } diff --git a/src/plugins/multimedia/gstreamer/common/qgst_handle_types_p.h b/src/plugins/multimedia/gstreamer/common/qgst_handle_types_p.h index c37ac5971..5cbbfaf19 100644 --- a/src/plugins/multimedia/gstreamer/common/qgst_handle_types_p.h +++ b/src/plugins/multimedia/gstreamer/common/qgst_handle_types_p.h @@ -163,6 +163,18 @@ struct QUniqueGErrorHandleTraits } }; + +struct QUniqueGstDateTimeHandleTraits +{ + using Type = GstDateTime *; + static constexpr Type invalidValue() noexcept { return nullptr; } + static bool close(Type handle) noexcept + { + gst_date_time_unref(handle); + return true; + } +}; + struct QFileDescriptorHandleTraits { using Type = int; @@ -237,6 +249,7 @@ using QGstSampleHandle = QGstImpl::QSharedHandle<QGstImpl::QGstSampleHandleTrait using QUniqueGstStructureHandle = QUniqueHandle<QGstImpl::QUniqueGstStructureHandleTraits>; using QUniqueGStringHandle = QUniqueHandle<QGstImpl::QUniqueGStringHandleTraits>; using QUniqueGErrorHandle = QUniqueHandle<QGstImpl::QUniqueGErrorHandleTraits>; +using QUniqueGstDateTimeHandle = QUniqueHandle<QGstImpl::QUniqueGstDateTimeHandleTraits>; using QFileDescriptorHandle = QUniqueHandle<QGstImpl::QFileDescriptorHandleTraits>; using QGstBufferHandle = QGstImpl::QGstMiniObjectHandleHelper<GstBuffer>::SharedHandle; using QGstContextHandle = QGstImpl::QGstMiniObjectHandleHelper<GstContext>::UniqueHandle; diff --git a/src/plugins/multimedia/gstreamer/common/qgst_p.h b/src/plugins/multimedia/gstreamer/common/qgst_p.h index 394ee1777..34b7e7e22 100644 --- a/src/plugins/multimedia/gstreamer/common/qgst_p.h +++ b/src/plugins/multimedia/gstreamer/common/qgst_p.h @@ -79,6 +79,29 @@ struct GstObjectTraits }; \ static_assert(true, "ensure semicolon") +#define QGST_DEFINE_CAST_TRAITS_FOR_INTERFACE(ClassName, MACRO_LABEL) \ + template <> \ + struct GstObjectTraits<ClassName> \ + { \ + using Type = ClassName; \ + template <typename U> \ + static bool isObjectOfType(U *arg) \ + { \ + return GST_IS_##MACRO_LABEL(arg); \ + } \ + template <typename U> \ + static Type *cast(U *arg) \ + { \ + return checked_cast(arg); \ + } \ + template <typename U> \ + static Type *checked_cast(U *arg) \ + { \ + return GST_##MACRO_LABEL(arg); \ + } \ + }; \ + static_assert(true, "ensure semicolon") + QGST_DEFINE_CAST_TRAITS(GstBin, BIN); QGST_DEFINE_CAST_TRAITS(GstClock, CLOCK); QGST_DEFINE_CAST_TRAITS(GstElement, ELEMENT); @@ -88,6 +111,8 @@ QGST_DEFINE_CAST_TRAITS(GstPipeline, PIPELINE); QGST_DEFINE_CAST_TRAITS(GstBaseSink, BASE_SINK); QGST_DEFINE_CAST_TRAITS(GstBaseSrc, BASE_SRC); +QGST_DEFINE_CAST_TRAITS_FOR_INTERFACE(GstTagSetter, TAG_SETTER); + #if QT_CONFIG(gstreamer_app) QGST_DEFINE_CAST_TRAITS(GstAppSink, APP_SINK); QGST_DEFINE_CAST_TRAITS(GstAppSrc, APP_SRC); @@ -115,6 +140,7 @@ struct GstObjectTraits<GObject> }; #undef QGST_DEFINE_CAST_TRAITS +#undef QGST_DEFINE_CAST_TRAITS_FOR_INTERFACE } // namespace QGstImpl @@ -382,6 +408,7 @@ public: void disconnect(gulong handlerId); GType type() const; + const char *typeName() const; GstObject *object() const; const char *name() const; }; @@ -532,6 +559,8 @@ public: const char *name = nullptr); static QGstElement createFromDevice(const QGstDeviceHandle &, const char *name = nullptr); static QGstElement createFromDevice(GstDevice *, const char *name = nullptr); + static QGstElement createFromPipelineDescription(const char *); + static QGstElement createFromPipelineDescription(const QByteArray &); QGstPad staticPad(const char *name) const; QGstPad src() const; @@ -643,6 +672,12 @@ public: explicit QGstBin(GstBin *bin, RefMode mode = NeedsRef); static QGstBin create(const char *name); static QGstBin createFromFactory(const char *factory, const char *name); + static QGstBin createFromPipelineDescription(const QByteArray &pipelineDescription, + const char *name = nullptr, + bool ghostUnlinkedPads = false); + static QGstBin createFromPipelineDescription(const char *pipelineDescription, + const char *name = nullptr, + bool ghostUnlinkedPads = false); template <typename... Ts> std::enable_if_t<(std::is_base_of_v<QGstElement, Ts> && ...), void> add(const Ts &...ts) diff --git a/src/plugins/multimedia/gstreamer/common/qgstpipeline.cpp b/src/plugins/multimedia/gstreamer/common/qgstpipeline.cpp index 7d507f076..ae57f21d4 100644 --- a/src/plugins/multimedia/gstreamer/common/qgstpipeline.cpp +++ b/src/plugins/multimedia/gstreamer/common/qgstpipeline.cpp @@ -265,8 +265,8 @@ void QGstPipeline::beginConfig() break; } case GST_STATE_CHANGE_FAILURE: { - // should not happen - qCritical() << "QGstPipeline::beginConfig: state change failure"; + qDebug() << "QGstPipeline::beginConfig: state change failure"; + dumpGraph("beginConfigFailure"); break; } diff --git a/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput.cpp b/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput.cpp index 0381b921e..ca56c4572 100644 --- a/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput.cpp +++ b/src/plugins/multimedia/gstreamer/common/qgstreameraudioinput.cpp @@ -67,25 +67,34 @@ void QGstreamerAudioInput::setAudioDevice(const QAudioDevice &device) return; qCDebug(qLcMediaAudioInput) << "setAudioInput" << device.description() << device.isNull(); m_audioDevice = device; + const QByteArray &id = m_audioDevice.id(); QGstElement newSrc; if constexpr (QT_CONFIG(pulseaudio)) { - auto id = m_audioDevice.id(); newSrc = QGstElement::createFromFactory("pulsesrc", "audiosrc"); - if (!newSrc.isNull()) + if (newSrc) newSrc.set("device", id.constData()); else - qCWarning(qLcMediaAudioInput) << "Invalid audio device"; - } else { - auto *deviceInfo = static_cast<const QGStreamerAudioDeviceInfo *>(m_audioDevice.handle()); - if (deviceInfo && deviceInfo->gstDevice) - newSrc = QGstElement::createFromDevice(deviceInfo->gstDevice, "audiosrc"); + qWarning() << "Cannot create pulsesrc"; + } else if constexpr (QT_CONFIG(alsa)) { + newSrc = QGstElement::createFromFactory("alsasrc", "audiosrc"); + if (newSrc) + newSrc.set("device", id.constData()); else - qCWarning(qLcMediaAudioInput) << "Invalid audio device"; + qWarning() << "Cannot create alsasrc"; + } else { + auto *gstDeviceInfo = + dynamic_cast<const QGStreamerAudioDeviceInfo *>(m_audioDevice.handle()); + if (gstDeviceInfo && gstDeviceInfo->gstDevice) { + newSrc = QGstElement::createFromDevice(gstDeviceInfo->gstDevice, "audiosrc"); + } else { + qWarning() << "Invalid audio device"; + } } if (newSrc.isNull()) { - qCWarning(qLcMediaAudioInput) << "Failed to create a gst element for the audio device, using a default audio source"; + qWarning() << "Failed to create a gst element for the audio device, using a default audio " + "source"; newSrc = QGstElement::createFromFactory("autoaudiosrc", "audiosrc"); } diff --git a/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput.cpp b/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput.cpp index f45c371e9..6156c97be 100644 --- a/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput.cpp +++ b/src/plugins/multimedia/gstreamer/common/qgstreameraudiooutput.cpp @@ -73,26 +73,34 @@ void QGstreamerAudioOutput::setAudioDevice(const QAudioDevice &info) if (info == m_audioOutput) return; qCDebug(qLcMediaAudioOutput) << "setAudioOutput" << info.description() << info.isNull(); + m_audioOutput = info; + const QByteArray &id = m_audioOutput.id(); QGstElement newSink; if constexpr (QT_CONFIG(pulseaudio)) { - auto id = m_audioOutput.id(); newSink = QGstElement::createFromFactory("pulsesink", "audiosink"); - if (!newSink.isNull()) + if (newSink) + newSink.set("device", id.constData()); + else + qWarning() << "Cannot create pulsesink"; + } else if constexpr (QT_CONFIG(alsa)) { + newSink = QGstElement::createFromFactory("alsasink", "audiosink"); + if (newSink) newSink.set("device", id.constData()); else - qCWarning(qLcMediaAudioOutput) << "Invalid audio device"; + qWarning() << "Cannot create alsasink"; } else { - auto *deviceInfo = static_cast<const QGStreamerAudioDeviceInfo *>(m_audioOutput.handle()); + auto *deviceInfo = dynamic_cast<const QGStreamerAudioDeviceInfo *>(m_audioOutput.handle()); if (deviceInfo && deviceInfo->gstDevice) newSink = QGstElement::createFromDevice(deviceInfo->gstDevice, "audiosink"); else - qCWarning(qLcMediaAudioOutput) << "Invalid audio device"; + qWarning() << "Invalid audio device"; } if (newSink.isNull()) { - qCWarning(qLcMediaAudioOutput) << "Failed to create a gst element for the audio device, using a default audio sink"; + qWarning() << "Failed to create a gst element for the audio " + "device using a default audio sink"; newSink = QGstElement::createFromFactory("autoaudiosink", "audiosink"); } diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp b/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp index 4dd5695dc..6e3636441 100644 --- a/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp +++ b/src/plugins/multimedia/gstreamer/common/qgstreamermediaplayer.cpp @@ -306,9 +306,17 @@ bool QGstreamerMediaPlayer::processBusMessage(const QGstreamerMessage &message) gst_message_parse_tag(gm, &tagList); qCDebug(qLcMediaPlayer) << " Got tags: " << tagList.get(); - auto metaData = QGstreamerMetaData::fromGstTagList(tagList.get()); + auto metaData = taglistToMetaData(tagList); + auto keys = metaData.keys(); for (auto k : metaData.keys()) m_metaData.insert(k, metaData.value(k)); + if (!keys.isEmpty()) + emit metaDataChanged(); + + if (gstVideoOutput) { + QVariant rotation = m_metaData.value(QMediaMetaData::Orientation); + gstVideoOutput->setRotation(rotation.value<QtVideo::Rotation>()); + } break; } case GST_MESSAGE_DURATION_CHANGED: { @@ -879,7 +887,10 @@ void QGstreamerMediaPlayer::setMedia(const QUrl &content, QIODevice *stream) decoder.set("uri", content.toEncoded().constData()); decoder.set("use-buffering", true); - decoder.set("ring-buffer-max-size", 128 /*kb*/); + + constexpr int mb = 1024 * 1024; + decoder.set("ring-buffer-max-size", 2 * mb); + if (m_bufferProgress != 0) { m_bufferProgress = 0; emit bufferProgressChanged(0.); @@ -894,9 +905,11 @@ void QGstreamerMediaPlayer::setMedia(const QUrl &content, QIODevice *stream) padRemoved = decoder.onPadRemoved<&QGstreamerMediaPlayer::decoderPadRemoved>(this); mediaStatusChanged(QMediaPlayer::LoadingMedia); - - if (!playerPipeline.setState(GST_STATE_PAUSED)) + if (!playerPipeline.setState(GST_STATE_PAUSED)) { qCWarning(qLcMediaPlayer) << "Unable to set the pipeline to the paused state."; + // Note: no further error handling: errors will be delivered via a GstMessage + return; + } playerPipeline.setPosition(0); positionChanged(0); @@ -961,7 +974,7 @@ void QGstreamerMediaPlayer::parseStreamsAndMetadata() QGstTagListHandle tagList; gst_structure_get(topology.structure, "tags", GST_TYPE_TAG_LIST, &tagList, nullptr); - const auto metaData = QGstreamerMetaData::fromGstTagList(tagList.get()); + const auto metaData = taglistToMetaData(tagList); for (auto k : metaData.keys()) m_metaData.insert(k, metaData.value(k)); } @@ -1032,7 +1045,7 @@ QMediaMetaData QGstreamerMediaPlayer::trackMetaData(QPlatformMediaPlayer::TrackT QGstTagListHandle tagList; g_object_get(track.object(), "tags", &tagList, nullptr); - return tagList ? QGstreamerMetaData::fromGstTagList(tagList.get()) : QMediaMetaData{}; + return taglistToMetaData(tagList); } int QGstreamerMediaPlayer::activeTrack(TrackType type) diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamermetadata.cpp b/src/plugins/multimedia/gstreamer/common/qgstreamermetadata.cpp index 3567013f7..fab4750f9 100644 --- a/src/plugins/multimedia/gstreamer/common/qgstreamermetadata.cpp +++ b/src/plugins/multimedia/gstreamer/common/qgstreamermetadata.cpp @@ -143,122 +143,206 @@ const char *keyToTag(QMediaMetaData::Key key) #undef constexpr_lookup -//internal -void addTagToMap(const GstTagList *list, const gchar *tag, gpointer user_data) +QtVideo::Rotation parseRotationTag(const char *string) { using namespace std::string_view_literals; + if (string == "rotate-90"sv) + return QtVideo::Rotation::Clockwise90; + if (string == "rotate-180"sv) + return QtVideo::Rotation::Clockwise180; + if (string == "rotate-270"sv) + return QtVideo::Rotation::Clockwise270; + if (string == "rotate-0"sv) + return QtVideo::Rotation::None; + + qCritical() << "cannot parse orientation: {}" << string; + return QtVideo::Rotation::None; +} + +QDateTime parseDate(const GValue &val) +{ + Q_ASSERT(G_VALUE_TYPE(&val) == G_TYPE_DATE); + + const GDate *date = (const GDate *)g_value_get_boxed(&val); + if (!g_date_valid(date)) + return {}; + + int year = g_date_get_year(date); + int month = g_date_get_month(date); + int day = g_date_get_day(date); + return QDateTime(QDate(year, month, day), QTime()); +} + +QDateTime parseDateTime(const GValue &val) +{ + Q_ASSERT(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); + } + return QDateTime{ + QDate(year, month, day), + QTime(hour, minute, second), + QTimeZone(tz * 60 * 60), + }; +} + +QImage parseImage(const GValue &val) +{ + Q_ASSERT(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); + QImage image = QImage::fromData(info.data, info.size, name); + gst_buffer_unmap(buffer, &info); + return image; + } + } + } + + return {}; +} + +std::optional<double> parseFractionAsDouble(const GValue &val) +{ + Q_ASSERT(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) + return std::nullopt; + return double(nom) / double(denom); +} + +constexpr std::string_view extendedComment{ GST_TAG_EXTENDED_COMMENT }; + +void addTagsFromExtendedComment(const GstTagList *list, const gchar *tag, QMediaMetaData &metadata) +{ + using namespace Qt::Literals; + assert(tag == extendedComment); + + int entryCount = gst_tag_list_get_tag_size(list, tag); + for (int i = 0; i != entryCount; ++i) { + const GValue *value = gst_tag_list_get_value_index(list, tag, i); + + const QLatin1StringView strValue{ g_value_get_string(value) }; + + auto equalIndex = strValue.indexOf(QLatin1StringView("=")); + if (equalIndex == -1) { + qDebug() << "Cannot parse GST_TAG_EXTENDED_COMMENT entry: " << value; + continue; + } + + const QLatin1StringView key = strValue.first(equalIndex); + const QLatin1StringView valueString = strValue.last(strValue.size() - equalIndex - 1); + + if (key == "DURATION"_L1) { + QUniqueGstDateTimeHandle duration{ + gst_date_time_new_from_iso8601_string(valueString.data()), + }; + + if (duration) { + using namespace std::chrono; + + auto chronoDuration = hours(gst_date_time_get_hour(duration.get())) + + minutes(gst_date_time_get_minute(duration.get())) + + seconds(gst_date_time_get_second(duration.get())) + + microseconds(gst_date_time_get_microsecond(duration.get())); + + metadata.insert(QMediaMetaData::Duration, + QVariant::fromValue(round<milliseconds>(chronoDuration).count())); + } + } + } +} + +void addTagToMetaData(const GstTagList *list, const gchar *tag, void *userdata) +{ + QMediaMetaData &metadata = *reinterpret_cast<QMediaMetaData *>(userdata); + QMediaMetaData::Key key = tagToKey(tag); - if (key == QMediaMetaData::Key(-1)) - return; + if (key == QMediaMetaData::Key(-1)) { + if (tag == extendedComment) + addTagsFromExtendedComment(list, tag, metadata); - auto *map = reinterpret_cast<QHash<QMediaMetaData::Key, QVariant>* >(user_data); + return; + } - GValue val; - val.g_type = 0; + GValue val{}; gst_tag_list_copy_value(&val, list, tag); - switch (G_VALUE_TYPE(&val)) { - case G_TYPE_STRING: { + GType type = G_VALUE_TYPE(&val); + + if (auto entryCount = gst_tag_list_get_tag_size(list, tag) != 0; entryCount != 1) + qWarning() << "addTagToMetaData: invaled entry count for" << tag << "-" << entryCount; + + if (type == G_TYPE_STRING) { const gchar *str_value = g_value_get_string(&val); switch (key) { case QMediaMetaData::Language: { - map->emplace(key, - QVariant::fromValue(QLocale::codeToLanguage(QString::fromUtf8(str_value), - QLocale::ISO639Part2))); + metadata.insert(key, + QVariant::fromValue(QLocale::codeToLanguage( + QString::fromUtf8(str_value), QLocale::ISO639Part2))); break; } case QMediaMetaData::Orientation: { - if (str_value == "rotate-90"sv) - map->emplace(key, QVariant::fromValue(QtVideo::Rotation::Clockwise90)); - else if (str_value == "rotate-180"sv) - map->emplace(key, QVariant::fromValue(QtVideo::Rotation::Clockwise180)); - else if (str_value == "rotate-270"sv) - map->emplace(key, QVariant::fromValue(QtVideo::Rotation::Clockwise270)); - else if (str_value == "rotate-0"sv) - map->emplace(key, QVariant::fromValue(QtVideo::Rotation::None)); + metadata.insert(key, QVariant::fromValue(parseRotationTag(str_value))); break; } default: - map->emplace(key, QString::fromUtf8(str_value)); + metadata.insert(key, QString::fromUtf8(str_value)); break; }; - 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), - QTimeZone(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); - } + } else if (type == G_TYPE_INT) { + metadata.insert(key, g_value_get_int(&val)); + } else if (type == G_TYPE_UINT) { + metadata.insert(key, g_value_get_uint(&val)); + } else if (type == G_TYPE_LONG) { + metadata.insert(key, qint64(g_value_get_long(&val))); + } else if (type == G_TYPE_BOOLEAN) { + metadata.insert(key, g_value_get_boolean(&val)); + } else if (type == G_TYPE_CHAR) { + metadata.insert(key, g_value_get_schar(&val)); + } else if (type == G_TYPE_DOUBLE) { + metadata.insert(key, g_value_get_double(&val)); + } else if (type == G_TYPE_DATE) { + if (!metadata.keys().contains(key)) { + QDateTime date = parseDate(val); + if (date.isValid()) + metadata.insert(key, date); } - break; + } else if (type == GST_TYPE_DATE_TIME) { + metadata.insert(key, parseDateTime(val)); + } else if (type == GST_TYPE_SAMPLE) { + QImage image = parseImage(val); + if (!image.isNull()) + metadata.insert(key, image); + } else if (type == GST_TYPE_FRACTION) { + std::optional<double> fraction = parseFractionAsDouble(val); + + if (fraction) + metadata.insert(key, *fraction); } g_value_unset(&val); @@ -266,91 +350,99 @@ void addTagToMap(const GstTagList *list, const gchar *tag, gpointer user_data) } // namespace -QGstreamerMetaData QGstreamerMetaData::fromGstTagList(const GstTagList *tags) +QMediaMetaData taglistToMetaData(const GstTagList *tagList) { - QGstreamerMetaData m; - gst_tag_list_foreach(tags, addTagToMap, &m.data); + QMediaMetaData m; + if (tagList) + gst_tag_list_foreach(tagList, reinterpret_cast<GstTagForeachFunc>(&addTagToMetaData), &m); return m; } - -void QGstreamerMetaData::setMetaData(GstElement *element) const +QMediaMetaData taglistToMetaData(const QGstTagListHandle &handle) { - if (!GST_IS_TAG_SETTER(element)) - return; + return taglistToMetaData(handle.get()); +} - gst_tag_setter_reset_tags(GST_TAG_SETTER(element)); +static void applyMetaDataToTagSetter(const QMediaMetaData &metadata, GstTagSetter *element) +{ + gst_tag_setter_reset_tags(element); - for (auto it = data.cbegin(), end = data.cend(); it != end; ++it) { - const char *tagName = keyToTag(it.key()); + for (QMediaMetaData::Key key : metadata.keys()) { + const char *tagName = keyToTag(key); if (!tagName) continue; - const QVariant &tagValue = it.value(); + const QVariant &tagValue = metadata.value(key); + + auto setTag = [&](const auto &value) { + gst_tag_setter_add_tags(element, GST_TAG_MERGE_REPLACE, tagName, value, nullptr); + }; 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::QDate: - case QMetaType::QDateTime: { - QDateTime date = tagValue.toDateTime(); - - QGstGstDateTimeHandle dateTime{ - 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()), - QGstGstDateTimeHandle::HasRef, - }; - - gst_tag_setter_add_tags(GST_TAG_SETTER(element), GST_TAG_MERGE_REPLACE, tagName, - dateTime.get(), nullptr); - break; - } - default: { - if (tagValue.typeId() == qMetaTypeId<QLocale::Language>()) { - QByteArray language = QLocale::languageToCode(tagValue.value<QLocale::Language>(), QLocale::ISO639Part2).toUtf8(); - gst_tag_setter_add_tags(GST_TAG_SETTER(element), - GST_TAG_MERGE_REPLACE, - tagName, - language.constData(), - nullptr); - } - - break; + case QMetaType::QString: + setTag(tagValue.toString().toUtf8().constData()); + break; + case QMetaType::Int: + case QMetaType::LongLong: + setTag(tagValue.toInt()); + break; + case QMetaType::Double: + setTag(tagValue.toDouble()); + break; + case QMetaType::QDate: + case QMetaType::QDateTime: { + QDateTime date = tagValue.toDateTime(); + + QGstGstDateTimeHandle dateTime{ + 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()), + QGstGstDateTimeHandle::HasRef, + }; + + setTag(dateTime.get()); + break; + } + default: { + if (tagValue.typeId() == qMetaTypeId<QLocale::Language>()) { + QByteArray language = QLocale::languageToCode(tagValue.value<QLocale::Language>(), + QLocale::ISO639Part2) + .toUtf8(); + setTag(language.constData()); } + + break; + } } } } -void QGstreamerMetaData::setMetaData(GstBin *bin) const +void applyMetaDataToTagSetter(const QMediaMetaData &metadata, const QGstElement &element) { - GstIterator *elements = gst_bin_iterate_all_by_interface(bin, GST_TYPE_TAG_SETTER); - GValue item = G_VALUE_INIT; + GstTagSetter *tagSetter = qGstSafeCast<GstTagSetter>(element.element()); + if (tagSetter) + applyMetaDataToTagSetter(metadata, tagSetter); + else + qWarning() << "applyMetaDataToTagSetter failed: element not a GstTagSetter" + << element.name(); +} + +void applyMetaDataToTagSetter(const QMediaMetaData &metadata, const QGstBin &bin) +{ + GstIterator *elements = gst_bin_iterate_all_by_interface(bin.bin(), GST_TYPE_TAG_SETTER); + GValue item = {}; + while (gst_iterator_next(elements, &item) == GST_ITERATOR_OK) { - GstElement * const element = GST_ELEMENT(g_value_get_object(&item)); - setMetaData(element); + GstElement *element = static_cast<GstElement *>(g_value_get_object(&item)); + if (!element) + continue; + + GstTagSetter *tagSetter = qGstSafeCast<GstTagSetter>(element); + + if (tagSetter) + applyMetaDataToTagSetter(metadata, tagSetter); } + gst_iterator_free(elements); } - QT_END_NAMESPACE diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamermetadata_p.h b/src/plugins/multimedia/gstreamer/common/qgstreamermetadata_p.h index 7ff5552b2..d0d7620db 100644 --- a/src/plugins/multimedia/gstreamer/common/qgstreamermetadata_p.h +++ b/src/plugins/multimedia/gstreamer/common/qgstreamermetadata_p.h @@ -16,20 +16,16 @@ // #include <qmediametadata.h> -#include <qvariant.h> -#include <gst/gst.h> +#include "qgst_p.h" QT_BEGIN_NAMESPACE -class QGstreamerMetaData : public QMediaMetaData -{ -public: - static QGstreamerMetaData fromGstTagList(const GstTagList *tags); +QMediaMetaData taglistToMetaData(const GstTagList *); +QMediaMetaData taglistToMetaData(const QGstTagListHandle &); - void setMetaData(GstBin *bin) const; - void setMetaData(GstElement *element) const; -}; +void applyMetaDataToTagSetter(const QMediaMetaData &metadata, const QGstBin &); +void applyMetaDataToTagSetter(const QMediaMetaData &metadata, const QGstElement &); QT_END_NAMESPACE diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput.cpp b/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput.cpp index 053dd973b..6bc65693a 100644 --- a/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput.cpp +++ b/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput.cpp @@ -179,6 +179,14 @@ void QGstreamerVideoOutput::doLinkSubtitleStream() qLinkGstElements(subtitleSrc, subtitleSink); } +void QGstreamerVideoOutput::updateNativeSize() +{ + if (!m_videoSink) + return; + + m_videoSink->setNativeSize(qRotatedFrameSize(nativeSize, rotation)); +} + void QGstreamerVideoOutput::setIsPreview() { // configures the queue to be fast and lightweight for camera preview @@ -204,8 +212,13 @@ void QGstreamerVideoOutput::flushSubtitles() void QGstreamerVideoOutput::setNativeSize(QSize sz) { nativeSize = sz; - if (m_videoSink) - m_videoSink->setNativeSize(nativeSize); + updateNativeSize(); +} + +void QGstreamerVideoOutput::setRotation(QtVideo::Rotation rot) +{ + rotation = rot; + updateNativeSize(); } QT_END_NAMESPACE diff --git a/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput_p.h b/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput_p.h index 62bd4b219..42acb18cc 100644 --- a/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput_p.h +++ b/src/plugins/multimedia/gstreamer/common/qgstreamervideooutput_p.h @@ -50,12 +50,14 @@ public: void flushSubtitles(); void setNativeSize(QSize); + void setRotation(QtVideo::Rotation); private: QGstreamerVideoOutput(QGstElement videoConvert, QGstElement videoScale, QGstElement videoSink, QObject *parent); void doLinkSubtitleStream(); + void updateNativeSize(); QPointer<QGstreamerVideoSink> m_videoSink; @@ -72,6 +74,7 @@ private: QGstElement subtitleSink; QSize nativeSize; + QtVideo::Rotation rotation{}; }; QT_END_NAMESPACE diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera.cpp b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera.cpp index 8d3cd6baf..887f74b12 100644 --- a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera.cpp +++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamercamera.cpp @@ -80,6 +80,8 @@ void QGstreamerCamera::setActive(bool active) void QGstreamerCamera::setCamera(const QCameraDevice &camera) { + using namespace Qt::Literals; + if (m_cameraDevice == camera) return; @@ -90,7 +92,15 @@ void QGstreamerCamera::setCamera(const QCameraDevice &camera) gstNewCamera = QGstElement::createFromFactory("videotestsrc"); } else { auto *integration = static_cast<QGstreamerIntegration *>(QGstreamerIntegration::instance()); - auto *device = integration->videoDevice(camera.id()); + GstDevice *device = integration->videoDevice(camera.id()); + + if (!device) { + emit error(QCamera::Error::CameraError, + u"Failed to create GstDevice for camera: "_s + + QString::fromUtf8(camera.id())); + return; + } + gstNewCamera = QGstElement::createFromDevice(device, "camerasrc"); if (QGstStructure properties = gst_device_get_properties(device); !properties.isNull()) { if (properties.name() == "v4l2deviceprovider") diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp index 3500d6b6f..2f8a1d776 100644 --- a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp +++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture.cpp @@ -81,6 +81,7 @@ QGstreamerImageCapture::~QGstreamerImageCapture() bool QGstreamerImageCapture::isReadyForCapture() const { + QMutexLocker guard(&m_mutex); return m_session && !passImage && cameraActive; } @@ -107,6 +108,7 @@ int QGstreamerImageCapture::doCapture(const QString &fileName) QMetaObject::invokeMethod(this, std::forward<decltype(fn)>(fn), Qt::QueuedConnection); }; + QMutexLocker guard(&m_mutex); if (!m_session) { invokeDeferred([this] { emit error(-1, QImageCapture::ResourceError, @@ -161,6 +163,8 @@ void QGstreamerImageCapture::setResolution(const QSize &resolution) bool QGstreamerImageCapture::probeBuffer(GstBuffer *buffer) { + QMutexLocker guard(&m_mutex); + if (!passImage) return false; qCDebug(qLcImageCaptureGst) << "probe buffer"; @@ -204,14 +208,9 @@ bool QGstreamerImageCapture::probeBuffer(GstBuffer *buffer) 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; @@ -219,6 +218,7 @@ bool QGstreamerImageCapture::probeBuffer(GstBuffer *buffer) void QGstreamerImageCapture::setCaptureSession(QPlatformMediaCaptureSession *session) { + QMutexLocker guard(&m_mutex); QGstreamerMediaCapture *captureSession = static_cast<QGstreamerMediaCapture *>(session); if (m_session == captureSession) return; @@ -244,6 +244,17 @@ void QGstreamerImageCapture::setCaptureSession(QPlatformMediaCaptureSession *ses onCameraChanged(); } +void QGstreamerImageCapture::setMetaData(const QMediaMetaData &m) +{ + { + QMutexLocker guard(&m_mutex); + QPlatformImageCapture::setMetaData(m); + } + + // ensure taginject injects this metaData + applyMetaDataToTagSetter(m, muxer); +} + void QGstreamerImageCapture::cameraActiveChanged(bool active) { qCDebug(qLcImageCaptureGst) << "cameraActiveChanged" << cameraActive << active; @@ -256,9 +267,11 @@ void QGstreamerImageCapture::cameraActiveChanged(bool active) void QGstreamerImageCapture::onCameraChanged() { + QMutexLocker guard(&m_mutex); if (m_session->camera()) { cameraActiveChanged(m_session->camera()->isActive()); - connect(m_session->camera(), &QPlatformCamera::activeChanged, this, &QGstreamerImageCapture::cameraActiveChanged); + connect(m_session->camera(), &QPlatformCamera::activeChanged, this, + &QGstreamerImageCapture::cameraActiveChanged); } else { cameraActiveChanged(false); } @@ -273,6 +286,7 @@ gboolean QGstreamerImageCapture::saveImageFilter(GstElement *, GstBuffer *buffer void QGstreamerImageCapture::saveBufferToImage(GstBuffer *buffer) { + QMutexLocker guard(&m_mutex); passImage = false; if (pendingImages.isEmpty()) diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture_p.h b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture_p.h index 79c6a02e0..4b2220d12 100644 --- a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture_p.h +++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamerimagecapture_p.h @@ -18,6 +18,7 @@ #include <QtMultimedia/private/qplatformimagecapture_p.h> #include <QtMultimedia/private/qmultimediautils_p.h> +#include <QtCore/qmutex.h> #include <QtCore/qqueue.h> #include <common/qgst_p.h> @@ -48,6 +49,8 @@ public: QGstElement gstElement() const { return bin; } + void setMetaData(const QMediaMetaData &m) override; + public Q_SLOTS: void cameraActiveChanged(bool active); void onCameraChanged(); @@ -63,6 +66,8 @@ private: void saveBufferToImage(GstBuffer *buffer); + mutable QRecursiveMutex + m_mutex; // guard all elements accessed from probeBuffer/saveBufferToImage QGstreamerMediaCapture *m_session = nullptr; int m_lastId = 0; QImageEncoderSettings m_settings; diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder.cpp b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder.cpp index 9c335a185..4f9d3d364 100644 --- a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder.cpp +++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder.cpp @@ -54,7 +54,7 @@ bool QGstreamerMediaEncoder::isLocationWritable(const QUrl &) const void QGstreamerMediaEncoder::handleSessionError(QMediaRecorder::Error code, const QString &description) { - error(code, description); + updateError(code, description); stop(); } @@ -90,7 +90,7 @@ bool QGstreamerMediaEncoder::processBusMessage(const QGstreamerMessage &msg) QUniqueGErrorHandle err; QGString debug; gst_message_parse_error(msg.message(), &err, &debug); - error(QMediaRecorder::ResourceError, QString::fromUtf8(err.get()->message)); + updateError(QMediaRecorder::ResourceError, QString::fromUtf8(err.get()->message)); if (!m_finalizing) stop(); finalize(); @@ -262,7 +262,7 @@ void QGstreamerMediaEncoder::record(QMediaEncoderSettings &settings) const auto hasAudio = m_session->audioInput() != nullptr; if (!hasVideo && !hasAudio) { - error(QMediaRecorder::ResourceError, QMediaRecorder::tr("No camera or audio input")); + updateError(QMediaRecorder::ResourceError, QMediaRecorder::tr("No camera or audio input")); return; } @@ -313,7 +313,7 @@ void QGstreamerMediaEncoder::record(QMediaEncoderSettings &settings) gstPipeline.modifyPipelineWhileNotRunning([&] { gstPipeline.add(gstEncoder, gstFileSink); qLinkGstElements(gstEncoder, gstFileSink); - m_metaData.setMetaData(gstEncoder.bin()); + applyMetaDataToTagSetter(m_metaData, gstEncoder); m_session->linkEncoder(audioSink, videoSink); @@ -380,7 +380,7 @@ void QGstreamerMediaEncoder::setMetaData(const QMediaMetaData &metaData) { if (!m_session) return; - m_metaData = static_cast<const QGstreamerMetaData &>(metaData); + m_metaData = metaData; } QMediaMetaData QGstreamerMediaEncoder::metaData() const @@ -398,7 +398,8 @@ void QGstreamerMediaEncoder::setCaptureSession(QPlatformMediaCaptureSession *ses stop(); if (m_finalizing) { QEventLoop loop; - loop.connect(mediaRecorder(), SIGNAL(recorderStateChanged(RecorderState)), SLOT(quit())); + QObject::connect(mediaRecorder(), &QMediaRecorder::recorderStateChanged, &loop, + &QEventLoop::quit); loop.exec(); } diff --git a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder_p.h b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder_p.h index f570f069e..637fb7264 100644 --- a/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder_p.h +++ b/src/plugins/multimedia/gstreamer/mediacapture/qgstreamermediaencoder_p.h @@ -76,7 +76,7 @@ private: void finalize(); QGstreamerMediaCapture *m_session = nullptr; - QGstreamerMetaData m_metaData; + QMediaMetaData m_metaData; QTimer signalDurationChangedTimer; QGstPipeline gstPipeline; diff --git a/src/plugins/multimedia/wasm/mediacapture/qwasmmediarecorder.cpp b/src/plugins/multimedia/wasm/mediacapture/qwasmmediarecorder.cpp index 98d0d860b..98f04616a 100644 --- a/src/plugins/multimedia/wasm/mediacapture/qwasmmediarecorder.cpp +++ b/src/plugins/multimedia/wasm/mediacapture/qwasmmediarecorder.cpp @@ -285,8 +285,8 @@ void QWasmMediaRecorder::setStream(emscripten::val stream) theError["target"]["data-mediarecordercontext"].as<quintptr>()); if (recorder) { - recorder->error(QMediaRecorder::ResourceError, - QString::fromStdString(theError["message"].as<std::string>())); + recorder->updateError(QMediaRecorder::ResourceError, + QString::fromStdString(theError["message"].as<std::string>())); emit recorder->stateChanged(recorder->state()); } }; @@ -381,12 +381,12 @@ void QWasmMediaRecorder::audioDataAvailable(emscripten::val blob, double timeCod auto fileReader = std::make_shared<qstdweb::FileReader>(); fileReader->onError([=](emscripten::val theError) { - error(QMediaRecorder::ResourceError, - QString::fromStdString(theError["message"].as<std::string>())); + updateError(QMediaRecorder::ResourceError, + QString::fromStdString(theError["message"].as<std::string>())); }); fileReader->onAbort([=](emscripten::val) { - error(QMediaRecorder::ResourceError, QStringLiteral("File read aborted")); + updateError(QMediaRecorder::ResourceError, QStringLiteral("File read aborted")); }); fileReader->onLoad([=](emscripten::val) { @@ -473,7 +473,8 @@ void QWasmMediaRecorder::setTrackContraints(QMediaEncoderSettings &settings, ems qCDebug(qWasmMediaRecorder) << theError["code"].as<int>() << QString::fromStdString(theError["message"].as<std::string>()); - error(QMediaRecorder::ResourceError, QString::fromStdString(theError["message"].as<std::string>())); + updateError(QMediaRecorder::ResourceError, + QString::fromStdString(theError["message"].as<std::string>())); } }, constraints); } diff --git a/src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder.cpp b/src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder.cpp index 1d901c036..512110af6 100644 --- a/src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder.cpp +++ b/src/plugins/multimedia/windows/mediacapture/qwindowsmediaencoder.cpp @@ -52,8 +52,8 @@ void QWindowsMediaEncoder::record(QMediaEncoderSettings &settings) m_mediaDeviceSession->setActive(true); if (!m_mediaDeviceSession->isActivating()) { - error(QMediaRecorder::ResourceError, - QMediaRecorderPrivate::msgFailedStartRecording()); + updateError(QMediaRecorder::ResourceError, + QMediaRecorderPrivate::msgFailedStartRecording()); return; } } @@ -72,7 +72,7 @@ void QWindowsMediaEncoder::record(QMediaEncoderSettings &settings) stateChanged(m_state); } else { - error(ec, QMediaRecorderPrivate::msgFailedStartRecording()); + updateError(ec, QMediaRecorderPrivate::msgFailedStartRecording()); } } @@ -85,7 +85,7 @@ void QWindowsMediaEncoder::pause() m_state = QMediaRecorder::PausedState; stateChanged(m_state); } else { - error(QMediaRecorder::FormatError, tr("Failed to pause recording")); + updateError(QMediaRecorder::FormatError, tr("Failed to pause recording")); } } @@ -98,7 +98,7 @@ void QWindowsMediaEncoder::resume() m_state = QMediaRecorder::RecordingState; stateChanged(m_state); } else { - error(QMediaRecorder::FormatError, tr("Failed to resume recording")); + updateError(QMediaRecorder::FormatError, tr("Failed to resume recording")); } } @@ -178,11 +178,11 @@ void QWindowsMediaEncoder::onDurationChanged(qint64 duration) void QWindowsMediaEncoder::onStreamingError(int errorCode) { if (errorCode == MF_E_VIDEO_RECORDING_DEVICE_INVALIDATED) - error(QMediaRecorder::ResourceError, tr("Camera is no longer present")); + updateError(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")); + updateError(QMediaRecorder::ResourceError, tr("Audio input is no longer present")); else - error(QMediaRecorder::ResourceError, tr("Streaming error")); + updateError(QMediaRecorder::ResourceError, tr("Streaming error")); if (m_state != QMediaRecorder::StoppedState) { m_mediaDeviceSession->stopRecording(); @@ -194,7 +194,7 @@ void QWindowsMediaEncoder::onStreamingError(int errorCode) void QWindowsMediaEncoder::onRecordingError(int errorCode) { Q_UNUSED(errorCode); - error(QMediaRecorder::ResourceError, tr("Recording error")); + updateError(QMediaRecorder::ResourceError, tr("Recording error")); auto lastState = m_state; m_state = QMediaRecorder::StoppedState; diff --git a/tests/auto/integration/qaudiodecoderbackend/tst_qaudiodecoderbackend.cpp b/tests/auto/integration/qaudiodecoderbackend/tst_qaudiodecoderbackend.cpp index 0ab050ada..5fb3a81a6 100644 --- a/tests/auto/integration/qaudiodecoderbackend/tst_qaudiodecoderbackend.cpp +++ b/tests/auto/integration/qaudiodecoderbackend/tst_qaudiodecoderbackend.cpp @@ -167,7 +167,6 @@ void tst_QAudioDecoderBackend::indirectReadingByBufferReadySignal() connect(&decoder, &QAudioDecoder::bufferReady, this, [&]() { QVERIFY(decoder.bufferAvailable()); - QVERIFY(decoder.isDecoding()); auto buffer = decoder.read(); QVERIFY(buffer.isValid()); @@ -210,8 +209,6 @@ void tst_QAudioDecoderBackend::indirectReadingByBufferAvailableSignal() { if (!available) return; - QVERIFY(decoder.isDecoding()); - while (decoder.bufferAvailable()) { auto buffer = decoder.read(); QVERIFY(buffer.isValid()); @@ -371,13 +368,13 @@ void tst_QAudioDecoderBackend::fileTest() QVERIFY(!d.bufferAvailable()); QCOMPARE(d.source(), *m_wavFile); - QSignalSpy readySpy(&d, SIGNAL(bufferReady())); - QSignalSpy bufferChangedSpy(&d, SIGNAL(bufferAvailableChanged(bool))); + QSignalSpy readySpy(&d, &QAudioDecoder::bufferReady); + QSignalSpy bufferChangedSpy(&d, &QAudioDecoder::bufferAvailableChanged); QSignalSpy errorSpy(&d, SIGNAL(error(QAudioDecoder::Error))); - QSignalSpy isDecodingSpy(&d, SIGNAL(isDecodingChanged(bool))); - QSignalSpy durationSpy(&d, SIGNAL(durationChanged(qint64))); - QSignalSpy finishedSpy(&d, SIGNAL(finished())); - QSignalSpy positionSpy(&d, SIGNAL(positionChanged(qint64))); + QSignalSpy isDecodingSpy(&d, &QAudioDecoder::isDecodingChanged); + QSignalSpy durationSpy(&d, &QAudioDecoder::durationChanged); + QSignalSpy finishedSpy(&d, &QAudioDecoder::finished); + QSignalSpy positionSpy(&d, &QAudioDecoder::positionChanged); d.start(); @@ -507,8 +504,8 @@ void tst_QAudioDecoderBackend::fileTest() buffer = d.read(); QVERIFY(buffer.isValid()); QTRY_VERIFY(!positionSpy.isEmpty()); - QCOMPARE(positionSpy.takeLast().at(0).toLongLong(), qint64(duration / 1000)); - QVERIFY(d.position() - (duration / 1000) < 20); + QCOMPARE(positionSpy.takeLast().at(0).toLongLong(), qlonglong(duration / 1000)); + QCOMPARE_LT(d.position() - (duration / 1000), 20u); duration += buffer.duration(); sampleCount += buffer.sampleCount(); @@ -521,10 +518,10 @@ void tst_QAudioDecoderBackend::fileTest() // Resampling might end up with fewer or more samples // so be a bit sloppy - QVERIFY(qAbs(sampleCount - 22047) < 100); - QVERIFY(qAbs(byteCount - 22047) < 100); - QVERIFY(qAbs(qint64(duration) - 1000000) < 20000); - QVERIFY(qAbs((d.position() + (buffer.duration() / 1000)) - 1000) < 20); + QCOMPARE_LT(qAbs(sampleCount - 22047), 100); + QCOMPARE_LT(qAbs(byteCount - 22047), 100); + QCOMPARE_LT(qAbs(qint64(duration) - 1000000), 20000); + QCOMPARE_LT(qAbs((d.position() + (buffer.duration() / 1000)) - 1000), 20); QTRY_COMPARE(finishedSpy.size(), 1); QVERIFY(!d.bufferAvailable()); QVERIFY(!d.isDecoding()); @@ -558,13 +555,13 @@ void tst_QAudioDecoderBackend::unsupportedFileTest() QVERIFY(!d.bufferAvailable()); QCOMPARE(d.source(), url); - QSignalSpy readySpy(&d, SIGNAL(bufferReady())); - QSignalSpy bufferChangedSpy(&d, SIGNAL(bufferAvailableChanged(bool))); + QSignalSpy readySpy(&d, &QAudioDecoder::bufferReady); + QSignalSpy bufferChangedSpy(&d, &QAudioDecoder::bufferAvailableChanged); QSignalSpy errorSpy(&d, SIGNAL(error(QAudioDecoder::Error))); - QSignalSpy isDecodingSpy(&d, SIGNAL(isDecodingChanged(bool))); - QSignalSpy durationSpy(&d, SIGNAL(durationChanged(qint64))); - QSignalSpy finishedSpy(&d, SIGNAL(finished())); - QSignalSpy positionSpy(&d, SIGNAL(positionChanged(qint64))); + QSignalSpy isDecodingSpy(&d, &QAudioDecoder::isDecodingChanged); + QSignalSpy durationSpy(&d, &QAudioDecoder::durationChanged); + QSignalSpy finishedSpy(&d, &QAudioDecoder::finished); + QSignalSpy positionSpy(&d, &QAudioDecoder::positionChanged); d.start(); QTRY_VERIFY(!d.isDecoding()); @@ -637,13 +634,13 @@ void tst_QAudioDecoderBackend::corruptedFileTest() QVERIFY(!d.bufferAvailable()); QCOMPARE(d.source(), url); - QSignalSpy readySpy(&d, SIGNAL(bufferReady())); - QSignalSpy bufferChangedSpy(&d, SIGNAL(bufferAvailableChanged(bool))); + QSignalSpy readySpy(&d, &QAudioDecoder::bufferReady); + QSignalSpy bufferChangedSpy(&d, &QAudioDecoder::bufferAvailableChanged); QSignalSpy errorSpy(&d, SIGNAL(error(QAudioDecoder::Error))); - QSignalSpy isDecodingSpy(&d, SIGNAL(isDecodingChanged(bool))); - QSignalSpy durationSpy(&d, SIGNAL(durationChanged(qint64))); - QSignalSpy finishedSpy(&d, SIGNAL(finished())); - QSignalSpy positionSpy(&d, SIGNAL(positionChanged(qint64))); + QSignalSpy isDecodingSpy(&d, &QAudioDecoder::isDecodingChanged); + QSignalSpy durationSpy(&d, &QAudioDecoder::durationChanged); + QSignalSpy finishedSpy(&d, &QAudioDecoder::finished); + QSignalSpy positionSpy(&d, &QAudioDecoder::positionChanged); d.start(); QTRY_VERIFY(!d.isDecoding()); @@ -711,13 +708,13 @@ void tst_QAudioDecoderBackend::invalidSource() QVERIFY(!d.bufferAvailable()); QCOMPARE(d.source(), url); - QSignalSpy readySpy(&d, SIGNAL(bufferReady())); - QSignalSpy bufferChangedSpy(&d, SIGNAL(bufferAvailableChanged(bool))); + QSignalSpy readySpy(&d, &QAudioDecoder::bufferReady); + QSignalSpy bufferChangedSpy(&d, &QAudioDecoder::bufferAvailableChanged); QSignalSpy errorSpy(&d, SIGNAL(error(QAudioDecoder::Error))); - QSignalSpy isDecodingSpy(&d, SIGNAL(isDecodingChanged(bool))); - QSignalSpy durationSpy(&d, SIGNAL(durationChanged(qint64))); - QSignalSpy finishedSpy(&d, SIGNAL(finished())); - QSignalSpy positionSpy(&d, SIGNAL(positionChanged(qint64))); + QSignalSpy isDecodingSpy(&d, &QAudioDecoder::isDecodingChanged); + QSignalSpy durationSpy(&d, &QAudioDecoder::durationChanged); + QSignalSpy finishedSpy(&d, &QAudioDecoder::finished); + QSignalSpy positionSpy(&d, &QAudioDecoder::positionChanged); d.start(); QTRY_VERIFY(!d.isDecoding()); @@ -793,13 +790,13 @@ void tst_QAudioDecoderBackend::deviceTest() quint64 duration = 0; int sampleCount = 0; - QSignalSpy readySpy(&d, SIGNAL(bufferReady())); - QSignalSpy bufferChangedSpy(&d, SIGNAL(bufferAvailableChanged(bool))); + QSignalSpy readySpy(&d, &QAudioDecoder::bufferReady); + QSignalSpy bufferChangedSpy(&d, &QAudioDecoder::bufferAvailableChanged); QSignalSpy errorSpy(&d, SIGNAL(error(QAudioDecoder::Error))); - QSignalSpy isDecodingSpy(&d, SIGNAL(isDecodingChanged(bool))); - QSignalSpy durationSpy(&d, SIGNAL(durationChanged(qint64))); - QSignalSpy finishedSpy(&d, SIGNAL(finished())); - QSignalSpy positionSpy(&d, SIGNAL(positionChanged(qint64))); + QSignalSpy isDecodingSpy(&d, &QAudioDecoder::isDecodingChanged); + QSignalSpy durationSpy(&d, &QAudioDecoder::durationChanged); + QSignalSpy finishedSpy(&d, &QAudioDecoder::finished); + QSignalSpy positionSpy(&d, &QAudioDecoder::positionChanged); QVERIFY(!d.isDecoding()); QVERIFY(d.bufferAvailable() == false); diff --git a/tests/auto/integration/qaudiosink/tst_qaudiosink.cpp b/tests/auto/integration/qaudiosink/tst_qaudiosink.cpp index d86044a3d..6fdfe8221 100644 --- a/tests/auto/integration/qaudiosink/tst_qaudiosink.cpp +++ b/tests/auto/integration/qaudiosink/tst_qaudiosink.cpp @@ -398,7 +398,7 @@ void tst_QAudioSink::stopWhileStopped() QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()"); QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()"); - QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State))); + QSignalSpy stateSignal(&audioOutput, &QAudioSink::stateChanged); audioOutput.stop(); // Check that no state transition occurred @@ -418,7 +418,7 @@ void tst_QAudioSink::suspendWhileStopped() QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()"); QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()"); - QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State))); + QSignalSpy stateSignal(&audioOutput, &QAudioSink::stateChanged); audioOutput.suspend(); // Check that no state transition occurred @@ -438,7 +438,7 @@ void tst_QAudioSink::resumeWhileStopped() QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()"); QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()"); - QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State))); + QSignalSpy stateSignal(&audioOutput, &QAudioSink::stateChanged); audioOutput.resume(); // Check that no state transition occurred @@ -455,7 +455,7 @@ void tst_QAudioSink::pull() audioOutput.setVolume(0.1f); - QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State))); + QSignalSpy stateSignal(&audioOutput, &QAudioSink::stateChanged); // Check that we are in the default state before calling start QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()"); @@ -514,7 +514,7 @@ void tst_QAudioSink::pullSuspendResume() audioOutput.setVolume(0.1f); - QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State))); + QSignalSpy stateSignal(&audioOutput, &QAudioSink::stateChanged); // Check that we are in the default state before calling start QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()"); @@ -627,7 +627,7 @@ void tst_QAudioSink::pullResumeFromUnderrun() AudioPullSource audioSource; QAudioSink audioOutput(format, this); - QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State))); + QSignalSpy stateSignal(&audioOutput, &QAudioSink::stateChanged); audioSource.open(QIODeviceBase::ReadOnly); audioSource.available = chunkSize; @@ -673,7 +673,7 @@ void tst_QAudioSink::push() audioOutput.setVolume(0.1f); - QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State))); + QSignalSpy stateSignal(&audioOutput, &QAudioSink::stateChanged); // Check that we are in the default state before calling start QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()"); @@ -754,7 +754,7 @@ void tst_QAudioSink::pushSuspendResume() audioOutput.setVolume(0.1f); - QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State))); + QSignalSpy stateSignal(&audioOutput, &QAudioSink::stateChanged); // Check that we are in the default state before calling start QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()"); @@ -922,7 +922,7 @@ void tst_QAudioSink::pushUnderrun() audioOutput.setVolume(0.1f); - QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State))); + QSignalSpy stateSignal(&audioOutput, &QAudioSink::stateChanged); // Check that we are in the default state before calling start QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()"); diff --git a/tests/auto/integration/qaudiosource/tst_qaudiosource.cpp b/tests/auto/integration/qaudiosource/tst_qaudiosource.cpp index 3bf57e78b..ae100a08b 100644 --- a/tests/auto/integration/qaudiosource/tst_qaudiosource.cpp +++ b/tests/auto/integration/qaudiosource/tst_qaudiosource.cpp @@ -292,7 +292,7 @@ void tst_QAudioSource::stopWhileStopped() QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()"); QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()"); - QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State))); + QSignalSpy stateSignal(&audioInput, &QAudioSource::stateChanged); audioInput.stop(); // Check that no state transition occurred @@ -312,7 +312,7 @@ void tst_QAudioSource::suspendWhileStopped() QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()"); QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()"); - QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State))); + QSignalSpy stateSignal(&audioInput, &QAudioSource::stateChanged); audioInput.suspend(); // Check that no state transition occurred @@ -332,7 +332,7 @@ void tst_QAudioSource::resumeWhileStopped() QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()"); QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()"); - QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State))); + QSignalSpy stateSignal(&audioInput, &QAudioSource::stateChanged); audioInput.resume(); // Check that no state transition occurred @@ -347,7 +347,7 @@ void tst_QAudioSource::pull() QAudioSource audioInput(audioFormat, this); - QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State))); + QSignalSpy stateSignal(&audioInput, &QAudioSource::stateChanged); // Check that we are in the default state before calling start QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()"); @@ -416,7 +416,7 @@ void tst_QAudioSource::pullSuspendResume() QAudioSource audioInput(audioFormat, this); - QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State))); + QSignalSpy stateSignal(&audioInput, &QAudioSource::stateChanged); // Check that we are in the default state before calling start QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()"); @@ -518,7 +518,7 @@ void tst_QAudioSource::push() QAudioSource audioInput(audioFormat, this); - QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State))); + QSignalSpy stateSignal(&audioInput, &QAudioSource::stateChanged); // Check that we are in the default state before calling start QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()"); @@ -609,7 +609,7 @@ void tst_QAudioSource::pushSuspendResume() audioInput.setBufferSize(audioFormat.bytesForDuration(100000)); - QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State))); + QSignalSpy stateSignal(&audioInput, &QAudioSource::stateChanged); // Check that we are in the default state before calling start QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()"); @@ -735,7 +735,7 @@ void tst_QAudioSource::reset() { QAudioSource audioInput(audioFormat, this); - QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State))); + QSignalSpy stateSignal(&audioInput, &QAudioSource::stateChanged); // Check that we are in the default state before calling start QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()"); @@ -765,7 +765,7 @@ void tst_QAudioSource::reset() QBuffer buffer; buffer.open(QIODevice::WriteOnly); - QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State))); + QSignalSpy stateSignal(&audioInput, &QAudioSource::stateChanged); // Check that we are in the default state before calling start QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()"); diff --git a/tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp b/tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp index 43e1a9a31..fd6d819eb 100644 --- a/tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp +++ b/tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp @@ -216,8 +216,8 @@ void tst_QCameraBackend::testCameraActive() session.setCamera(&camera); session.setImageCapture(&imageCapture); - QSignalSpy errorSignal(&camera, SIGNAL(errorOccurred(QCamera::Error, const QString &))); - QSignalSpy activeChangedSignal(&camera, SIGNAL(activeChanged(bool))); + QSignalSpy errorSignal(&camera, &QCamera::errorOccurred); + QSignalSpy activeChangedSignal(&camera, &QCamera::activeChanged); QCOMPARE(camera.isActive(), false); @@ -280,7 +280,7 @@ void tst_QCameraBackend::testCameraFormat() if (videoFormats.isEmpty()) QSKIP("No Camera available, skipping test."); QCameraFormat cameraFormat = videoFormats.first(); - QSignalSpy spy(&camera, SIGNAL(cameraFormatChanged())); + QSignalSpy spy(&camera, &QCamera::cameraFormatChanged); QVERIFY(spy.size() == 0); QMediaCaptureSession session; @@ -344,9 +344,9 @@ void tst_QCameraBackend::testCameraCapture() QVERIFY(!imageCapture.isReadyForCapture()); - QSignalSpy capturedSignal(&imageCapture, SIGNAL(imageCaptured(int,QImage))); - QSignalSpy savedSignal(&imageCapture, SIGNAL(imageSaved(int,QString))); - QSignalSpy errorSignal(&imageCapture, SIGNAL(errorOccurred(int,QImageCapture::Error,const QString&))); + QSignalSpy capturedSignal(&imageCapture, &QImageCapture::imageCaptured); + QSignalSpy savedSignal(&imageCapture, &QImageCapture::imageSaved); + QSignalSpy errorSignal(&imageCapture, &QImageCapture::errorOccurred); imageCapture.captureToFile(); QTRY_COMPARE(errorSignal.size(), 1); @@ -403,10 +403,10 @@ void tst_QCameraBackend::testCaptureToBuffer() QTRY_VERIFY(camera.isActive()); - QSignalSpy capturedSignal(&imageCapture, SIGNAL(imageCaptured(int,QImage))); - QSignalSpy imageAvailableSignal(&imageCapture, SIGNAL(imageAvailable(int,QVideoFrame))); - QSignalSpy savedSignal(&imageCapture, SIGNAL(imageSaved(int,QString))); - QSignalSpy errorSignal(&imageCapture, SIGNAL(errorOccurred(int,QImageCapture::Error,const QString&))); + QSignalSpy capturedSignal(&imageCapture, &QImageCapture::imageCaptured); + QSignalSpy imageAvailableSignal(&imageCapture, &QImageCapture::imageAvailable); + QSignalSpy savedSignal(&imageCapture, &QImageCapture::imageSaved); + QSignalSpy errorSignal(&imageCapture, &QImageCapture::errorOccurred); camera.start(); QTRY_VERIFY(imageCapture.isReadyForCapture()); @@ -448,8 +448,14 @@ void tst_QCameraBackend::testCameraCaptureMetadata() camera.setFlashMode(QCamera::FlashOff); - QSignalSpy metadataSignal(&imageCapture, SIGNAL(imageMetadataAvailable(int,const QMediaMetaData&))); - QSignalSpy savedSignal(&imageCapture, SIGNAL(imageSaved(int,QString))); + QMediaMetaData referenceMetaData; + referenceMetaData.insert(QMediaMetaData::Title, QStringLiteral("Title")); + referenceMetaData.insert(QMediaMetaData::Language, QVariant::fromValue(QLocale::German)); + referenceMetaData.insert(QMediaMetaData::Description, QStringLiteral("Description")); + imageCapture.setMetaData(referenceMetaData); + + QSignalSpy metadataSignal(&imageCapture, &QImageCapture::imageMetadataAvailable); + QSignalSpy savedSignal(&imageCapture, &QImageCapture::imageSaved); camera.start(); @@ -460,7 +466,19 @@ void tst_QCameraBackend::testCameraCaptureMetadata() int id = imageCapture.captureToFile(tmpFile); QTRY_VERIFY(!savedSignal.isEmpty()); QVERIFY(!metadataSignal.isEmpty()); + QCOMPARE(metadataSignal.first().first().toInt(), id); + QMediaMetaData receivedMetaData = metadataSignal.first()[1].value<QMediaMetaData>(); + + if (isGStreamerPlatform()) { + for (auto key : { + QMediaMetaData::Title, + QMediaMetaData::Language, + QMediaMetaData::Description, + }) + QCOMPARE(receivedMetaData[key], referenceMetaData[key]); + QVERIFY(receivedMetaData[QMediaMetaData::Resolution].isValid()); + } } void tst_QCameraBackend::testExposureCompensation() @@ -472,9 +490,9 @@ void tst_QCameraBackend::testExposureCompensation() QCamera camera; session.setCamera(&camera); - QSignalSpy exposureCompensationSignal(&camera, SIGNAL(exposureCompensationChanged(float))); + QSignalSpy exposureCompensationSignal(&camera, &QCamera::exposureCompensationChanged); - //it should be possible to set exposure parameters in Unloaded state + // it should be possible to set exposure parameters in Unloaded state QCOMPARE(camera.exposureCompensation(), 0.); if (!(camera.supportedFeatures() & QCamera::Feature::ExposureCompensation)) return; @@ -571,10 +589,10 @@ void tst_QCameraBackend::testVideoRecording() QMediaRecorder recorder; session.setRecorder(&recorder); - QSignalSpy errorSignal(camera.data(), SIGNAL(errorOccurred(QCamera::Error, const QString &))); - QSignalSpy recorderErrorSignal(&recorder, SIGNAL(errorOccurred(QMediaRecorder::Error, const QString &))); - QSignalSpy recorderStateChanged(&recorder, SIGNAL(recorderStateChanged(RecorderState))); - QSignalSpy durationChanged(&recorder, SIGNAL(durationChanged(qint64))); + QSignalSpy errorSignal(camera.data(), &QCamera::errorOccurred); + QSignalSpy recorderErrorSignal(&recorder, &QMediaRecorder::errorOccurred); + QSignalSpy recorderStateChanged(&recorder, &QMediaRecorder::recorderStateChanged); + QSignalSpy durationChanged(&recorder, &QMediaRecorder::durationChanged); recorder.setVideoResolution(320, 240); @@ -643,10 +661,10 @@ void tst_QCameraBackend::testNativeMetadata() QMediaRecorder recorder; session.setRecorder(&recorder); - QSignalSpy errorSignal(&camera, SIGNAL(errorOccurred(QCamera::Error, const QString &))); - QSignalSpy recorderErrorSignal(&recorder, SIGNAL(errorOccurred(Error, const QString &))); - QSignalSpy recorderStateChanged(&recorder, SIGNAL(recorderStateChanged(RecorderState))); - QSignalSpy durationChanged(&recorder, SIGNAL(durationChanged(qint64))); + QSignalSpy errorSignal(&camera, &QCamera::errorOccurred); + QSignalSpy recorderErrorSignal(&recorder, &QMediaRecorder::errorOccurred); + QSignalSpy recorderStateChanged(&recorder, &QMediaRecorder::recorderStateChanged); + QSignalSpy durationChanged(&recorder, &QMediaRecorder::durationChanged); camera.start(); if (device.isNull()) { @@ -687,8 +705,6 @@ void tst_QCameraBackend::testNativeMetadata() QVERIFY(!fileName.isEmpty()); QVERIFY(QFileInfo(fileName).size() > 0); - QSKIP_GSTREAMER("QTBUG-124182: spurious failure while retrieving the metadata"); - // QMediaRecorder::metaData() can only test that QMediaMetaData is set properly on the recorder. // Use QMediaPlayer to test that the native metadata is properly set on the track QAudioOutput output; @@ -700,15 +716,20 @@ void tst_QCameraBackend::testNativeMetadata() player.setSource(QUrl::fromLocalFile(fileName)); player.play(); - QTRY_VERIFY(metadataChangedSpy.size() > 0); + int metadataChangedRequiredCount = isGStreamerPlatform() ? 2 : 1; + + QTRY_VERIFY(metadataChangedSpy.size() >= metadataChangedRequiredCount); - QCOMPARE(player.metaData().value(QMediaMetaData::Title).toString(), metaData.value(QMediaMetaData::Title).toString()); + QCOMPARE(player.metaData().value(QMediaMetaData::Title).toString(), + metaData.value(QMediaMetaData::Title).toString()); auto lang = player.metaData().value(QMediaMetaData::Language).value<QLocale::Language>(); if (lang != QLocale::AnyLanguage) QCOMPARE(lang, metaData.value(QMediaMetaData::Language).value<QLocale::Language>()); QCOMPARE(player.metaData().value(QMediaMetaData::Description).toString(), metaData.value(QMediaMetaData::Description).toString()); + QVERIFY(player.metaData().value(QMediaMetaData::Resolution).isValid()); - metadataChangedSpy.clear(); + if (isGStreamerPlatform()) + QVERIFY(player.metaData().value(QMediaMetaData::Date).isValid()); player.stop(); player.setSource({}); diff --git a/tests/auto/integration/qmediacapturesession/tst_qmediacapturesession.cpp b/tests/auto/integration/qmediacapturesession/tst_qmediacapturesession.cpp index d22d0a3df..e8376d54c 100644 --- a/tests/auto/integration/qmediacapturesession/tst_qmediacapturesession.cpp +++ b/tests/auto/integration/qmediacapturesession/tst_qmediacapturesession.cpp @@ -104,8 +104,8 @@ void tst_QMediaCaptureSession::recordOk(QMediaCaptureSession &session) QMediaRecorder recorder; session.setRecorder(&recorder); - QSignalSpy recorderErrorSignal(&recorder, SIGNAL(errorOccurred(Error, const QString &))); - QSignalSpy durationChanged(&recorder, SIGNAL(durationChanged(qint64))); + QSignalSpy recorderErrorSignal(&recorder, &QMediaRecorder::errorOccurred); + QSignalSpy durationChanged(&recorder, &QMediaRecorder::durationChanged); recorder.record(); QTRY_VERIFY_WITH_TIMEOUT(recorder.recorderState() == QMediaRecorder::RecordingState, 2000); @@ -124,7 +124,7 @@ void tst_QMediaCaptureSession::recordOk(QMediaCaptureSession &session) void tst_QMediaCaptureSession::recordFail(QMediaCaptureSession &session) { QMediaRecorder recorder; - QSignalSpy recorderErrorSignal(&recorder, SIGNAL(errorOccurred(Error, const QString &))); + QSignalSpy recorderErrorSignal(&recorder, &QMediaRecorder::errorOccurred); session.setRecorder(&recorder); recorder.record(); @@ -292,7 +292,7 @@ void tst_QMediaCaptureSession::record_video_without_preview() session.setRecorder(&recorder); - QSignalSpy cameraChanged(&session, SIGNAL(cameraChanged())); + QSignalSpy cameraChanged(&session, &QMediaCaptureSession::cameraChanged); session.setCamera(&camera); camera.setActive(true); @@ -317,8 +317,8 @@ void tst_QMediaCaptureSession::can_add_and_remove_AudioInput_with_and_without_Au QSKIP("No audio input available"); QMediaCaptureSession session; - QSignalSpy audioInputChanged(&session, SIGNAL(audioInputChanged())); - QSignalSpy audioOutputChanged(&session, SIGNAL(audioOutputChanged())); + QSignalSpy audioInputChanged(&session, &QMediaCaptureSession::audioInputChanged); + QSignalSpy audioOutputChanged(&session, &QMediaCaptureSession::audioOutputChanged); session.setAudioInput(&input); QTRY_COMPARE(audioInputChanged.size(), 1); @@ -349,10 +349,10 @@ void tst_QMediaCaptureSession::can_change_AudioDevices_on_attached_AudioInput() QSKIP("Two audio inputs are not available"); QAudioInput input(audioInputs[0]); - QSignalSpy deviceChanged(&input, SIGNAL(deviceChanged())); + QSignalSpy deviceChanged(&input, &QAudioInput::deviceChanged); QMediaCaptureSession session; - QSignalSpy audioInputChanged(&session, SIGNAL(audioInputChanged())); + QSignalSpy audioInputChanged(&session, &QMediaCaptureSession::audioInputChanged); session.setAudioInput(&input); QTRY_COMPARE(audioInputChanged.size(), 1); @@ -384,9 +384,9 @@ void tst_QMediaCaptureSession::can_change_AudioInput_during_recording() session.setRecorder(&recorder); - QSignalSpy audioInputChanged(&session, SIGNAL(audioInputChanged())); - QSignalSpy recorderErrorSignal(&recorder, SIGNAL(errorOccurred(Error, const QString &))); - QSignalSpy durationChanged(&recorder, SIGNAL(durationChanged(qint64))); + QSignalSpy audioInputChanged(&session, &QMediaCaptureSession::audioInputChanged); + QSignalSpy recorderErrorSignal(&recorder, &QMediaRecorder::errorOccurred); + QSignalSpy durationChanged(&recorder, &QMediaRecorder::durationChanged); session.setAudioInput(&input); QTRY_COMPARE(audioInputChanged.size(), 1); @@ -418,7 +418,7 @@ void tst_QMediaCaptureSession::disconnects_deleted_AudioInput() QSKIP("No audio input available"); QMediaCaptureSession session; - QSignalSpy audioInputChanged(&session, SIGNAL(audioInputChanged())); + QSignalSpy audioInputChanged(&session, &QMediaCaptureSession::audioInputChanged); { QAudioInput input; session.setAudioInput(&input); @@ -435,13 +435,13 @@ void tst_QMediaCaptureSession::can_move_AudioInput_between_sessions() QMediaCaptureSession session0; QMediaCaptureSession session1; - QSignalSpy audioInputChanged0(&session0, SIGNAL(audioInputChanged())); - QSignalSpy audioInputChanged1(&session1, SIGNAL(audioInputChanged())); + QSignalSpy audioInputChanged0(&session0, &QMediaCaptureSession::audioInputChanged); + QSignalSpy audioInputChanged1(&session1, &QMediaCaptureSession::audioInputChanged); QAudioInput input; { QMediaCaptureSession session2; - QSignalSpy audioInputChanged2(&session2, SIGNAL(audioInputChanged())); + QSignalSpy audioInputChanged2(&session2, &QMediaCaptureSession::audioInputChanged); session2.setAudioInput(&input); QTRY_COMPARE(audioInputChanged2.size(), 1); } @@ -462,7 +462,7 @@ void tst_QMediaCaptureSession::disconnects_deleted_AudioOutput() QSKIP("No audio output available"); QMediaCaptureSession session; - QSignalSpy audioOutputChanged(&session, SIGNAL(audioOutputChanged())); + QSignalSpy audioOutputChanged(&session, &QMediaCaptureSession::audioOutputChanged); { QAudioOutput output; session.setAudioOutput(&output); @@ -482,13 +482,13 @@ void tst_QMediaCaptureSession::can_move_AudioOutput_between_sessions_and_player( QMediaCaptureSession session0; QMediaCaptureSession session1; QMediaPlayer player; - QSignalSpy audioOutputChanged0(&session0, SIGNAL(audioOutputChanged())); - QSignalSpy audioOutputChanged1(&session1, SIGNAL(audioOutputChanged())); - QSignalSpy audioOutputChangedPlayer(&player, SIGNAL(audioOutputChanged())); + QSignalSpy audioOutputChanged0(&session0, &QMediaCaptureSession::audioOutputChanged); + QSignalSpy audioOutputChanged1(&session1, &QMediaCaptureSession::audioOutputChanged); + QSignalSpy audioOutputChangedPlayer(&player, &QMediaPlayer::audioOutputChanged); { QMediaCaptureSession session2; - QSignalSpy audioOutputChanged2(&session2, SIGNAL(audioOutputChanged())); + QSignalSpy audioOutputChanged2(&session2, &QMediaCaptureSession::audioOutputChanged); session2.setAudioOutput(&output); QTRY_COMPARE(audioOutputChanged2.size(), 1); } @@ -531,7 +531,7 @@ void tst_QMediaCaptureSession::can_add_and_remove_Camera() session.setRecorder(&recorder); - QSignalSpy cameraChanged(&session, SIGNAL(cameraChanged())); + QSignalSpy cameraChanged(&session, &QMediaCaptureSession::cameraChanged); session.setCamera(&camera); camera.setActive(true); @@ -552,13 +552,13 @@ void tst_QMediaCaptureSession::can_move_Camera_between_sessions() { QMediaCaptureSession session0; QMediaCaptureSession session1; - QSignalSpy cameraChanged0(&session0, SIGNAL(cameraChanged())); - QSignalSpy cameraChanged1(&session1, SIGNAL(cameraChanged())); + QSignalSpy cameraChanged0(&session0, &QMediaCaptureSession::cameraChanged); + QSignalSpy cameraChanged1(&session1, &QMediaCaptureSession::cameraChanged); { QCamera camera; { QMediaCaptureSession session2; - QSignalSpy cameraChanged2(&session2, SIGNAL(cameraChanged())); + QSignalSpy cameraChanged2(&session2, &QMediaCaptureSession::cameraChanged); session2.setCamera(&camera); QTRY_COMPARE(cameraChanged2.size(), 1); } @@ -592,9 +592,9 @@ void tst_QMediaCaptureSession::can_disconnect_Camera_when_recording() session.setRecorder(&recorder); - QSignalSpy cameraChanged(&session, SIGNAL(cameraChanged())); - QSignalSpy recorderErrorSignal(&recorder, SIGNAL(errorOccurred(Error, const QString &))); - QSignalSpy durationChanged(&recorder, SIGNAL(durationChanged(qint64))); + QSignalSpy cameraChanged(&session, &QMediaCaptureSession::cameraChanged); + QSignalSpy recorderErrorSignal(&recorder, &QMediaRecorder::errorOccurred); + QSignalSpy durationChanged(&recorder, &QMediaRecorder::durationChanged); camera.setActive(true); session.setCamera(&camera); @@ -634,7 +634,7 @@ void tst_QMediaCaptureSession::can_add_and_remove_different_Cameras() session.setRecorder(&recorder); - QSignalSpy cameraChanged(&session, SIGNAL(cameraChanged())); + QSignalSpy cameraChanged(&session, &QMediaCaptureSession::cameraChanged); camera.setActive(true); session.setCamera(&camera); @@ -667,8 +667,8 @@ void tst_QMediaCaptureSession::can_change_CameraDevice_on_attached_Camera() session.setRecorder(&recorder); - QSignalSpy cameraDeviceChanged(&camera, SIGNAL(cameraDeviceChanged())); - QSignalSpy cameraChanged(&session, SIGNAL(cameraChanged())); + QSignalSpy cameraDeviceChanged(&camera, &QCamera::cameraDeviceChanged); + QSignalSpy cameraChanged(&session, &QMediaCaptureSession::cameraChanged); session.setCamera(&camera); QTRY_COMPARE(cameraChanged.size(), 1); @@ -704,8 +704,8 @@ void tst_QMediaCaptureSession::can_change_VideoOutput_with_and_without_camera() QMediaCaptureSession session; - QSignalSpy videoOutputChanged(&session, SIGNAL(videoOutputChanged())); - QSignalSpy cameraChanged(&session, SIGNAL(cameraChanged())); + QSignalSpy videoOutputChanged(&session, &QMediaCaptureSession::videoOutputChanged); + QSignalSpy cameraChanged(&session, &QMediaCaptureSession::cameraChanged); session.setCamera(&camera); QTRY_COMPARE(cameraChanged.size(), 1); @@ -740,10 +740,10 @@ void tst_QMediaCaptureSession::can_change_VideoOutput_when_recording() session.setRecorder(&recorder); - QSignalSpy cameraChanged(&session, SIGNAL(cameraChanged())); - QSignalSpy recorderErrorSignal(&recorder, SIGNAL(errorOccurred(Error, const QString &))); - QSignalSpy durationChanged(&recorder, SIGNAL(durationChanged(qint64))); - QSignalSpy videoOutputChanged(&session, SIGNAL(videoOutputChanged())); + QSignalSpy cameraChanged(&session, &QMediaCaptureSession::cameraChanged); + QSignalSpy recorderErrorSignal(&recorder, &QMediaRecorder::errorOccurred); + QSignalSpy durationChanged(&recorder, &QMediaRecorder::durationChanged); + QSignalSpy videoOutputChanged(&session, &QMediaCaptureSession::videoOutputChanged); camera.setActive(true); session.setCamera(&camera); @@ -783,8 +783,8 @@ void tst_QMediaCaptureSession::can_add_and_remove_recorders() QMediaRecorder recorder2; QMediaCaptureSession session; - QSignalSpy audioInputChanged(&session, SIGNAL(audioInputChanged())); - QSignalSpy recorderChanged(&session, SIGNAL(recorderChanged())); + QSignalSpy audioInputChanged(&session, &QMediaCaptureSession::audioInputChanged); + QSignalSpy recorderChanged(&session, &QMediaCaptureSession::recorderChanged); session.setAudioInput(&input); QTRY_COMPARE(audioInputChanged.size(), 1); @@ -806,13 +806,13 @@ void tst_QMediaCaptureSession::can_move_Recorder_between_sessions() { QMediaCaptureSession session0; QMediaCaptureSession session1; - QSignalSpy recorderChanged0(&session0, SIGNAL(recorderChanged())); - QSignalSpy recorderChanged1(&session1, SIGNAL(recorderChanged())); + QSignalSpy recorderChanged0(&session0, &QMediaCaptureSession::recorderChanged); + QSignalSpy recorderChanged1(&session1, &QMediaCaptureSession::recorderChanged); { QMediaRecorder recorder; { QMediaCaptureSession session2; - QSignalSpy recorderChanged2(&session2, SIGNAL(recorderChanged())); + QSignalSpy recorderChanged2(&session2, &QMediaCaptureSession::recorderChanged); session2.setRecorder(&recorder); QTRY_COMPARE(recorderChanged2.size(), 1); } @@ -849,7 +849,7 @@ void tst_QMediaCaptureSession::can_record_AudioInput_with_null_AudioDevice() QAudioInput input(nullDevice); QMediaCaptureSession session; - QSignalSpy audioInputChanged(&session, SIGNAL(audioInputChanged())); + QSignalSpy audioInputChanged(&session, &QMediaCaptureSession::audioInputChanged); session.setAudioInput(&input); QTRY_COMPARE(audioInputChanged.size(), 1); @@ -867,7 +867,7 @@ void tst_QMediaCaptureSession::can_record_Camera_with_null_CameraDevice() QCamera camera(nullDevice); QMediaCaptureSession session; - QSignalSpy cameraChanged(&session, SIGNAL(cameraChanged())); + QSignalSpy cameraChanged(&session, &QMediaCaptureSession::cameraChanged); session.setCamera(&camera); QTRY_COMPARE(cameraChanged.size(), 1); @@ -888,10 +888,10 @@ void tst_QMediaCaptureSession::recording_stops_when_recorder_removed() QMediaRecorder recorder; QMediaCaptureSession session; - QSignalSpy audioInputChanged(&session, SIGNAL(audioInputChanged())); - QSignalSpy recorderChanged(&session, SIGNAL(recorderChanged())); - QSignalSpy recorderErrorSignal(&recorder, SIGNAL(errorOccurred(Error, const QString &))); - QSignalSpy durationChanged(&recorder, SIGNAL(durationChanged(qint64))); + QSignalSpy audioInputChanged(&session, &QMediaCaptureSession::audioInputChanged); + QSignalSpy recorderChanged(&session, &QMediaCaptureSession::recorderChanged); + QSignalSpy recorderErrorSignal(&recorder, &QMediaRecorder::errorOccurred); + QSignalSpy durationChanged(&recorder, &QMediaRecorder::durationChanged); session.setAudioInput(&input); QTRY_COMPARE(audioInputChanged.size(), 1); @@ -925,9 +925,9 @@ void tst_QMediaCaptureSession::can_add_and_remove_ImageCapture() QImageCapture capture; QMediaCaptureSession session; - QSignalSpy cameraChanged(&session, SIGNAL(cameraChanged())); - QSignalSpy imageCaptureChanged(&session, SIGNAL(imageCaptureChanged())); - QSignalSpy readyForCaptureChanged(&capture, SIGNAL(readyForCaptureChanged(bool))); + QSignalSpy cameraChanged(&session, &QMediaCaptureSession::cameraChanged); + QSignalSpy imageCaptureChanged(&session, &QMediaCaptureSession::imageCaptureChanged); + QSignalSpy readyForCaptureChanged(&capture, &QImageCapture::readyForCaptureChanged); QVERIFY(!capture.isAvailable()); QVERIFY(!capture.isReadyForCapture()); @@ -967,13 +967,13 @@ void tst_QMediaCaptureSession::can_move_ImageCapture_between_sessions() { QMediaCaptureSession session0; QMediaCaptureSession session1; - QSignalSpy imageCaptureChanged0(&session0, SIGNAL(imageCaptureChanged())); - QSignalSpy imageCaptureChanged1(&session1, SIGNAL(imageCaptureChanged())); + QSignalSpy imageCaptureChanged0(&session0, &QMediaCaptureSession::imageCaptureChanged); + QSignalSpy imageCaptureChanged1(&session1, &QMediaCaptureSession::imageCaptureChanged); { QImageCapture imageCapture; { QMediaCaptureSession session2; - QSignalSpy imageCaptureChanged2(&session2, SIGNAL(imageCaptureChanged())); + QSignalSpy imageCaptureChanged2(&session2, &QMediaCaptureSession::imageCaptureChanged); session2.setImageCapture(&imageCapture); QTRY_COMPARE(imageCaptureChanged2.size(), 1); } @@ -1006,9 +1006,9 @@ void tst_QMediaCaptureSession::capture_is_not_available_when_Camera_is_null() QImageCapture capture; QMediaCaptureSession session; - QSignalSpy cameraChanged(&session, SIGNAL(cameraChanged())); - QSignalSpy capturedSignal(&capture, SIGNAL(imageCaptured(int,QImage))); - QSignalSpy readyForCaptureChanged(&capture, SIGNAL(readyForCaptureChanged(bool))); + QSignalSpy cameraChanged(&session, &QMediaCaptureSession::cameraChanged); + QSignalSpy capturedSignal(&capture, &QImageCapture::imageCaptured); + QSignalSpy readyForCaptureChanged(&capture, &QImageCapture::readyForCaptureChanged); session.setImageCapture(&capture); session.setCamera(&camera); @@ -1043,12 +1043,12 @@ void tst_QMediaCaptureSession::can_add_ImageCapture_and_capture_during_recording QMediaCaptureSession session; QMediaRecorder recorder; - QSignalSpy recorderChanged(&session, SIGNAL(recorderChanged())); - QSignalSpy recorderErrorSignal(&recorder, SIGNAL(errorOccurred(Error, const QString &))); - QSignalSpy durationChanged(&recorder, SIGNAL(durationChanged(qint64))); - QSignalSpy imageCaptureChanged(&session, SIGNAL(imageCaptureChanged())); - QSignalSpy readyForCaptureChanged(&capture, SIGNAL(readyForCaptureChanged(bool))); - QSignalSpy capturedSignal(&capture, SIGNAL(imageCaptured(int,QImage))); + QSignalSpy recorderChanged(&session, &QMediaCaptureSession::recorderChanged); + QSignalSpy recorderErrorSignal(&recorder, &QMediaRecorder::errorOccurred); + QSignalSpy durationChanged(&recorder, &QMediaRecorder::durationChanged); + QSignalSpy imageCaptureChanged(&session, &QMediaCaptureSession::imageCaptureChanged); + QSignalSpy readyForCaptureChanged(&capture, &QImageCapture::readyForCaptureChanged); + QSignalSpy capturedSignal(&capture, &QImageCapture::imageCaptured); session.setCamera(&camera); camera.setActive(true); @@ -1100,7 +1100,7 @@ void tst_QMediaCaptureSession::testAudioMute() recorder.setOutputLocation(QStringLiteral("test")); QSignalSpy spy(&audioInput, &QAudioInput::mutedChanged); - QSignalSpy durationChanged(&recorder, SIGNAL(durationChanged(qint64))); + QSignalSpy durationChanged(&recorder, &QMediaRecorder::durationChanged); QMediaFormat format; format.setAudioCodec(QMediaFormat::AudioCodec::Wave); diff --git a/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp b/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp index 5ea590522..7f1af8bde 100644 --- a/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp +++ b/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp @@ -13,7 +13,9 @@ #include <qvideosink.h> #include <qvideoframe.h> #include <qaudiooutput.h> +#if QT_CONFIG(process) #include <qprocess.h> +#endif #include <private/qglobal_p.h> #ifdef QT_FEATURE_network #include <qtcpserver.h> @@ -182,7 +184,9 @@ private: QUrl selectVideoFile(const QStringList &mediaCandidates); bool canCreateRtspStream() const; +#if QT_CONFIG(process) std::unique_ptr<QProcess> createRtspStreamProcess(QString fileName, QString outputUrl); +#endif void detectVlcCommand(); // one second local wav file @@ -1078,6 +1082,9 @@ void tst_QMediaPlayerBackend::playAndSetSource_emitsExpectedSignalsAndStopsPlayb void tst_QMediaPlayerBackend:: play_createsFramesWithExpectedContentAndIncreasingFrameTime_whenPlayingRtspMediaStream() { +#if !QT_CONFIG(process) + QSKIP("This test requires QProcess support"); +#else if (!canCreateRtspStream()) QSKIP("Rtsp stream cannot be created"); @@ -1138,6 +1145,7 @@ void tst_QMediaPlayerBackend:: QCOMPARE(player.playbackState(), QMediaPlayer::StoppedState); QCOMPARE(errorSpy.size(), 0); +#endif //QT_CONFIG(process) } void tst_QMediaPlayerBackend::play_waitsForLastFrameEnd_whenPlayingVideoWithLongFrames() @@ -1173,8 +1181,6 @@ void tst_QMediaPlayerBackend::play_waitsForLastFrameEnd_whenPlayingVideoWithLong void tst_QMediaPlayerBackend::play_startsPlayback_withAndWithoutOutputsConnected() { - QSKIP_GSTREAMER("QTBUG-124501: Fails with gstreamer"); - QFETCH(const bool, audioConnected); QFETCH(const bool, videoConnected); @@ -1542,9 +1548,9 @@ void tst_QMediaPlayerBackend::deleteLaterAtEOS() // QTRY_VERIFY or QTest::qWait. QTest::qWait makes extra effort to process // DeferredDelete events during the wait, which interferes with this test. QEventLoop loop; - QTimer::singleShot(0, &deleter, SLOT(play())); - QTimer::singleShot(5000, &loop, SLOT(quit())); - connect(player.data(), SIGNAL(destroyed()), &loop, SLOT(quit())); + QTimer::singleShot(0, &deleter, &DeleteLaterAtEos::play); + QTimer::singleShot(5000, &loop, &QEventLoop::quit); + connect(player.data(), &QObject::destroyed, &loop, &QEventLoop::quit); loop.exec(); // Verify that the player was destroyed within the event loop. // This check will fail without the fix for QTBUG-24927. @@ -1651,7 +1657,7 @@ void tst_QMediaPlayerBackend::seekPauseSeek() player.setAudioOutput(&output); - QSignalSpy positionSpy(&player, SIGNAL(positionChanged(qint64))); + QSignalSpy positionSpy(&player, &QMediaPlayer::positionChanged); player.setVideoOutput(&surface); @@ -1727,8 +1733,8 @@ void tst_QMediaPlayerBackend::seekInStoppedState() player.setAudioOutput(&output); player.setVideoOutput(&surface); - QSignalSpy stateSpy(&player, SIGNAL(playbackStateChanged(QMediaPlayer::PlaybackState))); - QSignalSpy positionSpy(&player, SIGNAL(positionChanged(qint64))); + QSignalSpy stateSpy(&player, &QMediaPlayer::playbackStateChanged); + QSignalSpy positionSpy(&player, &QMediaPlayer::positionChanged); player.setSource(*m_localVideoFile); QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia); @@ -2039,7 +2045,7 @@ void tst_QMediaPlayerBackend::multipleSeekStressTest() }; auto seekAndCheck = [&](qint64 pos) { - QSignalSpy positionSpy(&player, SIGNAL(positionChanged(qint64))); + QSignalSpy positionSpy(&player, &QMediaPlayer::positionChanged); player.setPosition(pos); QTRY_VERIFY(positionSpy.size() >= 1); @@ -2323,8 +2329,8 @@ void tst_QMediaPlayerBackend::audioVideoAvailable() TestVideoSink surface(false); QAudioOutput output; QMediaPlayer player; - QSignalSpy hasVideoSpy(&player, SIGNAL(hasVideoChanged(bool))); - QSignalSpy hasAudioSpy(&player, SIGNAL(hasAudioChanged(bool))); + QSignalSpy hasVideoSpy(&player, &QMediaPlayer::hasVideoChanged); + QSignalSpy hasAudioSpy(&player, &QMediaPlayer::hasAudioChanged); player.setVideoOutput(&surface); player.setAudioOutput(&output); player.setSource(*m_localVideoFile); @@ -2971,6 +2977,7 @@ void tst_QMediaPlayerBackend::setMedia_setsVideoSinkSize_beforePlaying() QCOMPARE(spy2.size(), 1); } +#if QT_CONFIG(process) std::unique_ptr<QProcess> tst_QMediaPlayerBackend::createRtspStreamProcess(QString fileName, QString outputUrl) { @@ -3000,6 +3007,7 @@ std::unique_ptr<QProcess> tst_QMediaPlayerBackend::createRtspStreamProcess(QStri return process; } +#endif //QT_CONFIG(process) void tst_QMediaPlayerBackend::play_playsRotatedVideoOutput_whenVideoFileHasOrientationMetadata_data() { diff --git a/tests/auto/integration/qsoundeffect/tst_qsoundeffect.cpp b/tests/auto/integration/qsoundeffect/tst_qsoundeffect.cpp index 6b2871a38..24a6365a8 100644 --- a/tests/auto/integration/qsoundeffect/tst_qsoundeffect.cpp +++ b/tests/auto/integration/qsoundeffect/tst_qsoundeffect.cpp @@ -89,7 +89,7 @@ void tst_QSoundEffect::initTestCase() void tst_QSoundEffect::testSource() { - QSignalSpy readSignal(sound, SIGNAL(sourceChanged())); + QSignalSpy readSignal(sound, &QSoundEffect::sourceChanged); sound->setSource(url); sound->setVolume(0.1f); @@ -108,8 +108,8 @@ void tst_QSoundEffect::testLooping() sound->setSource(url); QTRY_COMPARE(sound->status(), QSoundEffect::Ready); - QSignalSpy readSignal_Count(sound, SIGNAL(loopCountChanged())); - QSignalSpy readSignal_Remaining(sound, SIGNAL(loopsRemainingChanged())); + QSignalSpy readSignal_Count(sound, &QSoundEffect::loopCountChanged); + QSignalSpy readSignal_Remaining(sound, &QSoundEffect::loopsRemainingChanged); sound->setLoopCount(3); sound->setVolume(0.1f); @@ -195,7 +195,7 @@ void tst_QSoundEffect::testLooping() void tst_QSoundEffect::testVolume() { - QSignalSpy readSignal(sound, SIGNAL(volumeChanged())); + QSignalSpy readSignal(sound, &QSoundEffect::volumeChanged); sound->setVolume(0.5); QCOMPARE(sound->volume(),0.5); @@ -205,7 +205,7 @@ void tst_QSoundEffect::testVolume() void tst_QSoundEffect::testMuting() { - QSignalSpy readSignal(sound, SIGNAL(mutedChanged())); + QSignalSpy readSignal(sound, &QSoundEffect::mutedChanged); sound->setMuted(true); QCOMPARE(sound->isMuted(),true); @@ -375,7 +375,7 @@ void tst_QSoundEffect::testSupportedMimeTypes() void tst_QSoundEffect::testCorruptFile() { for (int i = 0; i < 10; i++) { - QSignalSpy statusSpy(sound, SIGNAL(statusChanged())); + QSignalSpy statusSpy(sound, &QSoundEffect::statusChanged); sound->setSource(urlCorrupted); QVERIFY(!sound->isPlaying()); QVERIFY(sound->status() == QSoundEffect::Loading || sound->status() == QSoundEffect::Error); diff --git a/tests/auto/unit/mockbackend/qmockaudiodecoder.cpp b/tests/auto/unit/mockbackend/qmockaudiodecoder.cpp index 700bf4486..3c6b940a9 100644 --- a/tests/auto/unit/mockbackend/qmockaudiodecoder.cpp +++ b/tests/auto/unit/mockbackend/qmockaudiodecoder.cpp @@ -60,7 +60,7 @@ void QMockAudioDecoder::start() setIsDecoding(true); durationChanged(duration()); - QTimer::singleShot(50, this, SLOT(pretendDecode())); + QTimer::singleShot(50, this, &QMockAudioDecoder::pretendDecode); } else { error(QAudioDecoder::ResourceError, "No source set"); } @@ -92,7 +92,7 @@ QAudioBuffer QMockAudioDecoder::read() if (mBuffers.isEmpty() && mSerial >= MOCK_DECODER_MAX_BUFFERS) { finished(); } else - QTimer::singleShot(50, this, SLOT(pretendDecode())); + QTimer::singleShot(50, this, &QMockAudioDecoder::pretendDecode); } return a; diff --git a/tests/auto/unit/mockbackend/qmockimagecapture.cpp b/tests/auto/unit/mockbackend/qmockimagecapture.cpp index 74ab08e59..96e53b2f4 100644 --- a/tests/auto/unit/mockbackend/qmockimagecapture.cpp +++ b/tests/auto/unit/mockbackend/qmockimagecapture.cpp @@ -25,7 +25,7 @@ int QMockImageCapture::capture(const QString &fileName) m_fileName = fileName; m_captureRequest++; emit readyForCaptureChanged(m_ready = false); - QTimer::singleShot(5, this, SLOT(captured())); + QTimer::singleShot(5, this, &QMockImageCapture::captured); return m_captureRequest; } else { emit error(-1, QImageCapture::NotReadyError, diff --git a/tests/auto/unit/mockbackend/qmockmediaencoder.h b/tests/auto/unit/mockbackend/qmockmediaencoder.h index 6d475df62..6cfea1bb0 100644 --- a/tests/auto/unit/mockbackend/qmockmediaencoder.h +++ b/tests/auto/unit/mockbackend/qmockmediaencoder.h @@ -41,7 +41,7 @@ public: } virtual QMediaMetaData metaData() const override { return m_metaData; } - using QPlatformMediaRecorder::error; + using QPlatformMediaRecorder::updateError; public: void record(QMediaEncoderSettings &settings) override diff --git a/tests/auto/unit/multimedia/gstreamer_backend/tst_gstreamer_backend.cpp b/tests/auto/unit/multimedia/gstreamer_backend/tst_gstreamer_backend.cpp index 2fb4a26c2..763ff01bf 100644 --- a/tests/auto/unit/multimedia/gstreamer_backend/tst_gstreamer_backend.cpp +++ b/tests/auto/unit/multimedia/gstreamer_backend/tst_gstreamer_backend.cpp @@ -1,44 +1,53 @@ // Copyright (C) 2024 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "tst_gstreamer_backend.h" #include <QtTest/QtTest> -#include <QtQGstreamerMediaPlugin/private/qgstreamermetadata_p.h> + #include <QtQGstreamerMediaPlugin/private/qgst_handle_types_p.h> +#include <QtQGstreamerMediaPlugin/private/qgst_p.h> +#include <QtQGstreamerMediaPlugin/private/qgstreamermetadata_p.h> QT_USE_NAMESPACE using namespace Qt::Literals; -void tst_GStreamer::metadata_fromGstTagList() +QGstTagListHandle tst_GStreamer::parseTagList(const char *str) { QGstTagListHandle tagList{ - gst_tag_list_new_from_string(R"(taglist, title="My Video", comment="yada")"), + gst_tag_list_new_from_string(str), QGstTagListHandle::NeedsRef, }; + return tagList; +} + +QGstTagListHandle tst_GStreamer::parseTagList(const QByteArray &ba) +{ + return parseTagList(ba.constData()); +} + +void tst_GStreamer::metadata_taglistToMetaData() +{ + QGstTagListHandle tagList = parseTagList(R"(taglist, title="My Video", comment="yada")"); - QGstreamerMetaData parsed = QGstreamerMetaData::fromGstTagList(tagList.get()); + QMediaMetaData parsed = taglistToMetaData(tagList); QCOMPARE(parsed.stringValue(QMediaMetaData::Title), u"My Video"_s); QCOMPARE(parsed.stringValue(QMediaMetaData::Comment), u"yada"_s); } -void tst_GStreamer::metadata_fromGstTagList_extractsOrientation() +void tst_GStreamer::metadata_taglistToMetaData_extractsOrientation() { QFETCH(QByteArray, taglist); QFETCH(QtVideo::Rotation, rotation); - QGstTagListHandle tagList{ - gst_tag_list_new_from_string(taglist.constData()), - QGstTagListHandle::NeedsRef, - }; - - QGstreamerMetaData parsed = QGstreamerMetaData::fromGstTagList(tagList.get()); + QGstTagListHandle tagList = parseTagList(taglist); + QMediaMetaData parsed = taglistToMetaData(tagList); QCOMPARE(parsed[QMediaMetaData::Orientation].value<QtVideo::Rotation>(), rotation); } -void tst_GStreamer::metadata_fromGstTagList_extractsOrientation_data() +void tst_GStreamer::metadata_taglistToMetaData_extractsOrientation_data() { QTest::addColumn<QByteArray>("taglist"); QTest::addColumn<QtVideo::Rotation>("rotation"); @@ -56,6 +65,59 @@ void tst_GStreamer::metadata_fromGstTagList_extractsOrientation_data() << QtVideo::Rotation::Clockwise270; } +void tst_GStreamer::metadata_taglistToMetaData_extractsDuration() +{ + QGstTagListHandle tagList = parseTagList( + R"__(taglist, video-codec=(string)"On2\ VP9", container-specific-track-id=(string)1, extended-comment=(string){ "ALPHA_MODE\=1", "HANDLER_NAME\=Apple\ Video\ Media\ Handler", "VENDOR_ID\=appl", "TIMECODE\=00:00:00:00", "DURATION\=00:00:00.400000000" }, encoder=(string)"Lavc59.37.100\ libvpx-vp9")__"); + + QMediaMetaData parsed = taglistToMetaData(tagList.get()); + + QCOMPARE(parsed[QMediaMetaData::Duration].value<int>(), 400); +} + +void tst_GStreamer::QGstBin_createFromPipelineDescription() +{ + QGstBin bin = QGstBin::createFromPipelineDescription("identity name=foo ! identity name=bar"); + + QVERIFY(bin); + QVERIFY(bin.findByName("foo")); + QCOMPARE_EQ(bin.findByName("foo").getParent(), bin); + QVERIFY(bin.findByName("bar")); + QVERIFY(!bin.findByName("baz")); + bin.dumpGraph("QGstBin_createFromPipelineDescription"); +} + +void tst_GStreamer::QGstElement_createFromPipelineDescription() +{ + using namespace std::string_view_literals; + QGstElement element = QGstElement::createFromPipelineDescription("identity name=foo"); + QCOMPARE_EQ(element.name(), "foo"sv); + QCOMPARE_EQ(element.typeName(), "GstIdentity"sv); +} + +void tst_GStreamer::QGstElement_createFromPipelineDescription_multipleElementsCreatesBin() +{ + using namespace std::string_view_literals; + QGstElement element = + QGstElement::createFromPipelineDescription("identity name=foo ! identity name=bar"); + + QVERIFY(element); + QCOMPARE_EQ(element.typeName(), "GstPipeline"sv); + + QGstBin bin{ + qGstSafeCast<GstBin>(element.element()), + QGstBin::NeedsRef, + }; + + QVERIFY(bin); + QVERIFY(bin.findByName("foo")); + QCOMPARE_EQ(bin.findByName("foo").getParent(), bin); + QVERIFY(bin.findByName("bar")); + QVERIFY(!bin.findByName("baz")); + + bin.dumpGraph("QGstElement_createFromPipelineDescription_multipleElements"); +} + QTEST_GUILESS_MAIN(tst_GStreamer) #include "moc_tst_gstreamer_backend.cpp" diff --git a/tests/auto/unit/multimedia/gstreamer_backend/tst_gstreamer_backend.h b/tests/auto/unit/multimedia/gstreamer_backend/tst_gstreamer_backend.h index 88c77332a..0a1628031 100644 --- a/tests/auto/unit/multimedia/gstreamer_backend/tst_gstreamer_backend.h +++ b/tests/auto/unit/multimedia/gstreamer_backend/tst_gstreamer_backend.h @@ -1,5 +1,5 @@ // Copyright (C) 2024 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TST_GSTREAMER_BACKEND_H #define TST_GSTREAMER_BACKEND_H @@ -7,6 +7,7 @@ #include <QtTest/QtTest> #include <QtQGstreamerMediaPlugin/private/qgstreamerintegration_p.h> +#include <QtQGstreamerMediaPlugin/private/qgst_handle_types_p.h> QT_USE_NAMESPACE @@ -14,10 +15,18 @@ class tst_GStreamer : public QObject { Q_OBJECT + QGstTagListHandle parseTagList(const char *); + QGstTagListHandle parseTagList(const QByteArray &); + private slots: - void metadata_fromGstTagList(); - void metadata_fromGstTagList_extractsOrientation(); - void metadata_fromGstTagList_extractsOrientation_data(); + void metadata_taglistToMetaData(); + void metadata_taglistToMetaData_extractsOrientation(); + void metadata_taglistToMetaData_extractsOrientation_data(); + void metadata_taglistToMetaData_extractsDuration(); + + void QGstBin_createFromPipelineDescription(); + void QGstElement_createFromPipelineDescription(); + void QGstElement_createFromPipelineDescription_multipleElementsCreatesBin(); private: QGstreamerIntegration integration; diff --git a/tests/auto/unit/multimedia/qaudiodecoder/tst_qaudiodecoder.cpp b/tests/auto/unit/multimedia/qaudiodecoder/tst_qaudiodecoder.cpp index 1d2c39e7f..77e161fda 100644 --- a/tests/auto/unit/multimedia/qaudiodecoder/tst_qaudiodecoder.cpp +++ b/tests/auto/unit/multimedia/qaudiodecoder/tst_qaudiodecoder.cpp @@ -54,8 +54,8 @@ void tst_QAudioDecoder::read() QVERIFY(!d.isDecoding()); QVERIFY(d.bufferAvailable() == false); - QSignalSpy readySpy(&d, SIGNAL(bufferReady())); - QSignalSpy bufferChangedSpy(&d, SIGNAL(bufferAvailableChanged(bool))); + QSignalSpy readySpy(&d, &QAudioDecoder::bufferReady); + QSignalSpy bufferChangedSpy(&d, &QAudioDecoder::bufferAvailableChanged); QSignalSpy errorSpy(&d, SIGNAL(error(QAudioDecoder::Error))); // Starting with empty source == error @@ -115,8 +115,8 @@ void tst_QAudioDecoder::stop() QVERIFY(!d.isDecoding()); QVERIFY(d.bufferAvailable() == false); - QSignalSpy readySpy(&d, SIGNAL(bufferReady())); - QSignalSpy bufferChangedSpy(&d, SIGNAL(bufferAvailableChanged(bool))); + QSignalSpy readySpy(&d, &QAudioDecoder::bufferReady); + QSignalSpy bufferChangedSpy(&d, &QAudioDecoder::bufferAvailableChanged); QSignalSpy errorSpy(&d, SIGNAL(error(QAudioDecoder::Error))); // Starting with empty source == error @@ -167,8 +167,8 @@ void tst_QAudioDecoder::format() QVERIFY(!d.isDecoding()); QVERIFY(d.bufferAvailable() == false); - QSignalSpy readySpy(&d, SIGNAL(bufferReady())); - QSignalSpy bufferChangedSpy(&d, SIGNAL(bufferAvailableChanged(bool))); + QSignalSpy readySpy(&d, &QAudioDecoder::bufferReady); + QSignalSpy bufferChangedSpy(&d, &QAudioDecoder::bufferAvailableChanged); QSignalSpy errorSpy(&d, SIGNAL(error(QAudioDecoder::Error))); // Set the source to something @@ -255,11 +255,11 @@ void tst_QAudioDecoder::readAll() d.setSource(QUrl::fromLocalFile("Foo")); QVERIFY(!d.isDecoding()); - QSignalSpy durationSpy(&d, SIGNAL(durationChanged(qint64))); - QSignalSpy positionSpy(&d, SIGNAL(positionChanged(qint64))); - QSignalSpy isDecodingSpy(&d, SIGNAL(isDecodingChanged(bool))); - QSignalSpy finishedSpy(&d, SIGNAL(finished())); - QSignalSpy bufferAvailableSpy(&d, SIGNAL(bufferAvailableChanged(bool))); + QSignalSpy durationSpy(&d, &QAudioDecoder::durationChanged); + QSignalSpy positionSpy(&d, &QAudioDecoder::positionChanged); + QSignalSpy isDecodingSpy(&d, &QAudioDecoder::isDecodingChanged); + QSignalSpy finishedSpy(&d, &QAudioDecoder::finished); + QSignalSpy bufferAvailableSpy(&d, &QAudioDecoder::bufferAvailableChanged); d.start(); int i = 0; forever { diff --git a/tests/auto/unit/multimedia/qcamera/tst_qcamera.cpp b/tests/auto/unit/multimedia/qcamera/tst_qcamera.cpp index 848cd05b7..1ba624eec 100644 --- a/tests/auto/unit/multimedia/qcamera/tst_qcamera.cpp +++ b/tests/auto/unit/multimedia/qcamera/tst_qcamera.cpp @@ -197,7 +197,7 @@ void tst_QCamera::testSimpleCameraCapture() QCOMPARE(imageCapture.error(), QImageCapture::NoError); QVERIFY(imageCapture.errorString().isEmpty()); - QSignalSpy errorSignal(&imageCapture, SIGNAL(errorOccurred(int,QImageCapture::Error,QString))); + QSignalSpy errorSignal(&imageCapture, &QImageCapture::errorOccurred); imageCapture.captureToFile(QStringLiteral("/dev/null")); QCOMPARE(errorSignal.size(), 1); QCOMPARE(imageCapture.error(), QImageCapture::NotReadyError); @@ -220,8 +220,8 @@ void tst_QCamera::testCameraCapture() QVERIFY(!imageCapture.isReadyForCapture()); - QSignalSpy capturedSignal(&imageCapture, SIGNAL(imageCaptured(int,QImage))); - QSignalSpy errorSignal(&imageCapture, SIGNAL(errorOccurred(int,QImageCapture::Error,QString))); + QSignalSpy capturedSignal(&imageCapture, &QImageCapture::imageCaptured); + QSignalSpy errorSignal(&imageCapture, &QImageCapture::errorOccurred); imageCapture.captureToFile(QStringLiteral("/dev/null")); QCOMPARE(capturedSignal.size(), 0); @@ -249,8 +249,8 @@ void tst_QCamera::testCameraCaptureMetadata() session.setCamera(&camera); session.setImageCapture(&imageCapture); - QSignalSpy metadataSignal(&imageCapture, SIGNAL(imageMetadataAvailable(int,const QMediaMetaData&))); - QSignalSpy savedSignal(&imageCapture, SIGNAL(imageSaved(int,QString))); + QSignalSpy metadataSignal(&imageCapture, &QImageCapture::imageMetadataAvailable); + QSignalSpy savedSignal(&imageCapture, &QImageCapture::imageSaved); camera.start(); int id = imageCapture.captureToFile(QStringLiteral("/dev/null")); @@ -419,7 +419,7 @@ void tst_QCamera::testCameraEncodingProperyChange() session.setCamera(&camera); session.setImageCapture(&imageCapture); - QSignalSpy activeChangedSignal(&camera, SIGNAL(activeChanged(bool))); + QSignalSpy activeChangedSignal(&camera, &QCamera::activeChanged); camera.start(); QCOMPARE(camera.isActive(), true); @@ -604,7 +604,7 @@ void tst_QCamera::testErrorSignal() Q_ASSERT(service); Q_ASSERT(service->mockCameraControl); - QSignalSpy spyError(&camera, SIGNAL(errorOccurred(QCamera::Error,const QString&))); + QSignalSpy spyError(&camera, &QCamera::errorOccurred); /* Set the QPlatformCamera error and verify if the signal is emitted correctly in QCamera */ service->mockCameraControl->setError(QCamera::CameraError,QStringLiteral("Camera Error")); @@ -681,7 +681,7 @@ void tst_QCamera::testSetCameraFormat() auto videoFormats = device.videoFormats(); QVERIFY(videoFormats.size()); QCameraFormat cameraFormat = videoFormats.first(); - QSignalSpy spy(&camera, SIGNAL(cameraFormatChanged())); + QSignalSpy spy(&camera, &QCamera::cameraFormatChanged); QVERIFY(spy.size() == 0); camera.setCameraFormat(cameraFormat); QCOMPARE(spy.size(), 1); @@ -733,7 +733,7 @@ void tst_QCamera::testZoomChanged() QCamera camera; session.setCamera(&camera); - QSignalSpy spy(&camera, SIGNAL(zoomFactorChanged(float))); + QSignalSpy spy(&camera, &QCamera::zoomFactorChanged); QVERIFY(spy.size() == 0); camera.setZoomFactor(2.0); QVERIFY(spy.size() == 1); @@ -751,7 +751,7 @@ void tst_QCamera::testMaxZoomChangedSignal() QMockCamera *mock = QMockIntegration::instance()->lastCamera(); // ### change max zoom factor on backend, e.g. by changing camera - QSignalSpy spy(&camera, SIGNAL(maximumZoomFactorChanged(float))); + QSignalSpy spy(&camera, &QCamera::maximumZoomFactorChanged); mock->maximumZoomFactorChanged(55); QVERIFY(spy.size() == 1); QCOMPARE(camera.maximumZoomFactor(), 55); @@ -763,9 +763,9 @@ void tst_QCamera::testSignalExposureCompensationChanged() QCamera camera; session.setCamera(&camera); - QSignalSpy spyExposureCompensationChanged(&camera, SIGNAL(exposureCompensationChanged(float))); + QSignalSpy spyExposureCompensationChanged(&camera, &QCamera::exposureCompensationChanged); - QVERIFY(spyExposureCompensationChanged.size() ==0); + QVERIFY(spyExposureCompensationChanged.size() == 0); QVERIFY(camera.exposureCompensation() != 800); camera.setExposureCompensation(2.0); @@ -790,7 +790,7 @@ void tst_QCamera::testSignalIsoSensitivityChanged() QCamera camera; session.setCamera(&camera); - QSignalSpy spyisoSensitivityChanged(&camera, SIGNAL(isoSensitivityChanged(int))); + QSignalSpy spyisoSensitivityChanged(&camera, &QCamera::isoSensitivityChanged); QVERIFY(spyisoSensitivityChanged.size() ==0); @@ -805,9 +805,9 @@ void tst_QCamera::testSignalShutterSpeedChanged() QCamera camera; session.setCamera(&camera); - QSignalSpy spySignalExposureTimeChanged(&camera, SIGNAL(exposureTimeChanged(float))); + QSignalSpy spySignalExposureTimeChanged(&camera, &QCamera::exposureTimeChanged); - QVERIFY(spySignalExposureTimeChanged.size() ==0); + QVERIFY(spySignalExposureTimeChanged.size() == 0); camera.setManualExposureTime(2.0);//set the ManualShutterSpeed to 2.0 QTest::qWait(100); @@ -821,7 +821,7 @@ void tst_QCamera::testSignalFlashReady() QCamera camera; session.setCamera(&camera); - QSignalSpy spyflashReady(&camera,SIGNAL(flashReady(bool))); + QSignalSpy spyflashReady(&camera, &QCamera::flashReady); QVERIFY(spyflashReady.size() == 0); diff --git a/tests/auto/unit/multimedia/qimagecapture/tst_qimagecapture.cpp b/tests/auto/unit/multimedia/qimagecapture/tst_qimagecapture.cpp index c56712d14..3267b6f40 100644 --- a/tests/auto/unit/multimedia/qimagecapture/tst_qimagecapture.cpp +++ b/tests/auto/unit/multimedia/qimagecapture/tst_qimagecapture.cpp @@ -178,7 +178,7 @@ void tst_QImageCapture::error() session.setCamera(&camera); session.setImageCapture(&imageCapture); - QSignalSpy spy(&imageCapture, SIGNAL(errorOccurred(int,QImageCapture::Error,QString))); + QSignalSpy spy(&imageCapture, &QImageCapture::errorOccurred); imageCapture.captureToFile(); QTest::qWait(30); QVERIFY(spy.size() == 1); @@ -196,7 +196,7 @@ void tst_QImageCapture::imageCaptured() session.setCamera(&camera); session.setImageCapture(&imageCapture); - QSignalSpy spy(&imageCapture, SIGNAL(imageCaptured(int,QImage))); + QSignalSpy spy(&imageCapture, &QImageCapture::imageCaptured); QVERIFY(imageCapture.isAvailable() == true); QVERIFY(imageCapture.isReadyForCapture() == false); camera.start(); @@ -219,7 +219,7 @@ void tst_QImageCapture::imageExposed() session.setCamera(&camera); session.setImageCapture(&imageCapture); - QSignalSpy spy(&imageCapture, SIGNAL(imageExposed(int))); + QSignalSpy spy(&imageCapture, &QImageCapture::imageExposed); QVERIFY(imageCapture.isAvailable() == true); QVERIFY(imageCapture.isReadyForCapture() == false); camera.start(); @@ -240,7 +240,7 @@ void tst_QImageCapture::imageSaved() session.setCamera(&camera); session.setImageCapture(&imageCapture); - QSignalSpy spy(&imageCapture, SIGNAL(imageSaved(int,QString))); + QSignalSpy spy(&imageCapture, &QImageCapture::imageSaved); QVERIFY(imageCapture.isAvailable() == true); QVERIFY(imageCapture.isReadyForCapture() == false); camera.start(); @@ -262,7 +262,7 @@ void tst_QImageCapture::readyForCaptureChanged() session.setCamera(&camera); session.setImageCapture(&imageCapture); - QSignalSpy spy(&imageCapture, SIGNAL(readyForCaptureChanged(bool))); + QSignalSpy spy(&imageCapture, &QImageCapture::readyForCaptureChanged); QVERIFY(imageCapture.isReadyForCapture() == false); imageCapture.captureToFile(); QTest::qWait(100); diff --git a/tests/auto/unit/multimedia/qmediacapture_gstreamer/tst_qmediacapture_gstreamer.cpp b/tests/auto/unit/multimedia/qmediacapture_gstreamer/tst_qmediacapture_gstreamer.cpp index 21258005c..55bce42f5 100644 --- a/tests/auto/unit/multimedia/qmediacapture_gstreamer/tst_qmediacapture_gstreamer.cpp +++ b/tests/auto/unit/multimedia/qmediacapture_gstreamer/tst_qmediacapture_gstreamer.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2024 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> #include <QtMultimedia/QMediaCaptureSession> diff --git a/tests/auto/unit/multimedia/qmediaplayer/tst_qmediaplayer.cpp b/tests/auto/unit/multimedia/qmediaplayer/tst_qmediaplayer.cpp index 2deaaf846..3fb77ca2d 100644 --- a/tests/auto/unit/multimedia/qmediaplayer/tst_qmediaplayer.cpp +++ b/tests/auto/unit/multimedia/qmediaplayer/tst_qmediaplayer.cpp @@ -294,32 +294,40 @@ void tst_QMediaPlayer::testPosition() QVERIFY(player->duration() == duration); if (seekable) { - { QSignalSpy spy(player, SIGNAL(positionChanged(qint64))); - player->setPosition(position); - QCOMPARE(player->position(), position); - QCOMPARE(spy.size(), 0); } + { + QSignalSpy spy(player, &QMediaPlayer::positionChanged); + player->setPosition(position); + QCOMPARE(player->position(), position); + QCOMPARE(spy.size(), 0); + } mockPlayer->setPosition(position); - { QSignalSpy spy(player, SIGNAL(positionChanged(qint64))); - player->setPosition(0); - QCOMPARE(player->position(), qint64(0)); - QCOMPARE(spy.size(), position == 0 ? 0 : 1); } + { + QSignalSpy spy(player, &QMediaPlayer::positionChanged); + player->setPosition(0); + QCOMPARE(player->position(), qint64(0)); + QCOMPARE(spy.size(), position == 0 ? 0 : 1); + } mockPlayer->setPosition(position); - { QSignalSpy spy(player, SIGNAL(positionChanged(qint64))); - player->setPosition(duration); - QCOMPARE(player->position(), duration); - QCOMPARE(spy.size(), position == duration ? 0 : 1); } + { + QSignalSpy spy(player, &QMediaPlayer::positionChanged); + player->setPosition(duration); + QCOMPARE(player->position(), duration); + QCOMPARE(spy.size(), position == duration ? 0 : 1); + } mockPlayer->setPosition(position); - { QSignalSpy spy(player, SIGNAL(positionChanged(qint64))); - player->setPosition(-1); - QCOMPARE(player->position(), qint64(0)); - QCOMPARE(spy.size(), position == 0 ? 0 : 1); } + { + QSignalSpy spy(player, &QMediaPlayer::positionChanged); + player->setPosition(-1); + QCOMPARE(player->position(), qint64(0)); + QCOMPARE(spy.size(), position == 0 ? 0 : 1); + } } else { - QSignalSpy spy(player, SIGNAL(positionChanged(qint64))); + QSignalSpy spy(player, &QMediaPlayer::positionChanged); player->setPosition(position); QCOMPARE(player->position(), position); @@ -342,25 +350,33 @@ void tst_QMediaPlayer::testVolume() QVERIFY(audioOutput->volume() == vol); if (valid) { - { QSignalSpy spy(audioOutput, SIGNAL(volumeChanged(float))); - audioOutput->setVolume(.1f); - QCOMPARE(audioOutput->volume(), .1f); - QCOMPARE(spy.size(), 1); } - - { QSignalSpy spy(audioOutput, SIGNAL(volumeChanged(float))); - audioOutput->setVolume(-1000.f); - QCOMPARE(audioOutput->volume(), 0.f); - QCOMPARE(spy.size(), 1); } - - { QSignalSpy spy(audioOutput, SIGNAL(volumeChanged(float))); - audioOutput->setVolume(1.f); - QCOMPARE(audioOutput->volume(), 1.f); - QCOMPARE(spy.size(), 1); } - - { QSignalSpy spy(audioOutput, SIGNAL(volumeChanged(float))); - audioOutput->setVolume(1000.f); - QCOMPARE(audioOutput->volume(), 1.f); - QCOMPARE(spy.size(), 0); } + { + QSignalSpy spy(audioOutput, &QAudioOutput::volumeChanged); + audioOutput->setVolume(.1f); + QCOMPARE(audioOutput->volume(), .1f); + QCOMPARE(spy.size(), 1); + } + + { + QSignalSpy spy(audioOutput, &QAudioOutput::volumeChanged); + audioOutput->setVolume(-1000.f); + QCOMPARE(audioOutput->volume(), 0.f); + QCOMPARE(spy.size(), 1); + } + + { + QSignalSpy spy(audioOutput, &QAudioOutput::volumeChanged); + audioOutput->setVolume(1.f); + QCOMPARE(audioOutput->volume(), 1.f); + QCOMPARE(spy.size(), 1); + } + + { + QSignalSpy spy(audioOutput, &QAudioOutput::volumeChanged); + audioOutput->setVolume(1000.f); + QCOMPARE(audioOutput->volume(), 1.f); + QCOMPARE(spy.size(), 0); + } } } @@ -381,7 +397,7 @@ void tst_QMediaPlayer::testMuted() audioOutput->setVolume(vol); QVERIFY(audioOutput->isMuted() == muted); - QSignalSpy spy(audioOutput, SIGNAL(mutedChanged(bool))); + QSignalSpy spy(audioOutput, &QAudioOutput::mutedChanged); audioOutput->setMuted(!muted); QCOMPARE(audioOutput->isMuted(), !muted); QCOMPARE(audioOutput->volume(), vol); @@ -442,7 +458,7 @@ void tst_QMediaPlayer::testPlaybackRate() mockPlayer->setPlaybackRate(playbackRate); QVERIFY(player->playbackRate() == playbackRate); - QSignalSpy spy(player, SIGNAL(playbackRateChanged(qreal))); + QSignalSpy spy(player, &QMediaPlayer::playbackRateChanged); player->setPlaybackRate(playbackRate + 0.5f); QCOMPARE(player->playbackRate(), playbackRate + 0.5f); QCOMPARE(spy.size(), 1); @@ -512,8 +528,8 @@ void tst_QMediaPlayer::testPlay() QCOMPARE(player->isPlaying(), state == QMediaPlayer::PlayingState); QCOMPARE(player->source(), mediaContent); - QSignalSpy spy(player, SIGNAL(playbackStateChanged(QMediaPlayer::PlaybackState))); - QSignalSpy playingChanged(player, SIGNAL(playingChanged(bool))); + QSignalSpy spy(player, &QMediaPlayer::playbackStateChanged); + QSignalSpy playingChanged(player, &QMediaPlayer::playingChanged); player->play(); @@ -549,8 +565,8 @@ void tst_QMediaPlayer::testPause() QCOMPARE(player->isPlaying(), state == QMediaPlayer::PlayingState); QVERIFY(player->source() == mediaContent); - QSignalSpy spy(player, SIGNAL(playbackStateChanged(QMediaPlayer::PlaybackState))); - QSignalSpy playingChanged(player, SIGNAL(playingChanged(bool))); + QSignalSpy spy(player, &QMediaPlayer::playbackStateChanged); + QSignalSpy playingChanged(player, &QMediaPlayer::playingChanged); player->pause(); @@ -584,8 +600,8 @@ void tst_QMediaPlayer::testStop() QCOMPARE(player->isPlaying(), state == QMediaPlayer::PlayingState); QVERIFY(player->source() == mediaContent); - QSignalSpy spy(player, SIGNAL(playbackStateChanged(QMediaPlayer::PlaybackState))); - QSignalSpy playingChanged(player, SIGNAL(playingChanged(bool))); + QSignalSpy spy(player, &QMediaPlayer::playbackStateChanged); + QSignalSpy playingChanged(player, &QMediaPlayer::playingChanged); player->stop(); @@ -616,8 +632,8 @@ void tst_QMediaPlayer::testMediaStatus() mockPlayer->setMediaStatus(QMediaPlayer::NoMedia); mockPlayer->setBufferStatus(bufferProgress); - QSignalSpy statusSpy(player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus))); - QSignalSpy bufferSpy(player, SIGNAL(bufferProgressChanged(float))); + QSignalSpy statusSpy(player, &QMediaPlayer::mediaStatusChanged); + QSignalSpy bufferSpy(player, &QMediaPlayer::bufferProgressChanged); QCOMPARE(player->mediaStatus(), QMediaPlayer::NoMedia); @@ -786,9 +802,9 @@ void tst_QMediaPlayer::testQrc() mockPlayer->setState(QMediaPlayer::PlayingState, QMediaPlayer::NoMedia); mockPlayer->setStreamPlaybackSupported(backendHasStream); - QSignalSpy mediaSpy(player, SIGNAL(sourceChanged(QUrl))); - QSignalSpy statusSpy(player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus))); - QSignalSpy errorSpy(player, SIGNAL(errorOccurred(QMediaPlayer::Error,const QString&))); + QSignalSpy mediaSpy(player, &QMediaPlayer::sourceChanged); + QSignalSpy statusSpy(player, &QMediaPlayer::mediaStatusChanged); + QSignalSpy errorSpy(player, &QMediaPlayer::errorOccurred); player->setSource(mediaContent); diff --git a/tests/auto/unit/multimedia/qmediaplayer_gstreamer/tst_qmediaplayer_gstreamer.cpp b/tests/auto/unit/multimedia/qmediaplayer_gstreamer/tst_qmediaplayer_gstreamer.cpp index 8b0f3f073..a11e25f29 100644 --- a/tests/auto/unit/multimedia/qmediaplayer_gstreamer/tst_qmediaplayer_gstreamer.cpp +++ b/tests/auto/unit/multimedia/qmediaplayer_gstreamer/tst_qmediaplayer_gstreamer.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2024 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> #include <QtMultimedia/qmediaplayer.h> diff --git a/tests/auto/unit/multimedia/qmediarecorder/tst_qmediarecorder.cpp b/tests/auto/unit/multimedia/qmediarecorder/tst_qmediarecorder.cpp index 4873c2407..a11f39207 100644 --- a/tests/auto/unit/multimedia/qmediarecorder/tst_qmediarecorder.cpp +++ b/tests/auto/unit/multimedia/qmediarecorder/tst_qmediarecorder.cpp @@ -125,7 +125,7 @@ void tst_QMediaRecorder::testNullControls() QCOMPARE(recorder.mediaFormat().videoCodec(), QMediaFormat::VideoCodec::VP9); QCOMPARE(recorder.mediaFormat().fileFormat(), QMediaFormat::MPEG4); - QSignalSpy spy(&recorder, SIGNAL(recorderStateChanged(RecorderState))); + QSignalSpy spy(&recorder, &QMediaRecorder::recorderStateChanged); recorder.record(); QCOMPARE(recorder.recorderState(), QMediaRecorder::StoppedState); @@ -190,12 +190,12 @@ void tst_QMediaRecorder::testError() { const QString errorString(QLatin1String("format error")); - QSignalSpy spy(encoder, SIGNAL(errorOccurred(Error, const QString&))); + QSignalSpy spy(encoder, &QMediaRecorder::errorOccurred); QCOMPARE(encoder->error(), QMediaRecorder::NoError); QCOMPARE(encoder->errorString(), QString()); - mock->error(QMediaRecorder::FormatError, errorString); + mock->updateError(QMediaRecorder::FormatError, errorString); QCOMPARE(encoder->error(), QMediaRecorder::FormatError); QCOMPARE(encoder->errorString(), errorString); QCOMPARE(spy.size(), 1); @@ -230,8 +230,8 @@ void tst_QMediaRecorder::testSink() void tst_QMediaRecorder::testRecord() { - QSignalSpy stateSignal(encoder,SIGNAL(recorderStateChanged(RecorderState))); - QSignalSpy progressSignal(encoder, SIGNAL(durationChanged(qint64))); + QSignalSpy stateSignal(encoder, &QMediaRecorder::recorderStateChanged); + QSignalSpy progressSignal(encoder, &QMediaRecorder::durationChanged); encoder->record(); QCOMPARE(encoder->recorderState(), QMediaRecorder::RecordingState); QCOMPARE(encoder->error(), QMediaRecorder::NoError); @@ -413,12 +413,12 @@ void tst_QMediaRecorder::testEnum() { const QString errorString(QLatin1String("resource error")); - QSignalSpy spy(encoder, SIGNAL(errorOccurred(Error, const QString&))); + QSignalSpy spy(encoder, &QMediaRecorder::errorOccurred); QCOMPARE(encoder->error(), QMediaRecorder::NoError); QCOMPARE(encoder->errorString(), QString()); - emit mock->error(QMediaRecorder::ResourceError, errorString); + mock->updateError(QMediaRecorder::ResourceError, errorString); QCOMPARE(encoder->error(), QMediaRecorder::ResourceError); QCOMPARE(encoder->errorString(), errorString); QCOMPARE(spy.size(), 1); diff --git a/tests/auto/unit/multimedia/qvideoframe/tst_qvideoframe.cpp b/tests/auto/unit/multimedia/qvideoframe/tst_qvideoframe.cpp index 607479412..c3dd6e71a 100644 --- a/tests/auto/unit/multimedia/qvideoframe/tst_qvideoframe.cpp +++ b/tests/auto/unit/multimedia/qvideoframe/tst_qvideoframe.cpp @@ -257,21 +257,27 @@ void tst_QVideoFrame::create_data() { QTest::addColumn<QSize>("size"); QTest::addColumn<QVideoFrameFormat::PixelFormat>("pixelFormat"); - QTest::addColumn<int>("bytes"); QTest::addColumn<int>("bytesPerLine"); QTest::newRow("64x64 ARGB32") << QSize(64, 64) - << QVideoFrameFormat::Format_ARGB8888; + << QVideoFrameFormat::Format_ARGB8888 + << 64*4; QTest::newRow("32x256 YUV420P") << QSize(32, 256) - << QVideoFrameFormat::Format_YUV420P; + << QVideoFrameFormat::Format_YUV420P + << 32; + QTest::newRow("32x256 UYVY") + << QSize(32, 256) + << QVideoFrameFormat::Format_UYVY + << 32*2; } void tst_QVideoFrame::create() { QFETCH(QSize, size); QFETCH(QVideoFrameFormat::PixelFormat, pixelFormat); + QFETCH(int, bytesPerLine); QVideoFrame frame(QVideoFrameFormat(size, pixelFormat)); @@ -285,6 +291,9 @@ void tst_QVideoFrame::create() QCOMPARE(frame.height(), size.height()); QCOMPARE(frame.startTime(), qint64(-1)); QCOMPARE(frame.endTime(), qint64(-1)); + frame.map(QVideoFrame::ReadOnly); + QCOMPARE(frame.bytesPerLine(0), bytesPerLine); + frame.unmap(); } void tst_QVideoFrame::createInvalid_data() diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_AdobeRgb_Full.png Binary files differnew file mode 100644 index 000000000..4fd00f938 --- /dev/null +++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_AdobeRgb_Full.png diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_AdobeRgb_Video.png Binary files differnew file mode 100644 index 000000000..4fd00f938 --- /dev/null +++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_AdobeRgb_Video.png diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT2020_Full.png Binary files differnew file mode 100644 index 000000000..309454576 --- /dev/null +++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT2020_Full.png diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT2020_Video.png Binary files differnew file mode 100644 index 000000000..f97e71817 --- /dev/null +++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT2020_Video.png diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT601_Full.png Binary files differnew file mode 100644 index 000000000..d513a8123 --- /dev/null +++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT601_Full.png diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT601_Video.png Binary files differnew file mode 100644 index 000000000..6e9c36b39 --- /dev/null +++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT601_Video.png diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT709_Full.png Binary files differnew file mode 100644 index 000000000..c0568cb62 --- /dev/null +++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT709_Full.png diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT709_Video.png Binary files differnew file mode 100644 index 000000000..207fc0be1 --- /dev/null +++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_uyvy_BT709_Video.png diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_AdobeRgb_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_AdobeRgb_Full.png Binary files differnew file mode 100644 index 000000000..4fd00f938 --- /dev/null +++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_AdobeRgb_Full.png diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_AdobeRgb_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_AdobeRgb_Video.png Binary files differnew file mode 100644 index 000000000..4fd00f938 --- /dev/null +++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_AdobeRgb_Video.png diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT2020_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT2020_Full.png Binary files differnew file mode 100644 index 000000000..309454576 --- /dev/null +++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT2020_Full.png diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT2020_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT2020_Video.png Binary files differnew file mode 100644 index 000000000..f97e71817 --- /dev/null +++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT2020_Video.png diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT601_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT601_Full.png Binary files differnew file mode 100644 index 000000000..d513a8123 --- /dev/null +++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT601_Full.png diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT601_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT601_Video.png Binary files differnew file mode 100644 index 000000000..6e9c36b39 --- /dev/null +++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT601_Video.png diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT709_Full.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT709_Full.png Binary files differnew file mode 100644 index 000000000..c0568cb62 --- /dev/null +++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT709_Full.png diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT709_Video.png b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT709_Video.png Binary files differnew file mode 100644 index 000000000..207fc0be1 --- /dev/null +++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/testdata/umbrellas.jpg_yuyv_BT709_Video.png diff --git a/tests/auto/unit/multimedia/qvideoframecolormanagement/tst_qvideoframecolormanagement.cpp b/tests/auto/unit/multimedia/qvideoframecolormanagement/tst_qvideoframecolormanagement.cpp index 31337603e..83e78d2d8 100644 --- a/tests/auto/unit/multimedia/qvideoframecolormanagement/tst_qvideoframecolormanagement.cpp +++ b/tests/auto/unit/multimedia/qvideoframecolormanagement/tst_qvideoframecolormanagement.cpp @@ -62,6 +62,10 @@ QString toString(QVideoFrameFormat::PixelFormat f) return "420p"; case QVideoFrameFormat::Format_YUV422P: return "422p"; + case QVideoFrameFormat::Format_UYVY: + return "uyvy"; + case QVideoFrameFormat::Format_YUYV: + return "yuyv"; default: Q_ASSERT(false); return ""; // Not implemented yet @@ -73,7 +77,8 @@ std::vector<QVideoFrameFormat::PixelFormat> pixelFormats() return { QVideoFrameFormat::Format_NV12, QVideoFrameFormat::Format_NV21, QVideoFrameFormat::Format_IMC1, QVideoFrameFormat::Format_IMC2, QVideoFrameFormat::Format_IMC3, QVideoFrameFormat::Format_IMC4, - QVideoFrameFormat::Format_YUV420P, QVideoFrameFormat::Format_YUV422P }; + QVideoFrameFormat::Format_YUV420P, QVideoFrameFormat::Format_YUV422P, + QVideoFrameFormat::Format_UYVY, QVideoFrameFormat::Format_YUYV }; } QString toString(QVideoFrameFormat::ColorSpace s) @@ -155,10 +160,10 @@ constexpr uchar double2uchar(double v) return static_cast<uchar>(std::clamp(v + 0.5, 0.5, 255.5)); } -constexpr void rgb2y(const QRgb &rgb, uchar *y) +constexpr uchar rgb2y(const QRgb &rgb) { const double Y = rgb2yuv_bt709_full.Y(rgb); - y[0] = double2uchar(Y); + return double2uchar(Y); } constexpr uchar rgb2u(const QRgb &rgb) @@ -173,19 +178,19 @@ constexpr uchar rgb2v(const QRgb &rgb) return double2uchar(V); } -void rgb2y(const QImage &image, QVideoFrame &frame, int yPlane) +void rgb2y_planar(const QImage &image, QVideoFrame &frame, int yPlane) { uchar *bits = frame.bits(yPlane); for (int row = 0; row < image.height(); ++row) { for (int col = 0; col < image.width(); ++col) { const QRgb pixel = image.pixel(col, row); - rgb2y(pixel, bits + col); + bits[col] = rgb2y(pixel); } bits += frame.bytesPerLine(yPlane); } } -void rgb2uv(const QImage &image, QVideoFrame &frame) +void rgb2uv_planar(const QImage &image, QVideoFrame &frame) { uchar *vBits = nullptr; uchar *uBits = nullptr; @@ -254,7 +259,7 @@ void rgb2uv(const QImage &image, QVideoFrame &frame) } } -void naive_rgbToYuv(const QImage &image, QVideoFrame &frame) +void naive_rgbToYuv_planar(const QImage &image, QVideoFrame &frame) { Q_ASSERT(image.format() == QImage::Format_RGB32); Q_ASSERT(frame.planeCount() > 1); @@ -262,8 +267,53 @@ void naive_rgbToYuv(const QImage &image, QVideoFrame &frame) frame.map(QVideoFrame::WriteOnly); - rgb2y(image, frame, 0); - rgb2uv(image, frame); + rgb2y_planar(image, frame, 0); + rgb2uv_planar(image, frame); + + frame.unmap(); +} + +void naive_rgbToYuv422(const QImage &image, QVideoFrame &frame) +{ + // Packed format uyvy or yuyv. Each 32 bit frame sample represents + // two pixels with distinct y values, but shared u and v values + Q_ASSERT(image.format() == QImage::Format_RGB32); + Q_ASSERT(frame.planeCount() == 1); + Q_ASSERT(image.size() == frame.size()); + + const QVideoFrameFormat::PixelFormat format = frame.pixelFormat(); + + Q_ASSERT(format == QVideoFrameFormat::Format_UYVY || format == QVideoFrameFormat::Format_YUYV); + + constexpr int plane = 0; + frame.map(QVideoFrame::WriteOnly); + + uchar *line = frame.bits(plane); + for (int row = 0; row < image.height(); ++row) { + uchar *bits = line; + for (int col = 0; col < image.width() - 1; col += 2) { + // Handle to image pixels at a time + const QRgb pixel0 = image.pixel(col, row); + const QRgb pixel1 = image.pixel(col + 1, row); + + // Down-sample u and v channels + bits[0] = (rgb2u(pixel0) + rgb2u(pixel1)) / 2; + bits[2] = (rgb2v(pixel0) + rgb2v(pixel1)) / 2; + + // But not the y-channel + bits[1] = rgb2y(pixel0); + bits[3] = rgb2y(pixel1); + + // Swizzle fom uyuv to yuyv + if (format == QVideoFrameFormat::Format_YUYV) { + std::swap(bits[0], bits[1]); + std::swap(bits[2], bits[3]); + } + + bits += 4; + } + line += frame.bytesPerLine(plane); + } frame.unmap(); } @@ -285,7 +335,10 @@ QVideoFrame createTestFrame(const TestParams ¶ms, const QImage &image) || params.pixelFormat == QVideoFrameFormat::Format_NV21 || params.pixelFormat == QVideoFrameFormat::Format_YUV420P || params.pixelFormat == QVideoFrameFormat::Format_YUV422P) { - naive_rgbToYuv(image, frame); + naive_rgbToYuv_planar(image, frame); + } else if (params.pixelFormat == QVideoFrameFormat::Format_UYVY + || params.pixelFormat == QVideoFrameFormat::Format_YUYV) { + naive_rgbToYuv422(image, frame); } else { qDebug() << "Not implemented yet"; Q_ASSERT(false); diff --git a/tests/auto/unit/multimedia/qwavedecoder/tst_qwavedecoder.cpp b/tests/auto/unit/multimedia/qwavedecoder/tst_qwavedecoder.cpp index c78e9cfb8..079f98075 100644 --- a/tests/auto/unit/multimedia/qwavedecoder/tst_qwavedecoder.cpp +++ b/tests/auto/unit/multimedia/qwavedecoder/tst_qwavedecoder.cpp @@ -176,8 +176,8 @@ void tst_QWaveDecoder::http() QNetworkReply *reply = nam.get(QNetworkRequest(QUrl::fromLocalFile(file))); QWaveDecoder waveDecoder(reply); - QSignalSpy validFormatSpy(&waveDecoder, SIGNAL(formatKnown())); - QSignalSpy parsingErrorSpy(&waveDecoder, SIGNAL(parsingError())); + QSignalSpy validFormatSpy(&waveDecoder, &QWaveDecoder::formatKnown); + QSignalSpy parsingErrorSpy(&waveDecoder, &QWaveDecoder::parsingError); QVERIFY(waveDecoder.open(QIODeviceBase::ReadOnly)); @@ -227,7 +227,7 @@ void tst_QWaveDecoder::readAllAtOnce() QVERIFY(stream.isOpen()); QWaveDecoder waveDecoder(&stream); - QSignalSpy validFormatSpy(&waveDecoder, SIGNAL(formatKnown())); + QSignalSpy validFormatSpy(&waveDecoder, &QWaveDecoder::formatKnown); QVERIFY(waveDecoder.open(QIODeviceBase::ReadOnly)); @@ -255,7 +255,7 @@ void tst_QWaveDecoder::readPerByte() QVERIFY(stream.isOpen()); QWaveDecoder waveDecoder(&stream); - QSignalSpy validFormatSpy(&waveDecoder, SIGNAL(formatKnown())); + QSignalSpy validFormatSpy(&waveDecoder, &QWaveDecoder::formatKnown); QVERIFY(waveDecoder.open(QIODeviceBase::ReadOnly)); diff --git a/tests/auto/unit/multimediawidgets/qcamerawidgets/tst_qcamerawidgets.cpp b/tests/auto/unit/multimediawidgets/qcamerawidgets/tst_qcamerawidgets.cpp index 743eda276..6c31a4b66 100644 --- a/tests/auto/unit/multimediawidgets/qcamerawidgets/tst_qcamerawidgets.cpp +++ b/tests/auto/unit/multimediawidgets/qcamerawidgets/tst_qcamerawidgets.cpp @@ -51,7 +51,7 @@ void tst_QCameraWidgets::testCameraEncodingProperyChange() session.setCamera(&camera); session.setImageCapture(&imageCapture); - QSignalSpy activeChangedSignal(&camera, SIGNAL(activeChanged(bool))); + QSignalSpy activeChangedSignal(&camera, &QCamera::activeChanged); camera.start(); QCOMPARE(camera.isActive(), true); diff --git a/tests/auto/unit/multimediawidgets/qgraphicsvideoitem/tst_qgraphicsvideoitem.cpp b/tests/auto/unit/multimediawidgets/qgraphicsvideoitem/tst_qgraphicsvideoitem.cpp index 2fba9daa6..2a1538edc 100644 --- a/tests/auto/unit/multimediawidgets/qgraphicsvideoitem/tst_qgraphicsvideoitem.cpp +++ b/tests/auto/unit/multimediawidgets/qgraphicsvideoitem/tst_qgraphicsvideoitem.cpp @@ -214,7 +214,7 @@ void tst_QGraphicsVideoItem::nativeSize() QCOMPARE(item.nativeSize(), QSizeF()); - QSignalSpy spy(&item, SIGNAL(nativeSizeChanged(QSizeF))); + QSignalSpy spy(&item, &QGraphicsVideoItem::nativeSizeChanged); QVideoFrameFormat format(frameSize, QVideoFrameFormat::Format_ARGB8888); format.setViewport(viewport); diff --git a/tests/auto/unit/multimediawidgets/qvideowidget/tst_qvideowidget.cpp b/tests/auto/unit/multimediawidgets/qvideowidget/tst_qvideowidget.cpp index de1c5a4e3..3cfe5d18e 100644 --- a/tests/auto/unit/multimediawidgets/qvideowidget/tst_qvideowidget.cpp +++ b/tests/auto/unit/multimediawidgets/qvideowidget/tst_qvideowidget.cpp @@ -184,7 +184,7 @@ void tst_QVideoWidget::fullScreen() Qt::WindowFlags windowFlags = widget.windowFlags(); - QSignalSpy spy(&widget, SIGNAL(fullScreenChanged(bool))); + QSignalSpy spy(&widget, &QVideoWidget::fullScreenChanged); // Test showing full screen with setFullScreen(true). widget.setFullScreen(true); diff --git a/tests/manual/minimal-player/minimal-player.cpp b/tests/manual/minimal-player/minimal-player.cpp index 70512dff3..17a11b050 100644 --- a/tests/manual/minimal-player/minimal-player.cpp +++ b/tests/manual/minimal-player/minimal-player.cpp @@ -42,7 +42,7 @@ int mainToggleWidgets(QString filename) return QApplication::exec(); } -int mainSimple(QString filename) +int mainSimple(QString filename, bool loop) { QMediaPlayer player; QVideoWidget widget1; @@ -52,6 +52,10 @@ int mainSimple(QString filename) player.setSource(filename); widget1.show(); + + if (loop) + player.setLoops(QMediaPlayer::Infinite); + player.play(); return QApplication::exec(); } @@ -69,6 +73,9 @@ int main(int argc, char **argv) QCommandLineOption toggleWidgetsOption{ "toggle-widgets", "Toggle between widgets." }; parser.addOption(toggleWidgetsOption); + QCommandLineOption loopOption{ "loop", "Loop." }; + parser.addOption(loopOption); + parser.process(app); if (parser.positionalArguments().isEmpty()) { @@ -81,5 +88,7 @@ int main(int argc, char **argv) if (parser.isSet(toggleWidgetsOption)) return mainToggleWidgets(filename); - return mainSimple(filename); + bool loop = parser.isSet(loopOption); + + return mainSimple(filename, loop); } |