我们在做Android平台GB28181设备接入模块的时候,遇到这样的需求,做移动对讲的时候,是不需要视频数据的,但是国标平台侧,没有视频的话,大多平台又无法播纯音频打包的数据(网页端大多基于http-flv或webrtc实现),基于此,我们做了个简单的处理,通过Bitmap生成个32*32的黑帧,然后,确保上行的音视频数据都有,但是由于视频系黑帧数据,几乎不占用带宽。
废话不多说,上代码:
class LayerPostThread extends Thread { private volatile boolean is_exit_ = false; private long handle_ = 0; ByteBuffer image_buffer_ = null; private int width_ = 32; private int height_ = 32; @Override public void run() { image_buffer_ = null; if (0 == handle_) return; generateImageBuffer(width_, height_); while(!is_exit_) { postImageLayer(0, 0, 0, width_, height_); waitSleep(40); } image_buffer_ = null; } private void generateImageBuffer(int width, int height) { Bitmap bitmap = android.graphics.Bitmap.createBitmap(width, height, android.graphics.Bitmap.Config.ARGB_8888); bitmap.eraseColor(android.graphics.Color.argb(255, 0, 0, 0)); if (null == bitmap) return; if ( image_buffer_ != null) { image_buffer_.rewind(); if ( image_buffer_.remaining() < bitmap.getByteCount()) image_buffer_ = null; } if (null == image_buffer_) image_buffer_ = ByteBuffer.allocateDirect(bitmap.getByteCount()); bitmap.copyPixelsToBuffer(image_buffer_); } private int postImageLayer(int index, int left, int top, int video_w, int video_h) { if (video_w < 1 || video_h < 1) return 0; int scale_w = 0, scale_h = 0, scale_filter_mode = 0; libPublisher.PostLayerImageRGBA8888ByteBuffer(handle_, index, left, top, image_buffer_, 0, 4*video_w, video_w, video_h, 0, 0, scale_w, scale_h, scale_filter_mode,0); return 0; } public void startPost(long handle, int w, int h, boolean is_text, boolean is_pitcure) { this.is_exit_ = false; this.handle_ = handle; try { this.start(); } catch (Exception e) { e.printStackTrace(); } } public void stopPost() { this.is_exit_ = true; try { this.join(1000); } catch (Exception e) { e.printStackTrace(); } handle_ = 0; } }
外部调用逻辑如下:
private LayerPostThread layer_post_thread_ = null; private void startLayerPostThread() { if (null == layer_post_thread_) { layer_post_thread_ = new LayerPostThread(); layer_post_thread_.startPost(publisherHandle, video_width_, video_height_, isHasTextWatermark(), isHasPictureWatermark()); } } private void stopLayerPostThread() { if (layer_post_thread_ != null) { layer_post_thread_.stopPost(); layer_post_thread_ = null; } }
简单来说,读取到的Bitmap数据,转bytebuffer,然后通过调用PostLayerImageRGBA8888ByteBuffer()接口投递到底层,PostLayerImageRGBA8888ByteBuffer()的接口设计如下:
/* * SmartPublisherJniV2.java * Author: daniusdk.com */ /** * 投递层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);
这个接口非常强大,可以针对传下去的数据,进行裁剪、反转等各种操作,感兴趣的开发者可以尝试看看。