前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android平台RTMP推送|轻量级RTSP服务|GB28181接入之文字、png图片水印的精进之路

Android平台RTMP推送|轻量级RTSP服务|GB28181接入之文字、png图片水印的精进之路

原创
作者头像
音视频牛哥
发布2024-06-10 23:45:04
650
发布2024-06-10 23:45:04
举报

​技术背景

Android平台推流模块,添加文字或png水印,不是一件稀奇的事儿,常规的做法也非常多,本文,我们主要是以大牛直播SDK水印迭代,谈谈音视频行业的精进和工匠精神。

第一代:不可动态改变的文字、png水印

2015年,我们在做Android平台RTMP推送模块和轻量级RTSP服务模块的时候,有这样的场景诉求,应急指挥、智慧巡检或安防类,都有文字或png水印的技术诉求,针对这种情况,我们当时做了如下的接口设计:

代码语言:java
复制
   /*
    * SmartPublisherJniV2.java
    * WebSite: https://daniusdk.com
    * 
    * Created by DaniuLive on 2015/09/20.
    */ 
   /**
     * Set Text water-mark(设置文字水印)
     * 
     * @param fontSize: it should be "MEDIUM", "SMALL", "BIG"
     * 
     * @param waterPostion: it should be "TOPLEFT", "TOPRIGHT", "BOTTOMLEFT", "BOTTOMRIGHT".
     * 
     * @param xPading, yPading: the distance of the original picture.
     * 
     * <pre> The interface is only used for setting font water-mark when publishing stream. </pre>  
     * 
     * @return {0} if successful
     */
    /*
     * 已废弃, 请使用层模式加水印
     *public native int SmartPublisherSetTextWatermark(long handle, String waterText, int isAppendTime, int fontSize, int waterPostion, int xPading, int yPading);
    */
    
    /**
     * Set Text water-mark font file name(设置文字水印字体路径)
	 *
     * @param fontFileName:  font full file name, e.g: /system/fonts/DroidSansFallback.ttf
	 *
	 * @return {0} if successful
     */
    /* 已废弃, 请使用层模式加水印
     * public native int SmartPublisherSetTextWatermarkFontFileName(long handle, String fontFileName);
     */
	
    /**
     * Set picture water-mark(设置png图片水印)
     * 											
     * @param picPath: the picture working path, e.g: /sdcard/logo.png
     * 
     * @param waterPostion: it should be "TOPLEFT", "TOPRIGHT", "BOTTOMLEFT", "BOTTOMRIGHT".
     * 
     * @param picWidth, picHeight: picture width & height
     * 
     * @param xPading, yPading: the distance of the original picture.
     * 
     * <pre> The interface is only used for setting picture(logo) water-mark when publishing stream, with "*.png" format </pre>  
     * 
     * @return {0} if successful
     */
    /*
     * 已废弃, 请使用层模式加水印
     *public native int SmartPublisherSetPictureWatermark(long handle, String picPath, int waterPostion, int picWidth, int picHeight, int xPading, int yPading);
     */

第二代:实时动态文字、png水印

尽管上面的水印,已经可以满足大多技术场景的需求,但在我们内部,却被一直诟病,因为违背我们SDK设计和使用的smart策略。

随着Android平台GB28181设备接入模块的发布,基于GB28181设备接入模块,对水印提出来更高的要求,好多公司或开发者,需要实时更新水印内容(比如MobilePosition位置信息、实时时间、作业内容等),为此,我们想到的是,这一版,我们需要实现动态水印能力。

以文字水印为例,我们的实现和设计如下,通过bitmap获取到文字水印数据,然后通过PostLayerImageRGBA8888ByteBuffer()接口投递到jni,这种设计,几乎已经满足了100%的技术诉求:

代码语言: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;

        ByteBuffer buffer = ByteBuffer.allocateDirect(text_bitmap.getByteCount());
        text_bitmap.copyPixelsToBuffer(buffer);

        for (LibPublisherWrapper i : publisher_list)
            i.PostLayerImageRGBA8888ByteBuffer(index, left, top, buffer, 0,
                    text_bitmap.getRowBytes(), text_bitmap.getWidth(), text_bitmap.getHeight(),
                    0, 0, 0, 0, 0, 0); 

        int ret = text_bitmap.getHeight();

        text_bitmap.recycle();

        return ret;
    }

第三代:Bitmap接口设计

尽管第二代水印设计,已经满足了技术层面的场景诉求,但从效率角度,我们认为还有进步的空间,为此,我们直接把生成的bitmap数据投递到jni层,减少了一次拷贝,特别是在频繁水印处理时,提高了数据处理效率。

代码语言: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;
    }

对应封装设计:

代码语言:java
复制
    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();
        }
    }

总结

有人说,音视频行业最苦最没有意思、高投入低回报的就是做SDK。在我们看来,少一次拷贝、一次功能的迭代是进步,但大多数情况下,对于不了解细节的开发者看来,不深耕不细测很难看出端倪。大牛直播SDK的预期就是专注、极致、智慧、比快更快,做音视频行业的基石,帮助更多的行业,更少的精力实现音视频接入能力,任何行业,持续进步,才会有更大的收获。从另一个角度来说,看似每一次的精进,对我们技术从业者来说,都是持续的乐趣。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ​技术背景
    • 第一代:不可动态改变的文字、png水印
      • 第二代:实时动态文字、png水印
        • 第三代:Bitmap接口设计
        • 总结
        相关产品与服务
        实时音视频
        实时音视频(Tencent RTC)基于腾讯21年来在网络与音视频技术上的深度积累,以多人音视频通话和低延时互动直播两大场景化方案,通过腾讯云服务向开发者开放,致力于帮助开发者快速搭建低成本、低延时、高品质的音视频互动解决方案。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档