MYC-YT113i核心板及开发板
真正的国产核心板,100%国产物料认证
- 国产T113-i处理器配备2*Cortex-A7@1.2GHz ,RISC-V
- 外置DDR3接口、支持视频编解码器、HiFi4 DSP
- 接口丰富:视频采集接口、显示器接口、USB2.0 接口、CAN 接口、千兆以太网接口
- 工业级:-40℃~+85℃、尺寸37mm*39mm
- 邮票孔+LGA,140+50PIN
全志 T113-i 硬件编码支持情况
- JPEG/MJPEG up to 1080p@60fps
- Supports input picture scaler up/down
没了!
是的,你没看错!
全志T113-i只支持jpg/mjpg编码,并不支持mpeg4/h263/h264/h265/av1这些
也行吧,我们还可以拿这个加速JPG图片编码
开发环境配置
基础开发环境搭建参考上上一篇
https://bbs.elecfans.com/jishu_2408808_1_1.html
全志的视频引擎库环境搭建参考上一篇
https://bbs.elecfans.com/jishu_2412192_1_1.html
编码用的so库不同,额外从开发板上提取
adb pull /usr/lib/libvencoder.so
adb pull /usr/lib/libvenc_common.so
adb pull /usr/lib/libvenc_base.so
adb pull /usr/lib/libvenc_codec.so
adb pull /usr/lib/libvenc_jpeg.so
adb pull /usr/lib/libvenc_h264.so
adb pull /usr/lib/libvenc_h265.so
咦!似乎系统带了 h264/h264 的编码库,其实是不能用的,调用他们会报错
ERROR : cedarc <H264EncFrame:2721>: h264 encoder wait interrupt overtime
也难怪,硬件上本来就不支持 h264/h265 编码,但凡有一点点支持,全志应该也会写在pdf里(×
pic dir
基于opencv-mobile的jpg软件编码
https://github.com/nihui/opencv-mobile
opencv-mobile 通过调整编译参数,删减部分opencv源码,来最小化编译的 opencv 库
提供了 opencv 常用的功能,如读写图片,处理,矩阵操作等等 版本与上游同步,无第三方依赖
在绝大多数情况下,以 1/10 的体积无痛替换官方 opencv,尤其适合对体积有特殊要求的移动端和嵌入式环境
参考上上一篇的配置,我们编写基于opencv-mobile的jpg软件编码程序,读取图片,然后保存为新的jpg
cv::Mat bgr = cv::imread("in.jpg", 1);
cv::imwrite("out0.jpg", bgr);
基于cedarc的jpg硬件编码
- 创建 videoencoder 初始化,设置 jpg quality 参数
- 分配缓存区,存放YUV420数据
- RGB转YUV420
- 开始编码,获取返回的 bitstream 数据
- 将 bitstream 数据写入 out.jpg
其中步骤 1,2,4 是基于cedarc库完成,剩余步骤是软件实现
cedarc库的使用方法参考
https://github.com/MYIR-ALLWINNER/framework/tree/develop-yt113-framework/libcedarc/demo/vencoderDemo
vdecoder其实也自带了方便函数 AWJpecEnc,本文的实现代码也主要参考这个,精简代码去除不必要的 memcpy
https://github.com/MYIR-ALLWINNER/framework/blob/develop-yt113-framework/libcedarc/vencoder/vencoder.c#L743
硬件编码核心关键代码
#include <vencoder.h>
VideoEncoder* venc = VideoEncCreate(VENC_CODEC_JPEG);
VideoEncSetParameter(venc, VENC_IndexParamJpegQuality, (void*)&quality);
const int aligned_width = (width + 15) / 16 * 16;
const int aligned_height = (height + 15) / 16 * 16;
{
VencBaseConfig config;
memset(&config, 0, sizeof(config));
config.nInputWidth = width;
config.nInputHeight = height;
config.nDstWidth = width;
config.nDstHeight = height;
config.nStride = aligned_width;
config.eInputFormat = VENC_PIXEL_YUV420SP;
VideoEncInit(venc, &config);
}
{
VencAllocateBufferParam bufferParam;
bufferParam.nSizeY = aligned_width * aligned_height;
bufferParam.nSizeC = aligned_width * aligned_height / 2;
bufferParam.nBufferNum = 1;
AllocInputBuffer(venc, &bufferParam);
}
VencInputBuffer input_buffer;
memset(&input_buffer, 0, sizeof(input_buffer));
GetOneAllocInputBuffer(venc, &input_buffer);
unsigned char* yptr = (unsigned char*)input_buffer.pAddrVirY;
unsigned char* uvptr = (unsigned char*)input_buffer.pAddrVirC;
bgr2yuv420sp(bgr.data, width, height, yptr, uvptr, aligned_width);
FlushCacheAllocInputBuffer(venc, &input_buffer);
AddOneInputBuffer(venc, &input_buffer);
VideoEncodeOneFrame(venc);
AlreadyUsedInputBuffer(venc, &input_buffer);
ReturnOneAllocInputBuffer(venc, &input_buffer);
VencOutputBuffer output_buffer;
GetOneBitstreamFrame(venc, &output_buffer);
FILE* fp = fopen(path, "wb");
fwrite(output_buffer.pData0, 1, output_buffer.nSize0, fp);
if (output_buffer.nSize1)
{
fwrite(output_buffer.pData1, 1, output_buffer.nSize1, fp);
}
fclose(fp);
RGB转YUV420的neon加速
RGB转YUV的计算公式为
y = 0.29900 * r + 0.58700 * g + 0.11400 * b
u = -0.16874 * r - 0.33126 * g + 0.50000 * b + 128
v = 0.50000 * r - 0.41869 * g - 0.08131 * b + 128
这不是标准的 BT601 系数,而是JPG图片专用的系数,t113-i上的g2d硬件目前还没途径实现这样的颜色转换,因此只能使用CPU软件实现
Cortex-A7的浮点性能不强,先量化为纯整数实现
y = ( 38 * r + 75 * g + 15 * b + 64) >> 7
u = ((-43 * r - 84 * g + 127 * b + 128) >> 8) + 128
v = ((127 * r - 107 * g - 20 * b + 128) >> 8) + 128
基于新公式,加入ARM neon指令集加速
- vmull.u8 vmlal.u8 vmlsl.u8 等指令可以一条指令完成 8 个uchar与 8 个uchar乘法/累加/累减
- vld3.u8 指令可以一条指令完成 8 个RGB uchar加载和deinterleave
- vqrshrun.s16 指令可以一条指令完成 8 个short右移 + rounding + minmax(x,0,255)
uint8x8_t _v38 = vdup_n_u8(38);
uint8x8_t _v75 = vdup_n_u8(75);
uint8x8_t _v15 = vdup_n_u8(15);
uint8x8_t _v127 = vdup_n_u8(127);
uint8x8_t _v84 = vdup_n_u8(84);
uint8x8_t _v107 = vdup_n_u8(107);
uint8x8_t _v43 = vdup_n_u8(43);
uint8x8_t _v20 = vdup_n_u8(20);
uint16x8_t _v128 = vdupq_n_u16((128 << 8) + 128);
const unsigned char* bgr_ptr;
uint8x8x3_t _bgr = vld3_u8(bgr_ptr);
uint8x8_t _b = _bgr.val[0];
uint8x8_t _g = _bgr.val[1];
uint8x8_t _r = _bgr.val[2];
uint16x8_t _y = vmull_u8(_b, _v15);
_y = vmlal_u8(_y, _g, _v75);
_y = vmlal_u8(_y, _r, _v38);
uint8x8_t _y_u8 = vqrshrun_n_s16(vreinterpretq_s16_u16(_y), 7);
uint16x8_t _u = vmlal_u8(_v128, _b, _v127);
_u = vmlsl_u8(_u, _g, _v84);
_u = vmlsl_u8(_u, _r, _v43);
uint8x8_t _u_u8 = vqshrn_n_u16(_u, 8);
uint16x8_t _v = vmlal_u8(_v128, _r, _v127);
_v = vmlsl_u8(_v, _g, _v107);
_v = vmlsl_u8(_v, _b, _v20);
uint8x8_t _v_u8 = vqshrn_n_u16(_v, 8);
1080p图片编码性能测试
对1080p图片,分别使用 opencv-mobile 软编码和 cedarc 硬件编码库+neon加速做解码测试,循环调用记录最低耗时
可以看到米尔-全志T113-i开发板的jpg硬件编码能有效加速大约 12 倍!