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

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

语音广播的流程大家应该非常清楚了,简单来说,SIP服务器发送Broadcast语音广播命令到android接入端,接入端应答,在收到200 OK后,发送INVITE消息,Android接入端收到INVITE的200 OK响应后,回复ACK,开始读取并解析RTP包,然后对音频数据解码,输出到Android播放设备即可。

从DEMO来看,当有语音广播接入进来后,GB28181语音广播按钮会处于可用状态。

 

语音广播信令Listener如下:

package com.gb28181.ntsignalling;public interface GBSIPAgentListener
{/**收到语音广播通知*/void ntsOnNotifyBroadcastCommand(String fromUserName, String fromUserNameAtDomain, String sn, String sourceID, String targetID);/**需要准备接受语音广播的SDP内容*/void ntsOnAudioBroadcast(String commandFromUserName, String commandFromUserNameAtDomain, String sourceID, String targetID);/**音频广播, 发送Invite请求异常*/void ntsOnInviteAudioBroadcastException(String sourceID, String targetID, String errorInfo);/**音频广播, 等待Invite响应超时*/void ntsOnInviteAudioBroadcastTimeout(String sourceID, String targetID);/**音频广播, 收到Invite消息最终响应*/void ntsOnInviteAudioBroadcastResponse(String sourceID, String targetID, int statusCode, PlaySessionDescription sessionDescription);/** 音频广播, 收到BYE Message*/void ntsOnByeAudioBroadcast(String sourceID, String targetID);/** 不是在收到BYE Message情况下, 终止音频广播*/void ntsOnTerminateAudioBroadcast(String sourceID, String targetID);
}

相关信令接口如下:

package com.gb28181.ntsignalling;public interface GBSIPAgent {/**语音广播应答*/void respondBroadcastCommand(String fromUserName, String fromUserNameAtDomain, String sn, String sourceID, String targetID, boolean result);/**语音广播接收者发送Invite消息, rtp ssrc暂时由sdk生成*@param addressType: ipv4:"IP4", ipv6:"IP6", 其他不支持, 填充SDP用*@param localAddress: 本地IP地址, 填充SDP用*@param localPort: 本地端口, 填充SDP用*@param mediaTransportProtocol: 媒体传输协议, rtp over udp:"RTP/AVP", rtp over tcp:"TCP/RTP/AVP". 其他不支持, 填充SDP用*/boolean inviteAudioBroadcast(String commandFromUserName, String commandFromUserNameAtDomain, String sourceID, String targetID,String addressType, String localAddress, int localPort, String mediaTransportProtocol);/**取消音频广播, 这个需要在invite收到临时响应之后,最终响应之前才能成功, 如果UAS已经发送过最终响应, UAS收到cancel不做处理, 具体参考RFC3261*/boolean cancelAudioBroadcast(String sourceID, String targetID);/**终止语音广播会话, 发送BYE消息*/boolean byeAudioBroadcast(String sourceID, String targetID);
}

  RTP音频包接收和解码输出接口,由于我们已经有非常成熟的RTMP和RTSP Player,我们是要在此基础上,扩展一些接口即可:

/** SmartPlayerJniV2.java* SmartPlayerJniV2** Github: https://github.com/daniulive/SmarterStreaming* */package com.daniulive.smartplayer;public class SmartPlayerJniV2 {
/*** Initialize Player(启动播放实例)** @param ctx: get by this.getApplicationContext()** <pre>This function must be called firstly.</pre>** @return player handle if successful, if return 0, which means init failed. */public native long SmartPlayerOpen(Object ctx);/*** Set External Audio Output(设置回调PCM数据)** @param handle: return value from SmartPlayerOpen()** @param external_audio_output:  External Audio Output** @return {0} if successful*/public native int SmartPlayerSetExternalAudioOutput(long handle, Object external_audio_output);/*** Set Audio Data Callback(设置回调编码后音频数据)** @param handle: return value from SmartPlayerOpen()** @param audio_data_callback: Audio Data Callback.** @return {0} if successful*/public native int SmartPlayerSetAudioDataCallback(long handle, Object audio_data_callback);/*** Set buffer(设置缓冲时间,单位:毫秒)** @param handle: return value from SmartPlayerOpen()** @param buffer:** <pre> NOTE: Unit is millisecond, range is 0-5000 ms </pre>** @return {0} if successful*/public native int SmartPlayerSetBuffer(long handle, int buffer);/*** Set mute or not(设置实时静音)** @param handle: return value from SmartPlayerOpen()** @param is_mute: if with 1:mute, if with 0: does not mute** @return {0} if successful*/public native int SmartPlayerSetMute(long handle, int is_mute);/*** 设置播放音量** @param handle: return value from SmartPlayerOpen()** @param volume: 范围是[0, 100], 0是静音,100是最大音量, 默认是100** @return {0} if successful*/public native int SmartPlayerSetAudioVolume(long handle, int volume);/*** 清除所有 rtp receivers** @param handle: return value from SmartPlayerOpen()** @return {0} if successful*/public native int SmartPlayerClearRtpReceivers(long handle);/*** 增加 rtp receiver** @param handle: return value from SmartPlayerOpen()** @param rtp_receiver_handle: return value from CreateRTPReceiver()** @return {0} if successful*/public native int SmartPlayerAddRtpReceiver(long handle, long rtp_receiver_handle);/*** 设置需要播放或录像的RTMP/RTSP url** @param handle: return value from SmartPlayerOpen()** @param uri: rtsp/rtmp playback/recorder uri** @return {0} if successful*/public native int SmartPlayerSetUrl(long handle, String uri);/*** Start playback stream(开始播放)** @param handle: return value from SmartPlayerOpen()** @return {0} if successful*/public native int SmartPlayerStartPlay(long handle);/*** Stop playback stream(停止播放)** @param handle: return value from SmartPlayerOpen()** @return {0} if successful*/public native int SmartPlayerStopPlay(long handle);/*** Start pull stream(开始拉流,用于数据转发,只拉流不播放)** @param handle: return value from SmartPlayerOpen()** @return {0} if successful*/public native int SmartPlayerStartPullStream(long handle);/*** Stop pull stream(停止拉流)** @param handle: return value from SmartPlayerOpen()** @return {0} if successful*/public native int SmartPlayerStopPullStream(long handle);/*** 关闭播放实例,结束时必须调用close接口释放资源** @param handle: return value from SmartPlayerOpen()** <pre> NOTE: it could not use player handle after call this function. </pre> ** @return {0} if successful*/public native int SmartPlayerClose(long handle);/*++++++++++++++++++RTP Receiver++++++++++++++++++++++*//** 创建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++++++++++++++++++++++*/}

上层调用DEMO实例代码:

public class AndroidGB28181Demo implements GBSIPAgentListener {private String gb_source_id_ = null;private String gb_target_id_ = null;private long player_handle_ = 0;private long rtp_receiver_handle_ = 0;private AtomicLong last_receive_audio_data_time_ = new AtomicLong(0);@Overridepublic void ntsOnNotifyBroadcastCommand(String fromUserName, String fromUserNameAtDomain, String sn, String sourceID, String targetID) {handler_.postDelayed(new Runnable() {@Overridepublic void run() {if (gb28181_agent_ != null ) {gb28181_agent_.respondBroadcastCommand(from_user_name_, from_user_name_at_domain_,sn_,source_id_, target_id_, true);}}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);}@Overridepublic void ntsOnAudioBroadcast(String commandFromUserName, String commandFromUserNameAtDomain, String sourceID, String targetID) {handler_.postDelayed(new Runnable() {@Overridepublic void run() {stopAudioPlayer();destoryRTPReceiver();if (gb28181_agent_ != null ) {String local_ip_addr = IPAddrUtils.getIpAddress(context_);boolean is_tcp = true; // 默认用TCPrtp_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();}} else {destoryRTPReceiver();}}}}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);}@Overridepublic void ntsOnInviteAudioBroadcastException(String sourceID, String targetID, String errorInfo) {handler_.postDelayed(new Runnable() {@Overridepublic void run() {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);}@Overridepublic void ntsOnInviteAudioBroadcastTimeout(String sourceID, String targetID) {handler_.postDelayed(new Runnable() {@Overridepublic void run() {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);}class PlayerExternalPCMOutput implements NTExternalAudioOutput {private int buffer_size_ = 0;private ByteBuffer pcm_buffer_ = null;@Overridepublic ByteBuffer getPcmByteBuffer(int size)  {if(size < 1)return null;if(buffer_size_ != size) {buffer_size_ = size;pcm_buffer_ = ByteBuffer.allocateDirect(buffer_size_);}return pcm_buffer_;}public void onGetPcmFrame(int ret, int sampleRate, int channel, int sampleSize, int is_low_latency) {if (null == pcm_buffer_)return;pcm_buffer_.rewind();if (ret == 0 && isGB28181StreamRunning && publisherHandle != 0 )// 传给发送端做音频相关处理libPublisher.SmartPublisherOnFarEndPCMData(publisherHandle, pcm_buffer_, sampleRate, channel, sampleSize, is_low_latency);}}class PlayerAudioDataOutput implements NTAudioDataCallback {private int buffer_size_ = 0;private int param_info_size_ = 0;private ByteBuffer buffer_ = null;private ByteBuffer parameter_info_ = null;@Overridepublic ByteBuffer getAudioByteBuffer(int size) {if( size < 1 ) return null;if (size <= buffer_size_ && buffer_ != null )return buffer_;buffer_size_ = align(size + 256, 16);buffer_ = ByteBuffer.allocateDirect(buffer_size_);return buffer_;}@Overridepublic ByteBuffer getAudioParameterInfo(int size) {if(size < 1) return null;if ( size <= param_info_size_ &&  parameter_info_ != null )return  parameter_info_;param_info_size_ = align(size + 32, 16);parameter_info_ = ByteBuffer.allocateDirect(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)  {last_receive_audio_data_time_.set(SystemClock.elapsedRealtime());}}class AudioPlayerDataTimer implements Runnable {public static final int THRESHOLD_MS = 60*1000; public static final int INTERVAL_MS = 10*1000; public AudioPlayerDataTimer(long handle) {handle_ = handle;}@Overridepublic void run() {if (0 == handle_)return;if (handle_ != player_handle_)return;long last_update_time = last_receive_audio_data_time_.get();long cur_time = SystemClock.elapsedRealtime();if ( (last_update_time + this.THRESHOLD_MS) >  cur_time) {// 继续定时器handler_.postDelayed(new AudioPlayerDataTimer(this.handle_), this.INTERVAL_MS);}else {if (gb_source_id_!= null && gb_target_id_ != null) {if (gb28181_agent_ != null)gb28181_agent_.byeAudioBroadcast(gb_source_id_, gb_target_id_);}gb_source_id_= null;gb_target_id_ = null;stopAudioPlayer();destoryRTPReceiver();}}private long handle_;}private boolean startAudioPlay() {if (player_handle_ != 0 )return false;player_handle_ = lib_player_.SmartPlayerOpen(context_);if (player_handle_ == 0)return false;// lib_player_.SetSmartPlayerEventCallbackV2(player_handle_,new EventHandePlayerV2());lib_player_.SmartPlayerSetBuffer(player_handle_, 0);lib_player_.SmartPlayerSetReportDownloadSpeed(player_handle_, 1, 10);lib_player_.SmartPlayerClearRtpReceivers(player_handle_);lib_player_.SmartPlayerAddRtpReceiver(player_handle_, rtp_receiver_handle_);lib_player_.SmartPlayerSetSurface(player_handle_, null);// lib_player_.SmartPlayerSetRenderScaleMode(player_handle_, 1);lib_player_.SmartPlayerSetAudioOutputType(player_handle_, 1);lib_player_.SmartPlayerSetMute(player_handle_, 0);lib_player_.SmartPlayerSetAudioVolume(player_handle_, 100);lib_player_.SmartPlayerSetExternalAudioOutput(player_handle_, new PlayerExternalPCMOutput());lib_player_.SmartPlayerSetUrl(player_handle_, "rtp://xxxxxxxxxxxxxxxxxxx");if (0 != lib_player_.SmartPlayerStartPlay(player_handle_)) {lib_player_.SmartPlayerClose(player_handle_);player_handle_ = 0;Log.e(TAG,  "start audio paly failed");return false;}lib_player_.SmartPlayerSetAudioDataCallback(player_handle_, new PlayerAudioDataOutput());if (0 ==lib_player_.SmartPlayerStartPullStream(player_handle_) ) {// 启动定时器,长时间收不到音频数据,则停止播放,发送BYElast_receive_audio_data_time_.set(SystemClock.elapsedRealtime());handler_.postDelayed(new AudioPlayerDataTimer(player_handle_), AudioPlayerDataTimer.INTERVAL_MS);}return true;}private void stopAudioPlayer() {if (player_handle_ != 0 ) {lib_player_.SmartPlayerStopPullStream(player_handle_);lib_player_.SmartPlayerStopPlay(player_handle_);lib_player_.SmartPlayerClose(player_handle_);player_handle_ = 0;}}private void destoryRTPReceiver() {if (rtp_receiver_handle_ != 0) {lib_player_.UnInitRTPReceiver(rtp_receiver_handle_);lib_player_.DestoryRTPReceiverSession(rtp_receiver_handle_);lib_player_.DestoryRTPReceiver(rtp_receiver_handle_);rtp_receiver_handle_ = 0;}}@Overridepublic void ntsOnInviteAudioBroadcastResponse(String sourceID, String targetID, int statusCode, PlaySessionDescription sessionDescription) {handler_.postDelayed(new Runnable() {@Overridepublic void run() {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());int clock_rate = audio_attr.getClockRate();lib_player_.SetRTPReceiverPayloadType(rtp_receiver_handle_, audio_attr.getPayloadType(),  audio_attr.getEncodingName(), 2, clock_rate);// 如果是PCMA, 会默认填采样率8000, 通道1, 其他音频编码需要手动填入// lib_player_.SetRTPReceiverAudioSamplingRate(rtp_receiver_handle_, 8000);// lib_player_.SetRTPReceiverAudioChannels(rtp_receiver_handle_, 1);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_source_id_ = source_id_;gb_target_id_ = target_id_;}}} 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);}@Overridepublic void ntsOnByeAudioBroadcast(String sourceID, String targetID) {handler_.postDelayed(new Runnable() {@Overridepublic void run() {gb_source_id_ = null;gb_target_id_ = null;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);}@Overridepublic void ntsOnTerminateAudioBroadcast(String sourceID, String targetID) {handler_.postDelayed(new Runnable() {@Overridepublic void run() {gb_source_id_ = null;gb_target_id_ = null;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);}
}

以上是大概的流程,感兴趣的开发者,可Q我89030985,通过自测和现场的反馈,由于我们有回音消除机制,整体的体验还是非常不错的。

有开发者私信我们,如果从头开发Android平台的GB28181接入端,需要多久?我想说的是,如果是按照SPEC实现个DEMO,验证技术可行性的话不难,但是如果是产品级,确保功能完备性能优异长时间运行稳定的话,从头开发,难度还是挺大的。

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

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

相关文章

Android native层实现MediaCodec编码H264/HEVC

Android平台在上层实现mediacodec的编码&#xff0c;资料泛滥&#xff0c;已经不再是难事&#xff0c;今天给大家介绍下&#xff0c;如何在Android native层实现MediaCodec编码H264/HEVC&#xff0c;网上千篇一律的接口说明&#xff0c;这里不再赘述&#xff0c;本文主要介绍下…

GB/T 28181联网系统通信协议结构和技术实现

技术回顾 在本文开头&#xff0c;我们先一起回顾下GB/T28181联网系统通信协议结构&#xff1a; 联网系统在进行视音频传输及控制时应建立两个传输通道&#xff1a;会话通道和媒体流通道。 会话通道用于在设备之间建立会话并传输系统控制命令&#xff1b;媒体流通道用于传输视…

Android平台GB28181设备接入端对接编码前后音视频源类型浅析

前言 今天主要对Android平台GB28181设备接入模块支持的接入数据类型&#xff0c;做个简单的汇总&#xff1a; 编码前数据&#xff08;目前支持的有YV12/NV21/NV12/I420/RGB24/RGBA32/RGB565等数据类型&#xff09;&#xff0c;其中&#xff0c;Android平台前后摄像头数据&…

C++学习之-析构函数必须为虚函数吗?

今天讨论个比较有意思的话题&#xff1a;析构函数是不是必须要为虚函数&#xff1f; 先说答案&#xff1a; 析构函数可以是虚函数&#xff0c;也可以不是虚函数。 再说原因&#xff1a; 析构函数为虚函数的情况&#xff1a;继承 当父类指针释放子类对象时&#xff0c;如果…

如何实现RTMP或RTSP播放端回调YUV/RGB数据?

今天某乎收到个问题推荐&#xff0c;如何实现RTSP回调YUV数据&#xff0c;用于二次处理&#xff1f; 正好前些年我们做RTSP和RTMP直播播放的时候&#xff0c;实现过相关的需求&#xff0c;本文就以Android为例&#xff0c;大概说说具体实现吧。 先说回调yuv或rgb这块意义吧&a…

GB28181控制、传输流程和协议接口之注册|注销和技术实现

注册和注销基本要求 SIP客户端、网关、SIP设备、联网系统等 SIP代理(SIP UA)使用IETFRFC3261中定义的方法 15 GB/T28181—2016Register进行注册和注销。 注册和注销时应进行认证&#xff0c;认证方式应支持数字摘要认证方式&#xff0c;高安全级别的宜支持数字证书的认证方式&…

GB28181状态信息报送解读及Android端国标设备接入技术实现

今天主要聊聊GB/T28181状态信息报送这块&#xff0c;先回顾下协议规范相关细节&#xff0c;然后再针对代码实现&#xff0c;做个简单的说明。 状态消息报送基本要求 当源设备(包括网关、SIP设备、SIP客户端或联网系统)发现工作异常时,应立即向本 SIP监控域 的SIP服务器发送状…

GB28181设备接入端如何实现校时?

在探讨这个问题之前&#xff0c;我们先看看GB/T28181-2016官方文档怎么说的&#xff0c;9.10.1章节校时基本要求提到&#xff1a; 联网内设备支持基于SIP方式或 NTP方式的网络校时功能&#xff0c;标准时间为北京时间。 SIP方式校时见本节具体描述&#xff1b;NTP(见IETFRFC2…

如何在Unity下采集音视频实现轻量级RTSP服务(类似于IPC)

好多开发者在做虚拟仿真、VR教育等场景的时候&#xff0c;遇到个问题&#xff0c;想把头显里面的画面在内网环境下低延迟的同步出来&#xff0c;又不想单独部署流媒体服务器。为此&#xff0c;我们在Unity下&#xff0c;添加了轻量级RTSP服务模块&#xff0c;通过头显端启动个轻…

【Datawhale 大模型基础】第十一章 环境影响

第十一章 环境影响 This blog is based on datawhale files and a paper. The initial consideration revolves around the potential positive or negative direct impact on the environment. Other transformative technological advancements, like the metaverse, are li…

Android GB28181接入端实时位置订阅和上报之-如何获取当前经纬度

我们在做Android平台GB28181的时候&#xff0c;其中实时位置(MobilePosition)订阅和上报这块&#xff0c;涉及到实时经纬度的获取&#xff0c;特别是执法记录、车载系统的那个等场景&#xff0c;几乎就是标配。 今天主要是分享一段实时获取位置的代码&#xff1a; /** Camera…

如何实现Android平台GB28181设备对接Camera2数据

技术背景 在写如何实现Android平台GB28181设备对接Camera2数据说明之前&#xff0c;我在前两年的blog就有针对camera2的RTMP直播推送模块做过技术分享&#xff1a; 在Google 推出Android 5.0的时候, Android Camera API 版本升级到了API2(android.hardware.camera2), 之前使用…

Android平台GB28181设备接入端本地SIP端口被占用或屏蔽怎么办?

好多开发者或厂商&#xff0c;对Android平台GB28181接入模块的定位&#xff0c;大多是IPC国标流程打通模拟&#xff0c;基于这个目的&#xff0c;很难按照标准SPEC规范实现Android平台GB28181设备接入&#xff0c;我们在跟第三方国标平台厂商对接时发现&#xff0c;部分公司&am…

Android平台GB28181设备接入端如何实现本地录像?

实现Android平台GB28181设备接入的时候&#xff0c;有个功能点不可避免&#xff0c;那就是本地录像&#xff0c;实际上&#xff0c;在实现GB28181设备接入模块之前&#xff0c;我们前些年做RTMP推送和轻量级RTSP服务的时候&#xff0c;早已经实现了本地录像功能。 本地录像功能…

国网B接口注册(REGISTER)接口描述和消息示例

技术背景 电网视频监控系统是智能电网的一个重要组成部分&#xff0c;广泛应用于电网的建设、生产、运行、经营等方面。由于视频监控系统在不同的建设时期选用了不同的技术和不同厂家的产品&#xff0c;导致了标准不统一、技术路线不一致。目前国家电网公司智能电网建设&#…

国网B接口资源上报(Push_Resourse)接口描述和消息示例

上篇blog&#xff0c;梳理了国网B接口的REGISTER接口描述和消息示例&#xff0c;前端系统加电启动并初次注册成功后&#xff0c;向平台上报前端系统的设备资源信息&#xff08;包括&#xff1a;视频服务器、DVR/DVS、摄像机、告警设备、环境量采集设备等模拟或数字信号采集设备…

Android平台GB28181设备接入端语音广播如何实现实时音量调节

Android平台GB28181设备接入&#xff0c;语音广播功能非常重要&#xff0c;本文要介绍的&#xff0c;不是语音广播的流程&#xff0c;语音广播流程&#xff0c;之前的blog也有非常详细的分享&#xff0c;感兴趣的可以参考官方规范书的交互流程&#xff1a; 语音广播这块&#x…

GB28181基于TCP协议的视音频媒体传输探究及实现

我们先看看官方规范针对TCP协议的视音频传输描述&#xff1a; 实时视频点播、历史视频回放与下载的 TCP媒体传输应支持基于RTP封装的视音频PS流&#xff0c;封装格式参照IETFRFC4571。 流媒体服务器宜同时支持作为TCP媒体流传输服务端和客户端。默认情况下&#xff0c;前端设…

Android平台GB28181接入端如何对接UVC摄像头?

我们在对接Android平台GB28181接入的时候&#xff0c;有公司提出这样的需求&#xff0c;除了采集执法记录仪摄像头自带的数据外&#xff0c;还想通过执法记录仪采集外接UVC摄像头。 实际上&#xff0c;这块对我们来说有点炒冷饭了&#xff0c;不算新的诉求。​大牛直播SDK​在2…

Android平台实现系统内录(捕获播放的音频)并推送RTMP服务技术方案探究

几年来&#xff0c;我们在做无纸化同屏或在线教育相关场景的时候&#xff0c;总是被一件事情困扰&#xff1a;如何实现Android平台的系统内录&#xff0c;并推送到其他播放端&#xff0c;常用的场景比如做无纸化会议或教育的时候&#xff0c;主讲人或老师需要放一个视频&#x…