diff options
Diffstat (limited to 'src')
28 files changed, 1179 insertions, 233 deletions
diff --git a/src/plugins/android/jar/jar.pri b/src/plugins/android/jar/jar.pri index d31839c61..713123baf 100644 --- a/src/plugins/android/jar/jar.pri +++ b/src/plugins/android/jar/jar.pri @@ -10,7 +10,8 @@ JAVASOURCES += $$PWD/src/org/qtproject/qt5/android/multimedia/QtAndroidMediaPlay $$PWD/src/org/qtproject/qt5/android/multimedia/QtSurfaceTextureListener.java \ $$PWD/src/org/qtproject/qt5/android/multimedia/QtSurfaceTextureHolder.java \ $$PWD/src/org/qtproject/qt5/android/multimedia/QtMultimediaUtils.java \ - $$PWD/src/org/qtproject/qt5/android/multimedia/QtMediaRecorderListener.java + $$PWD/src/org/qtproject/qt5/android/multimedia/QtMediaRecorderListener.java \ + $$PWD/src/org/qtproject/qt5/android/multimedia/QtSurfaceHolderCallback.java # install target.path = $$[QT_INSTALL_PREFIX]/jar diff --git a/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtCameraListener.java b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtCameraListener.java index 974489c19..8724eeba4 100644 --- a/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtCameraListener.java +++ b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtCameraListener.java @@ -54,6 +54,8 @@ public class QtCameraListener implements Camera.ShutterCallback, private byte[][] m_previewBuffers = null; private byte[] m_lastPreviewBuffer = null; private Camera.Size m_previewSize = null; + private int m_previewFormat = ImageFormat.NV21; // Default preview format on all devices + private int m_previewBytesPerLine = -1; private QtCameraListener(int id) { @@ -86,6 +88,16 @@ public class QtCameraListener implements Camera.ShutterCallback, return m_previewSize.height; } + public int previewFormat() + { + return m_previewFormat; + } + + public int previewBytesPerLine() + { + return m_previewBytesPerLine; + } + public void setupPreviewCallback(Camera camera) { // Clear previous callback (also clears added buffers) @@ -94,8 +106,37 @@ public class QtCameraListener implements Camera.ShutterCallback, final Camera.Parameters params = camera.getParameters(); m_previewSize = params.getPreviewSize(); - double bytesPerPixel = ImageFormat.getBitsPerPixel(params.getPreviewFormat()) / 8.0; - int bufferSizeNeeded = (int) Math.ceil(bytesPerPixel * m_previewSize.width * m_previewSize.height); + m_previewFormat = params.getPreviewFormat(); + + int bufferSizeNeeded = 0; + if (m_previewFormat == ImageFormat.YV12) { + // For YV12, bytes per line must be a multiple of 16 + final int yStride = (int) Math.ceil(m_previewSize.width / 16.0) * 16; + final int uvStride = (int) Math.ceil((yStride / 2) / 16.0) * 16; + final int ySize = yStride * m_previewSize.height; + final int uvSize = uvStride * m_previewSize.height / 2; + bufferSizeNeeded = ySize + uvSize * 2; + + m_previewBytesPerLine = yStride; + + } else { + double bytesPerPixel = ImageFormat.getBitsPerPixel(m_previewFormat) / 8.0; + bufferSizeNeeded = (int) Math.ceil(bytesPerPixel * m_previewSize.width * m_previewSize.height); + + // bytes per line are calculated only for the first plane + switch (m_previewFormat) { + case ImageFormat.NV21: + m_previewBytesPerLine = m_previewSize.width; // 1 byte per sample and tightly packed + break; + case ImageFormat.RGB_565: + case ImageFormat.YUY2: + m_previewBytesPerLine = m_previewSize.width * 2; // 2 bytes per pixel + break; + default: + m_previewBytesPerLine = -1; + break; + } + } // We could keep the same buffers when they are already bigger than the required size // but the Android doc says the size must match, so in doubt just replace them. @@ -117,8 +158,12 @@ public class QtCameraListener implements Camera.ShutterCallback, m_lastPreviewBuffer = data; - if (data != null && m_notifyNewFrames) - notifyNewPreviewFrame(m_cameraId, data, m_previewSize.width, m_previewSize.height); + if (data != null && m_notifyNewFrames) { + notifyNewPreviewFrame(m_cameraId, data, + m_previewSize.width, m_previewSize.height, + m_previewFormat, + m_previewBytesPerLine); + } } @Override @@ -142,5 +187,6 @@ public class QtCameraListener implements Camera.ShutterCallback, private static native void notifyAutoFocusComplete(int id, boolean success); private static native void notifyPictureExposed(int id); private static native void notifyPictureCaptured(int id, byte[] data); - private static native void notifyNewPreviewFrame(int id, byte[] data, int width, int height); + private static native void notifyNewPreviewFrame(int id, byte[] data, int width, int height, + int pixelFormat, int bytesPerLine); } diff --git a/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtSurfaceHolderCallback.java b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtSurfaceHolderCallback.java new file mode 100644 index 000000000..266d8a150 --- /dev/null +++ b/src/plugins/android/jar/src/org/qtproject/qt5/android/multimedia/QtSurfaceHolderCallback.java @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtMultimedia of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +package org.qtproject.qt5.android.multimedia; + +import android.view.SurfaceHolder; + +public class QtSurfaceHolderCallback implements SurfaceHolder.Callback +{ + private long m_id = -1; + + public QtSurfaceHolderCallback(long id) + { + m_id = id; + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) + { + } + + @Override + public void surfaceCreated(SurfaceHolder holder) + { + notifySurfaceCreated(m_id); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) + { + notifySurfaceDestroyed(m_id); + } + + + private static native void notifySurfaceCreated(long id); + private static native void notifySurfaceDestroyed(long id); +} diff --git a/src/plugins/android/src/common/common.pri b/src/plugins/android/src/common/common.pri index f99dad507..9c741bd94 100644 --- a/src/plugins/android/src/common/common.pri +++ b/src/plugins/android/src/common/common.pri @@ -2,9 +2,8 @@ INCLUDEPATH += $$PWD HEADERS += \ $$PWD/qandroidvideooutput.h \ - $$PWD/qandroidvideorendercontrol.h \ $$PWD/qandroidmultimediautils.h SOURCES += \ - $$PWD/qandroidvideorendercontrol.cpp \ + $$PWD/qandroidvideooutput.cpp \ $$PWD/qandroidmultimediautils.cpp diff --git a/src/plugins/android/src/common/qandroidmultimediautils.cpp b/src/plugins/android/src/common/qandroidmultimediautils.cpp index 9255db549..6b6ca3255 100644 --- a/src/plugins/android/src/common/qandroidmultimediautils.cpp +++ b/src/plugins/android/src/common/qandroidmultimediautils.cpp @@ -68,5 +68,40 @@ bool qt_sizeLessThan(const QSize &s1, const QSize &s2) return s1.width() * s1.height() < s2.width() * s2.height(); } +QVideoFrame::PixelFormat qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat f) +{ + switch (f) { + case AndroidCamera::NV21: + return QVideoFrame::Format_NV21; + case AndroidCamera::YV12: + return QVideoFrame::Format_YV12; + case AndroidCamera::RGB565: + return QVideoFrame::Format_RGB565; + case AndroidCamera::YUY2: + return QVideoFrame::Format_YUYV; + case AndroidCamera::JPEG: + return QVideoFrame::Format_Jpeg; + default: + return QVideoFrame::Format_Invalid; + } +} + +AndroidCamera::ImageFormat qt_androidImageFormatFromPixelFormat(QVideoFrame::PixelFormat f) +{ + switch (f) { + case QVideoFrame::Format_NV21: + return AndroidCamera::NV21; + case QVideoFrame::Format_YV12: + return AndroidCamera::YV12; + case QVideoFrame::Format_RGB565: + return AndroidCamera::RGB565; + case QVideoFrame::Format_YUYV: + return AndroidCamera::YUY2; + case QVideoFrame::Format_Jpeg: + return AndroidCamera::JPEG; + default: + return AndroidCamera::UnknownImageFormat; + } +} QT_END_NAMESPACE diff --git a/src/plugins/android/src/common/qandroidmultimediautils.h b/src/plugins/android/src/common/qandroidmultimediautils.h index 6955c49e9..622f343f5 100644 --- a/src/plugins/android/src/common/qandroidmultimediautils.h +++ b/src/plugins/android/src/common/qandroidmultimediautils.h @@ -36,6 +36,7 @@ #include <qglobal.h> #include <qsize.h> +#include "androidcamera.h" QT_BEGIN_NAMESPACE @@ -45,6 +46,8 @@ int qt_findClosestValue(const QList<int> &list, int value); bool qt_sizeLessThan(const QSize &s1, const QSize &s2); +QVideoFrame::PixelFormat qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat f); +AndroidCamera::ImageFormat qt_androidImageFormatFromPixelFormat(QVideoFrame::PixelFormat f); QT_END_NAMESPACE diff --git a/src/plugins/android/src/common/qandroidvideorendercontrol.cpp b/src/plugins/android/src/common/qandroidvideooutput.cpp index cd9c9d1d6..82c27035d 100644 --- a/src/plugins/android/src/common/qandroidvideorendercontrol.cpp +++ b/src/plugins/android/src/common/qandroidvideooutput.cpp @@ -31,9 +31,9 @@ ** ****************************************************************************/ -#include "qandroidvideorendercontrol.h" -#include "androidsurfacetexture.h" +#include "qandroidvideooutput.h" +#include "androidsurfacetexture.h" #include <QAbstractVideoSurface> #include <QVideoSurfaceFormat> #include <qevent.h> @@ -59,19 +59,13 @@ static const GLfloat g_texture_data[] = { 0.f, 1.f }; -OpenGLResourcesDeleter::~OpenGLResourcesDeleter() -{ - glDeleteTextures(1, &m_textureID); - delete m_fbo; - delete m_program; -} class AndroidTextureVideoBuffer : public QAbstractVideoBuffer { public: - AndroidTextureVideoBuffer(QAndroidVideoRendererControl *control) + AndroidTextureVideoBuffer(QAndroidTextureVideoOutput *output) : QAbstractVideoBuffer(GLTextureHandle) - , m_control(control) + , m_output(output) , m_textureUpdated(false) , m_mapMode(NotMapped) { @@ -86,7 +80,7 @@ public: if (m_mapMode == NotMapped && mode == ReadOnly) { updateFrame(); m_mapMode = mode; - m_image = m_control->m_fbo->toImage(); + m_image = m_output->m_fbo->toImage(); if (numBytes) *numBytes = m_image.byteCount(); @@ -110,7 +104,7 @@ public: { AndroidTextureVideoBuffer *that = const_cast<AndroidTextureVideoBuffer*>(this); that->updateFrame(); - return m_control->m_fbo->texture(); + return m_output->m_fbo->texture(); } private: @@ -118,19 +112,47 @@ private: { if (!m_textureUpdated) { // update the video texture (called from the render thread) - m_control->renderFrameToFbo(); + m_output->renderFrameToFbo(); m_textureUpdated = true; } } - QAndroidVideoRendererControl *m_control; + QAndroidTextureVideoOutput *m_output; bool m_textureUpdated; MapMode m_mapMode; QImage m_image; }; -QAndroidVideoRendererControl::QAndroidVideoRendererControl(QObject *parent) - : QVideoRendererControl(parent) + +class OpenGLResourcesDeleter : public QObject +{ +public: + OpenGLResourcesDeleter() + : m_textureID(0) + , m_fbo(0) + , m_program(0) + { } + + ~OpenGLResourcesDeleter() + { + glDeleteTextures(1, &m_textureID); + delete m_fbo; + delete m_program; + } + + void setTexture(quint32 id) { m_textureID = id; } + void setFbo(QOpenGLFramebufferObject *fbo) { m_fbo = fbo; } + void setShaderProgram(QOpenGLShaderProgram *prog) { m_program = prog; } + +private: + quint32 m_textureID; + QOpenGLFramebufferObject *m_fbo; + QOpenGLShaderProgram *m_program; +}; + + +QAndroidTextureVideoOutput::QAndroidTextureVideoOutput(QObject *parent) + : QAndroidVideoOutput(parent) , m_surface(0) , m_surfaceTexture(0) , m_externalTex(0) @@ -138,9 +160,10 @@ QAndroidVideoRendererControl::QAndroidVideoRendererControl(QObject *parent) , m_program(0) , m_glDeleter(0) { + } -QAndroidVideoRendererControl::~QAndroidVideoRendererControl() +QAndroidTextureVideoOutput::~QAndroidTextureVideoOutput() { clearSurfaceTexture(); @@ -148,12 +171,12 @@ QAndroidVideoRendererControl::~QAndroidVideoRendererControl() m_glDeleter->deleteLater(); } -QAbstractVideoSurface *QAndroidVideoRendererControl::surface() const +QAbstractVideoSurface *QAndroidTextureVideoOutput::surface() const { return m_surface; } -void QAndroidVideoRendererControl::setSurface(QAbstractVideoSurface *surface) +void QAndroidTextureVideoOutput::setSurface(QAbstractVideoSurface *surface) { if (surface == m_surface) return; @@ -172,12 +195,12 @@ void QAndroidVideoRendererControl::setSurface(QAbstractVideoSurface *surface) } } -bool QAndroidVideoRendererControl::isReady() +bool QAndroidTextureVideoOutput::isReady() { return QOpenGLContext::currentContext() || m_externalTex; } -bool QAndroidVideoRendererControl::initSurfaceTexture() +bool QAndroidTextureVideoOutput::initSurfaceTexture() { if (m_surfaceTexture) return true; @@ -210,7 +233,7 @@ bool QAndroidVideoRendererControl::initSurfaceTexture() return m_surfaceTexture != 0; } -void QAndroidVideoRendererControl::clearSurfaceTexture() +void QAndroidTextureVideoOutput::clearSurfaceTexture() { if (m_surfaceTexture) { delete m_surfaceTexture; @@ -218,7 +241,7 @@ void QAndroidVideoRendererControl::clearSurfaceTexture() } } -AndroidSurfaceTexture *QAndroidVideoRendererControl::surfaceTexture() +AndroidSurfaceTexture *QAndroidTextureVideoOutput::surfaceTexture() { if (!initSurfaceTexture()) return 0; @@ -226,7 +249,7 @@ AndroidSurfaceTexture *QAndroidVideoRendererControl::surfaceTexture() return m_surfaceTexture; } -void QAndroidVideoRendererControl::setVideoSize(const QSize &size) +void QAndroidTextureVideoOutput::setVideoSize(const QSize &size) { QMutexLocker locker(&m_mutex); @@ -238,19 +261,19 @@ void QAndroidVideoRendererControl::setVideoSize(const QSize &size) m_nativeSize = size; } -void QAndroidVideoRendererControl::stop() +void QAndroidTextureVideoOutput::stop() { if (m_surface && m_surface->isActive()) m_surface->stop(); m_nativeSize = QSize(); } -void QAndroidVideoRendererControl::reset() +void QAndroidTextureVideoOutput::reset() { clearSurfaceTexture(); } -void QAndroidVideoRendererControl::onFrameAvailable() +void QAndroidTextureVideoOutput::onFrameAvailable() { if (!m_nativeSize.isValid() || !m_surface) return; @@ -274,7 +297,7 @@ void QAndroidVideoRendererControl::onFrameAvailable() m_surface->present(frame); } -void QAndroidVideoRendererControl::renderFrameToFbo() +void QAndroidTextureVideoOutput::renderFrameToFbo() { QMutexLocker locker(&m_mutex); @@ -333,7 +356,7 @@ void QAndroidVideoRendererControl::renderFrameToFbo() glEnable(GL_BLEND); } -void QAndroidVideoRendererControl::createGLResources() +void QAndroidTextureVideoOutput::createGLResources() { if (!m_fbo || m_fbo->size() != m_nativeSize) { delete m_fbo; @@ -374,7 +397,7 @@ void QAndroidVideoRendererControl::createGLResources() } } -void QAndroidVideoRendererControl::customEvent(QEvent *e) +void QAndroidTextureVideoOutput::customEvent(QEvent *e) { if (e->type() == QEvent::User) { // This is running in the render thread (OpenGL enabled) diff --git a/src/plugins/android/src/common/qandroidvideooutput.h b/src/plugins/android/src/common/qandroidvideooutput.h index d45779d12..f4401fa1d 100644 --- a/src/plugins/android/src/common/qandroidvideooutput.h +++ b/src/plugins/android/src/common/qandroidvideooutput.h @@ -34,19 +34,27 @@ #ifndef QANDROIDVIDEOOUTPUT_H #define QANDROIDVIDEOOUTPUT_H -#include <qglobal.h> +#include <qobject.h> #include <qsize.h> +#include <qmutex.h> QT_BEGIN_NAMESPACE class AndroidSurfaceTexture; +class AndroidSurfaceHolder; +class QOpenGLFramebufferObject; +class QOpenGLShaderProgram; +class OpenGLResourcesDeleter; +class QAbstractVideoSurface; -class QAndroidVideoOutput +class QAndroidVideoOutput : public QObject { + Q_OBJECT public: virtual ~QAndroidVideoOutput() { } virtual AndroidSurfaceTexture *surfaceTexture() { return 0; } + virtual AndroidSurfaceHolder *surfaceHolder() { return 0; } virtual bool isReady() { return true; } @@ -54,12 +62,56 @@ public: virtual void stop() { } virtual void reset() { } - // signals: - // void readyChanged(bool); +Q_SIGNALS: + void readyChanged(bool); + +protected: + QAndroidVideoOutput(QObject *parent) : QObject(parent) { } }; -#define QAndroidVideoOutput_iid "org.qt-project.qt.qandroidvideooutput/5.0" -Q_DECLARE_INTERFACE(QAndroidVideoOutput, QAndroidVideoOutput_iid) + +class QAndroidTextureVideoOutput : public QAndroidVideoOutput +{ + Q_OBJECT +public: + explicit QAndroidTextureVideoOutput(QObject *parent = 0); + ~QAndroidTextureVideoOutput() Q_DECL_OVERRIDE; + + QAbstractVideoSurface *surface() const; + void setSurface(QAbstractVideoSurface *surface); + + AndroidSurfaceTexture *surfaceTexture() Q_DECL_OVERRIDE; + + bool isReady() Q_DECL_OVERRIDE; + void setVideoSize(const QSize &) Q_DECL_OVERRIDE; + void stop() Q_DECL_OVERRIDE; + void reset() Q_DECL_OVERRIDE; + + void customEvent(QEvent *) Q_DECL_OVERRIDE; + +private Q_SLOTS: + void onFrameAvailable(); + +private: + bool initSurfaceTexture(); + void renderFrameToFbo(); + void createGLResources(); + + QMutex m_mutex; + void clearSurfaceTexture(); + + QAbstractVideoSurface *m_surface; + QSize m_nativeSize; + + AndroidSurfaceTexture *m_surfaceTexture; + + quint32 m_externalTex; + QOpenGLFramebufferObject *m_fbo; + QOpenGLShaderProgram *m_program; + OpenGLResourcesDeleter *m_glDeleter; + + friend class AndroidTextureVideoBuffer; +}; QT_END_NAMESPACE diff --git a/src/plugins/android/src/mediacapture/mediacapture.pri b/src/plugins/android/src/mediacapture/mediacapture.pri index fde0e3d6f..2811f0371 100644 --- a/src/plugins/android/src/mediacapture/mediacapture.pri +++ b/src/plugins/android/src/mediacapture/mediacapture.pri @@ -22,7 +22,8 @@ SOURCES += \ $$PWD/qandroidvideoencodersettingscontrol.cpp \ $$PWD/qandroidaudioinputselectorcontrol.cpp \ $$PWD/qandroidmediavideoprobecontrol.cpp \ - $$PWD/qandroidcamerainfocontrol.cpp + $$PWD/qandroidcamerainfocontrol.cpp \ + $$PWD/qandroidcameravideorenderercontrol.cpp HEADERS += \ $$PWD/qandroidcaptureservice.h \ @@ -46,4 +47,5 @@ HEADERS += \ $$PWD/qandroidvideoencodersettingscontrol.h \ $$PWD/qandroidaudioinputselectorcontrol.h \ $$PWD/qandroidmediavideoprobecontrol.h \ - $$PWD/qandroidcamerainfocontrol.h + $$PWD/qandroidcamerainfocontrol.h \ + $$PWD/qandroidcameravideorenderercontrol.h diff --git a/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp b/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp index cf1879eb1..d69e3ce84 100644 --- a/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp +++ b/src/plugins/android/src/mediacapture/qandroidcamerasession.cpp @@ -48,42 +48,6 @@ QT_BEGIN_NAMESPACE -class DataVideoBuffer : public QAbstractVideoBuffer -{ -public: - DataVideoBuffer(const QByteArray &d, int bpl = -1) - : QAbstractVideoBuffer(NoHandle) - , data(d) - , mode(NotMapped) - , bytesPerLine(bpl) - { } - - MapMode mapMode() const { return mode; } - - uchar *map(MapMode m, int *numBytes, int *bpl) - { - if (mode != NotMapped || m == NotMapped) - return 0; - - mode = m; - - if (numBytes) - *numBytes = data.size(); - - if (bpl) - *bpl = bytesPerLine; - - return reinterpret_cast<uchar *>(data.data()); - } - - void unmap() { mode = NotMapped; } - -private: - QByteArray data; - MapMode mode; - int bytesPerLine; -}; - Q_GLOBAL_STATIC(QList<AndroidCameraInfo>, g_availableCameras) QAndroidCameraSession::QAndroidCameraSession(QObject *parent) @@ -104,6 +68,7 @@ QAndroidCameraSession::QAndroidCameraSession(QObject *parent) , m_readyForCapture(false) , m_captureCanceled(false) , m_currentImageCaptureId(-1) + , m_previewCallback(0) { m_mediaStorageLocation.addStorageLocation( QMediaStorageLocation::Pictures, @@ -208,10 +173,11 @@ bool QAndroidCameraSession::open() if (m_camera) { connect(m_camera, SIGNAL(pictureExposed()), this, SLOT(onCameraPictureExposed())); - connect(m_camera, SIGNAL(lastPreviewFrameFetched(QByteArray,int,int)), - this, SLOT(onLastPreviewFrameFetched(QByteArray,int,int))); - connect(m_camera, SIGNAL(newPreviewFrame(QByteArray,int,int)), - this, SLOT(onNewPreviewFrame(QByteArray,int,int)), + connect(m_camera, SIGNAL(lastPreviewFrameFetched(QVideoFrame)), + this, SLOT(onLastPreviewFrameFetched(QVideoFrame)), + Qt::DirectConnection); + connect(m_camera, SIGNAL(newPreviewFrame(QVideoFrame)), + this, SLOT(onNewPreviewFrame(QVideoFrame)), Qt::DirectConnection); connect(m_camera, SIGNAL(pictureCaptured(QByteArray)), this, SLOT(onCameraPictureCaptured(QByteArray))); connect(m_camera, SIGNAL(previewStarted()), this, SLOT(onCameraPreviewStarted())); @@ -224,7 +190,7 @@ bool QAndroidCameraSession::open() if (m_camera->getPreviewFormat() != AndroidCamera::NV21) m_camera->setPreviewFormat(AndroidCamera::NV21); - m_camera->notifyNewFrames(m_videoProbes.count()); + m_camera->notifyNewFrames(m_videoProbes.count() || m_previewCallback); emit opened(); } else { @@ -259,16 +225,19 @@ void QAndroidCameraSession::close() emit statusChanged(m_status); } -void QAndroidCameraSession::setVideoPreview(QObject *videoOutput) +void QAndroidCameraSession::setVideoOutput(QAndroidVideoOutput *output) { if (m_videoOutput) { m_videoOutput->stop(); m_videoOutput->reset(); } - if (videoOutput) { - connect(videoOutput, SIGNAL(readyChanged(bool)), this, SLOT(onVideoOutputReady(bool))); - m_videoOutput = qobject_cast<QAndroidVideoOutput *>(videoOutput); + if (output) { + m_videoOutput = output; + if (m_videoOutput->isReady()) + onVideoOutputReady(true); + else + connect(m_videoOutput, SIGNAL(readyChanged(bool)), this, SLOT(onVideoOutputReady(bool))); } else { m_videoOutput = 0; } @@ -336,7 +305,10 @@ bool QAndroidCameraSession::startPreview() if (!m_videoOutput->isReady()) return true; // delay starting until the video output is ready - if (!m_camera->setPreviewTexture(m_videoOutput->surfaceTexture())) + Q_ASSERT(m_videoOutput->surfaceTexture() || m_videoOutput->surfaceHolder()); + + if ((m_videoOutput->surfaceTexture() && !m_camera->setPreviewTexture(m_videoOutput->surfaceTexture())) + || (m_videoOutput->surfaceHolder() && !m_camera->setPreviewDisplay(m_videoOutput->surfaceHolder()))) return false; m_status = QCamera::StartingStatus; @@ -366,6 +338,7 @@ void QAndroidCameraSession::stopPreview() m_camera->stopPreview(); m_camera->setPreviewSize(QSize()); m_camera->setPreviewTexture(0); + m_camera->setPreviewDisplay(0); if (m_videoOutput) { m_videoOutput->stop(); @@ -413,7 +386,7 @@ void QAndroidCameraSession::addProbe(QAndroidMediaVideoProbeControl *probe) if (probe) m_videoProbes << probe; if (m_camera) - m_camera->notifyNewFrames(m_videoProbes.count()); + m_camera->notifyNewFrames(m_videoProbes.count() || m_previewCallback); m_videoProbesMutex.unlock(); } @@ -422,7 +395,24 @@ void QAndroidCameraSession::removeProbe(QAndroidMediaVideoProbeControl *probe) m_videoProbesMutex.lock(); m_videoProbes.remove(probe); if (m_camera) - m_camera->notifyNewFrames(m_videoProbes.count()); + m_camera->notifyNewFrames(m_videoProbes.count() || m_previewCallback); + m_videoProbesMutex.unlock(); +} + +void QAndroidCameraSession::setPreviewFormat(AndroidCamera::ImageFormat format) +{ + if (format == AndroidCamera::UnknownImageFormat) + return; + + m_camera->setPreviewFormat(format); +} + +void QAndroidCameraSession::setPreviewCallback(PreviewCallback *callback) +{ + m_videoProbesMutex.lock(); + m_previewCallback = callback; + if (m_camera) + m_camera->notifyNewFrames(m_videoProbes.count() || m_previewCallback); m_videoProbesMutex.unlock(); } @@ -565,57 +555,37 @@ void QAndroidCameraSession::onCameraPictureExposed() m_camera->fetchLastPreviewFrame(); } -void QAndroidCameraSession::onLastPreviewFrameFetched(const QByteArray &preview, int width, int height) -{ - if (preview.size()) { - QtConcurrent::run(this, &QAndroidCameraSession::processPreviewImage, - m_currentImageCaptureId, - preview, - width, - height, - m_camera->getRotation()); - } -} - -void QAndroidCameraSession::processPreviewImage(int id, const QByteArray &data, int width, int height, int rotation) +void QAndroidCameraSession::onLastPreviewFrameFetched(const QVideoFrame &frame) { - emit imageCaptured(id, prepareImageFromPreviewData(data, width, height, rotation)); + QtConcurrent::run(this, &QAndroidCameraSession::processPreviewImage, + m_currentImageCaptureId, + frame, + m_camera->getRotation()); } -QImage QAndroidCameraSession::prepareImageFromPreviewData(const QByteArray &data, int width, int height, int rotation) +void QAndroidCameraSession::processPreviewImage(int id, const QVideoFrame &frame, int rotation) { - QVideoFrame frame(new QMemoryVideoBuffer(data, width), - QSize(width, height), QVideoFrame::Format_NV21); - - QImage result = qt_imageFromVideoFrame(frame); - - QTransform transform; - // Preview display of front-facing cameras is flipped horizontally, but the frame data // we get here is not. Flip it ourselves if the camera is front-facing to match what the user // sees on the viewfinder. + QTransform transform; if (m_camera->getFacing() == AndroidCamera::CameraFacingFront) transform.scale(-1, 1); - transform.rotate(rotation); - result = result.transformed(transform); - - return result; + emit imageCaptured(id, qt_imageFromVideoFrame(frame).transformed(transform)); } -void QAndroidCameraSession::onNewPreviewFrame(const QByteArray &frame, int width, int height) +void QAndroidCameraSession::onNewPreviewFrame(const QVideoFrame &frame) { m_videoProbesMutex.lock(); - if (frame.size() && m_videoProbes.count()) { - // Bytes per line should be only for the first plane. For NV21, the Y plane has 8 bits - // per sample, so bpl == width - QVideoFrame videoFrame(new DataVideoBuffer(frame, width), - QSize(width, height), - QVideoFrame::Format_NV21); - foreach (QAndroidMediaVideoProbeControl *probe, m_videoProbes) - probe->newFrameProbed(videoFrame); - } + + foreach (QAndroidMediaVideoProbeControl *probe, m_videoProbes) + probe->newFrameProbed(frame); + + if (m_previewCallback) + m_previewCallback->onFrameAvailable(frame); + m_videoProbesMutex.unlock(); } @@ -692,7 +662,7 @@ void QAndroidCameraSession::processCapturedImage(int id, } if (dest & QCameraImageCapture::CaptureToBuffer) { - QVideoFrame frame(new DataVideoBuffer(data), resolution, QVideoFrame::Format_Jpeg); + QVideoFrame frame(new QMemoryVideoBuffer(data, -1), resolution, QVideoFrame::Format_Jpeg); emit imageAvailable(id, frame); } } diff --git a/src/plugins/android/src/mediacapture/qandroidcamerasession.h b/src/plugins/android/src/mediacapture/qandroidcamerasession.h index a56721bcd..c4a813c91 100644 --- a/src/plugins/android/src/mediacapture/qandroidcamerasession.h +++ b/src/plugins/android/src/mediacapture/qandroidcamerasession.h @@ -68,7 +68,7 @@ public: void setCaptureMode(QCamera::CaptureModes mode); bool isCaptureModeSupported(QCamera::CaptureModes mode) const; - void setVideoPreview(QObject *videoOutput); + void setVideoOutput(QAndroidVideoOutput *output); void adjustViewfinderSize(const QSize &captureSize, bool restartPreview = true); QImageEncoderSettings imageSettings() const { return m_imageSettings; } @@ -90,6 +90,14 @@ public: void addProbe(QAndroidMediaVideoProbeControl *probe); void removeProbe(QAndroidMediaVideoProbeControl *probe); + void setPreviewFormat(AndroidCamera::ImageFormat format); + + struct PreviewCallback + { + virtual void onFrameAvailable(const QVideoFrame &frame) = 0; + }; + void setPreviewCallback(PreviewCallback *callback); + Q_SIGNALS: void statusChanged(QCamera::Status status); void stateChanged(QCamera::State); @@ -114,8 +122,8 @@ private Q_SLOTS: void onCameraPictureExposed(); void onCameraPictureCaptured(const QByteArray &data); - void onLastPreviewFrameFetched(const QByteArray &preview, int width, int height); - void onNewPreviewFrame(const QByteArray &frame, int width, int height); + void onLastPreviewFrameFetched(const QVideoFrame &frame); + void onNewPreviewFrame(const QVideoFrame &frame); void onCameraPreviewStarted(); void onCameraPreviewStopped(); @@ -129,8 +137,8 @@ private: void stopPreview(); void applyImageSettings(); - void processPreviewImage(int id, const QByteArray &data, int width, int height, int rotation); - QImage prepareImageFromPreviewData(const QByteArray &data, int width, int height, int rotation); + + void processPreviewImage(int id, const QVideoFrame &frame, int rotation); void processCapturedImage(int id, const QByteArray &data, const QSize &resolution, @@ -162,6 +170,7 @@ private: QSet<QAndroidMediaVideoProbeControl *> m_videoProbes; QMutex m_videoProbesMutex; + PreviewCallback *m_previewCallback; }; QT_END_NAMESPACE diff --git a/src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.cpp b/src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.cpp new file mode 100644 index 000000000..1d5b521b8 --- /dev/null +++ b/src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.cpp @@ -0,0 +1,275 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +#include "qandroidcameravideorenderercontrol.h" + +#include "qandroidcamerasession.h" +#include "qandroidvideooutput.h" +#include "androidsurfaceview.h" +#include "qandroidmultimediautils.h" +#include <qabstractvideosurface.h> +#include <qvideosurfaceformat.h> +#include <qcoreapplication.h> +#include <qthread.h> + +QT_BEGIN_NAMESPACE + +class QAndroidCameraDataVideoOutput : public QAndroidVideoOutput + , public QAndroidCameraSession::PreviewCallback +{ + Q_OBJECT +public: + explicit QAndroidCameraDataVideoOutput(QAndroidCameraVideoRendererControl *control); + ~QAndroidCameraDataVideoOutput() Q_DECL_OVERRIDE; + + AndroidSurfaceHolder *surfaceHolder() Q_DECL_OVERRIDE; + + bool isReady() Q_DECL_OVERRIDE; + + void stop() Q_DECL_OVERRIDE; + +private Q_SLOTS: + void onSurfaceCreated(); + void configureFormat(); + +private: + void onFrameAvailable(const QVideoFrame &frame); + void presentFrame(); + bool event(QEvent *); + + QAndroidCameraVideoRendererControl *m_control; + AndroidSurfaceView *m_surfaceView; + QMutex m_mutex; + QVideoFrame::PixelFormat m_pixelFormat; + QVideoFrame m_lastFrame; +}; + +QAndroidCameraDataVideoOutput::QAndroidCameraDataVideoOutput(QAndroidCameraVideoRendererControl *control) + : QAndroidVideoOutput(control) + , m_control(control) + , m_pixelFormat(QVideoFrame::Format_Invalid) +{ + // The camera preview cannot be started unless we set a SurfaceTexture or a + // SurfaceHolder. In this case we don't actually care about either of these, but since + // we need to, we setup an offscreen dummy SurfaceView in order to be able to start + // the camera preview. We'll then be able to use setPreviewCallbackWithBuffer() to + // get the raw data. + + m_surfaceView = new AndroidSurfaceView; + + connect(m_surfaceView, &AndroidSurfaceView::surfaceCreated, + this, &QAndroidCameraDataVideoOutput::onSurfaceCreated); + + m_surfaceView->setGeometry(-1, -1, 1, 1); + m_surfaceView->setVisible(true); + + connect(m_control->cameraSession(), &QAndroidCameraSession::opened, + this, &QAndroidCameraDataVideoOutput::configureFormat); + connect(m_control->surface(), &QAbstractVideoSurface::supportedFormatsChanged, + this, &QAndroidCameraDataVideoOutput::configureFormat); + configureFormat(); +} + +QAndroidCameraDataVideoOutput::~QAndroidCameraDataVideoOutput() +{ + m_control->cameraSession()->setPreviewCallback(Q_NULLPTR); + delete m_surfaceView; +} + +AndroidSurfaceHolder *QAndroidCameraDataVideoOutput::surfaceHolder() +{ + return m_surfaceView->holder(); +} + +bool QAndroidCameraDataVideoOutput::isReady() +{ + return m_surfaceView->holder() && m_surfaceView->holder()->isSurfaceCreated(); +} + +void QAndroidCameraDataVideoOutput::onSurfaceCreated() +{ + emit readyChanged(true); +} + +void QAndroidCameraDataVideoOutput::configureFormat() +{ + m_pixelFormat = QVideoFrame::Format_Invalid; + + if (!m_control->cameraSession()->camera()) + return; + + QList<QVideoFrame::PixelFormat> surfaceFormats = m_control->surface()->supportedPixelFormats(); + QList<AndroidCamera::ImageFormat> previewFormats = m_control->cameraSession()->camera()->getSupportedPreviewFormats(); + for (int i = 0; i < surfaceFormats.size(); ++i) { + QVideoFrame::PixelFormat pixFormat = surfaceFormats.at(i); + AndroidCamera::ImageFormat f = qt_androidImageFormatFromPixelFormat(pixFormat); + if (previewFormats.contains(f)) { + m_pixelFormat = pixFormat; + break; + } + } + + if (m_pixelFormat == QVideoFrame::Format_Invalid) { + m_control->cameraSession()->setPreviewCallback(Q_NULLPTR); + qWarning("The video surface is not compatible with any format supported by the camera"); + } else { + m_control->cameraSession()->setPreviewCallback(this); + + if (m_control->cameraSession()->status() > QCamera::LoadedStatus) + m_control->cameraSession()->camera()->stopPreview(); + + m_control->cameraSession()->setPreviewFormat(qt_androidImageFormatFromPixelFormat(m_pixelFormat)); + + if (m_control->cameraSession()->status() > QCamera::LoadedStatus) + m_control->cameraSession()->camera()->startPreview(); + } +} + +void QAndroidCameraDataVideoOutput::stop() +{ + m_mutex.lock(); + m_lastFrame = QVideoFrame(); + m_mutex.unlock(); + + if (m_control->surface() && m_control->surface()->isActive()) + m_control->surface()->stop(); +} + +void QAndroidCameraDataVideoOutput::onFrameAvailable(const QVideoFrame &frame) +{ + m_mutex.lock(); + m_lastFrame = frame; + m_mutex.unlock(); + + if (thread() == QThread::currentThread()) + presentFrame(); + else + QCoreApplication::postEvent(this, new QEvent(QEvent::User), Qt::HighEventPriority); +} + +bool QAndroidCameraDataVideoOutput::event(QEvent *e) +{ + if (e->type() == QEvent::User) { + presentFrame(); + return true; + } + + return QObject::event(e); +} + +void QAndroidCameraDataVideoOutput::presentFrame() +{ + Q_ASSERT(thread() == QThread::currentThread()); + + QMutexLocker locker(&m_mutex); + + if (m_control->surface() && m_lastFrame.isValid() && m_lastFrame.pixelFormat() == m_pixelFormat) { + + if (m_control->surface()->isActive() && (m_control->surface()->surfaceFormat().pixelFormat() != m_lastFrame.pixelFormat() + || m_control->surface()->surfaceFormat().frameSize() != m_lastFrame.size())) { + m_control->surface()->stop(); + } + + if (!m_control->surface()->isActive()) { + QVideoSurfaceFormat format(m_lastFrame.size(), m_lastFrame.pixelFormat(), m_lastFrame.handleType()); + // Front camera frames are automatically mirrored when using SurfaceTexture or SurfaceView, + // but the buffers we get from the data callback are not. Tell the QAbstractVideoSurface + // that it needs to mirror the frames. + if (m_control->cameraSession()->camera()->getFacing() == AndroidCamera::CameraFacingFront) + format.setProperty("mirrored", true); + + m_control->surface()->start(format); + } + + if (m_control->surface()->isActive()) + m_control->surface()->present(m_lastFrame); + } + + m_lastFrame = QVideoFrame(); +} + + +QAndroidCameraVideoRendererControl::QAndroidCameraVideoRendererControl(QAndroidCameraSession *session, QObject *parent) + : QVideoRendererControl(parent) + , m_cameraSession(session) + , m_surface(0) + , m_textureOutput(0) + , m_dataOutput(0) +{ +} + +QAndroidCameraVideoRendererControl::~QAndroidCameraVideoRendererControl() +{ + m_cameraSession->setVideoOutput(0); +} + +QAbstractVideoSurface *QAndroidCameraVideoRendererControl::surface() const +{ + return m_surface; +} + +void QAndroidCameraVideoRendererControl::setSurface(QAbstractVideoSurface *surface) +{ + if (m_surface == surface) + return; + + m_surface = surface; + QAndroidVideoOutput *oldOutput = m_textureOutput ? static_cast<QAndroidVideoOutput*>(m_textureOutput) + : static_cast<QAndroidVideoOutput*>(m_dataOutput); + QAndroidVideoOutput *newOutput = 0; + + if (m_surface) { + if (!m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).isEmpty()) { + if (!m_textureOutput) { + m_dataOutput = 0; + newOutput = m_textureOutput = new QAndroidTextureVideoOutput(this); + } + } else if (!m_dataOutput) { + m_textureOutput = 0; + newOutput = m_dataOutput = new QAndroidCameraDataVideoOutput(this); + } + + if (m_textureOutput) + m_textureOutput->setSurface(m_surface); + } + + if (newOutput != oldOutput) { + m_cameraSession->setVideoOutput(newOutput); + delete oldOutput; + } +} + +QT_END_NAMESPACE + +#include "qandroidcameravideorenderercontrol.moc" + diff --git a/src/plugins/android/src/common/qandroidvideorendercontrol.h b/src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.h index c660758fb..4b6428ba0 100644 --- a/src/plugins/android/src/common/qandroidvideorendercontrol.h +++ b/src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.h @@ -31,88 +31,36 @@ ** ****************************************************************************/ -#ifndef QANDROIDVIDEORENDERCONTROL_H -#define QANDROIDVIDEORENDERCONTROL_H +#ifndef QANDROIDCAMERAVIDEORENDERERCONTROL_H +#define QANDROIDCAMERAVIDEORENDERERCONTROL_H #include <qvideorenderercontrol.h> -#include <qmutex.h> -#include "qandroidvideooutput.h" QT_BEGIN_NAMESPACE -class QOpenGLTexture; -class QOpenGLFramebufferObject; -class QOpenGLShaderProgram; -class AndroidSurfaceTexture; +class QAndroidCameraSession; +class QAndroidTextureVideoOutput; +class QAndroidCameraDataVideoOutput; -class OpenGLResourcesDeleter : public QObject +class QAndroidCameraVideoRendererControl : public QVideoRendererControl { Q_OBJECT public: - OpenGLResourcesDeleter() - : m_textureID(0) - , m_fbo(0) - , m_program(0) - { } - - ~OpenGLResourcesDeleter(); - - void setTexture(quint32 id) { m_textureID = id; } - void setFbo(QOpenGLFramebufferObject *fbo) { m_fbo = fbo; } - void setShaderProgram(QOpenGLShaderProgram *prog) { m_program = prog; } - -private: - quint32 m_textureID; - QOpenGLFramebufferObject *m_fbo; - QOpenGLShaderProgram *m_program; -}; - -class QAndroidVideoRendererControl : public QVideoRendererControl, public QAndroidVideoOutput -{ - Q_OBJECT - Q_INTERFACES(QAndroidVideoOutput) -public: - explicit QAndroidVideoRendererControl(QObject *parent = 0); - ~QAndroidVideoRendererControl() Q_DECL_OVERRIDE; + QAndroidCameraVideoRendererControl(QAndroidCameraSession *session, QObject *parent = 0); + ~QAndroidCameraVideoRendererControl() Q_DECL_OVERRIDE; QAbstractVideoSurface *surface() const Q_DECL_OVERRIDE; void setSurface(QAbstractVideoSurface *surface) Q_DECL_OVERRIDE; - AndroidSurfaceTexture *surfaceTexture() Q_DECL_OVERRIDE; - bool isReady() Q_DECL_OVERRIDE; - void setVideoSize(const QSize &size) Q_DECL_OVERRIDE; - void stop() Q_DECL_OVERRIDE; - void reset() Q_DECL_OVERRIDE; - - void customEvent(QEvent *) Q_DECL_OVERRIDE; - -Q_SIGNALS: - void readyChanged(bool); - -private Q_SLOTS: - void onFrameAvailable(); + QAndroidCameraSession *cameraSession() const { return m_cameraSession; } private: - bool initSurfaceTexture(); - void renderFrameToFbo(); - void createGLResources(); - - QMutex m_mutex; - void clearSurfaceTexture(); - + QAndroidCameraSession *m_cameraSession; QAbstractVideoSurface *m_surface; - QSize m_nativeSize; - - AndroidSurfaceTexture *m_surfaceTexture; - - quint32 m_externalTex; - QOpenGLFramebufferObject *m_fbo; - QOpenGLShaderProgram *m_program; - OpenGLResourcesDeleter *m_glDeleter; - - friend class AndroidTextureVideoBuffer; + QAndroidTextureVideoOutput *m_textureOutput; + QAndroidCameraDataVideoOutput *m_dataOutput; }; QT_END_NAMESPACE -#endif // QANDROIDVIDEORENDERCONTROL_H +#endif // QANDROIDCAMERAVIDEORENDERERCONTROL_H diff --git a/src/plugins/android/src/mediacapture/qandroidcaptureservice.cpp b/src/plugins/android/src/mediacapture/qandroidcaptureservice.cpp index e9cdb1e78..d2107e8a5 100644 --- a/src/plugins/android/src/mediacapture/qandroidcaptureservice.cpp +++ b/src/plugins/android/src/mediacapture/qandroidcaptureservice.cpp @@ -40,7 +40,7 @@ #include "qandroidvideodeviceselectorcontrol.h" #include "qandroidaudioinputselectorcontrol.h" #include "qandroidcamerasession.h" -#include "qandroidvideorendercontrol.h" +#include "qandroidcameravideorenderercontrol.h" #include "qandroidcamerazoomcontrol.h" #include "qandroidcameraexposurecontrol.h" #include "qandroidcameraflashcontrol.h" @@ -196,8 +196,7 @@ QMediaControl *QAndroidCaptureService::requestControl(const char *name) if (qstrcmp(name, QVideoRendererControl_iid) == 0 && m_service == QLatin1String(Q_MEDIASERVICE_CAMERA) && !m_videoRendererControl) { - m_videoRendererControl = new QAndroidVideoRendererControl; - m_cameraSession->setVideoPreview(m_videoRendererControl); + m_videoRendererControl = new QAndroidCameraVideoRendererControl(m_cameraSession); return m_videoRendererControl; } @@ -217,7 +216,6 @@ void QAndroidCaptureService::releaseControl(QMediaControl *control) { if (control) { if (control == m_videoRendererControl) { - m_cameraSession->setVideoPreview(0); delete m_videoRendererControl; m_videoRendererControl = 0; return; diff --git a/src/plugins/android/src/mediacapture/qandroidcaptureservice.h b/src/plugins/android/src/mediacapture/qandroidcaptureservice.h index fc84ac124..02f063444 100644 --- a/src/plugins/android/src/mediacapture/qandroidcaptureservice.h +++ b/src/plugins/android/src/mediacapture/qandroidcaptureservice.h @@ -46,7 +46,7 @@ class QAndroidCameraInfoControl; class QAndroidVideoDeviceSelectorControl; class QAndroidAudioInputSelectorControl; class QAndroidCameraSession; -class QAndroidVideoRendererControl; +class QAndroidCameraVideoRendererControl; class QAndroidCameraZoomControl; class QAndroidCameraExposureControl; class QAndroidCameraFlashControl; @@ -82,7 +82,7 @@ private: QAndroidVideoDeviceSelectorControl *m_videoInputControl; QAndroidAudioInputSelectorControl *m_audioInputControl; QAndroidCameraSession *m_cameraSession; - QMediaControl *m_videoRendererControl; + QAndroidCameraVideoRendererControl *m_videoRendererControl; QAndroidCameraZoomControl *m_cameraZoomControl; QAndroidCameraExposureControl *m_cameraExposureControl; QAndroidCameraFlashControl *m_cameraFlashControl; diff --git a/src/plugins/android/src/mediaplayer/mediaplayer.pri b/src/plugins/android/src/mediaplayer/mediaplayer.pri index c386d996b..9f758a993 100644 --- a/src/plugins/android/src/mediaplayer/mediaplayer.pri +++ b/src/plugins/android/src/mediaplayer/mediaplayer.pri @@ -3,9 +3,11 @@ INCLUDEPATH += $$PWD HEADERS += \ $$PWD/qandroidmediaplayercontrol.h \ $$PWD/qandroidmediaservice.h \ - $$PWD/qandroidmetadatareadercontrol.h + $$PWD/qandroidmetadatareadercontrol.h \ + $$PWD/qandroidmediaplayervideorenderercontrol.h SOURCES += \ $$PWD/qandroidmediaplayercontrol.cpp \ $$PWD/qandroidmediaservice.cpp \ - $$PWD/qandroidmetadatareadercontrol.cpp + $$PWD/qandroidmetadatareadercontrol.cpp \ + $$PWD/qandroidmediaplayervideorenderercontrol.cpp diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp index 9a050e7ad..90beeabe7 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp +++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp @@ -345,7 +345,7 @@ void QAndroidMediaPlayerControl::setMedia(const QMediaContent &mediaContent, mReloadingMedia = false; } -void QAndroidMediaPlayerControl::setVideoOutput(QObject *videoOutput) +void QAndroidMediaPlayerControl::setVideoOutput(QAndroidVideoOutput *videoOutput) { if (mVideoOutput) { mMediaPlayer->setDisplay(0); @@ -353,7 +353,7 @@ void QAndroidMediaPlayerControl::setVideoOutput(QObject *videoOutput) mVideoOutput->reset(); } - mVideoOutput = qobject_cast<QAndroidVideoOutput *>(videoOutput); + mVideoOutput = videoOutput; if (!mVideoOutput) return; diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h index 3f92d809c..a015a6809 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h +++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.h @@ -67,7 +67,7 @@ public: const QIODevice *mediaStream() const Q_DECL_OVERRIDE; void setMedia(const QMediaContent &mediaContent, QIODevice *stream) Q_DECL_OVERRIDE; - void setVideoOutput(QObject *videoOutput); + void setVideoOutput(QAndroidVideoOutput *videoOutput); Q_SIGNALS: void metaDataUpdated(); diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp new file mode 100644 index 000000000..5dd51c395 --- /dev/null +++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +#include "qandroidmediaplayervideorenderercontrol.h" + +#include "qandroidmediaplayercontrol.h" +#include "qandroidvideooutput.h" +#include <qabstractvideosurface.h> + +QT_BEGIN_NAMESPACE + +QAndroidMediaPlayerVideoRendererControl::QAndroidMediaPlayerVideoRendererControl(QAndroidMediaPlayerControl *mediaPlayer, QObject *parent) + : QVideoRendererControl(parent) + , m_mediaPlayerControl(mediaPlayer) + , m_surface(0) + , m_textureOutput(new QAndroidTextureVideoOutput(this)) +{ + m_mediaPlayerControl->setVideoOutput(m_textureOutput); +} + +QAndroidMediaPlayerVideoRendererControl::~QAndroidMediaPlayerVideoRendererControl() +{ + m_mediaPlayerControl->setVideoOutput(0); +} + +QAbstractVideoSurface *QAndroidMediaPlayerVideoRendererControl::surface() const +{ + return m_surface; +} + +void QAndroidMediaPlayerVideoRendererControl::setSurface(QAbstractVideoSurface *surface) +{ + if (m_surface == surface) + return; + + m_surface = surface; + m_textureOutput->setSurface(m_surface); +} + +QT_END_NAMESPACE diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.h b/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.h new file mode 100644 index 000000000..cfa41980d --- /dev/null +++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayervideorenderercontrol.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +#ifndef QANDROIDMEDIAPLAYERVIDEORENDERERCONTROL_H +#define QANDROIDMEDIAPLAYERVIDEORENDERERCONTROL_H + +#include <qvideorenderercontrol.h> + +QT_BEGIN_NAMESPACE + +class QAndroidMediaPlayerControl; +class QAndroidTextureVideoOutput; + +class QAndroidMediaPlayerVideoRendererControl : public QVideoRendererControl +{ + Q_OBJECT +public: + QAndroidMediaPlayerVideoRendererControl(QAndroidMediaPlayerControl *mediaPlayer, QObject *parent = 0); + ~QAndroidMediaPlayerVideoRendererControl() Q_DECL_OVERRIDE; + + QAbstractVideoSurface *surface() const Q_DECL_OVERRIDE; + void setSurface(QAbstractVideoSurface *surface) Q_DECL_OVERRIDE; + +private: + QAndroidMediaPlayerControl *m_mediaPlayerControl; + QAbstractVideoSurface *m_surface; + QAndroidTextureVideoOutput *m_textureOutput; +}; + +QT_END_NAMESPACE + +#endif // QANDROIDMEDIAPLAYERVIDEORENDERERCONTROL_H diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp index 74943ca64..992bcead2 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp +++ b/src/plugins/android/src/mediaplayer/qandroidmediaservice.cpp @@ -35,7 +35,7 @@ #include "qandroidmediaplayercontrol.h" #include "qandroidmetadatareadercontrol.h" -#include "qandroidvideorendercontrol.h" +#include "qandroidmediaplayervideorenderercontrol.h" QT_BEGIN_NAMESPACE @@ -53,9 +53,9 @@ QAndroidMediaService::QAndroidMediaService(QObject *parent) QAndroidMediaService::~QAndroidMediaService() { - delete mMediaControl; - delete mMetadataControl; delete mVideoRendererControl; + delete mMetadataControl; + delete mMediaControl; } QMediaControl *QAndroidMediaService::requestControl(const char *name) @@ -68,8 +68,7 @@ QMediaControl *QAndroidMediaService::requestControl(const char *name) if (qstrcmp(name, QVideoRendererControl_iid) == 0) { if (!mVideoRendererControl) { - mVideoRendererControl = new QAndroidVideoRendererControl; - mMediaControl->setVideoOutput(mVideoRendererControl); + mVideoRendererControl = new QAndroidMediaPlayerVideoRendererControl(mMediaControl); return mVideoRendererControl; } } @@ -80,7 +79,6 @@ QMediaControl *QAndroidMediaService::requestControl(const char *name) void QAndroidMediaService::releaseControl(QMediaControl *control) { if (control == mVideoRendererControl) { - mMediaControl->setVideoOutput(0); delete mVideoRendererControl; mVideoRendererControl = 0; } diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaservice.h b/src/plugins/android/src/mediaplayer/qandroidmediaservice.h index 6babbb15f..798d6ef39 100644 --- a/src/plugins/android/src/mediaplayer/qandroidmediaservice.h +++ b/src/plugins/android/src/mediaplayer/qandroidmediaservice.h @@ -40,6 +40,7 @@ QT_BEGIN_NAMESPACE class QAndroidMediaPlayerControl; class QAndroidMetaDataReaderControl; +class QAndroidMediaPlayerVideoRendererControl; class QAndroidMediaService : public QMediaService { @@ -54,7 +55,7 @@ public: private: QAndroidMediaPlayerControl *mMediaControl; QAndroidMetaDataReaderControl *mMetadataControl; - QMediaControl *mVideoRendererControl; + QAndroidMediaPlayerVideoRendererControl *mVideoRendererControl; }; QT_END_NAMESPACE diff --git a/src/plugins/android/src/qandroidmediaserviceplugin.cpp b/src/plugins/android/src/qandroidmediaserviceplugin.cpp index 5d35ddf51..bf89badb3 100644 --- a/src/plugins/android/src/qandroidmediaserviceplugin.cpp +++ b/src/plugins/android/src/qandroidmediaserviceplugin.cpp @@ -43,6 +43,7 @@ #include "androidcamera.h" #include "androidmultimediautils.h" #include "androidmediarecorder.h" +#include "androidsurfaceview.h" #include <qdebug.h> QT_BEGIN_NAMESPACE @@ -160,7 +161,8 @@ Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/) if (!AndroidMediaPlayer::initJNI(jniEnv) || !AndroidCamera::initJNI(jniEnv) || - !AndroidMediaRecorder::initJNI(jniEnv)) { + !AndroidMediaRecorder::initJNI(jniEnv) || + !AndroidSurfaceHolder::initJNI(jniEnv)) { return JNI_ERR; } diff --git a/src/plugins/android/src/wrappers/jni/androidcamera.cpp b/src/plugins/android/src/wrappers/jni/androidcamera.cpp index 1fe0e666e..083803e38 100644 --- a/src/plugins/android/src/wrappers/jni/androidcamera.cpp +++ b/src/plugins/android/src/wrappers/jni/androidcamera.cpp @@ -33,6 +33,7 @@ #include "androidcamera.h" #include "androidsurfacetexture.h" +#include "androidsurfaceview.h" #include "qandroidmultimediautils.h" #include <qstringlist.h> @@ -41,6 +42,7 @@ #include <QtCore/qthread.h> #include <QtCore/qreadwritelock.h> #include <QtCore/qmutex.h> +#include <QtMultimedia/private/qmemoryvideobuffer_p.h> QT_BEGIN_NAMESPACE @@ -121,7 +123,8 @@ static void notifyPictureCaptured(JNIEnv *env, jobject, int id, jbyteArray data) Q_EMIT (*it)->pictureCaptured(bytes); } -static void notifyNewPreviewFrame(JNIEnv *env, jobject, int id, jbyteArray data, int width, int height) +static void notifyNewPreviewFrame(JNIEnv *env, jobject, int id, jbyteArray data, + int width, int height, int format, int bpl) { QReadLocker locker(rwLock); const auto it = cameras->constFind(id); @@ -129,10 +132,17 @@ static void notifyNewPreviewFrame(JNIEnv *env, jobject, int id, jbyteArray data, return; const int arrayLength = env->GetArrayLength(data); + if (arrayLength == 0) + return; + QByteArray bytes(arrayLength, Qt::Uninitialized); env->GetByteArrayRegion(data, 0, arrayLength, (jbyte*)bytes.data()); - Q_EMIT (*it)->newPreviewFrame(bytes, width, height); + QVideoFrame frame(new QMemoryVideoBuffer(bytes, bpl), + QSize(width, height), + qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat(format))); + + Q_EMIT (*it)->newPreviewFrame(frame); } class AndroidCameraPrivate : public QObject @@ -157,10 +167,12 @@ public: Q_INVOKABLE AndroidCamera::ImageFormat getPreviewFormat(); Q_INVOKABLE void setPreviewFormat(AndroidCamera::ImageFormat fmt); + Q_INVOKABLE QList<AndroidCamera::ImageFormat> getSupportedPreviewFormats(); Q_INVOKABLE QSize previewSize() const { return m_previewSize; } Q_INVOKABLE void updatePreviewSize(); Q_INVOKABLE bool setPreviewTexture(void *surfaceTexture); + Q_INVOKABLE bool setPreviewDisplay(void *surfaceHolder); Q_INVOKABLE bool isZoomSupported(); Q_INVOKABLE int getMaxZoom(); @@ -238,7 +250,7 @@ Q_SIGNALS: void whiteBalanceChanged(); - void lastPreviewFrameFetched(const QByteArray &preview, int width, int height); + void lastPreviewFrameFetched(const QVideoFrame &frame); }; AndroidCamera::AndroidCamera(AndroidCameraPrivate *d, QThread *worker) @@ -250,6 +262,7 @@ AndroidCamera::AndroidCamera(AndroidCameraPrivate *d, QThread *worker) qRegisterMetaType<QList<int> >(); qRegisterMetaType<QList<QSize> >(); qRegisterMetaType<QList<QRect> >(); + qRegisterMetaType<ImageFormat>(); connect(d, &AndroidCameraPrivate::previewSizeChanged, this, &AndroidCamera::previewSizeChanged); connect(d, &AndroidCameraPrivate::previewStarted, this, &AndroidCamera::previewStarted); @@ -368,6 +381,12 @@ void AndroidCamera::setPreviewFormat(ImageFormat fmt) QMetaObject::invokeMethod(d, "setPreviewFormat", Q_ARG(AndroidCamera::ImageFormat, fmt)); } +QList<AndroidCamera::ImageFormat> AndroidCamera::getSupportedPreviewFormats() +{ + Q_D(AndroidCamera); + return d->getSupportedPreviewFormats(); +} + QSize AndroidCamera::previewSize() const { Q_D(const AndroidCamera); @@ -399,6 +418,18 @@ bool AndroidCamera::setPreviewTexture(AndroidSurfaceTexture *surfaceTexture) return ok; } +bool AndroidCamera::setPreviewDisplay(AndroidSurfaceHolder *surfaceHolder) +{ + Q_D(AndroidCamera); + bool ok = true; + QMetaObject::invokeMethod(d, + "setPreviewDisplay", + Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, ok), + Q_ARG(void *, surfaceHolder ? surfaceHolder->surfaceHolder() : 0)); + return ok; +} + bool AndroidCamera::isZoomSupported() { Q_D(AndroidCamera); @@ -834,7 +865,7 @@ AndroidCamera::ImageFormat AndroidCameraPrivate::getPreviewFormat() QMutexLocker parametersLocker(&m_parametersMutex); if (!m_parameters.isValid()) - return AndroidCamera::Unknown; + return AndroidCamera::UnknownImageFormat; return AndroidCamera::ImageFormat(m_parameters.callMethod<jint>("getPreviewFormat")); } @@ -850,6 +881,27 @@ void AndroidCameraPrivate::setPreviewFormat(AndroidCamera::ImageFormat fmt) applyParameters(); } +QList<AndroidCamera::ImageFormat> AndroidCameraPrivate::getSupportedPreviewFormats() +{ + QList<AndroidCamera::ImageFormat> list; + + QMutexLocker parametersLocker(&m_parametersMutex); + + if (m_parameters.isValid()) { + QJNIObjectPrivate formatList = m_parameters.callObjectMethod("getSupportedPreviewFormats", + "()Ljava/util/List;"); + int count = formatList.callMethod<jint>("size"); + for (int i = 0; i < count; ++i) { + QJNIObjectPrivate format = formatList.callObjectMethod("get", + "(I)Ljava/lang/Object;", + i); + list.append(AndroidCamera::ImageFormat(format.callMethod<jint>("intValue"))); + } + } + + return list; +} + void AndroidCameraPrivate::updatePreviewSize() { QMutexLocker parametersLocker(&m_parametersMutex); @@ -871,6 +923,15 @@ bool AndroidCameraPrivate::setPreviewTexture(void *surfaceTexture) return !exceptionCheckAndClear(env); } +bool AndroidCameraPrivate::setPreviewDisplay(void *surfaceHolder) +{ + QJNIEnvironmentPrivate env; + m_camera.callMethod<void>("setPreviewDisplay", + "(Landroid/view/SurfaceHolder;)V", + static_cast<jobject>(surfaceHolder)); + return !exceptionCheckAndClear(env); +} + bool AndroidCameraPrivate::isZoomSupported() { QMutexLocker parametersLocker(&m_parametersMutex); @@ -1361,15 +1422,25 @@ void AndroidCameraPrivate::fetchLastPreviewFrame() return; const int arrayLength = env->GetArrayLength(static_cast<jbyteArray>(data.object())); + if (arrayLength == 0) + return; + QByteArray bytes(arrayLength, Qt::Uninitialized); env->GetByteArrayRegion(static_cast<jbyteArray>(data.object()), 0, arrayLength, reinterpret_cast<jbyte *>(bytes.data())); - emit lastPreviewFrameFetched(bytes, - m_cameraListener.callMethod<jint>("previewWidth"), - m_cameraListener.callMethod<jint>("previewHeight")); + const int width = m_cameraListener.callMethod<jint>("previewWidth"); + const int height = m_cameraListener.callMethod<jint>("previewHeight"); + const int format = m_cameraListener.callMethod<jint>("previewFormat"); + const int bpl = m_cameraListener.callMethod<jint>("previewBytesPerLine"); + + QVideoFrame frame(new QMemoryVideoBuffer(bytes, bpl), + QSize(width, height), + qt_pixelFormatFromAndroidImageFormat(AndroidCamera::ImageFormat(format))); + + emit lastPreviewFrameFetched(frame); } void AndroidCameraPrivate::applyParameters() @@ -1414,7 +1485,7 @@ bool AndroidCamera::initJNI(JNIEnv *env) {"notifyAutoFocusComplete", "(IZ)V", (void *)notifyAutoFocusComplete}, {"notifyPictureExposed", "(I)V", (void *)notifyPictureExposed}, {"notifyPictureCaptured", "(I[B)V", (void *)notifyPictureCaptured}, - {"notifyNewPreviewFrame", "(I[BII)V", (void *)notifyNewPreviewFrame} + {"notifyNewPreviewFrame", "(I[BIIII)V", (void *)notifyNewPreviewFrame} }; if (clazz && env->RegisterNatives(clazz, diff --git a/src/plugins/android/src/wrappers/jni/androidcamera.h b/src/plugins/android/src/wrappers/jni/androidcamera.h index 7a8ae8b23..9e4c387c4 100644 --- a/src/plugins/android/src/wrappers/jni/androidcamera.h +++ b/src/plugins/android/src/wrappers/jni/androidcamera.h @@ -46,6 +46,7 @@ class QThread; class AndroidCameraPrivate; class AndroidSurfaceTexture; +class AndroidSurfaceHolder; struct AndroidCameraInfo { @@ -67,7 +68,7 @@ public: }; enum ImageFormat { // same values as in android.graphics.ImageFormat Java class - Unknown = 0, + UnknownImageFormat = 0, RGB565 = 4, NV16 = 16, NV21 = 17, @@ -95,10 +96,12 @@ public: ImageFormat getPreviewFormat(); void setPreviewFormat(ImageFormat fmt); + QList<ImageFormat> getSupportedPreviewFormats(); QSize previewSize() const; void setPreviewSize(const QSize &size); bool setPreviewTexture(AndroidSurfaceTexture *surfaceTexture); + bool setPreviewDisplay(AndroidSurfaceHolder *surfaceHolder); bool isZoomSupported(); int getMaxZoom(); @@ -177,8 +180,8 @@ Q_SIGNALS: void pictureExposed(); void pictureCaptured(const QByteArray &data); - void lastPreviewFrameFetched(const QByteArray &preview, int width, int height); - void newPreviewFrame(const QByteArray &frame, int width, int height); + void lastPreviewFrameFetched(const QVideoFrame &frame); + void newPreviewFrame(const QVideoFrame &frame); private: AndroidCamera(AndroidCameraPrivate *d, QThread *worker); @@ -188,6 +191,8 @@ private: QScopedPointer<QThread> m_worker; }; +Q_DECLARE_METATYPE(AndroidCamera::ImageFormat) + QT_END_NAMESPACE #endif // ANDROIDCAMERA_H diff --git a/src/plugins/android/src/wrappers/jni/androidsurfaceview.cpp b/src/plugins/android/src/wrappers/jni/androidsurfaceview.cpp new file mode 100644 index 000000000..67560baf4 --- /dev/null +++ b/src/plugins/android/src/wrappers/jni/androidsurfaceview.cpp @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +#include "androidsurfaceview.h" + +#include <QtCore/private/qjnihelpers_p.h> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qvector.h> +#include <QtCore/qdebug.h> +#include <QtCore/qmutex.h> +#include <QtGui/qwindow.h> + +QT_BEGIN_NAMESPACE + +static const char QtSurfaceHolderCallbackClassName[] = "org/qtproject/qt5/android/multimedia/QtSurfaceHolderCallback"; +typedef QVector<AndroidSurfaceHolder *> SurfaceHolders; +Q_GLOBAL_STATIC(SurfaceHolders, surfaceHolders) +Q_GLOBAL_STATIC(QMutex, shLock) + +AndroidSurfaceHolder::AndroidSurfaceHolder(QJNIObjectPrivate object) + : m_surfaceHolder(object) + , m_surfaceCreated(false) +{ + if (!m_surfaceHolder.isValid()) + return; + + { + QMutexLocker locker(shLock); + surfaceHolders->append(this); + } + + QJNIObjectPrivate callback(QtSurfaceHolderCallbackClassName, "(J)V", reinterpret_cast<jlong>(this)); + m_surfaceHolder.callMethod<void>("addCallback", + "(Landroid/view/SurfaceHolder$Callback;)V", + callback.object()); +} + +AndroidSurfaceHolder::~AndroidSurfaceHolder() +{ + QMutexLocker locker(shLock); + const int i = surfaceHolders->indexOf(this); + if (Q_UNLIKELY(i == -1)) + return; + + surfaceHolders->remove(i); +} + +jobject AndroidSurfaceHolder::surfaceHolder() const +{ + return m_surfaceHolder.object(); +} + +bool AndroidSurfaceHolder::isSurfaceCreated() const +{ + QMutexLocker locker(shLock); + return m_surfaceCreated; +} + +void AndroidSurfaceHolder::handleSurfaceCreated(JNIEnv*, jobject, jlong id) +{ + QMutexLocker locker(shLock); + const int i = surfaceHolders->indexOf(reinterpret_cast<AndroidSurfaceHolder *>(id)); + if (Q_UNLIKELY(i == -1)) + return; + + (*surfaceHolders)[i]->m_surfaceCreated = true; + Q_EMIT (*surfaceHolders)[i]->surfaceCreated(); +} + +void AndroidSurfaceHolder::handleSurfaceDestroyed(JNIEnv*, jobject, jlong id) +{ + QMutexLocker locker(shLock); + const int i = surfaceHolders->indexOf(reinterpret_cast<AndroidSurfaceHolder *>(id)); + if (Q_UNLIKELY(i == -1)) + return; + + (*surfaceHolders)[i]->m_surfaceCreated = false; +} + +bool AndroidSurfaceHolder::initJNI(JNIEnv *env) +{ + jclass clazz = QJNIEnvironmentPrivate::findClass(QtSurfaceHolderCallbackClassName, + env); + + static const JNINativeMethod methods[] = { + {"notifySurfaceCreated", "(J)V", (void *)AndroidSurfaceHolder::handleSurfaceCreated}, + {"notifySurfaceDestroyed", "(J)V", (void *)AndroidSurfaceHolder::handleSurfaceDestroyed} + }; + + if (clazz && env->RegisterNatives(clazz, + methods, + sizeof(methods) / sizeof(methods[0])) != JNI_OK) { + return false; + } + + return true; +} + +AndroidSurfaceView::AndroidSurfaceView() + : m_window(0) + , m_surfaceHolder(0) + , m_pendingVisible(-1) +{ + setAutoDelete(false); + QtAndroidPrivate::runOnUiThread(this, QJNIEnvironmentPrivate()); +} + +AndroidSurfaceView::~AndroidSurfaceView() +{ + delete m_surfaceHolder; + delete m_window; +} + +AndroidSurfaceHolder *AndroidSurfaceView::holder() const +{ + return m_surfaceHolder; +} + +void AndroidSurfaceView::setVisible(bool v) +{ + if (m_window) + m_window->setVisible(v); + else + m_pendingVisible = int(v); +} + +void AndroidSurfaceView::setGeometry(int x, int y, int width, int height) +{ + if (m_window) + m_window->setGeometry(x, y, width, height); + else + m_pendingGeometry = QRect(x, y, width, height); +} + +bool AndroidSurfaceView::event(QEvent *e) +{ + if (e->type() == QEvent::User) { + Q_ASSERT(m_surfaceView.isValid()); + + QJNIObjectPrivate holder = m_surfaceView.callObjectMethod("getHolder", + "()Landroid/view/SurfaceHolder;"); + if (!holder.isValid()) { + m_surfaceView = QJNIObjectPrivate(); + } else { + m_surfaceHolder = new AndroidSurfaceHolder(holder); + connect(m_surfaceHolder, &AndroidSurfaceHolder::surfaceCreated, + this, &AndroidSurfaceView::surfaceCreated); + { // Lock now to avoid a race with handleSurfaceCreated() + QMutexLocker locker(shLock); + m_window = QWindow::fromWinId(WId(m_surfaceView.object())); + + if (m_pendingVisible != -1) + m_window->setVisible(m_pendingVisible); + if (m_pendingGeometry.isValid()) + m_window->setGeometry(m_pendingGeometry); + } + } + + return true; + } + + return QObject::event(e); +} + +// Called on the Android UI thread. +void AndroidSurfaceView::run() +{ + m_surfaceView = QJNIObjectPrivate("android/view/SurfaceView", + "(Landroid/content/Context;)V", + QtAndroidPrivate::activity()); + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); +} + +QT_END_NAMESPACE diff --git a/src/plugins/android/src/wrappers/jni/androidsurfaceview.h b/src/plugins/android/src/wrappers/jni/androidsurfaceview.h new file mode 100644 index 000000000..661a5959f --- /dev/null +++ b/src/plugins/android/src/wrappers/jni/androidsurfaceview.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +#ifndef ANDROIDSURFACEVIEW_H +#define ANDROIDSURFACEVIEW_H + +#include <QtCore/private/qjni_p.h> +#include <qrect.h> +#include <QtCore/qrunnable.h> + +QT_BEGIN_NAMESPACE + +class QWindow; + +class AndroidSurfaceHolder : public QObject +{ + Q_OBJECT +public: + ~AndroidSurfaceHolder(); + + jobject surfaceHolder() const; + bool isSurfaceCreated() const; + + static bool initJNI(JNIEnv *env); + +Q_SIGNALS: + void surfaceCreated(); + +private: + AndroidSurfaceHolder(QJNIObjectPrivate object); + + static void handleSurfaceCreated(JNIEnv*, jobject, jlong id); + static void handleSurfaceDestroyed(JNIEnv*, jobject, jlong id); + + QJNIObjectPrivate m_surfaceHolder; + bool m_surfaceCreated; + + friend class AndroidSurfaceView; +}; + +class AndroidSurfaceView : public QObject, public QRunnable +{ + Q_OBJECT +public: + AndroidSurfaceView(); + ~AndroidSurfaceView(); + + AndroidSurfaceHolder *holder() const; + + void setVisible(bool v); + void setGeometry(int x, int y, int width, int height); + + bool event(QEvent *); + +Q_SIGNALS: + void surfaceCreated(); + +protected: + void run() override; + +private: + QJNIObjectPrivate m_surfaceView; + QWindow *m_window; + AndroidSurfaceHolder *m_surfaceHolder; + int m_pendingVisible; + QRect m_pendingGeometry; +}; + +QT_END_NAMESPACE + +#endif // ANDROIDSURFACEVIEW_H diff --git a/src/plugins/android/src/wrappers/jni/jni.pri b/src/plugins/android/src/wrappers/jni/jni.pri index e96baff1c..930d7e922 100644 --- a/src/plugins/android/src/wrappers/jni/jni.pri +++ b/src/plugins/android/src/wrappers/jni/jni.pri @@ -8,7 +8,8 @@ HEADERS += \ $$PWD/androidmediametadataretriever.h \ $$PWD/androidcamera.h \ $$PWD/androidmultimediautils.h \ - $$PWD/androidmediarecorder.h + $$PWD/androidmediarecorder.h \ + $$PWD/androidsurfaceview.h SOURCES += \ $$PWD/androidmediaplayer.cpp \ @@ -16,4 +17,5 @@ SOURCES += \ $$PWD/androidmediametadataretriever.cpp \ $$PWD/androidcamera.cpp \ $$PWD/androidmultimediautils.cpp \ - $$PWD/androidmediarecorder.cpp + $$PWD/androidmediarecorder.cpp \ + $$PWD/androidsurfaceview.cpp |