Android平台GB28181设备接入模块动态文字图片水印技术探究

​技术背景

前几年,我们发布的了Android平台GB28181设备接入模块,实现了不具备国标音视频能力的 Android终端,通过平台注册接入到现有的GB/T28181---2016或GB/T28181---2022服务。

Android终端除支持常规的音视频数据接入外,还可以支持移动设备位置(MobilePosition)订阅和通知、图像抓拍、语音广播和语音对讲、历史视音频下载和回放,支持对接数据类型如下:

  1. 编码前数据(目前支持的有YV12/NV21/NV12/I420/RGB24/RGBA32/RGB565等数据类型),其中,Android平台前后摄像头数据,或者屏幕数据,或者Unity拿到的数据,均属编码前数据;
  2. 编码后数据(如无人机等264/HEVC数据,或者本地解析的MP4音视频数据);
  3. 拉取RTSP或RTMP流并接入至GB28181平台(比如其他IPC的RTSP流,可通过Android平台GB28181接入到国标平台)。

实现的主要功能如下:

  • 视频格式H.264/H.265(Android H.265硬编码);
  • 音频格式G.711 A律、AAC;
  • 音量调节Android平台采集端支持实时音量调节;
  • H.264硬编码支持H.264特定机型硬编码;
  • H.265硬编码支持H.265特定机型硬编码;
  • 软硬编码参数配置支持gop间隔、帧率、bit-rate设置;
  • 软编码参数配置支持软编码profile、软编码速度、可变码率设置;
  • 支持横屏、竖屏推流;
  • Android平台支持后台service推送屏幕(推送屏幕需要5.0+版本);
  • 支持纯视频、音视频PS打包传输;
  • 支持RTP OVER UDP和RTP OVER TCP被动模式(TCP媒体流传输客户端);
  • 支持信令通道网络传输协议TCP/UDP设置;
  • 支持注册、注销,支持注册刷新及注册有效期设置;
  • 支持设备目录查询应答;
  • 支持心跳机制,支持心跳间隔、心跳检测次数设置;
  • 支持移动设备位置(MobilePosition)订阅和通知;
  • 适用国家标准:GB/T 28181---2016、GB/T28181---2022;
  • 支持语音广播;
  • 支持语音对讲;
  • 支持图像抓拍;
  • 支持历史视音频文件检索;
  • 支持历史视音频文件下载;
  • 支持历史视音频文件回放;
  • 支持云台控制和预置位查询;
  • 实时水印支持动态文字水印、png水印;
  • 镜像Android平台支持前置摄像头实时镜像功能;
  • 实时静音支持实时静音/取消静音;
  • 实时快照支持实时快照;
  • 降噪支持环境音、手机干扰等引起的噪音降噪处理、自动增益、VAD检测;
  • 外部编码前视频数据对接支持YUV数据对接;
  • 外部编码前音频数据对接支持PCM对接;
  • 外部编码后视频数据对接支持外部H.264数据对接;
  • 外部编码后音频数据对接外部AAC数据对接;
  • 扩展录像功能支持和录像SDK组合使用,录像相关功能。

Android平台的GB28181设备接入端模块,对水印要求比较高,除了传统的文字外,还需要图片、时间戳等,更重要的是,有动态水印的技术诉求。下面我们从水印创建、叠加、更新等维度介绍下动态水印的实现。

创建水印内容

  • 实时时间水印:获取当前系统时间,并将其格式化为字符串,以便添加到视频帧中作为水印。例如,使用 SimpleDateFormat 类来格式化时间:

    private String makeTimestampString() { if (null == date_format_) date_format_ = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    javascript 复制代码
    return date_format_.format(new Date());

    }

  • 文本水印:定义你想要添加到视频中的特定文本信息,比如设备编号、用户信息等。这些文本信息可以在代码中预先定义好,也可以根据实际情况从外部获取,比如从设备的配置文件中读取或者通过网络请求获取。

  • 图片水印:准备好要作为水印的图片文件,可以将其放置在项目的资源文件夹中,然后通过 BitmapFactory 类来加载图片:

    private Bitmap getAssetsBitmap() { if (null == context_) return null;

    ini 复制代码
    Context context = context_.get();
    if (null == context)
    	return null;
    
    Bitmap bitmap = null;
    try {
    	InputStream s = context.getAssets().open("tca.png");
    	bitmap = BitmapFactory.decodeStream(s);
    	s.close();
    } catch (Exception e) {
    	e.printStackTrace();
    }
    return bitmap;

    }

创建水印图像

  • 根据水印内容创建一个图像对象,用于表示水印。如果是文本水印,可以使用 Canvas 和 Paint 在一个空白的 Bitmap 上绘制文本。如果是图片水印,则直
    接使用之前加载的图片 Bitmap 作为水印图像。不过,可能需要根据实际需求对图片进行缩放、旋转等处理,以适应视频帧的大小和方向。

将水印图像叠加到视频帧上

  • 将水印图像的像素数据与视频帧的像素数据进行合并处理,从而实现水印的叠加。这一步通常需要对像素数据进行逐行或逐像素的操作,根据一定的算法将水印图像的像素值与视频帧的像素值进行混合。
  • 可以使用类似于图像处理软件中的图层叠加方式,将水印图像作为一个图层叠加到视频帧上。具体的实现方式可能因使用的视频处理库或框架而有所不同,但一般需要获取视频帧的像素数据缓冲区,然后在缓冲区中进行数据的修改和叠加操作。
  • 在叠加水印时,需要注意水印的位置、透明度和大小等参数,以确保水印不会遮挡视频的重要内容,同时又能够清晰地显示出来。可以根据实际需求在代码中设置这些参数,或者提供用户界面让用户可以自定义水印的参数。

更新动态水印

如果水印内容是动态变化的(如实时时间),需要定期更新水印图像。可以使用定时器或 Handler 来周期性地获取新的水印内容,重新创建水印图像,并将其叠加到视频帧上。例如,使用 Handler 的 postDelayed() 方法来实现定时更新。

SmartGBD动态水印设计

本文以大牛直播SDK的Android平台GB28181设备接入模块(SmartGBD)的动态水印为例,探讨下我们的动态水印设计实现,我们提供了图片水印、文字水印、全部水印、不加水印几个选项。

ini 复制代码
/* SmartGbdActivity.java
 * Created by daniusdk.com
 * WeChat: xinsheng120
 */
watermarkSelctor = (Spinner) findViewById(R.id.watermarkSelctor);

final String[] watermarks = new String[]{"图片水印", "全部水印", "文字水印", "不加水印"};

ArrayAdapter<String> adapterWatermark = new ArrayAdapter<String>(this,
		android.R.layout.simple_spinner_item, watermarks);

adapterWatermark.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

watermarkSelctor.setAdapter(adapterWatermark);

watermarkSelctor.setSelection(3,true);
watemarkType = 3;   //默认不加水印

watermarkSelctor.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

	@Override
	public void onItemSelected(AdapterView<?> parent, View view,
							   int position, long id) {

		watemarkType = position;

		Log.i(TAG, "[水印类型]Currently choosing: " + watermarks[position] + ", watemarkType: " + watemarkType);

			if (layer_post_thread_ != null) {
				layer_post_thread_.enableText(isHasTextWatermark());
				layer_post_thread_.enablePicture(isHasPictureWatermark());
				layer_post_thread_.update_layers();
			}
	}

	@Override
	public void onNothingSelected(AdapterView<?> parent) {

	}
});

以文字水印设计和数据投递为例:

java 复制代码
    private int postText1Layer(List<LibPublisherWrapper> publisher_list, int index, int left, int top, int video_w, int video_h) {
        Bitmap text_bitmap = makeTextBitmap("文本水印一", getFontSize(video_w) + 8,
                Color.argb(255, 200, 250, 0),
                false, 0, false);

        if (null == text_bitmap)
            return 0;

        for (LibPublisherWrapper i : publisher_list)
            i.PostLayerBitmap(index, left, top, text_bitmap, 0, 0, 0, 0,
                    0, 0, 0, 0, 0, 0);

        int ret = text_bitmap.getHeight();

        text_bitmap.recycle();

        return ret;
    }

对应封装设计:

csharp 复制代码
    public boolean PostLayerBitmap(int index, int left, int top,
                                      android.graphics.Bitmap bitmap, int clip_left, int clip_top, int clip_width, int clip_height,
                                      int is_vertical_flip, int is_horizontal_flip,
                                      int scale_width, int scale_height, int scale_filter_mode,
                                      int rotation_degree) {
        if (!check_native_handle())
            return false;

        if (!read_lock_.tryLock())
            return false;

        try {
            if (!check_native_handle())
                return false;

            return OK == lib_publisher_.PostLayerBitmap(get(), index, left, top,
                    bitmap, clip_left, clip_top, clip_width, clip_height, is_vertical_flip, is_horizontal_flip,
                    scale_width, scale_height, scale_filter_mode, rotation_degree);

        } catch (Exception e) {
            Log.e(TAG, "PostLayerBitmap Exception:", e);
            return false;
        } finally {
            read_lock_.unlock();
        }
    }

总结

Android平台的GB28181设备接入模块,主要用在如执法记录仪、智能安全帽、智能监控、智慧零售、智慧教育、远程办公、明厨亮灶、智慧交通、智慧工地、雪亮工程、平安乡村、生产运输、车载终端等场景,这些场景,好多对动态水印的诉求都非常大,动态水印的设计和实现,一方面需要足够灵活,另一方面,还需要尽可能的资源占用低,以上是大概的技术实现,感兴趣的开发者,可以跟我单独沟通探讨。

相关推荐
字节跳动视频云技术团队2 天前
从 VCloud 到 Agentic VCloud:Agent 时代的范式重构
人工智能·音视频开发
花椒技术6 天前
HJPusher / HJPlayer SDK 实践:我们为什么把直播推播链路拆成一套可复用能力
设计模式·harmonyos·直播
Bigger6 天前
我写了一个AI图像视频生成工具,免费API+本地部署,分享给大家
人工智能·图像识别·音视频开发
ltlovezh15 天前
ROI 编码学习指南:Android 与 FFmpeg 的真实实现边界
android·ffmpeg·音视频开发
iOStanhaitao17 天前
23.视频播放器项目实战-音视频播放
音视频开发
iOStanhaitao17 天前
6.第一个c++安卓程序编译运行
音视频开发
音视频牛哥25 天前
不只是等待 IDR:SmartMediaKit 播放器对 H.264 GDR 码流的完整适配实践
音视频开发·视频编码·直播
三木彤1 个月前
语音转文本python
音视频开发
鹧鸪晏1 个月前
Android GLSurfaceView 完全指南
android·音视频开发
ltlovezh1 个月前
AAC 元数据:ADTS 与 ASC 的区别、转换和常见坑
后端·ffmpeg·音视频开发