技术背景
我们在对接Android平台GB28181设备接入模块的开发者时,遇到这样的场景,除了Android设备(如执法记录仪、智能安全帽等)自带的camera或camera2前后摄像头数据外,还有些场景是需要外部编码前或编码后数据,比如对接OTG类似的外置数据源,如NV12、NV21、YV12、RGB或YUV等格式,这里做个简单的汇总。
接口描述
1. Android设备前后摄像头数据
这里以Camera2的接口为例。
@Override public void onCameraImageData(Image image) { if (null == libPublisher) return; if (isPushingRtmp || isRTSPPublisherRunning || isGB28181StreamRunning || isRecording) { if (0 == publisherHandle) return; Image.Plane[] planes = image.getPlanes(); int w = image.getWidth(), h = image.getHeight(); int y_offset = 0, u_offset = 0, v_offset = 0; Rect crop_rect = image.getCropRect(); if (crop_rect != null && !crop_rect.isEmpty()) { w = crop_rect.width(); h = crop_rect.height(); y_offset += crop_rect.top * planes[0].getRowStride() + crop_rect.left * planes[0].getPixelStride(); u_offset += (crop_rect.top / 2) * planes[1].getRowStride() + (crop_rect.left / 2) * planes[1].getPixelStride(); v_offset += (crop_rect.top / 2) * planes[2].getRowStride() + (crop_rect.left / 2) * planes[2].getPixelStride(); ; // Log.i(TAG, "crop w:" + w + " h:" + h + " y_offset:"+ y_offset + " u_offset:" + u_offset + " v_offset:" + v_offset); } int scale_w = 0, scale_h = 0, scale_filter_mode = 0; scale_filter_mode = 3; int rotation_degree = cameraImageRotationDegree_; if (rotation_degree < 0) { Log.i(TAG, "onCameraImageData rotation_degree < 0, may need to set orientation_ to 0, 90, 180 or 270"); return; } if (!post_image_lock_.tryLock()) { Log.i(TAG, "post_image_lock_.tryLock return false"); return; } try { if (publisherHandle != 0) { if (isPushingRtmp || isRTSPPublisherRunning || isGB28181StreamRunning || isRecording) { libPublisher.PostLayerImageYUV420888ByteBuffer(publisherHandle, 0, 0, 0, planes[0].getBuffer(), y_offset, planes[0].getRowStride(), planes[1].getBuffer(), u_offset, planes[1].getRowStride(), planes[2].getBuffer(), v_offset, planes[2].getRowStride(), planes[1].getPixelStride(), w, h, 0, 0, scale_w, scale_h, scale_filter_mode, rotation_degree); } } }catch (Exception e) { Log.e(TAG, "onCameraImageData Exception:", e); }finally { post_image_lock_.unlock(); } } }
对应接口定义:
/* * SmartPublisherJniV2.java * SmartPublisherJniV2 * * Author: daniusdk.com * Created by DaniuLive on 2015/09/20. */ /** * 投递层YUV420888图像, 专门为android.media.Image的android.graphics.ImageFormat.YUV_420_888格式提供的接口 * * @param index: 层索引, 必须大于等于0 * * @param left: 层叠加的左上角坐标, 对于第0层的话传0 * * @param top: 层叠加的左上角坐标, 对于第0层的话传0 * * @param y_plane: 对应android.media.Image.Plane[0].getBuffer() * * @param y_offset: 图像偏移, 这个主要目的是用来做clip的,一般传0 * * @param y_row_stride: 对应android.media.Image.Plane[0].getRowStride() * * @param u_plane: android.media.Image.Plane[1].getBuffer() * * @param u_offset: 图像偏移, 这个主要目的是用来做clip的,一般传0 * * @param u_row_stride: android.media.Image.Plane[1].getRowStride() * * @param v_plane: 对应android.media.Image.Plane[2].getBuffer() * * @param v_offset: 图像偏移, 这个主要目的是用来做clip的,一般传0 * * @param v_row_stride: 对应android.media.Image.Plane[2].getRowStride() * * @param uv_pixel_stride: 对应android.media.Image.Plane[1].getPixelStride() * * @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 PostLayerImageYUV420888ByteBuffer(long handle, int index, int left, int top, ByteBuffer y_plane, int y_offset, int y_row_stride, ByteBuffer u_plane, int u_offset, int u_row_stride, ByteBuffer v_plane, int v_offset, int v_row_stride, int uv_pixel_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);
2. YV12的数据接口
YV12的数据接口,主要是用于第三方的设备对接居多,这个接口的u_stride, v_stride分别是(width+1)/2,如果出来的数据需要旋转,通过rotation_degree来控制旋转角度即可。
/** * YV12数据接口 * * @param data: YV12 data * * @param width: 图像宽 * * @param height: 图像高 * * @param y_stride: y面步长 * * @param v_stride: v面步长 * * @param u_stride: u面步长 * * rotation_degree: 顺时针旋转, 必须是0, 90, 180, 270 * * @return {0} if successful */ public native int SmartPublisherOnYV12Data(long handle, byte[] data, int width, int height, int y_stride, int v_stride, int u_stride, int rotation_degree);
3. YUV数据接口
支持标准的I420数据接口对接,不再赘述:
/** * 投递层I420图像 * * @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 u_plane: u平面图像数据 * * @param u_offset: 图像偏移, 这个主要目的是用来做clip的,一般传0 * * @param u_row_stride: stride information * * * @param v_plane: v平面图像数据 * * @param v_offset: 图像偏移, 这个主要目的是用来做clip的,一般传0 * * @param v_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 PostLayerImageI420ByteBuffer(long handle, int index, int left, int top, ByteBuffer y_plane, int y_offset, int y_row_stride, ByteBuffer u_plane, int u_offset, int u_row_stride, ByteBuffer v_plane, int v_offset, int v_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);
4. NV21转I420并旋转接口
这个接口也是主要用于特定的数据类型对接,NV21的数据,直接转I420后,对接即可,接口参数比较简单,不再赘述。
/** * NV21转换到I420并旋转 * * @param src: nv21 data * * @param dst: 输出I420 data * * @param width: 图像宽 * * @param height: 图像高 * * rotation_degree: 顺时针旋转, 必须是0, 90, 180, 270 * * @return {0} if successful */ public native int SmartPublisherNV21ToI420Rotate(long handle, byte[] src, int src_y_stride, int src_uv_stride, byte[] dst, int dst_y_stride, int dst_u_stride, int dst_v_stride, int width, int height, int rotation_degree);
5. 支持RGBA数据接入(支持裁剪后数据接入,主要用于同屏场景)
RGBA的主要用于屏幕共享场景下。
/** * Set live video data(no encoded data). * * @param data: RGBA data * * @param rowStride: stride information * * @param width: width * * @param height: height * * @return {0} if successful */ public native int SmartPublisherOnCaptureVideoRGBAData(long handle, ByteBuffer data, int rowStride, int width, int height); /** * 投递裁剪过的RGBA数据 * * @param data: RGBA data * * @param rowStride: stride information * * @param width: width * * @param height: height * * @param clipedLeft: 左; clipedTop: 上; clipedwidth: 裁剪后的宽; clipedHeight: 裁剪后的高; 确保传下去裁剪后的宽、高均为偶数 * * @return {0} if successful */ public native int SmartPublisherOnCaptureVideoClipedRGBAData(long handle, ByteBuffer data, int rowStride, int width, int height, int clipedLeft, int clipedTop, int clipedWidth, int clipedHeight); /** * Set live video data(no encoded data). * * @param data: ABGR flip vertical(垂直翻转) data * * @param rowStride: stride information * * @param width: width * * @param height: height * * @return {0} if successful */ public native int SmartPublisherOnCaptureVideoABGRFlipVerticalData(long handle, ByteBuffer data, int rowStride, int width, int height);
6. 支持RGB565数据接入(主要用于同屏场景)
RGB565数据类型也主要用于屏幕采集这块。
/** * Set live video data(no encoded data). * * @param data: RGB565 data * * @param row_stride: stride information * * @param width: width * * @param height: height * * @return {0} if successful */ public native int SmartPublisherOnCaptureVideoRGB565Data(long handle,ByteBuffer data, int row_stride, int width, int height);
7. 针对NV12、NV21格式
/** * 投递层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); /** * 投递层NV12图像, 详细说明请参考PostLayerImageNV21ByteBuffer * * @return {0} if successful */ public native int PostLayerImageNV12ByteBuffer(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); /** * 投递层NV12图像, 详细说明请参考PostLayerImageNV21ByteBuffer * * @return {0} if successful */ public native int PostLayerImageNV12ByteArray(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);
8. RGBA8888、RGBX8888接口
/** * 投递层RGBA8888图像,如果不需要Aplpha通道的话, 请使用RGBX8888接口, 效率高 * * @param index: 层索引, 必须大于等于0, 注意:如果index是0的话,将忽略Alpha通道 * * @param left: 层叠加的左上角坐标, 对于第0层的话传0 * * @param top: 层叠加的左上角坐标, 对于第0层的话传0 * * @param rgba_plane: rgba 图像数据 * * @param offset: 图像偏移, 这个主要目的是用来做clip的, 一般传0 * * @param row_stride: stride information * * @param width: width, 必须大于1, 如果是奇数, 将减1 * * @param height: height, 必须大于1, 如果是奇数, 将减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 PostLayerImageRGBA8888ByteBuffer(long handle, int index, int left, int top, ByteBuffer rgba_plane, int offset, int 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); /** * 投递层RGBA8888图像, 详细说明请参考PostLayerImageRGBA8888ByteBuffer * * @return {0} if successful */ public native int PostLayerImageRGBA8888ByteArray(long handle, int index, int left, int top, byte[] rgba_plane, int offset, int 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); /** * 投递层RGBA8888图像, 详细说明请参考PostLayerImageRGBA8888ByteBuffer * * @return {0} if successful */ public native int PostLayerImageRGBA8888Native(long handle, int index, int left, int top, long rgba_plane, int offset, int 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); /** * 投递层RGBX8888图像 * * @param index: 层索引, 必须大于等于0 * * @param left: 层叠加的左上角坐标, 对于第0层的话传0 * * @param top: 层叠加的左上角坐标, 对于第0层的话传0 * * @param rgbx_plane: rgbx 图像数据 * * @param offset: 图像偏移, 这个主要目的是用来做clip的,一般传0 * * @param row_stride: stride information * * @param width: width, 必须大于1, 如果是奇数, 将减1 * * @param height: height, 必须大于1, 如果是奇数, 将减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 PostLayerImageRGBX8888ByteBuffer(long handle, int index, int left, int top, ByteBuffer rgbx_plane, int offset, int 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); /** * 投递层RGBX8888图像, 详细说明请参考PostLayerImageRGBX8888ByteBuffer * * @return {0} if successful */ public native int PostLayerImageRGBX8888ByteArray(long handle, int index, int left, int top, byte[] rgbx_plane, int offset, int 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); /** * 投递层RGBX8888图像, 详细说明请参考PostLayerImageRGBX8888ByteBuffer * * @return {0} if successful */ public native int PostLayerImageRGBX8888Native(long handle, int index, int left, int top, long rgbx_plane, int offset, int 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); /** * 投递层RGB888图像 * * @param index: 层索引, 必须大于等于0 * * @param left: 层叠加的左上角坐标, 对于第0层的话传0 * * @param top: 层叠加的左上角坐标, 对于第0层的话传0 * * @param rgb_plane: rgb888 图像数据 * * @param offset: 图像偏移, 这个主要目的是用来做clip的,一般传0 * * @param row_stride: stride information * * @param width: width, 必须大于1, 如果是奇数, 将减1 * * @param height: height, 必须大于1, 如果是奇数, 将减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 PostLayerImageRGB888Native(long handle, int index, int left, int top, long rgb_plane, int offset, int 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);
技术总结
以上是Android平台GB28181设备接入模块,视频编码前的数据接口类型设计,基本上涵盖了可能用到的所有编码前所有数据类型,当然,如果是编码后的H.264、H.265数据,也很容易对接,也都有相关的接口,简单来说,无论是自带的数据类型还是第三方外部数据对接(如Unity采集的数据),都可以很容易对接进来。