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:
zhupengfei 2020-04-18 19:50:31 +08:00 committed by bunnei
parent d0612bd3bf
commit b95f628f98
6 changed files with 106 additions and 45 deletions

View File

@ -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 * Button type for use in onTouchEvent
*/ */

View File

@ -202,6 +202,12 @@ public final class EmulationActivity extends AppCompatActivity {
NativeLibrary.retryDisplayAlertPrompt(); NativeLibrary.retryDisplayAlertPrompt();
} }
@Override
public void onRestart() {
super.onRestart();
NativeLibrary.ReloadCameraDevices();
}
@Override @Override
public void onBackPressed() { public void onBackPressed() {
NativeLibrary.PauseEmulation(); NativeLibrary.PauseEmulation();

View File

@ -26,11 +26,13 @@ namespace Camera::NDK {
* The pixel format is 'Android 420' which can contain a variety of YUV420 formats. The exact * 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'. * format used can be determined by examing the 'pixel stride'.
*/ */
class CaptureSession final { struct CaptureSession final {
public: explicit CaptureSession(ACameraManager* manager, const std::string& id) {
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{}; std::pair<int, int> selected_resolution{};
ACameraDevice_StateCallbacks device_callbacks{}; ACameraDevice_StateCallbacks device_callbacks{};
@ -48,9 +50,7 @@ private:
MEMBER(ACameraDevice, device, close); MEMBER(ACameraDevice, device, close);
MEMBER(AImageReader, image_reader, delete); MEMBER(AImageReader, image_reader, delete);
MEMBER(ANativeWindow, native_window, release);
// This window doesn't need to be destructed as it is managed by AImageReader
ANativeWindow* native_window{};
MEMBER(ACaptureSessionOutputContainer, outputs, free); MEMBER(ACaptureSessionOutputContainer, outputs, free);
MEMBER(ACaptureSessionOutput, output, free); MEMBER(ACaptureSessionOutput, output, free);
@ -74,12 +74,19 @@ private:
int sensor_orientation{}; // Sensor Orientation int sensor_orientation{}; // Sensor Orientation
bool facing_front{}; // Whether this camera faces front. Used for handling device rotation. bool facing_front{}; // Whether this camera faces front. Used for handling device rotation.
friend class Interface; std::mutex status_mutex;
friend void ImageCallback(void* context, AImageReader* reader); bool disconnected{}; // Whether this device has been closed and should be reopened
bool reload_requested{};
}; };
static void OnDisconnected(void* context, ACameraDevice* device) { void OnDisconnected(void* context, ACameraDevice* device) {
LOG_ERROR(Service_CAM, "Camera device disconnected"); 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) { 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) { void ImageCallback(void* context, AImageReader* reader) {
AImage* image{}; AImage* image{};
MEDIA_CALL(AImageReader_acquireLatestImage(reader, &image)) MEDIA_CALL(AImageReader_acquireLatestImage(reader, &image));
SCOPE_EXIT({ AImage_delete(image); }); SCOPE_EXIT({ AImage_delete(image); });
std::array<std::shared_ptr<u8>, 3> data; std::array<std::shared_ptr<u8>, 3> data;
@ -115,7 +122,6 @@ void ImageCallback(void* context, AImageReader* reader) {
u8* ptr; u8* ptr;
int size; int size;
MEDIA_CALL(AImage_getPlaneData(image, plane, &ptr, &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[]>()); data[plane].reset(new u8[size], std::default_delete<u8[]>());
std::memcpy(data[plane].get(), ptr, static_cast<std::size_t>(size)); 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); 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}; std::lock_guard lock{that->data_mutex};
that->data = data; that->data = data;
that->row_stride = row_stride; that->row_stride = row_stride;
MEDIA_CALL(AImage_getPlanePixelStride(image, 1, &that->pixel_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) \ #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 OnReady(void* context, ACameraCaptureSession* session) {}
static void OnActive(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 = { device_callbacks = {
/*context*/ nullptr, /*context*/ this,
/*onDisconnected*/ &OnDisconnected, /*onDisconnected*/ &OnDisconnected,
/*onError*/ &OnError, /*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_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, CREATE(ACaptureSessionOutput, output,
CAMERA_CALL(ACaptureSessionOutput_create(native_window, &raw))); CAMERA_CALL(ACaptureSessionOutput_create(native_window.get(), &raw)));
CREATE(ACaptureSessionOutputContainer, outputs, CREATE(ACaptureSessionOutputContainer, outputs,
CAMERA_CALL(ACaptureSessionOutputContainer_create(&raw))); CAMERA_CALL(ACaptureSessionOutputContainer_create(&raw)));
@ -231,9 +246,8 @@ CaptureSession::CaptureSession(ACameraManager* manager, const std::string& id) {
CREATE(ACaptureRequest, request, CREATE(ACaptureRequest, request,
CAMERA_CALL(ACameraDevice_createCaptureRequest(device.get(), TEMPLATE_PREVIEW, &raw))); CAMERA_CALL(ACameraDevice_createCaptureRequest(device.get(), TEMPLATE_PREVIEW, &raw)));
ANativeWindow_acquire(native_window);
CREATE(ACameraOutputTarget, target, 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())); CAMERA_CALL(ACaptureRequest_addTarget(request.get(), target.get()));
requests = {request.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 image.y.data(), image.width, image.u.data(), image.width / 2, image.v.data(), image.width / 2
std::vector<u16> Interface::ReceiveFrame() { 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<std::shared_ptr<u8>, 3> data;
std::array<int, 3> row_stride; 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>(); 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 } // namespace Camera::NDK

View File

@ -13,7 +13,7 @@
namespace Camera::NDK { namespace Camera::NDK {
class CaptureSession; struct CaptureSession;
class Factory; class Factory;
class Interface : public CameraInterface { class Interface : public CameraInterface {
@ -31,7 +31,6 @@ public:
bool IsPreviewAvailable() override; bool IsPreviewAvailable() override;
private: private:
// jstring path;
Factory& factory; Factory& factory;
std::shared_ptr<CaptureSession> session; std::shared_ptr<CaptureSession> session;
std::string id; std::string id;
@ -45,8 +44,6 @@ private:
bool invert{}; bool invert{};
Service::CAM::OutputFormat format; 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' // Placeholders to mean 'use any front/back camera'
@ -61,8 +58,12 @@ public:
std::unique_ptr<CameraInterface> Create(const std::string& config, std::unique_ptr<CameraInterface> Create(const std::string& config,
const Service::CAM::Flip& flip) override; 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: 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_requested = false;
bool camera_permission_granted = false; bool camera_permission_granted = false;

View File

@ -88,6 +88,8 @@ static int AlertPromptButton() {
IDCache::GetAlertPromptButton())); IDCache::GetAlertPromptButton()));
} }
static Camera::NDK::Factory* g_ndk_factory{};
static Core::System::ResultStatus RunCitra(const std::string& filepath) { static Core::System::ResultStatus RunCitra(const std::string& filepath) {
// Citra core only supports a single running instance // Citra core only supports a single running instance
std::lock_guard<std::mutex> lock(running_mutex); std::lock_guard<std::mutex> lock(running_mutex);
@ -110,7 +112,10 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
Settings::Apply(); Settings::Apply();
Camera::RegisterFactory("image", std::make_unique<Camera::StillImage::Factory>()); 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 // Register frontend applets
Frontend::RegisterDefaultApplets(); Frontend::RegisterDefaultApplets();
@ -458,4 +463,10 @@ jobjectArray Java_org_citra_citra_1emu_NativeLibrary_GetTextureFilterNames(JNIEn
return ret; return ret;
} }
void Java_org_citra_citra_1emu_NativeLibrary_ReloadCameraDevices(JNIEnv* env, jclass clazz) {
if (g_ndk_factory) {
g_ndk_factory->ReloadCameraDevices();
}
}
} // extern "C" } // extern "C"

View File

@ -49,15 +49,15 @@ JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetTitle(JNIEn
jclass clazz, jclass clazz,
jstring j_filename); jstring j_filename);
JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetDescription(JNIEnv* env, JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetDescription(
jclass clazz, JNIEnv* env, jclass clazz, jstring j_filename);
jstring j_filename);
JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetGameId(JNIEnv* env, JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetGameId(JNIEnv* env,
jclass clazz, jclass clazz,
jstring j_filename); jstring j_filename);
JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetRegions(JNIEnv* env, jclass clazz, JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetRegions(JNIEnv* env,
jclass clazz,
jstring j_filename); jstring j_filename);
JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetCompany(JNIEnv* env, JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetCompany(JNIEnv* env,
@ -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, JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetGitRevision(JNIEnv* env,
jclass clazz); jclass clazz);
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_SetUserDirectory(JNIEnv* env, JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_SetUserDirectory(
jclass clazz, JNIEnv* env, jclass clazz, jstring j_directory);
jstring j_directory);
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_utils_DirectoryInitialization_SetSysDirectory( JNIEXPORT void JNICALL Java_org_citra_citra_1emu_utils_DirectoryInitialization_SetSysDirectory(
JNIEnv* env, jclass clazz, jstring path_); JNIEnv* env, jclass clazz, jstring path_);
@ -112,14 +111,16 @@ JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_SurfaceChanged(JN
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_SurfaceDestroyed(JNIEnv* env, JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_SurfaceDestroyed(JNIEnv* env,
jclass clazz); 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); jstring j_game_id);
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_ReloadSettings(JNIEnv* env, JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_ReloadSettings(JNIEnv* env,
jclass clazz); jclass clazz);
JNIEXPORT void JNICALL Java_org_citra_citra_1emu_NativeLibrary_SetUserSetting( 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( 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);
@ -127,8 +128,11 @@ JNIEXPORT jstring JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetUserSetting
JNIEXPORT jdoubleArray JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetPerfStats(JNIEnv* env, JNIEXPORT jdoubleArray JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetPerfStats(JNIEnv* env,
jclass clazz); jclass clazz);
JNIEXPORT jobjectArray JNICALL Java_org_citra_citra_1emu_NativeLibrary_GetTextureFilterNames( JNIEXPORT jobjectArray JNICALL
JNIEnv* env, jclass clazz); 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 #ifdef __cplusplus
} }