Android平台实现mp4文件实时推送RTMP|轻量级RTSP服务|GB28181平台

118 篇文章 20 订阅
115 篇文章 1 订阅

好多开发者有这样的诉求,想把本地录制的MP4文件,以实时流数据的形式,推送到RTMP服务器,注入轻量级RTSP服务,或者对接到GB28181平台,这块前几年我们就有对接。

本次以MediaExtractor为例,先利用MediaExtractor,把mp4文件的音视频数据分离,然后调用我们publisher模块,实现编码后的数据对接到RTMP服务器、轻量级RTSP服务或GB28181平台即可,废话不多说,上代码,由于实例代码比较简单,不再赘述用法:

/*
 * SmartPublisherActivity.java
 * Github: https://github.com/daniulive/SmarterStreaming
 */	
private void InitMediaExtractor(){
		File mFile = new File("/storage/emulated/0/","2022.mp4");
	
		if (!mFile.exists()){
			Log.e(TAG, "mp4文件不存在");
			return;
		}

		MediaExtractor mediaExtractor = new MediaExtractor();
		try {
			mediaExtractor.setDataSource(mFile.getAbsolutePath());
		} catch (IOException e) {
			e.printStackTrace();
		}

		int count = mediaExtractor.getTrackCount();//获取轨道数量
		Log.e(TAG, "轨道数量 = "+count);

		for (int i = 0; i < count; i++)
		{
			MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
			String mineType = trackFormat.getString(MediaFormat.KEY_MIME);
			Log.e(TAG, i + "编号通道格式 = " + mineType);

			//视频信道
			if (mineType.startsWith("video/")) {
				video_track_index = i;
				is_has_video = true;

				try {
					video_media_extractor.setDataSource(mFile.getAbsolutePath());
				} catch (IOException e) {
					e.printStackTrace();
				}

				if(mineType.equals("video/avc"))
				{
					video_codec_id = 1;
				}
				else if(mineType.equals("video/hevc"))
				{
					video_codec_id = 2;
				}

				int width = trackFormat.getInteger(MediaFormat.KEY_WIDTH);
				int height = trackFormat.getInteger(MediaFormat.KEY_HEIGHT);
				long duration = trackFormat.getLong(MediaFormat.KEY_DURATION);//总时间
				int video_fps = trackFormat.getInteger(MediaFormat.KEY_FRAME_RATE);//帧率
				max_sample_size = trackFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);//获取视频缓存输出的最大大小

				Log.e(TAG, "video width " + width + ", height: " + height + ", duration: " + duration + ", max_sample_size: " + max_sample_size + ", fps: " + video_fps);
			}

			//音频信道
			if (mineType.startsWith("audio/")) {
				audio_track_index = i;
				is_has_audio = true;

				try {
					audio_media_extractor.setDataSource(mFile.getAbsolutePath());
				} catch (IOException e) {
					e.printStackTrace();
				}


				if(mineType.equals("audio/mp4a-latm"))
				{
					audio_codec_id = 0x10002;
				}

				audio_sample_rate = trackFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);//获取采样率
				int audioTrackBitrate = trackFormat.getInteger(MediaFormat.KEY_BIT_RATE);      //获取比特率
				int channels = trackFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);		   //获取声道数量

				Log.e(TAG, "mp4 audio_sample_rate " + audio_sample_rate + ", audioTrackBitrate: " + audioTrackBitrate + ", channels: " + channels);
			}
		}
	}

视频数据处理,先切到视频信道,然后调用readSampleData(),读取到video数据后,判断是不是关键帧,是关键帧的话,带上sps pps(如果是h265 带上vps sps pps),一般来说,比如无人机等设备回调,大多都贴心的实现了关键帧前携带sps pps,也有的设备是单独发sps pps,所以,对接的时候,可以先把数据打印出来看看,具体问题具体分析即可,获取video数据后,通过SmartPublisherPostVideoEncodedData()投递到底层:

//切换到视频信道
video_media_extractor.selectTrack(video_track_index);
if(IsVpsSpsPps(video_header_checker_buffer, video_codec_id))
{
	is_key_frame = true;
}

if ( isPushing || isRTSPPublisherRunning || isGB28181StreamRunning) {
	libPublisher.SmartPublisherPostVideoEncodedData(publisherHandle, video_codec_id, byteBuffer, video_sample_size, is_key_frame?1:0, cur_sample_time, cur_sample_time);
}

audio也是类似的流程,audio有一点,需要先拿到audio param info,然后,调用readSampleData()获取到audio数据,调用SmartPublisherPostAudioEncodedData()投递出去即可。

byte[] audio_param_info = GetAudioParamInfo();
ByteBuffer parameter_info = ByteBuffer.allocateDirect(2);
parameter_info.put(audio_param_info);

int parameter_info_size = 2;

audio_media_extractor.selectTrack(audio_track_index);
int audio_sample_size = audio_media_extractor.readSampleData(byteBuffer, 0);

if(audio_sample_size < 0)
{
	Log.i(TAG, "audio reach the end..");
	break;
}

long cur_sample_time = audio_media_extractor.getSampleTime()/1000;

if ( isPushing || isRTSPPublisherRunning || isGB28181StreamRunning) {
	libPublisher.SmartPublisherPostAudioEncodedData(publisherHandle, audio_codec_id, byteBuffer, audio_sample_size, 0, cur_sample_time, parameter_info, parameter_info_size);
}

数据投递讲完后,就是推送模块接口的处理,获取到的数据,是可以对接到RTMP推送模块,或者轻量级RTSP服务亦或GB28181设备接入模块,这些模块,都可以在一个实例内完成,所以,我们先调用OpenPushHandle()完成publisherHandle生成,并设置event callback。

	private boolean OpenPushHandle()
	{
		if(publisherHandle != 0)
		{
			return true;
		}

		int audio_opt = 2;
		int video_opt = 2;

		int videoWidth = 640;
		int videoHeight  = 480;

		publisherHandle = libPublisher.SmartPublisherOpen(context_, audio_opt, video_opt,
				videoWidth, videoHeight);

		if (publisherHandle == 0 )
		{
			Log.e(TAG, "OpenPushHandle failed!");
			return false;
		}

		Log.i(TAG, "publisherHandle=" + publisherHandle);

		libPublisher.SetSmartPublisherEventCallbackV2(publisherHandle, new EventHandePublisherV2());

		return true;
	}

RTMP推送相关处理:

	private boolean StartPush()
	{
		if (isPushing)
			return false;

		//relayStreamUrl = "rtmp://192.168.1.77/hls/stream1";

		if (relayStreamUrl == null) {
			Log.e(TAG, "StartPush URL is null...");
			return false;
		}

		if (!OpenPushHandle())
			return false;

		if ( libPublisher.SmartPublisherSetURL(publisherHandle, relayStreamUrl) != 0 )
		{
			Log.e(TAG, "StartPush failed!");
		}

		int startRet = libPublisher.SmartPublisherStartPublisher(publisherHandle);
		if( startRet != 0)
		{
			Log.e(TAG, "Failed to call StartPublisher!");

			if(!isRTSPPublisherRunning && !isGB28181StreamRunning)
			{
				libPublisher.SmartPublisherClose(publisherHandle);
				publisherHandle = 0;
			}

			return false;
		}

		isPushing = true;

		return true;
	}

	public void StopPush()
	{
		if (!isPushing)
			return;

		isPushing = false;

		libPublisher.SmartPublisherStopPublisher(publisherHandle);

		if(!isRTSPPublisherRunning && !isRTSPServiceRunning && !isGB28181StreamRunning)
		{
			libPublisher.SmartPublisherClose(publisherHandle);
			publisherHandle = 0;
		}
	}

轻量级RTSP服务相关处理:

	//启动/停止RTSP服务
	class ButtonRtspServiceListener implements OnClickListener {
		public void onClick(View v) {
			if (isRTSPServiceRunning) {
				stopRtspService();

				btnRtspService.setText("启动RTSP服务");
				btnRtspPublisher.setEnabled(false);

				isRTSPServiceRunning = false;
				return;
			}

			if(!OpenPushHandle())
			{
				return;
			}

			Log.i(TAG, "onClick start rtsp service..");

			rtsp_handle_ = libPublisher.OpenRtspServer(0);

			if (rtsp_handle_ == 0) {
				Log.e(TAG, "创建rtsp server实例失败! 请检查SDK有效性");
			} else {
				int port = 8554;
				if (libPublisher.SetRtspServerPort(rtsp_handle_, port) != 0) {
					libPublisher.CloseRtspServer(rtsp_handle_);
					rtsp_handle_ = 0;
					Log.e(TAG, "创建rtsp server端口失败! 请检查端口是否重复或者端口不在范围内!");
				}


				if (libPublisher.StartRtspServer(rtsp_handle_, 0) == 0) {
					Log.i(TAG, "启动rtsp server 成功!");
				} else {
					libPublisher.CloseRtspServer(rtsp_handle_);
					rtsp_handle_ = 0;
					Log.e(TAG, "启动rtsp server失败! 请检查设置的端口是否被占用!");
				}

				btnRtspService.setText("停止RTSP服务");
				btnRtspPublisher.setEnabled(true);

				isRTSPServiceRunning = true;
			}
		}
	}

	//发布/停止RTSP流
	class ButtonRtspPublisherListener implements OnClickListener {
		public void onClick(View v) {
			if (isRTSPPublisherRunning) {
				stopRtspPublisher();

				btnRtspPublisher.setText("发布RTSP流");
				btnGetRtspSessionNumbers.setEnabled(false);
				btnRtspService.setEnabled(true);
			}
			else
			{
				Log.i(TAG, "onClick start rtsp publisher..");

				boolean startRet = StartRtspStream();

				if (!startRet) {
					Log.e(TAG, "Failed to call StartRtspStream().");
					return;
				}

				btnRtspPublisher.setText("停止RTSP流");
				btnGetRtspSessionNumbers.setEnabled(true);
				btnRtspService.setEnabled(false);
			}
		}
	};

	//当前RTSP会话数弹出框
	private void PopRtspSessionNumberDialog(int session_numbers) {
		final EditText inputUrlTxt = new EditText(this);
		inputUrlTxt.setFocusable(true);
		inputUrlTxt.setEnabled(false);

		String session_numbers_tag = "RTSP服务当前客户会话数: " + session_numbers;
		inputUrlTxt.setText(session_numbers_tag);

		AlertDialog.Builder builderUrl = new AlertDialog.Builder(this);
		builderUrl
				.setTitle("内置RTSP服务")
				.setView(inputUrlTxt).setNegativeButton("确定", null);
		builderUrl.show();
	}

	//获取RTSP会话数
	class ButtonGetRtspSessionNumbersListener implements OnClickListener {
		public void onClick(View v) {
			if (libPublisher != null && rtsp_handle_ != 0) {
				int session_numbers = libPublisher.GetRtspServerClientSessionNumbers(rtsp_handle_);

				Log.i(TAG, "GetRtspSessionNumbers: " + session_numbers);

				PopRtspSessionNumberDialog(session_numbers);
			}
		}
	};

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 boolean initGB28181Agent() {
		if ( gb28181_agent_ != null )
			return  true;

		getLocation(context_);

		String local_ip_addr = IPAddrUtils.getIpAddress(context_);
		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_.addPlayListener(this);
		gb28181_agent_.addDeviceControlListener(this);

		// 必填信息
		gb28181_agent_.setLocalAddress(local_ip_addr);
		gb28181_agent_.setServerParameter(gb28181_sip_server_addr_, gb28181_sip_server_port_, gb28181_sip_server_id_, gb28181_sip_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.gb.ntsignalling.Device gb_device = new com.gb.ntsignalling.Device("34020000001310000001", "安卓测试设备", Build.MANUFACTURER, Build.MODEL,
				"宇宙","火星1","火星", true);

		if (mLongitude != null && mLatitude != null) {
			com.gb.ntsignalling.DevicePosition device_pos = new com.gb.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);

/*
        com.gb28181.ntsignalling.Device gb_device1 = new com.gb28181.ntsignalling.Device("34020000001380000002", "安卓测试设备2", 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_device1.setPosition(device_pos);

            gb_device1.setSupportMobilePosition(true);
        }

        gb28181_agent_.addDevice(gb_device1);


 */

		if (!gb28181_agent_.createSipStack()) {
			gb28181_agent_ = null;
			Log.e(TAG, "initGB28181Agent gb28181_agent_.createSipStack failed.");
			return  false;
		}

		boolean is_bind_local_port_ok = false;

		// 最多尝试5000个端口
		int try_end_port = gb28181_sip_local_port_base_ + 5000;
		try_end_port = try_end_port > 65536 ?65536: try_end_port;

		for (int i = gb28181_sip_local_port_base_; i < try_end_port; ++i) {
			if (gb28181_agent_.bindLocalPort(i)) {
				is_bind_local_port_ok = true;
				break;
			}
		}

		if (!is_bind_local_port_ok) {
			gb28181_agent_.releaseSipStack();
			gb28181_agent_ = null;
			Log.e(TAG, "initGB28181Agent gb28181_agent_.bindLocalPort failed.");
			return  false;
		}

		if (!gb28181_agent_.initialize()) {
			gb28181_agent_.unBindLocalPort();
			gb28181_agent_.releaseSipStack();
			gb28181_agent_ = null;
			Log.e(TAG, "initGB28181Agent gb28181_agent_.initialize failed.");
			return  false;
		}

		return true;
	}

	@Override
	public void ntsRegisterOK(String dateString) {
		Log.i(TAG, "ntsRegisterOK Date: " + (dateString!= null? dateString : ""));
	}

	@Override
	public void ntsRegisterTimeout() {
		Log.e(TAG, "ntsRegisterTimeout");
	}

	@Override
	public void ntsRegisterTransportError(String errorInfo) {
		Log.e(TAG, "ntsRegisterTransportError error:" + (errorInfo != null?errorInfo :""));
	}

	@Override
	public void ntsOnHeartBeatException(int exceptionCount,  String lastExceptionInfo) {
		Log.e(TAG, "ntsOnHeartBeatException heart beat timeout count reached, count:" + exceptionCount +
				", exception info:" + (lastExceptionInfo != null ? lastExceptionInfo : ""));

		// 停止信令, 然后重启
		handler_.postDelayed(new Runnable() {
			@Override
			public void run() {
				Log.i(TAG, "gb28281_heart_beart_timeout");

				stopGB28181Stream();
				destoryRTPSender();

				if (gb28181_agent_ != null) {
					gb28181_agent_.terminateAllPlays(true);

					Log.i(TAG, "gb28281_heart_beart_timeout sip stop");
					gb28181_agent_.stop();

					String local_ip_addr = IPAddrUtils.getIpAddress(context_);
					if (local_ip_addr != null && !local_ip_addr.isEmpty()) {
						Log.i(TAG, "gb28281_heart_beart_timeout get local ip addr: " + local_ip_addr);
						gb28181_agent_.setLocalAddress(local_ip_addr);
					}

					Log.i(TAG, "gb28281_heart_beart_timeout sip start");
					gb28181_agent_.start();
				}
			}

		}, 0);
	}

	@Override
	public void ntsOnInvitePlay(String deviceId, PlaySessionDescription session_des) {
		handler_.postDelayed(new Runnable() {
			@Override
			public void run() {
				MediaSessionDescription video_des = session_des_.getVideoDescription();
				SDPRtpMapAttribute ps_rtpmap_attr = video_des.getPSRtpMapAttribute();

				Log.i(TAG,"ntsInviteReceived, 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());

				// 可以先给信令服务器发送临时振铃响应
				//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_  = 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); // 设置到2M
				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_);
				gb28181_agent_.respondPlayInviteOK(device_id_,local_ip_addr, local_port);

				gb28181_rtp_sender_handle_ = rtp_sender_handle;
			}

			private String device_id_;
			private PlaySessionDescription session_des_;

			public Runnable set(String device_id, PlaySessionDescription 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);
	}

	@Override
	public void ntsOnAckPlay(String deviceId) {
		handler_.postDelayed(new Runnable() {
			@Override
			public 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_, gb28181_rtp_encoding_name_);
				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);
	}

	@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);
	}

	@Override
	public void ntsOnByePlay(String deviceId) {
		handler_.postDelayed(new Runnable() {
			@Override
			public 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);
	}

	@Override
	public void ntsOnTerminatePlay(String deviceId) {
		handler_.postDelayed(new Runnable() {
			@Override
			public void run() {
				Log.i(TAG, "ntsOnTerminatePlay, 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);
	}

	@Override
	public void ntsOnPlayDialogTerminated(String deviceId) {
        /*
        Play会话对应的对话终止, 一般不会出发这个回调,目前只有在响应了200K, 但在64*T1时间后还没收到ACK,才可能会出发
        收到这个请做相关清理处理
        */
		handler_.postDelayed(new Runnable() {
			@Override
			public 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);
	}

	@Override
	public void ntsOnDevicePositionRequest(String deviceId, int interval) {
		handler_.postDelayed(new Runnable() {
			@Override
			public void run() {
				getLocation(context_);

				Log.v(TAG, "ntsOnDevicePositionRequest, deviceId:" + this.device_id_ + ", Longitude:" + mLongitude
						+ ", Latitude:" + mLatitude + ", Time:" + mLocationTime);


				if (mLongitude != null && mLatitude != null) {
					com.gb.ntsignalling.DevicePosition device_pos = new com.gb.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);
	}

	@Override
	public void ntsOnDeviceControlTeleBootCommand(String deviceId, String teleBootValue) {
		handler_.postDelayed(new Runnable() {
			@Override
			public void run() {
				Log.i(TAG, "ntsOnDeviceControlTeleBootCommand device_id:" + device_id_ + " tele_boot_value:" + tele_boot_value_);

				stopGB28181Stream();
				destoryRTPSender();

				if (gb28181_agent_ != null ) {
					gb28181_agent_.terminateAllPlays(true);
					gb28181_agent_.stop();
				}

				// 发送注销消息后,等待2000毫秒, 再释放资源
				handler_.postDelayed(new Runnable() {
					@Override
					public void run() {
						if (gb28181_agent_ != null ) {
							Log.i(TAG, " gb28181_agent_.unInitialize++");
							gb28181_agent_.unInitialize();
							gb28181_agent_.unBindLocalPort();
							gb28181_agent_.releaseSipStack();
							Log.i(TAG, " gb28181_agent_.unInitialize--");

							gb28181_agent_ = null;
						}

						// 200毫秒后再重启
						handler_.postDelayed(new Runnable() {
							@Override
							public void run() {
								Log.i(TAG, "restart gb sip agent.");

								if (null==gb28181_agent_) {
									if (!initGB28181Agent()) {
										Log.e(TAG, "init gb sip agent failed.");
										return;
									}
								}

								if (!gb28181_agent_.isRunning()) {
									if ( !gb28181_agent_.start() ) {
										Log.e(TAG, "restart gb sip agent failed.");
									}
								}
							}

						},200);

					}

				},2000);
			}

			private String device_id_;
			private String tele_boot_value_;

			public Runnable set(String device_id, String tele_boot_value) {
				this.device_id_ = device_id;
				this.tele_boot_value_ = tele_boot_value;
				return this;
			}

		}.set(deviceId, teleBootValue),0);
	}

以上就是大概流程,需要注意的是,本地MP4文件作为实时数据发送的时候,需要注意时间戳的问题,简单来说,确保“1分钟的数据,按照时间戳间隔,1分钟均匀的发出去”。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值