diff options
Diffstat (limited to 'src/plugins')
79 files changed, 1539 insertions, 972 deletions
diff --git a/src/plugins/alsa/qalsaaudiodeviceinfo.h b/src/plugins/alsa/qalsaaudiodeviceinfo.h index 65675df54..cdf08bfab 100644 --- a/src/plugins/alsa/qalsaaudiodeviceinfo.h +++ b/src/plugins/alsa/qalsaaudiodeviceinfo.h @@ -79,15 +79,15 @@ public: bool testSettings(const QAudioFormat& format) const; void updateLists(); - QAudioFormat preferredFormat() const; - bool isFormatSupported(const QAudioFormat& format) const; - QString deviceName() const; - QStringList supportedCodecs(); - QList<int> supportedSampleRates(); - QList<int> supportedChannelCounts(); - QList<int> supportedSampleSizes(); - QList<QAudioFormat::Endian> supportedByteOrders(); - QList<QAudioFormat::SampleType> supportedSampleTypes(); + QAudioFormat preferredFormat() const override; + bool isFormatSupported(const QAudioFormat& format) const override; + QString deviceName() const override; + QStringList supportedCodecs() override; + QList<int> supportedSampleRates() override; + QList<int> supportedChannelCounts() override; + QList<int> supportedSampleSizes() override; + QList<QAudioFormat::Endian> supportedByteOrders() override; + QList<QAudioFormat::SampleType> supportedSampleTypes() override; static QByteArray defaultDevice(QAudio::Mode mode); static QList<QByteArray> availableDevices(QAudio::Mode); static QString deviceFromCardName(const QString &card); diff --git a/src/plugins/alsa/qalsaaudioinput.h b/src/plugins/alsa/qalsaaudioinput.h index fa9c954d7..62e1be039 100644 --- a/src/plugins/alsa/qalsaaudioinput.h +++ b/src/plugins/alsa/qalsaaudioinput.h @@ -103,26 +103,26 @@ public: qint64 read(char* data, qint64 len); - void start(QIODevice* device); - QIODevice* start(); - void stop(); - void reset(); - void suspend(); - void resume(); - int bytesReady() const; - int periodSize() const; - void setBufferSize(int value); - int bufferSize() const; - void setNotifyInterval(int milliSeconds); - int notifyInterval() const; - qint64 processedUSecs() const; - qint64 elapsedUSecs() const; - QAudio::Error error() const; - QAudio::State state() const; - void setFormat(const QAudioFormat& fmt); - QAudioFormat format() const; - void setVolume(qreal); - qreal volume() const; + void start(QIODevice* device) override; + QIODevice* start() override; + void stop() override; + void reset() override; + void suspend() override; + void resume() override; + int bytesReady() const override; + int periodSize() const override; + void setBufferSize(int value) override; + int bufferSize() const override; + void setNotifyInterval(int milliSeconds) override; + int notifyInterval() const override; + qint64 processedUSecs() const override; + qint64 elapsedUSecs() const override; + QAudio::Error error() const override; + QAudio::State state() const override; + void setFormat(const QAudioFormat& fmt) override; + QAudioFormat format() const override; + void setVolume(qreal) override; + qreal volume() const override; bool resuming; snd_pcm_t* handle; qint64 totalTimeValue; @@ -171,8 +171,8 @@ public: AlsaInputPrivate(QAlsaAudioInput* audio); ~AlsaInputPrivate(); - qint64 readData( char* data, qint64 len); - qint64 writeData(const char* data, qint64 len); + qint64 readData( char* data, qint64 len) override; + qint64 writeData(const char* data, qint64 len) override; void trigger(); private: diff --git a/src/plugins/alsa/qalsaaudiooutput.cpp b/src/plugins/alsa/qalsaaudiooutput.cpp index 5c8ae171c..ee5aee989 100644 --- a/src/plugins/alsa/qalsaaudiooutput.cpp +++ b/src/plugins/alsa/qalsaaudiooutput.cpp @@ -707,7 +707,7 @@ bool QAlsaAudioOutput::deviceReady() if(l > 0) { // Got some data to output - if(deviceState != QAudio::ActiveState) + if (deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState) return true; qint64 bytesWritten = write(audioBuffer,l); if (bytesWritten != l) diff --git a/src/plugins/alsa/qalsaaudiooutput.h b/src/plugins/alsa/qalsaaudiooutput.h index 8002322cb..72b9c2e4c 100644 --- a/src/plugins/alsa/qalsaaudiooutput.h +++ b/src/plugins/alsa/qalsaaudiooutput.h @@ -77,26 +77,26 @@ public: qint64 write( const char *data, qint64 len ); - void start(QIODevice* device); - QIODevice* start(); - void stop(); - void reset(); - void suspend(); - void resume(); - int bytesFree() const; - int periodSize() const; - void setBufferSize(int value); - int bufferSize() const; - void setNotifyInterval(int milliSeconds); - int notifyInterval() const; - qint64 processedUSecs() const; - qint64 elapsedUSecs() const; - QAudio::Error error() const; - QAudio::State state() const; - void setFormat(const QAudioFormat& fmt); - QAudioFormat format() const; - void setVolume(qreal); - qreal volume() const; + void start(QIODevice* device) override; + QIODevice* start() override; + void stop() override; + void reset() override; + void suspend() override; + void resume() override; + int bytesFree() const override; + int periodSize() const override; + void setBufferSize(int value) override; + int bufferSize() const override; + void setNotifyInterval(int milliSeconds) override; + int notifyInterval() const override; + qint64 processedUSecs() const override; + qint64 elapsedUSecs() const override; + QAudio::Error error() const override; + QAudio::State state() const override; + void setFormat(const QAudioFormat& fmt) override; + QAudioFormat format() const override; + void setVolume(qreal) override; + qreal volume() const override; QIODevice* audioSource; @@ -151,8 +151,8 @@ public: AlsaOutputPrivate(QAlsaAudioOutput* audio); ~AlsaOutputPrivate(); - qint64 readData( char* data, qint64 len); - qint64 writeData(const char* data, qint64 len); + qint64 readData( char* data, qint64 len) override; + qint64 writeData(const char* data, qint64 len) override; private: QAlsaAudioOutput *audioDevice; diff --git a/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp b/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp index 7cc3ad619..c0484a139 100644 --- a/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp +++ b/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp @@ -188,19 +188,30 @@ void QAndroidCaptureSession::setState(QMediaRecorder::State state) start(); break; case QMediaRecorder::PausedState: - // Not supported by Android API - qWarning("QMediaRecorder::PausedState is not supported on Android"); + pause(); break; } } void QAndroidCaptureSession::start() { - if (m_state == QMediaRecorder::RecordingState || m_status != QMediaRecorder::LoadedStatus) + if (m_state == QMediaRecorder::RecordingState + || (m_status != QMediaRecorder::LoadedStatus && m_status != QMediaRecorder::PausedStatus)) return; setStatus(QMediaRecorder::StartingStatus); + if (m_state == QMediaRecorder::PausedState) { + if (!m_mediaRecorder || !m_mediaRecorder->resume()) { + emit error(QMediaRecorder::FormatError, QLatin1String("Unable to resume the media recorder.")); + if (m_cameraSession) + restartViewfinder(); + } else { + updateStartState(); + } + return; + } + if (m_mediaRecorder) { m_mediaRecorder->release(); delete m_mediaRecorder; @@ -289,7 +300,11 @@ void QAndroidCaptureSession::start() restartViewfinder(); return; } + updateStartState(); +} +void QAndroidCaptureSession::updateStartState() +{ m_elapsedTime.start(); m_notifyTimer.start(); updateDuration(); @@ -302,9 +317,20 @@ void QAndroidCaptureSession::start() m_cameraSession->camera()->setupPreviewFrameCallback(); } + QMediaRecorder::State oldState = m_state; + QMediaRecorder::Status oldStatus = m_status; + m_state = QMediaRecorder::RecordingState; - emit stateChanged(m_state); - setStatus(QMediaRecorder::RecordingStatus); + m_status = QMediaRecorder::RecordingStatus; + + m_actualOutputLocation = m_usedOutputLocation; + emit actualLocationChanged(m_actualOutputLocation); + + if (m_state != oldState) + emit stateChanged(m_state); + + if (m_status != oldStatus) + emit statusChanged(m_status); } void QAndroidCaptureSession::stop(bool error) @@ -317,6 +343,7 @@ void QAndroidCaptureSession::stop(bool error) m_mediaRecorder->stop(); m_notifyTimer.stop(); updateDuration(); + m_previousElapsedTime = 0; m_elapsedTime.invalidate(); m_mediaRecorder->release(); delete m_mediaRecorder; @@ -347,6 +374,23 @@ void QAndroidCaptureSession::stop(bool error) setStatus(QMediaRecorder::LoadedStatus); } +void QAndroidCaptureSession::pause() +{ + if (m_state == QMediaRecorder::PausedState || m_mediaRecorder == 0) + return; + + setStatus(QMediaRecorder::PausedStatus); + + m_mediaRecorder->pause(); + m_notifyTimer.stop(); + updateDuration(); + m_previousElapsedTime = m_duration; + m_elapsedTime.invalidate(); + + m_state = QMediaRecorder::PausedState; + emit stateChanged(m_state); +} + void QAndroidCaptureSession::setStatus(QMediaRecorder::Status status) { if (m_status == status) @@ -503,7 +547,7 @@ void QAndroidCaptureSession::restartViewfinder() void QAndroidCaptureSession::updateDuration() { if (m_elapsedTime.isValid()) - m_duration = m_elapsedTime.elapsed(); + m_duration = m_elapsedTime.elapsed() + m_previousElapsedTime; emit durationChanged(m_duration); } diff --git a/src/plugins/android/src/mediacapture/qandroidcapturesession.h b/src/plugins/android/src/mediacapture/qandroidcapturesession.h index 8cfb9ad2a..cee148a4c 100644 --- a/src/plugins/android/src/mediacapture/qandroidcapturesession.h +++ b/src/plugins/android/src/mediacapture/qandroidcapturesession.h @@ -136,7 +136,9 @@ private: CaptureProfile getProfile(int id); void start(); + void updateStartState(); void stop(bool error = false); + void pause(); void setStatus(QMediaRecorder::Status status); @@ -154,6 +156,7 @@ private: QElapsedTimer m_elapsedTime; QTimer m_notifyTimer; qint64 m_duration; + qint64 m_previousElapsedTime = 0; QMediaRecorder::State m_state; QMediaRecorder::Status m_status; diff --git a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp index ef86af896..66eafc765 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp +++ b/src/plugins/android/src/mediaplayer/qandroidmetadatareadercontrol.cpp @@ -176,12 +176,12 @@ void QAndroidMetaDataReaderControl::extractMetadata(QAndroidMetaDataReaderContro if (!string.isNull()) { metadata.insert(isVideo ? QMediaMetaData::LeadPerformer : QMediaMetaData::ContributingArtist, - string.split('/', QString::SkipEmptyParts)); + string.split('/', Qt::SkipEmptyParts)); } string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Author); if (!string.isNull()) - metadata.insert(QMediaMetaData::Author, string.split('/', QString::SkipEmptyParts)); + metadata.insert(QMediaMetaData::Author, string.split('/', Qt::SkipEmptyParts)); string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Bitrate); if (!string.isNull()) { @@ -196,7 +196,7 @@ void QAndroidMetaDataReaderControl::extractMetadata(QAndroidMetaDataReaderContro string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Composer); if (!string.isNull()) - metadata.insert(QMediaMetaData::Composer, string.split('/', QString::SkipEmptyParts)); + metadata.insert(QMediaMetaData::Composer, string.split('/', Qt::SkipEmptyParts)); string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Date); if (!string.isNull()) @@ -231,7 +231,7 @@ void QAndroidMetaDataReaderControl::extractMetadata(QAndroidMetaDataReaderContro string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Writer); if (!string.isNull()) - metadata.insert(QMediaMetaData::Writer, string.split('/', QString::SkipEmptyParts)); + metadata.insert(QMediaMetaData::Writer, string.split('/', Qt::SkipEmptyParts)); string = retriever.extractMetadata(AndroidMediaMetadataRetriever::Year); if (!string.isNull()) diff --git a/src/plugins/android/src/wrappers/jni/androidcamera.cpp b/src/plugins/android/src/wrappers/jni/androidcamera.cpp index 3ea7bc773..50baaed21 100644 --- a/src/plugins/android/src/wrappers/jni/androidcamera.cpp +++ b/src/plugins/android/src/wrappers/jni/androidcamera.cpp @@ -797,6 +797,12 @@ void AndroidCamera::getCameraInfo(int id, AndroidCameraInfo *info) default: break; } + // Add a number to allow correct access to cameras on systems with two + // (and more) front/back cameras + if (id > 1) { + info->name.append(QByteArray::number(id)); + info->description.append(QString(" %1").arg(id)); + } } void AndroidCamera::startPreview() diff --git a/src/plugins/android/src/wrappers/jni/androidmediaplayer.cpp b/src/plugins/android/src/wrappers/jni/androidmediaplayer.cpp index f899481f0..de8422b86 100644 --- a/src/plugins/android/src/wrappers/jni/androidmediaplayer.cpp +++ b/src/plugins/android/src/wrappers/jni/androidmediaplayer.cpp @@ -271,7 +271,7 @@ void AndroidMediaPlayer::setAudioRole(QAudio::Role role) void AndroidMediaPlayer::setCustomAudioRole(const QString &role) { - QStringList roles = role.split(",", QString::SkipEmptyParts); + QStringList roles = role.split(",", Qt::SkipEmptyParts); int type = 0; // CONTENT_TYPE_UNKNOWN int usage = 0; // USAGE_UNKNOWN diff --git a/src/plugins/android/src/wrappers/jni/androidmediarecorder.cpp b/src/plugins/android/src/wrappers/jni/androidmediarecorder.cpp index d607ab806..10508a358 100644 --- a/src/plugins/android/src/wrappers/jni/androidmediarecorder.cpp +++ b/src/plugins/android/src/wrappers/jni/androidmediarecorder.cpp @@ -213,6 +213,32 @@ void AndroidMediaRecorder::stop() } } +void AndroidMediaRecorder::pause() +{ + QJNIEnvironmentPrivate env; + m_mediaRecorder.callMethod<void>("pause"); + if (env->ExceptionCheck()) { +#ifdef QT_DEBUG + env->ExceptionDescribe(); +#endif + env->ExceptionClear(); + } +} + +bool AndroidMediaRecorder::resume() +{ + QJNIEnvironmentPrivate env; + m_mediaRecorder.callMethod<void>("resume"); + if (env->ExceptionCheck()) { +#ifdef QT_DEBUG + env->ExceptionDescribe(); +#endif + env->ExceptionClear(); + return false; + } + return true; +} + void AndroidMediaRecorder::setAudioChannels(int numChannels) { m_mediaRecorder.callMethod<void>("setAudioChannels", "(I)V", numChannels); diff --git a/src/plugins/android/src/wrappers/jni/androidmediarecorder.h b/src/plugins/android/src/wrappers/jni/androidmediarecorder.h index e4b3a80ea..55b370cf1 100644 --- a/src/plugins/android/src/wrappers/jni/androidmediarecorder.h +++ b/src/plugins/android/src/wrappers/jni/androidmediarecorder.h @@ -138,6 +138,8 @@ public: bool start(); void stop(); + void pause(); + bool resume(); void setAudioChannels(int numChannels); void setAudioEncoder(AudioEncoder encoder); diff --git a/src/plugins/avfoundation/camera/avfcameraflashcontrol.mm b/src/plugins/avfoundation/camera/avfcameraflashcontrol.mm index 42303ce17..1e27dc472 100644 --- a/src/plugins/avfoundation/camera/avfcameraflashcontrol.mm +++ b/src/plugins/avfoundation/camera/avfcameraflashcontrol.mm @@ -171,6 +171,20 @@ bool AVFCameraFlashControl::applyFlashSettings() return false; } + auto setAvTorchModeSafe = [&captureDevice](AVCaptureTorchMode avTorchMode) { + if ([captureDevice isTorchModeSupported:avTorchMode]) + captureDevice.torchMode = avTorchMode; + else + qDebugCamera() << Q_FUNC_INFO << "Attempt to setup unsupported torch mode " << avTorchMode; + }; + + auto setAvFlashModeSafe = [&captureDevice](AVCaptureFlashMode avFlashMode) { + if ([captureDevice isFlashModeSupported:avFlashMode]) + captureDevice.flashMode = avFlashMode; + else + qDebugCamera() << Q_FUNC_INFO << "Attempt to setup unsupported flash mode " << avFlashMode; + }; + if (!isFlashModeSupported(m_flashMode)) { qDebugCamera() << Q_FUNC_INFO << "unsupported mode" << m_flashMode; return false; @@ -192,7 +206,7 @@ bool AVFCameraFlashControl::applyFlashSettings() return false; } #endif - captureDevice.torchMode = AVCaptureTorchModeOff; + setAvTorchModeSafe(AVCaptureTorchModeOff); } #ifdef Q_OS_IOS if (![captureDevice isFlashAvailable]) { @@ -209,7 +223,7 @@ bool AVFCameraFlashControl::applyFlashSettings() return false; } #endif - captureDevice.flashMode = AVCaptureFlashModeOff; + setAvFlashModeSafe(AVCaptureFlashModeOff); } #ifdef Q_OS_IOS @@ -221,13 +235,13 @@ bool AVFCameraFlashControl::applyFlashSettings() } if (m_flashMode == QCameraExposure::FlashOff) - captureDevice.flashMode = AVCaptureFlashModeOff; + setAvFlashModeSafe(AVCaptureFlashModeOff); else if (m_flashMode == QCameraExposure::FlashOn) - captureDevice.flashMode = AVCaptureFlashModeOn; + setAvFlashModeSafe(AVCaptureFlashModeOn); else if (m_flashMode == QCameraExposure::FlashAuto) - captureDevice.flashMode = AVCaptureFlashModeAuto; + setAvFlashModeSafe(AVCaptureFlashModeAuto); else if (m_flashMode == QCameraExposure::FlashVideoLight) - captureDevice.torchMode = AVCaptureTorchModeOn; + setAvTorchModeSafe(AVCaptureTorchModeOn); return true; } diff --git a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm b/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm index 0359f5d0a..7bf9de071 100644 --- a/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm +++ b/src/plugins/avfoundation/camera/avfcamerarenderercontrol.mm @@ -278,7 +278,8 @@ AVFCameraRendererControl::AVFCameraRendererControl(QObject *parent) AVFCameraRendererControl::~AVFCameraRendererControl() { - [m_cameraSession->captureSession() removeOutput:m_videoDataOutput]; + if ([m_cameraSession->captureSession().outputs containsObject:m_videoDataOutput]) + [m_cameraSession->captureSession() removeOutput:m_videoDataOutput]; [m_viewfinderFramesDelegate release]; if (m_delegateQueue) dispatch_release(m_delegateQueue); @@ -297,11 +298,9 @@ void AVFCameraRendererControl::setSurface(QAbstractVideoSurface *surface) { if (m_surface != surface) { m_surface = surface; -#ifdef Q_OS_IOS m_supportsTextures = m_surface - ? m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).contains(QVideoFrame::Format_BGRA32) + ? !m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).isEmpty() : false; -#endif Q_EMIT surfaceChanged(surface); } } diff --git a/src/plugins/avfoundation/camera/avfcameraservice.h b/src/plugins/avfoundation/camera/avfcameraservice.h index 2969882b0..1397a7dee 100644 --- a/src/plugins/avfoundation/camera/avfcameraservice.h +++ b/src/plugins/avfoundation/camera/avfcameraservice.h @@ -70,6 +70,8 @@ class AVFMediaRecorderControlIOS; class AVFAudioEncoderSettingsControl; class AVFVideoEncoderSettingsControl; class AVFMediaContainerControl; +class AVFCameraWindowControl; +class AVFCaptureDestinationControl; class AVFCameraService : public QMediaService { @@ -99,6 +101,7 @@ public: AVFAudioEncoderSettingsControl *audioEncoderSettingsControl() const { return m_audioEncoderSettingsControl; } AVFVideoEncoderSettingsControl *videoEncoderSettingsControl() const {return m_videoEncoderSettingsControl; } AVFMediaContainerControl *mediaContainerControl() const { return m_mediaContainerControl; } + AVFCaptureDestinationControl *captureDestinationControl() const { return m_captureDestinationControl; } private: AVFCameraSession *m_session; @@ -120,6 +123,8 @@ private: AVFAudioEncoderSettingsControl *m_audioEncoderSettingsControl; AVFVideoEncoderSettingsControl *m_videoEncoderSettingsControl; AVFMediaContainerControl *m_mediaContainerControl; + AVFCameraWindowControl *m_captureWindowControl; + AVFCaptureDestinationControl *m_captureDestinationControl; }; QT_END_NAMESPACE diff --git a/src/plugins/avfoundation/camera/avfcameraservice.mm b/src/plugins/avfoundation/camera/avfcameraservice.mm index 33b4b72aa..79bf73910 100644 --- a/src/plugins/avfoundation/camera/avfcameraservice.mm +++ b/src/plugins/avfoundation/camera/avfcameraservice.mm @@ -61,6 +61,8 @@ #include "avfaudioencodersettingscontrol.h" #include "avfvideoencodersettingscontrol.h" #include "avfmediacontainercontrol.h" +#include "avfcapturedestinationcontrol.h" +#include "avfcamerawindowcontrol.h" #ifdef Q_OS_IOS #include "avfcamerazoomcontrol.h" @@ -74,7 +76,8 @@ QT_USE_NAMESPACE AVFCameraService::AVFCameraService(QObject *parent): QMediaService(parent), - m_videoOutput(nullptr) + m_videoOutput(nullptr), + m_captureWindowControl(nullptr) { m_session = new AVFCameraSession(this); m_cameraControl = new AVFCameraControl(this); @@ -109,6 +112,7 @@ AVFCameraService::AVFCameraService(QObject *parent): m_audioEncoderSettingsControl = new AVFAudioEncoderSettingsControl(this); m_videoEncoderSettingsControl = new AVFVideoEncoderSettingsControl(this); m_mediaContainerControl = new AVFMediaContainerControl(this); + m_captureDestinationControl = new AVFCaptureDestinationControl; } AVFCameraService::~AVFCameraService() @@ -119,6 +123,12 @@ AVFCameraService::~AVFCameraService() delete m_recorderControl; #endif + if (m_captureWindowControl) { + m_session->setCapturePreviewOutput(nullptr); + delete m_captureWindowControl; + m_captureWindowControl = nullptr; + } + if (m_videoOutput) { m_session->setVideoOutput(nullptr); delete m_videoOutput; @@ -143,6 +153,7 @@ AVFCameraService::~AVFCameraService() delete m_audioEncoderSettingsControl; delete m_videoEncoderSettingsControl; delete m_mediaContainerControl; + delete m_captureDestinationControl; delete m_session; } @@ -210,6 +221,17 @@ QMediaControl *AVFCameraService::requestControl(const char *name) return m_cameraZoomControl; #endif + if (qstrcmp(name, QCameraCaptureDestinationControl_iid) == 0) + return m_captureDestinationControl; + + if (!m_captureWindowControl) { + if (qstrcmp(name, QVideoWindowControl_iid) == 0) { + m_captureWindowControl = new AVFCameraWindowControl(this); + m_session->setCapturePreviewOutput(m_captureWindowControl); + return m_captureWindowControl; + } + } + if (!m_videoOutput) { if (qstrcmp(name, QVideoRendererControl_iid) == 0) m_videoOutput = new AVFCameraRendererControl(this); @@ -234,6 +256,11 @@ void AVFCameraService::releaseControl(QMediaControl *control) delete m_videoOutput; m_videoOutput = nullptr; } + else if (m_captureWindowControl == control) { + m_session->setCapturePreviewOutput(nullptr); + delete m_captureWindowControl; + m_captureWindowControl = nullptr; + } } diff --git a/src/plugins/avfoundation/camera/avfcamerasession.h b/src/plugins/avfoundation/camera/avfcamerasession.h index 103ec0e17..a449bb806 100644 --- a/src/plugins/avfoundation/camera/avfcamerasession.h +++ b/src/plugins/avfoundation/camera/avfcamerasession.h @@ -54,6 +54,7 @@ class AVFCameraControl; class AVFCameraService; class AVFCameraRendererControl; class AVFMediaVideoProbeControl; +class AVFCameraWindowControl; struct AVFCameraInfo { @@ -79,6 +80,7 @@ public: AVFCameraInfo activeCameraInfo() const { return m_activeCameraInfo; } void setVideoOutput(AVFCameraRendererControl *output); + void setCapturePreviewOutput(AVFCameraWindowControl *output); AVCaptureSession *captureSession() const { return m_captureSession; } AVCaptureDevice *videoCaptureDevice() const; @@ -122,6 +124,7 @@ private: AVFCameraService *m_service; AVFCameraRendererControl *m_videoOutput; + AVFCameraWindowControl *m_capturePreviewWindowOutput; QCamera::State m_state; bool m_active; diff --git a/src/plugins/avfoundation/camera/avfcamerasession.mm b/src/plugins/avfoundation/camera/avfcamerasession.mm index 3c5f8f09a..6ee9c2636 100644 --- a/src/plugins/avfoundation/camera/avfcamerasession.mm +++ b/src/plugins/avfoundation/camera/avfcamerasession.mm @@ -48,6 +48,7 @@ #include "avfcameraviewfindersettingscontrol.h" #include "avfimageencodercontrol.h" #include "avfcamerautility.h" +#include "avfcamerawindowcontrol.h" #include <CoreFoundation/CoreFoundation.h> #include <Foundation/Foundation.h> @@ -146,6 +147,7 @@ QList<AVFCameraInfo> AVFCameraSession::m_cameraDevices; AVFCameraSession::AVFCameraSession(AVFCameraService *service, QObject *parent) : QObject(parent) , m_service(service) + , m_capturePreviewWindowOutput(nullptr) , m_state(QCamera::UnloadedState) , m_active(false) , m_videoInput(nil) @@ -160,6 +162,10 @@ AVFCameraSession::AVFCameraSession(AVFCameraService *service, QObject *parent) AVFCameraSession::~AVFCameraSession() { + if (m_capturePreviewWindowOutput) { + m_capturePreviewWindowOutput->setLayer(nil); + } + if (m_videoInput) { [m_captureSession removeInput:m_videoInput]; [m_videoInput release]; @@ -257,6 +263,29 @@ void AVFCameraSession::setVideoOutput(AVFCameraRendererControl *output) output->configureAVCaptureSession(this); } +void AVFCameraSession::setCapturePreviewOutput(AVFCameraWindowControl *output) +{ +#ifdef QT_DEBUG_AVF + qDebug() << Q_FUNC_INFO << output; +#endif + + if (m_capturePreviewWindowOutput == output) + return; + + if (m_capturePreviewWindowOutput) + m_capturePreviewWindowOutput->setLayer(nil); + + m_capturePreviewWindowOutput = output; + + if (m_capturePreviewWindowOutput) { + AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:m_captureSession]; + m_capturePreviewWindowOutput->setLayer(previewLayer); + if (AVFCameraViewfinderSettingsControl2 *vfControl = m_service->viewfinderSettingsControl2()) { + m_capturePreviewWindowOutput->setNativeSize(vfControl->viewfinderSettings().resolution()); + } + } +} + AVCaptureDevice *AVFCameraSession::videoCaptureDevice() const { if (m_videoInput) @@ -409,6 +438,10 @@ bool AVFCameraSession::applyViewfinderSettings() } vfControl->applySettings(vfSettings); + + if (m_capturePreviewWindowOutput) + m_capturePreviewWindowOutput->setNativeSize(vfControl->viewfinderSettings().resolution()); + return !vfSettings.isNull(); } diff --git a/src/plugins/avfoundation/camera/avfcamerautility.h b/src/plugins/avfoundation/camera/avfcamerautility.h index 370302b1c..33956fc8a 100644 --- a/src/plugins/avfoundation/camera/avfcamerautility.h +++ b/src/plugins/avfoundation/camera/avfcamerautility.h @@ -161,7 +161,7 @@ QSize qt_device_format_high_resolution(AVCaptureDeviceFormat *format); QSize qt_device_format_pixel_aspect_ratio(AVCaptureDeviceFormat *format); QVector<AVFPSRange> qt_device_format_framerates(AVCaptureDeviceFormat *format); AVCaptureDeviceFormat *qt_find_best_resolution_match(AVCaptureDevice *captureDevice, const QSize &res, - FourCharCode preferredFormat); + FourCharCode preferredFormat, bool stillImage = true); AVCaptureDeviceFormat *qt_find_best_framerate_match(AVCaptureDevice *captureDevice, FourCharCode preferredFormat, Float64 fps); diff --git a/src/plugins/avfoundation/camera/avfcamerautility.mm b/src/plugins/avfoundation/camera/avfcamerautility.mm index 8c743dcbc..25ccc4b01 100644 --- a/src/plugins/avfoundation/camera/avfcamerautility.mm +++ b/src/plugins/avfoundation/camera/avfcamerautility.mm @@ -239,7 +239,8 @@ QSize qt_device_format_pixel_aspect_ratio(AVCaptureDeviceFormat *format) AVCaptureDeviceFormat *qt_find_best_resolution_match(AVCaptureDevice *captureDevice, const QSize &request, - FourCharCode filter) + FourCharCode filter, + bool stillImage) { Q_ASSERT(captureDevice); Q_ASSERT(!request.isNull() && request.isValid()); @@ -254,7 +255,7 @@ AVCaptureDeviceFormat *qt_find_best_resolution_match(AVCaptureDevice *captureDev if (qt_device_format_resolution(format) == request) return format; // iOS only (still images). - if (qt_device_format_high_resolution(format) == request) + if (stillImage && qt_device_format_high_resolution(format) == request) return format; } @@ -272,7 +273,7 @@ AVCaptureDeviceFormat *qt_find_best_resolution_match(AVCaptureDevice *captureDev if (!res.isNull() && res.isValid() && qt_area_sane(res)) pairs << FormatPair(res, format); const QSize highRes(qt_device_format_high_resolution(format)); - if (!highRes.isNull() && highRes.isValid() && qt_area_sane(highRes)) + if (stillImage && !highRes.isNull() && highRes.isValid() && qt_area_sane(highRes)) pairs << FormatPair(highRes, format); } diff --git a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm index a77d7de03..dd0393f96 100644 --- a/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm +++ b/src/plugins/avfoundation/camera/avfcameraviewfindersettingscontrol.mm @@ -185,7 +185,16 @@ void AVFCameraViewfinderSettingsControl2::setViewfinderSettings(const QCameraVie return; m_settings = settings; +#if defined(Q_OS_IOS) + bool active = m_service->session()->state() == QCamera::ActiveState; + if (active) + [m_service->session()->captureSession() beginConfiguration]; applySettings(m_settings); + if (active) + [m_service->session()->captureSession() commitConfiguration]; +#else + applySettings(m_settings); +#endif } QVideoFrame::PixelFormat AVFCameraViewfinderSettingsControl2::QtPixelFormatFromCVFormat(unsigned avPixelFormat) @@ -264,7 +273,7 @@ AVCaptureDeviceFormat *AVFCameraViewfinderSettingsControl2::findBestFormatMatch( // Either the exact match (including high resolution for images on iOS) // or a format with a resolution close to the requested one. return qt_find_best_resolution_match(captureDevice, resolution, - m_service->session()->defaultCodec()); + m_service->session()->defaultCodec(), false); } // No resolution requested, what about framerates? @@ -332,7 +341,10 @@ bool AVFCameraViewfinderSettingsControl2::convertPixelFormatIfSupported(QVideoFr if (m_service->videoOutput()->surface()) { const QAbstractVideoSurface *surface = m_service->videoOutput()->surface(); - if (!surface->supportedPixelFormats().contains(qtFormat)) + QAbstractVideoBuffer::HandleType h = m_service->videoOutput()->supportsTextures() + ? QAbstractVideoBuffer::GLTextureHandle + : QAbstractVideoBuffer::NoHandle; + if (!surface->supportedPixelFormats(h).contains(qtFormat)) return false; } @@ -380,21 +392,19 @@ bool AVFCameraViewfinderSettingsControl2::applySettings(const QCameraViewfinderS // format, or if no surface is set, the preferred capture device format const QVector<QVideoFrame::PixelFormat> deviceFormats = viewfinderPixelFormats(); - QVideoFrame::PixelFormat pickedFormat = deviceFormats.first(); - QAbstractVideoSurface *surface = m_service->videoOutput()->surface(); + QVideoFrame::PixelFormat pickedFormat = deviceFormats.first(); if (surface) { - if (m_service->videoOutput()->supportsTextures()) { - pickedFormat = QVideoFrame::Format_ARGB32; - } else { - QList<QVideoFrame::PixelFormat> surfaceFormats = surface->supportedPixelFormats(); - - for (int i = 0; i < surfaceFormats.count(); ++i) { - const QVideoFrame::PixelFormat surfaceFormat = surfaceFormats.at(i); - if (deviceFormats.contains(surfaceFormat)) { - pickedFormat = surfaceFormat; - break; - } + pickedFormat = QVideoFrame::Format_Invalid; + QAbstractVideoBuffer::HandleType h = m_service->videoOutput()->supportsTextures() + ? QAbstractVideoBuffer::GLTextureHandle + : QAbstractVideoBuffer::NoHandle; + QList<QVideoFrame::PixelFormat> surfaceFormats = surface->supportedPixelFormats(h); + for (int i = 0; i < surfaceFormats.count(); ++i) { + const QVideoFrame::PixelFormat surfaceFormat = surfaceFormats.at(i); + if (deviceFormats.contains(surfaceFormat)) { + pickedFormat = surfaceFormat; + break; } } } @@ -402,13 +412,15 @@ bool AVFCameraViewfinderSettingsControl2::applySettings(const QCameraViewfinderS CVPixelFormatFromQtFormat(pickedFormat, avfPixelFormat); } - if (avfPixelFormat != 0) { - NSMutableDictionary *videoSettings = [NSMutableDictionary dictionaryWithCapacity:1]; - [videoSettings setObject:[NSNumber numberWithUnsignedInt:avfPixelFormat] - forKey:(id)kCVPixelBufferPixelFormatTypeKey]; + NSMutableDictionary *videoSettings = [NSMutableDictionary dictionaryWithCapacity:1]; + [videoSettings setObject:[NSNumber numberWithUnsignedInt:avfPixelFormat] + forKey:(id)kCVPixelBufferPixelFormatTypeKey]; - videoOutput.videoSettings = videoSettings; - } + const AVFConfigurationLock lock(captureDevice); + if (!lock) + qWarning("Failed to set active format (lock failed)"); + + videoOutput.videoSettings = videoSettings; } qt_set_framerate_limits(captureDevice, videoConnection(), settings.minimumFrameRate(), settings.maximumFrameRate()); diff --git a/src/plugins/avfoundation/camera/avfcamerawindowcontrol.h b/src/plugins/avfoundation/camera/avfcamerawindowcontrol.h new file mode 100644 index 000000000..d1a950e38 --- /dev/null +++ b/src/plugins/avfoundation/camera/avfcamerawindowcontrol.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef AVFCAMERAWINDOWCONTROL_H +#define AVFCAMERAWINDOWCONTROL_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QVideoWindowControl> + +@class AVCaptureVideoPreviewLayer; +#if defined(Q_OS_MACOS) +@class NSView; +typedef NSView NativeView; +#else +@class UIView; +typedef UIView NativeView; +#endif + +QT_BEGIN_NAMESPACE + +class AVFCameraWindowControl : public QVideoWindowControl +{ + Q_OBJECT +public: + AVFCameraWindowControl(QObject *parent = nullptr); + virtual ~AVFCameraWindowControl() override; + + // QVideoWindowControl interface +public: + WId winId() const override; + void setWinId(WId id) override; + + QRect displayRect() const override; + void setDisplayRect(const QRect &rect) override; + + bool isFullScreen() const override; + void setFullScreen(bool fullScreen) override; + + void repaint() override; + + QSize nativeSize() const override; + + Qt::AspectRatioMode aspectRatioMode() const override; + void setAspectRatioMode(Qt::AspectRatioMode mode) override; + + int brightness() const override; + void setBrightness(int brightness) override; + + int contrast() const override; + void setContrast(int contrast) override; + + int hue() const override; + void setHue(int hue) override; + + int saturation() const override; + void setSaturation(int saturation) override; + + // AVF Camera implementation details + void setNativeSize(QSize size); + void setLayer(AVCaptureVideoPreviewLayer *capturePreviewLayer); + +private: + void updateAspectRatio(); + void updateCaptureLayerBounds(); + + void retainNativeLayer(); + void releaseNativeLayer(); + + void attachNativeLayer(); + void detachNativeLayer(); + + WId m_winId{0}; + QRect m_displayRect; + bool m_fullscreen{false}; + Qt::AspectRatioMode m_aspectRatioMode{Qt::IgnoreAspectRatio}; + QSize m_nativeSize; + AVCaptureVideoPreviewLayer *m_captureLayer{nullptr}; + NativeView *m_nativeView{nullptr}; +}; + +QT_END_NAMESPACE + +#endif // AVFCAMERAWINDOWCONTROL_H diff --git a/src/plugins/avfoundation/camera/avfcamerawindowcontrol.mm b/src/plugins/avfoundation/camera/avfcamerawindowcontrol.mm new file mode 100644 index 000000000..5154d0646 --- /dev/null +++ b/src/plugins/avfoundation/camera/avfcamerawindowcontrol.mm @@ -0,0 +1,262 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "avfcamerawindowcontrol.h" + +#import <AVFoundation/AVFoundation.h> +#import <QuartzCore/CATransaction.h> + +#if QT_HAS_INCLUDE(<AppKit/AppKit.h>) +#import <AppKit/AppKit.h> +#endif + +#if QT_HAS_INCLUDE(<UIKit/UIKit.h>) +#import <UIKit/UIKit.h> +#endif + +QT_USE_NAMESPACE + +AVFCameraWindowControl::AVFCameraWindowControl(QObject *parent) + : QVideoWindowControl(parent) +{ + setObjectName(QStringLiteral("AVFCameraWindowControl")); +} + +AVFCameraWindowControl::~AVFCameraWindowControl() +{ + releaseNativeLayer(); +} + +WId AVFCameraWindowControl::winId() const +{ + return m_winId; +} + +void AVFCameraWindowControl::setWinId(WId id) +{ + if (m_winId == id) + return; + + m_winId = id; + + detachNativeLayer(); + m_nativeView = (NativeView*)m_winId; + attachNativeLayer(); +} + +QRect AVFCameraWindowControl::displayRect() const +{ + return m_displayRect; +} + +void AVFCameraWindowControl::setDisplayRect(const QRect &rect) +{ + if (m_displayRect != rect) { + m_displayRect = rect; + updateCaptureLayerBounds(); + } +} + +bool AVFCameraWindowControl::isFullScreen() const +{ + return m_fullscreen; +} + +void AVFCameraWindowControl::setFullScreen(bool fullscreen) +{ + if (m_fullscreen != fullscreen) { + m_fullscreen = fullscreen; + Q_EMIT fullScreenChanged(fullscreen); + } +} + +void AVFCameraWindowControl::repaint() +{ + if (m_captureLayer) + [m_captureLayer setNeedsDisplay]; +} + +QSize AVFCameraWindowControl::nativeSize() const +{ + return m_nativeSize; +} + +void AVFCameraWindowControl::setNativeSize(QSize size) +{ + if (m_nativeSize != size) { + m_nativeSize = size; + Q_EMIT nativeSizeChanged(); + } +} + +Qt::AspectRatioMode AVFCameraWindowControl::aspectRatioMode() const +{ + return m_aspectRatioMode; +} + +void AVFCameraWindowControl::setAspectRatioMode(Qt::AspectRatioMode mode) +{ + if (m_aspectRatioMode != mode) { + m_aspectRatioMode = mode; + updateAspectRatio(); + } +} + +int AVFCameraWindowControl::brightness() const +{ + return 0; +} + +void AVFCameraWindowControl::setBrightness(int brightness) +{ + if (0 != brightness) + qWarning("AVFCameraWindowControl doesn't support changing Brightness"); +} + +int AVFCameraWindowControl::contrast() const +{ + return 0; +} + +void AVFCameraWindowControl::setContrast(int contrast) +{ + if (0 != contrast) + qWarning("AVFCameraWindowControl doesn't support changing Contrast"); +} + +int AVFCameraWindowControl::hue() const +{ + return 0; +} + +void AVFCameraWindowControl::setHue(int hue) +{ + if (0 != hue) + qWarning("AVFCameraWindowControl doesn't support changing Hue"); +} + +int AVFCameraWindowControl::saturation() const +{ + return 0; +} + +void AVFCameraWindowControl::setSaturation(int saturation) +{ + if (0 != saturation) + qWarning("AVFCameraWindowControl doesn't support changing Saturation"); +} + +void AVFCameraWindowControl::setLayer(AVCaptureVideoPreviewLayer *capturePreviewLayer) +{ + if (m_captureLayer == capturePreviewLayer) + return; + + releaseNativeLayer(); + + m_captureLayer = capturePreviewLayer; + + if (m_captureLayer) + retainNativeLayer(); +} + +void AVFCameraWindowControl::updateAspectRatio() +{ + if (m_captureLayer) { + switch (m_aspectRatioMode) { + case Qt::IgnoreAspectRatio: + [m_captureLayer setVideoGravity:AVLayerVideoGravityResize]; + break; + case Qt::KeepAspectRatio: + [m_captureLayer setVideoGravity:AVLayerVideoGravityResizeAspect]; + break; + case Qt::KeepAspectRatioByExpanding: + [m_captureLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill]; + break; + default: + break; + } + } +} + +void AVFCameraWindowControl::updateCaptureLayerBounds() +{ + if (m_captureLayer && m_nativeView) { + [CATransaction begin]; + [CATransaction setDisableActions: YES]; // disable animation/flicks + m_captureLayer.frame = m_displayRect.toCGRect(); + [CATransaction commit]; + } +} + +void AVFCameraWindowControl::retainNativeLayer() +{ + [m_captureLayer retain]; + + updateAspectRatio(); + attachNativeLayer(); +} + +void AVFCameraWindowControl::releaseNativeLayer() +{ + if (m_captureLayer) { + detachNativeLayer(); + [m_captureLayer release]; + m_captureLayer = nullptr; + } +} + +void AVFCameraWindowControl::attachNativeLayer() +{ + if (m_captureLayer && m_nativeView) { +#if defined(Q_OS_MACOS) + m_nativeView.wantsLayer = YES; +#endif + CALayer *nativeLayer = m_nativeView.layer; + [nativeLayer addSublayer:m_captureLayer]; + updateCaptureLayerBounds(); + } +} + +void AVFCameraWindowControl::detachNativeLayer() +{ + if (m_captureLayer && m_nativeView) + [m_captureLayer removeFromSuperlayer]; +} + +#include "moc_avfcamerawindowcontrol.cpp" diff --git a/src/plugins/avfoundation/camera/avfcapturedestinationcontrol.h b/src/plugins/avfoundation/camera/avfcapturedestinationcontrol.h new file mode 100644 index 000000000..04493437e --- /dev/null +++ b/src/plugins/avfoundation/camera/avfcapturedestinationcontrol.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef AVFCAPTUREDESTINATIONCONTROL_H +#define AVFCAPTUREDESTINATIONCONTROL_H + +#include <qcameracapturedestinationcontrol.h> + +QT_BEGIN_NAMESPACE + +class AVFCaptureDestinationControl : public QCameraCaptureDestinationControl +{ +public: + AVFCaptureDestinationControl() = default; + ~AVFCaptureDestinationControl() = default; + + bool isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const override; + QCameraImageCapture::CaptureDestinations captureDestination() const override; + void setCaptureDestination(QCameraImageCapture::CaptureDestinations destination) override; + +private: + QCameraImageCapture::CaptureDestinations m_destination = QCameraImageCapture::CaptureToFile; +}; + +QT_END_NAMESPACE + +#endif // AVFCAPTUREDESTINATIONCONTROL_H diff --git a/src/plugins/avfoundation/camera/avfcapturedestinationcontrol.mm b/src/plugins/avfoundation/camera/avfcapturedestinationcontrol.mm new file mode 100644 index 000000000..d0700d69d --- /dev/null +++ b/src/plugins/avfoundation/camera/avfcapturedestinationcontrol.mm @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "avfcapturedestinationcontrol.h" + +QT_BEGIN_NAMESPACE + +bool AVFCaptureDestinationControl::isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const +{ + return destination & (QCameraImageCapture::CaptureToFile | QCameraImageCapture::CaptureToBuffer); +} + +QCameraImageCapture::CaptureDestinations AVFCaptureDestinationControl::captureDestination() const +{ + return m_destination; +} + +void AVFCaptureDestinationControl::setCaptureDestination(QCameraImageCapture::CaptureDestinations destination) +{ + if (m_destination != destination) { + m_destination = destination; + Q_EMIT captureDestinationChanged(m_destination); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/avfoundation/camera/avfimagecapturecontrol.mm b/src/plugins/avfoundation/camera/avfimagecapturecontrol.mm index 55a20b1bd..6fca55d29 100644 --- a/src/plugins/avfoundation/camera/avfimagecapturecontrol.mm +++ b/src/plugins/avfoundation/camera/avfimagecapturecontrol.mm @@ -42,6 +42,8 @@ #include "avfcameraservice.h" #include "avfcamerautility.h" #include "avfcameracontrol.h" +#include "avfcapturedestinationcontrol.h" +#include <private/qmemoryvideobuffer_p.h> #include <QtCore/qurl.h> #include <QtCore/qfile.h> @@ -111,12 +113,16 @@ int AVFImageCaptureControl::capture(const QString &fileName) return m_lastCaptureId; } - QString actualFileName = m_storageLocation.generateFileName(fileName, - QCamera::CaptureStillImage, - QLatin1String("img_"), - QLatin1String("jpg")); + auto destination = m_service->captureDestinationControl()->captureDestination(); + QString actualFileName; + if (destination & QCameraImageCapture::CaptureToFile) { + actualFileName = m_storageLocation.generateFileName(fileName, + QCamera::CaptureStillImage, + QLatin1String("img_"), + QLatin1String("jpg")); - qDebugCamera() << "Capture image to" << actualFileName; + qDebugCamera() << "Capture image to" << actualFileName; + } CaptureRequest request = { m_lastCaptureId, QSharedPointer<QSemaphore>::create()}; m_requestsMutex.lock(); @@ -152,11 +158,24 @@ int AVFImageCaptureControl::capture(const QString &fileName) // so we cannot reliably check the camera's status. Instead, we wait // with a timeout and treat a failure to acquire a semaphore as an error. if (!m_service->videoOutput() || request.previewReady->tryAcquire(1, 1000)) { - qDebugCamera() << "Image capture completed:" << actualFileName; + qDebugCamera() << "Image capture completed"; NSData *nsJpgData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; QByteArray jpgData = QByteArray::fromRawData((const char *)[nsJpgData bytes], [nsJpgData length]); + if (destination & QCameraImageCapture::CaptureToBuffer) { + QBuffer data(&jpgData); + QImageReader reader(&data, "JPEG"); + QSize size = reader.size(); + QVideoFrame frame(new QMemoryVideoBuffer(QByteArray(jpgData.constData(), jpgData.size()), -1), size, QVideoFrame::Format_Jpeg); + QMetaObject::invokeMethod(this, "imageAvailable", Qt::QueuedConnection, + Q_ARG(int, request.captureId), + Q_ARG(QVideoFrame, frame)); + } + + if (!(destination & QCameraImageCapture::CaptureToFile)) + return; + QFile f(actualFileName); if (f.open(QFile::WriteOnly)) { if (f.write(jpgData) != -1) { diff --git a/src/plugins/avfoundation/camera/camera.pro b/src/plugins/avfoundation/camera/camera.pro index 4b960ed5a..76aa8af85 100644 --- a/src/plugins/avfoundation/camera/camera.pro +++ b/src/plugins/avfoundation/camera/camera.pro @@ -42,7 +42,9 @@ HEADERS += \ avfcameraflashcontrol.h \ avfvideoencodersettingscontrol.h \ avfmediacontainercontrol.h \ - avfaudioencodersettingscontrol.h + avfaudioencodersettingscontrol.h \ + avfcamerawindowcontrol.h \ + avfcapturedestinationcontrol.h OBJECTIVE_SOURCES += \ avfcameraserviceplugin.mm \ @@ -65,7 +67,9 @@ OBJECTIVE_SOURCES += \ avfcameraflashcontrol.mm \ avfvideoencodersettingscontrol.mm \ avfmediacontainercontrol.mm \ - avfaudioencodersettingscontrol.mm + avfaudioencodersettingscontrol.mm \ + avfcamerawindowcontrol.mm \ + avfcapturedestinationcontrol.mm osx { diff --git a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm index 6a2dba6a0..a601bb5ac 100644 --- a/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm +++ b/src/plugins/avfoundation/mediaplayer/avfmediaplayersession.mm @@ -110,6 +110,12 @@ static void *AVFMediaPlayerSessionObserverCurrentItemDurationObservationContext self->m_session = session; self->m_bufferIsLikelyToKeepUp = FALSE; + + m_playerLayer = [AVPlayerLayer playerLayerWithPlayer:nil]; + [m_playerLayer retain]; + m_playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; + m_playerLayer.anchorPoint = CGPointMake(0.0f, 0.0f); + return self; } @@ -162,6 +168,10 @@ static void *AVFMediaPlayerSessionObserverCurrentItemDurationObservationContext [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemTimeJumpedNotification object:m_playerItem]; + for (AVPlayerItemOutput *output in m_playerItem.outputs) { + if ([output isKindOfClass:[AVPlayerItemVideoOutput class]]) + [m_playerItem removeOutput:output]; + } m_playerItem = 0; } if (m_player) { @@ -172,10 +182,6 @@ static void *AVFMediaPlayerSessionObserverCurrentItemDurationObservationContext [m_player release]; m_player = 0; } - if (m_playerLayer) { - [m_playerLayer release]; - m_playerLayer = 0; - } } - (void) prepareToPlayAsset:(AVURLAsset *)asset @@ -260,14 +266,8 @@ static void *AVFMediaPlayerSessionObserverCurrentItemDurationObservationContext [m_player setMuted:m_session->isMuted()]; } - //Create a new player layer if we don't have one already - if (!m_playerLayer) - { - m_playerLayer = [AVPlayerLayer playerLayerWithPlayer:m_player]; - [m_playerLayer retain]; - m_playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; - m_playerLayer.anchorPoint = CGPointMake(0.0f, 0.0f); - } + //Assign the output layer to the new player + m_playerLayer.player = m_player; //Observe the AVPlayer "currentItem" property to find out when any //AVPlayer replaceCurrentItemWithPlayerItem: replacement will/did @@ -413,6 +413,7 @@ static void *AVFMediaPlayerSessionObserverCurrentItemDurationObservationContext } [m_mimeType release]; + [m_playerLayer release]; [super dealloc]; } @@ -536,16 +537,16 @@ QIODevice *AVFMediaPlayerSession::mediaStream() const return m_mediaStream; } -static void setURL(void *observer, const QString &url, const QString &mimeType = QString()) +static void setURL(void *observer, const QByteArray &url, const QString &mimeType = QString()) { - NSString *urlString = [NSString stringWithUTF8String:url.toUtf8().constData()]; + NSString *urlString = [NSString stringWithUTF8String:url.constData()]; NSURL *nsurl = [NSURL URLWithString:urlString]; [static_cast<AVFMediaPlayerSessionObserver*>(observer) setURL:nsurl mimeType:[NSString stringWithUTF8String:mimeType.toLatin1().constData()]]; } -static void setStreamURL(void *observer, const QString &url) +static void setStreamURL(void *observer, const QByteArray &url) { - setURL(observer, QLatin1String("iodevice://") + url, QFileInfo(url).suffix()); + setURL(observer, QByteArrayLiteral("iodevice://") + url, QFileInfo(url).suffix()); } void AVFMediaPlayerSession::setMedia(const QMediaContent &content, QIODevice *stream) @@ -588,11 +589,11 @@ void AVFMediaPlayerSession::setMedia(const QMediaContent &content, QIODevice *st // If there is a data, try to load it, // otherwise wait for readyRead. if (m_mediaStream->size()) - setStreamURL(m_observer, m_resources.request().url().toString()); + setStreamURL(m_observer, m_resources.request().url().toEncoded()); } else { //Load AVURLAsset //initialize asset using content's URL - setURL(m_observer, m_resources.request().url().toString()); + setURL(m_observer, m_resources.request().url().toEncoded()); } m_state = QMediaPlayer::StoppedState; @@ -940,9 +941,11 @@ void AVFMediaPlayerSession::processLoadStateChange(QMediaPlayer::State newState) // Get the native size of the video, and reset the bounds of the player layer AVPlayerLayer *playerLayer = [static_cast<AVFMediaPlayerSessionObserver*>(m_observer) playerLayer]; if (videoTrack && playerLayer) { - playerLayer.bounds = CGRectMake(0.0f, 0.0f, - videoTrack.naturalSize.width, - videoTrack.naturalSize.height); + if (!playerLayer.bounds.size.width || !playerLayer.bounds.size.height) { + playerLayer.bounds = CGRectMake(0.0f, 0.0f, + videoTrack.naturalSize.width, + videoTrack.naturalSize.height); + } if (m_videoOutput && newState != QMediaPlayer::StoppedState) { m_videoOutput->setLayer(playerLayer); @@ -1035,7 +1038,7 @@ void AVFMediaPlayerSession::processMediaLoadError() void AVFMediaPlayerSession::streamReady() { - setStreamURL(m_observer, m_resources.request().url().toString()); + setStreamURL(m_observer, m_resources.request().url().toEncoded()); } void AVFMediaPlayerSession::streamDestroyed() diff --git a/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.h b/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.h index 99b6bb0b5..ac67090a5 100644 --- a/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.h +++ b/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.h @@ -43,18 +43,48 @@ #include <QtCore/QObject> #include <QtGui/QImage> #include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLTextureBlitter> #include <QtCore/QSize> -@class CARenderer; @class AVPlayerLayer; +@class AVPlayerItemVideoOutput; QT_BEGIN_NAMESPACE -class QOpenGLFramebufferObject; -class QWindow; class QOpenGLContext; +class QOpenGLFramebufferObject; +class QOpenGLShaderProgram; +class QOffscreenSurface; class QAbstractVideoSurface; +typedef struct __CVBuffer *CVBufferRef; +typedef CVBufferRef CVImageBufferRef; +typedef CVImageBufferRef CVPixelBufferRef; + +#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) + typedef struct __CVOpenGLESTextureCache *CVOpenGLESTextureCacheRef; + typedef CVImageBufferRef CVOpenGLESTextureRef; + // helpers to avoid boring if def + typedef CVOpenGLESTextureCacheRef CVOGLTextureCacheRef; + typedef CVOpenGLESTextureRef CVOGLTextureRef; + #define CVOGLTextureGetTarget CVOpenGLESTextureGetTarget + #define CVOGLTextureGetName CVOpenGLESTextureGetName + #define CVOGLTextureCacheCreate CVOpenGLESTextureCacheCreate + #define CVOGLTextureCacheCreateTextureFromImage CVOpenGLESTextureCacheCreateTextureFromImage + #define CVOGLTextureCacheFlush CVOpenGLESTextureCacheFlush +#else + typedef struct __CVOpenGLTextureCache *CVOpenGLTextureCacheRef; + typedef CVImageBufferRef CVOpenGLTextureRef; + // helpers to avoid boring if def + typedef CVOpenGLTextureCacheRef CVOGLTextureCacheRef; + typedef CVOpenGLTextureRef CVOGLTextureRef; + #define CVOGLTextureGetTarget CVOpenGLTextureGetTarget + #define CVOGLTextureGetName CVOpenGLTextureGetName + #define CVOGLTextureCacheCreate CVOpenGLTextureCacheCreate + #define CVOGLTextureCacheCreateTextureFromImage CVOpenGLTextureCacheCreateTextureFromImage + #define CVOGLTextureCacheFlush CVOpenGLTextureCacheFlush +#endif + class AVFVideoFrameRenderer : public QObject { public: @@ -62,22 +92,31 @@ public: virtual ~AVFVideoFrameRenderer(); - GLuint renderLayerToTexture(AVPlayerLayer *layer); - QImage renderLayerToImage(AVPlayerLayer *layer); + void setPlayerLayer(AVPlayerLayer *layer); + + CVOGLTextureRef renderLayerToTexture(AVPlayerLayer *layer, QSize *size); +#ifdef Q_OS_MACOS + GLuint renderLayerToFBO(AVPlayerLayer *layer, QSize *size); +#endif + QImage renderLayerToImage(AVPlayerLayer *layer, QSize *size); private: - QOpenGLFramebufferObject* initRenderer(AVPlayerLayer *layer); - void renderLayerToFBO(AVPlayerLayer *layer, QOpenGLFramebufferObject *fbo); + void initRenderer(); + CVPixelBufferRef copyPixelBufferFromLayer(AVPlayerLayer *layer, size_t& width, size_t& height); + CVOGLTextureRef createCacheTextureFromLayer(AVPlayerLayer *layer, size_t& width, size_t& height); - CARenderer *m_videoLayerRenderer; - QAbstractVideoSurface *m_surface; - QOpenGLFramebufferObject *m_fbo[2]; - QWindow *m_offscreenSurface; QOpenGLContext *m_glContext; - QSize m_targetSize; - - uint m_currentBuffer; + QOffscreenSurface *m_offscreenSurface; + QAbstractVideoSurface *m_surface; + CVOGLTextureCacheRef m_textureCache; + AVPlayerItemVideoOutput* m_videoOutput; bool m_isContextShared; + +#ifdef Q_OS_MACOS + QOpenGLFramebufferObject *m_fbo[2]; + uint m_currentFBO; + QOpenGLTextureBlitter m_blitter; +#endif }; QT_END_NAMESPACE diff --git a/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.mm b/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.mm index 51f961729..a22ee2b82 100644 --- a/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.mm +++ b/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer.mm @@ -41,12 +41,20 @@ #include <QtMultimedia/qabstractvideosurface.h> #include <QtGui/QOpenGLFramebufferObject> -#include <QtGui/QWindow> +#include <QtGui/QOpenGLShaderProgram> +#include <QtGui/QOffscreenSurface> + +#include <QtCore/private/qcore_mac_p.h> #ifdef QT_DEBUG_AVF #include <QtCore/qdebug.h> #endif +#ifdef Q_OS_MACOS +#import <AppKit/AppKit.h> +#include <CoreVideo/CVOpenGLTextureCache.h> +#endif + #import <CoreVideo/CVBase.h> #import <AVFoundation/AVFoundation.h> @@ -54,15 +62,23 @@ QT_USE_NAMESPACE AVFVideoFrameRenderer::AVFVideoFrameRenderer(QAbstractVideoSurface *surface, QObject *parent) : QObject(parent) - , m_videoLayerRenderer(nullptr) - , m_surface(surface) - , m_offscreenSurface(nullptr) , m_glContext(nullptr) - , m_currentBuffer(1) + , m_offscreenSurface(nullptr) + , m_surface(surface) + , m_textureCache(nullptr) + , m_videoOutput(nullptr) , m_isContextShared(true) { + m_videoOutput = [[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:@{ + (NSString *)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA), + (NSString *)kCVPixelBufferOpenGLCompatibilityKey: @YES + }]; + [m_videoOutput setDelegate:nil queue:nil]; + +#ifdef Q_OS_MACOS m_fbo[0] = nullptr; m_fbo[1] = nullptr; +#endif } AVFVideoFrameRenderer::~AVFVideoFrameRenderer() @@ -71,81 +87,200 @@ AVFVideoFrameRenderer::~AVFVideoFrameRenderer() qDebug() << Q_FUNC_INFO; #endif - [m_videoLayerRenderer release]; - delete m_fbo[0]; - delete m_fbo[1]; + [m_videoOutput release]; + if (m_textureCache) + CFRelease(m_textureCache); delete m_offscreenSurface; delete m_glContext; + +#ifdef Q_OS_MACOS + delete m_fbo[0]; + delete m_fbo[1]; +#endif } -GLuint AVFVideoFrameRenderer::renderLayerToTexture(AVPlayerLayer *layer) +#ifdef Q_OS_MACOS +GLuint AVFVideoFrameRenderer::renderLayerToFBO(AVPlayerLayer *layer, QSize *size) { - //Is layer valid - if (!layer) + QCFType<CVOGLTextureRef> texture = renderLayerToTexture(layer, size); + if (!texture) return 0; - //If the glContext isn't shared, it doesn't make sense to return a texture for us - if (m_offscreenSurface && !m_isContextShared) - return 0; + Q_ASSERT(size); - QOpenGLFramebufferObject *fbo = initRenderer(layer); + // Do we have FBO's already? + if ((!m_fbo[0] && !m_fbo[0]) || (m_fbo[0]->size() != *size)) { + delete m_fbo[0]; + delete m_fbo[1]; + m_fbo[0] = new QOpenGLFramebufferObject(*size); + m_fbo[1] = new QOpenGLFramebufferObject(*size); + } + + // Switch buffer target + m_currentFBO = !m_currentFBO; + QOpenGLFramebufferObject *fbo = m_fbo[m_currentFBO]; - if (!fbo) + if (!fbo || !fbo->bind()) return 0; - renderLayerToFBO(layer, fbo); - if (m_glContext) - m_glContext->doneCurrent(); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + glViewport(0, 0, size->width(), size->height()); + + if (!m_blitter.isCreated()) + m_blitter.create(); + + m_blitter.bind(GL_TEXTURE_RECTANGLE); + m_blitter.blit(CVOpenGLTextureGetName(texture), QMatrix4x4(), QMatrix3x3()); + m_blitter.release(); + glFinish(); + + fbo->release(); return fbo->texture(); } +#endif + +CVOGLTextureRef AVFVideoFrameRenderer::renderLayerToTexture(AVPlayerLayer *layer, QSize *size) +{ + initRenderer(); + + // If the glContext isn't shared, it doesn't make sense to return a texture for us + if (!m_isContextShared) + return nullptr; + + size_t width = 0, height = 0; + auto texture = createCacheTextureFromLayer(layer, width, height); + if (size) + *size = QSize(width, height); + return texture; +} -QImage AVFVideoFrameRenderer::renderLayerToImage(AVPlayerLayer *layer) +CVPixelBufferRef AVFVideoFrameRenderer::copyPixelBufferFromLayer(AVPlayerLayer *layer, + size_t& width, size_t& height) { //Is layer valid if (!layer) { - return QImage(); +#ifdef QT_DEBUG_AVF + qWarning("copyPixelBufferFromLayer: invalid layer"); +#endif + return nullptr; } - QOpenGLFramebufferObject *fbo = initRenderer(layer); + AVPlayerItem *item = layer.player.currentItem; + if (![item.outputs containsObject:m_videoOutput]) + [item addOutput:m_videoOutput]; - if (!fbo) - return QImage(); + CFTimeInterval currentCAFrameTime = CACurrentMediaTime(); + CMTime currentCMFrameTime = [m_videoOutput itemTimeForHostTime:currentCAFrameTime]; + + // Happens when buffering / loading + if (CMTimeCompare(currentCMFrameTime, kCMTimeZero) < 0) + return nullptr; + + if (![m_videoOutput hasNewPixelBufferForItemTime:currentCMFrameTime]) + return nullptr; + + CVPixelBufferRef pixelBuffer = [m_videoOutput copyPixelBufferForItemTime:currentCMFrameTime + itemTimeForDisplay:nil]; + if (!pixelBuffer) { +#ifdef QT_DEBUG_AVF + qWarning("copyPixelBufferForItemTime returned nil"); + CMTimeShow(currentCMFrameTime); +#endif + return nullptr; + } + + width = CVPixelBufferGetWidth(pixelBuffer); + height = CVPixelBufferGetHeight(pixelBuffer); + return pixelBuffer; +} - renderLayerToFBO(layer, fbo); - QImage fboImage = fbo->toImage(); - if (m_glContext) - m_glContext->doneCurrent(); +CVOGLTextureRef AVFVideoFrameRenderer::createCacheTextureFromLayer(AVPlayerLayer *layer, + size_t& width, size_t& height) +{ + CVPixelBufferRef pixelBuffer = copyPixelBufferFromLayer(layer, width, height); + + if (!pixelBuffer) + return nullptr; + + CVOGLTextureCacheFlush(m_textureCache, 0); + + CVOGLTextureRef texture = nullptr; +#ifdef Q_OS_MACOS + CVReturn err = CVOpenGLTextureCacheCreateTextureFromImage(kCFAllocatorDefault, + m_textureCache, + pixelBuffer, + nil, + &texture); +#else + CVReturn err = CVOGLTextureCacheCreateTextureFromImage(kCFAllocatorDefault, m_textureCache, pixelBuffer, nullptr, + GL_TEXTURE_2D, GL_RGBA, + (GLsizei) width, (GLsizei) height, + GL_BGRA, GL_UNSIGNED_BYTE, 0, + &texture); +#endif + + if (!texture || err) { + qWarning() << "CVOGLTextureCacheCreateTextureFromImage failed error:" << err << m_textureCache; + } + + CVPixelBufferRelease(pixelBuffer); - return fboImage; + return texture; } -QOpenGLFramebufferObject *AVFVideoFrameRenderer::initRenderer(AVPlayerLayer *layer) +QImage AVFVideoFrameRenderer::renderLayerToImage(AVPlayerLayer *layer, QSize *size) { + size_t width = 0; + size_t height = 0; + CVPixelBufferRef pixelBuffer = copyPixelBufferFromLayer(layer, width, height); + if (size) + *size = QSize(width, height); + + if (!pixelBuffer) + return QImage(); - //Get size from AVPlayerLayer - m_targetSize = QSize(layer.bounds.size.width, layer.bounds.size.height); + OSType pixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer); + if (pixelFormat != kCVPixelFormatType_32BGRA) { +#ifdef QT_DEBUG_AVF + qWarning("CVPixelBuffer format is not BGRA32 (got: %d)", static_cast<quint32>(pixelFormat)); +#endif + return QImage(); + } - QOpenGLContext *shareContext = !m_glContext && m_surface - ? qobject_cast<QOpenGLContext*>(m_surface->property("GLContext").value<QObject*>()) - : nullptr; + CVPixelBufferLockBaseAddress(pixelBuffer, 0); + char *data = (char *)CVPixelBufferGetBaseAddress(pixelBuffer); + size_t stride = CVPixelBufferGetBytesPerRow(pixelBuffer); + + // format here is not relevant, only using for storage + QImage img = QImage(width, height, QImage::Format_ARGB32); + for (size_t j = 0; j < height; j++) { + memcpy(img.scanLine(j), data, width * 4); + data += stride; + } + + CVPixelBufferUnlockBaseAddress(pixelBuffer, 0); + CVPixelBufferRelease(pixelBuffer); + return img; +} + +void AVFVideoFrameRenderer::initRenderer() +{ + // even for using a texture directly, we need to be able to make a context current, + // so we need an offscreen, and we shouldn't assume we can make the surface context + // current on that offscreen, so use our own (sharing with it). Slightly + // excessive but no performance penalty and makes the QImage path easier to maintain //Make sure we have an OpenGL context to make current - if ((shareContext && shareContext != QOpenGLContext::currentContext()) - || (!QOpenGLContext::currentContext() && !m_glContext)) { - - //Create Hidden QWindow surface to create context in this thread - delete m_offscreenSurface; - m_offscreenSurface = new QWindow(); - m_offscreenSurface->setSurfaceType(QWindow::OpenGLSurface); - //Needs geometry to be a valid surface, but size is not important - m_offscreenSurface->setGeometry(0, 0, 1, 1); - m_offscreenSurface->create(); + if (!m_glContext) { + //Create OpenGL context and set share context from surface + QOpenGLContext *shareContext = nullptr; + if (m_surface) + shareContext = qobject_cast<QOpenGLContext*>(m_surface->property("GLContext").value<QObject*>()); - delete m_glContext; m_glContext = new QOpenGLContext(); - m_glContext->setFormat(m_offscreenSurface->requestedFormat()); - if (shareContext) { m_glContext->setShareContext(shareContext); m_isContextShared = true; @@ -156,83 +291,40 @@ QOpenGLFramebufferObject *AVFVideoFrameRenderer::initRenderer(AVPlayerLayer *lay m_isContextShared = false; } if (!m_glContext->create()) { +#ifdef QT_DEBUG_AVF qWarning("failed to create QOpenGLContext"); - return nullptr; - } - - // CARenderer must be re-created with different current context, so release it now. - // See lines below where m_videoLayerRenderer is constructed. - if (m_videoLayerRenderer) { - [m_videoLayerRenderer release]; - m_videoLayerRenderer = nullptr; +#endif + return; } } - //Need current context - if (m_glContext) - m_glContext->makeCurrent(m_offscreenSurface); - - //Create the CARenderer if needed - if (!m_videoLayerRenderer) { - m_videoLayerRenderer = [CARenderer rendererWithCGLContext: CGLGetCurrentContext() options: nil]; - [m_videoLayerRenderer retain]; - } - - //Set/Change render source if needed - if (m_videoLayerRenderer.layer != layer) { - m_videoLayerRenderer.layer = layer; - m_videoLayerRenderer.bounds = layer.bounds; - } - - //Do we have FBO's already? - if ((!m_fbo[0] && !m_fbo[0]) || (m_fbo[0]->size() != m_targetSize)) { - delete m_fbo[0]; - delete m_fbo[1]; - m_fbo[0] = new QOpenGLFramebufferObject(m_targetSize); - m_fbo[1] = new QOpenGLFramebufferObject(m_targetSize); + if (!m_offscreenSurface) { + m_offscreenSurface = new QOffscreenSurface(); + m_offscreenSurface->setFormat(m_glContext->format()); + m_offscreenSurface->create(); } - //Switch buffer target - m_currentBuffer = !m_currentBuffer; - return m_fbo[m_currentBuffer]; -} - -void AVFVideoFrameRenderer::renderLayerToFBO(AVPlayerLayer *layer, QOpenGLFramebufferObject *fbo) -{ - //Start Rendering - //NOTE: This rendering method will NOT work on iOS as there is no CARenderer in iOS - if (!fbo->bind()) { - qWarning("AVFVideoRender FBO failed to bind"); - return; + // Need current context + m_glContext->makeCurrent(m_offscreenSurface); + + if (!m_textureCache) { +#ifdef Q_OS_MACOS + auto *currentContext = NSOpenGLContext.currentContext; + // Create an OpenGL CoreVideo texture cache from the pixel buffer. + auto err = CVOpenGLTextureCacheCreate( + kCFAllocatorDefault, + nullptr, + currentContext.CGLContextObj, + currentContext.pixelFormat.CGLPixelFormatObj, + nil, + &m_textureCache); +#else + CVReturn err = CVOGLTextureCacheCreate(kCFAllocatorDefault, nullptr, + [EAGLContext currentContext], + nullptr, &m_textureCache); +#endif + if (err) + qWarning("Error at CVOGLTextureCacheCreate %d", err); } - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - - glViewport(0, 0, m_targetSize.width(), m_targetSize.height()); - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - - //Render to FBO with inverted Y - glOrtho(0.0, m_targetSize.width(), 0.0, m_targetSize.height(), 0.0, 1.0); - - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - [m_videoLayerRenderer beginFrameAtTime:CACurrentMediaTime() timeStamp:NULL]; - [m_videoLayerRenderer addUpdateRect:layer.bounds]; - [m_videoLayerRenderer render]; - [m_videoLayerRenderer endFrame]; - - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - - glFinish(); //Rendering needs to be done before passing texture to video frame - - fbo->release(); } diff --git a/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer_ios.h b/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer_ios.h deleted file mode 100644 index d9f6baa7e..000000000 --- a/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer_ios.h +++ /dev/null @@ -1,113 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef AVFVIDEOFRAMERENDERER_H -#define AVFVIDEOFRAMERENDERER_H - -#include <QtCore/QObject> -#include <QtGui/QImage> -#include <QtGui/QOpenGLContext> -#include <QtCore/QSize> - -@class AVPlayerLayer; -@class AVPlayerItemVideoOutput; - -QT_BEGIN_NAMESPACE - -class QOpenGLContext; -class QOpenGLFramebufferObject; -class QOpenGLShaderProgram; -class QOffscreenSurface; -class QAbstractVideoSurface; - -typedef struct __CVBuffer *CVBufferRef; -typedef CVBufferRef CVImageBufferRef; -typedef CVImageBufferRef CVPixelBufferRef; -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) -typedef struct __CVOpenGLESTextureCache *CVOpenGLESTextureCacheRef; -typedef CVImageBufferRef CVOpenGLESTextureRef; -// helpers to avoid boring if def -typedef CVOpenGLESTextureCacheRef CVOGLTextureCacheRef; -typedef CVOpenGLESTextureRef CVOGLTextureRef; -#define CVOGLTextureGetTarget CVOpenGLESTextureGetTarget -#define CVOGLTextureGetName CVOpenGLESTextureGetName -#define CVOGLTextureCacheCreate CVOpenGLESTextureCacheCreate -#define CVOGLTextureCacheCreateTextureFromImage CVOpenGLESTextureCacheCreateTextureFromImage -#define CVOGLTextureCacheFlush CVOpenGLESTextureCacheFlush -#else -typedef struct __CVOpenGLTextureCache *CVOpenGLTextureCacheRef; -typedef CVImageBufferRef CVOpenGLTextureRef; -// helpers to avoid boring if def -typedef CVOpenGLTextureCacheRef CVOGLTextureCacheRef; -typedef CVOpenGLTextureRef CVOGLTextureRef; -#define CVOGLTextureGetTarget CVOpenGLTextureGetTarget -#define CVOGLTextureGetName CVOpenGLTextureGetName -#define CVOGLTextureCacheCreate CVOpenGLTextureCacheCreate -#define CVOGLTextureCacheCreateTextureFromImage CVOpenGLTextureCacheCreateTextureFromImage -#define CVOGLTextureCacheFlush CVOpenGLTextureCacheFlush -#endif - -class AVFVideoFrameRenderer : public QObject -{ -public: - AVFVideoFrameRenderer(QAbstractVideoSurface *surface, QObject *parent = nullptr); - - virtual ~AVFVideoFrameRenderer(); - - void setPlayerLayer(AVPlayerLayer *layer); - - CVOGLTextureRef renderLayerToTexture(AVPlayerLayer *layer); - QImage renderLayerToImage(AVPlayerLayer *layer); - -private: - void initRenderer(); - CVPixelBufferRef copyPixelBufferFromLayer(AVPlayerLayer *layer, size_t& width, size_t& height); - CVOGLTextureRef createCacheTextureFromLayer(AVPlayerLayer *layer, size_t& width, size_t& height); - - QOpenGLContext *m_glContext; - QOffscreenSurface *m_offscreenSurface; - QAbstractVideoSurface *m_surface; - CVOGLTextureCacheRef m_textureCache; - AVPlayerItemVideoOutput* m_videoOutput; - bool m_isContextShared; -}; - -QT_END_NAMESPACE - -#endif // AVFVIDEOFRAMERENDERER_H diff --git a/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer_ios.mm b/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer_ios.mm deleted file mode 100644 index 70e402e6c..000000000 --- a/src/plugins/avfoundation/mediaplayer/avfvideoframerenderer_ios.mm +++ /dev/null @@ -1,261 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies). -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "avfvideoframerenderer_ios.h" - -#include <QtMultimedia/qabstractvideosurface.h> -#include <QtGui/QOpenGLFramebufferObject> -#include <QtGui/QOpenGLShaderProgram> -#include <QtGui/QOffscreenSurface> - -#ifdef QT_DEBUG_AVF -#include <QtCore/qdebug.h> -#endif - -#import <CoreVideo/CVBase.h> -#import <AVFoundation/AVFoundation.h> -QT_USE_NAMESPACE - -AVFVideoFrameRenderer::AVFVideoFrameRenderer(QAbstractVideoSurface *surface, QObject *parent) - : QObject(parent) - , m_glContext(nullptr) - , m_offscreenSurface(nullptr) - , m_surface(surface) - , m_textureCache(nullptr) - , m_videoOutput(nullptr) - , m_isContextShared(true) -{ -} - -AVFVideoFrameRenderer::~AVFVideoFrameRenderer() -{ -#ifdef QT_DEBUG_AVF - qDebug() << Q_FUNC_INFO; -#endif - - [m_videoOutput release]; // sending to nil is fine - if (m_textureCache) - CFRelease(m_textureCache); - delete m_offscreenSurface; - delete m_glContext; -} - -void AVFVideoFrameRenderer::setPlayerLayer(AVPlayerLayer *layer) -{ - Q_UNUSED(layer) - if (m_videoOutput) { - [m_videoOutput release]; - m_videoOutput = nullptr; - // will be re-created in first call to copyPixelBufferFromLayer - } -} - -CVOGLTextureRef AVFVideoFrameRenderer::renderLayerToTexture(AVPlayerLayer *layer) -{ - initRenderer(); - - // If the glContext isn't shared, it doesn't make sense to return a texture for us - if (!m_isContextShared) - return nullptr; - - size_t dummyWidth = 0, dummyHeight = 0; - return createCacheTextureFromLayer(layer, dummyWidth, dummyHeight); -} - -static NSString* const AVF_PIXEL_FORMAT_KEY = (NSString*)kCVPixelBufferPixelFormatTypeKey; -static NSNumber* const AVF_PIXEL_FORMAT_VALUE = [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA]; -static NSDictionary* const AVF_OUTPUT_SETTINGS = [NSDictionary dictionaryWithObject:AVF_PIXEL_FORMAT_VALUE forKey:AVF_PIXEL_FORMAT_KEY]; - - -CVPixelBufferRef AVFVideoFrameRenderer::copyPixelBufferFromLayer(AVPlayerLayer *layer, - size_t& width, size_t& height) -{ - //Is layer valid - if (!layer) { -#ifdef QT_DEBUG_AVF - qWarning("copyPixelBufferFromLayer: invalid layer"); -#endif - return nullptr; - } - - if (!m_videoOutput) { - m_videoOutput = [[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:AVF_OUTPUT_SETTINGS]; - [m_videoOutput setDelegate:nil queue:nil]; - AVPlayerItem * item = [[layer player] currentItem]; - [item addOutput:m_videoOutput]; - } - - CFTimeInterval currentCAFrameTime = CACurrentMediaTime(); - CMTime currentCMFrameTime = [m_videoOutput itemTimeForHostTime:currentCAFrameTime]; - // happens when buffering / loading - if (CMTimeCompare(currentCMFrameTime, kCMTimeZero) < 0) { - return nullptr; - } - - CVPixelBufferRef pixelBuffer = [m_videoOutput copyPixelBufferForItemTime:currentCMFrameTime - itemTimeForDisplay:nil]; - if (!pixelBuffer) { -#ifdef QT_DEBUG_AVF - qWarning("copyPixelBufferForItemTime returned nil"); - CMTimeShow(currentCMFrameTime); -#endif - return nullptr; - } - - width = CVPixelBufferGetWidth(pixelBuffer); - height = CVPixelBufferGetHeight(pixelBuffer); - return pixelBuffer; -} - -CVOGLTextureRef AVFVideoFrameRenderer::createCacheTextureFromLayer(AVPlayerLayer *layer, - size_t& width, size_t& height) -{ - CVPixelBufferRef pixelBuffer = copyPixelBufferFromLayer(layer, width, height); - - if (!pixelBuffer) - return nullptr; - - CVOGLTextureCacheFlush(m_textureCache, 0); - - CVOGLTextureRef texture = nullptr; - CVReturn err = CVOGLTextureCacheCreateTextureFromImage(kCFAllocatorDefault, m_textureCache, pixelBuffer, nullptr, - GL_TEXTURE_2D, GL_RGBA, - (GLsizei) width, (GLsizei) height, - GL_BGRA, GL_UNSIGNED_BYTE, 0, - &texture); - - if (!texture || err) { -#ifdef QT_DEBUG_AVF - qWarning("CVOGLTextureCacheCreateTextureFromImage failed (error: %d)", err); -#endif - } - - CVPixelBufferRelease(pixelBuffer); - - return texture; -} - -QImage AVFVideoFrameRenderer::renderLayerToImage(AVPlayerLayer *layer) -{ - size_t width = 0; - size_t height = 0; - CVPixelBufferRef pixelBuffer = copyPixelBufferFromLayer(layer, width, height); - - if (!pixelBuffer) - return QImage(); - - OSType pixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer); - if (pixelFormat != kCVPixelFormatType_32BGRA) { -#ifdef QT_DEBUG_AVF - qWarning("CVPixelBuffer format is not BGRA32 (got: %d)", static_cast<quint32>(pixelFormat)); -#endif - return QImage(); - } - - CVPixelBufferLockBaseAddress(pixelBuffer, 0); - char *data = (char *)CVPixelBufferGetBaseAddress(pixelBuffer); - size_t stride = CVPixelBufferGetBytesPerRow(pixelBuffer); - - // format here is not relevant, only using for storage - QImage img = QImage(width, height, QImage::Format_ARGB32); - for (size_t j = 0; j < height; j++) { - memcpy(img.scanLine(j), data, width * 4); - data += stride; - } - - CVPixelBufferUnlockBaseAddress(pixelBuffer, 0); - CVPixelBufferRelease(pixelBuffer); - return img; -} - -void AVFVideoFrameRenderer::initRenderer() -{ - // even for using a texture directly, we need to be able to make a context current, - // so we need an offscreen, and we shouldn't assume we can make the surface context - // current on that offscreen, so use our own (sharing with it). Slightly - // excessive but no performance penalty and makes the QImage path easier to maintain - - //Make sure we have an OpenGL context to make current - if (!m_glContext) { - //Create OpenGL context and set share context from surface - QOpenGLContext *shareContext = nullptr; - if (m_surface) { - shareContext = qobject_cast<QOpenGLContext*>(m_surface->property("GLContext").value<QObject*>()); - } - - m_glContext = new QOpenGLContext(); - if (shareContext) { - m_glContext->setShareContext(shareContext); - m_isContextShared = true; - } else { -#ifdef QT_DEBUG_AVF - qWarning("failed to get Render Thread context"); -#endif - m_isContextShared = false; - } - if (!m_glContext->create()) { -#ifdef QT_DEBUG_AVF - qWarning("failed to create QOpenGLContext"); -#endif - return; - } - } - - if (!m_offscreenSurface) { - m_offscreenSurface = new QOffscreenSurface(); - m_offscreenSurface->setFormat(m_glContext->format()); - m_offscreenSurface->create(); - } - - //Need current context - m_glContext->makeCurrent(m_offscreenSurface); - - if (!m_textureCache) { - // Create a new open gl texture cache - CVReturn err = CVOGLTextureCacheCreate(kCFAllocatorDefault, nullptr, - [EAGLContext currentContext], - nullptr, &m_textureCache); - if (err) { - #ifdef QT_DEBUG_AVF - qWarning("Error at CVOGLTextureCacheCreate %d", err); - #endif - } - } - -} diff --git a/src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.h b/src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.h index 85dc19d31..a88573eaa 100644 --- a/src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.h +++ b/src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.h @@ -41,6 +41,8 @@ #define AVFVIDEORENDERERCONTROL_H #include <QtMultimedia/QVideoRendererControl> +#include <QtMultimedia/qabstractvideobuffer.h> + #include <QtCore/QMutex> #include <QtCore/QSize> @@ -82,8 +84,7 @@ private: AVFVideoFrameRenderer *m_frameRenderer; AVFDisplayLink *m_displayLink; - QSize m_nativeSize; - bool m_enableOpenGL; + QAbstractVideoBuffer::HandleType m_surfaceType = QAbstractVideoBuffer::NoHandle; }; QT_END_NAMESPACE diff --git a/src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.mm b/src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.mm index 63bdee4f5..f299d5f86 100644 --- a/src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.mm +++ b/src/plugins/avfoundation/mediaplayer/avfvideorenderercontrol.mm @@ -40,11 +40,7 @@ #include "avfvideorenderercontrol.h" #include "avfdisplaylink.h" -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) -#include "avfvideoframerenderer_ios.h" -#else #include "avfvideoframerenderer.h" -#endif #include <QtMultimedia/qabstractvideobuffer.h> #include <QtMultimedia/qabstractvideosurface.h> @@ -58,69 +54,52 @@ QT_USE_NAMESPACE -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) -class TextureCacheVideoBuffer : public QAbstractVideoBuffer +class TextureVideoBuffer : public QAbstractVideoBuffer { public: - TextureCacheVideoBuffer(CVOGLTextureRef texture) - : QAbstractVideoBuffer(GLTextureHandle) + TextureVideoBuffer(GLuint texture, QAbstractVideoBuffer::HandleType type) + : QAbstractVideoBuffer(type) , m_texture(texture) {} - virtual ~TextureCacheVideoBuffer() - { - // absolutely critical that we drop this - // reference of textures will stay in the cache - CFRelease(m_texture); - } - MapMode mapMode() const { return NotMapped; } uchar *map(MapMode, int*, int*) { return nullptr; } void unmap() {} QVariant handle() const { - GLuint texId = CVOGLTextureGetName(m_texture); - return QVariant::fromValue<unsigned int>(texId); + return QVariant::fromValue<unsigned int>(m_texture); } private: - CVOGLTextureRef m_texture; + GLuint m_texture; }; -#else -class TextureVideoBuffer : public QAbstractVideoBuffer + +class CoreVideoTextureVideoBuffer : public TextureVideoBuffer { public: - TextureVideoBuffer(GLuint tex) - : QAbstractVideoBuffer(GLTextureHandle) - , m_texture(tex) + CoreVideoTextureVideoBuffer(CVOGLTextureRef texture, QAbstractVideoBuffer::HandleType type) + : TextureVideoBuffer(CVOGLTextureGetName(texture), type) + , m_coreVideoTexture(texture) {} - virtual ~TextureVideoBuffer() + virtual ~CoreVideoTextureVideoBuffer() { - } - - MapMode mapMode() const { return NotMapped; } - uchar *map(MapMode, int*, int*) { return 0; } - void unmap() {} - - QVariant handle() const - { - return QVariant::fromValue<unsigned int>(m_texture); + // absolutely critical that we drop this + // reference of textures will stay in the cache + CFRelease(m_coreVideoTexture); } private: - GLuint m_texture; + CVOGLTextureRef m_coreVideoTexture; }; -#endif + AVFVideoRendererControl::AVFVideoRendererControl(QObject *parent) : QVideoRendererControl(parent) , m_surface(nullptr) , m_playerLayer(nullptr) , m_frameRenderer(nullptr) - , m_enableOpenGL(false) - { m_displayLink = new AVFDisplayLink(this); connect(m_displayLink, SIGNAL(tick(CVTimeStamp)), SLOT(updateVideoFrame(CVTimeStamp))); @@ -170,18 +149,26 @@ void AVFVideoRendererControl::setSurface(QAbstractVideoSurface *surface) //Surface changed, so we need a new frame renderer m_frameRenderer = new AVFVideoFrameRenderer(m_surface, this); -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) - if (m_playerLayer) { - m_frameRenderer->setPlayerLayer(static_cast<AVPlayerLayer*>(m_playerLayer)); - } -#endif - //Check for needed formats to render as OpenGL Texture - auto handleGlEnabled = [this] { - m_enableOpenGL = m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).contains(QVideoFrame::Format_BGR32); + auto updateSurfaceType = [this] { + auto preferredOpenGLSurfaceTypes = { +#ifdef Q_OS_MACOS + QAbstractVideoBuffer::GLTextureRectangleHandle, // GL_TEXTURE_RECTANGLE +#endif + QAbstractVideoBuffer::GLTextureHandle // GL_TEXTURE_2D + }; + + for (auto surfaceType : preferredOpenGLSurfaceTypes) { + auto supportedFormats = m_surface->supportedPixelFormats(surfaceType); + if (supportedFormats.contains(QVideoFrame::Format_BGR32)) { + m_surfaceType = surfaceType; + return; + } + m_surfaceType = QAbstractVideoBuffer::NoHandle; // QImage + } }; - handleGlEnabled(); - connect(m_surface, &QAbstractVideoSurface::supportedFormatsChanged, this, handleGlEnabled); + updateSurfaceType(); + connect(m_surface, &QAbstractVideoSurface::supportedFormatsChanged, this, updateSurfaceType); //If we already have a layer, but changed surfaces start rendering again if (m_playerLayer && !m_displayLink->isActive()) { @@ -204,12 +191,6 @@ void AVFVideoRendererControl::setLayer(void *playerLayer) if (m_surface && m_surface->isActive()) m_surface->stop(); -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) - if (m_frameRenderer) { - m_frameRenderer->setPlayerLayer(static_cast<AVPlayerLayer*>(playerLayer)); - } -#endif - //If there is no layer to render, stop scheduling updates if (m_playerLayer == nullptr) { m_displayLink->stop(); @@ -238,36 +219,39 @@ void AVFVideoRendererControl::updateVideoFrame(const CVTimeStamp &ts) if (!playerLayer.readyForDisplay) return; - if (m_enableOpenGL) { -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) - CVOGLTextureRef tex = m_frameRenderer->renderLayerToTexture(playerLayer); - - //Make sure we got a valid texture - if (tex == nullptr) - return; - - QAbstractVideoBuffer *buffer = new TextureCacheVideoBuffer(tex); + if (m_surfaceType == QAbstractVideoBuffer::GLTextureHandle + || m_surfaceType == QAbstractVideoBuffer::GLTextureRectangleHandle) { + QSize size; + QAbstractVideoBuffer *buffer = nullptr; + +#ifdef Q_OS_MACOS + if (m_surfaceType == QAbstractVideoBuffer::GLTextureRectangleHandle) { + // Render to GL_TEXTURE_RECTANGLE directly + if (CVOGLTextureRef tex = m_frameRenderer->renderLayerToTexture(playerLayer, &size)) + buffer = new CoreVideoTextureVideoBuffer(tex, m_surfaceType); + } else { + // Render to GL_TEXTURE_2D via FBO + if (GLuint tex = m_frameRenderer->renderLayerToFBO(playerLayer, &size)) + buffer = new TextureVideoBuffer(tex, m_surfaceType); + } #else - GLuint tex = m_frameRenderer->renderLayerToTexture(playerLayer); - //Make sure we got a valid texture - if (tex == 0) + Q_ASSERT(m_surfaceType != QAbstractVideoBuffer::GLTextureRectangleHandle); + // Render to GL_TEXTURE_2D directly + if (CVOGLTextureRef tex = m_frameRenderer->renderLayerToTexture(playerLayer, &size)) + buffer = new CoreVideoTextureVideoBuffer(tex, m_surfaceType); +#endif + if (!buffer) return; - QAbstractVideoBuffer *buffer = new TextureVideoBuffer(tex); -#endif - QVideoFrame frame = QVideoFrame(buffer, m_nativeSize, QVideoFrame::Format_BGR32); + QVideoFrame frame = QVideoFrame(buffer, size, QVideoFrame::Format_BGR32); if (m_surface && frame.isValid()) { if (m_surface->isActive() && m_surface->surfaceFormat().pixelFormat() != frame.pixelFormat()) m_surface->stop(); if (!m_surface->isActive()) { - QVideoSurfaceFormat format(frame.size(), frame.pixelFormat(), QAbstractVideoBuffer::GLTextureHandle); -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) + QVideoSurfaceFormat format(frame.size(), frame.pixelFormat(), m_surfaceType); format.setScanLineDirection(QVideoSurfaceFormat::TopToBottom); -#else - format.setScanLineDirection(QVideoSurfaceFormat::BottomToTop); -#endif if (!m_surface->start(format)) { //Surface doesn't support GLTextureHandle qWarning("Failed to activate video surface"); @@ -279,20 +263,21 @@ void AVFVideoRendererControl::updateVideoFrame(const CVTimeStamp &ts) } } else { //fallback to rendering frames to QImages - QImage frameData = m_frameRenderer->renderLayerToImage(playerLayer); + QSize size; + QImage frameData = m_frameRenderer->renderLayerToImage(playerLayer, &size); if (frameData.isNull()) { return; } QAbstractVideoBuffer *buffer = new QImageVideoBuffer(frameData); - QVideoFrame frame = QVideoFrame(buffer, m_nativeSize, QVideoFrame::Format_ARGB32); + QVideoFrame frame = QVideoFrame(buffer, size, QVideoFrame::Format_ARGB32); if (m_surface && frame.isValid()) { if (m_surface->isActive() && m_surface->surfaceFormat().pixelFormat() != frame.pixelFormat()) m_surface->stop(); if (!m_surface->isActive()) { - QVideoSurfaceFormat format(frame.size(), frame.pixelFormat(), QAbstractVideoBuffer::NoHandle); + QVideoSurfaceFormat format(frame.size(), frame.pixelFormat(), m_surfaceType); if (!m_surface->start(format)) { qWarning("Failed to activate video surface"); @@ -308,7 +293,4 @@ void AVFVideoRendererControl::updateVideoFrame(const CVTimeStamp &ts) void AVFVideoRendererControl::setupVideoOutput() { - AVPlayerLayer *playerLayer = static_cast<AVPlayerLayer*>(m_playerLayer); - if (playerLayer) - m_nativeSize = QSize(playerLayer.bounds.size.width, playerLayer.bounds.size.height); } diff --git a/src/plugins/avfoundation/mediaplayer/avfvideowidget.mm b/src/plugins/avfoundation/mediaplayer/avfvideowidget.mm index be349710c..0987342b4 100644 --- a/src/plugins/avfoundation/mediaplayer/avfvideowidget.mm +++ b/src/plugins/avfoundation/mediaplayer/avfvideowidget.mm @@ -38,12 +38,9 @@ ****************************************************************************/ #include "avfvideowidget.h" -#include <QtCore/QDebug> -#include <AVFoundation/AVFoundation.h> -#include <QtGui/QResizeEvent> -#include <QtGui/QPaintEvent> -#include <QtGui/QPainter> +#import <AVFoundation/AVFoundation.h> +#import <QuartzCore/CATransaction.h> #if defined(Q_OS_MACOS) #import <AppKit/AppKit.h> @@ -51,6 +48,11 @@ #import <UIKit/UIKit.h> #endif +#include <QtCore/QDebug> +#include <QtGui/QResizeEvent> +#include <QtGui/QPaintEvent> +#include <QtGui/QPainter> + QT_USE_NAMESPACE AVFVideoWidget::AVFVideoWidget(QWidget *parent) @@ -178,5 +180,8 @@ void AVFVideoWidget::updateAspectRatio() void AVFVideoWidget::updatePlayerLayerBounds(const QSize &size) { - m_playerLayer.bounds = CGRectMake(0.0f, 0.0f, (float)size.width(), (float)size.height()); + [CATransaction begin]; + [CATransaction setDisableActions: YES]; // disable animation/flicks + m_playerLayer.bounds = QRect(QPoint(0, 0), size).toCGRect(); + [CATransaction commit]; } diff --git a/src/plugins/avfoundation/mediaplayer/avfvideowindowcontrol.mm b/src/plugins/avfoundation/mediaplayer/avfvideowindowcontrol.mm index 7fa41fdc2..d61129ec9 100644 --- a/src/plugins/avfoundation/mediaplayer/avfvideowindowcontrol.mm +++ b/src/plugins/avfoundation/mediaplayer/avfvideowindowcontrol.mm @@ -40,6 +40,7 @@ #include "avfvideowindowcontrol.h" #include <AVFoundation/AVFoundation.h> +#import <QuartzCore/CATransaction.h> #if QT_HAS_INCLUDE(<AppKit/AppKit.h>) #include <AppKit/AppKit.h> @@ -244,10 +245,10 @@ void AVFVideoWindowControl::updateAspectRatio() void AVFVideoWindowControl::updatePlayerLayerBounds() { if (m_playerLayer) { - CGRect newBounds = CGRectMake(0, 0, - m_displayRect.width(), m_displayRect.height()); - m_playerLayer.bounds = newBounds; - m_playerLayer.position = CGPointMake(m_displayRect.x(), m_displayRect.y()); + [CATransaction begin]; + [CATransaction setDisableActions: YES]; // disable animation/flicks + m_playerLayer.frame = m_displayRect.toCGRect(); + [CATransaction commit]; } } diff --git a/src/plugins/avfoundation/mediaplayer/mediaplayer.pro b/src/plugins/avfoundation/mediaplayer/mediaplayer.pro index b60b276e9..f71e0c3b3 100644 --- a/src/plugins/avfoundation/mediaplayer/mediaplayer.pro +++ b/src/plugins/avfoundation/mediaplayer/mediaplayer.pro @@ -39,33 +39,23 @@ OBJECTIVE_SOURCES += \ avfvideowidget.mm } -ios|tvos { - qtConfig(opengl) { - HEADERS += \ - avfvideoframerenderer_ios.h \ - avfvideorenderercontrol.h \ - avfdisplaylink.h +qtConfig(opengl) { + HEADERS += \ + avfvideoframerenderer.h \ + avfvideorenderercontrol.h \ + avfdisplaylink.h - OBJECTIVE_SOURCES += \ - avfvideoframerenderer_ios.mm \ - avfvideorenderercontrol.mm \ - avfdisplaylink.mm - } + OBJECTIVE_SOURCES += \ + avfvideoframerenderer.mm \ + avfvideorenderercontrol.mm \ + avfdisplaylink.mm +} + +ios|tvos { LIBS += -framework Foundation } else { - LIBS += -framework AppKit - - qtConfig(opengl) { - HEADERS += \ - avfvideoframerenderer.h \ - avfvideorenderercontrol.h \ - avfdisplaylink.h - - OBJECTIVE_SOURCES += \ - avfvideoframerenderer.mm \ - avfvideorenderercontrol.mm \ - avfdisplaylink.mm - } + INCLUDEPATH += $$[QT_INSTALL_HEADERS] + LIBS += -framework AppKit -framework Metal } OTHER_FILES += \ diff --git a/src/plugins/common/evr/evrcustompresenter.cpp b/src/plugins/common/evr/evrcustompresenter.cpp index b2dd0426c..3405f6373 100644 --- a/src/plugins/common/evr/evrcustompresenter.cpp +++ b/src/plugins/common/evr/evrcustompresenter.cpp @@ -1065,6 +1065,13 @@ void EVRCustomPresenter::setSurface(QAbstractVideoSurface *surface) supportedFormatsChanged(); } +void EVRCustomPresenter::setCropRect(QRect cropRect) +{ + m_mutex.lock(); + m_cropRect = cropRect; + m_mutex.unlock(); +} + HRESULT EVRCustomPresenter::configureMixer(IMFTransform *mixer) { // Set the zoom rectangle (ie, the source clipping rectangle). @@ -1355,13 +1362,30 @@ HRESULT EVRCustomPresenter::createOptimalVideoType(IMFMediaType *proposedType, I hr = proposedType->GetUINT64(MF_MT_FRAME_SIZE, &size); width = int(HI32(size)); height = int(LO32(size)); - rcOutput.left = 0; - rcOutput.top = 0; - rcOutput.right = width; - rcOutput.bottom = height; + + if (m_cropRect.isValid()) { + rcOutput.left = m_cropRect.x(); + rcOutput.top = m_cropRect.y(); + rcOutput.right = m_cropRect.x() + m_cropRect.width(); + rcOutput.bottom = m_cropRect.y() + m_cropRect.height(); + + m_sourceRect.left = float(m_cropRect.x()) / width; + m_sourceRect.top = float(m_cropRect.y()) / height; + m_sourceRect.right = float(m_cropRect.x() + m_cropRect.width()) / width; + m_sourceRect.bottom = float(m_cropRect.y() + m_cropRect.height()) / height; + + if (m_mixer) + configureMixer(m_mixer); + } else { + rcOutput.left = 0; + rcOutput.top = 0; + rcOutput.right = width; + rcOutput.bottom = height; + } // Set the geometric aperture, and disable pan/scan. - displayArea = qt_evr_makeMFArea(0, 0, rcOutput.right, rcOutput.bottom); + displayArea = qt_evr_makeMFArea(0, 0, rcOutput.right - rcOutput.left, + rcOutput.bottom - rcOutput.top); hr = mtOptimal->SetUINT32(MF_MT_PAN_SCAN_ENABLED, FALSE); if (FAILED(hr)) @@ -1427,7 +1451,7 @@ HRESULT EVRCustomPresenter::setMediaType(IMFMediaType *mediaType) // Initialize the presenter engine with the new media type. // The presenter engine allocates the samples. - hr = m_presentEngine->createVideoSamples(mediaType, sampleQueue); + hr = m_presentEngine->createVideoSamples(mediaType, sampleQueue, m_cropRect.size()); if (FAILED(hr)) goto done; diff --git a/src/plugins/common/evr/evrcustompresenter.h b/src/plugins/common/evr/evrcustompresenter.h index c1c21580e..403158619 100644 --- a/src/plugins/common/evr/evrcustompresenter.h +++ b/src/plugins/common/evr/evrcustompresenter.h @@ -44,6 +44,7 @@ #include <qmutex.h> #include <qqueue.h> #include <qevent.h> +#include <qrect.h> #include <qvideosurfaceformat.h> #include "evrdefs.h" @@ -257,6 +258,7 @@ public: void supportedFormatsChanged(); void setSurface(QAbstractVideoSurface *surface); + void setCropRect(QRect cropRect); void startSurface(); void stopSurface(); @@ -368,6 +370,7 @@ private: QAbstractVideoSurface *m_surface; bool m_canRenderToSurface; qint64 m_positionOffset; // Seek position in microseconds. + QRect m_cropRect; // Video crop rectangle }; bool qt_evr_setCustomPresenter(IUnknown *evr, EVRCustomPresenter *presenter); diff --git a/src/plugins/common/evr/evrd3dpresentengine.cpp b/src/plugins/common/evr/evrd3dpresentengine.cpp index d8e2da6d3..0bf8a0506 100644 --- a/src/plugins/common/evr/evrd3dpresentengine.cpp +++ b/src/plugins/common/evr/evrd3dpresentengine.cpp @@ -520,7 +520,7 @@ void D3DPresentEngine::setHint(Hint hint, bool enable) m_useTextureRendering = enable && supportsTextureRendering(); } -HRESULT D3DPresentEngine::createVideoSamples(IMFMediaType *format, QList<IMFSample*> &videoSampleQueue) +HRESULT D3DPresentEngine::createVideoSamples(IMFMediaType *format, QList<IMFSample*> &videoSampleQueue, QSize frameSize) { if (!format) return MF_E_UNEXPECTED; @@ -537,6 +537,11 @@ HRESULT D3DPresentEngine::createVideoSamples(IMFMediaType *format, QList<IMFSamp if (FAILED(hr)) return hr; + if (frameSize.isValid() && !frameSize.isEmpty()) { + width = frameSize.width(); + height = frameSize.height(); + } + DWORD d3dFormat = 0; hr = qt_evr_getFourCC(format, &d3dFormat); if (FAILED(hr)) diff --git a/src/plugins/common/evr/evrd3dpresentengine.h b/src/plugins/common/evr/evrd3dpresentengine.h index 8e2a444f3..d7eeacfc0 100644 --- a/src/plugins/common/evr/evrd3dpresentengine.h +++ b/src/plugins/common/evr/evrd3dpresentengine.h @@ -41,6 +41,7 @@ #define EVRD3DPRESENTENGINE_H #include <QMutex> +#include <QSize> #include <QVideoSurfaceFormat> #include <d3d9.h> @@ -120,7 +121,7 @@ public: bool supportsTextureRendering() const; bool isTextureRenderingEnabled() const { return m_useTextureRendering; } - HRESULT createVideoSamples(IMFMediaType *format, QList<IMFSample*>& videoSampleQueue); + HRESULT createVideoSamples(IMFMediaType *format, QList<IMFSample*>& videoSampleQueue, QSize frameSize); QVideoSurfaceFormat videoSurfaceFormat() const { return m_surfaceFormat; } QVideoFrame makeVideoFrame(IMFSample* sample); diff --git a/src/plugins/common/evr/evrvideowindowcontrol.cpp b/src/plugins/common/evr/evrvideowindowcontrol.cpp index 95f63c2e7..cec263da9 100644 --- a/src/plugins/common/evr/evrvideowindowcontrol.cpp +++ b/src/plugins/common/evr/evrvideowindowcontrol.cpp @@ -134,26 +134,29 @@ void EvrVideoWindowControl::setDisplayRect(const QRect &rect) if (m_displayControl) { RECT displayRect = { rect.left(), rect.top(), rect.right() + 1, rect.bottom() + 1 }; - QSize sourceSize = nativeSize(); + QSize fullSize = nativeSize(); - RECT sourceRect = { 0, 0, sourceSize.width(), sourceSize.height() }; + RECT sourceRect; + if (m_cropRect.isValid()) + sourceRect = { m_cropRect.x(), m_cropRect.y(), m_cropRect.right() + 1, m_cropRect.bottom() + 1 }; + else + sourceRect = { 0, 0, fullSize.width(), fullSize.height() }; if (m_aspectRatioMode == Qt::KeepAspectRatioByExpanding) { QSize clippedSize = rect.size(); clippedSize.scale(sourceRect.right, sourceRect.bottom, Qt::KeepAspectRatio); - sourceRect.left = (sourceRect.right - clippedSize.width()) / 2; sourceRect.top = (sourceRect.bottom - clippedSize.height()) / 2; sourceRect.right = sourceRect.left + clippedSize.width(); sourceRect.bottom = sourceRect.top + clippedSize.height(); } - if (sourceSize.width() > 0 && sourceSize.height() > 0) { + if (sourceRect.right - sourceRect.left > 0 && sourceRect.bottom - sourceRect.top > 0) { MFVideoNormalizedRect sourceNormRect; - sourceNormRect.left = float(sourceRect.left) / float(sourceRect.right); - sourceNormRect.top = float(sourceRect.top) / float(sourceRect.bottom); - sourceNormRect.right = float(sourceRect.right) / float(sourceRect.right); - sourceNormRect.bottom = float(sourceRect.bottom) / float(sourceRect.bottom); + sourceNormRect.left = float(sourceRect.left) / float(fullSize.width()); + sourceNormRect.top = float(sourceRect.top) / float(fullSize.height()); + sourceNormRect.right = float(sourceRect.right) / float(fullSize.width()); + sourceNormRect.bottom = float(sourceRect.bottom) / float(fullSize.height()); m_displayControl->SetVideoPosition(&sourceNormRect, &displayRect); } else { m_displayControl->SetVideoPosition(NULL, &displayRect); @@ -164,6 +167,11 @@ void EvrVideoWindowControl::setDisplayRect(const QRect &rect) } } +void EvrVideoWindowControl::setCropRect(QRect cropRect) +{ + m_cropRect = cropRect; +} + bool EvrVideoWindowControl::isFullScreen() const { return m_fullScreen; diff --git a/src/plugins/common/evr/evrvideowindowcontrol.h b/src/plugins/common/evr/evrvideowindowcontrol.h index ce3b7746f..49d111b99 100644 --- a/src/plugins/common/evr/evrvideowindowcontrol.h +++ b/src/plugins/common/evr/evrvideowindowcontrol.h @@ -85,6 +85,8 @@ public: void applyImageControls(); + void setCropRect(QRect cropRect); + private: void clear(); DXVA2_Fixed32 scaleProcAmpValue(DWORD prop, int value) const; @@ -94,6 +96,7 @@ private: DWORD m_dirtyValues; Qt::AspectRatioMode m_aspectRatioMode; QRect m_displayRect; + QRect m_cropRect; int m_brightness; int m_contrast; int m_hue; diff --git a/src/plugins/coreaudio/coreaudiodeviceinfo.mm b/src/plugins/coreaudio/coreaudiodeviceinfo.mm index 1a79438cb..05d6c613d 100644 --- a/src/plugins/coreaudio/coreaudiodeviceinfo.mm +++ b/src/plugins/coreaudio/coreaudiodeviceinfo.mm @@ -60,11 +60,6 @@ CoreAudioDeviceInfo::CoreAudioDeviceInfo(const QByteArray &device, QAudio::Mode m_deviceId = AudioDeviceID(deviceID); #else //iOS m_device = device; - if (mode == QAudio::AudioInput) { - if (CoreAudioSessionManager::instance().category() != CoreAudioSessionManager::PlayAndRecord) { - CoreAudioSessionManager::instance().setCategory(CoreAudioSessionManager::PlayAndRecord); - } - } #endif } @@ -336,14 +331,6 @@ QList<QByteArray> CoreAudioDeviceInfo::availableDevices(QAudio::Mode mode) } } #else //iOS - if (mode == QAudio::AudioInput) { - if (CoreAudioSessionManager::instance().category() != CoreAudioSessionManager::PlayAndRecord) { - CoreAudioSessionManager::instance().setCategory(CoreAudioSessionManager::PlayAndRecord); - } - } - - CoreAudioSessionManager::instance().setActive(true); - if (mode == QAudio::AudioOutput) return CoreAudioSessionManager::instance().outputDevices(); if (mode == QAudio::AudioInput) diff --git a/src/plugins/coreaudio/coreaudioinput.mm b/src/plugins/coreaudio/coreaudioinput.mm index f7d511d27..7f305168f 100644 --- a/src/plugins/coreaudio/coreaudioinput.mm +++ b/src/plugins/coreaudio/coreaudioinput.mm @@ -483,6 +483,11 @@ CoreAudioInput::~CoreAudioInput() bool CoreAudioInput::open() { +#if defined(Q_OS_IOS) + CoreAudioSessionManager::instance().setCategory(CoreAudioSessionManager::PlayAndRecord, CoreAudioSessionManager::MixWithOthers); + CoreAudioSessionManager::instance().setActive(true); +#endif + if (m_isOpen) return true; diff --git a/src/plugins/coreaudio/coreaudiooutput.mm b/src/plugins/coreaudio/coreaudiooutput.mm index caa4a1abb..b8addc1cf 100644 --- a/src/plugins/coreaudio/coreaudiooutput.mm +++ b/src/plugins/coreaudio/coreaudiooutput.mm @@ -549,6 +549,13 @@ OSStatus CoreAudioOutput::renderCallback(void *inRefCon, AudioUnitRenderActionFl bool CoreAudioOutput::open() { +#if defined(Q_OS_IOS) + // Set default category to Ambient (implies MixWithOthers). This makes sure audio stops playing + // if the screen is locked or if the Silent switch is toggled. + CoreAudioSessionManager::instance().setCategory(CoreAudioSessionManager::Ambient, CoreAudioSessionManager::None); + CoreAudioSessionManager::instance().setActive(true); +#endif + if (m_errorCode != QAudio::NoError) return false; diff --git a/src/plugins/coreaudio/coreaudiosessionmanager.mm b/src/plugins/coreaudio/coreaudiosessionmanager.mm index 923a1942f..6c86f0753 100644 --- a/src/plugins/coreaudio/coreaudiosessionmanager.mm +++ b/src/plugins/coreaudio/coreaudiosessionmanager.mm @@ -38,7 +38,6 @@ ****************************************************************************/ #include "coreaudiosessionmanager.h" - #import <AVFoundation/AVAudioSession.h> #import <Foundation/Foundation.h> @@ -215,10 +214,6 @@ CoreAudioSessionManager::CoreAudioSessionManager() : QObject(0) { m_sessionObserver = [[CoreAudioSessionObserver alloc] initWithAudioSessionManager:this]; - setActive(true); - // Set default category to Ambient (implies MixWithOthers). This makes sure audio stops playing - // if the screen is locked or if the Silent switch is toggled. - setCategory(CoreAudioSessionManager::Ambient, CoreAudioSessionManager::None); } CoreAudioSessionManager::~CoreAudioSessionManager() diff --git a/src/plugins/directshow/camera/dscamerasession.cpp b/src/plugins/directshow/camera/dscamerasession.cpp index cee3e9c56..e47142be1 100644 --- a/src/plugins/directshow/camera/dscamerasession.cpp +++ b/src/plugins/directshow/camera/dscamerasession.cpp @@ -428,6 +428,7 @@ bool DSCameraSession::unload() SAFE_RELEASE(m_nullRendererFilter); SAFE_RELEASE(m_filterGraph); SAFE_RELEASE(m_graphBuilder); + SAFE_RELEASE(m_outputPin); setStatus(QCamera::UnloadedStatus); @@ -781,6 +782,9 @@ bool DSCameraSession::createFilterGraph() goto failed; } + if (!DirectShowUtils::getPin(m_sourceFilter, PINDIR_OUTPUT, PIN_CATEGORY_CAPTURE, &m_outputPin, &hr)) + qWarning() << "Failed to get the pin for the video control:" << hr; + // Sample grabber filter if (!m_previewSampleGrabber) { m_previewSampleGrabber = new DirectShowSampleGrabber(this); @@ -1055,24 +1059,18 @@ void DSCameraSession::updateSourceCapabilities() reinterpret_cast<void**>(&pVideoControl)); if (FAILED(hr)) { qWarning() << "Failed to get the video control"; - } else { - IPin *pPin = nullptr; - if (!DirectShowUtils::getPin(m_sourceFilter, PINDIR_OUTPUT, &pPin, &hr)) { - qWarning() << "Failed to get the pin for the video control"; - } else { - long supportedModes; - hr = pVideoControl->GetCaps(pPin, &supportedModes); - if (FAILED(hr)) { - qWarning() << "Failed to get the supported modes of the video control"; - } else if (supportedModes & VideoControlFlag_FlipHorizontal) { - long mode; - hr = pVideoControl->GetMode(pPin, &mode); - if (FAILED(hr)) - qWarning() << "Failed to get the mode of the video control"; - else if (supportedModes & VideoControlFlag_FlipHorizontal) - m_needsHorizontalMirroring = (mode & VideoControlFlag_FlipHorizontal); - } - pPin->Release(); + } else if (m_outputPin) { + long supportedModes; + hr = pVideoControl->GetCaps(m_outputPin, &supportedModes); + if (FAILED(hr)) { + qWarning() << "Failed to get the supported modes of the video control"; + } else if (supportedModes & VideoControlFlag_FlipHorizontal) { + long mode; + hr = pVideoControl->GetMode(m_outputPin, &mode); + if (FAILED(hr)) + qWarning() << "Failed to get the mode of the video control"; + else if (supportedModes & VideoControlFlag_FlipHorizontal) + m_needsHorizontalMirroring = (mode & VideoControlFlag_FlipHorizontal); } pVideoControl->Release(); } @@ -1107,28 +1105,22 @@ void DSCameraSession::updateSourceCapabilities() QList<QCamera::FrameRateRange> frameRateRanges; - if (pVideoControl) { - IPin *pPin = nullptr; - if (!DirectShowUtils::getPin(m_sourceFilter, PINDIR_OUTPUT, &pPin, &hr)) { - qWarning() << "Failed to get the pin for the video control"; - } else { - long listSize = 0; - LONGLONG *frameRates = nullptr; - SIZE size = { resolution.width(), resolution.height() }; - hr = pVideoControl->GetFrameRateList(pPin, iIndex, size, &listSize, &frameRates); - if (hr == S_OK && listSize > 0 && frameRates) { - for (long i = 0; i < listSize; ++i) { - qreal fr = qreal(10000000) / frameRates[i]; - frameRateRanges.append(QCamera::FrameRateRange(fr, fr)); - } - - // Make sure higher frame rates come first - std::sort(frameRateRanges.begin(), frameRateRanges.end(), qt_frameRateRangeGreaterThan); + if (pVideoControl && m_outputPin) { + long listSize = 0; + LONGLONG *frameRates = nullptr; + SIZE size = { resolution.width(), resolution.height() }; + hr = pVideoControl->GetFrameRateList(m_outputPin, iIndex, size, &listSize, &frameRates); + if (hr == S_OK && listSize > 0 && frameRates) { + for (long i = 0; i < listSize; ++i) { + qreal fr = qreal(10000000) / frameRates[i]; + frameRateRanges.append(QCamera::FrameRateRange(fr, fr)); } - CoTaskMemFree(frameRates); - pPin->Release(); + // Make sure higher frame rates come first + std::sort(frameRateRanges.begin(), frameRateRanges.end(), qt_frameRateRangeGreaterThan); } + + CoTaskMemFree(frameRates); } if (frameRateRanges.isEmpty()) { @@ -1146,9 +1138,14 @@ void DSCameraSession::updateSourceCapabilities() m_supportedViewfinderSettings.append(settings); m_supportedFormats.append(DirectShowMediaType(*pmt)); } - - + } else { + OLECHAR *guidString = nullptr; + StringFromCLSID(pmt->subtype, &guidString); + if (guidString) + qWarning() << "Unsupported media type:" << QString::fromWCharArray(guidString); + ::CoTaskMemFree(guidString); } + DirectShowMediaType::deleteType(pmt); } } diff --git a/src/plugins/directshow/camera/dscamerasession.h b/src/plugins/directshow/camera/dscamerasession.h index 5e7d026c2..9f88163b9 100644 --- a/src/plugins/directshow/camera/dscamerasession.h +++ b/src/plugins/directshow/camera/dscamerasession.h @@ -231,6 +231,8 @@ private: QMap<QCameraImageProcessingControl::ProcessingParameter, QVariant> m_pendingImageProcessingParametrs; + IPin *m_outputPin = nullptr; + friend class SampleGrabberCallbackPrivate; }; diff --git a/src/plugins/directshow/common/directshowmediatype.cpp b/src/plugins/directshow/common/directshowmediatype.cpp index 103f1ddc1..3429f4848 100644 --- a/src/plugins/directshow/common/directshowmediatype.cpp +++ b/src/plugins/directshow/common/directshowmediatype.cpp @@ -40,6 +40,11 @@ #include "directshowmediatype.h" #include "directshowglobal.h" +#include <initguid.h> + +DEFINE_GUID(MEDIASUBTYPE_Y800, 0x30303859, 0x0000, 0x0010, 0x80, 0x00, + 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); + namespace { struct TypeLookup @@ -66,7 +71,8 @@ namespace { QVideoFrame::Format_NV12, MEDIASUBTYPE_NV12 }, { QVideoFrame::Format_YUV420P, MEDIASUBTYPE_IYUV }, { QVideoFrame::Format_YUV420P, MEDIASUBTYPE_I420 }, - { QVideoFrame::Format_Jpeg, MEDIASUBTYPE_MJPG } + { QVideoFrame::Format_Jpeg, MEDIASUBTYPE_MJPG }, + { QVideoFrame::Format_Y8, MEDIASUBTYPE_Y800 }, }; } @@ -294,6 +300,7 @@ int DirectShowMediaType::bytesPerLine(const QVideoSurfaceFormat &format) return format.frameWidth() * 4; // 24 bpp packed formats. case QVideoFrame::Format_RGB24: + case QVideoFrame::Format_BGR24: return PAD_TO_DWORD(format.frameWidth() * 3); // 16 bpp packed formats. case QVideoFrame::Format_RGB565: diff --git a/src/plugins/directshow/common/directshowmediatypeenum.cpp b/src/plugins/directshow/common/directshowmediatypeenum.cpp index 0ff147fea..02281bb98 100644 --- a/src/plugins/directshow/common/directshowmediatypeenum.cpp +++ b/src/plugins/directshow/common/directshowmediatypeenum.cpp @@ -42,7 +42,8 @@ #include "directshowpin.h" DirectShowMediaTypeEnum::DirectShowMediaTypeEnum(DirectShowPin *pin) - : m_mediaTypes(pin->supportedMediaTypes()) + : m_pin(pin) + , m_mediaTypes(pin->supportedMediaTypes()) { m_pin->AddRef(); } diff --git a/src/plugins/directshow/common/directshowutils.cpp b/src/plugins/directshow/common/directshowutils.cpp index 1457837ce..9222ad779 100644 --- a/src/plugins/directshow/common/directshowutils.cpp +++ b/src/plugins/directshow/common/directshowutils.cpp @@ -93,6 +93,25 @@ bool DirectShowUtils::hasPinDirection(IPin *pin, PIN_DIRECTION direction, HRESUL return (pinDir == direction); } +bool pinMatchesCategory(IPin* pPin, REFGUID category) +{ + bool found = false; + IKsPropertySet *pKs = nullptr; + DirectShowUtils::ScopedSafeRelease<IKsPropertySet> ks_property { &pKs }; + HRESULT hr = pPin->QueryInterface(IID_PPV_ARGS(&pKs)); + + if (SUCCEEDED(hr)) { + GUID pin_category; + DWORD return_value; + hr = pKs->Get(AMPROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, NULL, 0, + &pin_category, sizeof(pin_category), &return_value); + if (SUCCEEDED(hr) && (return_value == sizeof(pin_category))) + found = (pin_category == category); + } + + return found; +} + /** * @brief DirectShowUtils::getPin * @param filter @@ -101,7 +120,7 @@ bool DirectShowUtils::hasPinDirection(IPin *pin, PIN_DIRECTION direction, HRESUL * @param hrOut * @return */ -bool DirectShowUtils::getPin(IBaseFilter *filter, PIN_DIRECTION pinDirection, IPin **pin, HRESULT *hrOut) +bool DirectShowUtils::getPin(IBaseFilter *filter, PIN_DIRECTION pinDirection, REFGUID category, IPin **pin, HRESULT *hrOut) { IEnumPins *enumPins = nullptr; const ScopedSafeRelease<IEnumPins> releaseEnumPins { &enumPins }; @@ -122,9 +141,11 @@ bool DirectShowUtils::getPin(IBaseFilter *filter, PIN_DIRECTION pinDirection, IP PIN_DIRECTION currentPinDir; *hrOut = nextPin->QueryDirection(¤tPinDir); if (currentPinDir == pinDirection) { - *pin = nextPin; - (*pin)->AddRef(); - return true; + if (category == GUID_NULL || pinMatchesCategory(nextPin, category)) { + *pin = nextPin; + (*pin)->AddRef(); + return true; + } } } diff --git a/src/plugins/directshow/common/directshowutils.h b/src/plugins/directshow/common/directshowutils.h index 5f2cfaa23..ec761abe6 100644 --- a/src/plugins/directshow/common/directshowutils.h +++ b/src/plugins/directshow/common/directshowutils.h @@ -68,7 +68,7 @@ struct ScopedSafeRelease } }; -bool getPin(IBaseFilter *filter, PIN_DIRECTION pinDirection, IPin **pin, HRESULT *hrOut); +bool getPin(IBaseFilter *filter, PIN_DIRECTION pinDirection, REFGUID category, IPin **pin, HRESULT *hrOut); bool isPinConnected(IPin *pin, HRESULT *hrOut = nullptr); bool hasPinDirection(IPin *pin, PIN_DIRECTION direction, HRESULT *hrOut = nullptr); bool matchPin(IPin *pin, PIN_DIRECTION pinDirection, BOOL shouldBeConnected, HRESULT *hrOut = nullptr); diff --git a/src/plugins/directshow/player/directshowioreader.cpp b/src/plugins/directshow/player/directshowioreader.cpp index ced10ea10..3318d57b5 100644 --- a/src/plugins/directshow/player/directshowioreader.cpp +++ b/src/plugins/directshow/player/directshowioreader.cpp @@ -155,7 +155,7 @@ HRESULT DirectShowIOReader::Request(IMediaSample *pSample, DWORD_PTR dwUser) return VFW_E_SAMPLE_TIME_NOT_SET; } LONGLONG position = startTime / 10000000; - LONG length = (endTime - startTime) / 10000000; + LONG length = qMin<qint64>((endTime - startTime) / 10000000, m_availableLength); auto request = new DirectShowSampleRequest(pSample, dwUser, position, length, buffer); diff --git a/src/plugins/directshow/player/directshowmetadatacontrol.cpp b/src/plugins/directshow/player/directshowmetadatacontrol.cpp index 46674143e..d9864870a 100644 --- a/src/plugins/directshow/player/directshowmetadatacontrol.cpp +++ b/src/plugins/directshow/player/directshowmetadatacontrol.cpp @@ -64,7 +64,7 @@ #endif #if QT_CONFIG(wshellitem) -#include <ShlObj.h> +#include <shlobj.h> #include <propkeydef.h> #include <private/qsystemlibrary_p.h> diff --git a/src/plugins/gstreamer/camerabin/camerabinfocus.cpp b/src/plugins/gstreamer/camerabin/camerabinfocus.cpp index d4e7fa699..a9cdf8e67 100644 --- a/src/plugins/gstreamer/camerabin/camerabinfocus.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinfocus.cpp @@ -192,8 +192,6 @@ void CameraBinFocus::setFocusPointMode(QCameraFocus::FocusPointMode mode) bool CameraBinFocus::isFocusPointModeSupported(QCameraFocus::FocusPointMode mode) const { - return mode == QCameraFocus::FocusPointAuto || mode == QCameraFocus::FocusPointCustom; - switch (mode) { case QCameraFocus::FocusPointAuto: case QCameraFocus::FocusPointCustom: diff --git a/src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp b/src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp index 52ec75f44..b164bc31a 100644 --- a/src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp @@ -366,12 +366,9 @@ bool CameraBinImageCapture::processBusMessage(const QGstreamerMessage &message) #ifdef DEBUG_CAPTURE qDebug() << Q_FUNC_INFO << "Dropped saving file" << fileName; #endif - //camerabin creates an empty file when captured buffer is dropped, - //let's remove it QFileInfo info(QString::fromUtf8(fileName)); - if (info.exists() && info.isFile() && info.size() == 0) { + if (info.exists() && info.isFile()) QFile(info.absoluteFilePath()).remove(); - } } } } diff --git a/src/plugins/gstreamer/camerabin/camerabinsession.cpp b/src/plugins/gstreamer/camerabin/camerabinsession.cpp index 3e505a413..532fb43ec 100644 --- a/src/plugins/gstreamer/camerabin/camerabinsession.cpp +++ b/src/plugins/gstreamer/camerabin/camerabinsession.cpp @@ -347,10 +347,12 @@ void CameraBinSession::setupCaptureResolution() // If capture resolution is specified, use it also for the viewfinder to avoid caps negotiation // to fail. if (m_usingWrapperCameraBinSrc) { - if (m_captureMode == QCamera::CaptureStillImage && !imageResolution.isEmpty()) - viewfinderResolution = imageResolution; - else if (m_captureMode == QCamera::CaptureVideo && !videoResolution.isEmpty()) - viewfinderResolution = videoResolution; + if (viewfinderResolution.isEmpty()) { + if (m_captureMode == QCamera::CaptureStillImage && !imageResolution.isEmpty()) + viewfinderResolution = imageResolution; + else if (m_captureMode == QCamera::CaptureVideo && !videoResolution.isEmpty()) + viewfinderResolution = videoResolution; + } // Make sure we don't use incompatible frame rate and pixel format with the new resolution if (viewfinderResolution != m_viewfinderSettings.resolution() && @@ -477,6 +479,15 @@ GstElement *CameraBinSession::buildCameraSource() m_inputDeviceHasChanged = false; m_usingWrapperCameraBinSrc = false; + if (m_videoSrc) { + gst_object_unref(GST_OBJECT(m_videoSrc)); + m_videoSrc = 0; + } + if (m_cameraSrc) { + gst_object_unref(GST_OBJECT(m_cameraSrc)); + m_cameraSrc = 0; + } + GstElement *camSrc = 0; g_object_get(G_OBJECT(m_camerabin), CAMERA_SOURCE_PROPERTY, &camSrc, NULL); @@ -1325,6 +1336,7 @@ QList< QPair<int,int> > CameraBinSession::supportedFrameRates(const QSize &frame g_value_copy(oldRate, &rate); gst_structure_remove_all_fields(structure); gst_structure_set_value(structure, "framerate", &rate); + g_value_unset(&rate); } #if GST_CHECK_VERSION(1,0,0) caps = gst_caps_simplify(caps); @@ -1446,6 +1458,8 @@ QList<QSize> CameraBinSession::supportedResolutions(QPair<int,int> rate, gst_structure_remove_all_fields(structure); gst_structure_set_value(structure, "width", &w); gst_structure_set_value(structure, "height", &h); + g_value_unset(&w); + g_value_unset(&h); } #if GST_CHECK_VERSION(1,0,0) diff --git a/src/plugins/m3u/qm3uhandler.cpp b/src/plugins/m3u/qm3uhandler.cpp index 5e05994ef..c621a181e 100644 --- a/src/plugins/m3u/qm3uhandler.cpp +++ b/src/plugins/m3u/qm3uhandler.cpp @@ -78,14 +78,14 @@ public: delete m_textStream; } - virtual bool atEnd() const + bool atEnd() const override { //we can't just use m_textStream->atEnd(), //for files with empty lines/comments at end return nextResource.isNull(); } - virtual QMediaContent readItem() + QMediaContent readItem() override { QMediaContent item; if (!nextResource.isNull()) @@ -136,7 +136,7 @@ public: return item; } - virtual void close() + void close() override { } @@ -161,13 +161,13 @@ public: delete m_textStream; } - virtual bool writeItem(const QMediaContent& item) + bool writeItem(const QMediaContent& item) override { *m_textStream << item.request().url().toString() << Qt::endl; return true; } - virtual void close() + void close() override { } diff --git a/src/plugins/m3u/qm3uhandler.h b/src/plugins/m3u/qm3uhandler.h index 509ac11d5..1bc0684d3 100644 --- a/src/plugins/m3u/qm3uhandler.h +++ b/src/plugins/m3u/qm3uhandler.h @@ -54,15 +54,15 @@ public: explicit QM3uPlaylistPlugin(QObject *parent = 0); virtual ~QM3uPlaylistPlugin(); - virtual bool canRead(QIODevice *device, const QByteArray &format = QByteArray() ) const; - virtual bool canRead(const QUrl& location, const QByteArray &format = QByteArray()) const; + bool canRead(QIODevice *device, const QByteArray &format = QByteArray()) const override; + bool canRead(const QUrl& location, const QByteArray &format = QByteArray()) const override; - virtual bool canWrite(QIODevice *device, const QByteArray &format) const; + bool canWrite(QIODevice *device, const QByteArray &format) const override; - virtual QMediaPlaylistReader *createReader(QIODevice *device, const QByteArray &format = QByteArray()); - virtual QMediaPlaylistReader *createReader(const QUrl& location, const QByteArray &format = QByteArray()); + QMediaPlaylistReader *createReader(QIODevice *device, const QByteArray &format = QByteArray()) override; + QMediaPlaylistReader *createReader(const QUrl& location, const QByteArray &format = QByteArray()) override; - virtual QMediaPlaylistWriter *createWriter(QIODevice *device, const QByteArray &format); + QMediaPlaylistWriter *createWriter(QIODevice *device, const QByteArray &format) override; }; #endif // QM3UHANDLER_H diff --git a/src/plugins/pulseaudio/qaudiodeviceinfo_pulse.h b/src/plugins/pulseaudio/qaudiodeviceinfo_pulse.h index 64537229f..1cec772c0 100644 --- a/src/plugins/pulseaudio/qaudiodeviceinfo_pulse.h +++ b/src/plugins/pulseaudio/qaudiodeviceinfo_pulse.h @@ -69,15 +69,15 @@ public: QPulseAudioDeviceInfo(const QByteArray &device, QAudio::Mode mode); ~QPulseAudioDeviceInfo() {} - QAudioFormat preferredFormat() const; - bool isFormatSupported(const QAudioFormat &format) const; - QString deviceName() const; - QStringList supportedCodecs(); - QList<int> supportedSampleRates(); - QList<int> supportedChannelCounts(); - QList<int> supportedSampleSizes(); - QList<QAudioFormat::Endian> supportedByteOrders(); - QList<QAudioFormat::SampleType> supportedSampleTypes(); + QAudioFormat preferredFormat() const override; + bool isFormatSupported(const QAudioFormat &format) const override; + QString deviceName() const override; + QStringList supportedCodecs() override; + QList<int> supportedSampleRates() override; + QList<int> supportedChannelCounts() override; + QList<int> supportedSampleSizes() override; + QList<QAudioFormat::Endian> supportedByteOrders() override; + QList<QAudioFormat::SampleType> supportedSampleTypes() override; private: QByteArray m_device; diff --git a/src/plugins/pulseaudio/qaudioinput_pulse.cpp b/src/plugins/pulseaudio/qaudioinput_pulse.cpp index 2b5325132..b68b4af1b 100644 --- a/src/plugins/pulseaudio/qaudioinput_pulse.cpp +++ b/src/plugins/pulseaudio/qaudioinput_pulse.cpp @@ -402,6 +402,8 @@ int QPulseAudioInput::bytesReady() const qint64 QPulseAudioInput::read(char *data, qint64 len) { + Q_ASSERT(data != nullptr || len == 0); + m_bytesAvailable = checkBytesReady(); setError(QAudio::NoError); @@ -411,7 +413,8 @@ qint64 QPulseAudioInput::read(char *data, qint64 len) if (!m_pullMode && !m_tempBuffer.isEmpty()) { readBytes = qMin(static_cast<int>(len), m_tempBuffer.size()); - memcpy(data, m_tempBuffer.constData(), readBytes); + if (readBytes) + memcpy(data, m_tempBuffer.constData(), readBytes); m_totalTimeValue += readBytes; if (readBytes < m_tempBuffer.size()) { @@ -502,9 +505,10 @@ qint64 QPulseAudioInput::read(char *data, qint64 len) void QPulseAudioInput::applyVolume(const void *src, void *dest, int len) { + Q_ASSERT((src && dest) || len == 0); if (m_volume < 1.f) QAudioHelperInternal::qMultiplySamples(m_volume, m_format, src, dest, len); - else + else if (len) memcpy(dest, src, len); } diff --git a/src/plugins/pulseaudio/qaudioinput_pulse.h b/src/plugins/pulseaudio/qaudioinput_pulse.h index 3a6cf03c4..dce212a25 100644 --- a/src/plugins/pulseaudio/qaudioinput_pulse.h +++ b/src/plugins/pulseaudio/qaudioinput_pulse.h @@ -78,27 +78,27 @@ public: qint64 read(char *data, qint64 len); - void start(QIODevice *device); - QIODevice *start(); - void stop(); - void reset(); - void suspend(); - void resume(); - int bytesReady() const; - int periodSize() const; - void setBufferSize(int value); - int bufferSize() const; - void setNotifyInterval(int milliSeconds); - int notifyInterval() const; - qint64 processedUSecs() const; - qint64 elapsedUSecs() const; - QAudio::Error error() const; - QAudio::State state() const; - void setFormat(const QAudioFormat &format); - QAudioFormat format() const; - - void setVolume(qreal volume); - qreal volume() const; + void start(QIODevice *device) override; + QIODevice *start() override; + void stop() override; + void reset() override; + void suspend() override; + void resume() override; + int bytesReady() const override; + int periodSize() const override; + void setBufferSize(int value) override; + int bufferSize() const override; + void setNotifyInterval(int milliSeconds) override; + int notifyInterval() const override; + qint64 processedUSecs() const override; + qint64 elapsedUSecs() const override; + QAudio::Error error() const override; + QAudio::State state() const override; + void setFormat(const QAudioFormat &format) override; + QAudioFormat format() const override; + + void setVolume(qreal volume) override; + qreal volume() const override; qint64 m_totalTimeValue; QIODevice *m_audioSource; @@ -147,8 +147,8 @@ public: PulseInputPrivate(QPulseAudioInput *audio); ~PulseInputPrivate() {}; - qint64 readData(char *data, qint64 len); - qint64 writeData(const char *data, qint64 len); + qint64 readData(char *data, qint64 len) override; + qint64 writeData(const char *data, qint64 len) override; void trigger(); diff --git a/src/plugins/pulseaudio/qaudiooutput_pulse.cpp b/src/plugins/pulseaudio/qaudiooutput_pulse.cpp index 19ddac1e5..765efe036 100644 --- a/src/plugins/pulseaudio/qaudiooutput_pulse.cpp +++ b/src/plugins/pulseaudio/qaudiooutput_pulse.cpp @@ -506,27 +506,30 @@ qint64 QPulseAudioOutput::write(const char *data, qint64 len) pulseEngine->lock(); - len = qMin(len, static_cast<qint64>(pa_stream_writable_size(m_stream))); + size_t nbytes = len; + void *dest = nullptr; + + if (pa_stream_begin_write(m_stream, &dest, &nbytes) < 0) { + qWarning("QAudioSink(pulseaudio): pa_stream_begin_write, error = %s", + pa_strerror(pa_context_errno(pulseEngine->context()))); + setError(QAudio::IOError); + return 0; + } + + len = qMin(len, qint64(nbytes)); if (m_volume < 1.0f) { // Don't use PulseAudio volume, as it might affect all other streams of the same category // or even affect the system volume if flat volumes are enabled - void *dest = NULL; - size_t nbytes = len; - if (pa_stream_begin_write(m_stream, &dest, &nbytes) < 0) { - qWarning("QAudioOutput(pulseaudio): pa_stream_begin_write, error = %s", - pa_strerror(pa_context_errno(pulseEngine->context()))); - setError(QAudio::IOError); - return 0; - } - - len = int(nbytes); QAudioHelperInternal::qMultiplySamples(m_volume, m_format, data, dest, len); - data = reinterpret_cast<char *>(dest); + } else { + memcpy(dest, data, len); } - if (pa_stream_write(m_stream, data, len, NULL, 0, PA_SEEK_RELATIVE) < 0) { - qWarning("QAudioOutput(pulseaudio): pa_stream_write, error = %s", + data = reinterpret_cast<char *>(dest); + + if ((pa_stream_write(m_stream, data, len, nullptr, 0, PA_SEEK_RELATIVE)) < 0) { + qWarning("QAudioSink(pulseaudio): pa_stream_write, error = %s", pa_strerror(pa_context_errno(pulseEngine->context()))); setError(QAudio::IOError); return 0; @@ -683,7 +686,6 @@ qint64 PulseOutputPrivate::readData(char *data, qint64 len) qint64 PulseOutputPrivate::writeData(const char *data, qint64 len) { - int retry = 0; qint64 written = 0; if ((m_audioDevice->m_deviceState == QAudio::ActiveState @@ -691,10 +693,8 @@ qint64 PulseOutputPrivate::writeData(const char *data, qint64 len) while(written < len) { int chunk = m_audioDevice->write(data+written, (len-written)); if (chunk <= 0) - retry++; - written+=chunk; - if (retry > 10) return written; + written+=chunk; } } diff --git a/src/plugins/pulseaudio/qaudiooutput_pulse.h b/src/plugins/pulseaudio/qaudiooutput_pulse.h index 40d052681..e11f2ab2f 100644 --- a/src/plugins/pulseaudio/qaudiooutput_pulse.h +++ b/src/plugins/pulseaudio/qaudiooutput_pulse.h @@ -75,30 +75,30 @@ public: QPulseAudioOutput(const QByteArray &device); ~QPulseAudioOutput(); - void start(QIODevice *device); - QIODevice *start(); - void stop(); - void reset(); - void suspend(); - void resume(); - int bytesFree() const; - int periodSize() const; - void setBufferSize(int value); - int bufferSize() const; - void setNotifyInterval(int milliSeconds); - int notifyInterval() const; - qint64 processedUSecs() const; - qint64 elapsedUSecs() const; - QAudio::Error error() const; - QAudio::State state() const; - void setFormat(const QAudioFormat &format); - QAudioFormat format() const; - - void setVolume(qreal volume); - qreal volume() const; - - void setCategory(const QString &category); - QString category() const; + void start(QIODevice *device) override; + QIODevice *start() override; + void stop() override; + void reset() override; + void suspend() override; + void resume() override; + int bytesFree() const override; + int periodSize() const override; + void setBufferSize(int value) override; + int bufferSize() const override; + void setNotifyInterval(int milliSeconds) override; + int notifyInterval() const override; + qint64 processedUSecs() const override; + qint64 elapsedUSecs() const override; + QAudio::Error error() const override; + QAudio::State state() const override; + void setFormat(const QAudioFormat &format) override; + QAudioFormat format() const override; + + void setVolume(qreal volume) override; + qreal volume() const override; + + void setCategory(const QString &category) override; + QString category() const override; public: void streamUnderflowCallback(); @@ -154,8 +154,8 @@ public: virtual ~PulseOutputPrivate() {} protected: - qint64 readData(char *data, qint64 len); - qint64 writeData(const char *data, qint64 len); + qint64 readData(char *data, qint64 len) override; + qint64 writeData(const char *data, qint64 len) override; private: QPulseAudioOutput *m_audioDevice; diff --git a/src/plugins/pulseaudio/qpulseaudioplugin.h b/src/plugins/pulseaudio/qpulseaudioplugin.h index 120d57df5..7d27cad48 100644 --- a/src/plugins/pulseaudio/qpulseaudioplugin.h +++ b/src/plugins/pulseaudio/qpulseaudioplugin.h @@ -58,11 +58,11 @@ public: QPulseAudioPlugin(QObject *parent = 0); ~QPulseAudioPlugin() {} - QByteArray defaultDevice(QAudio::Mode mode) const; - QList<QByteArray> availableDevices(QAudio::Mode mode) const; - QAbstractAudioInput *createInput(const QByteArray &device); - QAbstractAudioOutput *createOutput(const QByteArray &device); - QAbstractAudioDeviceInfo *createDeviceInfo(const QByteArray &device, QAudio::Mode mode); + QByteArray defaultDevice(QAudio::Mode mode) const override; + QList<QByteArray> availableDevices(QAudio::Mode mode) const override; + QAbstractAudioInput *createInput(const QByteArray &device) override; + QAbstractAudioOutput *createOutput(const QByteArray &device) override; + QAbstractAudioDeviceInfo *createDeviceInfo(const QByteArray &device, QAudio::Mode mode) override; private: QPulseAudioEngine *m_pulseEngine; diff --git a/src/plugins/qnx-audio/audio/audio.pro b/src/plugins/qnx-audio/audio/audio.pro index bd69dfe1e..d63e58676 100644 --- a/src/plugins/qnx-audio/audio/audio.pro +++ b/src/plugins/qnx-audio/audio/audio.pro @@ -19,4 +19,5 @@ SOURCES += qnxaudioplugin.cpp \ OTHER_FILES += qnx_audio.json PLUGIN_TYPE = audio +PLUGIN_CLASS_NAME = QnxAudioPlugin load(qt_plugin) diff --git a/src/plugins/qnx-audio/audio/qnxaudioinput.cpp b/src/plugins/qnx-audio/audio/qnxaudioinput.cpp index 70b83390c..9fca8a7e1 100644 --- a/src/plugins/qnx-audio/audio/qnxaudioinput.cpp +++ b/src/plugins/qnx-audio/audio/qnxaudioinput.cpp @@ -331,7 +331,7 @@ bool QnxAudioInput::open() m_pcmNotifier = new QSocketNotifier(snd_pcm_file_descriptor(m_pcmHandle, SND_PCM_CHANNEL_CAPTURE), QSocketNotifier::Read, this); - connect(m_pcmNotifier, SIGNAL(activated(int)), SLOT(userFeed())); + connect(m_pcmNotifier, SIGNAL(activated(QSocketDescriptor)), SLOT(userFeed())); return true; } diff --git a/src/plugins/qnx/common/windowgrabber.cpp b/src/plugins/qnx/common/windowgrabber.cpp index 9a65ad3a0..b2470c250 100644 --- a/src/plugins/qnx/common/windowgrabber.cpp +++ b/src/plugins/qnx/common/windowgrabber.cpp @@ -393,8 +393,12 @@ GLuint WindowGrabberImage::getTexture(screen_window_t window, const QSize &size) { if (size != m_size) { - if (!m_glTexture) - glGenTextures(1, &m_glTexture); + // create a brand new texture to be the KHR image sibling, as + // previously used textures cannot be reused with new KHR image + // sources - note that glDeleteTextures handles nullptr gracefully + glDeleteTextures(1, &m_glTexture); + glGenTextures(1, &m_glTexture); + glBindTexture(GL_TEXTURE_2D, m_glTexture); if (m_eglImage) { glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, 0); diff --git a/src/plugins/videonode/imx6/imx6.pro b/src/plugins/videonode/imx6/imx6.pro index 43e17e725..0e9ed8b73 100644 --- a/src/plugins/videonode/imx6/imx6.pro +++ b/src/plugins/videonode/imx6/imx6.pro @@ -1,12 +1,8 @@ TARGET = imx6vivantevideonode -QT += multimedia-private qtmultimediaquicktools-private +QT += multimedia-private qtmultimediaquicktools-private multimediagsttools-private -qtConfig(gstreamer_imxcommon) { - QT += multimediagsttools-private - QMAKE_USE += gstreamer_imxcommon - DEFINES += GST_USE_UNSTABLE_API -} +QMAKE_USE += gstreamer HEADERS += \ qsgvivantevideonode.h \ diff --git a/src/plugins/videonode/imx6/qsgvivantevideomaterial.cpp b/src/plugins/videonode/imx6/qsgvivantevideomaterial.cpp index e1468fe34..c44a03896 100644 --- a/src/plugins/videonode/imx6/qsgvivantevideomaterial.cpp +++ b/src/plugins/videonode/imx6/qsgvivantevideomaterial.cpp @@ -50,9 +50,9 @@ #include <unistd.h> #include <QtMultimedia/private/qtmultimediaglobal_p.h> -#if QT_CONFIG(gstreamer_imxcommon) #include "private/qgstvideobuffer_p.h" -#include <gst/allocators/imx/phys_mem_meta.h> +#if GST_CHECK_VERSION(1,14,0) +#include <gst/allocators/gstphysmemory.h> #endif //#define QT_VIVANTE_VIDEO_DEBUG @@ -107,7 +107,7 @@ void QSGVivanteVideoMaterial::updateBlending() { void QSGVivanteVideoMaterial::setCurrentFrame(const QVideoFrame &frame, QSGVideoNode::FrameFlags flags) { QMutexLocker lock(&mFrameMutex); - mNextFrame = frame; + mCurrentFrame = frame; mMappable = mMapError == GL_NO_ERROR && !flags.testFlag(QSGVideoNode::FrameFiltered); #ifdef QT_VIVANTE_VIDEO_DEBUG @@ -124,12 +124,8 @@ void QSGVivanteVideoMaterial::bind() } QMutexLocker lock(&mFrameMutex); - if (mNextFrame.isValid()) { - mCurrentFrame.unmap(); - - mCurrentFrame = mNextFrame; - mCurrentTexture = vivanteMapping(mNextFrame); - } + if (mCurrentFrame.isValid()) + mCurrentTexture = vivanteMapping(mCurrentFrame); else glBindTexture(GL_TEXTURE_2D, mCurrentTexture); } @@ -229,11 +225,12 @@ GLuint QSGVivanteVideoMaterial::vivanteMapping(QVideoFrame vF) #endif GLuint physical = ~0U; -#if QT_CONFIG(gstreamer_imxcommon) +#if GST_CHECK_VERSION(1,14,0) auto buffer = reinterpret_cast<QGstVideoBuffer *>(vF.buffer()); - GstImxPhysMemMeta *meta = GST_IMX_PHYS_MEM_META_GET(buffer->buffer()); - if (meta && meta->phys_addr) - physical = meta->phys_addr; + auto mem = gst_buffer_peek_memory(buffer->buffer(), 0); + auto phys_addr = gst_is_phys_memory(mem) ? gst_phys_memory_get_phys_addr(mem) : 0; + if (phys_addr) + physical = phys_addr; #endif glBindTexture(GL_TEXTURE_2D, tmpTexId); glTexDirectVIVMap_LOCAL(GL_TEXTURE_2D, diff --git a/src/plugins/videonode/imx6/qsgvivantevideomaterial.h b/src/plugins/videonode/imx6/qsgvivantevideomaterial.h index adbd960a4..db59e8fc7 100644 --- a/src/plugins/videonode/imx6/qsgvivantevideomaterial.h +++ b/src/plugins/videonode/imx6/qsgvivantevideomaterial.h @@ -78,7 +78,7 @@ private: QVideoFrame::PixelFormat mFormat; QMap<const uchar*, GLuint> mBitsToTextureMap; - QVideoFrame mCurrentFrame, mNextFrame; + QVideoFrame mCurrentFrame; GLuint mCurrentTexture; bool mMappable; GLenum mMapError = GL_NO_ERROR; diff --git a/src/plugins/videonode/videonode.pro b/src/plugins/videonode/videonode.pro index ab7295406..889862c36 100644 --- a/src/plugins/videonode/videonode.pro +++ b/src/plugins/videonode/videonode.pro @@ -1,7 +1,7 @@ TEMPLATE = subdirs QT_FOR_CONFIG += gui-private multimedia-private -qtConfig(gpu_vivante) { +qtConfig(gpu_vivante):qtConfig(gstreamer) { SUBDIRS += imx6 } diff --git a/src/plugins/windowsaudio/qwindowsaudiooutput.cpp b/src/plugins/windowsaudio/qwindowsaudiooutput.cpp index 1182647fc..6ccffc8b2 100644 --- a/src/plugins/windowsaudio/qwindowsaudiooutput.cpp +++ b/src/plugins/windowsaudio/qwindowsaudiooutput.cpp @@ -630,7 +630,7 @@ qreal QWindowsAudioOutput::volume() const void QWindowsAudioOutput::reset() { - close(); + stop(); } OutputPrivate::OutputPrivate(QWindowsAudioOutput* audio) diff --git a/src/plugins/winrt/qwinrtcameracontrol.cpp b/src/plugins/winrt/qwinrtcameracontrol.cpp index ede3f6b04..98dd7c2f7 100644 --- a/src/plugins/winrt/qwinrtcameracontrol.cpp +++ b/src/plugins/winrt/qwinrtcameracontrol.cpp @@ -54,7 +54,7 @@ #include <functional> #include <mfapi.h> -#include <Mferror.h> +#include <mferror.h> #include <mfidl.h> #include <wrl.h> #include <windows.devices.enumeration.h> diff --git a/src/plugins/wmf/player/mfplayersession.cpp b/src/plugins/wmf/player/mfplayersession.cpp index 10ba25998..daf6f801f 100644 --- a/src/plugins/wmf/player/mfplayersession.cpp +++ b/src/plugins/wmf/player/mfplayersession.cpp @@ -47,6 +47,7 @@ #include <QtCore/qdebug.h> #include <QtCore/qfile.h> #include <QtCore/qbuffer.h> +#include <QtMultimedia/qmediametadata.h> #include "mfplayercontrol.h" #include "mfevrvideowindowcontrol.h" @@ -56,7 +57,7 @@ #include "mfplayersession.h" #include "mfplayerservice.h" #include "mfmetadatacontrol.h" -#include <Mferror.h> +#include <mferror.h> #include <nserror.h> #include "sourceresolver.h" #include "samplegrabber.h" @@ -277,10 +278,13 @@ MFPlayerSession::MediaType MFPlayerSession::getStreamType(IMFStreamDescriptor *s if (!stream) return Unknown; - IMFMediaTypeHandler *typeHandler = NULL; - if (SUCCEEDED(stream->GetMediaTypeHandler(&typeHandler))) { + struct SafeRelease { + IMFMediaTypeHandler *ptr = nullptr; + ~SafeRelease() { if (ptr) ptr->Release(); } + } typeHandler; + if (SUCCEEDED(stream->GetMediaTypeHandler(&typeHandler.ptr))) { GUID guidMajorType; - if (SUCCEEDED(typeHandler->GetMajorType(&guidMajorType))) { + if (SUCCEEDED(typeHandler.ptr->GetMajorType(&guidMajorType))) { if (guidMajorType == MFMediaType_Audio) return Audio; else if (guidMajorType == MFMediaType_Video) @@ -425,9 +429,15 @@ IMFTopologyNode* MFPlayerSession::addOutputNode(MediaType mediaType, IMFTopology if (mediaType == Audio) { activate = m_playerService->audioEndpointControl()->createActivate(); } else if (mediaType == Video) { + + QSize resolution = m_playerService->metaDataControl()->metaData(QMediaMetaData::Resolution).toSize(); + QRect cropRect = QRect(QPoint(), resolution); + if (m_playerService->videoRendererControl()) { + m_playerService->videoRendererControl()->setCropRect(cropRect); activate = m_playerService->videoRendererControl()->createActivate(); } else if (m_playerService->videoWindowControl()) { + m_playerService->videoWindowControl()->setCropRect(cropRect); activate = m_playerService->videoWindowControl()->createActivate(); } else { qWarning() << "no videoWindowControl or videoRendererControl, unable to add output node for video data"; diff --git a/src/plugins/wmf/player/mftvideo.cpp b/src/plugins/wmf/player/mftvideo.cpp index 879911d55..9dce654f2 100644 --- a/src/plugins/wmf/player/mftvideo.cpp +++ b/src/plugins/wmf/player/mftvideo.cpp @@ -40,7 +40,7 @@ #include "mftvideo.h" #include "mfvideoprobecontrol.h" #include <private/qmemoryvideobuffer_p.h> -#include <Mferror.h> +#include <mferror.h> #include <strmif.h> #include <uuids.h> #include <InitGuid.h> diff --git a/src/plugins/wmf/player/mfvideorenderercontrol.cpp b/src/plugins/wmf/player/mfvideorenderercontrol.cpp index 94d5f68be..38455090b 100644 --- a/src/plugins/wmf/player/mfvideorenderercontrol.cpp +++ b/src/plugins/wmf/player/mfvideorenderercontrol.cpp @@ -2239,10 +2239,12 @@ public: STDMETHODIMP DetachObject(); void setSurface(QAbstractVideoSurface *surface); + void setCropRect(QRect cropRect); private: EVRCustomPresenter *m_presenter; QAbstractVideoSurface *m_surface; + QRect m_cropRect; QMutex m_mutex; }; @@ -2305,6 +2307,14 @@ void MFVideoRendererControl::setSurface(QAbstractVideoSurface *surface) static_cast<VideoRendererActivate*>(m_currentActivate)->setSurface(m_surface); } +void MFVideoRendererControl::setCropRect(QRect cropRect) +{ + m_cropRect = cropRect; + + if (m_presenterActivate) + m_presenterActivate->setCropRect(cropRect); +} + void MFVideoRendererControl::customEvent(QEvent *event) { if (m_presenterActivate) @@ -2365,6 +2375,7 @@ IMFActivate* MFVideoRendererControl::createActivate() if (SUCCEEDED(MFCreateVideoRendererActivate(::GetShellWindow(), &m_currentActivate))) { m_presenterActivate = new EVRCustomPresenterActivate; m_currentActivate->SetUnknown(MF_ACTIVATE_CUSTOM_VIDEO_PRESENTER_ACTIVATE, m_presenterActivate); + m_presenterActivate->setCropRect(m_cropRect); } else { m_currentActivate = new VideoRendererActivate(this); } @@ -2388,6 +2399,7 @@ HRESULT EVRCustomPresenterActivate::ActivateObject(REFIID riid, void **ppv) QMutexLocker locker(&m_mutex); if (!m_presenter) { m_presenter = new EVRCustomPresenter; + m_presenter->setCropRect(m_cropRect); if (m_surface) m_presenter->setSurface(m_surface); } @@ -2423,5 +2435,17 @@ void EVRCustomPresenterActivate::setSurface(QAbstractVideoSurface *surface) m_presenter->setSurface(surface); } +void EVRCustomPresenterActivate::setCropRect(QRect cropRect) +{ + QMutexLocker locker(&m_mutex); + if (m_cropRect == cropRect) + return; + + m_cropRect = cropRect; + + if (m_presenter) + m_presenter->setCropRect(cropRect); +} + #include "moc_mfvideorenderercontrol.cpp" #include "mfvideorenderercontrol.moc" diff --git a/src/plugins/wmf/player/mfvideorenderercontrol.h b/src/plugins/wmf/player/mfvideorenderercontrol.h index da9e97ba9..8eab19b40 100644 --- a/src/plugins/wmf/player/mfvideorenderercontrol.h +++ b/src/plugins/wmf/player/mfvideorenderercontrol.h @@ -43,6 +43,7 @@ #include "qvideorenderercontrol.h" #include <mfapi.h> #include <mfidl.h> +#include <qrect.h> QT_USE_NAMESPACE @@ -61,6 +62,8 @@ public: IMFActivate* createActivate(); void releaseActivate(); + void setCropRect(QRect cropRect); + protected: void customEvent(QEvent *event); @@ -74,8 +77,8 @@ private: QAbstractVideoSurface *m_surface; IMFActivate *m_currentActivate; IMFSampleGrabberSinkCallback *m_callback; - EVRCustomPresenterActivate *m_presenterActivate; + QRect m_cropRect; }; #endif diff --git a/src/plugins/wmf/sourceresolver.cpp b/src/plugins/wmf/sourceresolver.cpp index c6f4e8566..15ef6f0ab 100644 --- a/src/plugins/wmf/sourceresolver.cpp +++ b/src/plugins/wmf/sourceresolver.cpp @@ -39,7 +39,7 @@ #include "mfstream.h" #include "sourceresolver.h" -#include <Mferror.h> +#include <mferror.h> #include <nserror.h> #include <QtCore/qfile.h> #include <QtCore/qdebug.h> |