/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "video_capture_android.h"

#include <stdio.h>

#include "critical_section_wrapper.h"
#include "ref_count.h"
#include "trace.h"

namespace webrtc
{
namespace videocapturemodule
{

VideoCaptureModule* VideoCaptureImpl::Create(
    const WebRtc_Word32 id,
    const char* deviceUniqueIdUTF8) {

  RefCountImpl<videocapturemodule::VideoCaptureAndroid>* implementation =
      new RefCountImpl<videocapturemodule::VideoCaptureAndroid>(id);

  if (!implementation || implementation->Init(id, deviceUniqueIdUTF8) != 0) {
    delete implementation;
    implementation = NULL;
  }
  return implementation;
}

// Android logging, uncomment to print trace to
// logcat instead of trace file/callback
// #include <android/log.h>
// #undef WEBRTC_TRACE
// #define WEBRTC_TRACE(a,b,c,...)
// __android_log_print(ANDROID_LOG_DEBUG, "*WEBRTCN*", __VA_ARGS__)

JavaVM* VideoCaptureAndroid::g_jvm = NULL;
//VideoCaptureAndroid.java
jclass VideoCaptureAndroid::g_javaCmClass = NULL;
//VideoCaptureDeviceInfoAndroid.java
jclass VideoCaptureAndroid::g_javaCmDevInfoClass = NULL;
//static instance of VideoCaptureDeviceInfoAndroid.java
jobject VideoCaptureAndroid::g_javaCmDevInfoObject = NULL;
jobject VideoCaptureAndroid::g_javaContext = NULL;

/*
 * Register references to Java Capture class.
 */
WebRtc_Word32 VideoCaptureAndroid::SetAndroidObjects(void* javaVM,
                                                     void* javaContext) {

  g_jvm = static_cast<JavaVM*> (javaVM);
  g_javaContext = static_cast<jobject> (javaContext);

  if (javaVM) {
    JNIEnv* env = NULL;
    if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
      WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
                   "%s: could not get Java environment", __FUNCTION__);
      return -1;
    }
    // get java capture class type (note path to class packet)
    jclass javaCmClassLocal = env->FindClass(AndroidJavaCaptureClass);
    if (!javaCmClassLocal) {
      WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
                   "%s: could not find java class", __FUNCTION__);
      return -1;
    }
    // create a global reference to the class
    // (to tell JNI that we are referencing it
    // after this function has returned)
    g_javaCmClass = static_cast<jclass>
        (env->NewGlobalRef(javaCmClassLocal));
    if (!g_javaCmClass) {
      WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
                   "%s: InitVideoEngineJava(): could not create"
                   " Java Camera class reference",
                   __FUNCTION__);
      return -1;
    }
    // Delete local class ref, we only use the global ref
    env->DeleteLocalRef(javaCmClassLocal);
    JNINativeMethod nativeFunctions =
        { "ProvideCameraFrame", "([BIJ)V",
          (void*) &VideoCaptureAndroid::ProvideCameraFrame };
    if (env->RegisterNatives(g_javaCmClass, &nativeFunctions, 1) == 0) {
      WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1,
                   "%s: Registered native functions", __FUNCTION__);
    }
    else {
      WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
                   "%s: Failed to register native functions",
                   __FUNCTION__);
      return -1;
    }

    // get java capture class type (note path to class packet)
    jclass javaCmDevInfoClassLocal = env->FindClass(
        AndroidJavaCaptureDeviceInfoClass);
    if (!javaCmDevInfoClassLocal) {
      WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
                   "%s: could not find java class", __FUNCTION__);
      return -1;
    }

    // create a global reference to the class
    // (to tell JNI that we are referencing it
    // after this function has returned)
    g_javaCmDevInfoClass = static_cast<jclass>
        (env->NewGlobalRef(javaCmDevInfoClassLocal));
    if (!g_javaCmDevInfoClass) {
      WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
                   "%s: InitVideoEngineJava(): could not create Java "
                   "Camera Device info class reference",
                   __FUNCTION__);
      return -1;
    }
    // Delete local class ref, we only use the global ref
    env->DeleteLocalRef(javaCmDevInfoClassLocal);

    WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1,
                 "VideoCaptureDeviceInfoAndroid get method id");

    // get the method ID for the Android Java CaptureClass static
    //CreateVideoCaptureAndroid factory method.
    jmethodID cid = env->GetStaticMethodID(
        g_javaCmDevInfoClass,
        "CreateVideoCaptureDeviceInfoAndroid",
        "(ILandroid/content/Context;)"
        "Lorg/webrtc/videoengine/VideoCaptureDeviceInfoAndroid;");
    if (cid == NULL) {
      WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
                   "%s: could not get java"
                   "VideoCaptureDeviceInfoAndroid constructor ID",
                   __FUNCTION__);
      return -1;
    }

    WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1,
                 "%s: construct static java device object", __FUNCTION__);

    // construct the object by calling the static constructor object
    jobject javaCameraDeviceInfoObjLocal =
        env->CallStaticObjectMethod(g_javaCmDevInfoClass,
                                    cid, (int) -1,
                                    g_javaContext);
    if (!javaCameraDeviceInfoObjLocal) {
      WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, -1,
                   "%s: could not create Java Capture Device info object",
                   __FUNCTION__);
      return -1;
    }
    // create a reference to the object (to tell JNI that
    // we are referencing it after this function has returned)
    g_javaCmDevInfoObject = env->NewGlobalRef(javaCameraDeviceInfoObjLocal);
    if (!g_javaCmDevInfoObject) {
      WEBRTC_TRACE(webrtc::kTraceError,
                   webrtc::kTraceAudioDevice,
                   -1,
                   "%s: could not create Java"
                   "cameradevinceinfo object reference",
                   __FUNCTION__);
      return -1;
    }
    // Delete local object ref, we only use the global ref
    env->DeleteLocalRef(javaCameraDeviceInfoObjLocal);
    return 0;
  }
  else {
    WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, -1,
                 "%s: JVM is NULL, assuming deinit", __FUNCTION__);
    if (!g_jvm) {
      WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
                   "%s: SetAndroidObjects not called with a valid JVM.",
                   __FUNCTION__);
      return -1;
    }
    JNIEnv* env = NULL;
    bool attached = false;
    if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
      // try to attach the thread and get the env
      // Attach this thread to JVM
      jint res = g_jvm->AttachCurrentThread(&env, NULL);
      if ((res < 0) || !env) {
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture,
                     -1, "%s: Could not attach thread to JVM (%d, %p)",
                     __FUNCTION__, res, env);
        return -1;
      }
      attached = true;
    }
    env->DeleteGlobalRef(g_javaCmDevInfoObject);
    env->DeleteGlobalRef(g_javaCmDevInfoClass);
    env->DeleteGlobalRef(g_javaCmClass);
    if (attached && g_jvm->DetachCurrentThread() < 0) {
      WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, -1,
                   "%s: Could not detach thread from JVM", __FUNCTION__);
      return -1;
    }
    return 0;
    env = (JNIEnv *) NULL;
  }
  return 0;
}

WebRtc_Word32 VideoCaptureAndroid::AttachAndUseAndroidDeviceInfoObjects(
    JNIEnv*& env,
    jclass& javaCmDevInfoClass,
    jobject& javaCmDevInfoObject,
    bool& attached) {
  // get the JNI env for this thread
  if (!g_jvm) {
    WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
                 "%s: SetAndroidObjects not called with a valid JVM.",
                 __FUNCTION__);
    return -1;
  }
  attached = false;
  if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
    // try to attach the thread and get the env
    // Attach this thread to JVM
    jint res = g_jvm->AttachCurrentThread(&env, NULL);
    if ((res < 0) || !env) {
      WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
                   "%s: Could not attach thread to JVM (%d, %p)",
                   __FUNCTION__, res, env);
      return -1;
    }
    attached = true;
  }
  javaCmDevInfoClass = g_javaCmDevInfoClass;
  javaCmDevInfoObject = g_javaCmDevInfoObject;
  return 0;

}

WebRtc_Word32 VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(
    bool attached) {
  if (attached && g_jvm->DetachCurrentThread() < 0) {
    WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, -1,
                 "%s: Could not detach thread from JVM", __FUNCTION__);
    return -1;
  }
  return 0;
}

/*
 * JNI callback from Java class. Called
 * when the camera has a new frame to deliver
 * Class:     org_webrtc_capturemodule_VideoCaptureAndroid
 * Method:    ProvideCameraFrame
 * Signature: ([BIJ)V
 */
void JNICALL VideoCaptureAndroid::ProvideCameraFrame(JNIEnv * env,
                                                     jobject,
                                                     jbyteArray javaCameraFrame,
                                                     jint length,
                                                     jlong context) {
  VideoCaptureAndroid* captureModule =
      reinterpret_cast<VideoCaptureAndroid*>(context);
  WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture,
               -1, "%s: IncomingFrame %d", __FUNCTION__,length);
  jbyte* cameraFrame= env->GetByteArrayElements(javaCameraFrame,NULL);
  captureModule->IncomingFrame((WebRtc_UWord8*) cameraFrame,
                               length,captureModule->_frameInfo,0);
  env->ReleaseByteArrayElements(javaCameraFrame,cameraFrame,JNI_ABORT);
}



VideoCaptureAndroid::VideoCaptureAndroid(const WebRtc_Word32 id)
    : VideoCaptureImpl(id), _capInfo(id), _javaCaptureObj(NULL),
      _captureStarted(false) {
  WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1,
               "%s: context %x", __FUNCTION__, (int) this);
}

// ----------------------------------------------------------------------------
//  Init
//
//  Initializes needed Java resources like the JNI interface to
//  VideoCaptureAndroid.java
// ----------------------------------------------------------------------------
WebRtc_Word32 VideoCaptureAndroid::Init(const WebRtc_Word32 id,
                                        const char* deviceUniqueIdUTF8) {
  const int nameLength = strlen(deviceUniqueIdUTF8);
  if (nameLength >= kVideoCaptureUniqueNameLength) {
    return -1;
  }

  // Store the device name
  _deviceUniqueId = new char[nameLength + 1];
  memcpy(_deviceUniqueId, deviceUniqueIdUTF8, nameLength + 1);

  if (_capInfo.Init() != 0) {
    WEBRTC_TRACE(webrtc::kTraceError,
                 webrtc::kTraceVideoCapture,
                 _id,
                 "%s: Failed to initialize CaptureDeviceInfo",
                 __FUNCTION__);
    return -1;
  }

  WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1, "%s:",
               __FUNCTION__);
  // use the jvm that has been set
  if (!g_jvm) {
    WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
                 "%s: Not a valid Java VM pointer", __FUNCTION__);
    return -1;
  }
  // get the JNI env for this thread
  JNIEnv *env;
  bool isAttached = false;

  // get the JNI env for this thread
  if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
    // try to attach the thread and get the env
    // Attach this thread to JVM
    jint res = g_jvm->AttachCurrentThread(&env, NULL);
    if ((res < 0) || !env) {
      WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
                   "%s: Could not attach thread to JVM (%d, %p)",
                   __FUNCTION__, res, env);
      return -1;
    }
    isAttached = true;
  }

  WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id,
               "get method id");

  // get the method ID for the Android Java
  // CaptureDeviceInfoClass AllocateCamera factory method.
  char signature[256];
  sprintf(signature, "(IJLjava/lang/String;)L%s;", AndroidJavaCaptureClass);

  jmethodID cid = env->GetMethodID(g_javaCmDevInfoClass, "AllocateCamera",
                                   signature);
  if (cid == NULL) {
    WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
                 "%s: could not get constructor ID", __FUNCTION__);
    return -1; /* exception thrown */
  }

  jstring capureIdString = env->NewStringUTF((char*) deviceUniqueIdUTF8);
  // construct the object by calling the static constructor object
  jobject javaCameraObjLocal = env->CallObjectMethod(g_javaCmDevInfoObject,
                                                     cid, (jint) id,
                                                     (jlong) this,
                                                     capureIdString);
  if (!javaCameraObjLocal) {
    WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id,
                 "%s: could not create Java Capture object", __FUNCTION__);
    return -1;
  }

  // create a reference to the object (to tell JNI that we are referencing it
  // after this function has returned)
  _javaCaptureObj = env->NewGlobalRef(javaCameraObjLocal);
  if (!_javaCaptureObj) {
    WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioDevice, _id,
                 "%s: could not create Java camera object reference",
                 __FUNCTION__);
    return -1;
  }

  // Delete local object ref, we only use the global ref
  env->DeleteLocalRef(javaCameraObjLocal);

  // Detach this thread if it was attached
  if (isAttached) {
    if (g_jvm->DetachCurrentThread() < 0) {
      WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioDevice, _id,
                   "%s: Could not detach thread from JVM", __FUNCTION__);
    }
  }

  return 0;
}

VideoCaptureAndroid::~VideoCaptureAndroid() {
  WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1, "%s:",
               __FUNCTION__);
  if (_javaCaptureObj == NULL || g_jvm == NULL) {
    WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
                 "%s: Nothing to clean", __FUNCTION__);
  }
  else {
    bool isAttached = false;
    // get the JNI env for this thread
    JNIEnv *env;
    if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
      // try to attach the thread and get the env
      // Attach this thread to JVM
      jint res = g_jvm->AttachCurrentThread(&env, NULL);
      if ((res < 0) || !env) {
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture,
                     _id,
                     "%s: Could not attach thread to JVM (%d, %p)",
                     __FUNCTION__, res, env);
      }
      else {
        isAttached = true;
      }
    }

    // get the method ID for the Android Java CaptureClass static
    // DeleteVideoCaptureAndroid  method. Call this to release the camera so
    // another application can use it.
    jmethodID cid = env->GetStaticMethodID(
        g_javaCmClass,
        "DeleteVideoCaptureAndroid",
        "(Lorg/webrtc/videoengine/VideoCaptureAndroid;)V");
    if (cid != NULL) {
      WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1,
                   "%s: Call DeleteVideoCaptureAndroid", __FUNCTION__);
      // Close the camera by calling the static destruct function.
      env->CallStaticVoidMethod(g_javaCmClass, cid, _javaCaptureObj);

      // Delete global object ref to the camera.
      env->DeleteGlobalRef(_javaCaptureObj);
      _javaCaptureObj = NULL;
    }
    else {
      WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
                   "%s: Failed to find DeleteVideoCaptureAndroid id",
                   __FUNCTION__);
    }

    // Detach this thread if it was attached
    if (isAttached) {
      if (g_jvm->DetachCurrentThread() < 0) {
        WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioDevice,
                     _id, "%s: Could not detach thread from JVM",
                     __FUNCTION__);
      }
    }
  }
}

WebRtc_Word32 VideoCaptureAndroid::StartCapture(
    const VideoCaptureCapability& capability) {
  CriticalSectionScoped cs(&_apiCs);
  WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, -1,
               "%s: ", __FUNCTION__);

  bool isAttached = false;
  WebRtc_Word32 result = 0;
  // get the JNI env for this thread
  JNIEnv *env;
  if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
    // try to attach the thread and get the env
    // Attach this thread to JVM
    jint res = g_jvm->AttachCurrentThread(&env, NULL);
    if ((res < 0) || !env) {
      WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
                   "%s: Could not attach thread to JVM (%d, %p)",
                   __FUNCTION__, res, env);
    }
    else {
      isAttached = true;
    }
  }

  if (_capInfo.GetBestMatchedCapability(_deviceUniqueId, capability,
                                        _frameInfo) < 0) {
    WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
                 "%s: GetBestMatchedCapability failed. Req cap w%d h%d",
                 __FUNCTION__, capability.width, capability.height);
    return -1;
  }

  // Store the new expected capture delay
  _captureDelay = _frameInfo.expectedCaptureDelay;

  WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1,
               "%s: _frameInfo w%d h%d", __FUNCTION__, _frameInfo.width,
               _frameInfo.height);

  // get the method ID for the Android Java
  // CaptureClass static StartCapture  method.
  jmethodID cid = env->GetMethodID(g_javaCmClass, "StartCapture", "(III)I");
  if (cid != NULL) {
    WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1,
                 "%s: Call StartCapture", __FUNCTION__);
    // Close the camera by calling the static destruct function.
    result = env->CallIntMethod(_javaCaptureObj, cid, _frameInfo.width,
                                _frameInfo.height, _frameInfo.maxFPS);
  }
  else {
    WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
                 "%s: Failed to find StartCapture id", __FUNCTION__);
  }

  // Detach this thread if it was attached
  if (isAttached) {
    if (g_jvm->DetachCurrentThread() < 0) {
      WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioDevice, _id,
                   "%s: Could not detach thread from JVM", __FUNCTION__);
    }
  }
  if (result == 0) {
    _requestedCapability = capability;
    _captureStarted = true;
  }
  WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, -1,
               "%s: result %d", __FUNCTION__, result);
  return result;
}

WebRtc_Word32 VideoCaptureAndroid::StopCapture() {
  CriticalSectionScoped cs(&_apiCs);
  WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, -1,
               "%s: ", __FUNCTION__);

  bool isAttached = false;
  WebRtc_Word32 result = 0;
  // get the JNI env for this thread
  JNIEnv *env = NULL;
  if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
    // try to attach the thread and get the env
    // Attach this thread to JVM
    jint res = g_jvm->AttachCurrentThread(&env, NULL);
    if ((res < 0) || !env) {
      WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
                   "%s: Could not attach thread to JVM (%d, %p)",
                   __FUNCTION__, res, env);
    }
    else {
      isAttached = true;
    }
  }

  memset(&_requestedCapability, 0, sizeof(_requestedCapability));
  memset(&_frameInfo, 0, sizeof(_frameInfo));

  // get the method ID for the Android Java CaptureClass StopCapture  method.
  jmethodID cid = env->GetMethodID(g_javaCmClass, "StopCapture", "()I");
  if (cid != NULL) {
    WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1,
                 "%s: Call StopCapture", __FUNCTION__);
    // Close the camera by calling the static destruct function.
    result = env->CallIntMethod(_javaCaptureObj, cid);
  }
  else {
    WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
                 "%s: Failed to find StopCapture id", __FUNCTION__);
  }

  // Detach this thread if it was attached
  if (isAttached) {
    if (g_jvm->DetachCurrentThread() < 0) {
      WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioDevice, _id,
                   "%s: Could not detach thread from JVM", __FUNCTION__);
    }
  }
  _captureStarted = false;

  WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, -1,
               "%s: result %d", __FUNCTION__, result);
  return result;
}

bool VideoCaptureAndroid::CaptureStarted() {
  CriticalSectionScoped cs(&_apiCs);
  WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, -1,
               "%s: ", __FUNCTION__);
  return _captureStarted;
}

WebRtc_Word32 VideoCaptureAndroid::CaptureSettings(
    VideoCaptureCapability& settings) {
  CriticalSectionScoped cs(&_apiCs);
  WEBRTC_TRACE(webrtc::kTraceStateInfo, webrtc::kTraceVideoCapture, -1,
               "%s: ", __FUNCTION__);
  settings = _requestedCapability;
  return 0;
}

WebRtc_Word32 VideoCaptureAndroid::SetCaptureRotation(
    VideoCaptureRotation rotation) {
  CriticalSectionScoped cs(&_apiCs);
  if (VideoCaptureImpl::SetCaptureRotation(rotation) == 0) {
    if (!g_jvm)
      return -1;

    // get the JNI env for this thread
    JNIEnv *env;
    bool isAttached = false;

    // get the JNI env for this thread
    if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
      // try to attach the thread and get the env
      // Attach this thread to JVM
      jint res = g_jvm->AttachCurrentThread(&env, NULL);
      if ((res < 0) || !env) {
        WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture,
                     _id,
                     "%s: Could not attach thread to JVM (%d, %p)",
                     __FUNCTION__, res, env);
        return -1;
      }
      isAttached = true;
    }

    jmethodID cid = env->GetMethodID(g_javaCmClass, "SetPreviewRotation",
                                     "(I)V");
    if (cid == NULL) {
      WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
                   "%s: could not get java SetPreviewRotation ID",
                   __FUNCTION__);
      return -1;
    }
    jint rotateFrame = 0;
    switch (rotation) {
      case kCameraRotate0:
        rotateFrame = 0;
        break;
      case kCameraRotate90:
        rotateFrame = 90;
        break;
      case kCameraRotate180:
        rotateFrame = 180;
        break;
      case kCameraRotate270:
        rotateFrame = 270;
        break;
    }
    env->CallVoidMethod(_javaCaptureObj, cid, rotateFrame);

    // Detach this thread if it was attached
    if (isAttached) {
      if (g_jvm->DetachCurrentThread() < 0) {
        WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioDevice,
                     _id, "%s: Could not detach thread from JVM",
                     __FUNCTION__);
      }
    }

  }
  return 0;
}

}  // namespace videocapturemodule
}  // namespace webrtc
