成都免费网站制作,国外做详情页网站,wordpress花瓣模板,菏泽建设公司网站背景
我们在做Android平台GB28181设备接入模块的时候#xff0c;有开发者提到这样的诉求#xff1a;他们的智能头盔、执法记录仪等设备#xff0c;采集到的图像#xff0c;是旋转了90、180甚至270的#xff0c;设备本身无法针对图像做翻转或者旋转操作#xff0c;问我们…背景
我们在做Android平台GB28181设备接入模块的时候有开发者提到这样的诉求他们的智能头盔、执法记录仪等设备采集到的图像是旋转了90、180甚至270°的设备本身无法针对图像做翻转或者旋转操作问我们这种情况下需要如何处理
实际上这块我们前几年在做RTMP推送和轻量级RTSP服务模块的时候老早处理了这类问题。
鉴于Android平台video数据采集分camera和camera2Android 5.0接口我们单独说明
camera接口示例 //Github: https://github.com/daniulive/SmarterStreaming//author: 89030985qq.comOverridepublic void onPreviewFrame(byte[] data, Camera camera) {frameCount;if (frameCount % 3000 0) {Log.i(OnPre, gc);System.gc();Log.i(OnPre, gc-);}if (data null) {Parameters params camera.getParameters();Size size params.getPreviewSize();int bufferSize (((size.width | 0x1f) 1) * size.height * ImageFormat.getBitsPerPixel(params.getPreviewFormat())) / 8;camera.addCallbackBuffer(new byte[bufferSize]);} else {if (isRTSPPublisherRunning || isPushingRtmp || isRecording || isGB28181StreamRunning) {if (1 video_opt_) {/* byte[] i420_data new byte[videoWidth*videoHeight*3/2];libPublisher.SmartPublisherNV21ToI420Rotate(publisherHandle, data, videoWidth, videoWidth,i420_data, videoHeight, videoHeight/2, videoHeight/2,videoWidth, videoHeight, 90);libPublisher.SmartPublisherOnCaptureVideoI420DataV2(publisherHandle, i420_data, videoHeight, videoWidth,videoHeight, videoHeight/2, videoHeight/2);*/libPublisher.SmartPublisherOnCaptureVideoData(publisherHandle, data, data.length, currentCameraType, currentOrigentation);} else if (3 video_opt_) {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;// 镜像只用在前置摄像头场景下if (is_mirror FRONT currentCameraType) {// 竖屏, (垂直翻转-顺时旋转270度)等价于(顺时旋转旋转270度-水平翻转)if (PORTRAIT currentOrigentation)is_vertical_flip 1;elseis_horizontal_flip 1;}if (PORTRAIT currentOrigentation) {if (BACK currentCameraType)rotation_degree 90;elserotation_degree 270;} else if (LANDSCAPE_LEFT_HOME_KEY currentOrigentation) {rotation_degree 180;}if (640 w 480 h PORTRAIT currentOrigentation) {// 480 * 640 竖屏情况下裁剪到 368 * 640, 均匀裁剪掉视频的上下两部分h 368;y_offset 56 * y_stride;uv_offset (56 1) * uv_stride;}int scale_w 0, scale_h 0, scale_filter_mode 0;// 缩放测试/*if (w 1280 h 720) {scale_w align((int)(w * 0.8 0.5), 2);scale_h align((int)(h * 0.8 0.5), 2);} else {scale_w align((int)(w * 1.5 0.5), 2);scale_h align((int)(h * 1.5 0.5), 2);}if(scale_w 0 scale_h 0) {scale_filter_mode 3;Log.i(TAG, onPreviewFrame w: w , h: h s_w: scale_w , s_h: scale_h);}*/// 缩放测试---libPublisher.PostLayerImageNV21ByteArray(publisherHandle, 0, 0, 0,data, y_offset, y_stride, data, uv_offset, uv_stride, w, h,is_vertical_flip, is_horizontal_flip, scale_w, scale_h, scale_filter_mode, rotation_degree);// i420接口测试/*byte[] i420_data new byte[videoWidth*videoHeight*3/2];int u_stride videoWidth 1;int v_stride u_stride;libPublisher.SmartPublisherNV21ToI420Rotate(publisherHandle, data, y_stride, uv_stride, i420_data, y_stride, u_stride, v_stride,videoWidth, videoHeight, 0);y_offset 0;int u_offset y_offset videoWidth * videoHeight;int v_offset u_offset videoWidth*videoHeight/4;libPublisher.PostLayerImageI420ByteArray(publisherHandle, 0, 0, 0,i420_data, y_offset, y_stride, i420_data, u_offset, u_stride, i420_data, v_offset, v_stride,w, h, is_vertical_flip, is_horizontal_flip, scale_w, scale_h, scale_filter_mode, rotation_degree);*/// i420接口测试--}}camera.addCallbackBuffer(data);}}
对应的接口设计如下 /*** 投递层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 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);
对应Camera2的接口示例 Overridepublic void onCameraImageData(Image image) {Rect crop_rect image.getCropRect();if (isPushingRtmp || isRTSPPublisherRunning || isGB28181StreamRunning || isRecording) {if (libPublisher ! null) {Image.Plane[] planes image.getPlanes();int w image.getWidth(), h image.getHeight();int y_offset 0, u_offset 0, v_offset 0;if (!crop_rect.isEmpty()) {// 裁剪测试, 视频中心裁剪320*180一块区域/*crop_rect.left image.getWidth()/2 - 320/2;crop_rect.top image.getHeight()/2 - 180/2;crop_rect.right crop_rect.left 320;crop_rect.bottom crop_rect.top 180;*/// 裁剪测试--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;}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);}}}
对应的接口设计如下 /*** 投递层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);
总结
无需赘述看过以上两个接口后是不是觉得即使数据需要更客制化的处理比如缩放、水平翻转、垂直翻转、旋转等也都可以实现
实际上数据源这块不止Android自带的采集设备其他编码前数据类型如YV12/NV21/NV12/I420/RGB24/RGBA32/RGB565均可实现更精细的处理。