我们在对接Android平台GB28181接入的时候,有公司提出这样的需求,除了采集执法记录仪摄像头自带的数据外,还想通过执法记录仪采集外接UVC摄像头。
实际上,这块对我们来说有点炒冷饭了,不算新的诉求。大牛直播SDK在2016年对接RTMP推送的时候,就有提供过YUV数据接口,供外部比如UVC Camera使用。
UVC Camera获取到YUV数据后,调用I420的接口编码打包RTMP推出去即可,废话不多说,上接口设计:
/*** 传I420图像接口** @param data: I420 data** @param width: 图像宽** @param height: 图像高** @param y_stride: y stride** @param u_stride: u stride** @param v_stride: v stride** @return {0} if successful*/public native int SmartPublisherOnCaptureVideoI420DataV2(long handle, byte[] data, int width, int height, int y_stride, int u_stride, int v_stride);
以经典的UVCCamera为例,我们看看它能提供的数据类型:
#define PIXEL_FORMAT_RAW 0 // same as PIXEL_FORMAT_YUV
#define PIXEL_FORMAT_YUV 1
#define PIXEL_FORMAT_NV12 2 // one format of YUV420SemiPlanar
#define PIXEL_FORMAT_NV21 3 // one format of YUV420SemiPlanar
#define PIXEL_FORMAT_RGB 4
#define PIXEL_FORMAT_RGB565 5
#define PIXEL_FORMAT_RGBX 6
#define PIXEL_FORMAT_BGR 7
比较常见的是PIXEL_FORMAT_YUV、PIXEL_FORMAT_NV12、或PIXEL_FORMAT_NV21,这里我们就以PIXEL_FORMAT_NV21为例,我们提供了ByteBuffer和byte数组两种类型的数据接口:
/*** 投递层NV21图像** @param index: 层索引, 必须大于等于0** @param left: 层叠加的左上角坐标, 对于第0层的话传0** @param top: 层叠加的左上角坐标, 对于第0层的话传0** @param y_plane: y平面图像数据** @param y_offset: 图像偏移, 这个主要目的是用来做clip的,一般传0** @param y_row_stride: stride information** @param uv_plane: uv平面图像数据** @param uv_offset: 图像偏移, 这个主要目的是用来做clip的,一般传0** @param uv_row_stride: stride information** @param width: width, 必须大于1, 且必须是偶数** @param height: height, 必须大于1, 且必须是偶数** @param is_vertical_flip: 是否垂直翻转, 0不翻转, 1翻转** @param is_horizontal_flip:是否水平翻转, 0不翻转, 1翻转** @param scale_width: 缩放宽,必须是偶数, 0或负数不缩放** @param scale_height: 缩放高, 必须是偶数, 0或负数不缩放** @param scale_filter_mode: 缩放质量, 传0使用默认速度,可选等级范围是:[1,3],值越大缩放质量越好, 但速度越慢** @param rotation_degree: 顺时针旋转, 必须是0, 90, 180, 270, 注意:旋转是在缩放, 垂直/水品反转之后再做, 请留意顺序** @return {0} if successful*/public native int PostLayerImageNV21ByteBuffer(long handle, int index, int left, int top,ByteBuffer y_plane, int y_offset, int y_row_stride,ByteBuffer uv_plane, int uv_offset, int uv_row_stride,int width, int height, int is_vertical_flip, int is_horizontal_flip,int scale_width, int scale_height, int scale_filter_mode,int rotation_degree);/*** 投递层NV21图像, 详细说明请参考PostLayerImageNV21ByteBuffer** @return {0} if successful*/public native int PostLayerImageNV21ByteArray(long handle, int index, int left, int top,byte[] y_plane, int y_offset, int y_row_stride,byte[] uv_plane, int uv_offset, int uv_row_stride,int width, int height, int is_vertical_flip, int is_horizontal_flip,int scale_width, int scale_height, int scale_filter_mode,int rotation_degree);
具体的参数,可参考SmartPublisherV2的DEMO,看看onPreviewFrame()里面,我们针对摄像头数据回调的处理:
int w = videoWidth, h = videoHeight;
int y_stride = videoWidth, uv_stride = videoWidth;
int y_offset = 0, uv_offset = videoWidth * videoHeight;
int is_vertical_flip = 0, is_horizontal_flip = 0;
int rotation_degree = 0;
如果有水平镜像问题,is_horizontal_flip传1,水平翻转下就好。
需要注意的是,UVC Camera采集的多大分辨率,就设置多大分辨率下去,如果需要缩放数据,调用native接口的时候,传递scale_width和scale_height下去,让底层缩放就好了,甚至如果摄像头角度不对,旋转下就好了,是的,接口就这么智能。
其他,GB28181的接口,本文就不再赘述了,之前已经提过多次,UVC Camera和执法记录仪自带的摄像头,就是数据源不同而已,其他信令交互和媒体传输并无差别。