diff --git a/talk/app/webrtc/java/jni/peerconnection_jni.cc b/talk/app/webrtc/java/jni/peerconnection_jni.cc index a0aa1d4a5..6bb8fed49 100644 --- a/talk/app/webrtc/java/jni/peerconnection_jni.cc +++ b/talk/app/webrtc/java/jni/peerconnection_jni.cc @@ -1897,7 +1897,7 @@ JOW(jboolean, PeerConnectionFactory_initializeAndroidGlobals)( CHECK(g_jvm, "JNI_OnLoad failed to run?"); bool failure = false; if (initialize_video) - failure |= webrtc::VideoEngine::SetAndroidObjects(g_jvm); + failure |= webrtc::VideoEngine::SetAndroidObjects(g_jvm, context); if (initialize_audio) failure |= webrtc::VoiceEngine::SetAndroidObjects(g_jvm, jni, context); return !failure; diff --git a/talk/examples/android/AndroidManifest.xml b/talk/examples/android/AndroidManifest.xml index 1fb60ad5d..f898641f8 100644 --- a/talk/examples/android/AndroidManifest.xml +++ b/talk/examples/android/AndroidManifest.xml @@ -21,7 +21,8 @@ android:allowBackup="false"> diff --git a/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java b/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java index dea251a5f..9a3d72ed7 100644 --- a/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java +++ b/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java @@ -31,6 +31,7 @@ import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; +import android.content.res.Configuration; import android.graphics.Color; import android.graphics.Point; import android.media.AudioManager; @@ -226,6 +227,13 @@ public class AppRTCDemoActivity extends Activity } } + @Override + public void onConfigurationChanged (Configuration newConfig) { + Point displaySize = new Point(); + getWindowManager().getDefaultDisplay().getSize(displaySize); + vsv.updateDisplaySize(displaySize); + super.onConfigurationChanged(newConfig); + } // Just for fun (and to regression-test bug 2302) make sure that DataChannels // can be created, queried, and disposed. diff --git a/talk/examples/android/src/org/appspot/apprtc/VideoStreamsView.java b/talk/examples/android/src/org/appspot/apprtc/VideoStreamsView.java index ca9cd4e1a..f069f0a17 100644 --- a/talk/examples/android/src/org/appspot/apprtc/VideoStreamsView.java +++ b/talk/examples/android/src/org/appspot/apprtc/VideoStreamsView.java @@ -84,11 +84,15 @@ public class VideoStreamsView setRenderMode(RENDERMODE_WHEN_DIRTY); } + public void updateDisplaySize(Point screenDimensions) { + this.screenDimensions = screenDimensions; + } + /** Queue |frame| to be uploaded. */ public void queueFrame(final Endpoint stream, I420Frame frame) { // Paying for the copy of the YUV data here allows CSC and painting time // to get spent on the render thread instead of the UI thread. - abortUnless(framePool.validateDimensions(frame), "Frame too large!"); + abortUnless(FramePool.validateDimensions(frame), "Frame too large!"); final I420Frame frameCopy = framePool.takeFrame(frame).copyFrom(frame); boolean needToScheduleRender; synchronized (framesToRender) { diff --git a/webrtc/examples/android/media_demo/jni/on_load.cc b/webrtc/examples/android/media_demo/jni/on_load.cc index 9f9a0c7ac..9fc4ca92b 100644 --- a/webrtc/examples/android/media_demo/jni/on_load.cc +++ b/webrtc/examples/android/media_demo/jni/on_load.cc @@ -38,7 +38,7 @@ JOWW(void, NativeWebRtcContextRegistry_register)( jobject context) { webrtc_examples::SetVoeDeviceObjects(g_vm); webrtc_examples::SetVieDeviceObjects(g_vm); - CHECK(webrtc::VideoEngine::SetAndroidObjects(g_vm) == 0, + CHECK(webrtc::VideoEngine::SetAndroidObjects(g_vm, context) == 0, "Failed to register android objects to video engine"); CHECK(webrtc::VoiceEngine::SetAndroidObjects(g_vm, jni, context) == 0, "Failed to register android objects to voice engine"); @@ -47,7 +47,7 @@ JOWW(void, NativeWebRtcContextRegistry_register)( JOWW(void, NativeWebRtcContextRegistry_unRegister)( JNIEnv* jni, jclass) { - CHECK(webrtc::VideoEngine::SetAndroidObjects(NULL) == 0, + CHECK(webrtc::VideoEngine::SetAndroidObjects(NULL, NULL) == 0, "Failed to unregister android objects from video engine"); CHECK(webrtc::VoiceEngine::SetAndroidObjects(NULL, NULL, NULL) == 0, "Failed to unregister android objects from voice engine"); diff --git a/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java b/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java index e8a2fc1fb..c8abdbe37 100644 --- a/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java +++ b/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java @@ -15,6 +15,7 @@ import java.util.Locale; import java.util.concurrent.Exchanger; import java.util.concurrent.locks.ReentrantLock; +import android.content.Context; import android.graphics.ImageFormat; import android.graphics.PixelFormat; import android.graphics.Rect; @@ -25,6 +26,7 @@ import android.hardware.Camera; import android.os.Handler; import android.os.Looper; import android.util.Log; +import android.view.OrientationEventListener; import android.view.SurfaceHolder.Callback; import android.view.SurfaceHolder; @@ -46,6 +48,7 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback { private Handler cameraThreadHandler; private final int id; private final Camera.CameraInfo info; + private final OrientationEventListener orientationListener; private final long native_capturer; // |VideoCaptureAndroid*| in C++. private SurfaceTexture dummySurfaceTexture; // Arbitrary queue depth. Higher number means more memory allocated & held, @@ -66,8 +69,30 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback { this.native_capturer = native_capturer; this.info = new Camera.CameraInfo(); Camera.getCameraInfo(id, info); + + // Must be the last thing in the ctor since we pass a reference to |this|! + final VideoCaptureAndroid self = this; + orientationListener = new OrientationEventListener(GetContext()) { + @Override public void onOrientationChanged(int degrees) { + if (degrees == OrientationEventListener.ORIENTATION_UNKNOWN) { + return; + } + if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { + degrees = (info.orientation - degrees + 360) % 360; + } else { // back-facing + degrees = (info.orientation + degrees) % 360; + } + self.OnOrientationChanged(self.native_capturer, degrees); + } + }; + // Don't add any code here; see the comment above |self| above! } + // Return the global application context. + private static native Context GetContext(); + // Request frame rotation post-capture. + private native void OnOrientationChanged(long captureObject, int degrees); + private class CameraThread extends Thread { private Exchanger handlerExchanger; public CameraThread(Exchanger handlerExchanger) { @@ -96,6 +121,7 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback { cameraThread = new CameraThread(handlerExchanger); cameraThread.start(); cameraThreadHandler = exchange(handlerExchanger, null); + orientationListener.enable(); final Exchanger result = new Exchanger(); cameraThreadHandler.post(new Runnable() { @@ -186,6 +212,7 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback { } cameraThreadHandler = null; cameraThread = null; + orientationListener.disable(); return status; } diff --git a/webrtc/modules/video_capture/android/video_capture_android.cc b/webrtc/modules/video_capture/android/video_capture_android.cc index f42329e30..2ecac2e4f 100644 --- a/webrtc/modules/video_capture/android/video_capture_android.cc +++ b/webrtc/modules/video_capture/android/video_capture_android.cc @@ -19,9 +19,16 @@ static JavaVM* g_jvm = NULL; static jclass g_java_capturer_class = NULL; // VideoCaptureAndroid.class. +static jobject g_context = NULL; // Owned android.content.Context. namespace webrtc { +// Called by Java to get the global application context. +jobject JNICALL GetContext(JNIEnv* env, jclass) { + assert(g_context); + return g_context; +} + // Called by Java when the camera has a new frame to deliver. void JNICALL ProvideCameraFrame( JNIEnv* env, @@ -38,11 +45,31 @@ void JNICALL ProvideCameraFrame( env->ReleaseByteArrayElements(javaCameraFrame, cameraFrame, JNI_ABORT); } -int32_t SetCaptureAndroidVM(JavaVM* javaVM) { +// Called by Java when the device orientation has changed. +void JNICALL OnOrientationChanged( + JNIEnv* env, jobject, jlong context, jint degrees) { + webrtc::videocapturemodule::VideoCaptureAndroid* captureModule = + reinterpret_cast( + context); + degrees = (360 + degrees) % 360; + assert(degrees >= 0 && degrees < 360); + VideoCaptureRotation rotation = + (degrees <= 45 || degrees > 315) ? kCameraRotate0 : + (degrees > 45 && degrees <= 135) ? kCameraRotate90 : + (degrees > 135 && degrees <= 225) ? kCameraRotate180 : + (degrees > 225 && degrees <= 315) ? kCameraRotate270 : + kCameraRotate0; // Impossible. + int32_t status = + captureModule->VideoCaptureImpl::SetCaptureRotation(rotation); + assert(status == 0); +} + +int32_t SetCaptureAndroidVM(JavaVM* javaVM, jobject context) { if (javaVM) { assert(!g_jvm); g_jvm = javaVM; AttachThreadScoped ats(g_jvm); + g_context = ats.env()->NewGlobalRef(context); videocapturemodule::DeviceInfoAndroid::Initialize(ats.env()); @@ -53,12 +80,18 @@ int32_t SetCaptureAndroidVM(JavaVM* javaVM) { reinterpret_cast(ats.env()->NewGlobalRef(j_capture_class)); assert(g_java_capturer_class); - JNINativeMethod native_method = { - "ProvideCameraFrame", "([BIJ)V", - reinterpret_cast(&ProvideCameraFrame) - }; + JNINativeMethod native_methods[] = { + {"GetContext", + "()Landroid/content/Context;", + reinterpret_cast(&GetContext)}, + {"OnOrientationChanged", + "(JI)V", + reinterpret_cast(&OnOrientationChanged)}, + {"ProvideCameraFrame", + "([BIJ)V", + reinterpret_cast(&ProvideCameraFrame)}}; if (ats.env()->RegisterNatives(g_java_capturer_class, - &native_method, 1) != 0) + native_methods, 3) != 0) assert(false); } else { if (g_jvm) { @@ -66,6 +99,8 @@ int32_t SetCaptureAndroidVM(JavaVM* javaVM) { ats.env()->UnregisterNatives(g_java_capturer_class); ats.env()->DeleteGlobalRef(g_java_capturer_class); g_java_capturer_class = NULL; + ats.env()->DeleteGlobalRef(g_context); + g_context = NULL; videocapturemodule::DeviceInfoAndroid::DeInitialize(); g_jvm = NULL; } @@ -198,8 +233,9 @@ int32_t VideoCaptureAndroid::CaptureSettings( int32_t VideoCaptureAndroid::SetCaptureRotation( VideoCaptureRotation rotation) { CriticalSectionScoped cs(&_apiCs); - if (VideoCaptureImpl::SetCaptureRotation(rotation) != 0) - return 0; + int32_t status = VideoCaptureImpl::SetCaptureRotation(rotation); + if (status != 0) + return status; AttachThreadScoped ats(g_jvm); JNIEnv* env = ats.env(); diff --git a/webrtc/modules/video_capture/ensure_initialized.cc b/webrtc/modules/video_capture/ensure_initialized.cc index 995202dfc..65c9a8dbe 100644 --- a/webrtc/modules/video_capture/ensure_initialized.cc +++ b/webrtc/modules/video_capture/ensure_initialized.cc @@ -36,7 +36,7 @@ void EnsureInitialized() {} namespace webrtc { // Declared in webrtc/modules/video_capture/include/video_capture.h. -int32_t SetCaptureAndroidVM(JavaVM* javaVM); +int32_t SetCaptureAndroidVM(JavaVM* javaVM, jobject g_context); namespace videocapturemodule { @@ -44,10 +44,11 @@ static pthread_once_t g_initialize_once = PTHREAD_ONCE_INIT; void EnsureInitializedOnce() { JNIEnv* jni = ::base::android::AttachCurrentThread(); + jobject context = ::base::android::GetApplicationContext(); JavaVM* jvm = NULL; int status = jni->GetJavaVM(&jvm); ASSERT(status == 0); - status = webrtc::SetCaptureAndroidVM(jvm) == 0; + status = webrtc::SetCaptureAndroidVM(jvm, context) == 0; ASSERT(status); } diff --git a/webrtc/modules/video_capture/include/video_capture.h b/webrtc/modules/video_capture/include/video_capture.h index 6966c2381..7398af604 100644 --- a/webrtc/modules/video_capture/include/video_capture.h +++ b/webrtc/modules/video_capture/include/video_capture.h @@ -21,7 +21,7 @@ namespace webrtc { #if defined(ANDROID) -int32_t SetCaptureAndroidVM(JavaVM* javaVM); +int32_t SetCaptureAndroidVM(JavaVM* javaVM, jobject context); #endif class VideoCaptureModule: public RefCountedModule { diff --git a/webrtc/video_engine/include/vie_base.h b/webrtc/video_engine/include/vie_base.h index 56916ce3b..47fc0a791 100644 --- a/webrtc/video_engine/include/vie_base.h +++ b/webrtc/video_engine/include/vie_base.h @@ -150,7 +150,7 @@ class WEBRTC_DLLEXPORT VideoEngine { #if defined(ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD) // Android specific. - static int SetAndroidObjects(JavaVM* java_vm); + static int SetAndroidObjects(JavaVM* java_vm, jobject context); #endif protected: diff --git a/webrtc/video_engine/test/auto_test/source/vie_autotest_android.cc b/webrtc/video_engine/test/auto_test/source/vie_autotest_android.cc index cc283b041..86b9b1d3a 100644 --- a/webrtc/video_engine/test/auto_test/source/vie_autotest_android.cc +++ b/webrtc/video_engine/test/auto_test/source/vie_autotest_android.cc @@ -21,7 +21,7 @@ int ViEAutoTestAndroid::RunAutotest(int testSelection, int subTestSelection, JavaVM* javaVM, void* env, void* context) { ViEAutoTest vieAutoTest(window1, window2); ViETest::Log("RunAutoTest(%d, %d)", testSelection, subTestSelection); - webrtc::VideoEngine::SetAndroidObjects(javaVM); + webrtc::VideoEngine::SetAndroidObjects(javaVM, context); #ifndef WEBRTC_ANDROID_OPENSLES // voice engine calls into ADM directly webrtc::VoiceEngine::SetAndroidObjects(javaVM, env, context); diff --git a/webrtc/video_engine/vie_impl.cc b/webrtc/video_engine/vie_impl.cc index 34de82026..3cdf5da2f 100644 --- a/webrtc/video_engine/vie_impl.cc +++ b/webrtc/video_engine/vie_impl.cc @@ -140,10 +140,10 @@ int VideoEngine::SetTraceCallback(TraceCallback* callback) { } #if defined(ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD) -int VideoEngine::SetAndroidObjects(JavaVM* javaVM) { +int VideoEngine::SetAndroidObjects(JavaVM* javaVM, jobject context) { LOG_F(LS_INFO); - if (SetCaptureAndroidVM(javaVM) != 0) { + if (SetCaptureAndroidVM(javaVM, context) != 0) { LOG(LS_ERROR) << "Could not set capture Android VM"; return -1; }