重写Android相机适配不同的设备,对于相机旋转角度问题解决方案

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上的解答。

 

posted @ 2017-12-18 17:08  Travis1022  阅读(4428)  评论(0编辑  收藏  举报