自己做的网站能备案,湖南建筑信息网首页,网站建设一般多少钱一年,一个网站用几个域名技术背景
最近不少开发者找到我们#xff0c;他们在做智能家居等传统行业时#xff0c;希望实现在Android板件拉取本地的RTSP或RTMP流#xff0c;然后对外推送RTMP出去#xff0c;亦或内部启个轻量级RTSP服务#xff0c;提供个对外对接的媒介URL#xff0c;简单来说他们在做智能家居等传统行业时希望实现在Android板件拉取本地的RTSP或RTMP流然后对外推送RTMP出去亦或内部启个轻量级RTSP服务提供个对外对接的媒介URL简单来说设计架构图如下 基于上诉诉求我们以大牛直播SDK 官方Android端的 SmartRelayDemoV2 工程为例大概介绍下相关实现。
整体设计
1. 拉流通过RTSP|RTMP直播播放SDK的数据回调接口拿到音视频数据
2. 转推通过RTMP直播推送SDK的编码后数据输入接口把回调上来的数据传给RTMP直播推送模块实现RTSP|RTMP数据流到RTMP服务器的转发
3. 录像如果需要录像借助RTSP|RTMP直播播放SDK拉到音视频数据后直接存储MP4文件即可
4. 快照如果需要实时快照拉流后解码调用播放端快照接口生成快照因为快照涉及到video数据解码如无必要可不必开启不然会额外消耗性能。
5. 拉流预览如需预览拉流数据只要调用播放端的播放接口即可实现拉流数据预览
6. 数据转AAC后转发考虑到好多监控设备出来的音频可能是PCMA/PCMU的如需要更通用的音频格式可以转AAC后在通过RTMP推送
7. 转推RTMP实时静音只需要在传audio数据的地方加个判断即可
8. 拉流速度反馈通过RTSP播放端的实时码率反馈event拿到实时带宽占用即可
9. 整体网络状态反馈考虑到有些摄像头可能会临时或异常关闭RTMP服务器亦是可以通过推拉流的event回调状态查看那整体网络情况如此界定是拉不到流还是推不到RTMP服务器
10. 数据注入轻量级RTSP服务拉流的数据注入轻量级RTSP服务对外提供RTSP URL。
先上图 Demo主要实现了以下几个功能点展示
1. 设置RTMP、RTSP拉流的URL
2. 设置转推RTMP的URL
3. 实时播放|录像过程中实时静音、实施快照
4. 实时播放
5. 实时录像
6. 拉取的流数据实时转推对应“开始推流”
7. 拉取的流数据注入轻量级RTSP服务启动服务后发布RTSP流对外提供可访问的RTSP URL。
注意以上播放、录像、转推RTMP、注入轻量级RTSP服务四者是可单独工作也可随时启动或停止相关功能互不影响。
相关代码实现
开始拉流
拉流的目的主要是启动数据回调注意拉流并不是直接播放出来窗口只是拿数据如果需要本地预览拉流数据可以点击“开始播放”。
注意“开始推流”和“发布RTSP流”之前一定要先“开始拉流”拿到音视频数据。 private boolean StartPull(){if ( isPulling )return false;if (!OpenPullHandle())return false;libPlayer.SmartPlayerSetAudioDataCallback(playerHandle, new PlayerAudioDataCallback());libPlayer.SmartPlayerSetVideoDataCallback(playerHandle, new PlayerVideoDataCallback());int is_pull_trans_code 1;libPlayer.SmartPlayerSetPullStreamAudioTranscodeAAC(playerHandle, is_pull_trans_code);int startRet libPlayer.SmartPlayerStartPullStream(playerHandle);if (startRet ! 0) {Log.e(TAG, Failed to start pull stream!);if(!isPlaying !isRecording isPushing !isRTSPPublisherRunning){libPlayer.SmartPlayerClose(playerHandle);playerHandle 0;}return false;}isPulling true;return true;}
这里调到OpenPullHandle()封装其实就是启动调研Player的Open()接口获取到player handle然后设置一下基础数据接口比如event callbackbuffer timeTCP/UDP模式、拉流的URL等 private boolean OpenPullHandle(){if (playerHandle ! 0) {return true;}playbackUrl rtsp://admin:daniulive12345192.168.0.120:554/h265/ch1/main/av_stream;if (playbackUrl null) {Log.e(TAG, playback URL with NULL...);return false;}playerHandle libPlayer.SmartPlayerOpen(myContext);if (playerHandle 0) {Log.e(TAG, playerHandle is nil..);return false;}libPlayer.SetSmartPlayerEventCallbackV2(playerHandle,new EventHandePlayerV2());libPlayer.SmartPlayerSetBuffer(playerHandle, playBuffer);// set report download speed// libPlayer.SmartPlayerSetReportDownloadSpeed(playerHandle, 1, 5);//设置RTSP超时时间int rtsp_timeout 12;libPlayer.SmartPlayerSetRTSPTimeout(playerHandle, rtsp_timeout);//设置RTSP TCP/UDP模式自动切换int is_auto_switch_tcp_udp 1;libPlayer.SmartPlayerSetRTSPAutoSwitchTcpUdp(playerHandle, is_auto_switch_tcp_udp);libPlayer.SmartPlayerSaveImageFlag(playerHandle, 1);// It only used when playback RTSP stream..//libPlayer.SmartPlayerSetRTSPTcpMode(playerHandle, 1);libPlayer.SmartPlayerSetUrl(playerHandle, playbackUrl);return true;}
停止拉流 private void StopPull(){if ( !isPulling )return;libPlayer.SmartPlayerStopPullStream(playerHandle);if ( !isPlaying !isRecording !isPushing !isRTSPPublisherRunning){libPlayer.SmartPlayerClose(playerHandle);playerHandle 0;}isPulling false;}
开始播放 private boolean StartPlay(){if (!OpenPullHandle())return false;// 如果第二个参数设置为null则播放纯音频libPlayer.SmartPlayerSetSurface(playerHandle, sSurfaceView);libPlayer.SmartPlayerSetRenderScaleMode(playerHandle, 1);// External Render test// libPlayer.SmartPlayerSetExternalRender(playerHandle, new// RGBAExternalRender());// libPlayer.SmartPlayerSetExternalRender(playerHandle, new// I420ExternalRender());libPlayer.SmartPlayerSetFastStartup(playerHandle, isFastStartup ? 1 : 0);libPlayer.SmartPlayerSetAudioOutputType(playerHandle, 1);if (isMute) {libPlayer.SmartPlayerSetMute(playerHandle, isMute ? 1: 0);}if (isHardwareDecoder){int isSupportHevcHwDecoder libPlayer.SetSmartPlayerVideoHevcHWDecoder(playerHandle, 1);int isSupportH264HwDecoder libPlayer.SetSmartPlayerVideoHWDecoder(playerHandle, 1);Log.i(TAG, isSupportH264HwDecoder: isSupportH264HwDecoder , isSupportHevcHwDecoder: isSupportHevcHwDecoder);}libPlayer.SmartPlayerSetLowLatencyMode(playerHandle, isLowLatency ? 1: 0);libPlayer.SmartPlayerSetRotation(playerHandle, rotate_degrees);int iPlaybackRet libPlayer.SmartPlayerStartPlay(playerHandle);if (iPlaybackRet ! 0) {Log.e(TAG, StartPlay failed!);if ( !isPulling !isRecording !isPushing !isRTSPPublisherRunning){libPlayer.SmartPlayerClose(playerHandle);playerHandle 0;}return false;}isPlaying true;return true;}
停止播放 private void StopPlay(){if ( !isPlaying )return;isPlaying false;libPlayer.SmartPlayerStopPlay(playerHandle);if ( !isPulling !isRecording !isPushing !isRTSPPublisherRunning){libPlayer.SmartPlayerClose(playerHandle);playerHandle 0;}}
开始录像 private boolean StartRecorder(){if (!OpenPullHandle())return false;ConfigRecorderFuntion();int iRecRet libPlayer.SmartPlayerStartRecorder(playerHandle);if (iRecRet ! 0) {Log.e(TAG, StartRecorder failed!);if ( !isPulling !isPlaying !isPushing !isRTSPPublisherRunning){libPlayer.SmartPlayerClose(playerHandle);playerHandle 0;}return false;}isRecording true;return true;}停止录像 private void StopRecorder(){if ( !isRecording )return;isRecording false;libPlayer.SmartPlayerStopRecorder(playerHandle);if ( !isPlaying !isPulling !isPushing !isRTSPPublisherRunning){libPlayer.SmartPlayerClose(playerHandle);playerHandle 0;}}
开始推流 private boolean StartPush(){if (isPushing)return false;relayStreamUrl rtmp://192.168.0.211:1935/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){libPublisher.SmartPublisherClose(publisherHandle);publisherHandle 0;}return false;}isPushing true;return true;}开始推流调到了OpenPushHandle()封装具体代码如下 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(myContext, 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;}
停止推流 public void StopPush(){if (!isPushing)return;isPushing false;libPublisher.SmartPublisherStopPublisher(publisherHandle);if(!isRTSPPublisherRunning !isRTSPServiceRunning){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端口失败! 请检查端口是否重复或者端口不在范围内!);}//String user_name admin;//String password 12345;//libPublisher.SetRtspServerUserNamePassword(rtsp_handle_, user_name, password);//一般来说单播网络设备支持的好wifi组播很多路由器不支持默认单播模式如需使用组播模式确保设备支持后打开注释代码测试即可/*boolean is_enable_multicast true;if(is_enable_multicast){int is_multicast 1;libPublisher.SetRtspServerMulticast(rtsp_handle_, is_multicast);boolean is_enable_ssm_multicast true;String multicast_address ;if(is_enable_ssm_multicast){multicast_address MakeSSMMulticastAddress();}else{multicast_address MakeMulticastAddress();}Log.i(TAG, is_enable_ssm_multicast: is_enable_ssm_multicast multiAddr: multicast_address);libPublisher.SetRtspServerMulticastAddress(rtsp_handle_, multicast_address);}*/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服务 //停止RTSP服务private void stopRtspService() {if(!isRTSPServiceRunning)return;if (libPublisher ! null rtsp_handle_ ! 0) {libPublisher.StopRtspServer(rtsp_handle_);libPublisher.CloseRtspServer(rtsp_handle_);rtsp_handle_ 0;}if(!isPushing){libPublisher.SmartPublisherClose(publisherHandle);publisherHandle 0;}isRTSPServiceRunning false;}
开始发布RTSP流 private boolean StartRtspStream(){if (isRTSPPublisherRunning)return false;String rtsp_stream_name stream1;libPublisher.SetRtspStreamName(publisherHandle, rtsp_stream_name);libPublisher.ClearRtspStreamServer(publisherHandle);libPublisher.AddRtspStreamServer(publisherHandle, rtsp_handle_, 0);if (libPublisher.StartRtspStream(publisherHandle, 0) ! 0){Log.e(TAG, 调用发布rtsp流接口失败!);if (!isPushing){libPublisher.SmartPublisherClose(publisherHandle);publisherHandle 0;}return false;}isRTSPPublisherRunning true;return true;}停止发布RTSP流 //停止发布RTSP流private void stopRtspPublisher(){if(!isRTSPPublisherRunning)return;if (libPublisher ! null) {libPublisher.StopRtspStream(publisherHandle);}if (!isPushing !isRTSPServiceRunning){libPublisher.SmartPublisherClose(publisherHandle);publisherHandle 0;}isRTSPPublisherRunning false;}
获取RTSP连接会话数 //当前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);}}};
总结
以上是大概的流程感兴趣的开发者可自行参考。