Android開發中經常需要重寫相機,由此會導致一些旋轉的情況(不同的設備攝像頭角度是不一樣的),此處按照解決思路給出解決方案:
情形一:只需要旋轉攝像頭方向以及最終的照片,注意兩者需要保持一致
1. 獲取當前相機攝像頭的角度,並進行相應的旋轉,方法如下:
此處獲取到的攝像頭角度可以保存下來,在后面情形二中會用到,這里存到靜態變量 orientationDegree 中。
public static int orientationDegree = 0;
/**
* 適配相機旋轉
*
* @param activity
* @param cameraId
* @param camera
*/
public void setCameraDisplayOrientation(Activity activity, int cameraId, Camera camera) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
//前置
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360;
}
//后置
else {
result = (info.orientation - degrees + 360) % 360;
}
orientationDegree = result;
camera.setDisplayOrientation(result);
}
2. 上述方法中涉及到的參數,第一個Activity,可以傳入當前相機Activity的Context,隨后進行強轉;第二個參數為相機ID(一般設備有多個攝像頭,前置,后置等等),下面將給出獲取相機ID的方法;
第三個參數為camera對象,當調用open方法的時候就可以獲取到 mCamera = Camera.open();
獲取相機ID的方法如下:
/**
* 獲取攝像頭ID
*
* @return
*/
private int getDefaultCameraId() {
int defaultId = -1;
int numberOfCameras = Camera.getNumberOfCameras();
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
for (int i = 0; i < numberOfCameras; i++) {
Camera.getCameraInfo(i, cameraInfo);
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
defaultId = i;
}
}
if (defaultId == -1) {
if (numberOfCameras > 0) {
//沒有后置攝像頭
defaultId = 0;
} else {
Logger.e("沒有攝像頭");
}
}
return defaultId;
}
情形二:在情形一的基礎上,需要旋轉拍攝過程中的視頻幀,與其他平台對接,涉及到JNI編程。此處參考了stackoverflow上的解答,鏈接如下:
https://stackoverflow.com/questions/14167976/rotate-an-yuv-byte-array-on-android
拍照回調過程中會產生圖片數據,格式為NV21.類型是字節數據。如果需要將此數據傳輸到其他平台或者保存到本地(本地視頻),則也需要進行相應的旋轉。
public class CameraPreviewCallback implements Camera.PreviewCallback {
public CameraPreviewCallback(CameraPreviewSend previewSend) {
this.mCameraPreviewSend = previewSend;
}
/**
* 在相機預覽時每產生一幀時回調
*
* @param data 圖片數據,格式 NV21
* @param camera 相機對象
*/
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
//todo:業務邏輯
}
}
情形一中獲取到的相機旋轉角度,此處根據角度來進行相應的旋轉。具體方法如下:
switch (orientationDegree) { case 0: localFrameSave(data); break; case 90: localFrameSave(picRotate90(data, previewWidth, previewHeight)); break; case 180: localFrameSave(picRotate180(data, previewWidth, previewHeight)); break; case 270: localFrameSave(picRotate270(data, previewWidth, previewHeight)); break; }
/** * 旋轉圖片,順時針旋轉90度 * * @param data nv21格式的圖片 * @param width 圖片寬度 * @param height 圖片高度 * @return 轉換后的圖片數據 */ public native byte[] picRotate90(byte[] data, int width, int height); static { System.loadLibrary("native-lib"); }
cpp部分:
/**
* 將nv21格式的圖片旋轉90度
*/
extern "C"
jbyteArray
Java_com_XXX_io_CameraPreviewSend_picRotate90(
JNIEnv *env, jobject /*this*/, jbyteArray data, jint width, jint height) {
jbyte *pBuffer = env->GetByteArrayElements(data, 0);
int length = env->GetArrayLength(data);
jbyte newData[length];//返回的圖片數據
//根據原數據pBuffer生成新數據newData
doRotate(pBuffer, newData, width, height);
jbyteArray array = env->NewByteArray(length);
env->SetByteArrayRegion(array, 0, length, newData);
env->ReleaseByteArrayElements(data, pBuffer, 0);
return array;
}
/**
* 將nv21格式的圖片旋轉90度
* @param data 原圖片數據
* @param newData 轉換所得圖片數據
* @param width 轉換后的圖片的寬度
* @param height 轉換后的圖片的高度
*/
void doRotate(jbyte *data, jbyte *newData, jint width, jint height) {
int i = 0;
for (int x = 0; x < width; x++) {
for (int y = height - 1; y >= 0; y--) {
newData[i] = data[y * width + x];
i++;
}
}
i = width * height * 3 / 2 - 1;
for (int x = width - 1; x > 0; x = x - 2) {
for (int y = 0; y < height / 2; y++) {
newData[i] = data[(width * height) + (y * width) + x];
i--;
newData[i] = data[(width * height) + (y * width) + (x - 1)];
i--;
}
}
}
旋轉180度的情況:
/** * 將nv21格式的圖片旋轉180度 * @param data * @param newData * @param width * @param height */ void doRotate180(jbyte *data, jbyte *newData, jint width, jint height) { int i = 0; int count = 0; for (i = width * height - 1; i >= 0; i--) { newData[count] = data[i]; count++; } for (i = width * height * 3 / 2 - 1; i >= width * height; i -= 2) { newData[count++] = data[i - 1]; newData[count++] = data[i]; } } /** * 將nv21格式的圖片旋轉180度 */ extern "C" jbyteArray Java_com_XXX_io_CameraPreviewSend_picRotate180( JNIEnv *env, jobject /*this*/, jbyteArray data, jint width, jint height) { jbyte *pBuffer = env->GetByteArrayElements(data, 0); int length = env->GetArrayLength(data); jbyte newData[length];//返回的圖片數據 //根據原數據pBuffer生成新數據newData doRotate180(pBuffer, newData, width, height); jbyteArray array = env->NewByteArray(length); env->SetByteArrayRegion(array, 0, length, newData); env->ReleaseByteArrayElements(data, pBuffer, 0); return array; }
旋轉270度的情況:
/** * 將nv21格式的圖片旋轉270度 * @param data * @param newData * @param width * @param height */ void doRotate270(jbyte *data, jbyte *newData, jint width, jint height) { // Rotate the Y int i = 0; for (int x = width - 1; x >= 0; x--) { for (int y = 0; y < height; y++) { newData[i] = data[y * width + x]; i++; } }// Rotate the U and V color components i = width * height; for (int x = width - 1; x > 0; x = x - 2) { for (int y = 0; y < height / 2; y++) { newData[i] = data[(width * height) + (y * width) + (x - 1)]; i++; newData[i] = data[(width * height) + (y * width) + x]; i++; } } } /** * 將nv21格式的圖片旋轉270度 */ extern "C" jbyteArray Java_com_XXX_io_CameraPreviewSend_picRotate270( JNIEnv *env, jobject /*this*/, jbyteArray data, jint width, jint height) { jbyte *pBuffer = env->GetByteArrayElements(data, 0); int length = env->GetArrayLength(data); jbyte newData[length];//返回的圖片數據 //根據原數據pBuffer生成新數據newData doRotate270(pBuffer, newData, width, height); jbyteArray array = env->NewByteArray(length); env->SetByteArrayRegion(array, 0, length, newData); env->ReleaseByteArrayElements(data, pBuffer, 0); return array; }
完結,很感謝stackoverflow上的解答。
