diff options
Diffstat (limited to 'talk/app/webrtc/java/jni/androidvideocapturer_jni.cc')
-rw-r--r-- | talk/app/webrtc/java/jni/androidvideocapturer_jni.cc | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/talk/app/webrtc/java/jni/androidvideocapturer_jni.cc b/talk/app/webrtc/java/jni/androidvideocapturer_jni.cc new file mode 100644 index 0000000000..02b9f22015 --- /dev/null +++ b/talk/app/webrtc/java/jni/androidvideocapturer_jni.cc @@ -0,0 +1,266 @@ +/* + * libjingle + * Copyright 2015 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "talk/app/webrtc/java/jni/androidvideocapturer_jni.h" +#include "talk/app/webrtc/java/jni/classreferenceholder.h" +#include "talk/app/webrtc/java/jni/native_handle_impl.h" +#include "webrtc/base/bind.h" +#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" + +namespace webrtc_jni { + +jobject AndroidVideoCapturerJni::application_context_ = nullptr; + +// static +int AndroidVideoCapturerJni::SetAndroidObjects(JNIEnv* jni, + jobject appliction_context) { + if (application_context_) { + jni->DeleteGlobalRef(application_context_); + } + application_context_ = NewGlobalRef(jni, appliction_context); + + return 0; +} + +AndroidVideoCapturerJni::AndroidVideoCapturerJni(JNIEnv* jni, + jobject j_video_capturer) + : j_capturer_global_(jni, j_video_capturer), + j_video_capturer_class_( + jni, FindClass(jni, "org/webrtc/VideoCapturerAndroid")), + j_observer_class_( + jni, + FindClass(jni, + "org/webrtc/VideoCapturerAndroid$NativeObserver")), + capturer_(nullptr) { + LOG(LS_INFO) << "AndroidVideoCapturerJni ctor"; + thread_checker_.DetachFromThread(); +} + +AndroidVideoCapturerJni::~AndroidVideoCapturerJni() { + LOG(LS_INFO) << "AndroidVideoCapturerJni dtor"; + jni()->CallVoidMethod( + *j_capturer_global_, + GetMethodID(jni(), *j_video_capturer_class_, "release", "()V")); + CHECK_EXCEPTION(jni()) << "error during VideoCapturerAndroid.release()"; +} + +void AndroidVideoCapturerJni::Start(int width, int height, int framerate, + webrtc::AndroidVideoCapturer* capturer) { + LOG(LS_INFO) << "AndroidVideoCapturerJni start"; + RTC_DCHECK(thread_checker_.CalledOnValidThread()); + { + rtc::CritScope cs(&capturer_lock_); + RTC_CHECK(capturer_ == nullptr); + RTC_CHECK(invoker_.get() == nullptr); + capturer_ = capturer; + invoker_.reset(new rtc::GuardedAsyncInvoker()); + } + jobject j_frame_observer = + jni()->NewObject(*j_observer_class_, + GetMethodID(jni(), *j_observer_class_, "<init>", "(J)V"), + jlongFromPointer(this)); + CHECK_EXCEPTION(jni()) << "error during NewObject"; + + jmethodID m = GetMethodID( + jni(), *j_video_capturer_class_, "startCapture", + "(IIILandroid/content/Context;" + "Lorg/webrtc/VideoCapturerAndroid$CapturerObserver;)V"); + jni()->CallVoidMethod(*j_capturer_global_, + m, width, height, + framerate, + application_context_, + j_frame_observer); + CHECK_EXCEPTION(jni()) << "error during VideoCapturerAndroid.startCapture"; +} + +void AndroidVideoCapturerJni::Stop() { + LOG(LS_INFO) << "AndroidVideoCapturerJni stop"; + RTC_DCHECK(thread_checker_.CalledOnValidThread()); + { + rtc::CritScope cs(&capturer_lock_); + // Destroying |invoker_| will cancel all pending calls to |capturer_|. + invoker_ = nullptr; + capturer_ = nullptr; + } + jmethodID m = GetMethodID(jni(), *j_video_capturer_class_, + "stopCapture", "()V"); + jni()->CallVoidMethod(*j_capturer_global_, m); + CHECK_EXCEPTION(jni()) << "error during VideoCapturerAndroid.stopCapture"; + LOG(LS_INFO) << "AndroidVideoCapturerJni stop done"; +} + +template <typename... Args> +void AndroidVideoCapturerJni::AsyncCapturerInvoke( + const char* method_name, + void (webrtc::AndroidVideoCapturer::*method)(Args...), + typename Identity<Args>::type... args) { + rtc::CritScope cs(&capturer_lock_); + if (!invoker_) { + LOG(LS_WARNING) << method_name << "() called for closed capturer."; + return; + } + invoker_->AsyncInvoke<void>(rtc::Bind(method, capturer_, args...)); +} + +void AndroidVideoCapturerJni::ReturnBuffer(int64_t time_stamp) { + jmethodID m = GetMethodID(jni(), *j_video_capturer_class_, + "returnBuffer", "(J)V"); + jni()->CallVoidMethod(*j_capturer_global_, m, time_stamp); + CHECK_EXCEPTION(jni()) << "error during VideoCapturerAndroid.returnBuffer"; +} + +std::string AndroidVideoCapturerJni::GetSupportedFormats() { + jmethodID m = + GetMethodID(jni(), *j_video_capturer_class_, + "getSupportedFormatsAsJson", "()Ljava/lang/String;"); + jstring j_json_caps = + (jstring) jni()->CallObjectMethod(*j_capturer_global_, m); + CHECK_EXCEPTION(jni()) << "error during supportedFormatsAsJson"; + return JavaToStdString(jni(), j_json_caps); +} + +void AndroidVideoCapturerJni::OnCapturerStarted(bool success) { + LOG(LS_INFO) << "AndroidVideoCapturerJni capture started: " << success; + AsyncCapturerInvoke("OnCapturerStarted", + &webrtc::AndroidVideoCapturer::OnCapturerStarted, + success); +} + +void AndroidVideoCapturerJni::OnMemoryBufferFrame(void* video_frame, + int length, + int width, + int height, + int rotation, + int64_t timestamp_ns) { + const uint8_t* y_plane = static_cast<uint8_t*>(video_frame); + // Android guarantees that the stride is a multiple of 16. + // http://developer.android.com/reference/android/hardware/Camera.Parameters.html#setPreviewFormat%28int%29 + int y_stride; + int uv_stride; + webrtc::Calc16ByteAlignedStride(width, &y_stride, &uv_stride); + const uint8_t* v_plane = y_plane + y_stride * height; + const uint8_t* u_plane = + v_plane + uv_stride * webrtc::AlignInt(height, 2) / 2; + + // Wrap the Java buffer, and call ReturnBuffer() in the wrapped + // VideoFrameBuffer destructor. + rtc::scoped_refptr<webrtc::VideoFrameBuffer> buffer( + new rtc::RefCountedObject<webrtc::WrappedI420Buffer>( + width, height, y_plane, y_stride, u_plane, uv_stride, v_plane, + uv_stride, + rtc::Bind(&AndroidVideoCapturerJni::ReturnBuffer, this, + timestamp_ns))); + AsyncCapturerInvoke("OnIncomingFrame", + &webrtc::AndroidVideoCapturer::OnIncomingFrame, + buffer, rotation, timestamp_ns); +} + +void AndroidVideoCapturerJni::OnTextureFrame( + int width, + int height, + int64_t timestamp_ns, + const NativeTextureHandleImpl& handle) { + // TODO(magjed): Fix this. See bug webrtc:4993. + RTC_NOTREACHED() + << "The rest of the stack for Android expects the native " + "handle to be a NativeHandleImpl with a SurfaceTexture, not a " + "NativeTextureHandleImpl"; + rtc::scoped_refptr<webrtc::VideoFrameBuffer> buffer( + new rtc::RefCountedObject<AndroidTextureBuffer>( + width, height, handle, + rtc::Bind(&AndroidVideoCapturerJni::ReturnBuffer, this, + timestamp_ns))); + AsyncCapturerInvoke("OnIncomingFrame", + &webrtc::AndroidVideoCapturer::OnIncomingFrame, + buffer, 0, timestamp_ns); +} + +void AndroidVideoCapturerJni::OnOutputFormatRequest(int width, + int height, + int fps) { + AsyncCapturerInvoke("OnOutputFormatRequest", + &webrtc::AndroidVideoCapturer::OnOutputFormatRequest, + width, height, fps); +} + +JNIEnv* AndroidVideoCapturerJni::jni() { return AttachCurrentThreadIfNeeded(); } + +JOW(void, + VideoCapturerAndroid_00024NativeObserver_nativeOnByteBufferFrameCaptured) + (JNIEnv* jni, jclass, jlong j_capturer, jbyteArray j_frame, jint length, + jint width, jint height, jint rotation, jlong timestamp) { + jboolean is_copy = true; + jbyte* bytes = jni->GetByteArrayElements(j_frame, &is_copy); + // If this is a copy of the original frame, it means that the memory + // is not direct memory and thus VideoCapturerAndroid does not guarantee + // that the memory is valid when we have released |j_frame|. + // TODO(magjed): Move ReleaseByteArrayElements() into ReturnBuffer() and + // remove this check. + RTC_CHECK(!is_copy) + << "NativeObserver_nativeOnFrameCaptured: frame is a copy"; + reinterpret_cast<AndroidVideoCapturerJni*>(j_capturer) + ->OnMemoryBufferFrame(bytes, length, width, height, rotation, timestamp); + jni->ReleaseByteArrayElements(j_frame, bytes, JNI_ABORT); +} + +JOW(void, VideoCapturerAndroid_00024NativeObserver_nativeOnTextureFrameCaptured) + (JNIEnv* jni, jclass, jlong j_capturer, jint j_width, jint j_height, + jint j_oes_texture_id, jfloatArray j_transform_matrix, + jlong j_timestamp) { + reinterpret_cast<AndroidVideoCapturerJni*>(j_capturer) + ->OnTextureFrame(j_width, j_height, j_timestamp, + NativeTextureHandleImpl(jni, j_oes_texture_id, + j_transform_matrix)); +} + +JOW(void, VideoCapturerAndroid_00024NativeObserver_nativeCapturerStarted) + (JNIEnv* jni, jclass, jlong j_capturer, jboolean j_success) { + LOG(LS_INFO) << "NativeObserver_nativeCapturerStarted"; + reinterpret_cast<AndroidVideoCapturerJni*>(j_capturer)->OnCapturerStarted( + j_success); +} + +JOW(void, VideoCapturerAndroid_00024NativeObserver_nativeOnOutputFormatRequest) + (JNIEnv* jni, jclass, jlong j_capturer, jint j_width, jint j_height, + jint j_fps) { + LOG(LS_INFO) << "NativeObserver_nativeOnOutputFormatRequest"; + reinterpret_cast<AndroidVideoCapturerJni*>(j_capturer)->OnOutputFormatRequest( + j_width, j_height, j_fps); +} + +JOW(jlong, VideoCapturerAndroid_nativeCreateVideoCapturer) + (JNIEnv* jni, jclass, jobject j_video_capturer) { + rtc::scoped_refptr<webrtc::AndroidVideoCapturerDelegate> delegate = + new rtc::RefCountedObject<AndroidVideoCapturerJni>(jni, j_video_capturer); + rtc::scoped_ptr<cricket::VideoCapturer> capturer( + new webrtc::AndroidVideoCapturer(delegate)); + // Caller takes ownership of the cricket::VideoCapturer* pointer. + return jlongFromPointer(capturer.release()); +} + +} // namespace webrtc_jni |