diff --git a/dist/changes-5.4.2 b/dist/changes-5.4.2
new file mode 100644
index 00000000..07e55302
--- /dev/null
+++ b/dist/changes-5.4.2
@@ -0,0 +1,64 @@
+Qt 5.4.2 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.4.0 and 5.4.1.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+ http://qt-project.org/doc/qt-5.4
+
+The Qt version 5.4 series is binary compatible with the 5.3.x series.
+Applications compiled for 5.3 will continue to run with 5.4.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+ http://bugreports.qt-project.org/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* Library *
+****************************************************************************
+
+QtMultimedia
+------------
+
+ - QCameraInfo::availableCameras() and QtMultimedia.availableCameras (QML)
+ now return an up-to-date list when a camera is added or removed from the
+ system.
+ - Fixed memory leak in QMediaPlaylist::load().
+
+****************************************************************************
+* Platform Specific Changes *
+****************************************************************************
+
+Android
+-------
+
+ - [QTBUG-37525] Fixed VideoOutput not working on some devices when used
+ with a Camera.
+ - [QTBUG-44383] Fixed setting a URL with special characters on a
+ MediaPlayer.
+
+iOS
+---
+
+ - [QTBUG-44790] Fixed crash when using QAudioOutput/QAudioInput on iOS 5.
+ - [QTBUG-45659] Using QCamera doesn't always prompt for microphone
+ permission anymore, it now only does so when using the CaptureVideo mode.
+
+Linux
+-----
+
+ - [QTBUG-42326] Fixed regression causing software devices to not be
+ included in QAudioDeviceInfo::availableDevices() when using the ALSA
+ backend.
+
+Windows
+-------
+
+ - [QTBUG-32746] QMediaPlayer doesn't resume playback anymore when seeking
+ an audio media while paused.
+ - [QTBUG-42648] Greatly improved QAudioDeviceInfo supported formats detection.
+ - [QTBUG-44305] QAudioDeviceInfo: Fixed memory leak in availableDevices().
diff --git a/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/ContentCamera.qml b/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/ContentCamera.qml
new file mode 100644
index 00000000..f78b9916
--- /dev/null
+++ b/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/ContentCamera.qml
@@ -0,0 +1,43 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.1
+import QtMultimedia 5.0
+
+VideoOutput {
+ source: camera
+
+ Camera {
+ id: camera
+ }
+}
diff --git a/examples/multimedia/video/qmlvideofx/qmlvideofx.qrc b/examples/multimedia/video/qmlvideofx/qmlvideofx.qrc
index e7978e39..f3ad2770 100644
--- a/examples/multimedia/video/qmlvideofx/qmlvideofx.qrc
+++ b/examples/multimedia/video/qmlvideofx/qmlvideofx.qrc
@@ -3,6 +3,7 @@
images/qt-logo.png
qml/qmlvideofx/Button.qml
qml/qmlvideofx/Content.qml
+ qml/qmlvideofx/ContentCamera.qml
qml/qmlvideofx/ContentImage.qml
qml/qmlvideofx/ContentVideo.qml
qml/qmlvideofx/Divider.qml
diff --git a/qtmultimedia.pro b/qtmultimedia.pro
index 21d72d09..1225ffb6 100644
--- a/qtmultimedia.pro
+++ b/qtmultimedia.pro
@@ -14,7 +14,7 @@ win32 {
qtCompileTest(avfoundation)
} else:qnx {
qtCompileTest(mmrenderer)
-} else {
+} else:!android {
contains(QT_CONFIG, alsa):qtCompileTest(alsa)
contains(QT_CONFIG, pulseaudio):qtCompileTest(pulseaudio)
diff --git a/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp b/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp
index fcc10458..af803302 100644
--- a/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp
+++ b/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp
@@ -151,23 +151,19 @@ void QAndroidCaptureSession::setState(QMediaRecorder::State state)
stop();
break;
case QMediaRecorder::RecordingState:
- if (!start())
- return;
+ start();
break;
case QMediaRecorder::PausedState:
// Not supported by Android API
qWarning("QMediaRecorder::PausedState is not supported on Android");
- return;
+ break;
}
-
- m_state = state;
- emit stateChanged(m_state);
}
-bool QAndroidCaptureSession::start()
+void QAndroidCaptureSession::start()
{
if (m_state == QMediaRecorder::RecordingState || m_status != QMediaRecorder::LoadedStatus)
- return false;
+ return;
setStatus(QMediaRecorder::StartingStatus);
@@ -225,13 +221,13 @@ bool QAndroidCaptureSession::start()
if (!m_mediaRecorder->prepare()) {
emit error(QMediaRecorder::FormatError, QLatin1String("Unable to prepare the media recorder."));
restartViewfinder();
- return false;
+ return;
}
if (!m_mediaRecorder->start()) {
emit error(QMediaRecorder::FormatError, QLatin1String("Unable to start the media recorder."));
restartViewfinder();
- return false;
+ return;
}
m_elapsedTime.start();
@@ -241,22 +237,21 @@ bool QAndroidCaptureSession::start()
if (m_cameraSession)
m_cameraSession->setReadyForCapture(false);
- return true;
+ m_state = QMediaRecorder::RecordingState;
+ emit stateChanged(m_state);
}
void QAndroidCaptureSession::stop(bool error)
{
- if (m_state == QMediaRecorder::StoppedState)
+ if (m_state == QMediaRecorder::StoppedState || m_mediaRecorder == 0)
return;
setStatus(QMediaRecorder::FinalizingStatus);
m_mediaRecorder->stop();
-
m_notifyTimer.stop();
updateDuration();
m_elapsedTime.invalidate();
-
m_mediaRecorder->release();
delete m_mediaRecorder;
m_mediaRecorder = 0;
@@ -279,6 +274,9 @@ void QAndroidCaptureSession::stop(bool error)
m_actualOutputLocation = m_usedOutputLocation;
emit actualLocationChanged(m_actualOutputLocation);
}
+
+ m_state = QMediaRecorder::StoppedState;
+ emit stateChanged(m_state);
}
void QAndroidCaptureSession::setStatus(QMediaRecorder::Status status)
@@ -541,8 +539,6 @@ void QAndroidCaptureSession::onError(int what, int extra)
Q_UNUSED(what)
Q_UNUSED(extra)
stop(true);
- m_state = QMediaRecorder::StoppedState;
- emit stateChanged(m_state);
emit error(QMediaRecorder::ResourceError, QLatin1String("Unknown error."));
}
diff --git a/src/plugins/android/src/mediacapture/qandroidcapturesession.h b/src/plugins/android/src/mediacapture/qandroidcapturesession.h
index 81e57042..90af39fd 100644
--- a/src/plugins/android/src/mediacapture/qandroidcapturesession.h
+++ b/src/plugins/android/src/mediacapture/qandroidcapturesession.h
@@ -130,7 +130,7 @@ private:
CaptureProfile getProfile(int id);
- bool start();
+ void start();
void stop(bool error = false);
void setStatus(QMediaRecorder::Status status);
diff --git a/src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.h b/src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.h
index b4704160..8ad89bcc 100644
--- a/src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.h
+++ b/src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.h
@@ -69,6 +69,7 @@ private:
QString m_activeInput;
bool m_dirty;
+ QString m_defaultDevice;
QStringList m_devices;
QMap m_deviceDescriptions;
};
diff --git a/src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.mm b/src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.mm
index ebfce1ed..d0d0c820 100644
--- a/src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.mm
+++ b/src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.mm
@@ -52,8 +52,11 @@ AVFAudioInputSelectorControl::AVFAudioInputSelectorControl(AVFCameraService *ser
QString::fromUtf8([[device localizedName] UTF8String]));
}
- if (m_devices.size() > 0)
- m_activeInput = m_devices.first();
+ AVCaptureDevice *defaultDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
+ if (defaultDevice) {
+ m_defaultDevice = QString::fromUtf8([defaultDevice.uniqueID UTF8String]);
+ m_activeInput = m_defaultDevice;
+ }
}
AVFAudioInputSelectorControl::~AVFAudioInputSelectorControl()
@@ -72,7 +75,7 @@ QString AVFAudioInputSelectorControl::inputDescription(const QString &name) cons
QString AVFAudioInputSelectorControl::defaultInput() const
{
- return m_devices.size() > 0 ? m_devices.first() : QString();
+ return m_defaultDevice;
}
QString AVFAudioInputSelectorControl::activeInput() const
diff --git a/src/plugins/avfoundation/camera/avfcamerasession.h b/src/plugins/avfoundation/camera/avfcamerasession.h
index 4772351e..7b25a99c 100644
--- a/src/plugins/avfoundation/camera/avfcamerasession.h
+++ b/src/plugins/avfoundation/camera/avfcamerasession.h
@@ -99,7 +99,7 @@ Q_SIGNALS:
private:
static void updateCameraDevices();
- void attachInputDevices();
+ void attachVideoInputDevice();
void applyImageEncoderSettings();
void applyViewfinderSettings();
@@ -114,7 +114,6 @@ private:
AVCaptureSession *m_captureSession;
AVCaptureDeviceInput *m_videoInput;
- AVCaptureDeviceInput *m_audioInput;
AVFCameraSessionObserver *m_observer;
QSet m_videoProbes;
diff --git a/src/plugins/avfoundation/camera/avfcamerasession.mm b/src/plugins/avfoundation/camera/avfcamerasession.mm
index 48fe428d..6e4803f3 100644
--- a/src/plugins/avfoundation/camera/avfcamerasession.mm
+++ b/src/plugins/avfoundation/camera/avfcamerasession.mm
@@ -143,7 +143,6 @@ AVFCameraSession::AVFCameraSession(AVFCameraService *service, QObject *parent)
, m_state(QCamera::UnloadedState)
, m_active(false)
, m_videoInput(nil)
- , m_audioInput(nil)
, m_defaultCodec(0)
{
m_captureSession = [[AVCaptureSession alloc] init];
@@ -160,11 +159,6 @@ AVFCameraSession::~AVFCameraSession()
[m_videoInput release];
}
- if (m_audioInput) {
- [m_captureSession removeInput:m_audioInput];
- [m_audioInput release];
- }
-
[m_observer release];
[m_captureSession release];
}
@@ -283,10 +277,9 @@ void AVFCameraSession::setState(QCamera::State newState)
QCamera::State oldState = m_state;
m_state = newState;
- //attach audio and video inputs during Unloaded->Loaded transition
- if (oldState == QCamera::UnloadedState) {
- attachInputDevices();
- }
+ //attach video input during Unloaded->Loaded transition
+ if (oldState == QCamera::UnloadedState)
+ attachVideoInputDevice();
if (m_state == QCamera::ActiveState) {
Q_EMIT readyToConfigureConnections();
@@ -332,7 +325,7 @@ void AVFCameraSession::processSessionStopped()
}
}
-void AVFCameraSession::attachInputDevices()
+void AVFCameraSession::attachVideoInputDevice()
{
//Attach video input device:
if (m_service->videoDeviceControl()->isDirty()) {
@@ -360,29 +353,6 @@ void AVFCameraSession::attachInputDevices()
}
}
}
-
- //Attach audio input device:
- if (m_service->audioInputSelectorControl()->isDirty()) {
- if (m_audioInput) {
- [m_captureSession removeInput:m_audioInput];
- [m_audioInput release];
- m_audioInput = 0;
- }
-
- AVCaptureDevice *audioDevice = m_service->audioInputSelectorControl()->createCaptureDevice();
-
- NSError *error = nil;
- m_audioInput = [AVCaptureDeviceInput
- deviceInputWithDevice:audioDevice
- error:&error];
-
- if (!m_audioInput) {
- qWarning() << "Failed to create audio device input";
- } else {
- [m_audioInput retain];
- [m_captureSession addInput:m_audioInput];
- }
- }
}
void AVFCameraSession::applyImageEncoderSettings()
diff --git a/src/plugins/avfoundation/camera/avfmediarecordercontrol.h b/src/plugins/avfoundation/camera/avfmediarecordercontrol.h
index a303c0b5..4ea25ae2 100644
--- a/src/plugins/avfoundation/camera/avfmediarecordercontrol.h
+++ b/src/plugins/avfoundation/camera/avfmediarecordercontrol.h
@@ -46,6 +46,7 @@ QT_BEGIN_NAMESPACE
class AVFCameraSession;
class AVFCameraControl;
+class AVFAudioInputSelectorControl;
class AVFCameraService;
class AVFMediaRecorderControl : public QMediaRecorderControl
@@ -78,11 +79,12 @@ public Q_SLOTS:
void handleRecordingFailed(const QString &message);
private Q_SLOTS:
- void reconnectMovieOutput();
+ void setupSessionForCapture();
void updateStatus();
private:
AVFCameraControl *m_cameraControl;
+ AVFAudioInputSelectorControl *m_audioInputControl;
AVFCameraSession *m_session;
bool m_connected;
@@ -96,6 +98,7 @@ private:
bool m_muted;
qreal m_volume;
+ AVCaptureDeviceInput *m_audioInput;
AVCaptureMovieFileOutput *m_movieOutput;
AVFMediaRecorderDelegate *m_recorderDelagate;
AVFStorageLocation m_storageLocation;
diff --git a/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm b/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm
index 3bc24fcc..412dab76 100644
--- a/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm
+++ b/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm
@@ -36,6 +36,7 @@
#include "avfcamerasession.h"
#include "avfcameraservice.h"
#include "avfcameracontrol.h"
+#include "avfaudioinputselectorcontrol.h"
#include
#include
@@ -114,6 +115,7 @@ QT_USE_NAMESPACE
AVFMediaRecorderControl::AVFMediaRecorderControl(AVFCameraService *service, QObject *parent)
: QMediaRecorderControl(parent)
, m_cameraControl(service->cameraControl())
+ , m_audioInputControl(service->audioInputSelectorControl())
, m_session(service->session())
, m_connected(false)
, m_state(QMediaRecorder::StoppedState)
@@ -122,21 +124,29 @@ AVFMediaRecorderControl::AVFMediaRecorderControl(AVFCameraService *service, QObj
, m_recordingFinished(false)
, m_muted(false)
, m_volume(1.0)
+ , m_audioInput(nil)
{
m_movieOutput = [[AVCaptureMovieFileOutput alloc] init];
m_recorderDelagate = [[AVFMediaRecorderDelegate alloc] initWithRecorder:this];
connect(m_cameraControl, SIGNAL(stateChanged(QCamera::State)), SLOT(updateStatus()));
connect(m_cameraControl, SIGNAL(statusChanged(QCamera::Status)), SLOT(updateStatus()));
- connect(m_cameraControl, SIGNAL(captureModeChanged(QCamera::CaptureModes)), SLOT(reconnectMovieOutput()));
-
- reconnectMovieOutput();
+ connect(m_cameraControl, SIGNAL(captureModeChanged(QCamera::CaptureModes)), SLOT(setupSessionForCapture()));
+ connect(m_session, SIGNAL(readyToConfigureConnections()), SLOT(setupSessionForCapture()));
+ connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(setupSessionForCapture()));
}
AVFMediaRecorderControl::~AVFMediaRecorderControl()
{
- if (m_movieOutput)
+ if (m_movieOutput) {
[m_session->captureSession() removeOutput:m_movieOutput];
+ [m_movieOutput release];
+ }
+
+ if (m_audioInput) {
+ [m_session->captureSession() removeInput:m_audioInput];
+ [m_audioInput release];
+ }
[m_recorderDelagate release];
}
@@ -307,13 +317,39 @@ void AVFMediaRecorderControl::handleRecordingFailed(const QString &message)
Q_EMIT error(QMediaRecorder::ResourceError, message);
}
-void AVFMediaRecorderControl::reconnectMovieOutput()
+void AVFMediaRecorderControl::setupSessionForCapture()
{
//adding movie output causes high CPU usage even when while recording is not active,
- //connect it only while video capture mode is enabled
+ //connect it only while video capture mode is enabled.
+ // Similarly, connect the Audio input only in that mode, since it's only necessary
+ // when recording anyway. Adding an Audio input will trigger the microphone permission
+ // request on iOS, but it shoudn't do so until we actually try to record.
AVCaptureSession *captureSession = m_session->captureSession();
- if (!m_connected && m_cameraControl->captureMode().testFlag(QCamera::CaptureVideo)) {
+ if (!m_connected
+ && m_cameraControl->captureMode().testFlag(QCamera::CaptureVideo)
+ && m_session->state() != QCamera::UnloadedState) {
+
+ // Add audio input
+ // Allow recording even if something wrong happens with the audio input initialization
+ AVCaptureDevice *audioDevice = m_audioInputControl->createCaptureDevice();
+ if (!audioDevice) {
+ qWarning("No audio input device available");
+ } else {
+ NSError *error = nil;
+ m_audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error];
+
+ if (!m_audioInput) {
+ qWarning() << "Failed to create audio device input";
+ } else if (![captureSession canAddInput:m_audioInput]) {
+ qWarning() << "Could not connect the audio input";
+ m_audioInput = 0;
+ } else {
+ [m_audioInput retain];
+ [captureSession addInput:m_audioInput];
+ }
+ }
+
if ([captureSession canAddOutput:m_movieOutput]) {
[captureSession addOutput:m_movieOutput];
m_connected = true;
@@ -321,8 +357,18 @@ void AVFMediaRecorderControl::reconnectMovieOutput()
Q_EMIT error(QMediaRecorder::ResourceError, tr("Could not connect the video recorder"));
qWarning() << "Could not connect the video recorder";
}
- } else if (m_connected && !m_cameraControl->captureMode().testFlag(QCamera::CaptureVideo)) {
+ } else if (m_connected
+ && (!m_cameraControl->captureMode().testFlag(QCamera::CaptureVideo)
+ || m_session->state() != QCamera::ActiveState)) {
+
[captureSession removeOutput:m_movieOutput];
+
+ if (m_audioInput) {
+ [captureSession removeInput:m_audioInput];
+ [m_audioInput release];
+ m_audioInput = nil;
+ }
+
m_connected = false;
}
diff --git a/src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp b/src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp
index 74afabaf..bcae8a20 100644
--- a/src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp
+++ b/src/plugins/winrt/qwinrtcameravideorenderercontrol.cpp
@@ -132,11 +132,14 @@ private:
ComPtr m_outputView;
};
+#define CAMERA_SAMPLE_QUEUE_SIZE 5
class QWinRTCameraVideoRendererControlPrivate
{
public:
QScopedPointer blitter;
- QVector> buffers;
+ ComPtr buffers[CAMERA_SAMPLE_QUEUE_SIZE];
+ QAtomicInteger writeIndex;
+ QAtomicInteger readIndex;
};
QWinRTCameraVideoRendererControl::QWinRTCameraVideoRendererControl(const QSize &size, QObject *parent)
@@ -153,13 +156,17 @@ bool QWinRTCameraVideoRendererControl::render(ID3D11Texture2D *target)
{
Q_D(QWinRTCameraVideoRendererControl);
- if (d->buffers.isEmpty()) {
+ const quint16 readIndex = d->readIndex;
+ if (readIndex == d->writeIndex) {
emit bufferRequested();
return false;
}
HRESULT hr;
- ComPtr buffer = d->buffers.takeFirst();
+ ComPtr buffer = d->buffers[readIndex];
+ Q_ASSERT(buffer);
+ d->buffers[readIndex].Reset();
+ d->readIndex = (readIndex + 1) % CAMERA_SAMPLE_QUEUE_SIZE;
ComPtr sourceTexture;
ComPtr dxgiBuffer;
@@ -186,11 +193,17 @@ void QWinRTCameraVideoRendererControl::queueBuffer(IMF2DBuffer *buffer)
{
Q_D(QWinRTCameraVideoRendererControl);
Q_ASSERT(buffer);
- d->buffers.append(buffer);
+ const quint16 writeIndex = (d->writeIndex + 1) % CAMERA_SAMPLE_QUEUE_SIZE;
+ if (d->readIndex == writeIndex) // Drop new sample if queue is full
+ return;
+ d->buffers[d->writeIndex] = buffer;
+ d->writeIndex = writeIndex;
}
void QWinRTCameraVideoRendererControl::discardBuffers()
{
Q_D(QWinRTCameraVideoRendererControl);
- d->buffers.clear();
+ d->writeIndex = d->readIndex = 0;
+ for (ComPtr &buffer : d->buffers)
+ buffer.Reset();
}
diff --git a/src/qtmultimediaquicktools/qsgvideonode_rgb.cpp b/src/qtmultimediaquicktools/qsgvideonode_rgb.cpp
index 659dc8bd..0f682d1e 100644
--- a/src/qtmultimediaquicktools/qsgvideonode_rgb.cpp
+++ b/src/qtmultimediaquicktools/qsgvideonode_rgb.cpp
@@ -68,14 +68,15 @@ QSGVideoNode *QSGVideoNodeFactory_RGB::createNode(const QVideoSurfaceFormat &for
class QSGVideoMaterialShader_RGB : public QSGMaterialShader
{
public:
- QSGVideoMaterialShader_RGB(QVideoFrame::PixelFormat pixelFormat)
+ QSGVideoMaterialShader_RGB()
: QSGMaterialShader(),
m_id_matrix(-1),
m_id_width(-1),
m_id_rgbTexture(-1),
- m_id_opacity(-1),
- m_pixelFormat(pixelFormat)
+ m_id_opacity(-1)
{
+ setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qtmultimediaquicktools/shaders/rgbvideo_padded.vert"));
+ setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qtmultimediaquicktools/shaders/rgbvideo.frag"));
}
void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
@@ -90,54 +91,6 @@ public:
}
protected:
-
- virtual const char *vertexShader() const {
- const char *shader =
- "uniform highp mat4 qt_Matrix; \n"
- "uniform highp float width; \n"
- "attribute highp vec4 qt_VertexPosition; \n"
- "attribute highp vec2 qt_VertexTexCoord; \n"
- "varying highp vec2 qt_TexCoord; \n"
- "void main() { \n"
- " qt_TexCoord = qt_VertexTexCoord * vec2(width, 1);\n"
- " gl_Position = qt_Matrix * qt_VertexPosition; \n"
- "}";
- return shader;
- }
-
- virtual const char *fragmentShader() const {
- static const char *shader =
- "uniform sampler2D rgbTexture;"
- "uniform lowp float opacity;"
- ""
- "varying highp vec2 qt_TexCoord;"
- ""
- "void main()"
- "{"
- " gl_FragColor = texture2D(rgbTexture, qt_TexCoord) * opacity;"
- "}";
-
- static const char *colorsSwapShader =
- "uniform sampler2D rgbTexture;"
- "uniform lowp float opacity;"
- ""
- "varying highp vec2 qt_TexCoord;"
- ""
- "void main()"
- "{"
- " gl_FragColor = vec4(texture2D(rgbTexture, qt_TexCoord).bgr, 1.0) * opacity;"
- "}";
-
-
- switch (m_pixelFormat) {
- case QVideoFrame::Format_RGB32:
- case QVideoFrame::Format_ARGB32:
- return colorsSwapShader;
- default:
- return shader;
- }
- }
-
virtual void initialize() {
m_id_matrix = program()->uniformLocation("qt_Matrix");
m_id_width = program()->uniformLocation("width");
@@ -149,7 +102,16 @@ protected:
int m_id_width;
int m_id_rgbTexture;
int m_id_opacity;
- QVideoFrame::PixelFormat m_pixelFormat;
+};
+
+class QSGVideoMaterialShader_RGB_swizzle : public QSGVideoMaterialShader_RGB
+{
+public:
+ QSGVideoMaterialShader_RGB_swizzle()
+ : QSGVideoMaterialShader_RGB()
+ {
+ setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qtmultimediaquicktools/shaders/rgbvideo_swizzle.frag"));
+ }
};
@@ -172,12 +134,13 @@ public:
}
virtual QSGMaterialType *type() const {
- static QSGMaterialType theType;
- return &theType;
+ static QSGMaterialType normalType, swizzleType;
+ return needsSwizzling() ? &swizzleType : &normalType;
}
virtual QSGMaterialShader *createShader() const {
- return new QSGVideoMaterialShader_RGB(m_format.pixelFormat());
+ return needsSwizzling() ? new QSGVideoMaterialShader_RGB_swizzle
+ : new QSGVideoMaterialShader_RGB;
}
virtual int compare(const QSGMaterial *other) const {
@@ -263,6 +226,12 @@ public:
GLuint m_textureId;
qreal m_opacity;
GLfloat m_width;
+
+private:
+ bool needsSwizzling() const {
+ return m_format.pixelFormat() == QVideoFrame::Format_RGB32
+ || m_format.pixelFormat() == QVideoFrame::Format_ARGB32;
+ }
};
diff --git a/src/qtmultimediaquicktools/qsgvideonode_texture.cpp b/src/qtmultimediaquicktools/qsgvideonode_texture.cpp
index 3dc845c8..fd8d8719 100644
--- a/src/qtmultimediaquicktools/qsgvideonode_texture.cpp
+++ b/src/qtmultimediaquicktools/qsgvideonode_texture.cpp
@@ -69,10 +69,11 @@ QSGVideoNode *QSGVideoNodeFactory_Texture::createNode(const QVideoSurfaceFormat
class QSGVideoMaterialShader_Texture : public QSGMaterialShader
{
public:
- QSGVideoMaterialShader_Texture(QVideoFrame::PixelFormat pixelFormat)
- : QSGMaterialShader(),
- m_pixelFormat(pixelFormat)
+ QSGVideoMaterialShader_Texture()
+ : QSGMaterialShader()
{
+ setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qtmultimediaquicktools/shaders/rgbvideo.vert"));
+ setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qtmultimediaquicktools/shaders/rgbvideo.frag"));
}
void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
@@ -87,56 +88,6 @@ public:
}
protected:
-
- virtual const char *vertexShader() const {
- const char *shader =
- "uniform highp mat4 qt_Matrix; \n"
- "attribute highp vec4 qt_VertexPosition; \n"
- "attribute highp vec2 qt_VertexTexCoord; \n"
- "varying highp vec2 qt_TexCoord; \n"
- "void main() { \n"
- " qt_TexCoord = qt_VertexTexCoord; \n"
- " gl_Position = qt_Matrix * qt_VertexPosition; \n"
- "}";
- return shader;
- }
-
- virtual const char *fragmentShader() const {
- static const char *shader =
- "uniform sampler2D rgbTexture;"
- "uniform lowp float opacity;"
- ""
- "varying highp vec2 qt_TexCoord;"
- ""
- "void main()"
- "{"
- " gl_FragColor = texture2D(rgbTexture, qt_TexCoord) * opacity;"
- "}";
-
- static const char *colorsSwapShader =
- "uniform sampler2D rgbTexture;"
- "uniform lowp float opacity;"
- ""
- "varying highp vec2 qt_TexCoord;"
- ""
- "void main()"
- "{"
- " gl_FragColor = vec4(texture2D(rgbTexture, qt_TexCoord).bgr, 1.0) * opacity;"
- "}";
-
- if (!QMediaOpenGLHelper::isANGLE()) {
- switch (m_pixelFormat) {
- case QVideoFrame::Format_RGB32:
- case QVideoFrame::Format_ARGB32:
- return colorsSwapShader;
- default:
- break;
- }
- }
-
- return shader;
- }
-
virtual void initialize() {
m_id_matrix = program()->uniformLocation("qt_Matrix");
m_id_Texture = program()->uniformLocation("rgbTexture");
@@ -146,7 +97,16 @@ protected:
int m_id_matrix;
int m_id_Texture;
int m_id_opacity;
- QVideoFrame::PixelFormat m_pixelFormat;
+};
+
+class QSGVideoMaterialShader_Texture_swizzle : public QSGVideoMaterialShader_Texture
+{
+public:
+ QSGVideoMaterialShader_Texture_swizzle()
+ : QSGVideoMaterialShader_Texture()
+ {
+ setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qtmultimediaquicktools/shaders/rgbvideo_swizzle.frag"));
+ }
};
@@ -167,12 +127,13 @@ public:
}
virtual QSGMaterialType *type() const {
- static QSGMaterialType theType;
- return &theType;
+ static QSGMaterialType normalType, swizzleType;
+ return needsSwizzling() ? &swizzleType : &normalType;
}
virtual QSGMaterialShader *createShader() const {
- return new QSGVideoMaterialShader_Texture(m_format.pixelFormat());
+ return needsSwizzling() ? new QSGVideoMaterialShader_Texture_swizzle
+ : new QSGVideoMaterialShader_Texture;
}
virtual int compare(const QSGMaterial *other) const {
@@ -220,6 +181,13 @@ public:
QVideoSurfaceFormat m_format;
GLuint m_textureId;
qreal m_opacity;
+
+private:
+ bool needsSwizzling() const {
+ return !QMediaOpenGLHelper::isANGLE()
+ && (m_format.pixelFormat() == QVideoFrame::Format_RGB32
+ || m_format.pixelFormat() == QVideoFrame::Format_ARGB32);
+ }
};
diff --git a/src/qtmultimediaquicktools/qsgvideonode_yuv.cpp b/src/qtmultimediaquicktools/qsgvideonode_yuv.cpp
index fc40d659..9da1023b 100644
--- a/src/qtmultimediaquicktools/qsgvideonode_yuv.cpp
+++ b/src/qtmultimediaquicktools/qsgvideonode_yuv.cpp
@@ -62,10 +62,17 @@ QSGVideoNode *QSGVideoNodeFactory_YUV::createNode(const QVideoSurfaceFormat &for
}
-class QSGVideoMaterialShader_YUV420 : public QSGMaterialShader
+class QSGVideoMaterialShader_YUV_BiPlanar : public QSGMaterialShader
{
public:
- void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
+ QSGVideoMaterialShader_YUV_BiPlanar()
+ : QSGMaterialShader()
+ {
+ setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qtmultimediaquicktools/shaders/biplanaryuvvideo.vert"));
+ setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qtmultimediaquicktools/shaders/biplanaryuvvideo.frag"));
+ }
+
+ virtual void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
virtual char const *const *attributeNames() const {
static const char *names[] = {
@@ -77,133 +84,58 @@ public:
}
protected:
-
- virtual const char *vertexShader() const {
- const char *shader =
- "uniform highp mat4 qt_Matrix; \n"
- "uniform highp float yWidth; \n"
- "uniform highp float uvWidth; \n"
- "attribute highp vec4 qt_VertexPosition; \n"
- "attribute highp vec2 qt_VertexTexCoord; \n"
- "varying highp vec2 yTexCoord; \n"
- "varying highp vec2 uvTexCoord; \n"
- "void main() { \n"
- " yTexCoord = qt_VertexTexCoord * vec2(yWidth, 1);\n"
- " uvTexCoord = qt_VertexTexCoord * vec2(uvWidth, 1);\n"
- " gl_Position = qt_Matrix * qt_VertexPosition; \n"
- "}";
- return shader;
- }
-
- virtual const char *fragmentShader() const {
- static const char *shader =
- "uniform sampler2D yTexture;"
- "uniform sampler2D uTexture;"
- "uniform sampler2D vTexture;"
- "uniform mediump mat4 colorMatrix;"
- "uniform lowp float opacity;"
- ""
- "varying highp vec2 yTexCoord;"
- "varying highp vec2 uvTexCoord;"
- ""
- "void main()"
- "{"
- " mediump float Y = texture2D(yTexture, yTexCoord).r;"
- " mediump float U = texture2D(uTexture, uvTexCoord).r;"
- " mediump float V = texture2D(vTexture, uvTexCoord).r;"
- " mediump vec4 color = vec4(Y, U, V, 1.);"
- " gl_FragColor = colorMatrix * color * opacity;"
- "}";
- return shader;
- }
-
virtual void initialize() {
m_id_matrix = program()->uniformLocation("qt_Matrix");
- m_id_yWidth = program()->uniformLocation("yWidth");
- m_id_uvWidth = program()->uniformLocation("uvWidth");
- m_id_yTexture = program()->uniformLocation("yTexture");
- m_id_uTexture = program()->uniformLocation("uTexture");
- m_id_vTexture = program()->uniformLocation("vTexture");
+ m_id_plane1Width = program()->uniformLocation("plane1Width");
+ m_id_plane2Width = program()->uniformLocation("plane2Width");
+ m_id_plane1Texture = program()->uniformLocation("plane1Texture");
+ m_id_plane2Texture = program()->uniformLocation("plane2Texture");
m_id_colorMatrix = program()->uniformLocation("colorMatrix");
m_id_opacity = program()->uniformLocation("opacity");
}
int m_id_matrix;
- int m_id_yWidth;
- int m_id_uvWidth;
- int m_id_yTexture;
- int m_id_uTexture;
- int m_id_vTexture;
+ int m_id_plane1Width;
+ int m_id_plane2Width;
+ int m_id_plane1Texture;
+ int m_id_plane2Texture;
int m_id_colorMatrix;
int m_id_opacity;
};
-class QSGVideoMaterialShader_NV_12_21 : public QSGVideoMaterialShader_YUV420
+
+class QSGVideoMaterialShader_YUV_BiPlanar_swizzle : public QSGVideoMaterialShader_YUV_BiPlanar
{
public:
- QSGVideoMaterialShader_NV_12_21(bool isNV21) : m_isNV21(isNV21) {
+ QSGVideoMaterialShader_YUV_BiPlanar_swizzle()
+ : QSGVideoMaterialShader_YUV_BiPlanar()
+ {
+ setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qtmultimediaquicktools/shaders/biplanaryuvvideo_swizzle.frag"));
+ }
+};
+
+
+class QSGVideoMaterialShader_YUV_TriPlanar : public QSGVideoMaterialShader_YUV_BiPlanar
+{
+public:
+ QSGVideoMaterialShader_YUV_TriPlanar()
+ : QSGVideoMaterialShader_YUV_BiPlanar()
+ {
+ setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qtmultimediaquicktools/shaders/triplanaryuvvideo.vert"));
+ setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qtmultimediaquicktools/shaders/triplanaryuvvideo.frag"));
}
virtual void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
protected:
-
- virtual const char *vertexShader() const {
- const char *shader =
- "uniform highp mat4 qt_Matrix; \n"
- "uniform highp float yWidth; \n"
- "attribute highp vec4 qt_VertexPosition; \n"
- "attribute highp vec2 qt_VertexTexCoord; \n"
- "varying highp vec2 yTexCoord; \n"
- "void main() { \n"
- " yTexCoord = qt_VertexTexCoord * vec2(yWidth, 1);\n"
- " gl_Position = qt_Matrix * qt_VertexPosition; \n"
- "}";
- return shader;
- }
-
- virtual const char *fragmentShader() const {
- static const char *shaderNV12 =
- "uniform sampler2D yTexture; \n"
- "uniform sampler2D uvTexture; \n"
- "uniform mediump mat4 colorMatrix; \n"
- "uniform lowp float opacity; \n"
- "varying highp vec2 yTexCoord; \n"
- "void main() \n"
- "{ \n"
- " mediump float Y = texture2D(yTexture, yTexCoord).r; \n"
- " mediump vec2 UV = texture2D(uvTexture, yTexCoord).ra; \n"
- " mediump vec4 color = vec4(Y, UV.x, UV.y, 1.); \n"
- " gl_FragColor = colorMatrix * color * opacity; \n"
- "}";
-
- static const char *shaderNV21 =
- "uniform sampler2D yTexture; \n"
- "uniform sampler2D uvTexture; \n"
- "uniform mediump mat4 colorMatrix; \n"
- "uniform lowp float opacity; \n"
- "varying highp vec2 yTexCoord; \n"
- "void main() \n"
- "{ \n"
- " mediump float Y = texture2D(yTexture, yTexCoord).r; \n"
- " mediump vec2 UV = texture2D(uvTexture, yTexCoord).ar; \n"
- " mediump vec4 color = vec4(Y, UV.x, UV.y, 1.); \n"
- " gl_FragColor = colorMatrix * color * opacity; \n"
- "}";
- return m_isNV21 ? shaderNV21 : shaderNV12;
- }
-
virtual void initialize() {
- m_id_yTexture = program()->uniformLocation("yTexture");
- m_id_uTexture = program()->uniformLocation("uvTexture");
- m_id_matrix = program()->uniformLocation("qt_Matrix");
- m_id_yWidth = program()->uniformLocation("yWidth");
- m_id_colorMatrix = program()->uniformLocation("colorMatrix");
- m_id_opacity = program()->uniformLocation("opacity");
+ m_id_plane3Width = program()->uniformLocation("plane3Width");
+ m_id_plane3Texture = program()->uniformLocation("plane3Texture");
+ QSGVideoMaterialShader_YUV_BiPlanar::initialize();
}
-private:
- bool m_isNV21;
+ int m_id_plane3Width;
+ int m_id_plane3Texture;
};
@@ -213,22 +145,28 @@ public:
QSGVideoMaterial_YUV(const QVideoSurfaceFormat &format);
~QSGVideoMaterial_YUV();
- bool isNV12_21() const {
- const QVideoFrame::PixelFormat pf = m_format.pixelFormat();
- return pf == QVideoFrame::Format_NV12 || pf == QVideoFrame::Format_NV21;
- }
-
virtual QSGMaterialType *type() const {
- static QSGMaterialType theType;
- return &theType;
+ static QSGMaterialType biPlanarType, biPlanarSwizzleType, triPlanarType;
+
+ switch (m_format.pixelFormat()) {
+ case QVideoFrame::Format_NV12:
+ return &biPlanarType;
+ case QVideoFrame::Format_NV21:
+ return &biPlanarSwizzleType;
+ default: // Currently: YUV420P and YV12
+ return &triPlanarType;
+ }
}
virtual QSGMaterialShader *createShader() const {
- const QVideoFrame::PixelFormat pf = m_format.pixelFormat();
- if (isNV12_21())
- return new QSGVideoMaterialShader_NV_12_21(pf == QVideoFrame::Format_NV21);
-
- return new QSGVideoMaterialShader_YUV420;
+ switch (m_format.pixelFormat()) {
+ case QVideoFrame::Format_NV12:
+ return new QSGVideoMaterialShader_YUV_BiPlanar;
+ case QVideoFrame::Format_NV21:
+ return new QSGVideoMaterialShader_YUV_BiPlanar_swizzle;
+ default: // Currently: YUV420P and YV12
+ return new QSGVideoMaterialShader_YUV_TriPlanar;
+ }
}
virtual int compare(const QSGMaterial *other) const {
@@ -236,13 +174,10 @@ public:
int d = m_textureIds[0] - m->m_textureIds[0];
if (d)
return d;
-
- d = m_textureIds[1] - m->m_textureIds[1];
-
- if (m_textureIds.size() == 2 || d != 0)
+ else if ((d = m_textureIds[1] - m->m_textureIds[1]) != 0)
return d;
-
- return m_textureIds[2] - m->m_textureIds[2];
+ else
+ return m_textureIds[2] - m->m_textureIds[2];
}
void updateBlending() {
@@ -259,12 +194,12 @@ public:
QVideoSurfaceFormat m_format;
QSize m_textureSize;
+ int m_planeCount;
- QVector m_textureIds;
+ GLuint m_textureIds[3];
+ GLfloat m_planeWidth[3];
qreal m_opacity;
- GLfloat m_yWidth;
- GLfloat m_uvWidth;
QMatrix4x4 m_colorMatrix;
QVideoFrame m_frame;
@@ -273,11 +208,23 @@ public:
QSGVideoMaterial_YUV::QSGVideoMaterial_YUV(const QVideoSurfaceFormat &format) :
m_format(format),
- m_opacity(1.0),
- m_yWidth(1.0),
- m_uvWidth(1.0)
+ m_opacity(1.0)
{
- m_textureIds.resize(isNV12_21() ? 2 : 3);
+ memset(m_textureIds, 0, sizeof(m_textureIds));
+
+ switch (format.pixelFormat()) {
+ case QVideoFrame::Format_NV12:
+ case QVideoFrame::Format_NV21:
+ m_planeCount = 2;
+ break;
+ case QVideoFrame::Format_YUV420P:
+ case QVideoFrame::Format_YV12:
+ m_planeCount = 3;
+ break;
+ default:
+ m_planeCount = 1;
+ break;
+ }
switch (format.yCbCrColorSpace()) {
case QVideoSurfaceFormat::YCbCr_JPEG:
@@ -310,7 +257,7 @@ QSGVideoMaterial_YUV::~QSGVideoMaterial_YUV()
{
if (!m_textureSize.isEmpty()) {
if (QOpenGLContext *current = QOpenGLContext::currentContext())
- current->functions()->glDeleteTextures(m_textureIds.size(), &m_textureIds[0]);
+ current->functions()->glDeleteTextures(m_planeCount, m_textureIds);
else
qWarning() << "QSGVideoMaterial_YUV: Cannot obtain GL context, unable to delete textures";
}
@@ -328,8 +275,8 @@ void QSGVideoMaterial_YUV::bind()
// Frame has changed size, recreate textures...
if (m_textureSize != m_frame.size()) {
if (!m_textureSize.isEmpty())
- functions->glDeleteTextures(m_textureIds.size(), &m_textureIds[0]);
- functions->glGenTextures(m_textureIds.size(), &m_textureIds[0]);
+ functions->glDeleteTextures(m_planeCount, m_textureIds);
+ functions->glGenTextures(m_planeCount, m_textureIds);
m_textureSize = m_frame.size();
}
@@ -337,24 +284,25 @@ void QSGVideoMaterial_YUV::bind()
functions->glGetIntegerv(GL_UNPACK_ALIGNMENT, &previousAlignment);
functions->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- if (isNV12_21()) {
+ if (m_format.pixelFormat() == QVideoFrame::Format_NV12
+ || m_format.pixelFormat() == QVideoFrame::Format_NV21) {
const int y = 0;
const int uv = 1;
- m_yWidth = qreal(fw) / m_frame.bytesPerLine(y);
- m_uvWidth = m_yWidth;
+ m_planeWidth[0] = m_planeWidth[1] = qreal(fw) / m_frame.bytesPerLine(y);
functions->glActiveTexture(GL_TEXTURE1);
bindTexture(m_textureIds[1], m_frame.bytesPerLine(uv) / 2, fh / 2, m_frame.bits(uv), GL_LUMINANCE_ALPHA);
functions->glActiveTexture(GL_TEXTURE0); // Finish with 0 as default texture unit
bindTexture(m_textureIds[0], m_frame.bytesPerLine(y), fh, m_frame.bits(y), GL_LUMINANCE);
- } else {
+
+ } else { // YUV420P || YV12
const int y = 0;
const int u = m_frame.pixelFormat() == QVideoFrame::Format_YUV420P ? 1 : 2;
const int v = m_frame.pixelFormat() == QVideoFrame::Format_YUV420P ? 2 : 1;
- m_yWidth = qreal(fw) / m_frame.bytesPerLine(y);
- m_uvWidth = qreal(fw) / (2 * m_frame.bytesPerLine(u));
+ m_planeWidth[0] = qreal(fw) / m_frame.bytesPerLine(y);
+ m_planeWidth[1] = m_planeWidth[2] = qreal(fw) / (2 * m_frame.bytesPerLine(u));
functions->glActiveTexture(GL_TEXTURE1);
bindTexture(m_textureIds[1], m_frame.bytesPerLine(u), fh / 2, m_frame.bits(u), GL_LUMINANCE);
@@ -370,14 +318,10 @@ void QSGVideoMaterial_YUV::bind()
m_frame = QVideoFrame();
} else {
- functions->glActiveTexture(GL_TEXTURE1);
- functions->glBindTexture(GL_TEXTURE_2D, m_textureIds[1]);
- if (!isNV12_21()) {
- functions->glActiveTexture(GL_TEXTURE2);
- functions->glBindTexture(GL_TEXTURE_2D, m_textureIds[2]);
+ for (int i = 0; i < m_planeCount; ++i) {
+ functions->glActiveTexture(GL_TEXTURE0 + i);
+ functions->glBindTexture(GL_TEXTURE_2D, m_textureIds[i]);
}
- functions->glActiveTexture(GL_TEXTURE0); // Finish with 0 as default texture unit
- functions->glBindTexture(GL_TEXTURE_2D, m_textureIds[0]);
}
}
@@ -411,22 +355,21 @@ void QSGVideoNode_YUV::setCurrentFrame(const QVideoFrame &frame, FrameFlags)
markDirty(DirtyMaterial);
}
-void QSGVideoMaterialShader_YUV420::updateState(const RenderState &state,
- QSGMaterial *newMaterial,
- QSGMaterial *oldMaterial)
+void QSGVideoMaterialShader_YUV_BiPlanar::updateState(const RenderState &state,
+ QSGMaterial *newMaterial,
+ QSGMaterial *oldMaterial)
{
Q_UNUSED(oldMaterial);
QSGVideoMaterial_YUV *mat = static_cast(newMaterial);
- program()->setUniformValue(m_id_yTexture, 0);
- program()->setUniformValue(m_id_uTexture, 1);
- program()->setUniformValue(m_id_vTexture, 2);
+ program()->setUniformValue(m_id_plane1Texture, 0);
+ program()->setUniformValue(m_id_plane2Texture, 1);
mat->bind();
program()->setUniformValue(m_id_colorMatrix, mat->m_colorMatrix);
- program()->setUniformValue(m_id_yWidth, mat->m_yWidth);
- program()->setUniformValue(m_id_uvWidth, mat->m_uvWidth);
+ program()->setUniformValue(m_id_plane1Width, mat->m_planeWidth[0]);
+ program()->setUniformValue(m_id_plane2Width, mat->m_planeWidth[1]);
if (state.isOpacityDirty()) {
mat->m_opacity = state.opacity();
program()->setUniformValue(m_id_opacity, GLfloat(mat->m_opacity));
@@ -435,26 +378,15 @@ void QSGVideoMaterialShader_YUV420::updateState(const RenderState &state,
program()->setUniformValue(m_id_matrix, state.combinedMatrix());
}
-void QSGVideoMaterialShader_NV_12_21::updateState(const RenderState &state,
- QSGMaterial *newMaterial,
- QSGMaterial *oldMaterial)
+void QSGVideoMaterialShader_YUV_TriPlanar::updateState(const RenderState &state,
+ QSGMaterial *newMaterial,
+ QSGMaterial *oldMaterial)
{
- Q_UNUSED(oldMaterial);
+ QSGVideoMaterialShader_YUV_BiPlanar::updateState(state, newMaterial, oldMaterial);
QSGVideoMaterial_YUV *mat = static_cast(newMaterial);
- program()->setUniformValue(m_id_yTexture, 0);
- program()->setUniformValue(m_id_uTexture, 1);
-
- mat->bind();
-
- program()->setUniformValue(m_id_colorMatrix, mat->m_colorMatrix);
- program()->setUniformValue(m_id_yWidth, mat->m_yWidth);
- if (state.isOpacityDirty()) {
- mat->m_opacity = state.opacity();
- program()->setUniformValue(m_id_opacity, GLfloat(mat->m_opacity));
- }
- if (state.isMatrixDirty())
- program()->setUniformValue(m_id_matrix, state.combinedMatrix());
+ program()->setUniformValue(m_id_plane3Texture, 2);
+ program()->setUniformValue(m_id_plane3Width, mat->m_planeWidth[2]);
}
QT_END_NAMESPACE
diff --git a/src/qtmultimediaquicktools/qtmultimediaquicktools.pro b/src/qtmultimediaquicktools/qtmultimediaquicktools.pro
index 917a5ac2..61c5f7b2 100644
--- a/src/qtmultimediaquicktools/qtmultimediaquicktools.pro
+++ b/src/qtmultimediaquicktools/qtmultimediaquicktools.pro
@@ -32,3 +32,17 @@ HEADERS += \
qsgvideonode_yuv.h \
qsgvideonode_rgb.h \
qsgvideonode_texture.h
+
+RESOURCES += \
+ qtmultimediaquicktools.qrc
+
+OTHER_FILES += \
+ shaders/rgbvideo.vert \
+ shaders/rgbvideo_padded.vert \
+ shaders/rgbvideo.frag \
+ shaders/rgbvideo_swizzle.frag \
+ shaders/biplanaryuvvideo.vert \
+ shaders/biplanaryuvvideo.frag \
+ shaders/biplanaryuvvideo_swizzle.frag \
+ shaders/triplanaryuvvideo.vert \
+ shaders/triplanaryuvvideo.frag
diff --git a/src/qtmultimediaquicktools/qtmultimediaquicktools.qrc b/src/qtmultimediaquicktools/qtmultimediaquicktools.qrc
new file mode 100644
index 00000000..b0a7f078
--- /dev/null
+++ b/src/qtmultimediaquicktools/qtmultimediaquicktools.qrc
@@ -0,0 +1,13 @@
+
+
+ shaders/rgbvideo.vert
+ shaders/rgbvideo.frag
+ shaders/rgbvideo_swizzle.frag
+ shaders/rgbvideo_padded.vert
+ shaders/biplanaryuvvideo.frag
+ shaders/biplanaryuvvideo.vert
+ shaders/biplanaryuvvideo_swizzle.frag
+ shaders/triplanaryuvvideo.frag
+ shaders/triplanaryuvvideo.vert
+
+
diff --git a/src/qtmultimediaquicktools/shaders/biplanaryuvvideo.frag b/src/qtmultimediaquicktools/shaders/biplanaryuvvideo.frag
new file mode 100644
index 00000000..2ede9108
--- /dev/null
+++ b/src/qtmultimediaquicktools/shaders/biplanaryuvvideo.frag
@@ -0,0 +1,14 @@
+uniform sampler2D plane1Texture;
+uniform sampler2D plane2Texture;
+uniform mediump mat4 colorMatrix;
+uniform lowp float opacity;
+varying highp vec2 plane1TexCoord;
+varying highp vec2 plane2TexCoord;
+
+void main()
+{
+ mediump float Y = texture2D(plane1Texture, plane1TexCoord).r;
+ mediump vec2 UV = texture2D(plane2Texture, plane2TexCoord).ra;
+ mediump vec4 color = vec4(Y, UV.x, UV.y, 1.);
+ gl_FragColor = colorMatrix * color * opacity;
+}
diff --git a/src/qtmultimediaquicktools/shaders/biplanaryuvvideo.vert b/src/qtmultimediaquicktools/shaders/biplanaryuvvideo.vert
new file mode 100644
index 00000000..d01b6907
--- /dev/null
+++ b/src/qtmultimediaquicktools/shaders/biplanaryuvvideo.vert
@@ -0,0 +1,13 @@
+uniform highp mat4 qt_Matrix;
+uniform highp float plane1Width;
+uniform highp float plane2Width;
+attribute highp vec4 qt_VertexPosition;
+attribute highp vec2 qt_VertexTexCoord;
+varying highp vec2 plane1TexCoord;
+varying highp vec2 plane2TexCoord;
+
+void main() {
+ plane1TexCoord = qt_VertexTexCoord * vec2(plane1Width, 1);
+ plane2TexCoord = qt_VertexTexCoord * vec2(plane2Width, 1);
+ gl_Position = qt_Matrix * qt_VertexPosition;
+}
diff --git a/src/qtmultimediaquicktools/shaders/biplanaryuvvideo_swizzle.frag b/src/qtmultimediaquicktools/shaders/biplanaryuvvideo_swizzle.frag
new file mode 100644
index 00000000..560a0d15
--- /dev/null
+++ b/src/qtmultimediaquicktools/shaders/biplanaryuvvideo_swizzle.frag
@@ -0,0 +1,14 @@
+uniform sampler2D plane1Texture;
+uniform sampler2D plane2Texture;
+uniform mediump mat4 colorMatrix;
+uniform lowp float opacity;
+varying highp vec2 plane1TexCoord;
+varying highp vec2 plane2TexCoord;
+
+void main()
+{
+ mediump float Y = texture2D(plane1Texture, plane1TexCoord).r;
+ mediump vec2 UV = texture2D(plane2Texture, plane2TexCoord).ar;
+ mediump vec4 color = vec4(Y, UV.x, UV.y, 1.);
+ gl_FragColor = colorMatrix * color * opacity;
+}
diff --git a/src/qtmultimediaquicktools/shaders/rgbvideo.frag b/src/qtmultimediaquicktools/shaders/rgbvideo.frag
new file mode 100644
index 00000000..673eb4bf
--- /dev/null
+++ b/src/qtmultimediaquicktools/shaders/rgbvideo.frag
@@ -0,0 +1,8 @@
+uniform sampler2D rgbTexture;
+uniform lowp float opacity;
+varying highp vec2 qt_TexCoord;
+
+void main()
+{
+ gl_FragColor = texture2D(rgbTexture, qt_TexCoord) * opacity;
+}
diff --git a/src/qtmultimediaquicktools/shaders/rgbvideo.vert b/src/qtmultimediaquicktools/shaders/rgbvideo.vert
new file mode 100644
index 00000000..19915f2f
--- /dev/null
+++ b/src/qtmultimediaquicktools/shaders/rgbvideo.vert
@@ -0,0 +1,9 @@
+uniform highp mat4 qt_Matrix;
+attribute highp vec4 qt_VertexPosition;
+attribute highp vec2 qt_VertexTexCoord;
+varying highp vec2 qt_TexCoord;
+
+void main() {
+ qt_TexCoord = qt_VertexTexCoord;
+ gl_Position = qt_Matrix * qt_VertexPosition;
+}
diff --git a/src/qtmultimediaquicktools/shaders/rgbvideo_padded.vert b/src/qtmultimediaquicktools/shaders/rgbvideo_padded.vert
new file mode 100644
index 00000000..5c7c41d1
--- /dev/null
+++ b/src/qtmultimediaquicktools/shaders/rgbvideo_padded.vert
@@ -0,0 +1,10 @@
+uniform highp mat4 qt_Matrix;
+uniform highp float width;
+attribute highp vec4 qt_VertexPosition;
+attribute highp vec2 qt_VertexTexCoord;
+varying highp vec2 qt_TexCoord;
+
+void main() {
+ qt_TexCoord = qt_VertexTexCoord * vec2(width, 1);
+ gl_Position = qt_Matrix * qt_VertexPosition;
+}
diff --git a/src/qtmultimediaquicktools/shaders/rgbvideo_swizzle.frag b/src/qtmultimediaquicktools/shaders/rgbvideo_swizzle.frag
new file mode 100644
index 00000000..f01dc86a
--- /dev/null
+++ b/src/qtmultimediaquicktools/shaders/rgbvideo_swizzle.frag
@@ -0,0 +1,8 @@
+uniform sampler2D rgbTexture;
+uniform lowp float opacity;
+varying highp vec2 qt_TexCoord;
+
+void main()
+{
+ gl_FragColor = vec4(texture2D(rgbTexture, qt_TexCoord).bgr, 1.0) * opacity;
+}
diff --git a/src/qtmultimediaquicktools/shaders/triplanaryuvvideo.frag b/src/qtmultimediaquicktools/shaders/triplanaryuvvideo.frag
new file mode 100644
index 00000000..dad8bb5a
--- /dev/null
+++ b/src/qtmultimediaquicktools/shaders/triplanaryuvvideo.frag
@@ -0,0 +1,18 @@
+uniform sampler2D plane1Texture;
+uniform sampler2D plane2Texture;
+uniform sampler2D plane3Texture;
+uniform mediump mat4 colorMatrix;
+uniform lowp float opacity;
+
+varying highp vec2 plane1TexCoord;
+varying highp vec2 plane2TexCoord;
+varying highp vec2 plane3TexCoord;
+
+void main()
+{
+ mediump float Y = texture2D(plane1Texture, plane1TexCoord).r;
+ mediump float U = texture2D(plane2Texture, plane2TexCoord).r;
+ mediump float V = texture2D(plane3Texture, plane3TexCoord).r;
+ mediump vec4 color = vec4(Y, U, V, 1.);
+ gl_FragColor = colorMatrix * color * opacity;
+}
diff --git a/src/qtmultimediaquicktools/shaders/triplanaryuvvideo.vert b/src/qtmultimediaquicktools/shaders/triplanaryuvvideo.vert
new file mode 100644
index 00000000..d76b3f35
--- /dev/null
+++ b/src/qtmultimediaquicktools/shaders/triplanaryuvvideo.vert
@@ -0,0 +1,16 @@
+uniform highp mat4 qt_Matrix;
+uniform highp float plane1Width;
+uniform highp float plane2Width;
+uniform highp float plane3Width;
+attribute highp vec4 qt_VertexPosition;
+attribute highp vec2 qt_VertexTexCoord;
+varying highp vec2 plane1TexCoord;
+varying highp vec2 plane2TexCoord;
+varying highp vec2 plane3TexCoord;
+
+void main() {
+ plane1TexCoord = qt_VertexTexCoord * vec2(plane1Width, 1);
+ plane2TexCoord = qt_VertexTexCoord * vec2(plane2Width, 1);
+ plane3TexCoord = qt_VertexTexCoord * vec2(plane3Width, 1);
+ gl_Position = qt_Matrix * qt_VertexPosition;
+}