1 // Copyright (c) 2012- PPSSPP Project.
2 
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
6 
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 // GNU General Public License 2.0 for more details.
11 
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14 
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17 
18 #include <algorithm>
19 #include <mutex>
20 
21 #include "ppsspp_config.h"
22 
23 #include "Common/System/System.h"
24 #include "Common/Serialize/Serializer.h"
25 #include "Common/Serialize/SerializeFuncs.h"
26 #include "Core/HLE/HLE.h"
27 #include "Core/HLE/sceUsbCam.h"
28 #include "Core/HLE/sceUsbMic.h"
29 #include "Core/HW/Camera.h"
30 #include "Core/MemMapHelpers.h"
31 
32 #if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) && !defined(__LIBRETRO__)
33 #define HAVE_WIN32_CAMERA
34 #endif
35 
36 #ifdef HAVE_WIN32_CAMERA
37 #include "Common/CommonWindows.h"
38 #include "Windows/CaptureDevice.h"
39 #endif
40 
41 Camera::Config *config;
42 
43 unsigned int videoBufferLength = 0;
44 unsigned int nextVideoFrame = 0;
45 uint8_t *videoBuffer;
46 std::mutex videoBufferMutex;
47 
48 enum {
49 	VIDEO_BUFFER_SIZE = 40 * 1000,
50 };
51 
__UsbCamInit()52 void __UsbCamInit() {
53 	config       = new Camera::Config();
54 	config->mode = Camera::Mode::Unused;
55 	config->type = Camera::ConfigType::CfNone;
56 	videoBuffer  = new uint8_t[VIDEO_BUFFER_SIZE];
57 }
58 
__UsbCamDoState(PointerWrap & p)59 void __UsbCamDoState(PointerWrap &p) {
60 	auto s = p.Section("sceUsbCam", 0, 1);
61 	if (!s) {
62 		return;
63 	}
64 
65 	Do(p, *config);
66 	if (config->mode == Camera::Mode::Video) { // stillImage? TBD
67 		Camera::stopCapture();
68 		Camera::startCapture();
69 	}
70 }
71 
__UsbCamShutdown()72 void __UsbCamShutdown() {
73 	if (config->mode == Camera::Mode::Video) { // stillImage? TBD
74 		Camera::stopCapture();
75 	}
76 	delete[] videoBuffer;
77 	videoBuffer = nullptr;
78 	delete config;
79 	config = nullptr;
80 }
81 
82 // TODO: Technically, we should store the videoBuffer into the savestate, if this
83 // module has been initialized.
84 
getCameraResolution(Camera::ConfigType type,int * width,int * height)85 static int getCameraResolution(Camera::ConfigType type, int *width, int *height) {
86 	if (type == Camera::ConfigType::CfStill || type == Camera::ConfigType::CfVideo) {
87 		switch(config->stillParam.resolution) {
88 			case 0: *width  = 160; *height = 120; return 0;
89 			case 1: *width  = 176; *height = 144; return 0;
90 			case 2: *width  = 320; *height = 240; return 0;
91 			case 3: *width  = 352; *height = 288; return 0;
92 			case 4: *width  = 640; *height = 480; return 0;
93 			case 5: *width  =1024; *height = 768; return 0;
94 			case 6: *width  =1280; *height = 960; return 0;
95 			case 7: *width  = 480; *height = 272; return 0;
96 			case 8: *width  = 360; *height = 272; return 0;
97 		}
98 	} else if (type == Camera::ConfigType::CfStillEx || type == Camera::ConfigType::CfVideoEx) {
99 		switch(config->stillExParam.resolution) {
100 			case 0: *width  = 160; *height = 120; return 0;
101 			case 1: *width  = 176; *height = 144; return 0;
102 			case 2: *width  = 320; *height = 240; return 0;
103 			case 3: *width  = 352; *height = 288; return 0;
104 			case 4: *width  = 360; *height = 272; return 0;
105 			case 5: *width  = 480; *height = 272; return 0;
106 			case 6: *width  = 640; *height = 480; return 0;
107 			case 7: *width  =1024; *height = 768; return 0;
108 			case 8: *width  =1280; *height = 960; return 0;
109 		}
110 	}
111 	*width  = 0; *height = 0; return 1;
112 }
113 
114 
sceUsbCamSetupMic(u32 paramAddr,u32 workareaAddr,int wasize)115 static int sceUsbCamSetupMic(u32 paramAddr, u32 workareaAddr, int wasize) {
116 	INFO_LOG(HLE, "sceUsbCamSetupMic");
117 	if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupMicParam))) {
118 		Memory::ReadStruct(paramAddr, &config->micParam);
119 	}
120 	return 0;
121 }
122 
sceUsbCamStartMic()123 static int sceUsbCamStartMic() {
124 	INFO_LOG(HLE, "UNIMPL sceUsbCamStartMic");
125 	return 0;
126 }
127 
sceUsbCamStopMic()128 static int sceUsbCamStopMic() {
129 	INFO_LOG(HLE, "UNIMPL sceUsbCamStopMic");
130 	return 0;
131 }
132 
sceUsbCamReadMicBlocking(u32 bufAddr,u32 size)133 static int sceUsbCamReadMicBlocking(u32 bufAddr, u32 size) {
134 	if (!Memory::IsValidAddress(bufAddr)) {
135 		ERROR_LOG(HLE,"sceUsbCamReadMicBlocking(%08x, %d): invalid addresses", bufAddr, size);
136 		return -1;
137 	}
138 
139 	INFO_LOG(HLE, "sceUsbCamReadMicBlocking: size: %d", size);
140 	return __MicInput(size >> 1, config->micParam.frequency, bufAddr, CAMERAMIC);
141 }
142 
sceUsbCamReadMic(u32 bufAddr,u32 size)143 static int sceUsbCamReadMic(u32 bufAddr, u32 size) {
144 	if (!Memory::IsValidAddress(bufAddr)) {
145 		ERROR_LOG(HLE, "sceUsbCamReadMic(%08x, %d): invalid addresses", bufAddr, size);
146 		return -1;
147 	}
148 
149 	INFO_LOG(HLE, "sceUsbCamReadMic: size: %d", size);
150 	return __MicInput(size >> 1, config->micParam.frequency, bufAddr, CAMERAMIC, false);
151 }
152 
sceUsbCamGetMicDataLength()153 static int sceUsbCamGetMicDataLength() {
154 	return Microphone::getReadMicDataLength();
155 }
156 
sceUsbCamSetupVideo(u32 paramAddr,u32 workareaAddr,int wasize)157 static int sceUsbCamSetupVideo(u32 paramAddr, u32 workareaAddr, int wasize) {
158 	if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupVideoParam))) {
159 		Memory::ReadStruct(paramAddr, &config->videoParam);
160 	}
161 	config->type = Camera::ConfigType::CfVideo;
162 	return 0;
163 }
164 
sceUsbCamSetupVideoEx(u32 paramAddr,u32 workareaAddr,int wasize)165 static int sceUsbCamSetupVideoEx(u32 paramAddr, u32 workareaAddr, int wasize) {
166 	if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupVideoExParam))) {
167 		Memory::ReadStruct(paramAddr, &config->videoExParam);
168 	}
169 	config->type = Camera::ConfigType::CfVideoEx;
170 	return 0;
171 }
172 
sceUsbCamStartVideo()173 static int sceUsbCamStartVideo() {
174 	std::lock_guard<std::mutex> lock(videoBufferMutex);
175 
176 	int width, height;
177 	getCameraResolution(config->type, &width, &height);
178 
179 	unsigned char* jpegData = nullptr;
180 	int jpegLen = 0;
181 	__cameraDummyImage(width, height, &jpegData, &jpegLen);
182 	videoBufferLength = jpegLen;
183 	memset(videoBuffer, 0, VIDEO_BUFFER_SIZE);
184 	if (jpegData) {
185 		memcpy(videoBuffer, jpegData, jpegLen);
186 		free(jpegData);
187 		jpegData = nullptr;
188 	}
189 
190 	Camera::startCapture();
191 	return 0;
192 }
193 
sceUsbCamStopVideo()194 static int sceUsbCamStopVideo() {
195 	Camera::stopCapture();
196 	return 0;
197 }
198 
sceUsbCamReadVideoFrameBlocking(u32 bufAddr,u32 size)199 static int sceUsbCamReadVideoFrameBlocking(u32 bufAddr, u32 size) {
200 	std::lock_guard<std::mutex> lock(videoBufferMutex);
201 	u32 transferSize = std::min(videoBufferLength, size);
202 	if (Memory::IsValidRange(bufAddr, size)) {
203 		Memory::Memcpy(bufAddr, videoBuffer, transferSize);
204 	}
205 	return transferSize;
206 }
207 
sceUsbCamReadVideoFrame(u32 bufAddr,u32 size)208 static int sceUsbCamReadVideoFrame(u32 bufAddr, u32 size) {
209 	std::lock_guard<std::mutex> lock(videoBufferMutex);
210 	u32 transferSize = std::min(videoBufferLength, size);
211 	if (Memory::IsValidRange(bufAddr, size)) {
212 		Memory::Memcpy(bufAddr, videoBuffer, transferSize);
213 	}
214 	nextVideoFrame = transferSize;
215 	return 0;
216 }
217 
sceUsbCamPollReadVideoFrameEnd()218 static int sceUsbCamPollReadVideoFrameEnd() {
219 	VERBOSE_LOG(HLE, "UNIMPL sceUsbCamPollReadVideoFrameEnd: %d", nextVideoFrame);
220 	return nextVideoFrame;
221 }
222 
sceUsbCamSetupStill(u32 paramAddr)223 static int sceUsbCamSetupStill(u32 paramAddr) {
224 	INFO_LOG(HLE, "UNIMPL sceUsbCamSetupStill");
225 	if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupStillParam))) {
226 		Memory::ReadStruct(paramAddr, &config->stillParam);
227 	}
228 	config->type = Camera::ConfigType::CfStill;
229 	return 0;
230 }
231 
sceUsbCamSetupStillEx(u32 paramAddr)232 static int sceUsbCamSetupStillEx(u32 paramAddr) {
233 	INFO_LOG(HLE, "UNIMPL sceUsbCamSetupStillEx");
234 	if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupStillExParam))) {
235 		Memory::ReadStruct(paramAddr, &config->stillExParam);
236 	}
237 	config->type = Camera::ConfigType::CfStillEx;
238 	return 0;
239 }
240 
sceUsbCamAutoImageReverseSW(int on)241 static int sceUsbCamAutoImageReverseSW(int on) {
242 	INFO_LOG(HLE, "UNIMPL sceUsbCamAutoImageReverseSW: %d", on);
243 	return 0;
244 }
245 
sceUsbCamGetLensDirection()246 static int sceUsbCamGetLensDirection() {
247 	INFO_LOG(HLE, "UNIMPL sceUsbCamGetLensDirection");
248 	return 0;
249 }
250 
sceUsbCamSetReverseMode(int reverseflags)251 static int sceUsbCamSetReverseMode(int reverseflags) {
252 	INFO_LOG(HLE, "UNIMPL sceUsbCamSetReverseMode %d", reverseflags);
253 	return 0;
254 }
255 
256 const HLEFunction sceUsbCam[] =
257 {
258 	{ 0X03ED7A82, &WrapI_UUI<sceUsbCamSetupMic>,              "sceUsbCamSetupMic",                       'i', "xxi" },
259 	{ 0X2E930264, nullptr,                                    "sceUsbCamSetupMicEx",                     '?', "" },
260 	{ 0X82A64030, &WrapI_V<sceUsbCamStartMic>,                "sceUsbCamStartMic",                       'i', "" },
261 	{ 0X5145868A, &WrapI_V<sceUsbCamStopMic>,                 "sceUsbCamStopMic",                        'i', "" },
262 	{ 0X36636925, &WrapI_UU<sceUsbCamReadMicBlocking>,        "sceUsbCamReadMicBlocking",                'i', "xx" },
263 	{ 0X3DC0088E, &WrapI_UU<sceUsbCamReadMic>,                "sceUsbCamReadMic",                        'i', "xx" },
264 	{ 0XB048A67D, nullptr,                                    "sceUsbCamWaitReadMicEnd",                 '?', "" },
265 	{ 0XF8847F60, nullptr,                                    "sceUsbCamPollReadMicEnd",                 '?', "" },
266 	{ 0X5778B452, &WrapI_V<sceUsbCamGetMicDataLength>,        "sceUsbCamGetMicDataLength",               'i', "" },
267 	{ 0X08AEE98A, nullptr,                                    "sceUsbCamSetMicGain",                     '?', "" },
268 
269 	{ 0X17F7B2FB, &WrapI_UUI<sceUsbCamSetupVideo>,            "sceUsbCamSetupVideo",                     'i', "xxi" },
270 	{ 0XCFE9E999, &WrapI_UUI<sceUsbCamSetupVideoEx>,          "sceUsbCamSetupVideoEx",                   'i', "xxi" },
271 	{ 0X574A8C3F, &WrapI_V<sceUsbCamStartVideo>,              "sceUsbCamStartVideo",                     'i', "" },
272 	{ 0X6CF32CB9, &WrapI_V<sceUsbCamStopVideo>,               "sceUsbCamStopVideo",                      'i', "" },
273 	{ 0X7DAC0C71, &WrapI_UU<sceUsbCamReadVideoFrameBlocking>, "sceUsbCamReadVideoFrameBlocking",         'i', "xx" },
274 	{ 0X99D86281, &WrapI_UU<sceUsbCamReadVideoFrame>,         "sceUsbCamReadVideoFrame",                 'i', "xx" },
275 	{ 0XF90B2293, nullptr,                                    "sceUsbCamWaitReadVideoFrameEnd",          '?', "" },
276 	{ 0X41E73E95, &WrapI_V<sceUsbCamPollReadVideoFrameEnd>,   "sceUsbCamPollReadVideoFrameEnd",          'i', "" },
277 	{ 0XDF9D0C92, nullptr,                                    "sceUsbCamGetReadVideoFrameSize",          '?', "" },
278 
279 	{ 0X3F0CF289, &WrapI_U<sceUsbCamSetupStill>,              "sceUsbCamSetupStill",                     'i', "x" },
280 	{ 0X0A41A298, &WrapI_U<sceUsbCamSetupStillEx>,            "sceUsbCamSetupStillEx",                   'i', "x" },
281 	{ 0X61BE5CAC, nullptr,                                    "sceUsbCamStillInputBlocking",             '?', "" },
282 	{ 0XFB0A6C5D, nullptr,                                    "sceUsbCamStillInput",                     '?', "" },
283 	{ 0X7563AFA1, nullptr,                                    "sceUsbCamStillWaitInputEnd",              '?', "" },
284 	{ 0X1A46CFE7, nullptr,                                    "sceUsbCamStillPollInputEnd",              '?', "" },
285 	{ 0XA720937C, nullptr,                                    "sceUsbCamStillCancelInput",               '?', "" },
286 	{ 0XE5959C36, nullptr,                                    "sceUsbCamStillGetInputLength",            '?', "" },
287 
288 	{ 0XF93C4669, &WrapI_I<sceUsbCamAutoImageReverseSW>,      "sceUsbCamAutoImageReverseSW",             'i', "i" },
289 	{ 0X11A1F128, nullptr,                                    "sceUsbCamGetAutoImageReverseState",       '?', "" },
290 	{ 0X4C34F553, &WrapI_V<sceUsbCamGetLensDirection>,        "sceUsbCamGetLensDirection",               'i', "" },
291 
292 	{ 0X383E9FA8, nullptr,                                    "sceUsbCamGetSaturation",                  '?', "" },
293 	{ 0X6E205974, nullptr,                                    "sceUsbCamSetSaturation",                  '?', "" },
294 	{ 0X70F522C5, nullptr,                                    "sceUsbCamGetBrightness",                  '?', "" },
295 	{ 0X4F3D84D5, nullptr,                                    "sceUsbCamSetBrightness",                  '?', "" },
296 	{ 0XA063A957, nullptr,                                    "sceUsbCamGetContrast",                    '?', "" },
297 	{ 0X09C26C7E, nullptr,                                    "sceUsbCamSetContrast",                    '?', "" },
298 	{ 0XFDB68C23, nullptr,                                    "sceUsbCamGetSharpness",                   '?', "" },
299 	{ 0X622F83CC, nullptr,                                    "sceUsbCamSetSharpness",                   '?', "" },
300 	{ 0X994471E0, nullptr,                                    "sceUsbCamGetImageEffectMode",             '?', "" },
301 	{ 0XD4876173, nullptr,                                    "sceUsbCamSetImageEffectMode",             '?', "" },
302 	{ 0X2BCD50C0, nullptr,                                    "sceUsbCamGetEvLevel",                     '?', "" },
303 	{ 0X1D686870, nullptr,                                    "sceUsbCamSetEvLevel",                     '?', "" },
304 	{ 0XD5279339, nullptr,                                    "sceUsbCamGetReverseMode",                 '?', "" },
305 	{ 0X951BEDF5, &WrapI_I<sceUsbCamSetReverseMode>,          "sceUsbCamSetReverseMode",                 'i', "i" },
306 	{ 0X9E8AAF8D, nullptr,                                    "sceUsbCamGetZoom",                        '?', "" },
307 	{ 0XC484901F, nullptr,                                    "sceUsbCamSetZoom",                        '?', "" },
308 	{ 0XAA7D94BA, nullptr,                                    "sceUsbCamGetAntiFlicker",                 '?', "" },
309 	{ 0X6784E6A8, nullptr,                                    "sceUsbCamSetAntiFlicker",                 '?', "" },
310 
311 	{ 0XD293A100, nullptr,                                    "sceUsbCamRegisterLensRotationCallback",   '?', "" },
312 	{ 0X41EE8797, nullptr,                                    "sceUsbCamUnregisterLensRotationCallback", '?', "" },
313 };
314 
Register_sceUsbCam()315 void Register_sceUsbCam()
316 {
317 	RegisterModule("sceUsbCam", ARRAY_SIZE(sceUsbCam), sceUsbCam);
318 }
319 
getDeviceList()320 std::vector<std::string> Camera::getDeviceList() {
321 	#ifdef HAVE_WIN32_CAMERA
322 		if (winCamera) {
323 			return winCamera->getDeviceList();
324 		}
325 	#elif PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS)
326 		return __cameraGetDeviceList();
327 	#elif defined(USING_QT_UI) // Qt:macOS / Qt:Linux
328 		return __qt_getDeviceList();
329 	#elif PPSSPP_PLATFORM(LINUX) // SDL:Linux
330 		return __v4l_getDeviceList();
331 	#endif
332 	return std::vector<std::string>();
333 }
334 
startCapture()335 int Camera::startCapture() {
336 	int width, height;
337 	getCameraResolution(config->type, &width, &height);
338 	INFO_LOG(HLE, "%s resolution: %dx%d", __FUNCTION__, width, height);
339 
340 	config->mode = Camera::Mode::Video;
341 	#ifdef HAVE_WIN32_CAMERA
342 		if (winCamera) {
343 			if (winCamera->isShutDown()) {
344 				delete winCamera;
345 				winCamera = new WindowsCaptureDevice(CAPTUREDEVIDE_TYPE::VIDEO);
346 				winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::INITIALIZE, nullptr });
347 			}
348 			void* resolution = static_cast<void*>(new std::vector<int>({ width, height }));
349 			winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::START, resolution });
350 		}
351 	#elif PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) || defined(USING_QT_UI)
352 		char command[40] = {0};
353 		snprintf(command, sizeof(command), "startVideo_%dx%d", width, height);
354 		System_SendMessage("camera_command", command);
355 	#elif PPSSPP_PLATFORM(LINUX)
356 		__v4l_startCapture(width, height);
357 	#else
358 		ERROR_LOG(HLE, "%s not implemented", __FUNCTION__);
359 	#endif
360 	return 0;
361 }
362 
stopCapture()363 int Camera::stopCapture() {
364 	INFO_LOG(HLE, "%s", __FUNCTION__);
365 	#ifdef HAVE_WIN32_CAMERA
366 		if (winCamera) {
367 			winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::STOP, nullptr });
368 		}
369 	#elif PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) || defined(USING_QT_UI)
370 		System_SendMessage("camera_command", "stopVideo");
371 	#elif PPSSPP_PLATFORM(LINUX)
372 		__v4l_stopCapture();
373 	#else
374 		ERROR_LOG(HLE, "%s not implemented", __FUNCTION__);
375 	#endif
376 	config->mode = Camera::Mode::Unused;
377 	return 0;
378 }
379 
onCameraDeviceChange()380 void Camera::onCameraDeviceChange() {
381 	if (config != nullptr && config->mode == Camera::Mode::Video) {
382 		stopCapture();
383 		startCapture();
384 	}
385 }
386 
pushCameraImage(long long length,unsigned char * image)387 void Camera::pushCameraImage(long long length, unsigned char* image) {
388 	std::lock_guard<std::mutex> lock(videoBufferMutex);
389 	if (!videoBuffer) {
390 		return;
391 	}
392 	memset(videoBuffer, 0, VIDEO_BUFFER_SIZE);
393 	if (length > VIDEO_BUFFER_SIZE) {
394 		videoBufferLength = 0;
395 		ERROR_LOG(HLE, "pushCameraImage: length error: %lld > %d", length, VIDEO_BUFFER_SIZE);
396 	} else {
397 		videoBufferLength = length;
398 		memcpy(videoBuffer, image, length);
399 	}
400 }
401