Commit 87b3a36a authored by Christian Fremerey's avatar Christian Fremerey Committed by Commit Bot

[Image Capture, Android] Fix various threading issues

This CL simplifies the threading of class VideoCaptureCamera2.java
and by doing so resolves potential issues caused by concurrent
access to member variables and Android video API calls.

See https://bugs.chromium.org/p/chromium/issues/detail?id=857530 for
details on issues.

The simplified model is to (still) have the constructor and public API
calls happen on a native thread, and to use a single dedicated thread
owned by the class instance to post to, do work on, and call back into
the native code.

This CL is part of a series, see Design Doc at
https://docs.google.com/document/d/1h1kva4VR1gaV3HVXaSYZFY41icfaB58j-WnHmJdyqc8/edit?usp=sharing

Bug: 857530
Change-Id: I75ffcc4a14f2395d833d80f300acef7b456676e8
Reviewed-on: https://chromium-review.googlesource.com/1117857
Commit-Queue: Christian Fremerey <chfremer@chromium.org>
Reviewed-by: default avatarEmircan Uysaler <emircan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#576155}
parent 00d64e2e
...@@ -62,14 +62,22 @@ public abstract class VideoCapture { ...@@ -62,14 +62,22 @@ public abstract class VideoCapture {
@CalledByNative @CalledByNative
public abstract boolean allocate(int width, int height, int frameRate); public abstract boolean allocate(int width, int height, int frameRate);
// Success is indicated by returning true and a callback to
// nativeOnStarted(), which may occur synchronously or asynchronously.
// Failure can be indicated by one of the following:
// * Returning false. In this case no callback to nativeOnStarted() is made.
// * Returning true, and asynchronously invoking nativeOnError. In this case
// also no callback to nativeOnStarted() is made.
@CalledByNative @CalledByNative
public abstract boolean startCapture(); public abstract boolean startCaptureMaybeAsync();
// Blocks until it is guaranteed that no more frames are sent.
@CalledByNative @CalledByNative
public abstract boolean stopCapture(); public abstract boolean stopCaptureAndBlockUntilStopped();
// Replies by calling nativeOnGetPhotoCapabilitiesReply().
@CalledByNative @CalledByNative
public abstract PhotoCapabilities getPhotoCapabilities(); public abstract void getPhotoCapabilitiesAsync(long callbackId);
/** /**
* @param zoom Zoom level, should be ignored if 0. * @param zoom Zoom level, should be ignored if 0.
...@@ -95,8 +103,9 @@ public abstract class VideoCapture { ...@@ -95,8 +103,9 @@ public abstract class VideoCapture {
boolean hasRedEyeReduction, boolean redEyeReduction, int fillLightMode, boolean hasRedEyeReduction, boolean redEyeReduction, int fillLightMode,
boolean hasTorch, boolean torch, double colorTemperature); boolean hasTorch, boolean torch, double colorTemperature);
// Replies by calling nativeOnPhotoTaken().
@CalledByNative @CalledByNative
public abstract boolean takePhoto(final long callbackId); public abstract void takePhotoAsync(long callbackId);
@CalledByNative @CalledByNative
public abstract void deallocate(); public abstract void deallocate();
...@@ -164,6 +173,12 @@ public abstract class VideoCapture { ...@@ -164,6 +173,12 @@ public abstract class VideoCapture {
return orientation; return orientation;
} }
// {@link nativeOnPhotoTaken()} needs to be called back if there's any
// problem after {@link takePhotoAsync()} has returned true.
protected void notifyTakePhotoError(long callbackId) {
nativeOnPhotoTaken(mNativeVideoCaptureDeviceAndroid, callbackId, null);
}
/** /**
* Finds the framerate range matching |targetFramerate|. Tries to find a range with as low of a * Finds the framerate range matching |targetFramerate|. Tries to find a range with as low of a
* minimum value as possible to allow the camera adjust based on the lighting conditions. * minimum value as possible to allow the camera adjust based on the lighting conditions.
...@@ -230,10 +245,17 @@ public abstract class VideoCapture { ...@@ -230,10 +245,17 @@ public abstract class VideoCapture {
// Method for VideoCapture implementations to signal an asynchronous error. // Method for VideoCapture implementations to signal an asynchronous error.
public native void nativeOnError(long nativeVideoCaptureDeviceAndroid, String message); public native void nativeOnError(long nativeVideoCaptureDeviceAndroid, String message);
// Method for VideoCapture implementations to send Photos back to. public native void nativeOnGetPhotoCapabilitiesReply(
long nativeVideoCaptureDeviceAndroid, long callbackId, PhotoCapabilities result);
// Callback for calls to takePhoto(). This can indicate both success and
// failure. Failure is indicated by |data| being null.
public native void nativeOnPhotoTaken( public native void nativeOnPhotoTaken(
long nativeVideoCaptureDeviceAndroid, long callbackId, byte[] data); long nativeVideoCaptureDeviceAndroid, long callbackId, byte[] data);
// Method for VideoCapture implementations to report device started event. // Method for VideoCapture implementations to report device started event.
public native void nativeOnStarted(long nativeVideoCaptureDeviceAndroid); public native void nativeOnStarted(long nativeVideoCaptureDeviceAndroid);
public native void nativeDCheckCurrentlyOnIncomingTaskRunner(
long nativeVideoCaptureDeviceAndroid);
} }
...@@ -146,8 +146,7 @@ public class VideoCaptureCamera ...@@ -146,8 +146,7 @@ public class VideoCaptureCamera
synchronized (mPhotoTakenCallbackLock) { synchronized (mPhotoTakenCallbackLock) {
if (mPhotoTakenCallbackId == 0) return; if (mPhotoTakenCallbackId == 0) return;
nativeOnPhotoTaken( notifyTakePhotoError(mPhotoTakenCallbackId);
mNativeVideoCaptureDeviceAndroid, mPhotoTakenCallbackId, new byte[0]);
mPhotoTakenCallbackId = 0; mPhotoTakenCallbackId = 0;
} }
} }
...@@ -425,9 +424,9 @@ public class VideoCaptureCamera ...@@ -425,9 +424,9 @@ public class VideoCaptureCamera
} }
@Override @Override
public boolean startCapture() { public boolean startCaptureMaybeAsync() {
if (mCamera == null) { if (mCamera == null) {
Log.e(TAG, "startCapture: mCamera is null"); Log.e(TAG, "startCaptureAsync: mCamera is null");
return false; return false;
} }
...@@ -444,7 +443,7 @@ public class VideoCaptureCamera ...@@ -444,7 +443,7 @@ public class VideoCaptureCamera
try { try {
mCamera.startPreview(); mCamera.startPreview();
} catch (RuntimeException ex) { } catch (RuntimeException ex) {
Log.e(TAG, "startCapture: Camera.startPreview: " + ex); Log.e(TAG, "startCaptureAsync: Camera.startPreview: " + ex);
return false; return false;
} }
...@@ -459,9 +458,9 @@ public class VideoCaptureCamera ...@@ -459,9 +458,9 @@ public class VideoCaptureCamera
} }
@Override @Override
public boolean stopCapture() { public boolean stopCaptureAndBlockUntilStopped() {
if (mCamera == null) { if (mCamera == null) {
Log.e(TAG, "stopCapture: mCamera is null"); Log.e(TAG, "stopCaptureAndBlockUntilStopped: mCamera is null");
return true; return true;
} }
...@@ -481,7 +480,7 @@ public class VideoCaptureCamera ...@@ -481,7 +480,7 @@ public class VideoCaptureCamera
} }
@Override @Override
public PhotoCapabilities getPhotoCapabilities() { public void getPhotoCapabilitiesAsync(long callbackId) {
final android.hardware.Camera.Parameters parameters = getCameraParameters(mCamera); final android.hardware.Camera.Parameters parameters = getCameraParameters(mCamera);
PhotoCapabilities.Builder builder = new PhotoCapabilities.Builder(); PhotoCapabilities.Builder builder = new PhotoCapabilities.Builder();
Log.i(TAG, " CAM params: %s", parameters.flatten()); Log.i(TAG, " CAM params: %s", parameters.flatten());
...@@ -634,7 +633,8 @@ public class VideoCaptureCamera ...@@ -634,7 +633,8 @@ public class VideoCaptureCamera
builder.setFillLightModes(integerArrayListToArray(modes)); builder.setFillLightModes(integerArrayListToArray(modes));
} }
return builder.build(); nativeOnGetPhotoCapabilitiesReply(
mNativeVideoCaptureDeviceAndroid, callbackId, builder.build());
} }
@Override @Override
...@@ -776,15 +776,19 @@ public class VideoCaptureCamera ...@@ -776,15 +776,19 @@ public class VideoCaptureCamera
} }
@Override @Override
public boolean takePhoto(final long callbackId) { public void takePhotoAsync(final long callbackId) {
if (mCamera == null || !mIsRunning) { if (mCamera == null || !mIsRunning) {
Log.e(TAG, "takePhoto: mCamera is null or is not running"); Log.e(TAG, "takePhotoAsync: mCamera is null or is not running");
return false; notifyTakePhotoError(callbackId);
return;
} }
// Only one picture can be taken at once. // Only one picture can be taken at once.
synchronized (mPhotoTakenCallbackLock) { synchronized (mPhotoTakenCallbackLock) {
if (mPhotoTakenCallbackId != 0) return false; if (mPhotoTakenCallbackId != 0) {
notifyTakePhotoError(callbackId);
return;
}
mPhotoTakenCallbackId = callbackId; mPhotoTakenCallbackId = callbackId;
} }
mPreviewParameters = getCameraParameters(mCamera); mPreviewParameters = getCameraParameters(mCamera);
...@@ -817,18 +821,18 @@ public class VideoCaptureCamera ...@@ -817,18 +821,18 @@ public class VideoCaptureCamera
mCamera.setParameters(photoParameters); mCamera.setParameters(photoParameters);
} catch (RuntimeException ex) { } catch (RuntimeException ex) {
Log.e(TAG, "setParameters " + ex); Log.e(TAG, "setParameters " + ex);
return false; notifyTakePhotoError(callbackId);
return;
} }
mCamera.takePicture(null, null, null, new CrPictureCallback()); mCamera.takePicture(null, null, null, new CrPictureCallback());
return true;
} }
@Override @Override
public void deallocate() { public void deallocate() {
if (mCamera == null) return; if (mCamera == null) return;
stopCapture(); stopCaptureAndBlockUntilStopped();
try { try {
mCamera.setPreviewTexture(null); mCamera.setPreviewTexture(null);
if (mGlTextures != null) GLES20.glDeleteTextures(1, mGlTextures, 0); if (mGlTextures != null) GLES20.glDeleteTextures(1, mGlTextures, 0);
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
namespace base { namespace base {
class Location; class Location;
class SingleThreadTaskRunner; class SingleThreadTaskRunner;
} } // namespace base
namespace media { namespace media {
...@@ -91,6 +91,12 @@ class CAPTURE_EXPORT VideoCaptureDeviceAndroid : public VideoCaptureDevice { ...@@ -91,6 +91,12 @@ class CAPTURE_EXPORT VideoCaptureDeviceAndroid : public VideoCaptureDevice {
const base::android::JavaParamRef<jobject>& obj, const base::android::JavaParamRef<jobject>& obj,
const base::android::JavaParamRef<jstring>& message); const base::android::JavaParamRef<jstring>& message);
void OnGetPhotoCapabilitiesReply(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
jlong callback_id,
jobject photo_capabilities);
// Implement org.chromium.media.VideoCapture.nativeOnPhotoTaken. // Implement org.chromium.media.VideoCapture.nativeOnPhotoTaken.
void OnPhotoTaken(JNIEnv* env, void OnPhotoTaken(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj, const base::android::JavaParamRef<jobject>& obj,
...@@ -100,6 +106,12 @@ class CAPTURE_EXPORT VideoCaptureDeviceAndroid : public VideoCaptureDevice { ...@@ -100,6 +106,12 @@ class CAPTURE_EXPORT VideoCaptureDeviceAndroid : public VideoCaptureDevice {
// Implement org.chromium.media.VideoCapture.nativeOnStarted. // Implement org.chromium.media.VideoCapture.nativeOnStarted.
void OnStarted(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj); void OnStarted(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
// Implement
// org.chromium.media.VideoCapture.nativeDCheckCurrentlyOnIncomingTaskRunner.
void DCheckCurrentlyOnIncomingTaskRunner(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj);
void ConfigureForTesting(); void ConfigureForTesting();
protected: protected:
...@@ -152,9 +164,10 @@ class CAPTURE_EXPORT VideoCaptureDeviceAndroid : public VideoCaptureDevice { ...@@ -152,9 +164,10 @@ class CAPTURE_EXPORT VideoCaptureDeviceAndroid : public VideoCaptureDevice {
base::TimeTicks expected_next_frame_time_; base::TimeTicks expected_next_frame_time_;
base::TimeDelta frame_interval_; base::TimeDelta frame_interval_;
// List of |photo_callbacks_| in flight, being served in Java side. // List of callbacks for photo API in flight, being served in Java side.
base::Lock photo_callbacks_lock_; base::Lock photo_callbacks_lock_;
std::list<std::unique_ptr<TakePhotoCallback>> photo_callbacks_; std::list<std::unique_ptr<GetPhotoStateCallback>> get_photo_state_callbacks_;
std::list<std::unique_ptr<TakePhotoCallback>> take_photo_callbacks_;
const VideoCaptureDeviceDescriptor device_descriptor_; const VideoCaptureDeviceDescriptor device_descriptor_;
VideoCaptureFormat capture_format_; VideoCaptureFormat capture_format_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment