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 be539cb115
commit 1a659ae4b6
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
*/

View File

@ -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();

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
* 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

View File

@ -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;

View File

@ -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"

View File

@ -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
}