Add camera/location support for iOS

Add front camera support for Android
Allow to change the active camera in-game
This commit is contained in:
Florin9doi 2020-01-09 15:57:09 +02:00
parent 1d6d66ac43
commit 5fbf2d7713
24 changed files with 409 additions and 44 deletions

View File

@ -745,6 +745,10 @@ elseif(IOS)
ios/ViewController.h ios/ViewController.h
ios/iOSCoreAudio.mm ios/iOSCoreAudio.mm
ios/iOSCoreAudio.h ios/iOSCoreAudio.h
ios/CameraHelper.mm
ios/CameraHelper.h
ios/LocationHelper.mm
ios/LocationHelper.h
ios/PPSSPPUIApplication.h ios/PPSSPPUIApplication.h
ios/PPSSPPUIApplication.mm ios/PPSSPPUIApplication.mm
ios/SmartKeyboardMap.cpp ios/SmartKeyboardMap.cpp
@ -755,7 +759,7 @@ elseif(IOS)
ios/iCade/iCadeReaderView.m ios/iCade/iCadeReaderView.m
ios/iCade/iCadeState.h ios/iCade/iCadeState.h
) )
set(nativeExtraLibs ${nativeExtraLibs} "-framework Foundation -framework MediaPlayer -framework AudioToolbox -framework CoreGraphics -framework QuartzCore -framework UIKit -framework GLKit -framework OpenAL -framework AVFoundation") set(nativeExtraLibs ${nativeExtraLibs} "-framework Foundation -framework MediaPlayer -framework AudioToolbox -framework CoreGraphics -framework QuartzCore -framework UIKit -framework GLKit -framework OpenAL -framework AVFoundation -framework CoreLocation -framework CoreVideo -framework CoreMedia" )
if(EXISTS "${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks/GameController.framework") if(EXISTS "${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks/GameController.framework")
set(nativeExtraLibs ${nativeExtraLibs} "-weak_framework GameController") set(nativeExtraLibs ${nativeExtraLibs} "-weak_framework GameController")
endif() endif()
@ -770,7 +774,8 @@ elseif(IOS)
set_source_files_properties(ios/PPSSPPUIApplication.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) set_source_files_properties(ios/PPSSPPUIApplication.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
set_source_files_properties(ios/iCade/iCadeReaderView.m PROPERTIES COMPILE_FLAGS -fobjc-arc) set_source_files_properties(ios/iCade/iCadeReaderView.m PROPERTIES COMPILE_FLAGS -fobjc-arc)
set_source_files_properties(ios/main.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) set_source_files_properties(ios/main.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
set_source_files_properties(ios/CameraHelper.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
set_source_files_properties(ios/LocationHelper.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)
set(TargetBin PPSSPP) set(TargetBin PPSSPP)
elseif(USING_QT_UI) elseif(USING_QT_UI)

View File

@ -707,9 +707,7 @@ static ConfigSetting graphicsSettings[] = {
#ifdef _WIN32 #ifdef _WIN32
ConfigSetting("D3D11Device", &g_Config.sD3D11Device, "", true, false), ConfigSetting("D3D11Device", &g_Config.sD3D11Device, "", true, false),
#endif #endif
#if (defined(_WIN32) && !PPSSPP_PLATFORM(UWP)) || PPSSPP_PLATFORM(LINUX)
ConfigSetting("CameraDevice", &g_Config.sCameraDevice, "", true, false), ConfigSetting("CameraDevice", &g_Config.sCameraDevice, "", true, false),
#endif
ConfigSetting("VendorBugChecksEnabled", &g_Config.bVendorBugChecksEnabled, true, false, false), ConfigSetting("VendorBugChecksEnabled", &g_Config.bVendorBugChecksEnabled, true, false, false),
ReportedConfigSetting("RenderingMode", &g_Config.iRenderingMode, 1, true, true), ReportedConfigSetting("RenderingMode", &g_Config.iRenderingMode, 1, true, true),
ConfigSetting("SoftwareRenderer", &g_Config.bSoftwareRendering, false, true, true), ConfigSetting("SoftwareRenderer", &g_Config.bSoftwareRendering, false, true, true),

View File

@ -169,6 +169,7 @@ void __KernelShutdown()
kernelObjects.Clear(); kernelObjects.Clear();
__UsbCamShutdown(); __UsbCamShutdown();
__UsbGpsShutdown();
__AudioCodecShutdown(); __AudioCodecShutdown();
__VideoPmpShutdown(); __VideoPmpShutdown();

View File

@ -205,8 +205,18 @@ static int sceUsbCamSetupStillEx(u32 paramAddr) {
return 0; return 0;
} }
static int sceUsbCamAutoImageReverseSW(int rev) { static int sceUsbCamAutoImageReverseSW(int on) {
INFO_LOG(HLE, "UNIMPL sceUsbCamAutoImageReverseSW"); INFO_LOG(HLE, "UNIMPL sceUsbCamAutoImageReverseSW: %d", on);
return 0;
}
static int sceUsbCamGetLensDirection() {
INFO_LOG(HLE, "UNIMPL sceUsbCamGetLensDirection");
return 0;
}
static int sceUsbCamSetReverseMode(int reverseflags) {
INFO_LOG(HLE, "UNIMPL sceUsbCamSetReverseMode %d", reverseflags);
return 0; return 0;
} }
@ -244,7 +254,7 @@ const HLEFunction sceUsbCam[] =
{ 0XF93C4669, &WrapI_I<sceUsbCamAutoImageReverseSW>, "sceUsbCamAutoImageReverseSW", 'i', "i" }, { 0XF93C4669, &WrapI_I<sceUsbCamAutoImageReverseSW>, "sceUsbCamAutoImageReverseSW", 'i', "i" },
{ 0X11A1F128, nullptr, "sceUsbCamGetAutoImageReverseState", '?', "" }, { 0X11A1F128, nullptr, "sceUsbCamGetAutoImageReverseState", '?', "" },
{ 0X4C34F553, nullptr, "sceUsbCamGetLensDirection", '?', "" }, { 0X4C34F553, &WrapI_V<sceUsbCamGetLensDirection>, "sceUsbCamGetLensDirection", 'i', "" },
{ 0X383E9FA8, nullptr, "sceUsbCamGetSaturation", '?', "" }, { 0X383E9FA8, nullptr, "sceUsbCamGetSaturation", '?', "" },
{ 0X6E205974, nullptr, "sceUsbCamSetSaturation", '?', "" }, { 0X6E205974, nullptr, "sceUsbCamSetSaturation", '?', "" },
@ -259,7 +269,7 @@ const HLEFunction sceUsbCam[] =
{ 0X2BCD50C0, nullptr, "sceUsbCamGetEvLevel", '?', "" }, { 0X2BCD50C0, nullptr, "sceUsbCamGetEvLevel", '?', "" },
{ 0X1D686870, nullptr, "sceUsbCamSetEvLevel", '?', "" }, { 0X1D686870, nullptr, "sceUsbCamSetEvLevel", '?', "" },
{ 0XD5279339, nullptr, "sceUsbCamGetReverseMode", '?', "" }, { 0XD5279339, nullptr, "sceUsbCamGetReverseMode", '?', "" },
{ 0X951BEDF5, nullptr, "sceUsbCamSetReverseMode", '?', "" }, { 0X951BEDF5, &WrapI_I<sceUsbCamSetReverseMode>, "sceUsbCamSetReverseMode", 'i', "i" },
{ 0X9E8AAF8D, nullptr, "sceUsbCamGetZoom", '?', "" }, { 0X9E8AAF8D, nullptr, "sceUsbCamGetZoom", '?', "" },
{ 0XC484901F, nullptr, "sceUsbCamSetZoom", '?', "" }, { 0XC484901F, nullptr, "sceUsbCamSetZoom", '?', "" },
{ 0XAA7D94BA, nullptr, "sceUsbCamGetAntiFlicker", '?', "" }, { 0XAA7D94BA, nullptr, "sceUsbCamGetAntiFlicker", '?', "" },
@ -275,7 +285,9 @@ void Register_sceUsbCam()
} }
std::vector<std::string> Camera::getDeviceList() { std::vector<std::string> Camera::getDeviceList() {
#if PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID) #if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS)
return __cameraGetDeviceList();
#elif PPSSPP_PLATFORM(LINUX)
return __v4l_getDeviceList(); return __v4l_getDeviceList();
#elif defined(_WIN32) && !PPSSPP_PLATFORM(UWP) #elif defined(_WIN32) && !PPSSPP_PLATFORM(UWP)
if (winCamera) { if (winCamera) {
@ -291,7 +303,7 @@ int Camera::startCapture() {
INFO_LOG(HLE, "%s resolution: %dx%d", __FUNCTION__, width, height); INFO_LOG(HLE, "%s resolution: %dx%d", __FUNCTION__, width, height);
config->mode = Camera::Mode::Video; config->mode = Camera::Mode::Video;
#if PPSSPP_PLATFORM(ANDROID) #if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS)
System_SendMessage("camera_command", "startVideo"); System_SendMessage("camera_command", "startVideo");
#elif PPSSPP_PLATFORM(LINUX) #elif PPSSPP_PLATFORM(LINUX)
__v4l_startCapture(width, height); __v4l_startCapture(width, height);
@ -312,7 +324,7 @@ int Camera::startCapture() {
int Camera::stopCapture() { int Camera::stopCapture() {
INFO_LOG(HLE, "%s", __FUNCTION__); INFO_LOG(HLE, "%s", __FUNCTION__);
#if PPSSPP_PLATFORM(ANDROID) #if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS)
System_SendMessage("camera_command", "stopVideo"); System_SendMessage("camera_command", "stopVideo");
#elif PPSSPP_PLATFORM(LINUX) #elif PPSSPP_PLATFORM(LINUX)
__v4l_stopCapture(); __v4l_stopCapture();
@ -328,6 +340,13 @@ int Camera::stopCapture() {
return 0; return 0;
} }
void Camera::onCameraDeviceChange() {
if (config != nullptr && config->mode == Camera::Mode::Video) {
stopCapture();
startCapture();
}
}
void Camera::pushCameraImage(long long length, unsigned char* image) { void Camera::pushCameraImage(long long length, unsigned char* image) {
std::lock_guard<std::mutex> lock(videoBufferMutex); std::lock_guard<std::mutex> lock(videoBufferMutex);
if (!videoBuffer) { if (!videoBuffer) {

View File

@ -117,6 +117,7 @@ namespace Camera {
} Config; } Config;
std::vector<std::string> getDeviceList(); std::vector<std::string> getDeviceList();
void onCameraDeviceChange();
int startCapture(); int startCapture();
int stopCapture(); int stopCapture();
void pushCameraImage(long long length, unsigned char *image); void pushCameraImage(long long length, unsigned char *image);

View File

@ -43,6 +43,15 @@ void __UsbGpsDoState(PointerWrap &p) {
p.Do(gpsStatus); p.Do(gpsStatus);
} }
void __UsbGpsShutdown() {
gpsStatus = GPS_STATE_OFF;
System_SendMessage("gps_command", "close");
};
static int sceUsbGpsGetInitDataLocation(u32 addr) {
return 0;
}
static int sceUsbGpsGetState(u32 stateAddr) { static int sceUsbGpsGetState(u32 stateAddr) {
if (Memory::IsValidAddress(stateAddr)) { if (Memory::IsValidAddress(stateAddr)) {
Memory::Write_U32(gpsStatus, stateAddr); Memory::Write_U32(gpsStatus, stateAddr);
@ -79,7 +88,7 @@ const HLEFunction sceUsbGps[] =
{ {
{0X268F95CA, nullptr, "sceUsbGpsSetInitDataLocation", '?', "" }, {0X268F95CA, nullptr, "sceUsbGpsSetInitDataLocation", '?', "" },
{0X31F95CDE, nullptr, "sceUsbGpsGetPowerSaveMode", '?', "" }, {0X31F95CDE, nullptr, "sceUsbGpsGetPowerSaveMode", '?', "" },
{0X54D26AA4, nullptr, "sceUsbGpsGetInitDataLocation", '?', "" }, {0X54D26AA4, &WrapI_U<sceUsbGpsGetInitDataLocation>, "sceUsbGpsGetInitDataLocation", 'i', "x" },
{0X63D1F89D, nullptr, "sceUsbGpsResetInitialPosition", '?', "" }, {0X63D1F89D, nullptr, "sceUsbGpsResetInitialPosition", '?', "" },
{0X69E4AAA8, nullptr, "sceUsbGpsSaveInitData", '?', "" }, {0X69E4AAA8, nullptr, "sceUsbGpsSaveInitData", '?', "" },
{0X6EED4811, &WrapI_V<sceUsbGpsClose>, "sceUsbGpsClose", 'i', "" }, {0X6EED4811, &WrapI_V<sceUsbGpsClose>, "sceUsbGpsClose", 'i', "" },

View File

@ -21,6 +21,7 @@ void Register_sceUsbGps();
void __UsbGpsInit(); void __UsbGpsInit();
void __UsbGpsDoState(PointerWrap &p); void __UsbGpsDoState(PointerWrap &p);
void __UsbGpsShutdown();
#pragma pack(push, 1) #pragma pack(push, 1)

View File

@ -192,9 +192,10 @@ int __v4l_startCapture(int ideal_width, int ideal_height) {
frmsize.index++; frmsize.index++;
if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) { if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
INFO_LOG(HLE, "V4L2: frame size supported: %dx%d", frmsize.discrete.width, frmsize.discrete.height); INFO_LOG(HLE, "V4L2: frame size supported: %dx%d", frmsize.discrete.width, frmsize.discrete.height);
if (fmt.fmt.pix.width != 0 && fmt.fmt.pix.height != 0) { if (frmsize.discrete.width >= ideal_width && frmsize.discrete.height >= ideal_height
continue; && fmt.fmt.pix.width == 0 && fmt.fmt.pix.height == 0
} else if (frmsize.discrete.width >= ideal_width && frmsize.discrete.height >= ideal_height) { || frmsize.discrete.width >= ideal_width && frmsize.discrete.height >= ideal_height
&& frmsize.discrete.width < fmt.fmt.pix.width && frmsize.discrete.height < fmt.fmt.pix.height) {
fmt.fmt.pix.width = frmsize.discrete.width; fmt.fmt.pix.width = frmsize.discrete.width;
fmt.fmt.pix.height = frmsize.discrete.height; fmt.fmt.pix.height = frmsize.discrete.height;
} }

View File

@ -263,13 +263,12 @@ void GameSettingsScreen::CreateViews() {
softwareGPU->SetEnabled(false); softwareGPU->SetEnabled(false);
} }
#if (defined(_WIN32) && !PPSSPP_PLATFORM(UWP)) || PPSSPP_PLATFORM(LINUX)
std::vector<std::string> cameraList = Camera::getDeviceList(); std::vector<std::string> cameraList = Camera::getDeviceList();
if (cameraList.size() >= 1) { if (cameraList.size() >= 1) {
graphicsSettings->Add(new ItemHeader(gr->T("Camera"))); graphicsSettings->Add(new ItemHeader(gr->T("Camera")));
PopupMultiChoiceDynamic *cameraChoice = graphicsSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sCameraDevice, gr->T("Camera Device"), cameraList, nullptr, screenManager())); PopupMultiChoiceDynamic *cameraChoice = graphicsSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sCameraDevice, gr->T("Camera Device"), cameraList, nullptr, screenManager()));
cameraChoice->OnChoice.Handle(this, &GameSettingsScreen::OnCameraDeviceChange);
} }
#endif
graphicsSettings->Add(new ItemHeader(gr->T("Frame Rate Control"))); graphicsSettings->Add(new ItemHeader(gr->T("Frame Rate Control")));
static const char *frameSkip[] = {"Off", "1", "2", "3", "4", "5", "6", "7", "8"}; static const char *frameSkip[] = {"Off", "1", "2", "3", "4", "5", "6", "7", "8"};
@ -1200,6 +1199,11 @@ UI::EventReturn GameSettingsScreen::OnRenderingDevice(UI::EventParams &e) {
return UI::EVENT_DONE; return UI::EVENT_DONE;
} }
UI::EventReturn GameSettingsScreen::OnCameraDeviceChange(UI::EventParams& e) {
Camera::onCameraDeviceChange();
return UI::EVENT_DONE;
}
UI::EventReturn GameSettingsScreen::OnAudioDevice(UI::EventParams &e) { UI::EventReturn GameSettingsScreen::OnAudioDevice(UI::EventParams &e) {
I18NCategory *a = GetI18NCategory("Audio"); I18NCategory *a = GetI18NCategory("Audio");
if (g_Config.sAudioDevice == a->T("Auto")) { if (g_Config.sAudioDevice == a->T("Auto")) {

View File

@ -97,6 +97,7 @@ private:
UI::EventReturn OnRenderingMode(UI::EventParams &e); UI::EventReturn OnRenderingMode(UI::EventParams &e);
UI::EventReturn OnRenderingBackend(UI::EventParams &e); UI::EventReturn OnRenderingBackend(UI::EventParams &e);
UI::EventReturn OnRenderingDevice(UI::EventParams &e); UI::EventReturn OnRenderingDevice(UI::EventParams &e);
UI::EventReturn OnCameraDeviceChange(UI::EventParams& e);
UI::EventReturn OnAudioDevice(UI::EventParams &e); UI::EventReturn OnAudioDevice(UI::EventParams &e);
UI::EventReturn OnJitAffectingSetting(UI::EventParams &e); UI::EventReturn OnJitAffectingSetting(UI::EventParams &e);
#if PPSSPP_PLATFORM(ANDROID) #if PPSSPP_PLATFORM(ANDROID)

View File

@ -1358,7 +1358,7 @@ void NativeShutdown() {
// I think we handle most globals correctly or correct-enough now. // I think we handle most globals correctly or correct-enough now.
} }
void PushNewGpsData(float latitude, float longitude, float altitude, float speed, float bearing, long long time) { void SetGpsData(float latitude, float longitude, float altitude, float speed, float bearing, long long time) {
GPS::setGpsData(latitude, longitude, altitude, speed, bearing, time); GPS::setGpsData(latitude, longitude, altitude, speed, bearing, time);
} }

View File

@ -921,12 +921,40 @@ extern "C" jint JNICALL Java_org_ppsspp_ppsspp_NativeApp_getDesiredBackbufferHei
return desiredBackbufferSizeY; return desiredBackbufferSizeY;
} }
extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_pushNewGpsData(JNIEnv *, jclass,
jfloat latitude, jfloat longitude, jfloat altitude, jfloat speed, jfloat bearing, jlong time) { std::vector<std::string> __cameraGetDeviceList() {
PushNewGpsData(latitude, longitude, altitude, speed, bearing, time); jclass cameraClass = findClass("org/ppsspp/ppsspp/CameraHelper");
jmethodID deviceListMethod = getEnv()->GetStaticMethodID(cameraClass, "getDeviceList", "()Ljava/util/ArrayList;");
jobject deviceListObject = getEnv()->CallStaticObjectMethod(cameraClass, deviceListMethod);
jclass arrayListClass = getEnv()->FindClass("java/util/ArrayList");
jmethodID arrayListSize = getEnv()->GetMethodID(arrayListClass, "size", "()I");
jmethodID arrayListGet = getEnv()->GetMethodID(arrayListClass, "get", "(I)Ljava/lang/Object;");
jint arrayListObjectLen = getEnv()->CallIntMethod(deviceListObject, arrayListSize);
std::vector<std::string> deviceListVector;
for (int i=0; i < arrayListObjectLen; i++) {
jstring dev = static_cast<jstring>(getEnv()->CallObjectMethod(deviceListObject, arrayListGet, i));
const char* cdev = getEnv()->GetStringUTFChars(dev, nullptr);
deviceListVector.push_back(cdev);
getEnv()->ReleaseStringUTFChars(dev, cdev);
getEnv()->DeleteLocalRef(dev);
}
return deviceListVector;
} }
extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_pushCameraImage(JNIEnv *env, jclass, extern "C" jint Java_org_ppsspp_ppsspp_NativeApp_getSelectedCamera(JNIEnv *, jclass) {
int cameraId = 0;
sscanf(g_Config.sCameraDevice.c_str(), "%d:", &cameraId);
return cameraId;
}
extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_setGpsDataAndroid(JNIEnv *, jclass,
jfloat latitude, jfloat longitude, jfloat altitude, jfloat speed, jfloat bearing, jlong time) {
SetGpsData(latitude, longitude, altitude, speed, bearing, time);
}
extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_pushCameraImageAndroid(JNIEnv *env, jclass,
jbyteArray image) { jbyteArray image) {
if (image != NULL) { if (image != NULL) {

View File

@ -15,6 +15,7 @@ import android.view.WindowManager;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List; import java.util.List;
@TargetApi(23) @TargetApi(23)
@ -24,6 +25,7 @@ class CameraHelper {
private Display mDisplay; private Display mDisplay;
private Camera mCamera = null; private Camera mCamera = null;
private boolean mIsCameraRunning = false; private boolean mIsCameraRunning = false;
private int mCameraFacing = 0;
private int mCameraOrientation = 0; private int mCameraOrientation = 0;
private Camera.Size mPreviewSize = null; private Camera.Size mPreviewSize = null;
private long mLastFrameTime = 0; private long mLastFrameTime = 0;
@ -38,8 +40,12 @@ class CameraHelper {
case Surface.ROTATION_180: displayDegrees = 180; break; case Surface.ROTATION_180: displayDegrees = 180; break;
case Surface.ROTATION_270: displayDegrees = 270; break; case Surface.ROTATION_270: displayDegrees = 270; break;
} }
if (mCameraFacing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
return (mCameraOrientation + displayDegrees) % 360;
} else {
return (mCameraOrientation - displayDegrees + 360) % 360; return (mCameraOrientation - displayDegrees + 360) % 360;
} }
}
static byte[] rotateNV21(final byte[] input, final int inWidth, final int inHeight, static byte[] rotateNV21(final byte[] input, final int inWidth, final int inHeight,
final int outWidth, final int outHeight, final int rotation) { final int outWidth, final int outHeight, final int rotation) {
@ -117,7 +123,7 @@ class CameraHelper {
// convert to Jpeg // convert to Jpeg
Rect crop = new Rect(0, 0, targetW, targetH); Rect crop = new Rect(0, 0, targetW, targetH);
yuvImage.compressToJpeg(crop, 80, baos); yuvImage.compressToJpeg(crop, 80, baos);
NativeApp.pushCameraImage(baos.toByteArray()); NativeApp.pushCameraImageAndroid(baos.toByteArray());
try { try {
baos.close(); baos.close();
} catch (IOException e) { } catch (IOException e) {
@ -132,14 +138,29 @@ class CameraHelper {
mSurfaceTexture = new SurfaceTexture(10); mSurfaceTexture = new SurfaceTexture(10);
} }
static ArrayList<String> getDeviceList() {
ArrayList<String> deviceList = new ArrayList<>();
int nrCam = Camera.getNumberOfCameras();
for (int index = 0; index < nrCam; index++) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(index, info);
String devName = index + ":" + (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK ? "Back Camera" : "Front Camera");
deviceList.add(devName);
}
return deviceList;
}
void startCamera() { void startCamera() {
Log.d(TAG, "startCamera");
try { try {
Camera.CameraInfo info = new android.hardware.Camera.CameraInfo(); int cameraId = NativeApp.getSelectedCamera();
Camera.getCameraInfo(0, info); Log.d(TAG, "startCamera: " + cameraId);
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
mCameraFacing = info.facing;
mCameraOrientation = info.orientation; mCameraOrientation = info.orientation;
mCamera = Camera.open(); mCamera = Camera.open(cameraId);
Camera.Parameters param = mCamera.getParameters(); Camera.Parameters param = mCamera.getParameters();
// Set preview size // Set preview size

View File

@ -50,12 +50,13 @@ class LocationHelper implements LocationListener {
public void onLocationChanged(Location location) { public void onLocationChanged(Location location) {
float latitude = (float) location.getLatitude(); float latitude = (float) location.getLatitude();
float longitude = (float) location.getLongitude(); float longitude = (float) location.getLongitude();
// Android altitude is in meters above the WGS 84 reference ellipsoid
float altitude = (float) location.getAltitude(); float altitude = (float) location.getAltitude();
float speed = location.getSpeed(); float speed = location.getSpeed();
float bearing = location.getBearing(); float bearing = location.getBearing();
long time = location.getTime() / 1000; // ms to s !! long time = location.getTime() / 1000; // ms to s !!
NativeApp.pushNewGpsData(latitude, longitude, altitude, speed, bearing, time); NativeApp.setGpsDataAndroid(latitude, longitude, altitude, speed, bearing, time);
} }
@Override @Override

View File

@ -51,7 +51,7 @@ public class NativeApp {
public static native String queryConfig(String queryName); public static native String queryConfig(String queryName);
public static native void pushNewGpsData(float latitude, float longitude, float altitude, float speed, float bearing, long time); public static native int getSelectedCamera();
public static native void setGpsDataAndroid(float latitude, float longitude, float altitude, float speed, float bearing, long time);
public static native void pushCameraImage(byte[] image); public static native void pushCameraImageAndroid(byte[] image);
} }

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <string> #include <string>
#include <vector>
// The Native App API. // The Native App API.
// //
@ -174,5 +175,6 @@ std::string System_GetProperty(SystemProperty prop);
int System_GetPropertyInt(SystemProperty prop); int System_GetPropertyInt(SystemProperty prop);
bool System_GetPropertyBool(SystemProperty prop); bool System_GetPropertyBool(SystemProperty prop);
void PushNewGpsData(float latitude, float longitude, float altitude, float speed, float bearing, long long time); std::vector<std::string> __cameraGetDeviceList();
void SetGpsData(float latitude, float longitude, float altitude, float speed, float bearing, long long time);
void PushCameraImage(long long length, unsigned char* image); void PushCameraImage(long long length, unsigned char* image);

17
ios/CameraHelper.h Normal file
View File

@ -0,0 +1,17 @@
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
@protocol CameraFrameDelegate <NSObject>
@required
- (void) PushCameraImageIOS:(long long)len buffer:(unsigned char*)data;
@end
@interface CameraHelper : NSObject<AVCaptureVideoDataOutputSampleBufferDelegate>
@property (nonatomic, strong) id<CameraFrameDelegate> delegate;
- (int) checkPermission;
- (void) startVideo;
- (void) stopVideo;
@end

144
ios/CameraHelper.mm Normal file
View File

@ -0,0 +1,144 @@
#include <vector>
#include <string>
#include "Core/Config.h"
#import "CameraHelper.h"
#import <UIKit/UIKit.h>
@interface CameraHelper() {
AVCaptureSession *captureSession;
AVCaptureVideoPreviewLayer *previewLayer;
}
@end
@implementation CameraHelper
std::vector<std::string> __cameraGetDeviceList() {
std::vector<std::string> deviceList;
for (AVCaptureDevice *device in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) {
deviceList.push_back([device.localizedName UTF8String]);
}
return deviceList;
}
NSString *getSelectedCamera() {
NSString *selectedCamera = [NSString stringWithCString:g_Config.sCameraDevice.c_str() encoding:[NSString defaultCStringEncoding]];
return selectedCamera;
}
-(int) checkPermission {
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
NSLog(@"CameraHelper::checkPermission %ld", (long)status);
switch (status) {
case AVAuthorizationStatusNotDetermined: {
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if (granted) {
NSLog(@"camera permission granted");
dispatch_async(dispatch_get_main_queue(), ^{
[self startVideo];
});
} else {
NSLog(@"camera permission denied");
}
}];
return 1;
}
case AVAuthorizationStatusRestricted:
case AVAuthorizationStatusDenied: {
NSLog(@"camera permission denied");
return 1;
}
case AVAuthorizationStatusAuthorized: {
return 0;
}
}
}
-(void) startVideo {
NSLog(@"CameraHelper::startVideo");
if ([self checkPermission]) {
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
NSError *error = nil;
captureSession = [[AVCaptureSession alloc] init];
captureSession.sessionPreset = AVCaptureSessionPresetMedium;
AVCaptureDeviceInput *videoInput = nil;
NSString *selectedCamera = getSelectedCamera();
for (AVCaptureDevice *device in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) {
if ([device.localizedName isEqualToString:selectedCamera]) {
videoInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
}
}
if (videoInput == nil || error) {
NSLog(@"selectedCamera error; try default device");
AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if (videoDevice == nil) {
NSLog(@"videoDevice error");
return;
}
videoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error];
if (videoInput == nil) {
NSLog(@"videoInput error");
return;
}
}
[captureSession addInput:videoInput];
AVCaptureVideoDataOutput *videoOutput = [[AVCaptureVideoDataOutput alloc] init];
videoOutput.videoSettings = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey: (id)kCVPixelBufferPixelFormatTypeKey];
[captureSession addOutput:videoOutput];
dispatch_queue_t queue = dispatch_queue_create("cameraQueue", NULL);
[videoOutput setSampleBufferDelegate:self queue:queue];
previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:captureSession];
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
[previewLayer setFrame:CGRectMake(0, 0, 480, 272)];
[captureSession startRunning];
});
}
-(void) stopVideo {
dispatch_async(dispatch_get_main_queue(), ^{
[captureSession stopRunning];
});
}
- (void) captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection {
CGImageRef cgImage = [self imageFromSampleBuffer:sampleBuffer];
UIImage *theImage = [UIImage imageWithCGImage: cgImage];
CGImageRelease(cgImage);
NSData *imageData = UIImageJPEGRepresentation(theImage, 0.6);
[self.delegate PushCameraImageIOS:imageData.length buffer:(unsigned char*)imageData.bytes];
}
- (CGImageRef) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer {
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CVPixelBufferLockBaseAddress(imageBuffer,0);
void* baseAddress = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0);
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
CGImageRef newImage = CGBitmapContextCreateImage(newContext);
CGContextRelease(newContext);
CGColorSpaceRelease(colorSpace);
CVPixelBufferUnlockBaseAddress(imageBuffer,0);
return newImage;
}
@end

18
ios/LocationHelper.h Normal file
View File

@ -0,0 +1,18 @@
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
@protocol LocationHandlerDelegate <NSObject>
@required
- (void) SetGpsDataIOS:(CLLocation*)newLocation;
@end
@interface LocationHelper : NSObject<CLLocationManagerDelegate> {
CLLocationManager *locationManager;
}
@property(nonatomic,strong) id<LocationHandlerDelegate> delegate;
- (void) startLocationUpdates;
- (void) stopLocationUpdates;
@end

34
ios/LocationHelper.mm Normal file
View File

@ -0,0 +1,34 @@
#import "LocationHelper.h"
@interface LocationHelper()
@end
@implementation LocationHelper
-(id) init {
NSLog(@"LocationHelper::init");
locationManager = [[CLLocationManager alloc] init];
[locationManager setDelegate:self];
return self;
}
-(void) startLocationUpdates {
NSLog(@"LocationHelper::startLocationUpdates");
if ([locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
[locationManager requestWhenInUseAuthorization];
}
[locationManager startUpdatingLocation];
}
-(void) stopLocationUpdates {
NSLog(@"LocationHelper::stopLocationUpdates");
[locationManager stopUpdatingLocation];
}
- (void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
for (id location in locations) {
[self.delegate SetGpsDataIOS:location];
}
}
@end

View File

@ -4,6 +4,12 @@
<dict> <dict>
<key>UISupportsDocumentBrowser</key> <key>UISupportsDocumentBrowser</key>
<true/> <true/>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Your location may be used to emulate Go!Explore, a GPS accessory</string>
<key>NSCameraUsageDescription</key>
<string>Your camera may be used to emulate Go!Cam, a camera accessory</string>
<key>NSMicrophoneUsageDescription</key>
<string>Your microphone may be used to emulate Go!Cam/Talkman, a microphone accessory</string>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>en</string> <string>en</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>

View File

@ -6,11 +6,18 @@
#import <GameController/GameController.h> #import <GameController/GameController.h>
#endif #endif
#import "iCade/iCadeReaderView.h" #import "iCade/iCadeReaderView.h"
#import "CameraHelper.h"
#import "LocationHelper.h"
@interface ViewController : GLKViewController <iCadeEventDelegate> @interface ViewController : GLKViewController <iCadeEventDelegate,
LocationHandlerDelegate, CameraFrameDelegate>
- (void)shutdown; - (void)shutdown;
@end @end
extern __unsafe_unretained ViewController* sharedViewController; extern __unsafe_unretained ViewController* sharedViewController;
void startVideo();
void stopVideo();
void startLocation();
void stopLocation();

View File

@ -84,6 +84,8 @@ static bool threadStopped = false;
__unsafe_unretained ViewController* sharedViewController; __unsafe_unretained ViewController* sharedViewController;
static GraphicsContext *graphicsContext; static GraphicsContext *graphicsContext;
static CameraHelper *cameraHelper;
static LocationHelper *locationHelper;
@interface ViewController () { @interface ViewController () {
std::map<uint16_t, uint16_t> iCadeToKeyMap; std::map<uint16_t, uint16_t> iCadeToKeyMap;
@ -203,6 +205,12 @@ static GraphicsContext *graphicsContext;
[self.view addSubview:volume]; [self.view addSubview:volume];
[self.view bringSubviewToFront:volume]; [self.view bringSubviewToFront:volume];
cameraHelper = [[CameraHelper alloc] init];
[cameraHelper setDelegate:self];
locationHelper = [[LocationHelper alloc] init];
[locationHelper setDelegate:self];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NativeInitGraphics(graphicsContext); NativeInitGraphics(graphicsContext);
@ -667,6 +675,33 @@ static GraphicsContext *graphicsContext;
} }
#endif #endif
void startVideo() {
[cameraHelper startVideo];
}
void stopVideo() {
[cameraHelper stopVideo];
}
-(void) PushCameraImageIOS:(long long)len buffer:(unsigned char*)data {
PushCameraImage(len, data);
}
void startLocation() {
[locationHelper startLocationUpdates];
}
void stopLocation() {
[locationHelper stopLocationUpdates];
}
-(void) SetGpsDataIOS:(CLLocation *)newLocation {
NSLog(@"SetGpsDataIOS: speed: %f", newLocation.speed); // m/s
SetGpsData(newLocation.coordinate.latitude, newLocation.coordinate.longitude,
newLocation.altitude, 0 /* speed */, 0 /* bearing */,
(long long)newLocation.timestamp.timeIntervalSince1970);
}
@end @end
void OpenDirectory(const char *path) { void OpenDirectory(const char *path) {

View File

@ -112,6 +112,18 @@ void System_SendMessage(const char *command, const char *parameter) {
// [sharedViewController shutdown]; // [sharedViewController shutdown];
// exit(0); // exit(0);
// }); // });
} else if (!strcmp(command, "camera_command")) {
if (!strcmp(parameter, "startVideo")) {
startVideo();
} else if (!strcmp(parameter, "stopVideo")) {
stopVideo();
}
} else if (!strcmp(command, "gps_command")) {
if (!strcmp(parameter, "open")) {
startLocation();
} else if (!strcmp(parameter, "close")) {
stopLocation();
}
} }
} }