ndk_camera: Fixes
Removed debug logs and unused code Turned CaptureSession struct for simplicity Added support for camera reload Fixed ANativeWindow not released
This commit is contained in:
parent
d0612bd3bf
commit
b95f628f98
@ -438,6 +438,9 @@ public final class NativeLibrary {
|
||||
}
|
||||
}
|
||||
|
||||
/// Notifies that the activity is now in foreground and camera devices can now be reloaded
|
||||
public static native void ReloadCameraDevices();
|
||||
|
||||
/**
|
||||
* Button type for use in onTouchEvent
|
||||
*/
|
||||
|
@ -202,6 +202,12 @@ public final class EmulationActivity extends AppCompatActivity {
|
||||
NativeLibrary.retryDisplayAlertPrompt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestart() {
|
||||
super.onRestart();
|
||||
NativeLibrary.ReloadCameraDevices();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
NativeLibrary.PauseEmulation();
|
||||
|
@ -26,11 +26,13 @@ namespace Camera::NDK {
|
||||
* The pixel format is 'Android 420' which can contain a variety of YUV420 formats. The exact
|
||||
* format used can be determined by examing the 'pixel stride'.
|
||||
*/
|
||||
class CaptureSession final {
|
||||
public:
|
||||
explicit CaptureSession(ACameraManager* manager, const std::string& id);
|
||||
struct CaptureSession final {
|
||||
explicit CaptureSession(ACameraManager* manager, const std::string& id) {
|
||||
Load(manager, id);
|
||||
}
|
||||
|
||||
void Load(ACameraManager* manager, const std::string& id);
|
||||
|
||||
private:
|
||||
std::pair<int, int> selected_resolution{};
|
||||
|
||||
ACameraDevice_StateCallbacks device_callbacks{};
|
||||
@ -48,9 +50,7 @@ private:
|
||||
|
||||
MEMBER(ACameraDevice, device, close);
|
||||
MEMBER(AImageReader, image_reader, delete);
|
||||
|
||||
// This window doesn't need to be destructed as it is managed by AImageReader
|
||||
ANativeWindow* native_window{};
|
||||
MEMBER(ANativeWindow, native_window, release);
|
||||
|
||||
MEMBER(ACaptureSessionOutputContainer, outputs, free);
|
||||
MEMBER(ACaptureSessionOutput, output, free);
|
||||
@ -74,12 +74,19 @@ private:
|
||||
int sensor_orientation{}; // Sensor Orientation
|
||||
bool facing_front{}; // Whether this camera faces front. Used for handling device rotation.
|
||||
|
||||
friend class Interface;
|
||||
friend void ImageCallback(void* context, AImageReader* reader);
|
||||
std::mutex status_mutex;
|
||||
bool disconnected{}; // Whether this device has been closed and should be reopened
|
||||
bool reload_requested{};
|
||||
};
|
||||
|
||||
static void OnDisconnected(void* context, ACameraDevice* device) {
|
||||
LOG_ERROR(Service_CAM, "Camera device disconnected");
|
||||
void OnDisconnected(void* context, ACameraDevice* device) {
|
||||
LOG_WARNING(Service_CAM, "Camera device disconnected");
|
||||
|
||||
CaptureSession* that = reinterpret_cast<CaptureSession*>(context);
|
||||
{
|
||||
std::lock_guard lock{that->status_mutex};
|
||||
that->disconnected = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void OnError(void* context, ACameraDevice* device, int error) {
|
||||
@ -106,7 +113,7 @@ static void OnError(void* context, ACameraDevice* device, int error) {
|
||||
|
||||
void ImageCallback(void* context, AImageReader* reader) {
|
||||
AImage* image{};
|
||||
MEDIA_CALL(AImageReader_acquireLatestImage(reader, &image))
|
||||
MEDIA_CALL(AImageReader_acquireLatestImage(reader, &image));
|
||||
SCOPE_EXIT({ AImage_delete(image); });
|
||||
|
||||
std::array<std::shared_ptr<u8>, 3> data;
|
||||
@ -115,7 +122,6 @@ void ImageCallback(void* context, AImageReader* reader) {
|
||||
u8* ptr;
|
||||
int size;
|
||||
MEDIA_CALL(AImage_getPlaneData(image, plane, &ptr, &size));
|
||||
LOG_CRITICAL(Service_CAM, "Plane data for {} got, size {}", plane, size);
|
||||
data[plane].reset(new u8[size], std::default_delete<u8[]>());
|
||||
std::memcpy(data[plane].get(), ptr, static_cast<std::size_t>(size));
|
||||
|
||||
@ -123,16 +129,15 @@ void ImageCallback(void* context, AImageReader* reader) {
|
||||
}
|
||||
|
||||
CaptureSession* that = reinterpret_cast<CaptureSession*>(context);
|
||||
if (!that->ready) {
|
||||
that->active_event.Set(); // Mark the session as active
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard lock{that->data_mutex};
|
||||
that->data = data;
|
||||
that->row_stride = row_stride;
|
||||
MEDIA_CALL(AImage_getPlanePixelStride(image, 1, &that->pixel_stride));
|
||||
}
|
||||
if (!that->ready) {
|
||||
that->active_event.Set(); // Mark the session as active
|
||||
}
|
||||
}
|
||||
|
||||
#define CREATE(type, name, statement) \
|
||||
@ -147,9 +152,16 @@ static void OnClosed(void* context, ACameraCaptureSession* session) {}
|
||||
static void OnReady(void* context, ACameraCaptureSession* session) {}
|
||||
static void OnActive(void* context, ACameraCaptureSession* session) {}
|
||||
|
||||
CaptureSession::CaptureSession(ACameraManager* manager, const std::string& id) {
|
||||
void CaptureSession::Load(ACameraManager* manager, const std::string& id) {
|
||||
if (ready) {
|
||||
// If already open, we'd like to first shutdown the session
|
||||
// session.reset();
|
||||
// native_window.reset();
|
||||
}
|
||||
ready = disconnected = reload_requested = false;
|
||||
|
||||
device_callbacks = {
|
||||
/*context*/ nullptr,
|
||||
/*context*/ this,
|
||||
/*onDisconnected*/ &OnDisconnected,
|
||||
/*onError*/ &OnError,
|
||||
};
|
||||
@ -211,9 +223,12 @@ CaptureSession::CaptureSession(ACameraManager* manager, const std::string& id) {
|
||||
};
|
||||
MEDIA_CALL(AImageReader_setImageListener(image_reader.get(), &listener));
|
||||
|
||||
MEDIA_CALL(AImageReader_getWindow(image_reader.get(), &native_window));
|
||||
CREATE(ANativeWindow, native_window,
|
||||
MEDIA_CALL(AImageReader_getWindow(image_reader.get(), &raw)));
|
||||
ANativeWindow_acquire(native_window.get());
|
||||
|
||||
CREATE(ACaptureSessionOutput, output,
|
||||
CAMERA_CALL(ACaptureSessionOutput_create(native_window, &raw)));
|
||||
CAMERA_CALL(ACaptureSessionOutput_create(native_window.get(), &raw)));
|
||||
|
||||
CREATE(ACaptureSessionOutputContainer, outputs,
|
||||
CAMERA_CALL(ACaptureSessionOutputContainer_create(&raw)));
|
||||
@ -231,9 +246,8 @@ CaptureSession::CaptureSession(ACameraManager* manager, const std::string& id) {
|
||||
CREATE(ACaptureRequest, request,
|
||||
CAMERA_CALL(ACameraDevice_createCaptureRequest(device.get(), TEMPLATE_PREVIEW, &raw)));
|
||||
|
||||
ANativeWindow_acquire(native_window);
|
||||
CREATE(ACameraOutputTarget, target,
|
||||
CAMERA_CALL(ACameraOutputTarget_create(native_window, &raw)));
|
||||
CAMERA_CALL(ACameraOutputTarget_create(native_window.get(), &raw)));
|
||||
CAMERA_CALL(ACaptureRequest_addTarget(request.get(), target.get()));
|
||||
|
||||
requests = {request.get()};
|
||||
@ -316,6 +330,19 @@ struct YUVImage {
|
||||
image.y.data(), image.width, image.u.data(), image.width / 2, image.v.data(), image.width / 2
|
||||
|
||||
std::vector<u16> Interface::ReceiveFrame() {
|
||||
bool should_reload = false;
|
||||
{
|
||||
std::lock_guard lock{session->status_mutex};
|
||||
if (session->reload_requested) {
|
||||
session->reload_requested = false;
|
||||
should_reload = session->disconnected;
|
||||
}
|
||||
}
|
||||
if (should_reload) {
|
||||
LOG_INFO(Service_CAM, "Reloading camera session");
|
||||
session->Load(factory.manager.get(), id);
|
||||
}
|
||||
|
||||
std::array<std::shared_ptr<u8>, 3> data;
|
||||
std::array<int, 3> row_stride;
|
||||
{
|
||||
@ -472,4 +499,13 @@ std::unique_ptr<CameraInterface> Factory::Create(const std::string& config,
|
||||
return std::make_unique<Camera::BlankCamera>();
|
||||
}
|
||||
|
||||
void Factory::ReloadCameraDevices() {
|
||||
for (const auto& [id, ptr] : opened_camera_map) {
|
||||
if (auto session = ptr.lock()) {
|
||||
std::lock_guard lock{session->status_mutex};
|
||||
session->reload_requested = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Camera::NDK
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
namespace Camera::NDK {
|
||||
|
||||
class CaptureSession;
|
||||
struct CaptureSession;
|
||||
class Factory;
|
||||
|
||||
class Interface : public CameraInterface {
|
||||
@ -31,7 +31,6 @@ public:
|
||||
bool IsPreviewAvailable() override;
|
||||
|
||||
private:
|
||||
// jstring path;
|
||||
Factory& factory;
|
||||
std::shared_ptr<CaptureSession> session;
|
||||
std::string id;
|
||||
@ -45,8 +44,6 @@ private:
|
||||
bool invert{};
|
||||
|
||||
Service::CAM::OutputFormat format;
|
||||
// std::vector<u16> image; // Data fetched from the frontend
|
||||
// bool opened{}; // Whether the camera was successfully opened
|
||||
};
|
||||
|
||||
// Placeholders to mean 'use any front/back camera'
|
||||
@ -61,8 +58,12 @@ public:
|
||||
std::unique_ptr<CameraInterface> Create(const std::string& config,
|
||||
const Service::CAM::Flip& flip) override;
|
||||
|
||||
// Request the reopening of all previously disconnected camera devices.
|
||||
// Called when the application is brought to foreground (i.e. we have priority with the camera)
|
||||
void ReloadCameraDevices();
|
||||
|
||||
private:
|
||||
// Avoid requesting for permisson more than once on each call
|
||||
// Avoid requesting for permission more than once on each call
|
||||
bool camera_permission_requested = false;
|
||||
bool camera_permission_granted = false;
|
||||
|
||||
|
@ -88,6 +88,8 @@ static int AlertPromptButton() {
|
||||
IDCache::GetAlertPromptButton()));
|
||||
}
|
||||
|
||||
static Camera::NDK::Factory* g_ndk_factory{};
|
||||
|
||||
static Core::System::ResultStatus RunCitra(const std::string& filepath) {
|
||||
// Citra core only supports a single running instance
|
||||
std::lock_guard<std::mutex> lock(running_mutex);
|
||||
@ -110,7 +112,10 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
|
||||
Settings::Apply();
|
||||
|
||||
Camera::RegisterFactory("image", std::make_unique<Camera::StillImage::Factory>());
|
||||
Camera::RegisterFactory("ndk", std::make_unique<Camera::NDK::Factory>());
|
||||
|
||||
auto ndk_factory = std::make_unique<Camera::NDK::Factory>();
|
||||
g_ndk_factory = ndk_factory.get();
|
||||
Camera::RegisterFactory("ndk", std::move(ndk_factory));
|
||||
|
||||
// Register frontend applets
|
||||
Frontend::RegisterDefaultApplets();
|
||||
@ -458,4 +463,10 @@ jobjectArray Java_org_citra_citra_1emu_NativeLibrary_GetTextureFilterNames(JNIEn
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Java_org_citra_citra_1emu_NativeLibrary_ReloadCameraDevices(JNIEnv* env, jclass clazz) {
|
||||
if (g_ndk_factory) {
|
||||
g_ndk_factory->ReloadCameraDevices();
|
||||
}
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
@ -27,10 +27,10 @@ JNIEXPORT jboolean JNICALL Java_org_citra_citra_1emu_NativeLibrary_onGamePadEven
|
||||
JNIEnv* env, jclass clazz, jstring j_device, jint j_button, jint action);
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_org_citra_citra_1emu_NativeLibrary_onGamePadMoveEvent(
|
||||
JNIEnv* env, jclass clazz, jstring j_device, jint axis, jfloat x, jfloat y);
|
||||
JNIEnv* env, jclass clazz, jstring j_device, jint axis, jfloat x, jfloat y);
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_org_citra_citra_1emu_NativeLibrary_onGamePadAxisEvent(
|
||||
JNIEnv* env, jclass clazz, jstring j_device, jint axis_id, jfloat axis_val);
|
||||
JNIEnv* env, jclass clazz, jstring j_device, jint axis_id, jfloat axis_val);
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_onTouchEvent(JNIEnv* env,
|
||||
jclass clazz, jfloat x,
|
||||
@ -49,16 +49,16 @@ JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetTitle(JNIEn
|
||||
jclass clazz,
|
||||
jstring j_filename);
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetDescription(JNIEnv* env,
|
||||
jclass clazz,
|
||||
jstring j_filename);
|
||||
JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetDescription(
|
||||
JNIEnv* env, jclass clazz, jstring j_filename);
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetGameId(JNIEnv* env,
|
||||
jclass clazz,
|
||||
jstring j_filename);
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetRegions(JNIEnv* env, jclass clazz,
|
||||
jstring j_filename);
|
||||
JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetRegions(JNIEnv* env,
|
||||
jclass clazz,
|
||||
jstring j_filename);
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetCompany(JNIEnv* env,
|
||||
jclass clazz,
|
||||
@ -67,9 +67,8 @@ JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetCompany(JNI
|
||||
JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetGitRevision(JNIEnv* env,
|
||||
jclass clazz);
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_SetUserDirectory(JNIEnv* env,
|
||||
jclass clazz,
|
||||
jstring j_directory);
|
||||
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_SetUserDirectory(
|
||||
JNIEnv* env, jclass clazz, jstring j_directory);
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_utils_DirectoryInitialization_SetSysDirectory(
|
||||
JNIEnv* env, jclass clazz, jstring path_);
|
||||
@ -103,7 +102,7 @@ JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_Run__Ljava_lang_S
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_citra_citra_1emu_NativeLibrary_Run__Ljava_lang_String_2Ljava_lang_String_2Z(
|
||||
JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate);
|
||||
JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate);
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env,
|
||||
jclass clazz,
|
||||
@ -112,23 +111,28 @@ JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_SurfaceChanged(JN
|
||||
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_SurfaceDestroyed(JNIEnv* env,
|
||||
jclass clazz);
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_InitGameIni(JNIEnv* env, jclass clazz,
|
||||
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_InitGameIni(JNIEnv* env,
|
||||
jclass clazz,
|
||||
jstring j_game_id);
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_ReloadSettings(JNIEnv* env,
|
||||
jclass clazz);
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_SetUserSetting(
|
||||
JNIEnv* env, jclass clazz, jstring j_game_id, jstring j_section, jstring j_key, jstring j_value);
|
||||
JNIEnv* env, jclass clazz, jstring j_game_id, jstring j_section, jstring j_key,
|
||||
jstring j_value);
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetUserSetting(
|
||||
JNIEnv* env, jclass clazz, jstring game_id, jstring section, jstring key);
|
||||
JNIEnv* env, jclass clazz, jstring game_id, jstring section, jstring key);
|
||||
|
||||
JNIEXPORT jdoubleArray JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetPerfStats(JNIEnv* env,
|
||||
jclass clazz);
|
||||
|
||||
JNIEXPORT jobjectArray JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetTextureFilterNames(
|
||||
JNIEnv* env, jclass clazz);
|
||||
JNIEXPORT jobjectArray JNICALL
|
||||
Java_org_citra_citra_1emu_NativeLibrary_GetTextureFilterNames(JNIEnv* env, jclass clazz);
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_ReloadCameraDevices(JNIEnv* env,
|
||||
jclass clazz);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user