zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

【Android RTMP】NV21 图像旋转处理 ( 问题描述 | 图像顺时针旋转 90 度方案 | YUV 图像旋转细节 | 手机屏幕旋转方向 )

Android手机 问题 处理 方案 图像 屏幕 细节
2023-06-13 09:17:42 时间

文章目录

一、 NV21 图像格式与 Camera图像传感器方向问题


1. Camera 采集画面并预览推流 : 这里注意 , 之前图像被逆时针旋转了 90 度 , 设置了图像传感器角度后 , 预览图片纠正过来了 , 但是 Camera 的图像传感器采集的 NV21 格式的图像还是被旋转了 90 度 ;

2 . 电脑端观看直播效果展示 : 屏幕画面被逆时针旋转了 90 度 , 这是因为之前摄像头传感器只设置了将预览画面纠正过来 , 但是 NV21 格式的图像数据还是被逆时针旋转了 90 度的数据 ;

具体涉及到的图像格式 , 以及图像传感器方向 , 屏幕方向的关系 , 参考博客 【Android RTMP】Android Camera 视频数据采集预览 ( 图像传感器方向设置 | Camera 使用流程 | 动态权限申请 )

二、 NV21 图像格式视频旋转


1. 图像旋转问题及解决方案 ( 顺时针旋转 90 度 )

图像旋转问题及解决方案 :

① 问题描述 : 分析上面的画面 , 可以看到视频被逆时针旋转了 90 度 , 即画面图像被逆时针旋转了 90 度 ;

② 解决方案 : 将 Camera 采集的 NV21 格式的图像顺时针旋转 90 度 , 即可解决上述问题 ;

2. NV21 图像格式数旋转方案

NV21 图像格式数据排列 :

4 \times 4

像素的图片为例 , 其有

16

个 Y 数据 , UV 数据只有

4

组 , 共

8

个 ;

1 . 数据的排列格式如下矩阵 :

16

个 Y 数据在前 , 然后

4

组 (

8

个 ) VU 数据交替存放 ;

\begin{bmatrix} y1 & y2 & y3 & y4 \\\\ y5 & y6 & y7 & y8 \\\\ y9 & y10& y11& y12 \\\\ y13& y14& y15& y16 \\\\ v1 & u1 & v2 & u2 \\\\ v3 & u3 & v4 & u4\\ \end{bmatrix}

2 . 旋转像素灰度值 Y : 像素值顺时针 90 度旋转后的样式 ;

① 旋转矩阵 :

② 旋转后的最终 Y 灰度值 矩阵 :

\begin{bmatrix} y13 & y9 & y5 & y1 \\\\ y14 & y10 & y6 & y2 \\\\ y15 & y11& y7& y3 \\\\ y16& y12& y8& y4 \\ \end{bmatrix}

3. 旋转图像的 饱和度 色彩值 UV

旋转图像的 饱和度 色彩值 UV : UV 数据旋转后 , 只是给出了 UV 数据的位置 , 还需要将 UV 数据按照顺序排列 :

① 旋转 UV 数据矩阵 : 该旋转后只能代表 UV 数据组的位置 , 即 第一组 UV 数据 (

v3 \quad u3

) 在左上角 , 第二组 UV 数据 (

v1 \quad u1

) 在右上角 , 第三组 UV 数据 (

v4 \quad u4

) 在左下角 , 第四组 UV 数据 (

v2 \quad u2

) 在右下角 ;

② 旋转后的最终 UV 色彩值 饱和度 矩阵 :

\begin{bmatrix} v3 & u3 & v1 & u1 \\\\ v4 & u4 & v2 & u2\\ \end{bmatrix}

4. 旋转后的 NV21 格式

NV21 格式的图像的 YUV 值顺时针旋转 90 度后的 YUV 矩阵为 :

\begin{bmatrix} y13 & y9 & y5 & y1 \\\\ y14 & y10 & y6 & y2 \\\\ y15 & y11& y7& y3 \\\\ y16& y12& y8& y4 \\\\ v3 & u3 & v1 & u1 \\\\ v4 & u4 & v2 & u2\\ \end{bmatrix}

三、 Android 手机端屏幕旋转方向


1. 获取手机屏幕方向

获取手机屏幕方向 : 调用下面的方法 , 可以获取到

4

个手机屏幕方向 ;

mRotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();

调用上述方法 , 获取的手机屏幕方向是 Surface.ROTATION_0 , Surface.ROTATION_90 , Surface.ROTATION_180 , Surface.ROTATION_270 , 四个常量中的一个

2. Surface.ROTATION_0 正常竖屏方向

Surface.ROTATION_0 正常竖屏方向 :

① 常量含义 : ROTATION_0 常量代表手机自然方向逆时针旋转 0 度, 竖屏 ;

② 方向说明 :

  • 头部 ( 摄像头的一边 ) 在上边
  • 尾部 ( Home / 返回 键的一边 ) 在下边

一般的竖屏操作方式, 也是最常用的方式 ;

3. Surface.ROTATION_90 正常竖屏方向

Surface.ROTATION_90 正常竖屏方向 :

① 常量含义 : ROTATION_90 常量代表手机自然方向逆时针旋转 90 度, 横屏 ;

② 方向说明 :

  • 头部 ( 摄像头的一边 ) 在左边
  • 尾部 ( Home / 返回 键的一边 ) 在右边

一般横屏操作方式 ;

4. Surface.ROTATION_180 正常竖屏方向

Surface.ROTATION_180 正常竖屏方向 :

① 常量含义 : ROTATION_180 常量代表手机自然方向逆时针旋转 180 度, 竖屏 ;

② 方向说明 :

  • 头部 ( 摄像头的一边 ) 在下边
  • 尾部 ( Home / 返回 键的一边 ) 在上边

一般很少这样操作 ;

5. Surface.ROTATION_270 正常竖屏方向

Surface.ROTATION_270 正常竖屏方向 :

① 常量含义 : ROTATION_270 常量代表手机自然方向逆时针旋转 270 度, 横屏 ;

② 方向说明 :

  • 头部 ( 摄像头的一边 ) 在右边
  • 尾部 ( Home / 返回 键的一边 ) 在左边

一般横屏操作方式 ;

四、 Android 手机端屏幕方向获取代码示例


Android 手机端屏幕方向获取代码示例 :

    /**
     * 设置 Camera 预览方向
     * 如果不设置, 视频是颠倒的
     * 该方法内容拷贝自 {@link Camera#setDisplayOrientation} 注释, 这是 Google Docs 提供的
     * @param parameters
     */
    private void setCameraPreviewOrientation(Camera.Parameters parameters) {
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(mCameraFacing, info);

        /*
            获取屏幕相对于自然方向的角度
            自然方向就是正常的竖屏方向, 摄像头在上, Home 键在下, 对应 Surface.ROTATION_0

            ROTATION_0 是自然方向逆时针旋转 0 度, 竖屏
            头部 ( 摄像头的一边 ) 在上边
            尾部 ( Home / 返回 键的一边 ) 在下边
            一般竖屏操作方式, 也是最常用的方式

            ROTATION_90 是自然方向逆时针旋转 90 度, 横屏
            头部 ( 摄像头的一边 ) 在左边
            尾部 ( Home / 返回 键的一边 ) 在右边
            一般横屏操作方式

            ROTATION_180 是自然方向逆时针旋转 180 度, 竖屏
            头部 ( 摄像头的一边 ) 在下边
            尾部 ( Home / 返回 键的一边 ) 在上边
            一般很少这样操作

            ROTATION_270 是自然方向逆时针旋转 270 度, 横屏
            头部 ( 摄像头的一边 ) 在右边
            尾部 ( Home / 返回 键的一边 ) 在左边
            一般很少这样操作

            博客中配合截图说明这些方向
         */
        mRotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();

        int degrees = 0;
        switch (mRotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                /*
                    Camera 图像传感器采集的数据是按照竖屏采集的
                    原来设置的图像的宽高是 800 x 400
                    如果屏幕竖过来, 其宽高就变成 400 x 800, 宽高需要交换一下
                    这里需要通知 Native 层的 x264 编码器, 修改编码参数 , 按照 400 x 800 的尺寸进行编码
                    需要重新设置 x264 的编码参数
                 */
                mOnChangedSizeListener.onChanged(mHeight, mWidth);
                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; // compensate the mirror
        } else { // back-facing
            result = (info.orientation - degrees + 360) % 360;
        }
        mCamera.setDisplayOrientation(result);
    }