diff options
author | Christian Strømme <christian.stromme@theqtcompany.com> | 2016-01-15 17:27:32 +0100 |
---|---|---|
committer | Christian Strømme <christian.stromme@theqtcompany.com> | 2016-01-15 17:28:17 +0100 |
commit | 84e426c3af2a3bb1b7f916e54263aea758db38d0 (patch) | |
tree | 4fe09a8da5b15ba466e5771239d06f29a6c123da /src/plugins/android/src/mediacapture | |
parent | 84aaa48fdfc1f35c9870518a3d4b6f08a1f99449 (diff) | |
parent | 924dc7f48c7003b46079623738ae531f34aed903 (diff) |
Merge remote-tracking branch 'origin/5.6' into dev
Conflicts:
src/plugins/android/src/mediacapture/qandroidcamerasession.cpp
src/plugins/wmf/mftvideo.cpp
Change-Id: I78868b416ea4baec89ca3e2dc9eb4712db16d5fc
Diffstat (limited to 'src/plugins/android/src/mediacapture')
8 files changed, 483 insertions, 101 deletions
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 3623ce05a..eec31e65e 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,14 +173,17 @@ 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())); connect(m_camera, SIGNAL(previewStopped()), this, SLOT(onCameraPreviewStopped())); + connect(m_camera, &AndroidCamera::previewFailedToStart, this, &QAndroidCameraSession::onCameraPreviewFailedToStart); + connect(m_camera, &AndroidCamera::takePictureFailed, this, &QAndroidCameraSession::onCameraTakePictureFailed); m_nativeOrientation = m_camera->getNativeOrientation(); @@ -224,7 +192,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 +227,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 +307,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 +340,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 +388,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 +397,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(); } @@ -556,6 +548,12 @@ void QAndroidCameraSession::cancelCapture() m_captureCanceled = true; } +void QAndroidCameraSession::onCameraTakePictureFailed() +{ + emit imageCaptureError(m_currentImageCaptureId, QCameraImageCapture::ResourceError, + tr("Failed to capture image")); +} + void QAndroidCameraSession::onCameraPictureExposed() { if (m_captureCanceled) @@ -565,57 +563,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); - for (QAndroidMediaVideoProbeControl *probe : qAsConst(m_videoProbes)) - probe->newFrameProbed(videoFrame); - } + + for (QAndroidMediaVideoProbeControl *probe : qAsConst(m_videoProbes)) + probe->newFrameProbed(frame); + + if (m_previewCallback) + m_previewCallback->onFrameAvailable(frame); + m_videoProbesMutex.unlock(); } @@ -647,6 +625,27 @@ void QAndroidCameraSession::onCameraPreviewStarted() setReadyForCapture(true); } +void QAndroidCameraSession::onCameraPreviewFailedToStart() +{ + if (m_status == QCamera::StartingStatus) { + Q_EMIT error(QCamera::CameraError, tr("Camera preview failed to start.")); + + AndroidMultimediaUtils::enableOrientationListener(false); + m_camera->setPreviewSize(QSize()); + m_camera->setPreviewTexture(0); + if (m_videoOutput) { + m_videoOutput->stop(); + m_videoOutput->reset(); + } + m_previewStarted = false; + + m_status = QCamera::LoadedStatus; + emit statusChanged(m_status); + + setReadyForCapture(false); + } +} + void QAndroidCameraSession::onCameraPreviewStopped() { if (m_status == QCamera::StoppingStatus) { @@ -692,7 +691,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..d15509fe8 100644 --- a/src/plugins/android/src/mediacapture/qandroidcamerasession.h +++ b/src/plugins/android/src/mediacapture/qandroidcamerasession.h @@ -68,7 +68,8 @@ public: void setCaptureMode(QCamera::CaptureModes mode); bool isCaptureModeSupported(QCamera::CaptureModes mode) const; - void setVideoPreview(QObject *videoOutput); + QAndroidVideoOutput *videoOutput() const { return m_videoOutput; } + void setVideoOutput(QAndroidVideoOutput *output); void adjustViewfinderSize(const QSize &captureSize, bool restartPreview = true); QImageEncoderSettings imageSettings() const { return m_imageSettings; } @@ -90,6 +91,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); @@ -112,11 +121,13 @@ private Q_SLOTS: void onApplicationStateChanged(Qt::ApplicationState state); + void onCameraTakePictureFailed(); 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 onCameraPreviewFailedToStart(); void onCameraPreviewStopped(); private: @@ -129,8 +140,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 +173,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/mediacapture/qandroidcameravideorenderercontrol.h b/src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.h new file mode 100644 index 000000000..4b6428ba0 --- /dev/null +++ b/src/plugins/android/src/mediacapture/qandroidcameravideorenderercontrol.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** 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 QANDROIDCAMERAVIDEORENDERERCONTROL_H +#define QANDROIDCAMERAVIDEORENDERERCONTROL_H + +#include <qvideorenderercontrol.h> + +QT_BEGIN_NAMESPACE + +class QAndroidCameraSession; +class QAndroidTextureVideoOutput; +class QAndroidCameraDataVideoOutput; + +class QAndroidCameraVideoRendererControl : public QVideoRendererControl +{ + Q_OBJECT +public: + QAndroidCameraVideoRendererControl(QAndroidCameraSession *session, QObject *parent = 0); + ~QAndroidCameraVideoRendererControl() Q_DECL_OVERRIDE; + + QAbstractVideoSurface *surface() const Q_DECL_OVERRIDE; + void setSurface(QAbstractVideoSurface *surface) Q_DECL_OVERRIDE; + + QAndroidCameraSession *cameraSession() const { return m_cameraSession; } + +private: + QAndroidCameraSession *m_cameraSession; + QAbstractVideoSurface *m_surface; + QAndroidTextureVideoOutput *m_textureOutput; + QAndroidCameraDataVideoOutput *m_dataOutput; +}; + +QT_END_NAMESPACE + +#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/mediacapture/qandroidcapturesession.cpp b/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp index f2ea1b9d7..f02016654 100644 --- a/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp +++ b/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp @@ -37,6 +37,7 @@ #include "qandroidcamerasession.h" #include "androidmultimediautils.h" #include "qandroidmultimediautils.h" +#include "qandroidvideooutput.h" QT_BEGIN_NAMESPACE @@ -217,15 +218,31 @@ void QAndroidCaptureSession::start() m_usedOutputLocation = QUrl::fromLocalFile(filePath); m_mediaRecorder->setOutputFile(filePath); + // Even though the Android doc explicitly says that calling MediaRecorder.setPreviewDisplay() + // is not necessary when the Camera already has a Surface, it doesn't actually work on some + // devices. For example on the Samsung Galaxy Tab 2, the camera server dies after prepare() + // and start() if MediaRecorder.setPreviewDispaly() is not called. + if (m_cameraSession) { + // When using a SurfaceTexture, we need to pass a new one to the MediaRecorder, not the same + // one that is set on the Camera or it will crash, hence the reset(). + m_cameraSession->videoOutput()->reset(); + if (m_cameraSession->videoOutput()->surfaceTexture()) + m_mediaRecorder->setSurfaceTexture(m_cameraSession->videoOutput()->surfaceTexture()); + else if (m_cameraSession->videoOutput()->surfaceHolder()) + m_mediaRecorder->setSurfaceHolder(m_cameraSession->videoOutput()->surfaceHolder()); + } + if (!m_mediaRecorder->prepare()) { emit error(QMediaRecorder::FormatError, QLatin1String("Unable to prepare the media recorder.")); - restartViewfinder(); + if (m_cameraSession) + restartViewfinder(); return; } if (!m_mediaRecorder->start()) { emit error(QMediaRecorder::FormatError, QLatin1String("Unable to start the media recorder.")); - restartViewfinder(); + if (m_cameraSession) + restartViewfinder(); return; } @@ -412,13 +429,26 @@ void QAndroidCaptureSession::applySettings() void QAndroidCaptureSession::updateViewfinder() { - m_cameraSession->camera()->stopPreview(); + m_cameraSession->camera()->stopPreviewSynchronous(); m_cameraSession->adjustViewfinderSize(m_videoSettings.resolution(), false); } void QAndroidCaptureSession::restartViewfinder() { + if (!m_cameraSession) + return; + m_cameraSession->camera()->reconnect(); + + // This is not necessary on most devices, but it crashes on some if we don't stop the + // preview and reset the preview display on the camera when recording is over. + m_cameraSession->camera()->stopPreviewSynchronous(); + m_cameraSession->videoOutput()->reset(); + if (m_cameraSession->videoOutput()->surfaceTexture()) + m_cameraSession->camera()->setPreviewTexture(m_cameraSession->videoOutput()->surfaceTexture()); + else if (m_cameraSession->videoOutput()->surfaceHolder()) + m_cameraSession->camera()->setPreviewDisplay(m_cameraSession->videoOutput()->surfaceHolder()); + m_cameraSession->camera()->startPreview(); m_cameraSession->setReadyForCapture(true); } |