Android平台实现RTSP|RTMP转GB28181网关接入

背景

在事先Android平台RTSP、RTMP转GB28181网关之前,我们已经实现了Android平台GB28181的接入,可实现Android平台采集到的音视频数据,编码后,打包按需发到GB28181服务平台。此外,拉流端,我们已经有了成熟的RTSP和RTMP拉流播放方案。

今天,我们要做的是,把外部的RTSP或RTMP流,拉过来,然后对接到GB28181国标平台,实现媒体流数据的按需播放

和我们之前实现的轻量级RTSP服务网关模块类似,我们要做的是,实现RTSP或RTMP流,按需打包对接到GB28181服务平台。

简单来说,Android平台RTSP、RTMP转GB28181网关平台,是GB28181设备接入模块的一个扩展,由拉流端、GB28181接入端两个模块组成。

轻量级RTSP服务模块、RTSP|RTMP转GB28181网关模块和内置RTSP网关模块的区别和联系:

内置轻量级RTSP服务模块和内置RTSP网关模块,核心痛点是避免用户或者开发者单独部署RTSP或者RTMP服务,数据汇聚到内置RTSP服务,对外提供可供拉流的RTSP URL,适用于内网环境下,对并发要求不高的场景,支持H.264/H.265,支持RTSP鉴权、单播、组播模式,考虑到单个服务承载能力,我们支持同时创建多个RTSP服务,并支持获取当前RTSP服务会话连接数。

RTSP|RTMP转GB28181网关模块,实现的是音视频数据的转发,类似于RTSP|RTMP转RTMP推送模块,把本地数据源,对接到GB28181服务平台或RTMP服务平台。

三者不同点:数据来源不同

1. 内置轻量级RTSP服务模块,数据源来自摄像头、屏幕、麦克风等编码前数据,或者本地编码后的对接数据,这点和GB28181的设备接入模块类似。

2. 内置RTSP网关模块,实际上是RTSP/RTMP拉流模块+内置轻量级RTSP服务模块组合出来的。数据源来自RTSP或RTMP网络流,拉流模块完成编码后的音视频数据回调,然后,汇聚到内置轻量级RTSP服务模块。RTSP|RTMP转GB28181网关模块,和内置RTSP网关模块数据源接入一样。

技术实现

本文以之前Android平台RTSP|RTMP转发demo为例,在这个基础上,加GB28181网关扩展。

拉流端音频数据回调,拉流端获取到编码后是数据,回调上来,通过SmartPublisherPostAudioEncodedData()发送到推送模块。

class PlayerAudioDataCallback implements NTAudioDataCallback{private int audio_buffer_size = 0;private int param_info_size = 0;private ByteBuffer audio_buffer_ = null;private ByteBuffer parameter_info_ = null;@Overridepublic ByteBuffer getAudioByteBuffer(int size){//Log.i("getAudioByteBuffer", "size: " + size);if( size < 1 ){return null;}if ( size <= audio_buffer_size && audio_buffer_ != null ){return audio_buffer_;}audio_buffer_size = size + 512;audio_buffer_size = (audio_buffer_size+0xf) & (~0xf);audio_buffer_ = ByteBuffer.allocateDirect(audio_buffer_size);// Log.i("getAudioByteBuffer", "size: " + size + " buffer_size:" + audio_buffer_size);return audio_buffer_;}@Overridepublic ByteBuffer getAudioParameterInfo(int size){//Log.i("getAudioParameterInfo", "size: " + size);if(size < 1){return null;}if ( size <= param_info_size &&  parameter_info_ != null ){return  parameter_info_;}param_info_size = size + 32;param_info_size = (param_info_size+0xf) & (~0xf);parameter_info_ = ByteBuffer.allocateDirect(param_info_size);//Log.i("getAudioParameterInfo", "size: " + size + " buffer_size:" + param_info_size);return parameter_info_;}public void onAudioDataCallback(int ret, int audio_codec_id, int sample_size, int is_key_frame, long timestamp, int sample_rate, int channel, int parameter_info_size, long reserve){//Log.i("onAudioDataCallback", "ret: " + ret + ", audio_codec_id: " + audio_codec_id + ", sample_size: " + sample_size + ", timestamp: " + timestamp +//    ",sample_rate:" + sample_rate);if ( audio_buffer_ == null)return;audio_buffer_.rewind();if ( ret == 0 && (isPushing || isRTSPPublisherRunning || isGB28181StreamRunning)) {libPublisher.SmartPublisherPostAudioEncodedData(publisherHandle, audio_codec_id, audio_buffer_, sample_size, is_key_frame, timestamp, parameter_info_, parameter_info_size);}// test/*byte[] test_buffer = new byte[16];pcm_buffer_.get(test_buffer);Log.i(TAG, "onGetPcmFrame data:" + bytesToHexString(test_buffer));*/}}

拉流端视频数据回调,拉流端获取到编码后是数据,回调上来,通过SmartPublisherPostVideoEncodedData()发送到推送模块。

class PlayerVideoDataCallback implements NTVideoDataCallback{private int video_buffer_size = 0;private ByteBuffer video_buffer_ = null;@Overridepublic ByteBuffer getVideoByteBuffer(int size){//Log.i("getVideoByteBuffer", "size: " + size);if( size < 1 ){return null;}if ( size <= video_buffer_size &&  video_buffer_ != null ){return  video_buffer_;}video_buffer_size = size + 1024;video_buffer_size = (video_buffer_size+0xf) & (~0xf);video_buffer_ = ByteBuffer.allocateDirect(video_buffer_size);// Log.i("getVideoByteBuffer", "size: " + size + " buffer_size:" + video_buffer_size);return video_buffer_;}public void onVideoDataCallback(int ret, int video_codec_id, int sample_size, int is_key_frame, long timestamp, int width, int height, long presentation_timestamp){//Log.i("onVideoDataCallback", "ret: " + ret + ", video_codec_id: " + video_codec_id + ", sample_size: " + sample_size + ", is_key_frame: "+ is_key_frame +  ", timestamp: " + timestamp +//    ",presentation_timestamp:" + presentation_timestamp);if ( video_buffer_ == null)return;video_buffer_.rewind();if ( ret == 0 &&  (isPushing || isRTSPPublisherRunning || isGB28181StreamRunning) ) {libPublisher.SmartPublisherPostVideoEncodedData(publisherHandle, video_codec_id, video_buffer_, sample_size, is_key_frame, timestamp, presentation_timestamp);}}}

GB28181网关模块,初始化:

/** gb28181 agent初始化* Github: https://github.com/daniulive/SmarterStreaming*/private boolean initGB28181Agent() {if ( gb28181_agent_ != null )return  true;getLocation(myContext);String local_ip_addr = IPAddrUtils.getIpAddress(myContext);Log.i(TAG, "initGB28181Agent local ip addr: " + local_ip_addr);if ( local_ip_addr == null || local_ip_addr.isEmpty() ) {Log.e(TAG, "initGB28181Agent local ip is empty");return  false;}gb28181_agent_ = GBSIPAgentFactory.getInstance().create();if ( gb28181_agent_ == null ) {Log.e(TAG, "initGB28181Agent create agent failed");return false;}gb28181_agent_.addListener(this);// 必填信息gb28181_agent_.setLocalAddressInfo(local_ip_addr, gb28181_sip_local_port_);gb28181_agent_.setServerParameter(gb28181_sip_server_addr_, gb28181_sip_server_port_, gb28181_sip_server_id_, gb28181_sip_server_domain_);gb28181_agent_.setUserInfo(gb28181_sip_username_, gb28181_sip_password_);// 可选参数gb28181_agent_.setUserAgent(gb28181_sip_user_agent_filed_);gb28181_agent_.setTransportProtocol(gb28181_sip_trans_protocol_==0?"UDP":"TCP");// GB28181配置gb28181_agent_.config(gb28181_reg_expired_, gb28181_heartbeat_interval_, gb28181_heartbeat_count_);com.gb28181.ntsignalling.Device gb_device = new com.gb28181.ntsignalling.Device("34020000001380000001", "安卓测试设备", Build.MANUFACTURER, Build.MODEL,"宇宙","火星1","火星", true);if (mLongitude != null && mLatitude != null) {com.gb28181.ntsignalling.DevicePosition device_pos = new com.gb28181.ntsignalling.DevicePosition();device_pos.setTime(mLocationTime);device_pos.setLongitude(mLongitude);device_pos.setLatitude(mLatitude);gb_device.setPosition(device_pos);gb_device.setSupportMobilePosition(true); // 设置支持移动位置上报}gb28181_agent_.addDevice(gb_device);if (!gb28181_agent_.initialize()) {gb28181_agent_ = null;Log.e(TAG, "initGB28181Agent gb28181_agent_.initialize failed.");return  false;}return true;}

GB28181网关模块按钮相关代码实现,整体和GB28181设备接入一致:

class ButtonGB28181AgentListener implements OnClickListener {public void onClick(View v) {stopGB28181Stream();destoryRTPSender();if (null == gb28181_agent_ ) {if( !initGB28181Agent() )return;}if (gb28181_agent_.isRunning()) {gb28181_agent_.terminateAllPlays(true);// 目前测试下来,发送BYE之后,有些服务器会立即发送INVITE,是否发送BYE根据实际情况看gb28181_agent_.stop();btnGB28181Agent.setText("启动GB28181");}else {if ( gb28181_agent_.start() ) {btnGB28181Agent.setText("停止GB28181");}}}}//停止GB28181 媒体流private void stopGB28181Stream() {if(!isGB28181StreamRunning)return;if (libPublisher != null) {libPublisher.StopGB28181MediaStream(publisherHandle);}if (!isRecording && !isRTSPPublisherRunning && !isPushing) {if (publisherHandle != 0) {if (libPublisher != null) {libPublisher.SmartPublisherClose(publisherHandle);publisherHandle = 0;}}}isGB28181StreamRunning = false;}private void destoryRTPSender() {if (gb28181_rtp_sender_handle_ != 0) {libPublisher.DestoryRTPSender(gb28181_rtp_sender_handle_);gb28181_rtp_sender_handle_ = 0;}}
GB28181网关模块,事件回调处理:@Overridepublic void ntsRegisterOK(String dateString) {Log.i(TAG, "ntsRegisterOK Date: " + (dateString!= null? dateString : ""));}@Overridepublic void ntsRegisterTimeout() {Log.e(TAG, "ntsRegisterTimeout");}@Overridepublic void ntsRegisterTransportError(String errorInfo) {Log.e(TAG, "ntsRegisterTransportError error:" + (errorInfo != null?errorInfo :""));}@Overridepublic void ntsOnHeartBeatException(int exceptionCount,  String lastExceptionInfo) {Log.e(TAG, "ntsOnHeartBeatException heart beat timeout count reached, count:" + exceptionCount+", exception info:" + (lastExceptionInfo!=null?lastExceptionInfo:""));// 10毫秒后,停止信令, 然后重启handler.postDelayed(new Runnable() {@Overridepublic void run() {Log.i(TAG, "gb28281_heart_beart_timeout");stopGB28181Stream();destoryRTPSender();if (gb28181_agent_ != null) {Log.i(TAG, "gb28281_heart_beart_timeout sip stop");gb28181_agent_.stop();Log.i(TAG, "gb28281_heart_beart_timeout sip start");gb28181_agent_.start();}}},10);}@Overridepublic void ntsOnInvitePlay(String deviceId, InvitePlaySessionDescription session_des) {handler.postDelayed(new Runnable() {@Overridepublic void run() {Log.i(TAG,"ntsInviteReceived, device_id:" +device_id_+", is_tcp:" + session_des_.isRTPOverTCP()+ " rtp_port:" + session_des_.getMediaPort() + " ssrc:" + session_des_.getSSRC()+ " address_type:" + session_des_.getAddressType() + " address:" + session_des_.getAddress());// 可以先给信令服务器发送临时振铃响应//sip_stack_android.respondPlayInvite(180, device_id_);long rtp_sender_handle = libPublisher.CreateRTPSender(0);if ( rtp_sender_handle == 0 ) {gb28181_agent_.respondPlayInvite(488, device_id_);Log.i(TAG, "ntsInviteReceived CreateRTPSender failed, response 488, device_id:" + device_id_);return;}gb28181_rtp_payload_type_ = session_des_.getPSRtpMapAttribute().getPayloadType();libPublisher.SetRTPSenderTransportProtocol(rtp_sender_handle, session_des_.isRTPOverUDP()?0:1);libPublisher.SetRTPSenderIPAddressType(rtp_sender_handle, session_des_.isIPv4()?0:1);libPublisher.SetRTPSenderLocalPort(rtp_sender_handle, 0);libPublisher.SetRTPSenderSSRC(rtp_sender_handle, session_des_.getSSRC());libPublisher.SetRTPSenderSocketSendBuffer(rtp_sender_handle, 2*1024*1024); // 设置到2MlibPublisher.SetRTPSenderClockRate(rtp_sender_handle, session_des_.getPSRtpMapAttribute().getClockRate());libPublisher.SetRTPSenderDestination(rtp_sender_handle, session_des_.getAddress(), session_des_.getMediaPort());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(myContext);gb28181_agent_.respondPlayInviteOK(device_id_,local_ip_addr, local_port);gb28181_rtp_sender_handle_ = rtp_sender_handle;}private String device_id_;private InvitePlaySessionDescription session_des_;public Runnable set(String device_id, InvitePlaySessionDescription session_des) {this.device_id_ = device_id;this.session_des_ = session_des;return this;}}.set(deviceId, session_des),0);}@Overridepublic void ntsOnCancelPlay(String deviceId) {// 这里取消Play会话handler.postDelayed(new Runnable() {@Overridepublic 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);}@Overridepublic void ntsOnAckPlay(String deviceId) {handler.postDelayed(new Runnable() {@Overridepublic void run() {Log.i(TAG,"ntsOnACKPlay, device_id:" +device_id_);if (!isRecording && !isRTSPPublisherRunning && !isPushing) {OpenPushHandle();}libPublisher.SetGB28181RTPSender(publisherHandle, gb28181_rtp_sender_handle_, gb28181_rtp_payload_type_);int startRet = libPublisher.StartGB28181MediaStream(publisherHandle);if (startRet != 0) {if (!isRecording && !isRTSPPublisherRunning && !isPushing) {if (publisherHandle != 0) {libPublisher.SmartPublisherClose(publisherHandle);publisherHandle = 0;}}destoryRTPSender();Log.e(TAG, "Failed to start GB28181 service..");return;}isGB28181StreamRunning = true;}private String device_id_;public Runnable set(String device_id) {this.device_id_ = device_id;return this;}}.set(deviceId),0);}@Overridepublic void ntsOnPlayInviteResponseException(String deviceId, int statusCode, String errorInfo) {// 这里要释放掉响应的资源Log.i(TAG, "ntsOnPlayInviteResponseException, deviceId=" + deviceId + " statusCode=" +statusCode+ " errorInfo:" + errorInfo);handler.postDelayed(new Runnable() {@Overridepublic 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);}@Overridepublic void ntsOnByePlay(String deviceId) {handler.postDelayed(new Runnable() {@Overridepublic void run() {Log.i(TAG, "ntsOnByePlay, stop GB28181 media stream, deviceId=" + device_id_);stopGB28181Stream();destoryRTPSender();}private String device_id_;public Runnable set(String device_id) {this.device_id_ = device_id;return this;}}.set(deviceId),0);}@Overridepublic void ntsOnPlayDialogTerminated(String deviceId) {/*Play会话对应的对话终止, 一般不会出发这个回调,目前只有在响应了200K, 但在64*T1时间后还没收到ACK,才可能会出发收到这个请做相关清理处理*/handler.postDelayed(new Runnable() {@Overridepublic void run() {Log.i(TAG, "ntsOnPlayDialogTerminated, deviceId=" + device_id_);stopGB28181Stream();destoryRTPSender();}private String device_id_;public Runnable set(String device_id) {this.device_id_ = device_id;return this;}}.set(deviceId),0);}@Overridepublic void ntsOnDevicePositionRequest(String deviceId, int interval) {handler.postDelayed(new Runnable() {@Overridepublic void run() {getLocation(myContext);Log.v(TAG, "ntsOnDevicePositionRequest, deviceId:" + this.device_id_ + ", Longitude:" + mLongitude+ ", Latitude:" + mLatitude + ", Time:" + mLocationTime);if (mLongitude != null && mLatitude != null) {com.gb28181.ntsignalling.DevicePosition device_pos = new com.gb28181.ntsignalling.DevicePosition();device_pos.setTime(mLocationTime);device_pos.setLongitude(mLongitude);device_pos.setLatitude(mLatitude);if (gb28181_agent_ != null ) {gb28181_agent_.updateDevicePosition(device_id_, device_pos);}}}private String device_id_;private int interval_;public Runnable set(String device_id, int interval) {this.device_id_ = device_id;this.interval_ = interval;return this;}}.set(deviceId, interval),0);}

OnDestroy(),停掉GB28181 stream,释放资源。

@Overrideprotected void onDestroy() {Log.i(TAG, "Run into activity destory++");if (gb28181_agent_ != null ) {if (gb28181_agent_.isRunning()) {gb28181_agent_.terminateAllPlays(false);gb28181_agent_.stop();}Log.i(TAG, " gb28181_agent_.unInitialize++");gb28181_agent_.unInitialize();Log.i(TAG, " gb28181_agent_.unInitialize--");gb28181_agent_ = null;}stopGB28181Stream();destoryRTPSender();super.onDestroy();finish();System.exit(0);}

以上是大概流程,感兴趣的开发者,可酌情参考。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/552999.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一句话解释C++指针和引用区别

记住一句话就够了&#xff1a;指针三心二意&#xff0c;引用从一而终&#xff01; 指针是一个实体&#xff0c;而引用可理解为一个别名&#xff1b; ”sizeof(指针)”得到的是指针本身的大小&#xff0c;”sizeof(引用)”返回所指向的变量(对象)的大小。 引用一定不为空&…

大地Win11 64位全新专业版系统V2021.08

大地Win11 64位全新专业版系统V2021.08以微软官方原版作为母盘对系统进行了全面优化更新&#xff0c;用户使用更加流畅顺手&#xff0c;轻松体验到系统的优秀性能&#xff0c;适用目前市场最新机型以及老旧机型&#xff0c;多种安装方式供用户选择&#xff0c;且更新时间短无需…

Unity3D下实现Linux平台RTMP推流(以采集Unity窗体和声音为例)

技术背景 随着物联网等行业的崛起&#xff0c;越来越多的传统行业如虚拟仿真、航天工业、工业仿真、城市规划等&#xff0c;对Linux下的生态构建&#xff0c;有了更大的期望&#xff0c;Linux平台下&#xff0c;可选的直播推拉流解决方案相对Windows和移动端&#xff0c;非常少…

电脑公司Win11 64位全新旗舰版镜像V2021.08

电脑公司Win11 64位全新旗舰版镜像V2021.08以微软官方原版作为母盘对系统进行了全面优化更新&#xff0c;用户使用更加流畅顺手&#xff0c;轻松体验到系统的优秀性能&#xff0c;适用目前市场最新机型以及老旧机型&#xff0c;多种安装方式供用户选择&#xff0c;且更新时间短…

Android平台实现VR头显Unity下音视频数据RTMP推送

背景 随着技术发展的日新月异&#xff0c;虚拟现实产业已经从过去的探索期&#xff0c;自2020年起&#xff0c;慢慢过渡到高速发展期&#xff0c;随着5G时代的到来&#xff0c;大带宽高可靠低延迟网络环境&#xff0c;为虚拟现实产业提供了很好的网络保障&#xff0c;虚拟现实…

如何快速区分C++左值和右值

C左值和右值&#xff0c;初学者经常傻傻分不清&#xff0c;可参看“C Primer”书中的描述&#xff0c;简单来说&#xff1a; 当一个对象被用作右值的时候&#xff0c;用的是对象的值&#xff08;内容&#xff09;&#xff1b;当对象被用作左值的时候&#xff0c;用的是对象的身…

Win11将沿用Win10升级模式 并会有LTSC版本

微软官方已经确认 Windows 11 将沿用 Windows 10 的升级模式&#xff0c;包括每月的安全更新、可选累积更新、紧急的带外更新、服务栈更新等等。此外&#xff0c;微软还确认将会有个长期服务渠道&#xff08;LTSC&#xff09;版本的 Windows 11。 在近日更新的官方支持文档中&…

C++11特性之std:call_once介绍

std:call_once是C11引入的新特性&#xff0c;如需使用&#xff0c;只需要#include <mutex>即可&#xff0c;简单来说std:call_once的作用&#xff0c;确保函数或代码片段在多线程环境下&#xff0c;只需要执行一次&#xff0c;常用的场景如Init()操作或一些系统参数的获取…

2022年了,该学C++还是Java?

最近好多朋友私信我&#xff0c;C好不好学&#xff1f;学C好还是Java好&#xff1f; 我的回答是&#xff1a;C不好学&#xff0c;但你觉得C不好学的话&#xff0c;Java也不好学。因为C难是难在语言本身&#xff0c;java难是难在各种框架和库。 C学习进阶比较陡, 对新手不友好&…

Outlook怎么打印日历 Outlook日历打印教程

Outlook中的日历想要打印出来&#xff0c;该怎么打印日历呢?下面我们就来看看详细的教程。 Outlook怎么打印日历? Outlook日历打印教程 1、下载安装outlook软件。 Outlook怎么打印日历? Outlook日历打印教程 2、双击打开outlook软件&#xff0c;登入邮箱账户。 Outlook…

Android平台音视频RTMP推送|GB28181对接之动态水印设计

技术背景 随着移动单兵、智能车载、智慧安防、智能家居、工业仿真、GB28281技术对接等行业的发展&#xff0c;现场已经不再限于采集到视频数据编码打包发送或对接到流媒体服务端&#xff0c;大多场景对视频水印的要求越来越高&#xff0c;从之前的固定位置静态文字水印、png水…

TIM怎么更新版本 TIM检查更新版本教程

TIM是非常好用的软件&#xff0c;与传统的QQ比起来比较轻便&#xff0c;简洁大方&#xff0c;今天小编教大家怎样TIM检查更新。 TIM检查更新版本教程 首先&#xff0c;打开TIM&#xff0c;如下图所示&#xff0c;TIM是这个图标&#xff0c;如果在metro界面&#xff0c;就是第…

探究C++11智能指针之std::unique_ptr

背景 谈起C&#xff0c;它被公认为最难学的编程语言之一&#xff0c;不仅语法知识点广泛&#xff0c;细节内容之多&#xff0c;学习难度和学习周期也长&#xff0c;导致好多新入行的开发者对C“敬而远之”&#xff0c;甚至“从入门到放弃”。自C11开始&#xff0c;好多C程序员…

C++17新特性之try_emplace与insert_or_assign

由于std::map中&#xff0c;元素的key是唯一的&#xff0c;我们经常遇到这样的场景&#xff0c;向map中插入元素时&#xff0c;先检测map指定的key是否存在&#xff0c;不存在时才做插入操作&#xff0c;如果存在&#xff0c;直接取出来使用&#xff0c;或者key不存在时&#x…

Foxmail邮件字体大小怎么设置 Foxmail字体的设置方法

Foxmail写邮件的时候&#xff0c;想要设置邮件的字体&#xff0c;和字体大小&#xff0c;该怎么设置呢?下面我们就来看看详细的教程。 Foxmail邮件字体大小怎么设置? Foxmail字体的设置方法 1、下载并安装Foxmail邮件客户端。 Foxmail邮件字体大小怎么设置? Foxmail字体的…

C++11/14/17中提供的mutex系列区别

C11/14/17中提供的mutex系列类型如下&#xff1a; 互斥量 C版本 作用 mutex C11 基本的互斥量 timed_mutex C11 timed_mutex带超时功能。在规定的等待时间内&#xff0c;没有获取锁&#xff0c;线程不会一直阻塞&#xff0c;代码会继续执行 recursive_mutex C11 递归…

Unity环境下实现Camera高帧率RTMP推送

Unity下RTMP直播背景方面不再赘述&#xff0c;今天主要讨论的是&#xff0c;Unity环境下&#xff0c;如何实现Camera高帧率RTMP推送&#xff0c;这里提到的高帧率&#xff0c;不再局限于常规环境下的30帧&#xff0c;以VR头显为例&#xff0c;更高的帧率&#xff08;比如50帧&a…

Outlook2010怎么关联邮箱 Outlook邮箱关联的教程

Outlook想要关联自己的邮箱&#xff0c;该怎么关联呢?下面我们就来看看详细的教程。 1、首先我们登录进入我们的Outlook&#xff0c;点击文件-添加账户 Outlook2010怎么关联邮箱? Outlook邮箱关联的教程 2、之后进入我们的添加账户导航&#xff0c;选择第一个电子邮件 Ou…

如何在Android平台GB28181接入终端实现语音广播和语音对讲

技术背景 在之前的blog&#xff0c;我们以Android平台国标接入终端为例&#xff0c;分别介绍了一些常规的功能&#xff0c;比如REGISTER、CATALOG、INVITE、Keepalive、SUBSCRIBE、NOTIFY等常规操作&#xff0c;今天主要介绍下语音广播和语音对讲这部分。 GB28181平台广播和对…

Android GB28181设备接入端语音广播和语音对讲技术实现探究

上篇文章提到Android端GB28181接入端的语音广播和语音对讲的实现&#xff0c;从spec角度大概介绍了下流程和简单的接口设计&#xff0c;好多开发者私信我&#xff0c;希望展开说一下。其实这块难度不大&#xff0c;只是广播和对讲涉及到双向实现&#xff0c;如果之前没有相关的…