Android: support non OpenGL video surfaces for the camera.

QCamera can now pass raw frame data to its QAbstractVideoSurface.
The now deprecated Android camera API we're using doesn't allow to
get frame data without also displaying the frames in a SurfaceView
or a SurfaceTexture. To work around that, an invisible dummy
SurfaceView is used.
This allows to retrieve frames in the NV21, YV12, YUY2 or RGB565
formats, depending on the Android version and on the device.

Task-number: QTBUG-35416
Change-Id: I77b4f50505c3b91efb4b2288a57f50398922c0db
Reviewed-by: Christian Stromme <christian.stromme@theqtcompany.com>
Reviewed-by: Yoann Lopes <yoann.lopes@theqtcompany.com>
This commit is contained in:
Yoann Lopes
2015-09-24 16:58:36 +02:00
parent ece9005efe
commit 8debbfbc9b
28 changed files with 1179 additions and 233 deletions

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)
void QAndroidCameraSession::onLastPreviewFrameFetched(const QVideoFrame &frame)
{
if (preview.size()) {
QtConcurrent::run(this, &QAndroidCameraSession::processPreviewImage,
m_currentImageCaptureId,
preview,
width,
height,
m_camera->getRotation());
}
QtConcurrent::run(this, &QAndroidCameraSession::processPreviewImage,
m_currentImageCaptureId,
frame,
m_camera->getRotation());
}
void QAndroidCameraSession::processPreviewImage(int id, const QByteArray &data, int width, int height, int rotation)
void QAndroidCameraSession::processPreviewImage(int id, const QVideoFrame &frame, int rotation)
{
emit imageCaptured(id, prepareImageFromPreviewData(data, width, height, rotation));
}
QImage QAndroidCameraSession::prepareImageFromPreviewData(const QByteArray &data, int width, int height, 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);
}
}

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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();

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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