Qt: Fix QCamera threading woes
Vicki Pfau vi@endrift.com
Wed, 26 Jul 2017 23:15:50 -0700
5 files changed,
68 insertions(+),
41 deletions(-)
M
src/platform/qt/CMakeLists.txt
→
src/platform/qt/CMakeLists.txt
@@ -160,7 +160,8 @@ AudioDevice.cpp)
list(APPEND SOURCE_FILES VideoDumper.cpp) if (WIN32 AND QT_STATIC) - list(APPEND QT_LIBRARIES qtaudio_windows strmiids winmm) + list(APPEND QT_LIBRARIES Qt5::QWindowsAudioPlugin Qt5::DSServicePlugin + strmiids winmm mfuuid mfplat mf ksguid dxva2 evr d3d9) endif() list(APPEND QT_LIBRARIES Qt5::Multimedia) list(APPEND QT_DEFINES BUILD_QT_MULTIMEDIA)@@ -283,10 +284,12 @@ if(APPLE)
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") get_target_property(QTCOCOA Qt5::QCocoaIntegrationPlugin LOCATION) get_target_property(COREAUDIO Qt5::CoreAudioPlugin LOCATION) + get_target_property(QTAVFSERVICE Qt5::AVFServicePlugin LOCATION) set(BUNDLE_PATH ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.app) target_sources(${BINARY_NAME}-qt PRIVATE "${PLUGINS}") set_source_files_properties("${QTCOCOA}" PROPERTIES MACOSX_PACKAGE_LOCATION Contents/PlugIns) set_source_files_properties("${COREAUDIO}" PROPERTIES MACOSX_PACKAGE_LOCATION Contents/PlugIns) + set_source_files_properties("${QTAVFSERVICE}" PROPERTIES MACOSX_PACKAGE_LOCATION Contents/PlugIns) install(CODE " include(BundleUtilities) set(BU_CHMOD_BUNDLE_ITEMS ON)@@ -294,7 +297,7 @@ file(GLOB_RECURSE PLUGINS \"${BUNDLE_PATH}/Contents/PlugIns/*${CMAKE_SHARED_LIBRARY_SUFFIX}\")
fixup_bundle(\"${BUNDLE_PATH}\" \"${PLUGINS}\" \"\") " COMPONENT ${BINARY_NAME}-qt) else() - set(DEPLOY_OPTIONS -p platforms/libqcocoa.dylib,audio/libqtaudio_coreaudio.dylib) + set(DEPLOY_OPTIONS -p platforms/libqcocoa.dylib,audio/libqtaudio_coreaudio.dylib,mediaservice/libqavfcamera.dylib.dylib) if(NOT CMAKE_INSTALL_NAME_TOOL EQUAL "install_name_tool") set(DEPLOY_OPTIONS ${DEPLOY_OPTIONS} -I "${CMAKE_INSTALL_NAME_TOOL}") endif()
M
src/platform/qt/InputController.cpp
→
src/platform/qt/InputController.cpp
@@ -15,6 +15,7 @@ #include <QTimer>
#include <QWidget> #ifdef BUILD_QT_MULTIMEDIA #include <QCamera> +#include <QVideoSurfaceFormat> #endif #include <mgba/core/interface.h>@@ -94,37 +95,9 @@ image->h = h;
if (image->image.isNull()) { image->image.load(":/res/no-cam.png"); } - image->resizedImage = image->image.scaled(w, h, Qt::KeepAspectRatioByExpanding); #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; - } -#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) - QCameraViewfinderSettings settings; - QSize size(1920, 1080); - auto cameraRes = image->p->m_camera->supportedViewfinderResolutions(settings); - for (auto& cameraSize : cameraRes) { - if (cameraSize.width() < w || cameraSize.height() < h) { - continue; - } - if (cameraSize.width() <= size.width() && cameraSize.height() <= size.height()) { - size = cameraSize; - } - } - settings.setResolution(size); - auto cameraFormats = image->p->m_camera->supportedViewfinderPixelFormats(settings); - auto goodFormats = image->p->m_videoDumper.supportedPixelFormats(); - for (auto& format : goodFormats) { - if (cameraFormats.contains(format)) { - settings.setPixelFormat(format); - break; - } - } - image->p->m_camera->setViewfinderSettings(settings); -#endif - image->p->m_camera->setViewfinder(&image->p->m_videoDumper); - image->p->m_camera->start(); + QMetaObject::invokeMethod(image->p, "setupCam"); } #endif };@@ -132,15 +105,15 @@
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; - } + QMetaObject::invokeMethod(image->p, "teardownCam"); #endif }; m_image.requestImage = [](mImageSource* context, const uint32_t** buffer, size_t* stride) { InputControllerImage* image = static_cast<InputControllerImage*>(context); + if (image->resizedImage.isNull()) { + image->resizedImage = image->image.scaled(image->w, image->h, Qt::KeepAspectRatioByExpanding); + } image->resizedImage = image->resizedImage.convertToFormat(QImage::Format_RGB32); const uint32_t* bits = reinterpret_cast<const uint32_t*>(image->resizedImage.constBits()); QSize size = image->resizedImage.size();@@ -737,3 +710,48 @@ }
emit luminanceValueChanged(m_luxValue); } +void InputController::setupCam() { +#ifdef BUILD_QT_MULTIMEDIA + if (!m_camera) { + m_camera = std::make_unique<QCamera>(); + } + QVideoFrame::PixelFormat format(QVideoFrame::Format_RGB32); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) + m_camera->load(); + QCameraViewfinderSettings settings; + QSize size(1920, 1080); + auto cameraRes = m_camera->supportedViewfinderResolutions(settings); + for (auto& cameraSize : cameraRes) { + if (cameraSize.width() < m_image.w || cameraSize.height() < m_image.h) { + continue; + } + if (cameraSize.width() <= size.width() && cameraSize.height() <= size.height()) { + size = cameraSize; + } + } + settings.setResolution(size); + auto cameraFormats = m_camera->supportedViewfinderPixelFormats(settings); + auto goodFormats = m_videoDumper.supportedPixelFormats(); + for (auto& goodFormat : goodFormats) { + if (cameraFormats.contains(goodFormat)) { + settings.setPixelFormat(goodFormat); + format = goodFormat; + break; + } + } + m_camera->setViewfinderSettings(settings); +#endif + m_camera->setCaptureMode(QCamera::CaptureVideo); + m_camera->setViewfinder(&m_videoDumper); + m_camera->start(); +#endif +} + +void InputController::teardownCam() { +#ifdef BUILD_QT_MULTIMEDIA + if (m_camera) { + m_camera->stop(); + m_camera.reset(); + } +#endif +}
M
src/platform/qt/InputController.h
→
src/platform/qt/InputController.h
@@ -15,6 +15,8 @@ #include <QSet>
#include <QTimer> #include <QVector> +#include <memory> + #include <mgba/gba/interface.h> #include <mgba/internal/gba/input.h>@@ -121,6 +123,10 @@
void loadCamImage(const QString& path); void setCamImage(const QImage& image); +private slots: + void setupCam(); + void teardownCam(); + private: void postPendingEvent(GBAKey); void clearPendingEvent(GBAKey);@@ -142,7 +148,7 @@ unsigned w, h;
} m_image; #ifdef BUILD_QT_MULTIMEDIA - QCamera* m_camera = nullptr; + std::unique_ptr<QCamera> m_camera; VideoDumper m_videoDumper; #endif
M
src/platform/qt/VideoDumper.cpp
→
src/platform/qt/VideoDumper.cpp
@@ -20,12 +20,11 @@ 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 + uchar* bits = mappedFrame.bits(); + QImage image(bits, mappedFrame.width(), mappedFrame.height(), mappedFrame.bytesPerLine(), format); + image = image.copy(); // Create a deep copy of the bits + mappedFrame.unmap(); emit imageAvailable(image); - mappedFrame.unmap(); return true; }
M
src/platform/qt/main.cpp
→
src/platform/qt/main.cpp
@@ -23,6 +23,7 @@ #ifdef _WIN32
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); #ifdef BUILD_QT_MULTIMEDIA Q_IMPORT_PLUGIN(QWindowsAudioPlugin); +Q_IMPORT_PLUGIN(DSServicePlugin); #endif #endif #endif