Android平台GB28181接入端如何对接UVC摄像头?

时间:2022-11-02 07:17:38

我们在对接Android平台GB28181接入的时候,有公司提出这样的需求,除了采集执法记录仪摄像头自带的数据外,还想通过执法记录仪采集外接UVC摄像头。

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和执法记录仪自带的摄像头,就是数据源不同而已,其他信令交互和媒体传输并无差别。