mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	citra_qt: camera integration (#3566)
* Implement camera feature * Make OpenCVCamera optional * Fix styling problems * CI configuration * Fix CI * Hopefully final fix * Hopefully final fix * Fix all the problems * Oops.. * Add Qt Multimedia Camera * Another oops * Try to fix broken linux CI * Try to fix broken linux CI * Fix problems * Improve UI * Fix problems * camera: Add support for Qt <5.10 and fix preview error * CI: try to fix linux-frozen travis build * camera: fix problems and add multiple handlers support * fix CI * remove most ServiceFramework changes * Fix format * Remove last ServiceFramework change * camera: remove unused interfaces; revert submodule change * camera: fix CI error * ci: use ccache for opencv build * citra_qt: fix configuration error; CI: add mediaservice plugin * citra_qt: fix clang-format * citra_qt: fix documentation error * citra_qt: fix configuration page; camera: fix pausing crash * citra_qt: fix preview not stopping * camera: extend handlers length * camera: fix camera resume error * camera: fix clang-format * camera: remove all OpenCV; citra_qt: rewrite configuration * camera: remove all OpenCV; citra_qt: rewrite configuration * camera: remove all OpenCV; citra_qt: rewrite configuration * CI: fix linux ci * camera: check settings update; citra_qt: fix configuration error * service_cam: use a better way to apply configuration * Service_CAM: rewrite camera reload * cam: fix clang format * citra_qt: fix argument load issue; camera: base of system camera selection * citra_qt: Add system camera selection * camera: fix image upside down, Implement SetFrameRate in Qt Multimedia Camera * camera: Add missing <array> include, update SetFrameRate and add settings in Qt Multimedia Camera header * camera: move started in Qt Multimedia Camera header * QtMultimediaCamera: Move frame rates to SetFrameRate; Set minimum and maximum frame rate * Update appveyor.yml
This commit is contained in:
		
							parent
							
								
									bcecfac0ef
								
							
						
					
					
						commit
						57827de38b
					
				
					 28 changed files with 1531 additions and 18 deletions
				
			
		|  | @ -8,10 +8,14 @@ apt-get install -y build-essential wget git python-launchpadlib ccache | ||||||
| # Install specific versions of packages with their dependencies | # Install specific versions of packages with their dependencies | ||||||
| # The apt repositories remove older versions regularly, so we can't use | # The apt repositories remove older versions regularly, so we can't use | ||||||
| # apt-get and have to pull the packages directly from the archives. | # apt-get and have to pull the packages directly from the archives. | ||||||
|  | # qt5-qmltooling-plugins and qtdeclarative5-dev are required for qtmultimedia5-dev | ||||||
| /citra/.travis/linux-frozen/install_package.py       \ | /citra/.travis/linux-frozen/install_package.py       \ | ||||||
|     libsdl2-dev 2.0.7+dfsg1-3ubuntu1 bionic          \ |     libsdl2-dev 2.0.7+dfsg1-3ubuntu1 bionic          \ | ||||||
|     qtbase5-dev 5.9.3+dfsg-0ubuntu2 bionic           \ |     qtbase5-dev 5.9.3+dfsg-0ubuntu2 bionic           \ | ||||||
|     libqt5opengl5-dev 5.9.3+dfsg-0ubuntu2 bionic     \ |     libqt5opengl5-dev 5.9.3+dfsg-0ubuntu2 bionic     \ | ||||||
|  |     qt5-qmltooling-plugins 5.9.3-0ubuntu1 bionic     \ | ||||||
|  |     qtdeclarative5-dev 5.9.3-0ubuntu1 bionic         \ | ||||||
|  |     qtmultimedia5-dev 5.9.3-0ubuntu3 bionic          \ | ||||||
|     libicu57 57.1-6ubuntu0.2 bionic |     libicu57 57.1-6ubuntu0.2 bionic | ||||||
| 
 | 
 | ||||||
| # Get a recent version of CMake | # Get a recent version of CMake | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ | ||||||
| cd /citra | cd /citra | ||||||
| 
 | 
 | ||||||
| apt-get update | apt-get update | ||||||
| apt-get install -y build-essential libsdl2-dev qtbase5-dev libqt5opengl5-dev qttools5-dev qttools5-dev-tools wget git ccache | apt-get install -y build-essential libsdl2-dev qtbase5-dev libqt5opengl5-dev qtmultimedia5-dev qttools5-dev qttools5-dev-tools wget git ccache | ||||||
| 
 | 
 | ||||||
| # Get a recent version of CMake | # Get a recent version of CMake | ||||||
| wget https://cmake.org/files/v3.10/cmake-3.10.1-Linux-x86_64.sh | wget https://cmake.org/files/v3.10/cmake-3.10.1-Linux-x86_64.sh | ||||||
|  |  | ||||||
|  | @ -225,7 +225,7 @@ endif() | ||||||
| if (ENABLE_QT) | if (ENABLE_QT) | ||||||
|     if (CITRA_USE_BUNDLED_QT) |     if (CITRA_USE_BUNDLED_QT) | ||||||
|         if (MSVC14 AND ARCHITECTURE_x86_64) |         if (MSVC14 AND ARCHITECTURE_x86_64) | ||||||
|             set(QT_VER qt-5.7-msvc2015_64) |             set(QT_VER qt-5.10.0-msvc2017_64) | ||||||
|         else() |         else() | ||||||
|             message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable CITRA_USE_BUNDLED_QT and provide your own.") |             message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable CITRA_USE_BUNDLED_QT and provide your own.") | ||||||
|         endif() |         endif() | ||||||
|  | @ -241,7 +241,7 @@ if (ENABLE_QT) | ||||||
|         set(QT_PREFIX_HINT) |         set(QT_PREFIX_HINT) | ||||||
|     endif() |     endif() | ||||||
| 
 | 
 | ||||||
|     find_package(Qt5 REQUIRED COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT}) |     find_package(Qt5 REQUIRED COMPONENTS Widgets OpenGL Multimedia ${QT_PREFIX_HINT}) | ||||||
| 
 | 
 | ||||||
|     if (ENABLE_QT_TRANSLATION) |     if (ENABLE_QT_TRANSLATION) | ||||||
|         find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT}) |         find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT}) | ||||||
|  |  | ||||||
|  | @ -3,8 +3,10 @@ function(copy_citra_Qt5_deps target_dir) | ||||||
|     set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/") |     set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/") | ||||||
|     set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin") |     set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin") | ||||||
|     set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/") |     set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/") | ||||||
|  |     set(Qt5_MEDIASERVICE_DIR "${Qt5_DIR}/../../../plugins/mediaservice/") | ||||||
|     set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/") |     set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/") | ||||||
|     set(PLATFORMS ${DLL_DEST}platforms/) |     set(PLATFORMS ${DLL_DEST}platforms/) | ||||||
|  |     set(MEDIASERVICE ${DLL_DEST}mediaservice/) | ||||||
|     set(STYLES ${DLL_DEST}styles/) |     set(STYLES ${DLL_DEST}styles/) | ||||||
|     windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST} |     windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST} | ||||||
|         icudt*.dll |         icudt*.dll | ||||||
|  | @ -14,7 +16,13 @@ function(copy_citra_Qt5_deps target_dir) | ||||||
|         Qt5Gui$<$<CONFIG:Debug>:d>.* |         Qt5Gui$<$<CONFIG:Debug>:d>.* | ||||||
|         Qt5OpenGL$<$<CONFIG:Debug>:d>.* |         Qt5OpenGL$<$<CONFIG:Debug>:d>.* | ||||||
|         Qt5Widgets$<$<CONFIG:Debug>:d>.* |         Qt5Widgets$<$<CONFIG:Debug>:d>.* | ||||||
|  |         Qt5Multimedia$<$<CONFIG:Debug>:d>.* | ||||||
|  |         Qt5Network$<$<CONFIG:Debug>:d>.* | ||||||
|     ) |     ) | ||||||
|     windows_copy_files(citra-qt ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*) |     windows_copy_files(citra-qt ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*) | ||||||
|  |     windows_copy_files(citra-qt ${Qt5_MEDIASERVICE_DIR} ${MEDIASERVICE} | ||||||
|  |         dsengine$<$<CONFIG:Debug>:d>.* | ||||||
|  |         wmfengine$<$<CONFIG:Debug>:d>.* | ||||||
|  |     ) | ||||||
|     windows_copy_files(citra-qt ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*) |     windows_copy_files(citra-qt ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*) | ||||||
| endfunction(copy_citra_Qt5_deps) | endfunction(copy_citra_Qt5_deps) | ||||||
|  |  | ||||||
|  | @ -110,6 +110,7 @@ after_build: | ||||||
| 
 | 
 | ||||||
|           mkdir $RELEASE_DIST |           mkdir $RELEASE_DIST | ||||||
|           mkdir $RELEASE_DIST/platforms |           mkdir $RELEASE_DIST/platforms | ||||||
|  |           mkdir $RELEASE_DIST/mediaservice | ||||||
|           mkdir $RELEASE_DIST/styles |           mkdir $RELEASE_DIST/styles | ||||||
| 
 | 
 | ||||||
|           # copy the compiled binaries and other release files to the release folder |           # copy the compiled binaries and other release files to the release folder | ||||||
|  | @ -131,6 +132,9 @@ after_build: | ||||||
|           # copy the qt windows plugin dll to platforms |           # copy the qt windows plugin dll to platforms | ||||||
|           Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/platforms/qwindows.dll" -force -destination "$RELEASE_DIST/platforms" |           Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/platforms/qwindows.dll" -force -destination "$RELEASE_DIST/platforms" | ||||||
| 
 | 
 | ||||||
|  |           # copy the qt mediaservice plugin dll | ||||||
|  |           Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/mediaservice/dsengine.dll" -force -destination "$RELEASE_DIST/mediaservice" | ||||||
|  | 
 | ||||||
|           # copy the qt windows vista style dll to platforms |           # copy the qt windows vista style dll to platforms | ||||||
|           Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/styles/qwindowsvistastyle.dll" -force -destination "$RELEASE_DIST/styles" |           Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/styles/qwindowsvistastyle.dll" -force -destination "$RELEASE_DIST/styles" | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,11 +9,21 @@ add_executable(citra-qt | ||||||
|     aboutdialog.h |     aboutdialog.h | ||||||
|     bootmanager.cpp |     bootmanager.cpp | ||||||
|     bootmanager.h |     bootmanager.h | ||||||
|  |     camera/camera_util.cpp | ||||||
|  |     camera/camera_util.h | ||||||
|  |     camera/still_image_camera.cpp | ||||||
|  |     camera/still_image_camera.h | ||||||
|  |     camera/qt_camera_factory.cpp | ||||||
|  |     camera/qt_camera_factory.h | ||||||
|  |     camera/qt_multimedia_camera.cpp | ||||||
|  |     camera/qt_multimedia_camera.h | ||||||
|     citra-qt.rc |     citra-qt.rc | ||||||
|     configuration/config.cpp |     configuration/config.cpp | ||||||
|     configuration/config.h |     configuration/config.h | ||||||
|     configuration/configure_audio.cpp |     configuration/configure_audio.cpp | ||||||
|     configuration/configure_audio.h |     configuration/configure_audio.h | ||||||
|  |     configuration/configure_camera.cpp | ||||||
|  |     configuration/configure_camera.h | ||||||
|     configuration/configure_debug.cpp |     configuration/configure_debug.cpp | ||||||
|     configuration/configure_debug.h |     configuration/configure_debug.h | ||||||
|     configuration/configure_dialog.cpp |     configuration/configure_dialog.cpp | ||||||
|  | @ -92,6 +102,7 @@ add_executable(citra-qt | ||||||
| set(UIS | set(UIS | ||||||
|     configuration/configure.ui |     configuration/configure.ui | ||||||
|     configuration/configure_audio.ui |     configuration/configure_audio.ui | ||||||
|  |     configuration/configure_camera.ui | ||||||
|     configuration/configure_debug.ui |     configuration/configure_debug.ui | ||||||
|     configuration/configure_general.ui |     configuration/configure_general.ui | ||||||
|     configuration/configure_graphics.ui |     configuration/configure_graphics.ui | ||||||
|  | @ -180,7 +191,7 @@ endif() | ||||||
| create_target_directory_groups(citra-qt) | create_target_directory_groups(citra-qt) | ||||||
| 
 | 
 | ||||||
| target_link_libraries(citra-qt PRIVATE audio_core common core input_common network video_core) | target_link_libraries(citra-qt PRIVATE audio_core common core input_common network video_core) | ||||||
| target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::OpenGL Qt5::Widgets) | target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::OpenGL Qt5::Widgets Qt5::Multimedia) | ||||||
| target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) | target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) | ||||||
| 
 | 
 | ||||||
| if(UNIX AND NOT APPLE) | if(UNIX AND NOT APPLE) | ||||||
|  |  | ||||||
							
								
								
									
										228
									
								
								src/citra_qt/camera/camera_util.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										228
									
								
								src/citra_qt/camera/camera_util.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,228 @@ | ||||||
|  | // Copyright 2018 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include <cstring> | ||||||
|  | #include <QImage> | ||||||
|  | #include "citra_qt/camera/camera_util.h" | ||||||
|  | #include "common/math_util.h" | ||||||
|  | #include "core/frontend/camera/factory.h" | ||||||
|  | #include "core/frontend/camera/interface.h" | ||||||
|  | 
 | ||||||
|  | namespace CameraUtil { | ||||||
|  | 
 | ||||||
|  | // The following are data tables for RGB -> YUV conversions.
 | ||||||
|  | namespace YuvTable { | ||||||
|  | 
 | ||||||
|  | constexpr std::array<int, 256> Y_R = { | ||||||
|  |     53,  53,  53,  54,  54,  54,  55,  55,  55,  56,  56,  56,  56,  57,  57,  57,  58,  58,  58, | ||||||
|  |     59,  59,  59,  59,  60,  60,  60,  61,  61,  61,  62,  62,  62,  62,  63,  63,  63,  64,  64, | ||||||
|  |     64,  65,  65,  65,  65,  66,  66,  66,  67,  67,  67,  67,  68,  68,  68,  69,  69,  69,  70, | ||||||
|  |     70,  70,  70,  71,  71,  71,  72,  72,  72,  73,  73,  73,  73,  74,  74,  74,  75,  75,  75, | ||||||
|  |     76,  76,  76,  76,  77,  77,  77,  78,  78,  78,  79,  79,  79,  79,  80,  80,  80,  81,  81, | ||||||
|  |     81,  82,  82,  82,  82,  83,  83,  83,  84,  84,  84,  85,  85,  85,  85,  86,  86,  86,  87, | ||||||
|  |     87,  87,  87,  88,  88,  88,  89,  89,  89,  90,  90,  90,  90,  91,  91,  91,  92,  92,  92, | ||||||
|  |     93,  93,  93,  93,  94,  94,  94,  95,  95,  95,  96,  96,  96,  96,  97,  97,  97,  98,  98, | ||||||
|  |     98,  99,  99,  99,  99,  100, 100, 100, 101, 101, 101, 102, 102, 102, 102, 103, 103, 103, 104, | ||||||
|  |     104, 104, 105, 105, 105, 105, 106, 106, 106, 107, 107, 107, 108, 108, 108, 108, 109, 109, 109, | ||||||
|  |     110, 110, 110, 110, 111, 111, 111, 112, 112, 112, 113, 113, 113, 113, 114, 114, 114, 115, 115, | ||||||
|  |     115, 116, 116, 116, 116, 117, 117, 117, 118, 118, 118, 119, 119, 119, 119, 120, 120, 120, 121, | ||||||
|  |     121, 121, 122, 122, 122, 122, 123, 123, 123, 124, 124, 124, 125, 125, 125, 125, 126, 126, 126, | ||||||
|  |     127, 127, 127, 128, 128, 128, 128, 129, 129, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | constexpr std::array<int, 256> Y_G = { | ||||||
|  |     -79, -79, -78, -78, -77, -77, -76, -75, -75, -74, -74, -73, -72, -72, -71, -71, -70, -70, -69, | ||||||
|  |     -68, -68, -67, -67, -66, -65, -65, -64, -64, -63, -62, -62, -61, -61, -60, -60, -59, -58, -58, | ||||||
|  |     -57, -57, -56, -55, -55, -54, -54, -53, -52, -52, -51, -51, -50, -50, -49, -48, -48, -47, -47, | ||||||
|  |     -46, -45, -45, -44, -44, -43, -42, -42, -41, -41, -40, -40, -39, -38, -38, -37, -37, -36, -35, | ||||||
|  |     -35, -34, -34, -33, -33, -32, -31, -31, -30, -30, -29, -28, -28, -27, -27, -26, -25, -25, -24, | ||||||
|  |     -24, -23, -23, -22, -21, -21, -20, -20, -19, -18, -18, -17, -17, -16, -15, -15, -14, -14, -13, | ||||||
|  |     -13, -12, -11, -11, -10, -10, -9,  -8,  -8,  -7,  -7,  -6,  -5,  -5,  -4,  -4,  -3,  -3,  -2, | ||||||
|  |     -1,  -1,  0,   0,   0,   1,   1,   2,   2,   3,   4,   4,   5,   5,   6,   6,   7,   8,   8, | ||||||
|  |     9,   9,   10,  11,  11,  12,  12,  13,  13,  14,  15,  15,  16,  16,  17,  18,  18,  19,  19, | ||||||
|  |     20,  21,  21,  22,  22,  23,  23,  24,  25,  25,  26,  26,  27,  28,  28,  29,  29,  30,  31, | ||||||
|  |     31,  32,  32,  33,  33,  34,  35,  35,  36,  36,  37,  38,  38,  39,  39,  40,  41,  41,  42, | ||||||
|  |     42,  43,  43,  44,  45,  45,  46,  46,  47,  48,  48,  49,  49,  50,  50,  51,  52,  52,  53, | ||||||
|  |     53,  54,  55,  55,  56,  56,  57,  58,  58,  59,  59,  60,  60,  61,  62,  62,  63,  63,  64, | ||||||
|  |     65,  65,  66,  66,  67,  68,  68,  69,  69, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | constexpr std::array<int, 256> Y_B = { | ||||||
|  |     25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, | ||||||
|  |     28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, | ||||||
|  |     31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, | ||||||
|  |     34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, 36, 36, 36, 36, 36, | ||||||
|  |     36, 36, 36, 37, 37, 37, 37, 37, 37, 37, 37, 38, 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, | ||||||
|  |     39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 41, 42, | ||||||
|  |     42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 44, 44, 44, | ||||||
|  |     44, 45, 45, 45, 45, 45, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, | ||||||
|  |     47, 47, 47, 48, 48, 48, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49, 49, 49, 49, 49, 49, 50, 50, 50, | ||||||
|  |     50, 50, 50, 50, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, | ||||||
|  |     53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 54, 54, 54, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static constexpr int Y(int r, int g, int b) { | ||||||
|  |     return Y_R[r] + Y_G[g] + Y_B[b]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | constexpr std::array<int, 256> U_R = { | ||||||
|  |     30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, | ||||||
|  |     34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 36, 36, 36, 36, 37, 37, 37, 37, 37, 37, 38, | ||||||
|  |     38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 42, | ||||||
|  |     42, 42, 42, 42, 42, 43, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 45, 45, 45, 45, 45, 45, 46, 46, | ||||||
|  |     46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49, 49, 49, 50, 50, | ||||||
|  |     50, 50, 50, 50, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, 53, 54, 54, | ||||||
|  |     54, 54, 54, 54, 55, 55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 58, 58, | ||||||
|  |     58, 58, 58, 59, 59, 59, 59, 59, 59, 60, 60, 60, 60, 60, 60, 61, 61, 61, 61, 61, 61, 62, 62, 62, | ||||||
|  |     62, 62, 62, 63, 63, 63, 63, 63, 63, 64, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 65, 66, 66, 66, | ||||||
|  |     66, 66, 66, 67, 67, 67, 67, 67, 67, 68, 68, 68, 68, 68, 68, 69, 69, 69, 69, 69, 69, 70, 70, 70, | ||||||
|  |     70, 70, 70, 71, 71, 71, 71, 71, 72, 72, 72, 72, 72, 72, 73, 73, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | constexpr std::array<int, 256> U_G = { | ||||||
|  |     -45, -44, -44, -44, -43, -43, -43, -42, -42, -42, -41, -41, -41, -40, -40, -40, -39, -39, -39, | ||||||
|  |     -38, -38, -38, -37, -37, -37, -36, -36, -36, -35, -35, -35, -34, -34, -34, -33, -33, -33, -32, | ||||||
|  |     -32, -32, -31, -31, -31, -30, -30, -30, -29, -29, -29, -28, -28, -28, -27, -27, -27, -26, -26, | ||||||
|  |     -26, -25, -25, -25, -24, -24, -24, -23, -23, -23, -22, -22, -22, -21, -21, -21, -20, -20, -20, | ||||||
|  |     -19, -19, -19, -18, -18, -18, -17, -17, -17, -16, -16, -16, -15, -15, -15, -14, -14, -14, -14, | ||||||
|  |     -13, -13, -13, -12, -12, -12, -11, -11, -11, -10, -10, -10, -9,  -9,  -9,  -8,  -8,  -8,  -7, | ||||||
|  |     -7,  -7,  -6,  -6,  -6,  -5,  -5,  -5,  -4,  -4,  -4,  -3,  -3,  -3,  -2,  -2,  -2,  -1,  -1, | ||||||
|  |     -1,  0,   0,   0,   0,   0,   0,   1,   1,   1,   2,   2,   2,   3,   3,   3,   4,   4,   4, | ||||||
|  |     5,   5,   5,   6,   6,   6,   7,   7,   7,   8,   8,   8,   9,   9,   9,   10,  10,  10,  11, | ||||||
|  |     11,  11,  12,  12,  12,  13,  13,  13,  14,  14,  14,  15,  15,  15,  16,  16,  16,  17,  17, | ||||||
|  |     17,  18,  18,  18,  19,  19,  19,  20,  20,  20,  21,  21,  21,  22,  22,  22,  23,  23,  23, | ||||||
|  |     24,  24,  24,  25,  25,  25,  26,  26,  26,  27,  27,  27,  28,  28,  28,  29,  29,  29,  30, | ||||||
|  |     30,  30,  31,  31,  31,  32,  32,  32,  33,  33,  33,  34,  34,  34,  35,  35,  35,  36,  36, | ||||||
|  |     36,  37,  37,  37,  38,  38,  38,  39,  39, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | constexpr std::array<int, 256> U_B = { | ||||||
|  |     113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, | ||||||
|  |     122, 123, 123, 124, 124, 125, 125, 126, 126, 127, 127, 128, 128, 129, 129, 130, 130, 131, 131, | ||||||
|  |     132, 132, 133, 133, 134, 134, 135, 135, 136, 136, 137, 137, 138, 138, 139, 139, 140, 140, 141, | ||||||
|  |     141, 142, 142, 143, 143, 144, 144, 145, 145, 146, 146, 147, 147, 148, 148, 149, 149, 150, 150, | ||||||
|  |     151, 151, 152, 152, 153, 153, 154, 154, 155, 155, 156, 156, 157, 157, 158, 158, 159, 159, 160, | ||||||
|  |     160, 161, 161, 162, 162, 163, 163, 164, 164, 165, 165, 166, 166, 167, 167, 168, 168, 169, 169, | ||||||
|  |     170, 170, 171, 171, 172, 172, 173, 173, 174, 174, 175, 175, 176, 176, 177, 177, 178, 178, 179, | ||||||
|  |     179, 180, 180, 181, 181, 182, 182, 183, 183, 184, 184, 185, 185, 186, 186, 187, 187, 188, 188, | ||||||
|  |     189, 189, 190, 190, 191, 191, 192, 192, 193, 193, 194, 194, 195, 195, 196, 196, 197, 197, 198, | ||||||
|  |     198, 199, 199, 200, 200, 201, 201, 202, 202, 203, 203, 204, 204, 205, 205, 206, 206, 207, 207, | ||||||
|  |     208, 208, 209, 209, 210, 210, 211, 211, 212, 212, 213, 213, 214, 214, 215, 215, 216, 216, 217, | ||||||
|  |     217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224, 225, 225, 226, 226, | ||||||
|  |     227, 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, | ||||||
|  |     236, 237, 237, 238, 238, 239, 239, 240, 240, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static constexpr int U(int r, int g, int b) { | ||||||
|  |     return -U_R[r] - U_G[g] + U_B[b]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | constexpr std::array<int, 256> V_R = { | ||||||
|  |     89,  90,  90,  91,  91,  92,  92,  93,  93,  94,  94,  95,  95,  96,  96,  97,  97,  98,  98, | ||||||
|  |     99,  99,  100, 100, 101, 101, 102, 102, 103, 103, 104, 104, 105, 105, 106, 106, 107, 107, 108, | ||||||
|  |     108, 109, 109, 110, 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, | ||||||
|  |     118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126, 127, | ||||||
|  |     127, 128, 128, 129, 129, 130, 130, 131, 131, 132, 132, 133, 133, 134, 134, 135, 135, 136, 136, | ||||||
|  |     137, 137, 138, 138, 139, 139, 140, 140, 141, 141, 142, 142, 143, 143, 144, 144, 145, 145, 146, | ||||||
|  |     146, 147, 147, 148, 148, 149, 149, 150, 150, 151, 151, 152, 152, 153, 153, 154, 154, 155, 155, | ||||||
|  |     156, 156, 157, 157, 158, 158, 159, 159, 160, 160, 161, 161, 162, 162, 163, 163, 164, 164, 165, | ||||||
|  |     165, 166, 166, 167, 167, 168, 168, 169, 169, 170, 170, 171, 171, 172, 172, 173, 173, 174, 174, | ||||||
|  |     175, 175, 176, 176, 177, 177, 178, 178, 179, 179, 180, 180, 181, 181, 182, 182, 183, 183, 184, | ||||||
|  |     184, 185, 185, 186, 186, 187, 187, 188, 188, 189, 189, 190, 190, 191, 191, 192, 192, 193, 193, | ||||||
|  |     194, 194, 195, 195, 196, 196, 197, 197, 198, 198, 199, 199, 200, 200, 201, 201, 202, 202, 203, | ||||||
|  |     203, 204, 205, 205, 206, 206, 207, 207, 208, 208, 209, 209, 210, 210, 211, 211, 212, 212, 213, | ||||||
|  |     213, 214, 214, 215, 215, 216, 216, 217, 217, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | constexpr std::array<int, 256> V_G = { | ||||||
|  |     -57, -56, -56, -55, -55, -55, -54, -54, -53, -53, -52, -52, -52, -51, -51, -50, -50, -50, -49, | ||||||
|  |     -49, -48, -48, -47, -47, -47, -46, -46, -45, -45, -45, -44, -44, -43, -43, -42, -42, -42, -41, | ||||||
|  |     -41, -40, -40, -39, -39, -39, -38, -38, -37, -37, -37, -36, -36, -35, -35, -34, -34, -34, -33, | ||||||
|  |     -33, -32, -32, -31, -31, -31, -30, -30, -29, -29, -29, -28, -28, -27, -27, -26, -26, -26, -25, | ||||||
|  |     -25, -24, -24, -24, -23, -23, -22, -22, -21, -21, -21, -20, -20, -19, -19, -18, -18, -18, -17, | ||||||
|  |     -17, -16, -16, -16, -15, -15, -14, -14, -13, -13, -13, -12, -12, -11, -11, -10, -10, -10, -9, | ||||||
|  |     -9,  -8,  -8,  -8,  -7,  -7,  -6,  -6,  -5,  -5,  -5,  -4,  -4,  -3,  -3,  -3,  -2,  -2,  -1, | ||||||
|  |     -1,  0,   0,   0,   0,   0,   1,   1,   2,   2,   2,   3,   3,   4,   4,   4,   5,   5,   6, | ||||||
|  |     6,   7,   7,   7,   8,   8,   9,   9,   10,  10,  10,  11,  11,  12,  12,  12,  13,  13,  14, | ||||||
|  |     14,  15,  15,  15,  16,  16,  17,  17,  17,  18,  18,  19,  19,  20,  20,  20,  21,  21,  22, | ||||||
|  |     22,  23,  23,  23,  24,  24,  25,  25,  25,  26,  26,  27,  27,  28,  28,  28,  29,  29,  30, | ||||||
|  |     30,  31,  31,  31,  32,  32,  33,  33,  33,  34,  34,  35,  35,  36,  36,  36,  37,  37,  38, | ||||||
|  |     38,  38,  39,  39,  40,  40,  41,  41,  41,  42,  42,  43,  43,  44,  44,  44,  45,  45,  46, | ||||||
|  |     46,  46,  47,  47,  48,  48,  49,  49,  49, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | constexpr std::array<int, 256> V_B = { | ||||||
|  |     18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, | ||||||
|  |     20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, | ||||||
|  |     22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, | ||||||
|  |     24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, | ||||||
|  |     26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, | ||||||
|  |     28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 30, 30, | ||||||
|  |     30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, | ||||||
|  |     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, | ||||||
|  |     34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, | ||||||
|  |     36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, | ||||||
|  |     38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static constexpr int V(int r, int g, int b) { | ||||||
|  |     return V_R[r] - V_G[g] - V_B[b]; | ||||||
|  | } | ||||||
|  | } // namespace YuvTable
 | ||||||
|  | 
 | ||||||
|  | std::vector<u16> Rgb2Yuv(const QImage& source, int width, int height) { | ||||||
|  |     auto buffer = std::vector<u16>(width * height); | ||||||
|  |     auto dest = buffer.begin(); | ||||||
|  |     bool write = false; | ||||||
|  |     int py, pu, pv; | ||||||
|  |     for (int y = 0; y < height; ++y) { | ||||||
|  |         for (int x = 0; x < width; ++x) { | ||||||
|  |             QRgb rgb = source.pixel(x, y); | ||||||
|  |             int r = qRed(rgb); | ||||||
|  |             int g = qGreen(rgb); | ||||||
|  |             int b = qBlue(rgb); | ||||||
|  | 
 | ||||||
|  |             // The following transformation is a reverse of the one in Y2R using ITU_Rec601
 | ||||||
|  |             int y = YuvTable::Y(r, g, b); | ||||||
|  |             int u = YuvTable::U(r, g, b); | ||||||
|  |             int v = YuvTable::V(r, g, b); | ||||||
|  | 
 | ||||||
|  |             if (write) { | ||||||
|  |                 pu = (pu + u) / 2; | ||||||
|  |                 pv = (pv + v) / 2; | ||||||
|  |                 using MathUtil::Clamp; | ||||||
|  |                 *(dest++) = static_cast<u16>(Clamp(py, 0, 0xFF) | (Clamp(pu, 0, 0xFF) << 8)); | ||||||
|  |                 *(dest++) = static_cast<u16>(Clamp(y, 0, 0xFF) | (Clamp(pv, 0, 0xFF) << 8)); | ||||||
|  |             } else { | ||||||
|  |                 py = y; | ||||||
|  |                 pu = u; | ||||||
|  |                 pv = v; | ||||||
|  |             } | ||||||
|  |             write = !write; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return buffer; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::vector<u16> ProcessImage(const QImage& image, int width, int height, bool output_rgb = false, | ||||||
|  |                               bool flip_horizontal = false, bool flip_vertical = false) { | ||||||
|  |     std::vector<u16> buffer(width * height); | ||||||
|  |     if (image.isNull()) { | ||||||
|  |         return buffer; | ||||||
|  |     } | ||||||
|  |     QImage scaled = | ||||||
|  |         image.scaled(width, height, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); | ||||||
|  |     QImage transformed = | ||||||
|  |         scaled.copy((scaled.width() - width) / 2, (scaled.height() - height) / 2, width, height) | ||||||
|  |             .mirrored(flip_horizontal, flip_vertical); | ||||||
|  |     if (output_rgb) { | ||||||
|  |         QImage converted = transformed.convertToFormat(QImage::Format_RGB16); | ||||||
|  |         std::memcpy(buffer.data(), converted.bits(), width * height * sizeof(u16)); | ||||||
|  |     } else { | ||||||
|  |         return CameraUtil::Rgb2Yuv(transformed, width, height); | ||||||
|  |     } | ||||||
|  |     return buffer; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace CameraUtil
 | ||||||
							
								
								
									
										21
									
								
								src/citra_qt/camera/camera_util.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/citra_qt/camera/camera_util.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | ||||||
|  | // Copyright 2018 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <vector> | ||||||
|  | #include "common/common_types.h" | ||||||
|  | 
 | ||||||
|  | class QImage; | ||||||
|  | 
 | ||||||
|  | namespace CameraUtil { | ||||||
|  | 
 | ||||||
|  | /// Converts QImage to a yuv formatted std::vector
 | ||||||
|  | std::vector<u16> Rgb2Yuv(const QImage& source, int width, int height); | ||||||
|  | 
 | ||||||
|  | /// Processes the QImage (resizing, flipping ...) and converts it to a std::vector
 | ||||||
|  | std::vector<u16> ProcessImage(const QImage& source, int width, int height, bool output_rgb, | ||||||
|  |                               bool flip_horizontal, bool flip_vertical); | ||||||
|  | 
 | ||||||
|  | } // namespace CameraUtil
 | ||||||
							
								
								
									
										24
									
								
								src/citra_qt/camera/qt_camera_factory.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/citra_qt/camera/qt_camera_factory.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,24 @@ | ||||||
|  | // Copyright 2018 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include <QMessageBox> | ||||||
|  | #include "citra_qt/camera/qt_camera_factory.h" | ||||||
|  | 
 | ||||||
|  | namespace Camera { | ||||||
|  | 
 | ||||||
|  | std::unique_ptr<CameraInterface> QtCameraFactory::CreatePreview(const std::string& config, | ||||||
|  |                                                                 int width, int height) const { | ||||||
|  |     std::unique_ptr<CameraInterface> camera = Create(config); | ||||||
|  | 
 | ||||||
|  |     if (camera->IsPreviewAvailable()) { | ||||||
|  |         return camera; | ||||||
|  |     } | ||||||
|  |     QMessageBox::critical(nullptr, QObject::tr("Error"), | ||||||
|  |                           QObject::tr("Couldn't load ") + | ||||||
|  |                               (config.empty() ? QObject::tr("the camera") : "") + | ||||||
|  |                               QString::fromStdString(config)); | ||||||
|  |     return nullptr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace Camera
 | ||||||
							
								
								
									
										18
									
								
								src/citra_qt/camera/qt_camera_factory.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/citra_qt/camera/qt_camera_factory.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | // Copyright 2018 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | #include "core/frontend/camera/factory.h" | ||||||
|  | 
 | ||||||
|  | namespace Camera { | ||||||
|  | 
 | ||||||
|  | // Base class for camera factories of citra_qt
 | ||||||
|  | class QtCameraFactory : public CameraFactory { | ||||||
|  |     std::unique_ptr<CameraInterface> CreatePreview(const std::string& config, int width, | ||||||
|  |                                                    int height) const override; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace Camera
 | ||||||
							
								
								
									
										224
									
								
								src/citra_qt/camera/qt_multimedia_camera.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								src/citra_qt/camera/qt_multimedia_camera.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,224 @@ | ||||||
|  | // Copyright 2018 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include <QCamera> | ||||||
|  | #include <QCameraInfo> | ||||||
|  | #include <QImageReader> | ||||||
|  | #include <QMessageBox> | ||||||
|  | #include <QThread> | ||||||
|  | #include "citra_qt/camera/qt_multimedia_camera.h" | ||||||
|  | #include "citra_qt/main.h" | ||||||
|  | 
 | ||||||
|  | namespace Camera { | ||||||
|  | 
 | ||||||
|  | QList<QVideoFrame::PixelFormat> QtCameraSurface::supportedPixelFormats( | ||||||
|  |     QAbstractVideoBuffer::HandleType handleType) const { | ||||||
|  |     Q_UNUSED(handleType); | ||||||
|  |     return QList<QVideoFrame::PixelFormat>() | ||||||
|  |            << QVideoFrame::Format_ARGB32 << QVideoFrame::Format_ARGB32_Premultiplied | ||||||
|  |            << QVideoFrame::Format_RGB32 << QVideoFrame::Format_RGB24 << QVideoFrame::Format_RGB565 | ||||||
|  |            << QVideoFrame::Format_RGB555 << QVideoFrame::Format_ARGB8565_Premultiplied | ||||||
|  |            << QVideoFrame::Format_BGRA32 << QVideoFrame::Format_BGRA32_Premultiplied | ||||||
|  |            << QVideoFrame::Format_BGR32 << QVideoFrame::Format_BGR24 << QVideoFrame::Format_BGR565 | ||||||
|  |            << QVideoFrame::Format_BGR555 << QVideoFrame::Format_BGRA5658_Premultiplied | ||||||
|  |            << QVideoFrame::Format_AYUV444 << QVideoFrame::Format_AYUV444_Premultiplied | ||||||
|  |            << QVideoFrame::Format_YUV444 << QVideoFrame::Format_YUV420P << QVideoFrame::Format_YV12 | ||||||
|  |            << QVideoFrame::Format_UYVY << QVideoFrame::Format_YUYV << QVideoFrame::Format_NV12 | ||||||
|  |            << QVideoFrame::Format_NV21 << QVideoFrame::Format_IMC1 << QVideoFrame::Format_IMC2 | ||||||
|  |            << QVideoFrame::Format_IMC3 << QVideoFrame::Format_IMC4 << QVideoFrame::Format_Y8 | ||||||
|  |            << QVideoFrame::Format_Y16 << QVideoFrame::Format_Jpeg << QVideoFrame::Format_CameraRaw | ||||||
|  |            << QVideoFrame::Format_AdobeDng; // Supporting all the formats
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool QtCameraSurface::present(const QVideoFrame& frame) { | ||||||
|  |     if (!frame.isValid()) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     QVideoFrame cloneFrame(frame); | ||||||
|  |     cloneFrame.map(QAbstractVideoBuffer::ReadOnly); | ||||||
|  |     const QImage image(cloneFrame.bits(), cloneFrame.width(), cloneFrame.height(), | ||||||
|  |                        QVideoFrame::imageFormatFromPixelFormat(cloneFrame.pixelFormat())); | ||||||
|  |     QMutexLocker locker(&mutex); | ||||||
|  |     current_frame = image.mirrored(true, true); | ||||||
|  |     locker.unlock(); | ||||||
|  |     cloneFrame.unmap(); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | QtMultimediaCamera::QtMultimediaCamera(const std::string& camera_name) | ||||||
|  |     : handler(QtMultimediaCameraHandler::GetHandler()) { | ||||||
|  |     if (handler->thread() == QThread::currentThread()) { | ||||||
|  |         handler->CreateCamera(camera_name); | ||||||
|  |     } else { | ||||||
|  |         QMetaObject::invokeMethod(handler.get(), "CreateCamera", Qt::BlockingQueuedConnection, | ||||||
|  |                                   Q_ARG(const std::string&, camera_name)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | QtMultimediaCamera::~QtMultimediaCamera() { | ||||||
|  |     handler->StopCamera(); | ||||||
|  |     QtMultimediaCameraHandler::ReleaseHandler(handler); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QtMultimediaCamera::StartCapture() { | ||||||
|  |     if (handler->thread() == QThread::currentThread()) { | ||||||
|  |         handler->StartCamera(); | ||||||
|  |     } else { | ||||||
|  |         QMetaObject::invokeMethod(handler.get(), "StartCamera", Qt::BlockingQueuedConnection); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QtMultimediaCamera::StopCapture() { | ||||||
|  |     handler->StopCamera(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QtMultimediaCamera::SetFormat(Service::CAM::OutputFormat output_format) { | ||||||
|  |     output_rgb = output_format == Service::CAM::OutputFormat::RGB565; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QtMultimediaCamera::SetFrameRate(Service::CAM::FrameRate frame_rate) { | ||||||
|  |     const std::array<QCamera::FrameRateRange, 13> FrameRateList = { | ||||||
|  |         /* Rate_15 */ QCamera::FrameRateRange(15, 15), | ||||||
|  |         /* Rate_15_To_5 */ QCamera::FrameRateRange(5, 15), | ||||||
|  |         /* Rate_15_To_2 */ QCamera::FrameRateRange(2, 15), | ||||||
|  |         /* Rate_10 */ QCamera::FrameRateRange(10, 10), | ||||||
|  |         /* Rate_8_5 */ QCamera::FrameRateRange(8.5, 8.5), | ||||||
|  |         /* Rate_5 */ QCamera::FrameRateRange(5, 5), | ||||||
|  |         /* Rate_20 */ QCamera::FrameRateRange(20, 20), | ||||||
|  |         /* Rate_20_To_5 */ QCamera::FrameRateRange(5, 20), | ||||||
|  |         /* Rate_30 */ QCamera::FrameRateRange(30, 30), | ||||||
|  |         /* Rate_30_To_5 */ QCamera::FrameRateRange(5, 30), | ||||||
|  |         /* Rate_15_To_10 */ QCamera::FrameRateRange(10, 15), | ||||||
|  |         /* Rate_20_To_10 */ QCamera::FrameRateRange(10, 20), | ||||||
|  |         /* Rate_30_To_10 */ QCamera::FrameRateRange(10, 30), | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     auto framerate = FrameRateList[static_cast<int>(frame_rate)]; | ||||||
|  | 
 | ||||||
|  |     handler->settings.setMinimumFrameRate(framerate.minimumFrameRate); | ||||||
|  |     handler->settings.setMinimumFrameRate(framerate.maximumFrameRate); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QtMultimediaCamera::SetResolution(const Service::CAM::Resolution& resolution) { | ||||||
|  |     width = resolution.width; | ||||||
|  |     height = resolution.height; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QtMultimediaCamera::SetFlip(Service::CAM::Flip flip) { | ||||||
|  |     using namespace Service::CAM; | ||||||
|  |     flip_horizontal = (flip == Flip::Horizontal) || (flip == Flip::Reverse); | ||||||
|  |     flip_vertical = (flip == Flip::Vertical) || (flip == Flip::Reverse); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QtMultimediaCamera::SetEffect(Service::CAM::Effect effect) { | ||||||
|  |     if (effect != Service::CAM::Effect::None) { | ||||||
|  |         NGLOG_ERROR(Service_CAM, "Unimplemented effect {}", static_cast<int>(effect)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::vector<u16> QtMultimediaCamera::ReceiveFrame() { | ||||||
|  |     QMutexLocker locker(&handler->camera_surface.mutex); | ||||||
|  |     return CameraUtil::ProcessImage(handler->camera_surface.current_frame, width, height, | ||||||
|  |                                     output_rgb, flip_horizontal, flip_vertical); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool QtMultimediaCamera::IsPreviewAvailable() { | ||||||
|  |     return handler->CameraAvailable(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::unique_ptr<CameraInterface> QtMultimediaCameraFactory::Create( | ||||||
|  |     const std::string& config) const { | ||||||
|  |     return std::make_unique<QtMultimediaCamera>(config); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::array<std::shared_ptr<QtMultimediaCameraHandler>, 3> QtMultimediaCameraHandler::handlers; | ||||||
|  | 
 | ||||||
|  | std::array<bool, 3> QtMultimediaCameraHandler::status; | ||||||
|  | 
 | ||||||
|  | void QtMultimediaCameraHandler::Init() { | ||||||
|  |     for (auto& handler : handlers) { | ||||||
|  |         handler = std::make_shared<QtMultimediaCameraHandler>(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::shared_ptr<QtMultimediaCameraHandler> QtMultimediaCameraHandler::GetHandler() { | ||||||
|  |     for (int i = 0; i < handlers.size(); i++) { | ||||||
|  |         if (!status[i]) { | ||||||
|  |             NGLOG_INFO(Service_CAM, "Successfully got handler {}", i); | ||||||
|  |             status[i] = true; | ||||||
|  |             return handlers[i]; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     NGLOG_CRITICAL(Service_CAM, "All handlers taken up"); | ||||||
|  |     return nullptr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QtMultimediaCameraHandler::ReleaseHandler( | ||||||
|  |     const std::shared_ptr<Camera::QtMultimediaCameraHandler>& handler) { | ||||||
|  |     for (int i = 0; i < handlers.size(); i++) { | ||||||
|  |         if (handlers[i] == handler) { | ||||||
|  |             NGLOG_INFO(Service_CAM, "Successfully released handler {}", i); | ||||||
|  |             status[i] = false; | ||||||
|  |             handlers[i]->started = false; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QtMultimediaCameraHandler::CreateCamera(const std::string& camera_name) { | ||||||
|  |     QList<QCameraInfo> cameras = QCameraInfo::availableCameras(); | ||||||
|  |     for (const QCameraInfo& cameraInfo : cameras) { | ||||||
|  |         if (cameraInfo.deviceName().toStdString() == camera_name) | ||||||
|  |             camera = std::make_unique<QCamera>(cameraInfo); | ||||||
|  |     } | ||||||
|  |     if (!camera) { // no cameras found, using default camera
 | ||||||
|  |         camera = std::make_unique<QCamera>(); | ||||||
|  |     } | ||||||
|  |     settings.setMinimumFrameRate(30); | ||||||
|  |     settings.setMaximumFrameRate(30); | ||||||
|  |     camera->setViewfinder(&camera_surface); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QtMultimediaCameraHandler::StopCamera() { | ||||||
|  |     camera->stop(); | ||||||
|  |     started = false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QtMultimediaCameraHandler::StartCamera() { | ||||||
|  |     camera->setViewfinderSettings(settings); | ||||||
|  |     camera->start(); | ||||||
|  |     started = true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool QtMultimediaCameraHandler::CameraAvailable() const { | ||||||
|  |     return camera && camera->isAvailable(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QtMultimediaCameraHandler::StopCameras() { | ||||||
|  |     NGLOG_INFO(Service_CAM, "Stopping all cameras"); | ||||||
|  |     for (auto& handler : handlers) { | ||||||
|  |         if (handler && handler->started) { | ||||||
|  |             handler->StopCamera(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QtMultimediaCameraHandler::ResumeCameras() { | ||||||
|  |     for (auto& handler : handlers) { | ||||||
|  |         if (handler && handler->started) { | ||||||
|  |             handler->StartCamera(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QtMultimediaCameraHandler::ReleaseHandlers() { | ||||||
|  |     StopCameras(); | ||||||
|  |     NGLOG_INFO(Service_CAM, "Releasing all handlers"); | ||||||
|  |     for (int i = 0; i < handlers.size(); i++) { | ||||||
|  |         status[i] = false; | ||||||
|  |         handlers[i]->started = false; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace Camera
 | ||||||
							
								
								
									
										105
									
								
								src/citra_qt/camera/qt_multimedia_camera.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								src/citra_qt/camera/qt_multimedia_camera.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,105 @@ | ||||||
|  | // Copyright 2018 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <array> | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | #include <QAbstractVideoSurface> | ||||||
|  | #include <QCamera> | ||||||
|  | #include <QCameraViewfinderSettings> | ||||||
|  | #include <QImage> | ||||||
|  | #include <QMutex> | ||||||
|  | #include "citra_qt/camera/camera_util.h" | ||||||
|  | #include "citra_qt/camera/qt_camera_factory.h" | ||||||
|  | #include "core/frontend/camera/interface.h" | ||||||
|  | 
 | ||||||
|  | class GMainWindow; | ||||||
|  | 
 | ||||||
|  | namespace Camera { | ||||||
|  | 
 | ||||||
|  | class QtCameraSurface final : public QAbstractVideoSurface { | ||||||
|  | public: | ||||||
|  |     QList<QVideoFrame::PixelFormat> supportedPixelFormats( | ||||||
|  |         QAbstractVideoBuffer::HandleType) const override; | ||||||
|  |     bool present(const QVideoFrame&) override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     QMutex mutex; | ||||||
|  |     QImage current_frame; | ||||||
|  | 
 | ||||||
|  |     friend class QtMultimediaCamera; // For access to current_frame
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class QtMultimediaCameraHandler; | ||||||
|  | 
 | ||||||
|  | /// This class is only an interface. It just calls QtMultimediaCameraHandler.
 | ||||||
|  | class QtMultimediaCamera final : public CameraInterface { | ||||||
|  | public: | ||||||
|  |     QtMultimediaCamera(const std::string& camera_name); | ||||||
|  |     ~QtMultimediaCamera(); | ||||||
|  |     void StartCapture() override; | ||||||
|  |     void StopCapture() override; | ||||||
|  |     void SetResolution(const Service::CAM::Resolution&) override; | ||||||
|  |     void SetFlip(Service::CAM::Flip) override; | ||||||
|  |     void SetEffect(Service::CAM::Effect) override; | ||||||
|  |     void SetFormat(Service::CAM::OutputFormat) override; | ||||||
|  |     void SetFrameRate(Service::CAM::FrameRate frame_rate) override; | ||||||
|  |     std::vector<u16> ReceiveFrame() override; | ||||||
|  |     bool IsPreviewAvailable() override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     std::shared_ptr<QtMultimediaCameraHandler> handler; | ||||||
|  |     int width, height; | ||||||
|  |     bool output_rgb; | ||||||
|  |     bool flip_horizontal, flip_vertical; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class QtMultimediaCameraFactory final : public QtCameraFactory { | ||||||
|  | public: | ||||||
|  |     std::unique_ptr<CameraInterface> Create(const std::string& config) const override; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class QtMultimediaCameraHandler final : public QObject { | ||||||
|  |     Q_OBJECT | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     /// Creates the global handler. Must be called in UI thread.
 | ||||||
|  |     static void Init(); | ||||||
|  |     static std::shared_ptr<QtMultimediaCameraHandler> GetHandler(); | ||||||
|  |     static void ReleaseHandler(const std::shared_ptr<QtMultimediaCameraHandler>& handler); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Creates the camera. | ||||||
|  |      * Note: This function must be called via QMetaObject::invokeMethod in UI thread. | ||||||
|  |      */ | ||||||
|  |     Q_INVOKABLE void CreateCamera(const std::string& camera_name); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Starts the camera. | ||||||
|  |      * Note: This function must be called via QMetaObject::invokeMethod in UI thread when | ||||||
|  |      *       starting the camera for the first time. 'Resume' calls can be in other threads. | ||||||
|  |      */ | ||||||
|  |     Q_INVOKABLE void StartCamera(); | ||||||
|  | 
 | ||||||
|  |     void StopCamera(); | ||||||
|  |     bool CameraAvailable() const; | ||||||
|  |     static void StopCameras(); | ||||||
|  |     static void ResumeCameras(); | ||||||
|  |     static void ReleaseHandlers(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     std::unique_ptr<QCamera> camera; | ||||||
|  |     QtCameraSurface camera_surface{}; | ||||||
|  |     QCameraViewfinderSettings settings; | ||||||
|  |     bool started = false; | ||||||
|  | 
 | ||||||
|  |     static std::array<std::shared_ptr<QtMultimediaCameraHandler>, 3> handlers; | ||||||
|  |     static std::array<bool, 3> status; | ||||||
|  | 
 | ||||||
|  |     friend class QtMultimediaCamera; // For access to camera_surface (and camera)
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace Camera
 | ||||||
							
								
								
									
										73
									
								
								src/citra_qt/camera/still_image_camera.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/citra_qt/camera/still_image_camera.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,73 @@ | ||||||
|  | // Copyright 2018 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include <QFileDialog> | ||||||
|  | #include <QImageReader> | ||||||
|  | #include <QMessageBox> | ||||||
|  | #include "citra_qt/camera/still_image_camera.h" | ||||||
|  | 
 | ||||||
|  | namespace Camera { | ||||||
|  | 
 | ||||||
|  | StillImageCamera::StillImageCamera(QImage image_) : image(std::move(image_)) {} | ||||||
|  | 
 | ||||||
|  | void StillImageCamera::StartCapture() {} | ||||||
|  | 
 | ||||||
|  | void StillImageCamera::StopCapture() {} | ||||||
|  | 
 | ||||||
|  | void StillImageCamera::SetFormat(Service::CAM::OutputFormat output_format) { | ||||||
|  |     output_rgb = output_format == Service::CAM::OutputFormat::RGB565; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void StillImageCamera::SetResolution(const Service::CAM::Resolution& resolution) { | ||||||
|  |     width = resolution.width; | ||||||
|  |     height = resolution.height; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void StillImageCamera::SetFlip(Service::CAM::Flip flip) { | ||||||
|  |     using namespace Service::CAM; | ||||||
|  |     flip_horizontal = (flip == Flip::Horizontal) || (flip == Flip::Reverse); | ||||||
|  |     flip_vertical = (flip == Flip::Vertical) || (flip == Flip::Reverse); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void StillImageCamera::SetEffect(Service::CAM::Effect effect) { | ||||||
|  |     if (effect != Service::CAM::Effect::None) { | ||||||
|  |         NGLOG_ERROR(Service_CAM, "Unimplemented effect {}", static_cast<int>(effect)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::vector<u16> StillImageCamera::ReceiveFrame() { | ||||||
|  |     return CameraUtil::ProcessImage(image, width, height, output_rgb, flip_horizontal, | ||||||
|  |                                     flip_vertical); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool StillImageCamera::IsPreviewAvailable() { | ||||||
|  |     return !image.isNull(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const std::string StillImageCameraFactory::getFilePath() { | ||||||
|  |     QList<QByteArray> types = QImageReader::supportedImageFormats(); | ||||||
|  |     QList<QString> temp_filters; | ||||||
|  |     for (QByteArray type : types) { | ||||||
|  |         temp_filters << QString("*." + QString(type)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     QString filter = QObject::tr("Supported image files (") + temp_filters.join(" ") + ")"; | ||||||
|  | 
 | ||||||
|  |     return QFileDialog::getOpenFileName(nullptr, QObject::tr("Open File"), ".", filter) | ||||||
|  |         .toStdString(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::unique_ptr<CameraInterface> StillImageCameraFactory::Create(const std::string& config) const { | ||||||
|  |     std::string real_config = config; | ||||||
|  |     if (config.empty()) { | ||||||
|  |         real_config = getFilePath(); | ||||||
|  |     } | ||||||
|  |     QImage image(QString::fromStdString(real_config)); | ||||||
|  |     if (image.isNull()) { | ||||||
|  |         NGLOG_ERROR(Service_CAM, "Couldn't load image \"{}\"", real_config.c_str()); | ||||||
|  |     } | ||||||
|  |     return std::make_unique<StillImageCamera>(image); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace Camera
 | ||||||
							
								
								
									
										43
									
								
								src/citra_qt/camera/still_image_camera.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/citra_qt/camera/still_image_camera.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,43 @@ | ||||||
|  | // Copyright 2018 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <vector> | ||||||
|  | #include <QImage> | ||||||
|  | #include "citra_qt/camera/camera_util.h" | ||||||
|  | #include "citra_qt/camera/qt_camera_factory.h" | ||||||
|  | #include "core/frontend/camera/interface.h" | ||||||
|  | 
 | ||||||
|  | namespace Camera { | ||||||
|  | 
 | ||||||
|  | class StillImageCamera final : public CameraInterface { | ||||||
|  | public: | ||||||
|  |     StillImageCamera(QImage image); | ||||||
|  |     void StartCapture() override; | ||||||
|  |     void StopCapture() override; | ||||||
|  |     void SetResolution(const Service::CAM::Resolution&) override; | ||||||
|  |     void SetFlip(Service::CAM::Flip) override; | ||||||
|  |     void SetEffect(Service::CAM::Effect) override; | ||||||
|  |     void SetFormat(Service::CAM::OutputFormat) override; | ||||||
|  |     void SetFrameRate(Service::CAM::FrameRate frame_rate) override {} | ||||||
|  |     std::vector<u16> ReceiveFrame() override; | ||||||
|  |     bool IsPreviewAvailable() override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     QImage image; | ||||||
|  |     int width, height; | ||||||
|  |     bool output_rgb; | ||||||
|  |     bool flip_horizontal, flip_vertical; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class StillImageCameraFactory final : public QtCameraFactory { | ||||||
|  | public: | ||||||
|  |     std::unique_ptr<CameraInterface> Create(const std::string& config) const override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     static const std::string getFilePath(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace Camera
 | ||||||
|  | @ -43,6 +43,11 @@ | ||||||
|       <attribute name="title"> |       <attribute name="title"> | ||||||
|        <string>Audio</string> |        <string>Audio</string> | ||||||
|       </attribute> |       </attribute> | ||||||
|  |      </widget> | ||||||
|  |       <widget class="ConfigureCamera" name="cameraTab"> | ||||||
|  |       <attribute name="title"> | ||||||
|  |        <string>Camera</string> | ||||||
|  |       </attribute> | ||||||
|      </widget> |      </widget> | ||||||
|      <widget class="ConfigureDebug" name="debugTab"> |      <widget class="ConfigureDebug" name="debugTab"> | ||||||
|       <attribute name="title"> |       <attribute name="title"> | ||||||
|  | @ -83,6 +88,12 @@ | ||||||
|    <extends>QWidget</extends> |    <extends>QWidget</extends> | ||||||
|    <header>configuration/configure_audio.h</header> |    <header>configuration/configure_audio.h</header> | ||||||
|    <container>1</container> |    <container>1</container> | ||||||
|  |   </customwidget> | ||||||
|  |    <customwidget> | ||||||
|  |    <class>ConfigureCamera</class> | ||||||
|  |    <extends>QWidget</extends> | ||||||
|  |    <header>configuration/configure_camera.h</header> | ||||||
|  |    <container>1</container> | ||||||
|   </customwidget> |   </customwidget> | ||||||
|   <customwidget> |   <customwidget> | ||||||
|    <class>ConfigureDebug</class> |    <class>ConfigureDebug</class> | ||||||
|  |  | ||||||
							
								
								
									
										324
									
								
								src/citra_qt/configuration/configure_camera.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										324
									
								
								src/citra_qt/configuration/configure_camera.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,324 @@ | ||||||
|  | // Copyright 2018 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include <memory> | ||||||
|  | #include <QCameraInfo> | ||||||
|  | #include <QDirIterator> | ||||||
|  | #include <QFileDialog> | ||||||
|  | #include <QImageReader> | ||||||
|  | #include <QMessageBox> | ||||||
|  | #include <QWidget> | ||||||
|  | #include "citra_qt/configuration/configure_camera.h" | ||||||
|  | #include "citra_qt/ui_settings.h" | ||||||
|  | #include "core/core.h" | ||||||
|  | #include "core/settings.h" | ||||||
|  | #include "ui_configure_camera.h" | ||||||
|  | 
 | ||||||
|  | const std::array<std::string, 3> ConfigureCamera::Implementations = { | ||||||
|  |     "blank", /* Blank */ | ||||||
|  |     "image", /* Image */ | ||||||
|  |     "qt"     /* System Camera */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | ConfigureCamera::ConfigureCamera(QWidget* parent) | ||||||
|  |     : QWidget(parent), ui(std::make_unique<Ui::ConfigureCamera>()) { | ||||||
|  |     ui->setupUi(this); | ||||||
|  |     // Load settings
 | ||||||
|  |     camera_name = Settings::values.camera_name; | ||||||
|  |     camera_config = Settings::values.camera_config; | ||||||
|  |     for (auto&& item : camera_name) { | ||||||
|  |         if (item == "opencv") { | ||||||
|  |             QMessageBox::critical(this, tr("Error"), | ||||||
|  |                                   tr("Sorry, Citra has removed support for OpenCV cameras.\n\nYour " | ||||||
|  |                                      "existing OpenCV cameras have been replaced with Blank.")); | ||||||
|  |             item = "blank"; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     QList<QCameraInfo> cameras = QCameraInfo::availableCameras(); | ||||||
|  |     for (const QCameraInfo& cameraInfo : cameras) { | ||||||
|  |         ui->system_camera->addItem(cameraInfo.deviceName()); | ||||||
|  |     } | ||||||
|  |     updateCameraMode(); | ||||||
|  |     setConfiguration(); | ||||||
|  |     connectEvents(); | ||||||
|  |     ui->preview_box->setHidden(true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ConfigureCamera::~ConfigureCamera() { | ||||||
|  |     stopPreviewing(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ConfigureCamera::connectEvents() { | ||||||
|  |     connect(ui->image_source, | ||||||
|  |             static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this] { | ||||||
|  |                 stopPreviewing(); | ||||||
|  |                 updateImageSourceUI(); | ||||||
|  |             }); | ||||||
|  |     connect(ui->camera_selection, | ||||||
|  |             static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this] { | ||||||
|  |                 stopPreviewing(); | ||||||
|  |                 if (getCameraSelection() != current_selected) { | ||||||
|  |                     recordConfig(); | ||||||
|  |                 } | ||||||
|  |                 if (ui->camera_selection->currentIndex() == 1) { | ||||||
|  |                     ui->camera_mode->setCurrentIndex(1); // Double
 | ||||||
|  |                     if (camera_name[0] == camera_name[2] && camera_config[0] == camera_config[2]) { | ||||||
|  |                         ui->camera_mode->setCurrentIndex(0); // Single
 | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 updateCameraMode(); | ||||||
|  |                 setConfiguration(); | ||||||
|  |             }); | ||||||
|  |     connect(ui->camera_mode, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), | ||||||
|  |             this, [this] { | ||||||
|  |                 stopPreviewing(); | ||||||
|  |                 ui->camera_position_label->setVisible(ui->camera_mode->currentIndex() == 1); | ||||||
|  |                 ui->camera_position->setVisible(ui->camera_mode->currentIndex() == 1); | ||||||
|  |                 current_selected = getCameraSelection(); | ||||||
|  |             }); | ||||||
|  |     connect(ui->camera_position, | ||||||
|  |             static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this] { | ||||||
|  |                 stopPreviewing(); | ||||||
|  |                 if (getCameraSelection() != current_selected) { | ||||||
|  |                     recordConfig(); | ||||||
|  |                 } | ||||||
|  |                 setConfiguration(); | ||||||
|  |             }); | ||||||
|  |     connect(ui->toolButton, &QToolButton::clicked, this, &ConfigureCamera::onToolButtonClicked); | ||||||
|  |     connect(ui->preview_button, &QPushButton::clicked, this, [=] { startPreviewing(); }); | ||||||
|  |     connect(ui->prompt_before_load, &QCheckBox::stateChanged, this, [this](int state) { | ||||||
|  |         ui->camera_file->setDisabled(state == Qt::Checked); | ||||||
|  |         ui->toolButton->setDisabled(state == Qt::Checked); | ||||||
|  |         if (state == Qt::Checked) { | ||||||
|  |             ui->camera_file->setText(""); | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  |     connect(ui->camera_file, &QLineEdit::textChanged, this, [=] { stopPreviewing(); }); | ||||||
|  |     connect(ui->system_camera, | ||||||
|  |             static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, | ||||||
|  |             [=] { stopPreviewing(); }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ConfigureCamera::updateCameraMode() { | ||||||
|  |     CameraPosition pos = getCameraSelection(); | ||||||
|  |     // Set the visibility of the camera mode selection widgets
 | ||||||
|  |     if (pos == CameraPosition::RearBoth) { | ||||||
|  |         ui->camera_position->setHidden(true); | ||||||
|  |         ui->camera_position_label->setHidden(true); | ||||||
|  |         ui->camera_mode->setHidden(false); | ||||||
|  |         ui->camera_mode_label->setHidden(false); | ||||||
|  |     } else { | ||||||
|  |         ui->camera_position->setHidden(pos == CameraPosition::Front); | ||||||
|  |         ui->camera_position_label->setHidden(pos == CameraPosition::Front); | ||||||
|  |         ui->camera_mode->setHidden(pos == CameraPosition::Front); | ||||||
|  |         ui->camera_mode_label->setHidden(pos == CameraPosition::Front); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ConfigureCamera::updateImageSourceUI() { | ||||||
|  |     int image_source = ui->image_source->currentIndex(); | ||||||
|  |     switch (image_source) { | ||||||
|  |     case 0: /* blank */ | ||||||
|  |     case 2: /* system camera */ | ||||||
|  |         ui->prompt_before_load->setHidden(true); | ||||||
|  |         ui->prompt_before_load->setChecked(false); | ||||||
|  |         ui->camera_file_label->setHidden(true); | ||||||
|  |         ui->camera_file->setHidden(true); | ||||||
|  |         ui->camera_file->setText(""); | ||||||
|  |         ui->toolButton->setHidden(true); | ||||||
|  |         break; | ||||||
|  |     case 1: /* still image */ | ||||||
|  |         ui->prompt_before_load->setHidden(false); | ||||||
|  |         ui->camera_file_label->setHidden(false); | ||||||
|  |         ui->camera_file->setHidden(false); | ||||||
|  |         ui->toolButton->setHidden(false); | ||||||
|  |         if (camera_config[getSelectedCameraIndex()].empty()) { | ||||||
|  |             ui->prompt_before_load->setChecked(true); | ||||||
|  |             ui->camera_file->setDisabled(true); | ||||||
|  |             ui->toolButton->setDisabled(true); | ||||||
|  |             ui->camera_file->setText(""); | ||||||
|  |         } else { | ||||||
|  |             ui->camera_file->setDisabled(false); | ||||||
|  |             ui->toolButton->setDisabled(false); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         NGLOG_ERROR(Service_CAM, "Unknown image source {}", image_source); | ||||||
|  |     } | ||||||
|  |     ui->system_camera_label->setHidden(image_source != 2); | ||||||
|  |     ui->system_camera->setHidden(image_source != 2); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ConfigureCamera::recordConfig() { | ||||||
|  |     std::string implementation = Implementations[ui->image_source->currentIndex()]; | ||||||
|  |     int image_source = ui->image_source->currentIndex(); | ||||||
|  |     std::string config; | ||||||
|  |     if (image_source == 2) { /* system camera */ | ||||||
|  |         if (ui->system_camera->currentIndex() == 0) { | ||||||
|  |             config = ""; | ||||||
|  |         } else { | ||||||
|  |             config = ui->system_camera->currentText().toStdString(); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         config = ui->camera_file->text().toStdString(); | ||||||
|  |     } | ||||||
|  |     if (current_selected == CameraPosition::RearBoth) { | ||||||
|  |         camera_name[0] = camera_name[2] = implementation; | ||||||
|  |         camera_config[0] = camera_config[2] = config; | ||||||
|  |     } else if (current_selected != CameraPosition::Null) { | ||||||
|  |         int index = static_cast<int>(current_selected); | ||||||
|  |         camera_name[index] = implementation; | ||||||
|  |         camera_config[index] = config; | ||||||
|  |     } | ||||||
|  |     current_selected = getCameraSelection(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ConfigureCamera::startPreviewing() { | ||||||
|  |     current_selected = getCameraSelection(); | ||||||
|  |     recordConfig(); | ||||||
|  |     int camera_selection = getSelectedCameraIndex(); | ||||||
|  |     stopPreviewing(); | ||||||
|  |     // Init preview box
 | ||||||
|  |     ui->preview_box->setHidden(false); | ||||||
|  |     ui->preview_button->setHidden(true); | ||||||
|  |     preview_width = ui->preview_box->size().width(); | ||||||
|  |     preview_height = preview_width * 0.75; | ||||||
|  |     ui->preview_box->setToolTip(tr("Resolution: ") + QString::number(preview_width) + "*" + | ||||||
|  |                                 QString::number(preview_height)); | ||||||
|  |     // Load previewing camera
 | ||||||
|  |     previewing_camera = | ||||||
|  |         Camera::CreateCameraPreview(camera_name[camera_selection], camera_config[camera_selection], | ||||||
|  |                                     preview_width, preview_height); | ||||||
|  |     if (!previewing_camera) { | ||||||
|  |         stopPreviewing(); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     previewing_camera->SetResolution( | ||||||
|  |         {static_cast<u16>(preview_width), static_cast<u16>(preview_height)}); | ||||||
|  |     previewing_camera->SetEffect(Service::CAM::Effect::None); | ||||||
|  |     previewing_camera->SetFlip(Service::CAM::Flip::None); | ||||||
|  |     previewing_camera->SetFormat(Service::CAM::OutputFormat::RGB565); | ||||||
|  |     previewing_camera->SetFrameRate(Service::CAM::FrameRate::Rate_30); | ||||||
|  |     previewing_camera->StartCapture(); | ||||||
|  | 
 | ||||||
|  |     timer_id = startTimer(1000 / 30); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ConfigureCamera::stopPreviewing() { | ||||||
|  |     ui->preview_box->setHidden(true); | ||||||
|  |     ui->preview_button->setHidden(false); | ||||||
|  | 
 | ||||||
|  |     if (previewing_camera) { | ||||||
|  |         previewing_camera->StopCapture(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (timer_id != 0) { | ||||||
|  |         killTimer(timer_id); | ||||||
|  |         timer_id = 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ConfigureCamera::timerEvent(QTimerEvent* event) { | ||||||
|  |     if (event->timerId() != timer_id) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     if (!previewing_camera) { | ||||||
|  |         killTimer(timer_id); | ||||||
|  |         timer_id = 0; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     std::vector<u16> frame = previewing_camera->ReceiveFrame(); | ||||||
|  |     int width = ui->preview_box->size().width(); | ||||||
|  |     int height = width * 0.75; | ||||||
|  |     if (width != preview_width || height != preview_height) { | ||||||
|  |         stopPreviewing(); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     QImage image(width, height, QImage::Format::Format_RGB16); | ||||||
|  |     std::memcpy(image.bits(), frame.data(), width * height * sizeof(u16)); | ||||||
|  |     ui->preview_box->setPixmap(QPixmap::fromImage(image)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ConfigureCamera::setConfiguration() { | ||||||
|  |     int index = getSelectedCameraIndex(); | ||||||
|  |     for (int i = 0; i < Implementations.size(); i++) { | ||||||
|  |         if (Implementations[i] == camera_name[index]) { | ||||||
|  |             ui->image_source->setCurrentIndex(i); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (camera_name[index] == "image") { | ||||||
|  |         ui->camera_file->setDisabled(camera_config[index].empty()); | ||||||
|  |         ui->toolButton->setDisabled(camera_config[index].empty()); | ||||||
|  |         if (camera_config[index].empty()) { | ||||||
|  |             ui->camera_file->setText(""); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (camera_name[index] == "qt") { | ||||||
|  |         ui->system_camera->setCurrentIndex(0); | ||||||
|  |         if (!camera_config[index].empty()) { | ||||||
|  |             ui->system_camera->setCurrentText(QString::fromStdString(camera_config[index])); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         ui->camera_file->setText(QString::fromStdString(camera_config[index])); | ||||||
|  |     } | ||||||
|  |     updateImageSourceUI(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ConfigureCamera::onToolButtonClicked() { | ||||||
|  |     stopPreviewing(); | ||||||
|  |     int camera_selection = getSelectedCameraIndex(); | ||||||
|  |     QString filter; | ||||||
|  |     if (camera_name[camera_selection] == "image") { | ||||||
|  |         QList<QByteArray> types = QImageReader::supportedImageFormats(); | ||||||
|  |         QList<QString> temp_filters; | ||||||
|  |         for (const QByteArray& type : types) { | ||||||
|  |             temp_filters << QString("*." + QString(type)); | ||||||
|  |         } | ||||||
|  |         filter = tr("Supported image files (%1)").arg(temp_filters.join(" ")); | ||||||
|  |     } | ||||||
|  |     QString path = QFileDialog::getOpenFileName(this, tr("Open File"), ".", filter); | ||||||
|  |     if (!path.isEmpty()) { | ||||||
|  |         ui->camera_file->setText(path); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ConfigureCamera::applyConfiguration() { | ||||||
|  |     recordConfig(); | ||||||
|  |     stopPreviewing(); | ||||||
|  |     Settings::values.camera_name = camera_name; | ||||||
|  |     Settings::values.camera_config = camera_config; | ||||||
|  |     Settings::Apply(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ConfigureCamera::CameraPosition ConfigureCamera::getCameraSelection() { | ||||||
|  |     switch (ui->camera_selection->currentIndex()) { | ||||||
|  |     case 0: // Front
 | ||||||
|  |         return CameraPosition::Front; | ||||||
|  |     case 1: // Rear
 | ||||||
|  |         if (ui->camera_mode->currentIndex() == 0) { | ||||||
|  |             // Single (2D) mode
 | ||||||
|  |             return CameraPosition::RearBoth; | ||||||
|  |         } else { | ||||||
|  |             // Double (3D) mode
 | ||||||
|  |             return (ui->camera_position->currentIndex() == 0) ? CameraPosition::RearLeft | ||||||
|  |                                                               : CameraPosition::RearRight; | ||||||
|  |         } | ||||||
|  |     default: | ||||||
|  |         NGLOG_ERROR(Frontend, "Unknown camera selection"); | ||||||
|  |         return CameraPosition::Front; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int ConfigureCamera::getSelectedCameraIndex() { | ||||||
|  |     CameraPosition pos = getCameraSelection(); | ||||||
|  |     int camera_selection = static_cast<int>(pos); | ||||||
|  |     if (pos == CameraPosition::RearBoth) { // Single Mode
 | ||||||
|  |         camera_selection = 0;              // Either camera is the same, so we return RearRight
 | ||||||
|  |     } | ||||||
|  |     return camera_selection; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ConfigureCamera::retranslateUi() { | ||||||
|  |     ui->retranslateUi(this); | ||||||
|  | } | ||||||
							
								
								
									
										56
									
								
								src/citra_qt/configuration/configure_camera.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/citra_qt/configuration/configure_camera.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,56 @@ | ||||||
|  | // Copyright 2018 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/frontend/camera/factory.h" | ||||||
|  | #include "core/frontend/camera/interface.h" | ||||||
|  | 
 | ||||||
|  | namespace Ui { | ||||||
|  | class ConfigureCamera; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class ConfigureCamera : public QWidget { | ||||||
|  |     Q_OBJECT | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     explicit ConfigureCamera(QWidget* parent = nullptr); | ||||||
|  |     ~ConfigureCamera(); | ||||||
|  | 
 | ||||||
|  |     void applyConfiguration(); | ||||||
|  |     void retranslateUi(); | ||||||
|  | 
 | ||||||
|  |     void timerEvent(QTimerEvent*) override; | ||||||
|  | 
 | ||||||
|  | public slots: | ||||||
|  |     /// recordConfig() and updateUiDisplay()
 | ||||||
|  |     void setConfiguration(); | ||||||
|  |     void onToolButtonClicked(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     enum class CameraPosition { RearRight, Front, RearLeft, RearBoth, Null }; | ||||||
|  |     static const std::array<std::string, 3> Implementations; | ||||||
|  |     /// Record the current configuration
 | ||||||
|  |     void recordConfig(); | ||||||
|  |     /// Updates camera mode
 | ||||||
|  |     void updateCameraMode(); | ||||||
|  |     /// Updates image source
 | ||||||
|  |     void updateImageSourceUI(); | ||||||
|  |     void startPreviewing(); | ||||||
|  |     void stopPreviewing(); | ||||||
|  |     void connectEvents(); | ||||||
|  |     CameraPosition getCameraSelection(); | ||||||
|  |     int getSelectedCameraIndex(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     std::unique_ptr<Ui::ConfigureCamera> ui; | ||||||
|  |     std::array<std::string, 3> camera_name; | ||||||
|  |     std::array<std::string, 3> camera_config; | ||||||
|  |     int timer_id = 0; | ||||||
|  |     int preview_width = 0; | ||||||
|  |     int preview_height = 0; | ||||||
|  |     CameraPosition current_selected = CameraPosition::Front; | ||||||
|  |     bool is_previewing = false; | ||||||
|  |     std::unique_ptr<Camera::CameraInterface> previewing_camera; | ||||||
|  | }; | ||||||
							
								
								
									
										257
									
								
								src/citra_qt/configuration/configure_camera.ui
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										257
									
								
								src/citra_qt/configuration/configure_camera.ui
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,257 @@ | ||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | 
 | ||||||
|  | <ui version="4.0"> | ||||||
|  |   <class>ConfigureCamera</class> | ||||||
|  |   <widget class="QWidget" name="ConfigureCamera"> | ||||||
|  |     <layout class="QVBoxLayout" name="verticalLayout"> | ||||||
|  |       <item> | ||||||
|  |         <widget class="QGroupBox" name="groupBox"> | ||||||
|  |           <property name="title"> | ||||||
|  |             <string>Camera</string> | ||||||
|  |           </property> | ||||||
|  |           <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||||
|  |             <item> | ||||||
|  |               <layout class="QHBoxLayout" name="horizontalLayout"> | ||||||
|  |                 <item> | ||||||
|  |                   <widget class="QLabel" name="camera_selection_label"> | ||||||
|  |                     <property name="toolTip"> | ||||||
|  |                       <string>Select the camera to configure</string> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="text"> | ||||||
|  |                       <string>Camera to configure:</string> | ||||||
|  |                     </property> | ||||||
|  |                   </widget> | ||||||
|  |                 </item> | ||||||
|  |                 <item> | ||||||
|  |                   <widget class="QComboBox" name="camera_selection"> | ||||||
|  |                     <property name="toolTip"> | ||||||
|  |                       <string>Select the camera to configure</string> | ||||||
|  |                     </property> | ||||||
|  |                     <item> | ||||||
|  |                       <property name="text"> | ||||||
|  |                         <string>Front</string> | ||||||
|  |                       </property> | ||||||
|  |                     </item> | ||||||
|  |                     <item> | ||||||
|  |                       <property name="text"> | ||||||
|  |                         <string>Rear</string> | ||||||
|  |                       </property> | ||||||
|  |                     </item> | ||||||
|  |                   </widget> | ||||||
|  |                 </item> | ||||||
|  |               </layout> | ||||||
|  |             </item> | ||||||
|  |             <item> | ||||||
|  |               <layout class="QHBoxLayout" name="horizontalLayout_2"> | ||||||
|  |                 <item> | ||||||
|  |                   <widget class="QLabel" name="camera_mode_label"> | ||||||
|  |                     <property name="toolTip"> | ||||||
|  |                       <string>Select the camera mode (single or double)</string> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="text"> | ||||||
|  |                       <string>Camera mode:</string> | ||||||
|  |                     </property> | ||||||
|  |                   </widget> | ||||||
|  |                 </item> | ||||||
|  |                 <item> | ||||||
|  |                   <widget class="QComboBox" name="camera_mode"> | ||||||
|  |                     <property name="toolTip"> | ||||||
|  |                       <string>Select the camera mode (single or double)</string> | ||||||
|  |                     </property> | ||||||
|  |                     <item> | ||||||
|  |                       <property name="text"> | ||||||
|  |                         <string>Single (2D)</string> | ||||||
|  |                       </property> | ||||||
|  |                     </item> | ||||||
|  |                     <item> | ||||||
|  |                       <property name="text"> | ||||||
|  |                         <string>Double (3D)</string> | ||||||
|  |                       </property> | ||||||
|  |                     </item> | ||||||
|  |                   </widget> | ||||||
|  |                 </item> | ||||||
|  |               </layout> | ||||||
|  |             </item> | ||||||
|  |             <item> | ||||||
|  |               <layout class="QHBoxLayout" name="horizontalLayout_3"> | ||||||
|  |                 <item> | ||||||
|  |                   <widget class="QLabel" name="camera_position_label"> | ||||||
|  |                     <property name="toolTip"> | ||||||
|  |                       <string>Select the position of camera to configure</string> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="text"> | ||||||
|  |                       <string>Camera position:</string> | ||||||
|  |                     </property> | ||||||
|  |                   </widget> | ||||||
|  |                 </item> | ||||||
|  |                 <item> | ||||||
|  |                   <widget class="QComboBox" name="camera_position"> | ||||||
|  |                     <property name="toolTip"> | ||||||
|  |                       <string>Select the position of camera to configure</string> | ||||||
|  |                     </property> | ||||||
|  |                     <item> | ||||||
|  |                       <property name="text"> | ||||||
|  |                         <string>Left</string> | ||||||
|  |                       </property> | ||||||
|  |                     </item> | ||||||
|  |                     <item> | ||||||
|  |                       <property name="text"> | ||||||
|  |                         <string>Right</string> | ||||||
|  |                       </property> | ||||||
|  |                     </item> | ||||||
|  |                   </widget> | ||||||
|  |                 </item> | ||||||
|  |               </layout> | ||||||
|  |             </item> | ||||||
|  |           </layout> | ||||||
|  |         </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |         <widget class="QGroupBox" name="configurationBox"> | ||||||
|  |           <property name="title"> | ||||||
|  |             <string>Configuration</string> | ||||||
|  |           </property> | ||||||
|  |           <layout class="QVBoxLayout" name="verticalLayout_configuration"> | ||||||
|  |             <item> | ||||||
|  |               <layout class="QHBoxLayout" name="horizontalLayout_4"> | ||||||
|  |                 <item> | ||||||
|  |                   <widget class="QLabel" name="image_source_label"> | ||||||
|  |                     <property name="toolTip"> | ||||||
|  |                       <string>Select where the image of the emulated camera come from. It may be an image or a real camera.</string> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="text"> | ||||||
|  |                       <string>Camera Image Source:</string> | ||||||
|  |                     </property> | ||||||
|  |                   </widget> | ||||||
|  |                 </item> | ||||||
|  |                 <item> | ||||||
|  |                   <widget class="QComboBox" name="image_source"> | ||||||
|  |                     <property name="toolTip"> | ||||||
|  |                       <string>Select where the image of the emulated camera come from. It may be an image or a real camera.</string> | ||||||
|  |                     </property> | ||||||
|  |                     <item> | ||||||
|  |                       <property name="text"> | ||||||
|  |                         <string>Blank (blank)</string> | ||||||
|  |                       </property> | ||||||
|  |                     </item> | ||||||
|  |                     <item> | ||||||
|  |                       <property name="text"> | ||||||
|  |                         <string>Still Image (image)</string> | ||||||
|  |                       </property> | ||||||
|  |                     </item> | ||||||
|  |                     <item> | ||||||
|  |                       <property name="text"> | ||||||
|  |                         <string>System Camera (qt)</string> | ||||||
|  |                       </property> | ||||||
|  |                     </item> | ||||||
|  |                   </widget> | ||||||
|  |                 </item> | ||||||
|  |               </layout> | ||||||
|  |             </item> | ||||||
|  |             <item> | ||||||
|  |               <layout class="QHBoxLayout" name="horizontalLayout_6"> | ||||||
|  |                 <item> | ||||||
|  |                   <widget class="QLabel" name="camera_file_label"> | ||||||
|  |                     <property name="frameShape"> | ||||||
|  |                       <enum>QFrame::NoFrame</enum> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="text"> | ||||||
|  |                       <string>File:</string> | ||||||
|  |                     </property> | ||||||
|  |                   </widget> | ||||||
|  |                 </item> | ||||||
|  |                 <item> | ||||||
|  |                   <widget class="QLineEdit" name="camera_file"/> | ||||||
|  |                 </item> | ||||||
|  |                 <item> | ||||||
|  |                   <widget class="QToolButton" name="toolButton"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                       <string>...</string> | ||||||
|  |                     </property> | ||||||
|  |                   </widget> | ||||||
|  |                 </item> | ||||||
|  |               </layout> | ||||||
|  |             </item> | ||||||
|  |             <item> | ||||||
|  |               <layout class="QHBoxLayout" name="horizontalLayout_7"> | ||||||
|  |                 <item> | ||||||
|  |                   <widget class="QLabel" name="system_camera_label"> | ||||||
|  |                     <property name="frameShape"> | ||||||
|  |                       <enum>QFrame::NoFrame</enum> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="text"> | ||||||
|  |                       <string>Camera:</string> | ||||||
|  |                     </property> | ||||||
|  |                   </widget> | ||||||
|  |                 </item> | ||||||
|  |                 <item> | ||||||
|  |                   <widget class="QComboBox" name="system_camera"> | ||||||
|  |                     <item> | ||||||
|  |                       <property name="text"> | ||||||
|  |                         <string><Default></string> | ||||||
|  |                       </property> | ||||||
|  |                     </item> | ||||||
|  |                   </widget> | ||||||
|  |                 </item> | ||||||
|  |               </layout> | ||||||
|  |             </item> | ||||||
|  |             <item> | ||||||
|  |               <widget class="QCheckBox" name="prompt_before_load"> | ||||||
|  |                 <property name="text"> | ||||||
|  |                   <string>Prompt before load</string> | ||||||
|  |                 </property> | ||||||
|  |               </widget> | ||||||
|  |             </item> | ||||||
|  |           </layout> | ||||||
|  |         </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |         <widget class="QGroupBox" name="previewBox"> | ||||||
|  |           <property name="title"> | ||||||
|  |             <string>Preview</string> | ||||||
|  |           </property> | ||||||
|  |           <layout class="QVBoxLayout" name="verticalLayout_3"> | ||||||
|  |             <item> | ||||||
|  |               <widget class="QLabel" name="preview_box"> | ||||||
|  |                 <property name="baseSize"> | ||||||
|  |                   <size> | ||||||
|  |                     <width>512</width> | ||||||
|  |                     <height>384</height> | ||||||
|  |                   </size> | ||||||
|  |                 </property> | ||||||
|  |                 <property name="toolTip"> | ||||||
|  |                   <string>Resolution: 512*384</string> | ||||||
|  |                 </property> | ||||||
|  |                 <property name="text"> | ||||||
|  |                   <string/> | ||||||
|  |                 </property> | ||||||
|  |               </widget> | ||||||
|  |             </item> | ||||||
|  |             <item> | ||||||
|  |               <widget class="QPushButton" name="preview_button"> | ||||||
|  |                 <property name="text"> | ||||||
|  |                   <string>Click to preview</string> | ||||||
|  |                 </property> | ||||||
|  |               </widget> | ||||||
|  |             </item> | ||||||
|  |           </layout> | ||||||
|  |         </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |         <spacer> | ||||||
|  |           <property name="orientation"> | ||||||
|  |             <enum>Qt::Vertical</enum> | ||||||
|  |           </property> | ||||||
|  |           <property name="sizeHint" stdset="0"> | ||||||
|  |             <size> | ||||||
|  |               <width>20</width> | ||||||
|  |               <height>40</height> | ||||||
|  |             </size> | ||||||
|  |           </property> | ||||||
|  |         </spacer> | ||||||
|  |       </item> | ||||||
|  |     </layout> | ||||||
|  |   </widget> | ||||||
|  |   <resources/> | ||||||
|  |   <connections/> | ||||||
|  | </ui> | ||||||
|  | @ -24,6 +24,7 @@ void ConfigureDialog::applyConfiguration() { | ||||||
|     ui->inputTab->applyConfiguration(); |     ui->inputTab->applyConfiguration(); | ||||||
|     ui->graphicsTab->applyConfiguration(); |     ui->graphicsTab->applyConfiguration(); | ||||||
|     ui->audioTab->applyConfiguration(); |     ui->audioTab->applyConfiguration(); | ||||||
|  |     ui->cameraTab->applyConfiguration(); | ||||||
|     ui->debugTab->applyConfiguration(); |     ui->debugTab->applyConfiguration(); | ||||||
|     ui->webTab->applyConfiguration(); |     ui->webTab->applyConfiguration(); | ||||||
|     Settings::Apply(); |     Settings::Apply(); | ||||||
|  | @ -37,6 +38,7 @@ void ConfigureDialog::onLanguageChanged(const QString& locale) { | ||||||
|     ui->inputTab->retranslateUi(); |     ui->inputTab->retranslateUi(); | ||||||
|     ui->graphicsTab->retranslateUi(); |     ui->graphicsTab->retranslateUi(); | ||||||
|     ui->audioTab->retranslateUi(); |     ui->audioTab->retranslateUi(); | ||||||
|  |     ui->cameraTab->applyConfiguration(); | ||||||
|     ui->debugTab->retranslateUi(); |     ui->debugTab->retranslateUi(); | ||||||
|     ui->webTab->retranslateUi(); |     ui->webTab->retranslateUi(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -16,6 +16,8 @@ | ||||||
| #include <QtWidgets> | #include <QtWidgets> | ||||||
| #include "citra_qt/aboutdialog.h" | #include "citra_qt/aboutdialog.h" | ||||||
| #include "citra_qt/bootmanager.h" | #include "citra_qt/bootmanager.h" | ||||||
|  | #include "citra_qt/camera/qt_multimedia_camera.h" | ||||||
|  | #include "citra_qt/camera/still_image_camera.h" | ||||||
| #include "citra_qt/compatdb.h" | #include "citra_qt/compatdb.h" | ||||||
| #include "citra_qt/configuration/config.h" | #include "citra_qt/configuration/config.h" | ||||||
| #include "citra_qt/configuration/configure_dialog.h" | #include "citra_qt/configuration/configure_dialog.h" | ||||||
|  | @ -696,6 +698,8 @@ void GMainWindow::ShutdownGame() { | ||||||
|     emu_thread->wait(); |     emu_thread->wait(); | ||||||
|     emu_thread = nullptr; |     emu_thread = nullptr; | ||||||
| 
 | 
 | ||||||
|  |     Camera::QtMultimediaCameraHandler::ReleaseHandlers(); | ||||||
|  | 
 | ||||||
|     // The emulation is stopped, so closing the window or not does not matter anymore
 |     // The emulation is stopped, so closing the window or not does not matter anymore
 | ||||||
|     disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); |     disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); | ||||||
| 
 | 
 | ||||||
|  | @ -910,6 +914,7 @@ void GMainWindow::OnMenuRecentFile() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GMainWindow::OnStartGame() { | void GMainWindow::OnStartGame() { | ||||||
|  |     Camera::QtMultimediaCameraHandler::ResumeCameras(); | ||||||
|     emu_thread->SetRunning(true); |     emu_thread->SetRunning(true); | ||||||
|     qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus"); |     qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus"); | ||||||
|     qRegisterMetaType<std::string>("std::string"); |     qRegisterMetaType<std::string>("std::string"); | ||||||
|  | @ -925,7 +930,7 @@ void GMainWindow::OnStartGame() { | ||||||
| 
 | 
 | ||||||
| void GMainWindow::OnPauseGame() { | void GMainWindow::OnPauseGame() { | ||||||
|     emu_thread->SetRunning(false); |     emu_thread->SetRunning(false); | ||||||
| 
 |     Camera::QtMultimediaCameraHandler::StopCameras(); | ||||||
|     ui.action_Start->setEnabled(true); |     ui.action_Start->setEnabled(true); | ||||||
|     ui.action_Pause->setEnabled(false); |     ui.action_Pause->setEnabled(false); | ||||||
|     ui.action_Stop->setEnabled(true); |     ui.action_Stop->setEnabled(true); | ||||||
|  | @ -1363,6 +1368,11 @@ int main(int argc, char* argv[]) { | ||||||
|     Log::AddBackend( |     Log::AddBackend( | ||||||
|         std::make_unique<Log::FileBackend>(FileUtil::GetUserPath(D_LOGS_IDX) + LOG_FILE)); |         std::make_unique<Log::FileBackend>(FileUtil::GetUserPath(D_LOGS_IDX) + LOG_FILE)); | ||||||
| 
 | 
 | ||||||
|  |     // Register CameraFactory
 | ||||||
|  |     Camera::RegisterFactory("image", std::make_unique<Camera::StillImageCameraFactory>()); | ||||||
|  |     Camera::RegisterFactory("qt", std::make_unique<Camera::QtMultimediaCameraFactory>()); | ||||||
|  |     Camera::QtMultimediaCameraHandler::Init(); | ||||||
|  | 
 | ||||||
|     main_window.show(); |     main_window.show(); | ||||||
|     return app.exec(); |     return app.exec(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -23,9 +23,13 @@ void BlankCamera::SetFlip(Service::CAM::Flip) {} | ||||||
| 
 | 
 | ||||||
| void BlankCamera::SetEffect(Service::CAM::Effect) {} | void BlankCamera::SetEffect(Service::CAM::Effect) {} | ||||||
| 
 | 
 | ||||||
| std::vector<u16> BlankCamera::ReceiveFrame() const { | std::vector<u16> BlankCamera::ReceiveFrame() { | ||||||
|     // Note: 0x80008000 stands for two black pixels in YUV422
 |     // Note: 0x80008000 stands for two black pixels in YUV422
 | ||||||
|     return std::vector<u16>(width * height, output_rgb ? 0 : 0x8000); |     return std::vector<u16>(width * height, output_rgb ? 0 : 0x8000); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool BlankCamera::IsPreviewAvailable() { | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace Camera
 | } // namespace Camera
 | ||||||
|  |  | ||||||
|  | @ -17,7 +17,9 @@ public: | ||||||
|     void SetFlip(Service::CAM::Flip) override; |     void SetFlip(Service::CAM::Flip) override; | ||||||
|     void SetEffect(Service::CAM::Effect) override; |     void SetEffect(Service::CAM::Effect) override; | ||||||
|     void SetFormat(Service::CAM::OutputFormat) override; |     void SetFormat(Service::CAM::OutputFormat) override; | ||||||
|     std::vector<u16> ReceiveFrame() const override; |     void SetFrameRate(Service::CAM::FrameRate frame_rate) override {} | ||||||
|  |     std::vector<u16> ReceiveFrame() override; | ||||||
|  |     bool IsPreviewAvailable() override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     int width = 0; |     int width = 0; | ||||||
|  |  | ||||||
|  | @ -29,4 +29,18 @@ std::unique_ptr<CameraInterface> CreateCamera(const std::string& name, const std | ||||||
|     return std::make_unique<BlankCamera>(); |     return std::make_unique<BlankCamera>(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | std::unique_ptr<CameraInterface> CreateCameraPreview(const std::string& name, | ||||||
|  |                                                      const std::string& config, int width, | ||||||
|  |                                                      int height) { | ||||||
|  |     auto pair = factories.find(name); | ||||||
|  |     if (pair != factories.end()) { | ||||||
|  |         return pair->second->CreatePreview(config, width, height); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (name != "blank") { | ||||||
|  |         LOG_ERROR(Service_CAM, "Unknown camera \"%s\"", name.c_str()); | ||||||
|  |     } | ||||||
|  |     return std::make_unique<BlankCamera>(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace Camera
 | } // namespace Camera
 | ||||||
|  |  | ||||||
|  | @ -21,6 +21,20 @@ public: | ||||||
|      * @returns a unique_ptr to the created camera object. |      * @returns a unique_ptr to the created camera object. | ||||||
|      */ |      */ | ||||||
|     virtual std::unique_ptr<CameraInterface> Create(const std::string& config) const = 0; |     virtual std::unique_ptr<CameraInterface> Create(const std::string& config) const = 0; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Creates a camera object for preview based on the configuration string. | ||||||
|  |      * @param config Configuration string to create the camera. The implementation can decide the | ||||||
|  |      *               meaning of this string. | ||||||
|  |      * @returns a unique_ptr to the created camera object. | ||||||
|  |      * Note: The default implementation for this is to call Create(). Derived classes may have other | ||||||
|  |      *       Implementations. For example, A dialog may be used instead of LOG_ERROR when error | ||||||
|  |      * occurs. | ||||||
|  |      */ | ||||||
|  |     virtual std::unique_ptr<CameraInterface> CreatePreview(const std::string& config, int width, | ||||||
|  |                                                            int height) const { | ||||||
|  |         return Create(config); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -38,4 +52,14 @@ void RegisterFactory(const std::string& name, std::unique_ptr<CameraFactory> fac | ||||||
|  */ |  */ | ||||||
| std::unique_ptr<CameraInterface> CreateCamera(const std::string& name, const std::string& config); | std::unique_ptr<CameraInterface> CreateCamera(const std::string& name, const std::string& config); | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * Creates a camera from the factory for previewing. | ||||||
|  |  * @param name Identifier of the camera factory. | ||||||
|  |  * @param config Configuration string to create the camera. The meaning of this string is | ||||||
|  |  *     defined by the factory. | ||||||
|  |  */ | ||||||
|  | std::unique_ptr<CameraInterface> CreateCameraPreview(const std::string& name, | ||||||
|  |                                                      const std::string& config, int width, | ||||||
|  |                                                      int height); | ||||||
|  | 
 | ||||||
| } // namespace Camera
 | } // namespace Camera
 | ||||||
|  |  | ||||||
|  | @ -49,13 +49,27 @@ public: | ||||||
|      */ |      */ | ||||||
|     virtual void SetFormat(Service::CAM::OutputFormat format) = 0; |     virtual void SetFormat(Service::CAM::OutputFormat format) = 0; | ||||||
| 
 | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Sets the recommended framerate of the camera. | ||||||
|  |      * @param frame_rate Recommended framerate | ||||||
|  |      */ | ||||||
|  |     virtual void SetFrameRate(Service::CAM::FrameRate frame_rate) = 0; | ||||||
|  | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Receives a frame from the camera. |      * Receives a frame from the camera. | ||||||
|      * This function should be only called between a StartCapture call and a StopCapture call. |      * This function should be only called between a StartCapture call and a StopCapture call. | ||||||
|      * @returns A std::vector<u16> containing pixels. The total size of the vector is width * height |      * @returns A std::vector<u16> containing pixels. The total size of the vector is width * height | ||||||
|      *     where width and height are set by a call to SetResolution. |      *     where width and height are set by a call to SetResolution. | ||||||
|      */ |      */ | ||||||
|     virtual std::vector<u16> ReceiveFrame() const = 0; |     virtual std::vector<u16> ReceiveFrame() = 0; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Test if the camera is opened successfully and can receive a preview frame. Only used for | ||||||
|  |      * preview. This function should be only called between a StartCapture call and a StopCapture | ||||||
|  |      * call. | ||||||
|  |      * @returns true if the camera is opened successfully and false otherwise | ||||||
|  |      */ | ||||||
|  |     virtual bool IsPreviewAvailable() = 0; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } // namespace Camera
 | } // namespace Camera
 | ||||||
|  |  | ||||||
|  | @ -22,6 +22,8 @@ | ||||||
| namespace Service { | namespace Service { | ||||||
| namespace CAM { | namespace CAM { | ||||||
| 
 | 
 | ||||||
|  | static std::weak_ptr<Module> current_cam; | ||||||
|  | 
 | ||||||
| // built-in resolution parameters
 | // built-in resolution parameters
 | ||||||
| constexpr std::array<Resolution, 8> PRESET_RESOLUTION{{ | constexpr std::array<Resolution, 8> PRESET_RESOLUTION{{ | ||||||
|     {640, 480, 0, 0, 639, 479},  // VGA
 |     {640, 480, 0, 0, 639, 479},  // VGA
 | ||||||
|  | @ -138,9 +140,16 @@ void Module::StartReceiving(int port_id) { | ||||||
|     port.is_receiving = true; |     port.is_receiving = true; | ||||||
| 
 | 
 | ||||||
|     // launches a capture task asynchronously
 |     // launches a capture task asynchronously
 | ||||||
|     const CameraConfig& camera = cameras[port.camera_id]; |     CameraConfig& camera = cameras[port.camera_id]; | ||||||
|     port.capture_result = |     port.capture_result = std::async(std::launch::async, [&camera, &port, this] { | ||||||
|         std::async(std::launch::async, &Camera::CameraInterface::ReceiveFrame, camera.impl.get()); |         if (is_camera_reload_pending.exchange(false)) { | ||||||
|  |             // reinitialize the camera according to new settings
 | ||||||
|  |             camera.impl->StopCapture(); | ||||||
|  |             LoadCameraImplementation(camera, port.camera_id); | ||||||
|  |             camera.impl->StartCapture(); | ||||||
|  |         } | ||||||
|  |         return camera.impl->ReceiveFrame(); | ||||||
|  |     }); | ||||||
| 
 | 
 | ||||||
|     // schedules a completion event according to the frame rate. The event will block on the
 |     // schedules a completion event according to the frame rate. The event will block on the
 | ||||||
|     // capture task if it is not finished within the expected time
 |     // capture task if it is not finished within the expected time
 | ||||||
|  | @ -771,7 +780,7 @@ void Module::Interface::SetFrameRate(Kernel::HLERequestContext& ctx) { | ||||||
|     if (camera_select.IsValid()) { |     if (camera_select.IsValid()) { | ||||||
|         for (int camera : camera_select) { |         for (int camera : camera_select) { | ||||||
|             cam->cameras[camera].frame_rate = frame_rate; |             cam->cameras[camera].frame_rate = frame_rate; | ||||||
|             // TODO(wwylele): consider hinting the actual camera with the expected frame rate
 |             cam->cameras[camera].impl->SetFrameRate(frame_rate); | ||||||
|         } |         } | ||||||
|         rb.Push(RESULT_SUCCESS); |         rb.Push(RESULT_SUCCESS); | ||||||
|     } else { |     } else { | ||||||
|  | @ -980,12 +989,7 @@ void Module::Interface::DriverInitialize(Kernel::HLERequestContext& ctx) { | ||||||
|             context.resolution = |             context.resolution = | ||||||
|                 context_id == 0 ? PRESET_RESOLUTION[5 /*DS_LCD*/] : PRESET_RESOLUTION[0 /*VGA*/]; |                 context_id == 0 ? PRESET_RESOLUTION[5 /*DS_LCD*/] : PRESET_RESOLUTION[0 /*VGA*/]; | ||||||
|         } |         } | ||||||
|         camera.impl = Camera::CreateCamera(Settings::values.camera_name[camera_id], |         cam->LoadCameraImplementation(camera, camera_id); | ||||||
|                                            Settings::values.camera_config[camera_id]); |  | ||||||
|         camera.impl->SetFlip(camera.contexts[0].flip); |  | ||||||
|         camera.impl->SetEffect(camera.contexts[0].effect); |  | ||||||
|         camera.impl->SetFormat(camera.contexts[0].format); |  | ||||||
|         camera.impl->SetResolution(camera.contexts[0].resolution); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     for (PortConfig& port : cam->ports) { |     for (PortConfig& port : cam->ports) { | ||||||
|  | @ -1032,8 +1036,28 @@ Module::~Module() { | ||||||
|     CancelReceiving(1); |     CancelReceiving(1); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void Module::ReloadCameraDevices() { | ||||||
|  |     is_camera_reload_pending.store(true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Module::LoadCameraImplementation(CameraConfig& camera, int camera_id) { | ||||||
|  |     camera.impl = Camera::CreateCamera(Settings::values.camera_name[camera_id], | ||||||
|  |                                        Settings::values.camera_config[camera_id]); | ||||||
|  |     camera.impl->SetFlip(camera.contexts[0].flip); | ||||||
|  |     camera.impl->SetEffect(camera.contexts[0].effect); | ||||||
|  |     camera.impl->SetFormat(camera.contexts[0].format); | ||||||
|  |     camera.impl->SetResolution(camera.contexts[0].resolution); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ReloadCameraDevices() { | ||||||
|  |     if (auto cam = current_cam.lock()) | ||||||
|  |         cam->ReloadCameraDevices(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void InstallInterfaces(SM::ServiceManager& service_manager) { | void InstallInterfaces(SM::ServiceManager& service_manager) { | ||||||
|     auto cam = std::make_shared<Module>(); |     auto cam = std::make_shared<Module>(); | ||||||
|  |     current_cam = cam; | ||||||
|  | 
 | ||||||
|     std::make_shared<CAM_U>(cam)->InstallAsService(service_manager); |     std::make_shared<CAM_U>(cam)->InstallAsService(service_manager); | ||||||
|     std::make_shared<CAM_S>(cam)->InstallAsService(service_manager); |     std::make_shared<CAM_S>(cam)->InstallAsService(service_manager); | ||||||
|     std::make_shared<CAM_C>(cam)->InstallAsService(service_manager); |     std::make_shared<CAM_C>(cam)->InstallAsService(service_manager); | ||||||
|  |  | ||||||
|  | @ -240,6 +240,7 @@ class Module final { | ||||||
| public: | public: | ||||||
|     Module(); |     Module(); | ||||||
|     ~Module(); |     ~Module(); | ||||||
|  |     void ReloadCameraDevices(); | ||||||
| 
 | 
 | ||||||
|     class Interface : public ServiceFramework<Interface> { |     class Interface : public ServiceFramework<Interface> { | ||||||
|     public: |     public: | ||||||
|  | @ -771,11 +772,17 @@ private: | ||||||
|         void Clear(); |         void Clear(); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     void LoadCameraImplementation(CameraConfig& camera, int camera_id); | ||||||
|  | 
 | ||||||
|     std::array<CameraConfig, NumCameras> cameras; |     std::array<CameraConfig, NumCameras> cameras; | ||||||
|     std::array<PortConfig, 2> ports; |     std::array<PortConfig, 2> ports; | ||||||
|     CoreTiming::EventType* completion_event_callback; |     CoreTiming::EventType* completion_event_callback; | ||||||
|  |     std::atomic<bool> is_camera_reload_pending{false}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | /// Reload camera devices. Used when input configuration changed
 | ||||||
|  | void ReloadCameraDevices(); | ||||||
|  | 
 | ||||||
| void InstallInterfaces(SM::ServiceManager& service_manager); | void InstallInterfaces(SM::ServiceManager& service_manager); | ||||||
| 
 | 
 | ||||||
| } // namespace CAM
 | } // namespace CAM
 | ||||||
|  |  | ||||||
|  | @ -35,6 +35,7 @@ void Apply() { | ||||||
| 
 | 
 | ||||||
|     Service::HID::ReloadInputDevices(); |     Service::HID::ReloadInputDevices(); | ||||||
|     Service::IR::ReloadInputDevices(); |     Service::IR::ReloadInputDevices(); | ||||||
|  |     Service::CAM::ReloadCameraDevices(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } // namespace Settings
 | } // namespace Settings
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue