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流的话,需要注意的是,在一个实例里面完成,确保只编码一路音视频数据,然后分别打包注入两个模块,尽可能的降低设备性能消耗。

        相关文章
        |
        2月前
        |
        Java Android开发 Swift
        安卓与iOS开发对比:平台选择对项目成功的影响
        【10月更文挑战第4天】在移动应用开发的世界中,选择合适的平台是至关重要的。本文将深入探讨安卓和iOS两大主流平台的开发环境、用户基础、市场份额和开发成本等方面的差异,并分析这些差异如何影响项目的最终成果。通过比较这两个平台的优势与挑战,开发者可以更好地决定哪个平台更适合他们的项目需求。
        120 1
        |
        3月前
        |
        IDE Android开发 iOS开发
        探索Android与iOS开发的差异:平台选择对项目成功的影响
        【9月更文挑战第27天】在移动应用开发的世界中,Android和iOS是两个主要的操作系统平台。每个系统都有其独特的开发环境、工具和用户群体。本文将深入探讨这两个平台的关键差异点,并分析这些差异如何影响应用的性能、用户体验和最终的市场表现。通过对比分析,我们将揭示选择正确的开发平台对于确保项目成功的重要作用。
        |
        6天前
        |
        IDE 开发工具 Android开发
        移动应用开发之旅:探索Android和iOS平台
        在这篇文章中,我们将深入探讨移动应用开发的两个主要平台——Android和iOS。我们将了解它们的操作系统、开发环境和工具,并通过代码示例展示如何在这两个平台上创建一个简单的“Hello World”应用。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧,帮助你更好地理解和掌握移动应用开发。
        29 17
        |
        2月前
        |
        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开发知识可参考相关书籍。
        106 0
        FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
        |
        4月前
        |
        传感器 Android开发 芯片
        不写一行代码(三):实现安卓基于i2c bus的Slaver设备驱动
        本文是系列文章的第三篇,展示了如何在Android系统中利用现有的i2c bus驱动,通过编写设备树节点和应用层的控制代码,实现对基于i2c bus的Slaver设备(如六轴陀螺仪模块QMI8658C)的控制,而无需编写设备驱动代码。
        56 0
        不写一行代码(三):实现安卓基于i2c bus的Slaver设备驱动
        |
        4月前
        |
        Android开发
        不写一行代码(二):实现安卓基于PWM的LED设备驱动
        本文介绍了在Android系统中不编写任何代码,通过设备树配置和内核支持的通用PWM LED驱动来实现基于PWM的LED设备驱动,并通过测试命令调整LED亮度级别。
        54 0
        不写一行代码(二):实现安卓基于PWM的LED设备驱动
        |
        4月前
        |
        Linux Android开发 C语言
        不写一行代码(一):实现安卓基于GPIO的LED设备驱动
        本文通过实践操作,展示了在Android系统中不编写任何代码,利用设备树(DTS)配置和内核支持的通用GPIO LED驱动来控制LED设备,并进一步通过C语言编写NDK测试APP来实现LED的闪烁效果。
        177 0
        不写一行代码(一):实现安卓基于GPIO的LED设备驱动
        |
        19天前
        |
        开发框架 前端开发 Android开发
        安卓与iOS开发中的跨平台策略
        在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
        |
        24天前
        |
        搜索推荐 Android开发 开发者
        探索安卓开发中的自定义视图:打造个性化UI组件
        【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。