规范解读
GB28181 中的 “INVITE” 是会话初始协议(SIP)中的一种请求方法,主要用于邀请一个或多个参与者加入特定的会话。在 GB28181 标准中,“INVITE” 请求通常用于发起媒体流的传输请求。当一个设备想要接收来自另一个设备的媒体流时,它会向目标设备发送一个 “INVITE” 请求,其中包含了关于会话的描述信息,如媒体类型、编码格式、传输协议等。
以下是 GB28181 中 “INVITE” 请求的一些关键特点和作用:
一、发起会话
- 触发媒体流传输:“INVITE” 请求是启动媒体流传输的关键步骤。当源设备发送 “INVITE” 请求时,它向目标设备表明了希望建立一个媒体会话的意图。这个请求中包含了源设备支持的媒体格式、编码方式和传输协议等信息,以便目标设备能够确定是否可以满足这些要求并接受会话邀请。
- 携带会话描述信息:“INVITE” 请求中通常包含会话描述协议(SDP)信息,用于描述媒体流的特性。SDP 信息包括媒体类型(音频、视频或两者兼有)、编码格式、媒体的传输地址和端口等。目标设备通过解析 SDP 信息,可以了解源设备的媒体能力,并决定是否能够参与会话。
二、协商媒体参数
- **媒体能力协商:**在 GB28181 中,不同的设备可能具有不同的媒体处理能力。通过 “INVITE” 请求和响应的交互过程,可以进行媒体能力的协商。目标设备在接收到 “INVITE” 请求后,会检查自己的媒体能力,并根据源设备的要求进行相应的调整。如果目标设备无法满足源设备的要求,它可以返回一个错误响应,或者提出替代的媒体参数建议。
- 编码格式选择:“INVITE” 请求还可以用于协商媒体流的编码格式。不同的编码格式在压缩效率、图像质量和带宽需求等方面有所不同。通过协商,可以选择一种双方都支持的编码格式,以确保媒体流的正常传输和播放。
三、建立连接
- **确定传输路径:**一旦目标设备接受了 “INVITE” 请求,双方就可以开始建立媒体流的传输连接。这个过程涉及确定媒体流的传输协议(如 RTP/RTCP)、传输地址和端口等信息。根据 GB28181 标准,媒体流可以通过 IP 网络进行传输,使用 UDP 或 TCP 协议。
- **会话管理:**在媒体流传输过程中,“INVITE” 请求所建立的会话可以通过其他 SIP 方法进行管理,如 “BYE” 用于结束会话,“ACK” 用于确认会话的建立等。这些方法可以帮助设备在会话期间进行状态监测、错误处理和资源管理。
Android GB28181技术实现
本文以大牛直播SDK的GB28181设备接入模块为例,大牛直播SDK推出的Android平台GB28181接入SDK(SmartGBD),可实现不具备国标音视频能力的 Android终端,通过平台注册接入到现有的GB/T28181—2016服务,可用于如执法记录仪、智能安全帽、智能监控、智慧零售、智慧教育、远程办公、明厨亮灶、智慧交通、智慧工地、雪亮工程、平安乡村、生产运输、车载终端等场景。进入系统后,先启动GB28181,注册到平台,等待国标平台发起回传请求。
class ButtonGB28181AgentListener implements View.OnClickListener {
public void onClick(View v) {
record_executor_.cancel_tasks();
stopRecordDownloads(true);
stopPlaybacks(true);
stopAudioPlayer();
destoryRTPReceiver();
gb_broadcast_source_id_ = null;
gb_broadcast_target_id_ = null;
btnGB28181AudioBroadcast.setText("GB28181语音广播");
btnGB28181AudioBroadcast.setEnabled(false);
stopGB28181Stream();
destoryRTPSender();
if (null == gb28181_agent_ ) {
if( !initGB28181Agent() )
return;
}
if (gb28181_agent_.isRunning()) {
gb28181_agent_.terminateAllAudioBroadcasts(true);
gb28181_agent_.terminateAllPlays(true);
gb28181_agent_.stop();
btnGB28181Agent.setText("启动GB28181");
}
else {
record_executor_.cancel_tasks();
initPlaybacks(null);
initRecordDownloads(null);
if ( gb28181_agent_.start() ) {
btnGB28181Agent.setText("停止GB28181");
}
}
}
}
GBSIPAgentPlayListener主要系GB28181的Invite、Ack、Bye等处理:
public interface GBSIPAgentPlayListener {
void ntsOnInvitePlay(String deviceId, SessionDescription sessionDescription);
void ntsOnPlayInviteResponseException(String deviceId, int statusCode, String errorInfo);
void ntsOnCancelPlay(String deviceId);
void ntsOnAckPlay(String deviceId);
void ntsOnByePlay(String deviceId);
void ntsOnTerminatePlay(String deviceId);
void ntsOnPlayDialogTerminated(String deviceId);
}
平台发起回传请求时,ntsOnInvitePlay()来响应invite请求。
/*
* Camera2MainActivity.java
* Author: daniusdk.com
* WeChat: xinsheng120
*/
@Override
public void ntsOnInvitePlay(String deviceId, SessionDescription session_des) {
handler_.postDelayed(new Runnable() {
@Override
public void run() {
// 先振铃响应下
gb28181_agent_.respondPlayInvite(180, device_id_)
MediaSessionDescription video_des = null
SDPRtpMapAttribute ps_rtpmap_attr = null
// 28181 视频使用PS打包
Vector video_des_list = session_des_.getVideoPSDescriptions()
if (video_des_list != null && !video_des_list.isEmpty()) {
for(MediaSessionDescription m : video_des_list) {
if (m != null && m.isValidAddressType() && m.isHasAddress() ) {
video_des = m
ps_rtpmap_attr = video_des.getPSRtpMapAttribute()
break
}
}
}
if (null == video_des) {
gb28181_agent_.respondPlayInvite(488, device_id_)
Log.i(TAG, "ntsOnInvitePlay get video description is null, response 488, device_id:" + device_id_)
return
}
if (null == ps_rtpmap_attr) {
gb28181_agent_.respondPlayInvite(488, device_id_)
Log.i(TAG, "ntsOnInvitePlay get ps rtp map attribute is null, response 488, device_id:" + device_id_)
return
}
Log.i(TAG,"ntsOnInvitePlay, device_id:" +device_id_+", is_tcp:" + video_des.isRTPOverTCP()
+ " rtp_port:" + video_des.getPort() + " ssrc:" + video_des.getSSRC()
+ " address_type:" + video_des.getAddressType() + " address:" + video_des.getAddress())
long rtp_sender_handle = libPublisher.CreateRTPSender(0)
if ( rtp_sender_handle == 0 ) {
gb28181_agent_.respondPlayInvite(488, device_id_)
Log.i(TAG, "ntsOnInvitePlay CreateRTPSender failed, response 488, device_id:" + device_id_)
return
}
gb28181_rtp_payload_type_ = ps_rtpmap_attr.getPayloadType()
gb28181_rtp_encoding_name_ = ps_rtpmap_attr.getEncodingName()
libPublisher.SetRTPSenderTransportProtocol(rtp_sender_handle, video_des.isRTPOverUDP()?0:1)
libPublisher.SetRTPSenderIPAddressType(rtp_sender_handle, video_des.isIPv4()?0:1)
libPublisher.SetRTPSenderLocalPort(rtp_sender_handle, 0)
libPublisher.SetRTPSenderSSRC(rtp_sender_handle, video_des.getSSRC())
libPublisher.SetRTPSenderSocketSendBuffer(rtp_sender_handle, 2*1024*1024)
libPublisher.SetRTPSenderClockRate(rtp_sender_handle, ps_rtpmap_attr.getClockRate())
libPublisher.SetRTPSenderDestination(rtp_sender_handle, video_des.getAddress(), video_des.getPort())
if ( libPublisher.InitRTPSender(rtp_sender_handle) != 0 ) {
gb28181_agent_.respondPlayInvite(488, device_id_)
libPublisher.DestoryRTPSender(rtp_sender_handle)
return
}
int local_port = libPublisher.GetRTPSenderLocalPort(rtp_sender_handle)
if (local_port == 0) {
gb28181_agent_.respondPlayInvite(488, device_id_)
libPublisher.DestoryRTPSender(rtp_sender_handle)
return
}
Log.i(TAG,"get local_port:" + local_port)
String local_ip_addr = IPAddrUtils.getIpAddress(context_)
MediaSessionDescription local_video_des = new MediaSessionDescription(video_des.getType())
local_video_des.addFormat(String.valueOf(ps_rtpmap_attr.getPayloadType()))
local_video_des.addRtpMapAttribute(ps_rtpmap_attr)
local_video_des.setAddressType(video_des.getAddressType())
local_video_des.setAddress(local_ip_addr)
local_video_des.setPort(local_port)
local_video_des.setTransportProtocol(video_des.getTransportProtocol())
local_video_des.setSSRC(video_des.getSSRC())
if (!gb28181_agent_.respondPlayInviteOK(device_id_,local_video_des) ) {
libPublisher.DestoryRTPSender(rtp_sender_handle)
Log.e(TAG, "ntsOnInvitePlay call respondPlayInviteOK failed.")
return
}
gb28181_rtp_sender_handle_ = rtp_sender_handle
}
private String device_id_
private SessionDescription session_des_
public Runnable set(String device_id, SessionDescription session_des) {
this.device_id_ = device_id
this.session_des_ = session_des
return this
}
}.set(deviceId, session_des),0)
}
@Override
public void ntsOnCancelPlay(String deviceId) {
// 这里取消Play会话
handler_.postDelayed(new Runnable() {
@Override
public void run() {
Log.i(TAG, "ntsOnCancelPlay, deviceId=" + device_id_);
destoryRTPSender();
}
private String device_id_;
public Runnable set(String device_id) {
this.device_id_ = device_id;
return this;
}
}.set(deviceId),0);
}
收到Ack后,ntsOnAckPlay()处理后续逻辑:
@Override
public void ntsOnAckPlay(String deviceId) {
handler_.postDelayed(new Runnable() {
@Override
public void run() {
Log.i(TAG,"ntsOnACKPlay, device_id:" +device_id_);
InitAndSetConfig();
stream_publisher_.SetGB28181RTPSender(gb28181_rtp_sender_handle_, gb28181_rtp_payload_type_, gb28181_rtp_encoding_name_);
boolean start_ret = stream_publisher_.StartGB28181MediaStream();
if (!start_ret) {
stream_publisher_.try_release();
destoryRTPSender();
Log.e(TAG, "Failed to start GB28181 service..");
return;
}
startAudioRecorder();
startLayerPostThread();
}
private String device_id_;
public Runnable set(String device_id) {
this.device_id_ = device_id;
return this;
}
}.set(deviceId),0);
}
@Override
public void ntsOnPlayInviteResponseException(String deviceId, int statusCode, String errorInfo) {
Log.i(TAG, "ntsOnPlayInviteResponseException, deviceId=" + deviceId + " statusCode=" +statusCode
+ " errorInfo:" + errorInfo);
handler_.postDelayed(new Runnable() {
@Override
public void run() {
Log.i(TAG, "ntsOnPlayInviteResponseException, deviceId=" + device_id_);
destoryRTPSender();
}
private String device_id_;
public Runnable set(String device_id) {
this.device_id_ = device_id;
return this;
}
}.set(deviceId),0);
}
总结
GB28181 中的 “INVITE” 请求在媒体流传输中起着至关重要的作用,它通过发起会话、协商媒体参数和建立连接等步骤,实现了设备之间的媒体通信。在实际应用中,需要根据具体的需求和场景,合理地使用 “INVITE” 请求和其他 SIP 方法,以确保媒体流的稳定传输和高质量播放。
阅读全文
下载说明:
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.dandroid.cn/archives/22547,转载请注明出处。
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.dandroid.cn/archives/22547,转载请注明出处。
评论0