Android平台GB28181设备接入侧如何同时对外输出RTSP流?

简介: Android平台GB28181设备接入侧如何同时对外输出RTSP流?

 技术背景

GB28181的应用场景非常广泛,如公共安全、交通管理、企业安全、教育、医疗等众多领域,细分场景可用于如执法记录仪、智能安全帽、智能监控、智慧零售、智慧教育、远程办公、明厨亮灶、智慧交通、智慧工地、雪亮工程、平安乡村、生产运输、车载终端等:

    1. 公共安全:通过GB28181协议,用户可以实时监控特定区域的视频画面,从而提高公共安全水平。
    2. 交通管理:GB28181可用于交通监控系统,帮助交通部门实时监控道路交通情况,提高交通管理效率。
    3. 企业安全:GB28181可以用于构建企业视频监控系统,保护企业资产,提高安全工作效率。
    4. 教育:通过GB28181协议,用户可以进行远程视频会议和教学,为学生提供更为灵活的学习方式。
    5. 医疗:GB28181可以用于医疗领域的视频监控,提高医疗安全和管理效率。

    技术实现

    本文以Android平台GB28181设备接入模块为例,谈谈具体实现,还有如何对外输出RTSP流。

    Android终端除支持常规的音视频数据接入外,还可以支持移动设备位置(MobilePosition)订阅和通知、语音广播和语音对讲、云台控制回调和预置位查询,支持对接数据类型如下:

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

      技术设计架构图:

      视沃科技(大牛直播SDK)GB28181设备接入SDK.png

      功能设计:

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

        Android平台GB28181设备接入模块,除了上述的功能点外,我们遇到的诉求有,如何同时对外输出RTSP,供如内网平台预览播放?

        这里就提到了轻量级RTSP服务,音视频数据源过来后,编码分别注入GB28181模块和轻量级RTSP服务模块,如果需要做到对外输出RTSP流,只需要启动RTSP服务,然后发布RTSP流即可,具体的操作如下:

        image.gif编辑

        启动、停止RTSP服务:

        //启动/停止RTSP服务classButtonRtspServiceListenerimplementsView.OnClickListener {
        publicvoidonClick(Viewv) {
        if (isRTSPServiceRunning) {
        stopRtspService();
        btnRtspService.setText("启动RTSP服务");
        btnRtspPublisher.setEnabled(false);
        isRTSPServiceRunning=false;
        return;
            }
        Log.i(TAG, "onClick start rtsp service..");
        rtsp_handle_=libPublisher.OpenRtspServer(0);
        if (rtsp_handle_==0) {
        Log.e(TAG, "创建rtsp server实例失败! 请联系 https://daniusdk.com 检查SDK有效性");
            } else {
        intport=8554;
        if (libPublisher.SetRtspServerPort(rtsp_handle_, port) !=0) {
        libPublisher.CloseRtspServer(rtsp_handle_);
        rtsp_handle_=0;
        Log.e(TAG, "创建rtsp server端口失败! 请检查端口是否重复或者端口不在范围内!");
              }
        if (libPublisher.StartRtspServer(rtsp_handle_, 0) ==0) {
        Log.i(TAG, "启动rtsp server 成功!");
              } else {
        libPublisher.CloseRtspServer(rtsp_handle_);
        rtsp_handle_=0;
        Log.e(TAG, "启动rtsp server失败! 请检查设置的端口是否被占用!");
              }
        btnRtspService.setText("停止RTSP服务");
        btnRtspPublisher.setEnabled(true);
        isRTSPServiceRunning=true;
            }
          }
        }

        image.gif

        发布、停止RTSP流:

        //发布/停止RTSP流classButtonRtspPublisherListenerimplementsView.OnClickListener {
        publicvoidonClick(Viewv) {
        if (isRTSPPublisherRunning) {
        stopRtspPublisher();
        btnRtspPublisher.setText("发布RTSP流");
        btnGetRtspSessionNumbers.setEnabled(false);
        btnRtspService.setEnabled(true);
        return;
            }
        Log.i(TAG, "onClick start rtsp publisher..");
        if (!isPushingRtmp&&!isGB28181StreamRunning&&!isRecording) {
        InitAndSetConfig();
            }
        if (publisherHandle==0) {
        Log.e(TAG, "Start rtsp publisher, publisherHandle is null..");
        return;
            }
        Stringrtsp_stream_name="stream1";
        libPublisher.SetRtspStreamName(publisherHandle, rtsp_stream_name);
        libPublisher.ClearRtspStreamServer(publisherHandle);
        libPublisher.AddRtspStreamServer(publisherHandle, rtsp_handle_, 0);
        if (libPublisher.StartRtspStream(publisherHandle, 0) !=0) {
        Log.e(TAG, "调用发布rtsp流接口失败!");
        return;
            }
        if (!isPushingRtmp&&!isGB28181StreamRunning&&!isRecording) {
        CheckInitAudioRecorder();    //enable pure video publisher..    }
        startLayerPostThread();
        btnRtspPublisher.setText("停止RTSP流");
        btnGetRtspSessionNumbers.setEnabled(true);
        btnRtspService.setEnabled(false);
        isRTSPPublisherRunning=true;
          }
        }

        image.gif

        获取RTSP链接数:

        //获取RTSP会话数classButtonGetRtspSessionNumbersListenerimplementsView.OnClickListener {
        publicvoidonClick(Viewv) {
        if (libPublisher!=null&&rtsp_handle_!=0) {
        intsession_numbers=libPublisher.GetRtspServerClientSessionNumbers(rtsp_handle_);
        Log.i(TAG, "GetRtspSessionNumbers: "+session_numbers);
        PopRtspSessionNumberDialog(session_numbers);
            }
          }
        }

        image.gif

        获取回调上来的RTSP URL,对应的事件ID为EVENT_DANIULIVE_ERC_PUBLISHER_RTSP_URL

        privatestaticclassEventHandlerPublisherV2implementsNTSmartEventCallbackV2 {
        @OverridepublicvoidonNTSmartEventCallbackV2(longhandle, intid, longparam1, longparam2, Stringparam3, Stringparam4, Objectparam5) {
        Log.i(TAG, "EventHandeV2: handle="+handle+" id:"+id);
        Stringpublisher_event="";
        switch (id) {
             .....
        caseNTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_RECORDER_START_NEW_FILE:
        publisher_event="开始一个新的录像文件 : "+param3;
        break;
        caseNTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_ONE_RECORDER_FILE_FINISHED:
        if (recorder_io_executor_!=null) {
        ExecutorServiceexecutor=recorder_io_executor_.get();
        if (executor!=null)
        executor.execute(newRecordFileFinishedHandler().set(handle, param3, param1));
                }
        publisher_event="已生成一个录像文件 : "+param3;
        break;
        caseNTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_SEND_DELAY:
        publisher_event="发送时延: "+param1+" 帧数:"+param2;
        break;
        caseNTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CAPTURE_IMAGE:
        publisher_event="快照: "+param1+" 路径:"+param3;
        if (param1==0) {
        publisher_event=publisher_event+"截取快照成功..";
                } else {
        publisher_event=publisher_event+"截取快照失败..";
                }
        break;
        caseNTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_RTSP_URL:
        publisher_event="RTSP服务URL: "+param3;
        break;
            }
        Stringstr="当前回调状态:"+publisher_event;
        Log.i(TAG, str);
        if (handler_!=null) {
        android.os.Handlerhandler=handler_.get();
        if (handler!=null) {
        Messagemessage=newMessage();
        message.what=PUBLISHER_EVENT_MSG;
        message.obj=publisher_event;
        handler.sendMessage(message);
              }
            }
          }
        publicNTSmartEventCallbackV2set(android.os.Handlerhandler, ExecutorServicerecorder_io_executor) {
        this.handler_=newWeakReference<>(handler);
        this.recorder_io_executor_=newWeakReference<>(recorder_io_executor);
        returnthis;
          }
        privateWeakReference<android.os.Handler>handler_;
        privateWeakReference<ExecutorService>recorder_io_executor_;
        }

        image.gif

        总结

        GB28181设备接入模块同时输出RTSP流的话,需要注意的是,在一个实例里面完成,确保只编码一路音视频数据,然后分别打包注入两个模块,尽可能的降低设备性能消耗。

        相关文章
        |
        13天前
        |
        Java Android开发 Swift
        安卓与iOS开发对比:平台选择对项目成功的影响
        【10月更文挑战第4天】在移动应用开发的世界中,选择合适的平台是至关重要的。本文将深入探讨安卓和iOS两大主流平台的开发环境、用户基础、市场份额和开发成本等方面的差异,并分析这些差异如何影响项目的最终成果。通过比较这两个平台的优势与挑战,开发者可以更好地决定哪个平台更适合他们的项目需求。
        54 1
        |
        29天前
        |
        IDE Android开发 iOS开发
        探索Android与iOS开发的差异:平台选择对项目成功的影响
        【9月更文挑战第27天】在移动应用开发的世界中,Android和iOS是两个主要的操作系统平台。每个系统都有其独特的开发环境、工具和用户群体。本文将深入探讨这两个平台的关键差异点,并分析这些差异如何影响应用的性能、用户体验和最终的市场表现。通过对比分析,我们将揭示选择正确的开发平台对于确保项目成功的重要作用。
        |
        4天前
        |
        Linux API 开发工具
        FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
        ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
        23 0
        FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
        |
        1月前
        |
        开发工具 Android开发 iOS开发
        安卓与iOS开发环境对比:选择适合你的平台
        【9月更文挑战第26天】在移动应用开发的广阔天地中,安卓和iOS是两大巨头。它们各自拥有独特的优势和挑战,影响着开发者的选择和决策。本文将深入探讨这两个平台的开发环境,帮助你理解它们的核心差异,并指导你根据个人或项目需求做出明智的选择。无论你是初学者还是资深开发者,了解这些平台的异同都至关重要。让我们一起探索,找到最适合你的那片开发天地。
        |
        19天前
        |
        XML 存储 Java
        探索安卓开发之旅:从基础到进阶
        【9月更文挑战第37天】安卓开发,一个充满无限可能的领域。它不仅关乎技术的深度与广度,更关乎开发者的成长与突破。本文将带你走进安卓开发的世界,从基础知识的学习到进阶技巧的掌握,一起感受编程的魅力与乐趣。
        |
        Android开发 数据安全/隐私保护 开发工具
        |
        13天前
        |
        缓存 搜索推荐 Android开发
        安卓开发中的自定义控件实践
        【10月更文挑战第4天】在安卓开发的海洋中,自定义控件是那片璀璨的星辰。它不仅让应用界面设计变得丰富多彩,还提升了用户体验。本文将带你探索自定义控件的核心概念、实现过程以及优化技巧,让你的应用在众多竞争者中脱颖而出。
        |
        16天前
        |
        Android开发
        Android开发表情emoji功能开发
        本文介绍了一种在Android应用中实现emoji表情功能的方法,通过将图片与表情字符对应,实现在`TextView`中的正常显示。示例代码展示了如何使用自定义适配器加载emoji表情,并在编辑框中输入或删除表情。项目包含完整的源码结构,可作为开发参考。视频演示和源码详情见文章内链接。
        41 4
        Android开发表情emoji功能开发
        |
        14天前
        |
        Web App开发 安全 程序员
        FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
        多年的互联网寒冬在今年尤为凛冽,坚守安卓开发愈发不易。面对是否转行或学习新技术的迷茫,安卓程序员可从三个方向进阶:1)钻研谷歌新技术,如Kotlin、Flutter、Jetpack等;2)拓展新功能应用,掌握Socket、OpenGL、WebRTC等专业领域技能;3)结合其他行业,如汽车、游戏、安全等,拓宽职业道路。这三个方向各有学习难度和保饭碗指数,助你在安卓开发领域持续成长。
        42 1
        FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
        |
        12天前
        |
        缓存 搜索推荐 Android开发
        安卓开发中的自定义控件基础与进阶
        【10月更文挑战第5天】在Android应用开发中,自定义控件是提升用户体验和界面个性化的重要手段。本文将通过浅显易懂的语言和实例,引导你了解自定义控件的基本概念、创建流程以及高级应用技巧,帮助你在开发过程中更好地掌握自定义控件的使用和优化。
        25 10