From 3db042e2f09f1df7d8b5d40f30766f780848ecd9 Mon Sep 17 00:00:00 2001 From: "perkj@webrtc.org" Date: Thu, 19 Feb 2015 08:43:38 +0000 Subject: [PATCH] Stop AndroidVideoCapturer asynchronously. The purpose is to avoid a deadlock between the C++ thread calling Stop and the Java thread that provides video frames. BUG=4318 R=glaznev@webrtc.org, magjed@webrtc.org Review URL: https://webrtc-codereview.appspot.com/35249004 Cr-Commit-Position: refs/heads/master@{#8425} git-svn-id: http://webrtc.googlecode.com/svn/trunk@8425 4adac7df-926f-26a2-2b94-8c16560cd09d --- .../org/webrtc/VideoCapturerAndroidTest.java | 22 ++- talk/app/webrtc/androidvideocapturer.cc | 23 +-- talk/app/webrtc/androidvideocapturer.h | 11 +- .../java/jni/androidvideocapturer_jni.cc | 134 +++++++++++++++--- .../java/jni/androidvideocapturer_jni.h | 16 ++- .../webrtc/java/jni/classreferenceholder.cc | 2 +- .../app/webrtc/java/jni/peerconnection_jni.cc | 11 +- .../src/org/webrtc/VideoCapturerAndroid.java | 120 ++++++++-------- 8 files changed, 217 insertions(+), 122 deletions(-) diff --git a/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java b/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java index 109c29489..fec3b50fa 100644 --- a/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java +++ b/talk/app/webrtc/androidtests/src/org/webrtc/VideoCapturerAndroidTest.java @@ -67,6 +67,7 @@ public class VideoCapturerAndroidTest extends ActivityTestCase { private int frameSize = 0; private Object frameLock = 0; private Object capturerStartLock = 0; + private Object capturerStopLock = 0; private boolean captureStartResult = false; @Override @@ -77,6 +78,13 @@ public class VideoCapturerAndroidTest extends ActivityTestCase { } } + @Override + public void OnCapturerStopped() { + synchronized (capturerStopLock) { + capturerStopLock.notify(); + } + } + @Override public void OnFrameCaptured(byte[] data, int rotation, long timeStamp) { synchronized (frameLock) { @@ -93,6 +101,12 @@ public class VideoCapturerAndroidTest extends ActivityTestCase { } } + public void WaitForCapturerToStop() throws InterruptedException { + synchronized (capturerStopLock) { + capturerStopLock.wait(); + } + } + public int WaitForNextCapturedFrame() throws InterruptedException { synchronized (frameLock) { frameLock.wait(); @@ -124,6 +138,7 @@ public class VideoCapturerAndroidTest extends ActivityTestCase { assertTrue(callbacks.WaitForNextFrameToRender() > 0); track.dispose(); source.dispose(); + factory.dispose(); } @Override @@ -199,6 +214,7 @@ public class VideoCapturerAndroidTest extends ActivityTestCase { assertTrue(callbacks.WaitForNextFrameToRender() > 0); track.dispose(); source.dispose(); + factory.dispose(); } @SmallTest @@ -223,6 +239,7 @@ public class VideoCapturerAndroidTest extends ActivityTestCase { assertEquals(MediaSource.State.LIVE, source.state()); track.dispose(); source.dispose(); + factory.dispose(); } @SmallTest @@ -242,9 +259,10 @@ public class VideoCapturerAndroidTest extends ActivityTestCase { getInstrumentation().getContext(), observer); assertTrue(observer.WaitForCapturerToStart()); observer.WaitForNextCapturedFrame(); - // Check the frame size. NV21 is assumed. + // Check the frame size. assertEquals((format.width*format.height*3)/2, observer.frameSize()); - assertTrue(capturer.stopCapture()); + capturer.stopCapture(); + observer.WaitForCapturerToStop(); } capturer.dispose(); } diff --git a/talk/app/webrtc/androidvideocapturer.cc b/talk/app/webrtc/androidvideocapturer.cc index 3264421db..02f77dbb8 100644 --- a/talk/app/webrtc/androidvideocapturer.cc +++ b/talk/app/webrtc/androidvideocapturer.cc @@ -27,7 +27,6 @@ #include "talk/app/webrtc/androidvideocapturer.h" #include "talk/media/webrtc/webrtcvideoframe.h" -#include "webrtc/base/bind.h" #include "webrtc/base/common.h" #include "webrtc/base/json.h" #include "webrtc/base/timeutils.h" @@ -101,7 +100,7 @@ AndroidVideoCapturer::AndroidVideoCapturer( delegate_(delegate.Pass()), worker_thread_(NULL), frame_factory_(NULL), - current_state_(cricket::CS_STOPPED){ + current_state_(cricket::CS_STOPPED) { std::string json_string = delegate_->GetSupportedFormats(); LOG(LS_INFO) << json_string; @@ -174,13 +173,6 @@ bool AndroidVideoCapturer::GetPreferredFourccs(std::vector* fourccs) { } void AndroidVideoCapturer::OnCapturerStarted(bool success) { - // This method is called from a Java thread. - DCHECK(!worker_thread_->IsCurrent()); - worker_thread_->Invoke( - rtc::Bind(&AndroidVideoCapturer::OnCapturerStarted_w, this, success)); -} - -void AndroidVideoCapturer::OnCapturerStarted_w(bool success) { DCHECK(worker_thread_->IsCurrent()); cricket::CaptureState new_state = success ? cricket::CS_RUNNING : cricket::CS_FAILED; @@ -194,21 +186,10 @@ void AndroidVideoCapturer::OnCapturerStarted_w(bool success) { SignalStateChange(this, new_state); } -void AndroidVideoCapturer::OnIncomingFrame(signed char* videoFrame, +void AndroidVideoCapturer::OnIncomingFrame(signed char* frame_data, int length, int rotation, int64 time_stamp) { - // This method is called from a Java thread. - DCHECK(!worker_thread_->IsCurrent()); - worker_thread_->Invoke( - rtc::Bind(&AndroidVideoCapturer::OnIncomingFrame_w, this, videoFrame, - length, rotation, time_stamp)); -} - -void AndroidVideoCapturer::OnIncomingFrame_w(signed char* frame_data, - int length, - int rotation, - int64 time_stamp) { DCHECK(worker_thread_->IsCurrent()); frame_factory_->UpdateCapturedFrame(frame_data, length, rotation, time_stamp); SignalFrameCaptured(this, frame_factory_->GetCapturedFrame()); diff --git a/talk/app/webrtc/androidvideocapturer.h b/talk/app/webrtc/androidvideocapturer.h index ef72cd6f7..ed31cd2e1 100644 --- a/talk/app/webrtc/androidvideocapturer.h +++ b/talk/app/webrtc/androidvideocapturer.h @@ -44,9 +44,9 @@ class AndroidVideoCapturerDelegate { virtual void Start(int width, int height, int framerate, AndroidVideoCapturer* capturer) = 0; - // Stops capturing. The implementation must synchronously stop the capturer. + // Stops capturing. // The delegate may not call into AndroidVideoCapturer after this call. - virtual bool Stop() = 0; + virtual void Stop() = 0; // Must returns a JSON string "{{width=xxx, height=xxx, framerate = xxx}}" virtual std::string GetSupportedFormats() = 0; @@ -74,13 +74,6 @@ class AndroidVideoCapturer : public cricket::VideoCapturer { AndroidVideoCapturerDelegate* delegate() { return delegate_.get(); } private: - void OnCapturerStarted_w(bool success); - - void OnIncomingFrame_w(signed char* frame_data, - int length, - int rotation, - int64 time_stamp); - // cricket::VideoCapturer implementation. // Video frames will be delivered using // cricket::VideoCapturer::SignalFrameCaptured on the thread that calls Start. diff --git a/talk/app/webrtc/java/jni/androidvideocapturer_jni.cc b/talk/app/webrtc/java/jni/androidvideocapturer_jni.cc index 4f575b1b4..6e9710f24 100644 --- a/talk/app/webrtc/java/jni/androidvideocapturer_jni.cc +++ b/talk/app/webrtc/java/jni/androidvideocapturer_jni.cc @@ -29,11 +29,70 @@ #include "talk/app/webrtc/java/jni/androidvideocapturer_jni.h" #include "talk/app/webrtc/java/jni/classreferenceholder.h" +#include "webrtc/base/bind.h" namespace webrtc_jni { jobject AndroidVideoCapturerJni::application_context_ = nullptr; +// JavaCaptureProxy is responsible for marshaling calls from the +// Java VideoCapturerAndroid to the C++ class AndroidVideoCapturer. +// Calls from Java occur on a Java thread and are marshaled to +// AndroidVideoCapturer on the thread that creates an instance of this object. +// +// An instance is created when AndroidVideoCapturerJni::Start is called and +// ownership is passed to an instance of the Java class NativeObserver. +// JavaCaptureProxy is destroyed when NativeObserver has reported that the +// capturer has stopped, see +// VideoCapturerAndroid_00024NativeObserver_nativeCapturerStopped. +// Marshaling is done as long as JavaCaptureProxy has a pointer to the +// AndroidVideoCapturer. +class JavaCaptureProxy { + public: + JavaCaptureProxy() : thread_(rtc::Thread::Current()), capturer_(nullptr) { + } + + ~JavaCaptureProxy() { + } + + void SetAndroidCapturer(webrtc::AndroidVideoCapturer* capturer) { + DCHECK(thread_->IsCurrent()); + capturer_ = capturer; + } + + void OnCapturerStarted(bool success) { + thread_->Invoke( + rtc::Bind(&JavaCaptureProxy::OnCapturerStarted_w, this, success)); + } + + void OnIncomingFrame(signed char* video_frame, + int length, + int rotation, + int64 time_stamp) { + thread_->Invoke( + rtc::Bind(&JavaCaptureProxy::OnIncomingFrame_w, this, video_frame, + length, rotation, time_stamp)); + } + + private: + void OnCapturerStarted_w(bool success) { + DCHECK(thread_->IsCurrent()); + if (capturer_) + capturer_->OnCapturerStarted(success); + } + void OnIncomingFrame_w(signed char* video_frame, + int length, + int rotation, + int64 time_stamp) { + DCHECK(thread_->IsCurrent()); + if (capturer_) + capturer_->OnIncomingFrame(video_frame, length, rotation, time_stamp); + } + + rtc::Thread* thread_; + webrtc::AndroidVideoCapturer* capturer_; +}; + // static int AndroidVideoCapturerJni::SetAndroidObjects(JNIEnv* jni, jobject appliction_context) { @@ -50,24 +109,50 @@ AndroidVideoCapturerJni::AndroidVideoCapturerJni(JNIEnv* jni, : j_capturer_global_(jni, j_video_capturer), j_video_capturer_class_( jni, FindClass(jni, "org/webrtc/VideoCapturerAndroid")), - j_frame_observer_class_( + j_observer_class_( jni, FindClass(jni, - "org/webrtc/VideoCapturerAndroid$NativeFrameObserver")) { - } + "org/webrtc/VideoCapturerAndroid$NativeObserver")), + proxy_(nullptr) { + thread_checker_.DetachFromThread(); +} -AndroidVideoCapturerJni::~AndroidVideoCapturerJni() {} +bool AndroidVideoCapturerJni::Init(jstring device_name) { + const jmethodID m(GetMethodID( + jni(), *j_video_capturer_class_, "init", "(Ljava/lang/String;)Z")); + if (!jni()->CallBooleanMethod(*j_capturer_global_, m, device_name)) { + return false; + } + CHECK_EXCEPTION(jni()) << "error during CallVoidMethod"; + return true; +} + +AndroidVideoCapturerJni::~AndroidVideoCapturerJni() { + DeInit(); +} + +void AndroidVideoCapturerJni::DeInit() { + DCHECK(proxy_ == nullptr); + jmethodID m = GetMethodID(jni(), *j_video_capturer_class_, "deInit", "()V"); + jni()->CallVoidMethod(*j_capturer_global_, m); + CHECK_EXCEPTION(jni()) << "error during VideoCapturerAndroid.DeInit"; +} void AndroidVideoCapturerJni::Start(int width, int height, int framerate, webrtc::AndroidVideoCapturer* capturer) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(proxy_ == nullptr); + proxy_ = new JavaCaptureProxy(); + proxy_->SetAndroidCapturer(capturer); + j_frame_observer_ = NewGlobalRef( jni(), - jni()->NewObject(*j_frame_observer_class_, + jni()->NewObject(*j_observer_class_, GetMethodID(jni(), - *j_frame_observer_class_, + *j_observer_class_, "", "(J)V"), - jlongFromPointer(capturer))); + jlongFromPointer(proxy_))); CHECK_EXCEPTION(jni()) << "error during NewObject"; jmethodID m = GetMethodID( @@ -82,13 +167,15 @@ void AndroidVideoCapturerJni::Start(int width, int height, int framerate, CHECK_EXCEPTION(jni()) << "error during VideoCapturerAndroid.startCapture"; } -bool AndroidVideoCapturerJni::Stop() { +void AndroidVideoCapturerJni::Stop() { + DCHECK(thread_checker_.CalledOnValidThread()); + proxy_->SetAndroidCapturer(nullptr); + proxy_ = nullptr; jmethodID m = GetMethodID(jni(), *j_video_capturer_class_, - "stopCapture", "()Z"); - jboolean result = jni()->CallBooleanMethod(*j_capturer_global_, m); + "stopCapture", "()V"); + jni()->CallVoidMethod(*j_capturer_global_, m); CHECK_EXCEPTION(jni()) << "error during VideoCapturerAndroid.stopCapture"; DeleteGlobalRef(jni(), j_frame_observer_); - return result; } std::string AndroidVideoCapturerJni::GetSupportedFormats() { @@ -103,20 +190,27 @@ std::string AndroidVideoCapturerJni::GetSupportedFormats() { JNIEnv* AndroidVideoCapturerJni::jni() { return AttachCurrentThreadIfNeeded(); } -JOW(void, VideoCapturerAndroid_00024NativeFrameObserver_nativeOnFrameCaptured) - (JNIEnv* jni, jclass, jlong j_capturer, jbyteArray j_frame, +JOW(void, VideoCapturerAndroid_00024NativeObserver_nativeOnFrameCaptured) + (JNIEnv* jni, jclass, jlong j_proxy, jbyteArray j_frame, jint rotation, jlong ts) { jbyte* bytes = jni->GetByteArrayElements(j_frame, NULL); - reinterpret_cast( - j_capturer)->OnIncomingFrame(bytes, jni->GetArrayLength(j_frame), - rotation, ts); + reinterpret_cast( + j_proxy)->OnIncomingFrame(bytes, jni->GetArrayLength(j_frame), rotation, + ts); jni->ReleaseByteArrayElements(j_frame, bytes, JNI_ABORT); } -JOW(void, VideoCapturerAndroid_00024NativeFrameObserver_nativeCapturerStarted) - (JNIEnv* jni, jclass, jlong j_capturer, jboolean j_success) { - reinterpret_cast( - j_capturer)->OnCapturerStarted(j_success); +JOW(void, VideoCapturerAndroid_00024NativeObserver_nativeCapturerStarted) + (JNIEnv* jni, jclass, jlong j_proxy, jboolean j_success) { + JavaCaptureProxy* proxy = reinterpret_cast(j_proxy); + proxy->OnCapturerStarted(j_success); + if (!j_success) + delete proxy; +} + +JOW(void, VideoCapturerAndroid_00024NativeObserver_nativeCapturerStopped) + (JNIEnv* jni, jclass, jlong j_proxy) { + delete reinterpret_cast(j_proxy); } } // namespace webrtc_jni diff --git a/talk/app/webrtc/java/jni/androidvideocapturer_jni.h b/talk/app/webrtc/java/jni/androidvideocapturer_jni.h index 846905a9f..33c586fad 100644 --- a/talk/app/webrtc/java/jni/androidvideocapturer_jni.h +++ b/talk/app/webrtc/java/jni/androidvideocapturer_jni.h @@ -33,9 +33,12 @@ #include "talk/app/webrtc/androidvideocapturer.h" #include "talk/app/webrtc/java/jni/jni_helpers.h" +#include "webrtc/base/thread_checker.h" namespace webrtc_jni { +class JavaCaptureProxy; + // AndroidVideoCapturerJni implements AndroidVideoCapturerDelegate. // The purpose of the delegate is to hide the JNI specifics from the C++ only // AndroidVideoCapturer. @@ -45,20 +48,29 @@ class AndroidVideoCapturerJni : public webrtc::AndroidVideoCapturerDelegate { AndroidVideoCapturerJni(JNIEnv* jni, jobject j_video_capturer); ~AndroidVideoCapturerJni(); + bool Init(jstring device_name); + void Start(int width, int height, int framerate, webrtc::AndroidVideoCapturer* capturer) override; - bool Stop() override; + void Stop() override; std::string GetSupportedFormats() override; private: JNIEnv* jni(); + void DeInit(); const ScopedGlobalRef j_capturer_global_; const ScopedGlobalRef j_video_capturer_class_; - const ScopedGlobalRef j_frame_observer_class_; + const ScopedGlobalRef j_observer_class_; jobject j_frame_observer_; + rtc::ThreadChecker thread_checker_; + + // The proxy is a valid pointer between calling Start and Stop. + // It destroys itself when Java VideoCapturerAndroid has been stopped. + JavaCaptureProxy* proxy_; + static jobject application_context_; DISALLOW_COPY_AND_ASSIGN(AndroidVideoCapturerJni); diff --git a/talk/app/webrtc/java/jni/classreferenceholder.cc b/talk/app/webrtc/java/jni/classreferenceholder.cc index 25a110101..8c30a0a3e 100644 --- a/talk/app/webrtc/java/jni/classreferenceholder.cc +++ b/talk/app/webrtc/java/jni/classreferenceholder.cc @@ -72,7 +72,7 @@ ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) { #if defined(ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD) LoadClass(jni, "android/graphics/SurfaceTexture"); LoadClass(jni, "org/webrtc/VideoCapturerAndroid"); - LoadClass(jni, "org/webrtc/VideoCapturerAndroid$NativeFrameObserver"); + LoadClass(jni, "org/webrtc/VideoCapturerAndroid$NativeObserver"); LoadClass(jni, "org/webrtc/MediaCodecVideoEncoder"); LoadClass(jni, "org/webrtc/MediaCodecVideoEncoder$OutputBufferInfo"); LoadClass(jni, "org/webrtc/MediaCodecVideoDecoder"); diff --git a/talk/app/webrtc/java/jni/peerconnection_jni.cc b/talk/app/webrtc/java/jni/peerconnection_jni.cc index d21005418..b8d1311ce 100644 --- a/talk/app/webrtc/java/jni/peerconnection_jni.cc +++ b/talk/app/webrtc/java/jni/peerconnection_jni.cc @@ -1385,15 +1385,10 @@ JOW(jobject, VideoCapturer_nativeCreateVideoCapturer)( j_videocapturer_ctor); CHECK_EXCEPTION(jni) << "error during NewObject"; - const jmethodID m(GetMethodID( - jni, j_video_capturer_class, "Init", "(Ljava/lang/String;)Z")); - if (!jni->CallBooleanMethod(j_video_capturer, m, j_device_name)) { - return nullptr; - } - CHECK_EXCEPTION(jni) << "error during CallVoidMethod"; - - rtc::scoped_ptr delegate( + rtc::scoped_ptr delegate( new AndroidVideoCapturerJni(jni, j_video_capturer)); + if (!delegate->Init(j_device_name)) + return nullptr; rtc::scoped_ptr capturer( new webrtc::AndroidVideoCapturer(delegate.Pass())); diff --git a/talk/app/webrtc/java/src/org/webrtc/VideoCapturerAndroid.java b/talk/app/webrtc/java/src/org/webrtc/VideoCapturerAndroid.java index 2650e7137..c68e27008 100644 --- a/talk/app/webrtc/java/src/org/webrtc/VideoCapturerAndroid.java +++ b/talk/app/webrtc/java/src/org/webrtc/VideoCapturerAndroid.java @@ -178,30 +178,50 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba // Called by native code. // Enumerates resolution and frame rates for all cameras to be able to switch - // cameras. Initializes local variables for the camera named |deviceName|. + // cameras. Initializes local variables for the camera named |deviceName| and + // starts a thread to be used for capturing. // If deviceName is empty, the first available device is used in order to be // compatible with the generic VideoCapturer class. - boolean Init(String deviceName) { - Log.e(TAG, "Init " + deviceName); - if (!InitStatics()) + boolean init(String deviceName) { + Log.d(TAG, "init " + deviceName); + if (!initStatics()) return false; + boolean foundDevice = false; if (deviceName.isEmpty()) { this.id = 0; - return true; - } - - Boolean foundDevice = false; - for (int i = 0; i < Camera.getNumberOfCameras(); ++i) { - if (deviceName.equals(getDeviceName(i))) { - this.id = i; - foundDevice = true; + foundDevice = true; + } else { + for (int i = 0; i < Camera.getNumberOfCameras(); ++i) { + if (deviceName.equals(getDeviceName(i))) { + this.id = i; + foundDevice = true; + } } } + Exchanger handlerExchanger = new Exchanger(); + cameraThread = new CameraThread(handlerExchanger); + cameraThread.start(); + cameraThreadHandler = exchange(handlerExchanger, null); return foundDevice; } - private static boolean InitStatics() { + // Called by native code. Frees the Java thread created in Init. + void deInit() throws InterruptedException { + Log.d(TAG, "deInit"); + if (cameraThreadHandler != null) { + cameraThreadHandler.post(new Runnable() { + @Override public void run() { + Log.d(TAG, "stop CameraThread"); + Looper.myLooper().quit(); + } + }); + cameraThread.join(); + cameraThreadHandler = null; + } + } + + private static boolean initStatics() { if (supportedFormats != null) return true; try { @@ -298,20 +318,15 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba final Context applicationContext, final CapturerObserver frameObserver) { Log.d(TAG, "startCapture requested: " + width + "x" + height + "@" + framerate); - if (cameraThread != null || cameraThreadHandler != null) { - throw new RuntimeException("Camera thread already started!"); - } if (applicationContext == null) { throw new RuntimeException("applicationContext not set."); } if (frameObserver == null) { throw new RuntimeException("frameObserver not set."); } - - Exchanger handlerExchanger = new Exchanger(); - cameraThread = new CameraThread(handlerExchanger); - cameraThread.start(); - cameraThreadHandler = exchange(handlerExchanger, null); + this.width = width; + this.height = height; + this.framerate = framerate; cameraThreadHandler.post(new Runnable() { @Override public void run() { @@ -327,9 +342,6 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba Throwable error = null; this.applicationContext = applicationContext; this.frameObserver = frameObserver; - this.width = width; - this.height = height; - this.framerate = framerate; try { this.camera = Camera.open(id); this.info = new Camera.CameraInfo(); @@ -406,8 +418,7 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba } Log.e(TAG, "startCapture failed", error); if (camera != null) { - Exchanger resultDropper = new Exchanger(); - stopCaptureOnCameraThread(resultDropper); + stopCaptureOnCameraThread(); frameObserver.OnCapturerStarted(false); } frameObserver.OnCapturerStarted(false); @@ -415,37 +426,19 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba } // Called by native code. Returns true when camera is known to be stopped. - synchronized boolean stopCapture() { + synchronized void stopCapture() { Log.d(TAG, "stopCapture"); - final Exchanger result = new Exchanger(); cameraThreadHandler.post(new Runnable() { @Override public void run() { - stopCaptureOnCameraThread(result); + stopCaptureOnCameraThread(); } }); - boolean status = exchange(result, false); // |false| is a dummy value here. - Log.d(TAG, "stopCapture wait"); - try { - cameraThread.join(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - cameraThreadHandler = null; - cameraThread = null; - Log.d(TAG, "stopCapture done"); - return status; } - private void stopCaptureOnCameraThread(Exchanger result) { + private void stopCaptureOnCameraThread() { Log.d(TAG, "stopCaptureOnCameraThread"); - if (camera == null) { - throw new RuntimeException("Camera is already stopped!"); - } - frameObserver = null; - doStopCaptureOnCamerathread(); - exchange(result, true); - Looper.myLooper().quit(); + frameObserver.OnCapturerStopped(); return; } @@ -570,9 +563,13 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba // Interface used for providing callbacks to an observer. interface CapturerObserver { - // Notify if the camera have beens started successfully or not. + // Notify if the camera have been started successfully or not. // Called on a Java thread owned by VideoCapturerAndroid. abstract void OnCapturerStarted(boolean success); + + // Notify that the camera have been stopped. + // Called on a Java thread owned by VideoCapturerAndroid. + abstract void OnCapturerStopped(); // Delivers a captured frame. Called on a Java thread owned by // VideoCapturerAndroid. abstract void OnFrameCaptured(byte[] data, int rotation, long timeStamp); @@ -580,27 +577,32 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba // An implementation of CapturerObserver that forwards all calls from // Java to the C layer. - public static class NativeFrameObserver implements CapturerObserver { - private final long nativeCapturer; + public static class NativeObserver implements CapturerObserver { + private final long nativeProxy; - public NativeFrameObserver(long nativeCapturer) { - this.nativeCapturer = nativeCapturer; + public NativeObserver(long nativeProxy) { + this.nativeProxy = nativeProxy; } @Override public void OnFrameCaptured(byte[] data, int rotation, long timeStamp) { - nativeOnFrameCaptured(nativeCapturer, data, rotation, timeStamp); + nativeOnFrameCaptured(nativeProxy, data, rotation, timeStamp); } - private native void nativeOnFrameCaptured( - long captureObject, byte[] data, int rotation, long timeStamp); - @Override public void OnCapturerStarted(boolean success) { - nativeCapturerStarted(nativeCapturer, success); + nativeCapturerStarted(nativeProxy, success); } - private native void nativeCapturerStarted(long captureObject, + @Override + public void OnCapturerStopped() { + nativeCapturerStopped(nativeProxy); + } + + private native void nativeCapturerStarted(long proxyObject, boolean success); + private native void nativeCapturerStopped(long proxyObject); + private native void nativeOnFrameCaptured( + long proxyObject, byte[] data, int rotation, long timeStamp); } }