
一直以来,好多开发者苦于很难在unity3d下实现RTMP直播推送,本次以大牛直播SDK(Github)的Windows平台RTMP推送模块(以推摄像头为例,如需推屏幕数据,设置相关参数即可)为例,介绍下unity3d的RTMP推送集成。 简单来说,Unity3D环境下,可以直接调用C#的接口封装,针对此,我们先做了一层封装 (nt_publisher_wrapper.cs),核心代码如下: 初始化和基础参数设置: private bool InitSDK() { if (!is_pusher_sdk_init_) { // 设置日志路径(请确保目录存在) String log_path = "D:\\pulisherlog"; NTSmartLog.NT_SL_SetPath(log_path); UInt32 isInited = NTSmartPublisherSDK.NT_PB_Init(0, IntPtr.Zero); if (isInited != 0) { Debug.Log("调用NT_PB_Init失败.."); return false; } is_pusher_sdk_init_ = true; } return true; } public bool OpenPublisherHandle(uint video_option, uint audio_option) { if (publisher_handle_ != IntPtr.Zero) { return true; } publisher_handle_count_ = 0; if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_Open(out publisher_handle_, video_option, audio_option, 0, IntPtr.Zero)) { return false; } if (publisher_handle_ != IntPtr.Zero) { pb_event_call_back_ = new NT_PB_SDKEventCallBack(PbEventCallBack); NTSmartPublisherSDK.NT_PB_SetEventCallBack(publisher_handle_, IntPtr.Zero, pb_event_call_back_); return true; } else { return false; } } private void SetCommonOptionToPublisherSDK() { if (!IsPublisherHandleAvailable()) { Debug.Log("SetCommonOptionToPublisherSDK, publisher handle with null.."); return; } CameraInfo camera = cameras_[cur_sel_camera_index_]; NT_PB_VideoCaptureCapability cap = camera.capabilities_[cur_sel_camera_resolutions_index_]; SetVideoCaptureDeviceBaseParameter(camera.id_.ToString(), (UInt32)cap.width_, (UInt32)cap.height_); SetFrameRate((UInt32)CalBitRate(edit_key_frame_, cap.width_, cap.height_)); SetVideoEncoderType(is_h264_encoder ? 1 : 2); SetVideoQualityV2(CalVideoQuality(cap.width_, cap.height_, is_h264_encoder)); SetVideoMaxBitRate((CalMaxKBitRate(edit_key_frame_, cap.width_, cap.height_, false))); SetVideoKeyFrameInterval((edit_key_frame_)); if (is_h264_encoder) { SetVideoEncoderProfile(1); } SetVideoEncoderSpeed(CalVideoEncoderSpeed(cap.width_, cap.height_, is_h264_encoder)); // 音频相关设置 SetAuidoInputDeviceId(0); SetPublisherAudioCodecType(1); SetPublisherMute(is_mute); SetInputAudioVolume(Convert.ToSingle(edit_audio_input_volume_)); } 预览、停止预览: public bool StartPreview() { if(CheckPublisherHandleAvailable() == false) return false; video_preview_image_callback_ = new NT_PB_SDKVideoPreviewImageCallBack(SDKVideoPreviewImageCallBack); NTSmartPublisherSDK.NT_PB_SetVideoPreviewImageCallBack(publisher_handle_, (int)NTSmartPublisherDefine.NT_PB_E_IMAGE_FORMAT.NT_PB_E_IMAGE_FORMAT_RGB32, IntPtr.Zero, video_preview_image_callback_); if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_StartPreview(publisher_handle_, 0, IntPtr.Zero)) { if (0 == publisher_handle_count_) { NTSmartPublisherSDK.NT_PB_Close(publisher_handle_); publisher_handle_ = IntPtr.Zero; } return false; } publisher_handle_count_++; is_previewing_ = true; return true; } public void StopPreview() { if (is_previewing_ == false) return; is_previewing_ = false; publisher_handle_count_--; NTSmartPublisherSDK.NT_PB_StopPreview(publisher_handle_); if (0 == publisher_handle_count_) { NTSmartPublisherSDK.NT_PB_Close(publisher_handle_); publisher_handle_ = IntPtr.Zero; } } 开始推送、停止推送: public bool StartPublisher(String url) { if (CheckPublisherHandleAvailable() == false) return false; if (publisher_handle_ == IntPtr.Zero) { return false; } if (!String.IsNullOrEmpty(url)) { NTSmartPublisherSDK.NT_PB_SetURL(publisher_handle_, url, IntPtr.Zero); } if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_StartPublisher(publisher_handle_, IntPtr.Zero)) { if (0 == publisher_handle_count_) { NTSmartPublisherSDK.NT_PB_Close(publisher_handle_); publisher_handle_ = IntPtr.Zero; } is_publishing_ = false; return false; } publisher_handle_count_++; is_publishing_ = true; return true; } public void StopPublisher() { if (is_publishing_ == false) return; publisher_handle_count_--; NTSmartPublisherSDK.NT_PB_StopPublisher(publisher_handle_); if (0 == publisher_handle_count_) { NTSmartPublisherSDK.NT_PB_Close(publisher_handle_); publisher_handle_ = IntPtr.Zero; } is_publishing_ = false; } 相关event事件回调: private void PbEventCallBack(IntPtr handle, IntPtr user_data, UInt32 event_id, Int64 param1, Int64 param2, UInt64 param3, UInt64 param4, [MarshalAs(UnmanagedType.LPStr)] String param5, [MarshalAs(UnmanagedType.LPStr)] String param6, IntPtr param7) { String event_log = ""; switch (event_id) { case (uint)NTSmartPublisherDefine.NT_PB_E_EVENT_ID.NT_PB_E_EVENT_ID_CONNECTING: event_log = "连接中"; if (!String.IsNullOrEmpty(param5)) { event_log = event_log + " url:" + param5; } break; case (uint)NTSmartPublisherDefine.NT_PB_E_EVENT_ID.NT_PB_E_EVENT_ID_CONNECTION_FAILED: event_log = "连接失败"; if (!String.IsNullOrEmpty(param5)) { event_log = event_log + " url:" + param5; } break; case (uint)NTSmartPublisherDefine.NT_PB_E_EVENT_ID.NT_PB_E_EVENT_ID_CONNECTED: event_log = "已连接"; if (!String.IsNullOrEmpty(param5)) { event_log = event_log + " url:" + param5; } break; case (uint)NTSmartPublisherDefine.NT_PB_E_EVENT_ID.NT_PB_E_EVENT_ID_DISCONNECTED: event_log = "断开连接"; if (!String.IsNullOrEmpty(param5)) { event_log = event_log + " url:" + param5; } break; default: break; } if(OnLogEventMsg != null) OnLogEventMsg.Invoke(event_id, event_log); } SmartPublishWinMono.cs 调用上述封装的代码即可,本地预览的话,拿到回调的RGB数据,在unity3d上层刷下即可,如下图: 经测试,unity3d下,RTMP推送,配合RTMP播放端,依然可以实现毫秒级延迟的推拉流体验。
我们在实现Windows平台RTSP播放器或RTMP播放器的时候,需要考虑的点很多,比如多实例设计、多绘制模式兼容、软硬解码支持、快照、RTSP下TCP-UDP自动切换等,以下就其中几个方面,做个大概的探讨。 1. 视频绘制模式 我们在实现Windows平台播放的时候,一般首选D3D,D3D不支持的情况下,考虑数据回上来,采用GDI模式,一般实现如下,先做D3D检测,以大牛直播SDK播放端为例(Github),调用NT_SP_IsSupportD3DRender(),检测是否支持D3D模式,如果支持的话,调用NT_SP_SetRenderWindow(), 然后,设置是否等比例缩放(调用NT_SP_SetRenderScaleMode())。 bool is_support_d3d_render = false; Int32 in_support_d3d_render = 0; if (NT.NTBaseCodeDefine.NT_ERC_OK == NTSmartPlayerSDK.NT_SP_IsSupportD3DRender(player_handle_, playWnd.Handle, ref in_support_d3d_render)) { if (1 == in_support_d3d_render) { is_support_d3d_render = true; } } if (is_support_d3d_render) { is_gdi_render_ = false; // 支持d3d绘制的话,就用D3D绘制 NTSmartPlayerSDK.NT_SP_SetRenderWindow(player_handle_, playWnd.Handle); if (btn_check_render_scale_mode.Checked) { NTSmartPlayerSDK.NT_SP_SetRenderScaleMode(player_handle_, 1); } else { NTSmartPlayerSDK.NT_SP_SetRenderScaleMode(player_handle_, 0); } } else { is_gdi_render_ = true; playWnd.Visible = false; // 不支持D3D就让播放器吐出数据来,用GDI绘制 //video frame callback (YUV/RGB) //format请参见 NT_SP_E_VIDEO_FRAME_FORMAT,如需回调YUV,请设置为 NT_SP_E_VIDEO_FRAME_FROMAT_I420 video_frame_call_back_ = new SP_SDKVideoFrameCallBack(SetVideoFrameCallBack); NTSmartPlayerSDK.NT_SP_SetVideoFrameCallBack(player_handle_, (Int32)NT.NTSmartPlayerDefine.NT_SP_E_VIDEO_FRAME_FORMAT.NT_SP_E_VIDEO_FRAME_FORMAT_RGB32, IntPtr.Zero, video_frame_call_back_); } 如果不支持D3D,设置RGB数据回调: video_frame_call_back_ = new SP_SDKVideoFrameCallBack(SetVideoFrameCallBack); NTSmartPlayerSDK.NT_SP_SetVideoFrameCallBack(player_handle_, (Int32)NT.NTSmartPlayerDefine.NT_SP_E_VIDEO_FRAME_FORMAT.NT_SP_E_VIDEO_FRAME_FORMAT_RGB32, IntPtr.Zero, video_frame_call_back_); 数据处理如下: public void SetVideoFrameCallBack(IntPtr handle, IntPtr userData, UInt32 status, IntPtr frame) { if (frame == IntPtr.Zero) { return; } //如需直接处理RGB数据,请参考以下流程 NT_SP_VideoFrame video_frame = (NT_SP_VideoFrame)Marshal.PtrToStructure(frame, typeof(NT_SP_VideoFrame)); NT_SP_VideoFrame pVideoFrame = new NT_SP_VideoFrame(); pVideoFrame.format_ = video_frame.format_; pVideoFrame.width_ = video_frame.width_; pVideoFrame.height_ = video_frame.height_; pVideoFrame.timestamp_ = video_frame.timestamp_; pVideoFrame.stride0_ = video_frame.stride0_; pVideoFrame.stride1_ = video_frame.stride1_; pVideoFrame.stride2_ = video_frame.stride2_; pVideoFrame.stride3_ = video_frame.stride3_; Int32 argb_size = video_frame.stride0_ * video_frame.height_; pVideoFrame.plane0_ = Marshal.AllocHGlobal(argb_size); CopyMemory(pVideoFrame.plane0_, video_frame.plane0_, (UInt32)argb_size); if (playWnd.InvokeRequired) { BeginInvoke(set_video_frame_call_back_, status, pVideoFrame); } else { set_video_frame_call_back_(status, pVideoFrame); } } 在OnPaint()绘制即可: private void SmartPlayerForm_Paint(object sender, PaintEventArgs e) { if (player_handle_ == IntPtr.Zero || !is_gdi_render_ || !is_playing_) { return; } if (cur_video_frame_.plane0_ == IntPtr.Zero) { return; } Bitmap bitmap = new Bitmap(cur_video_frame_.width_, cur_video_frame_.height_, cur_video_frame_.stride0_, System.Drawing.Imaging.PixelFormat.Format32bppRgb, cur_video_frame_.plane0_); int image_width = cur_video_frame_.width_; int image_height = cur_video_frame_.height_; Graphics g = e.Graphics; //获取窗体画布 g.SmoothingMode = SmoothingMode.HighSpeed; int limit_w = this.Width - 60; int limit_h = this.Height - playWnd.Top - 60; if (btn_check_render_scale_mode.Checked) { int d_w = 0, d_h = 0; int left_offset = 0; int top_offset = 0; Brush brush = new SolidBrush(Color.Black); g.FillRectangle(brush, playWnd.Left, playWnd.Top, limit_w, limit_h); GetRenderRect(limit_w, limit_h, image_width, image_height, ref left_offset, ref top_offset, ref d_w, ref d_h); g.DrawImage(bitmap, playWnd.Left + left_offset, playWnd.Top + top_offset, d_w, d_h); //在窗体的画布中绘画出内存中的图像 } else { g.DrawImage(bitmap, playWnd.Left, playWnd.Top, limit_w, limit_h); //在窗体的画布中绘画出内存中的图像 } } 2. 特定机型硬解码 Windows平台硬解码,主要适用于性能偏弱的PC端,或者有多路播放诉求的场景,一般建议在软解性能没问题的情况下,尽量软解,具体处理如下,先检测系统是否支持硬解,如果支持,再做硬解设置,这样的好处在于如果系统不支持硬解,可以继续软解播放,具体设置如下,在调用NT_SP_Open()之前,做检测,因为NT_SP_Open()每个句柄对应一个player实例,多个实例只需要做一次判断即可: is_support_h264_hardware_decoder_ = NT.NTBaseCodeDefine.NT_ERC_OK == NT.NTSmartPlayerSDK.NT_SP_IsSupportH264HardwareDecoder(); is_support_h265_hardware_decoder_ = NT.NTBaseCodeDefine.NT_ERC_OK == NT.NTSmartPlayerSDK.NT_SP_IsSupportH265HardwareDecoder(); if (player_handle_ == IntPtr.Zero) { player_handle_ = new IntPtr(); UInt32 ret_open = NTSmartPlayerSDK.NT_SP_Open(out player_handle_, IntPtr.Zero, 0, IntPtr.Zero); if (ret_open != 0) { player_handle_ = IntPtr.Zero; MessageBox.Show("调用NT_SP_Open失败.."); return; } } 播放之前,设置硬解码: if (checkBox_hardware_decoder.Checked) { NTSmartPlayerSDK.NT_SP_SetH264HardwareDecoder(player_handle_, is_support_h264_hardware_decoder_ ? 1 : 0, 0); NTSmartPlayerSDK.NT_SP_SetH265HardwareDecoder(player_handle_, is_support_h265_hardware_decoder_ ? 1 : 0, 0); } else { NTSmartPlayerSDK.NT_SP_SetH264HardwareDecoder(player_handle_, 0, 0); NTSmartPlayerSDK.NT_SP_SetH265HardwareDecoder(player_handle_, 0, 0); } 3. 只解码关键帧 只解关键帧的场景,也是用于多路播放诉求,比如一般的监控场景,考虑到多路的场景,一般关键帧间隔不大(如1-2秒一个),平台可对现场场景有个宏观了解,如需重点关注某几路画面的时候,再实时取消这个选项,实现全帧播放,所以,只解关键帧一定要做成实时调用的接口才更有设计意义。 // 设置是否只解码关键帧 if (btn_check_only_decode_video_key_frame.Checked) { NTSmartPlayerSDK.NT_SP_SetOnlyDecodeVideoKeyFrame(player_handle_, 1); } else { NTSmartPlayerSDK.NT_SP_SetOnlyDecodeVideoKeyFrame(player_handle_, 0); } 4. 视频view旋转 好多现场的开发人员有这样的困惑,有些设备,在安装时,可能没调整好角度,导致拍出来的角度倒立等,看着很不方便,这时候,如果现场设备比较多的话,不可能每台设备都到现场重新安装,实时view旋转,就体现了价值,具体如下: /* * 设置旋转,顺时针旋转 * degress: 设置0, 90, 180, 270度有效,其他值无效 * 注意:除了0度,其他角度播放会耗费更多CPU * 接口调用成功返回NT_ERC_OK */ [DllImport(@"SmartPlayerSDK.dll")] public static extern UInt32 NT_SP_SetRotation(IntPtr handle, Int32 degress); 视频view选择,会消耗一定的CPU。 5. 实时快照 实时快照功能不表,是一个好的RTSP播放器和RTMP播放器必备的功能,实时快照是把解码后的yuv数据重新编码成png,所以有一定的CPU消耗,不建议过于频繁操作,具体实现如下: if ( String.IsNullOrEmpty(capture_image_path_) ) { MessageBox.Show("请先设置保存截图文件的目录! 点击截图左边的按钮设置!"); return; } if ( player_handle_ == IntPtr.Zero ) { return; } if ( !is_playing_) { MessageBox.Show("请在播放状态下截图!"); return; } String name = capture_image_path_ + "\\" + DateTime.Now.ToString("hh-mm-ss") + ".png"; byte[] buffer1 = Encoding.Default.GetBytes(name); byte[] buffer2 = Encoding.Convert(Encoding.Default, Encoding.UTF8, buffer1, 0, buffer1.Length); byte[] buffer3 = new byte[buffer2.Length + 1]; buffer3[buffer2.Length] = 0; Array.Copy(buffer2, buffer3, buffer2.Length); IntPtr file_name_ptr = Marshal.AllocHGlobal(buffer3.Length); Marshal.Copy(buffer3, 0, file_name_ptr, buffer3.Length); capture_image_call_back_ = new SP_SDKCaptureImageCallBack(SDKCaptureImageCallBack); UInt32 ret = NTSmartPlayerSDK.NT_SP_CaptureImage(player_handle_, file_name_ptr, IntPtr.Zero, capture_image_call_back_); Marshal.FreeHGlobal(file_name_ptr); if (NT.NTBaseCodeDefine.NT_ERC_OK == ret) { // 发送截图请求成功 } else if ((UInt32)NT.NTSmartPlayerDefine.SP_E_ERROR_CODE.NT_ERC_SP_TOO_MANY_CAPTURE_IMAGE_REQUESTS == ret) { // 通知用户延时 MessageBox.Show("Too many capture image requests!"); } else { // 其他失败 } public void SDKCaptureImageCallBack(IntPtr handle, IntPtr userData, UInt32 result, IntPtr file_name) { if (file_name == IntPtr.Zero) return; int index = 0; while (true) { if (0 == Marshal.ReadByte(file_name, index)) break; index++; } byte[] file_name_buffer = new byte[index]; Marshal.Copy(file_name, file_name_buffer, 0, index); byte[] dst_buffer = Encoding.Convert(Encoding.UTF8, Encoding.Default, file_name_buffer, 0, file_name_buffer.Length); String image_name = Encoding.Default.GetString(dst_buffer, 0, dst_buffer.Length); if (playWnd.InvokeRequired) { BeginInvoke(set_capture_image_call_back_, result, image_name); } else { set_capture_image_call_back_(result, image_name); } } 后续,我们将针对RTSP和RTMP播放器设计过程中的其他点,做更进一步的探讨,欢迎大家关注。
一、背景 为满足内网无纸化/电子教室等内网超低延迟需求,避免让用户配置单独的服务器,我们研发了轻量级RTSP服务开发包。 单播不再赘述,这里重点介绍下我们的组播技术方案: 组播解决的主要痛点是服务器部署和带宽占用问题,一般来说,内网电子教室/无纸化/实时同屏场景用RTMP推送+RTMP服务器,然后其他端从服务器拉取RTMP流,这个方案的劣势在于,如果单独部署服务器,需要额外的机器,增加了成本开销,如果教师端机器作为服务器,网络和机器性能双重压力下,负荷过重。 通过组播技术方案,只要网络设备支持组播组网,轻松实现多并发的同屏/摄像头直播场景。 但是,组播的劣势在于,高码率的无线网络环境体验很差,也就是说,如果是Windows或者Android平台推送,Android无线PAD播放,真正好用的,还是RTMP推拉流技术解决方案。 二、基于组播的技术方案 设置需要共享的视音频,设置码率后,点击“配置查看Rtsp服务”,选中“组播”和“SSM”选项,点击启动服务即可: 确定后,返回主界面,点击“发布Rtsp流”,拷贝回调的RTSP url,用我们的SmartPlayer.exe或移动端播放器,播放即可。 注意:需要内网网络设备支持组播功能。 经长时间测试,毫秒级延迟,完全满足内网同屏技术指标。 内置RTSP服务核心接口(以Windows C++ 接口为例:nt_smart_publisher_sdk.h): /* * 创建一个rtsp server * pRtspServerHandle: rtsp server 句柄 * reserve:保留参数传0 * 成功返回 NT_ERC_OK */ NT_UINT32(NT_API *OpenRtspServer)(NT_PHANDLE pRtspServerHandle, NT_INT32 reserve); /* * 设置rtsp server 监听端口, 在StartRtspServer之前必须要设置端口 * rtsp_server_handle: rtsp server 句柄 * port: 端口号,可以设置为554,或者是1024到65535之间,其他值返回失败 * 成功返回 NT_ERC_OK */ NT_UINT32(NT_API *SetRtspServerPort)(NT_HANDLE rtsp_server_handle, NT_INT32 port); /* * 设置rtsp server 鉴权用户名和密码, 这个可以不设置,只有需要鉴权的再设置 * rtsp_server_handle: rtsp server 句柄 * user_name: 用户名,必须是英文 * password:密码,必须是英文 * 成功返回 NT_ERC_OK */ NT_UINT32(NT_API *SetRtspServerUserNamePassword)(NT_HANDLE rtsp_server_handle, NT_PCSTR user_name, NT_PCSTR password); /* * 设置rtsp server 组播, 如果server设置成组播就不能单播,组播和单播只能选一个, 一般来说单播网络设备支持的好,wifi组播很多路由器不支持 * rtsp_server_handle: rtsp server 句柄 * is_multicast: 是否组播, 1为组播, 0为单播, 其他值接口返回错误, 默认是单播 * 成功返回 NT_ERC_OK */ NT_UINT32(NT_API *SetRtspServerMulticast)(NT_HANDLE rtsp_server_handle, NT_INT32 is_multicast); /* * 设置rtsp server 组播组播地址 * rtsp_server_handle: rtsp server 句柄 * multicast_address: 组播地址 * 如果设置的不是组播地址, 将返回错误 * 组播地址范围说明: [224.0.0.0, 224.0.0.255] 为组播预留地址, 不能设置. 可设置范围为[224.0.1.0, 239.255.255.255], 其中SSM地址范围为[232.0.0.0, 232.255.255.255] * 成功返回 NT_ERC_OK */ NT_UINT32(NT_API *SetRtspServerMulticastAddress)(NT_HANDLE rtsp_server_handle, NT_PCSTR multicast_address); /* * 获取rtsp server当前的客户会话数, 这个接口必须在StartRtspServer之后再调用 * rtsp_server_handle: rtsp server 句柄 * session_numbers: 会话数 * 成功返回 NT_ERC_OK */ NT_UINT32(NT_API *GetRtspServerClientSessionNumbers)(NT_HANDLE rtsp_server_handle, NT_INT32* session_numbers); /* * 启动rtsp server * rtsp_server_handle: rtsp server 句柄 * reserve: 保留参数传0 * 成功返回 NT_ERC_OK */ NT_UINT32(NT_API *StartRtspServer)(NT_HANDLE rtsp_server_handle, NT_INT32 reserve); /* * 停止rtsp server * rtsp_server_handle: rtsp server 句柄 * 成功返回 NT_ERC_OK */ NT_UINT32(NT_API *StopRtspServer)(NT_HANDLE rtsp_server_handle); /* * 关闭rtsp server * 调用这个接口之后rtsp_server_handle失效, * 成功返回 NT_ERC_OK */ NT_UINT32 (NT_API *CloseRtspServer)(NT_HANDLE rtsp_server_handle); /*---rtsp server操作接口---*/ 三、基于RTMP的技术方案 注意事项 组网:无线组网,需要好的AP模块才能撑得住大的并发流量,推送端到AP,最好是有线网链接; 服务器部署:如果Windows平台,可以考虑NGINX,如果是Linux,可以考虑SRS或NGINX,服务器可以和Windows平台的教师机部署在一台机器; 教师端:如教师有移动的PAD,可以直接推到RTMP服务器,然后共享出去; 学生端:直接拉取RTMP流播放即可; 教师和学生互动:学生端如需作为示范案例,屏幕数据共享给其他同学,只需请求同屏,数据反推到RTMP服务器,其他学生查看即可。 扩展监控:如果需要更进一步的技术方案,如教师端想监控学生端的屏幕情况,可以有两种方案,如学生端直接推RTMP过来,或者,学生端启动内置RTSP服务,教师端想看的时候,随时看即可(亦可轮询播放)。 RTMP延迟大,这种说法,相对片面,好多是由于推拉流模块本身问题导致(如果服务器系NIGNX或SRS,基本可排除服务器转发导致的大时延,不要再赖服务器了),从我们官方和实际场景来看,RTMP整体技术方案,延迟可做到1秒内,毫秒级。
好多开发者一直搞不清轻量级RTSP服务SDK和RTSP推流SDK的区别,以下是相关区别: 轻量级RTSP服务模块:轻量级RTSP服务解决的核心痛点是避免用户或者开发者单独部署RTSP或者RTMP服务,实现本地的音视频数据(如摄像头、麦克风),编码后,汇聚到内置RTSP服务,对外提供可供拉流的RTSP URL,轻量级RTSP服务,适用于内网环境下,对并发要求不高的场景,支持H.264/H.265,支持RTSP鉴权、单播、组播模式,考虑到单个服务承载能力,我们支持同时创建多个RTSP服务,并支持获取当前RTSP服务会话连接数。 RTSP推流模块:RTSP推流模块,和RTMP推流模块类似,适用于内网或公网环境下,主要适用于第三方RTSP服务对接,如darwin stream server,或者第三方RTSP服务平台,如视频分析平台等特定场景的服务器,支持H.264/H.265,支持TCP、UDP传输模式设定,也支持鉴权服务,RTSP协议的优势主要在于UDP这块,但是UDP数据包,公网容易被block住,而且,网络不稳定容易丢包,所以,能用RTMP推流的场景,一般建议走RTMP,需要特定系统对接的,再走RTSP。
大牛直播SDK多路RTMP/RTSP转RTMP转发软件,系原有转发SDK基础上,官方推出的Windows平台定制版。在秉承低延迟、灵活稳定、低资源占用的前提下,客户无需关注开发细节,只需图形化配置转发等各类参数,实现产品快速上线目的。 如监控类摄像机、NVR等,通过厂商说明或Onvif工具,获取拉流的RTSP地址,图形化配置,完成拉流转发等操作,轻松实现标准RTMP服务器(或CDN)对接。 视频转发支持H.264、H.265(需要RTMP服务器或CDN支持扩展H.265),音频支持配置PCMA/PCMU转AAC后转发,并支持只转发/录制视频或音频,RTSP拉流端支持鉴权和TCP/UDP模式设置和TCP/UDP模式自动切换,整个拉流、转发模块都有非常完善的自动重连机制。 此外,可以通过点击拉流地址或推流地址栏,实现推拉流地址,同步到左侧预览框,实现推拉流音视频数据预览。 运维方面,官方定制版转发系统支持7*24小时不间断运行,自带守护进程,转发程序被误关等各种操作后,会自动启动运行,此外,还支持开机自动启动转发或录像。 功能说明 启动程序支持从守护进程(如需启动转发程序,可点击SmartStreamRelayToolDaemon.exe,守护进程会自动拉起SmartStreamRelayTool.exe,如需关闭转发程序,请先关闭SmartStreamRelayToolDaemon,转发程序方可正常关闭): 添加转发项配置信息 配置说明: 添加配置项:点击页面“添加”按钮: ² 序号:无需关注,系统自动生成; ² 名称:该路转发配置项的描述信息; ² 拉流地址(必须填):需要转发的RTSP或RTMP地址; ² 推流RTMP地址:需要转推的RTMP地址; ² 推流播放地址:需要预览的播放地址; ² 音视频转发选项:可选择之转发音频或视频,亦或同时转发音视频; ² 录像参数配置:可选择录制音频或视频,亦或音视频同时录制,并可设定录像文件前缀。 备注:双击列表配置项,可以查看或编辑配置信息; 删除配置项:选中需要删除的配置数据,点击页面“删除”按钮: 如何转发数据? 选中需要转发的配置数据项目(如需全部转发,点击全选选项即可);点击“拉流”按钮,拉流生效后,页面“流下载速度”会显示当前下载速度; 如需停止拉流,选中配置项,点击“停止拉流”即可;拉流后,选中需要转发的配置项,点击“推流”按钮; 如需停止推流,选中配置项,点击“停止推流”即可;如需对某一路录像,在完成“录像全局配置”的前提下,选中配置项,点击“录像”即可; 如需停止录像,选中配置项,点击“停止录像”即可。 如何预览推拉流数据? 点击需要预览的“拉流地址”或“推流地址”,URL会同步到左侧预览框,即可实现推拉流数据本地预览。如不需播放音频,点击“静音”选项即可。 系统配置: ² 支持程序启动后自动开启转发; ² 支持程序启动后自动开启录像(考虑到Windows平台磁盘读写性能,Windows平台不做多路录像承诺); ² 开机后自动启动(可配置开机自动启动配置名); 录像全局配置: ² 支持设置录像存储目录; ² 支持设定单个录像文件大小; ² 支持设置文件是否增加日期、时间; ² 支持设置是否音频自动转AAC编码后存储。