如何在Android平台GB28181接入终端实现语音广播和语音对讲
技術背景
在之前的blog,我們以Android平臺國標接入終端為例,分別介紹了一些常規的功能,比如REGISTER、CATALOG、INVITE、Keepalive、SUBSCRIBE、NOTIFY等常規操作,今天主要介紹下語音廣播和語音對講這部分。
GB28181平臺廣播和對講這塊,重要性不言而喻,沒有廣播的接入終端,數據只是單向流入,加入后,指揮中心和終端之間的聯系更緊密,實時雙向溝通更方便,適用的行業范圍也更廣泛。
相關SPEC解讀
關于語音廣播和對講,感興趣的開發者可直接參閱GBT 28181-2016.pdf相關技術規范里面的9.12章節,以下是部分精選介紹:
命令交互流程
命令描述流程
a) 1:SIP服務器向語音流接收者發送語音廣播通知消息,消息中通過 To頭域標明作為目的地址 的語音流接收者ID,消息采用 Message方法攜帶。
舉例說明:
MESSAGE sip:34020000001380000001@192.168.2.212:12070 SIP/2.0 Via: SIP/2.0/UDP 192.168.2.154:15060;rport;branch=z9hG4bK311226558 From: <sip:34020000002000000001@3402000000>;tag=280226558 To: <sip:34020000001380000001@192.168.2.212:12070> Call-ID: 172226558 CSeq: 207 MESSAGE Content-Type: Application/MANSCDP+xml Max-Forwards: 70 Content-Length: 206<?xml version="1.0" encoding="GB2312"?> <Notify><CmdType>Broadcast</CmdType><SN>461226558</SN><SourceID>34020000002000000001</SourceID><TargetID>34020000001380000001</TargetID> </Notify>b) 2:語音流接收者收到語音廣播通知消息后,向SIP服務器發送200OK 響應。
SIP/2.0 200 OK CSeq: 207 MESSAGE Call-ID: 172226558 From: <sip:34020000002000000001@3402000000>;tag=280226558 To: <sip:34020000001380000001@192.168.2.212:12070> Via: SIP/2.0/UDP 192.168.2.154:15060;rport=15060;branch=z9hG4bK311226558;received=192.168.2.154 Content-Length: 0c) 3:語音流接收者向SIP服務器發送語音廣播應答消息,消息中通過 To頭域標明作為目的地 址的SIP服務器ID,消息采用 Message方法攜帶。
MESSAGE sip:34020000002000000001@3402000000 SIP/2.0 Call-ID: 0fc1f2c83c28898a29e146d7ef581908@192.168.2.212 CSeq: 337044229 MESSAGE From: <sip:34020000001380000001@3402000000>;tag=93882333 To: <sip:34020000002000000001@3402000000> Via: SIP/2.0/UDP 192.168.2.212:12070;rport;branch=z9hG4bK-363733-79fd88c45667975e5ebaf18f84b91a8e Max-Forwards: 70 User-Agent: NT GB28181 User Agent V1.2(daniusdk.com) Content-Type: Application/MANSCDP+xml Content-Length: 180<?xml version="1.0" encoding="GB2312"?> <Response> <CmdType>Broadcast</CmdType> <SN>461226558</SN> <DeviceID>34020000001380000001</DeviceID> <Result>OK</Result> </Response>d) 4:SIP服務器收到語音廣播應答消息后,向語音流接收者發送200OK 響應。
SIP/2.0 200 OK Via: SIP/2.0/UDP 192.168.2.212:12070;rport=12070;received=192.168.2.212;branch=z9hG4bK-363733-79fd88c45667975e5ebaf18f84b91a8e From: <sip:34020000001380000001@3402000000>;tag=93882333 To: <sip:34020000002000000001@3402000000>;tag=355226593 CSeq: 337044229 MESSAGE Call-ID: 0fc1f2c83c28898a29e146d7ef581908@192.168.2.212 Content-Length: 0e) 5:語音流接收者向SIP服務器發送Invite消息,消息中通過 To頭域標明作為目的地址的語音 流發送者ID,消息頭域中攜帶Subject字段,表明請求的語音流發送者ID、發送方媒體流序列 號、語音流接收者ID、接收方媒體流序列號等參數,SDP消息體中s字段為“Play”代表實時點 播,m 字段中媒體參數標識為“audio”表示請求語音媒體流。
INVITE sip:34020000002000000001@3402000000 SIP/2.0 Call-ID: 2b4f0f0512aa1a49ffc645618d0e8bae@192.168.2.212 CSeq: 44264 INVITE From: <sip:34020000001380000001@3402000000>;tag=32ecf22a To: <sip:34020000002000000001@3402000000> Via: SIP/2.0/UDP 192.168.2.212:12070;rport;branch=z9hG4bK-363733-15283c8a0ea0a1e9dbf295ce2359dbe7 Max-Forwards: 70 Contact: <sip:34020000001380000001@192.168.2.212:12070> Subject: 34020000002000000001:0200006727,34020000001380000001:0 User-Agent: NT GB28181 User Agent V1.2(daniusdk.com) Content-Type: APPLICATION/SDP Content-Length: 221v=0 o=34020000002000000001 0 0 IN IP4 192.168.2.212 s=Play c=IN IP4 192.168.2.212 t=0 0 m=audio 25002 TCP/RTP/AVP 8 a=setup:active a=connection:new a=recvonly a=rtpmap:8 PCMA/8000 y=0200006727 f=v/a/1/8/1f) 6:SIP服務器收到Invite請求后,通過三方呼叫控制建立媒體服務器和語音流發送者之間的媒體連接。向媒體服務器發送Invite消息,此消息不攜帶SDP消息體。
g) 7:媒體服務器收到SIP服務器的Invite請求后,回復200OK 響應,攜帶SDP消息體,消息體 中描述了媒體服務器接收媒體流的IP、端口、媒體格式等內容。
h) 8:SIP服務器收到媒體服務器返回的200OK 響應后,向語音流發送者發送Invite請求,消息 中通過 To頭域標明作為目的地址的語音流發送者ID,消息頭域中攜帶 Subject字段,表明請 求的語音流發送者ID、發送方媒體流序列號、語音流接收者ID、接收方媒體流序列號等參數, 請求中攜帶消息7中媒體服務器回復的200OK 響應消息體,s字段為“Play”代表實時點播, m 字段中媒體參數標識為“audio”表示請求語音媒體流,增加y字段描述SSRC值,f字段描述 媒體參數。
SIP/2.0 200 OK Via: SIP/2.0/UDP 192.168.2.212:12070;rport=12070;received=192.168.2.212;branch=z9hG4bK-363733-15283c8a0ea0a1e9dbf295ce2359dbe7 From: <sip:34020000001380000001@3402000000>;tag=32ecf22a To: <sip:34020000002000000001@3402000000>;tag=954226632 CSeq: 44264 INVITE Call-ID: 2b4f0f0512aa1a49ffc645618d0e8bae@192.168.2.212 Contact: <sip:34020000002000000001@192.168.2.154:15060> Content-Length: 222 Content-Type: APPLICATION/SDPv=0 o=34020000002000000001 0 0 IN IP4 192.168.2.154 s=Play c=IN IP4 192.168.2.154 t=0 0 m=audio 30005 TCP/RTP/AVP 8 a=sendonly a=rtpmap:8 PCMA/8000 a=setup:passive a=connection:new y=0200006727 f=v/a/1/8/1i) 9:語音流發送者收到SIP服務器的Invite請求后,回復200OK 響應,攜帶SDP消息體,消息 體中描述了媒體流發送者發送媒體流的IP、端口、媒體格式、SSRC 字段等內容,s字段為 “Play”代表實時點播,m 字段中媒體參數標識為“audio”表示請求語音媒體流。
j) 10:SIP服務器收到語音流發送者返回的200OK 響應后,向媒體服務器發送 ACK 請求,請求 中攜帶消息9中語音流發送者回復的200OK 響應消息體,完成與媒體服務器的Invite會話 建立過程。
k) 11:SIP服務器收到語音流發送者返回的200OK 響應后,向語音流發送者發送 ACK 請求,請 求中不攜帶消息體,完成與語音流發送者的Invite會話建立過程。
l) 12:完成三方呼叫控制后,SIP服務器通過 B2BUA 代理方式建立語音流接收者和媒體服務器 之間的媒體連接。在消息5中增加SSRC值,轉發給媒體服務器。
m)13:媒體服務器收到Invite請求,回復200OK 響應,攜帶SDP消息體,消息體中描述了媒體服 務器發送媒體流的IP、端口、媒體格式、SSRC值等內容,s字段為“Play”代表實時點播,m 字段 中媒體參數標識為“audio”表示請求語音媒體流。
n) 14:SIP服務器將消息13轉發給語音流接收者。
o) 15:語音流接收者收到200OK 響應后,回復 ACK 消息,完成與SIP服務器的Invite會話建立 過程。
p) 16:SIP服務器將消息15轉發給媒體服務器,完成與媒體服務器的Invite會話建立過程。
q) 17:SIP服務器向語音流接收者發送 BYE消息,斷開消息5、14、15建立的Invite會話。
r) 18:語音流接收者收到 BYE消息后回復200OK 響應,會話斷開。
s) 19:SIP服務器向媒體服務器發送 BYE 消息,斷開消息 12、13、16 建立的同媒體服務器的 Invite會話。
t) 20:媒體服務器收到 BYE消息后回復200OK 響應,會話斷開。
u) 21:SIP服務器向媒體服務器發送 BYE消息,斷開消息6、7、10建立的同媒體服務器的Invite 會話。
v) 22:媒體服務器收到 BYE消息后回復200OK 響應,會話斷開。
w)23:SIP服務器向語音流發送者發送 BYE 消息,斷開消息8、9、11建立的同語音流發送者的 Invite會話。
x) 24:語音流發送者收到 BYE消息后回復200OK 響應,會話斷開。
注:語音廣播通知消息除上述流程中通過SIP服務器發出外,也可由語音流發送者發出,消息中通過 To頭域標明 作為目的地址的語音流接收者ID,經SIP服務器中轉后發往語音流接收者;語音流接收者處理后發送應答消 息,消息中通過 To頭域標明作為目的地址的語音流發送者ID,經SIP服務器中轉后回復給語音流發送者。后續呼叫流程與上述流程相同。
語音對講
語音對講功能實現中心用戶與前端用戶之間的一對一語音對講功能。 語音對講功能由下述兩個獨立的流程組合實現:
a) 通過9.2的實時視音頻點播功能,中心用戶獲得前端設備的實時視音頻媒體流;
b) 通過9.12的語音廣播功能,中心用戶向前端對講設備發送實時音頻媒體流,語音流的封裝格 式見 C.2.4音頻流的 RTP封裝定義。
C.2.4 音頻流的 RTP封裝
語音比特流宜采用標準的 RTP協議進行打包,這里只摘錄G.711A律的:
在一個 RTP包中,音頻載荷數據應為整數個音頻編碼幀,且時間長度在20ms~180ms之間。
音頻載荷數據的 RTP封裝參數如下:
a) G.711的主要參數 G.711A律語音編碼 RTP包的負載類型(PayloadType)的參數規定如下(見IETFRFC3551— 2003中的表4): 1)負載類型(PT):8; 2) 編碼名稱(encodingname):PCMA; 3) 時鐘頻率(clockrate):8kHz; 4) 通道數:1; 5) SDP描述中“m”字段的“media”項:audio。
技術實現
語音廣播接收這塊,由于有之前的RTMP和RTSP播放器積累,直接在player端做相應擴展即可,當收到廣播后,GB28181語音廣播按鈕使能。
相關接口設計如下:
/*++++++++++++++++++RTP Receiver++++++++++++++++++++++*///GitHub: https://github.com/daniulive/SmarterStreaming//WebSite: https://daniusdk.com/** 創建RTP Receiver** @param reserve:保留參數傳0** @return RTP Receiver 句柄,0表示失敗*/public native long CreateRTPReceiver(int reserve);/***設置 RTP Receiver傳輸協議** @param rtp_receiver_handle, CreateRTPReceiver* @param transport_protocol, 0:UDP, 1:TCP, 默認是UDP** @return {0} if successful*/public native int SetRTPReceiverTransportProtocol(long rtp_receiver_handle, int transport_protocol);/***設置 RTP Receiver IP地址類型** @param rtp_receiver_handle, CreateRTPReceiver* @param ip_address_type, 0:IPV4, 1:IPV6, 默認是IPV4** @return {0} if successful*/public native int SetRTPReceiverIPAddressType(long rtp_receiver_handle, int ip_address_type);/***設置 RTP Receiver RTP Socket本地端口** @param rtp_receiver_handle, CreateRTPReceiver* @param port, 必須是偶數,設置0的話SDK會自動分配, 默認值是0** @return {0} if successful*/public native int SetRTPReceiverLocalPort(long rtp_receiver_handle, int port);/***設置 RTP Receiver SSRC** @param rtp_receiver_handle, CreateRTPReceiver* @param ssrc, 如果設置的話,這個字符串要能轉換成uint32類型, 否則設置失敗** @return {0} if successful*/public native int SetRTPReceiverSSRC(long rtp_receiver_handle, String ssrc);/***創建 RTP Receiver 會話** @param rtp_receiver_handle, CreateRTPReceiver* @param reserve, 保留值,目前傳0** @return {0} if successful*/public native int CreateRTPReceiverSession(long rtp_receiver_handle, int reserve);/***獲取 RTP Receiver RTP Socket本地端口** @param rtp_receiver_handle, CreateRTPReceiver** @return 失敗返回0, 成功的話返回響應的端口, 請在CreateRTPReceiverSession返回成功之后調用*/public native int GetRTPReceiverLocalPort(long rtp_receiver_handle);/***設置 RTP Receiver Payload 相關信息** @param rtp_receiver_handle, CreateRTPReceiver** @param payload_type, 請參考 RFC 3551** @param encoding_name, 編碼名, 請參考 RFC 3551, 如果payload_type不是動態的, 可能傳null就好** @param media_type, 媒體類型, 請參考 RFC 3551, 1 是視頻, 2是音頻** @param clock_rate, 請參考 RFC 3551** @return {0} if successful*/public native int SetRTPReceiverPayloadType(long rtp_receiver_handle, int payload_type, String encoding_name, int media_type, int clock_rate);/***設置 RTP Receiver 音頻采樣率** @param rtp_receiver_handle, CreateRTPReceiver* @param sampling_rate, 音頻采樣率** @return {0} if successful*/public native int SetRTPReceiverAudioSamplingRate(long rtp_receiver_handle, int sampling_rate);/***設置 RTP Receiver 音頻通道數** @param rtp_receiver_handle, CreateRTPReceiver* @param channels, 音頻通道數** @return {0} if successful*/public native int SetRTPReceiverAudioChannels(long rtp_receiver_handle, int channels);/***設置 RTP Receiver 遠端地址** @param rtp_receiver_handle, CreateRTPReceiver* @param address, IP地址* @param port, 端口** @return {0} if successful*/public native int SetRTPReceiverRemoteAddress(long rtp_receiver_handle, String address, int port);/***初始化 RTP Receiver** @param rtp_receiver_handle, CreateRTPReceiver** @return {0} if successful*/public native int InitRTPReceiver(long rtp_receiver_handle);/***UnInit RTP Receiver** @param rtp_receiver_handle, CreateRTPReceiver** @return {0} if successful*/public native int UnInitRTPReceiver(long rtp_receiver_handle);/***Destory RTP Receiver Session** @param rtp_receiver_handle, CreateRTPReceiver** @return {0} if successful*/public native int DestoryRTPReceiverSession(long rtp_receiver_handle);/***Destory RTP Receiver** @param rtp_receiver_handle, CreateRTPReceiver** @return {0} if successful*/public native int DestoryRTPReceiver(long rtp_receiver_handle);/*++++++++++++++++++RTP Receiver++++++++++++++++++++++*/相關調用代碼:
class ButtonGB28181AudioBroadcastListener implements OnClickListener {public void onClick(View v) {if (gb_broadcast_source_id_ != null && gb_broadcast_target_id_ != null) {if (gb28181_agent_ != null ) {if (gb28181_agent_.byeAudioBroadcast(gb_broadcast_source_id_, gb_broadcast_target_id_) ) {gb_broadcast_source_id_ = null;gb_broadcast_target_id_ = null;btnGB28181AudioBroadcast.setText("GB28181語音廣播");btnGB28181AudioBroadcast.setEnabled(false);}}}stopAudioPlayer();destoryRTPReceiver();}} @Override public void ntsOnNotifyBroadcastCommand(String fromUserName, String fromUserNameAtDomain, String sn, String sourceID, String targetID) {handler_.postDelayed(new Runnable() {@Overridepublic void run() {Log.i(TAG, "ntsOnNotifyBroadcastCommand, fromUserName:"+ from_user_name_ + ", fromUserNameAtDomain:"+ from_user_name_at_domain_+ ", SN:" + sn_ + ", sourceID:" + source_id_ + ", targetID:" + target_id_);if (gb28181_agent_ != null ) {gb28181_agent_.respondBroadcastCommand(from_user_name_, from_user_name_at_domain_,sn_,source_id_, target_id_, true);btnGB28181AudioBroadcast.setText("收到GB28181語音廣播通知");}}private String from_user_name_;private String from_user_name_at_domain_;private String sn_;private String source_id_;private String target_id_;public Runnable set(String from_user_name, String from_user_name_at_domain, String sn, String source_id, String target_id) {this.from_user_name_ = from_user_name;this.from_user_name_at_domain_ = from_user_name_at_domain;this.sn_ = sn;this.source_id_ = source_id;this.target_id_ = target_id;return this;}}.set(fromUserName, fromUserNameAtDomain, sn, sourceID, targetID),0); }@Override public void ntsOnAudioBroadcast(String commandFromUserName, String commandFromUserNameAtDomain, String sourceID, String targetID) {handler_.postDelayed(new Runnable() {@Overridepublic void run() {Log.i(TAG, "ntsOnAudioBroadcastPlay, fromFromUserName:" + command_from_user_name_+ " FromUserNameAtDomain:" + command_from_user_name_at_domain_+ " sourceID:" + source_id_ + ", targetID:" + target_id_);stopAudioPlayer();destoryRTPReceiver();if (gb28181_agent_ != null ) {String local_ip_addr = IPAddrUtils.getIpAddress(context_);boolean is_tcp = true; // 考慮到跨網段, 默認用TCP傳輸rtp包rtp_receiver_handle_ = lib_player_.CreateRTPReceiver(0);if (rtp_receiver_handle_ != 0 ) {lib_player_.SetRTPReceiverTransportProtocol(rtp_receiver_handle_, is_tcp?1:0);lib_player_.SetRTPReceiverIPAddressType(rtp_receiver_handle_, 0);if (0 == lib_player_.CreateRTPReceiverSession(rtp_receiver_handle_, 0) ) {int local_port = lib_player_.GetRTPReceiverLocalPort(rtp_receiver_handle_);boolean ret = gb28181_agent_.inviteAudioBroadcast(command_from_user_name_,command_from_user_name_at_domain_,source_id_, target_id_, "IP4", local_ip_addr, local_port, is_tcp?"TCP/RTP/AVP":"RTP/AVP");if (!ret ) {destoryRTPReceiver();btnGB28181AudioBroadcast.setText("GB28181語音廣播");}else {btnGB28181AudioBroadcast.setText("GB28181語音廣播呼叫中");}} else {destoryRTPReceiver();btnGB28181AudioBroadcast.setText("GB28181語音廣播");}}}}private String command_from_user_name_;private String command_from_user_name_at_domain_;private String source_id_;private String target_id_;public Runnable set(String command_from_user_name, String command_from_user_name_at_domain, String source_id, String target_id) {this.command_from_user_name_ = command_from_user_name;this.command_from_user_name_at_domain_ = command_from_user_name_at_domain;this.source_id_ = source_id;this.target_id_ = target_id;return this;}}.set(commandFromUserName, commandFromUserNameAtDomain, sourceID, targetID),0); }@Override public void ntsOnInviteAudioBroadcastException(String sourceID, String targetID, String errorInfo) {handler_.postDelayed(new Runnable() {@Overridepublic void run() {Log.i(TAG, "ntsOnInviteAudioBroadcastException, sourceID:" + source_id_ + ", targetID:" + target_id_);destoryRTPReceiver();btnGB28181AudioBroadcast.setText("GB28181語音廣播");}private String source_id_;private String target_id_;public Runnable set(String source_id, String target_id) {this.source_id_ = source_id;this.target_id_ = target_id;return this;}}.set(sourceID, targetID),0); }@Override public void ntsOnInviteAudioBroadcastTimeout(String sourceID, String targetID) {handler_.postDelayed(new Runnable() {@Overridepublic void run() {Log.i(TAG, "ntsOnInviteAudioBroadcastTimeout, sourceID:" + source_id_ + ", targetID:" + target_id_);destoryRTPReceiver();btnGB28181AudioBroadcast.setText("GB28181語音廣播");}private String source_id_;private String target_id_;public Runnable set(String source_id, String target_id) {this.source_id_ = source_id;this.target_id_ = target_id;return this;}}.set(sourceID, targetID),0); } @Override public void ntsOnInviteAudioBroadcastResponse(String sourceID, String targetID, int statusCode, PlaySessionDescription sessionDescription) {handler_.postDelayed(new Runnable() {@Overridepublic void run() {Log.i(TAG, "ntsOnInviteAudioBroadcastResponse, statusCode:" + status_code_ +" sourceID:" + source_id_ + ", targetID:" + target_id_);boolean is_need_destory_rtp = true;if (gb28181_agent_ != null ) {boolean is_need_bye = 200==status_code_;if (200 == status_code_ && session_description_ != null && rtp_receiver_handle_ != 0 ) {MediaSessionDescription audio_des = session_description_.getAudioDescription();SDPRtpMapAttribute audio_attr = null;if (audio_des != null && audio_des.getRtpMapAttributes() != null && !audio_des.getRtpMapAttributes().isEmpty() )audio_attr = audio_des.getRtpMapAttributes().get(0);if ( audio_des != null && audio_attr != null ) {lib_player_.SetRTPReceiverSSRC(rtp_receiver_handle_, audio_des.getSSRC());lib_player_.SetRTPReceiverPayloadType(rtp_receiver_handle_, audio_attr.getPayloadType(),audio_attr.getEncodingName(), 2, audio_attr.getClockRate());lib_player_.SetRTPReceiverRemoteAddress(rtp_receiver_handle_, audio_des.getAddress(), audio_des.getPort());lib_player_.InitRTPReceiver(rtp_receiver_handle_);if (startAudioPlay()) {is_need_bye = false;is_need_destory_rtp = false;gb_broadcast_source_id_ = source_id_;gb_broadcast_target_id_ = target_id_;btnGB28181AudioBroadcast.setText("終止GB28181語音廣播");btnGB28181AudioBroadcast.setEnabled(true);}}} else {btnGB28181AudioBroadcast.setText("GB28181語音廣播");}if (is_need_bye)gb28181_agent_.byeAudioBroadcast(source_id_, target_id_);}if (is_need_destory_rtp)destoryRTPReceiver();}private String source_id_;private String target_id_;private int status_code_;private PlaySessionDescription session_description_;public Runnable set(String source_id, String target_id, int status_code, PlaySessionDescription session_description) {this.source_id_ = source_id;this.target_id_ = target_id;this.status_code_ = status_code;this.session_description_ = session_description;return this;}}.set(sourceID, targetID, statusCode, sessionDescription),0); }@Override public void ntsOnByeAudioBroadcast(String sourceID, String targetID) {handler_.postDelayed(new Runnable() {@Overridepublic void run() {Log.i(TAG, "ntsOnByeAudioBroadcast sourceID:" + source_id_ + " targetID:" + target_id_);gb_broadcast_source_id_ = null;gb_broadcast_target_id_ = null;btnGB28181AudioBroadcast.setText("GB28181語音廣播");btnGB28181AudioBroadcast.setEnabled(false);stopAudioPlayer();destoryRTPReceiver();}private String source_id_;private String target_id_;public Runnable set(String source_id, String target_id) {this.source_id_ = source_id;this.target_id_ = target_id;return this;}}.set(sourceID, targetID),0); }@Override public void ntsOnTerminateAudioBroadcast(String sourceID, String targetID) {handler_.postDelayed(new Runnable() {@Overridepublic void run() {Log.i(TAG, "ntsOnTerminateAudioBroadcast sourceID:" + source_id_ + " targetID:" + target_id_);gb_broadcast_source_id_ = null;gb_broadcast_target_id_ = null;btnGB28181AudioBroadcast.setText("GB28181語音廣播");btnGB28181AudioBroadcast.setEnabled(false);stopAudioPlayer();destoryRTPReceiver();}private String source_id_;private String target_id_;public Runnable set(String source_id, String target_id) {this.source_id_ = source_id;this.target_id_ = target_id;return this;}}.set(sourceID, targetID),0); }總結
至此、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、軟編碼速度、可變碼率設置;
- 支持純視頻、音視頻PS打包傳輸;
- 支持RTP OVER UDP和RTP OVER TCP被動模式;
- 支持信令通道網絡傳輸協議TCP/UDP設置;
- 支持注冊、注銷,支持注冊刷新及注冊有效期設置;
- 支持設備目錄查詢應答;
- 支持心跳機制,支持心跳間隔、心跳檢測次數設置;
- 支持移動設備位置(MobilePosition)訂閱和通知;
- 支持國標GB/T28181—2016平臺接入;
- 支持語音廣播及語音對講;
- [實時水印]支持動態文字水印、png水印;
- [實時靜音]支持實時靜音/取消靜音;
- [實時快照]支持實時快照;
- [降噪]支持環境音、手機干擾等引起的噪音降噪處理、自動增益、VAD檢測。?
特別是語音廣播和語音對講這塊,是GB28181終端接入模塊的一個核心擴展功能,在智能門禁、工業與物聯網、監控等行業,用途非常廣泛,技術實現這塊,不要忽略的技術點還有降噪和回音消除這塊,由于之前我們有技術積累,可以直接復用,對新入手的開發者來說,也提出了新的挑戰,感興趣的開發者,可以酌情參考。
總結
以上是生活随笔為你收集整理的如何在Android平台GB28181接入终端实现语音广播和语音对讲的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: kafka 异常:return ‘<Si
- 下一篇: 机器学习算法之生成树