从相机传感器获取相对旋转到当前设备方向
Getting relative rotation from the camera sensor to the current device orientation
有几个样本可以获取从相机传感器到当前设备方向的相对旋转,例如用于校正相机预览或 MediaRecorder.setOrientationHint(int)
但来自最新官方 github 相机样本的最新方法与旧方法不同(对于已弃用的 camera2 API 和存档的 camera2 样本的方法)
这是我使用所有方法并记录所有结果的示例代码,我们可以看到函数 computeRelativeRotationCamera2New
returns Surface.ROTATION_90
和 Surface.ROTATION_270
显示旋转的不同结果
那么正确的做法是什么?
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"
}
}
测试了不同方法录制的视频,发现最新示例中的最新方法对横向视频录制不正确,它播放视频倒置
我决定更新使用已弃用相机 api 的旧方法以使用 camera2 api(适用于 21+ API)并在我的项目中使用它
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
有几个样本可以获取从相机传感器到当前设备方向的相对旋转,例如用于校正相机预览或 MediaRecorder.setOrientationHint(int)
但来自最新官方 github 相机样本的最新方法与旧方法不同(对于已弃用的 camera2 API 和存档的 camera2 样本的方法)
这是我使用所有方法并记录所有结果的示例代码,我们可以看到函数 computeRelativeRotationCamera2New
returns Surface.ROTATION_90
和 Surface.ROTATION_270
显示旋转的不同结果
那么正确的做法是什么?
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"
}
}
测试了不同方法录制的视频,发现最新示例中的最新方法对横向视频录制不正确,它播放视频倒置
我决定更新使用已弃用相机 api 的旧方法以使用 camera2 api(适用于 21+ API)并在我的项目中使用它
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