【Android RTMP】x264 图像数据编码 ( NV21 格式中的 YUV 数据排列 | Y 灰度数据拷贝 | U 色彩值数据拷贝 | V 饱和度数据拷贝 | 图像编码操作 )
文章目录
- 一、 NV21 图像数据中的 YUV 数据简介
- 二、向 x264 编码图片
- 三、 提取 NV21 数据中的灰度数据 Y
- 四、 提取 NV21 数据中的饱和度数据 U 和 色彩值数据 V
- 五、 图像编码操作
- 六、 x264 视频数据编码代码示例
一、 NV21 图像数据中的 YUV 数据简介
Camera 采集的数据是 NV21 格式的 ;
NV21 是 YUV 格式中的一种 , Y 代表灰度 , U 代表色彩值 , V 代表色彩的饱和度 ;
NV21 格式数据在内存中的表示 : 以
大小的图片为例 , 先存放
个像素的灰度值 Y 数据 , 然后
组色彩值 V 数据和饱和度 U 数据交替存放 ;
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 ,
}
【Android RTMP】Android Camera 视频数据采集预览 ( NV21 图像格式 | I420 图像格式 | NV21 与 I420 格式对比 | NV21 转 I420 算法 ) 博客中详细介绍了 NV21 数据中的 YUV 数据格式 ;
二、向 x264 编码图片
1 . x264 编码图片引入 : x264 编码器对图像数据进行编码 , 要先将 NV21 的图像数据中的 YUV 数据分别存储到 x264 编码图片中 ;
2 . x264_picture_t 结构体 : 该结构体代表了 x264 编码图片 , 该结构体定义在 x264.h 中 ;
typedef struct x264_picture_t
{
// ...
// 存储要编码的图片数据
x264_image_t img;
// ...
} x264_picture_t;
3 . x264 编码图片使用 :
① 声明 x264_picture_t 指针变量 : C++ 堆内存中的对象必须使用指针接收 ;
// x264 需要编码的图片
x264_picture_t *x264EncodePicture = 0;
② 初始化 x264_picture_t 对象 : 首先创建 x264_picture_t 对象 , 设置编码方式为 I420 , 以及图片的宽度 x264Param.i_width , 和图片高度 x264Param.i_height ;
// 初始化 x264 编码图片
x264EncodePicture = new x264_picture_t;
// 为 x264 编码图片分配内存
x264_picture_alloc(x264EncodePicture, X264_CSP_I420, x264Param.i_width, x264Param.i_height);
③ 释放 x264_picture_t 对象 : 调用 x264_picture_clean 方法释放资源 , 然后销毁对象 ;
// 只要调用该方法, x264_picture_t 必须重新进行初始化
// 因为图片大小改变了, 那么对应的图片不能再使用原来的参数了
// 释放原来的 x264_picture_t 图片, 重新进行初始化
// 析构函数中也要进行释放
if (x264EncodePicture) {
x264_picture_clean(x264EncodePicture);
delete x264EncodePicture;
x264EncodePicture = 0;
}
三、 提取 NV21 数据中的灰度数据 Y
1 . 计算灰度数据的个数 : 灰度数据的个数 , 就是像素的个数 , 每个像素点都有一个灰度数据 ;
// 灰色值的个数, 单位字节
YByteCount = width * height;
2 . 将灰度数据存储到 x264_picture_t 中 : 在 NV21 格式的图像数据中 , 前 YByteCount 个数据是 YByteCount 个像素点的灰度数据 , 将这些灰度数据拷贝到 x264 编码图像中 ;
3 . 数据接收方 : x264_picture_t* x264EncodePicture 图像的 img 成员的 plane[0] 指针指向的地址 , 接收 YByteCount 个灰度数据 ;
4 . 代码示例 :
// 从 Camera 采集的 NV21 格式的 data 数据中
// 将 YUV 中的 Y 灰度值数据, U 色彩值数据, V 色彩饱和度数据提取出来
memcpy(x264EncodePicture->img.plane[0], data, YByteCount);
四、 提取 NV21 数据中的饱和度数据 U 和 色彩值数据 V
1 . 计算饱和度数据 U 的个数 : 饱和度数据 U 的个数 , 与色彩值数据 V 的个数相同 , 是灰度值数据 Y 个数的
;
// 灰色值的个数, 单位字节
YByteCount = width * height;
// U 色彩值, V 饱和度 个数
UVByteCount = YByteCount / 4;
2 . 将灰度数据存储到 x264_picture_t 中 : 在 NV21 格式的图像数据中 , 色彩值数据 V , 饱和度数据 U , 交替存储 , V 在前 ( 偶数位置 ), U 在后 ( 奇数位置 ) ;
① U 色相 / 色彩值数据 : 存储在 YByteCount 后的奇数索引位置
② V 色彩饱和度数据 : 存储在 YByteCount 后的偶数索引位置
3 . 代码示例 :
// 取出 NV21 数据中交替存储的 VU 数据
// V 在前 ( 偶数位置 ), U 在后 ( 奇数位置 ), 交替存储
for(int i = 0; i < UVByteCount; i ++){
// U 色相 / 色彩值数据, 存储在 YByteCount 后的奇数索引位置
*(x264EncodePicture->img.plane[1] + i) = *(data + YByteCount + i * 2 + 1);
// V 色彩饱和度数据, 存储在 YByteCount 后的偶数索引位置
*(x264EncodePicture->img.plane[2] + i) = *(data + YByteCount + i * 2);
}
五、 图像编码操作
1 . 图片编码 :
① 普通帧 : 一般情况下, 一张图像编码出一帧数据 , pp_nal 是一帧数据, pi_nal 表示帧数为 1
② 关键帧 : 如果这个帧是关键帧, 那么 pp_nal 将会编码出 3 帧数据 , pi_nal 表示帧数为 3
③ 关键帧数据 : SPS 帧, PPS 帧, 画面帧 ;
2 . 编码方法函数原型 :
int x264_encoder_encode( x264_t *, x264_nal_t **pp_nal, int *pi_nal,
x264_picture_t *pic_in, x264_picture_t *pic_out );
① x264_t * 参数 : x264 视频编码器
② x264_nal_t **pp_nal 参数 : 编码后的帧数据, 可能是 1 帧, 也可能是 3 帧
③ int *pi_nal 参数 : 编码后的帧数, 1 或 3
④ x264_picture_t *pic_in 参数 : 输入的 NV21 格式的图片数据
⑤ x264_picture_t *pic_out 参数 : 输出的图片数据
3 . 图像编码代码示例 :
// 编码后的数据, 这是一个帧数据, 1 帧或 3帧
x264_nal_t *pp_nal;
// 编码后的数据个数, 帧的个数, 1 或 3
int pi_nal;
// 输出的图片数据
x264_picture_t pic_out;
// 编码核心操作
x264_encoder_encode(x264VedioCodec, &pp_nal, &pi_nal, x264EncodePicture, &pic_out);
六、 x264 视频数据编码代码示例
x264 编码器将 NV21 图像数据编码为 H.264 代码 :
/**
* 视频数据编码
* 接收 int8_t 类型的原因是, 这里处理的是 jbyte* 类型参数
* jbyte 类型就是 int8_t 类型
* @param data 视频数据指针
*/
void VedioChannel::encodeCameraData(int8_t *data) {
// 加锁, 设置视频编码参数 与 编码互斥
pthread_mutex_lock(&mMutex);
// 参数中的 data 是 NV21 格式的
// 前面 YByteCount 字节个 Y 灰度数据
// 之后是 UVByteCount 字节个 VU 数据交替存储
// UVByteCount 字节 V 数据, UVByteCount 字节 U 数据
// 从 Camera 采集的 NV21 格式的 data 数据中
// 将 YUV 中的 Y 灰度值数据, U 色彩值数据, V 色彩饱和度数据提取出来
memcpy(x264EncodePicture->img.plane[0], data, YByteCount);
// 取出 NV21 数据中交替存储的 VU 数据
// V 在前 ( 偶数位置 ), U 在后 ( 奇数位置 ), 交替存储
for(int i = 0; i < UVByteCount; i ++){
// U 色相 / 色彩值数据, 存储在 YByteCount 后的奇数索引位置
*(x264EncodePicture->img.plane[1] + i) = *(data + YByteCount + i * 2 + 1);
// V 色彩饱和度数据, 存储在 YByteCount 后的偶数索引位置
*(x264EncodePicture->img.plane[2] + i) = *(data + YByteCount + i * 2);
}
// 下面两个是编码时需要传入的参数, 这两个参数地址, x264 编码器会想这两个地址写入值
// 编码后的数据, 这是一个帧数据
x264_nal_t *pp_nal;
// 编码后的数据个数, 帧的个数
int pi_nal;
// 输出的图片数据
x264_picture_t pic_out;
/*
int x264_encoder_encode( x264_t *, x264_nal_t **pp_nal, int *pi_nal,
x264_picture_t *pic_in, x264_picture_t *pic_out );
函数原型 :
x264_t * 参数 : x264 视频编码器
x264_nal_t **pp_nal 参数 : 编码后的帧数据, 可能是 1 帧, 也可能是 3 帧
int *pi_nal 参数 : 编码后的帧数, 1 或 3
x264_picture_t *pic_in 参数 : 输入的 NV21 格式的图片数据
x264_picture_t *pic_out 参数 : 输出的图片数据
普通帧 : 一般情况下, 一张图像编码出一帧数据, pp_nal 是一帧数据, pi_nal 表示帧数为 1
关键帧 : 如果这个帧是关键帧, 那么 pp_nal 将会编码出 3 帧数据, pi_nal 表示帧数为 3
关键帧数据 : SPS 帧, PPS 帧, 画面帧
*/
x264_encoder_encode(x264VedioCodec, &pp_nal, &pi_nal, x264EncodePicture, &pic_out);
// 后续还有操作, 本博客中暂时省略 ...
// 解锁, 设置视频编码参数 与 编码互斥
pthread_mutex_unlock(&mMutex);
}
相关文章
- 使用青花瓷对Android app 抓包
- strictmode android,Android 应用性能优化-StrictMode(严格模式)
- strictmode android,Android中的StrictMode
- Android自定义控件之滑动解锁
- android studio安装教程(包安装成功,不成功你找我)
- 【Android布局】在程序中设置android gravity 和 android layout Gravity属性
- android调用相册并显示图片_Android获取相册列表
- 【Android FFMPEG 开发】FFMPEG 读取音视频流中的数据到 AVPacket ( 初始化 AVPacket 数据 | 读取 AVPacket )
- 【Android RTMP】x264 图像数据编码 ( Camera 图像数据采集 | NV21 图像数据传到 Native 处理 | JNI 传输字节数组 | 局部引用变量处理 | 线程互斥 )
- 【Android 内存优化】Android 工程中使用 libjpeg-turbo 压缩图片 ( JNI 传递 Bitmap | 获取位图信息 | 获取图像数据 | 图像数据过滤 | 释放资源 )
- 【Android 高性能音频】Oboe 开发流程 ( Oboe 音频帧简介 | AudioStreamCallback 中的数据帧说明 )
- 【Android 安装包优化】资源混淆 ( resources.arsc 资源映射表文件格式 | 头文件 数据格式 | 全局字符串池 数据格式 | 包数据 数据格式 | 包头 数据格式 )
- 【错误记录】反射内部类报错 ( Android 使用 Hook 时反射内部类报错 )
- 【IOC 控制反转】Android 事件依赖注入 ( 事件依赖注入具体的操作细节 | 创建 事件监听器 对应的 动态代理 | 动态代理的数据准备 | 创建调用处理程序 | 创建动态代理实例对象 )
- 【Android Gradle 插件】自定义 Gradle 插件优化图片 ① ( Android 中的 WebP 图片格式使用 | WebP 格式转换 | WebP 参考文档 )
- 【错误记录】Android Studio 编译报错 ( Module was compiled with an incompatible version of Kotlin. The binary )
- 【Android Gradle 插件】组件化中的 Gradle 构建脚本实现 ⑤ ( 优化 Gradle 构建脚本 | 构建脚本结构 | 闭包定义及用法 | 依赖配置 | android 块配置 )
- 利用Socket与服务器端交互的简单Android范例代码详解手机开发
- 再谈Android Binder跨进程通信原理详解手机开发
- Android实现PHP连接MySQL进行数据交互(android通过php连接mysql)
- Connecting Android to Oracle: The Ultimate Guide for Seamless Integration.(android连接oracle)
- Android系统自带样式(android:theme)
- Android不同Activity间数据的传递Bundle对象的应用
- Android应用内调用第三方应用的方法
- Android实现GPS定位代码实例
- android中WebView和javascript实现数据交互实例
- Android实现字幕滚动的方法