- 安卓直播推流专栏博客总结
- 一、 NV21 与 I420 数据格式转换
- 二、 NV21 数据格式
- 三、 I420 数据格式
- 四、 NV21 格式与 I420 格式对比
- 五、 NV21 与 I420 图像大小
- 六、 NV21 格式转为 I420 格式算法
Android RTMP 直播推流技术专栏 :
0 . 资源和源码地址 :
- 资源下载地址 : 资源下载地址 , 服务器搭建 , x264 , faac , RTMPDump , 源码及交叉编译库 , 本专栏 Android 直播推流源码 ;
- GitHub 源码地址 : han1202012 / RTMP_Pusher
1. 搭建 RTMP 服务器 : 下面的博客中讲解了如何在 VMWare 虚拟机中搭建 RTMP 直播推流服务器 ;
- 【Android RTMP】RTMP 直播推流服务器搭建 ( Ubuntu 18.04.4 虚拟机 )
2. 准备视频编码的 x264 编码器开源库 , 和 RTMP 数据包封装开源库 :
-
【Android RTMP】RTMPDumb 源码导入 Android Studio ( 交叉编译 | 配置 CMakeList.txt 构建脚本 )
-
【Android RTMP】Android Studio 集成 x264 开源库 ( Ubuntu 交叉编译 | Android Studio 导入函数库 )
3. 讲解 RTMP 数据包封装格式 :
-
【Android RTMP】RTMP 数据格式 ( FLV 视频格式分析 | 文件头 Header 分析 | 标签 Tag 分析 | 视频标签 Tag 数据分析 )
-
【Android RTMP】RTMP 数据格式 ( FLV 视频格式分析 | AVC 序列头格式解析 )
4. 图像数据采集 : 从 Camera 摄像头中采集 NV21 格式的图像数据 , 并预览该数据 ;
-
【Android RTMP】Android Camera 视频数据采集预览 ( 视频采集相关概念 | 摄像头预览参数设置 | 摄像头预览数据回调接口 )
-
【Android RTMP】Android Camera 视频数据采集预览 ( NV21 图像格式 | I420 图像格式 | NV21 与 I420 格式对比 | NV21 转 I420 算法 )
-
【Android RTMP】Android Camera 视频数据采集预览 ( 图像传感器方向设置 | Camera 使用流程 | 动态权限申请 )
5. NV21 格式的图像数据编码成 H.264 格式的视频数据 :
-
【Android RTMP】x264 编码器初始化及设置 ( 获取 x264 编码参数 | 编码规格 | 码率 | 帧率 | B帧个数 | 关键帧间隔 | 关键帧解码数据 SPS PPS )
-
【Android RTMP】x264 图像数据编码 ( Camera 图像数据采集 | NV21 图像数据传到 Native 处理 | JNI 传输字节数组 | 局部引用变量处理 | 线程互斥 )
-
【Android RTMP】x264 图像数据编码 ( NV21 格式中的 YUV 数据排列 | Y 灰度数据拷贝 | U 色彩值数据拷贝 | V 饱和度数据拷贝 | 图像编码操作 )
6. 将 H.264 格式的视频数据封装到 RTMP 数据包中 :
-
【Android RTMP】RTMPDump 封装 RTMPPacket 数据包 ( 封装 SPS / PPS 数据包 )
-
【Android RTMP】RTMPDump 封装 RTMPPacket 数据包 ( 关键帧数据格式 | 非关键帧数据格式 | x264 编码后的数据处理 | 封装 H.264 视频数据帧 )
-
【Android RTMP】RTMPDump 推流过程 ( 独立线程推流 | 创建推流器 | 初始化操作 | 设置推流地址 | 启用写出 | 连接 RTMP 服务器 | 发送 RTMP 数据包 )
7. 阶段总结 : 阿里云服务器中搭建 RTMP 服务器 , 并使用电脑软件推流和观看直播内容 ;
-
【Android RTMP】RTMP 直播推流 ( 阿里云服务器购买 | 远程服务器控制 | 搭建 RTMP 服务器 | 服务器配置 | 推流软件配置 | 直播软件配置 | 推流直播效果展示 )
-
【Android RTMP】RTMP 直播推流阶段总结 ( 服务器端搭建 | Android 手机端编码推流 | 电脑端观看直播 | 服务器状态查看 )
8. 处理 Camera 图像传感器导致的 NV21 格式图像旋转问题 :
-
【Android RTMP】NV21 图像旋转处理 ( 问题描述 | 图像顺时针旋转 90 度方案 | YUV 图像旋转细节 | 手机屏幕旋转方向 )
-
【Android RTMP】NV21 图像旋转处理 ( 图像旋转算法 | 后置摄像头顺时针旋转 90 度 | 前置摄像头顺时针旋转 90 度 )
9. 下面这篇博客比较重要 , 里面有一个快速搭建 RTMP 服务器的脚本 , 强烈建议使用 ;
- 【Android RTMP】NV21 图像旋转处理 ( 快速搭建 RTMP 服务器 Shell 脚本 | 创建 RTMP 服务器镜像 | 浏览器观看直播 | 前置 / 后置摄像头图像旋转效果展示 )
10. 编码 AAC 音频数据的开源库 FAAC 交叉编译与 Android Studio 环境搭建 :
-
【Android RTMP】音频数据采集编码 ( 音频数据采集编码 | AAC 高级音频编码 | FAAC 编码器 | Ubuntu 交叉编译 FAAC 编码器 )
-
【Android RTMP】音频数据采集编码 ( FAAC 头文件与静态库拷贝到 AS | CMakeList.txt 配置 FAAC | AudioRecord 音频采样 PCM 格式 )
11. 解析 AAC 音频格式 :
- 【Android RTMP】音频数据采集编码 ( AAC 音频格式解析 | FLV 音频数据标签解析 | AAC 音频数据标签头 | 音频解码配置信息 )
12 . 将麦克风采集的 PCM 音频采样编码成 AAC 格式音频 , 并封装到 RTMP 包中 , 推流到客户端 :
-
【Android RTMP】音频数据采集编码 ( FAAC 音频编码参数设置 | FAAC 编码器创建 | 获取编码器参数 | 设置 AAC 编码规格 | 设置编码器输入输出参数 )
-
【Android RTMP】音频数据采集编码 ( FAAC 编码器编码 AAC 音频解码信息 | 封装 RTMP 音频数据头 | 设置 AAC 音频数据类型 | 封装 RTMP 数据包 )
-
【Android RTMP】音频数据采集编码 ( FAAC 编码器编码 AAC 音频采样数据 | 封装 RTMP 音频数据头 | 设置 AAC 音频数据类型 | 封装 RTMP 数据包 )
Android 直播推流流程 : 手机采集视频 / 音频数据 , 视频数据使用 H.264 编码 , 音频数据使用 AAC 编码 , 最后将音视频数据都打包到 RTMP 数据包中 , 使用 RTMP 协议上传到 RTMP 服务器中 ;
Android 端中主要完成手机端采集视频数据操作 , 并将视频数据传递给 JNI , 在 NDK 中使用 x264 将图像转为 H.264 格式的视频 , 最后将 H.264 格式的视频打包到 RTMP 数据包中 , 上传到 RTMP 服务器中 ;
本篇博客中主要讲解 Android 端数据采集 , Camera 摄像头获取 NV21 数据后 , 将 NV21 数据转为 x264 需要的 I420 数据 , 然后将 I420 数据编码成 H.264 格式的数据 ;
一、 NV21 与 I420 数据格式转换1 . 采集数据 :
① Camera 采集的数据 : 上面讲解了 Camera 摄像头采集图像数据之后 , 会回调 PreviewCallback 接口的 onPreviewFrame 方法 , onPreviewFrame 方法的 byte[] data 参数 , 就是 Camera 采集的图像数据 ;
② 采集的数据格式 : Camera 采集的图像数据是 NV21 格式的 ;
2 . x264 接收的格式 :
① RTMP 推流过程 : 在 RTMP 推流过程中 , 手机 Camera 采集到数据后 , 需要先使用 x264 将数据编码成 H.264 格式的 , 然后将 H.264 数据打包成 RTMP 数据 ;
② x264 接收格式 : x264 开源库的输入数据类型必须是 I420 格式 , 这里就涉及到了将 Camera 采集的 NV21 格式的数据 , 转为 x264 开源库需要的 I420 格式的数据 ;
3 . YUV420 格式 :
① 两个分支格式 : NV21 图像格式 和 I420 图像格式都属于 YUV420 图像格式的分支 ;
② YUV 说明 : YUV 各种中的 Y 表示明亮度和灰度 , UV 表示色度和饱和度 ;
③ 黑白图片 : 如果只有 Y 数据 , 那么就是灰度图片 , 这张图片是黑白的 ; UV 数据表示的是图像的色彩 ;
④ 存储原理 : 灰度数据 Y 保证全部满足 , 多个灰度数据公用 UV 色彩数据 , 这样就可以减少图像数据的大小 ;
二、 NV21 数据格式NV21 图像格式数据排列 : 以 4 × 4 4 \times 4 4×4 像素的图片为例 , 其有 16 16 16 个 Y 数据 , UV 数据只有 4 4 4 组 , 共 8 8 8 个 ;
① 数据的排列格式如下矩阵 :
[ y 1 y 2 y 3 y 4 y 5 y 6 y 7 y 8 y 9 y 10 y 11 y 12 y 13 y 14 y 15 y 16 v 1 u 1 v 2 u 2 v 3 u 3 v 4 u 4 ] \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} ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡y1y5y9y13v1v3y2y6y10y14u1u3y3y7y11y15v2v4y4y8y12y16u2u4⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤
② 内存中的数据表示 : 这也是 NV21 数据中的数组排列方式 , 每个 YUV 数据各占 1 1 1 字节 , 如 1 1 1 个 Y 数据占 1 1 1 字节 ;
byte[] data = {
y1 , y2 , y3 , y4 ,
y5 , y6 , y7 , y8 ,
y9 , y10, y11, y12,
y13, y14, y15, y16,
v1 , u1 , v2 , u2 ,
v3 , u3 , v4 , u4 ,
}
③ 数据使用情况 :
- 灰度数据 y1 , y2 , y5 , y6 使用的是 v1 , u1 色彩数据 ;
- 灰度数据 y3 , y4 , y7 , y8 使用的是 v2 , u2 色彩数据 ;
- 灰度数据 y9 , y10, y13, y14 使用的是 v3 , u3 色彩数据 ;
- 灰度数据 y11, y12, y15, y16 使用的是 v4 , u5 色彩数据 ;
I420 图像格式数据排列 : 以 4 × 4 4 \times 4 4×4 像素的图片为例 , 其有 16 16 16 个 Y 数据 , UV 数据只有 4 4 4 组 , 共 8 8 8 个 ;
① 数据的排列格式如下矩阵 :
[ y 1 y 2 y 3 y 4 y 5 y 6 y 7 y 8 y 9 y 10 y 11 y 12 y 13 y 14 y 15 y 16 u 1 u 2 u 3 u 4 v 1 v 2 v 3 v 4 ] \begin{bmatrix} y1 & y2 & y3 & y4 \\\\ y5 & y6 & y7 & y8 \\\\ y9 & y10& y11& y12 \\\\ y13& y14& y15& y16 \\\\ u1 & u2 & u3 & u4 \\\\ v1 & v2 & v3 & v4\\ \end{bmatrix} ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡y1y5y9y13u1v1y2y6y10y14u2v2y3y7y11y15u3v3y4y8y12y16u4v4⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤
② 内存中的数据表示 : 这也是 NV21 数据中的数组排列方式 , 每个 YUV 数据各占 1 1 1 字节 , 如 1 1 1 个 Y 数据占 1 1 1 字节 ;
byte[] data = {
y1 , y2 , y3 , y4 ,
y5 , y6 , y7 , y8 ,
y9 , y10, y11, y12,
y13, y14, y15, y16,
u1 , u2 , u3 , u4 ,
v1 , v2 , v3 , v4
}
③ 数据使用情况 :
- 灰度数据 y1 , y2 , y5 , y6 使用的是 v1 , u1 色彩数据 ;
- 灰度数据 y3 , y4 , y7 , y8 使用的是 v2 , u2 色彩数据 ;
- 灰度数据 y9 , y10, y13, y14 使用的是 v3 , u3 色彩数据 ;
- 灰度数据 y11, y12, y15, y16 使用的是 v4 , u5 色彩数据 ;
NV21 格式与 I420 格式对比 :
① 数据量 : 相同像素点数的图像 , 其数据大小是相同的 ;
② Y 灰度值排列 : 其灰度值排列方式是相同的 , 都是在 1 ~ 16 位置依次排列 16 个像素点数 ;
③ UV 色彩值排列 : 其色彩值排列是不同的 ,
-
NV21 格式中 , UV 色彩值是交替排序的 , v1 , u1 , v2 , u2 , v3 , u3 , v4 , u4 ;
-
I420 格式中 , UV 色彩值是 4 4 4 个 u 先排列 , 然后排 4 4 4 个 v 数据 , u1 , u2 , u3 , u4 , v1 , v2 , v3 , v4 ;
NV21 与 I420 图像大小 :
① 推导过程 :
-
4 4 4 像素值数据 : 4 4 4 个像素值 , 有 4 4 4 个 Y 灰度数据 , 占 4 4 4 字节 ; 1 1 1 个 Y 和 1 1 1 个 U 数据 , 占 2 2 2 字节 ;
-
4 4 4 像素值数据大小 :总共有 4 + 1 + 1 = 6 4 + 1 + 1 = 6 4+1+1=6 字节 ;
-
4 4 4 个像素对应 6 6 6 个字节 ;
-
归纳 : n n n 个像素对应 n × 3 2 \cfrac{n \times 3}{2} 2n×3 个字节数据 ;
② 计算公式为 : 已知图像像素的宽度和高度 ;
图 像 大 小 ( 字 节 ) = 宽 度 × 高 度 × 3 2 图像大小 ( 字节 ) = \cfrac{宽度 \times 高度 \times 3}{2} 图像大小(字节)=2宽度×高度×3
六、 NV21 格式转为 I420 格式算法NV21 格式与 I420 格式的区别只是 UV 色彩值的排列方式不一样 , NV21图像转为 I420 图像 , 只需要将 NV21 格式图像交替排列的 UV 数据 , 按照 I420 格式图像的先 U 后 V 的顺序重新进行排列即可完成图像格式转换 ;
先将 NV21 格式数据中的 Y 灰度数据拷贝出来放在一个数组中 , 再将 NV21 数据中的 V 数据 , 和 U 数据分别拷贝出来 , 这里得到三个数组 , 分别存储 Y 数据 , U 数据 , V 数据 ;
将 NV21 数据中的 V 数据 , 和 U 数据分别拷贝出来的方式时 , 读取 Y 数据完毕后 , 取下一个数据就是 V 数据 , 放入 V 数组 , 再取下一个数据就是 U 数据 , 放入 U 数组 , 之后继续获取数据 , 交替放入 V 数组 / U 数组 , 最终就得到了 3 3 3 个数组 , 即 Y 数组 , U 数组 , V 数组 ;