mirror of https://github.com/mgba-emu/mgba.git
Qt: Preliminary QCamera support
This commit is contained in:
parent
a1acf8bcef
commit
cf15ea91d7
|
@ -157,6 +157,8 @@ if(Qt5Multimedia_FOUND)
|
||||||
list(APPEND AUDIO_SRC
|
list(APPEND AUDIO_SRC
|
||||||
AudioProcessorQt.cpp
|
AudioProcessorQt.cpp
|
||||||
AudioDevice.cpp)
|
AudioDevice.cpp)
|
||||||
|
list(APPEND SOURCE_FILES
|
||||||
|
VideoDumper.cpp)
|
||||||
if (WIN32 AND QT_STATIC)
|
if (WIN32 AND QT_STATIC)
|
||||||
list(APPEND QT_LIBRARIES qtaudio_windows strmiids winmm)
|
list(APPEND QT_LIBRARIES qtaudio_windows strmiids winmm)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -13,6 +13,9 @@
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
#ifdef BUILD_QT_MULTIMEDIA
|
||||||
|
#include <QCamera>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <mgba/core/interface.h>
|
#include <mgba/core/interface.h>
|
||||||
#include <mgba-util/configuration.h>
|
#include <mgba-util/configuration.h>
|
||||||
|
@ -53,6 +56,10 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren
|
||||||
m_gamepadTimer.setInterval(50);
|
m_gamepadTimer.setInterval(50);
|
||||||
m_gamepadTimer.start();
|
m_gamepadTimer.start();
|
||||||
|
|
||||||
|
#ifdef BUILD_QT_MULTIMEDIA
|
||||||
|
connect(&m_videoDumper, &VideoDumper::imageAvailable, this, &InputController::setCamImage);
|
||||||
|
#endif
|
||||||
|
|
||||||
mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
|
mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
|
||||||
mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
|
mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
|
||||||
mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L);
|
mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L);
|
||||||
|
@ -79,13 +86,33 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren
|
||||||
setLuminanceLevel(0);
|
setLuminanceLevel(0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
m_image.p = this;
|
||||||
m_image.startRequestImage = [](mImageSource* context) {
|
m_image.startRequestImage = [](mImageSource* context) {
|
||||||
InputControllerImage* image = static_cast<InputControllerImage*>(context);
|
InputControllerImage* image = static_cast<InputControllerImage*>(context);
|
||||||
if (image->image.isNull()) {
|
if (image->image.isNull()) {
|
||||||
image->image.load(":/res/no-cam.png");
|
image->image.load(":/res/no-cam.png");
|
||||||
}
|
}
|
||||||
|
#ifdef BUILD_QT_MULTIMEDIA
|
||||||
|
if (image->p->m_config->getQtOption("cameraDriver").toInt() == static_cast<int>(CameraDriver::QT_MULTIMEDIA)) {
|
||||||
|
if (!image->p->m_camera) {
|
||||||
|
image->p->m_camera = new QCamera;
|
||||||
|
}
|
||||||
|
image->p->m_camera->setViewfinder(&image->p->m_videoDumper);
|
||||||
|
image->p->m_camera->start();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
m_image.stopRequestImage = nullptr;
|
|
||||||
|
m_image.stopRequestImage = [](mImageSource* context) {
|
||||||
|
InputControllerImage* image = static_cast<InputControllerImage*>(context);
|
||||||
|
#ifdef BUILD_QT_MULTIMEDIA
|
||||||
|
if (image->p->m_camera) {
|
||||||
|
image->p->m_camera->stop();
|
||||||
|
delete image->p->m_camera;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
m_image.requestImage = [](mImageSource* context, unsigned w, unsigned h, const uint32_t** buffer, size_t* stride) {
|
m_image.requestImage = [](mImageSource* context, unsigned w, unsigned h, const uint32_t** buffer, size_t* stride) {
|
||||||
InputControllerImage* image = static_cast<InputControllerImage*>(context);
|
InputControllerImage* image = static_cast<InputControllerImage*>(context);
|
||||||
image->resizedImage = image->image.scaled(w, h, Qt::KeepAspectRatioByExpanding);
|
image->resizedImage = image->image.scaled(w, h, Qt::KeepAspectRatioByExpanding);
|
||||||
|
@ -650,6 +677,11 @@ void InputController::loadCamImage(const QString& path) {
|
||||||
m_image.resizedImage = QImage();
|
m_image.resizedImage = QImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InputController::setCamImage(const QImage& image) {
|
||||||
|
m_image.image = image;
|
||||||
|
m_image.resizedImage = QImage();
|
||||||
|
}
|
||||||
|
|
||||||
void InputController::increaseLuminanceLevel() {
|
void InputController::increaseLuminanceLevel() {
|
||||||
setLuminanceLevel(m_luxLevel + 1);
|
setLuminanceLevel(m_luxLevel + 1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,9 +22,16 @@
|
||||||
#include "platform/sdl/sdl-events.h"
|
#include "platform/sdl/sdl-events.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef BUILD_QT_MULTIMEDIA
|
||||||
|
#include "VideoDumper.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
struct mRotationSource;
|
struct mRotationSource;
|
||||||
struct mRumble;
|
struct mRumble;
|
||||||
|
|
||||||
|
class QCamera;
|
||||||
|
|
||||||
namespace QGBA {
|
namespace QGBA {
|
||||||
|
|
||||||
class ConfigController;
|
class ConfigController;
|
||||||
|
@ -33,6 +40,13 @@ class InputController : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum class CameraDriver : int {
|
||||||
|
NONE = 0,
|
||||||
|
#ifdef BUILD_QT_MULTIMEDIA
|
||||||
|
QT_MULTIMEDIA = 1,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
static const uint32_t KEYBOARD = 0x51545F4B;
|
static const uint32_t KEYBOARD = 0x51545F4B;
|
||||||
|
|
||||||
InputController(int playerId = 0, QWidget* topLevel = nullptr, QObject* parent = nullptr);
|
InputController(int playerId = 0, QWidget* topLevel = nullptr, QObject* parent = nullptr);
|
||||||
|
@ -81,8 +95,6 @@ public:
|
||||||
void stealFocus(QWidget* focus);
|
void stealFocus(QWidget* focus);
|
||||||
void releaseFocus(QWidget* focus);
|
void releaseFocus(QWidget* focus);
|
||||||
|
|
||||||
void loadCamImage(const QString& path);
|
|
||||||
|
|
||||||
mRumble* rumble();
|
mRumble* rumble();
|
||||||
mRotationSource* rotationSource();
|
mRotationSource* rotationSource();
|
||||||
mImageSource* imageSource() { return &m_image; }
|
mImageSource* imageSource() { return &m_image; }
|
||||||
|
@ -106,6 +118,9 @@ public slots:
|
||||||
void setLuminanceLevel(int level);
|
void setLuminanceLevel(int level);
|
||||||
void setLuminanceValue(uint8_t value);
|
void setLuminanceValue(uint8_t value);
|
||||||
|
|
||||||
|
void loadCamImage(const QString& path);
|
||||||
|
void setCamImage(const QImage& image);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void postPendingEvent(GBAKey);
|
void postPendingEvent(GBAKey);
|
||||||
void clearPendingEvent(GBAKey);
|
void clearPendingEvent(GBAKey);
|
||||||
|
@ -125,6 +140,11 @@ private:
|
||||||
QImage resizedImage;
|
QImage resizedImage;
|
||||||
} m_image;
|
} m_image;
|
||||||
|
|
||||||
|
#ifdef BUILD_QT_MULTIMEDIA
|
||||||
|
QCamera* m_camera = nullptr;
|
||||||
|
VideoDumper m_videoDumper;
|
||||||
|
#endif
|
||||||
|
|
||||||
mInputMap m_inputMap;
|
mInputMap m_inputMap;
|
||||||
ConfigController* m_config = nullptr;
|
ConfigController* m_config = nullptr;
|
||||||
int m_playerId;
|
int m_playerId;
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include "VideoDumper.h"
|
||||||
|
|
||||||
|
#include <QImage>
|
||||||
|
|
||||||
|
using namespace QGBA;
|
||||||
|
|
||||||
|
VideoDumper::VideoDumper(QObject* parent)
|
||||||
|
: QAbstractVideoSurface(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VideoDumper::present(const QVideoFrame& frame) {
|
||||||
|
QVideoFrame mappedFrame(frame);
|
||||||
|
if (!mappedFrame.map(QAbstractVideoBuffer::ReadOnly)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QImage::Format format = QVideoFrame::imageFormatFromPixelFormat(mappedFrame.pixelFormat());
|
||||||
|
const uchar* bits = mappedFrame.bits();
|
||||||
|
QImage image(bits, mappedFrame.width(), mappedFrame.height(), mappedFrame.bytesPerLine(),
|
||||||
|
format);
|
||||||
|
image = std::move(image.copy()); // Create a deep copy of the bits
|
||||||
|
emit imageAvailable(image);
|
||||||
|
mappedFrame.unmap();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QVideoFrame::PixelFormat> VideoDumper::supportedPixelFormats(QAbstractVideoBuffer::HandleType) const {
|
||||||
|
QList<QVideoFrame::PixelFormat> list;
|
||||||
|
list.append(QVideoFrame::Format_ARGB32);
|
||||||
|
list.append(QVideoFrame::Format_ARGB32_Premultiplied);
|
||||||
|
list.append(QVideoFrame::Format_RGB32);
|
||||||
|
list.append(QVideoFrame::Format_RGB24);
|
||||||
|
list.append(QVideoFrame::Format_RGB565);
|
||||||
|
list.append(QVideoFrame::Format_RGB555);
|
||||||
|
return list;
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef QGBA_VIDEO_DUMPER
|
||||||
|
#define QGBA_VIDEO_DUMPER
|
||||||
|
#include <QAbstractVideoSurface>
|
||||||
|
|
||||||
|
namespace QGBA {
|
||||||
|
|
||||||
|
class VideoDumper : public QAbstractVideoSurface {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
VideoDumper(QObject* parent = nullptr);
|
||||||
|
|
||||||
|
bool present(const QVideoFrame& frame) override;
|
||||||
|
QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType type = QAbstractVideoBuffer::NoHandle) const override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void imageAvailable(const QImage& image);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue