• 如何让Android平台像IPC一样实现GB28181前端设备接入


    技术背景

    好多开发者在做国标对接的时候,首先想到的是IPC摄像头,通过参数化配置,接入到国标平台,实现媒体数据的按需查看等操作。

    像执法记录仪等智能终端,跑在Android平台,对接GB28181平台的需求也非常大,网上相关demo也不少,但真正设计符合相关协议规范、功能完善、长时间稳定运行的并不多。基于此,我们研发了Android平台GB28181接入模块,目前功能设计,总的来说,IPC有的功能要有,IPC缺失的,但是对业务诉求有要求的,也要有,比如MobilePosition实时位置上报,实时快照、本地录像、屏幕采集、拉取RTSP或RTMP对接到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/UDP设置;
    • 支持注册、注销,支持注册刷新及注册有效期设置;
    • 支持设备目录查询应答;
    • 支持心跳机制,支持心跳间隔、心跳检测次数设置;
    • 支持移动设备位置(MobilePosition)订阅和通知;
    • 支持国标GB/T28181—2016平台接入;
    • 支持语音广播及语音对讲;
    •  [实时水印]支持动态文字水印、png水印;
    •  [镜像]Android平台支持前置摄像头实时镜像功能;
    •  [实时静音]支持实时静音/取消静音;
    •  [实时快照]支持实时快照;
    •  [降噪]支持环境音、手机干扰等引起的噪音降噪处理、自动增益、VAD检测;
    •  [外部编码前视频数据对接]支持YUV数据对接;
    •  [外部编码前音频数据对接]支持PCM对接;
    •  [外部编码后视频数据对接]支持外部H.264数据对接;
    •  [外部编码后音频数据对接]外部AAC数据对接;
    •  [扩展录像功能]支持录像相关功能。

    技术接口设计

    接口设计这块,我们分三个部分:一个是供Publisher实例调用的接口,一个是RTP发送相关的接口,还有一个是针对语音广播这块,实现的接收RTP数据播放接口。

    先说供GB28181推送实例调用的接口:

    1. /*+++++++++++++++GB28181相关接口+++++++++++++++*/
    2. /**
    3. * 设置GB28181 RTP Sender
    4. *
    5. * @param rtp_sender_handle, CreateRTPSender返回值
    6. * @param rtp_payload_type, 对于GB28181 PS, 协议定义是96, 具体以SDP为准
    7. *
    8. * @return {0} if successful
    9. */
    10. public native int SetGB28181RTPSender(long handle, long rtp_sender_handle, int rtp_payload_type);
    11. /**
    12. * 启动 GB28181 媒体流
    13. *
    14. * @return {0} if successful
    15. */
    16. public native int StartGB28181MediaStream(long handle);
    17. /**
    18. * 停止 GB28181 媒体流
    19. *
    20. * @return {0} if successful
    21. */
    22. public native int StopGB28181MediaStream(long handle);
    23. /*---------------GB28181相关接口---------------*/

    再说RTP发送相关的接口:

    1. /*
    2. * SmartPublisherJniV2.java
    3. * SmartPublisherJniV2
    4. *
    5. * WebSite: https://daniusdk.com
    6. * Github: https://github.com/daniulive/SmarterStreaming
    7. */
    8. /*+++++++++++++++RTP Sender相关接口+++++++++++++++*/
    9. /*
    10. * 创建RTP Sender实例
    11. *
    12. * @param reserve:保留参数传0
    13. *
    14. * @return RTP Sender 句柄,0表示失败
    15. */
    16. public native long CreateRTPSender(int reserve);
    17. /**
    18. *设置 RTP Sender传输协议
    19. *
    20. * @param rtp_sender_handle, CreateRTPSender返回值
    21. * @param transport_protocol, 0:UDP, 1:TCP, 默认是UDP
    22. *
    23. * @return {0} if successful
    24. */
    25. public native int SetRTPSenderTransportProtocol(long rtp_sender_handle, int transport_protocol);
    26. /**
    27. *设置 RTP Sender IP地址类型
    28. *
    29. * @param rtp_sender_handle, CreateRTPSender返回值
    30. * @param ip_address_type, 0:IPV4, 1:IPV6, 默认是IPV4, 当前仅支持IPV4
    31. *
    32. * @return {0} if successful
    33. */
    34. public native int SetRTPSenderIPAddressType(long rtp_sender_handle, int ip_address_type);
    35. /**
    36. *设置 RTP Sender RTP Socket本地端口
    37. *
    38. * @param rtp_sender_handle, CreateRTPSender返回值
    39. * @param port, 必须是偶数,设置0的话SDK会自动分配, 默认值是0
    40. *
    41. * @return {0} if successful
    42. */
    43. public native int SetRTPSenderLocalPort(long rtp_sender_handle, int port);
    44. /**
    45. *设置 RTP Sender SSRC
    46. *
    47. * @param rtp_sender_handle, CreateRTPSender返回值
    48. * @param ssrc, 如果设置的话,这个字符串要能转换成uint32类型, 否则设置失败
    49. *
    50. * @return {0} if successful
    51. */
    52. public native int SetRTPSenderSSRC(long rtp_sender_handle, String ssrc);
    53. /**
    54. *设置 RTP Sender RTP socket 发送Buffer大小
    55. *
    56. * @param rtp_sender_handle, CreateRTPSender返回值
    57. * @param buffer_size, 必须大于0, 默认是512*1024, 当前仅对UDP socket有效, 根据视频码率考虑设置合适的值
    58. *
    59. * @return {0} if successful
    60. */
    61. public native int SetRTPSenderSocketSendBuffer(long rtp_sender_handle, int buffer_size);
    62. /**
    63. *设置 RTP Sender RTP时间戳时钟频率
    64. *
    65. * @param rtp_sender_handle, CreateRTPSender返回值
    66. * @param clock_rate, 必须大于0, 对于GB28181 PS规定是90kHz, 也就是90000
    67. *
    68. * @return {0} if successful
    69. */
    70. public native int SetRTPSenderClockRate(long rtp_sender_handle, int clock_rate);
    71. /**
    72. *设置 RTP Sender 目的IP地址, 注意当前用在GB2818推送上,只设置一个地址,将来扩展如果用在其他地方,可能要设置多个目的地址,到时候接口可能会调整
    73. *
    74. * @param rtp_sender_handle, CreateRTPSender返回值
    75. * @param address, IP地址
    76. * @param port, 端口
    77. *
    78. * @return {0} if successful
    79. */
    80. public native int SetRTPSenderDestination(long rtp_sender_handle, String address, int port);
    81. /**
    82. *初始化RTP Sender, 初始化之前先调用上面的接口配置相关参数
    83. *
    84. * @param rtp_sender_handle, CreateRTPSender返回值
    85. *
    86. * @return {0} if successful
    87. */
    88. public native int InitRTPSender(long rtp_sender_handle);
    89. /**
    90. *获取RTP Sender RTP Socket本地端口
    91. *
    92. * @param rtp_sender_handle, CreateRTPSender返回值
    93. *
    94. * @return 失败返回0, 成功的话返回响应的端口, 请在InitRTPSender返回成功之后调用
    95. */
    96. public native int GetRTPSenderLocalPort(long rtp_sender_handle);
    97. /**
    98. * UnInit RTP Sender
    99. *
    100. * @param rtp_sender_handle, CreateRTPSender返回值
    101. *
    102. * @return {0} if successful
    103. */
    104. public native int UnInitRTPSender(long rtp_sender_handle);
    105. /**
    106. * 释放RTP Sender, 释放之后rtp_sender_handle就无效了,请不要再使用
    107. *
    108. * @param rtp_sender_handle, CreateRTPSender返回值
    109. *
    110. * @return {0} if successful
    111. */
    112. public native int DestoryRTPSender(long rtp_sender_handle);

    最后说,RTP接收相关的接口设计:

    1. /*
    2. * SmartPlayerJniV2.java
    3. * SmartPlayerJniV2
    4. *
    5. * WebSite: https://daniusdk.com
    6. * Github: https://github.com/daniulive/SmarterStreaming
    7. */
    8. /*++++++++++++++++++RTP Receiver++++++++++++++++++++++*/
    9. /*
    10. * 创建RTP Receiver
    11. *
    12. * @param reserve:保留参数传0
    13. *
    14. * @return RTP Receiver 句柄,0表示失败
    15. */
    16. public native long CreateRTPReceiver(int reserve);
    17. /**
    18. *设置 RTP Receiver传输协议
    19. *
    20. * @param rtp_receiver_handle, CreateRTPReceiver
    21. * @param transport_protocol, 0:UDP, 1:TCP, 默认是UDP
    22. *
    23. * @return {0} if successful
    24. */
    25. public native int SetRTPReceiverTransportProtocol(long rtp_receiver_handle, int transport_protocol);
    26. /**
    27. *设置 RTP Receiver IP地址类型
    28. *
    29. * @param rtp_receiver_handle, CreateRTPReceiver
    30. * @param ip_address_type, 0:IPV4, 1:IPV6, 默认是IPV4
    31. *
    32. * @return {0} if successful
    33. */
    34. public native int SetRTPReceiverIPAddressType(long rtp_receiver_handle, int ip_address_type);
    35. /**
    36. *设置 RTP Receiver RTP Socket本地端口
    37. *
    38. * @param rtp_receiver_handle, CreateRTPReceiver
    39. * @param port, 必须是偶数,设置0的话SDK会自动分配, 默认值是0
    40. *
    41. * @return {0} if successful
    42. */
    43. public native int SetRTPReceiverLocalPort(long rtp_receiver_handle, int port);
    44. /**
    45. *设置 RTP Receiver SSRC
    46. *
    47. * @param rtp_receiver_handle, CreateRTPReceiver
    48. * @param ssrc, 如果设置的话,这个字符串要能转换成uint32类型, 否则设置失败
    49. *
    50. * @return {0} if successful
    51. */
    52. public native int SetRTPReceiverSSRC(long rtp_receiver_handle, String ssrc);
    53. /**
    54. *创建 RTP Receiver 会话
    55. *
    56. * @param rtp_receiver_handle, CreateRTPReceiver
    57. * @param reserve, 保留值,目前传0
    58. *
    59. * @return {0} if successful
    60. */
    61. public native int CreateRTPReceiverSession(long rtp_receiver_handle, int reserve);
    62. /**
    63. *获取 RTP Receiver RTP Socket本地端口
    64. *
    65. * @param rtp_receiver_handle, CreateRTPReceiver
    66. *
    67. * @return 失败返回0, 成功的话返回响应的端口, 请在CreateRTPReceiverSession返回成功之后调用
    68. */
    69. public native int GetRTPReceiverLocalPort(long rtp_receiver_handle);
    70. /**
    71. *设置 RTP Receiver Payload 相关信息
    72. *
    73. * @param rtp_receiver_handle, CreateRTPReceiver
    74. *
    75. * @param payload_type, 请参考 RFC 3551
    76. *
    77. * @param encoding_name, 编码名, 请参考 RFC 3551, 如果payload_type不是动态的, 可能传null就好
    78. *
    79. * @param media_type, 媒体类型, 请参考 RFC 3551, 1 是视频, 2是音频
    80. *
    81. * @param clock_rate, 请参考 RFC 3551
    82. *
    83. * @return {0} if successful
    84. */
    85. public native int SetRTPReceiverPayloadType(long rtp_receiver_handle, int payload_type, String encoding_name, int media_type, int clock_rate);
    86. /**
    87. *设置 RTP Receiver 音频采样率
    88. *
    89. * @param rtp_receiver_handle, CreateRTPReceiver
    90. * @param sampling_rate, 音频采样率
    91. *
    92. * @return {0} if successful
    93. */
    94. public native int SetRTPReceiverAudioSamplingRate(long rtp_receiver_handle, int sampling_rate);
    95. /**
    96. *设置 RTP Receiver 音频通道数
    97. *
    98. * @param rtp_receiver_handle, CreateRTPReceiver
    99. * @param channels, 音频通道数
    100. *
    101. * @return {0} if successful
    102. */
    103. public native int SetRTPReceiverAudioChannels(long rtp_receiver_handle, int channels);
    104. /**
    105. *设置 RTP Receiver 远端地址
    106. *
    107. * @param rtp_receiver_handle, CreateRTPReceiver
    108. * @param address, IP地址
    109. * @param port, 端口
    110. *
    111. * @return {0} if successful
    112. */
    113. public native int SetRTPReceiverRemoteAddress(long rtp_receiver_handle, String address, int port);
    114. /**
    115. *初始化 RTP Receiver
    116. *
    117. * @param rtp_receiver_handle, CreateRTPReceiver
    118. *
    119. * @return {0} if successful
    120. */
    121. public native int InitRTPReceiver(long rtp_receiver_handle);
    122. /**
    123. *UnInit RTP Receiver
    124. *
    125. * @param rtp_receiver_handle, CreateRTPReceiver
    126. *
    127. * @return {0} if successful
    128. */
    129. public native int UnInitRTPReceiver(long rtp_receiver_handle);
    130. /**
    131. *Destory RTP Receiver Session
    132. *
    133. * @param rtp_receiver_handle, CreateRTPReceiver
    134. *
    135. * @return {0} if successful
    136. */
    137. public native int DestoryRTPReceiverSession(long rtp_receiver_handle);
    138. /**
    139. *Destory RTP Receiver
    140. *
    141. * @param rtp_receiver_handle, CreateRTPReceiver
    142. *
    143. * @return {0} if successful
    144. */
    145. public native int DestoryRTPReceiver(long rtp_receiver_handle);
    146. /*++++++++++++++++++RTP Receiver++++++++++++++++++++++*/

    上述接口说过之后,大家可能疑惑,信令交互呢?信令交互,我们是在Android上层实现。

    如基础参数配置:

    1. /*** GB28181 相关参数,可以修改相关参数后测试 ***/
    2. GBSIPAgent gb28181_agent_ = null;
    3. private int gb28181_sip_local_port_base_ = 5060;
    4. private String gb28181_sip_server_id_ = "34020000002000000001";
    5. private String gb28181_sip_domain_ = "3402000000";
    6. private String gb28181_sip_server_addr_ = "192.168.0.103";
    7. private int gb28181_sip_server_port_ = 15060;
    8. private String gb28181_sip_user_agent_filed_ = "NT GB User Agent V1.3";
    9. private String gb28181_sip_username_ = "31011500991320000069";
    10. private String gb28181_sip_password_ = "12345678";
    11. private long gb28181_rtp_sender_handle_ = 0;
    12. private int gb28181_rtp_payload_type_ = 96;
    13. private long player_handle_ = 0;
    14. private long rtp_receiver_handle_ = 0;
    15. private AtomicLong last_received_audio_data_time_ = new AtomicLong(0);
    16. /*** GB28181 相关参数,可以修改相关参数后测试 ***/

    InitGB28181Agent()相关:

    1. private boolean initGB28181Agent() {
    2. if ( gb28181_agent_ != null )
    3. return true;
    4. getLocation(context_);
    5. String local_ip_addr = IPAddrUtils.getIpAddress(context_);
    6. Log.i(TAG, "initGB28181Agent local ip addr: " + local_ip_addr);
    7. if ( local_ip_addr == null || local_ip_addr.isEmpty() ) {
    8. Log.e(TAG, "initGB28181Agent local ip is empty");
    9. return false;
    10. }
    11. gb28181_agent_ = GBSIPAgentFactory.getInstance().create();
    12. if ( gb28181_agent_ == null ) {
    13. Log.e(TAG, "initGB28181Agent create agent failed");
    14. return false;
    15. }
    16. gb28181_agent_.addListener(this);
    17. // 必填信息
    18. gb28181_agent_.setLocalAddress(local_ip_addr);
    19. gb28181_agent_.setServerParameter(gb28181_sip_server_addr_, gb28181_sip_server_port_, gb28181_sip_server_id_, gb28181_sip_domain_);
    20. gb28181_agent_.setUserInfo(gb28181_sip_username_, gb28181_sip_password_);
    21. // 可选参数
    22. gb28181_agent_.setUserAgent(gb28181_sip_user_agent_filed_);
    23. gb28181_agent_.setTransportProtocol(gb28181_sip_trans_protocol_==0?"UDP":"TCP");
    24. // GB28181配置
    25. gb28181_agent_.config(gb28181_reg_expired_, gb28181_heartbeat_interval_, gb28181_heartbeat_count_);
    26. //com.gb.ntsignalling.Device gb_device = new com.gb.ntsignalling.Device("34020000001380000001", "安卓测试设备", Build.MANUFACTURER, Build.MODEL,
    27. // "宇宙","火星1","火星", true);
    28. com.gb.ntsignalling.Device gb_device = new com.gb.ntsignalling.Device("33010752991327811433", "安卓测试设备", Build.MANUFACTURER, Build.MODEL,
    29. "宇宙","火星1","火星", true);
    30. if (mLongitude != null && mLatitude != null) {
    31. com.gb.ntsignalling.DevicePosition device_pos = new com.gb.ntsignalling.DevicePosition();
    32. device_pos.setTime(mLocationTime);
    33. device_pos.setLongitude(mLongitude);
    34. device_pos.setLatitude(mLatitude);
    35. gb_device.setPosition(device_pos);
    36. gb_device.setSupportMobilePosition(true); // 设置支持移动位置上报
    37. }
    38. gb28181_agent_.addDevice(gb_device);
    39. /*
    40. com.gb28181.ntsignalling.Device gb_device1 = new com.gb28181.ntsignalling.Device("34020000001380000002", "安卓测试设备2", Build.MANUFACTURER, Build.MODEL,
    41. "宇宙","火星1","火星", true);
    42. if (mLongitude != null && mLatitude != null) {
    43. com.gb28181.ntsignalling.DevicePosition device_pos = new com.gb28181.ntsignalling.DevicePosition();
    44. device_pos.setTime(mLocationTime);
    45. device_pos.setLongitude(mLongitude);
    46. device_pos.setLatitude(mLatitude);
    47. gb_device1.setPosition(device_pos);
    48. gb_device1.setSupportMobilePosition(true);
    49. }
    50. gb28181_agent_.addDevice(gb_device1);
    51. */
    52. if (!gb28181_agent_.createSipStack()) {
    53. gb28181_agent_ = null;
    54. Log.e(TAG, "initGB28181Agent gb28181_agent_.createSipStack failed.");
    55. return false;
    56. }
    57. boolean is_bind_local_port_ok = false;
    58. // 最多尝试5000个端口
    59. int try_end_port = gb28181_sip_local_port_base_ + 5000;
    60. try_end_port = try_end_port > 65536 ?65536: try_end_port;
    61. for (int i = gb28181_sip_local_port_base_; i < try_end_port; ++i) {
    62. if (gb28181_agent_.bindLocalPort(i)) {
    63. is_bind_local_port_ok = true;
    64. break;
    65. }
    66. }
    67. if (!is_bind_local_port_ok) {
    68. gb28181_agent_.releaseSipStack();
    69. gb28181_agent_ = null;
    70. Log.e(TAG, "initGB28181Agent gb28181_agent_.bindLocalPort failed.");
    71. return false;
    72. }
    73. if (!gb28181_agent_.initialize()) {
    74. gb28181_agent_.unBindLocalPort();
    75. gb28181_agent_.releaseSipStack();
    76. gb28181_agent_ = null;
    77. Log.e(TAG, "initGB28181Agent gb28181_agent_.initialize failed.");
    78. return false;
    79. }
    80. return true;
    81. }

    信令交互处理:

    1. @Override
    2. public void ntsRegisterOK(String dateString) {
    3. Log.i(TAG, "ntsRegisterOK Date: " + (dateString!= null? dateString : ""));
    4. }
    5. @Override
    6. public void ntsRegisterTimeout() {
    7. Log.e(TAG, "ntsRegisterTimeout");
    8. }
    9. @Override
    10. public void ntsRegisterTransportError(String errorInfo) {
    11. Log.e(TAG, "ntsRegisterTransportError error:" + (errorInfo != null?errorInfo :""));
    12. }
    13. @Override
    14. public void ntsOnHeartBeatException(int exceptionCount, String lastExceptionInfo) {
    15. }
    16. @Override
    17. public void ntsOnInvitePlay(String deviceId, PlaySessionDescription session_des) {
    18. }
    19. @Override
    20. public void ntsOnCancelPlay(String deviceId) {
    21. }
    22. @Override
    23. public void ntsOnAckPlay(String deviceId) {
    24. }
    25. @Override
    26. public void ntsOnPlayInviteResponseException(String deviceId, int statusCode, String errorInfo) {
    27. }
    28. @Override
    29. public void ntsOnByePlay(String deviceId) {
    30. }
    31. @Override
    32. public void ntsOnPlayDialogTerminated(String deviceId) {
    33. }
    34. @Override
    35. public void ntsOnDevicePositionRequest(String deviceId, int interval) {
    36. }
    37. @Override
    38. public void ntsOnNotifyBroadcastCommand(String fromUserName, String fromUserNameAtDomain, String sn, String sourceID, String targetID) {
    39. }
    40. @Override
    41. public void ntsOnAudioBroadcast(String commandFromUserName, String commandFromUserNameAtDomain, String sourceID, String targetID) {
    42. }
    43. @Override
    44. public void ntsOnInviteAudioBroadcastException(String sourceID, String targetID, String errorInfo) {
    45. }
    46. @Override
    47. public void ntsOnInviteAudioBroadcastTimeout(String sourceID, String targetID) {
    48. }

    除了基础的Camera接口外,5.0开始新的Camera2数据依旧可以正常对接,感兴趣的开发者可酌情参考。

  • 相关阅读:
    猿创征文|【C++游戏引擎Easy2D】炫酷动画来这学,位移动画构造函数让节点执行动画
    SpringBoot启动顺序
    java参数传递
    UI设计 ,我只推荐这6个网站,真的太好用了。
    面渣逆袭:二十二图、八千字、二十问,彻底搞定MyBatis
    面经-虚拟机-对象引用类型
    改进的多目标差分进化算法在电力系统环境经济调度中的应用(Python代码实现)【电气期刊论文复现】
    树莓派Raspberry Pico RP2040 开发环境配置完全缝合终极版C-SDK
    mysql学习笔记-底层原理详解
    第十三章 数据库
  • 原文地址:https://blog.csdn.net/renhui1112/article/details/126844019