Reputation: 12123
There are a couple of samples to get the relative rotation from the camera sensor to the current device orientation, e.g. for using it for correcting camera preview or for MediaRecorder.setOrientationHint(int)
But the newest method from the latest official github camera samples works different than older methods (for deprecated camera2 API and method from archived camera2 sample)
Here's my sample code that uses all methods and logs all results, we can see that function computeRelativeRotationCamera2New
returns different result for Surface.ROTATION_90
and Surface.ROTATION_270
display rotation
So what is the correct method to do it?
class MainOrientation : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val names = listOf(
"Surface.ROTATION_0",
"Surface.ROTATION_90",
"Surface.ROTATION_180",
"Surface.ROTATION_270"
)
listOf( // test all possible device rotation
Surface.ROTATION_0,
Surface.ROTATION_90,
Surface.ROTATION_180,
Surface.ROTATION_270
).forEachIndexed { idx, displayRotation ->
// get related orientation (between device orientation and its camera sensor)
// for example for MediaRecorder.setOrientationHint(int)
Log.d(TAG, "Display Surface Rotation is ${names[idx]}")
Log.d(TAG, " ")
// two different methods for camera2 API (based on Google GitHub samples)
computeRelativeRotationCamera2New(this, displayRotation) // from current camera2 samples https://github.com/android/camera-samples
computeRelativeRotationCamera2(this, displayRotation) // from archived camera2 sample https://github.com/googlearchive/android-Camera2Basic
// deprecated camera API (based on Android Camera API documentation)
computeRelativeRotationCameraDeprecated(displayRotation) // https://developer.android.com/reference/android/hardware/Camera.html
Log.d(TAG, "-------------------------")
/* results from logcat:
Display Surface Rotation is Surface.ROTATION_0
computeRelativeRotationCamera2New 90
computeRelativeRotationCamera2 90
computeRelativeRotationCameraDeprecated 90
-------------------------
Display Surface Rotation is Surface.ROTATION_90
computeRelativeRotationCamera2New 180
computeRelativeRotationCamera2 0
computeRelativeRotationCameraDeprecated 0
-------------------------
Display Surface Rotation is Surface.ROTATION_180
computeRelativeRotationCamera2New 270
computeRelativeRotationCamera2 270
computeRelativeRotationCameraDeprecated 270
-------------------------
Display Surface Rotation is Surface.ROTATION_270
computeRelativeRotationCamera2New 0
computeRelativeRotationCamera2 180
computeRelativeRotationCameraDeprecated 180
*/
}
}
/**
* Computes rotation required to transform from the camera sensor orientation to the
* device's current orientation in degrees.
*
* @param characteristics the [CameraCharacteristics] to query for the sensor orientation.
* @param surfaceRotation the current device orientation as a Surface constant
* @return the relative rotation from the camera sensor to the current device orientation.
*/
// https://github.com/android/camera-samples/blob/master/CameraUtils/lib/src/main/java/com/example/android/camera/utils/OrientationLiveData.kt#L71
// https://github.com/android/camera-samples/blob/master/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/CameraFragment.kt#L273
private fun computeRelativeRotationCamera2New(
context: Context,
surfaceRotation: Int
): Int {
val characteristics = getCameraCharacteristics(context)
val sensorOrientationDegrees =
characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!!
val deviceOrientationDegrees = when (surfaceRotation) {
Surface.ROTATION_0 -> 0
Surface.ROTATION_90 -> 90
Surface.ROTATION_180 -> 180
Surface.ROTATION_270 -> 270
else -> 0
}
// Reverse device orientation for front-facing cameras
val sign = if (characteristics.get(CameraCharacteristics.LENS_FACING) ==
CameraCharacteristics.LENS_FACING_FRONT
) 1 else -1
// Calculate desired JPEG orientation relative to camera orientation to make
// the image upright relative to the device orientation
val result = (sensorOrientationDegrees - (deviceOrientationDegrees * sign) + 360) % 360
Log.d(TAG, "computeRelativeRotationCamera2New $result")
return result
}
// https://github.com/googlearchive/android-Camera2Video/blob/master/kotlinApp/Application/src/main/java/com/example/android/camera2video/Camera2VideoFragment.kt#L479
private fun computeRelativeRotationCamera2(context: Context, surfaceRotation: Int) {
// for computeRelativeRotationCamera2()
val SENSOR_ORIENTATION_DEFAULT_DEGREES = 90
val SENSOR_ORIENTATION_INVERSE_DEGREES = 270
val DEFAULT_ORIENTATIONS = SparseIntArray().apply {
append(Surface.ROTATION_0, 90)
append(Surface.ROTATION_90, 0)
append(Surface.ROTATION_180, 270)
append(Surface.ROTATION_270, 180)
}
val INVERSE_ORIENTATIONS = SparseIntArray().apply {
append(Surface.ROTATION_0, 270)
append(Surface.ROTATION_90, 180)
append(Surface.ROTATION_180, 90)
append(Surface.ROTATION_270, 0)
}
when (getCameraCharacteristics(context).get(CameraCharacteristics.SENSOR_ORIENTATION)!!) {
SENSOR_ORIENTATION_DEFAULT_DEGREES ->
Log.d(
TAG,
"computeRelativeRotationCamera2 ${DEFAULT_ORIENTATIONS.get(surfaceRotation)}"
)
SENSOR_ORIENTATION_INVERSE_DEGREES ->
Log.d(
TAG,
"computeRelativeRotationCamera2 ${INVERSE_ORIENTATIONS.get(surfaceRotation)}"
)
}
}
// https://developer.android.com/reference/android/hardware/Camera.html#setDisplayOrientation%28int%29
private fun computeRelativeRotationCameraDeprecated(
surfaceRotation: Int
): Int {
val cameraInfo = getCameraDeprecatedInfo()
val degrees = when (surfaceRotation) {
Surface.ROTATION_0 -> 0
Surface.ROTATION_90 -> 90
Surface.ROTATION_180 -> 180
Surface.ROTATION_270 -> 270
else -> 0
}
var result: Int
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (cameraInfo.orientation + degrees) % 360
result = (360 - result) % 360 // compensate the mirror
} else { // back-facing
result = (cameraInfo.orientation - degrees + 360) % 360
}
Log.d(TAG, "computeRelativeRotationCameraDeprecated $result")
return result
}
companion object {
// methods to get info about camera using deprecated camera and camera2 APIs
private fun getCameraManager(context: Context) =
context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
private fun getCameraId(manager: CameraManager): String {
return manager.cameraIdList.first {
val characteristics = manager.getCameraCharacteristics(it)
val facing = characteristics.get(CameraCharacteristics.LENS_FACING)
facing == CameraCharacteristics.LENS_FACING_BACK
}
}
private fun getCameraCharacteristics(context: Context): CameraCharacteristics {
val manager = getCameraManager(context)
return manager.getCameraCharacteristics(getCameraId(manager))
}
private fun getCameraDeprecatedInfo(): Camera.CameraInfo {
val cameraInfo = Camera.CameraInfo()
Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_BACK, cameraInfo)
return cameraInfo
}
private const val TAG = "orientation"
}
}
Upvotes: 2
Views: 890
Reputation: 12123
Tested recorded videos with different methods and found out that the newest method from the latest sample is incorrect for landscape video recording, it plays videos upside down
I decided to update old method that uses deprecated camera api to use camera2 api (works for 21+ API) and use it in my project
fun computeRelativeRotation(
context: Context,
surfaceRotation: Int, // display rotation
cameraId: String
): Int {
val characteristics = getCameraCharacteristics(context, cameraId)
val sensorOrientationDegrees = characteristics.get(
CameraCharacteristics.SENSOR_ORIENTATION
)!!
val degrees = when (surfaceRotation) {
Surface.ROTATION_0 -> 0
Surface.ROTATION_90 -> 90
Surface.ROTATION_180 -> 180
Surface.ROTATION_270 -> 270
else -> 0
}
var result: Int
val cameraFacing = characteristics.get(CameraCharacteristics.LENS_FACING)
if (cameraFacing == CameraCharacteristics.LENS_FACING_FRONT) {
result = (sensorOrientationDegrees + degrees) % 360
result = (360 - result) % 360 // compensate the mirror
} else { // back-facing
result = (sensorOrientationDegrees - degrees + 360) % 360
}
return result
}
private fun getCameraCharacteristics(
context: Context,
cameraId: String
): CameraCharacteristics {
return getCameraManager(context).getCameraCharacteristics(cameraId)
}
private fun getCameraManager(context: Context) =
context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
Upvotes: 2